Add Kiro CLI as third coding agent with hooks and CLI skill#245
Add Kiro CLI as third coding agent with hooks and CLI skill#245sbertix merged 8 commits intosupabitapp:mainfrom
Conversation
f285a2c to
30cce77
Compare
- KiroHookSettings: progress (userPromptSubmit/stop) and notification (stop) payloads in Kiro's flat format - KiroHookSettingsFileInstaller: thin wrapper for Kiro's flat hook JSON format - KiroSettingsInstaller: targets ~/.kiro/agents/kiro_default.json, creates default agent config when absent - KiroSettingsClient: TCA dependency for install/uninstall/check - AgentHookSlot: add .kiroProgress/.kiroNotifications - SkillAgent: add .kiro - SettingsFeature: wire Kiro hook checks, install/uninstall, skill install - CLISkillContent: add Kiro skill (SKILL.md with frontmatter) - AgentHookPayload: decode assistant_response for Kiro stop events - DeveloperSettingsView: add Kiro section with progress, notifications, CLI skill rows - kiro-mark asset from kiro.dev/icon.svg
30cce77 to
0b831ce
Compare
# Conflicts: # SupacodeSettingsFeature/Reducer/SettingsFeature.swift # SupacodeSettingsShared/BusinessLogic/KiroHookSettings.swift # SupacodeSettingsShared/BusinessLogic/KiroHookSettingsFileInstaller.swift # SupacodeSettingsShared/BusinessLogic/KiroSettingsInstaller.swift # SupacodeSettingsShared/Models/SkillAgent.swift
sbertix
left a comment
There was a problem hiding this comment.
Thanks 💪 just a few changes required, though.
- Move KiroSettingsClient to SupacodeSettingsShared/Clients/CodingAgents/ with public access
- Collapse Kiro section in DeveloperSettingsView (isExpanded: false by default)
- Update KiroSettingsInstaller comment with Kiro version (1.x, 2026-04)
- Remove KiroHookPayloadSupport; reuse AgentHookPayloadSupport.extractHookGroups
- Add tests: KiroSettingsInstallerTests, KiroHookSettingsFileInstallerTests
- Extend SettingsFeatureAgentHookTests with kiro hook install/uninstall cases
- Update taskChecksAllFourHookSlotsOnStartup → All Six (includes kiro slots)
- Update receiveStartupHookChecks to assert kiro{Progress,Notifications,Skill}State
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
# Conflicts: # SupacodeSettingsFeature/Reducer/SettingsFeature.swift
sbertix
left a comment
There was a problem hiding this comment.
Nice work 💪 A few testing gaps + small things inline 🙇♂️
…, fix skill:// URIs - Add decode test for Kiro's assistant_response fallback field - Add precedence test (message > lastAssistantMessage > assistantResponse) - Assert full JSON config contents in KiroSettingsInstallerTests (name, tools, useLegacyMcpJson, resources, hooks) instead of just fileExists - Fix firstWrite/secondWrite comparison in does-not-overwrite test - Fix skill:// URIs to use ~/ prefix for home-directory resolution (skill://.kiro/... is project-relative, skill://~/.kiro/... is global)
- Log warning when all notification body fields are nil so field renames surface in make log-stream instead of silently delivering empty notifications - Add comment explaining the body fallback chain across agents - Add uninstallRemovesOnlyMatchingCommands test for Kiro's flat hook format, matching the existing Claude/Codex grouped-format coverage
Feedback addressed (2 commits)
Nil-body warning log: Added Full config assertions in
|
- KiroSettingsInstaller: gate `ensureDefaultAgentConfig` on a version probe (`kiro --version` via login shell) so future Kiro releases cannot be silently overridden by our hardcoded defaults; 5s watchdog + `process.terminate()` on hang; concurrent stdout/stderr drain to avoid pipe-buffer deadlock; parse stdout before stderr; log non-UTF8 output; differentiate 127 vs. other non-zero exits; `isSupportedVersion` compares the first dot-delimited component so `10.x` is rejected. - KiroHookSettingsFileInstaller: throw `invalidHooksObject` when existing `"hooks"` is non-nil but not a JSON object (previously silently overwrote user data); drop WHAT-narration comment. - KiroHookSettings: extract `defaultTimeoutMs`; `KiroHookEntry` init guards empty command / non-positive timeout via `assertionFailure` + `max(1, _)`. - AgentHookSocketServer: collapse `message` / `last_assistant_message` / `assistant_response` into a single `body` via custom `init(from:)`; each field decoded through a tolerant helper that logs on type mismatch; skip empty strings so an empty Claude `message` still yields to Codex/Kiro fallbacks. - CLISkillContent: document `-c` script UUID flag + `worktree run/stop/script list` in `kiroSkillMd`. - Tests: error paths (malformed JSON, array root, non-object hooks, non-array event, legacy pruning), version-gate matrix (missing binary, command-throw, unsupported version, unparseable output, 10.x rejection, stderr-banner precedence, file-exists short-circuit), and body-decoder edge cases (null, empty string, type mismatch, all-precedence chains).
Adds Kiro CLI as a third coding agent alongside Claude Code and Codex in Settings → Coding Agents.
Changes
New files
KiroHookSettings.swift— Hook entry struct (command+timeout_ms) and payloads for progress (userPromptSubmit→ busy on,stop→ busy off) and notification (stop→ notify) eventsKiroHookSettingsFileInstaller.swift— File installer for Kiro's flat hook format: install, uninstall, and containsMatchingHooksKiroSettingsInstaller.swift— Targets~/.kiro/agents/kiro_default.json, creates default agent config when file is absentKiroSettingsClient.swift— TCA dependency client wrapping the installerModified files
AgentHooksInstallState.swift— Added.kiroProgress,.kiroNotificationstoAgentHookSlotSkillAgent.swift— Added.kirocase withconfigDirectoryName: ".kiro"SettingsFeature.swift— Wired Kiro state, dependency, check/install/uninstall effectsDeveloperSettingsView.swift— Added Kiro section with progress, notifications, and CLI skill rowsCLISkillContent.swift— AddedkiroSkillMd(SKILL.md with YAML frontmatter)CLISkillInstaller.swift— Added.kirocaseAgentHookSocketServer.swift— Addedassistant_responsedecoding (see below)Assets
kiro-markimage set (SVG from kiro.dev)Design decisions
Why
kiro_default.jsonKiro has no global hooks file — hooks live per-agent in
~/.kiro/agents/<name>.json. Thekiro_defaultagent is what runs in Supacode terminals, so that's the only file we target. The built-inkiro_defaulthas no file on disk; creatingkiro_default.jsonoverrides it entirely, so the installer writes a complete config (name, tools, resources, hooks) — not just hooks.Why a separate
KiroHookSettingsFileInstallerKiro's hook format is flat:
{ "hooks": { "stop": [{ "command": "...", "timeout_ms": 10000 }] } }Claude/Codex use a grouped format:
{ "hooks": { "Stop": [{ "hooks": [{ "type": "command", "command": "...", "timeout": 10 }] }] } }Rather than generalizing
AgentHookSettingsFileInstallerand risking regressions in Claude/Codex, this isolates the format difference in a separate installer.Socket server:
assistant_responsefieldKiro sends the agent's final response in
assistant_responserather thanmessage(Claude) orlast_assistant_message(Codex). Extended the notification body fallback chain:Without this, Kiro notifications would have empty bodies.
Uninstall safety
Uninstall is a no-op if
kiro_default.jsondoesn't exist, preventing creation of a broken config file that would override Kiro's built-in agent with missing fields.Verification
make checkpasses (swift-format + swiftlint)make testpasses (716 tests, 57 suites)