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.
- package/LICENSE +21 -0
- package/README.md +111 -0
- package/bin/codecruise.js +68 -0
- package/config/CLAUDE.md +107 -0
- package/config/agents/analyst.md +48 -0
- package/config/agents/architect-reviewer.md +161 -0
- package/config/agents/architect.md +119 -0
- package/config/agents/critic.md +63 -0
- package/config/agents/developer.md +96 -0
- package/config/agents/devops.md +81 -0
- package/config/agents/orchestrator.md +91 -0
- package/config/agents/planner.md +139 -0
- package/config/agents/retro.md +52 -0
- package/config/agents/reviewer.md +101 -0
- package/config/agents/security-reviewer.md +57 -0
- package/config/agents/stack/expo/AGENT.md +473 -0
- package/config/agents/stack/expo/rules/critical.md +427 -0
- package/config/agents/stack/expo/rules/native.md +455 -0
- package/config/agents/stack/expo/rules/navigation.md +445 -0
- package/config/agents/stack/expo/rules/performance.md +415 -0
- package/config/agents/stack/fastify/AGENT.md +397 -0
- package/config/agents/stack/fastify/rules/api-design.md +283 -0
- package/config/agents/stack/fastify/rules/critical.md +232 -0
- package/config/agents/stack/fastify/rules/queues.md +303 -0
- package/config/agents/stack/fastify/rules/security.md +384 -0
- package/config/agents/stack/index.yaml +48 -0
- package/config/agents/stack/nextjs/AGENT.md +421 -0
- package/config/agents/stack/nextjs/rules/components.md +413 -0
- package/config/agents/stack/nextjs/rules/critical.md +391 -0
- package/config/agents/stack/nextjs/rules/performance.md +403 -0
- package/config/agents/stack/nextjs/rules/styling.md +334 -0
- package/config/agents/stack/shared-ts/AGENT.md +384 -0
- package/config/agents/stack/shared-ts/rules/critical.md +315 -0
- package/config/agents/stack/shared-ts/rules/patterns.md +384 -0
- package/config/agents/stack/shared-ts/rules/zod.md +427 -0
- package/config/agents/tester.md +79 -0
- package/config/commands/architect-discuss.md +366 -0
- package/config/commands/architect-list.md +160 -0
- package/config/commands/architect-review.md +111 -0
- package/config/commands/architect.md +118 -0
- package/config/commands/compact.md +118 -0
- package/config/commands/companion.md +279 -0
- package/config/commands/dashboard.md +152 -0
- package/config/commands/doctor.md +227 -0
- package/config/commands/dogfood-report.md +101 -0
- package/config/commands/flags/run-autonomous.md +110 -0
- package/config/commands/flags/run-pause.md +80 -0
- package/config/commands/ingest.md +173 -0
- package/config/commands/init.md +128 -0
- package/config/commands/metrics.md +87 -0
- package/config/commands/parallel.md +320 -0
- package/config/commands/pause.md +55 -0
- package/config/commands/plan-review.md +130 -0
- package/config/commands/plan.md +216 -0
- package/config/commands/production-check.md +308 -0
- package/config/commands/refine.md +323 -0
- package/config/commands/resume.md +72 -0
- package/config/commands/retro.md +121 -0
- package/config/commands/retry.md +75 -0
- package/config/commands/role.md +310 -0
- package/config/commands/run.md +417 -0
- package/config/commands/scope.md +85 -0
- package/config/commands/setup-permissions.md +104 -0
- package/config/commands/skip.md +75 -0
- package/config/commands/spec-forge.md +213 -0
- package/config/commands/spec-help.md +194 -0
- package/config/commands/spec-patch.md +342 -0
- package/config/commands/spec-resolve.md +110 -0
- package/config/commands/spec-review.md +153 -0
- package/config/commands/status.md +114 -0
- package/config/commands/sync.md +131 -0
- package/config/commands/task.md +138 -0
- package/config/commands/verify.md +124 -0
- package/config/hooks/README.md +632 -0
- package/config/hooks/activity-log.sh +187 -0
- package/config/hooks/anti-rationalize.sh +52 -0
- package/config/hooks/capture-verification.sh +112 -0
- package/config/hooks/collect-metrics.sh +135 -0
- package/config/hooks/enforce-file-scope.sh +75 -0
- package/config/hooks/enforce-state-machine.sh +161 -0
- package/config/hooks/enforce-tdd.sh +180 -0
- package/config/hooks/format.sh +40 -0
- package/config/hooks/lib/activity-helpers.sh +162 -0
- package/config/hooks/lib/read-settings.sh +71 -0
- package/config/hooks/load-context-skills.sh +95 -0
- package/config/hooks/notify.sh +81 -0
- package/config/hooks/pre-commit.sample +35 -0
- package/config/hooks/protect-files.sh +63 -0
- package/config/hooks/track-agents.sh +41 -0
- package/config/hooks/track-commands.sh +37 -0
- package/config/hooks/track-enforcement.sh +44 -0
- package/config/hooks/track-ooda.sh +77 -0
- package/config/hooks/validate-commit-msg.sh +35 -0
- package/config/hooks/validate-plan.sh +213 -0
- package/config/hooks/verify-criteria.sh +46 -0
- package/config/hooks/verify-todo-completion.sh +140 -0
- package/config/rules/comments.md +25 -0
- package/config/rules/decision-rules.md +308 -0
- package/config/rules/hygiene.md +247 -0
- package/config/rules/pattern-detection.md +372 -0
- package/config/rules/profiles.md +193 -0
- package/config/rules/recovery.md +83 -0
- package/config/rules/scope-detection.md +213 -0
- package/config/rules/standards.md +127 -0
- package/config/rules/workflow.md +121 -0
- package/config/schemas.md +767 -0
- package/config/settings.json +195 -0
- package/config/skills/backend/SKILL.md +734 -0
- package/config/skills/database/SKILL.md +426 -0
- package/config/skills/frontend/SKILL.md +434 -0
- package/config/skills/git/SKILL.md +396 -0
- package/config/skills/index.yaml +36 -0
- package/config/skills/observability/SKILL.md +430 -0
- package/config/skills/package-dev/SKILL.md +498 -0
- package/config/skills/performance/SKILL.md +378 -0
- package/config/skills/resilience/SKILL.md +573 -0
- package/config/skills/testing/SKILL.md +398 -0
- package/config/skills/testing-patterns/SKILL.md +276 -0
- package/config/skills/typescript/SKILL.md +152 -0
- package/config/templates/CLAUDE.md +70 -0
- package/config/templates/README.md +117 -0
- package/config/templates/steering/adr-template.md +102 -0
- package/config/templates/steering/product.md +60 -0
- package/config/templates/steering/rfc-template.md +159 -0
- package/config/templates/steering/structure.md +146 -0
- package/config/templates/steering/tech.md +85 -0
- package/package.json +40 -0
- package/src/install.js +163 -0
- 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
|