Skip to content

Add Kiro CLI as third coding agent with hooks and CLI skill#245

Merged
sbertix merged 8 commits intosupabitapp:mainfrom
b0x42:add-kiro-agent-hooks
Apr 20, 2026
Merged

Add Kiro CLI as third coding agent with hooks and CLI skill#245
sbertix merged 8 commits intosupabitapp:mainfrom
b0x42:add-kiro-agent-hooks

Conversation

@b0x42
Copy link
Copy Markdown
Contributor

@b0x42 b0x42 commented Apr 15, 2026

Adds Kiro CLI as a third coding agent alongside Claude Code and Codex in Settings → Coding Agents.

image

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) events
  • KiroHookSettingsFileInstaller.swift — File installer for Kiro's flat hook format: install, uninstall, and containsMatchingHooks
  • KiroSettingsInstaller.swift — Targets ~/.kiro/agents/kiro_default.json, creates default agent config when file is absent
  • KiroSettingsClient.swift — TCA dependency client wrapping the installer

Modified files

  • AgentHooksInstallState.swift — Added .kiroProgress, .kiroNotifications to AgentHookSlot
  • SkillAgent.swift — Added .kiro case with configDirectoryName: ".kiro"
  • SettingsFeature.swift — Wired Kiro state, dependency, check/install/uninstall effects
  • DeveloperSettingsView.swift — Added Kiro section with progress, notifications, and CLI skill rows
  • CLISkillContent.swift — Added kiroSkillMd (SKILL.md with YAML frontmatter)
  • CLISkillInstaller.swift — Added .kiro case
  • AgentHookSocketServer.swift — Added assistant_response decoding (see below)

Assets

  • kiro-mark image set (SVG from kiro.dev)

Design decisions

Why kiro_default.json

Kiro has no global hooks file — hooks live per-agent in ~/.kiro/agents/<name>.json. The kiro_default agent is what runs in Supacode terminals, so that's the only file we target. The built-in kiro_default has no file on disk; creating kiro_default.json overrides it entirely, so the installer writes a complete config (name, tools, resources, hooks) — not just hooks.

Why a separate KiroHookSettingsFileInstaller

Kiro'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 AgentHookSettingsFileInstaller and risking regressions in Claude/Codex, this isolates the format difference in a separate installer.

Socket server: assistant_response field

Kiro sends the agent's final response in assistant_response rather than message (Claude) or last_assistant_message (Codex). Extended the notification body fallback chain:

let body = payload.message ?? payload.lastAssistantMessage ?? payload.assistantResponse

Without this, Kiro notifications would have empty bodies.

Uninstall safety

Uninstall is a no-op if kiro_default.json doesn't exist, preventing creation of a broken config file that would override Kiro's built-in agent with missing fields.

Verification

  • make check passes (swift-format + swiftlint)
  • make test passes (716 tests, 57 suites)

@b0x42 b0x42 force-pushed the add-kiro-agent-hooks branch from f285a2c to 30cce77 Compare April 15, 2026 12:44
- 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
@b0x42 b0x42 force-pushed the add-kiro-agent-hooks branch from 30cce77 to 0b831ce Compare April 15, 2026 12:44
# Conflicts:
#	SupacodeSettingsFeature/Reducer/SettingsFeature.swift
#	SupacodeSettingsShared/BusinessLogic/KiroHookSettings.swift
#	SupacodeSettingsShared/BusinessLogic/KiroHookSettingsFileInstaller.swift
#	SupacodeSettingsShared/BusinessLogic/KiroSettingsInstaller.swift
#	SupacodeSettingsShared/Models/SkillAgent.swift
@b0x42 b0x42 marked this pull request as ready for review April 15, 2026 13:08
Copy link
Copy Markdown
Collaborator

@sbertix sbertix left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks 💪 just a few changes required, though.

Comment thread supacode/Clients/CodingAgents/KiroSettingsClient.swift Outdated
Comment thread SupacodeSettingsFeature/Reducer/SettingsFeature.swift
Comment thread SupacodeSettingsShared/BusinessLogic/KiroSettingsInstaller.swift
Comment thread supacode/Features/Settings/Views/DeveloperSettingsView.swift Outdated
Comment thread SupacodeSettingsShared/BusinessLogic/KiroHookSettings.swift Outdated
b0x42 and others added 2 commits April 15, 2026 21:58
- 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
@b0x42 b0x42 requested a review from sbertix April 17, 2026 11:51
Copy link
Copy Markdown
Collaborator

@sbertix sbertix left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work 💪 A few testing gaps + small things inline 🙇‍♂️

Comment thread supacode/Infrastructure/AgentHookSocketServer.swift Outdated
Comment thread supacode/Infrastructure/AgentHookSocketServer.swift Outdated
Comment thread SupacodeSettingsShared/BusinessLogic/KiroSettingsInstaller.swift
Comment thread SupacodeSettingsShared/BusinessLogic/KiroSettingsInstaller.swift
Comment thread supacodeTests/KiroHookSettingsFileInstallerTests.swift
Comment thread SupacodeSettingsShared/BusinessLogic/KiroHookSettingsFileInstaller.swift Outdated
Comment thread SupacodeSettingsShared/BusinessLogic/KiroHookSettings.swift
Comment thread supacodeTests/KiroSettingsInstallerTests.swift Outdated
b0x42 added 2 commits April 20, 2026 09:03
…, 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
@b0x42
Copy link
Copy Markdown
Contributor Author

b0x42 commented Apr 20, 2026

Feedback addressed (2 commits)

skill:// URI fix (bug): The skill:// scheme in kiro_default.json was using project-relative paths (skill://.kiro/skills/**/SKILL.md) but CLISkillInstaller writes skills to ~/.kiro/skills/ (home directory). Per Kiro docs, custom agents need skill://~/... for global skills. Fixed to skill://~/.kiro/skills/**/SKILL.md and skill://~/.kiro/steering/**/*.md. Without this, the SKILL.md row showed "installed" but Kiro never actually loaded it.

assistant_response decode tests: Added parsesNotificationWithAssistantResponseFallback (Kiro-only field) and messageFieldTakesPrecedenceOverAllFallbacks (all three fields present → message wins) to lock the fallback chain.

Nil-body warning log: Added SupaLogger.warning when all body fields (message, lastAssistantMessage, assistantResponse) are nil, plus a comment explaining which agent uses which field. If Kiro renames the field again, it now surfaces in make log-stream.

Full config assertions in KiroSettingsInstallerTests: Tests now decode the written JSON and assert name, tools: ["*"], useLegacyMcpJson, all 4 resources entries, and hooks — instead of just fileExists. Also fixed the firstWrite/secondWrite comparison (#expect(firstWrite == secondWrite) instead of _ = firstWrite // used).

uninstallRemovesOnlyMatchingCommands for flat format: Mirrors the existing Claude/Codex grouped-format test — installs managed hooks, manually adds a user hook (echo user-hook), uninstalls, asserts only the user hook remains.

@b0x42 b0x42 requested a review from sbertix April 20, 2026 08:11
- 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).
@sbertix sbertix enabled auto-merge (squash) April 20, 2026 20:24
Copy link
Copy Markdown
Collaborator

@sbertix sbertix left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you 💪

@sbertix sbertix merged commit 943f3fa into supabitapp:main Apr 20, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants