specweave 1.0.76 → 1.0.77

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 CHANGED
@@ -157,7 +157,7 @@ brew install omnisharp # or: dotnet tool install -g omnisharp
157
157
  - Combine with Explore agent for comprehensive understanding
158
158
  <!-- SW:END:lsp -->
159
159
 
160
- <!-- SW:SECTION:structure version="1.0.60" -->
160
+ <!-- SW:SECTION:structure version="1.0.61" -->
161
161
  ## Structure
162
162
 
163
163
  ```
@@ -170,6 +170,47 @@ brew install omnisharp # or: dotnet tool install -g omnisharp
170
170
  └── config.json
171
171
  ```
172
172
 
173
+ ### ⚠️ CRITICAL: Increment Folder Organization (MANDATORY)
174
+
175
+ **ONLY these files allowed at increment ROOT:**
176
+ ```
177
+ .specweave/increments/####-name/
178
+ ├── metadata.json # Increment state
179
+ ├── spec.md # Specification
180
+ ├── plan.md # Implementation plan
181
+ └── tasks.md # Task list
182
+ ```
183
+
184
+ **ALL other files MUST go in subfolders:**
185
+ ```
186
+ .specweave/increments/####-name/
187
+ ├── reports/ # Validation reports, QA reports, completion summaries
188
+ │ └── *.md # PM-VALIDATION-REPORT.md, qa-post-closure.md, etc.
189
+ ├── logs/ # Debug logs, execution traces, session logs
190
+ │ └── {YYYY-MM-DD}/ # Daily logs with assets/
191
+ ├── scripts/ # Helper scripts, automation tools
192
+ ├── docs/ # Additional documentation, domain knowledge
193
+ │ └── domain/ # Domain models for brownfield analysis
194
+ └── backups/ # Backup files
195
+ ```
196
+
197
+ **File Routing Rules:**
198
+ | File Type | Folder | Examples |
199
+ |-----------|--------|----------|
200
+ | Validation reports | `reports/` | `PM-VALIDATION-REPORT.md`, `validation-report.md` |
201
+ | QA assessments | `reports/` | `qa-post-closure.md`, `COMPLETION-SUMMARY.md` |
202
+ | Session logs | `logs/{date}/` | `session.md`, `assets/` |
203
+ | Domain analysis | `docs/domain/` | `appointments/domain-model.md` |
204
+ | Helper scripts | `scripts/` | `migrate.sh`, `validate.js` |
205
+
206
+ **FORBIDDEN - Files that pollute increment root:**
207
+ ```
208
+ ❌ .specweave/increments/0001/PM-VALIDATION-REPORT.md # → reports/
209
+ ❌ .specweave/increments/0001/completion-report.md # → reports/
210
+ ❌ .specweave/increments/0001/domain-model.md # → docs/domain/
211
+ ❌ .specweave/increments/0001/helper.sh # → scripts/
212
+ ```
213
+
173
214
  ### ⚠️ CRITICAL: Multi-Repo Project Paths (MANDATORY)
174
215
 
175
216
  **ALL multi-project repositories MUST be created in `repositories/` folder - NEVER in project root!**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specweave",
3
- "version": "1.0.76",
3
+ "version": "1.0.77",
4
4
  "description": "Spec-driven development framework for Claude Code. AI-native workflow with living documentation, intelligent agents, and multilingual support (9 languages). Enterprise-grade traceability with permanent specs and temporary increments.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -155,18 +155,22 @@ Before you finish ANY response, mentally verify:
155
155
  **MANDATORY subfolder organization**:
156
156
  ```
157
157
  .specweave/increments/####-name/
158
+ ├── metadata.json # ✅ Core file (auto-managed)
158
159
  ├── spec.md # ✅ ONLY core file 1
159
160
  ├── plan.md # ✅ ONLY core file 2
160
161
  ├── tasks.md # ✅ ONLY core file 3
161
162
  ├── reports/ # ✅ ALL reports here
162
163
  │ ├── PM-VALIDATION-REPORT.md
163
164
  │ ├── COMPLETION-SUMMARY.md
164
- │ ├── SESSION-NOTES.md
165
- │ └── ANALYSIS-*.md
165
+ │ ├── qa-post-closure.md
166
+ │ └── validation-report.md
166
167
  ├── scripts/ # ✅ ALL scripts here
167
168
  │ └── helper-*.sh
168
- └── logs/ # ✅ ALL logs here
169
- └── execution.log
169
+ ├── logs/ # ✅ ALL logs here
170
+ └── {YYYY-MM-DD}/session.md
171
+ ├── docs/ # ✅ Additional documentation
172
+ │ └── domain/ # Domain models (brownfield)
173
+ └── backups/ # ✅ Backup files
170
174
  ```
171
175
 
172
176
  **When writing ANY file**:
@@ -174,6 +178,7 @@ Before you finish ANY response, mentally verify:
174
178
  - ✅ **ALWAYS** write reports to `reports/` subfolder
175
179
  - ✅ **ALWAYS** write scripts to `scripts/` subfolder
176
180
  - ✅ **ALWAYS** write logs to `logs/` subfolder
181
+ - ✅ **ALWAYS** write additional docs to `docs/` subfolder
177
182
 
178
183
  **Example correct paths**:
179
184
  - ✅ `.specweave/increments/0001-auth/reports/PM-VALIDATION-REPORT.md`
@@ -44,19 +44,116 @@ The `/sw:save` command uses a **three-tier detection strategy**:
44
44
  - Works for microservices architectures without explicit configuration
45
45
  3. **Parent Project** - Always includes the root project if it has `.git`
46
46
 
