specweave 0.28.9 → 0.28.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.
- package/dist/src/cli/commands/init.d.ts.map +1 -1
- package/dist/src/cli/commands/init.js +35 -8
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/helpers/init/index.d.ts +2 -0
- package/dist/src/cli/helpers/init/index.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/index.js +4 -0
- package/dist/src/cli/helpers/init/index.js.map +1 -1
- package/dist/src/cli/helpers/init/language-selection.d.ts +40 -0
- package/dist/src/cli/helpers/init/language-selection.d.ts.map +1 -0
- package/dist/src/cli/helpers/init/language-selection.js +281 -0
- package/dist/src/cli/helpers/init/language-selection.js.map +1 -0
- package/dist/src/cli/helpers/init/repository-setup.d.ts +2 -0
- package/dist/src/cli/helpers/init/repository-setup.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/repository-setup.js +156 -12
- package/dist/src/cli/helpers/init/repository-setup.js.map +1 -1
- package/dist/src/cli/helpers/init/translation-config.d.ts +61 -0
- package/dist/src/cli/helpers/init/translation-config.d.ts.map +1 -0
- package/dist/src/cli/helpers/init/translation-config.js +437 -0
- package/dist/src/cli/helpers/init/translation-config.js.map +1 -0
- package/dist/src/cli/helpers/init/types.d.ts +33 -0
- package/dist/src/cli/helpers/init/types.d.ts.map +1 -1
- package/dist/src/core/config/types.d.ts +143 -0
- package/dist/src/core/config/types.d.ts.map +1 -1
- package/dist/src/core/config/types.js.map +1 -1
- package/dist/src/core/repo-structure/repo-id-generator.d.ts +24 -95
- package/dist/src/core/repo-structure/repo-id-generator.d.ts.map +1 -1
- package/dist/src/core/repo-structure/repo-id-generator.js +31 -223
- package/dist/src/core/repo-structure/repo-id-generator.js.map +1 -1
- package/dist/src/core/repo-structure/repo-structure-manager.d.ts.map +1 -1
- package/dist/src/core/repo-structure/repo-structure-manager.js +12 -46
- package/dist/src/core/repo-structure/repo-structure-manager.js.map +1 -1
- package/dist/src/sync/sync-coordinator.d.ts +5 -0
- package/dist/src/sync/sync-coordinator.d.ts.map +1 -1
- package/dist/src/sync/sync-coordinator.js +104 -6
- package/dist/src/sync/sync-coordinator.js.map +1 -1
- package/dist/src/utils/multi-repo-detector.d.ts +85 -0
- package/dist/src/utils/multi-repo-detector.d.ts.map +1 -0
- package/dist/src/utils/multi-repo-detector.js +264 -0
- package/dist/src/utils/multi-repo-detector.js.map +1 -0
- package/package.json +1 -1
- package/plugins/specweave/agents/pm/AGENT.md +178 -0
- package/plugins/specweave/agents/test-aware-planner/AGENT.md +54 -0
- package/plugins/specweave/commands/specweave-increment.md +30 -0
- package/plugins/specweave/commands/specweave-save.md +838 -0
- package/plugins/specweave/hooks/hooks.json +12 -0
- package/plugins/specweave/hooks/lib/update-status-line.sh +9 -1
- package/plugins/specweave/hooks/post-increment-completion.sh +4 -3
- package/plugins/specweave/hooks/post-increment-planning.sh +95 -51
- package/plugins/specweave/hooks/post-metadata-change.sh +18 -4
- package/plugins/specweave/hooks/pre-task-completion-edit.sh +355 -0
- package/plugins/specweave/lib/hooks/sync-living-docs.js +43 -0
- package/plugins/specweave/skills/increment-planner/SKILL.md +252 -2
- package/plugins/specweave/skills/spec-generator/SKILL.md +163 -0
- package/plugins/specweave/skills/umbrella-repo-detector/SKILL.md +286 -0
- package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +18 -0
- package/plugins/specweave-infrastructure/skills/hetzner-provisioner/README.md +1 -1
- package/plugins/specweave-release/commands/specweave-release-npm.md +14 -22
- package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +27 -0
|
@@ -0,0 +1,355 @@
|
|
|
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
|
|
@@ -70,6 +70,49 @@ async function syncLivingDocs(incrementId) {
|
|
|
70
70
|
}
|
|
71
71
|
console.log(`\u{1F4C4} Changed/created ${changedDocs.length} file(s)`);
|
|
72
72
|
|
|
73
|
+
// ========================================================================
|
|
74
|
+
// TRANSLATION TRIGGER (v0.29.0+ - Multi-Language Support)
|
|
75
|
+
// ========================================================================
|
|
76
|
+
// After living docs sync, check if translation is needed.
|
|
77
|
+
// User MUST opt-in during init (cost warning shown).
|
|
78
|
+
// Translation only happens if:
|
|
79
|
+
// - config.language != 'en'
|
|
80
|
+
// - config.translation.enabled == true
|
|
81
|
+
// - config.translation.scope.livingDocs == true
|
|
82
|
+
const targetLang = config.language || 'en';
|
|
83
|
+
const translationEnabled = config.translation?.enabled ?? false;
|
|
84
|
+
const translateLivingDocs = config.translation?.scope?.livingDocs ?? false;
|
|
85
|
+
|
|
86
|
+
if (targetLang !== 'en' && translationEnabled && translateLivingDocs && changedDocs.length > 0) {
|
|
87
|
+
console.log(`\n\u{1F30D} Translating ${changedDocs.length} updated file(s) to ${targetLang}...`);
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
// Try to load translate-file module
|
|
91
|
+
const { translateFile } = await import('./translate-file.js');
|
|
92
|
+
|
|
93
|
+
for (const docPath of changedDocs) {
|
|
94
|
+
try {
|
|
95
|
+
await translateFile({
|
|
96
|
+
filePath: docPath,
|
|
97
|
+
targetLang,
|
|
98
|
+
preview: false,
|
|
99
|
+
verbose: false,
|
|
100
|
+
});
|
|
101
|
+
console.log(` \u2705 ${path.basename(docPath)}`);
|
|
102
|
+
} catch (err) {
|
|
103
|
+
console.warn(` \u26A0\uFE0F Failed to translate ${path.basename(docPath)}: ${err.message}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
console.log(`\u2705 Living docs translation complete`);
|
|
107
|
+
} catch (importErr) {
|
|
108
|
+
// translate-file.js may not be compiled yet
|
|
109
|
+
console.log(` \u2139\uFE0F Translation module not available (run 'npm run build')`);
|
|
110
|
+
console.log(` \u{1F4A1} Tip: Run /specweave:translate to translate manually`);
|
|
111
|
+
}
|
|
112
|
+
} else if (targetLang !== 'en' && !translationEnabled) {
|
|
113
|
+
console.log(` \u2139\uFE0F Translation disabled (use /specweave:translate manually)`);
|
|
114
|
+
}
|
|
115
|
+
|
|
73
116
|
// ========================================================================
|
|
74
117
|
// CHECK PERMISSION: canUpdateExternalItems (v0.24.0 - Three-Permission Architecture)
|
|
75
118
|
// ========================================================================
|
|
@@ -130,9 +130,45 @@ Every increment MUST have `metadata.json` or:
|
|
|
130
130
|
|
|
131
131
|
## Workflow (Safe, Self-Contained)
|
|
132
132
|
|
|
133
|
-
### STEP 0:
|
|
133
|
+
### STEP 0: Detect Multi-Project Mode (MANDATORY FIRST!)
|
|
134
134
|
|
|
135
|
-
|
|
135
|
+
**⚠️ CRITICAL: Before creating ANY user stories, detect if this is a multi-project (umbrella) setup!**
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
# 1. Check config.json for umbrella mode
|
|
139
|
+
UMBRELLA_ENABLED=$(cat .specweave/config.json 2>/dev/null | jq -r '.umbrella.enabled // false')
|
|
140
|
+
|
|
141
|
+
# 2. Check for childRepos
|
|
142
|
+
CHILD_REPOS=$(cat .specweave/config.json 2>/dev/null | jq -r '.umbrella.childRepos[]?.id // empty' | tr '\n' ',')
|
|
143
|
+
|
|
144
|
+
# 3. Check for project folders in specs/
|
|
145
|
+
PROJECT_FOLDERS=$(ls -1 .specweave/docs/internal/specs/ 2>/dev/null | grep -v "^_" | head -5)
|
|
146
|
+
|
|
147
|
+
echo "Multi-project mode: $UMBRELLA_ENABLED"
|
|
148
|
+
echo "Child repos: $CHILD_REPOS"
|
|
149
|
+
echo "Project folders: $PROJECT_FOLDERS"
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**If multi-project detected (`umbrella.enabled: true` OR multiple project folders exist):**
|
|
153
|
+
- ✅ **MUST** generate project-scoped user stories: `US-FE-001`, `US-BE-001`, `US-SHARED-001`
|
|
154
|
+
- ✅ **MUST** use project-scoped AC-IDs: `AC-FE-US1-01`, `AC-BE-US1-01`
|
|
155
|
+
- ✅ **MUST** group user stories by project in spec.md
|
|
156
|
+
- ✅ **MUST** infer project from repo name if available (e.g., `sw-app-fe` → FE, `sw-app-be` → BE)
|
|
157
|
+
|
|
158
|
+
**Project Prefix Detection from Repo Names:**
|
|
159
|
+
```
|
|
160
|
+
sw-thumbnail-ab-fe → prefix: FE (frontend)
|
|
161
|
+
sw-thumbnail-ab-be → prefix: BE (backend)
|
|
162
|
+
sw-thumbnail-ab-shared → prefix: SHARED (shared library)
|
|
163
|
+
my-app-mobile → prefix: MOBILE (mobile app)
|
|
164
|
+
infra-terraform → prefix: INFRA (infrastructure)
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**Store this for use in STEP 4 (spec.md generation)!**
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
### STEP 0A: Read Config Values (MANDATORY)
|
|
136
172
|
|
|
137
173
|
```bash
|
|
138
174
|
# Read testMode (default: "TDD")
|
|
@@ -178,6 +214,10 @@ mkdir -p .specweave/increments/0021-feature-name
|
|
|
178
214
|
|
|
179
215
|
Create `.specweave/increments/0021-feature-name/spec.md`:
|
|
180
216
|
|
|
217
|
+
**⚠️ IMPORTANT: Use the correct template based on STEP 0 detection!**
|
|
218
|
+
|
|
219
|
+
#### 4A: Single-Project Template (umbrella.enabled: false)
|
|
220
|
+
|
|
181
221
|
```markdown
|
|
182
222
|
---
|
|
183
223
|
increment: 0021-feature-name
|
|
@@ -231,6 +271,110 @@ coverage_target: <VALUE FROM config.testing.defaultCoverageTarget OR 95>
|
|
|
231
271
|
[Other features or systems this depends on]
|
|
232
272
|
```
|
|
233
273
|
|
|
274
|
+
#### 4B: Multi-Project Template (umbrella.enabled: true) - USE THIS!
|
|
275
|
+
|
|
276
|
+
```markdown
|
|
277
|
+
---
|
|
278
|
+
increment: 0021-feature-name
|
|
279
|
+
title: "Feature Name"
|
|
280
|
+
type: feature
|
|
281
|
+
priority: P1
|
|
282
|
+
status: planned
|
|
283
|
+
created: 2025-11-24
|
|
284
|
+
structure: user-stories
|
|
285
|
+
test_mode: <VALUE FROM config.testing.defaultTestMode OR 'TDD'>
|
|
286
|
+
coverage_target: <VALUE FROM config.testing.defaultCoverageTarget OR 95>
|
|
287
|
+
multi_project: true
|
|
288
|
+
projects:
|
|
289
|
+
- id: sw-app-fe
|
|
290
|
+
prefix: FE
|
|
291
|
+
- id: sw-app-be
|
|
292
|
+
prefix: BE
|
|
293
|
+
- id: sw-app-shared
|
|
294
|
+
prefix: SHARED
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
# Feature: [Title]
|
|
298
|
+
|
|
299
|
+
## Overview
|
|
300
|
+
|
|
301
|
+
[High-level description - WHAT this feature does and WHY it's needed]
|
|
302
|
+
|
|
303
|
+
## User Stories by Project
|
|
304
|
+
|
|
305
|
+
### Frontend ([repo-name-fe])
|
|
306
|
+
|
|
307
|
+
#### US-FE-001: [Story Title] (P1)
|
|
308
|
+
**Related Repo**: [repo-name-fe]
|
|
309
|
+
**As a** [user type]
|
|
310
|
+
**I want** [goal]
|
|
311
|
+
**So that** [benefit]
|
|
312
|
+
|
|
313
|
+
**Acceptance Criteria**:
|
|
314
|
+
- [ ] **AC-FE-US1-01**: [Specific, testable criterion]
|
|
315
|
+
- [ ] **AC-FE-US1-02**: [Another criterion]
|
|
316
|
+
|
|
317
|
+
#### US-FE-002: [Story Title] (P2)
|
|
318
|
+
[Repeat structure with FE prefix]
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
### Backend ([repo-name-be])
|
|
323
|
+
|
|
324
|
+
#### US-BE-001: [Story Title] (P1)
|
|
325
|
+
**Related Repo**: [repo-name-be]
|
|
326
|
+
**As a** [system/frontend application]
|
|
327
|
+
**I want** [API endpoint/service goal]
|
|
328
|
+
**So that** [benefit]
|
|
329
|
+
|
|
330
|
+
**Acceptance Criteria**:
|
|
331
|
+
- [ ] **AC-BE-US1-01**: [API endpoint specification]
|
|
332
|
+
- [ ] **AC-BE-US1-02**: [Data validation rule]
|
|
333
|
+
|
|
334
|
+
#### US-BE-002: [Story Title] (P2)
|
|
335
|
+
[Repeat structure with BE prefix]
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
339
|
+
### Shared Library ([repo-name-shared])
|
|
340
|
+
|
|
341
|
+
#### US-SHARED-001: [Story Title] (P1)
|
|
342
|
+
**Related Repo**: [repo-name-shared]
|
|
343
|
+
**As a** developer in FE or BE repos
|
|
344
|
+
**I want** [shared types/utilities/validators]
|
|
345
|
+
**So that** [consistency across projects]
|
|
346
|
+
|
|
347
|
+
**Acceptance Criteria**:
|
|
348
|
+
- [ ] **AC-SHARED-US1-01**: [Type definition]
|
|
349
|
+
- [ ] **AC-SHARED-US1-02**: [Validator/utility function]
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
## Functional Requirements
|
|
354
|
+
|
|
355
|
+
### FR-001: [Requirement]
|
|
356
|
+
[Detailed description]
|
|
357
|
+
|
|
358
|
+
## Success Criteria
|
|
359
|
+
|
|
360
|
+
[Measurable outcomes - metrics, KPIs]
|
|
361
|
+
|
|
362
|
+
## Out of Scope
|
|
363
|
+
|
|
364
|
+
[What this explicitly does NOT include]
|
|
365
|
+
|
|
366
|
+
## Dependencies
|
|
367
|
+
|
|
368
|
+
[Other features or systems this depends on]
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
**Key Rules for Multi-Project spec.md:**
|
|
372
|
+
1. **User stories MUST be grouped by project** (Frontend, Backend, Shared, etc.)
|
|
373
|
+
2. **User story IDs MUST have project prefix**: `US-FE-001`, `US-BE-001`, `US-SHARED-001`
|
|
374
|
+
3. **AC-IDs MUST have project prefix**: `AC-FE-US1-01`, `AC-BE-US1-01`
|
|
375
|
+
4. **Each user story MUST have `Related Repo` field**
|
|
376
|
+
5. **Frontmatter MUST include `multi_project: true` and `projects` list**
|
|
377
|
+
|
|
234
378
|
### STEP 5: Create plan.md Template
|
|
235
379
|
|
|
236
380
|
Create `.specweave/increments/0021-feature-name/plan.md`:
|
|
@@ -292,6 +436,10 @@ Create `.specweave/increments/0021-feature-name/plan.md`:
|
|
|
292
436
|
|
|
293
437
|
Create `.specweave/increments/0021-feature-name/tasks.md`:
|
|
294
438
|
|
|
439
|
+
**⚠️ IMPORTANT: Use the correct template based on STEP 0 detection!**
|
|
440
|
+
|
|
441
|
+
#### 6A: Single-Project Template
|
|
442
|
+
|
|
295
443
|
```markdown
|
|
296
444
|
# Tasks: [Feature Title]
|
|
297
445
|
|
|
@@ -343,6 +491,108 @@ Create `.specweave/increments/0021-feature-name/tasks.md`:
|
|
|
343
491
|
- [ ] [T051] Verify all acceptance criteria
|
|
344
492
|
```
|
|
345
493
|
|
|
494
|
+
#### 6B: Multi-Project Template (umbrella.enabled: true) - USE THIS!
|
|
495
|
+
|
|
496
|
+
```markdown
|
|
497
|
+
# Tasks: [Feature Title]
|
|
498
|
+
|
|
499
|
+
## Task Notation
|
|
500
|
+
|
|
501
|
+
- `[T###]`: Task ID
|
|
502
|
+
- `[P]`: Parallelizable
|
|
503
|
+
- `[ ]`: Not started
|
|
504
|
+
- `[x]`: Completed
|
|
505
|
+
- Model hints: ⚡ haiku, 🧠 sonnet, 💎 opus
|
|
506
|
+
|
|
507
|
+
## Phase 1: Foundation & Setup
|
|
508
|
+
|
|
509
|
+
### T-001: Initialize Shared Library (sw-app-shared)
|
|
510
|
+
**User Story**: US-SHARED-001
|
|
511
|
+
**Satisfies ACs**: AC-SHARED-US1-01, AC-SHARED-US1-02
|
|
512
|
+
**Status**: [ ] Not Started
|
|
513
|
+
|
|
514
|
+
**Description**: Set up shared TypeScript types and validators
|
|
515
|
+
|
|
516
|
+
**Implementation**:
|
|
517
|
+
- Create shared types package
|
|
518
|
+
- Export common interfaces and types
|
|
519
|
+
- Add validation schemas
|
|
520
|
+
|
|
521
|
+
**Test Plan**:
|
|
522
|
+
- **File**: `sw-app-shared/tests/types.test.ts`
|
|
523
|
+
- **Tests**:
|
|
524
|
+
- **TC-001**: Type exports compile correctly
|
|
525
|
+
|
|
526
|
+
---
|
|
527
|
+
|
|
528
|
+
## Phase 2: Backend Implementation (sw-app-be)
|
|
529
|
+
|
|
530
|
+
### T-002: Database Schema & Models
|
|
531
|
+
**User Story**: US-BE-001
|
|
532
|
+
**Satisfies ACs**: AC-BE-US1-01, AC-BE-US1-02
|
|
533
|
+
**Status**: [ ] Not Started
|
|
534
|
+
|
|
535
|
+
**Description**: Create database schema for backend service
|
|
536
|
+
|
|
537
|
+
**Implementation**:
|
|
538
|
+
- Define Prisma/TypeORM models
|
|
539
|
+
- Run migrations
|
|
540
|
+
- Seed initial data
|
|
541
|
+
|
|
542
|
+
**Test Plan**:
|
|
543
|
+
- **File**: `sw-app-be/tests/models.test.ts`
|
|
544
|
+
- **Tests**:
|
|
545
|
+
- **TC-002**: Models create correctly
|
|
546
|
+
- **TC-003**: Relationships work
|
|
547
|
+
|
|
548
|
+
### T-003: API Endpoints
|
|
549
|
+
**User Story**: US-BE-001, US-BE-002
|
|
550
|
+
**Satisfies ACs**: AC-BE-US1-01, AC-BE-US2-01
|
|
551
|
+
**Status**: [ ] Not Started
|
|
552
|
+
|
|
553
|
+
**Description**: Implement REST API endpoints
|
|
554
|
+
|
|
555
|
+
---
|
|
556
|
+
|
|
557
|
+
## Phase 3: Frontend Implementation (sw-app-fe)
|
|
558
|
+
|
|
559
|
+
### T-004: UI Components
|
|
560
|
+
**User Story**: US-FE-001
|
|
561
|
+
**Satisfies ACs**: AC-FE-US1-01, AC-FE-US1-02
|
|
562
|
+
**Status**: [ ] Not Started
|
|
563
|
+
|
|
564
|
+
**Description**: Build frontend components
|
|
565
|
+
|
|
566
|
+
**Implementation**:
|
|
567
|
+
- Create upload component
|
|
568
|
+
- Add comparison view
|
|
569
|
+
- Wire up to backend API
|
|
570
|
+
|
|
571
|
+
**Test Plan**:
|
|
572
|
+
- **File**: `sw-app-fe/tests/components.test.tsx`
|
|
573
|
+
- **Tests**:
|
|
574
|
+
- **TC-004**: Component renders correctly
|
|
575
|
+
- **TC-005**: Upload triggers validation
|
|
576
|
+
|
|
577
|
+
---
|
|
578
|
+
|
|
579
|
+
## Phase 4: Integration & Testing
|
|
580
|
+
|
|
581
|
+
### T-005: End-to-End Tests
|
|
582
|
+
**User Story**: US-FE-001, US-BE-001
|
|
583
|
+
**Satisfies ACs**: (all FE and BE ACs)
|
|
584
|
+
**Status**: [ ] Not Started
|
|
585
|
+
|
|
586
|
+
**Description**: E2E tests across all projects
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
**Key Rules for Multi-Project tasks.md:**
|
|
590
|
+
1. **Tasks MUST reference project-scoped user stories**: `US-FE-001`, `US-BE-001`
|
|
591
|
+
2. **Tasks MUST reference project-scoped ACs**: `AC-FE-US1-01`, `AC-BE-US1-01`
|
|
592
|
+
3. **Group tasks by project/phase** (Shared first, then BE, then FE)
|
|
593
|
+
4. **Test file paths MUST include project folder**: `sw-app-be/tests/`, `sw-app-fe/tests/`
|
|
594
|
+
5. **Dependencies between projects should be explicit**
|
|
595
|
+
|
|
346
596
|
### STEP 7: Create metadata.json (MANDATORY)
|
|
347
597
|
|
|
348
598
|
**IMPORTANT**: Read `testMode` and `coverageTarget` from `.specweave/config.json`:
|