Refactor agent configuration

This commit is contained in:
den (work)
2025-10-10 11:12:54 -07:00
parent 4de1b6b6c3
commit bb9ec8e638

View File

@@ -64,22 +64,92 @@ def _github_auth_headers(cli_token: str | None = None) -> dict:
token = _github_token(cli_token) token = _github_token(cli_token)
return {"Authorization": f"Bearer {token}"} if token else {} return {"Authorization": f"Bearer {token}"} if token else {}
AI_CHOICES = { # Agent configuration with name, folder, install URL, and CLI tool requirement
"copilot": "GitHub Copilot", AGENT_CONFIG = {
"claude": "Claude Code", "copilot": {
"gemini": "Gemini CLI", "name": "GitHub Copilot",
"cursor": "Cursor", "folder": ".github/",
"qwen": "Qwen Code", "install_url": None, # IDE-based, no CLI check needed
"opencode": "opencode", "requires_cli": False,
"codex": "Codex CLI", },
"windsurf": "Windsurf", "claude": {
"kilocode": "Kilo Code", "name": "Claude Code",
"auggie": "Auggie CLI", "folder": ".claude/",
"codebuddy": "CodeBuddy", "install_url": "https://docs.anthropic.com/en/docs/claude-code/setup",
"roo": "Roo Code", "requires_cli": True,
"q": "Amazon Q Developer CLI", },
"gemini": {
"name": "Gemini CLI",
"folder": ".gemini/",
"install_url": "https://github.com/google-gemini/gemini-cli",
"requires_cli": True,
},
"cursor": {
"name": "Cursor",
"folder": ".cursor/",
"install_url": None, # IDE-based
"requires_cli": False,
},
"qwen": {
"name": "Qwen Code",
"folder": ".qwen/",
"install_url": "https://github.com/QwenLM/qwen-code",
"requires_cli": True,
},
"opencode": {
"name": "opencode",
"folder": ".opencode/",
"install_url": "https://opencode.ai",
"requires_cli": True,
},
"codex": {
"name": "Codex CLI",
"folder": ".codex/",
"install_url": "https://github.com/openai/codex",
"requires_cli": True,
},
"windsurf": {
"name": "Windsurf",
"folder": ".windsurf/",
"install_url": None, # IDE-based
"requires_cli": False,
},
"kilocode": {
"name": "Kilo Code",
"folder": ".kilocode/",
"install_url": None, # IDE-based
"requires_cli": False,
},
"auggie": {
"name": "Auggie CLI",
"folder": ".augment/",
"install_url": "https://docs.augmentcode.com/cli/setup-auggie/install-auggie-cli",
"requires_cli": True,
},
"codebuddy": {
"name": "CodeBuddy",
"folder": ".codebuddy/",
"install_url": "https://www.codebuddy.ai",
"requires_cli": True,
},
"roo": {
"name": "Roo Code",
"folder": ".roo/",
"install_url": None, # IDE-based
"requires_cli": False,
},
"q": {
"name": "Amazon Q Developer CLI",
"folder": ".amazonq/",
"install_url": "https://aws.amazon.com/developer/learning/q-developer-cli/",
"requires_cli": True,
},
} }
# Derived dictionaries for backward compatibility
AI_CHOICES = {key: config["name"] for key, config in AGENT_CONFIG.items()}
AGENT_FOLDER_MAP = {key: config["folder"] for key, config in AGENT_CONFIG.items()}
SCRIPT_TYPE_CHOICES = {"sh": "POSIX Shell (bash/zsh)", "ps": "PowerShell"} SCRIPT_TYPE_CHOICES = {"sh": "POSIX Shell (bash/zsh)", "ps": "PowerShell"}
CLAUDE_LOCAL_PATH = Path.home() / ".claude" / "local" / "claude" CLAUDE_LOCAL_PATH = Path.home() / ".claude" / "local" / "claude"
@@ -338,18 +408,17 @@ def run_command(cmd: list[str], check_return: bool = True, capture: bool = False
raise raise
return None return None
def check_tool_for_tracker(tool: str, tracker: StepTracker) -> bool: def check_tool(tool: str, install_hint: str = "", tracker: StepTracker = None) -> bool:
"""Check if a tool is installed and update tracker.""" """Check if a tool is installed. Optionally update tracker.
if shutil.which(tool):
tracker.complete(tool, "available")
return True
else:
tracker.error(tool, "not found")
return False
def check_tool(tool: str, install_hint: str) -> bool:
"""Check if a tool is installed."""
Args:
tool: Name of the tool to check
install_hint: URL or hint for installing the tool (for error messages)
tracker: Optional StepTracker to update with results
Returns:
True if tool is found, False otherwise
"""
# Special handling for Claude CLI after `claude migrate-installer` # Special handling for Claude CLI after `claude migrate-installer`
# See: https://github.com/github/spec-kit/issues/123 # See: https://github.com/github/spec-kit/issues/123
# The migrate-installer command REMOVES the original executable from PATH # The migrate-installer command REMOVES the original executable from PATH
@@ -357,12 +426,19 @@ def check_tool(tool: str, install_hint: str) -> bool:
# This path should be prioritized over other claude executables in PATH # This path should be prioritized over other claude executables in PATH
if tool == "claude": if tool == "claude":
if CLAUDE_LOCAL_PATH.exists() and CLAUDE_LOCAL_PATH.is_file(): if CLAUDE_LOCAL_PATH.exists() and CLAUDE_LOCAL_PATH.is_file():
if tracker:
tracker.complete(tool, "available")
return True return True
if shutil.which(tool): found = shutil.which(tool) is not None
return True
else: if tracker:
return False if found:
tracker.complete(tool, "available")
else:
tracker.error(tool, "not found")
return found
def is_git_repo(path: Path = None) -> bool: def is_git_repo(path: Path = None) -> bool:
"""Check if the specified path is inside a git repository.""" """Check if the specified path is inside a git repository."""
@@ -840,55 +916,26 @@ def init(
# Check agent tools unless ignored # Check agent tools unless ignored
if not ignore_agent_tools: if not ignore_agent_tools:
agent_tool_missing = False agent_config = AGENT_CONFIG.get(selected_ai)
install_url = "" if agent_config and agent_config["requires_cli"]:
if selected_ai == "claude": cli_tool = selected_ai
if not check_tool("claude", "https://docs.anthropic.com/en/docs/claude-code/setup"): if selected_ai == "cursor":
install_url = "https://docs.anthropic.com/en/docs/claude-code/setup" cli_tool = "cursor-agent"
agent_tool_missing = True
elif selected_ai == "gemini": install_url = agent_config["install_url"]
if not check_tool("gemini", "https://github.com/google-gemini/gemini-cli"): if not check_tool(cli_tool, install_url):
install_url = "https://github.com/google-gemini/gemini-cli" error_panel = Panel(
agent_tool_missing = True f"[cyan]{selected_ai}[/cyan] not found\n"
elif selected_ai == "qwen": f"Install from: [cyan]{install_url}[/cyan]\n"
if not check_tool("qwen", "https://github.com/QwenLM/qwen-code"): f"{AI_CHOICES[selected_ai]} is required to continue with this project type.\n\n"
install_url = "https://github.com/QwenLM/qwen-code" "Tip: Use [cyan]--ignore-agent-tools[/cyan] to skip this check",
agent_tool_missing = True title="[red]Agent Detection Error[/red]",
elif selected_ai == "opencode": border_style="red",
if not check_tool("opencode", "https://opencode.ai"): padding=(1, 2)
install_url = "https://opencode.ai" )
agent_tool_missing = True console.print()
elif selected_ai == "codex": console.print(error_panel)
if not check_tool("codex", "https://github.com/openai/codex"): raise typer.Exit(1)
install_url = "https://github.com/openai/codex"
agent_tool_missing = True
elif selected_ai == "auggie":
if not check_tool("auggie", "https://docs.augmentcode.com/cli/setup-auggie/install-auggie-cli"):
install_url = "https://docs.augmentcode.com/cli/setup-auggie/install-auggie-cli"
agent_tool_missing = True
elif selected_ai == "codebuddy":
if not check_tool("codebuddy", "https://www.codebuddy.ai"):
install_url = "https://www.codebuddy.ai"
agent_tool_missing = True
elif selected_ai == "q":
if not check_tool("q", "https://github.com/aws/amazon-q-developer-cli"):
install_url = "https://aws.amazon.com/developer/learning/q-developer-cli/"
agent_tool_missing = True
# GitHub Copilot and Cursor checks are not needed as they're typically available in supported IDEs
if agent_tool_missing:
error_panel = Panel(
f"[cyan]{selected_ai}[/cyan] not found\n"
f"Install with: [cyan]{install_url}[/cyan]\n"
f"{AI_CHOICES[selected_ai]} is required to continue with this project type.\n\n"
"Tip: Use [cyan]--ignore-agent-tools[/cyan] to skip this check",
title="[red]Agent Detection Error[/red]",
border_style="red",
padding=(1, 2)
)
console.print()
console.print(error_panel)
raise typer.Exit(1)
# Determine script type (explicit, interactive, or OS default) # Determine script type (explicit, interactive, or OS default)
if script_type: if script_type:
@@ -987,24 +1034,8 @@ def init(
console.print("\n[bold green]Project ready.[/bold green]") console.print("\n[bold green]Project ready.[/bold green]")
# Agent folder security notice # Agent folder security notice
agent_folder_map = { if selected_ai in AGENT_FOLDER_MAP:
"claude": ".claude/", agent_folder = AGENT_FOLDER_MAP[selected_ai]
"gemini": ".gemini/",
"cursor": ".cursor/",
"qwen": ".qwen/",
"opencode": ".opencode/",
"codex": ".codex/",
"windsurf": ".windsurf/",
"kilocode": ".kilocode/",
"auggie": ".augment/",
"codebuddy": ".codebuddy/",
"copilot": ".github/",
"roo": ".roo/",
"q": ".amazonq/"
}
if selected_ai in agent_folder_map:
agent_folder = agent_folder_map[selected_ai]
security_notice = Panel( security_notice = Panel(
f"Some agents may store credentials, auth tokens, or other identifying and private artifacts in the agent folder within your project.\n" f"Some agents may store credentials, auth tokens, or other identifying and private artifacts in the agent folder within your project.\n"
f"Consider adding [cyan]{agent_folder}[/cyan] (or parts of it) to [cyan].gitignore[/cyan] to prevent accidental credential leakage.", f"Consider adding [cyan]{agent_folder}[/cyan] (or parts of it) to [cyan].gitignore[/cyan] to prevent accidental credential leakage.",
@@ -1067,37 +1098,28 @@ def check():
tracker = StepTracker("Check Available Tools") tracker = StepTracker("Check Available Tools")
# Add git check
tracker.add("git", "Git version control") tracker.add("git", "Git version control")
tracker.add("claude", "Claude Code CLI") git_ok = check_tool("git", tracker=tracker)
tracker.add("gemini", "Gemini CLI")
tracker.add("qwen", "Qwen Code CLI") # Check AI agent tools
agent_results = {}
for agent_key, agent_config in AGENT_CONFIG.items():
agent_name = agent_config["name"]
# Determine the CLI tool name (usually same as agent key, with exceptions)
cli_tool = agent_key
if agent_key == "cursor":
cli_tool = "cursor-agent"
tracker.add(cli_tool, agent_name)
agent_results[agent_key] = check_tool(cli_tool, tracker=tracker)
# Check VS Code variants (not in agent config)
tracker.add("code", "Visual Studio Code") tracker.add("code", "Visual Studio Code")
code_ok = check_tool("code", tracker=tracker)
tracker.add("code-insiders", "Visual Studio Code Insiders") tracker.add("code-insiders", "Visual Studio Code Insiders")
tracker.add("cursor-agent", "Cursor IDE agent") code_insiders_ok = check_tool("code-insiders", tracker=tracker)
tracker.add("windsurf", "Windsurf IDE")
tracker.add("kilocode", "Kilo Code IDE")
tracker.add("opencode", "opencode")
tracker.add("codex", "Codex CLI")
tracker.add("auggie", "Auggie CLI")
tracker.add("roo", "Roo Code")
tracker.add("codebuddy", "CodeBuddy")
tracker.add("q", "Amazon Q Developer CLI")
git_ok = check_tool_for_tracker("git", tracker)
claude_ok = check_tool_for_tracker("claude", tracker)
gemini_ok = check_tool_for_tracker("gemini", tracker)
qwen_ok = check_tool_for_tracker("qwen", tracker)
code_ok = check_tool_for_tracker("code", tracker)
code_insiders_ok = check_tool_for_tracker("code-insiders", tracker)
cursor_ok = check_tool_for_tracker("cursor-agent", tracker)
windsurf_ok = check_tool_for_tracker("windsurf", tracker)
kilocode_ok = check_tool_for_tracker("kilocode", tracker)
opencode_ok = check_tool_for_tracker("opencode", tracker)
codex_ok = check_tool_for_tracker("codex", tracker)
auggie_ok = check_tool_for_tracker("auggie", tracker)
roo_ok = check_tool_for_tracker("roo", tracker)
codebuddy_ok = check_tool_for_tracker("codebuddy", tracker)
q_ok = check_tool_for_tracker("q", tracker)
console.print(tracker.render()) console.print(tracker.render())
@@ -1106,7 +1128,7 @@ def check():
if not git_ok: if not git_ok:
console.print("[dim]Tip: Install git for repository management[/dim]") console.print("[dim]Tip: Install git for repository management[/dim]")
if not (claude_ok or gemini_ok or cursor_ok or qwen_ok or windsurf_ok or kilocode_ok or opencode_ok or codex_ok or auggie_ok or codebuddy_ok or q_ok): if not any(agent_results.values()):
console.print("[dim]Tip: Install an AI assistant for the best experience[/dim]") console.print("[dim]Tip: Install an AI assistant for the best experience[/dim]")
def main(): def main():