specweave 0.26.11 → 0.26.13

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 (52) hide show
  1. package/dist/plugins/specweave-github/lib/completion-calculator.d.ts +4 -1
  2. package/dist/plugins/specweave-github/lib/completion-calculator.d.ts.map +1 -1
  3. package/dist/plugins/specweave-github/lib/completion-calculator.js +49 -29
  4. package/dist/plugins/specweave-github/lib/completion-calculator.js.map +1 -1
  5. package/dist/src/core/increment/increment-archiver.d.ts +3 -0
  6. package/dist/src/core/increment/increment-archiver.d.ts.map +1 -1
  7. package/dist/src/core/increment/increment-archiver.js +35 -4
  8. package/dist/src/core/increment/increment-archiver.js.map +1 -1
  9. package/dist/src/core/living-docs/feature-archiver.d.ts +5 -0
  10. package/dist/src/core/living-docs/feature-archiver.d.ts.map +1 -1
  11. package/dist/src/core/living-docs/feature-archiver.js +66 -18
  12. package/dist/src/core/living-docs/feature-archiver.js.map +1 -1
  13. package/package.json +1 -1
  14. package/plugins/specweave/commands/specweave-archive.md +10 -1
  15. package/plugins/specweave/hooks/docs-changed.sh.backup +79 -0
  16. package/plugins/specweave/hooks/hooks.json +10 -0
  17. package/plugins/specweave/hooks/human-input-required.sh.backup +75 -0
  18. package/plugins/specweave/hooks/lib/update-active-increment.sh +96 -0
  19. package/plugins/specweave/hooks/lib/update-status-line.sh +153 -189
  20. package/plugins/specweave/hooks/post-edit-write-consolidated.sh +6 -0
  21. package/plugins/specweave/hooks/post-first-increment.sh.backup +61 -0
  22. package/plugins/specweave/hooks/post-increment-change.sh.backup +98 -0
  23. package/plugins/specweave/hooks/post-increment-completion.sh.backup +231 -0
  24. package/plugins/specweave/hooks/post-increment-planning.sh.backup +1048 -0
  25. package/plugins/specweave/hooks/post-increment-status-change.sh.backup +147 -0
  26. package/plugins/specweave/hooks/post-metadata-change.sh +9 -0
  27. package/plugins/specweave/hooks/post-spec-update.sh.backup +158 -0
  28. package/plugins/specweave/hooks/post-task-completion.sh +8 -0
  29. package/plugins/specweave/hooks/post-task-edit.sh +37 -0
  30. package/plugins/specweave/hooks/post-user-story-complete.sh.backup +179 -0
  31. package/plugins/specweave/hooks/pre-command-deduplication.sh +43 -53
  32. package/plugins/specweave/hooks/pre-command-deduplication.sh.backup +83 -0
  33. package/plugins/specweave/hooks/pre-implementation.sh.backup +67 -0
  34. package/plugins/specweave/hooks/pre-task-completion.sh.backup +194 -0
  35. package/plugins/specweave/hooks/pre-tool-use.sh +5 -0
  36. package/plugins/specweave/hooks/pre-tool-use.sh.backup +133 -0
  37. package/plugins/specweave/hooks/user-prompt-submit.sh +143 -289
  38. package/plugins/specweave/hooks/user-prompt-submit.sh.backup +386 -0
  39. package/plugins/specweave-ado/hooks/post-living-docs-update.sh.backup +353 -0
  40. package/plugins/specweave-ado/hooks/post-task-completion.sh.backup +172 -0
  41. package/plugins/specweave-ado/lib/ado-multi-project-sync.js +1 -0
  42. package/plugins/specweave-ado/lib/enhanced-ado-sync.js +170 -0
  43. package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +1098 -0
  44. package/plugins/specweave-github/hooks/post-task-completion.sh.backup +258 -0
  45. package/plugins/specweave-github/lib/completion-calculator.js +34 -16
  46. package/plugins/specweave-github/lib/completion-calculator.ts +54 -32
  47. package/plugins/specweave-jira/hooks/post-task-completion.sh.backup +172 -0
  48. package/plugins/specweave-jira/lib/enhanced-jira-sync.js +3 -3
  49. package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +1008 -0
  50. package/plugins/specweave-release/hooks/post-task-completion.sh.backup +110 -0
  51. package/src/templates/AGENTS.md.template +301 -2452
  52. package/src/templates/CLAUDE.md.template +99 -667
