specweave 1.0.30 → 1.0.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/CLAUDE.md +140 -1235
  2. package/bin/specweave.js +23 -0
  3. package/dist/plugins/specweave-github/lib/github-client-v2.d.ts +3 -0
  4. package/dist/plugins/specweave-github/lib/github-client-v2.d.ts.map +1 -1
  5. package/dist/plugins/specweave-github/lib/github-client-v2.js +39 -0
  6. package/dist/plugins/specweave-github/lib/github-client-v2.js.map +1 -1
  7. package/dist/src/cli/commands/set-sync-target.d.ts +41 -0
  8. package/dist/src/cli/commands/set-sync-target.d.ts.map +1 -0
  9. package/dist/src/cli/commands/set-sync-target.js +126 -0
  10. package/dist/src/cli/commands/set-sync-target.js.map +1 -0
  11. package/dist/src/cli/helpers/issue-tracker/github-multi-repo.d.ts.map +1 -1
  12. package/dist/src/cli/helpers/issue-tracker/github-multi-repo.js +5 -8
  13. package/dist/src/cli/helpers/issue-tracker/github-multi-repo.js.map +1 -1
  14. package/dist/src/core/hooks/HookScanner.d.ts +32 -0
  15. package/dist/src/core/hooks/HookScanner.d.ts.map +1 -1
  16. package/dist/src/core/hooks/HookScanner.js +125 -1
  17. package/dist/src/core/hooks/HookScanner.js.map +1 -1
  18. package/dist/src/core/hooks/types.d.ts +10 -1
  19. package/dist/src/core/hooks/types.d.ts.map +1 -1
  20. package/dist/src/core/increment/metadata-manager.d.ts +67 -1
  21. package/dist/src/core/increment/metadata-manager.d.ts.map +1 -1
  22. package/dist/src/core/increment/metadata-manager.js +93 -0
  23. package/dist/src/core/increment/metadata-manager.js.map +1 -1
  24. package/dist/src/core/project/index.d.ts +21 -0
  25. package/dist/src/core/project/index.d.ts.map +1 -0
  26. package/dist/src/core/project/index.js +22 -0
  27. package/dist/src/core/project/index.js.map +1 -0
  28. package/dist/src/core/project/project-service.d.ts +122 -0
  29. package/dist/src/core/project/project-service.d.ts.map +1 -0
  30. package/dist/src/core/project/project-service.js +334 -0
  31. package/dist/src/core/project/project-service.js.map +1 -0
  32. package/dist/src/core/sync/external-tool-resolver.d.ts +171 -0
  33. package/dist/src/core/sync/external-tool-resolver.d.ts.map +1 -0
  34. package/dist/src/core/sync/external-tool-resolver.js +569 -0
  35. package/dist/src/core/sync/external-tool-resolver.js.map +1 -0
  36. package/dist/src/core/types/increment-metadata.d.ts +92 -0
  37. package/dist/src/core/types/increment-metadata.d.ts.map +1 -1
  38. package/dist/src/hooks/processor.d.ts +7 -3
  39. package/dist/src/hooks/processor.d.ts.map +1 -1
  40. package/dist/src/hooks/processor.js +11 -5
  41. package/dist/src/hooks/processor.js.map +1 -1
  42. package/package.json +1 -1
  43. package/plugins/specweave/hooks/hooks.json +0 -69
  44. package/plugins/specweave/hooks/v2/handlers/project-bridge-handler.sh +96 -0
  45. package/plugins/specweave/hooks/v2/queue/processor.sh +13 -5
  46. package/plugins/specweave/lib/hooks/project-bridge.js +76 -0
  47. package/plugins/specweave/lib/hooks/update-tasks-md.js +0 -0
  48. package/plugins/specweave/lib/hooks/us-completion-orchestrator.js +0 -0
  49. package/plugins/specweave/lib/vendor/core/increment/metadata-manager.d.ts +67 -1
  50. package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js +93 -0
  51. package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js.map +1 -1
  52. package/plugins/specweave/lib/vendor/core/types/increment-metadata.d.ts +92 -0
  53. package/plugins/specweave-github/lib/github-client-v2.js +39 -0
  54. package/plugins/specweave-github/lib/github-client-v2.ts +44 -0
  55. package/plugins/specweave/hooks/docs-changed.sh +0 -87
  56. package/plugins/specweave/hooks/human-input-required.sh +0 -83
  57. package/plugins/specweave/hooks/post-edit-write-consolidated.sh +0 -428
  58. package/plugins/specweave/hooks/post-first-increment.sh +0 -61
  59. package/plugins/specweave/hooks/post-increment-change.sh +0 -103
  60. package/plugins/specweave/hooks/post-increment-completion.sh +0 -513
  61. package/plugins/specweave/hooks/post-increment-planning.sh +0 -1204
  62. package/plugins/specweave/hooks/post-increment-status-change.sh +0 -243
  63. package/plugins/specweave/hooks/post-metadata-change.sh +0 -246
  64. package/plugins/specweave/hooks/post-spec-update.sh +0 -158
  65. package/plugins/specweave/hooks/post-task-completion.sh +0 -557
  66. package/plugins/specweave/hooks/post-task-edit.sh +0 -47
  67. package/plugins/specweave/hooks/post-user-story-complete.sh +0 -230
  68. package/plugins/specweave/hooks/pre-command-deduplication.sh +0 -68
  69. package/plugins/specweave/hooks/pre-edit-write-consolidated.sh +0 -225
  70. package/plugins/specweave/hooks/pre-implementation.sh +0 -75
  71. package/plugins/specweave/hooks/pre-increment-start.sh +0 -173
  72. package/plugins/specweave/hooks/pre-task-completion-edit.sh +0 -355
  73. package/plugins/specweave/hooks/pre-task-completion.sh +0 -269
  74. package/plugins/specweave/hooks/pre-tool-use.sh +0 -137
  75. package/plugins/specweave/hooks/session-start-reconcile.sh +0 -139
  76. package/plugins/specweave/hooks/shared/bulk-operation-detector.sh +0 -167
  77. package/plugins/specweave/hooks/test-pretooluse-env.sh +0 -72
  78. package/plugins/specweave/hooks/validate-increment-completion.sh +0 -113
  79. package/plugins/specweave/lib/hooks/consolidated-sync.js +0 -288
