diff --git a/AGENTS.md b/AGENTS.md index 061b8b12b1..82fde69548 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -10,6 +10,20 @@ The toolkit supports multiple AI coding assistants, allowing teams to use their --- +## Quickstart — Add a New Integration in 5 Steps + +If you are new to the codebase and want to add support for a new AI agent, here is the shortest path from zero to a working integration: + +1. **Choose a base class** — most agents only need `MarkdownIntegration`. See [Choose a base class](#1-choose-a-base-class). +2. **Create a subpackage** — add `src/specify_cli/integrations//__init__.py` with the required `key`, `config`, and `registrar_config` fields. +3. **Register it** — add one import and one `_register()` call in `src/specify_cli/integrations/__init__.py` (both alphabetical). +4. **Write a test file** — create `tests/integrations/test_integration_.py` (hyphens in the key become underscores in the filename). +5. **Run and verify** — use `specify init --integration ` to exercise the full install/uninstall cycle. + +Each step is expanded under [Adding a New Integration](#adding-a-new-integration). Note that agent **context files** (`CLAUDE.md`, `AGENTS.md`, …) are **not** handled by the integration — that is owned by the opt-in `agent-context` extension; see [Context file behavior](#4-context-file-behavior). + +--- + ## Integration Architecture Each AI agent is a self-contained **integration subpackage** under `src/specify_cli/integrations//`. The subpackage exposes a single class that declares all metadata and inherits setup/teardown logic from a base class. Built-in integrations are then instantiated and added to the global `INTEGRATION_REGISTRY` by `src/specify_cli/integrations/__init__.py` via `_register_builtins()`. @@ -34,6 +48,30 @@ The registry is the **single source of truth for Python integration metadata**. --- +## IntegrationManifest — File Tracking + +`manifest.py` provides the `IntegrationManifest` class, which records every file an integration installs. This record is what makes uninstall reliable and safe. + +### How it works + +`setup()` receives an `IntegrationManifest` and writes files through it rather than touching the filesystem directly: + +```python +# Produce a new file and record its hash for later verification. +manifest.record_file("commands/speckit.plan.md", processed_content) + +# Adopt a pre-existing file the integration is now responsible for. +manifest.record_existing(".vscode/settings.json") +``` + +The manifest is persisted at `.specify/integrations/.manifest.json` (one per integration, keyed by `key`) and stores a SHA-256 hash per file. When the user runs `specify integration uninstall `, `teardown()` delegates to `manifest.uninstall()`, which removes only files whose current hash still matches the recorded value — so files the user later edited by hand are skipped, not clobbered (use `specify integration uninstall --force` to remove modified tracked files anyway). + +### Why this matters + +Without hash-tracked manifests, uninstall would either remove files it should not (destructive) or leave orphans behind (messy). If you write a custom `setup()`, route **every** file you create through `manifest.record_file(...)` (or `record_existing(...)` for files you adopt) so uninstall can reason about them. + +--- + ## Adding a New Integration ### 1. Choose a base class @@ -511,4 +549,54 @@ Disclosure is **continuous**, not a one-time event. A single AI-disclosure parag --- +## Error Handling and Debugging + +### Common Errors and Fixes + +| Symptom | Likely Cause | Fix | +|---|---|---| +| `Integration '' not found` | Missing `_register()` call | Add `_register(Integration())` inside `_register_builtins()` | +| `NameError: name 'Integration' is not defined` at startup | Missing import | Add `from . import Integration` inside `_register_builtins()` | +| CLI check fails for a `requires_cli: True` agent | `key` does not match the executable name | Set `key` to the exact name `shutil.which(key)` must resolve (e.g. `"cursor-agent"`, not `"cursor"`) | +| Command files have the wrong argument syntax | Wrong `args` value in `registrar_config` | Use `$ARGUMENTS` for Markdown agents, `{{args}}` for TOML/YAML agents, or the agent's custom placeholder | +| `ModuleNotFoundError` on a brand-new subpackage under pytest only | Ambient interpreter with a stale editable `.pth` | Run inside this tree's own venv (see Common Pitfall 6) | +| Uninstall leaves files behind, or skips files you expected removed | Files not recorded via the manifest, or their hash changed after install | Route every created file through `manifest.record_file(...)`; user-edited files are intentionally skipped unless `force=True` | +| Context file (`CLAUDE.md`, etc.) not updated | Expecting the CLI to manage it | Context files are owned by the opt-in `agent-context` extension, not the integration — see [Context file behavior](#4-context-file-behavior) | + +### Debugging Tips + +**Inspect the manifest** to see what an installed integration tracks: + +```bash +cat .specify/integrations/.manifest.json +``` + +**Verify a CLI tool is detected** before debugging a `requires_cli` agent: + +```bash +which # Should print the executable path if installed +``` + +**Verify the installed output structure** after `specify init`: + +```bash +find my-project/ -type f +``` + +--- + +## Contribution Checklist + +Before opening or merging an integration PR, confirm the following: + +- [ ] Added the integration subpackage under `src/specify_cli/integrations//`. +- [ ] Registered it (import **and** `_register()`) in `src/specify_cli/integrations/__init__.py`, both alphabetical. +- [ ] Added or updated tests in `tests/integrations/test_integration_.py`. +- [ ] Verified the install/uninstall flow with `specify init --integration `. +- [ ] Did **not** add `context_file` handling to the CLI (that belongs to the `agent-context` extension). +- [ ] Updated devcontainer files if the agent needs a VS Code extension or CLI install step. +- [ ] Updated this guide or other relevant docs if the integration has special setup or limitations. + +--- + *This documentation should be updated whenever new integrations are added to maintain accuracy and completeness.*