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.
Files changed (79) hide show
  1. package/CLAUDE.md +140 -1235
  2. package/bin/specweave.js +23 -0
  3. package/dist/plugins/specweave-github/lib/github-client-v2.d.ts +3 -0
  4. package/dist/plugins/specweave-github/lib/github-client-v2.d.ts.map +1 -1
  5. package/dist/plugins/specweave-github/lib/github-client-v2.js +39 -0
  6. package/dist/plugins/specweave-github/lib/github-client-v2.js.map +1 -1
  7. package/dist/src/cli/commands/set-sync-target.d.ts +41 -0
  8. package/dist/src/cli/commands/set-sync-target.d.ts.map +1 -0
  9. package/dist/src/cli/commands/set-sync-target.js +126 -0
  10. package/dist/src/cli/commands/set-sync-target.js.map +1 -0
  11. package/dist/src/cli/helpers/issue-tracker/github-multi-repo.d.ts.map +1 -1
  12. package/dist/src/cli/helpers/issue-tracker/github-multi-repo.js +5 -8
  13. package/dist/src/cli/helpers/issue-tracker/github-multi-repo.js.map +1 -1
  14. package/dist/src/core/hooks/HookScanner.d.ts +32 -0
  15. package/dist/src/core/hooks/HookScanner.d.ts.map +1 -1
  16. package/dist/src/core/hooks/HookScanner.js +125 -1
  17. package/dist/src/core/hooks/HookScanner.js.map +1 -1
  18. package/dist/src/core/hooks/types.d.ts +10 -1
  19. package/dist/src/core/hooks/types.d.ts.map +1 -1
  20. package/dist/src/core/increment/metadata-manager.d.ts +67 -1
  21. package/dist/src/core/increment/metadata-manager.d.ts.map +1 -1
  22. package/dist/src/core/increment/metadata-manager.js +93 -0
  23. package/dist/src/core/increment/metadata-manager.js.map +1 -1
  24. package/dist/src/core/project/index.d.ts +21 -0
  25. package/dist/src/core/project/index.d.ts.map +1 -0
  26. package/dist/src/core/project/index.js +22 -0
  27. package/dist/src/core/project/index.js.map +1 -0
  28. package/dist/src/core/project/project-service.d.ts +122 -0
  29. package/dist/src/core/project/project-service.d.ts.map +1 -0
  30. package/dist/src/core/project/project-service.js +334 -0
  31. package/dist/src/core/project/project-service.js.map +1 -0
  32. package/dist/src/core/sync/external-tool-resolver.d.ts +171 -0
  33. package/dist/src/core/sync/external-tool-resolver.d.ts.map +1 -0
  34. package/dist/src/core/sync/external-tool-resolver.js +569 -0
  35. package/dist/src/core/sync/external-tool-resolver.js.map +1 -0
  36. package/dist/src/core/types/increment-metadata.d.ts +92 -0
  37. package/dist/src/core/types/increment-metadata.d.ts.map +1 -1
  38. package/dist/src/hooks/processor.d.ts +7 -3
  39. package/dist/src/hooks/processor.d.ts.map +1 -1
  40. package/dist/src/hooks/processor.js +11 -5
  41. package/dist/src/hooks/processor.js.map +1 -1
  42. package/package.json +1 -1
  43. package/plugins/specweave/hooks/hooks.json +0 -69
  44. package/plugins/specweave/hooks/v2/handlers/project-bridge-handler.sh +96 -0
  45. package/plugins/specweave/hooks/v2/queue/processor.sh +13 -5
  46. package/plugins/specweave/lib/hooks/project-bridge.js +76 -0
  47. package/plugins/specweave/lib/hooks/update-tasks-md.js +0 -0
  48. package/plugins/specweave/lib/hooks/us-completion-orchestrator.js +0 -0
  49. package/plugins/specweave/lib/vendor/core/increment/metadata-manager.d.ts +67 -1
  50. package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js +93 -0
  51. package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js.map +1 -1
  52. package/plugins/specweave/lib/vendor/core/types/increment-metadata.d.ts +92 -0
  53. package/plugins/specweave-github/lib/github-client-v2.js +39 -0
  54. package/plugins/specweave-github/lib/github-client-v2.ts +44 -0
  55. package/plugins/specweave/hooks/docs-changed.sh +0 -87
  56. package/plugins/specweave/hooks/human-input-required.sh +0 -83
  57. package/plugins/specweave/hooks/post-edit-write-consolidated.sh +0 -428
  58. package/plugins/specweave/hooks/post-first-increment.sh +0 -61
  59. package/plugins/specweave/hooks/post-increment-change.sh +0 -103
  60. package/plugins/specweave/hooks/post-increment-completion.sh +0 -513
  61. package/plugins/specweave/hooks/post-increment-planning.sh +0 -1204
  62. package/plugins/specweave/hooks/post-increment-status-change.sh +0 -243
  63. package/plugins/specweave/hooks/post-metadata-change.sh +0 -246
  64. package/plugins/specweave/hooks/post-spec-update.sh +0 -158
  65. package/plugins/specweave/hooks/post-task-completion.sh +0 -557
  66. package/plugins/specweave/hooks/post-task-edit.sh +0 -47
  67. package/plugins/specweave/hooks/post-user-story-complete.sh +0 -230
  68. package/plugins/specweave/hooks/pre-command-deduplication.sh +0 -68
  69. package/plugins/specweave/hooks/pre-edit-write-consolidated.sh +0 -225
  70. package/plugins/specweave/hooks/pre-implementation.sh +0 -75
  71. package/plugins/specweave/hooks/pre-increment-start.sh +0 -173
  72. package/plugins/specweave/hooks/pre-task-completion-edit.sh +0 -355
  73. package/plugins/specweave/hooks/pre-task-completion.sh +0 -269
  74. package/plugins/specweave/hooks/pre-tool-use.sh +0 -137
  75. package/plugins/specweave/hooks/session-start-reconcile.sh +0 -139
  76. package/plugins/specweave/hooks/shared/bulk-operation-detector.sh +0 -167
  77. package/plugins/specweave/hooks/test-pretooluse-env.sh +0 -72
  78. package/plugins/specweave/hooks/validate-increment-completion.sh +0 -113
  79. 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