Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# Only track:
# - cli-anything-plugin/**
# - codex-skill/**
# - gemini-skill/**
# - */agent-harness/** (under each software dir)
# - .gitignore
# Everything else is ignored.
Expand All @@ -19,6 +20,7 @@
# Step 3: Allow cli-anything-plugin entirely
!/cli-anything-plugin/
!/codex-skill/
!/gemini-skill/
!/openclaw-skill/

# Step 4: Allow each software dir (top level only)
Expand Down
87 changes: 87 additions & 0 deletions gemini-skill/cli-anything/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
---
name: cli-anything
description: Use when creating, testing, or maintaining Agent-Native CLIs for existing software following the HKUDS/CLI-Anything protocol. This is specifically for building harnesses that allow AI agents to control complex GUI software (Blender, GIMP, LibreOffice) via structured command-line interfaces.
---

# CLI-Anything: Agent Harness Standard Operating Procedure

## Core Vision & The Iron Law
CLI-Anything bridges the gap between AI agents and GUI software by creating a stateful, structured, and verifiable CLI.

> [!IMPORTANT]
> **THE IRON LAW (Rule #1):** The CLI MUST invoke the actual software for rendering and export. **DO NOT reimplement** the software's functionality (e.g., using Pillow to replace GIMP). The software is a **hard dependency**, not optional.

## Mandatory Directory Structure
All harnesses MUST follow this PEP 420 Namespace package structure:
```
<software>/
└── agent-harness/
├── <SOFTWARE>.md # Project-specific analysis
├── setup.py # Namespace package config (Phase 7)
├── cli_anything/ # NO __init__.py here (Namespace Package)
│ └── <software>/ # Sub-package for this CLI (HAS __init__.py)
│ ├── README.md # Installation & usage — required
│ ├── <software>_cli.py # Main entry (Click + REPL)
│ ├── core/ # Domain modules (project.py, export.py, etc.)
│ ├── utils/ # backend.py (wrappers), repl_skin.py
│ └── tests/
│ ├── TEST.md # Test plan and results — required
│ ├── test_core.py
│ └── test_full_e2e.py
```

## The 7-Phase SOP

### Phase 1: Codebase Analysis
- Identify the backend engine (MLT, ImageMagick, bpy).
- Map GUI actions to API calls and identify the data model (XML, JSON, .blend).
- Find existing low-level CLI tools (`melt`, `ffmpeg`, `convert`).

### Phase 2: CLI Architecture Design
- **Interaction Model**: Support both **Stateful REPL** and **Subcommand CLI**.
- **JSON Output**: Every command MUST support `--json` for machine parsing.
- **State Model**: Define what persists (open project, selection) and how it serializes.

### Phase 3: Implementation
- **Data Layer**: Start with direct manipulation of project files (XML/JSON).
- **Backend Wrapper**: Create `utils/<software>_backend.py`. Use `shutil.which()` to find the executable and `subprocess.run()` to invoke it. Raise `RuntimeError` with clear install instructions if missing.
- **REPL Skin**: Copy `repl_skin.py` from the plugin source to `utils/`. Use the `ReplSkin` class for banner, help, and styled messages. **REPL MUST be the default behavior** when no subcommand is given.

### Phase 4: Test Planning (TEST.md Part 1 - BEFORE Code)
**REQUIRED:** Create `tests/TEST.md` containing:
1. **Test Inventory Plan**: List planned files and test counts.
2. **Unit Test Plan**: Map modules/functions/edge cases.
3. **E2E Test Plan**: Scenarios with real files and verified output properties.
4. **Workflow Scenarios**: Titles, simulations (e.g. "podcast mix"), and operations chained.

### Phase 5: Test Implementation
- **Unit Tests**: Isolated with synthetic data.
- **E2E True Backend**: MUST invoke the real software. Verify file exists, size > 0, and **magic bytes/format validation**.
- **Artifact Paths**: Tests MUST print the paths of generated artifacts (PDF, MP4) for manual inspection.
- **Subprocess Tests**: Use the `_resolve_cli` helper to test the *installed* command. Never hardcode `python -m`.

### Phase 6: Test Documentation (TEST.md Part 2 - AFTER Execution)
**REQUIRED:** Append to `tests/TEST.md`:
1. **Test Results**: Full `pytest -v` output.
2. **Summary Stats**: Total tests, pass rate, time.

### Phase 7: Packaging & Publishing
- Use `setuptools.find_namespace_packages(include=["cli_anything.*"])`.
- Package name convention: `cli-anything-<software>`.
- **Namespace rule**: The `cli_anything` directory must NOT contain an `__init__.py`.

## Key Principles & Rules
- **No Graceful Degradation**: If the software is missing, fail loudly with install instructions.
- **The Rendering Gap**: Ensure filters/effects added via CLI are actually applied during render (use native engines or translated filtergraphs).
- **Timecode Precision**: Use `round()`, not `int()`, for float-to-frame conversion to avoid drifting.
- **Verification**: "It ran without errors" is NOT enough. Programmatically verify magic bytes, ZIP structure, or frame probes.

## Implementation Patterns
See [patterns.md](file:///C:/Users/SZGF/.gemini/skills/cli-anything/patterns.md) for boilerplate code regarding:
- Backend wrappers with `shutil.which`.
- Subprocess test helpers (`_resolve_cli`).
- REPL Skin integration.
- Output artifact verification.

---
*Reference: HKUDS/CLI-Anything HARNESS.md standard.*
81 changes: 81 additions & 0 deletions gemini-skill/cli-anything/patterns.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# CLI-Anything Implementation Patterns

## 1. Backend Integration (utils/<software>_backend.py)
```python
import shutil
import subprocess
import os

def find_software():
path = shutil.which("software-cmd")
if path:
return path
raise RuntimeError(
"Software is not installed. Install it with:\n"
" apt install software-package # Debian/Ubuntu\n"
" brew install software-package # macOS"
)

def run_operation(input_path, output_path):
cmd = find_software()
result = subprocess.run(
[cmd, "--headless", "--input", input_path, "--output", output_path],
capture_output=True, text=True, check=True
)
return {"status": "success", "output": output_path}
```

## 2. CLI Entry Point with REPL ( <software>_cli.py )
```python
import click
from .utils.repl_skin import ReplSkin

@click.group(invoke_without_command=True)
@click.pass_context
def cli(ctx):
if ctx.invoked_subcommand is None:
ctx.invoke(repl)

@cli.command()
def repl():
skin = ReplSkin("SoftwareName", version="1.0.0")
skin.print_banner()
# ... REPL loop using prompt_toolkit
```

## 3. Subprocess Test Helper (_resolve_cli)
Put this in `tests/test_full_e2e.py`:
```python
import shutil
import os
import sys
import subprocess

def _resolve_cli(name):
"""Resolve installed CLI command; falls back to python -m for dev."""
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. Install with: pip install -e .")
module = name.replace("cli-anything-", "cli_anything.") + "." + name.split("-")[-1] + "_cli"
return [sys.executable, "-m", module]

class TestCLISubprocess:
CLI_BASE = _resolve_cli("cli-anything-<software>")

def _run(self, args):
return subprocess.run(self.CLI_BASE + args, capture_output=True, text=True)
```

## 4. Output Verification (E2E Tests)
```python
def test_export_verification(tmp_dir):
# ... run export ...
assert os.path.exists(output_file)
assert os.path.getsize(output_file) > 1000
with open(output_file, "rb") as f:
# Check PDF Magic Bytes
assert f.read(5) == b"%PDF-"
```