@@ -1,225 +1,189 @@
1
1
  #!/usr/bin/env bash
2
2
  #
3
- # update-status-line.sh (Enhanced with AC Metrics)
3
+ # update-status-line.sh (v0.26.13 - ULTRA-OPTIMIZED for crash prevention)
4
4
  #
5
5
  # Updates status line cache with current increment progress.
6
6
  # Shows: [increment-name] ████░░░░ X/Y tasks | A/B ACs (Z open)
7
7
  #
8
- # Logic:
9
- # 1. Scan all spec.md for status=active/planning (SOURCE OF TRUTH!)
10
- # 2. Take first (oldest) as current increment
11
- # 3. Count all active/planning as openCount
12
- # 4. Parse current increment's tasks.md for task progress
13
- # 5. Parse current increment's spec.md for AC progress
14
- # 6. Write to cache
8
+ # OPTIMIZATIONS (v0.26.13):
9
+ # 1. TTL-based throttling (10s) - longer cache = fewer runs
10
+ # 2. Mtime checking via find -newer (no stat loops!)
11
+ # 3. Pure bash counting + JSON generation (NO jq!)
12
+ # 4. Single-pass awk for all counting (1 process vs 5 greps)
13
+ # 5. Exclude _archive/ with find -not -path
14
+ # 6. Lock file to prevent concurrent runs
15
15
  #
16
- # Performance: 50-100ms (runs async, user doesn't wait)
16
+ # Performance: <5ms (cached) / 15-25ms (full scan)
17
17
  #
18
- # EMERGENCY FIX (v0.24.4): Changed from set -euo pipefail to set +e
19
- # CRITICAL: Hooks MUST use set +e to prevent Claude Code crashes!
20
- # See: CLAUDE.md section 9a - Hook Performance & Safety
21
-
22
18
  set +e
23
19
 
24
- # Find project root
25
- find_project_root() {
26
- local dir="$PWD"
27
- while [[ "$dir" != "/" ]]; do
28
- if [[ -d "$dir/.specweave" ]]; then
29
- echo "$dir"
30
- return 0
31
- fi
32
- dir=$(dirname "$dir")
20
+ # ============================================================================
21
+ # PROJECT ROOT (FAST - cached in env if available)
22
+ # ============================================================================
23
+ if [[ -n "$SPECWEAVE_PROJECT_ROOT" ]] && [[ -d "$SPECWEAVE_PROJECT_ROOT/.specweave" ]]; then
24
+ PROJECT_ROOT="$SPECWEAVE_PROJECT_ROOT"
25
+ else
26
+ PROJECT_ROOT="$PWD"
27
+ while [[ "$PROJECT_ROOT" != "/" ]] && [[ ! -d "$PROJECT_ROOT/.specweave" ]]; do
28
+ PROJECT_ROOT=$(dirname "$PROJECT_ROOT")
33
29
  done
34
- echo "$PWD"
35
- }
36
-
37
- PROJECT_ROOT=$(find_project_root)
30
+ [[ ! -d "$PROJECT_ROOT/.specweave" ]] && PROJECT_ROOT="$PWD"
31
+ fi
38
32
 
39
33
  # ============================================================================
40
- # RECURSION PREVENTION (CRITICAL - v0.26.0 - FILE-BASED GUARD)
34
+ # ULTRA-FAST EXITS
41
35
  # ============================================================================
42
- # FIX: Don't update status line from within hook chain
43
- # WHY: Status line writes status-line.json which triggers post-edit-write hook
44
- # which tries to update status line AGAIN → infinite recursion!
45
- #
46
- # This is a CRITICAL part of Fix #4 in the root cause analysis:
47
- # See: .specweave/increments/0051-*/reports/GITHUB-COMMENT-RECURSION-ROOT-CAUSE-2025-11-24.md
36
+ STATE_DIR="$PROJECT_ROOT/.specweave/state"
37
+ CACHE_FILE="$STATE_DIR/status-line.json"
38
+ INCREMENTS_DIR="$PROJECT_ROOT/.specweave/increments"
39
+ LOCK_FILE="$STATE_DIR/.status-update.lock"
48
40
 
49
- RECURSION_GUARD_FILE="$PROJECT_ROOT/.specweave/state/.hook-recursion-guard"
41
+ # No .specweave? Exit immediately
42
+ [[ ! -d "$PROJECT_ROOT/.specweave" ]] && exit 0
50
43
 
