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.
- package/CLAUDE.md +140 -1235
- package/bin/specweave.js +23 -0
- package/dist/plugins/specweave-github/lib/github-client-v2.d.ts +3 -0
- package/dist/plugins/specweave-github/lib/github-client-v2.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-client-v2.js +39 -0
- package/dist/plugins/specweave-github/lib/github-client-v2.js.map +1 -1
- package/dist/src/cli/commands/set-sync-target.d.ts +41 -0
- package/dist/src/cli/commands/set-sync-target.d.ts.map +1 -0
- package/dist/src/cli/commands/set-sync-target.js +126 -0
- package/dist/src/cli/commands/set-sync-target.js.map +1 -0
- package/dist/src/cli/helpers/issue-tracker/github-multi-repo.d.ts.map +1 -1
- package/dist/src/cli/helpers/issue-tracker/github-multi-repo.js +5 -8
- package/dist/src/cli/helpers/issue-tracker/github-multi-repo.js.map +1 -1
- package/dist/src/core/hooks/HookScanner.d.ts +32 -0
- package/dist/src/core/hooks/HookScanner.d.ts.map +1 -1
- package/dist/src/core/hooks/HookScanner.js +125 -1
- package/dist/src/core/hooks/HookScanner.js.map +1 -1
- package/dist/src/core/hooks/types.d.ts +10 -1
- package/dist/src/core/hooks/types.d.ts.map +1 -1
- package/dist/src/core/increment/metadata-manager.d.ts +67 -1
- package/dist/src/core/increment/metadata-manager.d.ts.map +1 -1
- package/dist/src/core/increment/metadata-manager.js +93 -0
- package/dist/src/core/increment/metadata-manager.js.map +1 -1
- package/dist/src/core/project/index.d.ts +21 -0
- package/dist/src/core/project/index.d.ts.map +1 -0
- package/dist/src/core/project/index.js +22 -0
- package/dist/src/core/project/index.js.map +1 -0
- package/dist/src/core/project/project-service.d.ts +122 -0
- package/dist/src/core/project/project-service.d.ts.map +1 -0
- package/dist/src/core/project/project-service.js +334 -0
- package/dist/src/core/project/project-service.js.map +1 -0
- package/dist/src/core/sync/external-tool-resolver.d.ts +171 -0
- package/dist/src/core/sync/external-tool-resolver.d.ts.map +1 -0
- package/dist/src/core/sync/external-tool-resolver.js +569 -0
- package/dist/src/core/sync/external-tool-resolver.js.map +1 -0
- package/dist/src/core/types/increment-metadata.d.ts +92 -0
- package/dist/src/core/types/increment-metadata.d.ts.map +1 -1
- package/dist/src/hooks/processor.d.ts +7 -3
- package/dist/src/hooks/processor.d.ts.map +1 -1
- package/dist/src/hooks/processor.js +11 -5
- package/dist/src/hooks/processor.js.map +1 -1
- package/package.json +1 -1
- package/plugins/specweave/hooks/hooks.json +0 -69
- package/plugins/specweave/hooks/v2/handlers/project-bridge-handler.sh +96 -0
- package/plugins/specweave/hooks/v2/queue/processor.sh +13 -5
- package/plugins/specweave/lib/hooks/project-bridge.js +76 -0
- package/plugins/specweave/lib/hooks/update-tasks-md.js +0 -0
- package/plugins/specweave/lib/hooks/us-completion-orchestrator.js +0 -0
- package/plugins/specweave/lib/vendor/core/increment/metadata-manager.d.ts +67 -1
- package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js +93 -0
- package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js.map +1 -1
- package/plugins/specweave/lib/vendor/core/types/increment-metadata.d.ts +92 -0
- package/plugins/specweave-github/lib/github-client-v2.js +39 -0
- package/plugins/specweave-github/lib/github-client-v2.ts +44 -0
- package/plugins/specweave/hooks/docs-changed.sh +0 -87
- package/plugins/specweave/hooks/human-input-required.sh +0 -83
- package/plugins/specweave/hooks/post-edit-write-consolidated.sh +0 -428
- package/plugins/specweave/hooks/post-first-increment.sh +0 -61
- package/plugins/specweave/hooks/post-increment-change.sh +0 -103
- package/plugins/specweave/hooks/post-increment-completion.sh +0 -513
- package/plugins/specweave/hooks/post-increment-planning.sh +0 -1204
- package/plugins/specweave/hooks/post-increment-status-change.sh +0 -243
- package/plugins/specweave/hooks/post-metadata-change.sh +0 -246
- package/plugins/specweave/hooks/post-spec-update.sh +0 -158
- package/plugins/specweave/hooks/post-task-completion.sh +0 -557
- package/plugins/specweave/hooks/post-task-edit.sh +0 -47
- package/plugins/specweave/hooks/post-user-story-complete.sh +0 -230
- package/plugins/specweave/hooks/pre-command-deduplication.sh +0 -68
- package/plugins/specweave/hooks/pre-edit-write-consolidated.sh +0 -225
- package/plugins/specweave/hooks/pre-implementation.sh +0 -75
- package/plugins/specweave/hooks/pre-increment-start.sh +0 -173
- package/plugins/specweave/hooks/pre-task-completion-edit.sh +0 -355
- package/plugins/specweave/hooks/pre-task-completion.sh +0 -269
- package/plugins/specweave/hooks/pre-tool-use.sh +0 -137
- package/plugins/specweave/hooks/session-start-reconcile.sh +0 -139
- package/plugins/specweave/hooks/shared/bulk-operation-detector.sh +0 -167
- package/plugins/specweave/hooks/test-pretooluse-env.sh +0 -72
- package/plugins/specweave/hooks/validate-increment-completion.sh +0 -113
- 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
|