@paths.design/caws-cli 8.0.1 → 8.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/dist/commands/archive.d.ts +2 -1
- package/dist/commands/archive.d.ts.map +1 -1
- package/dist/commands/archive.js +114 -6
- package/dist/commands/burnup.d.ts.map +1 -1
- package/dist/commands/burnup.js +109 -10
- package/dist/commands/diagnose.js +1 -1
- package/dist/commands/mode.js +24 -14
- package/dist/commands/provenance.js +216 -93
- package/dist/commands/quality-gates.d.ts.map +1 -1
- package/dist/commands/quality-gates.js +3 -1
- package/dist/commands/specs.js +184 -6
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +134 -10
- package/dist/commands/templates.js +2 -2
- package/dist/error-handler.js +6 -98
- package/dist/generators/jest-config-generator.js +242 -0
- package/dist/index.js +4 -7
- package/dist/minimal-cli.js +3 -1
- package/dist/scaffold/claude-hooks.js +316 -0
- package/dist/scaffold/index.js +18 -0
- package/dist/templates/.claude/README.md +190 -0
- package/dist/templates/.claude/hooks/audit.sh +96 -0
- package/dist/templates/.claude/hooks/block-dangerous.sh +90 -0
- package/dist/templates/.claude/hooks/naming-check.sh +97 -0
- package/dist/templates/.claude/hooks/quality-check.sh +68 -0
- package/dist/templates/.claude/hooks/scan-secrets.sh +85 -0
- package/dist/templates/.claude/hooks/scope-guard.sh +105 -0
- package/dist/templates/.claude/hooks/validate-spec.sh +76 -0
- package/dist/templates/.claude/settings.json +95 -0
- package/dist/test-analysis.js +203 -10
- package/dist/utils/error-categories.js +210 -0
- package/dist/utils/quality-gates-utils.js +402 -0
- package/dist/utils/typescript-detector.js +36 -90
- package/dist/validation/spec-validation.js +59 -6
- package/package.json +5 -3
- package/templates/.claude/README.md +190 -0
- package/templates/.claude/hooks/audit.sh +96 -0
- package/templates/.claude/hooks/block-dangerous.sh +90 -0
- package/templates/.claude/hooks/naming-check.sh +97 -0
- package/templates/.claude/hooks/quality-check.sh +68 -0
- package/templates/.claude/hooks/scan-secrets.sh +85 -0
- package/templates/.claude/hooks/scope-guard.sh +105 -0
- package/templates/.claude/hooks/validate-spec.sh +76 -0
- package/templates/.claude/settings.json +95 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# CAWS Scope Guard Hook for Claude Code
|
|
3
|
+
# Validates file edits against the working spec's scope boundaries
|
|
4
|
+
# @author @darianrosebrook
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
# Read JSON input from Claude Code
|
|
9
|
+
INPUT=$(cat)
|
|
10
|
+
|
|
11
|
+
# Extract file path from PreToolUse input
|
|
12
|
+
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
|
|
13
|
+
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // ""')
|
|
14
|
+
|
|
15
|
+
# Only check Write/Edit operations
|
|
16
|
+
if [[ "$TOOL_NAME" != "Write" ]] && [[ "$TOOL_NAME" != "Edit" ]]; then
|
|
17
|
+
exit 0
|
|
18
|
+
fi
|
|
19
|
+
|
|
20
|
+
if [[ -z "$FILE_PATH" ]]; then
|
|
21
|
+
exit 0
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
|
|
25
|
+
SPEC_FILE="$PROJECT_DIR/.caws/working-spec.yaml"
|
|
26
|
+
|
|
27
|
+
# Check if spec file exists
|
|
28
|
+
if [[ ! -f "$SPEC_FILE" ]]; then
|
|
29
|
+
exit 0
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
# Get relative path from project root
|
|
33
|
+
REL_PATH=$(realpath --relative-to="$PROJECT_DIR" "$FILE_PATH" 2>/dev/null || echo "$FILE_PATH")
|
|
34
|
+
|
|
35
|
+
# Use Node.js to parse YAML and check scope
|
|
36
|
+
if command -v node >/dev/null 2>&1; then
|
|
37
|
+
SCOPE_CHECK=$(node -e "
|
|
38
|
+
const yaml = require('js-yaml');
|
|
39
|
+
const fs = require('fs');
|
|
40
|
+
const path = require('path');
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
const spec = yaml.load(fs.readFileSync('$SPEC_FILE', 'utf8'));
|
|
44
|
+
const filePath = '$REL_PATH';
|
|
45
|
+
|
|
46
|
+
// Check if file is explicitly out of scope
|
|
47
|
+
const outOfScope = spec.scope?.out_of_scope || [];
|
|
48
|
+
for (const pattern of outOfScope) {
|
|
49
|
+
// Simple glob-like matching
|
|
50
|
+
const regex = new RegExp(pattern.replace(/\*/g, '.*').replace(/\?/g, '.'));
|
|
51
|
+
if (regex.test(filePath)) {
|
|
52
|
+
console.log('out_of_scope:' + pattern);
|
|
53
|
+
process.exit(0);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Check if file is in scope (if scope is explicitly defined)
|
|
58
|
+
const inScope = spec.scope?.files || spec.scope?.directories || [];
|
|
59
|
+
if (inScope.length > 0) {
|
|
60
|
+
let found = false;
|
|
61
|
+
for (const pattern of inScope) {
|
|
62
|
+
const regex = new RegExp(pattern.replace(/\*/g, '.*').replace(/\?/g, '.'));
|
|
63
|
+
if (regex.test(filePath)) {
|
|
64
|
+
found = true;
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (!found) {
|
|
69
|
+
console.log('not_in_scope');
|
|
70
|
+
process.exit(0);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
console.log('in_scope');
|
|
75
|
+
} catch (error) {
|
|
76
|
+
console.log('error:' + error.message);
|
|
77
|
+
}
|
|
78
|
+
" 2>&1)
|
|
79
|
+
|
|
80
|
+
if [[ "$SCOPE_CHECK" == out_of_scope:* ]]; then
|
|
81
|
+
PATTERN="${SCOPE_CHECK#out_of_scope:}"
|
|
82
|
+
echo '{
|
|
83
|
+
"hookSpecificOutput": {
|
|
84
|
+
"hookEventName": "PreToolUse",
|
|
85
|
+
"permissionDecision": "ask",
|
|
86
|
+
"permissionDecisionReason": "This file ('"$REL_PATH"') is marked as out-of-scope in the working spec (pattern: '"$PATTERN"'). Editing it may cause scope creep. Please confirm this edit is intentional."
|
|
87
|
+
}
|
|
88
|
+
}'
|
|
89
|
+
exit 0
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
if [[ "$SCOPE_CHECK" == "not_in_scope" ]]; then
|
|
93
|
+
echo '{
|
|
94
|
+
"hookSpecificOutput": {
|
|
95
|
+
"hookEventName": "PreToolUse",
|
|
96
|
+
"permissionDecision": "ask",
|
|
97
|
+
"permissionDecisionReason": "This file ('"$REL_PATH"') is not in the defined scope of the working spec. Editing it may cause scope creep. Please confirm this edit is intentional."
|
|
98
|
+
}
|
|
99
|
+
}'
|
|
100
|
+
exit 0
|
|
101
|
+
fi
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
# File is in scope or scope couldn't be checked - allow
|
|
105
|
+
exit 0
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# CAWS Spec Validation Hook for Claude Code
|
|
3
|
+
# Validates working-spec.yaml when it's edited
|
|
4
|
+
# @author @darianrosebrook
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
# Read JSON input from Claude Code
|
|
9
|
+
INPUT=$(cat)
|
|
10
|
+
|
|
11
|
+
# Extract file path from PostToolUse input
|
|
12
|
+
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
|
|
13
|
+
|
|
14
|
+
# Only validate CAWS YAML files
|
|
15
|
+
if [[ "$FILE_PATH" != *".caws/"* ]] || ([[ "$FILE_PATH" != *.yaml ]] && [[ "$FILE_PATH" != *.yml ]]); then
|
|
16
|
+
exit 0
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
|
|
20
|
+
|
|
21
|
+
# First, validate YAML syntax using Node.js if available
|
|
22
|
+
if command -v node >/dev/null 2>&1; then
|
|
23
|
+
YAML_CHECK=$(node -e "
|
|
24
|
+
try {
|
|
25
|
+
const yaml = require('js-yaml');
|
|
26
|
+
const fs = require('fs');
|
|
27
|
+
const content = fs.readFileSync('$FILE_PATH', 'utf8');
|
|
28
|
+
yaml.load(content);
|
|
29
|
+
console.log('valid');
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error(error.message);
|
|
32
|
+
if (error.mark) {
|
|
33
|
+
console.error('Line: ' + (error.mark.line + 1) + ', Column: ' + (error.mark.column + 1));
|
|
34
|
+
}
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
" 2>&1)
|
|
38
|
+
|
|
39
|
+
if [ $? -ne 0 ]; then
|
|
40
|
+
echo '{
|
|
41
|
+
"decision": "block",
|
|
42
|
+
"reason": "YAML syntax error in spec file:\n'"$YAML_CHECK"'\n\nPlease fix the syntax before continuing. Common issues:\n- Check indentation (YAML uses 2 spaces)\n- Ensure arrays use consistent format\n- Remove duplicate keys"
|
|
43
|
+
}'
|
|
44
|
+
exit 0
|
|
45
|
+
fi
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
# Run CAWS CLI validation if available
|
|
49
|
+
if command -v caws &> /dev/null; then
|
|
50
|
+
if VALIDATION=$(caws validate "$FILE_PATH" --quiet 2>&1); then
|
|
51
|
+
echo '{
|
|
52
|
+
"hookSpecificOutput": {
|
|
53
|
+
"hookEventName": "PostToolUse",
|
|
54
|
+
"additionalContext": "Spec validation passed. The specification is valid and complete."
|
|
55
|
+
}
|
|
56
|
+
}'
|
|
57
|
+
else
|
|
58
|
+
# Get suggestions
|
|
59
|
+
SUGGESTIONS=$(caws validate "$FILE_PATH" --suggestions 2>/dev/null | head -5 | tr '\n' ' ' || echo "Run 'caws validate --suggestions' for details")
|
|
60
|
+
|
|
61
|
+
echo '{
|
|
62
|
+
"decision": "block",
|
|
63
|
+
"reason": "Spec validation failed:\n'"$VALIDATION"'\n\nSuggestions:\n'"$SUGGESTIONS"'"
|
|
64
|
+
}'
|
|
65
|
+
fi
|
|
66
|
+
else
|
|
67
|
+
# Basic validation without CAWS CLI
|
|
68
|
+
echo '{
|
|
69
|
+
"hookSpecificOutput": {
|
|
70
|
+
"hookEventName": "PostToolUse",
|
|
71
|
+
"additionalContext": "CAWS CLI not available for full spec validation. Install with: npm install -g @caws/cli"
|
|
72
|
+
}
|
|
73
|
+
}'
|
|
74
|
+
fi
|
|
75
|
+
|
|
76
|
+
exit 0
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"PreToolUse": [
|
|
4
|
+
{
|
|
5
|
+
"matcher": "Bash",
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block-dangerous.sh",
|
|
10
|
+
"timeout": 10
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"matcher": "Read",
|
|
16
|
+
"hooks": [
|
|
17
|
+
{
|
|
18
|
+
"type": "command",
|
|
19
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/scan-secrets.sh",
|
|
20
|
+
"timeout": 10
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"matcher": "Write|Edit",
|
|
26
|
+
"hooks": [
|
|
27
|
+
{
|
|
28
|
+
"type": "command",
|
|
29
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/scope-guard.sh",
|
|
30
|
+
"timeout": 10
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
],
|
|
35
|
+
"PostToolUse": [
|
|
36
|
+
{
|
|
37
|
+
"matcher": "Write|Edit",
|
|
38
|
+
"hooks": [
|
|
39
|
+
{
|
|
40
|
+
"type": "command",
|
|
41
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/quality-check.sh",
|
|
42
|
+
"timeout": 30
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"type": "command",
|
|
46
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/validate-spec.sh",
|
|
47
|
+
"timeout": 15
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"type": "command",
|
|
51
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/naming-check.sh",
|
|
52
|
+
"timeout": 10
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"type": "command",
|
|
56
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/audit.sh tool-use",
|
|
57
|
+
"timeout": 5
|
|
58
|
+
}
|
|
59
|
+
]
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"matcher": "Bash",
|
|
63
|
+
"hooks": [
|
|
64
|
+
{
|
|
65
|
+
"type": "command",
|
|
66
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/audit.sh tool-use",
|
|
67
|
+
"timeout": 5
|
|
68
|
+
}
|
|
69
|
+
]
|
|
70
|
+
}
|
|
71
|
+
],
|
|
72
|
+
"SessionStart": [
|
|
73
|
+
{
|
|
74
|
+
"hooks": [
|
|
75
|
+
{
|
|
76
|
+
"type": "command",
|
|
77
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/audit.sh session-start",
|
|
78
|
+
"timeout": 5
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
],
|
|
83
|
+
"Stop": [
|
|
84
|
+
{
|
|
85
|
+
"hooks": [
|
|
86
|
+
{
|
|
87
|
+
"type": "command",
|
|
88
|
+
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/audit.sh stop",
|
|
89
|
+
"timeout": 5
|
|
90
|
+
}
|
|
91
|
+
]
|
|
92
|
+
}
|
|
93
|
+
]
|
|
94
|
+
}
|
|
95
|
+
}
|