6.5 KiB
.claude/ -- Claude Code Configuration
This directory configures Claude Code for the AIPass project.
Related: DPLAN-0184 (Hook Migration), DPLAN-0053 (original hook architecture research).
How Hooks Work (Post-Migration)
All AIPass hooks run through a three-layer pipeline:
~/.claude/settings.json Provider settings (Claude Code reads these)
|
v
claude.py (bridge) Thin entry point -- normalizes stdin, calls engine
|
v
engine.py (dispatcher) Reads .aipass/hooks.json, imports + calls handlers
|
v
handlers/ Native Python handlers (the actual hook logic)
Provider settings in ~/.claude/settings.json call the bridge with an event type:
{
"type": "command",
"command": "$AIPASS_HOME/.venv/bin/python3 $AIPASS_HOME/src/aipass/hooks/apps/handlers/bridges/claude.py PreToolUse"
}
The bridge supports two invocation forms:
claude.py EventType-- dispatch ALL enabled hooks for that eventclaude.py EventType:hook_name-- dispatch ONLY one specific hook (used for UserPromptSubmit where each hook needs its own system-reminder block)
Per-project configuration lives in .aipass/hooks.json. Each hook entry specifies:
enabled-- whether the hook fireshandler-- dotted import path to the handler functionmatcher-- tool name filter (empty string = match all)timeout-- optional timeout in seconds
Quick Setup
Run setup.sh from the repo root. It creates the venv, installs the package, and wires bridge entries into ~/.claude/settings.json automatically.
./setup.sh
If hooks get out of sync, aipass doctor --fix can auto-wire missing hook entries.
No manual script copying is needed. No global_hooks directory. No git rev-parse tricks.
What's In This Directory
.claude/
├── settings.json # Project settings (permissions, env vars)
├── hooks/ # Legacy hook scripts (all disabled) + testing tools
│ ├── *.py(disabled) # 18 disabled scripts (pre-migration)
│ ├── hook_log.py # Shared logger -- hooks call run_and_log()
│ ├── hook_report.py # Report tool -- reads JSONL log, shows table
│ ├── hook_test.py # Test harness -- direct + integration tests
│ └── probes/ # Opt-in per-event diagnostic probes
├── agents/ # Agent definitions
│ └── builder.md
├── commands/ # Slash commands
│ └── memo.md # /memo -- memory update workflow
├── sounds/ # Audio files for sound hooks
└── README.md # This file
Hook logic has moved to src/aipass/hooks/apps/handlers/. See the handler README for the full layout.
Handler Layout
All 14 hooks are native Python handlers organized by domain:
src/aipass/hooks/apps/handlers/
├── bridges/
│ └── claude.py # Provider bridge (called from settings.json)
├── config/
│ ├── loader.py # Finds and reads .aipass/hooks.json
│ └── diagnostics.py # JSONL logging for hook execution
├── prompt/
│ ├── global_loader.py # UserPromptSubmit -- AIPass global prompt
│ ├── branch_loader.py # UserPromptSubmit -- branch-specific prompt
│ └── identity.py # UserPromptSubmit -- passport identity injection
├── notification/
│ ├── email.py # UserPromptSubmit -- unread email count
│ ├── tool_sound.py # PreToolUse -- key-press sound
│ ├── stop_sound.py # Stop -- achievement bell
│ └── announce.py # Notification -- notification sound
├── security/
│ ├── git_gate.py # PreToolUse -- blocks raw git/gh commands
│ ├── edit_gate.py # PreToolUse -- cross-branch write block
│ └── subagent_gate.py # SubagentStop -- seedgo checklist gate
└── lifecycle/
├── auto_fix.py # PostToolUse -- pyright + ruff after edits
├── auto_watchdog.py # PostToolUse -- watchdog reminder after dispatch
├── compact.py # PreCompact -- save context before compaction
└── rollover.py # PreCompact -- memory rollover on compaction
What Gets Injected Every Turn
- Global Prompt -- system context, terminology, commands, rules (
.aipass/aipass_global_prompt.md) - Branch Prompt -- branch-specific instructions based on CWD (
.aipass/aipass_local_prompt.md) - Identity -- passport summary: role, traits, purpose (
.trinity/passport.json) - Email -- notification only if unread mail exists (
.ai_mail.local/inbox.json)
Each is dispatched as a separate UserPromptSubmit:hook_name call so it gets its own system-reminder block.
Project Settings
Defined in settings.json (this directory). These fire from subdirectories.
Environment:
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1-- makes PostToolUse hooks fire inside subagentsAIPASS_HOME-- repo root path, used by bridge commands
Permissions:
- Denied:
git reset,git rebase,git config,git push --force,EnterPlanMode - Default mode:
acceptEdits
Adding a New Hook
- Create a handler in
src/aipass/hooks/apps/handlers/<domain>/your_hook.pywith ahandle(event_type, stdin_data, config)function - Add an entry to
.aipass/hooks.jsonunder the appropriate event type - If the hook needs its own system-reminder output (like prompt injectors), add a separate bridge entry in
~/.claude/settings.jsonusing theEventType:hook_nameform - Run
setup.shoraipass doctor --fixto sync provider settings
Architecture Notes
Why provider settings? Claude Code project settings (.claude/settings.json) do not fire UserPromptSubmit hooks from subdirectories. Since AIPass citizens launch from src/aipass/{name}/, prompt injection must live in provider settings (~/.claude/settings.json). The bridge pattern makes this clean -- one bridge binary, many handlers.
Why separate bridge calls for UserPromptSubmit? Each UserPromptSubmit hook entry gets its own system-reminder block in the conversation. Bundling them into one call would merge all prompt output into a single block, losing separation.
Why .aipass/hooks.json? Decouples hook configuration from provider settings. The engine reads this at dispatch time, so hooks can be enabled/disabled without editing ~/.claude/settings.json.