cli-anything
Turns any GUI-only software into an agent-usable CLI/REPL harness backed by the software's real engine (Blender, GIMP, LibreOffice, etc.).
SKILL.mdskills/cli-anything/SKILL.md
GitHubFull Role Description
Transforms any GUI software into an agent-usable, stateful CLI harness using the software's native CLI interface as the backend. Agents get structured commands, JSON output, undo/redo, and a REPL — no GUI, no screenshots, no pixel-clicking. Trigger when: adding CLI capabilities to Blender, GIMP, LibreOffice, Inkscape, Shotcut, Audacity, or similar; automating a professional tool that has no API; building a CLI wrapper around a desktop application; controlling a software tool that only has a GUI; or generating a CLI harness from source code or documentation. Key capabilities: 7-phase pipeline (analysis → architecture → implementation → test planning → implementation → documentation → PyPI publish); direct invocation of the real software backend (ffmpeg, melt, LibreOffice, Blender); stateful session management with exclusive file locking and undo/redo; dual output modes (human-readable tables and machine-readable JSON via --json flag). Ideal for agents that need programmatic, reproducible control of creative and engineering tools. Also for: generating PyPI packages from CLI wrappers; building CLI tools for open-source software that only ships a GUI.
CLI-Anything
Transform any open-source GUI application into a command-line tool an AI agent can operate — no display, no mouse. The agent uses Click subcommands to manipulate native project files (XCF, SVG, ODF, MLT XML, .blend) and calls the real software's CLI as a subprocess for rendering and export.
When Invoked
- User asks to make a GUI app agent-controllable
- User asks to add CLI capabilities to Blender, GIMP, LibreOffice, Inkscape, Shotcut, Audacity, OBS, or similar
- User asks how to control a software tool that only has a GUI
- User asks for a CLI wrapper around a desktop application
- User asks to automate a professional tool that has no API
- User asks to generate a CLI from source code or documentation
The 7-Phase Pipeline
Phase 1: Codebase Analysis
- Identify the backend engine — Find the core library/framework the GUI wraps (MLT for Shotcut, ImageMagick for GIMP, Blender Python API for Blender)
- Map GUI actions to API calls — Catalog every button, drag, and menu item as a function call
- Identify the data model — What file formats? How is state represented? (XML, JSON, binary, database?)
- Find existing CLI tools — Backends often ship their own CLI (
melt,ffmpeg,convert,libreoffice --headless,blender --background --python) - Catalog the command/undo system — If the app has undo/redo, it uses a command pattern. These are your CLI operations
Phase 2: CLI Architecture Design
- Choose interaction model — Stateful REPL + Subcommand CLI (recommended: both)
- Define command groups matching logical domains: Project management, Core operations, Import/Export, Configuration, Session/State management
- Design the state model — What persists between commands, where stored, how serialized
- Plan output format — Human-readable (tables, colors) + Machine-readable (JSON via
--jsonflag). Always both.
Phase 3: Implementation
- Start with the data layer — XML/JSON manipulation of native project files
- Add probe/info commands — Agents must inspect before modifying
- Add mutation commands — One command per logical operation
- Add backend integration —
utils/<software>_backend.pythat wraps the real software's CLI. Handle:- Finding the executable (
shutil.which()) - Invoking with proper arguments (
subprocess.run()) - Clear error messages with install instructions if not found
- Finding the executable (
- Add rendering/export — Generate valid project files, invoke the real software for conversion
- Add session management — State persistence, undo/redo with exclusive file locking
- Add the REPL with ReplSkin — Copy
repl_skin.pyintoutils/repl_skin.py, useinvoke_without_command=Trueto make REPL the default
Phase 4: Test Planning (TEST.md - Part 1)
BEFORE writing test code, create tests/TEST.md with:
- Test Inventory Plan — List planned test files and estimated test counts
- Unit Test Plan — For each core module, describe what will be tested with edge cases
- E2E Test Plan — Real-world scenarios with output property verification
- Realistic Workflow Scenarios — Multi-step operations chained together
Phase 5: Test Implementation
- Unit tests (
test_core.py) — Synthetic data, no external dependencies - E2E tests — intermediate files — Verify project files are structurally correct
- E2E tests — true backend — MUST invoke the real software. Create a project, export via the actual backend, verify output:
- File exists and size > 0
- Correct format (magic bytes, ZIP structure, etc.)
- Content verification where possible
- Print artifact paths for manual inspection
- Output verification — Never trust that export works just because it exits successfully
- CLI subprocess tests — Use
_resolve_clihelper to test the installed command:def _resolve_cli(name): import shutil, os, sys force = os.environ.get("CLI_ANYTHING_FORCE_INSTALLED", "").strip() == "1" path = shutil.which(name) if path: return [path] if force: raise RuntimeError(f"{name} not found in PATH") module = name.replace("cli-anything-", "cli_anything.") + "." + name.split("-")[-1] + "_cli" return [sys.executable, "-m", module] - Round-trip test — Create project via CLI, open in GUI, verify correctness
Phase 6: Test Documentation (TEST.md - Part 2)
After running all tests, append to TEST.md:
- Full
pytest -v --tb=nooutput - Summary statistics — total tests, pass rate, execution time
- Coverage notes — any gaps
Phase 6.5: SKILL.md Generation
Generate skills/SKILL.md inside the Python package so it installs with pip. Include:
- YAML frontmatter with name and description
- Command groups table
- Installation prerequisites
- Usage examples
- Agent-specific guidance (always use
--json, check return codes, verify outputs)
Ensure setup.py includes it as package data:
package_data={
"cli_anything.<software>": ["skills/*.md"],
},
Phase 7: PyPI Publishing
- Structure as PEP 420 namespace package —
cli_anything/has NO__init__.py, sub-packages DO - Create
setup.pywithentry_points={"console_scripts": ["cli-anything-<software>=..."]} - Test local install:
pip install -e . - Run tests against installed:
CLI_ANYTHING_FORCE_INSTALLED=1 python3 -m pytest
Critical Rules
The #1 Rule — Use the Real Software
- The CLI MUST call the actual software for rendering and export. Not reimplement in Python.
- Anti-pattern: Building a Pillow-based compositor to replace GIMP
- Correct: Generate valid project files, hand them to the real software
- The software is a hard dependency — not optional, not gracefully degraded
The Rendering Gap
- Most GUI apps apply effects at render time. Manipulating project files alone silently drops effects
- Priority: Native engine (
melt) → Filter translation layer → Manual render script - Never assume export works because it exited successfully — verify programmatically
File Locking for Session State
- When saving session JSON, use exclusive file locking. Never use bare
open("w") + json.dump() - Open with
"r+", lock, then truncate inside the lock
Timecode Precision
- Use
round(), notint(), for float-to-frame conversion - Accept ±1 frame tolerance at non-integer FPS (e.g. 29.97fps = 30000/1001)
Filter Translation Pitfalls (ffmpeg)
- Same filter twice in a chain: merge into one
- Stream ordering for concat: interleaved
[v0][a0][v1][a1] - Effect parameter scales differ between tools — document every mapping explicitly
Directory Structure
<software>/
└── agent-harness/
├── <SOFTWARE>.md # Project-specific analysis and SOP
├── setup.py # PyPI package configuration
└── cli_anything/ # Namespace package (NO __init__.py)
└── <software>/ # Sub-package (HAS __init__.py)
├── __init__.py
├── __main__.py
├── README.md # REQUIRED — installation, usage, testing
├── <software>_cli.py # Main Click CLI entry point
├── core/
│ ├── __init__.py
│ ├── project.py # Project create/open/save/info
│ ├── export.py # Render pipeline + filter translation
│ └── session.py # Stateful session, undo/redo
├── utils/
│ ├── __init__.py
│ ├── <software>_backend.py # Backend: real software invocation
│ └── repl_skin.py # Unified REPL skin
└── tests/
├── TEST.md # Test documentation and results — REQUIRED
├── test_core.py # Unit tests (synthetic data)
└── test_full_e2e.py # E2E tests (real files)
Backend Reference
| Software | Backend CLI | Native Format | System Package |
|---|---|---|---|
| LibreOffice | libreoffice --headless |
.odt/.ods/.odp (ODF ZIP) | apt install libreoffice |
| Blender | blender --background --python |
.blend-cli.json | apt install blender |
| GIMP | gimp -i -b '(script-fu ...)' |
.xcf | apt install gimp |
| Inkscape | inkscape --actions="..." |
.svg (XML) | apt install inkscape |
| Shotcut/Kdenlive | melt or ffmpeg |
.mlt (XML) | apt install melt ffmpeg |
| Audacity | sox |
.aup3 | apt install sox |
| OBS Studio | obs-websocket |
scene.json | apt install obs-studio |
The pattern is always the same: build the data → call the real software → verify the output.
Output Modes
Every command supports dual output:
- Human-readable (default): Tables, colors, formatted text
- Machine-readable (
--jsonflag): Structured JSON for agent consumption
# Human output
cli-anything-gimp project info -p project.json
# JSON output for agents
cli-anything-gimp --json project info -p project.json
Agent rules for CLI output:
- Always use
--jsonfor parseable output - Check return codes — 0 for success, non-zero for errors
- Parse stderr for error messages on failure
- Use absolute paths for all file operations
- Verify outputs exist after export operations