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 <noreply@anthropic.com>
This commit is contained in:
parent
beb24f71d2
commit
8fed5bc6ef
7 changed files with 603 additions and 2 deletions
|
|
@ -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
|
||||
|
|
|
|||
80
claude/issues/012-hooks-test-feedback.md
Normal file
80
claude/issues/012-hooks-test-feedback.md
Normal file
|
|
@ -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*`
|
||||
98
claude/issues/013-hooks-stop-verify.md
Normal file
98
claude/issues/013-hooks-stop-verify.md
Normal file
|
|
@ -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...
|
||||
```
|
||||
97
claude/issues/014-hooks-auto-test.md
Normal file
97
claude/issues/014-hooks-auto-test.md
Normal file
|
|
@ -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
|
||||
101
claude/issues/015-hooks-context-inject.md
Normal file
101
claude/issues/015-hooks-context-inject.md
Normal file
|
|
@ -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
|
||||
89
claude/issues/016-hooks-format-auto.md
Normal file
89
claude/issues/016-hooks-format-auto.md
Normal file
|
|
@ -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}'
|
||||
```
|
||||
124
claude/issues/017-hooks-expose-hide.md
Normal file
124
claude/issues/017-hooks-expose-hide.md
Normal file
|
|
@ -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"
|
||||
```
|
||||
Loading…
Add table
Reference in a new issue