51
- if [[ -f "$RECURSION_GUARD_FILE" ]]; then
52
- # We're inside a hook chain - skip status line update to prevent recursion
53
- # This is NORMAL behavior (not an error!)
54
- exit 0
44
+ # Recursion guard
45
+ [[ -f "$STATE_DIR/.hook-recursion-guard" ]] && exit 0
46
+
47
+ # Lock check (prevent concurrent runs - causes crashes!)
48
+ if [[ -f "$LOCK_FILE" ]]; then
49
+ # Check if lock is stale (>30s)
50
+ if [[ "$(uname)" == "Darwin" ]]; then
51
+ LOCK_AGE=$(( $(date +%s) - $(stat -f %m "$LOCK_FILE" 2>/dev/null || echo 0) ))
52
+ else
53
+ LOCK_AGE=$(( $(date +%s) - $(stat -c %Y "$LOCK_FILE" 2>/dev/null || echo 0) ))
54
+ fi
55
+ [[ $LOCK_AGE -lt 30 ]] && exit 0
55
56
  fi
56
57
 
57
- CACHE_FILE="$PROJECT_ROOT/.specweave/state/status-line.json"
58
- INCREMENTS_DIR="$PROJECT_ROOT/.specweave/increments"
59
- TMP_FILE="$PROJECT_ROOT/.specweave/state/.status-line-tmp.txt"
60
-
61
- # Ensure state directory exists
62
- mkdir -p "$PROJECT_ROOT/.specweave/state"
63
-
64
- # Step 1: Find all open increments (active/planning)
65
- # Read from spec.md (source of truth), not metadata.json
66
- # ONLY accepts official IncrementStatus enum values
67
- # Write to temp file: "timestamp increment_id"
68
- > "$TMP_FILE"
69
-
70
- if [[ -d "$INCREMENTS_DIR" ]]; then
71
- for spec_file in "$INCREMENTS_DIR"/*/spec.md; do
72
- if [[ -f "$spec_file" ]]; then
73
- # Parse YAML frontmatter for status (source of truth)
74
- status=$(grep -m1 "^status:" "$spec_file" 2>/dev/null | cut -d: -f2 | tr -d ' ' || echo "")
75
-
76
- # Check if increment is open (active, planning, or in-progress)
77
- # ONLY accepts official IncrementStatus enum values
78
- if [[ "$status" == "active" ]] || [[ "$status" == "planning" ]] || [[ "$status" == "in-progress" ]]; then
79
- increment_id=$(basename "$(dirname "$spec_file")")
80
- # Parse created date from spec.md YAML frontmatter
81
- created=$(grep -m1 "^created:" "$spec_file" 2>/dev/null | cut -d: -f2- | tr -d ' ' || echo "1970-01-01")
82
-
83
- # Write to temp file
84
- echo "$created $increment_id" >> "$TMP_FILE"
85
- fi
86
- fi
87
- done
58
+ # ============================================================================
59
+ # TTL CHECK (10 seconds - balanced for UX vs performance)
60
+ # ============================================================================
61
+ TTL_SECONDS=10
62
+
63
+ if [[ -f "$CACHE_FILE" ]]; then
64
+ if [[ "$(uname)" == "Darwin" ]]; then
65
+ CACHE_AGE=$(( $(date +%s) - $(stat -f %m "$CACHE_FILE" 2>/dev/null || echo 0) ))
66
+ else
67
+ CACHE_AGE=$(( $(date +%s) - $(stat -c %Y "$CACHE_FILE" 2>/dev/null || echo 0) ))
68
+ fi
69
+ [[ $CACHE_AGE -lt $TTL_SECONDS ]] && exit 0
88
70
  fi
89
71
 
90
- # Step 2: Count open increments
91
- OPEN_COUNT=$(wc -l < "$TMP_FILE" | tr -d ' ')
92
-
93
- if [[ $OPEN_COUNT -eq 0 ]]; then
94
- # No open increments
95
- jq -n '{
96
- current: null,
97
- openCount: 0,
98
- lastUpdate: (now | strftime("%Y-%m-%dT%H:%M:%SZ"))
99
- }' > "$CACHE_FILE"
100
- rm -f "$TMP_FILE"
72
+ # ============================================================================
73
+ # NO INCREMENTS? Write empty cache and exit
74
+ # ============================================================================
75
+ if [[ ! -d "$INCREMENTS_DIR" ]]; then
76
+ mkdir -p "$STATE_DIR"
77
+ printf '{"current":null,"openCount":0,"lastUpdate":"%s"}' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" > "$CACHE_FILE"
101
78
  exit 0
102
79
  fi
103
80
 
104
- # Step 3: Sort by timestamp (oldest first) and take first
105
- CURRENT_INCREMENT=$(sort "$TMP_FILE" | head -1 | awk '{print $2}')
81
+ # ============================================================================
82
+ # ACQUIRE LOCK
83
+ # ============================================================================
84
+ mkdir -p "$STATE_DIR"
85
+ echo $$ > "$LOCK_FILE"
86
+ trap 'rm -f "$LOCK_FILE"' EXIT
87
+
88
+ # ============================================================================
89
+ # FIND ACTIVE INCREMENTS (single find, no xargs)
90
+ # ============================================================================
91
+ ACTIVE_FILES=""
92
+ OPEN_COUNT=0
93
+ OLDEST_DATE="9999-99-99"
94
+ CURRENT_INCREMENT=""
106
95
 
107
- # Clean up temp file
108
- rm -f "$TMP_FILE"
96
+ while IFS= read -r spec_file; do
97
+ [[ -z "$spec_file" ]] && continue
109
98
 
110
- # Step 4: Parse current increment's tasks.md for progress
111
- TASKS_FILE="$INCREMENTS_DIR/$CURRENT_INCREMENT/tasks.md"
112
- TOTAL_TASKS=0
113
- COMPLETED_TASKS=0
114
- PERCENTAGE=0
99
+ # Quick status check with head + grep (faster than full file grep)
100
+ if head -20 "$spec_file" 2>/dev/null | grep -qE '^status:\s*(active|planning|in-progress)'; then
101
+ OPEN_COUNT=$((OPEN_COUNT + 1))
102
+ increment_id=$(basename "$(dirname "$spec_file")")
115
103
 
116
- if [[ -f "$TASKS_FILE" ]]; then
117
- # Use TaskCounter CLI for accurate counting (fixes overcounting bug)
118
- COUNT_TASKS_CLI="$PROJECT_ROOT/dist/src/cli/count-tasks.js"
104
+ # Get created date (first 30 lines only)
105
+ created=$(head -30 "$spec_file" 2>/dev/null | grep -m1 "^created:" | cut -d: -f2- | tr -d ' "' || echo "9999-99-99")
119
106
 
120
- if [[ -f "$COUNT_TASKS_CLI" ]]; then
121
- # Call CLI and parse JSON output
122
- TASK_COUNTS=$(node "$COUNT_TASKS_CLI" "$TASKS_FILE" 2>/dev/null || echo '{"total":0,"completed":0,"percentage":0}')
123
- TOTAL_TASKS=$(echo "$TASK_COUNTS" | jq -r '.total' 2>/dev/null || echo 0)
124
- COMPLETED_TASKS=$(echo "$TASK_COUNTS" | jq -r '.completed' 2>/dev/null || echo 0)
125
- PERCENTAGE=$(echo "$TASK_COUNTS" | jq -r '.percentage' 2>/dev/null || echo 0)
126
- else
127
- # Fallback to legacy counting if CLI not available (graceful degradation)
128
- # Count total tasks (## T- or ### T- headings)
129
- TOTAL_TASKS=$(grep -cE '^##+ T-' "$TASKS_FILE" 2>/dev/null || echo 0)
130
- TOTAL_TASKS=$(echo "$TOTAL_TASKS" | tr -d '\n\r ' || echo 0)
131
-
132
- # Count completed tasks - recognize all three completion marker formats:
133
- # 1. **Completed**: <date> (preferred format)
134
- # 2. **Status**: [x] (legacy format)
135
- # 3. [x] at line start (legacy checkbox format)
136
- COMPLETED_TASKS=$(grep -cE '(\*\*Completed\*\*:|\*\*Status\*\*:\s*\[x\]|^\[x\])' "$TASKS_FILE" 2>/dev/null || echo 0)
137
- COMPLETED_TASKS=$(echo "$COMPLETED_TASKS" | tr -d '\n\r ' || echo 0)
138
-
139
- # Calculate percentage
140
- if [[ $TOTAL_TASKS -gt 0 ]]; then
141
- PERCENTAGE=$((COMPLETED_TASKS * 100 / TOTAL_TASKS))
107
+ if [[ "$created" < "$OLDEST_DATE" ]]; then
108
+ OLDEST_DATE="$created"
109
+ CURRENT_INCREMENT="$increment_id"
142
110
  fi
111
+
112
+ ACTIVE_FILES="$ACTIVE_FILES $spec_file"
143
113
  fi
144
- fi
114
+ done < <(find "$INCREMENTS_DIR" -maxdepth 2 -name "spec.md" -not -path "*/_archive/*" 2>/dev/null)
145
115
 
146
- # Step 4b: Parse spec.md for AC (Acceptance Criteria) progress
147
- SPEC_FILE="$INCREMENTS_DIR/$CURRENT_INCREMENT/spec.md"
148
- TOTAL_ACS=0
149
- COMPLETED_ACS=0
150
- OPEN_ACS=0
151
-
152
- if [[ -f "$SPEC_FILE" ]]; then
153
- # Count total ACs: both checked and unchecked
154
- # Pattern: - [ ] **AC- OR - [x] **AC-
155
- TOTAL_ACS=$(grep -cE '^- \[(x| )\] \*\*AC-' "$SPEC_FILE" 2>/dev/null || echo 0)
156
- TOTAL_ACS=$(echo "$TOTAL_ACS" | tr -d '\n\r ' || echo 0)
157
-
158
- # Count completed ACs (checked)
159
- # Pattern: - [x] **AC-
160
- COMPLETED_ACS=$(grep -cE '^- \[x\] \*\*AC-' "$SPEC_FILE" 2>/dev/null || echo 0)
161
- COMPLETED_ACS=$(echo "$COMPLETED_ACS" | tr -d '\n\r ' || echo 0)
162
-
163
- # Count open ACs (unchecked)
164
- # Pattern: - [ ] **AC-
165
- OPEN_ACS=$(grep -cE '^- \[ \] \*\*AC-' "$SPEC_FILE" 2>/dev/null || echo 0)
166
- OPEN_ACS=$(echo "$OPEN_ACS" | tr -d '\n\r ' || echo 0)
116
+ # No active increments?
117
+ if [[ -z "$CURRENT_INCREMENT" ]]; then
118
+ printf '{"current":null,"openCount":0,"lastUpdate":"%s"}' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" > "$CACHE_FILE"
119
+ exit 0
167
120
  fi
168
121
 
169
- # Step 5: Extract increment ID and name
170
- # Format: XXXX-name where XXXX is 4-digit prefix (brackets added by manager)
171
- INCREMENT_ID=$(echo "$CURRENT_INCREMENT" | grep -oE '^[0-9]{4}')
172
- INCREMENT_NAME_ONLY=$(echo "$CURRENT_INCREMENT" | sed 's/^[0-9]\{4\}-//')
173
- INCREMENT_NAME="$INCREMENT_ID-$INCREMENT_NAME_ONLY"
174
-
175
- # Step 6: Write cache with atomic validation (v0.24.4 - prevents corruption)
176
- TMP_CACHE_FILE="$PROJECT_ROOT/.specweave/state/.status-line-tmp.json"
177
-
178
- # Validate all numeric inputs before jq (prevents jq failures)
179
- [[ "$TOTAL_TASKS" =~ ^[0-9]+$ ]] || TOTAL_TASKS=0
180
- [[ "$COMPLETED_TASKS" =~ ^[0-9]+$ ]] || COMPLETED_TASKS=0
181
- [[ "$PERCENTAGE" =~ ^[0-9]+$ ]] || PERCENTAGE=0
182
- [[ "$TOTAL_ACS" =~ ^[0-9]+$ ]] || TOTAL_ACS=0
183
- [[ "$COMPLETED_ACS" =~ ^[0-9]+$ ]] || COMPLETED_ACS=0
184
- [[ "$OPEN_ACS" =~ ^[0-9]+$ ]] || OPEN_ACS=0
185
- [[ "$OPEN_COUNT" =~ ^[0-9]+$ ]] || OPEN_COUNT=0
186
-
187
- # Generate cache to temp file first (atomic operation)
188
- if jq -n \
189
- --arg id "$CURRENT_INCREMENT" \
190
- --arg name "$INCREMENT_NAME" \
191
- --argjson completed "$COMPLETED_TASKS" \
192
- --argjson total "$TOTAL_TASKS" \
193
- --argjson percentage "$PERCENTAGE" \
194
- --argjson acsCompleted "$COMPLETED_ACS" \
195
- --argjson acsTotal "$TOTAL_ACS" \
196
- --argjson openCount "$OPEN_COUNT" \
197
- '{
198
- current: {
199
- id: $id,
200
- name: $name,
201
- completed: $completed,
202
- total: $total,
203
- percentage: $percentage,
204
- acsCompleted: $acsCompleted,
205
- acsTotal: $acsTotal
206
- },
207
- openCount: $openCount,
208
- lastUpdate: (now | strftime("%Y-%m-%dT%H:%M:%SZ"))
209
- }' > "$TMP_CACHE_FILE" 2>/dev/null; then
210
-
211
- # Validate generated JSON before replacing cache (corruption prevention)
212
- if jq empty "$TMP_CACHE_FILE" 2>/dev/null; then
213
- mv "$TMP_CACHE_FILE" "$CACHE_FILE"
214
- else
215
- # Invalid JSON generated - keep old cache
216
- echo "[$(date)] ERROR: Generated invalid JSON, keeping old cache" >> "$DEBUG_LOG" 2>/dev/null || true
217
- rm -f "$TMP_CACHE_FILE"
122
+ # ============================================================================
123
+ # MTIME CHECK (using find -newer - single syscall!)
124
+ # ============================================================================
125
+ MTIME_FILE="$STATE_DIR/.status-mtime-$CURRENT_INCREMENT"
126
+
127
+ if [[ -f "$MTIME_FILE" ]] && [[ -f "$CACHE_FILE" ]]; then
128
+ # Check if any relevant files are newer than our marker
129
+ TASKS_FILE="$INCREMENTS_DIR/$CURRENT_INCREMENT/tasks.md"
130
+ SPEC_FILE="$INCREMENTS_DIR/$CURRENT_INCREMENT/spec.md"
131
+
132
+ NEWER_FILES=$(find "$SPEC_FILE" "$TASKS_FILE" -newer "$MTIME_FILE" 2>/dev/null | head -1)
133
+
134
+ if [[ -z "$NEWER_FILES" ]]; then
135
+ # No changes - just touch cache to reset TTL
136
+ touch "$CACHE_FILE"
137
+ exit 0
218
138
  fi
219
- else
220
- # jq generation failed - keep old cache
221
- echo "[$(date)] ERROR: jq failed to generate status cache (exit $?)" >> "$DEBUG_LOG" 2>/dev/null || true
222
- rm -f "$TMP_CACHE_FILE"
223
139
  fi
224
140
 
141
+ # ============================================================================
142
+ # SINGLE-PASS COUNTING WITH AWK (replaces 5 grep calls!)
143
+ # ============================================================================
144
+ TASKS_FILE="$INCREMENTS_DIR/$CURRENT_INCREMENT/tasks.md"
145
+ SPEC_FILE="$INCREMENTS_DIR/$CURRENT_INCREMENT/spec.md"
146
+
147
+ # Count tasks with single awk call
148
+ read -r TOTAL_TASKS COMPLETED_TASKS < <(
149
+ awk '
150
+ /^###? T-/ { total++ }
151
+ /\*\*Completed\*\*:|\*\*Status\*\*:[ \t]*\[x\]/ { completed++ }
152
+ END { print total+0, completed+0 }
153
+ ' "$TASKS_FILE" 2>/dev/null || echo "0 0"
154
+ )
155
+
156
+ # Count ACs with single awk call
157
+ # Supports both formats: "- [ ] AC-US1-01:" and "- [ ] **AC-US1-01**:"
158
+ read -r TOTAL_ACS COMPLETED_ACS < <(
159
+ awk '
160
+ /^- \[(x| )\] (\*\*)?AC-/ { total++ }
161
+ /^- \[x\] (\*\*)?AC-/ { completed++ }
162
+ END { print total+0, completed+0 }
163
+ ' "$SPEC_FILE" 2>/dev/null || echo "0 0"
164
+ )
165
+
166
+ # Calculate percentage (pure bash)
167
+ PERCENTAGE=0
168
+ [[ ${TOTAL_TASKS:-0} -gt 0 ]] && PERCENTAGE=$((${COMPLETED_TASKS:-0} * 100 / TOTAL_TASKS))
169
+
170
+ # ============================================================================
171
+ # WRITE CACHE (PURE BASH - NO jq!)
172
+ # ============================================================================
173
+ # Sanitize values
174
+ TOTAL_TASKS=${TOTAL_TASKS:-0}
175
+ COMPLETED_TASKS=${COMPLETED_TASKS:-0}
176
+ TOTAL_ACS=${TOTAL_ACS:-0}
177
+ COMPLETED_ACS=${COMPLETED_ACS:-0}
178
+ OPEN_COUNT=${OPEN_COUNT:-0}
179
+ PERCENTAGE=${PERCENTAGE:-0}
180
+
181
+ # Generate JSON directly (avoids jq subprocess entirely!)
182
+ cat > "$CACHE_FILE" << EOF
183
+ {"current":{"id":"$CURRENT_INCREMENT","name":"$CURRENT_INCREMENT","completed":$COMPLETED_TASKS,"total":$TOTAL_TASKS,"percentage":$PERCENTAGE,"acsCompleted":$COMPLETED_ACS,"acsTotal":$TOTAL_ACS},"openCount":$OPEN_COUNT,"lastUpdate":"$(date -u +%Y-%m-%dT%H:%M:%SZ)"}
184
+ EOF
185
+
186
+ # Update mtime marker
187
+ touch "$MTIME_FILE"
188
+
225
189
  exit 0
@@ -49,6 +49,12 @@ find_project_root() {
49
49
  }
50
50
 
51
51
  PROJECT_ROOT=$(find_project_root)
52
+
53
+ # ULTRA-FAST EARLY EXIT FOR NON-SPECWEAVE PROJECTS (T-006 - v0.26.15)
54
+ if [[ ! -d "$PROJECT_ROOT/.specweave" ]]; then
55
+ exit 0
56
+ fi
57
+
52
58
  LOGS_DIR="$PROJECT_ROOT/.specweave/logs"
53
59
  DEBUG_LOG="$LOGS_DIR/hooks-debug.log"
54
60
 
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env bash
2
+ # SpecWeave Post-First-Increment Hook
3
+ #
4
+ # Triggers after the first increment is completed
5
+ # Congratulates the user on completing their first increment
6
+ #
7
+ # NON-INTERACTIVE: Just shows a message (hooks run in background)
8
+
9
+ set -euo pipefail
10
+
11
+ # Get project root (where .specweave/ lives)
12
+ PROJECT_ROOT="$(pwd)"
13
+
14
+ # Check if .specweave directory exists
15
+ if [ ! -d ".specweave" ]; then
16
+ # Not in SpecWeave project, skip
17
+ exit 0
18
+ fi
19
+
20
+ # Check if this is the first increment completion
21
+ # Count completed increments in .specweave/increments/
22
+ COMPLETED_COUNT=0
23
+ if [ -d ".specweave/increments" ]; then
24
+ # Count directories that have COMPLETION-REPORT.md or completion metadata
25
+ for inc_dir in .specweave/increments/[0-9][0-9][0-9][0-9]-*/; do
26
+ if [ -d "$inc_dir" ]; then
27
+ if [ -f "${inc_dir}reports/COMPLETION-REPORT.md" ] || \
28
+ [ -f "${inc_dir}COMPLETION-SUMMARY.md" ] || \
29
+ ([ -f "${inc_dir}metadata.json" ] && grep -q '"status".*"completed"' "${inc_dir}metadata.json" 2>/dev/null); then
30
+ COMPLETED_COUNT=$((COMPLETED_COUNT + 1))
31
+ fi
32
+ fi
33
+ done
34
+ fi
35
+
36
+ # Only trigger on first completion
37
+ if [ "$COMPLETED_COUNT" -ne 1 ]; then
38
+ exit 0
39
+ fi
40
+
41
+ # Show congratulations message (non-interactive)
42
+ echo ""
43
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
44
+ echo "🎉 Congratulations! You completed your first increment!"
45
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
46
+ echo ""
47
+ echo "✅ Your increment has been documented in:"
48
+ echo " .specweave/increments/[increment-id]/"
49
+ echo ""
50
+ echo "📚 View your documentation:"
51
+ echo " - Specs: .specweave/docs/internal/specs/"
52
+ echo " - Architecture: .specweave/docs/internal/architecture/"
53
+ echo ""
54
+ echo "🚀 Next steps:"
55
+ echo " - Review your increment: /specweave:status"
56
+ echo " - Start next increment: /specweave:increment \"feature name\""
57
+ echo ""
58
+ echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
59
+ echo ""
60
+
61
+ exit 0
@@ -0,0 +1,98 @@
1
+ #!/bin/bash
2
+
3
+ # SpecWeave Post-Increment-Change Hook
4
+ # Runs automatically after increment files (spec.md, plan.md, tasks.md) are modified
5
+ #
6
+ # Trigger: File watcher or git hook (pre-commit/post-commit)
7
+ # Purpose: Sync increment file changes to GitHub issues
8
+ #
9
+ # What it does:
10
+ # 1. Detects which file changed (spec.md, plan.md, tasks.md)
11
+ # 2. Invokes GitHub sync for increment changes
12
+ # 3. Updates GitHub issue with scope/plan/task changes
13
+ #
14
+ # Usage:
15
+ # ./post-increment-change.sh <incrementId> <changedFile>
16
+ #
17
+ # Example:
18
+ # ./post-increment-change.sh 0015-hierarchical-sync spec.md
19
+
20
+ set -e
21
+
22
+ # Find project root
23
+ find_project_root() {
24
+ local dir="$1"
25
+ while [ "$dir" != "/" ]; do
26
+ if [ -d "$dir/.specweave" ]; then
27
+ echo "$dir"
28
+ return 0
29
+ fi
30
+ dir="$(dirname "$dir")"
31
+ done
32
+ pwd
33
+ }
34
+
35
+ PROJECT_ROOT="$(find_project_root "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)")"
36
+ cd "$PROJECT_ROOT" 2>/dev/null || true
37
+
38
+ # Configuration
39
+ LOGS_DIR=".specweave/logs"
40
+ DEBUG_LOG="$LOGS_DIR/hooks-debug.log"
41
+
42
+ mkdir -p "$LOGS_DIR" 2>/dev/null || true
43
+
44
+ # Arguments
45
+ INCREMENT_ID="$1"
46
+ CHANGED_FILE="$2"
47
+
48
+ if [ -z "$INCREMENT_ID" ] || [ -z "$CHANGED_FILE" ]; then
49
+ echo "Usage: $0 <incrementId> <changedFile>" >&2
50
+ echo "Example: $0 0015-hierarchical-sync spec.md" >&2
51
+ exit 1
52
+ fi
53
+
54
+ echo "[$(date)] 📝 Increment file changed: $CHANGED_FILE" >> "$DEBUG_LOG" 2>/dev/null || true
55
+
56
+ # Validate changed file
57
+ case "$CHANGED_FILE" in
58
+ spec.md|plan.md|tasks.md)
59
+ ;;
60
+ *)
61
+ echo "[$(date)] ⚠️ Unknown file type: $CHANGED_FILE (skipping sync)" >> "$DEBUG_LOG" 2>/dev/null || true
62
+ exit 0
63
+ ;;
64
+ esac
65
+
66
+ # Check if Node.js available
67
+ if ! command -v node &> /dev/null; then
68
+ echo "[$(date)] ⚠️ Node.js not found, skipping sync" >> "$DEBUG_LOG" 2>/dev/null || true
69
+ exit 0
70
+ fi
71
+
72
+ # Check if GitHub CLI available
73
+ if ! command -v gh &> /dev/null; then
74
+ echo "[$(date)] ℹ️ GitHub CLI not found, skipping sync" >> "$DEBUG_LOG" 2>/dev/null || true
75
+ exit 0
76
+ fi
77
+
78
+ # Check if authenticated
79
+ if ! gh auth status &> /dev/null; then
80
+ echo "[$(date)] ℹ️ GitHub CLI not authenticated, skipping sync" >> "$DEBUG_LOG" 2>/dev/null || true
81
+ exit 0
82
+ fi
83
+
84
+ # Sync to GitHub
85
+ echo "[$(date)] 🔄 Syncing $CHANGED_FILE changes to GitHub..." >> "$DEBUG_LOG" 2>/dev/null || true
86
+
87
+ node dist/plugins/specweave-github/lib/github-sync-increment-changes.js "$INCREMENT_ID" "$CHANGED_FILE" 2>&1 | tee -a "$DEBUG_LOG" >/dev/null || {
88
+ echo "[$(date)] ⚠️ Failed to sync increment changes (non-blocking)" >> "$DEBUG_LOG" 2>/dev/null || true
89
+ }
90
+
91
+ echo "[$(date)] ✅ Post-increment-change hook complete" >> "$DEBUG_LOG" 2>/dev/null || true
92
+
93
+ # Update status line cache (increment changed)
94
+ HOOK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
95
+ bash "$HOOK_DIR/lib/update-status-line.sh" 2>/dev/null || true
96
+
97
+ # Return success (non-blocking)
98
+ exit 0