@@ -1,173 +0,0 @@
1
- #!/bin/bash
2
- #
3
- # Pre-Increment-Start Hook
4
- #
5
- # Validates increment readiness before allowing `/sw:do` to start work.
6
- #
7
- # **Critical Checks**:
8
- # 1. spec.md contains Acceptance Criteria
9
- # 2. AC count matches metadata.json
10
- # 3. No duplicate task files (tasks.md, tasks-detailed.md)
11
- # 4. Increment structure is valid
12
- #
13
- # **Triggered by**: /sw:do command (before starting implementation)
14
- #
15
- # **Exit codes**:
16
- # - 0: Validation passed, safe to start
17
- # - 1: Validation failed, BLOCK start
18
- #
19
- # **See**: ADR-0062 (AC Embedding Architecture)
20
- #
21
-
22
- # CRITICAL (v0.25.2): NEVER use 'set -e' in hooks - causes Claude Code crashes
23
- # See: CLAUDE.md Section 9a (Hook Safety Checklist), ADR-0060 (Hook Performance)
24
- # NOTE: This validation hook explicitly exits with code 1 on failures (controlled behavior)
25
- set +e # Allow commands to fail without terminating script
26
- set -u # Fail on undefined variables
27
- set -o pipefail # Fail if any command in pipeline fails
28
-
29
- # Get increment path from environment or argument
30
- INCREMENT_PATH="${1:-}"
31
-
32
- if [ -z "$INCREMENT_PATH" ]; then
33
- echo "❌ Error: INCREMENT_PATH not provided"
34
- echo "Usage: $0 <increment-path>"
35
- exit 1
36
- fi
37
-
38
- if [ ! -d "$INCREMENT_PATH" ]; then
39
- echo "❌ Error: Increment path not found: $INCREMENT_PATH"
40
- exit 1
41
- fi
42
-
43
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
44
- echo "PRE-START VALIDATION: $(basename "$INCREMENT_PATH")"
45
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
46
- echo ""
47
-
48
- VALIDATION_PASSED=true
49
-
50
- # Check 1: spec.md exists
51
- if [ ! -f "$INCREMENT_PATH/spec.md" ]; then
52
- echo "❌ CRITICAL: spec.md not found"
53
- VALIDATION_PASSED=false
54
- fi
55
-
56
- # Check 2: Acceptance Criteria section exists
57
- if [ -f "$INCREMENT_PATH/spec.md" ]; then
58
- if ! grep -q "## Acceptance Criteria" "$INCREMENT_PATH/spec.md"; then
59
- echo "❌ CRITICAL: spec.md missing '## Acceptance Criteria' section"
60
- echo ""
61
- echo " spec.md MUST contain inline Acceptance Criteria for AC sync to work."
62
- echo " Even when using 'structure: user-stories', ACs must be embedded in spec.md."
63
- echo ""
64
- echo " 💡 SUGGESTED FIX:"
65
- echo " Run: /sw:embed-acs $(basename "$INCREMENT_PATH")"
66
- echo ""
67
- VALIDATION_PASSED=false
68
- fi
69
- fi
70
-
71
- # Check 3: Count ACs in spec.md
72
- AC_COUNT=0
73
- if [ -f "$INCREMENT_PATH/spec.md" ]; then
74
- AC_COUNT=$(grep -cE "^- \[[x ]\] \*\*AC-US[0-9]+-[0-9]+\*\*:" "$INCREMENT_PATH/spec.md" || true)
75
- fi
76
-
77
- if [ "$AC_COUNT" -eq 0 ]; then
78
- echo "❌ CRITICAL: spec.md contains 0 Acceptance Criteria"
79
- echo ""
80
- echo " Cannot start increment with no ACs defined."
81
- echo ""
82
- echo " 💡 SUGGESTED FIX:"
83
- echo " 1. Add ACs manually to spec.md, OR"
84
- echo " 2. Run: /sw:embed-acs $(basename "$INCREMENT_PATH") (auto-embed from living docs)"
85
- echo ""
86
- VALIDATION_PASSED=false
87
- else
88
- echo "✅ Acceptance Criteria: $AC_COUNT ACs found in spec.md"
89
- fi
90
-
91
- # Check 4: Validate AC count matches metadata.json
92
- if [ -f "$INCREMENT_PATH/metadata.json" ]; then
93
- EXPECTED_AC_COUNT=$(jq -r '.total_acs // 0' "$INCREMENT_PATH/metadata.json")
94
-
95
- if [ "$EXPECTED_AC_COUNT" -ne "$AC_COUNT" ]; then
96
- echo "⚠️ WARNING: AC count mismatch"
97
- echo " spec.md: $AC_COUNT ACs"
98
- echo " metadata.json: $EXPECTED_AC_COUNT ACs"
99
- echo ""
100
- echo " This may cause status line desyncs."
101
- echo ""
102
- # Don't block, just warn (metadata.json may be stale)
103
- else
104
- echo "✅ AC Count: Matches metadata.json ($EXPECTED_AC_COUNT ACs)"
105
- fi
106
- fi
107
-
108
- # Check 5: No duplicate task files
109
- TASK_FILES=$(find "$INCREMENT_PATH" -maxdepth 1 -name "tasks*.md" -type f | wc -l)
110
- if [ "$TASK_FILES" -gt 1 ]; then
111
- echo "❌ CRITICAL: Multiple task files detected (MUST be only ONE tasks.md)"
112
- find "$INCREMENT_PATH" -maxdepth 1 -name "tasks*.md" -type f -exec basename {} \;
113
- echo ""
114
- echo " 💡 SUGGESTED FIX:"
115
- echo " Keep tasks.md, move others to reports/ directory"
116
- echo ""
117
- VALIDATION_PASSED=false
118
- else
119
- echo "✅ Structure: Single tasks.md file (no duplicates)"
120
- fi
121
-
122
- # Check 6: Validate frontmatter for 'structure: user-stories' pattern
123
- if [ -f "$INCREMENT_PATH/spec.md" ]; then
124
- if grep -q "structure: user-stories" "$INCREMENT_PATH/spec.md"; then
125
- if [ "$AC_COUNT" -eq 0 ]; then
126
- echo ""
127
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
128
- echo "🚨 CRITICAL ARCHITECTURE VIOLATION"
129
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
130
- echo ""
131
- echo "spec.md uses 'structure: user-stories' but contains NO inline ACs."
132
- echo ""
133
- echo "**Why this is critical**:"
134
- echo " - AC sync hooks expect ACs in spec.md (not living docs)"
135
- echo " - Without inline ACs, status line will show 0% completion"
136
- echo " - Tasks-to-AC traceability will be broken"
137
- echo ""
138
- echo "**Architecture requirement (ADR-0062)**:"
139
- echo " spec.md is ALWAYS the source of truth for ACs,"
140
- echo " even when living docs exist as documentation layer."
141
- echo ""
142
- echo "**IMMEDIATE ACTION REQUIRED**:"
143
- echo " Run: /sw:embed-acs $(basename "$INCREMENT_PATH")"
144
- echo ""
145
- echo "This will auto-embed ACs from living docs into spec.md."
146
- echo ""
147
- VALIDATION_PASSED=false
148
- else
149
- echo "✅ Living Docs Structure: ACs properly embedded in spec.md"
150
- fi
151
- fi
152
- fi
153
-
154
- echo ""
155
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
156
-
157
- if [ "$VALIDATION_PASSED" = true ]; then
158
- echo "✅ PRE-START VALIDATION: PASSED"
159
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
160
- echo ""
161
- echo "Increment is ready to start. Proceeding with /sw:do..."
162
- exit 0
163
- else
164
- echo "❌ PRE-START VALIDATION: FAILED"
165
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
166
- echo ""
167
- echo "Cannot start increment due to validation failures above."
168
- echo ""
169
- echo "Fix the issues and try again, or run validation manually:"
170
- echo " /sw:validate $(basename "$INCREMENT_PATH")"
171
- echo ""
172
- exit 1
173
- fi
@@ -1,355 +0,0 @@
1
- #!/bin/bash
2
- #
3
- # pre-task-completion-edit.sh (v1.0.0)
4
- #
5
- # QUALITY GATE: Validates AC completion BEFORE allowing task status change to completed
6
- #
7
- # Triggered by: PreToolUse:Edit on .specweave/increments/*/tasks.md
8
- #
9
- # WORKFLOW:
10
- # =========
11
- # 1. Edit tool called on tasks.md
12
- # 2. This hook fires (PreToolUse - BEFORE the edit executes)
13
- # 3. Detect if edit is marking a task as completed ([ ] → [x])
14
- # 4. Extract task's "Satisfies ACs" (e.g., AC-US1-01, AC-US1-02)
15
- # 5. Verify those ACs are checked [x] in spec.md
16
- # 6. If ACs verified → Allow edit (continue: true)
17
- # 7. If ACs NOT verified → BLOCK edit (continue: false)
18
- #
19
- # ENFORCEMENT:
20
- # ============
21
- # Tasks CANNOT be marked complete unless their linked ACs are verified.
22
- # This is the ONLY enforcement point - cannot be bypassed.
23
- #
24
- # See: ADR-0xxx (AC Verification Quality Gate)
25
-
26
- set +e # CRITICAL: Never crash Claude Code
27
-
28
- # ============================================================================
29
- # EMERGENCY KILL SWITCH
30
- # ============================================================================
31
- if [[ "${SPECWEAVE_DISABLE_HOOKS:-0}" == "1" ]]; then
32
- cat <<EOF
33
- {"continue": true}
34
- EOF
35
- exit 0
36
- fi
37
-
38
- # ============================================================================
39
- # RECURSION PREVENTION (v0.26.0 pattern)
40
- # ============================================================================
41
- find_project_root() {
42
- local dir="$PWD"
43
- while [[ "$dir" != "/" ]]; do
44
- if [[ -d "$dir/.specweave" ]]; then
45
- echo "$dir"
46
- return 0
47
- fi
48
- dir=$(dirname "$dir")
49
- done
50
- echo "$PWD"
51
- }
52
-
53
- PROJECT_ROOT=$(find_project_root)
54
-
55
- if [[ ! -d "$PROJECT_ROOT/.specweave" ]]; then
56
- cat <<EOF
57
- {"continue": true}
58
- EOF
59
- exit 0
60
- fi
61
-
62
- RECURSION_GUARD_FILE="$PROJECT_ROOT/.specweave/state/.hook-recursion-guard"
63
- if [[ -f "$RECURSION_GUARD_FILE" ]]; then
64
- cat <<EOF
65
- {"continue": true}
66
- EOF
67
- exit 0
68
- fi
69
-
70
- # ============================================================================
71
- # SETUP
72
- # ============================================================================
73
- LOGS_DIR="$PROJECT_ROOT/.specweave/logs"
74
- DEBUG_LOG="$LOGS_DIR/hooks-debug.log"
75
- mkdir -p "$LOGS_DIR" 2>/dev/null || true
76
-
77
- echo "[$(date)] pre-task-completion-edit: Hook triggered" >> "$DEBUG_LOG" 2>/dev/null || true
78
-
79
- # ============================================================================
80
- # CAPTURE INPUT (PreToolUse receives tool_input JSON)
81
- # ============================================================================
82
- STDIN_DATA=$(cat)
83
-
84
- # Log the input for debugging
85
- echo "[$(date)] pre-task-completion-edit: Input: $STDIN_DATA" >> "$DEBUG_LOG" 2>/dev/null || true
86
-
87
- # ============================================================================
88
- # EXTRACT EDIT DETAILS
89
- # ============================================================================
90
- # PreToolUse:Edit receives: { "tool_input": { "file_path": "...", "old_string": "...", "new_string": "..." } }
91
-
92
- FILE_PATH=""
93
- OLD_STRING=""
94
- NEW_STRING=""
95
-
96
- if command -v jq &>/dev/null; then
97
- FILE_PATH=$(echo "$STDIN_DATA" | jq -r '.tool_input.file_path // empty' 2>/dev/null || echo "")
98
- OLD_STRING=$(echo "$STDIN_DATA" | jq -r '.tool_input.old_string // empty' 2>/dev/null || echo "")
99
- NEW_STRING=$(echo "$STDIN_DATA" | jq -r '.tool_input.new_string // empty' 2>/dev/null || echo "")
100
- else
101
- # Fallback: basic regex extraction
102
- FILE_PATH=$(echo "$STDIN_DATA" | grep -o '"file_path"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)".*/\1/' || echo "")
103
- fi
104
-
105
- echo "[$(date)] pre-task-completion-edit: file_path=$FILE_PATH" >> "$DEBUG_LOG" 2>/dev/null || true
106
-
107
- # ============================================================================
108
- # EARLY EXIT: Only process tasks.md in active increments
109
- # ============================================================================
110
- if [[ "$FILE_PATH" != *"/tasks.md" ]]; then
111
- echo "[$(date)] pre-task-completion-edit: Not tasks.md, skipping" >> "$DEBUG_LOG" 2>/dev/null || true
112
- cat <<EOF
113
- {"continue": true}
114
- EOF
115
- exit 0
116
- fi
117
-
118
- if [[ "$FILE_PATH" != *"/.specweave/increments/"* ]] && [[ "$FILE_PATH" != *".specweave/increments/"* ]]; then
119
- echo "[$(date)] pre-task-completion-edit: Not in increments/, skipping" >> "$DEBUG_LOG" 2>/dev/null || true
120
- cat <<EOF
121
- {"continue": true}
122
- EOF
123
- exit 0
124
- fi
125
-
126
- if [[ "$FILE_PATH" == *"/_archive/"* ]]; then
127
- echo "[$(date)] pre-task-completion-edit: Archived increment, skipping" >> "$DEBUG_LOG" 2>/dev/null || true
128
- cat <<EOF
129
- {"continue": true}
130
- EOF
131
- exit 0
132
- fi
133
-
134
- # ============================================================================
135
- # DETECT TASK COMPLETION PATTERN
136
- # ============================================================================
137
- # Check if edit is changing status from pending to completed:
138
- # old_string: "**Status**: [ ] pending" or "**Status**: [ ]"
139
- # new_string: "**Status**: [x] completed" or "**Status**: [x]"
140
-
141
- IS_COMPLETION=false
142
-
143
- # Pattern 1: Full status line change
144
- if [[ "$OLD_STRING" == *"**Status**:"*"[ ]"* ]] && [[ "$NEW_STRING" == *"**Status**:"*"[x]"* ]]; then
145
- IS_COMPLETION=true
146
- fi
147
-
148
- # Pattern 2: Just checkbox change (some formats)
149
- if [[ "$OLD_STRING" == *"[ ] pending"* ]] && [[ "$NEW_STRING" == *"[x] completed"* ]]; then
150
- IS_COMPLETION=true
151
- fi
152
-
153
- # Pattern 3: Minimal checkbox change
154
- if [[ "$OLD_STRING" == "[ ]"* ]] && [[ "$NEW_STRING" == "[x]"* ]]; then
155
- IS_COMPLETION=true
156
- fi
157
-
158
- if [[ "$IS_COMPLETION" != "true" ]]; then
159
- echo "[$(date)] pre-task-completion-edit: Not a completion edit, allowing" >> "$DEBUG_LOG" 2>/dev/null || true
160
- cat <<EOF
161
- {"continue": true}
162
- EOF
163
- exit 0
164
- fi
165
-
166
- echo "[$(date)] pre-task-completion-edit: COMPLETION DETECTED - Validating ACs" >> "$DEBUG_LOG" 2>/dev/null || true
167
-
168
- # ============================================================================
169
- # EXTRACT INCREMENT AND TASK INFO
170
- # ============================================================================
171
- # Parse increment name from file path
172
- # Pattern: .specweave/increments/XXXX-name/tasks.md
173
- INCREMENT_NAME=$(echo "$FILE_PATH" | grep -o '[0-9]\{4\}-[a-zA-Z0-9_-]*' | tail -1 || echo "")
174
- INCREMENT_DIR="$PROJECT_ROOT/.specweave/increments/$INCREMENT_NAME"
175
- TASKS_FILE="$INCREMENT_DIR/tasks.md"
176
- SPEC_FILE="$INCREMENT_DIR/spec.md"
177
-
178
- echo "[$(date)] pre-task-completion-edit: INCREMENT=$INCREMENT_NAME" >> "$DEBUG_LOG" 2>/dev/null || true
179
-
180
- if [[ -z "$INCREMENT_NAME" ]] || [[ ! -f "$TASKS_FILE" ]]; then
181
- echo "[$(date)] pre-task-completion-edit: Cannot find increment, allowing (safety)" >> "$DEBUG_LOG" 2>/dev/null || true
182
- cat <<EOF
183
- {"continue": true}
184
- EOF
185
- exit 0
186
- fi
187
-
188
- if [[ ! -f "$SPEC_FILE" ]]; then
189
- echo "[$(date)] pre-task-completion-edit: No spec.md found, allowing (no ACs to verify)" >> "$DEBUG_LOG" 2>/dev/null || true
190
- cat <<EOF
191
- {"continue": true}
192
- EOF
193
- exit 0
194
- fi
195
-
196
- # ============================================================================
197
- # FIND TASK BEING COMPLETED
198
- # ============================================================================
199
- # We need to find which task is being completed by matching the old_string context
200
- # Look for the task section that contains this status line
201
-
202
- # Extract task context from old_string (task header should be nearby)
203
- # First, read tasks.md and find the task that contains this status line
204
-
205
- TASK_ID=""
206
- SATISFIES_ACS=""
207
-
208
- # Read tasks.md and find the task context
209
- TASKS_CONTENT=$(cat "$TASKS_FILE" 2>/dev/null || echo "")
210
-
211
- # Find the task ID that precedes the old_string status
212
- # Tasks follow pattern:
213
- # ### T-XXX: Title
214
- # **User Story**: US-XXX
215
- # **Satisfies ACs**: AC-XXX-YY, AC-XXX-ZZ
216
- # **Status**: [ ] pending
217
-
218
- # Strategy: Search for task header, then check if its status matches old_string
219
- CURRENT_TASK_ID=""
220
- CURRENT_ACS=""
221
- IN_TASK=false
222
-
223
- while IFS= read -r line; do
224
- # Detect task header
225
- if [[ "$line" =~ ^###[[:space:]]+(T-[0-9]+): ]]; then
226
- CURRENT_TASK_ID="${BASH_REMATCH[1]}"
227
- CURRENT_ACS=""
228
- IN_TASK=true
229
- fi
230
-
231
- # Capture Satisfies ACs
232
- if [[ "$IN_TASK" == "true" ]] && [[ "$line" == *"**Satisfies ACs**:"* ]]; then
233
- # Extract AC IDs (comma-separated)
234
- CURRENT_ACS=$(echo "$line" | sed 's/.*\*\*Satisfies ACs\*\*:[[:space:]]*//' | tr -d '\r')
235
- fi
236
-
237
- # Check if this task's status matches what we're editing
238
- if [[ "$IN_TASK" == "true" ]] && [[ "$line" == *"**Status**:"*"[ ]"* ]]; then
239
- # This task is pending - check if the old_string matches
240
- # Compare by checking if old_string is a substring of status line
241
- if [[ "$OLD_STRING" == *"[ ]"* ]]; then
242
- TASK_ID="$CURRENT_TASK_ID"
243
- SATISFIES_ACS="$CURRENT_ACS"
244
- # Don't break - continue to find the EXACT task
245
- fi
246
- fi
247
-
248
- # Task boundary
249
- if [[ "$line" == "---" ]]; then
250
- IN_TASK=false
251
- fi
252
- done <<< "$TASKS_CONTENT"
253
-
254
- echo "[$(date)] pre-task-completion-edit: TASK_ID=$TASK_ID, SATISFIES_ACS=$SATISFIES_ACS" >> "$DEBUG_LOG" 2>/dev/null || true
255
-
256
- if [[ -z "$TASK_ID" ]]; then
257
- echo "[$(date)] pre-task-completion-edit: Could not identify task, allowing (safety)" >> "$DEBUG_LOG" 2>/dev/null || true
258
- cat <<EOF
259
- {"continue": true, "systemMessage": "Warning: Could not identify which task is being completed. AC verification skipped."}
260
- EOF
261
- exit 0
262
- fi
263
-
264
- if [[ -z "$SATISFIES_ACS" ]]; then
265
- echo "[$(date)] pre-task-completion-edit: Task has no ACs, allowing" >> "$DEBUG_LOG" 2>/dev/null || true
266
- cat <<EOF
267
- {"continue": true, "systemMessage": "Task $TASK_ID has no Acceptance Criteria linked. Consider adding 'Satisfies ACs' field."}
268
- EOF
269
- exit 0
270
- fi
271
-
272
- # ============================================================================
273
- # VERIFY ACs IN spec.md
274
- # ============================================================================
275
- # Check that each AC in SATISFIES_ACS is marked [x] (checked) in spec.md
276
- # AC format in spec.md:
277
- # - [x] **AC-US1-01**: Description...
278
- # - [ ] **AC-US1-02**: Description...
279
-
280
- SPEC_CONTENT=$(cat "$SPEC_FILE" 2>/dev/null || echo "")
281
- FAILED_ACS=""
282
- PASSED_ACS=""
283
-
284
- # Parse comma-separated ACs
285
- IFS=',' read -ra AC_ARRAY <<< "$SATISFIES_ACS"
286
-
287
- for ac_raw in "${AC_ARRAY[@]}"; do
288
- # Trim whitespace
289
- AC_ID=$(echo "$ac_raw" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
290
-
291
- if [[ -z "$AC_ID" ]]; then
292
- continue
293
- fi
294
-
295
- echo "[$(date)] pre-task-completion-edit: Checking AC: $AC_ID" >> "$DEBUG_LOG" 2>/dev/null || true
296
-
297
- # Check if AC is checked [x] in spec.md
298
- # Look for: - [x] **AC-US1-01** (with optional bold around ID)
299
- if echo "$SPEC_CONTENT" | grep -qE "^\s*-\s*\[x\]\s*\*\*${AC_ID}\*\*"; then
300
- PASSED_ACS="$PASSED_ACS $AC_ID"
301
- echo "[$(date)] pre-task-completion-edit: $AC_ID is VERIFIED (checked in spec.md)" >> "$DEBUG_LOG" 2>/dev/null || true
302
- elif echo "$SPEC_CONTENT" | grep -qE "^\s*-\s*\[ \]\s*\*\*${AC_ID}\*\*"; then
303
- FAILED_ACS="$FAILED_ACS $AC_ID"
304
- echo "[$(date)] pre-task-completion-edit: $AC_ID is NOT verified (unchecked in spec.md)" >> "$DEBUG_LOG" 2>/dev/null || true
305
- else
306
- # AC not found in spec.md - warn but allow
307
- echo "[$(date)] pre-task-completion-edit: $AC_ID not found in spec.md (warning)" >> "$DEBUG_LOG" 2>/dev/null || true
308
- fi
309
- done
310
-
311
- # ============================================================================
312
- # DECISION: ALLOW OR BLOCK
313
- # ============================================================================
314
- FAILED_ACS=$(echo "$FAILED_ACS" | sed 's/^[[:space:]]*//')
315
-
316
- if [[ -n "$FAILED_ACS" ]]; then
317
- # BLOCK the completion - ACs not verified
318
- echo "[$(date)] pre-task-completion-edit: BLOCKING - Unverified ACs: $FAILED_ACS" >> "$DEBUG_LOG" 2>/dev/null || true
319
-
320
- # Escape for JSON
321
- FAILED_ACS_ESCAPED=$(echo "$FAILED_ACS" | sed 's/"/\\"/g')
322
-
323
- cat <<EOF
324
- {
325
- "continue": false,
326
- "systemMessage": "BLOCKED: Task $TASK_ID cannot be marked complete.
327
-
328
- Unverified Acceptance Criteria: $FAILED_ACS_ESCAPED
329
-
330
- These ACs are NOT checked [x] in spec.md:
331
- $FAILED_ACS_ESCAPED
332
-
333
- ACTION REQUIRED:
334
- 1. Verify each AC is actually satisfied by the implementation
335
- 2. Check the AC in spec.md: Edit the line from [ ] to [x]
336
- 3. Then retry marking this task as completed
337
-
338
- This quality gate ensures tasks are only completed when their ACs are verified."
339
- }
340
- EOF
341
- exit 0
342
- fi
343
-
344
- # All ACs verified - allow completion
345
- echo "[$(date)] pre-task-completion-edit: ALLOWING - All ACs verified" >> "$DEBUG_LOG" 2>/dev/null || true
346
-
347
- PASSED_ACS=$(echo "$PASSED_ACS" | sed 's/^[[:space:]]*//')
348
- cat <<EOF
349
- {
350
- "continue": true,
351
- "systemMessage": "AC Verification Passed for $TASK_ID. Verified ACs:$PASSED_ACS"
352
- }
353
- EOF
354
-
355
- exit 0