Skip to content

[Bug]: PR #2713's EXECUTE_COMMAND directive emits correctly but is never executed in pure-Claude-Code invocations #2730

@thlandgraf

Description

@thlandgraf

Follow-up to #2688

PR #2713 (shipped in v0.8.15) restructured the after_/before_ hook dispatch in core command SKILLs with a ## Mandatory Post-Execution Hooks H2 + directive language + ## Done When checklist. Empirically, the emission half works: the agent now reliably emits

EXECUTE_COMMAND: my-extension.my-hook-target

in its response stream during /speckit-plan invocations on v0.8.15+.

The execution half does not work in pure Claude Code (no specify-CLI wrapper). Nothing in Claude Code's slash-command runtime watches the agent's stdout for EXECUTE_COMMAND: lines and dispatches them. The agent emits the directive, waits ~6 seconds, and returns control without ever invoking the underlying hook script. The result is functionally identical to #2688's original failure mode (the registered hook does not run) — just with a more visible symptom (the directive shows in the agent's output, so the user can SEE that something should have happened).

Reproduction

The repro is structural and doesn't depend on any particular extension. A minimal stub is enough:

  1. specify init . --integration claude on spec-kit ≥ 0.8.15.
  2. Hand-create a minimal local extension at .specify/extensions/repro/:
    • extension.yml:
      schema_version: "1.0"
      extension:
        id: repro
        name: "Hook Dispatch Repro"
        version: "0.0.1"
      requires:
        speckit_version: ">=0.8.15"
      provides:
        commands:
          - name: repro.before-plan
            file: commands/before-plan.md
            description: "Hook dispatch repro target."
      hooks:
        before_plan:
          command: repro.before-plan
          optional: false
          description: "Should run before /speckit-plan."
    • commands/before-plan.md: any content; the command's body never runs in this repro so the contents don't matter beyond being present.
  3. Run specify extension add --dev .specify/extensions/repro so the hook is registered in .specify/extensions.yml and a SKILL is generated under .claude/skills/repro-before-plan/.
  4. From inside Claude Code (NOT via specify run or any CLI wrapper), invoke /speckit-plan against any feature directory.
  5. Observe: the agent emits the H2 block with EXECUTE_COMMAND: repro.before-plan correctly, then stalls. The hook command body never runs. /speckit-plan then proceeds (or fails, depending on what the hook was supposed to do) as if no hook were registered.

Empirical log grep on a real session against a before_plan hook that's supposed to write a file:

grep -E 'EXECUTE_COMMAND|tool_dispatch_start tool=Bash.*<hook-script>' debug.log
# → emits EXECUTE_COMMAND line, zero matching Bash dispatches

Why this is distinct from #2104

#2104 asked for auto-run as a new feature and was closed because the workflow engine is the right answer for deterministic dispatch. This issue is narrower: the existing best-effort dispatch contract documented in PR #2713 doesn't actually dispatch in the most common Claude Code invocation pattern. I am not asking for auto-run; I am asking that the contract the SKILL describes actually completes in the documented happy path.

Hypothesis

The v0.8.15 SKILL implicitly assumes a platform-level stdout watcher that parses EXECUTE_COMMAND: lines. That watcher exists when specify is the orchestrator (auto_execute_hooks: true in .specify/extensions.yml is presumably the flag that activates it). It does not exist when the agent's slash command is invoked directly in Claude Code, which is the default invocation pattern for ~all extension users.

Proposed Resolution Paths (one of)

  1. Strengthen the SKILL contract. Add an explicit instruction immediately after the EXECUTE_COMMAND: directive emission: "You MUST then invoke the Bash tool with the underlying hook script path to actually execute the command. Emitting the directive alone is not sufficient — the directive is a ceremony signal, not a dispatcher." This is the lowest-cost fix and makes the contract self-contained.

  2. Document the prerequisite explicitly. Add a note to extensions/EXTENSION-API-REFERENCE.md that EXECUTE_COMMAND: directives require an orchestrator process (e.g. specify run) and will not be dispatched by pure Claude Code slash invocations. Extension authors can then advertise the dispatch limitation in their READMEs.

  3. Ship a Claude Code hook. Bundle a small settings.json hook (CLAUDE_CODE_HOOKS_DIR) that watches assistant message text for EXECUTE_COMMAND: <command> lines and dispatches them via the Bash tool — closing the gap at the platform level without depending on the agent doing two things.

(1) is the cleanest from an extension-author perspective. (3) is the most robust but adds non-trivial machinery.

Component

Specify CLI (commands, templates) / Extensions / Agent Integration

AI Agent (if applicable)

Claude Code (Claude Opus 4.7) — but pattern is structural; same failure mode is likely with Cursor / Codex / Gemini CLI when invoked directly without a spec-kit CLI orchestrator.

Use Cases

Every extension that registers an after_* or before_* hook with optional: false and expects it to fire during agent-direct slash invocations. Concrete classes:

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions