Install
$ agentstack add skill-dwgx-smartcli-cmd-art ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
cmd-art
Render and design terminal visual effects with fx, a living-template engine: an Effect ABC + a @register decorator + pkgutil auto-discovery, so effects, themes, and multi-effect shows all compose. Pure Python stdlib (optional pyfiglet for fonts, PIL for real images). Truecolor + ANSI, tuned for Windows Terminal but works on any VT-capable terminal.
When to use
- User asks for a terminal/CMD animation or ANSI art: "spinning ball", "donut", "matrix rain", "plasma", "fire", "game of life", "3D title", "rainbow banner", "starfield", "tunnel", "fireworks".
- User wants a static ASCII title/banner with a color gradient, or an image turned into ASCII.
- User wants to invent a new console effect (drop one file in
fx/effects/).
The 18 effects (registry names)
Run python -m fx list for the live catalog. Names + aliases as registered:
| name | kind | what | aliases | |------|------|------|---------| | sphere | anim | rotating Lambert-shaded 3D ball ("spinning ball") | | | donut | anim | THE spinning ASCII torus (Sloane donut.c) | | | cube | anim | rotating wireframe cube | | | tunnel | anim | demoscene tunnel fly-through | | | starfield | anim | perspective starfield warp | | | plasma | anim | full-screen sine interference field | wave | | fire | anim | demoscene fire (heat buffer + cooling) | | | rain | anim | matrix digital rain | | | life | anim | Conway's Game of Life, toroidal | game_of_life | | boids | anim | Reynolds flocking | | | fireworks | anim | rising shells → gravity sparks | | | sparkle | anim | dim text under bright glints | | | text3d | anim* | big figlet/block banner, gradient; shimmer animates | banner | | gradient_text | anim | lolcat rainbow / theme-gradient text | lolcat | | banner_scroll | anim | scrolling figlet marquee | marquee | | typewriter | anim | char-by-char reveal with a cursor | | | decrypt | anim | scramble-to-plaintext reveal | | | image2ascii | static | image → half-block truecolor / ASCII ramp | img |
*text3d is static unless --set shimmer=true. image2ascii is a StaticEffect (one frame to the normal screen). Everything else animates in the alt-screen.
How to run
From skills/cmd-art, run the package:
python -m fx [args]
Or by path from any cwd (a PEP-366 prelude bootstraps the package):
python skills/cmd-art/fx/cli.py [args]
Legacy compat shim (preserves the old surface, translates to fx play):
python skills/cmd-art/scripts/ascii_fx.py [--fps N] [--seconds N] [--once] [--width W] [--height H] [args]
Prefer the fx CLI for new work; use the shim only for old commands/imports.
Commands
list [--tag T] [--json]— every registered effect (kind, tags, description, aliases).--tagfilters (e.g.3d,text,particles,classic,field,matrix).--jsonemits full param schemas.show— one effect's details: animated?, tags, aliases, preferred theme, and every--setparam (kind, default, choices, range, help), plus a ready-to-run example.showalso plays scripted shows via--seq/--script(see Compose a show).play [flags]— play one effect. Bounded by default (10s on a TTY when no--seconds/--frames). Flags:--theme NAME— palette (see Themes). Defaults to the effect'spreferred_theme.--seconds N— stop after N seconds.--frames N— stop after N frames.--once— render ONE frame to the normal screen and exit (best fortext3d,image2ascii, previews).--forever— explicitly unbounded (until Ctrl-C).--fps N— frame rate (default: the effect's owndefault_fps).--width W/--height H—0= terminal size (height leaves 1 row headroom).--set K=V— override an effect param (repeatable). Unknown keys fail loudly.gallery [--seconds-per N] [--tag T] [--theme NAME]— tour every animated effect back-to-back in one alt-screen (default 3s each).random [play-flags]— play a random animated effect with a random theme (defaults to 8s).
Non-TTY safety: play/shows degrade to a single plain frame when stdout is not a terminal, and the play loop always restores the terminal (cursor, wrap, alt-screen) via try/finally. Prefer --seconds/--once in automated contexts so the process always terminates.
Themes
--theme names (from fx/theme.py, live via theme_names()): mono, fire, ocean, synthwave, viridis, pastel, matrix-green, rainbow. Default theme is synthwave. Effects never bake in hex; they sample the active Theme (color_at(t) gradient, cycle(phase) HSV/lolcat sweep, .primary/.accent/.base), so swapping the theme restyles every effect for free. Unknown --theme on play fails loud; on shows it falls back to default.
Example commands
- Spinning ball, 5s, ocean:
python -m fx play sphere --seconds 5 --theme ocean - THE donut, faster spin:
python -m fx play donut --set speed=2 --seconds 8 - Matrix rain:
python -m fx play rain --seconds 8 - Fire, taller flames:
python -m fx play fire --set cool=2 - Game of Life, dense start:
python -m fx play life --set fill=0.35 --seconds 10 - 3D title, one static frame:
python -m fx play text3d --set text=KIRO --once - Shimmering animated title:
python -m fx play text3d --set text=KIRO --set shimmer=true --seconds 6 - Rainbow lolcat text:
python -m fx play gradient_text --set text="hello" --theme rainbow - Image → ASCII (half-block):
python -m fx play image2ascii --set path=pic.png --once - Tour everything:
python -m fx gallery --seconds-per 3 - Surprise me:
python -m fx random
Compose a show
Play a timed sequence of segments in ONE alt-screen (no flicker between segments) via show:
- Compact
--seqgrammareffect[:theme[:seconds]],..., withleft|rightsplit-screen in the effect slot:
`` python -m fx show --seq "donut:fire:4,plasma::3,rain:matrix-green:5" python -m fx show --seq "donut|fire:synthwave:6" # donut left, fire right ``
- JSON
--script show.json(list of segment objects):
``json [ {"effect": "donut", "theme": "fire", "seconds": 4, "params": {"speed": 2}}, {"split": ["donut", "fire"], "themes": ["ocean", "fire"], "seconds": 6} ] ` --seconds-per sets the default per-segment duration; --theme is the fallback for segments without one. Each segment gets a local clock (t restarts at 0). Split` is a combinator built from two real effects, not a registered effect.
Knowledge base — look before you build
Before inventing or tuning an effect, consult the SmartCLI knowledge graph at D:/Project/SmartCLI/knowledge/INDEX.md. Its effects/ domain carries exact-formula notes for most of what ships here — [[donut-torus]], [[plasma]], [[fire-lode]] / [[fire-doom-psx]], [[matrix-rain]], [[game-of-life]], [[boids]], [[starfield]], [[tunnel]], [[rotating-cube]], [[ascii-luminance-ramp]] — plus [[color-interpolation]] and [[hsv-cycling-lolcat]] for theme/lolcat color. The works/ wing ([[donut-c]], [[lolcat]], [[cmatrix]], [[terminaltexteffects]], ...) is the design brain for a NEW effect: pick your lane — replica a real effect by measuring its source math, or compose a new one from documented primitives. Read the relevant note before writing render code from scratch; references/effects.md is the local techniques sampler, the graph is the sourced depth.
Add a NEW effect
Drop one module in fx/effects/ — pkgutil auto-imports it, @register wires it into list/show/play/gallery. A module that fails to import is reported (stderr + registry.load_errors()) but never takes the catalog down.
Minimal contract (fx/base.py):
- Subclass
Effect(orStaticEffectfor one-shot art). Set class metadata:name(lowercase, unique registry key),description,tags, optionalaliases,preferred_theme,default_fps, and aparamstuple ofParam(...). Param(name, kind, default, help, choices=None, min=None, max=None)—kind∈int float str bool color. The CLI lists/parses/validates--setagainst these; unknown keys and out-of-range values fail loud.coloraccepts#RRGGBB/RRGGBB(empty = None).- Implement
render(self, ctx: FrameCtx) -> str: return ONE full frame —ctx.heightrows joined by\n, no trailing newline, EVERY cell written. Effects are pure frame producers: never print, sleep, or touch ANSI modes — the play loop owns the terminal. ctx: FrameCtxfields:t(seconds since start),frame_index,width,height,theme(activeTheme, never None),params(coerced dict).- Optional lifecycle:
setup()(allocate buffers/particles before the first frame) andteardown()(release state; also runs on error/Ctrl-C). Overrideis_animated(cls, params)for effects that are static by default but animate on a flag (seetext3d'sshimmer,gradient_text).
Skeleton:
from fx.base import Effect, FrameCtx, Param
from fx.registry import register
@register
class Swirl(Effect):
name = "swirl"
description = "example: a themed diagonal sweep."
tags = ("field", "demo")
default_fps = 24.0
params = (Param("speed", "float", 1.0, "sweep speed", min=0.0, max=10.0),)
def render(self, ctx: FrameCtx) -> str:
rows = []
for y in range(ctx.height):
cells = []
for x in range(ctx.width):
t = ((x + y) / max(1, ctx.width + ctx.height)
+ ctx.t * ctx.params["speed"] * 0.1) % 1.0
r, g, b = ctx.theme.color_at(t)
cells.append(f"\x1b[38;2;{r};{g};{b}m#")
rows.append("".join(cells) + "\x1b[0m")
return "\n".join(rows)
Then python -m fx play swirl --theme rainbow --seconds 5. Duplicate names raise RegistryError (pass @register(replace=True) to intentionally override a built-in).
Constraints / gotchas
- Keep frames exactly
ctx.heightrows andctx.widthcells; auto-size leaves 1 row headroom so the top doesn't scroll off. plasmaand full-field effects are the heaviest (O(width×height) escapes/frame); shrink dimensions or lower--fpsif they stutter.pyfigletpowerstext3d/banner_scroll/gradient_text --set big=trueif importable; otherwise a built-in block font covers A-Z, 0-9, space.image2asciiusesPILif present, else a PNM/built-in demo fallback.- ANSI on Windows is enabled automatically at startup (
enable_vt()), no action needed. references/effects.mdis a techniques sampler — it works a few effects in depth (sphere/block-text projection, plasma field, fire kernel, rain fade), not one section per shipped effect. Read it, plus the per-effect formula notes inknowledge/effects/(see Knowledge base above), when adding or tuning an effect.- Paths above are relative to this skill's folder.
Optional tmux launchers
The play loop already owns a single alt-screen session, so tmux is never required. For convenience, tmux/ ships two POSIX-sh launchers that drop an effect into a tmux popup or split pane:
tmux/fx-popup.sh [fx args]—display-popup -E(tmux >= 3.2).tmux/fx-split.sh [-v] EFFECT [EFFECT2] [fx args]— split the current window (one effect in a new pane, or a 2-up of two).
Both guard on command -v tmux: with no tmux they print the direct/compositor alternative plus install hints and exit 0. play/random runs get a default --seconds 10 bound. Truecolor through tmux needs set -g default-terminal "tmux-256color" + set -ga terminal-overrides ",*:Tc". See tmux/README.md. For a seamless side-by-side without tmux, use the compositor: python -m fx show --seq "donut|fire:synthwave:6".
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: dwgx
- Source: dwgx/SmartCLI
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.