specweave 1.0.27 → 1.0.29

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/CLAUDE.md CHANGED
@@ -1297,6 +1297,54 @@ npm run rebuild
1297
1297
 
1298
1298
  ---
1299
1299
 
1300
+ ## Hook Development (v1.0.27+)
1301
+
1302
+ ### Claude Code Hook Input Format
1303
+
1304
+ **CRITICAL: PreToolUse hooks receive JSON with `tool_input` wrapper!**
1305
+
1306
+ ```json
1307
+ // PreToolUse:Write/Edit receives:
1308
+ {
1309
+ "tool_name": "Write",
1310
+ "tool_input": {
1311
+ "file_path": "/path/to/file.md",
1312
+ "content": "file content..."
1313
+ }
1314
+ }
1315
+
1316
+ // PreToolUse:Bash receives (NO tool_input wrapper):
1317
+ {
1318
+ "command": "npm install"
1319
+ }
1320
+ ```
1321
+
1322
+ **Correct extraction patterns:**
1323
+ ```bash
1324
+ # For Write/Edit tools (nested in tool_input):
1325
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .file_path // empty')
1326
+ CONTENT=$(echo "$INPUT" | jq -r '.tool_input.content // .tool_input.new_string // empty')
1327
+
1328
+ # For Bash tool (direct):
1329
+ COMMAND=$(echo "$INPUT" | jq -r '.command // empty')
1330
+
1331
+ # ⚠️ WRONG - will fail silently:
1332
+ FILE_PATH=$(echo "$INPUT" | jq -r '.file_path // empty') # Missing tool_input!
1333
+ ```
1334
+
1335
+ **Hook return format (PreToolUse):**
1336
+ ```bash
1337
+ # Allow the tool call:
1338
+ echo '{"decision": "allow"}'
1339
+ exit 0
1340
+
1341
+ # Block the tool call:
1342
+ echo '{"decision": "block", "reason": "Error message here"}'
1343
+ exit 2
1344
+ ```
1345
+
1346
+ ---
1347
+
1300
1348
  ## Quick Reference
1301
1349
 
1302
1350
  | Aspect | Rule |
@@ -1305,6 +1353,7 @@ npm run rebuild
1305
1353
  | **Bash guard** | Hook `bash-file-guard.sh` BLOCKS dangerous patterns (v0.32.1+) |
1306
1354
  | Skills vs Agents | Skills = auto-activate (keywords), Agents = explicit `Task()` |
1307
1355
  | Hook events | PostToolUse, PreToolUse, UserPromptSubmit, Stop, SessionStart/End, etc. |
1356
+ | Hook input | Write/Edit use `.tool_input.file_path`, Bash uses `.command` |
1308
1357
  | Cache location | `.specweave/cache/` (24h TTL) |
1309
1358
  | Pre-commit | Blocks 50+ deletions, `rm -rf` on protected dirs |
1310
1359
  | Stuck session | Kill + `pkill -f "cat.*EOF"` + clean locks + restart |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specweave",
3
- "version": "1.0.27",
3
+ "version": "1.0.29",
4
4
  "description": "Spec-driven development framework for Claude Code. AI-native workflow with living documentation, intelligent agents, and multilingual support (9 languages). Enterprise-grade traceability with permanent specs and temporary increments.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -15,17 +15,19 @@
15
15
 
16
16
  set -euo pipefail
17
17
 
18
- TOOL_NAME="$1"
19
- shift
20
- TOOL_ARGS="$*"
18
+ # Read stdin for tool input (Claude Code passes JSON via stdin)
19
+ INPUT=$(cat)
20
+
21
+ # Extract tool name - Claude Code passes it at top level
22
+ TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // ""' 2>/dev/null || echo "")
21
23
 
22
24
  # Only check Write tool operations to specs/ folder
23
25
  if [ "$TOOL_NAME" != "Write" ]; then
24
26
  exit 0
25
27
  fi
26
28
 
27
- # Extract file_path from tool args (JSON format)
28
- FILE_PATH=$(echo "$TOOL_ARGS" | jq -r '.file_path // empty')
29
+ # Extract file_path from tool_input (correct structure for Claude Code hooks)
30
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .file_path // empty' 2>/dev/null || echo "")
29
31
 
30
32
  if [ -z "$FILE_PATH" ]; then
31
33
  exit 0
@@ -208,4 +208,5 @@ if [[ $BLOCKED -eq 1 ]]; then
208
208
  fi
209
209
 
210
210
  # All other bash commands are allowed
211
+ echo '{"decision":"allow"}'
211
212
  exit 0