47
+ #### Git-Scan Algorithm (CRITICAL for Microservices)
48
+
49
+ **MANDATORY: When no umbrella config exists, ALWAYS scan for nested repos:**
50
+
51
+ ```bash
52
+ # Step 1: Find all nested .git directories (excluding .git itself and node_modules)
53
+ find . -maxdepth 4 -type d -name ".git" \
54
+ -not -path "./.git" \
55
+ -not -path "*/node_modules/*" \
56
+ -not -path "*/.specweave/*" \
57
+ 2>/dev/null
58
+
59
+ # Step 2: For each .git found, the parent directory is a repo
60
+ # Example output:
61
+ # ./repositories/frontend/.git → repositories/frontend is a repo
62
+ # ./repositories/backend/.git → repositories/backend is a repo
63
+ # ./packages/shared/.git → packages/shared is a repo
64
+ ```
65
+
66
+ **Priority scan paths** (check these first for performance):
67
+ 1. `repositories/*/` - Standard SpecWeave multi-repo layout
68
+ 2. `packages/*/` - Monorepo packages
69
+ 3. `services/*/` - Microservices
70
+ 4. `apps/*/` - App directories
71
+ 5. `libs/*/` - Library directories
72
+
73
+ **Full discovery example:**
74
+
75
+ ```bash
76
+ # Discovery script that /sw:save internally executes
77
+ REPOS=()
78
+
79
+ # 1. Check umbrella config first
80
+ if [ -f ".specweave/config.json" ]; then
81
+ UMBRELLA_REPOS=$(jq -r '.umbrella.childRepos[]?.path // empty' .specweave/config.json 2>/dev/null)
82
+ if [ -n "$UMBRELLA_REPOS" ]; then
83
+ while IFS= read -r repo; do
84
+ [ -d "$repo/.git" ] && REPOS+=("$repo")
85
+ done <<< "$UMBRELLA_REPOS"
86
+ fi
87
+ fi
88
+
89
+ # 2. If no umbrella config OR it's empty, scan for nested repos
90
+ if [ ${#REPOS[@]} -eq 0 ]; then
91
+ # Scan priority paths first
92
+ for dir in repositories packages services apps libs; do
93
+ if [ -d "$dir" ]; then
94
+ for repo in "$dir"/*/; do
95
+ [ -d "${repo}.git" ] && REPOS+=("${repo%/}")
96
+ done
97
+ fi
98
+ done
99
+
100
+ # Full recursive scan if nothing found in priority paths
101
+ if [ ${#REPOS[@]} -eq 0 ]; then
102
+ while IFS= read -r git_dir; do
103
+ REPOS+=("$(dirname "$git_dir")")
104
+ done < <(find . -maxdepth 4 -type d -name ".git" -not -path "./.git" -not -path "*/node_modules/*" 2>/dev/null)
105
+ fi
106
+ fi
107
+
108
+ # 3. Always include parent project if it has .git
109
+ [ -d ".git" ] && REPOS+=(".")
110
+
111
+ # Result: REPOS array contains all repositories to process
112
+ echo "Discovered repos: ${REPOS[*]}"
113
+ ```
114
+
47
115
  **Example: Microservices without config**
48
116
 
49
117
  ```
50
- my-project/ # ← Parent repo (included)
118
+ my-project/ # ← Parent repo (included via tier 3)
51
119
  ├── repositories/
52
- │ ├── frontend/ # ← Auto-discovered
53
- ├── backend/ # ← Auto-discovered
54
- └── shared-lib/ # ← Auto-discovered
120
+ │ ├── frontend/ # ← Auto-discovered via tier 2
121
+ │ └── .git
122
+ ├── backend/ # ← Auto-discovered via tier 2
123
+ │ │ └── .git
124
+ │ └── shared-lib/ # ← Auto-discovered via tier 2
125
+ │ └── .git
55
126
  └── .specweave/
56
127
  ```
57
128
 
58
129
  Running `/sw:save` will detect and commit+push to **all 4 repositories** automatically!
59
130
 
131
+ **Microservices in `services/` folder:**
132
+ ```
133
+ my-platform/
134
+ ├── services/
135
+ │ ├── auth-service/ # ← Auto-discovered
136
+ │ │ └── .git
137
+ │ ├── payment-service/ # ← Auto-discovered
138
+ │ │ └── .git
139
+ │ └── notification-service/ # ← Auto-discovered
140
+ │ └── .git
141
+ └── .specweave/
142
+ ```
143
+
144
+ **Monorepo with `packages/`:**
145
+ ```
146
+ my-monorepo/
147
+ ├── packages/
148
+ │ ├── frontend/ # ← Auto-discovered
149
+ │ │ └── .git
150
+ │ ├── backend/ # ← Auto-discovered
151
+ │ │ └── .git
152
+ │ └── shared/ # ← Auto-discovered
153
+ │ └── .git
154
+ └── .git # ← Parent repo also included
155
+ ```
156
+
60
157
  ## Usage
61
158
 
