PreToolUse hooks route through kei-capability check when orchestrator
registers a capability via KEI_CAPABILITY_NAME env var on agent spawn.
hooks/agent-capability-check.sh (22 LOC):
- Pass-through (exit 0) when KEI_CAPABILITY_NAME unset — no-op by default
- Fail-open (exit 0) when kei-capability binary missing — kit convention
- Sources _lib/gate.sh for KEI_DISABLED_HOOKS / KEI_HOOK_PROFILE respect
- exec kei-capability check "$CAP_NAME" when active
hooks/agent-capability-verify.sh (24 LOC):
- Orchestrator-driven, NOT a Claude Code native hook
- Carries env: AGENT_ID, TASK_TOML, WORKTREE_PATH, MAIN_REPO, RUN_MODE
- exec kei-capability verify "$CAP_NAME"
Registered in hooks/hooks.json + settings-snippet.json under both
PreToolUse:Bash and PreToolUse:Edit|Write matchers. Internal NotApplicable
returns exit 0 so non-matching tool calls cost nothing.
install.sh unchanged — hooks/*.sh glob picks up both new files.
tests/hook_wiring_integration.sh (64 LOC) — 3 contract assertions:
(1) pass-through on unset KEI_CAPABILITY_NAME
(2) deny+exit 2 on git-op pattern
(3) allow+exit 0 on cargo-check pattern
Multi-capability routing (for phase 5): KEI_CAPABILITY_NAME currently
holds ONE name. When a role requires N capabilities, orchestrator will
either iterate or kei-capability gains a compose subcommand. Design
note left for phase 5.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Makes KeiSeiKit installable both as classic kit AND as an
Anthropic Claude Code plugin.
.claude-plugin/plugin.json — plugin manifest (name, version,
description, author OBJECT per schema, repository, license)
.claude-plugin/marketplace.json — own marketplace declaration
(owner OBJECT per schema, plugins[].source OBJECT)
.claude-plugin/mcp-template.json — template for .mcp.json (actual
.mcp.json write is blocked by hook; user copies template manually)
PLUGIN.md — dual-install docs (plugin vs classic)
hooks/hooks.json — uses ${CLAUDE_PLUGIN_ROOT} (per Anthropic
schema, NOT ${PLUGIN_ROOT}); wraps hooks under top-level
"hooks": {...} key
Schema corrections caught during agent validation:
- marketplace.json owner MUST be object (not string)
- hooks.json requires "hooks": {...} top-level wrapper
- env var is ${CLAUDE_PLUGIN_ROOT} not ${PLUGIN_ROOT}
Companion edits in install-split bundle: install/lib-args.sh
gains an 8-line plugin-first banner in print_help() directing
users toward the plugin install path as recommended default.
Dual-install strategy: users can pick
- `claude plugin marketplace add <url>` then install — latest
and iteration-friendly (this PR enables it)
- classic ./install.sh — legacy kit path, full 37-primitive
control
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>