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
Add cli_executable property to IntegrationBase for agents whose executable differs from their key
Closes#2558.
- Add cli_executable property and is_cli_available() method to
IntegrationBase. cli_executable delegates to the existing
_resolve_executable() hook (key, or SPECKIT_INTEGRATION_<KEY>_EXECUTABLE
override); is_cli_available() defaults to shutil.which(cli_executable).
- Refactor check_tool() in _utils.py to delegate to
get_integration(tool).is_cli_available() when the tool is a registered
integration, removing the hardcoded claude/kiro-cli/rovodev special cases.
Falls back to a plain shutil.which(tool) for unregistered tools (git,
code, etc).
- Override is_cli_available() in ClaudeIntegration (checks local
install paths before falling back to PATH) and KiroCliIntegration
(accepts both kiro-cli and legacy kiro binary names). RovoDev needs no
change: its existing _resolve_executable() override already returns
�cli, so cli_executable picks it up automatically.
- Scope note: the two workflow-step CLI-dispatch sites
(workflows/steps/command, workflows/steps/prompt) are intentionally left
unchanged in this PR. They already handle differing executables via an
existing impl.key / exec_args[0] fallback, and tests/test_workflows.py
has 15+ test cases patching shutil.which at those specific module paths;
migrating them to is_cli_available() is deferred to a fast-follow to
avoid unrelated test churn here.
- Document the new override mechanism in AGENTS.md, with the pitfall
fully explained (RovoDev's key=rovodev vs executable=acli).
- Add TestCliExecutableDetection to tests/integrations/test_base.py
covering default resolution, env-var override, and is_cli_available()
true/false paths.
All 9 existing tests/test_check_tool.py cases and 160+ integration tests
pass unmodified.
Assisted-by: GitHub Copilot (model: claude-sonnet-5, autonomous)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copy file name to clipboardExpand all lines: AGENTS.md
+17-1Lines changed: 17 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -151,6 +151,22 @@ class CodexIntegration(SkillsIntegration):
151
151
152
152
**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., `"kilocode"`, `"copilot"`).
153
153
154
+
**When `key` and the CLI executable genuinely differ** (issue [#2558](https://github.com/github/spec-kit/issues/2558)): sometimes the executable name legitimately cannot match `key` — e.g. RovoDev's `key` is `"rovodev"` but it's invoked via the `acli` binary (`acli rovodev …`). Don't hack around this — override `IntegrationBase`'s `cli_executable` property (or its underlying `_resolve_executable()` hook) instead:
155
+
156
+
```python
157
+
classRovodevIntegration(SkillsIntegration):
158
+
key ="rovodev"
159
+
...
160
+
161
+
def_resolve_executable(self) -> str:
162
+
# cli_executable delegates here by default; override the fallback
163
+
# instead of self.key while still honoring SPECKIT_INTEGRATION_ROVODEV_EXECUTABLE.
`check_tool()` in `_utils.py` detects installed CLIs by calling `get_integration(tool).is_cli_available()`, which defaults to `shutil.which(self.cli_executable) is not None`. Override `is_cli_available()` directly (rather than just `cli_executable`) when detection needs more than a single PATH lookup — e.g. `ClaudeIntegration` also checks local install paths (`~/.claude/local/claude`, npm-local), and `KiroCliIntegration` accepts both `kiro-cli` and the legacy `kiro` binary name. Tools with no registered integration (e.g. `"git"`) fall back to a plain `shutil.which(tool)` check.
169
+
154
170
### 3. Register it
155
171
156
172
In `src/specify_cli/integrations/__init__.py`, add one import and one `_register()` call inside `_register_builtins()`. Both lists are alphabetical:
@@ -502,7 +518,7 @@ Disclosure is **continuous**, not a one-time event. A single AI-disclosure parag
502
518
503
519
## Common Pitfalls
504
520
505
-
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.
521
+
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"`) whenever possible. `shutil.which(key)` is used for CLI tool checks by default. If the executable genuinely can't match `key` (e.g. RovoDev's `key="rovodev"` but binary is `acli`), override `cli_executable` / `_resolve_executable()` — or `is_cli_available()` for multi-path detection — instead of adding a hardcoded special case to `check_tool()` (see [#2558](https://github.com/github/spec-kit/issues/2558)). IDE-based integrations (`requires_cli: False`) are not subject to this constraint.
506
522
2. **Reintroducing context handling into the CLI**: The opt-in `agent-context` extension owns everything about context files — including the per-agent default mapping in `agent-context-defaults.json`. Integration classes must **not** declare a `context_file`, and no CLI code should read, write, resolve, or migrate context files. All context-file logic lives in `.specify/extensions/agent-context/` and its bundled scripts.
507
523
3. **Incorrect `requires_cli` value**: Set to `True` only for agents that have a CLI tool; set to `False` for IDE-based agents.
508
524
4. **Wrong argument format**: Use `$ARGUMENTS` for Markdown agents, `{{args}}` for TOML agents.
0 commit comments