62
159
  ```bash
@@ -12,7 +12,14 @@
12
12
  # - Extracts specific failure details for fix prompts
13
13
  # - Blocks on ANY test failure (not just >3)
14
14
  #
15
- # IMPORTANT (v2.3): Stop hook runs PER AGENT
15
+ # AGENT HIERARCHY LABELS (v2.4):
16
+ # - Detects agent type: MAIN ORCHESTRATOR vs SUBAGENT
17
+ # - Clear session stop/continue labels with box art
18
+ # - Shows "WHEN WILL SESSION STOP?" criteria prominently
19
+ # - Subagents show "Returns to: Main Orchestrator" on completion
20
+ # - Tracks subagent spawns in session stats
21
+ #
22
+ # IMPORTANT (v2.3+): Stop hook runs PER AGENT
16
23
  # - Each spawned subagent (Task tool) gets its own stop hook invocation
17
24
  # - Iteration count is SHARED across all agents via session file
18
25
  # - Parent agent's exit triggers hook, subagent exits do NOT by default
@@ -317,6 +324,196 @@ get_coverage_targets() {
317
324
  fi
318
325
  }
319
326
 
327
+ # ============================================================================
328
+ # AGENT TYPE DETECTION & HIERARCHY (NEW - v2.4)
329
+ # Distinguishes main orchestrator from subagents for clear labeling
330
+ # ============================================================================
331
+
332
+ # Agent type constants
333
+ AGENT_TYPE_ORCHESTRATOR="orchestrator"
334
+ AGENT_TYPE_SUBAGENT="subagent"
335
+
336
+ # Detect if this is main orchestrator or a subagent
337
+ # Returns: "orchestrator" or "subagent:<type>"
338
+ detect_agent_type() {
339
+ local transcript="$1"
340
+
341
+ # No transcript = assume orchestrator
342
+ if [ -z "$transcript" ] || [ ! -f "$transcript" ]; then
343
+ echo "$AGENT_TYPE_ORCHESTRATOR"
344
+ return
345
+ fi
346
+
347
+ # Check for /sw:auto command - indicates main orchestrator
348
+ if grep -qE '(/sw:auto|/specweave:auto|setup-auto\.sh)' "$transcript" 2>/dev/null; then
349
+ echo "$AGENT_TYPE_ORCHESTRATOR"
350
+ return
351
+ fi
352
+
353
+ # Check for subagent invocation patterns in transcript
354
+ # Subagents are spawned via Task tool with subagent_type parameter
355
+ local subagent_pattern='subagent_type.*?["\x27]([^"\x27]+)["\x27]'
356
+ local subagent_match=$(grep -oE 'subagent_type["\x27]*:\s*["\x27]?[a-zA-Z0-9_:-]+' "$transcript" 2>/dev/null | tail -1)
357
+
358
+ if [ -n "$subagent_match" ]; then
359
+ # Extract the type name
360
+ local agent_name=$(echo "$subagent_match" | sed 's/.*["\x27]\([^"\x27]*\)["\x27].*/\1/' | sed 's/subagent_type["\x27]*:\s*["\x27]*//')
361
+ if [ -n "$agent_name" ] && [ "$agent_name" != "subagent_type" ]; then
362
+ echo "$AGENT_TYPE_SUBAGENT:$agent_name"
363
+ return
364
+ fi
365
+ fi
366
+
367
+ # Check for specialized agent prompts (subagent indicators)
368
+ if grep -qE '(You are a specialized agent|Task tool|subagent|Agent type:)' "$transcript" 2>/dev/null; then
369
+ # Try to extract agent type from common patterns
370
+ local agent_desc=$(grep -oE '(architect|qa-engineer|security|devops|frontend|backend|ml-engineer|data-scientist|sre|tech-lead|docs-writer)' "$transcript" 2>/dev/null | head -1)
371
+ if [ -n "$agent_desc" ]; then
372
+ echo "$AGENT_TYPE_SUBAGENT:$agent_desc"
373
+ return
374
+ fi
375
+ echo "$AGENT_TYPE_SUBAGENT:unknown"
376
+ return
377
+ fi
378
+
379
+ # Default to orchestrator if no subagent patterns found
380
+ echo "$AGENT_TYPE_ORCHESTRATOR"
381
+ }
382
+
383
+ # Get human-readable agent display name
384
+ # Input: agent type from detect_agent_type()
385
+ # Output: formatted display name
386
+ get_agent_display_name() {
387
+ local agent_type="$1"
388
+
389
+ case "$agent_type" in
390
+ orchestrator)
391
+ echo "🤖 MAIN ORCHESTRATOR"
392
+ ;;
393
+ subagent:*)
394
+ local subtype="${agent_type#subagent:}"
395
+ # Format common agent types nicely
396
+ case "$subtype" in
397
+ sw:architect:architect|architect)
398
+ echo "🏗️ SUBAGENT: System Architect"
399
+ ;;
400
+ sw:tech-lead:tech-lead|tech-lead)
401
+ echo "👨‍💻 SUBAGENT: Tech Lead"
402
+ ;;
403
+ sw:qa-lead:qa-lead|qa-lead|qa-engineer)
404
+ echo "🧪 SUBAGENT: QA Engineer"
405
+ ;;
406
+ sw:security:security|security)
407
+ echo "🔐 SUBAGENT: Security Engineer"
408
+ ;;
409
+ sw-infra:devops:devops|devops)
410
+ echo "🚀 SUBAGENT: DevOps Engineer"
411
+ ;;
412
+ sw-frontend:frontend-architect:*|frontend*)
413
+ echo "🎨 SUBAGENT: Frontend Architect"
414
+ ;;
415
+ sw-backend:*|backend*)
416
+ echo "⚙️ SUBAGENT: Backend Engineer"
417
+ ;;
418
+ sw-ml:ml-engineer:*|ml-engineer)
419
+ echo "🧠 SUBAGENT: ML Engineer"
420
+ ;;
421
+ sw-ml:data-scientist:*|data-scientist)
422
+ echo "📊 SUBAGENT: Data Scientist"
423
+ ;;
424
+ sw-infra:sre:sre|sre)
425
+ echo "🔧 SUBAGENT: SRE"
426
+ ;;
427
+ sw:docs-writer:*|docs-writer)
428
+ echo "📝 SUBAGENT: Docs Writer"
429
+ ;;
430
+ Explore|explore)
431
+ echo "🔍 SUBAGENT: Explorer"
432
+ ;;
433
+ Plan|plan)
434
+ echo "📋 SUBAGENT: Planner"
435
+ ;;
436
+ unknown)
437
+ echo "🔧 SUBAGENT: Specialized Agent"
438
+ ;;
439
+ *)
440
+ echo "🔧 SUBAGENT: $subtype"
441
+ ;;
442
+ esac
443
+ ;;
444
+ *)
445
+ echo "🤖 AGENT: $agent_type"
446
+ ;;
447
+ esac
448
+ }
449
+
450
+ # Get short agent type for JSON logging
451
+ get_agent_type_short() {
452
+ local agent_type="$1"
453
+
454
+ case "$agent_type" in
455
+ orchestrator)
456
+ echo "main"
457
+ ;;
458
+ subagent:*)
459
+ echo "${agent_type#subagent:}"
460
+ ;;
461
+ *)
462
+ echo "$agent_type"
463
+ ;;
464
+ esac
465
+ }
466
+
467
+ # Detect recent subagent activity from transcript
468
+ # Returns JSON with subagent stats
469
+ detect_subagent_activity() {
470
+ local transcript="$1"
471
+
472
+ if [ -z "$transcript" ] || [ ! -f "$transcript" ]; then
473
+ echo '{"spawned":0,"types":[]}'
474
+ return
475
+ fi
476
+
477
+ # Count Task tool invocations
478
+ local task_count=$(grep -c 'Task tool\|subagent_type\|<invoke name="Task">' "$transcript" 2>/dev/null || echo "0")
479
+
480
+ # Extract unique agent types
481
+ local agent_types=$(grep -oE 'subagent_type["\x27]*:\s*["\x27]?[a-zA-Z0-9_:-]+' "$transcript" 2>/dev/null | \
482
+ sed 's/subagent_type["\x27]*:\s*["\x27]*//' | \
483
+ sort -u | \
484
+ head -10 | \
485
+ jq -R -s 'split("\n") | map(select(length > 0))' 2>/dev/null || echo '[]')
486
+
487
+ echo "{\"spawned\":$task_count,\"types\":$agent_types}"
488
+ }
489
+
490
+ # Track subagent in session (called when subagent activity detected)
491
+ track_subagent_spawn() {
492
+ local agent_type="$1"
493
+ local short_type=$(get_agent_type_short "$agent_type")
494
+
495
+ if [ -f "$SESSION_FILE" ]; then
496
+ local updated=$(cat "$SESSION_FILE" | jq \
497
+ --arg type "$short_type" \
498
+ --arg now "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
499
+ '.subagentStats.totalSpawned = ((.subagentStats.totalSpawned // 0) + 1) |
500
+ .subagentStats.lastSpawned = $type |
501
+ .subagentStats.lastSpawnTime = $now |
502
+ .subagentStats.history = ((.subagentStats.history // []) + [{"type": $type, "time": $now}]) |
503
+ .subagentStats.history = (.subagentStats.history | if length > 20 then .[-20:] else . end)')
504
+ echo "$updated" > "$SESSION_FILE"
505
+ fi
506
+ }
507
+
508
+ # Get subagent stats from session
509
+ get_subagent_stats() {
510
+ if [ -f "$SESSION_FILE" ]; then
511
+ jq -r '.subagentStats // {"totalSpawned":0,"history":[]}' "$SESSION_FILE"
512
+ else
513
+ echo '{"totalSpawned":0,"history":[]}'
514
+ fi
515
+ }
516
+
320
517
  # ============================================================================
321
518
  # STOP REASON TRACKING (NEW - v2.2)
322
519
  # Clear logging of WHY auto mode stops
@@ -375,72 +572,182 @@ detect_command_timeout() {
375
572
 
376
573
  # Helper: Output approve decision
377
574
  # ALWAYS log why we're stopping for debugging
575
+ # Enhanced v2.4: Agent-aware labeling with clear hierarchy
378
576
  approve() {
379
577
  local reason="${1:-Session complete}"
380
578
  local is_success="${2:-false}"
381
579
 
382
- # Log the stop reason
383
- log_stop_reason "$reason" "approve_called" "$is_success"
580
+ # Detect agent type for proper labeling
581
+ local agent_type=$(detect_agent_type "$TRANSCRIPT_PATH")
582
+ local agent_display=$(get_agent_display_name "$agent_type")
583
+ local agent_short=$(get_agent_type_short "$agent_type")
584
+
585
+ # Get subagent stats for summary
586
+ local subagent_stats=$(get_subagent_stats)
587
+ local subagents_spawned=$(echo "$subagent_stats" | jq -r '.totalSpawned // 0')
588
+
589
+ # Log the stop reason with agent info
590
+ log_stop_reason "$reason" "approve_called:$agent_short" "$is_success"
591
+
592
+ # Get test breakdown by type if available (NEW - v2.5)
593
+ local test_breakdown=$(get_test_type_breakdown "$TRANSCRIPT_PATH")
384
594
 
385
595
  # Display the stop reason prominently to STDERR (not stdout, which is for JSON)
386
596
  {
387
597
  echo ""
388
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
389
- echo "🛑 AUTO MODE STOPPING"
390
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
391
- echo "Reason: $reason"
392
- echo "Iteration: ${ITERATION:-0}/${MAX_ITERATIONS:-100}"
393
- [ -n "$CURRENT_INCREMENT" ] && echo "Increment: $CURRENT_INCREMENT"
394
- [ "${TESTS_RUN:-false}" = "true" ] && echo "Tests: ${TESTS_PASSED:-0} passed, ${TESTS_FAILED:-0} failed"
395
- echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
598
+ if [ "$agent_type" = "orchestrator" ]; then
599
+ # Main orchestrator stopping - this is a SESSION END
600
+ if [ "$is_success" = "true" ]; then
601
+ echo "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓"
602
+ echo "┃ ✅ AUTO SESSION COMPLETE ┃"
603
+ echo "$agent_display ┃"
604
+ echo "┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫"
605
+ echo "┃ Status: SUCCESS - All work completed ┃"
606
+ else
607
+ echo "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓"
608
+ echo "┃ 🛑 AUTO SESSION STOPPING ┃"
609
+ echo "┃ $agent_display ┃"
610
+ echo "┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫"
611
+ echo "┃ Status: STOPPED - Requires attention ┃"
612
+ fi
613
+ echo "┃ Reason: $(printf '%-48s' "$reason")┃"
614
+ echo "┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫"
615
+ echo "┃ 📊 SESSION SUMMARY ┃"
616
+ echo "┃ ├─ Iterations: ${ITERATION:-0}/${MAX_ITERATIONS:-100} ┃"
617
+ [ -n "$CURRENT_INCREMENT" ] && echo "┃ ├─ Increment: $(printf '%-42s' "$CURRENT_INCREMENT")┃"
618
+ [ "$subagents_spawned" -gt 0 ] && echo "┃ ├─ Subagents spawned: $(printf '%-35s' "$subagents_spawned")┃"
619
+ if [ "${TESTS_RUN:-false}" = "true" ]; then
620
+ if [ -n "$test_breakdown" ] && [ "$test_breakdown" != "{}" ]; then
621
+ # Show detailed breakdown by test type (NEW - v2.5)
622
+ echo "┃ └─ Tests (detailed breakdown): ┃"
623
+ local unit_passed=$(echo "$test_breakdown" | jq -r '.unit.passed // 0')
624
+ local unit_failed=$(echo "$test_breakdown" | jq -r '.unit.failed // 0')
625
+ local integration_passed=$(echo "$test_breakdown" | jq -r '.integration.passed // 0')
626
+ local integration_failed=$(echo "$test_breakdown" | jq -r '.integration.failed // 0')
627
+ local e2e_passed=$(echo "$test_breakdown" | jq -r '.e2e.passed // 0')
628
+ local e2e_failed=$(echo "$test_breakdown" | jq -r '.e2e.failed // 0')
629
+
630
+ if [ "$unit_passed" -gt 0 ] || [ "$unit_failed" -gt 0 ]; then
631
+ echo "┃ • Unit: ${unit_passed} passed, ${unit_failed} failed ┃"
632
+ fi
633
+ if [ "$integration_passed" -gt 0 ] || [ "$integration_failed" -gt 0 ]; then
634
+ echo "┃ • Integration: ${integration_passed} passed, ${integration_failed} failed ┃"
635
+ fi
636
+ if [ "$e2e_passed" -gt 0 ] || [ "$e2e_failed" -gt 0 ]; then
637
+ echo "┃ • E2E: ${e2e_passed} passed, ${e2e_failed} failed ┃"
638
+ fi
639
+ else
640
+ # Fallback to simple summary
641
+ echo "┃ └─ Tests: ${TESTS_PASSED:-0} passed, ${TESTS_FAILED:-0} failed ┃"
642
+ fi
643
+ fi
644
+ echo "┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛"
645
+
646
+ # ================================================================
647
+ # SUCCESS SOUND NOTIFICATION (NEW - v2.5)
648
+ # Play a satisfying sound when auto session completes successfully
649
+ # Glass.aiff is a clean, satisfying completion sound on macOS
650
+ # ================================================================
651
+ if [ "$is_success" = "true" ]; then
652
+ afplay /System/Library/Sounds/Glass.aiff 2>/dev/null &
653
+ fi
654
+ else
655
+ # Subagent stopping - this is a RETURN TO PARENT
656
+ echo "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓"
657
+ echo "┃ ↩️ SUBAGENT COMPLETE - Returning to parent ┃"
658
+ echo "┃ $agent_display ┃"
659
+ echo "┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫"
660
+ echo "┃ Result: $(printf '%-49s' "$reason")┃"
661
+ echo "┃ ↩️ Control returning to: 🤖 Main Orchestrator ┃"
662
+ echo "┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛"
663
+ fi
396
664
  echo ""
397
665
  } >&2
398
666
 
399
- echo "{\"decision\": \"approve\", \"reason\": \"$reason\"}"
667
+ echo "{\"decision\": \"approve\", \"reason\": \"$reason\", \"agentType\": \"$agent_short\"}"
400
668
  exit 0
401
669
  }
402
670
 
403
671
  # Helper: Output block decision with system message
404
672
  # Properly escapes JSON strings with newlines
405
- # Also displays stop criteria prominently to stderr (NEW - v2.2, enhanced v2.3)
673
+ # Enhanced v2.4: Agent-aware labeling with clear hierarchy and stop conditions
406
674
  block() {
407
675
  local reason="$1"
408
676
  local system_message="$2"
409
677
 
678
+ # Detect agent type for proper labeling
679
+ local agent_type=$(detect_agent_type "$TRANSCRIPT_PATH")
680
+ local agent_display=$(get_agent_display_name "$agent_type")
681
+ local agent_short=$(get_agent_type_short "$agent_type")
682
+
410
683
  # Get current stop criteria for display
411
684
  local tdd_mode=$(is_tdd_strict_mode)
412
685
  local stop_criteria=""
686
+ local stop_criteria_detail=""
413
687
 
414
- # Build stop criteria message - MORE PROMINENT (v2.3)
688
+ # Build stop criteria message - MORE PROMINENT (v2.4)
415
689
  if [ "$tdd_mode" = "true" ]; then
416
- stop_criteria="🔴 TDD MODE: ALL tasks [x] + ALL tests GREEN (0 failures)"
690
+ stop_criteria="TDD STRICT MODE"
691
+ stop_criteria_detail="ALL tasks [x] + ALL tests GREEN (0 failures)"
417
692
  else
418
- stop_criteria=" ALL tasks [x] completed + tests passing"
693
+ stop_criteria="STANDARD MODE"
694
+ stop_criteria_detail="ALL tasks [x] completed + tests passing"
419
695
  fi
420
696
 
421
- # Display stop criteria and continuation reason to STDERR (v2.3 enhanced)
697
+ # Get subagent activity for display
698
+ local subagent_stats=$(get_subagent_stats)
699
+ local subagents_spawned=$(echo "$subagent_stats" | jq -r '.totalSpawned // 0')
700
+
701
+ # Display stop criteria and continuation reason to STDERR (v2.4 enhanced)
422
702
  {
423
703
  echo ""
424
- echo "╔══════════════════════════════════════════════════════════════╗"
425
- echo "║ 🔄 AUTO MODE CONTINUING - Agent will keep working ║"
426
- echo "╠══════════════════════════════════════════════════════════════╣"
427
- echo "║ Reason: $(printf '%-50s' "$reason")║"
428
- echo "║ Iteration: $(printf '%-47s' "${ITERATION:-0}/${MAX_ITERATIONS:-2500}")║"
429
- [ -n "$CURRENT_INCREMENT" ] && echo "║ Increment: $(printf '%-47s' "$CURRENT_INCREMENT")║"
430
- echo "╠══════════════════════════════════════════════════════════════╣"
431
- echo "║ 🎯 STOP CONDITION: $stop_criteria"
432
- [ "${TESTS_RUN:-false}" = "true" ] && echo "║ Tests: ${TESTS_PASSED:-0} passed, ${TESTS_FAILED:-0} failed"
433
- [ "$tdd_mode" = "true" ] && echo "║ TDD Source: $(get_tdd_source)"
434
- echo "╚══════════════════════════════════════════════════════════════╝"
704
+ if [ "$agent_type" = "orchestrator" ]; then
705
+ # Main orchestrator continuing - SESSION CONTINUES
706
+ echo "╔══════════════════════════════════════════════════════════════╗"
707
+ echo "║ 🔄 AUTO SESSION CONTINUING ║"
708
+ echo "║ $agent_display ║"
709
+ echo "╠══════════════════════════════════════════════════════════════╣"
710
+ echo "║ Why: $(printf '%-52s' "$reason")║"
711
+ echo "║ Iteration: $(printf '%-47s' "${ITERATION:-0}/${MAX_ITERATIONS:-2500}")║"
712
+ [ -n "$CURRENT_INCREMENT" ] && echo "║ Increment: $(printf '%-47s' "$CURRENT_INCREMENT")║"
713
+ [ "$subagents_spawned" -gt 0 ] && echo "║ Subagents used: $(printf '%-42s' "$subagents_spawned")"
714
+ echo "╠══════════════════════════════════════════════════════════════╣"
715
+ echo "║ 🎯 WHEN WILL SESSION STOP? ║"
716
+ echo "║ ├─ Mode: $(printf '%-48s' "$stop_criteria")║"
717
+ echo "║ └─ Criteria: $(printf '%-44s' "$stop_criteria_detail")║"
718
+ if [ "${TESTS_RUN:-false}" = "true" ]; then
719
+ if [ "${TESTS_FAILED:-0}" -gt 0 ]; then
720
+ echo "║ ⚠️ Tests: ${TESTS_PASSED:-0} passed, ${TESTS_FAILED:-0} FAILED (blocking!) ║"
721
+ else
722
+ echo "║ ✅ Tests: ${TESTS_PASSED:-0} passed, 0 failed ║"
723
+ fi
724
+ else
725
+ echo "║ ⏳ Tests: NOT YET RUN ║"
726
+ fi
727
+ [ "$tdd_mode" = "true" ] && echo "║ 📋 TDD Source: $(printf '%-42s' "$(get_tdd_source)")║"
728
+ echo "╚══════════════════════════════════════════════════════════════╝"
729
+ else
730
+ # Subagent continuing - SUBAGENT KEEPS WORKING
731
+ echo "╔══════════════════════════════════════════════════════════════╗"
732
+ echo "║ 🔧 SUBAGENT CONTINUING WORK ║"
733
+ echo "║ $agent_display ║"
734
+ echo "╠══════════════════════════════════════════════════════════════╣"
735
+ echo "║ Task: $(printf '%-51s' "$reason")║"
736
+ echo "╠══════════════════════════════════════════════════════════════╣"
737
+ echo "║ 🎯 WHEN WILL SUBAGENT RETURN? ║"
738
+ echo "║ └─ When assigned task is complete ║"
739
+ echo "║ ↩️ Returns to: 🤖 Main Orchestrator ║"
740
+ echo "╚══════════════════════════════════════════════════════════════╝"
741
+ fi
435
742
  echo ""
436
743
  } >&2
437
744
 
438
745
  if [ -n "$system_message" ]; then
439
746
  # Escape special characters for JSON
440
747
  local escaped_message=$(echo "$system_message" | jq -Rs .)
441
- echo "{\"decision\": \"block\", \"reason\": \"$reason\", \"systemMessage\": $escaped_message}"
748
+ echo "{\"decision\": \"block\", \"reason\": \"$reason\", \"systemMessage\": $escaped_message, \"agentType\": \"$agent_short\"}"
442
749
  else
443
- echo "{\"decision\": \"block\", \"reason\": \"$reason\"}"
750
+ echo "{\"decision\": \"block\", \"reason\": \"$reason\", \"agentType\": \"$agent_short\"}"
444
751
  fi
445
752
  exit 0
446
753
  }
@@ -859,6 +1166,80 @@ parse_test_results() {
859
1166
  echo "{\"passed\":$passed,\"failed\":$failed,\"total\":$total,\"framework\":\"$framework\",\"testsRun\":$tests_run}"
860
1167
  }
861
1168
 
1169
+ # ============================================================================
1170
+ # TEST TYPE BREAKDOWN (NEW - v2.5)
1171
+ # Categorizes test results by type (unit/integration/E2E) for detailed summary
1172
+ # ============================================================================
1173
+
1174
+ # Get test type breakdown from transcript
1175
+ # Returns: JSON with {unit: {passed, failed}, integration: {passed, failed}, e2e: {passed, failed}}
1176
+ get_test_type_breakdown() {
1177
+ local transcript="$1"
1178
+
1179
+ if [ ! -f "$transcript" ]; then
1180
+ echo "{}"
1181
+ return
1182
+ fi
1183
+
1184
+ local unit_passed=0
1185
+ local unit_failed=0
1186
+ local integration_passed=0
1187
+ local integration_failed=0
1188
+ local e2e_passed=0
1189
+ local e2e_failed=0
1190
+
1191
+ # Check for test command patterns to categorize
1192
+ # Unit tests: vitest, jest, pytest, go test
1193
+ if grep -qE '(npm.*test:unit|npx vitest|npx jest|pytest|go test|cargo test)' "$transcript" 2>/dev/null; then
1194
+ # Parse unit test results
1195
+ local unit_result=$(grep -oE 'Tests?\s+[0-9]+\s+passed' "$transcript" 2>/dev/null | tail -1)
1196
+ if [ -n "$unit_result" ]; then
1197
+ unit_passed=$(echo "$unit_result" | grep -oE '[0-9]+' | head -1)
1198
+ local unit_failed_match=$(grep -oE '[0-9]+\s+failed' "$transcript" 2>/dev/null | tail -1 | grep -oE '[0-9]+' || echo "0")
1199
+ unit_failed=${unit_failed_match:-0}
1200
+ fi
1201
+ fi
1202
+
1203
+ # Integration tests: usually named test:integration
1204
+ if grep -qE 'npm.*test:integration' "$transcript" 2>/dev/null; then
1205
+ # Parse integration test results (same format as unit)
1206
+ local int_result=$(grep -oE 'Tests?\s+[0-9]+\s+passed' "$transcript" 2>/dev/null | tail -1)
1207
+ if [ -n "$int_result" ]; then
1208
+ integration_passed=$(echo "$int_result" | grep -oE '[0-9]+' | head -1)
1209
+ local int_failed_match=$(grep -oE '[0-9]+\s+failed' "$transcript" 2>/dev/null | tail -1 | grep -oE '[0-9]+' || echo "0")
1210
+ integration_failed=${int_failed_match:-0}
1211
+ fi
1212
+ fi
1213
+
1214
+ # E2E tests: Playwright, Cypress, Detox, Maestro
1215
+ if grep -qE '(npx playwright|npx cypress|npx detox|maestro test|test:e2e)' "$transcript" 2>/dev/null; then
1216
+ # Parse E2E test results
1217
+ local e2e_passed_match=$(grep -oE '[0-9]+\s+passed' "$transcript" 2>/dev/null | tail -1 | grep -oE '[0-9]+')
1218
+ local e2e_failed_match=$(grep -oE '[0-9]+\s+failed' "$transcript" 2>/dev/null | tail -1 | grep -oE '[0-9]+')
1219
+
1220
+ e2e_passed=${e2e_passed_match:-0}
1221
+ e2e_failed=${e2e_failed_match:-0}
1222
+ fi
1223
+
1224
+ # Return JSON with breakdown
1225
+ cat <<EOF
1226
+ {
1227
+ "unit": {
1228
+ "passed": $unit_passed,
1229
+ "failed": $unit_failed
1230
+ },
1231
+ "integration": {
1232
+ "passed": $integration_passed,
1233
+ "failed": $integration_failed
1234
+ },
1235
+ "e2e": {
1236
+ "passed": $e2e_passed,
1237
+ "failed": $e2e_failed
1238
+ }
1239
+ }
1240
+ EOF
1241
+ }
1242
+
862
1243
  # Extract detailed failure information from transcript
863
1244
  # Returns: JSON with failure details
864
1245
  extract_failure_details() {
@@ -1787,7 +2168,30 @@ Continue with /sw:do and add missing E2E tests."
1787
2168
  --argjson passed "${TESTS_PASSED:-0}" --argjson failed "${TESTS_FAILED:-0}" \
1788
2169
  '.status = "completed" | .endTime = $now | .endReason = "all_tasks_complete" | .finalTestResults = {"passed": $passed, "failed": $failed}' \
1789
2170
  > "$SESSION_FILE"
1790
- approve "All tasks completed, all tests passed ($TESTS_PASSED passed, 0 failed)" "true"
2171
+
2172
+ # Build detailed completion reason with test breakdown (NEW - v2.5)
2173
+ local completion_reason="All tasks completed"
2174
+ local test_breakdown=$(get_test_type_breakdown "$TRANSCRIPT_PATH")
2175
+ if [ -n "$test_breakdown" ] && [ "$test_breakdown" != "{}" ]; then
2176
+ local unit_p=$(echo "$test_breakdown" | jq -r '.unit.passed // 0')
2177
+ local int_p=$(echo "$test_breakdown" | jq -r '.integration.passed // 0')
2178
+ local e2e_p=$(echo "$test_breakdown" | jq -r '.e2e.passed // 0')
2179
+
2180
+ local test_details=""
2181
+ [ "$unit_p" -gt 0 ] && test_details="${test_details}${unit_p} unit"
2182
+ [ "$int_p" -gt 0 ] && test_details="${test_details}${test_details:+, }${int_p} integration"
2183
+ [ "$e2e_p" -gt 0 ] && test_details="${test_details}${test_details:+, }${e2e_p} E2E"
2184
+
2185
+ if [ -n "$test_details" ]; then
2186
+ completion_reason="$completion_reason, tests passed: $test_details"
2187
+ else
2188
+ completion_reason="$completion_reason, all tests passed ($TESTS_PASSED passed, 0 failed)"
2189
+ fi
2190
+ else
2191
+ completion_reason="$completion_reason, all tests passed ($TESTS_PASSED passed, 0 failed)"
2192
+ fi
2193
+
2194
+ approve "$completion_reason" "true"
1791
2195
  else
1792
2196
  # More increments in queue - transition to next
1793
2197
  NEXT_INCREMENT=$(echo "$SESSION" | jq -r '.incrementQueue[1] // null')
@@ -1909,7 +2313,15 @@ echo "$SESSION" | jq --argjson iter "$NEXT_ITERATION" --arg now "$(date -u +%Y-%
1909
2313
  '.iteration = $iter | .lastActivity = $now' \
1910
2314
  > "$SESSION_FILE"
1911
2315
 
1912
- # Build context message with test instructions (v2.2)
2316
+ # Build context message with test instructions (v2.4 enhanced with agent hierarchy)
2317
+ # Detect agent type for context message
2318
+ AGENT_TYPE=$(detect_agent_type "$TRANSCRIPT_PATH")
2319
+ AGENT_DISPLAY=$(get_agent_display_name "$AGENT_TYPE")
2320
+
2321
+ # Get subagent activity for context
2322
+ SUBAGENT_ACTIVITY=$(detect_subagent_activity "$TRANSCRIPT_PATH")
2323
+ SUBAGENT_COUNT=$(echo "$SUBAGENT_ACTIVITY" | jq -r '.spawned // 0')
2324
+
1913
2325
  if [ "$SIMPLE_MODE" = "true" ]; then
1914
2326
  CONTEXT="Continue working. Iteration $NEXT_ITERATION/$MAX_ITERATIONS."
1915
2327
  else
@@ -1936,36 +2348,75 @@ else
1936
2348
  # Get test instructions for this project (NEW - v2.2)
1937
2349
  TEST_INSTRUCTIONS=$(format_test_instructions)
1938
2350
 
1939
- # Build stop criteria message - MUST be clear for each agent
2351
+ # Build agent header based on type (NEW - v2.4)
2352
+ AGENT_HEADER=""
2353
+ if [ "$AGENT_TYPE" = "orchestrator" ]; then
2354
+ AGENT_HEADER="
2355
+ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2356
+ ┃ $AGENT_DISPLAY ┃
2357
+ ┃ AUTO SESSION - Iteration $NEXT_ITERATION/$MAX_ITERATIONS ┃
2358
+ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛"
2359
+ else
2360
+ AGENT_HEADER="
2361
+ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2362
+ ┃ $AGENT_DISPLAY ┃
2363
+ ┃ Working under: 🤖 Main Orchestrator ┃
2364
+ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛"
2365
+ fi
2366
+
2367
+ # Build stop criteria message - MUST be clear for each agent (v2.4)
1940
2368
  STOP_CRITERIA=""
1941
- if [ "$TDD_MODE" = "true" ]; then
1942
- STOP_CRITERIA="
2369
+ if [ "$AGENT_TYPE" = "orchestrator" ]; then
2370
+ # Main orchestrator stop criteria
2371
+ if [ "$TDD_MODE" = "true" ]; then
2372
+ STOP_CRITERIA="
1943
2373
  ╔══════════════════════════════════════════════════════════════╗
1944
- ║ 🎯 AGENT STOP CONDITION (TDD STRICT MODE)
2374
+ ║ 🎯 SESSION STOP CONDITION (TDD STRICT MODE)
1945
2375
  ╠══════════════════════════════════════════════════════════════╣
1946
2376
  ║ ✅ ALL tasks in tasks.md marked [x] completed ║
1947
2377
  ║ ✅ ALL tests pass (0 failures required) ║
1948
2378
  ║ ✅ Test execution detected in output ║
1949
2379
  ╠══════════════════════════════════════════════════════════════╣
1950
- ║ Source: $(printf '%-45s' "$(get_tdd_source)")║
2380
+ TDD Source: $(printf '%-42s' "$(get_tdd_source)")║
1951
2381
  ╚══════════════════════════════════════════════════════════════╝"
1952
- else
1953
- STOP_CRITERIA="
2382
+ else
2383
+ STOP_CRITERIA="
1954
2384
  ╔══════════════════════════════════════════════════════════════╗
1955
- ║ 🎯 AGENT STOP CONDITION
2385
+ ║ 🎯 SESSION STOP CONDITION
1956
2386
  ╠══════════════════════════════════════════════════════════════╣
1957
2387
  ║ ✅ ALL tasks in tasks.md marked [x] completed ║
1958
2388
  ║ ✅ Tests executed and passing ║
1959
2389
  ╚══════════════════════════════════════════════════════════════╝"
2390
+ fi
2391
+ else
2392
+ # Subagent stop criteria - simpler, just complete the task
2393
+ STOP_CRITERIA="
2394
+ ╔══════════════════════════════════════════════════════════════╗
2395
+ ║ 🎯 SUBAGENT STOP CONDITION ║
2396
+ ╠══════════════════════════════════════════════════════════════╣
2397
+ ║ ✅ Complete the assigned task ║
2398
+ ║ ↩️ Then return control to Main Orchestrator ║
2399
+ ╚══════════════════════════════════════════════════════════════╝"
2400
+ fi
2401
+
2402
+ # Build subagent info if any were spawned (NEW - v2.4)
2403
+ SUBAGENT_INFO=""
2404
+ if [ "$SUBAGENT_COUNT" -gt 0 ] && [ "$AGENT_TYPE" = "orchestrator" ]; then
2405
+ SUBAGENT_TYPES=$(echo "$SUBAGENT_ACTIVITY" | jq -r '.types | join(", ")' 2>/dev/null || echo "various")
2406
+ SUBAGENT_INFO="
2407
+ 📦 SUBAGENT ACTIVITY (this session):
2408
+ ├─ Spawned: $SUBAGENT_COUNT subagent(s)
2409
+ └─ Types: $SUBAGENT_TYPES"
1960
2410
  fi
1961
2411
 
1962
- # Build full context message
1963
- CONTEXT="🤖 AUTO MODE ACTIVE - Iteration $NEXT_ITERATION/$MAX_ITERATIONS
2412
+ # Build full context message (v2.4)
2413
+ CONTEXT="$AGENT_HEADER
1964
2414
  $STOP_CRITERIA
1965
2415
 
1966
2416
  📊 CURRENT PROGRESS:
1967
2417
  $PROGRESS
1968
2418
  $TEST_STATUS
2419
+ $SUBAGENT_INFO
1969
2420
 
1970
2421
  $TEST_INSTRUCTIONS
1971
2422
 
@@ -1981,8 +2432,9 @@ $TEST_INSTRUCTIONS
1981
2432
  ⚠️ This agent will NOT stop until the STOP CONDITION above is met!"
1982
2433
  fi
1983
2434
 
1984
- # Log iteration
1985
- echo "{\"timestamp\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"event\":\"iteration\",\"iteration\":$NEXT_ITERATION,\"increment\":\"$CURRENT_INCREMENT\",\"testsRun\":$TESTS_RUN,\"testsPassed\":${TESTS_PASSED:-0},\"testsFailed\":${TESTS_FAILED:-0}}" >> "$LOGS_DIR/auto-iterations.log"
2435
+ # Log iteration with agent info (v2.4)
2436
+ AGENT_SHORT=$(get_agent_type_short "$AGENT_TYPE")
2437
+ echo "{\"timestamp\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"event\":\"iteration\",\"iteration\":$NEXT_ITERATION,\"increment\":\"$CURRENT_INCREMENT\",\"agentType\":\"$AGENT_SHORT\",\"subagentsSpawned\":$SUBAGENT_COUNT,\"testsRun\":$TESTS_RUN,\"testsPassed\":${TESTS_PASSED:-0},\"testsFailed\":${TESTS_FAILED:-0}}" >> "$LOGS_DIR/auto-iterations.log"
1986
2438
 
1987
2439
  # Block exit and re-feed prompt
1988
2440
  block "Work incomplete, continuing..." "$CONTEXT"
@@ -98,7 +98,7 @@ Technology stack → .specweave/docs/internal/architecture/tech-stack.md
98
98
  Business rules → .specweave/docs/internal/strategy/business-rules.md
99
99
  Team workflows → .specweave/docs/internal/processes/team-workflows.md
100
100
  Deployment process → .specweave/docs/internal/processes/deployment.md
101
- Domain knowledge → .specweave/increments/{####-name}/domain/{domain}.md
101
+ Domain knowledge → .specweave/increments/{####-name}/docs/domain/{domain}.md
102
102
 
103
103
  # Public Documentation (user-facing, can be published)
104
104
  Project conventions → .specweave/docs/public/guides/project-conventions.md
@@ -438,7 +438,7 @@ function useCustomAuth() {
438
438
  **Domain**: Healthcare, Patient Management, Provider Scheduling
439
439
 
440
440
  ### Quick Links
441
- - [Domain Model](.specweave/increments/####-name/domain/appointments/domain-model.md)
441
+ - [Domain Model](.specweave/increments/####-name/docs/domain/appointments/domain-model.md)
442
442
  - [Existing System Architecture](.specweave/docs/internal/architecture/existing-system.md)
443
443
  - [Tech Stack](.specweave/docs/internal/architecture/tech-stack.md)
444
444
  - [Business Rules](.specweave/docs/internal/strategy/appointments/business-rules.md)
@@ -508,7 +508,7 @@ if (exists("specifications/modules/appointments/domain-model.md")) {
508
508
  I found the following project-specific content in your backup CLAUDE.md:
509
509
 
510
510
  📦 Domain Model (Healthcare Appointments)
511
- → .specweave/increments/####-name/domain/appointments/domain-model.md
511
+ → .specweave/increments/####-name/docs/domain/appointments/domain-model.md
512
512
 
513
513
  🏗️ Microservices Architecture
514
514
  → .specweave/docs/internal/architecture/existing-system.md
@@ -665,7 +665,7 @@ The following content remains in the backup and will be extracted when you work
665
665
 
666
666
  ## Files Created
667
667
 
668
- 1. ✅ `.specweave/increments/####-name/domain/appointments/domain-model.md` (450 lines)
668
+ 1. ✅ `.specweave/increments/####-name/docs/domain/appointments/domain-model.md` (450 lines)
669
669
  2. ✅ `.specweave/docs/internal/architecture/existing-system.md` (320 lines)
