codecruise 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +111 -0
  3. package/bin/codecruise.js +68 -0
  4. package/config/CLAUDE.md +107 -0
  5. package/config/agents/analyst.md +48 -0
  6. package/config/agents/architect-reviewer.md +161 -0
  7. package/config/agents/architect.md +119 -0
  8. package/config/agents/critic.md +63 -0
  9. package/config/agents/developer.md +96 -0
  10. package/config/agents/devops.md +81 -0
  11. package/config/agents/orchestrator.md +91 -0
  12. package/config/agents/planner.md +139 -0
  13. package/config/agents/retro.md +52 -0
  14. package/config/agents/reviewer.md +101 -0
  15. package/config/agents/security-reviewer.md +57 -0
  16. package/config/agents/stack/expo/AGENT.md +473 -0
  17. package/config/agents/stack/expo/rules/critical.md +427 -0
  18. package/config/agents/stack/expo/rules/native.md +455 -0
  19. package/config/agents/stack/expo/rules/navigation.md +445 -0
  20. package/config/agents/stack/expo/rules/performance.md +415 -0
  21. package/config/agents/stack/fastify/AGENT.md +397 -0
  22. package/config/agents/stack/fastify/rules/api-design.md +283 -0
  23. package/config/agents/stack/fastify/rules/critical.md +232 -0
  24. package/config/agents/stack/fastify/rules/queues.md +303 -0
  25. package/config/agents/stack/fastify/rules/security.md +384 -0
  26. package/config/agents/stack/index.yaml +48 -0
  27. package/config/agents/stack/nextjs/AGENT.md +421 -0
  28. package/config/agents/stack/nextjs/rules/components.md +413 -0
  29. package/config/agents/stack/nextjs/rules/critical.md +391 -0
  30. package/config/agents/stack/nextjs/rules/performance.md +403 -0
  31. package/config/agents/stack/nextjs/rules/styling.md +334 -0
  32. package/config/agents/stack/shared-ts/AGENT.md +384 -0
  33. package/config/agents/stack/shared-ts/rules/critical.md +315 -0
  34. package/config/agents/stack/shared-ts/rules/patterns.md +384 -0
  35. package/config/agents/stack/shared-ts/rules/zod.md +427 -0
  36. package/config/agents/tester.md +79 -0
  37. package/config/commands/architect-discuss.md +366 -0
  38. package/config/commands/architect-list.md +160 -0
  39. package/config/commands/architect-review.md +111 -0
  40. package/config/commands/architect.md +118 -0
  41. package/config/commands/compact.md +118 -0
  42. package/config/commands/companion.md +279 -0
  43. package/config/commands/dashboard.md +152 -0
  44. package/config/commands/doctor.md +227 -0
  45. package/config/commands/dogfood-report.md +101 -0
  46. package/config/commands/flags/run-autonomous.md +110 -0
  47. package/config/commands/flags/run-pause.md +80 -0
  48. package/config/commands/ingest.md +173 -0
  49. package/config/commands/init.md +128 -0
  50. package/config/commands/metrics.md +87 -0
  51. package/config/commands/parallel.md +320 -0
  52. package/config/commands/pause.md +55 -0
  53. package/config/commands/plan-review.md +130 -0
  54. package/config/commands/plan.md +216 -0
  55. package/config/commands/production-check.md +308 -0
  56. package/config/commands/refine.md +323 -0
  57. package/config/commands/resume.md +72 -0
  58. package/config/commands/retro.md +121 -0
  59. package/config/commands/retry.md +75 -0
  60. package/config/commands/role.md +310 -0
  61. package/config/commands/run.md +417 -0
  62. package/config/commands/scope.md +85 -0
  63. package/config/commands/setup-permissions.md +104 -0
  64. package/config/commands/skip.md +75 -0
  65. package/config/commands/spec-forge.md +213 -0
  66. package/config/commands/spec-help.md +194 -0
  67. package/config/commands/spec-patch.md +342 -0
  68. package/config/commands/spec-resolve.md +110 -0
  69. package/config/commands/spec-review.md +153 -0
  70. package/config/commands/status.md +114 -0
  71. package/config/commands/sync.md +131 -0
  72. package/config/commands/task.md +138 -0
  73. package/config/commands/verify.md +124 -0
  74. package/config/hooks/README.md +632 -0
  75. package/config/hooks/activity-log.sh +187 -0
  76. package/config/hooks/anti-rationalize.sh +52 -0
  77. package/config/hooks/capture-verification.sh +112 -0
  78. package/config/hooks/collect-metrics.sh +135 -0
  79. package/config/hooks/enforce-file-scope.sh +75 -0
  80. package/config/hooks/enforce-state-machine.sh +161 -0
  81. package/config/hooks/enforce-tdd.sh +180 -0
  82. package/config/hooks/format.sh +40 -0
  83. package/config/hooks/lib/activity-helpers.sh +162 -0
  84. package/config/hooks/lib/read-settings.sh +71 -0
  85. package/config/hooks/load-context-skills.sh +95 -0
  86. package/config/hooks/notify.sh +81 -0
  87. package/config/hooks/pre-commit.sample +35 -0
  88. package/config/hooks/protect-files.sh +63 -0
  89. package/config/hooks/track-agents.sh +41 -0
  90. package/config/hooks/track-commands.sh +37 -0
  91. package/config/hooks/track-enforcement.sh +44 -0
  92. package/config/hooks/track-ooda.sh +77 -0
  93. package/config/hooks/validate-commit-msg.sh +35 -0
  94. package/config/hooks/validate-plan.sh +213 -0
  95. package/config/hooks/verify-criteria.sh +46 -0
  96. package/config/hooks/verify-todo-completion.sh +140 -0
  97. package/config/rules/comments.md +25 -0
  98. package/config/rules/decision-rules.md +308 -0
  99. package/config/rules/hygiene.md +247 -0
  100. package/config/rules/pattern-detection.md +372 -0
  101. package/config/rules/profiles.md +193 -0
  102. package/config/rules/recovery.md +83 -0
  103. package/config/rules/scope-detection.md +213 -0
  104. package/config/rules/standards.md +127 -0
  105. package/config/rules/workflow.md +121 -0
  106. package/config/schemas.md +767 -0
  107. package/config/settings.json +195 -0
  108. package/config/skills/backend/SKILL.md +734 -0
  109. package/config/skills/database/SKILL.md +426 -0
  110. package/config/skills/frontend/SKILL.md +434 -0
  111. package/config/skills/git/SKILL.md +396 -0
  112. package/config/skills/index.yaml +36 -0
  113. package/config/skills/observability/SKILL.md +430 -0
  114. package/config/skills/package-dev/SKILL.md +498 -0
  115. package/config/skills/performance/SKILL.md +378 -0
  116. package/config/skills/resilience/SKILL.md +573 -0
  117. package/config/skills/testing/SKILL.md +398 -0
  118. package/config/skills/testing-patterns/SKILL.md +276 -0
  119. package/config/skills/typescript/SKILL.md +152 -0
  120. package/config/templates/CLAUDE.md +70 -0
  121. package/config/templates/README.md +117 -0
  122. package/config/templates/steering/adr-template.md +102 -0
  123. package/config/templates/steering/product.md +60 -0
  124. package/config/templates/steering/rfc-template.md +159 -0
  125. package/config/templates/steering/structure.md +146 -0
  126. package/config/templates/steering/tech.md +85 -0
  127. package/package.json +40 -0
  128. package/src/install.js +163 -0
  129. package/src/report.js +310 -0
