specweave 0.23.10 → 0.23.14

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 (135) 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/error-logger.d.ts +58 -0
  51. package/dist/src/core/progress/error-logger.d.ts.map +1 -0
  52. package/dist/src/core/progress/error-logger.js +99 -0
  53. package/dist/src/core/progress/error-logger.js.map +1 -0
  54. package/dist/src/core/progress/import-state.d.ts +71 -0
  55. package/dist/src/core/progress/import-state.d.ts.map +1 -0
  56. package/dist/src/core/progress/import-state.js +96 -0
  57. package/dist/src/core/progress/import-state.js.map +1 -0
  58. package/dist/src/core/progress/progress-tracker.d.ts +139 -0
  59. package/dist/src/core/progress/progress-tracker.d.ts.map +1 -0
  60. package/dist/src/core/progress/progress-tracker.js +223 -0
  61. package/dist/src/core/progress/progress-tracker.js.map +1 -0
  62. package/dist/src/init/architecture/types.d.ts +6 -6
  63. package/dist/src/integrations/ado/ado-client.d.ts +25 -0
  64. package/dist/src/integrations/ado/ado-client.d.ts.map +1 -1
  65. package/dist/src/integrations/ado/ado-client.js +67 -0
  66. package/dist/src/integrations/ado/ado-client.js.map +1 -1
  67. package/dist/src/integrations/ado/ado-dependency-loader.d.ts +99 -0
  68. package/dist/src/integrations/ado/ado-dependency-loader.d.ts.map +1 -0
  69. package/dist/src/integrations/ado/ado-dependency-loader.js +207 -0
  70. package/dist/src/integrations/ado/ado-dependency-loader.js.map +1 -0
  71. package/dist/src/integrations/jira/jira-client.d.ts +32 -0
  72. package/dist/src/integrations/jira/jira-client.d.ts.map +1 -1
  73. package/dist/src/integrations/jira/jira-client.js +81 -0
  74. package/dist/src/integrations/jira/jira-client.js.map +1 -1
  75. package/dist/src/integrations/jira/jira-dependency-loader.d.ts +101 -0
  76. package/dist/src/integrations/jira/jira-dependency-loader.d.ts.map +1 -0
  77. package/dist/src/integrations/jira/jira-dependency-loader.js +200 -0
  78. package/dist/src/integrations/jira/jira-dependency-loader.js.map +1 -0
  79. package/package.json +1 -1
  80. package/plugins/specweave/.claude-plugin/plugin.json +20 -0
  81. package/plugins/specweave/agents/architect/AGENT.md +100 -602
  82. package/plugins/specweave/agents/pm/AGENT.md +96 -597
  83. package/plugins/specweave/agents/pm/AGENT.md.bak +1893 -0
  84. package/plugins/specweave/agents/pm/AGENT.md.bak2 +1754 -0
  85. package/plugins/specweave/commands/check-hooks.md +257 -0
  86. package/plugins/specweave/hooks/docs-changed.sh +9 -1
  87. package/plugins/specweave/hooks/docs-changed.sh.backup +79 -0
  88. package/plugins/specweave/hooks/human-input-required.sh +9 -1
  89. package/plugins/specweave/hooks/human-input-required.sh.backup +75 -0
  90. package/plugins/specweave/hooks/post-edit-spec.sh +202 -31
  91. package/plugins/specweave/hooks/post-first-increment.sh.backup +61 -0
  92. package/plugins/specweave/hooks/post-increment-change.sh +6 -1
  93. package/plugins/specweave/hooks/post-increment-change.sh.backup +98 -0
  94. package/plugins/specweave/hooks/post-increment-completion.sh +6 -1
  95. package/plugins/specweave/hooks/post-increment-completion.sh.backup +231 -0
  96. package/plugins/specweave/hooks/post-increment-planning.sh +6 -1
  97. package/plugins/specweave/hooks/post-increment-planning.sh.backup +1048 -0
  98. package/plugins/specweave/hooks/post-increment-status-change.sh +6 -1
  99. package/plugins/specweave/hooks/post-increment-status-change.sh.backup +147 -0
  100. package/plugins/specweave/hooks/post-metadata-change.sh +7 -1
  101. package/plugins/specweave/hooks/post-spec-update.sh.backup +158 -0
  102. package/plugins/specweave/hooks/post-task-completion.sh +225 -228
  103. package/plugins/specweave/hooks/post-user-story-complete.sh.backup +179 -0
  104. package/plugins/specweave/hooks/post-write-spec.sh +207 -31
  105. package/plugins/specweave/hooks/pre-command-deduplication.sh.backup +83 -0
  106. package/plugins/specweave/hooks/pre-edit-spec.sh +151 -0
  107. package/plugins/specweave/hooks/pre-implementation.sh +9 -1
  108. package/plugins/specweave/hooks/pre-implementation.sh.backup +67 -0
  109. package/plugins/specweave/hooks/pre-task-completion.sh +14 -8
  110. package/plugins/specweave/hooks/pre-task-completion.sh.backup +194 -0
  111. package/plugins/specweave/hooks/pre-tool-use.sh +9 -1
  112. package/plugins/specweave/hooks/pre-tool-use.sh.backup +133 -0
  113. package/plugins/specweave/hooks/pre-write-spec.sh +151 -0
  114. package/plugins/specweave/hooks/test-pretooluse-env.sh +72 -0
  115. package/plugins/specweave/hooks/user-prompt-submit.sh.backup +386 -0
  116. package/plugins/specweave/skills/compliance-architecture/SKILL.md +374 -0
  117. package/plugins/specweave/skills/external-sync-wizard/SKILL.md +610 -0
  118. package/plugins/specweave/skills/pm-closure-validation/SKILL.md +541 -0
  119. package/plugins/specweave/skills/roadmap-planner/SKILL.md +473 -0
  120. package/plugins/specweave-ado/commands/refresh-cache.js +25 -0
  121. package/plugins/specweave-ado/commands/refresh-cache.ts +40 -0
  122. package/plugins/specweave-ado/hooks/post-living-docs-update.sh +9 -2
  123. package/plugins/specweave-ado/hooks/post-living-docs-update.sh.backup +353 -0
  124. package/plugins/specweave-ado/hooks/post-task-completion.sh +10 -2
  125. package/plugins/specweave-ado/hooks/post-task-completion.sh.backup +172 -0
  126. package/plugins/specweave-github/hooks/post-task-completion.sh +10 -2
  127. package/plugins/specweave-github/hooks/post-task-completion.sh.backup +258 -0
  128. package/plugins/specweave-jira/commands/refresh-cache.js +25 -0
  129. package/plugins/specweave-jira/commands/refresh-cache.ts +40 -0
  130. package/plugins/specweave-jira/hooks/post-task-completion.sh +10 -2
  131. package/plugins/specweave-jira/hooks/post-task-completion.sh.backup +172 -0
  132. package/plugins/specweave-kafka-streams/commands/topology.md +437 -0
  133. package/plugins/specweave-n8n/commands/workflow-template.md +262 -0
  134. package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +252 -6465
  135. package/plugins/specweave-release/hooks/post-task-completion.sh.backup +110 -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
