You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: AGENTS.md
+29-3Lines changed: 29 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -147,12 +147,12 @@ class CodexIntegration(SkillsIntegration):
147
147
148
148
| Field | Location | Purpose |
149
149
|---|---|---|
150
-
|`key`| Class attribute | Unique identifier; for CLI-based integrations (`requires_cli: True`), must match the CLI executable name|
150
+
|`key`| Class attribute | Unique identifier; for most CLI-based integrations this matches the executable name, but see `cli_executable` below for exceptions|
|`context_file`| Class attribute (str or None) | Path to agent context/instructions file (e.g., `"CLAUDE.md"`, `".github/copilot-instructions.md"`) |
154
154
155
-
**Key design rule:** For CLI-based integrations (`requires_cli: True`), `key`must be the actual executable name (e.g., `"cursor-agent"`not `"cursor"`). This ensures `shutil.which(key)`works for CLI-tool checks without special-case mappings. IDE-based integrations (`requires_cli: False`) should use their canonical identifier (e.g., `"windsurf"`, `"copilot"`).
155
+
**Key design rule:** For CLI-based integrations (`requires_cli: True`), `key`should generally match the CLI executable name so that the default `is_cli_available()` check works without any override. When the executable name differs from the key (e.g., RovoDev's key is `"rovodev"`but the binary is `"acli"`), override the `cli_executable` property or `is_cli_available()`method — see [§6 Optional overrides](#6-optional-overrides) below. IDE-based integrations (`requires_cli: False`) should use their canonical identifier (e.g., `"windsurf"`, `"copilot"`).
156
156
157
157
### 3. Register it
158
158
@@ -222,11 +222,37 @@ The base classes handle most work automatically. Override only when the agent de
222
222
223
223
| Override | When to use | Example |
224
224
|---|---|---|
225
+
| `cli_executable` | Binary name differs from `key` | RovoDev: key `"rovodev"`, binary `"acli"` → override returns `"acli"` |
226
+
| `is_cli_available()` | Multiple binary names or non-PATH installs | Claude checks `~/.claude/local/`; Kiro accepts both `kiro-cli` and `kiro` |
**`cli_executable` property** — Return the binary name to look up on `PATH` for tool-availability checks. The default implementation returns `self.key`. Override when the executable name differs from the integration key:
233
+
234
+
```python
235
+
@property
236
+
def cli_executable(self) -> str:
237
+
return "acli" # e.g. RovoDev: key="rovodev", binary="acli"
238
+
```
239
+
240
+
**`is_cli_available()` method** — Return `True` if the integration's CLI tool is installed. The default implementation calls `shutil.which(self.cli_executable)`. Override for more complex detection:
241
+
242
+
```python
243
+
def is_cli_available(self) -> bool:
244
+
# Multiple binary names (Kiro):
245
+
return shutil.which("kiro-cli") is not None or shutil.which("kiro") is not None
246
+
247
+
# Non-PATH install locations (Claude):
248
+
import specify_cli._utils as _utils_mod
249
+
if _utils_mod.CLAUDE_LOCAL_PATH.is_file() or _utils_mod.CLAUDE_NPM_LOCAL_PATH.is_file():
250
+
return True
251
+
return shutil.which(self.cli_executable) is not None
252
+
```
253
+
254
+
`is_cli_available()`is used by `check_tool()` in `_utils.py` and by both `CommandStep` and `PromptStep` workflow steps to gate CLI dispatch. No hardcoded special cases should be added to those callers — encode detection logic in the integration class instead.
255
+
230
256
**Example — Copilot (fully custom `setup`):**
231
257
232
258
Copilot extends `IntegrationBase` directly because it creates `.agent.md` commands, companion `.prompt.md` files, and merges `.vscode/settings.json`. It also supports a `--skills` mode that scaffolds `speckit-<name>/SKILL.md` under `.github/skills/` using composition with an internal `_CopilotSkillsHelper`. See `src/specify_cli/integrations/copilot/__init__.py` for the full implementation.
@@ -436,7 +462,7 @@ When an issue exists, include its number immediately after the prefix — this i
436
462
437
463
## Common Pitfalls
438
464
439
-
1. **Using shorthand keys for CLI-based integrations**: For CLI-based integrations (`requires_cli: True`), the `key` must match the executable name(e.g., `"cursor-agent"` not `"cursor"`). `shutil.which(key)` is used for CLI tool checks — mismatches require special-case mappings. IDE-based integrations (`requires_cli: False`) are not subject to this constraint.
465
+
1. **Using shorthand keys for CLI-based integrations**: For CLI-based integrations (`requires_cli: True`), `key` should generally match the executable name. When it cannot (e.g., the binary name differs), override `cli_executable` or `is_cli_available()` on the integration class. Do **not** add special-case mappings to `check_tool()`, `CommandStep`, or `PromptStep`.
440
466
2. **Forgetting context configuration**: The bundled `agent-context` extension reads from `.specify/extensions/agent-context/agent-context-config.yml`. New integrations only need to set `context_file` on the class — markers and dispatcher scripts are managed centrally.
441
467
3. **Incorrect `requires_cli` value**: Set to `True` only for agents that have a CLI tool; set to `False` for IDE-based agents.
442
468
4. **Wrong argument format**: Use `$ARGUMENTS` for Markdown agents, `{{args}}` for TOML agents.
0 commit comments