670
670
  3. ✅ `.specweave/docs/internal/architecture/tech-stack.md` (180 lines)
671
671
  4. ✅ `.specweave/docs/internal/strategy/appointments/business-rules.md` (280 lines)
@@ -688,7 +688,7 @@ The following content remains in the backup and will be extracted when you work
688
688
 
689
689
  | Content Type | Lines | Destination |
690
690
  |--------------|-------|-------------|
691
- | Domain Model | 450 | .specweave/increments/####-name/domain/ |
691
+ | Domain Model | 450 | .specweave/increments/####-name/docs/domain/ |
692
692
  | Architecture | 320 | .specweave/docs/internal/architecture/ |
693
693
  | Tech Stack | 180 | .specweave/docs/internal/architecture/ |
694
694
  | Business Rules | 280 | .specweave/docs/internal/strategy/ |
@@ -782,7 +782,7 @@ Proceed with merge? (y/n)
782
782
  ✅ Merge complete!
783
783
 
784
784
  Created:
785
- 1. .specweave/increments/####-name/domain/appointments/domain-model.md
785
+ 1. .specweave/increments/####-name/docs/domain/appointments/domain-model.md
786
786
  2. .specweave/docs/internal/architecture/existing-system.md
787
787
  3. .specweave/docs/internal/architecture/tech-stack.md
