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
Fixes#4797, both the dispatch and the prompt names. Workflow command and prompt steps with integration: kiro-cli ran kiro-cli -p "<prompt>", inherited from MarkdownIntegration.build_exec_args(). Kiro CLI rejects that at argument parsing, so every such step failed:
▸ [constitution] speckit.constitution …
error: unexpected argument '-p' found
Status: failed
Error: Command exited with code 2
KiroCliIntegration.build_exec_args() now builds kiro-cli chat --no-interactive --trust-all-tools [--model M] [--output-format stream-json] <prompt>, following kiro-cli chat --help on Kiro CLI 2.26.0:
chat --no-interactive runs one prompt headless, with the prompt as the positional input.
--trust-all-tools: headless mode can't ask for tool approval, so without it every write is denied (tool permission approval is not supported in non-interactive mode. Use --trust-all-tools to auto-approve.) and the run still exits 0. Same role as Copilot's --yolo and Cursor's --force.
Kiro has no json output format, so output_json=True maps to --output-format stream-json (JSON Lines), like Amp's --stream-json.
Extra args from SPECKIT_INTEGRATION_KIRO_CLI_EXTRA_ARGS go after the chat flags and before the prompt.
Kiro CLI also doesn't run dotted slash names: /speckit.constitution is rejected as "not a built-in Kiro CLI slash command", while /speckit-constitution runs .kiro/prompts/speckit-constitution.md. So the Kiro integration now follows Junie (#4073) and Cline:
Prompts install as .kiro/prompts/speckit-<command>.md, and dispatch sends /speckit-<command> (format_kiro_command_name, command_filename, build_command_invocation).
invoke_separator = "-", so shared templates and scripts render /speckit-plan. format_name in registrar_config gives extension and preset prompts the same names, e.g. speckit-git-commit.md.
Migration: specify integration upgrade kiro-cli stale-removes the old core speckit.*.md prompts through the manifest. If one was modified, the upgrade stops and lists it, and the file stays until --force. Enabled extension prompts are tracked in the extension registry, not the manifest. Kiro declares the dotted names as its legacy flat command files, so every registration pass (use, switch, and upgrade of the active integration) writes an extension's hyphenated prompts and then removes each dotted one whose replacement exists. That is the ExtensionManager._retire_legacy_flat_extension_commands() step Qoder's skills migration ([bug-fix] Fix qodercli-skills-migration: migrate QodercliIntegration to SkillsIntegration #4205) already uses. It also covers Kiro upgraded while another integration is active, once use or switch activates it, and an extension that was disabled during the upgrade. One that can't be re-registered keeps its old prompt and its registry entry. While presets have commands registered for Kiro, upgrade refuses the rename before changing files, like the Kilo command-root and command↔skills layout guards, and says to remove the preset, upgrade and add it back.
Detection: specify check and specify init also accepted a bare kiro as Kiro CLI, while both workflow preflights look for kiro-cli. A bare kiro launches Kiro IDE by default, and Kiro's command router sends it to the CLI only after kiro-cli installs the router. Kiro IDE 1.2.4 exits 0 on the headless argv without running anything. So the check now looks for kiro-cli only, like dispatch, and so does the devcontainer. SPECKIT_INTEGRATION_KIRO_CLI_EXECUTABLE still overrides the executable.
docs/reference/integrations.md: the Kiro row says prompts are hyphenated and why, and that Spec Kit looks for kiro-cli only.
Headless Kiro still drops any text after /speckit-<name>, so a step's input.args doesn't reach the model. That's the Kiro limitation the existing prose fallback already covers (#1926).
Reproduction with the workflow from #4797, Kiro CLI 2.26.0, logged in:
main: fails with the error above.
Dispatch fix only (88c9c13): the step runs, but Kiro rejects /speckit.constitution, and the model then finds and reads the prompt file itself.
This branch: Kiro expands /speckit-constitution directly. The model's first tool calls are the prompt's own steps (the hook check, resolve-template.sh), .specify/memory/constitution.md is written, and the workflow ends with Status: completed.
Upgrade: a project initialized on main with the git extension had 15 speckit.*.md prompts. specify integration upgrade kiro-cli on this branch printed "Removed 10 stale file(s) from previous install" (the core prompts) and left 15 speckit-*.md prompts. With the git extension's manifest corrupted, its 5 dotted prompts stayed and the registry still tracked them. With a preset overriding speckit.plan also installed, the upgrade stopped before changing anything; preset remove, the upgrade and preset add then left 15 speckit-*.md prompts with the override in speckit-plan.md. With Kiro secondary (a 1.0.13 project where Claude was then made the default), the upgrade renamed the core prompts, and integration use kiro-cli or integration switch kiro-cli then left 15 speckit-*.md prompts and no dotted ones.
Testing
Tested locally with uv run specify --help
Ran existing tests with uv sync && uv run pytest
Tested with a sample project (if applicable)
New tests in tests/integrations/test_integration_kiro_cli.py: the headless chat argv, --model plus stream-json, and extra-args placement; the name formatter, filenames and invocation; a CommandStep dispatch test with the exact Kiro argv; the hook note and handoffs; and hyphenated overrides of the base inventory tests. In tests/specify_cli/integrations/test_command_upgrade.py, test_upgrade_replaces_dotted_kiro_prompts, test_upgrade_refuses_kiro_prompt_rename_while_presets_are_installed, test_upgrade_keeps_dotted_kiro_prompts_when_reregistration_fails, test_activating_kiro_after_secondary_upgrade_retires_dotted_prompts (use and switch), test_enabling_extension_after_kiro_rename_retires_its_dotted_prompts and test_kiro_prompt_named_without_dots_is_not_retired cover the migration, and test_command_file_names_changed_needs_a_rename covers rename detection. The new tests fail on main, except test_kiro_prompt_named_without_dots_is_not_retired, which guards the new retirement step: it fails if that step's same-path check is removed. test_steps_do_not_dispatch_the_kiro_ide_launcher (command and prompt steps) and test_kiro_ide_launcher_is_not_kiro_cli cover detection.
tests/specify_cli/workflows/test_catalog_versions.py: _archive() now pins its zip entry time. The tests hashed one build for the catalog and served another as the download, so a 2-second clock tick between them failed the integrity check on Windows CI. The helper came from feat(workflows): select exact workflow catalog releases #4788; details are in the PR conversation.
Full suite: 9046 passed, 250 skipped (Linux, Python 3.13), after merging main.
ruff check src tests (0.15.0): clean.
Sample project: specify init --integration kiro-cli, then the workflow above on main and on this branch.
AI Disclosure
I did not use AI assistance for this contribution
I did use AI assistance (fill in the disclosure below)
AI disclosure: Claude Code (Claude Opus 5.5, autonomous agent mode) was used to investigate Kiro CLI and Kiro IDE, run the reproductions, and write the code changes, the tests and this description.
Kiro CLI runs /name from .kiro/prompts/name.md only when the name has
no dots, so the installed /speckit.plan is rejected as an unrecognized
slash command. Install speckit-<command>.md and dispatch
/speckit-<command>, following the Junie and Cline integrations, so
workflow steps run the prompt instead of relying on the model to find
the file. Upgrade stale-removes the old dotted prompts.
Refs github#4797
Assisted-by: Claude Code (model: claude-opus-5-5, autonomous)
Pushed 86afed7, which adds the second half of #4797: Kiro prompts are now hyphenated. The assessment on #4797 says the argv change alone is not a complete fix, and I agree. With only 88c9c13, a workflow step sends /speckit.constitution, and Kiro answers that it "is not a built-in Kiro CLI slash command". The step then passes only because the model searches for and reads .kiro/prompts/speckit.constitution.md on its own.
format_kiro_command_name and the command_filename, build_command_invocation and invoke_separator = "-" overrides, so shared templates and scripts render /speckit-plan.
format_name in registrar_config, so extension and preset prompts get the same names. The bundled git extension installs speckit-git-commit.md.
This also resolves the Copilot finding. The build_exec_args docstring now says a /speckit-* input runs the prompt file, which is true once the files are hyphenated.
New tests:
the formatter, filenames and invocation;
a CommandStep dispatch test asserting the exact Kiro argv with /speckit-constitution;
the hook note and handoffs;
hyphenated overrides of the base Markdown inventory tests;
test_upgrade_replaces_dotted_kiro_prompts.
12 of these fail with the previous kiro_cli/__init__.py.
docs/reference/integrations.md: the Kiro row now says prompts are hyphenated and why.
Answers to the assessment's open questions
Output format. Workflow command steps call dispatch_command() with the default stream=True, and prompt steps pass output_json=False. So Kiro gets no --output-format and prints text, which the runner streams without parsing. If a caller does ask for JSON, output_json=True maps to --output-format stream-json (JSON Lines), since Kiro has no plain json format.
--trust-all-tools. It's always added. Headless Kiro can't ask for approval, so without it every write is denied while the run still exits 0. A workflow step would then report success with nothing written. Copilot's --yolo and Cursor's --force do the same job in their integrations.
Migration.specify integration upgrade kiro-cli stale-removes the dotted prompts through the existing manifest contract.
I ran it on a project initialized with the code before this PR. Output: "Removed 10 stale file(s) from previous install", and all 10 prompts are now speckit-*.md.
If a dotted prompt was modified, the upgrade stops and lists it, and the file stays until --force is used. test_upgrade_replaces_dotted_kiro_prompts covers both cases.
Kiro dispatch exited 2 before this PR, so nothing that worked before depended on the dotted names being dispatched.
Versions. I tested only Kiro CLI 2.26.0, the current stable download, and added no version gate.
Real run (Kiro CLI 2.26.0, specify workflow run with a speckit.constitution step and a shell step)
With this commit, Kiro expands the prompt itself. The model's first tool calls are the prompt's own steps: the extensions.yml hook check and resolve-template.sh. It never opens .kiro/prompts, and the run ends Status: completed.
The full suite passes: 8771 passed, 251 skipped. ruff check src tests is clean.
One correction: 88c9c13 is missing the Assisted-by: trailer. The same agent made it, and 86afed7 carries the trailer.
Drafted on behalf of @kartsan03 by Claude Code (model: claude-opus-5-5, autonomous). The agent wrote the code, tests and this comment, and ran the Kiro CLI and upgrade checks above.
I updated the title and description for Copilot's scope finding. They now cover the prompt rename, the integration upgrade migration of the old speckit.*.md files, and the docs row, and they say Fixes #4797 because both halves are in this PR. The code is unchanged since 86afed7.
Drafted on behalf of @kartsan03 by Claude Code (model: claude-opus-5-5, autonomous). The agent rewrote the PR title and description and wrote this comment.
test_upgrade_replaces_dotted_kiro_prompts restored the edited prompt
with write_text(), which writes CRLF on Windows. Integration files are
written as LF bytes, so the restored file no longer matched its
manifest hash and the second upgrade was still blocked as modified
(pytest on windows-latest). Read and write the prompt as bytes.
Refs github#4797
Assisted-by: Claude Code (model: claude-opus-5-5, autonomous)
Fixed the Windows pytest failure in cb898ce. The problem was in the test I added, not the Kiro change.
test_upgrade_replaces_dotted_kiro_prompts edits .kiro/prompts/speckit.plan.md to check that upgrade stops on a modified prompt, then restores it. It restored the file with write_text(), which writes CRLF on Windows. write_file_and_record() writes LF bytes, so the restored file no longer matched its manifest hash, and the second upgrade was still blocked. The test now reads and writes the prompt as bytes.
The macOS 3.13 job was cancelled by fail-fast after the Windows failure; it didn't fail itself. The upgrade and Kiro test modules pass locally (75).
Drafted on behalf of @kartsan03 by Claude Code (model: claude-opus-5-5, autonomous). The agent diagnosed the CI failure from the job logs, fixed the test and wrote this comment.
Existing extension and preset prompts are not migrated. On upgrade, this formatter makes re-registration write new hyphenated files, but command_upgrade.py:314-345 only unregisters old registrations when the command directory changes; Kiro's directory remains .kiro/prompts. Those artifacts are tracked outside the integration manifest, so their old speckit.*.md copies remain beside the replacements. Add same-directory naming-migration cleanup before re-registration and cover an upgrade with an installed extension and preset.
Document that headless Kiro workflow dispatch always passes --trust-all-tools, which auto-approves tool use. This is a security-relevant runtime default introduced by this PR; unlike the MiniMax row below, the current Kiro row only describes prompt naming and argument substitution, so users cannot discover the permission behavior from the integration reference.
Upgrade only unregistered enabled extension commands when the command
directory changed. Kiro keeps .kiro/prompts but renamed its files, and
extension prompts are tracked in the extension registry rather than the
manifest, so upgrading a project with the git extension left the five
speckit.git.*.md prompts beside the new speckit-git-*.md ones. Treat a
same-directory rename of the core command files like a directory change,
so the existing cleanup removes them before re-registration.
Also document that headless Kiro dispatch passes --trust-all-tools.
Refs github#4797
Assisted-by: Claude Code (model: claude-opus-5-5, autonomous)
Extension prompts on upgrade. Confirmed and fixed. I initialized a project with the code before this PR and added the bundled git extension, then ran specify integration upgrade kiro-cli on the branch. The 10 core prompts were replaced, but the five speckit.git.*.md prompts stayed beside the new speckit-git-*.md ones. That happened because upgrade only calls _unregister_enabled_extension_commands_for_agent() when the command directory changes. _command_file_names_changed() now also triggers it when the core command files were renamed inside the same directory. unregister_commands() already removes both the formatted name and the raw registered name, so the existing cleanup deletes the dotted files before re-registration. The real upgrade now leaves 15 speckit-*.md prompts and no dotted ones. test_upgrade_replaces_dotted_kiro_prompts now installs the git extension under the old naming and fails without the fix.
Preset prompts. I didn't reproduce a leftover here. With the bundled lean preset installed under the old naming, upgrade --force (the preset overrides make a plain upgrade stop as "modified", same as on main) left the lean content in the hyphenated core prompts and no dotted copies.
--trust-all-tools. The Kiro row in docs/reference/integrations.md now says headless dispatch runs kiro-cli chat --no-interactive --trust-all-tools, which auto-approves every tool call, and why.
The full suite passes: 8771 passed, 251 skipped. ruff check src tests and markdownlint are clean.
Drafted on behalf of @kartsan03 by Claude Code (model: claude-opus-5-5, autonomous). The agent reproduced the upgrade cases, made the change and wrote this comment.
_command_file_names_changed() now needs a removed command file and an
added one with the same name up to "."/"-" separators, so a release that
adds one command and drops another no longer unregisters extension
commands. When the rename does happen on the active integration, preset
commands are unregistered before re-registration too, so dotted preset
prompts such as speckit.fakeext.cmd.md don't survive the upgrade.
Assisted-by: Claude Code (model: claude-opus-5-5, autonomous)
Both findings from the latest Copilot pass are addressed in 569a1bd.
Rename detection._command_file_names_changed() no longer treats "some files added, some removed" as a rename. It only fires when a removed command file and an added one have the same name once . and - are treated as the same separator, as with speckit.plan.md → speckit-plan.md. A release that adds speckit.new.md and drops speckit.old.md no longer unregisters extension commands. test_command_file_names_changed_needs_a_rename covers the rename case, the add-and-drop case, and an add-only case.
Preset commands. When that rename happens on the active integration, upgrade now calls _unregister_presets_for_agent() before the existing _register_presets_for_agent(), the same pairing switch uses. test_upgrade_replaces_dotted_kiro_prompts now installs a preset with a custom speckit.fakeext.cmd command under the old naming. After the upgrade, it checks that no speckit.*.md prompt is left and that speckit-fakeext-cmd.md has the preset content. Without the change, that test fails with speckit.fakeext.cmd.md still in .kiro/prompts.
tests/specify_cli/integrations, tests/integrations and tests/specify_cli/presets pass (3668 passed, 6 skipped), and ruff is clean on the changed files.
Drafted on behalf of @kartsan03 by Claude Code (model: claude-opus-5-5, autonomous). The agent reproduced both findings, made the change and wrote this comment.
…t of the Kiro rename
The Kiro prompt rename now only unregisters enabled presets before
re-registering them. Disabled presets keep their files until removal, as
`specify preset disable` promises; PresetManager.unregister_agent_artifacts
gains the same enabled_only switch ExtensionManager already has.
_command_file_names_changed() compares whole manifest paths instead of
file names, so skill layouts (every file is SKILL.md) no longer read an
added and a dropped command as a rename.
Assisted-by: Claude Code (model: claude-opus-5-5, autonomous)
Two more fixes in 268a908. The second one is Copilot's finding on 569a1bd; another review pass over the whole PR turned up both:
Disabled presets keep their prompts. The rename path unregistered every preset for the agent, but only enabled ones get re-registered. So integration upgrade kiro-cli deleted a disabled preset's prompt and never put it back, although specify preset disable says registered commands stay until the preset is removed. PresetManager.unregister_agent_artifacts() now has the same enabled_only switch as ExtensionManager's, and upgrade passes enabled_only=True. Covered by test_upgrade_keeps_disabled_preset_kiro_prompts.
Rename check on skill layouts._command_file_names_changed() compared file names only. In skill layouts every file is SKILL.md, so adding one command and dropping another still looked like a rename. It now compares whole manifest paths, keeping the parent directory as Copilot suggested. There's a new SKILL.md case in test_command_file_names_changed_needs_a_rename.
tests/specify_cli/integrations, tests/integrations, tests/specify_cli/presets and tests/extensions pass (4055 passed, 112 skipped), and ruff check src tests is clean.
Drafted on behalf of @kartsan03 by Claude Code (model: claude-opus-5-5, autonomous). The agent verified the review findings, made the change and wrote this comment.
…ements exist
The Kiro prompt rename unregistered enabled extension and preset commands
before re-registering them. Re-registration is best-effort, so an
extension or preset whose manifest couldn't be loaded lost its dotted
prompt without getting a hyphenated one, and its registry entry with it.
Upgrade now re-registers first, then removes each old speckit.*.md file
only when the file under the new name exists: the replacement-before-
retirement rule of _retire_legacy_flat_extension_commands(). The
enabled_only switch 268a908 added to PresetManager.unregister_agent_artifacts()
has no caller left, so it is reverted.
Refs github#4797
Assisted-by: Claude Code (model: claude-opus-5-5, autonomous)
Addressed Copilot's finding on 268a908 in cc91560. I reproduced it: with the git extension's extension.yml and a preset's preset.yml corrupted, specify integration upgrade kiro-cli on 268a908 exited 0 but left neither the dotted nor the hyphenated prompts for either of them, and both registries stopped tracking kiro-cli.
What changed
For the Kiro rename, upgrade no longer unregisters anything before re-registration. It re-registers enabled extensions and presets as before. Then _retire_renamed_command_files() removes each old speckit.*.md file only when the file under the new name exists. This is the replacement-before-retirement rule from _retire_legacy_flat_extension_commands() and test_upgrade_layout_change_preserves_extension_artifacts_when_reregistration_fails. It still runs only when _command_file_names_changed() finds a real rename, and only for the active integration.
With nothing unregistered up front, the enabled_only switch that 268a908 added to PresetManager.unregister_agent_artifacts() has no caller. presets/_manager_commands.py is back to main. Disabled extensions and presets aren't re-registered, so they keep their files; test_upgrade_keeps_disabled_preset_kiro_prompts still passes.
Test:test_upgrade_keeps_dotted_kiro_prompts_when_reregistration_fails corrupts both manifests and then upgrades. The dotted git and preset prompts and both registry entries must survive. It fails on 268a908 and passes now.
Real run. The project was made by main with the git extension and a custom preset command, and the upgrade ran offline:
git and preset prompts gone under both names; registries no longer track kiro-cli
the 6 dotted git and preset prompts kept; both registries still track them
Full suite: 8777 passed, 251 skipped. ruff check src tests (0.15.0) is clean. I also updated the Migration and Upgrade lines in the description.
Drafted on behalf of @kartsan03 by Claude Code (model: claude-opus-5-5, autonomous). The agent reproduced the finding, made the change and wrote this comment.
@mnriem Copilot's latest finding is about a preset that can't be re-registered during integration upgrade. main already loses that preset's overrides in the same case. The core-override example it gives is removed by the existing stale cleanup before this code runs. The real-run comparison is in my reply on the thread.
Would you take this PR as it is, with a follow-up PR that keeps a failed preset's overrides when another layer shares the path? Or do you want that in this PR?
Drafted on behalf of @kartsan03 by Claude Code (model: claude-opus-5-5, autonomous). The agent wrote this comment.
@kartsan03 Because you are touching shared code it can surface pre-existing bugs and as such I do think we should address those at the time they surface because if we do not it might be hiding other new bugs we might have introduced with the current set of changes. So please address the Copilot feedback. Thanks a lot for workng on this!
…stalled
A preset override shares its path with the command it overrides, and its
rescaffold is best-effort. If the preset couldn't be re-registered during
the rename, Phase 2 stale cleanup (core commands) or the extension's new
file (extension commands) left only the other layer's prompt, and the
override was gone.
Upgrade now refuses the rename before changing files while presets have
commands registered for the integration, like the Kilo command-root and
command/skills layout guards, and says to remove the preset, upgrade and
add it back. The rename is detected before setup by comparing the old
manifest with the files setup will write (_planned_command_files). With
presets out of the way, _retire_renamed_command_files only handles
extension commands.
Refs github#4797
Assisted-by: Claude Code (model: claude-opus-5-5, autonomous)
Addressed Copilot's finding on cc91560 in f53b21f, per your note. Rather than add more cleanup logic, I reused the guard integration upgrade already has for this class of problem.
What changed
While presets have commands registered for the integration, upgrade now refuses the Kiro rename before changing any files. It's the same guard as for the Kilo command-root move and the command↔skills layout change (review feat: update Bob integration to skills-based layout for Bob 2.0 #3415). A preset override shares its path with the command it overrides, and its rescaffold is best-effort. If the preset can't be re-registered, Phase 2 stale cleanup (for a core command) or the extension's new file (for an extension command) leaves only the other layer's prompt. The message says to remove the preset, upgrade, and add it back.
The rename is detected before setup. _command_file_names_changed() now compares the old manifest with _planned_command_files(), the files setup will write.
Presets no longer reach _retire_renamed_command_files(), so it only handles extension commands. It still removes an old file only once its replacement exists.
Test:test_upgrade_refuses_kiro_prompt_rename_while_presets_are_installed is the regression Copilot asked for. A preset overrides core speckit.plan, its installed source is removed, and upgrade --force must refuse and leave every prompt byte-identical. On cc91560 the upgrade exits 0 and the override is gone. It passes now. The two extension tests now install only the git extension.
Real runs (offline; project made by main with the git extension and a preset overriding speckit.plan and speckit.git.commit):
exit 0; speckit-plan.md has the default content, the override is gone
exit 1 with the remove/upgrade/add steps; all 15 prompts byte-identical
Following the message with an intact preset: preset remove, then integration upgrade kiro-cli (no --force needed), then preset add. The result is 15 speckit-*.md prompts with both overrides.
With only the git extension, all 15 prompts move to speckit-*.md. With its manifest corrupted, its 5 dotted prompts stay and are still tracked.
The check finds no rename on a fresh install of each of 41 integrations (all but generic, which has no fixed command dir). It also finds none for the 15 integrations with a --skills or --legacy-commands mode, installed in that mode. So the new guard can't block their ordinary upgrades.
Full suite: 8777 passed, 251 skipped. ruff check src tests (0.15.0) is clean. The Migration and Upgrade lines in the description are updated.
Drafted on behalf of @kartsan03 by Claude Code (model: claude-opus-5-5, autonomous). The agent made the change, ran the upgrade comparisons above and wrote this comment.
Kiro's documented compatibility executable is still missed here. is_tool_installed() accepts either kiro-cli or kiro (src/specify_cli/_utils.py:173-176), but _resolve_executable() falls back only to the integration key, so both workflow preflights check kiro-cli twice and report the CLI as unavailable when only kiro is on PATH. Resolve kiro-cli with a kiro fallback (while preserving the executable env override) and cover both command and prompt dispatch with the fallback binary.
check_tool("kiro-cli") also accepted a bare `kiro`, so `specify check`
and `specify init` reported Kiro CLI as installed while both workflow
preflights, which look for `kiro-cli`, reported it missing.
A bare `kiro` launches Kiro IDE by default. Kiro's command router sends
it to the CLI only after `kiro-cli` installs the router. Kiro IDE 1.2.4
takes `kiro chat --no-interactive --trust-all-tools /speckit-plan`,
warns about the unknown options and exits 0 without running anything,
so a dispatch fallback to `kiro` would mark steps completed. Look for
`kiro-cli` only, in the tool check and in the devcontainer.
SPECKIT_INTEGRATION_KIRO_CLI_EXECUTABLE still opts in to another binary.
Refs github#4797
Assisted-by: Claude Code (model: claude-opus-5-5, autonomous)
_archive() stamped workflow.yml with the current time, which zip stores
at 2-second resolution. The tests hash one build for the catalog and
serve another as the download, so a run that crossed an even second
between the two failed the integrity check (windows-latest on Python
3.13 in github#4338 and on 3.14 in github#4798).
Assisted-by: Claude Code (model: claude-opus-5-5, autonomous)
Addressed Copilot's "previously missed" finding on f53b21f in ec15335, but from the other side than it suggested. A kiro fallback in dispatch would make Kiro workflow steps pass without running anything.
Why not fall back to kiro
Kiro's CLI reference (Kiro Command Router section): "By default, the kiro command launches Kiro IDE." kiro opens the CLI only after kiro-cli integrations install kiro-command-router and kiro set-default cli, while "kiro-cli - Always launches CLI". The router is installed by kiro-cli itself, so a fallback that fires only when kiro-cli is missing would reach the IDE launcher.
Kiro CLI 2.26.0's Linux package and its installer create kiro-cli, kiro-cli-chat and kiro-cli-term, and no kiro.
Kiro IDE 1.2.4 (Linux tarball, only its kiro on PATH, offline):
$ kiro --version
1.2.4
22de6f99b82d81a5eb027ac417caa5e395f651a4
x64
$ kiro chat --no-interactive --trust-all-tools /speckit-plan
Warning: 'interactive' is not in the list of known options for subcommand 'chat'
Warning: 'trust-all-tools' is not in the list of known options for subcommand 'chat'
exit=0 elapsed=1.14s
kiro chat opens a chat session in the editor (per its --help), and the launcher exits 0, so the step would be marked completed.
What changed
The mismatch Copilot found was real, so I fixed it on the detection side. check_tool() in src/specify_cli/_utils.py no longer accepts kiro for kiro-cli, so specify check and specify init now agree with both workflow preflights. The kiro alternative came from feat: add kiro-cli and AGENT_CONFIG consistency coverage #1690 ("Kiro currently supports both executable names"), which Kiro's docs don't bear out. An IDE-only setup gets nothing from this integration anyway: Kiro IDE builds its slash commands from steering files, agents and skills, not from .kiro/prompts.
.devcontainer/post-create.sh checks for kiro-cli only, for the same reason.
SPECKIT_INTEGRATION_KIRO_CLI_EXECUTABLE=kiro still works as an explicit opt-in for anyone who routes kiro to the CLI.
Docs: one sentence in the Kiro row.
Tests:test_steps_do_not_dispatch_the_kiro_ide_launcher[command|prompt] puts only kiro on PATH. check_tool reports Kiro CLI missing, both steps fail without running anything, and with the executable override both dispatch kiro chat --no-interactive --trust-all-tools /speckit-plan. test_kiro_fallback is now test_kiro_ide_launcher_is_not_kiro_cli. With src/ reverted, all 3 fail. They pass now.
Windows failure on f53b21f:pytest (windows-latest, 3.14) failed test_unqualified_add_still_uses_current_and_invalid_version_scope with "Integrity check failed for 'history-wf'". That's a flake in the test helper from #4788, not this change. _archive() stamps workflow.yml with the current time, which zip stores at 2-second resolution, and the tests hash one build for the catalog and serve another as the download. #4338 hit the same failure on windows-latest 3.13 today. With zipfile's clock advancing between the two builds, 2 tests in test_catalog_versions.py fail every time. ed42004 pins the entry time, and with that fix they pass under the same clock. It's a separate commit, so it's easy to drop if you'd rather fix it elsewhere.
Full suite: 8779 passed, 251 skipped. ruff check src tests (0.15.0), shellcheck --severity=error and markdownlint are clean. The description now covers detection and the test fix.
Drafted on behalf of @kartsan03 by Claude Code (model: claude-opus-5-5, autonomous). The agent checked Kiro's docs and packages, ran Kiro IDE 1.2.4 locally, made both changes and wrote this comment.
When Kiro is upgraded as a secondary (non-active) integration, this block is skipped, so dotted extension prompts are not retired. A later integration use/switch re-registers the hyphenated files but never calls this helper, and the new manifest no longer reports a rename on subsequent upgrades; the old speckit.*.md extension files therefore remain indefinitely. Please run replacement-verified retirement when the integration is next activated (or persist a pending rename for that activation path), and cover the secondary-upgrade → use/switch flow.
…stration
Upgrading Kiro while another integration is active skips extension
registration (github#2948), and the new manifest no longer shows a rename, so
the dotted extension prompts stayed for good once `use` or `switch`
activated it. An extension disabled during the upgrade ended up the same
way.
Kiro now declares its dotted prompts as legacy flat command files, so
ExtensionManager._retire_legacy_flat_extension_commands(), which already
retires Qoder's old commands once their skills are written, removes a
dotted prompt once registration writes its hyphenated replacement. It
runs on every registration pass, uses the registrar's output path rather
than assuming a SKILL.md, and skips a name that is its own replacement.
This replaces the upgrade-only _retire_renamed_command_files().
Assisted-by: Claude Code (model: claude-opus-5-5, autonomous)
Addressed Copilot's "previously missed" finding on ed42004 in 234bbd2. I reproduced it on a project made by the released 1.0.13 with the git extension, then installed Claude and made it the default, so Kiro was secondary. On ed42004, specify integration upgrade kiro-cli renamed the 10 core prompts but left the 5 speckit.git.*.md prompts, because upgrade re-registers extensions only for the active integration (#2948). integration use kiro-cli or integration switch kiro-cli then wrote the 5 speckit-git-*.md prompts beside them. A later upgrade no longer saw a rename, so the dotted ones stayed for good. An extension that was disabled during the upgrade and enabled later ended up the same way.
What changed
Kiro now declares its dotted prompts as legacy flat command files (legacy_flat_command_dir = ".kiro/prompts"). ExtensionManager._retire_legacy_flat_extension_commands() retires them, the same step Qoder's skills migration ([bug-fix] Fix qodercli-skills-migration: migrate QodercliIntegration to SkillsIntegration #4205) uses: after a registration pass writes an extension command's new file, it removes the old one. It runs on every pass (use, switch, and upgrade of the active integration), so it no longer depends on the upgrade detecting the rename. It only handles names that pass just wrote and whose replacement exists, so an extension that can't be re-registered keeps its old prompt and its registry entry.
That step assumed the replacement is a SKILL.md. It now uses the registrar's output path, and it skips a name that is its own replacement. In Kiro a dot-free alias such as speckit-git-c is its own replacement, and without that check its new prompt would be deleted.
The upgrade-only _retire_renamed_command_files() from cc91560 is removed, so integrations/_helpers.py matches main again. Rename detection now feeds only the preset guard, which also refuses the rename when Kiro is upgraded as a secondary integration.
test_activating_kiro_after_secondary_upgrade_retires_dotted_prompts (use and switch) and test_enabling_extension_after_kiro_rename_retires_its_dotted_prompts fail on ed42004 (the dotted git prompts remain) and pass now.
test_kiro_prompt_named_without_dots_is_not_retired fails when the same-path check is removed.
The existing Kiro migration tests and the Qoder test pass unchanged.
Real runs (offline, project made by 1.0.13 as above):
Kiro secondary: upgrade kiro-cli, then use kiro-cli or switch kiro-cli
15 speckit-*.md plus 5 speckit.git.*.md, still there after another upgrade
15 speckit-*.md, no dotted prompts
Kiro active, git disabled during upgrade, then extension enable git and upgrade
5 speckit.git.*.md remain
15 speckit-*.md, no dotted prompts
Failure path: with the git extension's extension.yml corrupted before use kiro-cli, its 5 dotted prompts stay and the registry still tracks them.
Presets: with lean registered for Kiro on 1.0.13 and Claude as the default, integration upgrade kiro-cli --force is refused before any file changes. preset remove lean, the upgrade, use kiro-cli and preset add lean then leave 15 speckit-*.md prompts with lean's content.
Full suite: 9046 passed, 250 skipped. ruff check src tests (0.15.0) is clean. The Migration line and the test list in the description are updated.
Drafted on behalf of @kartsan03 by Claude Code (model: claude-opus-5-5, autonomous). The agent reproduced the finding, made the change, ran the checks above and wrote this comment.
This branch has not been deployed
No deployments
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
triage-can-waitVerdict: valid and in-scope but deprioritized; held behind the evidence gate
3 participants
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Fixes #4797, both the dispatch and the prompt names. Workflow
commandandpromptsteps withintegration: kiro-clirankiro-cli -p "<prompt>", inherited fromMarkdownIntegration.build_exec_args(). Kiro CLI rejects that at argument parsing, so every such step failed:KiroCliIntegration.build_exec_args()now buildskiro-cli chat --no-interactive --trust-all-tools [--model M] [--output-format stream-json] <prompt>, followingkiro-cli chat --helpon Kiro CLI 2.26.0:chat --no-interactiveruns one prompt headless, with the prompt as the positional input.--trust-all-tools: headless mode can't ask for tool approval, so without it every write is denied (tool permission approval is not supported in non-interactive mode. Use --trust-all-tools to auto-approve.) and the run still exits 0. Same role as Copilot's--yoloand Cursor's--force.jsonoutput format, sooutput_json=Truemaps to--output-format stream-json(JSON Lines), like Amp's--stream-json.SPECKIT_INTEGRATION_KIRO_CLI_EXTRA_ARGSgo after thechatflags and before the prompt.Kiro CLI also doesn't run dotted slash names:
/speckit.constitutionis rejected as "not a built-in Kiro CLI slash command", while/speckit-constitutionruns.kiro/prompts/speckit-constitution.md. So the Kiro integration now follows Junie (#4073) and Cline:.kiro/prompts/speckit-<command>.md, and dispatch sends/speckit-<command>(format_kiro_command_name,command_filename,build_command_invocation).invoke_separator = "-", so shared templates and scripts render/speckit-plan.format_nameinregistrar_configgives extension and preset prompts the same names, e.g.speckit-git-commit.md.specify integration upgrade kiro-clistale-removes the old corespeckit.*.mdprompts through the manifest. If one was modified, the upgrade stops and lists it, and the file stays until--force. Enabled extension prompts are tracked in the extension registry, not the manifest. Kiro declares the dotted names as its legacy flat command files, so every registration pass (use,switch, and upgrade of the active integration) writes an extension's hyphenated prompts and then removes each dotted one whose replacement exists. That is theExtensionManager._retire_legacy_flat_extension_commands()step Qoder's skills migration ([bug-fix] Fix qodercli-skills-migration: migrate QodercliIntegration to SkillsIntegration #4205) already uses. It also covers Kiro upgraded while another integration is active, onceuseorswitchactivates it, and an extension that was disabled during the upgrade. One that can't be re-registered keeps its old prompt and its registry entry. While presets have commands registered for Kiro, upgrade refuses the rename before changing files, like the Kilo command-root and command↔skills layout guards, and says to remove the preset, upgrade and add it back.specify checkandspecify initalso accepted a barekiroas Kiro CLI, while both workflow preflights look forkiro-cli. A barekirolaunches Kiro IDE by default, and Kiro's command router sends it to the CLI only afterkiro-cliinstalls the router. Kiro IDE 1.2.4 exits 0 on the headless argv without running anything. So the check now looks forkiro-clionly, like dispatch, and so does the devcontainer.SPECKIT_INTEGRATION_KIRO_CLI_EXECUTABLEstill overrides the executable.docs/reference/integrations.md: the Kiro row says prompts are hyphenated and why, and that Spec Kit looks forkiro-clionly.Headless Kiro still drops any text after
/speckit-<name>, so a step'sinput.argsdoesn't reach the model. That's the Kiro limitation the existing prose fallback already covers (#1926).Reproduction with the workflow from #4797, Kiro CLI 2.26.0, logged in:
main: fails with the error above./speckit.constitution, and the model then finds and reads the prompt file itself./speckit-constitutiondirectly. The model's first tool calls are the prompt's own steps (the hook check,resolve-template.sh),.specify/memory/constitution.mdis written, and the workflow ends withStatus: completed.mainwith the git extension had 15speckit.*.mdprompts.specify integration upgrade kiro-clion this branch printed "Removed 10 stale file(s) from previous install" (the core prompts) and left 15speckit-*.mdprompts. With the git extension's manifest corrupted, its 5 dotted prompts stayed and the registry still tracked them. With a preset overridingspeckit.planalso installed, the upgrade stopped before changing anything;preset remove, the upgrade andpreset addthen left 15speckit-*.mdprompts with the override inspeckit-plan.md. With Kiro secondary (a 1.0.13 project where Claude was then made the default), the upgrade renamed the core prompts, andintegration use kiro-cliorintegration switch kiro-clithen left 15speckit-*.mdprompts and no dotted ones.Testing
Tested locally with
uv run specify --helpRan existing tests with
uv sync && uv run pytestTested with a sample project (if applicable)
New tests in
tests/integrations/test_integration_kiro_cli.py: the headlesschatargv,--modelplusstream-json, and extra-args placement; the name formatter, filenames and invocation; aCommandStepdispatch test with the exact Kiro argv; the hook note and handoffs; and hyphenated overrides of the base inventory tests. Intests/specify_cli/integrations/test_command_upgrade.py,test_upgrade_replaces_dotted_kiro_prompts,test_upgrade_refuses_kiro_prompt_rename_while_presets_are_installed,test_upgrade_keeps_dotted_kiro_prompts_when_reregistration_fails,test_activating_kiro_after_secondary_upgrade_retires_dotted_prompts(useandswitch),test_enabling_extension_after_kiro_rename_retires_its_dotted_promptsandtest_kiro_prompt_named_without_dots_is_not_retiredcover the migration, andtest_command_file_names_changed_needs_a_renamecovers rename detection. The new tests fail onmain, excepttest_kiro_prompt_named_without_dots_is_not_retired, which guards the new retirement step: it fails if that step's same-path check is removed.test_steps_do_not_dispatch_the_kiro_ide_launcher(command and prompt steps) andtest_kiro_ide_launcher_is_not_kiro_clicover detection.tests/specify_cli/workflows/test_catalog_versions.py:_archive()now pins its zip entry time. The tests hashed one build for the catalog and served another as the download, so a 2-second clock tick between them failed the integrity check on Windows CI. The helper came from feat(workflows): select exact workflow catalog releases #4788; details are in the PR conversation.Full suite: 9046 passed, 250 skipped (Linux, Python 3.13), after merging
main.ruff check src tests(0.15.0): clean.Sample project:
specify init --integration kiro-cli, then the workflow above onmainand on this branch.AI Disclosure
AI disclosure: Claude Code (Claude Opus 5.5, autonomous agent mode) was used to investigate Kiro CLI and Kiro IDE, run the reproductions, and write the code changes, the tests and this description.