diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index caf184c1c6..88acdcd5fd 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -50,8 +50,6 @@ "kilocode.Kilo-Code", // Roo Code "RooVeterinaryInc.roo-cline", - // Amazon Developer Q - "AmazonWebServices.amazon-q-vscode", // Claude Code "anthropic.claude-code" ], diff --git a/.devcontainer/post-create.sh b/.devcontainer/post-create.sh index 9608a28a3d..024ff0db48 100755 --- a/.devcontainer/post-create.sh +++ b/.devcontainer/post-create.sh @@ -51,32 +51,33 @@ echo -e "\nšŸ¤– Installing OpenCode CLI..." run_command "npm install -g opencode-ai@latest" echo "āœ… Done" -echo -e "\nšŸ¤– Installing Amazon Q CLI..." -# šŸ‘‰šŸ¾ https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/command-line-verify-download.html - -run_command "curl --proto '=https' --tlsv1.2 -sSf 'https://desktop-release.q.us-east-1.amazonaws.com/latest/q-x86_64-linux.zip' -o 'q.zip'" -run_command "curl --proto '=https' --tlsv1.2 -sSf 'https://desktop-release.q.us-east-1.amazonaws.com/latest/q-x86_64-linux.zip.sig' -o 'q.zip.sig'" -cat > amazonq-public-key.asc << 'EOF' ------BEGIN PGP PUBLIC KEY BLOCK----- - -mDMEZig60RYJKwYBBAHaRw8BAQdAy/+G05U5/EOA72WlcD4WkYn5SInri8pc4Z6D -BKNNGOm0JEFtYXpvbiBRIENMSSBUZWFtIDxxLWNsaUBhbWF6b24uY29tPoiZBBMW -CgBBFiEEmvYEF+gnQskUPgPsUNx6jcJMVmcFAmYoOtECGwMFCQPCZwAFCwkIBwIC -IgIGFQoJCAsCBBYCAwECHgcCF4AACgkQUNx6jcJMVmef5QD/QWWEGG/cOnbDnp68 -SJXuFkwiNwlH2rPw9ZRIQMnfAS0A/0V6ZsGB4kOylBfc7CNfzRFGtovdBBgHqA6P -zQ/PNscGuDgEZig60RIKKwYBBAGXVQEFAQEHQC4qleONMBCq3+wJwbZSr0vbuRba -D1xr4wUPn4Avn4AnAwEIB4h+BBgWCgAmFiEEmvYEF+gnQskUPgPsUNx6jcJMVmcF -AmYoOtECGwwFCQPCZwAACgkQUNx6jcJMVmchMgEA6l3RveCM0YHAGQaSFMkguoAo -vK6FgOkDawgP0NPIP2oA/jIAO4gsAntuQgMOsPunEdDeji2t+AhV02+DQIsXZpoB -=f8yY ------END PGP PUBLIC KEY BLOCK----- -EOF -run_command "gpg --batch --import amazonq-public-key.asc" -run_command "gpg --verify q.zip.sig q.zip" -run_command "unzip -q q.zip" -run_command "chmod +x ./q/install.sh" -run_command "./q/install.sh --no-confirm" -run_command "rm -rf ./q q.zip q.zip.sig amazonq-public-key.asc" +echo -e "\nšŸ¤– Installing Kiro CLI..." +# https://kiro.dev/docs/cli/ +KIRO_INSTALLER_URL="https://cli.kiro.dev/install" +KIRO_INSTALLER_SHA256="7487a65cf310b7fb59b357c4b5e6e3f3259d383f4394ecedb39acf70f307cffb" +KIRO_INSTALLER_PATH="$(mktemp)" + +cleanup_kiro_installer() { + rm -f "$KIRO_INSTALLER_PATH" +} +trap cleanup_kiro_installer EXIT + +run_command "curl -fsSL \"$KIRO_INSTALLER_URL\" -o \"$KIRO_INSTALLER_PATH\"" +run_command "echo \"$KIRO_INSTALLER_SHA256 $KIRO_INSTALLER_PATH\" | sha256sum -c -" + +run_command "bash \"$KIRO_INSTALLER_PATH\"" + +kiro_binary="" +if command -v kiro-cli >/dev/null 2>&1; then + kiro_binary="kiro-cli" +elif command -v kiro >/dev/null 2>&1; then + kiro_binary="kiro" +else + echo -e "\033[0;31m[ERROR] Kiro CLI installation did not create 'kiro-cli' or 'kiro' in PATH.\033[0m" >&2 + exit 1 +fi + +run_command "$kiro_binary --help > /dev/null" echo "āœ… Done" echo -e "\nšŸ¤– Installing CodeBuddy CLI..." diff --git a/.github/ISSUE_TEMPLATE/agent_request.yml b/.github/ISSUE_TEMPLATE/agent_request.yml index a72dacda52..a6ac6c4bff 100644 --- a/.github/ISSUE_TEMPLATE/agent_request.yml +++ b/.github/ISSUE_TEMPLATE/agent_request.yml @@ -8,7 +8,7 @@ body: value: | Thanks for requesting a new agent! Before submitting, please check if the agent is already supported. - **Currently supported agents**: Claude Code, Gemini CLI, GitHub Copilot, Cursor, Qwen Code, opencode, Codex CLI, Windsurf, Kilo Code, Auggie CLI, Roo Code, CodeBuddy, Qoder CLI, Amazon Q Developer CLI, Amp, SHAI, IBM Bob, Antigravity + **Currently supported agents**: Claude Code, Gemini CLI, GitHub Copilot, Cursor, Qwen Code, opencode, Codex CLI, Windsurf, Kilo Code, Auggie CLI, Roo Code, CodeBuddy, Qoder CLI, Kiro CLI, Amp, SHAI, IBM Bob, Antigravity - type: input id: agent-name diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index cbe1955ad4..dd09f8e02a 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -75,7 +75,7 @@ body: - Roo Code - CodeBuddy - Qoder CLI - - Amazon Q Developer CLI + - Kiro CLI - Amp - SHAI - IBM Bob diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index 71786ddcef..3b5889288b 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -69,7 +69,7 @@ body: - Roo Code - CodeBuddy - Qoder CLI - - Amazon Q Developer CLI + - Kiro CLI - Amp - SHAI - IBM Bob diff --git a/.github/workflows/scripts/create-github-release.sh b/.github/workflows/scripts/create-github-release.sh index 29f5024f8c..0418ce2b08 100644 --- a/.github/workflows/scripts/create-github-release.sh +++ b/.github/workflows/scripts/create-github-release.sh @@ -46,8 +46,8 @@ gh release create "$VERSION" \ .genreleases/spec-kit-template-amp-ps-"$VERSION".zip \ .genreleases/spec-kit-template-shai-sh-"$VERSION".zip \ .genreleases/spec-kit-template-shai-ps-"$VERSION".zip \ - .genreleases/spec-kit-template-q-sh-"$VERSION".zip \ - .genreleases/spec-kit-template-q-ps-"$VERSION".zip \ + .genreleases/spec-kit-template-kiro-cli-sh-"$VERSION".zip \ + .genreleases/spec-kit-template-kiro-cli-ps-"$VERSION".zip \ .genreleases/spec-kit-template-agy-sh-"$VERSION".zip \ .genreleases/spec-kit-template-agy-ps-"$VERSION".zip \ .genreleases/spec-kit-template-bob-sh-"$VERSION".zip \ diff --git a/.github/workflows/scripts/create-release-packages.ps1 b/.github/workflows/scripts/create-release-packages.ps1 index ed04d9cd3f..0b33b0d19d 100644 --- a/.github/workflows/scripts/create-release-packages.ps1 +++ b/.github/workflows/scripts/create-release-packages.ps1 @@ -14,7 +14,7 @@ .PARAMETER Agents Comma or space separated subset of agents to build (default: all) - Valid agents: claude, gemini, copilot, cursor-agent, qwen, opencode, windsurf, codex, kilocode, auggie, roo, codebuddy, amp, q, bob, qodercli, shai, agy, generic + Valid agents: claude, gemini, copilot, cursor-agent, qwen, opencode, windsurf, codex, kilocode, auggie, roo, codebuddy, amp, kiro-cli, bob, qodercli, shai, agy, generic .PARAMETER Scripts Comma or space separated subset of script types to build (default: both) @@ -335,9 +335,9 @@ function Build-Variant { $cmdDir = Join-Path $baseDir ".agents/commands" Generate-Commands -Agent 'amp' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script } - 'q' { - $cmdDir = Join-Path $baseDir ".amazonq/prompts" - Generate-Commands -Agent 'q' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script + 'kiro-cli' { + $cmdDir = Join-Path $baseDir ".kiro/prompts" + Generate-Commands -Agent 'kiro-cli' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script } 'bob' { $cmdDir = Join-Path $baseDir ".bob/commands" @@ -347,10 +347,21 @@ function Build-Variant { $cmdDir = Join-Path $baseDir ".qoder/commands" Generate-Commands -Agent 'qodercli' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script } + 'shai' { + $cmdDir = Join-Path $baseDir ".shai/commands" + Generate-Commands -Agent 'shai' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script + } + 'agy' { + $cmdDir = Join-Path $baseDir ".agent/workflows" + Generate-Commands -Agent 'agy' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script + } 'generic' { $cmdDir = Join-Path $baseDir ".speckit/commands" Generate-Commands -Agent 'generic' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script } + default { + throw "Unsupported agent '$Agent'." + } } # Create zip archive @@ -360,7 +371,7 @@ function Build-Variant { } # Define all agents and scripts -$AllAgents = @('claude', 'gemini', 'copilot', 'cursor-agent', 'qwen', 'opencode', 'windsurf', 'codex', 'kilocode', 'auggie', 'roo', 'codebuddy', 'amp', 'q', 'bob', 'qodercli', 'shai', 'agy', 'generic') +$AllAgents = @('claude', 'gemini', 'copilot', 'cursor-agent', 'qwen', 'opencode', 'windsurf', 'codex', 'kilocode', 'auggie', 'roo', 'codebuddy', 'amp', 'kiro-cli', 'bob', 'qodercli', 'shai', 'agy', 'generic') $AllScripts = @('sh', 'ps') function Normalize-List { @@ -425,4 +436,4 @@ foreach ($agent in $AgentList) { Write-Host "`nArchives in ${GenReleasesDir}:" Get-ChildItem -Path $GenReleasesDir -Filter "spec-kit-template-*-${Version}.zip" | ForEach-Object { Write-Host " $($_.Name)" -} \ No newline at end of file +} diff --git a/.github/workflows/scripts/create-release-packages.sh b/.github/workflows/scripts/create-release-packages.sh index 1b2ced3ea3..08ff1de212 100755 --- a/.github/workflows/scripts/create-release-packages.sh +++ b/.github/workflows/scripts/create-release-packages.sh @@ -6,7 +6,7 @@ set -euo pipefail # Usage: .github/workflows/scripts/create-release-packages.sh # Version argument should include leading 'v'. # Optionally set AGENTS and/or SCRIPTS env vars to limit what gets built. -# AGENTS : space or comma separated subset of: claude gemini copilot cursor-agent qwen opencode windsurf codex amp shai bob generic (default: all) +# AGENTS : space or comma separated subset of: claude gemini copilot cursor-agent qwen opencode windsurf codex kilocode auggie roo codebuddy amp shai kiro-cli agy bob qodercli generic (default: all) # SCRIPTS : space or comma separated subset of: sh ps (default: both) # Examples: # AGENTS=claude SCRIPTS=sh $0 v0.2.0 @@ -212,9 +212,9 @@ build_variant() { shai) mkdir -p "$base_dir/.shai/commands" generate_commands shai md "\$ARGUMENTS" "$base_dir/.shai/commands" "$script" ;; - q) - mkdir -p "$base_dir/.amazonq/prompts" - generate_commands q md "\$ARGUMENTS" "$base_dir/.amazonq/prompts" "$script" ;; + kiro-cli) + mkdir -p "$base_dir/.kiro/prompts" + generate_commands kiro-cli md "\$ARGUMENTS" "$base_dir/.kiro/prompts" "$script" ;; agy) mkdir -p "$base_dir/.agent/workflows" generate_commands agy md "\$ARGUMENTS" "$base_dir/.agent/workflows" "$script" ;; @@ -230,7 +230,7 @@ build_variant() { } # Determine agent list -ALL_AGENTS=(claude gemini copilot cursor-agent qwen opencode windsurf codex kilocode auggie roo codebuddy amp shai q agy bob qodercli generic) +ALL_AGENTS=(claude gemini copilot cursor-agent qwen opencode windsurf codex kilocode auggie roo codebuddy amp shai kiro-cli agy bob qodercli generic) ALL_SCRIPTS=(sh ps) norm_list() { @@ -277,4 +277,3 @@ done echo "Archives in $GENRELEASES_DIR:" ls -1 "$GENRELEASES_DIR"/spec-kit-template-*-"${NEW_VERSION}".zip - diff --git a/AGENTS.md b/AGENTS.md index d8dc0f08f7..4cafa7defb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -44,7 +44,7 @@ Specify supports multiple AI agents by generating agent-specific command files a | **Roo Code** | `.roo/rules/` | Markdown | N/A (IDE-based) | Roo Code IDE | | **CodeBuddy CLI** | `.codebuddy/commands/` | Markdown | `codebuddy` | CodeBuddy CLI | | **Qoder CLI** | `.qoder/commands/` | Markdown | `qodercli` | Qoder CLI | -| **Amazon Q Developer CLI** | `.amazonq/prompts/` | Markdown | `q` | Amazon Q Developer CLI | +| **Kiro CLI** | `.kiro/prompts/` | Markdown | `kiro-cli` | Kiro CLI | | **Amp** | `.agents/commands/` | Markdown | `amp` | Amp CLI | | **SHAI** | `.shai/commands/` | Markdown | `shai` | SHAI CLI | | **IBM Bob** | `.bob/commands/` | Markdown | N/A (IDE-based) | IBM Bob IDE | @@ -86,7 +86,7 @@ This eliminates the need for special-case mappings throughout the codebase. - `folder`: Directory where agent-specific files are stored (relative to project root) - `commands_subdir`: Subdirectory name within the agent folder where command/prompt files are stored (default: `"commands"`) - Most agents use `"commands"` (e.g., `.claude/commands/`) - - Some agents use alternative names: `"agents"` (copilot), `"workflows"` (windsurf, kilocode, agy), `"prompts"` (codex, q), `"command"` (opencode - singular) + - Some agents use alternative names: `"agents"` (copilot), `"workflows"` (windsurf, kilocode, agy), `"prompts"` (codex, kiro-cli), `"command"` (opencode - singular) - This field enables `--ai-skills` to locate command templates correctly for skill generation - `install_url`: Installation documentation URL (set to `None` for IDE-based agents) - `requires_cli`: Whether the agent requires a CLI tool check during initialization @@ -96,7 +96,7 @@ This eliminates the need for special-case mappings throughout the codebase. Update the `--ai` parameter help text in the `init()` command to include the new agent: ```python -ai_assistant: str = typer.Option(None, "--ai", help="AI assistant to use: claude, gemini, copilot, cursor-agent, qwen, opencode, codex, windsurf, kilocode, auggie, codebuddy, new-agent-cli, or q"), +ai_assistant: str = typer.Option(None, "--ai", help="AI assistant to use: claude, gemini, copilot, cursor-agent, qwen, opencode, codex, windsurf, kilocode, auggie, codebuddy, new-agent-cli, or kiro-cli"), ``` Also update any function docstrings, examples, and error messages that list available agents. @@ -117,7 +117,7 @@ Modify `.github/workflows/scripts/create-release-packages.sh`: ##### Add to ALL_AGENTS array ```bash -ALL_AGENTS=(claude gemini copilot cursor-agent qwen opencode windsurf q) +ALL_AGENTS=(claude gemini copilot cursor-agent qwen opencode windsurf kiro-cli) ``` ##### Add case statement for directory structure @@ -317,7 +317,7 @@ Require a command-line tool to be installed: - **Cursor**: `cursor-agent` CLI - **Qwen Code**: `qwen` CLI - **opencode**: `opencode` CLI -- **Amazon Q Developer CLI**: `q` CLI +- **Kiro CLI**: `kiro-cli` CLI - **CodeBuddy CLI**: `codebuddy` CLI - **Qoder CLI**: `qodercli` CLI - **Amp**: `amp` CLI @@ -335,7 +335,7 @@ Work within integrated development environments: ### Markdown Format -Used by: Claude, Cursor, opencode, Windsurf, Amazon Q Developer, Amp, SHAI, IBM Bob +Used by: Claude, Cursor, opencode, Windsurf, Kiro CLI, Amp, SHAI, IBM Bob **Standard format:** diff --git a/README.md b/README.md index 85a9a8f1e9..5316c3a2a4 100644 --- a/README.md +++ b/README.md @@ -144,7 +144,7 @@ Want to see Spec Kit in action? Watch our [video overview](https://www.youtube.c | Agent | Support | Notes | | ------------------------------------------------------------------------------------ | ------- | ----------------------------------------------------------------------------------------------------------------------------------------- | | [Qoder CLI](https://qoder.com/cli) | āœ… | | -| [Amazon Q Developer CLI](https://aws.amazon.com/developer/learning/q-developer-cli/) | āš ļø | Amazon Q Developer CLI [does not support](https://github.com/aws/amazon-q-developer-cli/issues/3064) custom arguments for slash commands. | +| [Kiro CLI](https://kiro.dev/docs/cli/) | āœ… | Use `--ai kiro-cli` (alias: `--ai kiro`) | | [Amp](https://ampcode.com/) | āœ… | | | [Auggie CLI](https://docs.augmentcode.com/cli/overview) | āœ… | | | [Claude Code](https://www.anthropic.com/claude-code) | āœ… | | @@ -173,14 +173,14 @@ The `specify` command supports the following options: | Command | Description | | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | `init` | Initialize a new Specify project from the latest template | -| `check` | Check for installed tools (`git`, `claude`, `gemini`, `code`/`code-insiders`, `cursor-agent`, `windsurf`, `qwen`, `opencode`, `codex`, `shai`, `qodercli`) | +| `check` | Check for installed tools (`git`, `claude`, `gemini`, `code`/`code-insiders`, `cursor-agent`, `windsurf`, `qwen`, `opencode`, `codex`, `kiro-cli`, `shai`, `qodercli`) | ### `specify init` Arguments & Options | Argument/Option | Type | Description | | ---------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `` | Argument | Name for your new project directory (optional if using `--here`, or use `.` for current directory) | -| `--ai` | Option | AI assistant to use: `claude`, `gemini`, `copilot`, `cursor-agent`, `qwen`, `opencode`, `codex`, `windsurf`, `kilocode`, `auggie`, `roo`, `codebuddy`, `amp`, `shai`, `q`, `agy`, `bob`, `qodercli`, or `generic` (requires `--ai-commands-dir`) | +| `--ai` | Option | AI assistant to use: `claude`, `gemini`, `copilot`, `cursor-agent`, `qwen`, `opencode`, `codex`, `windsurf`, `kilocode`, `auggie`, `roo`, `codebuddy`, `amp`, `shai`, `kiro-cli` (`kiro` alias), `agy`, `bob`, `qodercli`, or `generic` (requires `--ai-commands-dir`) | | `--ai-commands-dir` | Option | Directory for agent command files (required with `--ai generic`, e.g. `.myagent/commands/`) | | `--script` | Option | Script variant to use: `sh` (bash/zsh) or `ps` (PowerShell) | | `--ignore-agent-tools` | Flag | Skip checks for AI agent tools like Claude Code | @@ -210,6 +210,9 @@ specify init my-project --ai qodercli # Initialize with Windsurf support specify init my-project --ai windsurf +# Initialize with Kiro CLI support +specify init my-project --ai kiro-cli + # Initialize with Amp support specify init my-project --ai amp @@ -393,7 +396,7 @@ specify init . --force --ai claude specify init --here --force --ai claude ``` -The CLI will check if you have Claude Code, Gemini CLI, Cursor CLI, Qwen CLI, opencode, Codex CLI, Qoder CLI, or Amazon Q Developer CLI installed. If you do not, or you prefer to get the templates without checking for the right tools, use `--ignore-agent-tools` with your command: +The CLI will check if you have Claude Code, Gemini CLI, Cursor CLI, Qwen CLI, opencode, Codex CLI, Qoder CLI, or Kiro CLI installed. If you do not, or you prefer to get the templates without checking for the right tools, use `--ignore-agent-tools` with your command: ```bash specify init --ai claude --ignore-agent-tools diff --git a/scripts/bash/update-agent-context.sh b/scripts/bash/update-agent-context.sh index e775707c1c..fdebac65f7 100644 --- a/scripts/bash/update-agent-context.sh +++ b/scripts/bash/update-agent-context.sh @@ -30,12 +30,12 @@ # # 5. Multi-Agent Support # - Handles agent-specific file paths and naming conventions -# - Supports: Claude, Gemini, Copilot, Cursor, Qwen, opencode, Codex, Windsurf, Kilo Code, Auggie CLI, Roo Code, CodeBuddy CLI, Qoder CLI, Amp, SHAI, Amazon Q Developer CLI, or Antigravity +# - Supports: Claude, Gemini, Copilot, Cursor, Qwen, opencode, Codex, Windsurf, Kilo Code, Auggie CLI, Roo Code, CodeBuddy CLI, Qoder CLI, Amp, SHAI, Kiro CLI, or Antigravity # - Can update single agents or all existing agent files # - Creates default Claude file if no agent files exist # # Usage: ./update-agent-context.sh [agent_type] -# Agent types: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|amp|shai|q|agy|bob|qodercli +# Agent types: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|amp|shai|kiro-cli|agy|bob|qodercli # Leave empty to update all existing agent files set -e @@ -73,7 +73,7 @@ CODEBUDDY_FILE="$REPO_ROOT/CODEBUDDY.md" QODER_FILE="$REPO_ROOT/QODER.md" AMP_FILE="$REPO_ROOT/AGENTS.md" SHAI_FILE="$REPO_ROOT/SHAI.md" -Q_FILE="$REPO_ROOT/AGENTS.md" +KIRO_FILE="$REPO_ROOT/AGENTS.md" AGY_FILE="$REPO_ROOT/.agent/rules/specify-rules.md" BOB_FILE="$REPO_ROOT/AGENTS.md" @@ -648,8 +648,8 @@ update_specific_agent() { shai) update_agent_file "$SHAI_FILE" "SHAI" ;; - q) - update_agent_file "$Q_FILE" "Amazon Q Developer CLI" + kiro-cli) + update_agent_file "$KIRO_FILE" "Kiro CLI" ;; agy) update_agent_file "$AGY_FILE" "Antigravity" @@ -662,7 +662,7 @@ update_specific_agent() { ;; *) log_error "Unknown agent type '$agent_type'" - log_error "Expected: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|amp|shai|q|agy|bob|qodercli|generic" + log_error "Expected: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|amp|shai|kiro-cli|agy|bob|qodercli|generic" exit 1 ;; esac @@ -737,8 +737,8 @@ update_all_existing_agents() { found_agent=true fi - if [[ -f "$Q_FILE" ]]; then - update_agent_file "$Q_FILE" "Amazon Q Developer CLI" + if [[ -f "$KIRO_FILE" ]]; then + update_agent_file "$KIRO_FILE" "Kiro CLI" found_agent=true fi @@ -775,7 +775,7 @@ print_summary() { echo - log_info "Usage: $0 [claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|amp|shai|q|agy|bob|qodercli]" + log_info "Usage: $0 [claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|amp|shai|kiro-cli|agy|bob|qodercli]" } #============================================================================== @@ -827,4 +827,3 @@ main() { if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then main "$@" fi - diff --git a/scripts/powershell/update-agent-context.ps1 b/scripts/powershell/update-agent-context.ps1 index ad8016a725..02ce102057 100644 --- a/scripts/powershell/update-agent-context.ps1 +++ b/scripts/powershell/update-agent-context.ps1 @@ -9,7 +9,7 @@ Mirrors the behavior of scripts/bash/update-agent-context.sh: 2. Plan Data Extraction 3. Agent File Management (create from template or update existing) 4. Content Generation (technology stack, recent changes, timestamp) - 5. Multi-Agent Support (claude, gemini, copilot, cursor-agent, qwen, opencode, codex, windsurf, kilocode, auggie, roo, codebuddy, amp, shai, q, agy, bob, qodercli) + 5. Multi-Agent Support (claude, gemini, copilot, cursor-agent, qwen, opencode, codex, windsurf, kilocode, auggie, roo, codebuddy, amp, shai, kiro-cli, agy, bob, qodercli) .PARAMETER AgentType Optional agent key to update a single agent. If omitted, updates all existing agent files (creating a default Claude file if none exist). @@ -25,7 +25,7 @@ Relies on common helper functions in common.ps1 #> param( [Parameter(Position=0)] - [ValidateSet('claude','gemini','copilot','cursor-agent','qwen','opencode','codex','windsurf','kilocode','auggie','roo','codebuddy','amp','shai','q','agy','bob','qodercli','generic')] + [ValidateSet('claude','gemini','copilot','cursor-agent','qwen','opencode','codex','windsurf','kilocode','auggie','roo','codebuddy','amp','shai','kiro-cli','agy','bob','qodercli','generic')] [string]$AgentType ) @@ -58,7 +58,7 @@ $CODEBUDDY_FILE = Join-Path $REPO_ROOT 'CODEBUDDY.md' $QODER_FILE = Join-Path $REPO_ROOT 'QODER.md' $AMP_FILE = Join-Path $REPO_ROOT 'AGENTS.md' $SHAI_FILE = Join-Path $REPO_ROOT 'SHAI.md' -$Q_FILE = Join-Path $REPO_ROOT 'AGENTS.md' +$KIRO_FILE = Join-Path $REPO_ROOT 'AGENTS.md' $AGY_FILE = Join-Path $REPO_ROOT '.agent/rules/specify-rules.md' $BOB_FILE = Join-Path $REPO_ROOT 'AGENTS.md' @@ -399,11 +399,11 @@ function Update-SpecificAgent { 'qodercli' { Update-AgentFile -TargetFile $QODER_FILE -AgentName 'Qoder CLI' } 'amp' { Update-AgentFile -TargetFile $AMP_FILE -AgentName 'Amp' } 'shai' { Update-AgentFile -TargetFile $SHAI_FILE -AgentName 'SHAI' } - 'q' { Update-AgentFile -TargetFile $Q_FILE -AgentName 'Amazon Q Developer CLI' } + 'kiro-cli' { Update-AgentFile -TargetFile $KIRO_FILE -AgentName 'Kiro CLI' } 'agy' { Update-AgentFile -TargetFile $AGY_FILE -AgentName 'Antigravity' } 'bob' { Update-AgentFile -TargetFile $BOB_FILE -AgentName 'IBM Bob' } 'generic' { Write-Info 'Generic agent: no predefined context file. Use the agent-specific update script for your agent.' } - default { Write-Err "Unknown agent type '$Type'"; Write-Err 'Expected: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|amp|shai|q|agy|bob|qodercli|generic'; return $false } + default { Write-Err "Unknown agent type '$Type'"; Write-Err 'Expected: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|amp|shai|kiro-cli|agy|bob|qodercli|generic'; return $false } } } @@ -423,7 +423,7 @@ function Update-AllExistingAgents { if (Test-Path $CODEBUDDY_FILE) { if (-not (Update-AgentFile -TargetFile $CODEBUDDY_FILE -AgentName 'CodeBuddy CLI')) { $ok = $false }; $found = $true } if (Test-Path $QODER_FILE) { if (-not (Update-AgentFile -TargetFile $QODER_FILE -AgentName 'Qoder CLI')) { $ok = $false }; $found = $true } if (Test-Path $SHAI_FILE) { if (-not (Update-AgentFile -TargetFile $SHAI_FILE -AgentName 'SHAI')) { $ok = $false }; $found = $true } - if (Test-Path $Q_FILE) { if (-not (Update-AgentFile -TargetFile $Q_FILE -AgentName 'Amazon Q Developer CLI')) { $ok = $false }; $found = $true } + if (Test-Path $KIRO_FILE) { if (-not (Update-AgentFile -TargetFile $KIRO_FILE -AgentName 'Kiro CLI')) { $ok = $false }; $found = $true } if (Test-Path $AGY_FILE) { if (-not (Update-AgentFile -TargetFile $AGY_FILE -AgentName 'Antigravity')) { $ok = $false }; $found = $true } if (Test-Path $BOB_FILE) { if (-not (Update-AgentFile -TargetFile $BOB_FILE -AgentName 'IBM Bob')) { $ok = $false }; $found = $true } if (-not $found) { @@ -440,7 +440,7 @@ function Print-Summary { if ($NEW_FRAMEWORK) { Write-Host " - Added framework: $NEW_FRAMEWORK" } if ($NEW_DB -and $NEW_DB -ne 'N/A') { Write-Host " - Added database: $NEW_DB" } Write-Host '' - Write-Info 'Usage: ./update-agent-context.ps1 [-AgentType claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|amp|shai|q|agy|bob|qodercli|generic]' + Write-Info 'Usage: ./update-agent-context.ps1 [-AgentType claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|amp|shai|kiro-cli|agy|bob|qodercli|generic]' } function Main { @@ -461,4 +461,3 @@ function Main { } Main - diff --git a/src/specify_cli/__init__.py b/src/specify_cli/__init__.py index 5651ac7226..597866bbce 100644 --- a/src/specify_cli/__init__.py +++ b/src/specify_cli/__init__.py @@ -216,11 +216,11 @@ def _format_rate_limit_error(status_code: int, headers: httpx.Headers, url: str) "install_url": None, # IDE-based "requires_cli": False, }, - "q": { - "name": "Amazon Q Developer CLI", - "folder": ".amazonq/", + "kiro-cli": { + "name": "Kiro CLI", + "folder": ".kiro/", "commands_subdir": "prompts", # Special: uses prompts/ not commands/ - "install_url": "https://aws.amazon.com/developer/learning/q-developer-cli/", + "install_url": "https://kiro.dev/docs/cli/", "requires_cli": True, }, "amp": { @@ -260,6 +260,34 @@ def _format_rate_limit_error(status_code: int, headers: httpx.Headers, url: str) }, } +AI_ASSISTANT_ALIASES = { + "kiro": "kiro-cli", +} + +def _build_ai_assistant_help() -> str: + """Build the --ai help text from AGENT_CONFIG so it stays in sync with runtime config.""" + + non_generic_agents = sorted(agent for agent in AGENT_CONFIG if agent != "generic") + base_help = ( + f"AI assistant to use: {', '.join(non_generic_agents)}, " + "or generic (requires --ai-commands-dir)." + ) + + if not AI_ASSISTANT_ALIASES: + return base_help + + alias_phrases = [] + for alias, target in sorted(AI_ASSISTANT_ALIASES.items()): + alias_phrases.append(f\"'{alias}' as an alias for '{target}'\") + + if len(alias_phrases) == 1: + aliases_text = alias_phrases[0] + else: + aliases_text = ', '.join(alias_phrases[:-1]) + ' and ' + alias_phrases[-1] + + return base_help + " Use " + aliases_text + "." +AI_ASSISTANT_HELP = _build_ai_assistant_help() + SCRIPT_TYPE_CHOICES = {"sh": "POSIX Shell (bash/zsh)", "ps": "PowerShell"} CLAUDE_LOCAL_PATH = Path.home() / ".claude" / "local" / "claude" @@ -534,7 +562,12 @@ def check_tool(tool: str, tracker: StepTracker = None) -> bool: tracker.complete(tool, "available") return True - found = shutil.which(tool) is not None + 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 + else: + found = shutil.which(tool) is not None if tracker: if found: @@ -1214,7 +1247,7 @@ def install_ai_skills(project_path: Path, selected_ai: str, tracker: StepTracker @app.command() def init( project_name: str = typer.Argument(None, help="Name for your new project directory (optional if using --here, or use '.' for current directory)"), - ai_assistant: str = typer.Option(None, "--ai", help="AI assistant to use: claude, gemini, copilot, cursor-agent, qwen, opencode, codex, windsurf, kilocode, auggie, codebuddy, amp, shai, q, agy, bob, qodercli, or generic (requires --ai-commands-dir)"), + ai_assistant: str = typer.Option(None, "--ai", help=AI_ASSISTANT_HELP), ai_commands_dir: str = typer.Option(None, "--ai-commands-dir", help="Directory for agent command files (required with --ai generic, e.g. .myagent/commands/)"), script_type: str = typer.Option(None, "--script", help="Script type to use: sh or ps"), ignore_agent_tools: bool = typer.Option(False, "--ignore-agent-tools", help="Skip checks for AI agent tools like Claude Code"), @@ -1270,6 +1303,9 @@ def init( console.print("[yellow]Example:[/yellow] specify init --ai generic --ai-commands-dir .myagent/commands/") raise typer.Exit(1) + if ai_assistant: + ai_assistant = AI_ASSISTANT_ALIASES.get(ai_assistant, ai_assistant) + if project_name == ".": here = True project_name = None # Clear project_name to use existing validation logic @@ -1464,8 +1500,9 @@ def init( if skills_ok and not here: agent_cfg = AGENT_CONFIG.get(selected_ai, {}) agent_folder = agent_cfg.get("folder", "") + commands_subdir = agent_cfg.get("commands_subdir", "commands") if agent_folder: - cmds_dir = project_path / agent_folder.rstrip("/") / "commands" + cmds_dir = project_path / agent_folder.rstrip("/") / commands_subdir if cmds_dir.exists(): try: shutil.rmtree(cmds_dir) @@ -2350,4 +2387,3 @@ def main(): if __name__ == "__main__": main() - diff --git a/src/specify_cli/extensions.py b/src/specify_cli/extensions.py index b8881e7c89..5a90b4d58d 100644 --- a/src/specify_cli/extensions.py +++ b/src/specify_cli/extensions.py @@ -653,8 +653,8 @@ class CommandRegistrar: "args": "$ARGUMENTS", "extension": ".md" }, - "q": { - "dir": ".amazonq/prompts", + "kiro-cli": { + "dir": ".kiro/prompts", "format": "markdown", "args": "$ARGUMENTS", "extension": ".md" @@ -1782,4 +1782,3 @@ def disable_hooks(self, extension_id: str): self.save_project_config(config) - diff --git a/tests/test_agent_config_consistency.py b/tests/test_agent_config_consistency.py new file mode 100644 index 0000000000..5296a7cb4f --- /dev/null +++ b/tests/test_agent_config_consistency.py @@ -0,0 +1,97 @@ +"""Consistency checks for agent configuration across runtime and packaging scripts.""" + +import re +from pathlib import Path + +from specify_cli import AGENT_CONFIG, AI_ASSISTANT_HELP +from specify_cli.extensions import CommandRegistrar + + +REPO_ROOT = Path(__file__).resolve().parent.parent + + +class TestAgentConfigConsistency: + """Ensure kiro-cli migration stays synchronized across key surfaces.""" + + def test_runtime_config_uses_kiro_cli_and_removes_q(self): + """AGENT_CONFIG should include kiro-cli and exclude legacy q.""" + assert "kiro-cli" in AGENT_CONFIG + assert AGENT_CONFIG["kiro-cli"]["folder"] == ".kiro/" + assert AGENT_CONFIG["kiro-cli"]["commands_subdir"] == "prompts" + assert "q" not in AGENT_CONFIG + + def test_extension_registrar_uses_kiro_cli_and_removes_q(self): + """Extension command registrar should target .kiro/prompts.""" + cfg = CommandRegistrar.AGENT_CONFIGS + + assert "kiro-cli" in cfg + assert cfg["kiro-cli"]["dir"] == ".kiro/prompts" + assert "q" not in cfg + + def test_release_agent_lists_include_kiro_cli_and_exclude_q(self): + """Bash and PowerShell release scripts should agree on agent key set for Kiro.""" + sh_text = (REPO_ROOT / ".github" / "workflows" / "scripts" / "create-release-packages.sh").read_text(encoding="utf-8") + ps_text = (REPO_ROOT / ".github" / "workflows" / "scripts" / "create-release-packages.ps1").read_text(encoding="utf-8") + + sh_match = re.search(r"ALL_AGENTS=\(([^)]*)\)", sh_text) + assert sh_match is not None + sh_agents = sh_match.group(1).split() + + ps_match = re.search(r"\$AllAgents = @\(([^)]*)\)", ps_text) + assert ps_match is not None + ps_agents = re.findall(r"'([^']+)'", ps_match.group(1)) + + assert "kiro-cli" in sh_agents + assert "kiro-cli" in ps_agents + assert "shai" in sh_agents + assert "shai" in ps_agents + assert "agy" in sh_agents + assert "agy" in ps_agents + assert "q" not in sh_agents + assert "q" not in ps_agents + + def test_release_ps_switch_has_shai_and_agy_generation(self): + """PowerShell release builder must generate files for shai and agy agents.""" + ps_text = (REPO_ROOT / ".github" / "workflows" / "scripts" / "create-release-packages.ps1").read_text(encoding="utf-8") + + assert re.search(r"'shai'\s*\{.*?\.shai/commands", ps_text, re.S) is not None + assert re.search(r"'agy'\s*\{.*?\.agent/workflows", ps_text, re.S) is not None + + def test_init_ai_help_includes_roo_and_kiro_alias(self): + """CLI help text for --ai should stay in sync with agent config and alias guidance.""" + assert "roo" in AI_ASSISTANT_HELP + assert "Use 'kiro' as an alias for 'kiro-cli'." in AI_ASSISTANT_HELP + + def test_devcontainer_kiro_installer_uses_pinned_checksum(self): + """Devcontainer installer should always verify Kiro installer via pinned SHA256.""" + post_create_text = (REPO_ROOT / ".devcontainer" / "post-create.sh").read_text(encoding="utf-8") + + assert 'KIRO_INSTALLER_SHA256="7487a65cf310b7fb59b357c4b5e6e3f3259d383f4394ecedb39acf70f307cffb"' in post_create_text + assert "sha256sum -c -" in post_create_text + assert "KIRO_SKIP_KIRO_INSTALLER_VERIFY" not in post_create_text + + def test_release_output_targets_kiro_prompt_dir(self): + """Packaging and release scripts should no longer emit amazonq artifacts.""" + sh_text = (REPO_ROOT / ".github" / "workflows" / "scripts" / "create-release-packages.sh").read_text(encoding="utf-8") + ps_text = (REPO_ROOT / ".github" / "workflows" / "scripts" / "create-release-packages.ps1").read_text(encoding="utf-8") + gh_release_text = (REPO_ROOT / ".github" / "workflows" / "scripts" / "create-github-release.sh").read_text(encoding="utf-8") + + assert ".kiro/prompts" in sh_text + assert ".kiro/prompts" in ps_text + assert ".amazonq/prompts" not in sh_text + assert ".amazonq/prompts" not in ps_text + + assert "spec-kit-template-kiro-cli-sh-" in gh_release_text + assert "spec-kit-template-kiro-cli-ps-" in gh_release_text + assert "spec-kit-template-q-sh-" not in gh_release_text + assert "spec-kit-template-q-ps-" not in gh_release_text + + def test_agent_context_scripts_use_kiro_cli(self): + """Agent context scripts should advertise kiro-cli and not legacy q agent key.""" + bash_text = (REPO_ROOT / "scripts" / "bash" / "update-agent-context.sh").read_text(encoding="utf-8") + pwsh_text = (REPO_ROOT / "scripts" / "powershell" / "update-agent-context.ps1").read_text(encoding="utf-8") + + assert "kiro-cli" in bash_text + assert "kiro-cli" in pwsh_text + assert "Amazon Q Developer CLI" not in bash_text + assert "Amazon Q Developer CLI" not in pwsh_text diff --git a/tests/test_ai_skills.py b/tests/test_ai_skills.py index 3eec4a419c..59f13bd5ee 100644 --- a/tests/test_ai_skills.py +++ b/tests/test_ai_skills.py @@ -162,6 +162,11 @@ def test_cursor_agent_skills_dir(self, project_dir): result = _get_skills_dir(project_dir, "cursor-agent") assert result == project_dir / ".cursor" / "skills" + def test_kiro_cli_skills_dir(self, project_dir): + """Kiro CLI should use .kiro/skills/.""" + result = _get_skills_dir(project_dir, "kiro-cli") + assert result == project_dir / ".kiro" / "skills" + def test_unknown_agent_uses_default(self, project_dir): """Unknown agents should fall back to DEFAULT_SKILLS_DIR.""" result = _get_skills_dir(project_dir, "nonexistent-agent") @@ -460,8 +465,9 @@ def _fake_extract(self, agent, project_path, **_kwargs): """Simulate template extraction: create agent commands dir.""" agent_cfg = AGENT_CONFIG.get(agent, {}) agent_folder = agent_cfg.get("folder", "") + commands_subdir = agent_cfg.get("commands_subdir", "commands") if agent_folder: - cmds_dir = project_path / agent_folder.rstrip("/") / "commands" + cmds_dir = project_path / agent_folder.rstrip("/") / commands_subdir cmds_dir.mkdir(parents=True, exist_ok=True) (cmds_dir / "speckit.specify.md").write_text("# spec") @@ -483,6 +489,7 @@ def fake_download(project_path, *args, **kwargs): patch("specify_cli.shutil.which", return_value="/usr/bin/git"): result = runner.invoke(app, ["init", str(target), "--ai", "claude", "--ai-skills", "--script", "sh", "--no-git"]) + assert result.exit_code == 0 # Skills should have been called mock_skills.assert_called_once() @@ -490,6 +497,30 @@ def fake_download(project_path, *args, **kwargs): cmds_dir = target / ".claude" / "commands" assert not cmds_dir.exists() + def test_new_project_nonstandard_commands_subdir_removed_after_skills_succeed(self, tmp_path): + """For non-standard agents, configured commands_subdir should be removed on success.""" + from typer.testing import CliRunner + + runner = CliRunner() + target = tmp_path / "new-kiro-proj" + + def fake_download(project_path, *args, **kwargs): + self._fake_extract("kiro-cli", project_path) + + with patch("specify_cli.download_and_extract_template", side_effect=fake_download), \ + patch("specify_cli.ensure_executable_scripts"), \ + patch("specify_cli.ensure_constitution_from_template"), \ + patch("specify_cli.install_ai_skills", return_value=True) as mock_skills, \ + patch("specify_cli.is_git_repo", return_value=False), \ + patch("specify_cli.shutil.which", return_value="/usr/bin/git"): + result = runner.invoke(app, ["init", str(target), "--ai", "kiro-cli", "--ai-skills", "--script", "sh", "--no-git"]) + + assert result.exit_code == 0 + mock_skills.assert_called_once() + + prompts_dir = target / ".kiro" / "prompts" + assert not prompts_dir.exists() + def test_commands_preserved_when_skills_fail(self, tmp_path): """If skills fail, commands should NOT be removed (safety net).""" from typer.testing import CliRunner @@ -508,6 +539,7 @@ def fake_download(project_path, *args, **kwargs): patch("specify_cli.shutil.which", return_value="/usr/bin/git"): result = runner.invoke(app, ["init", str(target), "--ai", "claude", "--ai-skills", "--script", "sh", "--no-git"]) + assert result.exit_code == 0 # Commands should still exist since skills failed cmds_dir = target / ".claude" / "commands" assert cmds_dir.exists() @@ -538,8 +570,9 @@ def fake_download(project_path, *args, **kwargs): patch("specify_cli.install_ai_skills", return_value=True), \ patch("specify_cli.is_git_repo", return_value=True), \ patch("specify_cli.shutil.which", return_value="/usr/bin/git"): - result = runner.invoke(app, ["init", "--here", "--ai", "claude", "--ai-skills", "--script", "sh", "--no-git"]) + result = runner.invoke(app, ["init", "--here", "--ai", "claude", "--ai-skills", "--script", "sh", "--no-git"], input="y\n") + assert result.exit_code == 0 # Commands must remain for --here assert cmds_dir.exists() assert (cmds_dir / "speckit.specify.md").exists() @@ -631,6 +664,42 @@ def test_ai_skills_flag_appears_in_help(self): assert "--ai-skills" in plain assert "agent skills" in plain.lower() + def test_kiro_alias_normalized_to_kiro_cli(self, tmp_path): + """--ai kiro should normalize to canonical kiro-cli agent key.""" + from typer.testing import CliRunner + + runner = CliRunner() + target = tmp_path / "kiro-alias-proj" + + with patch("specify_cli.download_and_extract_template") as mock_download, \ + patch("specify_cli.ensure_executable_scripts"), \ + patch("specify_cli.ensure_constitution_from_template"), \ + patch("specify_cli.is_git_repo", return_value=False), \ + patch("specify_cli.shutil.which", return_value="/usr/bin/git"): + result = runner.invoke( + app, + [ + "init", + str(target), + "--ai", + "kiro", + "--ignore-agent-tools", + "--script", + "sh", + "--no-git", + ], + ) + + assert result.exit_code == 0 + assert mock_download.called + # download_and_extract_template(project_path, ai_assistant, script_type, ...) + assert mock_download.call_args.args[1] == "kiro-cli" + + def test_q_removed_from_agent_config(self): + """Amazon Q legacy key should not remain in AGENT_CONFIG.""" + assert "q" not in AGENT_CONFIG + assert "kiro-cli" in AGENT_CONFIG + class TestParameterOrderingIssue: """Test fix for GitHub issue #1641: parameter ordering issues.""" diff --git a/tests/test_extensions.py b/tests/test_extensions.py index a2c4121ed4..e6849729e3 100644 --- a/tests/test_extensions.py +++ b/tests/test_extensions.py @@ -399,6 +399,12 @@ def test_config_backup_on_remove(self, extension_dir, project_dir): class TestCommandRegistrar: """Test CommandRegistrar command registration.""" + def test_kiro_cli_agent_config_present(self): + """Kiro CLI should be mapped to .kiro/prompts and legacy q removed.""" + assert "kiro-cli" in CommandRegistrar.AGENT_CONFIGS + assert CommandRegistrar.AGENT_CONFIGS["kiro-cli"]["dir"] == ".kiro/prompts" + assert "q" not in CommandRegistrar.AGENT_CONFIGS + def test_parse_frontmatter_valid(self): """Test parsing valid YAML frontmatter.""" content = """---