788
788
  4. .specweave/docs/internal/strategy/appointments/business-rules.md
@@ -762,6 +762,24 @@ node plugins/specweave/skills/increment-planner/scripts/feature-utils.js check-i
762
762
  mkdir -p .specweave/increments/0021-feature-name
763
763
  ```
764
764
 
765
+ **⚠️ CRITICAL: Increment Folder Structure Rules**
766
+
767
+ **ONLY these files allowed at increment ROOT:**
768
+ - `metadata.json` - Increment state (auto-managed)
769
+ - `spec.md` - Specification
770
+ - `plan.md` - Implementation plan
771
+ - `tasks.md` - Task list
772
+
773
+ **ALL other files MUST go in subfolders:**
774
+ ```
775
+ .specweave/increments/####-name/
776
+ ├── reports/ # Validation reports, QA, completion summaries
777
+ ├── logs/ # Debug logs, session traces
778
+ ├── scripts/ # Helper scripts
779
+ ├── docs/ # Additional documentation, domain knowledge
780
+ └── backups/ # Backup files
781
+ ```
782
+
765
783
  ### STEP 4: Create metadata.json FIRST (MANDATORY - CRITICAL ORDER!)
766
784
 
767
785
  **🚨 CRITICAL: metadata.json MUST be created BEFORE spec.md!**
@@ -271,12 +271,25 @@ Use `/sw:save` to commit and push changes across all repos at once:
271
271
  ```
272
272
 
273
273
  **Features:**
274
+ - **Auto-discovers nested repos** - Scans `repositories/`, `packages/`, `services/`, `apps/`, `libs/` for `.git` directories (up to 4 levels deep)
274
275
  - Auto-detects repos with changes
275
276
  - Sets up remotes if missing (prompts for URL or uses umbrella config)
276
277
  - Commits with same message to all repos
277
278
  - Pushes to origin
278
279
  - Skips repos with no changes
279
280
 
281
+ **Auto-Discovery (No Config Required):**
282
+ Even without umbrella config, `/sw:save` automatically finds all nested repos:
283
+ ```
284
+ my-project/
285
+ ├── repositories/
286
+ │ ├── frontend/.git # ← Auto-discovered
287
+ │ ├── backend/.git # ← Auto-discovered
288
+ │ └── shared/.git # ← Auto-discovered
289
+ └── .git # ← Parent repo included
290
+ ```
291
+ All 4 repos will be committed and pushed with a single `/sw:save` command!
292
+
280
293
  ## Important Notes
281
294
 
282
295
  1. **Each repo is independent** - Own `.specweave/`, own increments, own external tool sync