mirror of
https://github.com/garrytan/gstack.git
synced 2026-06-17 07:10:12 +02:00
feat(setup): install plan-tune cathedral hooks with explicit consent UX
Plan-tune cathedral T8. Wires the new PostToolUse capture hook and PreToolUse enforcement hook into ~/.claude/settings.json via the schema-aware gstack-settings-hook (T3) — respecting D4's "never mutate settings.json silently" boundary and the Codex outside-voice warning. Behavior at setup time: - Idempotency: if list-sources already shows 'plan-tune-cathedral', no-op with a one-line note. - Marker present (previously declined): no-op, no re-prompt. - Interactive terminal: print rationale + diff preview from settings-hook, rollback command, and prompt y/N. On accept, register both hooks (PostToolUse and PreToolUse) with --source plan-tune-cathedral. On decline, touch ~/.gstack/.plan-tune-hooks-prompted so we don't re-ask. - Non-interactive (CI / scripted): no prompt; print the two exact commands the user would need to install manually. - --no-team teardown also removes the plan-tune hooks via remove-source. gstack-uninstall extended to clean up plan-tune-cathedral hooks alongside the existing SessionStart cleanup. Listed as a separate "plan-tune cathedral hooks" line in the REMOVED summary when it fires. No new test file — coverage from T3's gstack-settings-hook-schema-aware tests proves the underlying bin behavior; setup-level integration is verified manually (re-running ./setup is cheap and the prompt makes it obvious whether install happened). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -232,6 +232,10 @@ SETTINGS_HOOK="$(dirname "$0")/gstack-settings-hook"
|
||||
SESSION_UPDATE="$(dirname "$0")/gstack-session-update"
|
||||
if [ -x "$SETTINGS_HOOK" ]; then
|
||||
"$SETTINGS_HOOK" remove "$SESSION_UPDATE" 2>/dev/null && REMOVED+=("SessionStart hook") || true
|
||||
# Cathedral T8 cleanup: also remove plan-tune PreToolUse + PostToolUse hooks.
|
||||
if "$SETTINGS_HOOK" remove-source --source plan-tune-cathedral 2>/dev/null | grep -q "removed [1-9]"; then
|
||||
REMOVED+=("plan-tune cathedral hooks")
|
||||
fi
|
||||
fi
|
||||
|
||||
# ─── Remove global state ────────────────────────────────────
|
||||
|
||||
@@ -1150,3 +1150,100 @@ if [ "$NO_TEAM_MODE" -eq 1 ]; then
|
||||
|
||||
log "Team mode disabled: auto-update hook removed."
|
||||
fi
|
||||
|
||||
# 11. Plan-tune cathedral hook install (T8).
|
||||
#
|
||||
# Registers PostToolUse (deterministic AUQ capture) + PreToolUse (preference
|
||||
# enforcement) hooks in ~/.claude/settings.json so /plan-tune actually does
|
||||
# something at runtime instead of being agent-convention. Explicit consent UX
|
||||
# per D4 + Codex: never mutate settings.json silently.
|
||||
#
|
||||
# Idempotent via _gstack_source tag = 'plan-tune-cathedral'. If both hooks
|
||||
# already registered under that tag, the install is a no-op (no prompt).
|
||||
PLAN_TUNE_LOG_HOOK="$SOURCE_GSTACK_DIR/hosts/claude/hooks/question-log-hook"
|
||||
PLAN_TUNE_PREF_HOOK="$SOURCE_GSTACK_DIR/hosts/claude/hooks/question-preference-hook"
|
||||
PLAN_TUNE_INSTALL_MARKER="$HOME/.gstack/.plan-tune-hooks-prompted"
|
||||
|
||||
if [ "$NO_TEAM_MODE" -ne 1 ] \
|
||||
&& [ -x "$SETTINGS_HOOK" ] \
|
||||
&& [ -x "$PLAN_TUNE_LOG_HOOK" ] \
|
||||
&& [ -x "$PLAN_TUNE_PREF_HOOK" ]; then
|
||||
|
||||
# Already installed? Check the settings.json for our source tag.
|
||||
ALREADY_INSTALLED=0
|
||||
if "$SETTINGS_HOOK" list-sources 2>/dev/null | grep -q "plan-tune-cathedral"; then
|
||||
ALREADY_INSTALLED=1
|
||||
fi
|
||||
|
||||
if [ "$ALREADY_INSTALLED" -eq 1 ]; then
|
||||
log ""
|
||||
log "Plan-tune hooks already installed. Run \`$SETTINGS_HOOK list-sources\` to inspect."
|
||||
elif [ -f "$PLAN_TUNE_INSTALL_MARKER" ]; then
|
||||
# Previously declined. Don't re-ask. User can re-enable via /update-config.
|
||||
:
|
||||
elif [ -t 0 ] && [ -t 1 ]; then
|
||||
# Interactive install with explicit consent + diff preview.
|
||||
log ""
|
||||
log "──────────────────────────────────────────────────────────"
|
||||
log "Plan-tune cathedral: install Claude Code hooks?"
|
||||
log "──────────────────────────────────────────────────────────"
|
||||
log ""
|
||||
log "These hooks make /plan-tune settings actually bind at runtime:"
|
||||
log " • PostToolUse hook captures every AskUserQuestion fire (no agent"
|
||||
log " compliance required). Today it's agent-convention and the log"
|
||||
log " is empty in dogfood."
|
||||
log " • PreToolUse hook enforces 'never-ask' preferences via Claude Code's"
|
||||
log " permissionDecision protocol. Today preferences are agent-honored"
|
||||
log " convention; this makes them binding."
|
||||
log ""
|
||||
log "Diff preview (PostToolUse capture hook):"
|
||||
"$SETTINGS_HOOK" diff-event \
|
||||
--event PostToolUse \
|
||||
--matcher '(AskUserQuestion|mcp__.*__AskUserQuestion)' \
|
||||
--command "$PLAN_TUNE_LOG_HOOK" \
|
||||
--source plan-tune-cathedral \
|
||||
--timeout 5 2>/dev/null || true
|
||||
log ""
|
||||
log "Backup: settings.json.bak.<ts> written before any mutation."
|
||||
log "Rollback: $SETTINGS_HOOK rollback"
|
||||
log ""
|
||||
printf "Install both hooks now? [y/N] "
|
||||
read -r PLAN_TUNE_INSTALL_REPLY
|
||||
if [ "$PLAN_TUNE_INSTALL_REPLY" = "y" ] || [ "$PLAN_TUNE_INSTALL_REPLY" = "Y" ]; then
|
||||
"$SETTINGS_HOOK" add-event \
|
||||
--event PostToolUse \
|
||||
--matcher '(AskUserQuestion|mcp__.*__AskUserQuestion)' \
|
||||
--command "$PLAN_TUNE_LOG_HOOK" \
|
||||
--source plan-tune-cathedral \
|
||||
--timeout 5
|
||||
"$SETTINGS_HOOK" add-event \
|
||||
--event PreToolUse \
|
||||
--matcher '(AskUserQuestion|mcp__.*__AskUserQuestion)' \
|
||||
--command "$PLAN_TUNE_PREF_HOOK" \
|
||||
--source plan-tune-cathedral \
|
||||
--timeout 5
|
||||
log ""
|
||||
log "Plan-tune hooks installed. Run /plan-tune anytime to inspect."
|
||||
else
|
||||
log ""
|
||||
log "Skipped. Re-run ./setup or use /update-config to install later."
|
||||
fi
|
||||
touch "$PLAN_TUNE_INSTALL_MARKER"
|
||||
else
|
||||
# Non-interactive (CI, scripted setup). Don't prompt; print one-liner.
|
||||
log ""
|
||||
log "Plan-tune cathedral hooks not installed (non-interactive setup)."
|
||||
log "Install with:"
|
||||
log " $SETTINGS_HOOK add-event --event PostToolUse \\"
|
||||
log " --matcher '(AskUserQuestion|mcp__.*__AskUserQuestion)' \\"
|
||||
log " --command $PLAN_TUNE_LOG_HOOK --source plan-tune-cathedral --timeout 5"
|
||||
log " $SETTINGS_HOOK add-event --event PreToolUse \\"
|
||||
log " --matcher '(AskUserQuestion|mcp__.*__AskUserQuestion)' \\"
|
||||
log " --command $PLAN_TUNE_PREF_HOOK --source plan-tune-cathedral --timeout 5"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Also tear down plan-tune hooks on --no-team (matches the existing pattern).
|
||||
if [ "$NO_TEAM_MODE" -eq 1 ] && [ -x "$SETTINGS_HOOK" ]; then
|
||||
"$SETTINGS_HOOK" remove-source --source plan-tune-cathedral 2>/dev/null || true
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user