@@ -0,0 +1,187 @@
1
+ #!/bin/bash
2
+ # activity-log.sh — Human-readable activity log for tail -f
3
+ #
4
+ # PostToolUse hook that writes to .codecruise/activity.log
5
+ # Designed for real-time monitoring: tail -f .codecruise/activity.log
6
+ #
7
+ # Log format:
8
+ # [YYYY-MM-DD HH:MM:SS] <emoji> <message>
9
+ # [YYYY-MM-DD HH:MM:SS] └─ <detail>
10
+ #
11
+ # Configuration:
12
+ # CODECRUISE_ACTIVITY_LOG - Custom log path (default: .codecruise/activity.log)
13
+ # CODECRUISE_LOG_ROTATION - Max size in bytes (default: 1048576 = 1MB)
14
+
15
+ set -e
16
+
17
+ # Configuration
18
+ ACTIVITY_LOG="${CODECRUISE_ACTIVITY_LOG:-.codecruise/activity.log}"
19
+ MAX_LOG_SIZE="${CODECRUISE_LOG_ROTATION:-1048576}" # 1MB default
20
+
21
+ # Ensure directory exists
22
+ mkdir -p "$(dirname "$ACTIVITY_LOG")"
23
+
24
+ # Read input from stdin (hook receives JSON)
25
+ INPUT=$(cat)
26
+
27
+ # Extract event data
28
+ TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty')
29
+ TOOL_RESULT=$(echo "$INPUT" | jq -r '.tool_result // empty' 2>/dev/null || echo "")
30
+ COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null || echo "")
31
+
32
+ # Get timestamp
33
+ TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S")
34
+
35
+ # Helper: append to activity log
36
+ log() {
37
+ echo "[$TIMESTAMP] $1" >> "$ACTIVITY_LOG"
38
+ }
39
+
40
+ # Helper: append indented detail
41
+ log_detail() {
42
+ echo "[$TIMESTAMP] $1" >> "$ACTIVITY_LOG"
43
+ }
44
+
45
+ # Log rotation: rotate if file exceeds max size
46
+ rotate_log() {
47
+ if [[ -f "$ACTIVITY_LOG" ]]; then
48
+ local size
49
+ # macOS uses -f%z, Linux uses -c%s
50
+ size=$(stat -f%z "$ACTIVITY_LOG" 2>/dev/null || stat -c%s "$ACTIVITY_LOG" 2>/dev/null || echo 0)
51
+ if [[ "$size" -gt "$MAX_LOG_SIZE" ]]; then
52
+ mv "$ACTIVITY_LOG" "${ACTIVITY_LOG}.1"
53
+ log "━━━ LOG ROTATED ━━━"
54
+ fi
55
+ fi
56
+ }
57
+
58
+ # Detect event type from command output patterns
59
+ detect_event() {
60
+ local cmd="$1"
61
+ local result="$2"
62
+
63
+ # Session events (detected from progress.yaml changes)
64
+ if [[ "$cmd" == *"progress.yaml"* ]] && [[ "$result" == *"status: running"* ]]; then
65
+ echo "session_start"
66
+ return
67
+ fi
68
+
69
+ if [[ "$cmd" == *"progress.yaml"* ]] && [[ "$result" == *"status: paused"* ]]; then
70
+ echo "session_pause"
71
+ return
72
+ fi
73
+
74
+ # Test results
75
+ if [[ "$cmd" == *"test"* ]] || [[ "$cmd" == *"jest"* ]] || [[ "$cmd" == *"vitest"* ]] || [[ "$cmd" == *"pytest"* ]]; then
76
+ if [[ "$result" == *"PASS"* ]] || [[ "$result" == *"passed"* ]]; then
77
+ echo "test_pass"
78
+ elif [[ "$result" == *"FAIL"* ]] || [[ "$result" == *"failed"* ]]; then
79
+ echo "test_fail"
80
+ fi
81
+ return
82
+ fi
83
+
84
+ # Lint results
85
+ if [[ "$cmd" == *"lint"* ]] || [[ "$cmd" == *"eslint"* ]] || [[ "$cmd" == *"biome"* ]]; then
86
+ if [[ "$result" == *"error"* ]] || [[ "$result" == *"Error"* ]]; then
87
+ echo "lint_fail"
88
+ else
89
+ echo "lint_pass"
90
+ fi
91
+ return
92
+ fi
93
+
94
+ # Git commits
95
+ if [[ "$cmd" == git\ commit* ]]; then
96
+ echo "commit"
97
+ return
98
+ fi
99
+
100
+ # Quality verification
101
+ if [[ "$cmd" == *"quality"* ]] || [[ "$cmd" == *"verify"* ]]; then
102
+ echo "verify"
103
+ return
104
+ fi
105
+
106
+ echo "unknown"
107
+ }
108
+
109
+ # Extract TODO ID from context if available
110
+ get_current_todo() {
111
+ if [[ -f "progress.yaml" ]]; then
112
+ grep -oE "todo-[0-9]+\.[0-9]+[a-z]-[0-9]+" progress.yaml 2>/dev/null | head -1 || echo ""
113
+ else
114
+ echo ""
115
+ fi
116
+ }
117
+
118
+ # Main logging logic
119
+ main() {
120
+ # Rotate if needed
121
+ rotate_log
122
+
123
+ # Skip if not a Bash command (we mainly log execution events)
124
+ if [[ "$TOOL_NAME" != "Bash" ]]; then
125
+ exit 0
126
+ fi
127
+
128
+ # Skip if no command
129
+ if [[ -z "$COMMAND" ]]; then
130
+ exit 0
131
+ fi
132
+
133
+ local event
134
+ event=$(detect_event "$COMMAND" "$TOOL_RESULT")
135
+ local todo
136
+ todo=$(get_current_todo)
137
+
138
+ case "$event" in
139
+ "session_start")
140
+ log "━━━ SESSION START ━━━"
141
+ ;;
142
+
143
+ "session_pause")
144
+ log "⏸ PAUSE"
145
+ log "━━━ SESSION PAUSE ━━━"
146
+ ;;
147
+
148
+ "test_pass")
149
+ log_detail "└─ ✓ TEST PASS"
150
+ ;;
151
+
152
+ "test_fail")
153
+ local error_msg
154
+ error_msg=$(echo "$TOOL_RESULT" | grep -oE "(FAIL|Error|failed).*" | head -1 | cut -c1-60)
155
+ log_detail "└─ ✗ TEST FAIL: ${error_msg:-unknown error}"
156
+ ;;
157
+
158
+ "lint_pass")
159
+ log_detail "└─ ✓ LINT PASS"
160
+ ;;
161
+
162
+ "lint_fail")
163
+ log_detail "└─ ✗ LINT FAIL"
164
+ ;;
165
+
166
+ "commit")
167
+ local commit_hash
168
+ commit_hash=$(echo "$TOOL_RESULT" | grep -oE "[a-f0-9]{7,}" | head -1 || echo "???")
169
+ local commit_msg
170
+ commit_msg=$(echo "$COMMAND" | grep -oP "(?<=-m ['\"])[^'\"]+")
171
+ log "✓ COMMIT $commit_hash: ${commit_msg:0:50}"
172
+ ;;
173
+
174
+ "verify")
175
+ log_detail "└─ VERIFY complete"
176
+ ;;
177
+
178
+ *)
179
+ # Don't log unknown events to avoid noise
180
+ ;;
181
+ esac
182
+ }
183
+
184
+ # Run main
185
+ main
186
+
187
+ exit 0
@@ -0,0 +1,52 @@
1
+ #!/bin/bash
2
+ # Stop hook - prevents Claude from declaring victory with incomplete work
3
+ # From Trail of Bits research: "Claude has a tendency to declare victory while leaving work undone."
4
+
5
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
+
7
+ # Load enforcement tracking
8
+ if [[ -f "$SCRIPT_DIR/track-enforcement.sh" ]]; then
9
+ source "$SCRIPT_DIR/track-enforcement.sh"
10
+ elif [[ -f "$HOME/.claude/hooks/track-enforcement.sh" ]]; then
11
+ source "$HOME/.claude/hooks/track-enforcement.sh"
12
+ else
13
+ track_enforcement() { :; }
14
+ fi
15
+
16
+ INPUT=$(cat)
17
+ RESPONSE=$(echo "$INPUT" | jq -r '.response // empty')
18
+
19
+ # Patterns that indicate rationalization of incomplete work
20
+ PATTERNS=(
21
+ "pre-existing"
22
+ "out of scope"
23
+ "leave.*for.*follow-up"
24
+ "too many issues"
25
+ "will address later"
26
+ "beyond the scope"
27
+ "for now.*sufficient"
28
+ "good enough for"
29
+ "can be improved later"
30
+ "defer.*to.*next"
31
+ "revisit.*later"
32
+ "skip.*for now"
33
+ "not critical"
34
+ "low priority.*skip"
35
+ )
36
+
37
+ # Check each pattern
38
+ for pattern in "${PATTERNS[@]}"; do
39
+ if echo "$RESPONSE" | grep -qi "$pattern"; then
40
+ track_enforcement "rationalization_caught" "detected: '$pattern'" "anti-rationalize"
41
+ cat <<EOF
42
+ {
43
+ "decision": "block",
44
+ "reason": "Rationalization detected: '$pattern'. Please complete the task fully or explicitly document what remains incomplete with specific next steps. Use /found to log discovered work that must be deferred."
45
+ }
46
+ EOF
47
+ exit 0
48
+ fi
49
+ done
50
+
51
+ # Allow the stop
52
+ exit 0
@@ -0,0 +1,112 @@
1
+ #!/bin/bash
2
+ # capture-verification.sh
3
+ # Captures output from quality commands for verified execution
4
+ # Called by Claude Code hooks after quality commands run
5
+
6
+ set -e
7
+
8
+ VERIFY_DIR=".codecruise/verification"
9
+ mkdir -p "$VERIFY_DIR"
10
+
11
+ # Capture timestamp
12
+ TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
13
+ echo "$TIMESTAMP" > "$VERIFY_DIR/timestamp.txt"
14
+
15
+ # Initialize summary
16
+ SUMMARY_FILE="$VERIFY_DIR/summary.json"
17
+
18
+ # Function to run and capture a command
19
+ capture_command() {
20
+ local name=$1
21
+ local cmd=$2
22
+ local output_file="$VERIFY_DIR/${name}.txt"
23
+ local exit_file="$VERIFY_DIR/${name}-exit.txt"
24
+
25
+ if eval "$cmd" > "$output_file" 2>&1; then
26
+ echo "0" > "$exit_file"
27
+ else
28
+ echo "$?" > "$exit_file"
29
+ fi
30
+ }
31
+
32
+ # Detect package manager
33
+ if [ -f "pnpm-lock.yaml" ]; then
34
+ PM="pnpm"
35
+ elif [ -f "yarn.lock" ]; then
36
+ PM="yarn"
37
+ elif [ -f "package-lock.json" ]; then
38
+ PM="npm"
39
+ else
40
+ PM="npm"
41
+ fi
42
+
43
+ # Run lint if available
44
+ if grep -q '"lint"' package.json 2>/dev/null; then
45
+ capture_command "lint" "$PM run lint"
46
+ LINT_EXIT=$(cat "$VERIFY_DIR/lint-exit.txt")
47
+ LINT_ERRORS=$(grep -c "error" "$VERIFY_DIR/lint.txt" 2>/dev/null || echo "0")
48
+ LINT_WARNINGS=$(grep -c "warning" "$VERIFY_DIR/lint.txt" 2>/dev/null || echo "0")
49
+ else
50
+ LINT_EXIT="-1"
51
+ LINT_ERRORS="0"
52
+ LINT_WARNINGS="0"
53
+ echo "Lint script not found" > "$VERIFY_DIR/lint.txt"
54
+ fi
55
+
56
+ # Run typecheck if TypeScript project
57
+ if [ -f "tsconfig.json" ]; then
58
+ if grep -q '"typecheck"' package.json 2>/dev/null; then
59
+ capture_command "typecheck" "$PM run typecheck"
60
+ else
61
+ capture_command "typecheck" "npx tsc --noEmit"
62
+ fi
63
+ TSC_EXIT=$(cat "$VERIFY_DIR/typecheck-exit.txt")
64
+ TSC_ERRORS=$(grep -c "error TS" "$VERIFY_DIR/typecheck.txt" 2>/dev/null || echo "0")
65
+ else
66
+ TSC_EXIT="-1"
67
+ TSC_ERRORS="0"
68
+ echo "Not a TypeScript project" > "$VERIFY_DIR/typecheck.txt"
69
+ fi
70
+
71
+ # Run tests if available
72
+ if grep -q '"test"' package.json 2>/dev/null; then
73
+ capture_command "test" "$PM test -- --reporter=verbose 2>&1 || $PM test 2>&1"
74
+ TEST_EXIT=$(cat "$VERIFY_DIR/test-exit.txt")
75
+
76
+ # Try to parse test results (varies by test runner)
77
+ TEST_PASSED=$(grep -oE "[0-9]+ passed" "$VERIFY_DIR/test.txt" | grep -oE "[0-9]+" | head -1 || echo "0")
78
+ TEST_FAILED=$(grep -oE "[0-9]+ failed" "$VERIFY_DIR/test.txt" | grep -oE "[0-9]+" | head -1 || echo "0")
79
+ TEST_TOTAL=$((TEST_PASSED + TEST_FAILED))
80
+ else
81
+ TEST_EXIT="-1"
82
+ TEST_PASSED="0"
83
+ TEST_FAILED="0"
84
+ TEST_TOTAL="0"
85
+ echo "Test script not found" > "$VERIFY_DIR/test.txt"
86
+ fi
87
+
88
+ # Generate summary JSON
89
+ cat << EOF > "$SUMMARY_FILE"
90
+ {
91
+ "timestamp": "$TIMESTAMP",
92
+ "lint": {
93
+ "exit_code": $LINT_EXIT,
94
+ "errors": $LINT_ERRORS,
95
+ "warnings": $LINT_WARNINGS
96
+ },
97
+ "typecheck": {
98
+ "exit_code": $TSC_EXIT,
99
+ "errors": $TSC_ERRORS
100
+ },
101
+ "tests": {
102
+ "exit_code": $TEST_EXIT,
103
+ "total": $TEST_TOTAL,
104
+ "passed": $TEST_PASSED,
105
+ "failed": $TEST_FAILED
106
+ }
107
+ }
108
+ EOF
109
+
110
+ echo "---"
111
+ echo "Verification complete. Results saved to $VERIFY_DIR/"
112
+ echo "Summary: lint=$LINT_EXIT, tsc=$TSC_EXIT, test=$TEST_EXIT"
@@ -0,0 +1,135 @@
1
+ #!/bin/bash
2
+ # PostToolUse hook - Collects metrics for codecruise execution
3
+ #
4
+ # P2: Tracks success rates, retry rates, intervention rates
5
+ #
6
+ # Metrics collected:
7
+ # - TODO start/complete/fail/skip events
8
+ # - Retry counts
9
+ # - Duration per TODO
10
+ # - Quality gate results
11
+ # - Human interventions (when ask mode used)
12
+ #
13
+ # Stores metrics in .codecruise/metrics/
14
+
15
+ METRICS_DIR=".codecruise/metrics"
16
+ METRICS_FILE="$METRICS_DIR/events.jsonl"
17
+ SUMMARY_FILE="$METRICS_DIR/summary.json"
18
+
19
+ # Create metrics directory if needed
20
+ mkdir -p "$METRICS_DIR"
21
+
22
+ INPUT=$(cat)
23
+ TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty')
24
+ TOOL_INPUT=$(echo "$INPUT" | jq -r '.tool_input // {}')
25
+ TOOL_RESULT=$(echo "$INPUT" | jq -r '.tool_result // empty')
26
+ TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
27
+
28
+ # Helper to append event
29
+ append_event() {
30
+ local event_type="$1"
31
+ local data="$2"
32
+
33
+ echo "{\"timestamp\":\"$TIMESTAMP\",\"event\":\"$event_type\",\"data\":$data}" >> "$METRICS_FILE"
34
+ }
35
+
36
+ # Detect TODO-related events based on tool usage patterns
37
+
38
+ # Track Bash commands (test runs, quality checks)
39
+ if [[ "$TOOL" == "Bash" ]]; then
40
+ COMMAND=$(echo "$TOOL_INPUT" | jq -r '.command // empty')
41
+
42
+ # Detect test runs
43
+ if echo "$COMMAND" | grep -qE "(npm|pnpm|yarn|bun) (run )?(test|vitest|jest)"; then
44
+ # Check if tests passed or failed based on exit code
45
+ EXIT_CODE=$(echo "$TOOL_RESULT" | grep -oE "exit code:? [0-9]+" | grep -oE "[0-9]+" | tail -1 || echo "0")
46
+
47
+ if [[ "$EXIT_CODE" == "0" ]]; then
48
+ append_event "test_pass" "{\"command\":\"$COMMAND\"}"
49
+ else
50
+ append_event "test_fail" "{\"command\":\"$COMMAND\",\"exit_code\":$EXIT_CODE}"
51
+ fi
52
+ fi
53
+
54
+ # Detect lint runs
55
+ if echo "$COMMAND" | grep -qE "(npm|pnpm|yarn|bun) (run )?(lint|eslint)"; then
56
+ EXIT_CODE=$(echo "$TOOL_RESULT" | grep -oE "exit code:? [0-9]+" | grep -oE "[0-9]+" | tail -1 || echo "0")
57
+
58
+ if [[ "$EXIT_CODE" == "0" ]]; then
59
+ append_event "lint_pass" "{\"command\":\"$COMMAND\"}"
60
+ else
61
+ append_event "lint_fail" "{\"command\":\"$COMMAND\",\"exit_code\":$EXIT_CODE}"
62
+ fi
63
+ fi
64
+
65
+ # Detect commits
66
+ if echo "$COMMAND" | grep -qE "git commit"; then
67
+ append_event "commit" "{\"command\":\"$COMMAND\"}"
68
+ fi
69
+ fi
70
+
71
+ # Track file edits (implementation activity)
72
+ if [[ "$TOOL" == "Write" || "$TOOL" == "Edit" ]]; then
73
+ FILE_PATH=$(echo "$TOOL_INPUT" | jq -r '.file_path // empty')
74
+
75
+ # Categorize file type
76
+ if [[ "$FILE_PATH" == *.test.* || "$FILE_PATH" == *.spec.* ]]; then
77
+ append_event "test_written" "{\"file\":\"$FILE_PATH\"}"
78
+ elif [[ "$FILE_PATH" == *.ts || "$FILE_PATH" == *.tsx || "$FILE_PATH" == *.js || "$FILE_PATH" == *.jsx ]]; then
79
+ append_event "code_written" "{\"file\":\"$FILE_PATH\"}"
80
+ fi
81
+ fi
82
+
83
+ # Update summary periodically (every 10 events)
84
+ EVENT_COUNT=$(wc -l < "$METRICS_FILE" 2>/dev/null || echo "0")
85
+ if (( EVENT_COUNT % 10 == 0 )); then
86
+ # Calculate summary statistics
87
+ TOTAL_EVENTS=$EVENT_COUNT
88
+ TEST_PASSES=$(grep -c '"event":"test_pass"' "$METRICS_FILE" 2>/dev/null || echo "0")
89
+ TEST_FAILS=$(grep -c '"event":"test_fail"' "$METRICS_FILE" 2>/dev/null || echo "0")
90
+ LINT_PASSES=$(grep -c '"event":"lint_pass"' "$METRICS_FILE" 2>/dev/null || echo "0")
91
+ LINT_FAILS=$(grep -c '"event":"lint_fail"' "$METRICS_FILE" 2>/dev/null || echo "0")
92
+ COMMITS=$(grep -c '"event":"commit"' "$METRICS_FILE" 2>/dev/null || echo "0")
93
+ TESTS_WRITTEN=$(grep -c '"event":"test_written"' "$METRICS_FILE" 2>/dev/null || echo "0")
94
+ CODE_WRITTEN=$(grep -c '"event":"code_written"' "$METRICS_FILE" 2>/dev/null || echo "0")
95
+
96
+ # Calculate rates
97
+ TOTAL_TESTS=$((TEST_PASSES + TEST_FAILS))
98
+ if (( TOTAL_TESTS > 0 )); then
99
+ TEST_SUCCESS_RATE=$(echo "scale=2; $TEST_PASSES * 100 / $TOTAL_TESTS" | bc)
100
+ else
101
+ TEST_SUCCESS_RATE="N/A"
102
+ fi
103
+
104
+ TOTAL_LINTS=$((LINT_PASSES + LINT_FAILS))
105
+ if (( TOTAL_LINTS > 0 )); then
106
+ LINT_SUCCESS_RATE=$(echo "scale=2; $LINT_PASSES * 100 / $TOTAL_LINTS" | bc)
107
+ else
108
+ LINT_SUCCESS_RATE="N/A"
109
+ fi
110
+
111
+ cat << EOF > "$SUMMARY_FILE"
112
+ {
113
+ "updated_at": "$TIMESTAMP",
114
+ "total_events": $TOTAL_EVENTS,
115
+ "tests": {
116
+ "passes": $TEST_PASSES,
117
+ "fails": $TEST_FAILS,
118
+ "success_rate": "$TEST_SUCCESS_RATE%"
119
+ },
120
+ "lint": {
121
+ "passes": $LINT_PASSES,
122
+ "fails": $LINT_FAILS,
123
+ "success_rate": "$LINT_SUCCESS_RATE%"
124
+ },
125
+ "activity": {
126
+ "commits": $COMMITS,
127
+ "tests_written": $TESTS_WRITTEN,
128
+ "code_written": $CODE_WRITTEN
129
+ }
130
+ }
131
+ EOF
132
+ fi
133
+
134
+ # Always exit successfully - metrics are passive
135
+ exit 0
@@ -0,0 +1,75 @@
1
+ #!/bin/bash
2
+ # PreToolUse hook - blocks edits outside owned file scope
3
+ # Essential for parallel execution with git worktrees
4
+
5
+ INPUT=$(cat)
6
+ TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty')
7
+ FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
8
+
9
+ # Skip if not a file operation
10
+ if [[ "$TOOL" != "Edit" && "$TOOL" != "Write" ]]; then
11
+ exit 0
12
+ fi
13
+
14
+ # Skip if no file path
15
+ if [[ -z "$FILE" ]]; then
16
+ exit 0
17
+ fi
18
+
19
+ # Check for scope file
20
+ SCOPE_FILE=".claude/file_scope.txt"
21
+ if [[ ! -f "$SCOPE_FILE" ]]; then
22
+ # No scope restriction - allow all
23
+ exit 0
24
+ fi
25
+
26
+ # Normalize file path (remove leading ./)
27
+ FILE="${FILE#./}"
28
+
29
+ # Check if file matches any pattern in scope
30
+ MATCH=false
31
+ while IFS= read -r pattern || [[ -n "$pattern" ]]; do
32
+ # Skip empty lines and comments
33
+ [[ -z "$pattern" || "$pattern" == \#* ]] && continue
34
+
35
+ # Trim whitespace
36
+ pattern=$(echo "$pattern" | xargs)
37
+
38
+ # Handle glob patterns
39
+ # Convert glob to regex-like matching
40
+ if [[ "$FILE" == $pattern ]]; then
41
+ MATCH=true
42
+ break
43
+ fi
44
+
45
+ # Also check if file is under a directory pattern
46
+ if [[ "$pattern" == *"/**/*" ]]; then
47
+ dir="${pattern%/**/*}"
48
+ if [[ "$FILE" == "$dir"/* ]]; then
49
+ MATCH=true
50
+ break
51
+ fi
52
+ elif [[ "$pattern" == *"/*" ]]; then
53
+ dir="${pattern%/*}"
54
+ if [[ "$FILE" == "$dir"/* ]]; then
55
+ MATCH=true
56
+ break
57
+ fi
58
+ fi
59
+ done < "$SCOPE_FILE"
60
+
61
+ if [[ "$MATCH" == "false" ]]; then
62
+ SCOPE_CONTENT=$(cat "$SCOPE_FILE" | grep -v '^#' | grep -v '^$' | head -5 | tr '\n' ', ')
63
+ cat <<EOF
64
+ {
65
+ "hookSpecificOutput": {
66
+ "hookEventName": "PreToolUse",
67
+ "permissionDecision": "deny",
68
+ "permissionDecisionReason": "File '$FILE' is outside your file scope. Your scope: $SCOPE_CONTENT. This prevents conflicts during parallel execution. If you need to modify this file, coordinate with the owner or update .claude/file_scope.txt."
69
+ }
70
+ }
71
+ EOF
72
+ exit 0
73
+ fi
74
+
75
+ exit 0