specweave 1.0.31 → 1.0.33

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 (123) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CLAUDE.md +205 -148
  3. package/README.md +0 -2
  4. package/bin/specweave.js +11 -0
  5. package/dist/src/cli/commands/init.js +1 -1
  6. package/dist/src/cli/commands/init.js.map +1 -1
  7. package/dist/src/cli/commands/update-instructions.d.ts +16 -0
  8. package/dist/src/cli/commands/update-instructions.d.ts.map +1 -0
  9. package/dist/src/cli/commands/update-instructions.js +134 -0
  10. package/dist/src/cli/commands/update-instructions.js.map +1 -0
  11. package/dist/src/cli/helpers/init/directory-structure.d.ts +28 -1
  12. package/dist/src/cli/helpers/init/directory-structure.d.ts.map +1 -1
  13. package/dist/src/cli/helpers/init/directory-structure.js +163 -33
  14. package/dist/src/cli/helpers/init/directory-structure.js.map +1 -1
  15. package/dist/src/cli/helpers/init/index.d.ts +2 -1
  16. package/dist/src/cli/helpers/init/index.d.ts.map +1 -1
  17. package/dist/src/cli/helpers/init/index.js +3 -1
  18. package/dist/src/cli/helpers/init/index.js.map +1 -1
  19. package/dist/src/cli/helpers/init/instruction-file-merger.d.ts +23 -0
  20. package/dist/src/cli/helpers/init/instruction-file-merger.d.ts.map +1 -0
  21. package/dist/src/cli/helpers/init/instruction-file-merger.js +243 -0
  22. package/dist/src/cli/helpers/init/instruction-file-merger.js.map +1 -0
  23. package/dist/src/cli/helpers/init/plugin-installer.js +49 -0
  24. package/dist/src/cli/helpers/init/plugin-installer.js.map +1 -1
  25. package/dist/src/config/types.d.ts +2 -2
  26. package/dist/src/core/living-docs/external-sync-orchestrator.d.ts +26 -0
  27. package/dist/src/core/living-docs/external-sync-orchestrator.d.ts.map +1 -1
  28. package/dist/src/core/living-docs/external-sync-orchestrator.js +61 -0
  29. package/dist/src/core/living-docs/external-sync-orchestrator.js.map +1 -1
  30. package/dist/src/core/living-docs/scaffolding/index.d.ts +12 -0
  31. package/dist/src/core/living-docs/scaffolding/index.d.ts.map +1 -0
  32. package/dist/src/core/living-docs/scaffolding/index.js +15 -0
  33. package/dist/src/core/living-docs/scaffolding/index.js.map +1 -0
  34. package/dist/src/core/living-docs/scaffolding/merger.d.ts +183 -0
  35. package/dist/src/core/living-docs/scaffolding/merger.d.ts.map +1 -0
  36. package/dist/src/core/living-docs/scaffolding/merger.js +523 -0
  37. package/dist/src/core/living-docs/scaffolding/merger.js.map +1 -0
  38. package/dist/src/core/living-docs/scaffolding/scaffold.d.ts +102 -0
  39. package/dist/src/core/living-docs/scaffolding/scaffold.d.ts.map +1 -0
  40. package/dist/src/core/living-docs/scaffolding/scaffold.js +346 -0
  41. package/dist/src/core/living-docs/scaffolding/scaffold.js.map +1 -0
  42. package/dist/src/core/living-docs/scaffolding/template-engine.d.ts +108 -0
  43. package/dist/src/core/living-docs/scaffolding/template-engine.d.ts.map +1 -0
  44. package/dist/src/core/living-docs/scaffolding/template-engine.js +204 -0
  45. package/dist/src/core/living-docs/scaffolding/template-engine.js.map +1 -0
  46. package/dist/src/core/living-docs/sync-helpers/generators.d.ts +38 -2
  47. package/dist/src/core/living-docs/sync-helpers/generators.d.ts.map +1 -1
  48. package/dist/src/core/living-docs/sync-helpers/generators.js +65 -10
  49. package/dist/src/core/living-docs/sync-helpers/generators.js.map +1 -1
  50. package/dist/src/core/living-docs/sync-helpers/index.d.ts +1 -1
  51. package/dist/src/core/living-docs/sync-helpers/index.d.ts.map +1 -1
  52. package/dist/src/core/living-docs/sync-helpers/index.js.map +1 -1
  53. package/dist/src/core/tools/index.d.ts +11 -0
  54. package/dist/src/core/tools/index.d.ts.map +1 -0
  55. package/dist/src/core/tools/index.js +10 -0
  56. package/dist/src/core/tools/index.js.map +1 -0
  57. package/dist/src/core/tools/tool-event-bus.d.ts +33 -0
  58. package/dist/src/core/tools/tool-event-bus.d.ts.map +1 -0
  59. package/dist/src/core/tools/tool-event-bus.js +84 -0
  60. package/dist/src/core/tools/tool-event-bus.js.map +1 -0
  61. package/dist/src/core/tools/tool-index-builder.d.ts +27 -0
  62. package/dist/src/core/tools/tool-index-builder.d.ts.map +1 -0
  63. package/dist/src/core/tools/tool-index-builder.js +289 -0
  64. package/dist/src/core/tools/tool-index-builder.js.map +1 -0
  65. package/dist/src/core/tools/tool-registry.d.ts +51 -0
  66. package/dist/src/core/tools/tool-registry.d.ts.map +1 -0
  67. package/dist/src/core/tools/tool-registry.js +224 -0
  68. package/dist/src/core/tools/tool-registry.js.map +1 -0
  69. package/dist/src/core/tools/tool-search-engine.d.ts +22 -0
  70. package/dist/src/core/tools/tool-search-engine.d.ts.map +1 -0
  71. package/dist/src/core/tools/tool-search-engine.js +174 -0
  72. package/dist/src/core/tools/tool-search-engine.js.map +1 -0
  73. package/dist/src/core/tools/types/tool-registry-types.d.ts +112 -0
  74. package/dist/src/core/tools/types/tool-registry-types.d.ts.map +1 -0
  75. package/dist/src/core/tools/types/tool-registry-types.js +7 -0
  76. package/dist/src/core/tools/types/tool-registry-types.js.map +1 -0
  77. package/dist/src/init/compliance/types.d.ts +1 -1
  78. package/package.json +1 -1
  79. package/plugins/specweave/hooks/hooks.json +3 -13
  80. package/plugins/specweave/hooks/lib/common-setup.sh +47 -321
  81. package/plugins/specweave/hooks/lib/migrate-increment-work.sh +5 -5
  82. package/plugins/specweave/hooks/lib/sync-spec-content.sh +5 -5
  83. package/plugins/specweave/hooks/universal/dispatcher.mjs +4 -5
  84. package/plugins/specweave/hooks/universal/fail-fast-wrapper.sh +43 -296
  85. package/plugins/specweave/hooks/universal/hook-wrapper.sh +3 -1
  86. package/plugins/specweave/hooks/user-prompt-submit.sh +1 -1
  87. package/plugins/specweave/hooks/v2/dispatchers/post-tool-use.sh +2 -2
  88. package/plugins/specweave/hooks/v2/dispatchers/session-start.sh +1 -10
  89. package/plugins/specweave/hooks/v2/guards/completion-guard.sh +12 -29
  90. package/plugins/specweave/hooks/v2/guards/increment-duplicate-guard.sh +27 -29
  91. package/plugins/specweave/hooks/v2/guards/metadata-json-guard.sh +10 -4
  92. package/plugins/specweave/hooks/v2/guards/spec-validation-guard.sh +139 -0
  93. package/plugins/specweave/hooks/v2/guards/task-ac-sync-guard.sh +4 -2
  94. package/plugins/specweave/hooks/v2/session-end.sh +3 -1
  95. package/plugins/specweave/hooks/v2/session-start.sh +3 -1
  96. package/plugins/specweave/skills/increment-planner/templates/plan.md +14 -0
  97. package/plugins/specweave/skills/update-instructions/SKILL.md +80 -0
  98. package/plugins/specweave-ado/hooks/post-living-docs-update.sh +1 -1
  99. package/plugins/specweave-mobile/README.md +55 -35
  100. package/plugins/specweave-mobile/agents/mobile-architect/AGENT.md +805 -329
  101. package/plugins/specweave-mobile/skills/expo-workflow/SKILL.md +226 -9
  102. package/plugins/specweave-mobile/skills/native-modules/SKILL.md +221 -20
  103. package/plugins/specweave-mobile/skills/performance-optimization/SKILL.md +186 -14
  104. package/plugins/specweave-mobile/skills/react-native-setup/SKILL.md +151 -54
  105. package/plugins/specweave-release/commands/npm.md +61 -17
  106. package/plugins/specweave-release/hooks/post-task-completion.sh +2 -3
  107. package/src/templates/AGENTS.md.template +34 -0
  108. package/src/templates/CLAUDE.md.template +121 -155
  109. package/plugins/specweave/hooks/config-env-separator.sh +0 -99
  110. package/plugins/specweave/hooks/github-metadata-guard.sh +0 -73
  111. package/plugins/specweave/hooks/lib/circuit-breaker.sh +0 -381
  112. package/plugins/specweave/hooks/lib/crash-prevention.sh +0 -336
  113. package/plugins/specweave/hooks/lib/logging.sh +0 -231
  114. package/plugins/specweave/hooks/lib/metrics.sh +0 -347
  115. package/plugins/specweave/hooks/lib/semaphore.sh +0 -216
  116. package/plugins/specweave/hooks/project-folder-guard.sh +0 -274
  117. package/plugins/specweave/hooks/spec-project-validator.sh +0 -210
  118. package/plugins/specweave/hooks/v2/guards/bash-file-guard.sh +0 -212
  119. package/plugins/specweave/hooks/v2/guards/bash-file-guard.test.sh +0 -163
  120. package/plugins/specweave/hooks/v2/guards/features-folder-guard.sh +0 -51
  121. package/plugins/specweave/hooks/v2/guards/increment-root-guard.sh +0 -63
  122. package/plugins/specweave/hooks/v2/guards/per-us-project-validator.sh +0 -335
  123. package/plugins/specweave/hooks/v2/guards/per-us-project-validator.test.sh +0 -406
