hooks_emit¶
Claude hooks emission for the copilot-vscode → claude bridge. Writes two artifacts under <project>/.claude/:
settings.agentteams.example.json— a sample hooks block the user merges into their ownsettings.json. agentteams never overwritessettings.jsonorsettings.local.json.hook-guard.sh— recursion-depth-bounded notification wrapper that logs to.claude/hook-notices/<date>.logand refuses re-entry beyondAGENTTEAMS_HOOK_MAX_DEPTH(default2). Bounds anyagent → write → hookcascade (see the security hardening guide).
Opt-in via --target-host-features bridge:copilot-vscode-to-claude:hooks.
Hook Mapping¶
Data-driven (canonical slug → event[, matcher]):
| Slug | Event | Matcher |
|---|---|---|
cleanup, agent-updater, code-hygiene, drift |
PostToolUse |
Write\|Edit |
security |
PreToolUse |
Bash\|Write\|Edit |
work-summarizer, post-production-auditor |
Stop |
(none) |
Default mode is notification (hook logs; user/orchestrator decides escalation), not automatic subagent invocation. Safer first cut; the user can edit settings.json to upgrade specific hooks to active invocation.
Public Surface¶
@dataclass
class HooksEmissionResult:
written: list[str]
skipped: list[str]
errors: list[str]
success: bool # property: len(errors) == 0
settings.json hooks block for active slugs. Only includes entries for slugs whose <slug>.agent.md exists in source_dir. Each entry runs bash .claude/hook-guard.sh <event> <slug>.
emit_hooks_artifacts(
*,
source_dir: Path,
output_root: Path,
dry_run: bool = False,
overwrite: bool = True,
) -> HooksEmissionResult
.claude/settings.agentteams.example.json and .claude/hook-guard.sh under output_root. The guard script is chmod-ed 0755 when possible. overwrite=True (default) matches --bridge-refresh semantics.
Recursion Guard Contract¶
hook-guard.sh reads AGENTTEAMS_HOOK_DEPTH from the environment (default 0) and compares it against AGENTTEAMS_HOOK_MAX_DEPTH (default 2). Every path exits 0 — the guard never fails a hook or blocks Claude from continuing; "refusal" means silently doing nothing, not signalling an error.
- At or beyond max depth: silent no-op. No log line is written and the depth is not incremented — this is the refused path.
- Below max depth: the guard writes a log line to
.claude/hook-notices/<YYYY-MM-DD>.log(event,slug, and the depth it fired at), then exportsAGENTTEAMS_HOOK_DEPTHincremented by one so any downstream tool invocation the hook triggers is inherited at the higher depth.
The log write happens only on the allowed path, immediately before that path's own exit 0 — the guard is silent exactly where it refuses, and logs exactly once where it lets the hook through.
See also¶
schedule_emit— sibling host-feature emitter (recurring/scheduleroutines).instructions_split— sibling host-feature emitter (cache-awareCLAUDE.md).