Skip to content

[BUG] Kiro hook integrator emits the legacy pre-1.0 hook schema #2071

Description

@TibRib

Describe the bug

apm install deploys hooks to the kiro target in the deprecated pre-1.0 Kiro hook schema
({ "when": {...}, "then": { "type": "runCommand" } }).

Kiro IDE 1.0 / CLI v3 replaced that schema with a versioned, standalone format:

{
  "version": "v1",
  "hooks": [
    { "name": "...", "trigger": "PreToolUse", "matcher": "...", "action": { "type": "command", "command": "..." } }
  ]
}

(See Kiro CLI v3 hooks and the
Kiro IDE 1.0 changelog.)

As a result, hooks that APM deploys to .kiro/hooks/ are treated by Kiro 1.0 as legacy
(migrate-only via the Agent Hooks panel), not as the current runtime format. On top of the
format mismatch there are three functional gaps (details below): no agent (prompt) action,
matcher/trigger naming differences, and several 1.0 triggers that don't map at all.

Context: the Kiro target was added relatively recently (#702, milestone 0.20.0), which predates
Kiro's 1.0 hook-schema change — so the integrator was written against the older shape.

To Reproduce

  1. Create a minimal package:

    apm.yml

    name: hook-repro
    version: 0.0.1
    targets: [kiro, claude, copilot]
    includes: auto

    .apm/instructions/dummy.instructions.md

    ---
    description: dummy
    applyTo: "**/*.md"
    ---
    - placeholder

    .apm/hooks/check.json (documented Claude shape, type: command)

    {
      "hooks": {
        "PreToolUse": [
          { "matcher": "fs_write|str_replace",
            "hooks": [ { "type": "command", "command": "echo pre-write-check", "timeout": 10 } ] }
        ]
      }
    }
  2. Run apm install.

  3. Inspect .kiro/hooks/hook-repro-check-pretooluse-1.json.

Actual output (APM 0.24.0)

{
  "name": "hook-repro preToolUse 1",
  "version": "1.0.0",
  "when": {
    "type": "preToolUse",
    "patterns": ["fs_write|str_replace"]
  },
  "then": {
    "type": "runCommand",
    "command": "echo pre-write-check"
  }
}

This is the pre-1.0 schema (when/then/runCommand, camelCase when.type, patterns).

Expected behavior
(Kiro 1.0 / v3 schema)

{
  "version": "v1",
  "hooks": [
    {
      "name": "hook-repro pre-write-check",
      "trigger": "PreToolUse",
      "matcher": "fs_write|str_replace",
      "action": { "type": "command", "command": "echo pre-write-check", "timeout": 10 }
    }
  ]
}

Differences: top-level version: "v1" + a hooks array; trigger (PascalCase) instead of
when.type (camelCase); matcher instead of patterns; action instead of then; and
action.type: "command" instead of then.type: "runCommand".

Additional gaps found while testing

Beyond the format, three things block real usage on Kiro 1.0:

  1. No agent action. APM only maps type: command -> runCommand. Kiro 1.0 supports
    action.type: "agent" (append a prompt to the model context, no subprocess), which is the
    right primitive for lightweight steering/guardrails. There is no way to author or emit an
    agent-action hook through APM today.

  2. Trigger mapping is stale / partial. Observed mappings on the kiro target:

    Authored (Claude shape) APM emits (when.type) Kiro 1.0 expects (trigger)
    PreToolUse preToolUse (+ patterns) PreToolUse (+ matcher)
    PostToolUse postToolUse (+ patterns) PostToolUse (+ matcher)
    Stop agentStop Stop
    UserPromptSubmit promptSubmit UserPromptSubmit
    SessionStart (passed through, casing-mismatch warning) SessionStart
    SubagentStop (passed through, warning) (n/a — use Stop)
    PreCompact (passed through, warning) (n/a)

    Kiro 1.0 also adds triggers with no APM path: PreTaskExec, PostTaskExec,
    PostFileCreate, PostFileSave, PostFileDelete.

  3. Native v1 files are rejected, so authors can't work around the above by shipping the
    correct format directly. A .apm/hooks/*.json in the v1 shape fails install with:

    WARNING apm_cli.integration.hook_integrator Skipping malformed hook file ...: 'hooks' must be a dict, got list
    

    (Kiro 1.0's hooks is an array; APM's validator requires an object.)

Impact

Packages that target kiro cannot ship hooks that run on Kiro 1.0 without a manual migration
step per consumer. The only clean cross-target workaround today is to express hook intent as
instructions (which compile correctly to .kiro/steering/), but that loses the
command/exit-code enforcement and event-fired triggering that hooks provide — and it can't
represent a genuine blocking validation gate (e.g. a pre-task branch/worktree check).

Suggested fix / acceptance criteria

  • Emit the Kiro 1.0 v1 schema for the kiro target: { "version": "v1", "hooks": [ { "name", "trigger", "matcher", "action": { "type", "command"|"prompt", "timeout"? } } ] }.
  • Map events to Kiro 1.0 PascalCase trigger names and use matcher (not patterns).
  • Support an agent/prompt action for Kiro (either a first-class portable action or a
    documented Kiro-scoped passthrough), so prompt-based guardrails can be deployed rather than
    only shell commands.
  • Optionally: accept native Kiro v1 hook files in .apm/hooks/ (array-shaped hooks) so
    authors can target Kiro directly when a portable expression isn't possible.

Environment

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/distributionInstallers (curl/PowerShell/Brew/Scoop), self-update, devcontainer, codespaces.status/acceptedDirection approved, safe to start work.status/triagedInitial agentic triage complete; pending maintainer ratification (silence = approval).theme/portabilityOne manifest, every target. Multi-target deploy, marketplace, packaging, install.type/bugSomething does not work as documented.

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    Status
    In Progress

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions