From c69893c1f7898a90fd78ae5f6ff2a484fb8c7367 Mon Sep 17 00:00:00 2001 From: ericnoam Date: Tue, 31 Mar 2026 19:15:06 +0200 Subject: [PATCH] fix: show 'forge' binary name in user-facing messages for forgecode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses Copilot PR feedback: Users should see the actual executable name 'forge' in status and error messages, not the agent key 'forgecode'. Changes: - Added 'cli_binary' field to forgecode AGENT_CONFIG (set to 'forge') - Updated check_tool() to accept optional display_key parameter - Updated check_tool() to use cli_binary from AGENT_CONFIG when available - Updated check() command to display cli_binary in StepTracker - Updated init() error message to show cli_binary instead of agent key UX improvements: - 'specify check' now shows: '● forge (available/not found)' - 'specify init --ai forgecode' error shows: 'forge not found' (instead of confusing 'forgecode not found') This makes it clear to users that they need to install the 'forge' binary, even though they selected the 'forgecode' agent. --- src/specify_cli/__init__.py | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/src/specify_cli/__init__.py b/src/specify_cli/__init__.py index d1fe663eb..36b7c5748 100644 --- a/src/specify_cli/__init__.py +++ b/src/specify_cli/__init__.py @@ -308,6 +308,7 @@ AGENT_CONFIG = { "commands_subdir": "commands", "install_url": "https://forgecode.dev/docs/", "requires_cli": True, + "cli_binary": "forge", # The actual executable users must install }, "generic": { "name": "Generic (bring your own agent)", @@ -603,16 +604,20 @@ def run_command(cmd: list[str], check_return: bool = True, capture: bool = False raise return None -def check_tool(tool: str, tracker: StepTracker = None) -> bool: +def check_tool(tool: str, tracker: StepTracker = None, display_key: str = None) -> bool: """Check if a tool is installed. Optionally update tracker. Args: - tool: Name of the tool to check + tool: Name of the tool to check (agent key from AGENT_CONFIG) tracker: Optional StepTracker to update with results + display_key: Optional key to use for tracker display (defaults to tool) Returns: True if tool is found, False otherwise """ + # Use display_key for tracker if provided, otherwise use tool + tracker_key = display_key if display_key else tool + # Special handling for Claude CLI local installs # See: https://github.com/github/spec-kit/issues/123 # See: https://github.com/github/spec-kit/issues/550 @@ -623,24 +628,25 @@ def check_tool(tool: str, tracker: StepTracker = None) -> bool: if tool == "claude": if CLAUDE_LOCAL_PATH.is_file() or CLAUDE_NPM_LOCAL_PATH.is_file(): if tracker: - tracker.complete(tool, "available") + tracker.complete(tracker_key, "available") return True if tool == "kiro-cli": # Kiro currently supports both executable names. Prefer kiro-cli and # accept kiro as a compatibility fallback. found = shutil.which("kiro-cli") is not None or shutil.which("kiro") is not None - elif tool == "forgecode": - # The forgecode agent key is 'forgecode' but the CLI binary is 'forge'. - found = shutil.which("forge") is not None else: - found = shutil.which(tool) is not None + # Check if this tool has a custom cli_binary name in AGENT_CONFIG + cli_binary = tool + if tool in AGENT_CONFIG and "cli_binary" in AGENT_CONFIG[tool]: + cli_binary = AGENT_CONFIG[tool]["cli_binary"] + found = shutil.which(cli_binary) is not None if tracker: if found: - tracker.complete(tool, "available") + tracker.complete(tracker_key, "available") else: - tracker.error(tool, "not found") + tracker.error(tracker_key, "not found") return found @@ -2009,9 +2015,10 @@ def init( agent_config = AGENT_CONFIG.get(selected_ai) if agent_config and agent_config["requires_cli"]: install_url = agent_config["install_url"] + cli_binary = agent_config.get("cli_binary", selected_ai) if not check_tool(selected_ai): error_panel = Panel( - f"[cyan]{selected_ai}[/cyan] not found\n" + f"[cyan]{cli_binary}[/cyan] not found\n" f"Install from: [cyan]{install_url}[/cyan]\n" f"{agent_config['name']} is required to continue with this project type.\n\n" "Tip: Use [cyan]--ignore-agent-tools[/cyan] to skip this check", @@ -2410,14 +2417,17 @@ def check(): continue # Generic is not a real agent to check agent_name = agent_config["name"] requires_cli = agent_config["requires_cli"] + + # Use cli_binary for display if specified, otherwise use agent_key + display_key = agent_config.get("cli_binary", agent_key) - tracker.add(agent_key, agent_name) + tracker.add(display_key, agent_name) if requires_cli: - agent_results[agent_key] = check_tool(agent_key, tracker=tracker) + agent_results[agent_key] = check_tool(agent_key, tracker=tracker, display_key=display_key) else: # IDE-based agent - skip CLI check and mark as optional - tracker.skip(agent_key, "IDE-based, no CLI check") + tracker.skip(display_key, "IDE-based, no CLI check") agent_results[agent_key] = False # Don't count IDE agents as "found" # Check VS Code variants (not in agent config)