specweave 0.23.10 → 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 (102) hide show
  1. package/.claude-plugin/marketplace.json +7 -7
  2. package/CLAUDE.md +384 -1449
  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/helpers/async-project-loader.d.ts +148 -0
  10. package/dist/src/cli/helpers/async-project-loader.d.ts.map +1 -0
  11. package/dist/src/cli/helpers/async-project-loader.js +351 -0
  12. package/dist/src/cli/helpers/async-project-loader.js.map +1 -0
  13. package/dist/src/cli/helpers/cancelation-handler.d.ts +123 -0
  14. package/dist/src/cli/helpers/cancelation-handler.d.ts.map +1 -0
  15. package/dist/src/cli/helpers/cancelation-handler.js +187 -0
  16. package/dist/src/cli/helpers/cancelation-handler.js.map +1 -0
  17. package/dist/src/cli/helpers/import-strategy-prompter.d.ts +43 -0
  18. package/dist/src/cli/helpers/import-strategy-prompter.d.ts.map +1 -0
  19. package/dist/src/cli/helpers/import-strategy-prompter.js +136 -0
  20. package/dist/src/cli/helpers/import-strategy-prompter.js.map +1 -0
  21. package/dist/src/cli/helpers/issue-tracker/ado.d.ts +5 -2
  22. package/dist/src/cli/helpers/issue-tracker/ado.d.ts.map +1 -1
  23. package/dist/src/cli/helpers/issue-tracker/ado.js +90 -40
  24. package/dist/src/cli/helpers/issue-tracker/ado.js.map +1 -1
  25. package/dist/src/cli/helpers/issue-tracker/jira.d.ts +2 -1
  26. package/dist/src/cli/helpers/issue-tracker/jira.d.ts.map +1 -1
  27. package/dist/src/cli/helpers/issue-tracker/jira.js +120 -35
  28. package/dist/src/cli/helpers/issue-tracker/jira.js.map +1 -1
  29. package/dist/src/cli/helpers/progress-tracker.d.ts +121 -0
  30. package/dist/src/cli/helpers/progress-tracker.d.ts.map +1 -0
  31. package/dist/src/cli/helpers/progress-tracker.js +202 -0
  32. package/dist/src/cli/helpers/progress-tracker.js.map +1 -0
  33. package/dist/src/cli/helpers/project-count-fetcher.d.ts +69 -0
  34. package/dist/src/cli/helpers/project-count-fetcher.d.ts.map +1 -0
  35. package/dist/src/cli/helpers/project-count-fetcher.js +173 -0
  36. package/dist/src/cli/helpers/project-count-fetcher.js.map +1 -0
  37. package/dist/src/config/types.d.ts +14 -14
  38. package/dist/src/core/cache/cache-manager.d.ts +119 -0
  39. package/dist/src/core/cache/cache-manager.d.ts.map +1 -0
  40. package/dist/src/core/cache/cache-manager.js +304 -0
  41. package/dist/src/core/cache/cache-manager.js.map +1 -0
  42. package/dist/src/core/cache/rate-limit-checker.d.ts +92 -0
  43. package/dist/src/core/cache/rate-limit-checker.d.ts.map +1 -0
  44. package/dist/src/core/cache/rate-limit-checker.js +160 -0
  45. package/dist/src/core/cache/rate-limit-checker.js.map +1 -0
  46. package/dist/src/core/progress/cancelation-handler.d.ts +79 -0
  47. package/dist/src/core/progress/cancelation-handler.d.ts.map +1 -0
  48. package/dist/src/core/progress/cancelation-handler.js +111 -0
  49. package/dist/src/core/progress/cancelation-handler.js.map +1 -0
  50. package/dist/src/core/progress/import-state.d.ts +71 -0
  51. package/dist/src/core/progress/import-state.d.ts.map +1 -0
  52. package/dist/src/core/progress/import-state.js +96 -0
  53. package/dist/src/core/progress/import-state.js.map +1 -0
  54. package/dist/src/core/progress/progress-tracker.d.ts +139 -0
  55. package/dist/src/core/progress/progress-tracker.d.ts.map +1 -0
  56. package/dist/src/core/progress/progress-tracker.js +223 -0
  57. package/dist/src/core/progress/progress-tracker.js.map +1 -0
  58. package/dist/src/init/architecture/types.d.ts +6 -6
  59. package/dist/src/integrations/ado/ado-client.d.ts +25 -0
  60. package/dist/src/integrations/ado/ado-client.d.ts.map +1 -1
  61. package/dist/src/integrations/ado/ado-client.js +67 -0
  62. package/dist/src/integrations/ado/ado-client.js.map +1 -1
  63. package/dist/src/integrations/ado/ado-dependency-loader.d.ts +99 -0
  64. package/dist/src/integrations/ado/ado-dependency-loader.d.ts.map +1 -0
  65. package/dist/src/integrations/ado/ado-dependency-loader.js +207 -0
  66. package/dist/src/integrations/ado/ado-dependency-loader.js.map +1 -0
  67. package/dist/src/integrations/jira/jira-client.d.ts +32 -0
  68. package/dist/src/integrations/jira/jira-client.d.ts.map +1 -1
  69. package/dist/src/integrations/jira/jira-client.js +81 -0
  70. package/dist/src/integrations/jira/jira-client.js.map +1 -1
  71. package/dist/src/integrations/jira/jira-dependency-loader.d.ts +101 -0
  72. package/dist/src/integrations/jira/jira-dependency-loader.d.ts.map +1 -0
  73. package/dist/src/integrations/jira/jira-dependency-loader.js +200 -0
  74. package/dist/src/integrations/jira/jira-dependency-loader.js.map +1 -0
  75. package/package.json +1 -1
  76. package/plugins/specweave/.claude-plugin/plugin.json +20 -0
  77. package/plugins/specweave/agents/architect/AGENT.md +100 -602
  78. package/plugins/specweave/agents/pm/AGENT.md +96 -597
  79. package/plugins/specweave/agents/pm/AGENT.md.bak +1893 -0
  80. package/plugins/specweave/agents/pm/AGENT.md.bak2 +1754 -0
  81. package/plugins/specweave/commands/check-hooks.md +257 -0
  82. package/plugins/specweave/hooks/post-edit-spec.sh +202 -31
  83. package/plugins/specweave/hooks/post-task-completion.sh +225 -228
  84. package/plugins/specweave/hooks/post-write-spec.sh +207 -31
  85. package/plugins/specweave/hooks/pre-edit-spec.sh +151 -0
  86. package/plugins/specweave/hooks/pre-task-completion.sh +5 -7
  87. package/plugins/specweave/hooks/pre-write-spec.sh +151 -0
  88. package/plugins/specweave/hooks/test-pretooluse-env.sh +72 -0
  89. package/plugins/specweave/skills/compliance-architecture/SKILL.md +374 -0
  90. package/plugins/specweave/skills/external-sync-wizard/SKILL.md +610 -0
  91. package/plugins/specweave/skills/pm-closure-validation/SKILL.md +541 -0
  92. package/plugins/specweave/skills/roadmap-planner/SKILL.md +473 -0
  93. package/plugins/specweave-ado/commands/refresh-cache.js +25 -0
  94. package/plugins/specweave-ado/commands/refresh-cache.ts +40 -0
  95. package/plugins/specweave-ado/hooks/post-task-completion.sh +1 -1
  96. package/plugins/specweave-github/hooks/post-task-completion.sh +1 -1
  97. package/plugins/specweave-jira/commands/refresh-cache.js +25 -0
  98. package/plugins/specweave-jira/commands/refresh-cache.ts +40 -0
  99. package/plugins/specweave-jira/hooks/post-task-completion.sh +1 -1
  100. package/plugins/specweave-kafka-streams/commands/topology.md +437 -0
  101. package/plugins/specweave-n8n/commands/workflow-template.md +262 -0
  102. package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +228 -6465
