From aradotso-trending-skills-37
Generates complete agent-ready CLI harness for any software or repo: analyzes code, builds Click CLI with JSON output/REPL/undo, writes tests/docs, publishes to PATH.
npx claudepluginhub joshuarweaver/cascade-ai-ml-agents-misc-1 --plugin aradotso-trending-skills-37This skill uses the workspace's default tool permissions.
```markdown
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Guides building MCP servers enabling LLMs to interact with external services via tools. Covers best practices, TypeScript/Node (MCP SDK), Python (FastMCP).
Generates original PNG/PDF visual art via design philosophy manifestos for posters, graphics, and static designs on user request.
---
name: cli-anything-agent-native
description: Make any software agent-controllable by generating a complete CLI harness with JSON output, REPL, tests, and docs in one command
triggers:
- make this software agent-native
- generate a CLI harness for this app
- use cli-anything to wrap this software
- build an agent-ready CLI for this project
- cli-anything build harness
- make this tool work with AI agents
- wrap this application with a CLI using cli-anything
- use cli-anything to expose this software to agents
---
# CLI-Anything: Making ALL Software Agent-Native
> Skill by [ara.so](https://ara.so) โ Daily 2026 Skills collection.
CLI-Anything generates a full agent-ready CLI harness for any software in one command. It analyzes source code or repos, designs a Click-based CLI with structured JSON output, implements REPL interaction, writes comprehensive tests, and publishes to PATH โ bridging AI agents and the world's software through the universal CLI interface.
---
## What CLI-Anything Does
Given any software (local path or GitHub repo), CLI-Anything runs a **7-phase pipeline**:
1. ๐ **Analyze** โ Scans source, maps GUI actions to APIs
2. ๐ **Design** โ Architects command groups, state model, output formats
3. ๐จ **Implement** โ Builds Click CLI with REPL, JSON output, undo/redo
4. ๐ **Plan Tests** โ Creates `TEST.md` with unit + E2E test plans
5. ๐งช **Write Tests** โ Implements comprehensive test suite
6. ๐ **Document** โ Updates `TEST.md` with results
7. ๐ฆ **Publish** โ Creates `setup.py`, installs to PATH
The output lives in `<software>/agent-harness/` and is immediately usable by any AI agent.
---
## Installation
### Prerequisites
- Python 3.10+
- Target software installed (GIMP, Blender, LibreOffice, or any app)
- A supported AI coding agent (Claude Code, OpenCode, OpenClaw, Codex, Qodercli)
### Claude Code (Recommended)
```bash
# Add the CLI-Anything marketplace
/plugin marketplace add HKUDS/CLI-Anything
# Install the plugin
/plugin install cli-anything
Windows users: Claude Code uses
bash. Install Git for Windows (includesbash+cygpath) or use WSL to avoidcygpath: command not founderrors.
git clone https://github.com/HKUDS/CLI-Anything.git
cp -r CLI-Anything/cli-anything-plugin ~/.claude/plugins/cli-anything
/reload-plugins
git clone https://github.com/HKUDS/CLI-Anything.git
# Global install
cp CLI-Anything/opencode-commands/*.md ~/.config/opencode/commands/
cp CLI-Anything/cli-anything-plugin/HARNESS.md ~/.config/opencode/commands/
# Or project-level
cp CLI-Anything/opencode-commands/*.md .opencode/commands/
cp CLI-Anything/cli-anything-plugin/HARNESS.md .opencode/commands/
git clone https://github.com/HKUDS/CLI-Anything.git
mkdir -p ~/.openclaw/skills/cli-anything
cp CLI-Anything/openclaw-skill/SKILL.md ~/.openclaw/skills/cli-anything/SKILL.md
Then invoke: @cli-anything build a CLI for ./gimp
git clone https://github.com/HKUDS/CLI-Anything.git
bash CLI-Anything/codex-skill/scripts/install.sh
# Windows: .\CLI-Anything\codex-skill\scripts\install.ps1
git clone https://github.com/HKUDS/CLI-Anything.git
bash CLI-Anything/qoder-plugin/setup-qodercli.sh
# Claude Code โ generate full CLI for a local app
/cli-anything:cli-anything ./gimp
# Claude Code โ generate from a GitHub repo
/cli-anything:cli-anything https://github.com/blender/blender
# OpenCode
/cli-anything ./gimp
/cli-anything https://github.com/blender/blender
# Codex (natural language)
# "Use CLI-Anything to build a harness for ./gimp"
Claude Code < 2.x: Use
/cli-anythinginstead of/cli-anything:cli-anything.
# Broad refinement โ agent analyzes all gaps
/cli-anything:refine ./gimp
# Focused refinement โ target a specific area
/cli-anything:refine ./gimp "image batch processing and filters"
# OpenCode equivalents
/cli-anything-refine ./gimp
/cli-anything-refine ./gimp "batch processing and filters"
# Codex
# "Use CLI-Anything to refine ./shotcut for picture-in-picture workflows"
Refine is incremental and non-destructive โ run it multiple times to expand coverage.
# OpenCode: validate the generated harness
/cli-anything-validate ./gimp
# OpenCode: run tests
/cli-anything-test ./gimp
# Codex
# "Use CLI-Anything to validate ./libreoffice"
# OpenCode
/cli-anything-list
After the pipeline completes, the harness is in <software>/agent-harness/.
cd gimp/agent-harness
pip install -e .
# Get help (self-documenting for agents)
cli-anything-gimp --help
cli-anything-gimp layer --help
# Create a new project
cli-anything-gimp project new --width 1920 --height 1080 -o poster.json
# Add a layer (JSON output mode)
cli-anything-gimp --json layer add -n "Background" --type solid --color "#1a1a2e"
# Open a file
cli-anything-gimp file open ./image.png
# Export
cli-anything-gimp file export --format png --output ./out.png
Every command supports --json for structured agent consumption:
cli-anything-gimp --json project new --width 800 --height 600
{
"status": "ok",
"command": "project new",
"result": {
"width": 800,
"height": 600,
"layers": [],
"id": "proj_abc123"
}
}
On error:
{
"status": "error",
"command": "layer add",
"error": "No active project. Run `project new` or `file open` first.",
"code": "NO_ACTIVE_PROJECT"
}
# Enter REPL (stateful session)
cli-anything-gimp
# Inside REPL:
# > project new --width 1920 --height 1080
# > layer add -n "Sky" --type gradient
# > layer add -n "Text" --type text --content "Hello"
# > undo
# > redo
# > file export --format jpeg --quality 90
# > exit
cli-anything-gimp undo
cli-anything-gimp redo
cli-anything-gimp history --last 10
gimp/
โโโ agent-harness/
โโโ setup.py # pip-installable package
โโโ TEST.md # Test plan + results
โโโ cli_anything_gimp/
โ โโโ __init__.py
โ โโโ main.py # Click CLI entrypoint
โ โโโ commands/
โ โ โโโ project.py # project new/open/save/close
โ โ โโโ layer.py # layer add/remove/reorder/merge
โ โ โโโ filter.py # filter apply/list/preview
โ โ โโโ file.py # file open/export/import
โ โ โโโ ...
โ โโโ state.py # Application state model
โ โโโ output.py # JSON + human output formatting
โ โโโ repl.py # Interactive REPL
โโโ tests/
โโโ unit/
โ โโโ test_project.py
โ โโโ test_layer.py
โ โโโ ...
โโโ e2e/
โโโ test_workflow_poster.py
โโโ ...
If you need to manually create or extend a harness, here's the standard pattern CLI-Anything follows:
main.py)import click
import json
import sys
from .state import AppState
from .output import output_result, output_error
from .commands import project, layer, file_ops, filters
@click.group()
@click.option("--json", "json_mode", is_flag=True, default=False,
help="Output results as JSON for agent consumption")
@click.pass_context
def cli(ctx, json_mode):
"""CLI-Anything harness for GIMP โ agent-ready interface."""
ctx.ensure_object(dict)
ctx.obj["json_mode"] = json_mode
ctx.obj["state"] = AppState()
cli.add_command(project.group)
cli.add_command(layer.group)
cli.add_command(file_ops.group)
cli.add_command(filters.group)
if __name__ == "__main__":
cli()
commands/layer.py)import click
from ..output import output_result, output_error
@click.group(name="layer")
def group():
"""Manage image layers."""
pass
@group.command("add")
@click.option("-n", "--name", required=True, help="Layer name")
@click.option("--type", "layer_type",
type=click.Choice(["solid", "gradient", "text", "image"]),
default="solid")
@click.option("--color", default="#ffffff", help="Fill color (hex)")
@click.pass_context
def add(ctx, name, layer_type, color):
"""Add a new layer to the active project."""
state = ctx.obj["state"]
json_mode = ctx.obj["json_mode"]
if not state.active_project:
output_error(ctx, "NO_ACTIVE_PROJECT",
"No active project. Run `project new` or `file open` first.")
return
try:
layer = state.active_project.add_layer(name, layer_type, color)
output_result(ctx, {
"layer_id": layer.id,
"name": layer.name,
"type": layer.type,
"index": layer.index,
}, message=f"Added layer '{name}'")
except Exception as e:
output_error(ctx, "LAYER_ADD_FAILED", str(e))
output.py)import click
import json
import sys
def output_result(ctx, data: dict, message: str = ""):
json_mode = ctx.obj.get("json_mode", False)
if json_mode:
click.echo(json.dumps({"status": "ok", "result": data}, indent=2))
else:
if message:
click.echo(f"โ {message}")
for key, val in data.items():
click.echo(f" {key}: {val}")
def output_error(ctx, code: str, message: str):
json_mode = ctx.obj.get("json_mode", False)
if json_mode:
click.echo(json.dumps({
"status": "error",
"code": code,
"error": message
}, indent=2))
else:
click.echo(f"โ Error [{code}]: {message}", err=True)
sys.exit(1)
state.py)from dataclasses import dataclass, field
from typing import Optional, List
import uuid
@dataclass
class Layer:
id: str = field(default_factory=lambda: f"layer_{uuid.uuid4().hex[:8]}")
name: str = ""
type: str = "solid"
color: str = "#ffffff"
index: int = 0
visible: bool = True
@dataclass
class Project:
id: str = field(default_factory=lambda: f"proj_{uuid.uuid4().hex[:8]}")
width: int = 1920
height: int = 1080
layers: List[Layer] = field(default_factory=list)
_history: List[dict] = field(default_factory=list)
def add_layer(self, name: str, layer_type: str, color: str) -> Layer:
layer = Layer(name=name, type=layer_type, color=color,
index=len(self.layers))
self._history.append({"action": "add_layer", "layer": layer})
self.layers.append(layer)
return layer
def undo(self):
if not self._history:
raise ValueError("Nothing to undo")
last = self._history.pop()
if last["action"] == "add_layer":
self.layers.remove(last["layer"])
@dataclass
class AppState:
active_project: Optional[Project] = None
def new_project(self, width: int, height: int) -> Project:
self.active_project = Project(width=width, height=height)
return self.active_project
setup.pyfrom setuptools import setup, find_packages
setup(
name="cli-anything-gimp",
version="0.1.0",
packages=find_packages(),
install_requires=["click>=8.0"],
entry_points={
"console_scripts": [
"cli-anything-gimp=cli_anything_gimp.main:cli",
],
},
)
# Run all tests
cd gimp/agent-harness
pytest tests/ -v
# Run only unit tests
pytest tests/unit/ -v
# Run E2E workflow tests
pytest tests/e2e/ -v
# With coverage
pytest tests/ --cov=cli_anything_gimp --cov-report=term-missing
# tests/unit/test_layer.py
from click.testing import CliRunner
from cli_anything_gimp.main import cli
import json
def test_add_layer_json_output():
runner = CliRunner()
# First create a project
result = runner.invoke(cli, ["--json", "project", "new",
"--width", "800", "--height", "600"])
assert result.exit_code == 0
result = runner.invoke(cli, ["--json", "layer", "add",
"-n", "Background", "--type", "solid",
"--color", "#ff0000"])
assert result.exit_code == 0
data = json.loads(result.output)
assert data["status"] == "ok"
assert data["result"]["name"] == "Background"
def test_add_layer_no_project_returns_error():
runner = CliRunner()
result = runner.invoke(cli, ["--json", "layer", "add", "-n", "Test"])
data = json.loads(result.output)
assert data["status"] == "error"
assert data["code"] == "NO_ACTIVE_PROJECT"
# tests/e2e/test_workflow_poster.py
from click.testing import CliRunner
from cli_anything_gimp.main import cli
import json
def test_full_poster_workflow():
runner = CliRunner()
# 1. New project
r = runner.invoke(cli, ["--json", "project", "new",
"--width", "1920", "--height", "1080"])
assert json.loads(r.output)["status"] == "ok"
# 2. Add background
r = runner.invoke(cli, ["--json", "layer", "add",
"-n", "Background", "--color", "#1a1a2e"])
assert json.loads(r.output)["status"] == "ok"
# 3. Add text layer
r = runner.invoke(cli, ["--json", "layer", "add",
"-n", "Title", "--type", "text"])
assert json.loads(r.output)["status"] == "ok"
# 4. Export
r = runner.invoke(cli, ["--json", "file", "export",
"--format", "png", "--output", "/tmp/poster.png"])
assert json.loads(r.output)["status"] == "ok"
CLI-Anything has been demonstrated and tested against 11 apps:
| App | Harness Name | Key Command Groups |
|---|---|---|
| GIMP | cli-anything-gimp | project, layer, filter, file |
| Blender | cli-anything-blender | scene, object, render, animation |
| LibreOffice | cli-anything-libreoffice | document, spreadsheet, presentation |
| Shotcut | cli-anything-shotcut | project, timeline, clip, export |
| Zoom | cli-anything-zoom | meeting, audio, video, participants |
| + 6 more | โ | โ |
# Agent workflow: create โ edit โ export
cli-anything-gimp --json project new --width 1200 --height 800 | \
jq -r '.result.id' | \
xargs -I{} cli-anything-gimp --json layer add -n "BG" --color "#000"
cli-anything-gimp --json file export --format png --output result.png
import subprocess, json
def run_cli(cmd: list[str]) -> dict:
result = subprocess.run(
["cli-anything-gimp", "--json"] + cmd,
capture_output=True, text=True
)
return json.loads(result.stdout)
state = run_cli(["project", "status"])
if state["status"] == "ok" and state["result"]["has_active_project"]:
run_cli(["layer", "add", "-n", "Overlay", "--type", "solid"])
else:
run_cli(["project", "new", "--width", "1920", "--height", "1080"])
# Agents can always self-discover via --help
cli-anything-gimp --help
cli-anything-gimp layer --help
cli-anything-gimp filter --help
cygpath: command not found (Windows)Claude Code runs via bash. Install Git for Windows (includes bash + cygpath) or use WSL.
command not found: cli-anything-gimp# Ensure the harness is installed
cd gimp/agent-harness && pip install -e .
# Verify entry point
pip show cli-anything-gimp
which cli-anything-gimp
No active project errorsThe generated CLI is stateful within a session. In scripts, always run project new or file open before other commands:
cli-anything-gimp project new --width 1920 --height 1080
cli-anything-gimp layer add -n "Layer 1"
/reload-plugins
# Verify with:
/plugin list
HARNESS.md must be in the same directory as the command .md files:
cp CLI-Anything/cli-anything-plugin/HARNESS.md ~/.config/opencode/commands/
The refine command adds new commands but existing tests are non-destructive. If tests break, run:
cd gimp/agent-harness
/cli-anything:refine ./gimp # re-run to fix gaps
pytest tests/ -v
Check that the application binary is on your $PATH or use absolute paths when invoking the harness's underlying API. The generated state.py typically includes a BINARY_PATH config you can override:
# In cli_anything_libreoffice/state.py
BINARY_PATH = "/Applications/LibreOffice.app/Contents/MacOS/soffice"
| Principle | Implementation |
|---|---|
| Structured output | Every command supports --json flag |
| Self-describing | Full --help on all commands and groups |
| Stateful REPL | AppState persists across REPL commands |
| Undo/redo | History stack in state model |
| Agent-first errors | Errors include code + message in JSON |
| Composable | Commands chain via pipes and scripting |
| Deterministic | Same inputs โ same outputs, no side effects |