@paths.design/caws-cli 8.0.1 → 8.2.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 (112) hide show
  1. package/README.md +5 -6
  2. package/dist/commands/archive.d.ts +1 -0
  3. package/dist/commands/archive.d.ts.map +1 -1
  4. package/dist/commands/archive.js +114 -6
  5. package/dist/commands/burnup.d.ts.map +1 -1
  6. package/dist/commands/burnup.js +109 -10
  7. package/dist/commands/diagnose.js +1 -1
  8. package/dist/commands/init.d.ts.map +1 -1
  9. package/dist/commands/init.js +185 -39
  10. package/dist/commands/mode.d.ts +2 -1
  11. package/dist/commands/mode.d.ts.map +1 -1
  12. package/dist/commands/mode.js +24 -14
  13. package/dist/commands/provenance.d.ts.map +1 -1
  14. package/dist/commands/provenance.js +216 -93
  15. package/dist/commands/quality-gates.d.ts.map +1 -1
  16. package/dist/commands/quality-gates.js +3 -1
  17. package/dist/commands/specs.d.ts.map +1 -1
  18. package/dist/commands/specs.js +184 -6
  19. package/dist/commands/status.d.ts.map +1 -1
  20. package/dist/commands/status.js +134 -10
  21. package/dist/commands/templates.js +2 -2
  22. package/dist/commands/worktree.d.ts +7 -0
  23. package/dist/commands/worktree.d.ts.map +1 -0
  24. package/dist/commands/worktree.js +136 -0
  25. package/dist/config/lite-scope.d.ts +33 -0
  26. package/dist/config/lite-scope.d.ts.map +1 -0
  27. package/dist/config/lite-scope.js +158 -0
  28. package/dist/config/modes.d.ts +90 -51
  29. package/dist/config/modes.d.ts.map +1 -1
  30. package/dist/config/modes.js +26 -0
  31. package/dist/error-handler.d.ts +3 -16
  32. package/dist/error-handler.d.ts.map +1 -1
  33. package/dist/error-handler.js +6 -98
  34. package/dist/generators/jest-config-generator.d.ts +32 -0
  35. package/dist/generators/jest-config-generator.d.ts.map +1 -0
  36. package/dist/generators/jest-config-generator.js +242 -0
  37. package/dist/index.js +40 -7
  38. package/dist/minimal-cli.js +3 -1
  39. package/dist/scaffold/claude-hooks.d.ts +28 -0
  40. package/dist/scaffold/claude-hooks.d.ts.map +1 -0
  41. package/dist/scaffold/claude-hooks.js +344 -0
  42. package/dist/scaffold/index.d.ts +2 -0
  43. package/dist/scaffold/index.d.ts.map +1 -1
  44. package/dist/scaffold/index.js +96 -76
  45. package/dist/templates/.caws/schemas/scope.schema.json +52 -0
  46. package/dist/templates/.caws/schemas/working-spec.schema.json +1 -1
  47. package/dist/templates/.caws/schemas/worktrees.schema.json +36 -0
  48. package/dist/templates/.claude/README.md +190 -0
  49. package/dist/templates/.claude/hooks/audit.sh +96 -0
  50. package/dist/templates/.claude/hooks/block-dangerous.sh +123 -0
  51. package/dist/templates/.claude/hooks/lite-sprawl-check.sh +117 -0
  52. package/dist/templates/.claude/hooks/naming-check.sh +97 -0
  53. package/dist/templates/.claude/hooks/quality-check.sh +68 -0
  54. package/dist/templates/.claude/hooks/scan-secrets.sh +85 -0
  55. package/dist/templates/.claude/hooks/scope-guard.sh +192 -0
  56. package/dist/templates/.claude/hooks/simplification-guard.sh +92 -0
  57. package/dist/templates/.claude/hooks/validate-spec.sh +76 -0
  58. package/dist/templates/.claude/settings.json +95 -0
  59. package/dist/templates/.cursor/README.md +0 -3
  60. package/dist/templates/.github/copilot-instructions.md +82 -0
  61. package/dist/templates/.junie/guidelines.md +73 -0
  62. package/dist/templates/.vscode/launch.json +0 -27
  63. package/dist/templates/.windsurf/rules/caws-quality-standards.md +54 -0
  64. package/dist/templates/CLAUDE.md +101 -0
  65. package/dist/templates/agents.md +73 -1016
  66. package/dist/templates/docs/README.md +5 -5
  67. package/dist/test-analysis.d.ts +50 -1
  68. package/dist/test-analysis.d.ts.map +1 -1
  69. package/dist/test-analysis.js +203 -10
  70. package/dist/utils/error-categories.d.ts +52 -0
  71. package/dist/utils/error-categories.d.ts.map +1 -0
  72. package/dist/utils/error-categories.js +210 -0
  73. package/dist/utils/gitignore-updater.d.ts +1 -1
  74. package/dist/utils/gitignore-updater.d.ts.map +1 -1
  75. package/dist/utils/gitignore-updater.js +4 -0
  76. package/dist/utils/ide-detection.js +133 -0
  77. package/dist/utils/quality-gates-utils.d.ts +49 -0
  78. package/dist/utils/quality-gates-utils.d.ts.map +1 -0
  79. package/dist/utils/quality-gates-utils.js +402 -0
  80. package/dist/utils/typescript-detector.d.ts +8 -5
  81. package/dist/utils/typescript-detector.d.ts.map +1 -1
  82. package/dist/utils/typescript-detector.js +36 -90
  83. package/dist/validation/spec-validation.d.ts.map +1 -1
  84. package/dist/validation/spec-validation.js +59 -6
  85. package/dist/worktree/worktree-manager.d.ts +54 -0
  86. package/dist/worktree/worktree-manager.d.ts.map +1 -0
  87. package/dist/worktree/worktree-manager.js +378 -0
  88. package/package.json +9 -3
  89. package/templates/.caws/schemas/scope.schema.json +52 -0
  90. package/templates/.caws/schemas/working-spec.schema.json +1 -1
  91. package/templates/.caws/schemas/worktrees.schema.json +36 -0
  92. package/templates/.claude/README.md +190 -0
  93. package/templates/.claude/hooks/audit.sh +96 -0
  94. package/templates/.claude/hooks/block-dangerous.sh +123 -0
  95. package/templates/.claude/hooks/lite-sprawl-check.sh +117 -0
  96. package/templates/.claude/hooks/naming-check.sh +97 -0
  97. package/templates/.claude/hooks/quality-check.sh +68 -0
  98. package/templates/.claude/hooks/scan-secrets.sh +85 -0
  99. package/templates/.claude/hooks/scope-guard.sh +192 -0
  100. package/templates/.claude/hooks/simplification-guard.sh +92 -0
  101. package/templates/.claude/hooks/validate-spec.sh +76 -0
  102. package/templates/.claude/settings.json +95 -0
  103. package/templates/.cursor/README.md +0 -3
  104. package/templates/.github/copilot-instructions.md +82 -0
  105. package/templates/.junie/guidelines.md +73 -0
  106. package/templates/.vscode/launch.json +0 -27
  107. package/templates/.windsurf/rules/caws-quality-standards.md +54 -0
  108. package/templates/AGENTS.md +104 -0
  109. package/templates/CLAUDE.md +101 -0
  110. package/templates/docs/README.md +5 -5
  111. package/templates/.github/copilot/instructions.md +0 -311
  112. package/templates/agents.md +0 -1047
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@paths.design/caws-cli",
3
- "version": "8.0.1",
4
- "description": "CAWS CLI - Coding Agent Workflow System command line tools",
3
+ "version": "8.2.0",
4
+ "description": "CAWS CLI - Coding Agent Workflow System command-line tools for spec management, quality gates, and AI-assisted development",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
7
7
  "caws": "dist/index.js"
