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 +49 -0
- package/package.json +1 -1
- package/plugins/specweave/hooks/project-folder-guard.sh +7 -5
- package/plugins/specweave/hooks/v2/guards/bash-file-guard.sh +1 -0
- package/plugins/specweave/hooks/v2/guards/completion-guard.sh +14 -4
- package/plugins/specweave/hooks/v2/guards/features-folder-guard.sh +2 -1
- package/plugins/specweave/hooks/v2/guards/increment-duplicate-guard.sh +6 -1
- package/plugins/specweave/hooks/v2/guards/increment-root-guard.sh +3 -1
- package/src/templates/AGENTS.md.template +12 -0
- /package/plugins/specweave/hooks/{hooks.json → hooks.json.bak} +0 -0
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.
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
|
28
|
-
FILE_PATH=$(echo "$
|
|
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
|
|
@@ -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
|
-
|
|
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
|
-
|
|
30
|
-
if
|
|
31
|
-
NEW_CONTENT=$(echo "$INPUT" |
|
|
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
|
-
|
|
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 |
|
|
File without changes
|