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.
Files changed (58) hide show
  1. package/dist/src/cli/commands/init.d.ts.map +1 -1
  2. package/dist/src/cli/commands/init.js +35 -8
  3. package/dist/src/cli/commands/init.js.map +1 -1
  4. package/dist/src/cli/helpers/init/index.d.ts +2 -0
  5. package/dist/src/cli/helpers/init/index.d.ts.map +1 -1
  6. package/dist/src/cli/helpers/init/index.js +4 -0
  7. package/dist/src/cli/helpers/init/index.js.map +1 -1
  8. package/dist/src/cli/helpers/init/language-selection.d.ts +40 -0
  9. package/dist/src/cli/helpers/init/language-selection.d.ts.map +1 -0
  10. package/dist/src/cli/helpers/init/language-selection.js +281 -0
  11. package/dist/src/cli/helpers/init/language-selection.js.map +1 -0
  12. package/dist/src/cli/helpers/init/repository-setup.d.ts +2 -0
  13. package/dist/src/cli/helpers/init/repository-setup.d.ts.map +1 -1
  14. package/dist/src/cli/helpers/init/repository-setup.js +156 -12
  15. package/dist/src/cli/helpers/init/repository-setup.js.map +1 -1
  16. package/dist/src/cli/helpers/init/translation-config.d.ts +61 -0
  17. package/dist/src/cli/helpers/init/translation-config.d.ts.map +1 -0
  18. package/dist/src/cli/helpers/init/translation-config.js +437 -0
  19. package/dist/src/cli/helpers/init/translation-config.js.map +1 -0
  20. package/dist/src/cli/helpers/init/types.d.ts +33 -0
  21. package/dist/src/cli/helpers/init/types.d.ts.map +1 -1
  22. package/dist/src/core/config/types.d.ts +143 -0
  23. package/dist/src/core/config/types.d.ts.map +1 -1
  24. package/dist/src/core/config/types.js.map +1 -1
  25. package/dist/src/core/repo-structure/repo-id-generator.d.ts +24 -95
  26. package/dist/src/core/repo-structure/repo-id-generator.d.ts.map +1 -1
  27. package/dist/src/core/repo-structure/repo-id-generator.js +31 -223
  28. package/dist/src/core/repo-structure/repo-id-generator.js.map +1 -1
  29. package/dist/src/core/repo-structure/repo-structure-manager.d.ts.map +1 -1
  30. package/dist/src/core/repo-structure/repo-structure-manager.js +12 -46
  31. package/dist/src/core/repo-structure/repo-structure-manager.js.map +1 -1
  32. package/dist/src/sync/sync-coordinator.d.ts +5 -0
  33. package/dist/src/sync/sync-coordinator.d.ts.map +1 -1
  34. package/dist/src/sync/sync-coordinator.js +104 -6
  35. package/dist/src/sync/sync-coordinator.js.map +1 -1
  36. package/dist/src/utils/multi-repo-detector.d.ts +85 -0
  37. package/dist/src/utils/multi-repo-detector.d.ts.map +1 -0
  38. package/dist/src/utils/multi-repo-detector.js +264 -0
  39. package/dist/src/utils/multi-repo-detector.js.map +1 -0
  40. package/package.json +1 -1
  41. package/plugins/specweave/agents/pm/AGENT.md +178 -0
  42. package/plugins/specweave/agents/test-aware-planner/AGENT.md +54 -0
  43. package/plugins/specweave/commands/specweave-increment.md +30 -0
  44. package/plugins/specweave/commands/specweave-save.md +838 -0
  45. package/plugins/specweave/hooks/hooks.json +12 -0
  46. package/plugins/specweave/hooks/lib/update-status-line.sh +9 -1
  47. package/plugins/specweave/hooks/post-increment-completion.sh +4 -3
  48. package/plugins/specweave/hooks/post-increment-planning.sh +95 -51
  49. package/plugins/specweave/hooks/post-metadata-change.sh +18 -4
  50. package/plugins/specweave/hooks/pre-task-completion-edit.sh +355 -0
  51. package/plugins/specweave/lib/hooks/sync-living-docs.js +43 -0
  52. package/plugins/specweave/skills/increment-planner/SKILL.md +252 -2
  53. package/plugins/specweave/skills/spec-generator/SKILL.md +163 -0
  54. package/plugins/specweave/skills/umbrella-repo-detector/SKILL.md +286 -0
  55. package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +18 -0
  56. package/plugins/specweave-infrastructure/skills/hetzner-provisioner/README.md +1 -1
  57. package/plugins/specweave-release/commands/specweave-release-npm.md +14 -22
  58. 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: Read Config Values (MANDATORY)
133
+ ### STEP 0: Detect Multi-Project Mode (MANDATORY FIRST!)
134
134
 
135
- **Read testing configuration from `.specweave/config.json`**:
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`: