docs/marker-file.md says the hook scripts forward `briefing` and
`briefing_budget` on the handoff lookup when the marker opts in. Only the
Kiro CLI and Kimi Code scripts did; the session-start scripts of Claude
Code, Codex, Cursor, Gemini CLI, OpenCode, Command Code, Devin and
Antigravity CLI built the GET from ai_memory_marker_qs alone, and the
PowerShell library added the briefing query only for Kimi and Grok. A
script install with `inject_on_session_start = true` therefore got the
handoff without the brief, while the native `ai-memory hook` command
sent both.
Each of those scripts now appends ai_memory_briefing_qs to its handoff
GET, and Invoke-AiMemoryHook adds Get-AiMemoryBriefingQuery to every
session-start fetch outside Kiro's once-per-session gate. No gate is
added: the native session-start path sends the keys on every start.
tests/hooks/test_lib.sh discovers every bundle whose session-start.sh
fetches the handoff and asserts the keys are present when the marker
opts in and absent when it opts out, plus a static check on the
PowerShell library.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019KhB86k7H61CePGZdxtwyz
After research against each agent's official docs / repo, completed
the install-hooks --apply matrix:
agent | shape | config file
------------|--------------------|---------------------------------
claude-code | nested JSON | ~/.claude/settings.json
codex | nested JSON | ~/.codex/hooks.json
cursor | flat JSON | ~/.cursor/hooks.json
gemini-cli | nested JSON | ~/.gemini/settings.json
open-code | TypeScript plugin | ~/.config/opencode/plugins/ai-memory.ts
openclaw | (no lifecycle hooks supported — only HTTP webhooks)
## Architecture
Refactored `render_shared.rs` into a data-driven design:
pub(crate) enum HookShape { Nested, Flat }
pub(crate) struct HookProfile {
pub events: &'static [(&'static str, &'static str)],
pub shape: HookShape,
}
pub(crate) const {CLAUDE_CODE,CODEX,CURSOR,GEMINI}_PROFILE: HookProfile = …;
pub(crate) fn build_profile_payload(&HookProfile, …) -> Value;
Adding a new JSON-config agent now needs only:
- hook script dir under hooks/<agent-name>/
- an event list constant + a HookProfile
- a thin apply_to_<agent>_settings() function pointing at the right
config-file path
No control-flow branching for new agents — the shape variant in the
profile is enough.
## Per-agent specifics discovered
- Cursor: FLAT shape (no inner `hooks` array), camelCase events,
requires top-level `"version": 1` key. ai-memory's
`beforeSubmitPrompt` is Cursor's closest match to
UserPromptSubmit. (Source: https://cursor.com/docs/agent/hooks)
- Gemini CLI: nested shape (same as Claude Code), PascalCase
events, DIFFERENT vocabulary:
BeforeTool / AfterTool (≈ PreToolUse / PostToolUse)
PreCompress (≈ PreCompact)
No UserPromptSubmit / Stop equivalents — skipped.
(Source: https://geminicli.com/docs/hooks/reference)
- Codex: nested shape, PascalCase, NO SessionEnd (uses Stop for
both turn-end and session-end). (Source:
openai/codex/codex-rs/config/src/hooks_tests.rs)
- OpenCode: hooks via TypeScript plugin, NOT JSON. Event names
use dot.case (`session.created`, `tool.execute.before`).
apply path generates a `.ts` file under
~/.config/opencode/plugins/ that uses Bun's shell ($) to
invoke ai-memory's shell hooks. (Source:
https://opencode.ai/docs/plugins/)
- OpenClaw: confirmed via https://docs.openclaw.ai/gateway/configuration
that it has NO lifecycle hooks — only HTTP webhook ingress.
apply path prints the explanation + workaround suggestion
rather than silently doing nothing.
## hooks/ directory
Two new agent dirs: hooks/cursor/, hooks/gemini-cli/ (copied from
claude-code/ with `agent=NAME` substituted in the curl query
strings). All hook scripts remain functionally identical — only
the agent-label tag in the URL differs.
## Tests
- `cursor_payload_uses_flat_shape` — asserts no inner `hooks`
array, camelCase events, command at outer level.
- `gemini_payload_uses_nested_shape_with_gemini_event_names` —
asserts CC's nested shape + Gemini-specific event vocab,
forbids CC-only events from leaking through.
- Existing claude_code_payload_* + codex tests continue to pass
against the shared `build_profile_payload` path.
All workspace tests green; clippy clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>