From 8fed5bc6ef1efa13a3cfd823f9f71497ecdad6a6 Mon Sep 17 00:00:00 2001 From: Snider Date: Sun, 1 Feb 2026 18:57:16 +0000 Subject: [PATCH] docs: add hook improvement tasks for better feedback cycle 6 new issues for Claude Code hook improvements: - 012: Test output filtering (reduce noise, show failures only) - 013: Stop verification (verify work complete before stopping) - 014: Auto-test on edit (async tests after code changes) - 015: Session context injection (git/issues/CI on startup) - 016: Silent auto-formatting (suppress formatter output) - 017: Expose/hide policy (define what to show vs suppress) Based on Claude Code hooks documentation: - PostToolUse with suppressOutput for noise reduction - Stop hooks with agent verification - Async hooks for background testing - SessionStart for context injection - additionalContext for exposing important info Co-Authored-By: Claude Opus 4.5 --- claude/issues/000-overview.md | 16 ++- claude/issues/012-hooks-test-feedback.md | 80 ++++++++++++++ claude/issues/013-hooks-stop-verify.md | 98 +++++++++++++++++ claude/issues/014-hooks-auto-test.md | 97 +++++++++++++++++ claude/issues/015-hooks-context-inject.md | 101 ++++++++++++++++++ claude/issues/016-hooks-format-auto.md | 89 ++++++++++++++++ claude/issues/017-hooks-expose-hide.md | 124 ++++++++++++++++++++++ 7 files changed, 603 insertions(+), 2 deletions(-) create mode 100644 claude/issues/012-hooks-test-feedback.md create mode 100644 claude/issues/013-hooks-stop-verify.md create mode 100644 claude/issues/014-hooks-auto-test.md create mode 100644 claude/issues/015-hooks-context-inject.md create mode 100644 claude/issues/016-hooks-format-auto.md create mode 100644 claude/issues/017-hooks-expose-hide.md diff --git a/claude/issues/000-overview.md b/claude/issues/000-overview.md index 69f3d3a..2764a26 100644 --- a/claude/issues/000-overview.md +++ b/claude/issues/000-overview.md @@ -58,9 +58,21 @@ claude/ No shell scripts. Just JSON config + markdown docs + `core` CLI calls. +### Hook Improvements (feedback cycle) + +| Issue | Feature | Purpose | +|-------|---------|---------| +| #012 | Test output filtering | Reduce noise, show only failures | +| #013 | Stop verification | Verify work complete before stopping | +| #014 | Auto-test on edit | Run tests async after code changes | +| #015 | Session context | Inject git/issues/CI context on start | +| #016 | Silent formatting | Auto-format without output noise | +| #017 | Expose/hide policy | Define what to show vs suppress | + ## Implementation Order 1. **Phase 1**: `core ai session` + `core ai context` (enables hooks to work) 2. **Phase 2**: `core ai hook` + `core qa debug` (safety + quality) -3. **Phase 3**: `core collect github` + `core collect bitcointalk` (most used) -4. **Phase 4**: Remaining collection commands +3. **Phase 3**: Hook improvements (012-017) - better feedback cycle +4. **Phase 4**: `core collect github` + `core collect bitcointalk` (most used) +5. **Phase 5**: Remaining collection commands diff --git a/claude/issues/012-hooks-test-feedback.md b/claude/issues/012-hooks-test-feedback.md new file mode 100644 index 0000000..3917ead --- /dev/null +++ b/claude/issues/012-hooks-test-feedback.md @@ -0,0 +1,80 @@ +# feat(hooks): Reduce test noise with smart output filtering + +## Summary + +Add PostToolUse hooks that filter test output to show only what matters - failures and summaries, not passing tests. + +## Problem + +When running `core go test` or `core php test`, the full output floods the context: +- Hundreds of "PASS" lines for passing tests +- Verbose coverage output +- Repetitive timing information + +This wastes context window and makes failures harder to spot. + +## Solution + +PostToolUse hooks that: +1. Parse test output +2. Extract only failures, errors, and summary +3. Return filtered output as `additionalContext` +4. Use `suppressOutput: true` to hide the verbose original + +## Hook Configuration + +```json +{ + "hooks": { + "PostToolUse": [ + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "command": "core ai filter test-output" + } + ] + } + ] + } +} +``` + +## Filter Logic + +**Go tests:** +- Show: FAIL, ERROR, panic, --- FAIL +- Hide: PASS, RUN, coverage lines (unless requested) +- Summary: "47 passed, 2 failed, 1 skipped" + +**PHP/Pest tests:** +- Show: ✗, FAIL, Error, Exception +- Hide: ✓, passing test names +- Summary: "Tests: 47 passed, 2 failed" + +**Lint output:** +- Show: actual errors/warnings with file:line +- Hide: "no issues found" for each file +- Summary: "3 issues in 2 files" + +## Output Format + +```json +{ + "suppressOutput": true, + "hookSpecificOutput": { + "hookEventName": "PostToolUse", + "additionalContext": "## Test Results\n\n✗ 2 failed, 47 passed\n\n### Failures:\n- TestFoo: expected 5, got 3\n- TestBar: nil pointer" + } +} +``` + +## Command Detection + +Only filter when command matches: +- `core go test*` +- `core php test*` +- `core go lint*` +- `core php stan*` +- `core qa*` diff --git a/claude/issues/013-hooks-stop-verify.md b/claude/issues/013-hooks-stop-verify.md new file mode 100644 index 0000000..f49a43c --- /dev/null +++ b/claude/issues/013-hooks-stop-verify.md @@ -0,0 +1,98 @@ +# feat(hooks): Stop hook to verify work is complete + +## Summary + +Add a Stop hook that verifies Claude has actually completed the task before stopping, preventing premature "I'm done" responses. + +## Problem + +Claude sometimes stops too early: +- "I've updated the file" (but didn't run tests) +- "The fix is complete" (but there are lint errors) +- "Done" (but left debug statements) + +## Solution + +A Stop hook (type: "agent") that spawns a verification subagent to check: +1. Were tests run? Did they pass? +2. Are there uncommitted changes that should be committed? +3. Are there debug statements left in code? +4. Does the output match what was requested? + +## Hook Configuration + +```json +{ + "hooks": { + "Stop": [ + { + "hooks": [ + { + "type": "agent", + "prompt": "Verify the work is complete. Check: 1) Tests passed if code was changed, 2) No debug statements (dd, dump, fmt.Println), 3) Code is formatted. Context: $ARGUMENTS", + "timeout": 60 + } + ] + } + ] + } +} +``` + +## Alternative: Command-based + +For faster verification without subagent overhead: + +```json +{ + "hooks": { + "Stop": [ + { + "hooks": [ + { + "type": "command", + "command": "core ai verify-complete" + } + ] + } + ] + } +} +``` + +## Verification Checks + +`core ai verify-complete` should check: + +1. **If code was modified:** + - Run `core go test` or `core php test` + - Check for debug statements + - Verify formatting + +2. **If commit was requested:** + - Check git status for uncommitted changes + +3. **Return decision:** + ```json + { + "decision": "block", + "reason": "Tests have not been run. Please run: core go test" + } + ``` + +## Guard Against Loops + +Check `stop_hook_active` to prevent infinite loops: + +```bash +#!/bin/bash +INPUT=$(cat) +STOP_ACTIVE=$(echo "$INPUT" | jq -r '.stop_hook_active') + +if [ "$STOP_ACTIVE" = "true" ]; then + # Already continued once, allow stop + exit 0 +fi + +# Run verification... +``` diff --git a/claude/issues/014-hooks-auto-test.md b/claude/issues/014-hooks-auto-test.md new file mode 100644 index 0000000..dc1abad --- /dev/null +++ b/claude/issues/014-hooks-auto-test.md @@ -0,0 +1,97 @@ +# feat(hooks): Auto-run tests after code changes (async) + +## Summary + +Add async PostToolUse hooks that automatically run tests after Write/Edit operations, reporting results without blocking. + +## Problem + +Claude often edits code but forgets to run tests, or runs tests manually each time which is slow and repetitive. + +## Solution + +Async PostToolUse hooks that: +1. Detect code file changes (*.go, *.php, *.ts) +2. Run relevant tests in background +3. Report results on next turn via `systemMessage` + +## Hook Configuration + +```json +{ + "hooks": { + "PostToolUse": [ + { + "matcher": "Write|Edit", + "hooks": [ + { + "type": "command", + "command": "core ai auto-test", + "async": true, + "timeout": 120, + "statusMessage": "Running tests..." + } + ] + } + ] + } +} +``` + +## Test Detection Logic + +`core ai auto-test` should: + +1. **Read file path from stdin:** + ```bash + FILE=$(jq -r '.tool_input.file_path') + ``` + +2. **Detect test scope:** + - `*.go` in `pkg/foo/` → `core go test ./pkg/foo/...` + - `*.php` in `src/` → `core php test --filter=related` + - `*_test.go` → run that specific test file + - `*.spec.ts` → run that specific spec + +3. **Run minimal test set:** + - Don't run full suite on every change + - Use `--filter` or package scope + - Cache recent test runs + +4. **Report results:** + ```json + { + "systemMessage": "Tests for pkg/foo: 12 passed, 0 failed (2.3s)" + } + ``` + +## Smart Test Selection + +For Go: +```bash +# Find related test file +TEST_FILE="${FILE%.go}_test.go" +if [ -f "$TEST_FILE" ]; then + core go test -run "$(basename $TEST_FILE .go)" ./... +else + # Run package tests + PKG_DIR=$(dirname "$FILE") + core go test "./$PKG_DIR/..." +fi +``` + +For PHP: +```bash +# Find related test +TEST_FILE=$(echo "$FILE" | sed 's/\.php$/Test.php/' | sed 's|src/|tests/|') +if [ -f "$TEST_FILE" ]; then + core php test "$TEST_FILE" +fi +``` + +## Debouncing + +Avoid running tests multiple times for rapid edits: +- Track last test run per package +- Skip if <5 seconds since last run +- Queue and batch rapid changes diff --git a/claude/issues/015-hooks-context-inject.md b/claude/issues/015-hooks-context-inject.md new file mode 100644 index 0000000..fab56dc --- /dev/null +++ b/claude/issues/015-hooks-context-inject.md @@ -0,0 +1,101 @@ +# feat(hooks): SessionStart context injection + +## Summary + +Add SessionStart hooks that inject relevant project context at the start of each session. + +## Problem + +Claude starts each session without knowing: +- Recent git changes +- Open issues assigned to the user +- CI status +- Current branch and uncommitted work + +This leads to repeated context-gathering at the start of every session. + +## Solution + +SessionStart hook that runs `core` commands to gather context and inject it. + +## Hook Configuration + +```json +{ + "hooks": { + "SessionStart": [ + { + "matcher": "startup|resume", + "hooks": [ + { + "type": "command", + "command": "core ai session-context", + "timeout": 30 + } + ] + } + ] + } +} +``` + +## Context to Gather + +`core ai session-context` should collect: + +1. **Git status:** + ```bash + BRANCH=$(git branch --show-current) + CHANGES=$(git status --short | head -10) + ``` + +2. **Recent activity:** + ```bash + COMMITS=$(git log --oneline -5) + ``` + +3. **Open issues (if gh available):** + ```bash + ISSUES=$(core dev issues --assignee @me --limit 5) + ``` + +4. **CI status:** + ```bash + CI=$(core qa health --brief) + ``` + +5. **Restored session state:** + ```bash + if [ -f ~/.claude/sessions/scratchpad.md ]; then + # Include previous session context + fi + ``` + +## Output Format + +```json +{ + "hookSpecificOutput": { + "hookEventName": "SessionStart", + "additionalContext": "## Session Context\n\n**Branch:** feature/foo\n**Uncommitted:** 3 files\n\n**Recent commits:**\n- abc123 fix: resolve test flake\n- def456 feat: add new endpoint\n\n**Your issues:**\n- #42 Bug in login flow\n- #38 Add caching\n\n**CI:** All green ✓" + } +} +``` + +## Environment Variables + +Also set useful env vars via `CLAUDE_ENV_FILE`: + +```bash +if [ -n "$CLAUDE_ENV_FILE" ]; then + echo "export PROJECT_ROOT=\"$(pwd)\"" >> "$CLAUDE_ENV_FILE" + echo "export GIT_BRANCH=\"$BRANCH\"" >> "$CLAUDE_ENV_FILE" +fi +``` + +## Conditional Context + +Only inject what's relevant: +- Skip CI status if not in a repo with workflows +- Skip issues if gh not authenticated +- Skip session restore if >3 hours old diff --git a/claude/issues/016-hooks-format-auto.md b/claude/issues/016-hooks-format-auto.md new file mode 100644 index 0000000..481da86 --- /dev/null +++ b/claude/issues/016-hooks-format-auto.md @@ -0,0 +1,89 @@ +# feat(hooks): Auto-format code silently after edits + +## Summary + +Add PostToolUse hooks that silently auto-format code after Write/Edit, without polluting context with formatter output. + +## Current State + +Current hooks call formatters but output is visible: +- `php-format.sh` - runs Pint +- `go-format.sh` - runs gofmt + +This creates noise in the conversation. + +## Solution + +Use `suppressOutput: true` to hide formatter output entirely. + +## Hook Configuration + +```json +{ + "hooks": { + "PostToolUse": [ + { + "matcher": "Write|Edit", + "hooks": [ + { + "type": "command", + "command": "core ai format-file", + "statusMessage": "Formatting..." + } + ] + } + ] + } +} +``` + +## Implementation + +`core ai format-file`: + +```bash +#!/bin/bash +INPUT=$(cat) +FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty') + +[ -z "$FILE" ] || [ ! -f "$FILE" ] && exit 0 + +case "$FILE" in + *.go) + core go fmt "$FILE" 2>/dev/null + ;; + *.php) + core php fmt "$FILE" 2>/dev/null + ;; + *.ts|*.tsx|*.js|*.jsx) + npx prettier --write "$FILE" 2>/dev/null + ;; + *.json) + jq '.' "$FILE" > "$FILE.tmp" && mv "$FILE.tmp" "$FILE" 2>/dev/null + ;; +esac + +# Suppress all output - formatting is silent +echo '{"suppressOutput": true}' +``` + +## Benefits + +1. **No context pollution** - formatter output hidden +2. **Consistent formatting** - every file formatted on save +3. **No manual step** - Claude doesn't need to remember to format +4. **Fast** - formatters are quick, doesn't block significantly + +## Error Handling + +Only report if formatting fails badly: + +```bash +if ! core go fmt "$FILE" 2>&1; then + # Only surface actual errors, not warnings + echo '{"systemMessage": "Format failed for '$FILE' - syntax error?"}' + exit 0 +fi + +echo '{"suppressOutput": true}' +``` diff --git a/claude/issues/017-hooks-expose-hide.md b/claude/issues/017-hooks-expose-hide.md new file mode 100644 index 0000000..8a9aafb --- /dev/null +++ b/claude/issues/017-hooks-expose-hide.md @@ -0,0 +1,124 @@ +# feat(hooks): Define what to expose vs hide in output + +## Summary + +Document and implement a consistent policy for what hook output should be exposed to Claude vs hidden. + +## Principles + +**Expose (additionalContext):** +- Errors that need fixing +- Failures that block progress +- Decisions that need to be made +- Security warnings +- Breaking changes + +**Hide (suppressOutput):** +- Success confirmations ("formatted", "passed") +- Verbose progress output +- Repetitive status messages +- Debug information +- Intermediate results + +## Output Categories + +### Always Expose + +| Category | Example | Reason | +|----------|---------|--------| +| Test failures | `FAIL: TestFoo` | Must be fixed | +| Build errors | `cannot find package` | Blocks progress | +| Lint errors | `undefined: foo` | Code quality | +| Security alerts | `HIGH vulnerability` | Critical | +| Type errors | `type mismatch` | Must be fixed | + +### Always Hide + +| Category | Example | Reason | +|----------|---------|--------| +| Pass confirmations | `PASS: TestFoo` | No action needed | +| Format success | `Formatted 3 files` | No action needed | +| Coverage numbers | `coverage: 84.2%` | Unless requested | +| Timing info | `(12.3s)` | Noise | +| Progress bars | `[=====> ]` | Noise | + +### Conditional + +| Category | Show When | Hide When | +|----------|-----------|-----------| +| Warnings | First occurrence | Repeated | +| Suggestions | Actionable | Informational | +| Diffs | Small (<10 lines) | Large | +| Stack traces | Unique error | Repeated | + +## Implementation Pattern + +```bash +#!/bin/bash +INPUT=$(cat) +OUTPUT=$(echo "$INPUT" | jq -r '.tool_response.output // empty') + +# Extract what matters +ERRORS=$(echo "$OUTPUT" | grep -E "FAIL|ERROR|panic" | head -10) +SUMMARY=$(echo "$OUTPUT" | tail -1) + +if [ -n "$ERRORS" ]; then + # Expose failures + cat << EOF +{ + "hookSpecificOutput": { + "hookEventName": "PostToolUse", + "additionalContext": "## Issues Found\n\n$ERRORS\n\n**Summary:** $SUMMARY" + } +} +EOF +else + # Hide success + echo '{"suppressOutput": true}' +fi +``` + +## Hook-Specific Policies + +### PostToolUse (Bash) + +``` +core go test → Expose failures, hide passes +core go lint → Expose errors, hide "no issues" +core build → Expose errors, hide success +git status → Always expose (informational) +git commit → Expose result, hide diff +``` + +### PostToolUse (Write/Edit) + +``` +Format result → Always hide +Debug check → Expose if found, hide if clean +``` + +### PostToolUseFailure + +``` +All errors → Always expose with context +Interrupts → Hide (user initiated) +``` + +## Aggregation + +When multiple issues, aggregate intelligently: + +``` +Instead of: +- FAIL: TestA +- FAIL: TestB +- FAIL: TestC +- (47 more) + +Show: +"50 tests failed. Top failures: +- TestA: nil pointer +- TestB: timeout +- TestC: assertion failed +Run `core go test -v` for full output" +```