@@ -0,0 +1,72 @@
1
+ #!/bin/bash
2
+ #
3
+ # PreToolUse Environment Variable Validation Test
4
+ #
5
+ # Purpose: Validate that PreToolUse hooks receive tool arguments via env vars
6
+ # This tests the core assumption of Tier 2 implementation
7
+ #
8
+ # Usage:
9
+ # 1. Register this hook in plugin.json under PreToolUse:Edit
10
+ # 2. Make 10 test edits to any file
11
+ # 3. Review /tmp/pretooluse-test.log
12
+ # 4. Check if TOOL_USE_ARGS is populated
13
+ #
14
+ # Success Criteria:
15
+ # - TOOL_USE_ARGS contains file_path
16
+ # - At least 80% of invocations have non-empty TOOL_USE_ARGS
17
+ #
18
+ # If this test fails, Tier 2 PreToolUse coordination cannot work
19
+ # and we must rely on Tier 1 (mtime fallback) or proceed to Tier 3
20
+ #
21
+ # Version: v0.24.2 (Tier 2 Validation)
22
+ # Date: 2025-11-22
23
+
24
+ set +e # Don't fail on errors (this is a diagnostic tool)
25
+
26
+ TEST_LOG="/tmp/pretooluse-test.log"
27
+ TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
28
+
29
+ # Initialize log on first run
30
+ if [[ ! -f "$TEST_LOG" ]]; then
31
+ echo "==================================" > "$TEST_LOG"
32
+ echo "PreToolUse Environment Variable Test" >> "$TEST_LOG"
33
+ echo "Started: $TIMESTAMP" >> "$TEST_LOG"
34
+ echo "==================================" >> "$TEST_LOG"
35
+ echo "" >> "$TEST_LOG"
36
+ fi
37
+
38
+ # Record this invocation
39
+ echo "[$TIMESTAMP] PreToolUse:Edit invocation #$(wc -l < "$TEST_LOG")" >> "$TEST_LOG"
40
+
41
+ # Check ALL tool-related environment variables
42
+ echo " TOOL_USE_ARGS: ${TOOL_USE_ARGS:-<EMPTY>}" >> "$TEST_LOG"
43
+ echo " TOOL_USE_CONTENT: ${TOOL_USE_CONTENT:-<EMPTY>}" >> "$TEST_LOG"
44
+ echo " TOOL_RESULT: ${TOOL_RESULT:-<EMPTY>}" >> "$TEST_LOG"
45
+ echo " PWD: $PWD" >> "$TEST_LOG"
46
+
47
+ # Try to extract file_path if TOOL_USE_ARGS exists
48
+ if [[ -n "${TOOL_USE_ARGS:-}" ]]; then
49
+ if command -v jq &> /dev/null; then
50
+ FILE_PATH=$(echo "$TOOL_USE_ARGS" | jq -r '.file_path // "<NOT_FOUND>"' 2>/dev/null)
51
+ echo " Extracted file_path: $FILE_PATH" >> "$TEST_LOG"
52
+ else
53
+ FILE_PATH=$(echo "$TOOL_USE_ARGS" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)".*/\1/' || echo "<PARSE_FAILED>")
54
+ echo " Extracted file_path (no jq): $FILE_PATH" >> "$TEST_LOG"
55
+ fi
56
+ fi
57
+
58
+ # Dump all TOOL* env vars
59
+ echo " All TOOL* environment variables:" >> "$TEST_LOG"
60
+ env | grep -E "^TOOL" | while read line; do
61
+ echo " $line" >> "$TEST_LOG"
62
+ done
63
+
64
+ echo "" >> "$TEST_LOG"
65
+
66
+ # Log rotation: Keep last 100 invocations
67
+ if [[ $(wc -l < "$TEST_LOG") -gt 500 ]]; then
68
+ tail -400 "$TEST_LOG" > "$TEST_LOG.tmp"
69
+ mv "$TEST_LOG.tmp" "$TEST_LOG"
70
+ fi
71
+
72
+ exit 0 # Always succeed (diagnostic tool)
@@ -0,0 +1,386 @@
1
+ #!/bin/bash
2
+
3
+ # SpecWeave UserPromptSubmit Hook
4
+ # Fires BEFORE user's command executes (prompt-based hook)
5
+ # Purpose: Discipline validation, context injection, command suggestions
6
+
7
+ set -euo pipefail
8
+
9
+ # Read input JSON from stdin
10
+ INPUT=$(cat)
11
+
12
+ # Extract prompt from JSON
13
+ PROMPT=$(echo "$INPUT" | node -e "
14
+ const input = JSON.parse(require('fs').readFileSync(0, 'utf-8'));
15
+ console.log(input.prompt || '');
16
+ ")
17
+
18
+ # ==============================================================================
19
+ # DISCIPLINE VALIDATION: Block /specweave:increment if incomplete increments exist
20
+ # ==============================================================================
21
+
22
+ if echo "$PROMPT" | grep -q "/specweave:increment"; then
23
+ # Check increment discipline using check-discipline CLI command
24
+ # This enforces WIP limits (max 1 active, hard cap 2)
25
+ SPECWEAVE_DIR=".specweave"
26
+
27
+ if [[ -d "$SPECWEAVE_DIR/increments" ]]; then
28
+ # Run discipline check (exit code: 0=pass, 1=violations, 2=error)
29
+ if command -v node >/dev/null 2>&1 && [[ -f "dist/src/core/increment/metadata-manager.js" ]]; then
30
+ # Check active increments using MetadataManager
31
+ ACTIVE_COUNT=$(node -e "
32
+ try {
33
+ const { MetadataManager } = require('./dist/src/core/increment/metadata-manager.js');
34
+ const active = MetadataManager.getActive();
35
+ console.log(active.length);
36
+ } catch (e) {
37
+ console.error('Error checking active increments:', e.message);
38
+ process.exit(2);
39
+ }
40
+ " 2>/dev/null || echo "0")
41
+
42
+ # Hard cap: never >2 active
43
+ if [[ "$ACTIVE_COUNT" -ge 2 ]]; then
44
+ # Get list of active increments for error message
45
+ ACTIVE_LIST=$(node -e "
46
+ try {
47
+ const { MetadataManager } = require('./dist/src/core/increment/metadata-manager.js');
48
+ const active = MetadataManager.getActive();
49
+ active.forEach(inc => console.log(' - ' + inc.id + ' [' + inc.type + ']'));
50
+ } catch (e) {}
51
+ " 2>/dev/null || echo "")
52
+
53
+ cat <<EOF
54
+ {
55
+ "decision": "block",
56
+ "reason": "❌ HARD CAP REACHED\n\nYou have $ACTIVE_COUNT active increments (absolute maximum: 2)\n\nActive increments:\n$ACTIVE_LIST\n\n💡 You MUST complete or pause existing work first:\n\n1️⃣ Complete an increment:\n /specweave:done <id>\n\n2️⃣ Pause an increment:\n /specweave:pause <id> --reason=\"...\"\n\n3️⃣ Check status:\n /specweave:status\n\n📝 Multiple hotfixes? Combine them into ONE increment!\n Example: 0009-security-fixes (SQL + XSS + CSRF)\n\n⛔ This limit is enforced for your productivity.\nResearch: 3+ concurrent tasks = 40% slower + more bugs"
57
+ }
58
+ EOF
59
+ exit 0
60
+ fi
61
+
62
+ # Soft warning: 1 active (recommended limit)
63
+ if [[ "$ACTIVE_COUNT" -ge 1 ]]; then
64
+ # Get list of active increments for warning
65
+ ACTIVE_LIST=$(node -e "
66
+ try {
67
+ const { MetadataManager } = require('./dist/src/core/increment/metadata-manager.js');
68
+ const active = MetadataManager.getActive();
69
+ active.forEach(inc => console.log(' - ' + inc.id + ' [' + inc.type + ']'));
70
+ } catch (e) {}
71
+ " 2>/dev/null || echo "")
72
+
73
+ # Just warn, don't block (user can choose to continue)
74
+ cat <<EOF
75
+ {
76
+ "decision": "approve",
77
+ "systemMessage": "⚠️ WIP LIMIT REACHED\n\nYou have $ACTIVE_COUNT active increment (recommended limit: 1)\n\nActive increments:\n$ACTIVE_LIST\n\n🧠 Focus Principle: ONE active increment = maximum productivity\nStarting a 2nd increment reduces focus and velocity.\n\n💡 Consider:\n 1️⃣ Complete current work (recommended)\n 2️⃣ Pause current work (/specweave:pause)\n 3️⃣ Continue anyway (accept 20% productivity cost)\n\n⚠️ Emergency hotfix/bug? Use --type=hotfix or --type=bug to bypass this warning."
78
+ }
79
+ EOF
80
+ exit 0
81
+ fi
82
+ else
83
+ # Fallback: check for active/planning status manually
84
+ INCOMPLETE_INCREMENTS=$(find "$SPECWEAVE_DIR/increments" -mindepth 1 -maxdepth 1 -type d | while read increment_dir; do
85
+ metadata="$increment_dir/metadata.json"
86
+ if [[ -f "$metadata" ]]; then
87
+ status=$(node -e "
88
+ try {
89
+ const data = JSON.parse(require('fs').readFileSync('$metadata', 'utf-8'));
90
+ console.log(data.status || 'unknown');
91
+ } catch (e) {
92
+ console.log('unknown');
93
+ }
94
+ ")
95
+
96
+ if [[ "$status" == "active" || "$status" == "planning" ]]; then
97
+ echo "$(basename "$increment_dir")"
98
+ fi
99
+ fi
100
+ done)
101
+
102
+ if [[ -n "$INCOMPLETE_INCREMENTS" ]]; then
103
+ COUNT=$(echo "$INCOMPLETE_INCREMENTS" | wc -l | xargs)
104
+
105
+ # Get incomplete task count for migration guidance
106
+ MIGRATION_SCRIPT="$(dirname "${BASH_SOURCE[0]}")/lib/migrate-increment-work.sh"
107
+ INCOMPLETE_TASKS=""
108
+
109
+ for increment in $INCOMPLETE_INCREMENTS; do
110
+ if [[ -x "$MIGRATION_SCRIPT" ]]; then
111
+ TASK_COUNT=$("$MIGRATION_SCRIPT" count-incomplete "$increment" 2>/dev/null || echo "?")
112
+ INCOMPLETE_TASKS="${INCOMPLETE_TASKS}\n - $increment ($TASK_COUNT incomplete tasks)"
113
+ else
114
+ INCOMPLETE_TASKS="${INCOMPLETE_TASKS}\n - $increment"
115
+ fi
116
+ done
117
+
118
+ cat <<EOF
119
+ {
120
+ "decision": "block",
121
+ "reason": "❌ Cannot create new increment! You have $COUNT incomplete increment(s):$INCOMPLETE_TASKS\n\n💡 **SMART MIGRATION OPTIONS:**\n\n1️⃣ **Transfer Work** (Recommended)\n Move incomplete tasks to new increment:\n \`\`\`bash\n # After creating new increment, run:\n bash plugins/specweave/hooks/lib/migrate-increment-work.sh transfer <old-id> <new-id>\n \`\`\`\n ✅ Clean closure + work continues\n\n2️⃣ **Adjust WIP Limit** (Emergency Only)\n Temporarily allow 3 active increments:\n \`\`\`bash\n bash plugins/specweave/hooks/lib/migrate-increment-work.sh adjust-wip 3\n \`\`\`\n ⚠️ 20% productivity cost, revert ASAP\n\n3️⃣ **Force-Close** (Quick Fix)\n Mark increment as complete (work lost):\n \`\`\`bash\n bash plugins/specweave/hooks/lib/migrate-increment-work.sh force-close <increment-id>\n \`\`\`\n ⚠️ Incomplete work NOT transferred!\n\n📝 **Traditional Options:**\n - /specweave:done <id> # Complete properly\n - /specweave:pause <id> # Pause for later\n - /specweave:abandon <id> # Abandon if obsolete\n\nℹ️ The discipline exists for a reason:\n ✓ Prevents scope creep\n ✓ Ensures completions are tracked\n ✓ Maintains living docs accuracy\n ✓ Keeps work focused"
122
+ }
123
+ EOF
124
+ exit 0
125
+ fi
126
+ fi
127
+ fi
128
+ fi
129
+
130
+ # ==============================================================================
131
+ # PRE-FLIGHT SYNC CHECK: Ensure living docs are fresh before operations
132
+ # ==============================================================================
133
+
134
+ # Detect increment operations that need fresh data
135
+ if echo "$PROMPT" | grep -qE "/(specweave:)?(done|validate|progress|do)"; then
136
+ # Extract increment ID from prompt (if provided)
137
+ INCREMENT_ID=$(echo "$PROMPT" | grep -oE "[0-9]{4}[a-z0-9-]*" | head -1)
138
+
139
+ # If no ID in prompt, try to find active increment
140
+ if [[ -z "$INCREMENT_ID" ]] && [[ -d ".specweave/increments" ]]; then
141
+ INCREMENT_ID=$(find .specweave/increments -mindepth 1 -maxdepth 1 -type d | while read increment_dir; do
142
+ metadata="$increment_dir/metadata.json"
143
+ if [[ -f "$metadata" ]]; then
144
+ status=$(node -e "
145
+ try {
146
+ const data = JSON.parse(require('fs').readFileSync('$metadata', 'utf-8'));
147
+ if (data.status === 'active') {
148
+ console.log('$(basename "$increment_dir")');
149
+ }
150
+ } catch (e) {}
151
+ " 2>/dev/null)
152
+
153
+ if [[ -n "$status" ]]; then
154
+ echo "$status"
155
+ break
156
+ fi
157
+ fi
158
+ done)
159
+ fi
160
+
161
+ # If we have an increment ID, check freshness
162
+ if [[ -n "$INCREMENT_ID" ]]; then
163
+ INCREMENT_SPEC=".specweave/increments/$INCREMENT_ID/spec.md"
164
+ LIVING_DOCS_SPEC=".specweave/docs/internal/specs/spec-$INCREMENT_ID.md"
165
+
166
+ # Check if increment spec exists
167
+ if [[ -f "$INCREMENT_SPEC" ]]; then
168
+ # Get modification times
169
+ if [[ "$(uname)" == "Darwin" ]]; then
170
+ # macOS
171
+ INCREMENT_MTIME=$(stat -f %m "$INCREMENT_SPEC" 2>/dev/null || echo 0)
172
+ LIVING_DOCS_MTIME=$(stat -f %m "$LIVING_DOCS_SPEC" 2>/dev/null || echo 0)
173
+ else
174
+ # Linux
175
+ INCREMENT_MTIME=$(stat -c %Y "$INCREMENT_SPEC" 2>/dev/null || echo 0)
176
+ LIVING_DOCS_MTIME=$(stat -c %Y "$LIVING_DOCS_SPEC" 2>/dev/null || echo 0)
177
+ fi
178
+
179
+ # Check if increment is newer than living docs (or living docs doesn't exist)
180
+ if [[ "$INCREMENT_MTIME" -gt "$LIVING_DOCS_MTIME" ]]; then
181
+ # Sync needed - run sync-living-docs
182
+ PLUGIN_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
183
+ SYNC_SCRIPT="$PLUGIN_ROOT/lib/hooks/sync-living-docs.js"
184
+
185
+ if [[ -f "$SYNC_SCRIPT" ]]; then
186
+ # Run sync (capture output but don't block on errors)
187
+ if node "$SYNC_SCRIPT" "$INCREMENT_ID" >/dev/null 2>&1; then
188
+ # Success - sync completed
189
+ :
190
+ else
191
+ # Sync failed - log but continue
192
+ echo "[WARNING] Pre-flight sync failed for $INCREMENT_ID" >&2
193
+ fi
194
+ fi
195
+ fi
196
+ fi
197
+ fi
198
+ fi
199
+
200
+ # ==============================================================================
201
+ # SPEC SYNC CHECK: Detect spec.md changes and warn about sync needed
202
+ # ==============================================================================
203
+
204
+ # Check if spec.md was modified after plan.md (requires sync)
205
+ if [[ -d ".specweave/increments" ]]; then
206
+ # Find active increment
207
+ ACTIVE_INCREMENT_FOR_SYNC=$(find .specweave/increments -mindepth 1 -maxdepth 1 -type d | while read increment_dir; do
208
+ metadata="$increment_dir/metadata.json"
209
+ if [[ -f "$metadata" ]]; then
210
+ status=$(node -e "
211
+ try {
212
+ const data = JSON.parse(require('fs').readFileSync('$metadata', 'utf-8'));
213
+ if (data.status === 'active') {
214
+ console.log('$(basename "$increment_dir")');
215
+ }
216
+ } catch (e) {}
217
+ " 2>/dev/null)
218
+
219
+ if [[ -n "$status" ]]; then
220
+ echo "$status"
221
+ break
222
+ fi
223
+ fi
224
+ done)
225
+
226
+ if [[ -n "$ACTIVE_INCREMENT_FOR_SYNC" ]]; then
227
+ # Check if SpecSyncManager detects changes
228
+ if command -v node >/dev/null 2>&1 && [[ -f "dist/src/core/increment/spec-sync-manager.js" ]]; then
229
+ SYNC_CHECK=$(node -e "
230
+ try {
231
+ const { SpecSyncManager } = require('./dist/src/core/increment/spec-sync-manager.js');
232
+ const manager = new SpecSyncManager(process.cwd());
233
+ const detection = manager.detectSpecChange('$ACTIVE_INCREMENT_FOR_SYNC');
234
+
235
+ if (detection.specChanged) {
236
+ const message = manager.formatSyncMessage(detection);
237
+ console.log(JSON.stringify({ needsSync: true, message }));
238
+ } else {
239
+ console.log(JSON.stringify({ needsSync: false }));
240
+ }
241
+ } catch (e) {
242
+ console.log(JSON.stringify({ needsSync: false, error: e.message }));
243
+ }
244
+ " 2>/dev/null || echo '{"needsSync":false}')
245
+
246
+ NEEDS_SYNC=$(echo "$SYNC_CHECK" | node -e "
247
+ try {
248
+ const data = JSON.parse(require('fs').readFileSync(0, 'utf-8'));
249
+ console.log(data.needsSync || false);
250
+ } catch (e) {
251
+ console.log(false);
252
+ }
253
+ ")
254
+
255
+ if [[ "$NEEDS_SYNC" == "true" ]]; then
256
+ SYNC_MESSAGE=$(echo "$SYNC_CHECK" | node -e "
257
+ try {
258
+ const data = JSON.parse(require('fs').readFileSync(0, 'utf-8'));
259
+ console.log(data.message || '');
260
+ } catch (e) {
261
+ console.log('');
262
+ }
263
+ ")
264
+
265
+ # Show sync warning (don't block, just warn)
266
+ cat <<EOF
267
+ {
268
+ "decision": "approve",
269
+ "systemMessage": "$SYNC_MESSAGE"
270
+ }
271
+ EOF
272
+ exit 0
273
+ fi
274
+ fi
275
+ fi
276
+ fi
277
+
278
+ # ==============================================================================
279
+ # CONTEXT INJECTION: Add current increment status
280
+ # ==============================================================================
281
+
282
+ CONTEXT=""
283
+
284
+ # Find active increment
285
+ if [[ -d ".specweave/increments" ]]; then
286
+ ACTIVE_INCREMENT=$(find .specweave/increments -mindepth 1 -maxdepth 1 -type d | while read increment_dir; do
287
+ metadata="$increment_dir/metadata.json"
288
+ if [[ -f "$metadata" ]]; then
289
+ status=$(node -e "
290
+ try {
291
+ const data = JSON.parse(require('fs').readFileSync('$metadata', 'utf-8'));
292
+ if (data.status === 'active') {
293
+ console.log('$(basename "$increment_dir")');
294
+ }
295
+ } catch (e) {}
296
+ ")
297
+
298
+ if [[ -n "$status" ]]; then
299
+ echo "$status"
300
+ break
301
+ fi
302
+ fi
303
+ done)
304
+
305
+ if [[ -n "$ACTIVE_INCREMENT" ]]; then
306
+ # Simple status: parse tasks.md for completion
307
+ TASKS_FILE=".specweave/increments/$ACTIVE_INCREMENT/tasks.md"
308
+ if [[ -f "$TASKS_FILE" ]]; then
309
+ # Count tasks (headers with T-NNN format - both ### and ####)
310
+ TOTAL_TASKS=$(grep -cE '^#{3,4}\s*T-[0-9]' "$TASKS_FILE" 2>/dev/null | tr -d '\n' || echo "0")
311
+ # Count completed (various formats)
312
+ COMPLETED_TASKS=$(grep -cE '(✅ COMPLETE|\[COMPLETED\]|\[x\] Completed)' "$TASKS_FILE" 2>/dev/null | tr -d '\n' || echo "0")
313
+
314
+ # Ensure valid numbers
315
+ TOTAL_TASKS=${TOTAL_TASKS:-0}
316
+ COMPLETED_TASKS=${COMPLETED_TASKS:-0}
317
+
318
+ if [[ "$TOTAL_TASKS" -gt 0 ]] 2>/dev/null; then
319
+ PERCENTAGE=$(( COMPLETED_TASKS * 100 / TOTAL_TASKS ))
320
+ CONTEXT="✓ Active: $ACTIVE_INCREMENT ($COMPLETED_TASKS/$TOTAL_TASKS tasks, $PERCENTAGE%)"
321
+ else
322
+ CONTEXT="✓ Active: $ACTIVE_INCREMENT"
323
+ fi
324
+ else
325
+ CONTEXT="✓ Active: $ACTIVE_INCREMENT"
326
+ fi
327
+ fi
328
+ fi
329
+
330
+ # ==============================================================================
331
+ # COMMAND SUGGESTIONS: Guide users to structured workflow
332
+ # ==============================================================================
333
+
334
+ if echo "$PROMPT" | grep -qiE "(add|create|implement|build|develop)" && ! echo "$PROMPT" | grep -q "/specweave:"; then
335
+ if [[ -n "$CONTEXT" ]]; then
336
+ CONTEXT="$CONTEXT
337
+
338
+ 💡 TIP: Consider using SpecWeave commands for structured development:
339
+ - /specweave:increment \"feature name\" # Plan new increment
340
+ - /specweave:do # Execute current tasks
341
+ - /specweave:progress # Check progress"
342
+ fi
343
+ fi
344
+
345
+ # ==============================================================================
346
+ # STATUS LINE REFRESH: Ensure cache is fresh before showing context
347
+ # ==============================================================================
348
+ # Performance: ~50-100ms (acceptable for UX)
349
+ # Frequency: Every user prompt (high coverage)
350
+ # Benefit: Catches ALL edge cases (manual edits, resume, direct changes)
351
+ #
352
+ # Why here? This hook runs on EVERY user prompt, ensuring status line
353
+ # is ALWAYS up-to-date before showing context to the user.
354
+ #
355
+ # Prevents desync scenarios:
356
+ # - Manual spec.md edits (status: planning → active)
357
+ # - /specweave:resume (status: paused → active)
358
+ # - Direct metadata changes (without hook triggers)
359
+ # - File system operations bypassing hooks
360
+ #
361
+ # Background execution: Runs async, doesn't block user prompt
362
+
363
+ HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
364
+ bash "$HOOK_DIR/lib/update-status-line.sh" 2>/dev/null || true
365
+
366
+ # ==============================================================================
367
+ # OUTPUT: Approve with context or no context
368
+ # ==============================================================================
369
+
370
+ if [[ -n "$CONTEXT" ]]; then
371
+ cat <<EOF
372
+ {
373
+ "decision": "approve",
374
+ "systemMessage": "$CONTEXT"
375
+ }
376
+ EOF
377
+ else
378
+ # Just approve, no extra context
379
+ cat <<EOF
380
+ {
381
+ "decision": "approve"
382
+ }
383
+ EOF
384
+ fi
385
+
386
+ exit 0