specweave 0.23.8 → 0.23.12

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 (130) hide show
  1. package/.claude-plugin/marketplace.json +7 -7
  2. package/CLAUDE.md +391 -1338
  3. package/dist/src/cli/commands/cleanup-cache.d.ts +14 -0
  4. package/dist/src/cli/commands/cleanup-cache.d.ts.map +1 -0
  5. package/dist/src/cli/commands/cleanup-cache.js +63 -0
  6. package/dist/src/cli/commands/cleanup-cache.js.map +1 -0
  7. package/dist/src/cli/commands/init.js +40 -0
  8. package/dist/src/cli/commands/init.js.map +1 -1
  9. package/dist/src/cli/commands/migrate-config.d.ts +22 -0
  10. package/dist/src/cli/commands/migrate-config.d.ts.map +1 -0
  11. package/dist/src/cli/commands/migrate-config.js +149 -0
  12. package/dist/src/cli/commands/migrate-config.js.map +1 -0
  13. package/dist/src/cli/helpers/async-project-loader.d.ts +148 -0
  14. package/dist/src/cli/helpers/async-project-loader.d.ts.map +1 -0
  15. package/dist/src/cli/helpers/async-project-loader.js +351 -0
  16. package/dist/src/cli/helpers/async-project-loader.js.map +1 -0
  17. package/dist/src/cli/helpers/cancelation-handler.d.ts +123 -0
  18. package/dist/src/cli/helpers/cancelation-handler.d.ts.map +1 -0
  19. package/dist/src/cli/helpers/cancelation-handler.js +187 -0
  20. package/dist/src/cli/helpers/cancelation-handler.js.map +1 -0
  21. package/dist/src/cli/helpers/import-strategy-prompter.d.ts +43 -0
  22. package/dist/src/cli/helpers/import-strategy-prompter.d.ts.map +1 -0
  23. package/dist/src/cli/helpers/import-strategy-prompter.js +136 -0
  24. package/dist/src/cli/helpers/import-strategy-prompter.js.map +1 -0
  25. package/dist/src/cli/helpers/issue-tracker/ado.d.ts +5 -2
  26. package/dist/src/cli/helpers/issue-tracker/ado.d.ts.map +1 -1
  27. package/dist/src/cli/helpers/issue-tracker/ado.js +90 -40
  28. package/dist/src/cli/helpers/issue-tracker/ado.js.map +1 -1
  29. package/dist/src/cli/helpers/issue-tracker/index.d.ts.map +1 -1
  30. package/dist/src/cli/helpers/issue-tracker/index.js +112 -60
  31. package/dist/src/cli/helpers/issue-tracker/index.js.map +1 -1
  32. package/dist/src/cli/helpers/issue-tracker/jira.d.ts +26 -2
  33. package/dist/src/cli/helpers/issue-tracker/jira.d.ts.map +1 -1
  34. package/dist/src/cli/helpers/issue-tracker/jira.js +197 -132
  35. package/dist/src/cli/helpers/issue-tracker/jira.js.map +1 -1
  36. package/dist/src/cli/helpers/progress-tracker.d.ts +121 -0
  37. package/dist/src/cli/helpers/progress-tracker.d.ts.map +1 -0
  38. package/dist/src/cli/helpers/progress-tracker.js +202 -0
  39. package/dist/src/cli/helpers/progress-tracker.js.map +1 -0
  40. package/dist/src/cli/helpers/project-count-fetcher.d.ts +69 -0
  41. package/dist/src/cli/helpers/project-count-fetcher.d.ts.map +1 -0
  42. package/dist/src/cli/helpers/project-count-fetcher.js +173 -0
  43. package/dist/src/cli/helpers/project-count-fetcher.js.map +1 -0
  44. package/dist/src/config/types.d.ts +14 -14
  45. package/dist/src/core/cache/cache-manager.d.ts +119 -0
  46. package/dist/src/core/cache/cache-manager.d.ts.map +1 -0
  47. package/dist/src/core/cache/cache-manager.js +304 -0
  48. package/dist/src/core/cache/cache-manager.js.map +1 -0
  49. package/dist/src/core/cache/rate-limit-checker.d.ts +92 -0
  50. package/dist/src/core/cache/rate-limit-checker.d.ts.map +1 -0
  51. package/dist/src/core/cache/rate-limit-checker.js +160 -0
  52. package/dist/src/core/cache/rate-limit-checker.js.map +1 -0
  53. package/dist/src/core/config/config-manager.d.ts +135 -0
  54. package/dist/src/core/config/config-manager.d.ts.map +1 -0
  55. package/dist/src/core/config/config-manager.js +341 -0
  56. package/dist/src/core/config/config-manager.js.map +1 -0
  57. package/dist/src/core/config/config-migrator.d.ts +102 -0
  58. package/dist/src/core/config/config-migrator.d.ts.map +1 -0
  59. package/dist/src/core/config/config-migrator.js +367 -0
  60. package/dist/src/core/config/config-migrator.js.map +1 -0
  61. package/dist/src/core/config/index.d.ts +10 -0
  62. package/dist/src/core/config/index.d.ts.map +1 -0
  63. package/dist/src/core/config/index.js +10 -0
  64. package/dist/src/core/config/index.js.map +1 -0
  65. package/dist/src/core/config/types.d.ts +216 -0
  66. package/dist/src/core/config/types.d.ts.map +1 -0
  67. package/dist/src/core/config/types.js +32 -0
  68. package/dist/src/core/config/types.js.map +1 -0
  69. package/dist/src/core/progress/cancelation-handler.d.ts +79 -0
  70. package/dist/src/core/progress/cancelation-handler.d.ts.map +1 -0
  71. package/dist/src/core/progress/cancelation-handler.js +111 -0
  72. package/dist/src/core/progress/cancelation-handler.js.map +1 -0
  73. package/dist/src/core/progress/import-state.d.ts +71 -0
  74. package/dist/src/core/progress/import-state.d.ts.map +1 -0
  75. package/dist/src/core/progress/import-state.js +96 -0
  76. package/dist/src/core/progress/import-state.js.map +1 -0
  77. package/dist/src/core/progress/progress-tracker.d.ts +139 -0
  78. package/dist/src/core/progress/progress-tracker.d.ts.map +1 -0
  79. package/dist/src/core/progress/progress-tracker.js +223 -0
  80. package/dist/src/core/progress/progress-tracker.js.map +1 -0
  81. package/dist/src/init/architecture/types.d.ts +6 -6
  82. package/dist/src/integrations/ado/ado-client.d.ts +25 -0
  83. package/dist/src/integrations/ado/ado-client.d.ts.map +1 -1
  84. package/dist/src/integrations/ado/ado-client.js +67 -0
  85. package/dist/src/integrations/ado/ado-client.js.map +1 -1
  86. package/dist/src/integrations/ado/ado-dependency-loader.d.ts +99 -0
  87. package/dist/src/integrations/ado/ado-dependency-loader.d.ts.map +1 -0
  88. package/dist/src/integrations/ado/ado-dependency-loader.js +207 -0
  89. package/dist/src/integrations/ado/ado-dependency-loader.js.map +1 -0
  90. package/dist/src/integrations/jira/jira-client.d.ts +32 -0
  91. package/dist/src/integrations/jira/jira-client.d.ts.map +1 -1
  92. package/dist/src/integrations/jira/jira-client.js +81 -0
  93. package/dist/src/integrations/jira/jira-client.js.map +1 -1
  94. package/dist/src/integrations/jira/jira-dependency-loader.d.ts +101 -0
  95. package/dist/src/integrations/jira/jira-dependency-loader.d.ts.map +1 -0
  96. package/dist/src/integrations/jira/jira-dependency-loader.js +200 -0
  97. package/dist/src/integrations/jira/jira-dependency-loader.js.map +1 -0
  98. package/dist/src/integrations/jira/jira-hierarchy-mapper.d.ts +104 -0
  99. package/dist/src/integrations/jira/jira-hierarchy-mapper.d.ts.map +1 -0
  100. package/dist/src/integrations/jira/jira-hierarchy-mapper.js +178 -0
  101. package/dist/src/integrations/jira/jira-hierarchy-mapper.js.map +1 -0
  102. package/package.json +1 -1
  103. package/plugins/specweave/.claude-plugin/plugin.json +20 -0
  104. package/plugins/specweave/agents/architect/AGENT.md +100 -602
  105. package/plugins/specweave/agents/pm/AGENT.md +96 -597
  106. package/plugins/specweave/agents/pm/AGENT.md.bak +1893 -0
  107. package/plugins/specweave/agents/pm/AGENT.md.bak2 +1754 -0
  108. package/plugins/specweave/commands/check-hooks.md +257 -0
  109. package/plugins/specweave/commands/migrate-config.md +104 -0
  110. package/plugins/specweave/hooks/post-edit-spec.sh +202 -31
  111. package/plugins/specweave/hooks/post-task-completion.sh +225 -228
  112. package/plugins/specweave/hooks/post-write-spec.sh +207 -31
  113. package/plugins/specweave/hooks/pre-edit-spec.sh +151 -0
  114. package/plugins/specweave/hooks/pre-task-completion.sh +5 -7
  115. package/plugins/specweave/hooks/pre-write-spec.sh +151 -0
  116. package/plugins/specweave/hooks/test-pretooluse-env.sh +72 -0
  117. package/plugins/specweave/skills/compliance-architecture/SKILL.md +374 -0
  118. package/plugins/specweave/skills/external-sync-wizard/SKILL.md +610 -0
  119. package/plugins/specweave/skills/pm-closure-validation/SKILL.md +541 -0
  120. package/plugins/specweave/skills/roadmap-planner/SKILL.md +473 -0
  121. package/plugins/specweave-ado/commands/refresh-cache.js +25 -0
  122. package/plugins/specweave-ado/commands/refresh-cache.ts +40 -0
  123. package/plugins/specweave-ado/hooks/post-task-completion.sh +1 -1
  124. package/plugins/specweave-github/hooks/post-task-completion.sh +1 -1
  125. package/plugins/specweave-jira/commands/refresh-cache.js +25 -0
  126. package/plugins/specweave-jira/commands/refresh-cache.ts +40 -0
  127. package/plugins/specweave-jira/hooks/post-task-completion.sh +1 -1
  128. package/plugins/specweave-kafka-streams/commands/topology.md +437 -0
  129. package/plugins/specweave-n8n/commands/workflow-template.md +262 -0
  130. package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +228 -6333
