
Cli Ascii Logo
- 10 installs
- Updated July 29, 2026
- full-statck-skills/ascii-skills
Generates CLI ASCII art logos and banners with box-drawing borders, block characters, and ANSI 24-bit gradient color, plus a runnable script.
About
Produces copy-pastable terminal ASCII logos with borders and gradient color, and provides a generate_logo.py script and CLI startup integration code. A developer uses it to build a colorful startup banner or figlet-style title for a CLI.
- Box-drawing borders, block characters, and ANSI gradient color
- Ships a runnable generate_logo.py script with integration guidance
Cli Ascii Logo by the numbers
- 10 all-time installs (skills.sh)
- Ranked #397 of 550 CLI & Terminal skills by installs in the Skillselion catalog
- Data as of Jul 30, 2026 (Skillselion catalog sync)
npx skills add https://github.com/full-statck-skills/ascii-skills --skill cli-ascii-logoAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 10 |
|---|---|
| Last updated | July 29, 2026 |
| Repository | full-statck-skills/ascii-skills ↗ |
What it does
Generates CLI ASCII art logos and banners with box-drawing borders, block characters, and ANSI 24-bit gradient color, plus a runnable script.
Files
CLI ASCII Logo
目标
- 生成可直接在终端输出的 ASCII 艺术 Logo(含边框与渐变色)
- 输出“可复制粘贴”的结果(纯文本/带 ANSI 颜色),并提供在 CLI 启动时展示的集成方式
- 提供可运行的生成脚本:
scripts/generate_logo.py
工作流
1. 明确输入
- 名称:如
auto-cli - 副标题:如
Command Line Interface - 终端宽度:默认 80(可根据项目/CI 输出调整)
- 风格:粗块(
█)/ 细线条(#/*)/ 无颜色 - 边框:
╔═╗║ ║╚═╝或纯文本 - 配色:青 → 紫(Spec Kit 风格)、青 → 蓝、橙 → 粉等
2. 生成结果
- 直接运行脚本生成(最可靠):见下方“快速开始”
- 或按需在目标语言里生成(Node/Python/Go),核心是:
- 先得到“等宽字符画”(多行字符串)
- 再做边框拼接
- 再做逐字符渐变(输出 ANSI TrueColor 序列)
3. 集成到 CLI
- 运行入口(
main/bin/__main__)启动时输出一次 - 支持禁用颜色:
- 尊重
NO_COLOR=1 - 提供
--no-color参数 - CI 环境默认关闭(可按需打开)
快速开始(脚本)
在支持 TrueColor 的终端(macOS Terminal / iTerm2 / VS Code 终端)效果最佳。
python3 scripts/generate_logo.py --text auto-cli --subtitle "Command Line Interface"常用参数:
python3 scripts/generate_logo.py \
--text auto-cli \
--subtitle "Command Line Interface" \
--width 46 \
--palette spec-kit \
--frame box交付格式
- 纯文本(无颜色):适合 README / 日志 / 不支持 ANSI 的环境
- ANSI 颜色文本:适合 CLI 启动页(建议提供
--no-color切换) - 建议同时提供:
banner.txt(无颜色)banner.ansi.txt(带颜色)renderBanner()(在你的 CLI 里按环境输出)
参考
- 配色与兼容性建议见 palettes.md
能力边界
✅ 适用场景
- 当你需要使用此技能对应的技术栈时
- 当项目需要遵循最佳实践时
- 当需要快速上手或深入理解核心概念时
⚠️ 需要注意
- 复杂业务逻辑需要结合具体场景调整
- 性能优化需要根据实际数据量评估
❌ 不适用场景
- 不相关的技术栈或框架
- 需要完全自定义的特殊场景
常见陷阱 (Gotchas)
1. 版本兼容性:注意框架版本与依赖库的兼容性,不同版本 API 可能有差异 2. 配置文件格式:配置文件格式错误是最常见的问题,建议使用编辑器的语法检查 3. 环境变量:确保所有必要的环境变量已正确设置,敏感信息不要硬编码 4. 依赖冲突:多版本共存时注意依赖冲突,使用 lock 文件锁定版本 5. 性能陷阱:大数据量场景下注意性能优化,避免 N+1 查询等常见问题
使用流程
Step 1: 环境准备
确保开发环境已安装必要的依赖和工具。
Step 2: 配置初始化
根据项目需求进行基础配置。
Step 3: 核心功能使用
按照示例代码实现核心功能。
Step 4: 测试验证
运行测试确保功能正常。
Step 5: 部署上线
完成开发后进行部署和监控。
配色与兼容性
内置配色(--palette)
spec-kit:青 → 紫(推荐,用于 Spec Kit 风格)cyan-purple:青 → 紫(更偏紫)cyan-blue:青 → 蓝orange-pink:橙 → 粉green-cyan:绿 → 青mono:单色白(也可用于调试)
颜色开关建议
- 通过
--no-color显式关闭颜色 - 尊重
NO_COLOR=1(脚本默认遵守) - 在 CI / 日志场景建议默认关闭,必要时用
--force-color打开
终端兼容性
- ANSI TrueColor(24-bit)在 macOS Terminal、iTerm2、VS Code 终端中通常可用
- 如果用户终端只支持 256 色,TrueColor 也可能“看起来还行”,但渐变会变粗糙
#!/usr/bin/env python3
"""
Generate a Spec-Kit-like ASCII logo/banner for CLI tools.
This script renders a small block font, optionally wraps it in a box frame,
and optionally applies a 24-bit ANSI gradient for terminals that support it.
"""
from __future__ import annotations
import argparse
import os
import sys
from dataclasses import dataclass
from typing import Dict, Iterable, List, Tuple
RGB = Tuple[int, int, int]
@dataclass(frozen=True)
class Palette:
"""Represents a gradient palette from start RGB to end RGB."""
start: RGB
end: RGB
def clamp_u8(value: int) -> int:
"""Clamp an integer to the [0, 255] range."""
return max(0, min(255, int(value)))
def lerp(a: float, b: float, t: float) -> float:
"""Linearly interpolate between a and b by t (0..1)."""
return a + (b - a) * t
def lerp_rgb(start: RGB, end: RGB, t: float) -> RGB:
"""Linearly interpolate between two RGB colors by t (0..1)."""
r = clamp_u8(round(lerp(start[0], end[0], t)))
g = clamp_u8(round(lerp(start[1], end[1], t)))
b = clamp_u8(round(lerp(start[2], end[2], t)))
return (r, g, b)
def ansi_fg(rgb: RGB) -> str:
"""Return an ANSI escape sequence for 24-bit foreground color."""
r, g, b = rgb
return f"\x1b[38;2;{r};{g};{b}m"
def ansi_reset() -> str:
"""Return an ANSI reset escape sequence."""
return "\x1b[0m"
def default_palettes() -> Dict[str, Palette]:
"""Return built-in palette presets."""
return {
"spec-kit": Palette((0, 255, 204), (153, 102, 255)),
"cyan-purple": Palette((0, 230, 255), (170, 0, 255)),
"cyan-blue": Palette((0, 255, 204), (0, 120, 255)),
"orange-pink": Palette((255, 153, 51), (255, 51, 153)),
"green-cyan": Palette((80, 255, 120), (0, 255, 255)),
"mono": Palette((255, 255, 255), (255, 255, 255)),
}
def supports_color(force: bool, no_color: bool) -> bool:
"""Decide whether ANSI coloring should be enabled."""
if force:
return True
if no_color:
return False
if os.environ.get("NO_COLOR"):
return False
return sys.stdout.isatty()
def block_font() -> Dict[str, List[str]]:
"""Return a 5x7 block font mapping for A-Z, 0-9, dash, and space."""
return {
"A": [
" ███ ",
"█ █",
"█ █",
"█████",
"█ █",
"█ █",
"█ █",
],
"B": [
"████ ",
"█ █",
"█ █",
"████ ",
"█ █",
"█ █",
"████ ",
],
"C": [
" ████",
"█ ",
"█ ",
"█ ",
"█ ",
"█ ",
" ████",
],
"D": [
"████ ",
"█ █",
"█ █",
"█ █",
"█ █",
"█ █",
"████ ",
],
"E": [
"█████",
"█ ",
"█ ",
"████ ",
"█ ",
"█ ",
"█████",
],
"F": [
"█████",
"█ ",
"█ ",
"████ ",
"█ ",
"█ ",
"█ ",
],
"G": [
" ████",
"█ ",
"█ ",
"█ ███",
"█ █",
"█ █",
" ████",
],
"H": [
"█ █",
"█ █",
"█ █",
"█████",
"█ █",
"█ █",
"█ █",
],
"I": [
"█████",
" █ ",
" █ ",
" █ ",
" █ ",
" █ ",
"█████",
],
"J": [
"█████",
" █ ",
" █ ",
" █ ",
" █ ",
"█ █ ",
" ██ ",
],
"K": [
"█ █",
"█ █ ",
"█ █ ",
"██ ",
"█ █ ",
"█ █ ",
"█ █",
],
"L": [
"█ ",
"█ ",
"█ ",
"█ ",
"█ ",
"█ ",
"█████",
],
"M": [
"█ █",
"██ ██",
"█ █ █",
"█ █",
"█ █",
"█ █",
"█ █",
],
"N": [
"█ █",
"██ █",
"█ █ █",
"█ ██",
"█ █",
"█ █",
"█ █",
],
"O": [
" ███ ",
"█ █",
"█ █",
"█ █",
"█ █",
"█ █",
" ███ ",
],
"P": [
"████ ",
"█ █",
"█ █",
"████ ",
"█ ",
"█ ",
"█ ",
],
"Q": [
" ███ ",
"█ █",
"█ █",
"█ █",
"█ █ █",
"█ █ ",
" ██ █",
],
"R": [
"████ ",
"█ █",
"█ █",
"████ ",
"█ █ ",
"█ █ ",
"█ █",
],
"S": [
" ████",
"█ ",
"█ ",
" ███ ",
" █",
" █",
"████ ",
],
"T": [
"█████",
" █ ",
" █ ",
" █ ",
" █ ",
" █ ",
" █ ",
],
"U": [
"█ █",
"█ █",
"█ █",
"█ █",
"█ █",
"█ █",
" ███ ",
],
"V": [
"█ █",
"█ █",
"█ █",
"█ █",
"█ █",
" █ █ ",
" █ ",
],
"W": [
"█ █",
"█ █",
"█ █",
"█ █",
"█ █ █",
"██ ██",
"█ █",
],
"X": [
"█ █",
"█ █",
" █ █ ",
" █ ",
" █ █ ",
"█ █",
"█ █",
],
"Y": [
"█ █",
"█ █",
" █ █ ",
" █ ",
" █ ",
" █ ",
" █ ",
],
"Z": [
"█████",
" █",
" █ ",
" █ ",
" █ ",
"█ ",
"█████",
],
"0": [
" ███ ",
"█ █",
"█ ██",
"█ █ █",
"██ █",
"█ █",
" ███ ",
],
"1": [
" █ ",
" ██ ",
" █ ",
" █ ",
" █ ",
" █ ",
" ███ ",
],
"2": [
" ███ ",
"█ █",
" █",
" █ ",
" █ ",
" █ ",
"█████",
],
"3": [
"████ ",
" █",
" █",
" ███ ",
" █",
" █",
"████ ",
],
"4": [
"█ █",
"█ █",
"█ █",
"█████",
" █",
" █",
" █",
],
"5": [
"█████",
"█ ",
"█ ",
"████ ",
" █",
" █",
"████ ",
],
"6": [
" ███ ",
"█ ",
"█ ",
"████ ",
"█ █",
"█ █",
" ███ ",
],
"7": [
"█████",
" █",
" █ ",
" █ ",
" █ ",
" █ ",
" █ ",
],
"8": [
" ███ ",
"█ █",
"█ █",
" ███ ",
"█ █",
"█ █",
" ███ ",
],
"9": [
" ███ ",
"█ █",
"█ █",
" ████",
" █",
" █",
" ███ ",
],
"-": [
" ",
" ",
" ",
"█████",
" ",
" ",
" ",
],
" ": [
" ",
" ",
" ",
" ",
" ",
" ",
" ",
],
"?": [
"████ ",
" █",
" █ ",
" █ ",
" █ ",
" ",
" █ ",
],
}
def normalize_text(text: str) -> str:
"""Normalize text for the 10、Company Manger font renderer."""
return (text or "").upper()
def render_block_text(text: str, font: Dict[str, List[str]]) -> List[str]:
"""Render text using the built-in 5x7 block font."""
normalized = normalize_text(text)
glyphs = [font.get(ch, font["?"]) for ch in normalized]
height = len(font["A"])
lines: List[str] = []
for row in range(height):
parts = [g[row] for g in glyphs]
lines.append(" ".join(parts).rstrip())
return trim_trailing_blank_lines(lines)
def trim_trailing_blank_lines(lines: List[str]) -> List[str]:
"""Trim empty lines at the end of a multi-line block."""
while lines and not lines[-1].strip():
lines.pop()
return lines
def pad_to_width(line: str, width: int) -> str:
"""Right-pad a line with spaces to the requested width."""
if len(line) >= width:
return line[:width]
return line + (" " * (width - len(line)))
def box_frame(lines: List[str], width: int) -> List[str]:
"""Wrap lines in a box frame using box-drawing characters."""
inner_width = max(1, width)
top = "╔" + ("═" * inner_width) + "╗"
bottom = "╚" + ("═" * inner_width) + "╝"
framed = [top]
for line in lines:
framed.append("║" + pad_to_width(line, inner_width) + "║")
framed.append(bottom)
return framed
def center_line(line: str, width: int) -> str:
"""Center a line inside a fixed width with space padding."""
if width <= 0:
return line
text = line.rstrip()
if len(text) >= width:
return text[:width]
left = (width - len(text)) // 2
right = width - len(text) - left
return (" " * left) + text + (" " * right)
def apply_gradient(line: str, palette: Palette) -> str:
"""Apply an ANSI foreground gradient to a single line."""
length = len(line)
if length == 0:
return line
colored: List[str] = []
for i, ch in enumerate(line):
t = 0.0 if length == 1 else (i / (length - 1))
rgb = lerp_rgb(palette.start, palette.end, t)
if ch == " ":
colored.append(ch)
else:
colored.append(ansi_fg(rgb) + ch)
return "".join(colored) + ansi_reset()
def parse_args(argv: Iterable[str]) -> argparse.Namespace:
"""Parse CLI arguments."""
parser = argparse.ArgumentParser(prog="generate_logo.py")
parser.add_argument("--text", required=True, help="Logo text, e.g. auto-cli")
parser.add_argument("--subtitle", default="", help="Subtitle line under the logo")
parser.add_argument("--width", type=int, default=46, help="Inner width of the frame")
parser.add_argument("--palette", default="spec-kit", help="Palette preset name")
parser.add_argument("--frame", choices=["box", "none"], default="box", help="Frame style")
parser.add_argument("--no-color", action="store_true", help="Disable ANSI colors")
parser.add_argument("--force-color", action="store_true", help="Force ANSI colors even when not TTY")
return parser.parse_args(list(argv))
def build_banner_lines(text: str, subtitle: str, width: int) -> Tuple[List[str], int]:
"""Build the uncolored inner lines for the banner and return (lines, inner_width)."""
font = block_font()
logo_lines = render_block_text(text, font)
max_inner = max((len(l) for l in logo_lines), default=0)
inner_width = max(width, max_inner)
centered_logo = [center_line(l, inner_width) for l in logo_lines]
lines: List[str] = list(centered_logo)
if subtitle.strip():
lines.append(center_line(subtitle.strip(), inner_width))
return lines, inner_width
def main(argv: Iterable[str]) -> int:
"""Program entry point."""
args = parse_args(argv)
palettes = default_palettes()
palette = palettes.get(args.palette, palettes["spec-kit"])
use_color = supports_color(force=args.force_color, no_color=args.no_color)
inner_lines, inner_width = build_banner_lines(args.text, args.subtitle, args.width)
if args.frame == "box":
output_lines = box_frame(inner_lines, inner_width)
if use_color:
output_lines = [apply_gradient(line, palette) for line in output_lines]
else:
output_lines = inner_lines
if use_color:
output_lines = [apply_gradient(line, palette) for line in output_lines]
sys.stdout.write("\n".join(output_lines) + "\n")
return 0
if __name__ == "__main__":
raise SystemExit(main(sys.argv[1:]))