@@ -18,7 +18,12 @@ INPUT=$(cat)
18
18
  # Pattern: file_path contains metadata.json AND (new_string OR content) contains "status"..."completed"
19
19
 
20
20
  # Extract file_path
21
- FILE_PATH=$(echo "$INPUT" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)".*/\1/')
21
+ # Claude Code passes tool input in .tool_input.file_path format
22
+ if command -v jq &> /dev/null; then
23
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .file_path // empty' 2>/dev/null)
24
+ else
25
+ FILE_PATH=$(echo "$INPUT" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)".*/\1/')
26
+ fi
22
27
 
23
28
  # Only care about metadata.json files
24
29
  if [[ "$FILE_PATH" != *metadata.json ]]; then
@@ -26,9 +31,14 @@ if [[ "$FILE_PATH" != *metadata.json ]]; then
26
31
  fi
27
32
 
28
33
  # Extract the content being written (new_string for Edit, content for Write)
29
- NEW_CONTENT=$(echo "$INPUT" | grep -o '"new_string"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1)
30
- if [[ -z "$NEW_CONTENT" ]]; then
31
- NEW_CONTENT=$(echo "$INPUT" | grep -o '"content"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1)
34
+ # Claude Code passes tool input in .tool_input format
35
+ if command -v jq &> /dev/null; then
36
+ NEW_CONTENT=$(echo "$INPUT" | jq -r '.tool_input.new_string // .tool_input.content // .new_string // .content // empty' 2>/dev/null)
37
+ else
38
+ NEW_CONTENT=$(echo "$INPUT" | grep -o '"new_string"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1)
39
+ if [[ -z "$NEW_CONTENT" ]]; then
40
+ NEW_CONTENT=$(echo "$INPUT" | grep -o '"content"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1)
41
+ fi
32
42
  fi
33
43
 
34
44
  # Check if trying to set status to "completed" directly
@@ -20,8 +20,9 @@ set +e
20
20
  INPUT=$(cat)
21
21
 
22
22
  # Extract the file_path being written/edited
23
+ # Claude Code passes tool input in .tool_input.file_path format
23
24
  if command -v jq &> /dev/null; then
24
- FILE_PATH=$(echo "$INPUT" | jq -r '.file_path // empty' 2>/dev/null)
25
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .file_path // empty' 2>/dev/null)
25
26
  else
26
27
  FILE_PATH=$(echo "$INPUT" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"file_path"[[:space:]]*:[[:space:]]*"\(.*\)"/\1/')
27
28
  fi
@@ -19,7 +19,12 @@ set +e
19
19
  INPUT=$(cat)
20
20
 
21
21
  # Extract file_path from the tool call
22
- FILE_PATH=$(echo "$INPUT" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)".*/\1/')
22
+ # Claude Code passes tool input in .tool_input.file_path format
23
+ if command -v jq &> /dev/null; then
24
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .file_path // empty' 2>/dev/null)
25
+ else
26
+ FILE_PATH=$(echo "$INPUT" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)".*/\1/')
27
+ fi
23
28
 
24
29
  # Only care about .specweave/increments/ paths
25
30
  if [[ "$FILE_PATH" != *.specweave/increments/* ]]; then
@@ -21,9 +21,11 @@ set +e
21
21
  INPUT=$(cat)
22
22
 
23
23
  # Extract the file_path being written
24
+ # Claude Code passes tool input in .tool_input.file_path format
24
25
  if command -v jq &> /dev/null; then
25
- FILE_PATH=$(echo "$INPUT" | jq -r '.file_path // empty' 2>/dev/null)
26
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .file_path // empty' 2>/dev/null)
26
27
  else
28
+ # Fallback: try both patterns
27
29
  FILE_PATH=$(echo "$INPUT" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"file_path"[[:space:]]*:[[:space:]]*"\(.*\)"/\1/')
28
30
  fi
29
31
 
@@ -5,6 +5,18 @@
5
5
 
6
6
  ---
7
7
 
8
+ > ⛔ **NON-CLAUDE TOOLS (Cursor, Copilot, etc.): READ THIS FIRST!**
9
+ >
10
+ > Before creating ANY increment, you MUST run: `specweave context projects`
11
+ >
12
+ > Then add `**Project**: <value>` to EVERY User Story in spec.md.
13
+ > For 2-level structures, also add `**Board**: <value>`.
14
+ >
15
+ > **Missing this = increment creation will FAIL!**
16
+ > See [#user-story-format](#user-story-format) for details.
17
+
18
+ ---
19
+
8
20
  ## Section Index (Use Ctrl+F to Navigate)
9
21
 
10
22
  | Section | Search For | Purpose |