@@ -33,7 +33,8 @@
33
33
  "format": "prettier --write src/**/*.js tests/**/*.js",
34
34
  "validate": "echo 'CLI package validation not required'",
35
35
  "caws:validate": "node ../../.caws/validate.js ../../.caws/working-spec.yaml",
36
- "clean": "rm -rf dist test-caws-project .agent && npm run test:cleanup"
36
+ "clean": "rm -rf dist test-caws-project .agent && npm run test:cleanup",
37
+ "prepare": "husky"
37
38
  },
38
39
  "keywords": [
39
40
  "caws",
@@ -50,6 +51,10 @@
50
51
  "url": "https://github.com/Paths-Design/coding-agent-working-standard.git",
51
52
  "directory": "packages/caws-cli"
52
53
  },
54
+ "publishConfig": {
55
+ "registry": "https://registry.npmjs.org/",
56
+ "access": "public"
57
+ },
53
58
  "dependencies": {
54
59
  "chalk": "4.1.2",
55
60
  "commander": "^11.0.0",
@@ -69,6 +74,7 @@
69
74
  "ajv": "8.17.1",
70
75
  "esbuild": "0.25.10",
71
76
  "eslint": "^9.0.0",
77
+ "husky": "9.1.7",
72
78
  "jest": "30.1.3",
73
79
  "js-yaml": "4.1.0",
74
80
  "lint-staged": "15.5.2",
@@ -0,0 +1,52 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "CAWS Lite Scope Configuration",
4
+ "description": "Scope configuration for CAWS lite mode — guardrails without YAML specs",
5
+ "type": "object",
6
+ "required": ["version", "allowedDirectories"],
7
+ "properties": {
8
+ "version": {
9
+ "type": "integer",
10
+ "const": 1,
11
+ "description": "Schema version"
12
+ },
13
+ "allowedDirectories": {
14
+ "type": "array",
15
+ "items": { "type": "string" },
16
+ "minItems": 1,
17
+ "description": "Directories the agent is allowed to modify (e.g., src/, tests/)"
18
+ },
19
+ "bannedPatterns": {
20
+ "type": "object",
21
+ "properties": {
22
+ "files": {
23
+ "type": "array",
24
+ "items": { "type": "string" },
25
+ "description": "Glob patterns for banned file names (e.g., *-enhanced.*, *-final.*)"
26
+ },
27
+ "directories": {
28
+ "type": "array",
29
+ "items": { "type": "string" },
30
+ "description": "Glob patterns for banned directory names (e.g., *venv*, .venv)"
31
+ },
32
+ "docs": {
33
+ "type": "array",
34
+ "items": { "type": "string" },
35
+ "description": "Glob patterns for banned doc file names (e.g., *-summary.md)"
36
+ }
37
+ },
38
+ "additionalProperties": false
39
+ },
40
+ "maxNewFilesPerCommit": {
41
+ "type": "integer",
42
+ "minimum": 1,
43
+ "maximum": 100,
44
+ "description": "Maximum number of new files allowed per commit (prevents file sprawl)"
45
+ },
46
+ "designatedVenvPath": {
47
+ "type": "string",
48
+ "description": "The only allowed virtual environment path (e.g., .venv)"
49
+ }
50
+ },
51
+ "additionalProperties": false
52
+ }
@@ -16,7 +16,7 @@
16
16
  "contracts"
