diff --git a/.gitignore b/.gitignore index 54f6250ed9..1f790e5ac8 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ # Only track: # - cli-anything-plugin/** # - codex-skill/** +# - gemini-skill/** # - */agent-harness/** (under each software dir) # - .gitignore # Everything else is ignored. @@ -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) diff --git a/gemini-skill/cli-anything/SKILL.md b/gemini-skill/cli-anything/SKILL.md new file mode 100644 index 0000000000..ca150ca619 --- /dev/null +++ b/gemini-skill/cli-anything/SKILL.md @@ -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: +``` +/ +└── agent-harness/ + ├── .md # Project-specific analysis + ├── setup.py # Namespace package config (Phase 7) + ├── cli_anything/ # NO __init__.py here (Namespace Package) + │ └── / # Sub-package for this CLI (HAS __init__.py) + │ ├── README.md # Installation & usage — required + │ ├── _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/_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-`. +- **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.* diff --git a/gemini-skill/cli-anything/patterns.md b/gemini-skill/cli-anything/patterns.md new file mode 100644 index 0000000000..6970a27e4b --- /dev/null +++ b/gemini-skill/cli-anything/patterns.md @@ -0,0 +1,81 @@ +# CLI-Anything Implementation Patterns + +## 1. Backend Integration (utils/_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 ( _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-") + + 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-" +```