@@ -1,212 +0,0 @@
1
- #!/bin/bash
2
- # bash-file-guard.sh - CRITICAL: Block Bash file creation patterns that cause infinite hangs
3
- #
4
- # ROOT CAUSE: When Claude uses heredoc (cat << EOF ... EOF) to create files and the
5
- # content gets truncated due to token limits, the shell waits FOREVER for the EOF
6
- # terminator that will never come. This causes Claude Code to hang indefinitely
7
- # (2+ hours observed in production crashes).
8
- #
9
- # SOLUTION: Block ALL bash file creation patterns and force use of Write/Edit tools.
10
- # Write/Edit tools are:
11
- # - Atomic (no intermediate states)
12
- # - Token-safe (handled by Claude Code, not shell)
13
- # - Recoverable (no zombie processes)
14
- #
15
- # PreToolUse hook for Bash commands - exit 0 allows, exit 2 blocks
16
- #
17
- # v0.32.1 - Initial implementation based on crash analysis from 2025-12-06
18
-
19
- set +e
20
-
21
- [[ "${SPECWEAVE_DISABLE_HOOKS:-0}" == "1" ]] && exit 0
22
-
23
- # Read stdin for tool input
24
- INPUT=$(cat)
25
-
26
- # Extract the command being executed
27
- # The Bash tool has a "command" parameter
28
- # Use jq for proper JSON parsing (handles escaped quotes correctly)
29
- if command -v jq &> /dev/null; then
30
- COMMAND=$(echo "$INPUT" | jq -r '.command // empty' 2>/dev/null)
31
- else
32
- # Fallback: simple regex (may fail on complex JSON with escaped quotes)
33
- COMMAND=$(echo "$INPUT" | grep -o '"command"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"command"[[:space:]]*:[[:space:]]*"\(.*\)"/\1/')
34
- fi
35
-
36
- # If no command found or empty, allow (safety)
37
- if [[ -z "$COMMAND" ]]; then
38
- exit 0
39
- fi
40
-
41
- # === PATTERN DETECTION ===
42
- # These patterns WILL cause infinite hangs or truncation issues
43
-
44
- BLOCKED=0
45
- PATTERN_NAME=""
46
-
47
- # === HEREDOC PATTERNS (MOST DANGEROUS - infinite hang!) ===
48
-
49
- # Pattern 1a: Standard heredoc with any delimiter
50
- # Matches: cat << EOF, cat << 'EOF', cat << "EOF", cat <<EOF (no space)
51
- if echo "$COMMAND" | grep -qE "(cat|tee).*<<-?[[:space:]]*['\"]?[A-Za-z]+" 2>/dev/null; then
52
- BLOCKED=1
53
- PATTERN_NAME="heredoc (cat << DELIMITER)"
54
- fi
55
-
56
- # Pattern 1b: Heredoc with escaped quotes in JSON (\"EOF\")
57
- # JSON escapes double quotes, so << "EOF" becomes << \"EOF\"
58
- if echo "$COMMAND" | grep -qE "(cat|tee).*<<-?[[:space:]]*\\\\\"[A-Za-z]+" 2>/dev/null; then
59
- BLOCKED=1
60
- PATTERN_NAME="heredoc with quoted delimiter"
61
- fi
62
-
63
- # Pattern 1c: SUPER AGGRESSIVE - block ANY heredoc-like pattern
64
- # Catches edge cases where content has << followed by word boundary
65
- # This is the NUCLEAR option - heredocs are NEVER safe in Claude context
66
- if echo "$COMMAND" | grep -qE "<<[[:space:]]*['\"]?[A-Za-z_]+" 2>/dev/null; then
67
- BLOCKED=1
68
- PATTERN_NAME="heredoc pattern (any form)"
69
- fi
70
-
71
- # Pattern 1d: Multiline content in the command itself
72
- # If the command contains literal newlines, it might be a heredoc in disguise
73
- if echo "$COMMAND" | grep -qE $'\n' 2>/dev/null; then
74
- # Commands with newlines are suspicious - check for file redirects
75
- if echo "$COMMAND" | grep -qE '>' 2>/dev/null; then
76
- BLOCKED=1
77
- PATTERN_NAME="multiline command with redirect (heredoc variant)"
78
- fi
79
- fi
80
-
81
- # === CAT STDIN REDIRECT (VERY DANGEROUS!) ===
82
- # Pattern 2: Plain cat with output redirect but no input
83
- # Matches: cat > file.txt (waits forever for stdin!)
84
- # This is just as dangerous as heredoc - shell waits for input
85
- if echo "$COMMAND" | grep -qE "^[[:space:]]*cat[[:space:]]+>[[:space:]]*[^>]" 2>/dev/null; then
86
- BLOCKED=1
87
- PATTERN_NAME="cat > file (stdin redirect - waits forever!)"
88
- fi
89
-
90
- # === DD COMMAND (EQUALLY DANGEROUS!) ===
91
- # Pattern 2b: dd with stdin or output file
92
- # Matches: dd if=/dev/stdin of=file, dd of=file bs=1024
93
- # dd waits for input EXACTLY like cat > file
94
- if echo "$COMMAND" | grep -qE "^[[:space:]]*dd[[:space:]]" 2>/dev/null; then
95
- if echo "$COMMAND" | grep -qE "of=" 2>/dev/null; then
96
- BLOCKED=1
97
- PATTERN_NAME="dd command (waits for stdin like cat)"
98
- fi
99
- fi
100
-
101
- # === READ COMMAND (WAITS FOR INPUT) ===
102
- # Pattern 2c: read with redirect
103
- # Matches: read VAR > file (waits for input from user/stdin)
104
- if echo "$COMMAND" | grep -qE "^[[:space:]]*read[[:space:]].*>" 2>/dev/null; then
105
- BLOCKED=1
106
- PATTERN_NAME="read command with redirect (waits for input)"
107
- fi
108
-
109
- # === STREAM REDIRECT EXCEPTION ===
110
- # Allow redirects to /dev/stdout, /dev/stderr, /dev/null (safe stream operations)
111
- if echo "$COMMAND" | grep -qE ">[[:space:]]*/dev/(stdout|stderr|null)" 2>/dev/null; then
112
- exit 0 # Safe stream redirect, not file creation
113
- fi
114
-
115
- # === ECHO/PRINTF PATTERNS ===
116
- # These can cause truncation issues and are bad practice
117
- # NOTE: Match any echo/printf that writes to file (single > not >>)
118
-
119
- # Pattern 3: Echo with redirect to file
120
- # Matches: echo anything > file (but not >>)
121
- # Use negative lookahead simulation: check for > but then verify not >>
122
- if echo "$COMMAND" | grep -qE '^[[:space:]]*(echo)[[:space:]]' 2>/dev/null; then
123
- if echo "$COMMAND" | grep -qE '>[[:space:]]*[^>]' 2>/dev/null; then
124
- # Check it's not append (>>)
125
- if ! echo "$COMMAND" | grep -qE '>>' 2>/dev/null; then
126
- BLOCKED=1
127
- PATTERN_NAME="echo > file"
128
- fi
129
- fi
130
- fi
131
-
132
- # Pattern 4: Printf with redirect to file
133
- if echo "$COMMAND" | grep -qE '^[[:space:]]*(printf)[[:space:]]' 2>/dev/null; then
134
- if echo "$COMMAND" | grep -qE '>[[:space:]]*[^>]' 2>/dev/null; then
135
- # Check it's not append (>>)
136
- if ! echo "$COMMAND" | grep -qE '>>' 2>/dev/null; then
137
- BLOCKED=1
138
- PATTERN_NAME="printf > file"
139
- fi
140
- fi
141
- fi
142
-
143
- # Pattern 5: Multi-line string with cat
144
- # Matches: cat > file -e "multi\nline", etc.
145
- if echo "$COMMAND" | grep -qE "cat[[:space:]]+-[a-z]*[[:space:]]+['\"].*\\\\n" 2>/dev/null; then
146
- BLOCKED=1
147
- PATTERN_NAME="cat with multi-line string"
148
- fi
149
-
150
- # === SAFE EXCEPTIONS ===
151
- # Append operations (>>) are safe - echo/printf complete immediately
152
- if echo "$COMMAND" | grep -qE "(echo|printf)[[:space:]].*>>[[:space:]]*" 2>/dev/null; then
153
- BLOCKED=0
154
- PATTERN_NAME=""
155
- fi
156
-
157
- # === EXCEPTION: Allow safe bash operations ===
158
- # These are safe because they don't involve file content creation
159
-
160
- # Exception 1: Simple redirects from commands (not content creation)
161
- # Safe: curl ... > file, wget ... > file (command output, not content creation)
162
- # These won't hang because the content comes from the command, not heredoc
163
- if echo "$COMMAND" | grep -qE "^(curl|wget|git|npm|node|python|pip)[[:space:]].*>" 2>/dev/null; then
164
- # These are downloading/generating content from commands, not creating from heredoc
165
- # Only block if also contains heredoc/echo patterns
166
- if [[ "$PATTERN_NAME" != "heredoc"* ]] && [[ "$PATTERN_NAME" != "echo"* ]] && [[ "$PATTERN_NAME" != "printf"* ]]; then
167
- exit 0 # Allow
168
- fi
169
- fi
170
-
171
- # === BLOCK DANGEROUS PATTERNS ===
172
- if [[ $BLOCKED -eq 1 ]]; then
173
- echo ""
174
- echo "=============================================================================="
175
- echo " šŸ›‘ BLOCKED: Bash file creation detected (Infinite Hang Prevention)"
176
- echo "=============================================================================="
177
- echo ""
178
- echo "Pattern detected: $PATTERN_NAME"
179
- echo ""
180
- echo "WHY THIS IS BLOCKED:"
181
- echo " When using heredoc (cat << EOF) or echo/printf redirects, if the content"
182
- echo " is truncated due to token limits, the shell waits FOREVER for input that"
183
- echo " will never come. This causes Claude Code to hang for hours with no recovery."
184
- echo ""
185
- echo " Observed crash: 2+ hour session hang from truncated heredoc (2025-12-06)"
186
- echo ""
187
- echo "SAFE ALTERNATIVES:"
188
- echo " ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”"
189
- echo " │ Instead of: Use: │"
190
- echo " ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤"
191
- echo " │ cat > file << EOF Write tool: Write(file_path, content) │"
192
- echo " │ echo \"...\" > file Write tool: Write(file_path, content) │"
193
- echo " │ printf ... > file Write tool: Write(file_path, content) │"
194
- echo " │ Modify existing Edit tool: Edit(file_path, old, new) │"
195
- echo " │ Append to file Read + Write tools │"
196
- echo " ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜"
197
- echo ""
198
- echo "WHAT IS ALLOWED:"
199
- echo " - mkdir -p /path/to/dir (directory creation)"
200
- echo " - git commit, git push (version control)"
201
- echo " - npm install, npm run (package management)"
202
- echo " - curl URL > file (downloading, not content creation)"
203
- echo " - rm, mv, cp (file operations)"
204
- echo ""
205
- echo "=============================================================================="
206
-
207
- exit 2 # Block the tool call
208
- fi
209
-
210
- # All other bash commands are allowed
211
- echo '{"decision":"allow"}'
212
- exit 0
@@ -1,163 +0,0 @@
1
- #!/bin/bash
2
- # Comprehensive test suite for bash-file-guard.sh
3
- # Tests ALL dangerous patterns that could cause infinite hangs
4
- #
5
- # Usage: bash bash-file-guard.test.sh
6
-
7
- set -e
8
-
9
- GUARD="$(dirname "$0")/bash-file-guard.sh"
10
- PASS=0
11
- FAIL=0
12
- TOTAL=0
13
-
14
- # Colors for output
15
- RED='\033[0;31m'
16
- GREEN='\033[0;32m'
17
- YELLOW='\033[1;33m'
18
- NC='\033[0m' # No Color
19
-
20
- test_should_block() {
21
- local name="$1"
22
- local command="$2"
23
- TOTAL=$((TOTAL + 1))
24
-
25
- # Properly escape the command for JSON: " becomes \"
26
- local escaped_command=$(echo "$command" | sed 's/"/\\"/g')
27
- result=$(echo "{\"command\": \"$escaped_command\"}" | bash "$GUARD" 2>&1; echo "EXIT:$?")
28
- exit_code=$(echo "$result" | grep -o 'EXIT:[0-9]*' | cut -d: -f2)
29
-
30
- if [[ "$exit_code" == "2" ]]; then
31
- echo -e "${GREEN}āœ“ BLOCKED${NC}: $name"
32
- PASS=$((PASS + 1))
33
- else
34
- echo -e "${RED}āœ— NOT BLOCKED${NC}: $name"
35
- echo " Command: $command"
36
- echo " Exit code: $exit_code (expected 2)"
37
- FAIL=$((FAIL + 1))
38
- fi
39
- }
40
-
41
- test_should_allow() {
42
- local name="$1"
43
- local command="$2"
44
- TOTAL=$((TOTAL + 1))
45
-
46
- # Properly escape the command for JSON: " becomes \"
47
- local escaped_command=$(echo "$command" | sed 's/"/\\"/g')
48
- result=$(echo "{\"command\": \"$escaped_command\"}" | bash "$GUARD" 2>&1; echo "EXIT:$?")
49
- exit_code=$(echo "$result" | grep -o 'EXIT:[0-9]*' | cut -d: -f2)
50
-
51
- if [[ "$exit_code" == "0" ]]; then
52
- echo -e "${GREEN}āœ“ ALLOWED${NC}: $name"
53
- PASS=$((PASS + 1))
54
- else
55
- echo -e "${RED}āœ— WRONGLY BLOCKED${NC}: $name"
56
- echo " Command: $command"
57
- echo " Exit code: $exit_code (expected 0)"
58
- FAIL=$((FAIL + 1))
59
- fi
60
- }
61
-
62
- echo "========================================"
63
- echo " BASH FILE GUARD - COMPREHENSIVE TESTS"
64
- echo "========================================"
65
- echo ""
66
-
67
- echo -e "${YELLOW}=== HEREDOC PATTERNS (MOST DANGEROUS) ===${NC}"
68
- test_should_block "Basic heredoc" "cat > file.txt << EOF"
69
- test_should_block "Heredoc with quotes" "cat > file.txt << 'EOF'"
70
- test_should_block "Heredoc double quotes" 'cat > file.txt << "EOF"'
71
- test_should_block "Heredoc reversed order" "cat << EOF > file.txt"
72
- test_should_block "Heredoc with END delimiter" "cat > file.txt << END"
73
- test_should_block "Heredoc with CONTENT delimiter" "cat << CONTENT > out.md"
74
- test_should_block "Heredoc with MARKER" "cat << MARKER"
75
- test_should_block "Heredoc tab strip" "cat <<- EOF > file.txt"
76
- test_should_block "Tee with heredoc" "tee file.txt << EOF"
77
- test_should_block "Tee reversed" "tee << EOF > file.txt"
78
- test_should_block "Heredoc with path" "cat > /tmp/test.md << EOF"
79
- test_should_block "Heredoc with variable path" 'cat > $HOME/test.txt << EOF'
80
-
81
- echo ""
82
- echo -e "${YELLOW}=== ECHO PATTERNS ===${NC}"
83
- test_should_block "Echo to file" "echo hello > file.txt"
84
- test_should_block "Echo quoted string" 'echo "hello world" > file.txt'
85
- test_should_block "Echo single quoted" "echo 'hello' > file.txt"
86
- test_should_block "Echo with -e flag" "echo -e 'hello\\nworld' > file.txt"
87
- test_should_block "Echo with -n flag" "echo -n 'hello' > file.txt"
88
- test_should_block "Echo variable" 'echo $VAR > file.txt'
89
- test_should_block "Echo to path" "echo content > /tmp/test.txt"
90
-
91
- echo ""
92
- echo -e "${YELLOW}=== PRINTF PATTERNS ===${NC}"
93
- test_should_block "Printf to file" "printf hello > file.txt"
94
- test_should_block "Printf format string" "printf '%s' hello > file.txt"
95
- test_should_block "Printf with newline" "printf '%s\\n' hello > file.txt"
96
- test_should_block "Printf quoted" 'printf "hello world" > file.txt'
97
-
98
- echo ""
99
- echo -e "${YELLOW}=== EDGE CASES - SHOULD BLOCK ===${NC}"
100
- test_should_block "Cat stdin to file (dangerous!)" "cat > file.txt"
101
- test_should_block "Bash -c with heredoc" "bash -c 'cat > f << EOF'"
102
- test_should_block "Double redirect" "echo a > file.txt > backup.txt"
103
- test_should_block "dd stdin to file" "dd if=/dev/stdin of=output.dat"
104
- test_should_block "dd output file" "dd of=file.bin bs=1024"
105
- test_should_block "dd with count" "dd of=output.img bs=512 count=100"
106
- test_should_block "read to file" "read VAR > input.txt"
107
- test_should_block "read prompt to file" 'read -p "Enter:" > log.txt'
108
-
109
- echo ""
110
- echo -e "${YELLOW}=== SAFE COMMANDS - SHOULD ALLOW ===${NC}"
111
- test_should_allow "Git status" "git status"
112
- test_should_allow "Git add" "git add ."
113
- test_should_allow "Git commit" "git commit -m 'message'"
114
- test_should_allow "Git push" "git push origin main"
115
- test_should_allow "Git diff" "git diff HEAD"
116
- test_should_allow "Npm install" "npm install"
117
- test_should_allow "Npm run build" "npm run build"
118
- test_should_allow "Npm test" "npm test"
119
- test_should_allow "Mkdir" "mkdir -p /tmp/test"
120
- test_should_allow "Ls" "ls -la"
121
- test_should_allow "Pwd" "pwd"
122
- test_should_allow "Cd" "cd /tmp"
123
- test_should_allow "Rm file" "rm -f /tmp/test.txt"
124
- test_should_allow "Mv file" "mv old.txt new.txt"
125
- test_should_allow "Cp file" "cp source.txt dest.txt"
126
- test_should_allow "Curl download" "curl -o file.txt https://example.com"
127
- test_should_allow "Wget download" "wget -O file.txt https://example.com"
128
- test_should_allow "Ps" "ps aux"
129
- test_should_allow "Kill" "kill -9 1234"
130
- test_should_allow "Grep" "grep pattern file.txt"
131
- test_should_allow "Find" "find . -name '*.ts'"
132
- test_should_allow "Chmod" "chmod +x script.sh"
133
- test_should_allow "Node run" "node script.js"
134
- test_should_allow "Python run" "python script.py"
135
- test_should_allow "Read from file" "cat file.txt"
136
- test_should_allow "Head" "head -n 10 file.txt"
137
- test_should_allow "Tail" "tail -f log.txt"
138
-
139
- echo ""
140
- echo -e "${YELLOW}=== APPEND OPERATIONS (should allow) ===${NC}"
141
- test_should_allow "Echo append" "echo hello >> file.txt"
142
- test_should_allow "Printf append" "printf '%s' data >> file.txt"
143
-
144
- echo ""
145
- echo -e "${YELLOW}=== STREAM REDIRECTS (should allow) ===${NC}"
146
- test_should_allow "Echo to stderr" "echo 'error' > /dev/stderr"
147
- test_should_allow "Printf to stdout" "printf '%s' output > /dev/stdout"
148
- test_should_allow "Echo to null" "echo 'discard' > /dev/null"
149
-
150
- echo ""
151
- echo "========================================"
152
- echo " RESULTS"
153
- echo "========================================"
154
- echo -e "Total: $TOTAL"
155
- echo -e "${GREEN}Passed: $PASS${NC}"
156
- if [[ $FAIL -gt 0 ]]; then
157
- echo -e "${RED}Failed: $FAIL${NC}"
158
- exit 1
159
- else
160
- echo -e "Failed: 0"
161
- echo ""
162
- echo -e "${GREEN}ALL TESTS PASSED!${NC}"
163
- fi
@@ -1,51 +0,0 @@
1
- #!/bin/bash
2
- # features-folder-guard.sh - Block writes to obsolete _features/ folder
3
- #
4
- # ROOT CAUSE: The _features/ folder is OBSOLETE since v5.0.0. Features should
5
- # live in {project}/FS-XXX/ folders. LLM agents sometimes create files in
6
- # _features/ due to outdated documentation references.
7
- #
8
- # SOLUTION: Block all Write/Edit operations targeting _features/ folder.
9
- # This forces proper project-based feature folder structure.
10
- #
11
- # PreToolUse hook for Write/Edit commands - exit 0 allows, exit 2 blocks
12
- #
13
- # v0.33.0 - Initial implementation based on bug analysis from 2025-12-06
14
-
15
- set +e
16
-
17
- [[ "${SPECWEAVE_DISABLE_HOOKS:-0}" == "1" ]] && exit 0
18
-
19
- # Read stdin for tool input
20
- INPUT=$(cat)
21
-
22
- # Extract the file_path being written/edited
23
- # Claude Code passes tool input in .tool_input.file_path format
24
- if command -v jq &> /dev/null; then
25
- FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .file_path // empty' 2>/dev/null)
26
- else
27
- FILE_PATH=$(echo "$INPUT" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"file_path"[[:space:]]*:[[:space:]]*"\(.*\)"/\1/')
28
- fi
29
-
30
- # If no file_path found, allow (safety)
31
- if [[ -z "$FILE_PATH" ]]; then
32
- exit 0
33
- fi
34
-
35
- # === PATTERN DETECTION ===
36
- # Block any write to _features/ folder (obsolete since v5.0.0)
37
-
38
- # Pattern: .specweave/docs/internal/specs/_features/
39
- if [[ "$FILE_PATH" =~ \.specweave/docs/internal/specs/_features/ ]]; then
40
- cat << 'EOF'
41
- {
42
- "decision": "block",
43
- "reason": "🚫 BLOCKED: Writing to _features/ folder (OBSOLETE since v5.0.0)\n\nāš ļø The _features/ folder has been deprecated.\nFeatures must now live in project folders: .specweave/docs/internal/specs/{project}/FS-XXX/\n\nšŸ“‹ CORRECT structure:\n .specweave/docs/internal/specs/specweave/FS-116/FEATURE.md āœ…\n .specweave/docs/internal/specs/backend/FS-117/us-001.md āœ…\n\nāŒ OBSOLETE structure:\n .specweave/docs/internal/specs/_features/FS-116/FEATURE.md āŒ\n\nšŸ”§ To fix: Use spec.md `project:` field to determine target folder.\nSee: CLAUDE.md section '2c. spec.md MUST Have project:'"
44
- }
45
- EOF
46
- exit 2
47
- fi
48
-
49
- # Allow all other writes
50
- printf '{"decision":"allow"}'
51
- exit 0
@@ -1,63 +0,0 @@
1
- #!/bin/bash
2
- # increment-root-guard.sh - Block files at increment root (except allowed)
3
- #
4
- # ROOT CAUSE: Agents sometimes create files like COMPLETION_REPORT.md,
5
- # COMPLETION_SUMMARY.md, or other .md files at increment root instead of
6
- # placing them in appropriate subfolders (reports/, scripts/, logs/, etc.)
7
- #
8
- # SOLUTION: Block Write operations to increment root for non-standard files.
9
- # Only allow: metadata.json, spec.md, plan.md, tasks.md
10
- # Everything else MUST go in subfolders: reports/, scripts/, logs/, backups/, docs/
11
- #
12
- # PreToolUse hook for Write command - exit 0 allows, exit 2 blocks
13
- #
14
- # v0.33.0 - Initial implementation based on bug analysis from 2025-12-09
15
-
16
- set +e
17
-
18
- [[ "${SPECWEAVE_DISABLE_HOOKS:-0}" == "1" ]] && exit 0
19
-
20
- # Read stdin for tool input
21
- INPUT=$(cat)
22
-
23
- # Extract the file_path being written
24
- # Claude Code passes tool input in .tool_input.file_path format
25
- if command -v jq &> /dev/null; then
26
- FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .file_path // empty' 2>/dev/null)
27
- else
28
- # Fallback: try both patterns
29
- FILE_PATH=$(echo "$INPUT" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"file_path"[[:space:]]*:[[:space:]]*"\(.*\)"/\1/')
30
- fi
31
-
32
- # If no file_path found, allow (safety)
33
- if [[ -z "$FILE_PATH" ]]; then
34
- exit 0
35
- fi
36
-
37
- # === PATTERN DETECTION ===
38
- # Block files at increment root that should be in subfolders
39
-
40
- # Pattern: .specweave/increments/####-name/FILE.md (at root, not in subfolder)
41
- # Allowed at root: metadata.json, spec.md, plan.md, tasks.md
42
- if [[ "$FILE_PATH" =~ \.specweave/increments/[0-9]{3,4}E?-[^/]+/([^/]+)$ ]]; then
43
- FILENAME="${BASH_REMATCH[1]}"
44
-
45
- # Allow standard increment files at root
46
- if [[ "$FILENAME" =~ ^(metadata\.json|spec\.md|plan\.md|tasks\.md)$ ]]; then
47
- printf '{"decision":"allow"}'
48
- exit 0
49
- fi
50
-
51
- # Block everything else at increment root
52
- cat << EOF
53
- {
54
- "decision": "block",
55
- "reason": "🚫 BLOCKED: File '$FILENAME' should be in a subfolder, not at increment root\n\nāš ļø CLAUDE.md Folder Structure Rule:\n Inside increment folders - ONLY at root: spec.md, plan.md, tasks.md, metadata.json\n Everything else → subfolders: reports/, scripts/, logs/, backups/, docs/\n\nšŸ“‹ CORRECT structure:\n .specweave/increments/####-name/reports/COMPLETION_REPORT.md āœ…\n .specweave/increments/####-name/reports/COMPLETION_SUMMARY.md āœ…\n .specweave/increments/####-name/scripts/analyze.sh āœ…\n\nāŒ WRONG structure:\n .specweave/increments/####-name/COMPLETION_REPORT.md āŒ\n .specweave/increments/####-name/COMPLETION_SUMMARY.md āŒ\n\nšŸ”§ To fix: Create file in reports/ subfolder instead.\nUse: Write({ file_path: \".specweave/increments/####-name/reports/$FILENAME\", ... })\n\nSee: CLAUDE.md section 'Folder Structure'"
56
- }
57
- EOF
58
- exit 2
59
- fi
60
-
61
- # Allow all other writes (files in subfolders, or outside increments)
62
- printf '{"decision":"allow"}'
63
- exit 0