@@ -5,12 +5,30 @@
5
5
  # Triggers: After Write tool creates/replaces spec.md or tasks.md
6
6
  # Action: Updates status line cache to reflect latest AC/task progress
7
7
  #
8
- # CRITICAL FIX (v0.24.1): Enhanced file detection to handle increment completion
8
+ # EMERGENCY FIXES (v0.24.3):
9
+ # - Kill switch: Set SPECWEAVE_DISABLE_HOOKS=1 to disable ALL hooks
10
+ # - Circuit breaker: Auto-disable after 3 consecutive failures
11
+ # - File locking: Prevent concurrent executions (max 1 at a time)
12
+ # - Aggressive debouncing: Increased from 1s to 5s
13
+ # - Complete error isolation: Never let errors reach Claude Code
14
+ #
15
+ # TIER 1 IMPROVEMENTS (v0.24.2):
16
+ # - Debouncing: Skip if updated less than 1 second ago (90% overhead reduction)
17
+ # - File mtime detection: Check recently modified spec.md/tasks.md as fallback
18
+ # - Non-blocking: Run update-status-line.sh in background
19
+ # - Smart detection: Only update if spec/tasks files actually changed
20
+ #
21
+ # Previous fix (v0.24.1): Enhanced file detection for increment completion
9
22
  # - Detects writes via TOOL_USE_CONTENT, TOOL_RESULT, and argument parsing
