From 0e66ce3efacda3e89bf3a3532574d7c043953128 Mon Sep 17 00:00:00 2001 From: nunziati Date: Fri, 10 Apr 2026 17:26:20 +0000 Subject: [PATCH] Fix: remove claude-specific reference from hooks. build: add platform_dir and dispatcher_name to all hook wrapper/plugin templates feat(hooks): platform-aware path checks using platform_dir and dispatcher_name from JSON input test: update regression snapshot for platform-aware hook wrappers and scripts --- .../templates/cc-hook-wrapper.sh.tmpl | 4 +++- .../templates/gemini-hook-wrapper.sh.tmpl | 4 +++- .../opencode/templates/plugin-stub.js.tmpl | 2 ++ hooks/notify.sh | 4 ++-- hooks/protect-system-files.sh | 23 ++++++++++++------- hooks/validate-frontmatter.sh | 3 ++- .../snapshot/.claude/hooks/notify-wrapper.sh | 4 +++- .../snapshot/.claude/hooks/notify.sh | 4 ++-- .../hooks/protect-system-files-wrapper.sh | 4 +++- .../.claude/hooks/protect-system-files.sh | 23 ++++++++++++------- .../hooks/validate-frontmatter-wrapper.sh | 4 +++- .../.claude/hooks/validate-frontmatter.sh | 3 ++- 12 files changed, 55 insertions(+), 27 deletions(-) diff --git a/adapters/claude-code/templates/cc-hook-wrapper.sh.tmpl b/adapters/claude-code/templates/cc-hook-wrapper.sh.tmpl index 411340e..7003047 100755 --- a/adapters/claude-code/templates/cc-hook-wrapper.sh.tmpl +++ b/adapters/claude-code/templates/cc-hook-wrapper.sh.tmpl @@ -17,7 +17,9 @@ NEUTRAL=$(echo "$INPUT" | jq -c '{ args: (.tool_input // {title: .title, message: .message}), session_id: (.session_id // ""), cwd: (.cwd // ""), - framework: "claude-code" + framework: "claude-code", + platform_dir: ".claude", + dispatcher_name: "CLAUDE.md" }') echo "$NEUTRAL" | bash "$HOOK_DIR/__HOOK_NAME__.sh" diff --git a/adapters/gemini-cli/templates/gemini-hook-wrapper.sh.tmpl b/adapters/gemini-cli/templates/gemini-hook-wrapper.sh.tmpl index 163575b..d256434 100755 --- a/adapters/gemini-cli/templates/gemini-hook-wrapper.sh.tmpl +++ b/adapters/gemini-cli/templates/gemini-hook-wrapper.sh.tmpl @@ -16,7 +16,9 @@ NEUTRAL=$(echo "$INPUT" | jq -c '{ args: (.tool_input // {}), session_id: (env.GEMINI_SESSION_ID // ""), cwd: (env.GEMINI_CWD // env.GEMINI_PROJECT_DIR // ""), - framework: "gemini-cli" + framework: "gemini-cli", + platform_dir: ".gemini", + dispatcher_name: "GEMINI.md" }') echo "$NEUTRAL" | bash "$HOOK_DIR/__HOOK_NAME__.sh" diff --git a/adapters/opencode/templates/plugin-stub.js.tmpl b/adapters/opencode/templates/plugin-stub.js.tmpl index 01bd37b..5812472 100755 --- a/adapters/opencode/templates/plugin-stub.js.tmpl +++ b/adapters/opencode/templates/plugin-stub.js.tmpl @@ -43,6 +43,8 @@ function buildPayload(eventName, input) { session_id: (input && input.sessionId) || "", cwd: (input && input.cwd) || process.cwd(), framework: "opencode", + platform_dir: ".opencode", + dispatcher_name: "AGENTS.md", }; } diff --git a/hooks/notify.sh b/hooks/notify.sh index 29005dd..7e9ac8c 100755 --- a/hooks/notify.sh +++ b/hooks/notify.sh @@ -2,7 +2,7 @@ # ============================================================================= # Hook: Desktop Notification (Notification event) # ============================================================================= -# Sends a macOS/Linux desktop notification when Claude Code needs attention. +# Sends a macOS/Linux desktop notification when your agent platform needs attention. # Useful during long agent chains that can take several minutes. # # macOS: uses osascript (built-in) @@ -11,7 +11,7 @@ INPUT=$(cat) TITLE=$(echo "$INPUT" | jq -r '.args.title // "Second Brain Crew"' 2>/dev/null) -MESSAGE=$(echo "$INPUT" | jq -r '.args.message // "Claude needs your attention"' 2>/dev/null) +MESSAGE=$(echo "$INPUT" | jq -r '.args.message // "Your obsidian crew needs your attention"' 2>/dev/null) if [[ "$(uname)" == "Darwin" ]]; then osascript -e "display notification \"$MESSAGE\" with title \"$TITLE\"" 2>/dev/null diff --git a/hooks/protect-system-files.sh b/hooks/protect-system-files.sh index ad59c17..48d94f6 100755 --- a/hooks/protect-system-files.sh +++ b/hooks/protect-system-files.sh @@ -3,8 +3,13 @@ # Hook: Protect System Files (PreToolUse on Write/Edit) # ============================================================================= # Prevents agents from accidentally overwriting core crew files at runtime. -# Custom agents in .claude/agents/ are allowed (the Architect creates them). -# User-mutable references (agents-registry.md, agents.md) are also allowed. +# Custom agents in the platform agents directory are allowed (the Architect +# creates them). User-mutable references (agents-registry.md, agents.md) are +# also allowed. +# +# Reads platform_dir and dispatcher_name from the neutral JSON input to +# determine which paths to protect. Falls back to .claude / CLAUDE.md if +# the fields are missing (backward compatibility). # # Exit codes: # 0 = allow the operation @@ -18,16 +23,18 @@ FILE=$(echo "$INPUT" | jq -r '.args.file_path // .args.command // ""' 2>/dev/nul [[ -z "$FILE" ]] && exit 0 BASENAME=$(basename "$FILE") +PLATFORM_DIR=$(echo "$INPUT" | jq -r '.platform_dir // ".claude"' 2>/dev/null) +DISPATCHER_NAME=$(echo "$INPUT" | jq -r '.dispatcher_name // "CLAUDE.md"' 2>/dev/null) -# ── CLAUDE.md: never modify at runtime ────────────────────────────────────── -if [[ "$BASENAME" == "CLAUDE.md" && "$FILE" != *".claude/"* ]]; then - echo "BLOCKED: CLAUDE.md is a system file. Update it in the repo and run updateme.sh." +# ── Dispatcher file: never modify at runtime ────────────────────────────── +if [[ "$BASENAME" == "$DISPATCHER_NAME" && "$FILE" != *"$PLATFORM_DIR/"* ]]; then + echo "BLOCKED: $DISPATCHER_NAME is a system file. Update it in the repo and run updateme.sh." exit 2 fi # ── Core agent definitions: never modify at runtime ───────────────────────── CORE_AGENTS="architect.md scribe.md sorter.md seeker.md connector.md librarian.md transcriber.md postman.md" -if [[ "$FILE" == *".claude/agents/"* ]]; then +if [[ "$FILE" == *"$PLATFORM_DIR/agents/"* ]]; then for core in $CORE_AGENTS; do if [[ "$BASENAME" == "$core" ]]; then echo "BLOCKED: $BASENAME is a core agent definition. Update it in the repo and run updateme.sh." @@ -39,13 +46,13 @@ if [[ "$FILE" == *".claude/agents/"* ]]; then fi # ── Skills: never modify at runtime ───────────────────────────────────────── -if [[ "$FILE" == *".claude/skills/"* ]]; then +if [[ "$FILE" == *"$PLATFORM_DIR/skills/"* ]]; then echo "BLOCKED: Skill files are managed by the repo. Update them in the repo and run updateme.sh." exit 2 fi # ── Core references: block all except user-mutable ones ───────────────────── -if [[ "$FILE" == *".claude/references/"* ]]; then +if [[ "$FILE" == *"$PLATFORM_DIR/references/"* ]]; then USER_MUTABLE="agents-registry.md agents.md" for allowed in $USER_MUTABLE; do [[ "$BASENAME" == "$allowed" ]] && exit 0 diff --git a/hooks/validate-frontmatter.sh b/hooks/validate-frontmatter.sh index 9984d84..8897634 100755 --- a/hooks/validate-frontmatter.sh +++ b/hooks/validate-frontmatter.sh @@ -17,6 +17,7 @@ # ============================================================================= INPUT=$(cat) +PLATFORM_DIR=$(echo "$INPUT" | jq -r '.platform_dir // ".claude"' 2>/dev/null) FILE=$(echo "$INPUT" | jq -r '.args.file_path // ""' 2>/dev/null) # Skip if we can't extract a path @@ -26,7 +27,7 @@ FILE=$(echo "$INPUT" | jq -r '.args.file_path // ""' 2>/dev/null) [[ "$FILE" == *.md ]] || exit 0 # Skip system files (agents, skills, references) -[[ "$FILE" == *".claude/"* ]] && exit 0 +[[ "$FILE" == *"$PLATFORM_DIR/"* ]] && exit 0 # Skip if file doesn't exist (deleted or moved) [[ -f "$FILE" ]] || exit 0 diff --git a/tests/regression/snapshot/.claude/hooks/notify-wrapper.sh b/tests/regression/snapshot/.claude/hooks/notify-wrapper.sh index ac1b3f7..6d974dc 100755 --- a/tests/regression/snapshot/.claude/hooks/notify-wrapper.sh +++ b/tests/regression/snapshot/.claude/hooks/notify-wrapper.sh @@ -17,7 +17,9 @@ NEUTRAL=$(echo "$INPUT" | jq -c '{ args: (.tool_input // {title: .title, message: .message}), session_id: (.session_id // ""), cwd: (.cwd // ""), - framework: "claude-code" + framework: "claude-code", + platform_dir: ".claude", + dispatcher_name: "CLAUDE.md" }') echo "$NEUTRAL" | bash "$HOOK_DIR/notify.sh" diff --git a/tests/regression/snapshot/.claude/hooks/notify.sh b/tests/regression/snapshot/.claude/hooks/notify.sh index 29005dd..7e9ac8c 100755 --- a/tests/regression/snapshot/.claude/hooks/notify.sh +++ b/tests/regression/snapshot/.claude/hooks/notify.sh @@ -2,7 +2,7 @@ # ============================================================================= # Hook: Desktop Notification (Notification event) # ============================================================================= -# Sends a macOS/Linux desktop notification when Claude Code needs attention. +# Sends a macOS/Linux desktop notification when your agent platform needs attention. # Useful during long agent chains that can take several minutes. # # macOS: uses osascript (built-in) @@ -11,7 +11,7 @@ INPUT=$(cat) TITLE=$(echo "$INPUT" | jq -r '.args.title // "Second Brain Crew"' 2>/dev/null) -MESSAGE=$(echo "$INPUT" | jq -r '.args.message // "Claude needs your attention"' 2>/dev/null) +MESSAGE=$(echo "$INPUT" | jq -r '.args.message // "Your obsidian crew needs your attention"' 2>/dev/null) if [[ "$(uname)" == "Darwin" ]]; then osascript -e "display notification \"$MESSAGE\" with title \"$TITLE\"" 2>/dev/null diff --git a/tests/regression/snapshot/.claude/hooks/protect-system-files-wrapper.sh b/tests/regression/snapshot/.claude/hooks/protect-system-files-wrapper.sh index 4095837..e91e795 100755 --- a/tests/regression/snapshot/.claude/hooks/protect-system-files-wrapper.sh +++ b/tests/regression/snapshot/.claude/hooks/protect-system-files-wrapper.sh @@ -17,7 +17,9 @@ NEUTRAL=$(echo "$INPUT" | jq -c '{ args: (.tool_input // {title: .title, message: .message}), session_id: (.session_id // ""), cwd: (.cwd // ""), - framework: "claude-code" + framework: "claude-code", + platform_dir: ".claude", + dispatcher_name: "CLAUDE.md" }') echo "$NEUTRAL" | bash "$HOOK_DIR/protect-system-files.sh" diff --git a/tests/regression/snapshot/.claude/hooks/protect-system-files.sh b/tests/regression/snapshot/.claude/hooks/protect-system-files.sh index ad59c17..48d94f6 100755 --- a/tests/regression/snapshot/.claude/hooks/protect-system-files.sh +++ b/tests/regression/snapshot/.claude/hooks/protect-system-files.sh @@ -3,8 +3,13 @@ # Hook: Protect System Files (PreToolUse on Write/Edit) # ============================================================================= # Prevents agents from accidentally overwriting core crew files at runtime. -# Custom agents in .claude/agents/ are allowed (the Architect creates them). -# User-mutable references (agents-registry.md, agents.md) are also allowed. +# Custom agents in the platform agents directory are allowed (the Architect +# creates them). User-mutable references (agents-registry.md, agents.md) are +# also allowed. +# +# Reads platform_dir and dispatcher_name from the neutral JSON input to +# determine which paths to protect. Falls back to .claude / CLAUDE.md if +# the fields are missing (backward compatibility). # # Exit codes: # 0 = allow the operation @@ -18,16 +23,18 @@ FILE=$(echo "$INPUT" | jq -r '.args.file_path // .args.command // ""' 2>/dev/nul [[ -z "$FILE" ]] && exit 0 BASENAME=$(basename "$FILE") +PLATFORM_DIR=$(echo "$INPUT" | jq -r '.platform_dir // ".claude"' 2>/dev/null) +DISPATCHER_NAME=$(echo "$INPUT" | jq -r '.dispatcher_name // "CLAUDE.md"' 2>/dev/null) -# ── CLAUDE.md: never modify at runtime ────────────────────────────────────── -if [[ "$BASENAME" == "CLAUDE.md" && "$FILE" != *".claude/"* ]]; then - echo "BLOCKED: CLAUDE.md is a system file. Update it in the repo and run updateme.sh." +# ── Dispatcher file: never modify at runtime ────────────────────────────── +if [[ "$BASENAME" == "$DISPATCHER_NAME" && "$FILE" != *"$PLATFORM_DIR/"* ]]; then + echo "BLOCKED: $DISPATCHER_NAME is a system file. Update it in the repo and run updateme.sh." exit 2 fi # ── Core agent definitions: never modify at runtime ───────────────────────── CORE_AGENTS="architect.md scribe.md sorter.md seeker.md connector.md librarian.md transcriber.md postman.md" -if [[ "$FILE" == *".claude/agents/"* ]]; then +if [[ "$FILE" == *"$PLATFORM_DIR/agents/"* ]]; then for core in $CORE_AGENTS; do if [[ "$BASENAME" == "$core" ]]; then echo "BLOCKED: $BASENAME is a core agent definition. Update it in the repo and run updateme.sh." @@ -39,13 +46,13 @@ if [[ "$FILE" == *".claude/agents/"* ]]; then fi # ── Skills: never modify at runtime ───────────────────────────────────────── -if [[ "$FILE" == *".claude/skills/"* ]]; then +if [[ "$FILE" == *"$PLATFORM_DIR/skills/"* ]]; then echo "BLOCKED: Skill files are managed by the repo. Update them in the repo and run updateme.sh." exit 2 fi # ── Core references: block all except user-mutable ones ───────────────────── -if [[ "$FILE" == *".claude/references/"* ]]; then +if [[ "$FILE" == *"$PLATFORM_DIR/references/"* ]]; then USER_MUTABLE="agents-registry.md agents.md" for allowed in $USER_MUTABLE; do [[ "$BASENAME" == "$allowed" ]] && exit 0 diff --git a/tests/regression/snapshot/.claude/hooks/validate-frontmatter-wrapper.sh b/tests/regression/snapshot/.claude/hooks/validate-frontmatter-wrapper.sh index cb14bc7..bad3705 100755 --- a/tests/regression/snapshot/.claude/hooks/validate-frontmatter-wrapper.sh +++ b/tests/regression/snapshot/.claude/hooks/validate-frontmatter-wrapper.sh @@ -17,7 +17,9 @@ NEUTRAL=$(echo "$INPUT" | jq -c '{ args: (.tool_input // {title: .title, message: .message}), session_id: (.session_id // ""), cwd: (.cwd // ""), - framework: "claude-code" + framework: "claude-code", + platform_dir: ".claude", + dispatcher_name: "CLAUDE.md" }') echo "$NEUTRAL" | bash "$HOOK_DIR/validate-frontmatter.sh" diff --git a/tests/regression/snapshot/.claude/hooks/validate-frontmatter.sh b/tests/regression/snapshot/.claude/hooks/validate-frontmatter.sh index 9984d84..8897634 100755 --- a/tests/regression/snapshot/.claude/hooks/validate-frontmatter.sh +++ b/tests/regression/snapshot/.claude/hooks/validate-frontmatter.sh @@ -17,6 +17,7 @@ # ============================================================================= INPUT=$(cat) +PLATFORM_DIR=$(echo "$INPUT" | jq -r '.platform_dir // ".claude"' 2>/dev/null) FILE=$(echo "$INPUT" | jq -r '.args.file_path // ""' 2>/dev/null) # Skip if we can't extract a path @@ -26,7 +27,7 @@ FILE=$(echo "$INPUT" | jq -r '.args.file_path // ""' 2>/dev/null) [[ "$FILE" == *.md ]] || exit 0 # Skip system files (agents, skills, references) -[[ "$FILE" == *".claude/"* ]] && exit 0 +[[ "$FILE" == *"$PLATFORM_DIR/"* ]] && exit 0 # Skip if file doesn't exist (deleted or moved) [[ -f "$FILE" ]] || exit 0