@@ -26,7 +26,20 @@
26
26
  #
27
27
  # DEBOUNCING: Prevents duplicate fires (Claude Code calls hooks twice)
28
28
 
29
- set -e
29
+ # EMERGENCY FIXES (v0.24.3): CRITICAL SAFETY FIRST
30
+ # - Remove set -e completely to prevent any errors from propagating
31
+ # - Kill switch: SPECWEAVE_DISABLE_HOOKS=1 disables all hooks
32
+ # - Circuit breaker: Auto-disable after 3 consecutive failures
33
+ # - File locking: Only 1 instance can run at a time
34
+ # - Aggressive debouncing: 5 seconds (was 5s, keeping it)
35
+ # - Complete error isolation: ALL background work wrapped
36
+
37
+ set +e # NEVER use set -e in hooks - it causes crashes
38
+
39
+ # EMERGENCY KILL SWITCH FIRST (before anything else)
40
+ if [[ "${SPECWEAVE_DISABLE_HOOKS:-0}" == "1" ]]; then
41
+ exit 0
42
+ fi
30
43
 
31
44
  # Find project root by searching upward for .specweave/ directory
32
45
  # Works regardless of where hook is installed (source or .claude/hooks/)
@@ -50,16 +63,64 @@ find_project_root() {
50
63
  PROJECT_ROOT="$(find_project_root "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")"
51
64
  cd "$PROJECT_ROOT" 2>/dev/null || true
52
65
 
66
+ # ============================================================================
67
+ # EMERGENCY SAFETY CHECKS
68
+ # ============================================================================
69
+
70
+ # CIRCUIT BREAKER: Auto-disable after consecutive failures
71
+ CIRCUIT_BREAKER_FILE=".specweave/state/.hook-circuit-breaker"
72
+ CIRCUIT_BREAKER_THRESHOLD=3
73
+
74
+ mkdir -p ".specweave/state" 2>/dev/null || true
75
+
76
+ if [[ -f "$CIRCUIT_BREAKER_FILE" ]]; then
77
+ FAILURE_COUNT=$(cat "$CIRCUIT_BREAKER_FILE" 2>/dev/null || echo 0)
78
+ if (( FAILURE_COUNT >= CIRCUIT_BREAKER_THRESHOLD )); then
79
+ # Circuit breaker is OPEN - hooks are disabled
80
+ exit 0
81
+ fi
82
+ fi
83
+
84
+ # FILE LOCK: Only allow 1 post-task-completion hook at a time
85
+ LOCK_FILE=".specweave/state/.hook-post-task.lock"
86
+ LOCK_TIMEOUT=10 # seconds (longer than others because this does more work)
87
+
88
+ LOCK_ACQUIRED=false
89
+ for i in {1..10}; do
90
+ if mkdir "$LOCK_FILE" 2>/dev/null; then
91
+ LOCK_ACQUIRED=true
92
+ trap 'rmdir "$LOCK_FILE" 2>/dev/null || true' EXIT
93
+ break
94
+ fi
95
+
96
+ # Check for stale lock
97
+ if [[ -d "$LOCK_FILE" ]]; then
98
+ LOCK_AGE=$(($(date +%s) - $(stat -f "%m" "$LOCK_FILE" 2>/dev/null || echo 0)))
99
+ if (( LOCK_AGE > LOCK_TIMEOUT )); then
100
+ rmdir "$LOCK_FILE" 2>/dev/null || true
101
+ continue
102
+ fi
103
+ fi
104
+
105
+ sleep 0.2
106
+ done
107
+
108
+ if [[ "$LOCK_ACQUIRED" == "false" ]]; then
109
+ # Another instance is running, skip
110
+ exit 0
111
+ fi
112
+
53
113
  # ============================================================================
54
114
  # CONFIGURATION
55
115
  # ============================================================================
56
116
 
57
117
  # Debounce window to prevent duplicate hook fires
58
- DEBOUNCE_SECONDS=2
118
+ # AGGRESSIVE: 5 seconds to prevent rapid-fire executions
119
+ DEBOUNCE_SECONDS=5
59
120
 
60
121
  # Inactivity threshold to detect session end
61
122
  # If gap between TodoWrite calls > this value, assume session is ending
62
- INACTIVITY_THRESHOLD=15 # seconds (configurable)
123
+ INACTIVITY_THRESHOLD=120 # seconds (2 minutes - increased from 15s to reduce false positives)
63
124
 
64
125
  # File paths
65
126
  LOGS_DIR=".specweave/logs"
@@ -70,6 +131,13 @@ TASKS_LOG="$LOGS_DIR/tasks.log"
70
131
 
71
132
  mkdir -p "$LOGS_DIR" 2>/dev/null || true
72
133
 
134
+ # Log rotation: Keep tasks.log under 100KB (keep last 200 lines)
135
+ if [[ -f "$TASKS_LOG" ]] && [[ $(wc -c < "$TASKS_LOG" 2>/dev/null || echo 0) -gt 102400 ]]; then
136
+ tail -200 "$TASKS_LOG" > "$TASKS_LOG.tmp" 2>/dev/null || true
137
+ mv "$TASKS_LOG.tmp" "$TASKS_LOG" 2>/dev/null || true
138
+ echo "[$(date)] Log rotated (was >100KB)" >> "$TASKS_LOG" 2>/dev/null || true
139
+ fi
140
+
73
141
  # ============================================================================
74
142
  # DEBOUNCING
75
143
  # ============================================================================
@@ -173,287 +241,223 @@ else
173
241
  fi
174
242
 
175
243
  # ============================================================================
176
- # UPDATE TASKS.MD (NEW in v0.6.1)
244
+ # CONSOLIDATED BACKGROUND WORK (ALL I/O OPERATIONS IN SINGLE PROCESS)
177
245
  # ============================================================================
246
+ # EMERGENCY FIX: Instead of spawning 6+ separate Node.js processes, consolidate
247
+ # ALL background work into a single background job with complete error isolation
248
+ # This prevents process exhaustion that causes Claude Code crashes
178
249
 
179
- if command -v node &> /dev/null; then
180
- # Detect current increment (latest non-backlog increment)
181
- CURRENT_INCREMENT=$(ls -t .specweave/increments/ 2>/dev/null | grep -v "_backlog" | grep -v "_archive" | grep -v "_working" | head -1)
250
+ (
251
+ set +e # Disable error propagation in background job
182
252
 
183
- if [ -n "$CURRENT_INCREMENT" ] && [ -f ".specweave/increments/$CURRENT_INCREMENT/tasks.md" ]; then
184
- echo "[$(date)] 📝 Updating tasks.md for $CURRENT_INCREMENT" >> "$DEBUG_LOG" 2>/dev/null || true
253
+ # Detect current increment ONCE
254
+ CURRENT_INCREMENT=$(ls -td .specweave/increments/*/ 2>/dev/null | xargs -n1 basename | grep -v "_backlog" | grep -v "_archive" | grep -v "_working" | head -1)
185
255
 
186
- # Run task updater (non-blocking, best-effort)
187
- node ${CLAUDE_PLUGIN_ROOT}/lib/hooks/update-tasks-md.js "$CURRENT_INCREMENT" 2>&1 | tee -a "$DEBUG_LOG" >/dev/null || {
188
- echo "[$(date)] ⚠️ Failed to update tasks.md (non-blocking)" >> "$DEBUG_LOG" 2>/dev/null || true
189
- }
190
- else
191
- echo "[$(date)] ℹ️ No current increment or tasks.md found, skipping tasks.md update" >> "$DEBUG_LOG" 2>/dev/null || true
256
+ if [[ -z "$CURRENT_INCREMENT" ]]; then
257
+ echo "[$(date)] No active increment, skipping all background work" >> "$DEBUG_LOG" 2>/dev/null || true
258
+ echo "0" > "$CIRCUIT_BREAKER_FILE" 2>/dev/null || true # Reset on success
259
+ exit 0
192
260
  fi
193
- else
194
- echo "[$(date)] ⚠️ Node.js not found, skipping tasks.md update" >> "$DEBUG_LOG" 2>/dev/null || true
195
- fi
196
261
 
197
- # ============================================================================
198
- # SYNC LIVING DOCS (NEW in v0.6.1)
199
- # ============================================================================
262
+ echo "[$(date)] Starting consolidated background work for $CURRENT_INCREMENT" >> "$DEBUG_LOG" 2>/dev/null || true
200
263
 
201
- if command -v node &> /dev/null; then
202
- if [ -n "$CURRENT_INCREMENT" ]; then
203
- # ========================================================================
204
- # SKIP ARCHIVED INCREMENTS (CRITICAL - prevents folder recreation)
205
- # ========================================================================
206
- # If increment is archived, skip living docs sync entirely
207
- # This prevents recreating archived feature folders in active location
208
- # See: ULTRATHINK-ARCHIVE-REORGANIZATION-BUG.md for full analysis
209
- if [ -d ".specweave/increments/_archive/$CURRENT_INCREMENT" ]; then
210
- echo "[$(date)] ⏭️ Skipping living docs sync for archived increment $CURRENT_INCREMENT" >> "$DEBUG_LOG" 2>/dev/null || true
211
- else
212
- echo "[$(date)] 📚 Checking living docs sync for $CURRENT_INCREMENT" >> "$DEBUG_LOG" 2>/dev/null || true
264
+ # Only proceed if Node.js is available
265
+ if ! command -v node &> /dev/null; then
266
+ echo "[$(date)] Node.js not found, skipping background work" >> "$DEBUG_LOG" 2>/dev/null || true
267
+ echo "0" > "$CIRCUIT_BREAKER_FILE" 2>/dev/null || true
268
+ exit 0
269
+ fi
270
+
271
+ # Track if ANY operation succeeded (for circuit breaker)
272
+ ANY_SUCCESS=false
273
+
274
+ # ============================================================================
275
+ # 1. UPDATE TASKS.MD
276
+ # ============================================================================
277
+ if [ -f ".specweave/increments/$CURRENT_INCREMENT/tasks.md" ]; then
278
+ echo "[$(date)] 📝 Updating tasks.md" >> "$DEBUG_LOG" 2>/dev/null || true
279
+
280
+ UPDATE_TASKS_SCRIPT=""
281
+ if [ -f "$PROJECT_ROOT/plugins/specweave/lib/hooks/update-tasks-md.js" ]; then
282
+ UPDATE_TASKS_SCRIPT="$PROJECT_ROOT/plugins/specweave/lib/hooks/update-tasks-md.js"
283
+ elif [ -f "$PROJECT_ROOT/dist/plugins/specweave/lib/hooks/update-tasks-md.js" ]; then
284
+ UPDATE_TASKS_SCRIPT="$PROJECT_ROOT/dist/plugins/specweave/lib/hooks/update-tasks-md.js"
285
+ elif [ -f "$PROJECT_ROOT/node_modules/specweave/dist/plugins/specweave/lib/hooks/update-tasks-md.js" ]; then
286
+ UPDATE_TASKS_SCRIPT="$PROJECT_ROOT/node_modules/specweave/dist/plugins/specweave/lib/hooks/update-tasks-md.js"
287
+ elif [ -n "${CLAUDE_PLUGIN_ROOT:-}" ] && [ -f "${CLAUDE_PLUGIN_ROOT}/lib/hooks/update-tasks-md.js" ]; then
288
+ UPDATE_TASKS_SCRIPT="${CLAUDE_PLUGIN_ROOT}/lib/hooks/update-tasks-md.js"
289
+ fi
290
+
291
+ if [ -n "$UPDATE_TASKS_SCRIPT" ]; then
292
+ if node "$UPDATE_TASKS_SCRIPT" "$CURRENT_INCREMENT" >> "$DEBUG_LOG" 2>&1; then
293
+ echo "[$(date)] ✅ tasks.md updated" >> "$DEBUG_LOG" 2>/dev/null || true
294
+ ANY_SUCCESS=true
295
+ else
296
+ echo "[$(date)] ⚠️ tasks.md update failed (non-blocking)" >> "$DEBUG_LOG" 2>/dev/null || true
297
+ fi
298
+ fi
299
+ fi
213
300
 
214
- # ========================================================================
215
- # EXTRACT FEATURE ID (NEW in v0.23.0 - Increment 0047: US-Task Linkage)
216
- # ========================================================================
217
- # Extract epic field from spec.md frontmatter (e.g., epic: FS-047)
218
- # This ensures sync uses the explicitly defined feature ID rather than
219
- # auto-generating one, maintaining traceability with living docs structure.
301
+ # ============================================================================
302
+ # 2. SYNC LIVING DOCS
303
+ # ============================================================================
304
+ # Skip if increment is archived
305
+ if [ ! -d ".specweave/increments/_archive/$CURRENT_INCREMENT" ]; then
306
+ echo "[$(date)] 📚 Syncing living docs" >> "$DEBUG_LOG" 2>/dev/null || true
220
307
 
308
+ # Extract feature ID and project ID
221
309
  FEATURE_ID=""
222
310
  SPEC_MD_PATH=".specweave/increments/$CURRENT_INCREMENT/spec.md"
223
311
 
224
312
  if [ -f "$SPEC_MD_PATH" ]; then
225
- # Extract epic field from YAML frontmatter
226
- FEATURE_ID=$(awk '
227
- BEGIN { in_frontmatter=0 }
228
- /^---$/ {
229
- if (in_frontmatter == 0) {
230
- in_frontmatter=1; next
231
- } else {
232
- exit
233
- }
234
- }
235
- in_frontmatter == 1 && /^epic:/ {
236
- gsub(/^epic:[ \t]*/, "");
237
- gsub(/["'\'']/, "");
238
- print;
239
- exit
240
- }
241
- ' "$SPEC_MD_PATH" | tr -d '\r\n')
242
-
243
- if [ -n "$FEATURE_ID" ]; then
244
- echo "[$(date)] 📎 Extracted feature ID from spec.md: $FEATURE_ID" >> "$DEBUG_LOG" 2>/dev/null || true
245
- else
246
- echo "[$(date)] ⚠️ No epic field found in spec.md frontmatter" >> "$DEBUG_LOG" 2>/dev/null || true
247
- echo "[$(date)] ℹ️ Sync will auto-generate feature ID from increment number" >> "$DEBUG_LOG" 2>/dev/null || true
248
- fi
249
- else
250
- echo "[$(date)] ⚠️ spec.md not found at $SPEC_MD_PATH" >> "$DEBUG_LOG" 2>/dev/null || true
313
+ FEATURE_ID=$(awk 'BEGIN{in_fm=0}/^---$/{if(in_fm==0){in_fm=1;next}else{exit}}in_fm==1&&/^epic:/{gsub(/^epic:[ \t]*/,"");gsub(/["'\'']/,"");print;exit}' "$SPEC_MD_PATH" | tr -d '\r\n')
251
314
  fi
252
315
 
253
- # Extract project ID from config or metadata (defaults to "default")
254
316
  PROJECT_ID="default"
255
- if [ -f ".specweave/config.json" ]; then
256
- # Try to extract activeProject from config
257
- if command -v jq >/dev/null 2>&1; then
258
- ACTIVE_PROJECT=$(jq -r '.activeProject // "default"' ".specweave/config.json" 2>/dev/null || echo "default")
259
- if [ -n "$ACTIVE_PROJECT" ] && [ "$ACTIVE_PROJECT" != "null" ]; then
260
- PROJECT_ID="$ACTIVE_PROJECT"
261
- fi
317
+ if [ -f ".specweave/config.json" ] && command -v jq >/dev/null 2>&1; then
318
+ ACTIVE_PROJECT=$(jq -r '.activeProject // "default"' ".specweave/config.json" 2>/dev/null || echo "default")
319
+ if [ -n "$ACTIVE_PROJECT" ] && [ "$ACTIVE_PROJECT" != "null" ]; then
320
+ PROJECT_ID="$ACTIVE_PROJECT"
262
321
  fi
263
322
  fi
264
- echo "[$(date)] 📁 Project ID: $PROJECT_ID" >> "$DEBUG_LOG" 2>/dev/null || true
265
323
 
266
- # Determine which sync script to use (project local or global)
324
+ # Find sync script
267
325
  SYNC_SCRIPT=""
268
326
  if [ -f "$PROJECT_ROOT/plugins/specweave/lib/hooks/sync-living-docs.js" ]; then
269
- # Development: Use in-place compiled hooks (esbuild, not tsc)
270
327
  SYNC_SCRIPT="$PROJECT_ROOT/plugins/specweave/lib/hooks/sync-living-docs.js"
271
- echo "[$(date)] Using in-place compiled hook: $SYNC_SCRIPT" >> "$DEBUG_LOG" 2>/dev/null || true
272
328
  elif [ -f "$PROJECT_ROOT/dist/plugins/specweave/lib/hooks/sync-living-docs.js" ]; then
273
- # Development: Use project's compiled files (has node_modules)
274
329
  SYNC_SCRIPT="$PROJECT_ROOT/dist/plugins/specweave/lib/hooks/sync-living-docs.js"
275
- echo "[$(date)] Using local dist: $SYNC_SCRIPT" >> "$DEBUG_LOG" 2>/dev/null || true
276
330
  elif [ -f "$PROJECT_ROOT/node_modules/specweave/dist/plugins/specweave/lib/hooks/sync-living-docs.js" ]; then
277
- # Installed as dependency: Use node_modules version
278
331
  SYNC_SCRIPT="$PROJECT_ROOT/node_modules/specweave/dist/plugins/specweave/lib/hooks/sync-living-docs.js"
279
- echo "[$(date)] Using node_modules: $SYNC_SCRIPT" >> "$DEBUG_LOG" 2>/dev/null || true
280
332
  elif [ -n "${CLAUDE_PLUGIN_ROOT}" ] && [ -f "${CLAUDE_PLUGIN_ROOT}/lib/hooks/sync-living-docs.js" ]; then
281
- # Fallback: Plugin marketplace (may fail if deps missing)
282
333
  SYNC_SCRIPT="${CLAUDE_PLUGIN_ROOT}/lib/hooks/sync-living-docs.js"
283
- echo "[$(date)] Using plugin marketplace: $SYNC_SCRIPT" >> "$DEBUG_LOG" 2>/dev/null || true
284
334
  fi
285
335
 
286
336
  if [ -n "$SYNC_SCRIPT" ]; then
287
- # Run living docs sync with feature ID (non-blocking, best-effort)
288
- # Pass FEATURE_ID and PROJECT_ID as environment variables if available
289
337
  if [ -n "$FEATURE_ID" ]; then
290
- (cd "$PROJECT_ROOT" && FEATURE_ID="$FEATURE_ID" PROJECT_ID="$PROJECT_ID" node "$SYNC_SCRIPT" "$CURRENT_INCREMENT") 2>&1 | tee -a "$DEBUG_LOG" >/dev/null || {
291
- echo "[$(date)] ⚠️ Failed to sync living docs (non-blocking)" >> "$DEBUG_LOG" 2>/dev/null || true
292
- }
338
+ if (cd "$PROJECT_ROOT" && FEATURE_ID="$FEATURE_ID" PROJECT_ID="$PROJECT_ID" node "$SYNC_SCRIPT" "$CURRENT_INCREMENT") >> "$DEBUG_LOG" 2>&1; then
339
+ echo "[$(date)] Living docs synced" >> "$DEBUG_LOG" 2>/dev/null || true
340
+ ANY_SUCCESS=true
341
+ else
342
+ echo "[$(date)] ⚠️ Living docs sync failed (non-blocking)" >> "$DEBUG_LOG" 2>/dev/null || true
343
+ fi
293
344
  else
294
- # No explicit feature ID - sync will auto-generate
295
- (cd "$PROJECT_ROOT" && PROJECT_ID="$PROJECT_ID" node "$SYNC_SCRIPT" "$CURRENT_INCREMENT") 2>&1 | tee -a "$DEBUG_LOG" >/dev/null || {
296
- echo "[$(date)] ⚠️ Failed to sync living docs (non-blocking)" >> "$DEBUG_LOG" 2>/dev/null || true
297
- }
345
+ if (cd "$PROJECT_ROOT" && PROJECT_ID="$PROJECT_ID" node "$SYNC_SCRIPT" "$CURRENT_INCREMENT") >> "$DEBUG_LOG" 2>&1; then
346
+ echo "[$(date)] Living docs synced" >> "$DEBUG_LOG" 2>/dev/null || true
347
+ ANY_SUCCESS=true
348
+ else
349
+ echo "[$(date)] ⚠️ Living docs sync failed (non-blocking)" >> "$DEBUG_LOG" 2>/dev/null || true
350
+ fi
298
351
  fi
299
- else
300
- echo "[$(date)] ⚠️ sync-living-docs.js not found in any location" >> "$DEBUG_LOG" 2>/dev/null || true
301
352
  fi
302
- fi # Close the archive check 'else' block
303
353
  fi
304
- fi
305
354
 
306
- # ============================================================================
307
- # UPDATE AC STATUS (NEW in v0.18.3 - Acceptance Criteria Checkbox Update)
308
- # ============================================================================
309
- # Updates acceptance criteria checkboxes in spec.md based on completed tasks
310
- # - Extracts AC-IDs from completed tasks (AC-US1-01, AC-US1-02, etc.)
311
- # - Checks off corresponding AC in spec.md
312
- # - Ensures external tool sync reflects current AC completion status
313
-
314
- if command -v node &> /dev/null; then
315
- if [ -n "$CURRENT_INCREMENT" ]; then
316
- echo "[$(date)] Updating AC status for $CURRENT_INCREMENT" >> "$DEBUG_LOG" 2>/dev/null || true
317
-
318
- # Determine which AC update script to use (project local or global)
319
- UPDATE_AC_SCRIPT=""
320
- if [ -f "$PROJECT_ROOT/plugins/specweave/lib/hooks/update-ac-status.js" ]; then
321
- # Development: Use in-place compiled hooks (esbuild, not tsc)
322
- UPDATE_AC_SCRIPT="$PROJECT_ROOT/plugins/specweave/lib/hooks/update-ac-status.js"
323
- echo "[$(date)] Using in-place compiled hook: $UPDATE_AC_SCRIPT" >> "$DEBUG_LOG" 2>/dev/null || true
324
- elif [ -f "$PROJECT_ROOT/dist/plugins/specweave/lib/hooks/update-ac-status.js" ]; then
325
- # Development: Use project's compiled files (has node_modules)
326
- UPDATE_AC_SCRIPT="$PROJECT_ROOT/dist/plugins/specweave/lib/hooks/update-ac-status.js"
327
- echo "[$(date)] Using local dist: $UPDATE_AC_SCRIPT" >> "$DEBUG_LOG" 2>/dev/null || true
328
- elif [ -f "$PROJECT_ROOT/node_modules/specweave/dist/plugins/specweave/lib/hooks/update-ac-status.js" ]; then
329
- # Installed as dependency: Use node_modules version
330
- UPDATE_AC_SCRIPT="$PROJECT_ROOT/node_modules/specweave/dist/plugins/specweave/lib/hooks/update-ac-status.js"
331
- echo "[$(date)] Using node_modules: $UPDATE_AC_SCRIPT" >> "$DEBUG_LOG" 2>/dev/null || true
332
- elif [ -n "${CLAUDE_PLUGIN_ROOT}" ] && [ -f "${CLAUDE_PLUGIN_ROOT}/lib/hooks/update-ac-status.js" ]; then
333
- # Fallback: Plugin marketplace (may fail if deps missing)
334
- UPDATE_AC_SCRIPT="${CLAUDE_PLUGIN_ROOT}/lib/hooks/update-ac-status.js"
335
- echo "[$(date)] Using plugin marketplace: $UPDATE_AC_SCRIPT" >> "$DEBUG_LOG" 2>/dev/null || true
336
- fi
355
+ # ============================================================================
356
+ # 3. UPDATE AC STATUS
357
+ # ============================================================================
358
+ echo "[$(date)] Updating AC status" >> "$DEBUG_LOG" 2>/dev/null || true
359
+
360
+ UPDATE_AC_SCRIPT=""
361
+ if [ -f "$PROJECT_ROOT/plugins/specweave/lib/hooks/update-ac-status.js" ]; then
362
+ UPDATE_AC_SCRIPT="$PROJECT_ROOT/plugins/specweave/lib/hooks/update-ac-status.js"
363
+ elif [ -f "$PROJECT_ROOT/dist/plugins/specweave/lib/hooks/update-ac-status.js" ]; then
364
+ UPDATE_AC_SCRIPT="$PROJECT_ROOT/dist/plugins/specweave/lib/hooks/update-ac-status.js"
365
+ elif [ -f "$PROJECT_ROOT/node_modules/specweave/dist/plugins/specweave/lib/hooks/update-ac-status.js" ]; then
366
+ UPDATE_AC_SCRIPT="$PROJECT_ROOT/node_modules/specweave/dist/plugins/specweave/lib/hooks/update-ac-status.js"
367
+ elif [ -n "${CLAUDE_PLUGIN_ROOT}" ] && [ -f "${CLAUDE_PLUGIN_ROOT}/lib/hooks/update-ac-status.js" ]; then
368
+ UPDATE_AC_SCRIPT="${CLAUDE_PLUGIN_ROOT}/lib/hooks/update-ac-status.js"
369
+ fi
337
370
 
338
- if [ -n "$UPDATE_AC_SCRIPT" ]; then
339
- # Run AC status update (non-blocking, best-effort)
340
- (cd "$PROJECT_ROOT" && node "$UPDATE_AC_SCRIPT" "$CURRENT_INCREMENT") 2>&1 | tee -a "$DEBUG_LOG" >/dev/null || {
341
- echo "[$(date)] ⚠️ Failed to update AC status (non-blocking)" >> "$DEBUG_LOG" 2>/dev/null || true
342
- }
371
+ if [ -n "$UPDATE_AC_SCRIPT" ]; then
372
+ if (cd "$PROJECT_ROOT" && node "$UPDATE_AC_SCRIPT" "$CURRENT_INCREMENT") >> "$DEBUG_LOG" 2>&1; then
373
+ echo "[$(date)] AC status updated" >> "$DEBUG_LOG" 2>/dev/null || true
374
+ ANY_SUCCESS=true
343
375
  else
344
- echo "[$(date)] ⚠️ update-ac-status.js not found in any location" >> "$DEBUG_LOG" 2>/dev/null || true
376
+ echo "[$(date)] ⚠️ AC status update failed (non-blocking)" >> "$DEBUG_LOG" 2>/dev/null || true
345
377
  fi
346
378
  fi
347
- fi
348
-
349
- # ============================================================================
350
- # TRANSLATE LIVING DOCS (NEW in v0.6.0 - i18n)
351
- # ============================================================================
352
379
 
353
- if command -v node &> /dev/null; then
354
- if [ -n "$CURRENT_INCREMENT" ]; then
355
- echo "[$(date)] 🌐 Checking if living docs translation is needed for $CURRENT_INCREMENT" >> "$DEBUG_LOG" 2>/dev/null || true
356
-
357
- # Determine which translation script to use (project local or global)
358
- TRANSLATE_SCRIPT=""
359
- if [ -f "$PROJECT_ROOT/plugins/specweave/lib/hooks/translate-living-docs.js" ]; then
360
- # Development: Use in-place compiled hooks (esbuild, not tsc)
361
- TRANSLATE_SCRIPT="$PROJECT_ROOT/plugins/specweave/lib/hooks/translate-living-docs.js"
362
- echo "[$(date)] Using in-place compiled hook: $TRANSLATE_SCRIPT" >> "$DEBUG_LOG" 2>/dev/null || true
363
- elif [ -f "$PROJECT_ROOT/dist/plugins/specweave/lib/hooks/translate-living-docs.js" ]; then
364
- # Development: Use project's compiled files (has node_modules)
365
- TRANSLATE_SCRIPT="$PROJECT_ROOT/dist/plugins/specweave/lib/hooks/translate-living-docs.js"
366
- echo "[$(date)] Using local dist: $TRANSLATE_SCRIPT" >> "$DEBUG_LOG" 2>/dev/null || true
367
- elif [ -f "$PROJECT_ROOT/node_modules/specweave/dist/plugins/specweave/lib/hooks/translate-living-docs.js" ]; then
368
- # Installed as dependency: Use node_modules version
369
- TRANSLATE_SCRIPT="$PROJECT_ROOT/node_modules/specweave/dist/plugins/specweave/lib/hooks/translate-living-docs.js"
370
- echo "[$(date)] Using node_modules: $TRANSLATE_SCRIPT" >> "$DEBUG_LOG" 2>/dev/null || true
371
- elif [ -n "${CLAUDE_PLUGIN_ROOT}" ] && [ -f "${CLAUDE_PLUGIN_ROOT}/lib/hooks/translate-living-docs.js" ]; then
372
- # Fallback: Plugin marketplace (may fail if deps missing)
373
- TRANSLATE_SCRIPT="${CLAUDE_PLUGIN_ROOT}/lib/hooks/translate-living-docs.js"
374
- echo "[$(date)] Using plugin marketplace: $TRANSLATE_SCRIPT" >> "$DEBUG_LOG" 2>/dev/null || true
375
- fi
380
+ # ============================================================================
381
+ # 4. TRANSLATE LIVING DOCS (if needed)
382
+ # ============================================================================
383
+ echo "[$(date)] 🌐 Checking translation needs" >> "$DEBUG_LOG" 2>/dev/null || true
384
+
385
+ TRANSLATE_SCRIPT=""
386
+ if [ -f "$PROJECT_ROOT/plugins/specweave/lib/hooks/translate-living-docs.js" ]; then
387
+ TRANSLATE_SCRIPT="$PROJECT_ROOT/plugins/specweave/lib/hooks/translate-living-docs.js"
388
+ elif [ -f "$PROJECT_ROOT/dist/plugins/specweave/lib/hooks/translate-living-docs.js" ]; then
389
+ TRANSLATE_SCRIPT="$PROJECT_ROOT/dist/plugins/specweave/lib/hooks/translate-living-docs.js"
390
+ elif [ -f "$PROJECT_ROOT/node_modules/specweave/dist/plugins/specweave/lib/hooks/translate-living-docs.js" ]; then
391
+ TRANSLATE_SCRIPT="$PROJECT_ROOT/node_modules/specweave/dist/plugins/specweave/lib/hooks/translate-living-docs.js"
392
+ elif [ -n "${CLAUDE_PLUGIN_ROOT}" ] && [ -f "${CLAUDE_PLUGIN_ROOT}/lib/hooks/translate-living-docs.js" ]; then
393
+ TRANSLATE_SCRIPT="${CLAUDE_PLUGIN_ROOT}/lib/hooks/translate-living-docs.js"
394
+ fi
376
395
 
377
- if [ -n "$TRANSLATE_SCRIPT" ]; then
378
- # Run living docs translation (non-blocking, best-effort)
379
- (cd "$PROJECT_ROOT" && node "$TRANSLATE_SCRIPT" "$CURRENT_INCREMENT") 2>&1 | tee -a "$DEBUG_LOG" >/dev/null || {
380
- echo "[$(date)] ⚠️ Failed to translate living docs (non-blocking)" >> "$DEBUG_LOG" 2>/dev/null || true
381
- }
382
- else
383
- echo "[$(date)] ⚠️ translate-living-docs.js not found in any location" >> "$DEBUG_LOG" 2>/dev/null || true
396
+ if [ -n "$TRANSLATE_SCRIPT" ]; then
397
+ if (cd "$PROJECT_ROOT" && node "$TRANSLATE_SCRIPT" "$CURRENT_INCREMENT") >> "$DEBUG_LOG" 2>&1; then
398
+ echo "[$(date)] Translation checked" >> "$DEBUG_LOG" 2>/dev/null || true
399
+ ANY_SUCCESS=true
384
400
  fi
385
401
  fi
386
- fi
387
-
388
- # ============================================================================
389
- # EXTERNAL TRACKER SYNC (REMOVED in v0.13.0 - Moved to Plugin Hooks)
390
- # ============================================================================
391
- #
392
- # External tool sync logic has been moved to respective plugin hooks:
393
- # - GitHub sync: plugins/specweave-github/hooks/post-task-completion.sh
394
- # - JIRA sync: plugins/specweave-jira/hooks/post-task-completion.sh
395
- # - Azure DevOps sync: plugins/specweave-ado/hooks/post-task-completion.sh
396
- #
397
- # When multiple plugins are installed, Claude Code fires all hooks in parallel.
398
- # This architecture provides:
399
- # - Clean separation of concerns (core vs. external tools)
400
- # - Optional plugins (GitHub sync only runs if specweave-github installed)
401
- # - Independent testing and maintenance
402
- # - No external tool dependencies in core plugin
403
- #
404
- # See: .specweave/increments/0018-strict-increment-discipline-enforcement/reports/HOOKS-ARCHITECTURE-ANALYSIS.md
405
-
406
- echo "[$(date)] ℹ️ External tracker sync moved to plugin hooks (GitHub/JIRA/ADO)" >> "$DEBUG_LOG" 2>/dev/null || true
407
402
 
408
- # ============================================================================
409
- # SELF-REFLECTION (NEW in v0.12.0-beta - AI Self-Reflection System)
410
- # ============================================================================
411
-
412
- if command -v node &> /dev/null; then
413
- if [ -n "$CURRENT_INCREMENT" ] && [ "$ALL_COMPLETED" = "true" ]; then
414
- echo "[$(date)] 🤔 Preparing self-reflection context for $CURRENT_INCREMENT" >> "$DEBUG_LOG" 2>/dev/null || true
403
+ # ============================================================================
404
+ # 5. SELF-REFLECTION (only if all tasks complete)
405
+ # ============================================================================
406
+ if [ "$ALL_COMPLETED" = "true" ]; then
407
+ echo "[$(date)] 🤔 Preparing reflection" >> "$DEBUG_LOG" 2>/dev/null || true
415
408
 
416
- # Detect latest completed task
417
409
  LATEST_TASK=$(grep "^## T-[0-9]" ".specweave/increments/$CURRENT_INCREMENT/tasks.md" 2>/dev/null | tail -1 | awk '{print $2}' | sed 's/://')
418
410
 
419
411
  if [ -n "$LATEST_TASK" ]; then
420
- # Determine which reflection script to use (project local or global)
421
412
  REFLECTION_SCRIPT=""
422
413
  if [ -f "$PROJECT_ROOT/plugins/specweave/lib/hooks/prepare-reflection-context.js" ]; then
423
- # Development: Use in-place compiled hooks (esbuild, not tsc)
424
414
  REFLECTION_SCRIPT="$PROJECT_ROOT/plugins/specweave/lib/hooks/prepare-reflection-context.js"
425
- echo "[$(date)] Using in-place compiled hook: $REFLECTION_SCRIPT" >> "$DEBUG_LOG" 2>/dev/null || true
426
415
  elif [ -f "$PROJECT_ROOT/dist/plugins/specweave/lib/hooks/prepare-reflection-context.js" ]; then
427
- # Development: Use project's compiled files (has node_modules)
428
416
  REFLECTION_SCRIPT="$PROJECT_ROOT/dist/plugins/specweave/lib/hooks/prepare-reflection-context.js"
429
- echo "[$(date)] Using local dist: $REFLECTION_SCRIPT" >> "$DEBUG_LOG" 2>/dev/null || true
430
417
  elif [ -f "$PROJECT_ROOT/node_modules/specweave/dist/plugins/specweave/lib/hooks/prepare-reflection-context.js" ]; then
431
- # Installed as dependency: Use node_modules version
432
418
  REFLECTION_SCRIPT="$PROJECT_ROOT/node_modules/specweave/dist/plugins/specweave/lib/hooks/prepare-reflection-context.js"
433
- echo "[$(date)] Using node_modules: $REFLECTION_SCRIPT" >> "$DEBUG_LOG" 2>/dev/null || true
434
419
  elif [ -n "${CLAUDE_PLUGIN_ROOT}" ] && [ -f "${CLAUDE_PLUGIN_ROOT}/lib/hooks/prepare-reflection-context.js" ]; then
435
- # Fallback: Plugin marketplace (may fail if deps missing)
436
420
  REFLECTION_SCRIPT="${CLAUDE_PLUGIN_ROOT}/lib/hooks/prepare-reflection-context.js"
437
- echo "[$(date)] Using plugin marketplace: $REFLECTION_SCRIPT" >> "$DEBUG_LOG" 2>/dev/null || true
438
421
  fi
439
422
 
440
423
  if [ -n "$REFLECTION_SCRIPT" ]; then
441
- # Prepare reflection context (non-blocking, best-effort)
442
- (cd "$PROJECT_ROOT" && node "$REFLECTION_SCRIPT" "$CURRENT_INCREMENT" "$LATEST_TASK") 2>&1 | tee -a "$DEBUG_LOG" >/dev/null || {
443
- echo "[$(date)] ⚠️ Failed to prepare reflection context (non-blocking)" >> "$DEBUG_LOG" 2>/dev/null || true
444
- }
445
- else
446
- echo "[$(date)] ⚠️ prepare-reflection-context.js not found in any location" >> "$DEBUG_LOG" 2>/dev/null || true
424
+ if (cd "$PROJECT_ROOT" && node "$REFLECTION_SCRIPT" "$CURRENT_INCREMENT" "$LATEST_TASK") >> "$DEBUG_LOG" 2>&1; then
425
+ echo "[$(date)] Reflection prepared" >> "$DEBUG_LOG" 2>/dev/null || true
426
+ ANY_SUCCESS=true
427
+ fi
447
428
  fi
448
- else
449
- echo "[$(date)] ℹ️ No tasks found in tasks.md, skipping reflection" >> "$DEBUG_LOG" 2>/dev/null || true
450
429
  fi
430
+ fi
431
+
432
+ # ============================================================================
433
+ # 6. STATUS LINE UPDATE
434
+ # ============================================================================
435
+ echo "[$(date)] 📊 Updating status line" >> "$DEBUG_LOG" 2>/dev/null || true
436
+
437
+ HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
438
+ if bash "$HOOK_DIR/lib/update-status-line.sh" >> "$DEBUG_LOG" 2>&1; then
439
+ echo "[$(date)] ✅ Status line updated" >> "$DEBUG_LOG" 2>/dev/null || true
440
+ ANY_SUCCESS=true
451
441
  else
452
- echo "[$(date)] ℹ️ Skipping reflection (tasks still pending or no increment)" >> "$DEBUG_LOG" 2>/dev/null || true
442
+ echo "[$(date)] ⚠️ Status line update failed (non-blocking)" >> "$DEBUG_LOG" 2>/dev/null || true
453
443
  fi
454
- else
455
- echo "[$(date)] ⚠️ Node.js not found, skipping reflection" >> "$DEBUG_LOG" 2>/dev/null || true
456
- fi
444
+
445
+ # ============================================================================
446
+ # CIRCUIT BREAKER UPDATE
447
+ # ============================================================================
448
+ if [ "$ANY_SUCCESS" = "true" ]; then
449
+ echo "0" > "$CIRCUIT_BREAKER_FILE" 2>/dev/null || true
450
+ else
451
+ CURRENT_FAILURES=$(cat "$CIRCUIT_BREAKER_FILE" 2>/dev/null || echo 0)
452
+ echo "$((CURRENT_FAILURES + 1))" > "$CIRCUIT_BREAKER_FILE" 2>/dev/null || true
453
+ fi
454
+
455
+ echo "[$(date)] Consolidated background work completed (success=$ANY_SUCCESS)" >> "$DEBUG_LOG" 2>/dev/null || true
456
+
457
+ ) &
458
+
459
+ # Disown background process immediately
460
+ disown 2>/dev/null || true
457
461
 
458
462
  # ============================================================================
459
463
  # PLAY SOUND (only if session is truly ending)
@@ -474,16 +478,6 @@ play_sound() {
474
478
  esac
475
479
  }
476
480
 
477
- # ============================================================================
478
- # STATUS LINE UPDATE
479
- # ============================================================================
480
- # Update status line cache BEFORE playing sound (async, non-blocking)
481
- # Cache will be read by status line renderer for fast display (<1ms)
482
-
483
- echo "[$(date)] 📊 Updating status line cache" >> "$DEBUG_LOG" 2>/dev/null || true
484
- HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
485
- bash "$HOOK_DIR/lib/update-status-line.sh" 2>/dev/null || true
486
-
487
481
  if [ "$SESSION_ENDING" = "true" ]; then
488
482
  echo "[$(date)] 🔔 Playing completion sound" >> "$DEBUG_LOG" 2>/dev/null || true
489
483
  play_sound
@@ -523,3 +517,6 @@ else
523
517
  }
524
518
  EOF
525
519
  fi
520
+
521
+ # ALWAYS exit 0 - NEVER let hook errors crash Claude Code
522
+ exit 0