Check if a tool is installed. Optionally update tracker. Args: tool: Name of the tool to check tracker: StepTracker | None to update with results Returns: True if tool is found, False otherwise
(tool: str, tracker=None)
| 102 | |
| 103 | |
| 104 | def check_tool(tool: str, tracker=None) -> bool: |
| 105 | """Check if a tool is installed. Optionally update tracker. |
| 106 | |
| 107 | Args: |
| 108 | tool: Name of the tool to check |
| 109 | tracker: StepTracker | None to update with results |
| 110 | |
| 111 | Returns: |
| 112 | True if tool is found, False otherwise |
| 113 | """ |
| 114 | # Special handling for Claude CLI local installs |
| 115 | # See: https://github.com/github/spec-kit/issues/123 |
| 116 | # See: https://github.com/github/spec-kit/issues/550 |
| 117 | # Claude Code can be installed in two local paths: |
| 118 | # 1. ~/.claude/local/claude (after `claude migrate-installer`) |
| 119 | # 2. ~/.claude/local/node_modules/.bin/claude (npm-local install, e.g. via nvm) |
| 120 | # Neither path may be on the system PATH, so we check them explicitly. |
| 121 | if tool == "claude": |
| 122 | if CLAUDE_LOCAL_PATH.is_file() or CLAUDE_NPM_LOCAL_PATH.is_file(): |
| 123 | if tracker: |
| 124 | tracker.complete(tool, "available") |
| 125 | return True |
| 126 | |
| 127 | # Per-integration executable resolution. |
| 128 | if tool == "kiro-cli": |
| 129 | # Kiro currently supports both executable names. Prefer kiro-cli and |
| 130 | # accept kiro as a compatibility fallback. |
| 131 | found = shutil.which("kiro-cli") is not None or shutil.which("kiro") is not None |
| 132 | elif tool == "rovodev": |
| 133 | found = shutil.which("acli") is not None |
| 134 | else: |
| 135 | found = shutil.which(tool) is not None |
| 136 | |
| 137 | if tracker: |
| 138 | if found: |
| 139 | tracker.complete(tool, "available") |
| 140 | else: |
| 141 | tracker.error(tool, "not found") |
| 142 | |
| 143 | return found |
| 144 | |
| 145 | |
| 146 |