10
23
  # - Always updates status line for ANY spec.md/tasks.md write in increments folder
11
- # - Matches post-edit-spec.sh robustness improvements
12
24
 
13
- set -e
25
+ # CRITICAL: Remove set -e to prevent hook errors from crashing Claude Code
26
+ set +e
27
+
28
+ # EMERGENCY KILL SWITCH: Disable all hooks if env variable set
29
+ if [[ "${SPECWEAVE_DISABLE_HOOKS:-0}" == "1" ]]; then
30
+ exit 0
31
+ fi
14
32
 
15
33
  # Find project root
16
34
  find_project_root() {
@@ -29,31 +47,134 @@ PROJECT_ROOT=$(find_project_root)
29
47
  LOGS_DIR="$PROJECT_ROOT/.specweave/logs"
30
48
  DEBUG_LOG="$LOGS_DIR/hooks-debug.log"
31
49
 
32
- # Ensure logs directory exists
33
- mkdir -p "$LOGS_DIR" 2>/dev/null || true
50
+ # Ensure state and logs directories exist
51
+ mkdir -p "$PROJECT_ROOT/.specweave/state" "$LOGS_DIR" 2>/dev/null || true
34
52
 
35
- # Extract written file from multiple sources (Claude Code provides this in various ways)
36
- WRITTEN_FILE=""
53
+ # EMERGENCY CIRCUIT BREAKER: Track consecutive failures
54
+ CIRCUIT_BREAKER_FILE="$PROJECT_ROOT/.specweave/state/.hook-circuit-breaker"
55
+ CIRCUIT_BREAKER_THRESHOLD=3
56
+
57
+ if [[ -f "$CIRCUIT_BREAKER_FILE" ]]; then
58
+ FAILURE_COUNT=$(cat "$CIRCUIT_BREAKER_FILE" 2>/dev/null || echo 0)
59
+ if (( FAILURE_COUNT >= CIRCUIT_BREAKER_THRESHOLD )); then
60
+ echo "[$(date)] CIRCUIT BREAKER OPEN: Hooks disabled after $FAILURE_COUNT failures. Run: rm $CIRCUIT_BREAKER_FILE" >> "$DEBUG_LOG" 2>/dev/null || true
61
+ exit 0
62
+ fi
63
+ fi
64
+
65
+ # EMERGENCY FILE LOCK: Prevent concurrent executions
66
+ LOCK_FILE="$PROJECT_ROOT/.specweave/state/.hook-post-write.lock"
67
+ LOCK_TIMEOUT=5 # seconds
68
+
69
+ # Try to acquire lock with timeout
70
+ LOCK_ACQUIRED=false
71
+ for i in {1..5}; do
72
+ if mkdir "$LOCK_FILE" 2>/dev/null; then
73
+ LOCK_ACQUIRED=true
74
+ trap 'rmdir "$LOCK_FILE" 2>/dev/null || true' EXIT
75
+ break
76
+ fi
77
+
78
+ # Check if lock is stale (older than LOCK_TIMEOUT seconds)
79
+ if [[ -d "$LOCK_FILE" ]]; then
80
+ LOCK_AGE=$(($(date +%s) - $(stat -f "%m" "$LOCK_FILE" 2>/dev/null || echo 0)))
81
+ if (( LOCK_AGE > LOCK_TIMEOUT )); then
82
+ rmdir "$LOCK_FILE" 2>/dev/null || true
83
+ continue
84
+ fi
85
+ fi
86
+
87
+ sleep 0.2
88
+ done
89
+
90
+ if [[ "$LOCK_ACQUIRED" == "false" ]]; then
91
+ echo "[$(date)] post-write-spec: Could not acquire lock, skipping" >> "$DEBUG_LOG" 2>/dev/null || true
92
+ exit 0
93
+ fi
94
+
95
+ # Log rotation: Keep debug log under 100KB
96
+ if [[ -f "$DEBUG_LOG" ]] && [[ $(wc -c < "$DEBUG_LOG" 2>/dev/null || echo 0) -gt 102400 ]]; then
97
+ tail -100 "$DEBUG_LOG" > "$DEBUG_LOG.tmp" 2>/dev/null || true
98
+ mv "$DEBUG_LOG.tmp" "$DEBUG_LOG" 2>/dev/null || true
99
+ echo "[$(date)] Log rotated" >> "$DEBUG_LOG" 2>/dev/null || true
100
+ fi
101
+
102
+ # ============================================================================
103
+ # TIER 1 FIX: Debouncing (Prevent Redundant Updates)
104
+ # ============================================================================
105
+ # Skip update if we updated less than 5 seconds ago (INCREASED FROM 1s)
106
+ # This handles rapid consecutive writes (e.g., spec.md regeneration)
107
+ LAST_UPDATE_FILE="$PROJECT_ROOT/.specweave/state/.last-status-update"
108
+ DEBOUNCE_SECONDS=5
109
+
110
+ if [[ -f "$LAST_UPDATE_FILE" ]]; then
111
+ LAST_UPDATE=$(cat "$LAST_UPDATE_FILE" 2>/dev/null || echo 0)
112
+ NOW=$(date +%s)
113
+ TIME_SINCE_UPDATE=$((NOW - LAST_UPDATE))
37
114
 
38
- # Method 1: TOOL_USE_CONTENT environment variable (primary)
39
- if [[ -n "${TOOL_USE_CONTENT:-}" ]]; then
40
- WRITTEN_FILE="$TOOL_USE_CONTENT"
115
+ if (( TIME_SINCE_UPDATE < DEBOUNCE_SECONDS )); then
116
+ echo "[$(date)] post-write-spec: Debounced (${TIME_SINCE_UPDATE}s since last update)" >> "$DEBUG_LOG" 2>/dev/null || true
117
+ exit 0 # Skip this update
118
+ fi
41
119
  fi
42
120
 
43
- # Method 2: TOOL_RESULT environment variable (fallback)
44
- if [[ -z "$WRITTEN_FILE" ]] && [[ -n "${TOOL_RESULT:-}" ]]; then
45
- # Extract file_path from tool result JSON
46
- WRITTEN_FILE=$(echo "$TOOL_RESULT" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)".*/\1/' || echo "")
121
+ # ============================================================================
122
+ # TIER 2: Check for PreToolUse Signal (Primary Detection Method)
123
+ # ============================================================================
124
+ PENDING_FILE="$PROJECT_ROOT/.specweave/state/.pending-status-update"
125
+ METRICS_FILE="$PROJECT_ROOT/.specweave/state/hook-metrics.jsonl"
126
+ WRITTEN_FILE=""
127
+ DETECTION_METHOD="none"
128
+
129
+ # First, check if PreToolUse hook left a signal
130
+ if [[ -f "$PENDING_FILE" ]]; then
131
+ WRITTEN_FILE=$(cat "$PENDING_FILE" 2>/dev/null || echo "")
132
+ # Delete pending file immediately (consume signal)
133
+ rm "$PENDING_FILE" 2>/dev/null || true
134
+
135
+ if [[ -n "$WRITTEN_FILE" ]]; then
136
+ DETECTION_METHOD="pretooluse"
137
+ echo "[$(date)] post-write-spec: File from PreToolUse signal: $WRITTEN_FILE" >> "$DEBUG_LOG" 2>/dev/null || true
138
+
139
+ # Record Tier 2 success metric
140
+ TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
141
+ echo "{\"timestamp\":\"$TIMESTAMP\",\"hook\":\"post-write-spec\",\"event\":\"tier2_success\",\"method\":\"pretooluse\"}" >> "$METRICS_FILE" 2>/dev/null || true
142
+ fi
47
143
  fi
48
144
 
49
- # Method 3: Parse tool use arguments (last resort)
50
- if [[ -z "$WRITTEN_FILE" ]] && [[ -n "${TOOL_USE_ARGS:-}" ]]; then
51
- # Extract file_path from tool arguments
52
- WRITTEN_FILE=$(echo "$TOOL_USE_ARGS" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)".*/\1/' || echo "")
145
+ # ============================================================================
146
+ # TIER 1 FALLBACK: Environment Variable Detection
147
+ # ============================================================================
148
+ # If PreToolUse didn't provide signal, fall back to Tier 1 methods
149
+ if [[ -z "$WRITTEN_FILE" ]]; then
150
+ # Method 1: TOOL_USE_CONTENT environment variable
151
+ if [[ -n "${TOOL_USE_CONTENT:-}" ]]; then
152
+ WRITTEN_FILE="$TOOL_USE_CONTENT"
153
+ DETECTION_METHOD="env_content"
154
+ fi
155
+
156
+ # Method 2: TOOL_RESULT environment variable
157
+ if [[ -z "$WRITTEN_FILE" ]] && [[ -n "${TOOL_RESULT:-}" ]]; then
158
+ WRITTEN_FILE=$(echo "$TOOL_RESULT" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)".*/\1/' || echo "")
159
+ DETECTION_METHOD="env_result"
160
+ fi
161
+
162
+ # Method 3: TOOL_USE_ARGS
163
+ if [[ -z "$WRITTEN_FILE" ]] && [[ -n "${TOOL_USE_ARGS:-}" ]]; then
164
+ WRITTEN_FILE=$(echo "$TOOL_USE_ARGS" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)".*/\1/' || echo "")
165
+ DETECTION_METHOD="env_args"
166
+ fi
167
+
168
+ # Log env var detection (for metrics)
169
+ if [[ -n "$WRITTEN_FILE" ]]; then
170
+ echo "[$(date)] post-write-spec: File from env vars ($DETECTION_METHOD): $WRITTEN_FILE" >> "$DEBUG_LOG" 2>/dev/null || true
171
+ fi
53
172
  fi
54
173
 
55
- # Log detection attempt
56
- echo "[$(date)] post-write-spec: Detected file: ${WRITTEN_FILE:-<none>}" >> "$DEBUG_LOG" 2>/dev/null || true
174
+ # Log detection attempt (only log if we actually detected a file, to reduce noise)
175
+ if [[ -n "$WRITTEN_FILE" ]]; then
176
+ echo "[$(date)] post-write-spec: Detected file: $WRITTEN_FILE" >> "$DEBUG_LOG" 2>/dev/null || true
177
+ fi
57
178
 
58
179
  # Check if we detected a spec.md or tasks.md write in increments folder
59
180
  SHOULD_UPDATE=false
@@ -69,23 +190,78 @@ if [[ -n "$WRITTEN_FILE" ]]; then
69
190
  fi
70
191
  fi
71
192
 
72
- # If we couldn't detect the file via environment variables, always update status line
73
- # This ensures we don't miss updates during increment closure
193
+ # ============================================================================
194
+ # TIER 1 FIX: File Modification Time Detection (Fallback)
195
+ # ============================================================================
196
+ # If we couldn't detect the file via environment variables, check which files
197
+ # were modified recently (within last 2 seconds) instead of blindly updating
74
198
  if [[ -z "$WRITTEN_FILE" ]]; then
75
- echo "[$(date)] post-write-spec: No file detected - updating status line as safety measure" >> "$DEBUG_LOG" 2>/dev/null || true
76
- SHOULD_UPDATE=true
199
+ echo "[$(date)] post-write-spec: Env vars empty - checking file mtimes" >> "$DEBUG_LOG" 2>/dev/null || true
200
+
201
+ NOW=$(date +%s)
202
+ INCREMENTS_DIR="$PROJECT_ROOT/.specweave/increments"
203
+
204
+ # Check for recently modified spec.md or tasks.md files
205
+ if [[ -d "$INCREMENTS_DIR" ]]; then
206
+ for file in "$INCREMENTS_DIR"/*/spec.md "$INCREMENTS_DIR"/*/tasks.md; do
207
+ if [[ -f "$file" ]]; then
208
+ # Get file modification time (platform-specific)
209
+ if [[ "$(uname)" == "Darwin" ]]; then
210
+ MTIME=$(stat -f "%m" "$file" 2>/dev/null || echo 0)
211
+ else
212
+ MTIME=$(stat -c "%Y" "$file" 2>/dev/null || echo 0)
213
+ fi
214
+
215
+ # If file was modified in last 2 seconds, consider it the written file
216
+ TIME_DIFF=$((NOW - MTIME))
217
+ if (( TIME_DIFF <= 2 )); then
218
+ WRITTEN_FILE="$file"
219
+ echo "[$(date)] post-write-spec: Detected recent modification: $file (${TIME_DIFF}s ago)" >> "$DEBUG_LOG" 2>/dev/null || true
220
+ SHOULD_UPDATE=true
221
+ break
222
+ fi
223
+ fi
224
+ done
225
+ fi
226
+
227
+ # If still no file detected, skip update (not a spec/tasks write)
228
+ if [[ -z "$WRITTEN_FILE" ]]; then
229
+ echo "[$(date)] post-write-spec: No spec/tasks modifications detected - skipping" >> "$DEBUG_LOG" 2>/dev/null || true
230
+ exit 0
231
+ fi
77
232
  fi
78
233
 
234
+ # ============================================================================
235
+ # TIER 1 FIX: Non-Blocking Background Update with COMPLETE ERROR ISOLATION
236
+ # ============================================================================
79
237
  # Update status line if needed
80
238
  if [[ "$SHOULD_UPDATE" == "true" ]]; then
81
- echo "[$(date)] post-write-spec: Running update-status-line.sh" >> "$DEBUG_LOG" 2>/dev/null || true
239
+ echo "[$(date)] post-write-spec: Running update-status-line.sh (background)" >> "$DEBUG_LOG" 2>/dev/null || true
82
240
 
83
- # Run status line update (capture errors for debugging)
84
- if "$PROJECT_ROOT/plugins/specweave/hooks/lib/update-status-line.sh" 2>&1 | tee -a "$DEBUG_LOG" >/dev/null; then
85
- echo "[$(date)] post-write-spec: Status line updated successfully" >> "$DEBUG_LOG" 2>/dev/null || true
86
- else
87
- echo "[$(date)] post-write-spec: Warning - status line update failed (non-blocking)" >> "$DEBUG_LOG" 2>/dev/null || true
88
- fi
241
+ # Record update time BEFORE spawning background process
242
+ # This ensures debouncing works even if update hasn't completed yet
243
+ echo "$(date +%s)" > "$LAST_UPDATE_FILE"
244
+
245
+ # Run status line update in background with COMPLETE error isolation
246
+ # This prevents Write tool from waiting for status line computation
247
+ (
248
+ set +e # Disable error propagation
249
+
250
+ if "$PROJECT_ROOT/plugins/specweave/hooks/lib/update-status-line.sh" 2>&1 | tee -a "$DEBUG_LOG" >/dev/null; then
251
+ echo "[$(date)] post-write-spec: Status line updated successfully" >> "$DEBUG_LOG" 2>/dev/null || true
252
+ # Reset circuit breaker on success
253
+ echo "0" > "$CIRCUIT_BREAKER_FILE" 2>/dev/null || true
254
+ else
255
+ echo "[$(date)] post-write-spec: Warning - status line update failed (non-blocking)" >> "$DEBUG_LOG" 2>/dev/null || true
256
+ # Increment circuit breaker
257
+ CURRENT_FAILURES=$(cat "$CIRCUIT_BREAKER_FILE" 2>/dev/null || echo 0)
258
+ echo "$((CURRENT_FAILURES + 1))" > "$CIRCUIT_BREAKER_FILE" 2>/dev/null || true
259
+ fi
260
+ ) &
261
+
262
+ # Disown the background process so it's not killed when hook exits
263
+ disown 2>/dev/null || true
89
264
  fi
90
265
 
266
+ # Always exit 0 to prevent hook errors from crashing Claude Code
91
267
  exit 0
@@ -0,0 +1,151 @@
1
+ #!/bin/bash
2
+ #
3
+ # Pre-Edit Hook: Capture File Path BEFORE Edit Executes (Tier 2)
4
+ #
5
+ # Purpose: Detect which file will be edited BEFORE the Edit tool runs
6
+ # Strategy: PreToolUse has better access to tool arguments than PostToolUse
7
+ #
8
+ # TIER 2 COORDINATION:
9
+ # 1. Extract file_path from TOOL_USE_ARGS (more reliable in PreToolUse)
10
+ # 2. If it's spec.md/tasks.md in increments folder, signal PostToolUse hook
11
+ # 3. Write file path to .pending-status-update for PostToolUse to consume
12
+ #
13
+ # This eliminates the need for mtime-based fallback detection (Tier 1)
14
+ # and reduces false positives to near zero.
15
+ #
16
+ # Architecture:
17
+ # PreToolUse:Edit → pre-edit-spec.sh (this file)
18
+ # ↓ writes to
19
+ # .specweave/state/.pending-status-update
20
+ # ↓ read by
21
+ # PostToolUse:Edit → post-edit-spec.sh
22
+ #
23
+ # Version: v0.24.3 (EMERGENCY FIXES)
24
+ # Date: 2025-11-22
25
+
26
+ # EMERGENCY FIX: Remove set -e - it causes Claude Code crashes!
27
+ set +e
28
+
29
+ # EMERGENCY KILL SWITCH: Disable all hooks if env variable set
30
+ if [[ "${SPECWEAVE_DISABLE_HOOKS:-0}" == "1" ]]; then
31
+ exit 0
32
+ fi
33
+
34
+ # Find project root
35
+ find_project_root() {
36
+ local dir="$PWD"
37
+ while [[ "$dir" != "/" ]]; do
38
+ if [[ -d "$dir/.specweave" ]]; then
39
+ echo "$dir"
40
+ return 0
41
+ fi
42
+ dir=$(dirname "$dir")
43
+ done
44
+ echo "$PWD"
45
+ }
46
+
47
+ PROJECT_ROOT=$(find_project_root)
48
+ STATE_DIR="$PROJECT_ROOT/.specweave/state"
49
+ LOGS_DIR="$PROJECT_ROOT/.specweave/logs"
50
+ DEBUG_LOG="$LOGS_DIR/hooks-debug.log"
51
+ PENDING_FILE="$STATE_DIR/.pending-status-update"
52
+ METRICS_FILE="$STATE_DIR/hook-metrics.jsonl"
53
+
54
+ # Ensure directories exist
55
+ mkdir -p "$STATE_DIR" "$LOGS_DIR" 2>/dev/null || true
56
+
57
+ # ============================================================================
58
+ # TIER 2: Extract File Path from Tool Arguments
59
+ # ============================================================================
60
+ # PreToolUse should have access to tool arguments BEFORE execution
61
+ # Try multiple methods to extract file_path
62
+
63
+ FILE_PATH=""
64
+
65
+ # Method 1: TOOL_USE_ARGS environment variable (primary for PreToolUse)
66
+ if [[ -n "${TOOL_USE_ARGS:-}" ]]; then
67
+ # Try to parse JSON with jq if available
68
+ if command -v jq &> /dev/null; then
69
+ FILE_PATH=$(echo "$TOOL_USE_ARGS" | jq -r '.file_path // empty' 2>/dev/null || echo "")
70
+ fi
71
+
72
+ # Fallback: Regex extraction if jq not available or failed
73
+ if [[ -z "$FILE_PATH" ]]; then
74
+ FILE_PATH=$(echo "$TOOL_USE_ARGS" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)".*/\1/' || echo "")
75
+ fi
76
+ fi
77
+
78
+ # Method 2: TOOL_USE_CONTENT (fallback)
79
+ if [[ -z "$FILE_PATH" ]] && [[ -n "${TOOL_USE_CONTENT:-}" ]]; then
80
+ FILE_PATH="$TOOL_USE_CONTENT"
81
+ fi
82
+
83
+ # Method 3: Parse from stdin (last resort - experimental)
84
+ if [[ -z "$FILE_PATH" ]] && [[ ! -t 0 ]]; then
85
+ # Read stdin and try to extract file_path
86
+ STDIN_DATA=$(cat 2>/dev/null || echo "")
87
+ if [[ -n "$STDIN_DATA" ]] && command -v jq &> /dev/null; then
88
+ FILE_PATH=$(echo "$STDIN_DATA" | jq -r '.file_path // empty' 2>/dev/null || echo "")
89
+ fi
90
+ fi
91
+
92
+ # ============================================================================
93
+ # TIER 2: Signal Detection and Validation
94
+ # ============================================================================
95
+
96
+ # Log what we detected (for debugging PreToolUse effectiveness)
97
+ if [[ -n "$FILE_PATH" ]]; then
98
+ echo "[$(date)] pre-edit-spec: Detected file_path: $FILE_PATH" >> "$DEBUG_LOG" 2>/dev/null || true
99
+ else
100
+ echo "[$(date)] pre-edit-spec: No file_path detected (will fall back to Tier 1)" >> "$DEBUG_LOG" 2>/dev/null || true
101
+ exit 0 # Let PostToolUse handle it with mtime fallback
102
+ fi
103
+
104
+ # Check if this is a spec.md or tasks.md file in increments folder
105
+ IS_SPEC_FILE=false
106
+ if [[ "$FILE_PATH" == *"/spec.md" ]] || [[ "$FILE_PATH" == *"/tasks.md" ]]; then
107
+ if [[ "$FILE_PATH" == *"/.specweave/increments/"* ]]; then
108
+ # Exclude archived increments
109
+ if [[ "$FILE_PATH" != *"/_archive/"* ]]; then
110
+ IS_SPEC_FILE=true
111
+ fi
112
+ fi
113
+ fi
114
+
115
+ # If not a spec/tasks file, exit silently (no signal to PostToolUse)
116
+ if [[ "$IS_SPEC_FILE" != "true" ]]; then
117
+ echo "[$(date)] pre-edit-spec: Not a spec/tasks file - no signal" >> "$DEBUG_LOG" 2>/dev/null || true
118
+ exit 0
119
+ fi
120
+
121
+ # ============================================================================
122
+ # TIER 2: Write Signal for PostToolUse Hook
123
+ # ============================================================================
124
+
125
+ # Write file path to pending file for PostToolUse to consume
126
+ echo "$FILE_PATH" > "$PENDING_FILE" 2>/dev/null || true
127
+
128
+ echo "[$(date)] pre-edit-spec: Signaled PostToolUse for: $FILE_PATH" >> "$DEBUG_LOG" 2>/dev/null || true
129
+
130
+ # ============================================================================
131
+ # TIER 2: Metrics Collection
132
+ # ============================================================================
133
+
134
+ # Record metrics (JSONL format - one JSON object per line)
135
+ TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
136
+ METRIC_ENTRY="{\"timestamp\":\"$TIMESTAMP\",\"hook\":\"pre-edit-spec\",\"event\":\"file_detected\",\"file_path\":\"$FILE_PATH\",\"method\":\"TOOL_USE_ARGS\"}"
137
+
138
+ # Append to metrics file (JSONL)
139
+ echo "$METRIC_ENTRY" >> "$METRICS_FILE" 2>/dev/null || true
140
+
141
+ # Log rotation for metrics (keep last 1000 entries)
142
+ if [[ -f "$METRICS_FILE" ]]; then
143
+ LINE_COUNT=$(wc -l < "$METRICS_FILE" 2>/dev/null || echo 0)
144
+ if (( LINE_COUNT > 1000 )); then
145
+ tail -1000 "$METRICS_FILE" > "$METRICS_FILE.tmp" 2>/dev/null || true
146
+ mv "$METRICS_FILE.tmp" "$METRICS_FILE" 2>/dev/null || true
147
+ fi
148
+ fi
149
+
150
+ # ALWAYS exit 0 - NEVER let hook errors crash Claude Code
151
+ exit 0
@@ -92,15 +92,14 @@ fi
92
92
  # DETECT CURRENT INCREMENT
93
93
  # ============================================================================
94
94
 
95
- CURRENT_INCREMENT=$(ls -t .specweave/increments/ 2>/dev/null | grep -v "_backlog" | grep -v "_archive" | head -1)
95
+ CURRENT_INCREMENT=$(ls -td .specweave/increments/*/ 2>/dev/null | xargs -n1 basename | grep -v "_backlog" | grep -v "_archive" | grep -v "_working" | head -1)
96
96
 
97
97
  if [ -z "$CURRENT_INCREMENT" ]; then
98
- echo "[$(date)] ⚠️ No active increment found, skipping validation" >> "$DEBUG_LOG" 2>/dev/null || true
98
+ echo "[$(date)] ℹ️ No active increment found, skipping validation" >> "$DEBUG_LOG" 2>/dev/null || true
99
99
  rm -f "$STDIN_DATA"
100
100
  cat <<EOF
101
101
  {
102
- "continue": true,
103
- "systemMessage": "⚠️ Warning: No active increment found. Task completion validation skipped."
102
+ "continue": true
104
103
  }
105
104
  EOF
106
105
  exit 0
@@ -109,12 +108,11 @@ fi
109
108
  TASKS_MD=".specweave/increments/$CURRENT_INCREMENT/tasks.md"
110
109
 
111
110
  if [ ! -f "$TASKS_MD" ]; then
112
- echo "[$(date)] ⚠️ tasks.md not found for $CURRENT_INCREMENT" >> "$DEBUG_LOG" 2>/dev/null || true
111
+ echo "[$(date)] ℹ️ tasks.md not found for $CURRENT_INCREMENT (increment may be in planning stage)" >> "$DEBUG_LOG" 2>/dev/null || true
113
112
  rm -f "$STDIN_DATA"
114
113
  cat <<EOF
115
114
  {
116
- "continue": true,
117
- "systemMessage": "⚠️ Warning: tasks.md not found. Task completion validation skipped."
115
+ "continue": true
118
116
  }
119
117
  EOF
120
118
  exit 0
@@ -0,0 +1,151 @@
1
+ #!/bin/bash
2
+ #
3
+ # Pre-Write Hook: Capture File Path BEFORE Write Executes (Tier 2)
4
+ #
5
+ # Purpose: Detect which file will be written BEFORE the Write tool runs
6
+ # Strategy: PreToolUse has better access to tool arguments than PostToolUse
7
+ #
8
+ # TIER 2 COORDINATION:
9
+ # 1. Extract file_path from TOOL_USE_ARGS (more reliable in PreToolUse)
10
+ # 2. If it's spec.md/tasks.md in increments folder, signal PostToolUse hook
11
+ # 3. Write file path to .pending-status-update for PostToolUse to consume
12
+ #
13
+ # This eliminates the need for mtime-based fallback detection (Tier 1)
14
+ # and reduces false positives to near zero.
15
+ #
16
+ # Architecture:
17
+ # PreToolUse:Write → pre-write-spec.sh (this file)
18
+ # ↓ writes to
19
+ # .specweave/state/.pending-status-update
20
+ # ↓ read by
21
+ # PostToolUse:Write → post-write-spec.sh
22
+ #
23
+ # Version: v0.24.3 (EMERGENCY FIXES)
24
+ # Date: 2025-11-22
25
+
26
+ # EMERGENCY FIX: Remove set -e - it causes Claude Code crashes!
27
+ set +e
28
+
29
+ # EMERGENCY KILL SWITCH: Disable all hooks if env variable set
30
+ if [[ "${SPECWEAVE_DISABLE_HOOKS:-0}" == "1" ]]; then
31
+ exit 0
32
+ fi
33
+
34
+ # Find project root
35
+ find_project_root() {
36
+ local dir="$PWD"
37
+ while [[ "$dir" != "/" ]]; do
38
+ if [[ -d "$dir/.specweave" ]]; then
39
+ echo "$dir"
40
+ return 0
41
+ fi
42
+ dir=$(dirname "$dir")
43
+ done
44
+ echo "$PWD"
45
+ }
46
+
47
+ PROJECT_ROOT=$(find_project_root)
48
+ STATE_DIR="$PROJECT_ROOT/.specweave/state"
49
+ LOGS_DIR="$PROJECT_ROOT/.specweave/logs"
50
+ DEBUG_LOG="$LOGS_DIR/hooks-debug.log"
51
+ PENDING_FILE="$STATE_DIR/.pending-status-update"
52
+ METRICS_FILE="$STATE_DIR/hook-metrics.jsonl"
53
+
54
+ # Ensure directories exist
55
+ mkdir -p "$STATE_DIR" "$LOGS_DIR" 2>/dev/null || true
56
+
57
+ # ============================================================================
58
+ # TIER 2: Extract File Path from Tool Arguments
59
+ # ============================================================================
60
+ # PreToolUse should have access to tool arguments BEFORE execution
61
+ # Try multiple methods to extract file_path
62
+
63
+ FILE_PATH=""
64
+
65
+ # Method 1: TOOL_USE_ARGS environment variable (primary for PreToolUse)
66
+ if [[ -n "${TOOL_USE_ARGS:-}" ]]; then
67
+ # Try to parse JSON with jq if available
68
+ if command -v jq &> /dev/null; then
69
+ FILE_PATH=$(echo "$TOOL_USE_ARGS" | jq -r '.file_path // empty' 2>/dev/null || echo "")
70
+ fi
71
+
72
+ # Fallback: Regex extraction if jq not available or failed
73
+ if [[ -z "$FILE_PATH" ]]; then
74
+ FILE_PATH=$(echo "$TOOL_USE_ARGS" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)".*/\1/' || echo "")
75
+ fi
76
+ fi
77
+
78
+ # Method 2: TOOL_USE_CONTENT (fallback)
79
+ if [[ -z "$FILE_PATH" ]] && [[ -n "${TOOL_USE_CONTENT:-}" ]]; then
80
+ FILE_PATH="$TOOL_USE_CONTENT"
81
+ fi
82
+
83
+ # Method 3: Parse from stdin (last resort - experimental)
84
+ if [[ -z "$FILE_PATH" ]] && [[ ! -t 0 ]]; then
85
+ # Read stdin and try to extract file_path
86
+ STDIN_DATA=$(cat 2>/dev/null || echo "")
87
+ if [[ -n "$STDIN_DATA" ]] && command -v jq &> /dev/null; then
88
+ FILE_PATH=$(echo "$STDIN_DATA" | jq -r '.file_path // empty' 2>/dev/null || echo "")
89
+ fi
90
+ fi
91
+
92
+ # ============================================================================
93
+ # TIER 2: Signal Detection and Validation
94
+ # ============================================================================
95
+
96
+ # Log what we detected (for debugging PreToolUse effectiveness)
97
+ if [[ -n "$FILE_PATH" ]]; then
98
+ echo "[$(date)] pre-write-spec: Detected file_path: $FILE_PATH" >> "$DEBUG_LOG" 2>/dev/null || true
99
+ else
100
+ echo "[$(date)] pre-write-spec: No file_path detected (will fall back to Tier 1)" >> "$DEBUG_LOG" 2>/dev/null || true
101
+ exit 0 # Let PostToolUse handle it with mtime fallback
102
+ fi
103
+
104
+ # Check if this is a spec.md or tasks.md file in increments folder
105
+ IS_SPEC_FILE=false
106
+ if [[ "$FILE_PATH" == *"/spec.md" ]] || [[ "$FILE_PATH" == *"/tasks.md" ]]; then
107
+ if [[ "$FILE_PATH" == *"/.specweave/increments/"* ]]; then
108
+ # Exclude archived increments
109
+ if [[ "$FILE_PATH" != *"/_archive/"* ]]; then
110
+ IS_SPEC_FILE=true
111
+ fi
112
+ fi
113
+ fi
114
+
115
+ # If not a spec/tasks file, exit silently (no signal to PostToolUse)
116
+ if [[ "$IS_SPEC_FILE" != "true" ]]; then
117
+ echo "[$(date)] pre-write-spec: Not a spec/tasks file - no signal" >> "$DEBUG_LOG" 2>/dev/null || true
118
+ exit 0
119
+ fi
120
+
121
+ # ============================================================================
122
+ # TIER 2: Write Signal for PostToolUse Hook
123
+ # ============================================================================
124
+
125
+ # Write file path to pending file for PostToolUse to consume
126
+ echo "$FILE_PATH" > "$PENDING_FILE" 2>/dev/null || true
127
+
128
+ echo "[$(date)] pre-write-spec: Signaled PostToolUse for: $FILE_PATH" >> "$DEBUG_LOG" 2>/dev/null || true
129
+
130
+ # ============================================================================
131
+ # TIER 2: Metrics Collection
132
+ # ============================================================================
133
+
134
+ # Record metrics (JSONL format - one JSON object per line)
135
+ TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
136
+ METRIC_ENTRY="{\"timestamp\":\"$TIMESTAMP\",\"hook\":\"pre-write-spec\",\"event\":\"file_detected\",\"file_path\":\"$FILE_PATH\",\"method\":\"TOOL_USE_ARGS\"}"
137
+
138
+ # Append to metrics file (JSONL)
139
+ echo "$METRIC_ENTRY" >> "$METRICS_FILE" 2>/dev/null || true
140
+
141
+ # Log rotation for metrics (keep last 1000 entries)
142
+ if [[ -f "$METRICS_FILE" ]]; then
143
+ LINE_COUNT=$(wc -l < "$METRICS_FILE" 2>/dev/null || echo 0)
144
+ if (( LINE_COUNT > 1000 )); then
145
+ tail -1000 "$METRICS_FILE" > "$METRICS_FILE.tmp" 2>/dev/null || true
146
+ mv "$METRICS_FILE.tmp" "$METRICS_FILE" 2>/dev/null || true
147
+ fi
148
+ fi
149
+
150
+ # ALWAYS exit 0 - NEVER let hook errors crash Claude Code
151
+ exit 0