17
17
  ],
18
18
  "properties": {
19
- "id": { "type": "string", "pattern": "^(PROJ|FEAT|FIX|ARCH)-\\d{4}$" },
19
+ "id": { "type": "string", "pattern": "^[A-Z]{2,6}-\\d{3,4}$" },
20
20
  "title": { "type": "string", "minLength": 10, "maxLength": 200 },
21
21
  "risk_tier": { "type": ["integer", "string"], "enum": [1, 2, 3, "1", "2", "3"] },
22
22
  "mode": { "type": "string", "enum": ["feature", "refactor", "fix", "doc", "chore"] },
@@ -0,0 +1,36 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "CAWS Worktree Registry",
4
+ "description": "Registry of git worktrees managed by CAWS for agent scope isolation",
5
+ "type": "object",
6
+ "required": ["version", "worktrees"],
7
+ "properties": {
8
+ "version": {
9
+ "type": "integer",
10
+ "const": 1
11
+ },
12
+ "worktrees": {
13
+ "type": "object",
14
+ "additionalProperties": {
15
+ "type": "object",
16
+ "required": ["name", "path", "branch", "baseBranch", "createdAt", "status"],
17
+ "properties": {
18
+ "name": { "type": "string", "pattern": "^[a-zA-Z0-9_-]+$" },
19
+ "path": { "type": "string" },
20
+ "branch": { "type": "string" },
21
+ "baseBranch": { "type": "string" },
22
+ "scope": { "type": ["string", "null"] },
23
+ "specId": { "type": ["string", "null"] },
24
+ "createdAt": { "type": "string", "format": "date-time" },
25
+ "destroyedAt": { "type": "string", "format": "date-time" },
26
+ "status": {
27
+ "type": "string",
28
+ "enum": ["active", "orphaned", "missing", "destroyed"]
29
+ }
30
+ },
31
+ "additionalProperties": false
32
+ }
33
+ }
34
+ },
35
+ "additionalProperties": false
36
+ }
@@ -0,0 +1,190 @@
1
+ # Claude Code Integration for CAWS
2
+
3
+ This directory contains Claude Code hooks and configuration for CAWS (Coding Agent Working Standard) integration.
4
+
5
+ ## Overview
6
+
7
+ CAWS hooks for Claude Code provide:
8
+
9
+ - **Safety Gates**: Block dangerous commands and scan for secrets
10
+ - **Quality Gates**: Run CAWS quality checks after file edits
11
+ - **Scope Guards**: Validate edits against the working spec's scope
12
+ - **Audit Logging**: Track agent actions for compliance
13
+
14
+ ## Directory Structure
15
+
16
+ ```
17
+ .claude/
18
+ ├── settings.json # Claude Code settings with hooks configuration
19
+ ├── hooks/ # Hook scripts
20
+ │ ├── audit.sh # Session and action logging
21
+ │ ├── block-dangerous.sh # Block destructive commands
22
+ │ ├── scan-secrets.sh # Warn when reading sensitive files
23
+ │ ├── quality-check.sh # Run CAWS quality gates
24
+ │ ├── validate-spec.sh # Validate spec files
25
+ │ ├── scope-guard.sh # Check scope boundaries
26
+ │ └── naming-check.sh # Validate file naming conventions
27
+ ├── logs/ # Audit logs (gitignored)
28
+ └── README.md # This file
29
+ ```
30
+
31
+ ## Hook Events
32
+
33
+ ### PreToolUse Hooks
34
+
35
+ Run before Claude executes a tool:
36
+
37
+ | Hook | Matcher | Purpose |
38
+ |------|---------|---------|
39
+ | `block-dangerous.sh` | `Bash` | Block destructive shell commands |
40
+ | `scan-secrets.sh` | `Read` | Warn when reading sensitive files |
41
+ | `scope-guard.sh` | `Write\|Edit` | Check scope boundaries before edits |
42
+
43
+ ### PostToolUse Hooks
44
+
45
+ Run after Claude executes a tool:
46
+
47
+ | Hook | Matcher | Purpose |
48
+ |------|---------|---------|
49
+ | `quality-check.sh` | `Write\|Edit` | Run CAWS quality gates |
50
+ | `validate-spec.sh` | `Write\|Edit` | Validate spec file changes |
51
+ | `naming-check.sh` | `Write` | Check file naming conventions |
52
+ | `audit.sh` | `Write\|Edit\|Bash` | Log tool usage |
53
+
54
+ ### Session Hooks
55
+
56
+ | Hook | Event | Purpose |
57
+ |------|-------|---------|
58
+ | `audit.sh session-start` | `SessionStart` | Log session start |
59
+ | `audit.sh stop` | `Stop` | Log session end |
60
+
61
+ ## Configuration
62
+
63
+ ### Enable/Disable Hooks
64
+
65
+ Edit `settings.json` to enable or disable specific hooks. Remove entries from the `hooks` object to disable them.
66
+
67
+ ### Hook Levels
68
+
69
+ The scaffold supports four hook levels:
70
+
71
+ - **safety**: Block dangerous commands, scan for secrets
72
+ - **quality**: Run quality gates on file edits
73
+ - **scope**: Validate edits against spec scope
74
+ - **audit**: Log all agent actions
75
+
76
+ Run `caws init --hooks=safety,quality` to enable specific levels.
77
+
78
+ ## Audit Logs
79
+
80
+ Audit logs are written to `.claude/logs/`:
81
+
82
+ - `audit.log` - All-time log (appended)
83
+ - `audit-YYYY-MM-DD.log` - Daily logs
84
+
85
+ Logs are JSON-formatted for easy parsing:
86
+
87
+ ```json
88
+ {
89
+ "timestamp": "2024-01-15T10:30:00Z",
90
+ "session_id": "abc123",
91
+ "event": "tool_use",
92
+ "tool": "Write",
93
+ "file": "src/index.ts",
94
+ "cwd": "/project"
95
+ }
96
+ ```
97
+
98
+ ## Customization
99
+
100
+ ### Adding Custom Hooks
101
+
102
+ 1. Create a new script in `.claude/hooks/`
103
+ 2. Make it executable: `chmod +x .claude/hooks/my-hook.sh`
104
+ 3. Add it to `settings.json`:
105
+
106
+ ```json
107
+ {
108
+ "hooks": {
109
+ "PostToolUse": [
110
+ {
111
+ "matcher": "Write|Edit",
112
+ "hooks": [
113
+ {
114
+ "type": "command",
115
+ "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/my-hook.sh",
116
+ "timeout": 10
117
+ }
118
+ ]
119
+ }
120
+ ]
121
+ }
122
+ }
123
+ ```
124
+
125
+ ### Hook Input/Output
126
+
127
+ Hooks receive JSON input via stdin:
128
+
129
+ ```json
130
+ {
131
+ "session_id": "abc123",
132
+ "hook_event_name": "PostToolUse",
133
+ "tool_name": "Write",
134
+ "tool_input": {
135
+ "file_path": "/path/to/file.ts",
136
+ "content": "..."
137
+ },
138
+ "tool_response": { "success": true }
139
+ }
140
+ ```
141
+
142
+ Hooks can output JSON to control Claude's behavior:
143
+
144
+ ```json
145
+ {
146
+ "decision": "block",
147
+ "reason": "Quality gate failed: ..."
148
+ }
149
+ ```
150
+
151
+ Or add context:
152
+
153
+ ```json
154
+ {
155
+ "hookSpecificOutput": {
156
+ "hookEventName": "PostToolUse",
157
+ "additionalContext": "Remember to update the tests."
158
+ }
159
+ }
160
+ ```
161
+
162
+ ## Troubleshooting
163
+
164
+ ### Hooks Not Running
165
+
166
+ 1. Check `settings.json` syntax: `cat .claude/settings.json | jq .`
167
+ 2. Verify scripts are executable: `ls -la .claude/hooks/`
168
+ 3. Test hooks manually: `echo '{}' | .claude/hooks/audit.sh`
169
+
170
+ ### Permission Errors
171
+
172
+ Make all hook scripts executable:
173
+
174
+ ```bash
175
+ chmod +x .claude/hooks/*.sh
176
+ ```
177
+
178
+ ### Debug Hooks
179
+
180
+ Run Claude Code with `--debug` to see hook execution details:
181
+
182
+ ```bash
183
+ claude --debug
184
+ ```
185
+
186
+ ## Further Reading
187
+
188
+ - [Claude Code Hooks Documentation](https://code.claude.com/docs/en/hooks)
189
+ - [CAWS Quality Gates](../../docs/quality-gates.md)
190
+ - [CAWS Scope Management](../../docs/scope-management.md)
@@ -0,0 +1,96 @@
1
+ #!/bin/bash
2
+ # CAWS Audit Hook for Claude Code
3
+ # Logs agent actions for compliance and debugging
4
+ # @author @darianrosebrook
5
+
6
+ set -euo pipefail
7
+
8
+ # Get event type from argument or input
9
+ EVENT_TYPE="${1:-tool-use}"
10
+
11
+ # Read JSON input from stdin
12
+ INPUT=$(cat)
13
+
14
+ # Parse common fields from Claude Code hook input
15
+ SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // "unknown"')
16
+ CWD=$(echo "$INPUT" | jq -r '.cwd // "."')
17
+ HOOK_EVENT=$(echo "$INPUT" | jq -r '.hook_event_name // "unknown"')
18
+ TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // ""')
19
+ PERMISSION_MODE=$(echo "$INPUT" | jq -r '.permission_mode // "default"')
20
+
21
+ # Ensure log directory exists
22
+ LOG_DIR="${CLAUDE_PROJECT_DIR:-.}/.claude/logs"
23
+ mkdir -p "$LOG_DIR"
24
+
25
+ # Log file path
26
+ LOG_FILE="$LOG_DIR/audit.log"
27
+ DATE_LOG_FILE="$LOG_DIR/audit-$(date +%Y-%m-%d).log"
28
+
29
+ # Timestamp
30
+ TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
31
+
32
+ # Build log entry based on event type
33
+ case "$EVENT_TYPE" in
34
+ session-start)
35
+ SOURCE=$(echo "$INPUT" | jq -r '.source // "unknown"')
36
+ MODEL=$(echo "$INPUT" | jq -r '.model // "unknown"')
37
+ LOG_ENTRY=$(jq -n \
38
+ --arg ts "$TIMESTAMP" \
39
+ --arg sid "$SESSION_ID" \
40
+ --arg event "session_start" \
41
+ --arg source "$SOURCE" \
42
+ --arg model "$MODEL" \
43
+ --arg cwd "$CWD" \
44
+ '{timestamp: $ts, session_id: $sid, event: $event, source: $source, model: $model, cwd: $cwd}')
45
+ ;;
46
+
47
+ stop)
48
+ STOP_HOOK_ACTIVE=$(echo "$INPUT" | jq -r '.stop_hook_active // false')
49
+ LOG_ENTRY=$(jq -n \
50
+ --arg ts "$TIMESTAMP" \
51
+ --arg sid "$SESSION_ID" \
52
+ --arg event "session_stop" \
53
+ --arg cwd "$CWD" \
54
+ --argjson hook_active "$STOP_HOOK_ACTIVE" \
55
+ '{timestamp: $ts, session_id: $sid, event: $event, cwd: $cwd, stop_hook_active: $hook_active}')
56
+ ;;
57
+
58
+ tool-use)
59
+ # Extract tool-specific info
60
+ TOOL_INPUT=$(echo "$INPUT" | jq -c '.tool_input // {}')
61
+ TOOL_RESPONSE=$(echo "$INPUT" | jq -c '.tool_response // {}')
62
+ TOOL_USE_ID=$(echo "$INPUT" | jq -r '.tool_use_id // ""')
63
+
64
+ # For file operations, extract the path
65
+ FILE_PATH=$(echo "$TOOL_INPUT" | jq -r '.file_path // ""')
66
+ COMMAND=$(echo "$TOOL_INPUT" | jq -r '.command // ""')
67
+
68
+ LOG_ENTRY=$(jq -n \
69
+ --arg ts "$TIMESTAMP" \
70
+ --arg sid "$SESSION_ID" \
71
+ --arg event "tool_use" \
72
+ --arg tool "$TOOL_NAME" \
73
+ --arg file "$FILE_PATH" \
74
+ --arg cmd "$COMMAND" \
75
+ --arg cwd "$CWD" \
76
+ --arg mode "$PERMISSION_MODE" \
77
+ '{timestamp: $ts, session_id: $sid, event: $event, tool: $tool, file: $file, command: $cmd, cwd: $cwd, permission_mode: $mode}')
78
+ ;;
79
+
80
+ *)
81
+ LOG_ENTRY=$(jq -n \
82
+ --arg ts "$TIMESTAMP" \
83
+ --arg sid "$SESSION_ID" \
84
+ --arg event "$EVENT_TYPE" \
85
+ --arg hook "$HOOK_EVENT" \
86
+ --arg cwd "$CWD" \
87
+ '{timestamp: $ts, session_id: $sid, event: $event, hook_event: $hook, cwd: $cwd}')
88
+ ;;
89
+ esac
90
+
91
+ # Append to log files
92
+ echo "$LOG_ENTRY" >> "$LOG_FILE"
93
+ echo "$LOG_ENTRY" >> "$DATE_LOG_FILE"
94
+
95
+ # Success - allow operation to continue
96
+ exit 0
@@ -0,0 +1,123 @@
1
+ #!/bin/bash
2
+ # CAWS Dangerous Command Blocker for Claude Code
3
+ # Blocks potentially destructive shell commands
4
+ # @author @darianrosebrook
5
+
6
+ set -euo pipefail
7
+
8
+ # Read JSON input from Claude Code
9
+ INPUT=$(cat)
10
+
11
+ # Extract tool info
12
+ TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // ""')
13
+ COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // ""')
14
+
15
+ # Only check Bash tool
16
+ if [[ "$TOOL_NAME" != "Bash" ]] || [[ -z "$COMMAND" ]]; then
17
+ exit 0
18
+ fi
19
+
20
+ # Dangerous command patterns
21
+ DANGEROUS_PATTERNS=(
22
+ # Destructive file operations
23
+ 'rm -rf /'
24
+ 'rm -rf ~'
25
+ 'rm -rf \*'
26
+ 'rm -rf \.'
27
+ 'rm -rf /\*'
28
+ 'dd if=/dev/zero'
29
+ 'dd if=/dev/random'
30
+ 'mkfs\.'
31
+ 'fdisk'
32
+ '> /dev/sd'
33
+
34
+ # Fork bombs and resource exhaustion
35
+ ':\(\)\{:\|:\&\};:'
36
+ 'while true.*fork'
37
+
38
+ # Credential/secret exposure
39
+ 'cat.*\.env'
40
+ 'cat.*/etc/passwd'
41
+ 'cat.*/etc/shadow'
42
+ 'cat.*id_rsa'
43
+ 'cat.*\.ssh/'
44
+ 'cat.*credentials'
45
+ 'cat.*\.aws/'
46
+
47
+ # Network exfiltration
48
+ 'curl.*\|.*sh'
49
+ 'wget.*\|.*sh'
50
+ 'curl.*\|.*bash'
51
+ 'wget.*\|.*bash'
52
+
53
+ # Permission escalation
54
+ 'chmod 777'
55
+ 'chmod -R 777'
56
+ 'chmod.*\+s'
57
+
58
+ # History manipulation
59
+ 'history -c'
60
+ 'rm.*\.bash_history'
61
+ 'rm.*\.zsh_history'
62
+
63
+ # System modification
64
+ 'shutdown'
65
+ 'reboot'
66
+ 'init 0'
67
+ 'init 6'
68
+
69
+ # Git destructive operations
70
+ 'git init'
71
+ 'git reset --hard'
72
+ 'git push --force'
73
+ 'git push -f '
74
+ 'git push --force-with-lease'
75
+ 'git clean -f'
76
+ 'git checkout \.'
77
+ 'git restore \.'
78
+
79
+ # Virtual environment creation (prevents venv sprawl)
80
+ 'python -m venv'
81
+ 'python3 -m venv'
82
+ 'virtualenv '
83
+ 'conda create'
84
+ )
85
+
86
+ # Check command against dangerous patterns
87
+ for pattern in "${DANGEROUS_PATTERNS[@]}"; do
88
+ if echo "$COMMAND" | grep -qiE "$pattern"; then
89
+ # Allow git init in worktree context
90
+ if [[ "$pattern" == "git init" ]] && [[ "${CAWS_WORKTREE_CONTEXT:-0}" == "1" ]]; then
91
+ continue
92
+ fi
93
+
94
+ # Allow venv commands if target matches designated venv path from scope.json
95
+ if echo "$pattern" | grep -qE '(python.*venv|virtualenv|conda create)'; then
96
+ PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
97
+ SCOPE_FILE="$PROJECT_DIR/.caws/scope.json"
98
+ if [[ -f "$SCOPE_FILE" ]] && command -v node >/dev/null 2>&1; then
99
+ DESIGNATED_VENV=$(node -e "try { const s = JSON.parse(require('fs').readFileSync('$SCOPE_FILE','utf8')); console.log(s.designatedVenvPath || ''); } catch(e) { console.log(''); }" 2>/dev/null || echo "")
100
+ if [[ -n "$DESIGNATED_VENV" ]] && echo "$COMMAND" | grep -qF "$DESIGNATED_VENV"; then
101
+ continue
102
+ fi
103
+ fi
104
+ fi
105
+
106
+ # Output to stderr for Claude to see
107
+ echo "BLOCKED: Command matches dangerous pattern: $pattern" >&2
108
+ echo "Command was: $COMMAND" >&2
109
+
110
+ # Exit code 2 blocks the tool and shows stderr to Claude
111
+ exit 2
112
+ fi
113
+ done
114
+
115
+ # Check for sudo without specific allowed commands
116
+ if echo "$COMMAND" | grep -qE '^sudo\s' && ! echo "$COMMAND" | grep -qE 'sudo (npm|yarn|pnpm|brew|apt-get|apt|dnf|yum)'; then
117
+ echo "BLOCKED: sudo commands require explicit approval" >&2
118
+ echo "If this command is safe, please run it manually in your terminal" >&2
119
+ exit 2
120
+ fi
121
+
122
+ # Allow the command
123
+ exit 0
@@ -0,0 +1,117 @@
1
+ #!/bin/bash
2
+ # CAWS Lite-Mode Sprawl Check Hook
3
+ # Checks for file sprawl patterns (banned names, venv dirs, doc sprawl)
4
+ # @author @darianrosebrook
5
+
6
+ set -euo pipefail
7
+
8
+ # Read JSON input from Claude Code
9
+ INPUT=$(cat)
10
+
11
+ # Extract tool info
12
+ TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // ""')
13
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
14
+
15
+ # Only check Write operations (new file creation)
16
+ if [[ "$TOOL_NAME" != "Write" ]]; 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
+ SCOPE_FILE="$PROJECT_DIR/.caws/scope.json"
26
+
27
+ # Only active in lite mode (scope.json present, no working-spec.yaml)
28
+ if [[ ! -f "$SCOPE_FILE" ]]; then
29
+ exit 0
30
+ fi
31
+
32
+ # Get relative path
33
+ # Get relative path (portable — macOS realpath lacks --relative-to)
34
+ if [[ "$FILE_PATH" == "$PROJECT_DIR"/* ]]; then
35
+ REL_PATH="${FILE_PATH#$PROJECT_DIR/}"
36
+ else
37
+ REL_PATH="$FILE_PATH"
38
+ fi
39
+ BASENAME=$(basename "$REL_PATH")
40
+
41
+ # Use Node.js to check banned patterns
42
+ if command -v node >/dev/null 2>&1; then
43
+ SPRAWL_CHECK=$(node -e "
44
+ const fs = require('fs');
45
+ const path = require('path');
46
+ try {
47
+ const scope = JSON.parse(fs.readFileSync('$SCOPE_FILE', 'utf8'));
48
+ const filePath = '$REL_PATH';
49
+ const basename = '$BASENAME';
50
+ const banned = scope.bannedPatterns || {};
51
+
52
+ function matchGlob(str, pattern) {
53
+ const regex = new RegExp('^' + pattern.replace(/\\*/g, '.*').replace(/\\?/g, '.') + '$');
54
+ return regex.test(str);
55
+ }
56
+
57
+ // Check banned file patterns
58
+ for (const p of (banned.files || [])) {
59
+ if (matchGlob(basename, p)) {
60
+ console.log('banned_file:' + p);
61
+ process.exit(0);
62
+ }
63
+ }
64
+
65
+ // Check banned doc patterns
66
+ for (const p of (banned.docs || [])) {
67
+ if (matchGlob(basename, p)) {
68
+ console.log('banned_doc:' + p);
69
+ process.exit(0);
70
+ }
71
+ }
72
+
73
+ // Check banned directory patterns
74
+ const parts = filePath.split('/');
75
+ for (const part of parts) {
76
+ for (const p of (banned.directories || [])) {
77
+ if (matchGlob(part, p)) {
78
+ console.log('banned_dir:' + p + ':' + part);
79
+ process.exit(0);
80
+ }
81
+ }
82
+ }
83
+
84
+ console.log('ok');
85
+ } catch (error) {
86
+ console.log('error:' + error.message);
87
+ }
88
+ " 2>&1)
89
+
90
+ if [[ "$SPRAWL_CHECK" == banned_file:* ]]; then
91
+ PATTERN="${SPRAWL_CHECK#banned_file:}"
92
+ echo "BLOCKED: File name matches banned sprawl pattern: $PATTERN" >&2
93
+ echo "File: $REL_PATH" >&2
94
+ echo "Banned patterns prevent shadow files like *-enhanced.*, *-final.*, *-v2.*, *-copy.*" >&2
95
+ echo "Instead, modify the original file directly." >&2
96
+ exit 2
97
+ fi
98
+
99
+ if [[ "$SPRAWL_CHECK" == banned_doc:* ]]; then
100
+ PATTERN="${SPRAWL_CHECK#banned_doc:}"
101
+ echo "BLOCKED: Doc file matches banned sprawl pattern: $PATTERN" >&2
102
+ echo "File: $REL_PATH" >&2
103
+ echo "Avoid creating many summary/recap/plan files. Update existing documentation instead." >&2
104
+ exit 2
105
+ fi
106
+
107
+ if [[ "$SPRAWL_CHECK" == banned_dir:* ]]; then
108
+ IFS=':' read -r _ PATTERN DIR_NAME <<< "$SPRAWL_CHECK"
109
+ echo "BLOCKED: Directory matches banned pattern: $PATTERN (directory: $DIR_NAME)" >&2
110
+ echo "File: $REL_PATH" >&2
111
+ echo "Use the designated venv path instead of creating new virtual environments." >&2
112
+ exit 2
113
+ fi
114
+ fi
115
+
116
+ # Allow the operation
117
+ exit 0