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,161 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# PreToolUse hook - Enforces valid state transitions in progress.yaml
|
|
3
|
+
#
|
|
4
|
+
# P1 Enforcement: Prevents invalid TODO state transitions
|
|
5
|
+
#
|
|
6
|
+
# Valid transitions:
|
|
7
|
+
# pending → in_progress
|
|
8
|
+
# in_progress → done, blocked, failed
|
|
9
|
+
# blocked → in_progress
|
|
10
|
+
# failed → in_progress, skipped
|
|
11
|
+
# done → (terminal, no transitions)
|
|
12
|
+
# skipped → (terminal, no transitions)
|
|
13
|
+
#
|
|
14
|
+
# Also enforces:
|
|
15
|
+
# - Cannot mark done without passing quality gates
|
|
16
|
+
# - Cannot skip from pending (must attempt first)
|
|
17
|
+
|
|
18
|
+
# Load settings library
|
|
19
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
20
|
+
if [[ -f "$SCRIPT_DIR/lib/read-settings.sh" ]]; then
|
|
21
|
+
source "$SCRIPT_DIR/lib/read-settings.sh"
|
|
22
|
+
elif [[ -f "$HOME/.claude/hooks/lib/read-settings.sh" ]]; then
|
|
23
|
+
source "$HOME/.claude/hooks/lib/read-settings.sh"
|
|
24
|
+
else
|
|
25
|
+
get_enforce_mode() { echo "${CODECRUISE_ENFORCE:-strict}"; }
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
# Load enforcement tracking
|
|
29
|
+
if [[ -f "$SCRIPT_DIR/track-enforcement.sh" ]]; then
|
|
30
|
+
source "$SCRIPT_DIR/track-enforcement.sh"
|
|
31
|
+
elif [[ -f "$HOME/.claude/hooks/track-enforcement.sh" ]]; then
|
|
32
|
+
source "$HOME/.claude/hooks/track-enforcement.sh"
|
|
33
|
+
else
|
|
34
|
+
track_enforcement() { :; }
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
INPUT=$(cat)
|
|
38
|
+
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty')
|
|
39
|
+
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
|
|
40
|
+
NEW_CONTENT=$(echo "$INPUT" | jq -r '.tool_input.content // empty')
|
|
41
|
+
|
|
42
|
+
# Only check Edit/Write to progress.yaml or roadmap files
|
|
43
|
+
if [[ "$TOOL" != "Write" && "$TOOL" != "Edit" ]]; then
|
|
44
|
+
exit 0
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# Check if editing state files
|
|
48
|
+
if [[ "$FILE" != *"progress.yaml"* && "$FILE" != *"roadmap/"* ]]; then
|
|
49
|
+
exit 0
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
# Get enforcement mode
|
|
53
|
+
ENFORCE_MODE=$(get_enforce_mode "state")
|
|
54
|
+
|
|
55
|
+
if [[ "$ENFORCE_MODE" == "off" ]]; then
|
|
56
|
+
exit 0
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
# For progress.yaml edits, validate state transitions
|
|
60
|
+
if [[ "$FILE" == *"progress.yaml"* ]]; then
|
|
61
|
+
# Read current state
|
|
62
|
+
if [[ -f "progress.yaml" ]]; then
|
|
63
|
+
CURRENT_STATUS=$(grep -E "^\s*status:" progress.yaml | head -1 | sed 's/.*status:\s*//' | tr -d ' "' || echo "")
|
|
64
|
+
else
|
|
65
|
+
CURRENT_STATUS=""
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
# Try to extract new status from the edit
|
|
69
|
+
# This is tricky because we get the new content, need to parse what's changing
|
|
70
|
+
NEW_STATUS=$(echo "$NEW_CONTENT" | grep -E "status:" | head -1 | sed 's/.*status:\s*//' | tr -d ' "' || echo "")
|
|
71
|
+
|
|
72
|
+
# If no status change detected, allow
|
|
73
|
+
if [[ -z "$NEW_STATUS" || "$NEW_STATUS" == "$CURRENT_STATUS" ]]; then
|
|
74
|
+
exit 0
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
# Define valid transitions
|
|
78
|
+
VALID=false
|
|
79
|
+
case "$CURRENT_STATUS" in
|
|
80
|
+
"pending")
|
|
81
|
+
[[ "$NEW_STATUS" == "in_progress" ]] && VALID=true
|
|
82
|
+
;;
|
|
83
|
+
"in_progress")
|
|
84
|
+
[[ "$NEW_STATUS" == "done" || "$NEW_STATUS" == "blocked" || "$NEW_STATUS" == "failed" ]] && VALID=true
|
|
85
|
+
;;
|
|
86
|
+
"blocked")
|
|
87
|
+
[[ "$NEW_STATUS" == "in_progress" ]] && VALID=true
|
|
88
|
+
;;
|
|
89
|
+
"failed")
|
|
90
|
+
[[ "$NEW_STATUS" == "in_progress" || "$NEW_STATUS" == "skipped" ]] && VALID=true
|
|
91
|
+
;;
|
|
92
|
+
"done"|"skipped")
|
|
93
|
+
# Terminal states - no transitions allowed
|
|
94
|
+
VALID=false
|
|
95
|
+
;;
|
|
96
|
+
"")
|
|
97
|
+
# No current status - allow setting initial status
|
|
98
|
+
VALID=true
|
|
99
|
+
;;
|
|
100
|
+
*)
|
|
101
|
+
# Unknown current status - allow
|
|
102
|
+
VALID=true
|
|
103
|
+
;;
|
|
104
|
+
esac
|
|
105
|
+
|
|
106
|
+
if [[ "$VALID" == false ]]; then
|
|
107
|
+
if [[ "$ENFORCE_MODE" == "ask" ]]; then
|
|
108
|
+
DECISION="ask"
|
|
109
|
+
REASON="Invalid state transition: $CURRENT_STATUS → $NEW_STATUS. Allow anyway?"
|
|
110
|
+
track_enforcement "state_ask" "$CURRENT_STATUS -> $NEW_STATUS" "enforce-state-machine"
|
|
111
|
+
else
|
|
112
|
+
DECISION="deny"
|
|
113
|
+
REASON="Invalid state transition: $CURRENT_STATUS → $NEW_STATUS. Valid transitions: pending→in_progress, in_progress→done/blocked/failed, blocked→in_progress, failed→in_progress/skipped. Terminal states (done, skipped) cannot transition."
|
|
114
|
+
track_enforcement "state_block" "$CURRENT_STATUS -> $NEW_STATUS" "enforce-state-machine"
|
|
115
|
+
fi
|
|
116
|
+
|
|
117
|
+
cat <<EOF
|
|
118
|
+
{
|
|
119
|
+
"hookSpecificOutput": {
|
|
120
|
+
"hookEventName": "PreToolUse",
|
|
121
|
+
"permissionDecision": "$DECISION",
|
|
122
|
+
"permissionDecisionReason": "$REASON"
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
EOF
|
|
126
|
+
exit 0
|
|
127
|
+
fi
|
|
128
|
+
|
|
129
|
+
# Additional check: Cannot mark done without quality gates
|
|
130
|
+
if [[ "$NEW_STATUS" == "done" ]]; then
|
|
131
|
+
SUMMARY_FILE=".codecruise/verification/summary.json"
|
|
132
|
+
if [[ -f "$SUMMARY_FILE" ]]; then
|
|
133
|
+
TEST_EXIT=$(jq -r '.tests.exit_code // 0' "$SUMMARY_FILE")
|
|
134
|
+
LINT_EXIT=$(jq -r '.lint.exit_code // 0' "$SUMMARY_FILE")
|
|
135
|
+
|
|
136
|
+
if [[ "$TEST_EXIT" != "0" && "$TEST_EXIT" != "-1" ]] || [[ "$LINT_EXIT" != "0" && "$LINT_EXIT" != "-1" ]]; then
|
|
137
|
+
if [[ "$ENFORCE_MODE" == "ask" ]]; then
|
|
138
|
+
DECISION="ask"
|
|
139
|
+
REASON="Quality gates not passed (test=$TEST_EXIT, lint=$LINT_EXIT). Mark as done anyway?"
|
|
140
|
+
else
|
|
141
|
+
DECISION="deny"
|
|
142
|
+
REASON="Cannot mark TODO as done: quality gates not passed (test exit=$TEST_EXIT, lint exit=$LINT_EXIT). Fix issues first or use /skip to skip this TODO."
|
|
143
|
+
fi
|
|
144
|
+
|
|
145
|
+
cat <<EOF
|
|
146
|
+
{
|
|
147
|
+
"hookSpecificOutput": {
|
|
148
|
+
"hookEventName": "PreToolUse",
|
|
149
|
+
"permissionDecision": "$DECISION",
|
|
150
|
+
"permissionDecisionReason": "$REASON"
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
EOF
|
|
154
|
+
exit 0
|
|
155
|
+
fi
|
|
156
|
+
fi
|
|
157
|
+
fi
|
|
158
|
+
fi
|
|
159
|
+
|
|
160
|
+
# Allow the operation
|
|
161
|
+
exit 0
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# PreToolUse hook - Enforces TDD by requiring tests before implementation
|
|
3
|
+
#
|
|
4
|
+
# P0 Enforcement: Blocks writes to implementation files unless:
|
|
5
|
+
# 1. A corresponding test file already exists, OR
|
|
6
|
+
# 2. The file being written IS a test file, OR
|
|
7
|
+
# 3. The file is configuration/non-code
|
|
8
|
+
#
|
|
9
|
+
# Configuration (highest to lowest priority):
|
|
10
|
+
# 1. Command flag: /run --enforce=off
|
|
11
|
+
# 2. Project: .claude/settings.json → codecruise.enforce_tdd
|
|
12
|
+
# 3. User: ~/.claude/settings.json → codecruise.enforce_tdd
|
|
13
|
+
# 4. Default: strict
|
|
14
|
+
|
|
15
|
+
# Load settings library
|
|
16
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
17
|
+
if [[ -f "$SCRIPT_DIR/lib/read-settings.sh" ]]; then
|
|
18
|
+
source "$SCRIPT_DIR/lib/read-settings.sh"
|
|
19
|
+
elif [[ -f "$HOME/.claude/hooks/lib/read-settings.sh" ]]; then
|
|
20
|
+
source "$HOME/.claude/hooks/lib/read-settings.sh"
|
|
21
|
+
else
|
|
22
|
+
# Fallback if library not found
|
|
23
|
+
get_enforce_mode() { echo "${CODECRUISE_ENFORCE:-strict}"; }
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
# Load enforcement tracking
|
|
27
|
+
if [[ -f "$SCRIPT_DIR/track-enforcement.sh" ]]; then
|
|
28
|
+
source "$SCRIPT_DIR/track-enforcement.sh"
|
|
29
|
+
elif [[ -f "$HOME/.claude/hooks/track-enforcement.sh" ]]; then
|
|
30
|
+
source "$HOME/.claude/hooks/track-enforcement.sh"
|
|
31
|
+
else
|
|
32
|
+
track_enforcement() { :; } # no-op fallback
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
INPUT=$(cat)
|
|
36
|
+
|
|
37
|
+
# Get enforcement mode from settings hierarchy
|
|
38
|
+
ENFORCE_MODE=$(get_enforce_mode "tdd")
|
|
39
|
+
|
|
40
|
+
# If enforcement is off, allow everything
|
|
41
|
+
if [[ "$ENFORCE_MODE" == "off" ]]; then
|
|
42
|
+
exit 0
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty')
|
|
46
|
+
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
|
|
47
|
+
|
|
48
|
+
# Only check Write and Edit operations
|
|
49
|
+
if [[ "$TOOL" != "Write" && "$TOOL" != "Edit" ]]; then
|
|
50
|
+
exit 0
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
# Skip if no file path
|
|
54
|
+
if [[ -z "$FILE" ]]; then
|
|
55
|
+
exit 0
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
# Skip if not in a codecruise project
|
|
59
|
+
if [[ ! -f "progress.yaml" ]]; then
|
|
60
|
+
exit 0
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
# Get execution status - only enforce during active TODO execution
|
|
64
|
+
EXEC_STATUS=$(grep "status:" progress.yaml 2>/dev/null | head -1 | sed 's/.*status: //' | tr -d ' "' || echo "")
|
|
65
|
+
if [[ "$EXEC_STATUS" != "running" && "$EXEC_STATUS" != "in_progress" ]]; then
|
|
66
|
+
exit 0
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
# Files that don't need tests (configuration, assets, etc.)
|
|
70
|
+
SKIP_PATTERNS=(
|
|
71
|
+
"*.json"
|
|
72
|
+
"*.yaml"
|
|
73
|
+
"*.yml"
|
|
74
|
+
"*.md"
|
|
75
|
+
"*.txt"
|
|
76
|
+
"*.css"
|
|
77
|
+
"*.scss"
|
|
78
|
+
"*.svg"
|
|
79
|
+
"*.png"
|
|
80
|
+
"*.jpg"
|
|
81
|
+
"*.ico"
|
|
82
|
+
"*.env*"
|
|
83
|
+
"*.config.*"
|
|
84
|
+
"*.d.ts"
|
|
85
|
+
"tsconfig*"
|
|
86
|
+
"package.json"
|
|
87
|
+
"pnpm-lock.yaml"
|
|
88
|
+
".gitignore"
|
|
89
|
+
".eslintrc*"
|
|
90
|
+
".prettierrc*"
|
|
91
|
+
"vite.config.*"
|
|
92
|
+
"next.config.*"
|
|
93
|
+
"tailwind.config.*"
|
|
94
|
+
"postcss.config.*"
|
|
95
|
+
"prisma/*"
|
|
96
|
+
"migrations/*"
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# Check if file matches skip patterns
|
|
100
|
+
for pattern in "${SKIP_PATTERNS[@]}"; do
|
|
101
|
+
if [[ "$FILE" == $pattern ]]; then
|
|
102
|
+
exit 0
|
|
103
|
+
fi
|
|
104
|
+
done
|
|
105
|
+
|
|
106
|
+
# Check if this IS a test file (allow these)
|
|
107
|
+
if [[ "$FILE" == *.test.* ]] || \
|
|
108
|
+
[[ "$FILE" == *.spec.* ]] || \
|
|
109
|
+
[[ "$FILE" == *_test.* ]] || \
|
|
110
|
+
[[ "$FILE" == *_spec.* ]] || \
|
|
111
|
+
[[ "$FILE" == */__tests__/* ]] || \
|
|
112
|
+
[[ "$FILE" == */test/* ]] || \
|
|
113
|
+
[[ "$FILE" == */tests/* ]] || \
|
|
114
|
+
[[ "$FILE" == */e2e/* ]]; then
|
|
115
|
+
exit 0
|
|
116
|
+
fi
|
|
117
|
+
|
|
118
|
+
# Check if this is a source file that needs a test
|
|
119
|
+
if [[ "$FILE" == *.ts ]] || \
|
|
120
|
+
[[ "$FILE" == *.tsx ]] || \
|
|
121
|
+
[[ "$FILE" == *.js ]] || \
|
|
122
|
+
[[ "$FILE" == *.jsx ]] || \
|
|
123
|
+
[[ "$FILE" == *.py ]] || \
|
|
124
|
+
[[ "$FILE" == *.go ]] || \
|
|
125
|
+
[[ "$FILE" == *.rs ]]; then
|
|
126
|
+
|
|
127
|
+
# Get base name and extension
|
|
128
|
+
BASENAME="${FILE%.*}"
|
|
129
|
+
EXT="${FILE##*.}"
|
|
130
|
+
DIR=$(dirname "$FILE")
|
|
131
|
+
FILENAME=$(basename "$FILE")
|
|
132
|
+
FILENAME_NO_EXT="${FILENAME%.*}"
|
|
133
|
+
|
|
134
|
+
# Possible test file patterns
|
|
135
|
+
TEST_PATTERNS=(
|
|
136
|
+
"${BASENAME}.test.${EXT}"
|
|
137
|
+
"${BASENAME}.spec.${EXT}"
|
|
138
|
+
"${BASENAME}_test.${EXT}"
|
|
139
|
+
"${DIR}/__tests__/${FILENAME_NO_EXT}.test.${EXT}"
|
|
140
|
+
"${DIR}/__tests__/${FILENAME_NO_EXT}.spec.${EXT}"
|
|
141
|
+
"${DIR}/tests/${FILENAME_NO_EXT}.test.${EXT}"
|
|
142
|
+
"${DIR}/test/${FILENAME_NO_EXT}.test.${EXT}"
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
# Check if any test file exists
|
|
146
|
+
TEST_EXISTS=false
|
|
147
|
+
for test_file in "${TEST_PATTERNS[@]}"; do
|
|
148
|
+
if [[ -f "$test_file" ]]; then
|
|
149
|
+
TEST_EXISTS=true
|
|
150
|
+
break
|
|
151
|
+
fi
|
|
152
|
+
done
|
|
153
|
+
|
|
154
|
+
if [[ "$TEST_EXISTS" == false ]]; then
|
|
155
|
+
# Determine action based on enforcement mode
|
|
156
|
+
if [[ "$ENFORCE_MODE" == "ask" ]]; then
|
|
157
|
+
DECISION="ask"
|
|
158
|
+
REASON="TDD: No test file found. Expected: ${BASENAME}.test.${EXT}. Allow implementation anyway?"
|
|
159
|
+
track_enforcement "tdd_ask" "Missing test for $FILE" "enforce-tdd"
|
|
160
|
+
else
|
|
161
|
+
DECISION="deny"
|
|
162
|
+
REASON="TDD violation: Write tests first before implementing. Expected test file: ${BASENAME}.test.${EXT} or ${DIR}/__tests__/${FILENAME_NO_EXT}.test.${EXT}. Create the test file with failing tests (RED), then implement (GREEN)."
|
|
163
|
+
track_enforcement "tdd_block" "Missing test for $FILE" "enforce-tdd"
|
|
164
|
+
fi
|
|
165
|
+
|
|
166
|
+
cat <<EOF
|
|
167
|
+
{
|
|
168
|
+
"hookSpecificOutput": {
|
|
169
|
+
"hookEventName": "PreToolUse",
|
|
170
|
+
"permissionDecision": "$DECISION",
|
|
171
|
+
"permissionDecisionReason": "$REASON"
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
EOF
|
|
175
|
+
exit 0
|
|
176
|
+
fi
|
|
177
|
+
fi
|
|
178
|
+
|
|
179
|
+
# Allow the operation
|
|
180
|
+
exit 0
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# PostToolUse hook - auto-formats files after edit/write
|
|
3
|
+
# "Handles the last 10% before CI" — Boris Cherny
|
|
4
|
+
|
|
5
|
+
INPUT=$(cat)
|
|
6
|
+
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
|
|
7
|
+
|
|
8
|
+
if [[ -z "$FILE" || ! -f "$FILE" ]]; then
|
|
9
|
+
exit 0
|
|
10
|
+
fi
|
|
11
|
+
|
|
12
|
+
EXT="${FILE##*.}"
|
|
13
|
+
|
|
14
|
+
case "$EXT" in
|
|
15
|
+
js|ts|jsx|tsx|json|css|scss|md|html|yaml|yml)
|
|
16
|
+
if command -v npx &> /dev/null; then
|
|
17
|
+
npx prettier --write "$FILE" 2>/dev/null || true
|
|
18
|
+
fi
|
|
19
|
+
;;
|
|
20
|
+
py)
|
|
21
|
+
if command -v black &> /dev/null; then
|
|
22
|
+
black "$FILE" 2>/dev/null || true
|
|
23
|
+
fi
|
|
24
|
+
if command -v isort &> /dev/null; then
|
|
25
|
+
isort "$FILE" 2>/dev/null || true
|
|
26
|
+
fi
|
|
27
|
+
;;
|
|
28
|
+
go)
|
|
29
|
+
if command -v gofmt &> /dev/null; then
|
|
30
|
+
gofmt -w "$FILE" 2>/dev/null || true
|
|
31
|
+
fi
|
|
32
|
+
;;
|
|
33
|
+
rs)
|
|
34
|
+
if command -v rustfmt &> /dev/null; then
|
|
35
|
+
rustfmt "$FILE" 2>/dev/null || true
|
|
36
|
+
fi
|
|
37
|
+
;;
|
|
38
|
+
esac
|
|
39
|
+
|
|
40
|
+
exit 0
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# activity-helpers.sh — Helper functions for activity logging
|
|
3
|
+
#
|
|
4
|
+
# Source this in hooks or commands to log OODA events:
|
|
5
|
+
# source ~/.claude/hooks/lib/activity-helpers.sh
|
|
6
|
+
#
|
|
7
|
+
# Usage:
|
|
8
|
+
# log_ooda_observe "todo-1.1b-004"
|
|
9
|
+
# log_ooda_orient "0.85" ""
|
|
10
|
+
# log_ooda_decide "execute"
|
|
11
|
+
# log_ooda_act "todo-1.1b-004" "Implement validation"
|
|
12
|
+
# log_todo_complete "todo-1.1b-004" "3m 30s" "abc123"
|
|
13
|
+
# log_todo_fail "todo-1.1b-004" "connection refused"
|
|
14
|
+
# log_retry "todo-1.1b-004" "2" "3"
|
|
15
|
+
# log_skip "todo-1.1b-004" "external dep down"
|
|
16
|
+
# log_session_start
|
|
17
|
+
# log_session_pause
|
|
18
|
+
|
|
19
|
+
ACTIVITY_LOG="${CODECRUISE_ACTIVITY_LOG:-.codecruise/activity.log}"
|
|
20
|
+
|
|
21
|
+
# Ensure directory exists
|
|
22
|
+
_ensure_log_dir() {
|
|
23
|
+
mkdir -p "$(dirname "$ACTIVITY_LOG")" 2>/dev/null || true
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
# Get timestamp
|
|
27
|
+
_ts() {
|
|
28
|
+
date "+%Y-%m-%d %H:%M:%S"
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
# Base log function
|
|
32
|
+
_log() {
|
|
33
|
+
_ensure_log_dir
|
|
34
|
+
echo "[$(_ts)] $1" >> "$ACTIVITY_LOG"
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
# Indented detail
|
|
38
|
+
_log_detail() {
|
|
39
|
+
_ensure_log_dir
|
|
40
|
+
echo "[$(_ts)] $1" >> "$ACTIVITY_LOG"
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
# --- Session Events ---
|
|
44
|
+
|
|
45
|
+
log_session_start() {
|
|
46
|
+
_log "━━━ SESSION START ━━━"
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
log_session_pause() {
|
|
50
|
+
_log "⏸ PAUSE"
|
|
51
|
+
_log "━━━ SESSION PAUSE ━━━"
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
log_session_resume() {
|
|
55
|
+
_log "▶ RESUME"
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
# --- OODA Phase Events ---
|
|
59
|
+
|
|
60
|
+
log_ooda_observe() {
|
|
61
|
+
local todo="$1"
|
|
62
|
+
_log "👁 OBSERVE $todo"
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
log_ooda_observe_detail() {
|
|
66
|
+
local key="$1"
|
|
67
|
+
local value="$2"
|
|
68
|
+
local symbol="${3:-├─}" # Use └─ for last item
|
|
69
|
+
_log_detail "$symbol $key: $value"
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
log_ooda_orient() {
|
|
73
|
+
local confidence="$1"
|
|
74
|
+
local patterns="$2"
|
|
75
|
+
_log "🧭 ORIENT confidence=$confidence"
|
|
76
|
+
if [[ -n "$patterns" ]]; then
|
|
77
|
+
_log_detail "└─ patterns: $patterns"
|
|
78
|
+
fi
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
log_ooda_decide() {
|
|
82
|
+
local decision="$1"
|
|
83
|
+
_log "⚡ DECIDE $decision"
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
log_ooda_act() {
|
|
87
|
+
local todo="$1"
|
|
88
|
+
local description="$2"
|
|
89
|
+
_log "▶ ACT $todo \"$description\""
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
log_ooda_act_phase() {
|
|
93
|
+
local phase="$1" # red, green, refactor
|
|
94
|
+
local detail="$2"
|
|
95
|
+
case "$phase" in
|
|
96
|
+
red) _log_detail "├─ RED: $detail" ;;
|
|
97
|
+
green) _log_detail "├─ GREEN: $detail" ;;
|
|
98
|
+
refactor) _log_detail "├─ REFACTOR: $detail" ;;
|
|
99
|
+
verify) _log_detail "└─ VERIFY: $detail" ;;
|
|
100
|
+
esac
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
# --- TODO Events ---
|
|
104
|
+
|
|
105
|
+
log_todo_complete() {
|
|
106
|
+
local todo="$1"
|
|
107
|
+
local duration="$2"
|
|
108
|
+
local commit="$3"
|
|
109
|
+
_log "✓ COMPLETE $todo ($duration) commit:$commit"
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
log_todo_fail() {
|
|
113
|
+
local todo="$1"
|
|
114
|
+
local error="$2"
|
|
115
|
+
_log_detail "└─ ✗ FAIL: $error"
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
log_retry() {
|
|
119
|
+
local todo="$1"
|
|
120
|
+
local attempt="$2"
|
|
121
|
+
local max="$3"
|
|
122
|
+
_log "⚠ RETRY $todo (attempt $attempt/$max)"
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
log_skip() {
|
|
126
|
+
local todo="$1"
|
|
127
|
+
local reason="$2"
|
|
128
|
+
_log "⏭ SKIP $todo ($reason)"
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
log_next() {
|
|
132
|
+
local todo="$1"
|
|
133
|
+
_log "→ NEXT $todo"
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
# --- Verification Events ---
|
|
137
|
+
|
|
138
|
+
log_verify() {
|
|
139
|
+
local lint="$1" # ✓ or ✗
|
|
140
|
+
local tsc="$2" # ✓ or ✗
|
|
141
|
+
local test="$3" # ✓ or ✗
|
|
142
|
+
_log_detail "└─ VERIFY: lint $lint | tsc $tsc | test $test"
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
# --- Blocker Events ---
|
|
146
|
+
|
|
147
|
+
log_blocker() {
|
|
148
|
+
local pattern="$1"
|
|
149
|
+
local detail="$2"
|
|
150
|
+
_log "🔴 BLOCKER DETECTED $pattern"
|
|
151
|
+
_log_detail "└─ $detail"
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
# --- Checkpoint Events ---
|
|
155
|
+
|
|
156
|
+
log_checkpoint() {
|
|
157
|
+
local type="$1" # subfeature, feature
|
|
158
|
+
local id="$2"
|
|
159
|
+
local todos_done="$3"
|
|
160
|
+
local total="$4"
|
|
161
|
+
_log "═══ CHECKPOINT: $type $id ($todos_done/$total) ═══"
|
|
162
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# read-settings.sh - Reads codecruise settings with proper hierarchy
|
|
3
|
+
#
|
|
4
|
+
# Priority (highest to lowest):
|
|
5
|
+
# 1. Environment variable (from command flags)
|
|
6
|
+
# 2. Project settings (.claude/settings.json)
|
|
7
|
+
# 3. User settings (~/.claude/settings.json)
|
|
8
|
+
# 4. Default value
|
|
9
|
+
#
|
|
10
|
+
# Usage:
|
|
11
|
+
# source ~/.claude/hooks/lib/read-settings.sh
|
|
12
|
+
# ENFORCE=$(get_setting "enforce" "strict")
|
|
13
|
+
# TDD_ENFORCE=$(get_setting "enforce_tdd" "$ENFORCE")
|
|
14
|
+
|
|
15
|
+
# Get a codecruise setting with fallback chain
|
|
16
|
+
# Args: $1 = setting name, $2 = default value
|
|
17
|
+
get_setting() {
|
|
18
|
+
local setting_name="$1"
|
|
19
|
+
local default_value="$2"
|
|
20
|
+
|
|
21
|
+
# Convert setting name to env var name (enforce -> CODECRUISE_ENFORCE)
|
|
22
|
+
local env_var="CODECRUISE_$(echo "$setting_name" | tr '[:lower:]' '[:upper:]')"
|
|
23
|
+
|
|
24
|
+
# 1. Check environment variable first (highest priority - from command flags)
|
|
25
|
+
local env_value="${!env_var}"
|
|
26
|
+
if [[ -n "$env_value" ]]; then
|
|
27
|
+
echo "$env_value"
|
|
28
|
+
return
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
# 2. Check project settings (.claude/settings.json)
|
|
32
|
+
if [[ -f ".claude/settings.json" ]]; then
|
|
33
|
+
local project_value
|
|
34
|
+
project_value=$(jq -r ".codecruise.${setting_name} // empty" .claude/settings.json 2>/dev/null)
|
|
35
|
+
if [[ -n "$project_value" && "$project_value" != "null" ]]; then
|
|
36
|
+
echo "$project_value"
|
|
37
|
+
return
|
|
38
|
+
fi
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
# 3. Check user settings (~/.claude/settings.json)
|
|
42
|
+
if [[ -f "$HOME/.claude/settings.json" ]]; then
|
|
43
|
+
local user_value
|
|
44
|
+
user_value=$(jq -r ".codecruise.${setting_name} // empty" "$HOME/.claude/settings.json" 2>/dev/null)
|
|
45
|
+
if [[ -n "$user_value" && "$user_value" != "null" ]]; then
|
|
46
|
+
echo "$user_value"
|
|
47
|
+
return
|
|
48
|
+
fi
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# 4. Return default
|
|
52
|
+
echo "$default_value"
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
# Check if enforcement is enabled for a specific check
|
|
56
|
+
# Args: $1 = check name (tdd, quality, criteria)
|
|
57
|
+
# Returns: "strict", "ask", or "off"
|
|
58
|
+
get_enforce_mode() {
|
|
59
|
+
local check_name="$1"
|
|
60
|
+
|
|
61
|
+
# Get specific setting first, fall back to global enforce
|
|
62
|
+
local specific_setting="enforce_${check_name}"
|
|
63
|
+
local global_enforce
|
|
64
|
+
global_enforce=$(get_setting "enforce" "strict")
|
|
65
|
+
|
|
66
|
+
get_setting "$specific_setting" "$global_enforce"
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
# Export functions for use in hooks
|
|
70
|
+
export -f get_setting
|
|
71
|
+
export -f get_enforce_mode
|