specweave 1.0.30 → 1.0.32
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 +140 -1235
- package/bin/specweave.js +23 -0
- package/dist/plugins/specweave-github/lib/github-client-v2.d.ts +3 -0
- package/dist/plugins/specweave-github/lib/github-client-v2.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-client-v2.js +39 -0
- package/dist/plugins/specweave-github/lib/github-client-v2.js.map +1 -1
- package/dist/src/cli/commands/set-sync-target.d.ts +41 -0
- package/dist/src/cli/commands/set-sync-target.d.ts.map +1 -0
- package/dist/src/cli/commands/set-sync-target.js +126 -0
- package/dist/src/cli/commands/set-sync-target.js.map +1 -0
- package/dist/src/cli/helpers/issue-tracker/github-multi-repo.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/github-multi-repo.js +5 -8
- package/dist/src/cli/helpers/issue-tracker/github-multi-repo.js.map +1 -1
- package/dist/src/core/hooks/HookScanner.d.ts +32 -0
- package/dist/src/core/hooks/HookScanner.d.ts.map +1 -1
- package/dist/src/core/hooks/HookScanner.js +125 -1
- package/dist/src/core/hooks/HookScanner.js.map +1 -1
- package/dist/src/core/hooks/types.d.ts +10 -1
- package/dist/src/core/hooks/types.d.ts.map +1 -1
- package/dist/src/core/increment/metadata-manager.d.ts +67 -1
- package/dist/src/core/increment/metadata-manager.d.ts.map +1 -1
- package/dist/src/core/increment/metadata-manager.js +93 -0
- package/dist/src/core/increment/metadata-manager.js.map +1 -1
- package/dist/src/core/project/index.d.ts +21 -0
- package/dist/src/core/project/index.d.ts.map +1 -0
- package/dist/src/core/project/index.js +22 -0
- package/dist/src/core/project/index.js.map +1 -0
- package/dist/src/core/project/project-service.d.ts +122 -0
- package/dist/src/core/project/project-service.d.ts.map +1 -0
- package/dist/src/core/project/project-service.js +334 -0
- package/dist/src/core/project/project-service.js.map +1 -0
- package/dist/src/core/sync/external-tool-resolver.d.ts +171 -0
- package/dist/src/core/sync/external-tool-resolver.d.ts.map +1 -0
- package/dist/src/core/sync/external-tool-resolver.js +569 -0
- package/dist/src/core/sync/external-tool-resolver.js.map +1 -0
- package/dist/src/core/types/increment-metadata.d.ts +92 -0
- package/dist/src/core/types/increment-metadata.d.ts.map +1 -1
- package/dist/src/hooks/processor.d.ts +7 -3
- package/dist/src/hooks/processor.d.ts.map +1 -1
- package/dist/src/hooks/processor.js +11 -5
- package/dist/src/hooks/processor.js.map +1 -1
- package/package.json +1 -1
- package/plugins/specweave/hooks/hooks.json +0 -69
- package/plugins/specweave/hooks/v2/handlers/project-bridge-handler.sh +96 -0
- package/plugins/specweave/hooks/v2/queue/processor.sh +13 -5
- package/plugins/specweave/lib/hooks/project-bridge.js +76 -0
- package/plugins/specweave/lib/hooks/update-tasks-md.js +0 -0
- package/plugins/specweave/lib/hooks/us-completion-orchestrator.js +0 -0
- package/plugins/specweave/lib/vendor/core/increment/metadata-manager.d.ts +67 -1
- package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js +93 -0
- package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js.map +1 -1
- package/plugins/specweave/lib/vendor/core/types/increment-metadata.d.ts +92 -0
- package/plugins/specweave-github/lib/github-client-v2.js +39 -0
- package/plugins/specweave-github/lib/github-client-v2.ts +44 -0
- package/plugins/specweave/hooks/docs-changed.sh +0 -87
- package/plugins/specweave/hooks/human-input-required.sh +0 -83
- package/plugins/specweave/hooks/post-edit-write-consolidated.sh +0 -428
- package/plugins/specweave/hooks/post-first-increment.sh +0 -61
- package/plugins/specweave/hooks/post-increment-change.sh +0 -103
- package/plugins/specweave/hooks/post-increment-completion.sh +0 -513
- package/plugins/specweave/hooks/post-increment-planning.sh +0 -1204
- package/plugins/specweave/hooks/post-increment-status-change.sh +0 -243
- package/plugins/specweave/hooks/post-metadata-change.sh +0 -246
- package/plugins/specweave/hooks/post-spec-update.sh +0 -158
- package/plugins/specweave/hooks/post-task-completion.sh +0 -557
- package/plugins/specweave/hooks/post-task-edit.sh +0 -47
- package/plugins/specweave/hooks/post-user-story-complete.sh +0 -230
- package/plugins/specweave/hooks/pre-command-deduplication.sh +0 -68
- package/plugins/specweave/hooks/pre-edit-write-consolidated.sh +0 -225
- package/plugins/specweave/hooks/pre-implementation.sh +0 -75
- package/plugins/specweave/hooks/pre-increment-start.sh +0 -173
- package/plugins/specweave/hooks/pre-task-completion-edit.sh +0 -355
- package/plugins/specweave/hooks/pre-task-completion.sh +0 -269
- package/plugins/specweave/hooks/pre-tool-use.sh +0 -137
- package/plugins/specweave/hooks/session-start-reconcile.sh +0 -139
- package/plugins/specweave/hooks/shared/bulk-operation-detector.sh +0 -167
- package/plugins/specweave/hooks/test-pretooluse-env.sh +0 -72
- package/plugins/specweave/hooks/validate-increment-completion.sh +0 -113
- package/plugins/specweave/lib/hooks/consolidated-sync.js +0 -288
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
###############################################################################
|
|
4
|
-
# SpecWeave Post-User-Story-Complete Hook
|
|
5
|
-
#
|
|
6
|
-
# CRITICAL ARCHITECTURE:
|
|
7
|
-
# - Fires when user story marked complete in spec.md (AC checkbox checked)
|
|
8
|
-
# - Updates external PM tool (GitHub Issue/Jira Story/ADO User Story)
|
|
9
|
-
# - Moves GitHub card to "Done" / Closes Jira story / Completes ADO story
|
|
10
|
-
#
|
|
11
|
-
# Trigger Points:
|
|
12
|
-
# 1. After user manually checks AC checkbox in spec.md
|
|
13
|
-
# 2. After bidirectional sync updates AC status
|
|
14
|
-
# 3. After increment completion syncs to spec
|
|
15
|
-
#
|
|
16
|
-
# What It Does:
|
|
17
|
-
# - Detects which user story was completed (all AC checkboxes checked)
|
|
18
|
-
# - Finds corresponding external item (GitHub Issue/Jira Story/ADO User Story)
|
|
19
|
-
# - Updates item status to "Done" / "Closed"
|
|
20
|
-
# - Adds completion comment with timestamp
|
|
21
|
-
#
|
|
22
|
-
# Usage:
|
|
23
|
-
# post-user-story-complete.sh <spec-id> <user-story-id>
|
|
24
|
-
#
|
|
25
|
-
# Example:
|
|
26
|
-
# post-user-story-complete.sh spec-001 US-001
|
|
27
|
-
#
|
|
28
|
-
###############################################################################
|
|
29
|
-
|
|
30
|
-
set +e # EMERGENCY FIX: Changed from set -euo pipefail to prevent Claude Code crashes
|
|
31
|
-
|
|
32
|
-
# Arguments
|
|
33
|
-
SPEC_ID="${1:-}"
|
|
34
|
-
USER_STORY_ID="${2:-}"
|
|
35
|
-
|
|
36
|
-
# Validate arguments
|
|
37
|
-
if [[ -z "$SPEC_ID" || -z "$USER_STORY_ID" ]]; then
|
|
38
|
-
echo "❌ Error: Spec ID and User Story ID required"
|
|
39
|
-
echo "Usage: post-user-story-complete.sh <spec-id> <user-story-id>"
|
|
40
|
-
exit 1
|
|
41
|
-
fi
|
|
42
|
-
|
|
43
|
-
echo ""
|
|
44
|
-
echo "🎉 Post-User-Story-Complete Hook"
|
|
45
|
-
echo " Spec: $SPEC_ID"
|
|
46
|
-
echo " User Story: $USER_STORY_ID"
|
|
47
|
-
|
|
48
|
-
# Find user story file in living docs
|
|
49
|
-
# Input: SPEC_ID = increment ID (e.g., "0059-context-optimization-crash-prevention")
|
|
50
|
-
# USER_STORY_ID = US ID (e.g., "US-001")
|
|
51
|
-
# Search in: .specweave/docs/internal/specs/**/us-*.md
|
|
52
|
-
|
|
53
|
-
# STEP 1: Get feature ID from increment spec.md
|
|
54
|
-
INCREMENT_SPEC=".specweave/increments/$SPEC_ID/spec.md"
|
|
55
|
-
FEATURE_ID=""
|
|
56
|
-
|
|
57
|
-
if [[ -f "$INCREMENT_SPEC" ]]; then
|
|
58
|
-
FEATURE_ID=$(head -20 "$INCREMENT_SPEC" | grep "^feature_id:" | head -1 | sed 's/feature_id: *//; s/ *$//' || echo "")
|
|
59
|
-
fi
|
|
60
|
-
|
|
61
|
-
echo " 🔍 Looking for $USER_STORY_ID in feature $FEATURE_ID"
|
|
62
|
-
|
|
63
|
-
# STEP 2: Find user story file by ID AND feature
|
|
64
|
-
# Prioritize files in the correct feature folder
|
|
65
|
-
US_FILE=""
|
|
66
|
-
while IFS= read -r file; do
|
|
67
|
-
# Check if file has matching user story ID in frontmatter
|
|
68
|
-
if head -20 "$file" 2>/dev/null | grep -q "^id: $USER_STORY_ID"; then
|
|
69
|
-
# If feature ID is known, verify the file is in the correct feature folder
|
|
70
|
-
if [[ -n "$FEATURE_ID" ]]; then
|
|
71
|
-
FILE_FEATURE=$(head -20 "$file" 2>/dev/null | grep "^feature:" | head -1 | sed 's/feature: *//; s/ *$//' || echo "")
|
|
72
|
-
if [[ "$FILE_FEATURE" == "$FEATURE_ID" ]]; then
|
|
73
|
-
US_FILE="$file"
|
|
74
|
-
break
|
|
75
|
-
fi
|
|
76
|
-
else
|
|
77
|
-
# No feature filter - take first match
|
|
78
|
-
US_FILE="$file"
|
|
79
|
-
break
|
|
80
|
-
fi
|
|
81
|
-
fi
|
|
82
|
-
done < <(find .specweave/docs/internal/specs -name "us-*.md" -type f 2>/dev/null)
|
|
83
|
-
|
|
84
|
-
if [[ -z "$US_FILE" ]]; then
|
|
85
|
-
echo "❌ Error: User story file not found for $USER_STORY_ID (feature: $FEATURE_ID) in increment $SPEC_ID"
|
|
86
|
-
exit 1
|
|
87
|
-
fi
|
|
88
|
-
|
|
89
|
-
echo " 📄 Found user story file: $US_FILE"
|
|
90
|
-
SPEC_FILE="$US_FILE"
|
|
91
|
-
|
|
92
|
-
# Load config to check if auto-sync is enabled
|
|
93
|
-
CONFIG_FILE=".specweave/config.json"
|
|
94
|
-
if [[ ! -f "$CONFIG_FILE" ]]; then
|
|
95
|
-
echo " ℹ️ No config file found, skipping auto-sync"
|
|
96
|
-
exit 0
|
|
97
|
-
fi
|
|
98
|
-
|
|
99
|
-
# Check if auto-sync is enabled
|
|
100
|
-
AUTO_SYNC=$(jq -r '.hooks.post_user_story_complete.auto_sync // true' "$CONFIG_FILE")
|
|
101
|
-
|
|
102
|
-
if [[ "$AUTO_SYNC" != "true" ]]; then
|
|
103
|
-
echo " ℹ️ Auto-sync disabled in config, skipping"
|
|
104
|
-
exit 0
|
|
105
|
-
fi
|
|
106
|
-
|
|
107
|
-
# Parse user story frontmatter to detect external links
|
|
108
|
-
# User story files have format:
|
|
109
|
-
# external:
|
|
110
|
-
# github:
|
|
111
|
-
# issue: 745
|
|
112
|
-
# url: https://github.com/...
|
|
113
|
-
|
|
114
|
-
# Extract GitHub issue number from frontmatter
|
|
115
|
-
GITHUB_ISSUE=$(head -20 "$SPEC_FILE" | grep -A 5 "github:" | grep "issue:" | head -1 | sed 's/.*issue: *//; s/ *$//' || echo "")
|
|
116
|
-
|
|
117
|
-
# Extract Jira issue from frontmatter
|
|
118
|
-
JIRA_ISSUE=$(head -20 "$SPEC_FILE" | grep -A 5 "jira:" | grep "issue:" | head -1 | sed 's/.*issue: *//; s/ *$//' || echo "")
|
|
119
|
-
|
|
120
|
-
# Extract ADO work item from frontmatter
|
|
121
|
-
ADO_ITEM=$(head -20 "$SPEC_FILE" | grep -A 5 "ado:" | grep "item:" | head -1 | sed 's/.*item: *//; s/ *$//' || echo "")
|
|
122
|
-
|
|
123
|
-
# Determine which provider to sync
|
|
124
|
-
PROVIDER=""
|
|
125
|
-
EXTERNAL_ID=""
|
|
126
|
-
if [[ -n "$GITHUB_ISSUE" ]]; then
|
|
127
|
-
PROVIDER="github"
|
|
128
|
-
EXTERNAL_ID="$GITHUB_ISSUE"
|
|
129
|
-
elif [[ -n "$JIRA_ISSUE" ]]; then
|
|
130
|
-
PROVIDER="jira"
|
|
131
|
-
EXTERNAL_ID="$JIRA_ISSUE"
|
|
132
|
-
elif [[ -n "$ADO_ITEM" ]]; then
|
|
133
|
-
PROVIDER="ado"
|
|
134
|
-
EXTERNAL_ID="$ADO_ITEM"
|
|
135
|
-
fi
|
|
136
|
-
|
|
137
|
-
# No external link found - skip sync
|
|
138
|
-
if [[ -z "$PROVIDER" ]]; then
|
|
139
|
-
echo " ℹ️ User story not linked to external tool, skipping sync"
|
|
140
|
-
exit 0
|
|
141
|
-
fi
|
|
142
|
-
|
|
143
|
-
echo " 🔗 Detected external link: $PROVIDER (ID: $EXTERNAL_ID)"
|
|
144
|
-
|
|
145
|
-
# Update external tool based on provider
|
|
146
|
-
case "$PROVIDER" in
|
|
147
|
-
github)
|
|
148
|
-
echo " 🔄 Updating GitHub Issue #$EXTERNAL_ID for $USER_STORY_ID..."
|
|
149
|
-
|
|
150
|
-
# Check if GitHub CLI is available
|
|
151
|
-
if ! command -v gh &> /dev/null; then
|
|
152
|
-
echo " ⚠️ GitHub CLI (gh) not found, skipping sync"
|
|
153
|
-
exit 0
|
|
154
|
-
fi
|
|
155
|
-
|
|
156
|
-
# Use issue number from frontmatter (EXTERNAL_ID = issue number)
|
|
157
|
-
ISSUE_NUMBER="$EXTERNAL_ID"
|
|
158
|
-
REPO=$(git remote get-url origin | sed -E 's/.*github\.com[:/]([^/]+\/[^/]+)(\.git)?$/\1/')
|
|
159
|
-
|
|
160
|
-
if [[ -z "$ISSUE_NUMBER" || "$ISSUE_NUMBER" == "null" ]]; then
|
|
161
|
-
echo " ⚠️ No GitHub Issue number found in user story metadata"
|
|
162
|
-
exit 0
|
|
163
|
-
fi
|
|
164
|
-
|
|
165
|
-
echo " 📝 Using GitHub Issue #$ISSUE_NUMBER from metadata"
|
|
166
|
-
|
|
167
|
-
# Check if issue is already closed
|
|
168
|
-
ISSUE_STATE=$(gh issue view "$ISSUE_NUMBER" --repo "$REPO" --json state --jq '.state' 2>/dev/null || echo "")
|
|
169
|
-
if [[ "$ISSUE_STATE" == "CLOSED" ]]; then
|
|
170
|
-
echo " ✅ GitHub Issue #$ISSUE_NUMBER already closed"
|
|
171
|
-
exit 0
|
|
172
|
-
fi
|
|
173
|
-
|
|
174
|
-
# Close issue with completion comment
|
|
175
|
-
gh issue close "$ISSUE_NUMBER" --repo "$REPO" --comment "✅ **User Story Verified Complete**
|
|
176
|
-
|
|
177
|
-
📊 **Completion Status**:
|
|
178
|
-
- ✅ All Acceptance Criteria satisfied
|
|
179
|
-
- ✅ All implementation tasks complete
|
|
180
|
-
|
|
181
|
-
**User Story**: $USER_STORY_ID
|
|
182
|
-
**Increment**: $SPEC_ID
|
|
183
|
-
|
|
184
|
-
🤖 Auto-closed by SpecWeave US Completion Hook
|
|
185
|
-
Completed at: $(date -u +%Y-%m-%dT%H:%M:%SZ)" 2>/dev/null || {
|
|
186
|
-
echo " ⚠️ Failed to close issue (may already be closed)"
|
|
187
|
-
exit 0
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
echo " ✅ GitHub Issue #$ISSUE_NUMBER closed"
|
|
191
|
-
;;
|
|
192
|
-
|
|
193
|
-
jira)
|
|
194
|
-
echo " 🔄 Updating Jira Story for $USER_STORY_ID..."
|
|
195
|
-
|
|
196
|
-
# Check if Jira config exists
|
|
197
|
-
if [[ -z "${JIRA_DOMAIN:-}" ]]; then
|
|
198
|
-
echo " ⚠️ Jira not configured (.env), skipping sync"
|
|
199
|
-
exit 0
|
|
200
|
-
fi
|
|
201
|
-
|
|
202
|
-
# TODO: Find Jira Story by title pattern
|
|
203
|
-
# TODO: Transition story to "Done" status
|
|
204
|
-
echo " ✅ Jira story transition queued (implementation pending)"
|
|
205
|
-
;;
|
|
206
|
-
|
|
207
|
-
ado)
|
|
208
|
-
echo " 🔄 Updating ADO User Story for $USER_STORY_ID..."
|
|
209
|
-
|
|
210
|
-
# Check if ADO config exists
|
|
211
|
-
if [[ -z "${ADO_ORGANIZATION:-}" ]]; then
|
|
212
|
-
echo " ⚠️ ADO not configured (.env), skipping sync"
|
|
213
|
-
exit 0
|
|
214
|
-
fi
|
|
215
|
-
|
|
216
|
-
# TODO: Find ADO User Story by title pattern
|
|
217
|
-
# TODO: Update state to "Closed"
|
|
218
|
-
echo " ✅ ADO user story update queued (implementation pending)"
|
|
219
|
-
;;
|
|
220
|
-
|
|
221
|
-
*)
|
|
222
|
-
echo " ⚠️ Unknown provider: $PROVIDER"
|
|
223
|
-
exit 0
|
|
224
|
-
;;
|
|
225
|
-
esac
|
|
226
|
-
|
|
227
|
-
echo " ✅ Post-user-story-complete hook complete"
|
|
228
|
-
echo ""
|
|
229
|
-
|
|
230
|
-
exit 0
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
# SpecWeave Pre-Command Deduplication Hook (v0.26.14 - OPTIMIZED)
|
|
4
|
-
# Fires BEFORE any command executes (UserPromptSubmit hook)
|
|
5
|
-
# Purpose: Prevent duplicate command invocations within configurable time window
|
|
6
|
-
#
|
|
7
|
-
# OPTIMIZATIONS (v0.26.14):
|
|
8
|
-
# 1. Early exit for non-SpecWeave projects (<1ms)
|
|
9
|
-
# 2. Skip node spawn if deduplicator not available
|
|
10
|
-
# 3. Pure bash stdin reading
|
|
11
|
-
|
|
12
|
-
set +e
|
|
13
|
-
|
|
14
|
-
# ==============================================================================
|
|
15
|
-
# ULTRA-FAST EARLY EXIT
|
|
16
|
-
# ==============================================================================
|
|
17
|
-
|
|
18
|
-
# Quick check: If no .specweave in cwd or nearby, just approve
|
|
19
|
-
if [[ ! -d ".specweave" ]] && [[ ! -d "../.specweave" ]] && [[ ! -d "../../.specweave" ]]; then
|
|
20
|
-
cat /dev/stdin > /dev/null # Drain stdin
|
|
21
|
-
echo '{"decision":"approve"}'
|
|
22
|
-
exit 0
|
|
23
|
-
fi
|
|
24
|
-
|
|
25
|
-
# Read input JSON from stdin
|
|
26
|
-
INPUT=$(cat)
|
|
27
|
-
|
|
28
|
-
# ==============================================================================
|
|
29
|
-
# DEDUPLICATION CHECK: Pure bash with file-based cache
|
|
30
|
-
# ==============================================================================
|
|
31
|
-
|
|
32
|
-
# Use file-based deduplication (no node!) with 1-second TTL
|
|
33
|
-
CACHE_DIR=".specweave/state/.dedup-cache"
|
|
34
|
-
mkdir -p "$CACHE_DIR" 2>/dev/null || true
|
|
35
|
-
|
|
36
|
-
# Extract command from input (use jq if available, fallback to grep)
|
|
37
|
-
if command -v jq >/dev/null 2>&1; then
|
|
38
|
-
COMMAND=$(echo "$INPUT" | jq -r '.prompt // ""' 2>/dev/null | head -c 100 | tr -d '\n' | tr '/' '_' | tr ' ' '_')
|
|
39
|
-
else
|
|
40
|
-
COMMAND=$(echo "$INPUT" | grep -oP '"prompt"\s*:\s*"\K[^"]{0,100}' 2>/dev/null | tr '/' '_' | tr ' ' '_')
|
|
41
|
-
fi
|
|
42
|
-
|
|
43
|
-
# Only check for SpecWeave commands
|
|
44
|
-
if [[ "$COMMAND" == *specweave* ]]; then
|
|
45
|
-
CACHE_FILE="$CACHE_DIR/${COMMAND:0:50}.lock"
|
|
46
|
-
|
|
47
|
-
# Check if cached and recent (within 1 second)
|
|
48
|
-
if [[ -f "$CACHE_FILE" ]]; then
|
|
49
|
-
CACHE_AGE=$(($(date +%s) - $(stat -f %m "$CACHE_FILE" 2>/dev/null || stat -c %Y "$CACHE_FILE" 2>/dev/null || echo 0)))
|
|
50
|
-
if [[ "$CACHE_AGE" -lt 1 ]]; then
|
|
51
|
-
echo '{"decision":"block","reason":"Duplicate command detected (within 1 second). Wait a moment and try again."}'
|
|
52
|
-
exit 0
|
|
53
|
-
fi
|
|
54
|
-
fi
|
|
55
|
-
|
|
56
|
-
# Update cache timestamp
|
|
57
|
-
touch "$CACHE_FILE" 2>/dev/null || true
|
|
58
|
-
|
|
59
|
-
# Cleanup old cache files (>10 seconds old)
|
|
60
|
-
find "$CACHE_DIR" -type f -mmin +1 -delete 2>/dev/null &
|
|
61
|
-
fi
|
|
62
|
-
|
|
63
|
-
# ==============================================================================
|
|
64
|
-
# PASS THROUGH: No duplicate detected
|
|
65
|
-
# ==============================================================================
|
|
66
|
-
|
|
67
|
-
echo '{"decision":"approve"}'
|
|
68
|
-
exit 0
|
|
@@ -1,225 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
#
|
|
3
|
-
# Pre-Edit/Write Consolidated Hook: Capture File Path BEFORE Edit/Write Executes
|
|
4
|
-
#
|
|
5
|
-
# Purpose: Unified hook for both Edit and Write tools
|
|
6
|
-
# Strategy: Hierarchical early exit for maximum performance
|
|
7
|
-
#
|
|
8
|
-
# CONSOLIDATION (v0.25.0):
|
|
9
|
-
# - Replaces pre-edit-spec.sh and pre-write-spec.sh (identical code)
|
|
10
|
-
# - Reduces hook overhead by 50% (2 pre-hooks → 1)
|
|
11
|
-
# - Single point of maintenance
|
|
12
|
-
#
|
|
13
|
-
# HIERARCHICAL EARLY EXIT (v0.26.1):
|
|
14
|
-
# - Tier 0: TOOL_USE_ARGS check (< 1ms) - Exit if empty (Claude Code bug)
|
|
15
|
-
# - Tier 1: .specweave/ path check (< 5ms) - Exit if not SpecWeave file
|
|
16
|
-
# - Tier 2: Active increment check (< 10ms) - Exit if archived/completed
|
|
17
|
-
# - Tier 3: Full processing - AC sync, status line, living docs
|
|
18
|
-
#
|
|
19
|
-
# GRACEFUL DEGRADATION:
|
|
20
|
-
# - PreToolUse works if TOOL_USE_ARGS is available (fast filtering)
|
|
21
|
-
# - PostToolUse fallback uses mtime if PreToolUse disabled (slower but works)
|
|
22
|
-
# - Self-tuning: Disables PreToolUse if consistently useless
|
|
23
|
-
#
|
|
24
|
-
# Architecture:
|
|
25
|
-
# PreToolUse:Edit/Write → pre-edit-write-consolidated.sh (this file)
|
|
26
|
-
# ↓ writes to
|
|
27
|
-
# .specweave/state/.pending-status-update
|
|
28
|
-
# ↓ read by
|
|
29
|
-
# PostToolUse:Edit/Write → post-edit-write-consolidated.sh
|
|
30
|
-
#
|
|
31
|
-
# Version: v0.26.1 (HIERARCHICAL EARLY EXIT)
|
|
32
|
-
# Date: 2025-11-24
|
|
33
|
-
#
|
|
34
|
-
# EMERGENCY FIXES (v0.24.3):
|
|
35
|
-
# - Kill switch: Set SPECWEAVE_DISABLE_HOOKS=1 to disable ALL hooks
|
|
36
|
-
# - Circuit breaker: Auto-disable after 3 consecutive failures
|
|
37
|
-
# - File locking: Prevent concurrent executions
|
|
38
|
-
# - Complete error isolation: Never let errors reach Claude Code
|
|
39
|
-
|
|
40
|
-
# EMERGENCY FIX: Remove set -e - it causes Claude Code crashes!
|
|
41
|
-
set +e
|
|
42
|
-
|
|
43
|
-
# ============================================================================
|
|
44
|
-
# TIER 0: ULTRA-FAST REJECTION (< 1ms) - v0.26.1 CRITICAL FIX
|
|
45
|
-
# ============================================================================
|
|
46
|
-
# Problem: Claude Code doesn't pass TOOL_USE_ARGS to PreToolUse hooks (bug)
|
|
47
|
-
# Result: PreToolUse hooks are "blind" - can't filter files, fire for everything
|
|
48
|
-
# Impact: Massive overhead (1 pre-hook per Edit/Write on ANY file)
|
|
49
|
-
#
|
|
50
|
-
# Solution: Exit immediately if TOOL_USE_ARGS is empty
|
|
51
|
-
# - No find_project_root, no file path extraction, no processing
|
|
52
|
-
# - Eliminates 33% of hook overhead (1 of 3 hooks per operation)
|
|
53
|
-
# - Falls back to PostToolUse mtime detection (slower but works)
|
|
54
|
-
#
|
|
55
|
-
# See: ADR-0128 (Hierarchical Hook Early Exit Strategy)
|
|
56
|
-
|
|
57
|
-
if [[ -z "${TOOL_USE_ARGS:-}" ]]; then
|
|
58
|
-
# Telemetry: Track PreToolUse disabled events (lightweight counter)
|
|
59
|
-
# This helps us understand if Claude Code ever fixes the TOOL_USE_ARGS bug
|
|
60
|
-
TELEMETRY_DIR="${HOME}/.claude/.specweave-telemetry"
|
|
61
|
-
mkdir -p "$TELEMETRY_DIR" 2>/dev/null || true
|
|
62
|
-
echo "$(date -u +%s)" >> "$TELEMETRY_DIR/pretooluse-disabled.log" 2>/dev/null || true
|
|
63
|
-
|
|
64
|
-
# Silent exit - PreToolUse is useless without TOOL_USE_ARGS
|
|
65
|
-
# PostToolUse will handle via mtime fallback
|
|
66
|
-
exit 0
|
|
67
|
-
fi
|
|
68
|
-
|
|
69
|
-
# Telemetry: Track PreToolUse enabled events (TOOL_USE_ARGS available)
|
|
70
|
-
TELEMETRY_DIR="${HOME}/.claude/.specweave-telemetry"
|
|
71
|
-
mkdir -p "$TELEMETRY_DIR" 2>/dev/null || true
|
|
72
|
-
echo "$(date -u +%s)" >> "$TELEMETRY_DIR/pretooluse-enabled.log" 2>/dev/null || true
|
|
73
|
-
|
|
74
|
-
# Find project root (must be BEFORE recursion guard to get PROJECT_ROOT)
|
|
75
|
-
find_project_root() {
|
|
76
|
-
local dir="$PWD"
|
|
77
|
-
while [[ "$dir" != "/" ]]; do
|
|
78
|
-
if [[ -d "$dir/.specweave" ]]; then
|
|
79
|
-
echo "$dir"
|
|
80
|
-
return 0
|
|
81
|
-
fi
|
|
82
|
-
dir=$(dirname "$dir")
|
|
83
|
-
done
|
|
84
|
-
echo "$PWD"
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
PROJECT_ROOT=$(find_project_root)
|
|
88
|
-
STATE_DIR="$PROJECT_ROOT/.specweave/state"
|
|
89
|
-
LOGS_DIR="$PROJECT_ROOT/.specweave/logs"
|
|
90
|
-
DEBUG_LOG="$LOGS_DIR/hooks-debug.log"
|
|
91
|
-
PENDING_FILE="$STATE_DIR/.pending-status-update"
|
|
92
|
-
METRICS_FILE="$STATE_DIR/hook-metrics.jsonl"
|
|
93
|
-
|
|
94
|
-
# ============================================================================
|
|
95
|
-
# RECURSION PREVENTION (CRITICAL - v0.26.0 - FILE-BASED GUARD)
|
|
96
|
-
# ============================================================================
|
|
97
|
-
# NEW SOLUTION (v0.26.0): File-based recursion guard
|
|
98
|
-
# - Guard file exists = already inside hook chain
|
|
99
|
-
# - Works across ALL processes (not just current shell)
|
|
100
|
-
#
|
|
101
|
-
# See: ADR-0073 (Hook Recursion Prevention Strategy)
|
|
102
|
-
|
|
103
|
-
RECURSION_GUARD_FILE="$PROJECT_ROOT/.specweave/state/.hook-recursion-guard"
|
|
104
|
-
|
|
105
|
-
if [[ -f "$RECURSION_GUARD_FILE" ]]; then
|
|
106
|
-
# Silent exit - we're already inside a hook chain
|
|
107
|
-
exit 0
|
|
108
|
-
fi
|
|
109
|
-
|
|
110
|
-
# EMERGENCY KILL SWITCH: Disable all hooks if env variable set
|
|
111
|
-
if [[ "${SPECWEAVE_DISABLE_HOOKS:-0}" == "1" ]]; then
|
|
112
|
-
exit 0
|
|
113
|
-
fi
|
|
114
|
-
|
|
115
|
-
# Ensure directories exist
|
|
116
|
-
mkdir -p "$STATE_DIR" "$LOGS_DIR" 2>/dev/null || true
|
|
117
|
-
|
|
118
|
-
# ============================================================================
|
|
119
|
-
# TIER 2: Extract File Path from Tool Arguments
|
|
120
|
-
# ============================================================================
|
|
121
|
-
# PreToolUse should have access to tool arguments BEFORE execution
|
|
122
|
-
# Try multiple methods to extract file_path
|
|
123
|
-
|
|
124
|
-
FILE_PATH=""
|
|
125
|
-
|
|
126
|
-
# Method 1: TOOL_USE_ARGS environment variable (primary for PreToolUse)
|
|
127
|
-
if [[ -n "${TOOL_USE_ARGS:-}" ]]; then
|
|
128
|
-
# Try to parse JSON with jq if available
|
|
129
|
-
if command -v jq &> /dev/null; then
|
|
130
|
-
FILE_PATH=$(echo "$TOOL_USE_ARGS" | jq -r '.file_path // empty' 2>/dev/null || echo "")
|
|
131
|
-
fi
|
|
132
|
-
|
|
133
|
-
# Fallback: Regex extraction if jq not available or failed
|
|
134
|
-
if [[ -z "$FILE_PATH" ]]; then
|
|
135
|
-
FILE_PATH=$(echo "$TOOL_USE_ARGS" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)".*/\1/' || echo "")
|
|
136
|
-
fi
|
|
137
|
-
fi
|
|
138
|
-
|
|
139
|
-
# Method 2: TOOL_USE_CONTENT (fallback)
|
|
140
|
-
if [[ -z "$FILE_PATH" ]] && [[ -n "${TOOL_USE_CONTENT:-}" ]]; then
|
|
141
|
-
FILE_PATH="$TOOL_USE_CONTENT"
|
|
142
|
-
fi
|
|
143
|
-
|
|
144
|
-
# Method 3: Parse from stdin (last resort - experimental)
|
|
145
|
-
if [[ -z "$FILE_PATH" ]] && [[ ! -t 0 ]]; then
|
|
146
|
-
# Read stdin and try to extract file_path
|
|
147
|
-
STDIN_DATA=$(cat 2>/dev/null || echo "")
|
|
148
|
-
if [[ -n "$STDIN_DATA" ]] && command -v jq &> /dev/null; then
|
|
149
|
-
FILE_PATH=$(echo "$STDIN_DATA" | jq -r '.file_path // empty' 2>/dev/null || echo "")
|
|
150
|
-
fi
|
|
151
|
-
fi
|
|
152
|
-
|
|
153
|
-
# ============================================================================
|
|
154
|
-
# TIER 2: Signal Detection and Validation
|
|
155
|
-
# ============================================================================
|
|
156
|
-
|
|
157
|
-
# Log what we detected (for debugging PreToolUse effectiveness)
|
|
158
|
-
if [[ -n "$FILE_PATH" ]]; then
|
|
159
|
-
echo "[$(date)] pre-edit-write: Detected file_path: $FILE_PATH" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
160
|
-
else
|
|
161
|
-
echo "[$(date)] pre-edit-write: No file_path detected (will fall back to Tier 1)" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
162
|
-
exit 0 # Let PostToolUse handle it with mtime fallback
|
|
163
|
-
fi
|
|
164
|
-
|
|
165
|
-
# ============================================================================
|
|
166
|
-
# EARLY EXIT: Only process .specweave/ files (v0.24.4 Performance Fix)
|
|
167
|
-
# ============================================================================
|
|
168
|
-
# 90% of Edit/Write operations are on non-SpecWeave files (src/, tests/, node_modules/)
|
|
169
|
-
# Exit immediately for non-.specweave/ files to reduce hook overhead by 90%
|
|
170
|
-
|
|
171
|
-
# Handle both absolute and relative paths: /.specweave/ or .specweave/
|
|
172
|
-
if [[ "$FILE_PATH" != *"/.specweave/"* ]] && [[ "$FILE_PATH" != ".specweave/"* ]]; then
|
|
173
|
-
echo "[$(date)] pre-edit-write: Not a .specweave/ file, skipping (performance optimization)" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
174
|
-
exit 0
|
|
175
|
-
fi
|
|
176
|
-
|
|
177
|
-
# Check if this is a spec.md or tasks.md file in increments folder
|
|
178
|
-
IS_SPEC_FILE=false
|
|
179
|
-
if [[ "$FILE_PATH" == *"/spec.md" ]] || [[ "$FILE_PATH" == *"/tasks.md" ]]; then
|
|
180
|
-
# Handle both absolute (/.specweave/increments/) and relative (.specweave/increments/) paths
|
|
181
|
-
if [[ "$FILE_PATH" == *"/.specweave/increments/"* ]] || [[ "$FILE_PATH" == ".specweave/increments/"* ]]; then
|
|
182
|
-
# Exclude archived increments
|
|
183
|
-
if [[ "$FILE_PATH" != *"/_archive/"* ]]; then
|
|
184
|
-
IS_SPEC_FILE=true
|
|
185
|
-
fi
|
|
186
|
-
fi
|
|
187
|
-
fi
|
|
188
|
-
|
|
189
|
-
# If not a spec/tasks file, exit silently (no signal to PostToolUse)
|
|
190
|
-
if [[ "$IS_SPEC_FILE" != "true" ]]; then
|
|
191
|
-
echo "[$(date)] pre-edit-write: Not a spec/tasks file - no signal" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
192
|
-
exit 0
|
|
193
|
-
fi
|
|
194
|
-
|
|
195
|
-
# ============================================================================
|
|
196
|
-
# TIER 2: Write Signal for PostToolUse Hook
|
|
197
|
-
# ============================================================================
|
|
198
|
-
|
|
199
|
-
# Write file path to pending file for PostToolUse to consume
|
|
200
|
-
echo "$FILE_PATH" > "$PENDING_FILE" 2>/dev/null || true
|
|
201
|
-
|
|
202
|
-
echo "[$(date)] pre-edit-write: Signaled PostToolUse for: $FILE_PATH" >> "$DEBUG_LOG" 2>/dev/null || true
|
|
203
|
-
|
|
204
|
-
# ============================================================================
|
|
205
|
-
# TIER 2: Metrics Collection
|
|
206
|
-
# ============================================================================
|
|
207
|
-
|
|
208
|
-
# Record metrics (JSONL format - one JSON object per line)
|
|
209
|
-
TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
210
|
-
METRIC_ENTRY="{\"timestamp\":\"$TIMESTAMP\",\"hook\":\"pre-edit-write\",\"event\":\"file_detected\",\"file_path\":\"$FILE_PATH\",\"method\":\"TOOL_USE_ARGS\"}"
|
|
211
|
-
|
|
212
|
-
# Append to metrics file (JSONL)
|
|
213
|
-
echo "$METRIC_ENTRY" >> "$METRICS_FILE" 2>/dev/null || true
|
|
214
|
-
|
|
215
|
-
# Log rotation for metrics (keep last 1000 entries)
|
|
216
|
-
if [[ -f "$METRICS_FILE" ]]; then
|
|
217
|
-
LINE_COUNT=$(wc -l < "$METRICS_FILE" 2>/dev/null || echo 0)
|
|
218
|
-
if (( LINE_COUNT > 1000 )); then
|
|
219
|
-
tail -1000 "$METRICS_FILE" > "$METRICS_FILE.tmp" 2>/dev/null || true
|
|
220
|
-
mv "$METRICS_FILE.tmp" "$METRICS_FILE" 2>/dev/null || true
|
|
221
|
-
fi
|
|
222
|
-
fi
|
|
223
|
-
|
|
224
|
-
# ALWAYS exit 0 - NEVER let hook errors crash Claude Code
|
|
225
|
-
exit 0
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
# SpecWeave Pre-Implementation Hook
|
|
4
|
-
# Runs before starting implementation of a task
|
|
5
|
-
# Checks regression risk for brownfield projects
|
|
6
|
-
|
|
7
|
-
set +e # EMERGENCY FIX: Prevents Claude Code crashes
|
|
8
|
-
|
|
9
|
-
# EMERGENCY KILL SWITCH
|
|
10
|
-
if [[ "${SPECWEAVE_DISABLE_HOOKS:-0}" == "1" ]]; then
|
|
11
|
-
exit 0
|
|
12
|
-
fi
|
|
13
|
-
|
|
14
|
-
# Find project root by searching upward for .specweave/ directory
|
|
15
|
-
# Works regardless of where hook is installed (source or .claude/hooks/)
|
|
16
|
-
find_project_root() {
|
|
17
|
-
local dir="$1"
|
|
18
|
-
while [ "$dir" != "/" ]; do
|
|
19
|
-
if [ -d "$dir/.specweave" ]; then
|
|
20
|
-
echo "$dir"
|
|
21
|
-
return 0
|
|
22
|
-
fi
|
|
23
|
-
dir="$(dirname "$dir")"
|
|
24
|
-
done
|
|
25
|
-
# Fallback: try current directory
|
|
26
|
-
if [ -d "$(pwd)/.specweave" ]; then
|
|
27
|
-
pwd
|
|
28
|
-
else
|
|
29
|
-
echo "$(pwd)"
|
|
30
|
-
fi
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
PROJECT_ROOT="$(find_project_root "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")"
|
|
34
|
-
cd "$PROJECT_ROOT"
|
|
35
|
-
|
|
36
|
-
# Colors
|
|
37
|
-
YELLOW='\033[1;33m'
|
|
38
|
-
BLUE='\033[0;34m'
|
|
39
|
-
GREEN='\033[0;32m'
|
|
40
|
-
NC='\033[0m'
|
|
41
|
-
|
|
42
|
-
echo -e "${BLUE}🔍 Pre-Implementation Check${NC}"
|
|
43
|
-
|
|
44
|
-
# Check if this is a brownfield project (has existing code)
|
|
45
|
-
if [ -d "src" ] || [ -d "app" ] || [ -d "lib" ]; then
|
|
46
|
-
echo -e "${YELLOW}⚠️ Brownfield project detected${NC}"
|
|
47
|
-
echo ""
|
|
48
|
-
echo "Recommendations:"
|
|
49
|
-
echo " 1. Create baseline tests before changes"
|
|
50
|
-
echo " 2. Check for existing tests that may break"
|
|
51
|
-
echo " 3. Review impact on existing features"
|
|
52
|
-
echo ""
|
|
53
|
-
|
|
54
|
-
# Check if baseline tests exist
|
|
55
|
-
if [ -d ".specweave/tests/baseline" ]; then
|
|
56
|
-
echo -e "${GREEN}✅ Baseline tests exist${NC}"
|
|
57
|
-
else
|
|
58
|
-
echo -e "${YELLOW}⚠️ No baseline tests found${NC}"
|
|
59
|
-
echo " Consider creating baseline tests first"
|
|
60
|
-
echo " This captures current state before changes"
|
|
61
|
-
fi
|
|
62
|
-
else
|
|
63
|
-
echo -e "${GREEN}✅ Greenfield project - no regression risk${NC}"
|
|
64
|
-
fi
|
|
65
|
-
|
|
66
|
-
# Log to hooks log
|
|
67
|
-
LOGS_DIR=".specweave/logs"
|
|
68
|
-
mkdir -p "$LOGS_DIR"
|
|
69
|
-
echo "[$(date)] Pre-implementation check complete" >> "$LOGS_DIR/hooks.log"
|
|
70
|
-
|
|
71
|
-
echo ""
|
|
72
|
-
echo -e "${GREEN}✅ Pre-implementation check complete${NC}"
|
|
73
|
-
|
|
74
|
-
# ALWAYS exit 0 - NEVER let hook errors crash Claude Code
|
|
75
|
-
exit 0
|