specweave 1.0.76 → 1.0.78

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!**
@@ -976,6 +1017,8 @@ For **contributors to SpecWeave itself** (not users).
976
1017
 
977
1018
  ## Marketplace Installation (CRITICAL)
978
1019
 
1020
+ ### For SpecWeave Contributors (Development)
1021
+
979
1022
  **ALWAYS use GitHub marketplace mode. NEVER use local symlinks or directory mode.**
980
1023
 
981
1024
  ```bash
@@ -996,6 +1039,32 @@ bash scripts/refresh-marketplace.sh --github
996
1039
  bash scripts/refresh-marketplace.sh # Defaults to --github
997
1040
  ```
998
1041
 
1042
+ ### For End Users (Production)
1043
+
1044
+ **Users install SpecWeave globally and use CLI commands:**
1045
+
1046
+ ```bash
1047
+ # Install SpecWeave globally
1048
+ npm install -g specweave
1049
+
1050
+ # Initialize project (first time)
1051
+ specweave init .
1052
+
1053
+ # Update marketplace plugins (gets latest from GitHub)
1054
+ specweave refresh-marketplace
1055
+
1056
+ # Update instruction files (CLAUDE.md, AGENTS.md)
1057
+ specweave update-instructions
1058
+ ```
1059
+
1060
+ **After marketplace updates**: Restart Claude Code for changes to take effect.
1061
+
1062
+ **Verify installation**:
1063
+ ```bash
1064
+ specweave --version # Check SpecWeave version
1065
+ /plugin list --installed # In Claude Code - check plugins loaded
1066
+ ```
1067
+
999
1068
  ---
1000
1069
 
1001
1070
  ## Critical Safety Rules
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specweave",
3
- "version": "1.0.76",
3
+ "version": "1.0.78",
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`
@@ -354,6 +354,28 @@ Pure Ralph Wiggum behavior:
354
354
  - **Max Iterations**: Prevents runaway loops (2500 default)
355
355
  - **Max Hours**: Time boxing (600 hours / 25 days default)
356
356
  - **stop_hook_active**: Prevents infinite continuation loops
357
+ - **Sound Notifications** (v2.6): Audible alerts when Claude stops working
358
+
359
+ ## 🔔 Sound Notifications (NEW in v2.6!)
360
+
361
+ **Auto mode plays a satisfying sound when work completes successfully!**
362
+
363
+ ### When Sound Plays
364
+
365
+ | Event | Sound | Platforms | Meaning |
366
+ |-------|-------|-----------|---------|
367
+ | **Session Complete (Success)** ✅ | Glass.aiff (macOS)<br>complete.oga (Linux)<br>Windows Notify (Windows) | All | All tasks done, tests passing - work finished! |
368
+
369
+ **Sound plays ONLY on complete success** - when all tasks are done AND all tests pass. This way you know when to check back without being interrupted during ongoing work.
370
+
371
+ ### Cross-Platform Support
372
+
373
+ The sound notification works automatically on:
374
+ - **macOS**: Glass.aiff (satisfying chime)
375
+ - **Linux**: PulseAudio/ALSA/speaker-test fallbacks
376
+ - **Windows**: PowerShell beeps
377
+
378
+ Sounds fail gracefully on systems without audio support.
357
379
 
358
380
  ## 🔧 v2.3 Per-Agent Stop Hook Behavior (NEW!)
359
381
 
@@ -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
@@ -117,6 +117,36 @@ Core hooks automate SpecWeave's fundamental workflows:
117
117
 
118
118
  ---
119
119
 
120
+ ### 5. `stop-auto.sh` (Auto Mode)
121
+ **Triggers**: When Claude tries to exit during autonomous execution (`/sw:auto`)
122
+
123
+ **Actions**:
124
+ 1. Checks if all tasks are complete
125
+ 2. Validates test execution (unit + E2E)
126
+ 3. Verifies completion criteria met
127
+ 4. Blocks exit if work incomplete
128
+ 5. Re-feeds prompt to continue iteration
129
+
130
+ **Configuration**: Registered in `hooks/hooks.json`:
131
+ ```json
132
+ {
133
+ "hooks": {
134
+ "Stop": [{
135
+ "hooks": [{
136
+ "type": "command",
137
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks/stop-auto.sh"
138
+ }]
139
+ }]
140
+ }
141
+ }
142
+ ```
143
+
144
+ **Use case**: Enables autonomous execution loops. Claude works until ALL tasks complete and tests pass, then gracefully exits.
145
+
146
+ **See**: `/sw:auto` command documentation for full auto mode details.
147
+
148
+ ---
149
+
120
150
  ## How Hooks Work (Claude Code Native)
121
151
 
122
152
  **CRITICAL**: Hooks are **NOT copied** to `.claude/hooks/`. They stay in `plugins/specweave/hooks/` and Claude Code discovers them automatically.
@@ -31,6 +31,16 @@
31
31
  }
32
32
  ]
33
33
  }
34
+ ],
35
+ "Stop": [
36
+ {
37
+ "hooks": [
38
+ {
39
+ "type": "command",
40
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks/stop-auto.sh"
41
+ }
42
+ ]
43
+ }
34
44
  ]
35
45
  }
36
46
  }
@@ -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
@@ -373,74 +570,236 @@ detect_command_timeout() {
373
570
  fi
374
571
  }
375
572
 
573
+ # ================================================================
574
+ # SOUND NOTIFICATION HELPER (v2.6)
575
+ # Cross-platform sound notification for user awareness
576
+ # ================================================================
577
+ play_notification_sound() {
578
+ local sound_type="${1:-attention}" # "success" or "attention"
579
+
580
+ # Detect OS and play appropriate sound
581
+ case "$(uname -s)" in
582
+ Darwin)
583
+ # macOS - use afplay with system sounds
584
+ if [ "$sound_type" = "success" ]; then
585
+ afplay /System/Library/Sounds/Glass.aiff 2>/dev/null &
586
+ else
587
+ afplay /System/Library/Sounds/Ping.aiff 2>/dev/null &
588
+ fi
589
+ ;;
590
+ Linux)
591
+ # Linux - try multiple sound systems (paplay, aplay, speaker-test)
592
+ if command -v paplay >/dev/null 2>&1; then
593
+ # PulseAudio (most common on modern Linux)
594
+ if [ "$sound_type" = "success" ]; then
595
+ paplay /usr/share/sounds/freedesktop/stereo/complete.oga 2>/dev/null &
596
+ else
597
+ paplay /usr/share/sounds/freedesktop/stereo/bell.oga 2>/dev/null &
598
+ fi
599
+ elif command -v aplay >/dev/null 2>&1; then
600
+ # ALSA fallback
601
+ aplay /usr/share/sounds/alsa/Front_Center.wav 2>/dev/null &
602
+ elif command -v speaker-test >/dev/null 2>&1; then
603
+ # Last resort - system beep
604
+ speaker-test -t sine -f 1000 -l 1 >/dev/null 2>&1 &
605
+ fi
606
+ ;;
607
+ MINGW*|MSYS*|CYGWIN*)
608
+ # Windows (Git Bash, WSL, Cygwin)
609
+ if command -v powershell.exe >/dev/null 2>&1; then
610
+ # Use PowerShell to play sound
611
+ if [ "$sound_type" = "success" ]; then
612
+ powershell.exe -c "(New-Object Media.SoundPlayer 'C:\Windows\Media\Windows Notify System Generic.wav').PlaySync();" 2>/dev/null &
613
+ else
614
+ powershell.exe -c "[console]::beep(800, 300)" 2>/dev/null &
615
+ fi
616
+ fi
617
+ ;;
618
+ esac
619
+ }
620
+
376
621
  # Helper: Output approve decision
377
622
  # ALWAYS log why we're stopping for debugging
623
+ # Enhanced v2.4: Agent-aware labeling with clear hierarchy
378
624
  approve() {
379
625
  local reason="${1:-Session complete}"
380
626
  local is_success="${2:-false}"
381
627
 
382
- # Log the stop reason
383
- log_stop_reason "$reason" "approve_called" "$is_success"
628
+ # Detect agent type for proper labeling
629
+ local agent_type=$(detect_agent_type "$TRANSCRIPT_PATH")
630
+ local agent_display=$(get_agent_display_name "$agent_type")
631
+ local agent_short=$(get_agent_type_short "$agent_type")
632
+
633
+ # Get subagent stats for summary
634
+ local subagent_stats=$(get_subagent_stats)
635
+ local subagents_spawned=$(echo "$subagent_stats" | jq -r '.totalSpawned // 0')
636
+
637
+ # Log the stop reason with agent info
638
+ log_stop_reason "$reason" "approve_called:$agent_short" "$is_success"
639
+
640
+ # Get test breakdown by type if available (NEW - v2.5)
641
+ local test_breakdown=$(get_test_type_breakdown "$TRANSCRIPT_PATH")
384
642
 
385
643
  # Display the stop reason prominently to STDERR (not stdout, which is for JSON)
386
644
  {
387
645
  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 "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
646
+ if [ "$agent_type" = "orchestrator" ]; then
647
+ # Main orchestrator stopping - this is a SESSION END
648
+ if [ "$is_success" = "true" ]; then
649
+ echo "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓"
650
+ echo "┃ ✅ AUTO SESSION COMPLETE ┃"
651
+ echo "$agent_display ┃"
652
+ echo "┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫"
653
+ echo "┃ Status: SUCCESS - All work completed ┃"
654
+ else
655
+ echo "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓"
656
+ echo "┃ 🛑 AUTO SESSION STOPPING ┃"
657
+ echo "┃ $agent_display ┃"
658
+ echo "┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫"
659
+ echo "┃ Status: STOPPED - Requires attention ┃"
660
+ fi
661
+ echo "┃ Reason: $(printf '%-48s' "$reason")┃"
662
+ echo "┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫"
663
+ echo "┃ 📊 SESSION SUMMARY ┃"
664
+ echo "┃ ├─ Iterations: ${ITERATION:-0}/${MAX_ITERATIONS:-100} ┃"
665
+ [ -n "$CURRENT_INCREMENT" ] && echo "┃ ├─ Increment: $(printf '%-42s' "$CURRENT_INCREMENT")┃"
666
+ [ "$subagents_spawned" -gt 0 ] && echo "┃ ├─ Subagents spawned: $(printf '%-35s' "$subagents_spawned")┃"
667
+ if [ "${TESTS_RUN:-false}" = "true" ]; then
668
+ if [ -n "$test_breakdown" ] && [ "$test_breakdown" != "{}" ]; then
669
+ # Show detailed breakdown by test type (NEW - v2.5)
670
+ echo "┃ └─ Tests (detailed breakdown): ┃"
671
+ local unit_passed=$(echo "$test_breakdown" | jq -r '.unit.passed // 0')
672
+ local unit_failed=$(echo "$test_breakdown" | jq -r '.unit.failed // 0')
673
+ local integration_passed=$(echo "$test_breakdown" | jq -r '.integration.passed // 0')
674
+ local integration_failed=$(echo "$test_breakdown" | jq -r '.integration.failed // 0')
675
+ local e2e_passed=$(echo "$test_breakdown" | jq -r '.e2e.passed // 0')
676
+ local e2e_failed=$(echo "$test_breakdown" | jq -r '.e2e.failed // 0')
677
+
678
+ if [ "$unit_passed" -gt 0 ] || [ "$unit_failed" -gt 0 ]; then
679
+ echo "┃ • Unit: ${unit_passed} passed, ${unit_failed} failed ┃"
680
+ fi
681
+ if [ "$integration_passed" -gt 0 ] || [ "$integration_failed" -gt 0 ]; then
682
+ echo "┃ • Integration: ${integration_passed} passed, ${integration_failed} failed ┃"
683
+ fi
684
+ if [ "$e2e_passed" -gt 0 ] || [ "$e2e_failed" -gt 0 ]; then
685
+ echo "┃ • E2E: ${e2e_passed} passed, ${e2e_failed} failed ┃"
686
+ fi
687
+ else
688
+ # Fallback to simple summary
689
+ echo "┃ └─ Tests: ${TESTS_PASSED:-0} passed, ${TESTS_FAILED:-0} failed ┃"
690
+ fi
691
+ fi
692
+ echo "┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛"
693
+
694
+ # ================================================================
695
+ # SOUND NOTIFICATION ON SUCCESS (v2.6)
696
+ # Play sound ONLY when session completes successfully
697
+ # This lets users know they can check back - work is done!
698
+ # Cross-platform support via helper function
699
+ # ================================================================
700
+ if [ "$is_success" = "true" ]; then
701
+ play_notification_sound "success"
702
+ fi
703
+ else
704
+ # Subagent stopping - this is a RETURN TO PARENT
705
+ echo "┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓"
706
+ echo "┃ ↩️ SUBAGENT COMPLETE - Returning to parent ┃"
707
+ echo "┃ $agent_display ┃"
708
+ echo "┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫"
709
+ echo "┃ Result: $(printf '%-49s' "$reason")┃"
710
+ echo "┃ ↩️ Control returning to: 🤖 Main Orchestrator ┃"
711
+ echo "┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛"
712
+ fi
396
713
  echo ""
397
714
  } >&2
398
715
 
399
- echo "{\"decision\": \"approve\", \"reason\": \"$reason\"}"
716
+ echo "{\"decision\": \"approve\", \"reason\": \"$reason\", \"agentType\": \"$agent_short\"}"
400
717
  exit 0
401
718
  }
402
719
 
403
720
  # Helper: Output block decision with system message
404
721
  # Properly escapes JSON strings with newlines
405
- # Also displays stop criteria prominently to stderr (NEW - v2.2, enhanced v2.3)
722
+ # Enhanced v2.4: Agent-aware labeling with clear hierarchy and stop conditions
406
723
  block() {
407
724
  local reason="$1"
408
725
  local system_message="$2"
409
726
 
727
+ # Detect agent type for proper labeling
728
+ local agent_type=$(detect_agent_type "$TRANSCRIPT_PATH")
729
+ local agent_display=$(get_agent_display_name "$agent_type")
730
+ local agent_short=$(get_agent_type_short "$agent_type")
731
+
410
732
  # Get current stop criteria for display
411
733
  local tdd_mode=$(is_tdd_strict_mode)
412
734
  local stop_criteria=""
735
+ local stop_criteria_detail=""
413
736
 
414
- # Build stop criteria message - MORE PROMINENT (v2.3)
737
+ # Build stop criteria message - MORE PROMINENT (v2.4)
415
738
  if [ "$tdd_mode" = "true" ]; then
416
- stop_criteria="🔴 TDD MODE: ALL tasks [x] + ALL tests GREEN (0 failures)"
739
+ stop_criteria="TDD STRICT MODE"
740
+ stop_criteria_detail="ALL tasks [x] + ALL tests GREEN (0 failures)"
417
741
  else
418
- stop_criteria=" ALL tasks [x] completed + tests passing"
742
+ stop_criteria="STANDARD MODE"
743
+ stop_criteria_detail="ALL tasks [x] completed + tests passing"
419
744
  fi
420
745
 
421
- # Display stop criteria and continuation reason to STDERR (v2.3 enhanced)
746
+ # Get subagent activity for display
747
+ local subagent_stats=$(get_subagent_stats)
748
+ local subagents_spawned=$(echo "$subagent_stats" | jq -r '.totalSpawned // 0')
749
+
750
+ # Display stop criteria and continuation reason to STDERR (v2.4 enhanced)
422
751
  {
423
752
  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 "╚══════════════════════════════════════════════════════════════╝"
753
+ if [ "$agent_type" = "orchestrator" ]; then
754
+ # Main orchestrator continuing - SESSION CONTINUES
755
+ echo "╔══════════════════════════════════════════════════════════════╗"
756
+ echo "║ 🔄 AUTO SESSION CONTINUING ║"
757
+ echo "║ $agent_display ║"
758
+ echo "╠══════════════════════════════════════════════════════════════╣"
759
+ echo "║ Why: $(printf '%-52s' "$reason")║"
760
+ echo "║ Iteration: $(printf '%-47s' "${ITERATION:-0}/${MAX_ITERATIONS:-2500}")║"
761
+ [ -n "$CURRENT_INCREMENT" ] && echo "║ Increment: $(printf '%-47s' "$CURRENT_INCREMENT")║"
762
+ [ "$subagents_spawned" -gt 0 ] && echo "║ Subagents used: $(printf '%-42s' "$subagents_spawned")"
763
+ echo "╠══════════════════════════════════════════════════════════════╣"
764
+ echo "║ 🎯 WHEN WILL SESSION STOP? ║"
765
+ echo "║ ├─ Mode: $(printf '%-48s' "$stop_criteria")║"
766
+ echo "║ └─ Criteria: $(printf '%-44s' "$stop_criteria_detail")║"
767
+ if [ "${TESTS_RUN:-false}" = "true" ]; then
768
+ if [ "${TESTS_FAILED:-0}" -gt 0 ]; then
769
+ echo "║ ⚠️ Tests: ${TESTS_PASSED:-0} passed, ${TESTS_FAILED:-0} FAILED (blocking!) ║"
770
+ else
771
+ echo "║ ✅ Tests: ${TESTS_PASSED:-0} passed, 0 failed ║"
772
+ fi
773
+ else
774
+ echo "║ ⏳ Tests: NOT YET RUN ║"
775
+ fi
776
+ [ "$tdd_mode" = "true" ] && echo "║ 📋 TDD Source: $(printf '%-42s' "$(get_tdd_source)")║"
777
+ echo "╚══════════════════════════════════════════════════════════════╝"
778
+ else
779
+ # Subagent continuing - SUBAGENT KEEPS WORKING
780
+ echo "╔══════════════════════════════════════════════════════════════╗"
781
+ echo "║ 🔧 SUBAGENT CONTINUING WORK ║"
782
+ echo "║ $agent_display ║"
783
+ echo "╠══════════════════════════════════════════════════════════════╣"
784
+ echo "║ Task: $(printf '%-51s' "$reason")║"
785
+ echo "╠══════════════════════════════════════════════════════════════╣"
786
+ echo "║ 🎯 WHEN WILL SUBAGENT RETURN? ║"
787
+ echo "║ └─ When assigned task is complete ║"
788
+ echo "║ ↩️ Returns to: 🤖 Main Orchestrator ║"
789
+ echo "╚══════════════════════════════════════════════════════════════╝"
790
+ fi
435
791
  echo ""
436
792
  } >&2
437
793
 
794
+ # NOTE: No sound notification on block - sounds only play on SUCCESS
795
+ # When Claude is continuing work, user doesn't need to be notified
796
+
438
797
  if [ -n "$system_message" ]; then
439
798
  # Escape special characters for JSON
440
799
  local escaped_message=$(echo "$system_message" | jq -Rs .)
441
- echo "{\"decision\": \"block\", \"reason\": \"$reason\", \"systemMessage\": $escaped_message}"
800
+ echo "{\"decision\": \"block\", \"reason\": \"$reason\", \"systemMessage\": $escaped_message, \"agentType\": \"$agent_short\"}"
442
801
  else
443
- echo "{\"decision\": \"block\", \"reason\": \"$reason\"}"
802
+ echo "{\"decision\": \"block\", \"reason\": \"$reason\", \"agentType\": \"$agent_short\"}"
444
803
  fi
445
804
  exit 0
446
805
  }
@@ -859,6 +1218,80 @@ parse_test_results() {
859
1218
  echo "{\"passed\":$passed,\"failed\":$failed,\"total\":$total,\"framework\":\"$framework\",\"testsRun\":$tests_run}"
860
1219
  }
861
1220
 
1221
+ # ============================================================================
1222
+ # TEST TYPE BREAKDOWN (NEW - v2.5)
1223
+ # Categorizes test results by type (unit/integration/E2E) for detailed summary
1224
+ # ============================================================================
1225
+
1226
+ # Get test type breakdown from transcript
1227
+ # Returns: JSON with {unit: {passed, failed}, integration: {passed, failed}, e2e: {passed, failed}}
1228
+ get_test_type_breakdown() {
1229
+ local transcript="$1"
1230
+
1231
+ if [ ! -f "$transcript" ]; then
1232
+ echo "{}"
1233
+ return
1234
+ fi
1235
+
1236
+ local unit_passed=0
1237
+ local unit_failed=0
1238
+ local integration_passed=0
1239
+ local integration_failed=0
1240
+ local e2e_passed=0
1241
+ local e2e_failed=0
1242
+
1243
+ # Check for test command patterns to categorize
1244
+ # Unit tests: vitest, jest, pytest, go test
1245
+ if grep -qE '(npm.*test:unit|npx vitest|npx jest|pytest|go test|cargo test)' "$transcript" 2>/dev/null; then
1246
+ # Parse unit test results
1247
+ local unit_result=$(grep -oE 'Tests?\s+[0-9]+\s+passed' "$transcript" 2>/dev/null | tail -1)
1248
+ if [ -n "$unit_result" ]; then
1249
+ unit_passed=$(echo "$unit_result" | grep -oE '[0-9]+' | head -1)
1250
+ local unit_failed_match=$(grep -oE '[0-9]+\s+failed' "$transcript" 2>/dev/null | tail -1 | grep -oE '[0-9]+' || echo "0")
1251
+ unit_failed=${unit_failed_match:-0}
1252
+ fi
1253
+ fi
1254
+
1255
+ # Integration tests: usually named test:integration
1256
+ if grep -qE 'npm.*test:integration' "$transcript" 2>/dev/null; then
1257
+ # Parse integration test results (same format as unit)
1258
+ local int_result=$(grep -oE 'Tests?\s+[0-9]+\s+passed' "$transcript" 2>/dev/null | tail -1)
1259
+ if [ -n "$int_result" ]; then
1260
+ integration_passed=$(echo "$int_result" | grep -oE '[0-9]+' | head -1)
1261
+ local int_failed_match=$(grep -oE '[0-9]+\s+failed' "$transcript" 2>/dev/null | tail -1 | grep -oE '[0-9]+' || echo "0")
1262
+ integration_failed=${int_failed_match:-0}
1263
+ fi
1264
+ fi
1265
+
1266
+ # E2E tests: Playwright, Cypress, Detox, Maestro
1267
+ if grep -qE '(npx playwright|npx cypress|npx detox|maestro test|test:e2e)' "$transcript" 2>/dev/null; then
1268
+ # Parse E2E test results
1269
+ local e2e_passed_match=$(grep -oE '[0-9]+\s+passed' "$transcript" 2>/dev/null | tail -1 | grep -oE '[0-9]+')
1270
+ local e2e_failed_match=$(grep -oE '[0-9]+\s+failed' "$transcript" 2>/dev/null | tail -1 | grep -oE '[0-9]+')
1271
+
1272
+ e2e_passed=${e2e_passed_match:-0}
1273
+ e2e_failed=${e2e_failed_match:-0}
1274
+ fi
1275
+
1276
+ # Return JSON with breakdown
1277
+ cat <<EOF
1278
+ {
1279
+ "unit": {
1280
+ "passed": $unit_passed,
1281
+ "failed": $unit_failed
1282
+ },
1283
+ "integration": {
1284
+ "passed": $integration_passed,
1285
+ "failed": $integration_failed
1286
+ },
1287
+ "e2e": {
1288
+ "passed": $e2e_passed,
1289
+ "failed": $e2e_failed
1290
+ }
1291
+ }
1292
+ EOF
1293
+ }
1294
+
862
1295
  # Extract detailed failure information from transcript
863
1296
  # Returns: JSON with failure details
864
1297
  extract_failure_details() {
@@ -1787,7 +2220,30 @@ Continue with /sw:do and add missing E2E tests."
1787
2220
  --argjson passed "${TESTS_PASSED:-0}" --argjson failed "${TESTS_FAILED:-0}" \
1788
2221
  '.status = "completed" | .endTime = $now | .endReason = "all_tasks_complete" | .finalTestResults = {"passed": $passed, "failed": $failed}' \
1789
2222
  > "$SESSION_FILE"
1790
- approve "All tasks completed, all tests passed ($TESTS_PASSED passed, 0 failed)" "true"
2223
+
2224
+ # Build detailed completion reason with test breakdown (NEW - v2.5)
2225
+ local completion_reason="All tasks completed"
2226
+ local test_breakdown=$(get_test_type_breakdown "$TRANSCRIPT_PATH")
2227
+ if [ -n "$test_breakdown" ] && [ "$test_breakdown" != "{}" ]; then
2228
+ local unit_p=$(echo "$test_breakdown" | jq -r '.unit.passed // 0')
2229
+ local int_p=$(echo "$test_breakdown" | jq -r '.integration.passed // 0')
2230
+ local e2e_p=$(echo "$test_breakdown" | jq -r '.e2e.passed // 0')
2231
+
2232
+ local test_details=""
2233
+ [ "$unit_p" -gt 0 ] && test_details="${test_details}${unit_p} unit"
2234
+ [ "$int_p" -gt 0 ] && test_details="${test_details}${test_details:+, }${int_p} integration"
2235
+ [ "$e2e_p" -gt 0 ] && test_details="${test_details}${test_details:+, }${e2e_p} E2E"
2236
+
2237
+ if [ -n "$test_details" ]; then
2238
+ completion_reason="$completion_reason, tests passed: $test_details"
2239
+ else
2240
+ completion_reason="$completion_reason, all tests passed ($TESTS_PASSED passed, 0 failed)"
2241
+ fi
2242
+ else
2243
+ completion_reason="$completion_reason, all tests passed ($TESTS_PASSED passed, 0 failed)"
2244
+ fi
2245
+
2246
+ approve "$completion_reason" "true"
1791
2247
  else
1792
2248
  # More increments in queue - transition to next
1793
2249
  NEXT_INCREMENT=$(echo "$SESSION" | jq -r '.incrementQueue[1] // null')
@@ -1909,7 +2365,15 @@ echo "$SESSION" | jq --argjson iter "$NEXT_ITERATION" --arg now "$(date -u +%Y-%
1909
2365
  '.iteration = $iter | .lastActivity = $now' \
1910
2366
  > "$SESSION_FILE"
1911
2367
 
1912
- # Build context message with test instructions (v2.2)
2368
+ # Build context message with test instructions (v2.4 enhanced with agent hierarchy)
2369
+ # Detect agent type for context message
2370
+ AGENT_TYPE=$(detect_agent_type "$TRANSCRIPT_PATH")
2371
+ AGENT_DISPLAY=$(get_agent_display_name "$AGENT_TYPE")
2372
+
2373
+ # Get subagent activity for context
2374
+ SUBAGENT_ACTIVITY=$(detect_subagent_activity "$TRANSCRIPT_PATH")
2375
+ SUBAGENT_COUNT=$(echo "$SUBAGENT_ACTIVITY" | jq -r '.spawned // 0')
2376
+
1913
2377
  if [ "$SIMPLE_MODE" = "true" ]; then
1914
2378
  CONTEXT="Continue working. Iteration $NEXT_ITERATION/$MAX_ITERATIONS."
1915
2379
  else
@@ -1936,36 +2400,75 @@ else
1936
2400
  # Get test instructions for this project (NEW - v2.2)
1937
2401
  TEST_INSTRUCTIONS=$(format_test_instructions)
1938
2402
 
1939
- # Build stop criteria message - MUST be clear for each agent
2403
+ # Build agent header based on type (NEW - v2.4)
2404
+ AGENT_HEADER=""
2405
+ if [ "$AGENT_TYPE" = "orchestrator" ]; then
2406
+ AGENT_HEADER="
2407
+ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2408
+ ┃ $AGENT_DISPLAY ┃
2409
+ ┃ AUTO SESSION - Iteration $NEXT_ITERATION/$MAX_ITERATIONS ┃
2410
+ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛"
2411
+ else
2412
+ AGENT_HEADER="
2413
+ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2414
+ ┃ $AGENT_DISPLAY ┃
2415
+ ┃ Working under: 🤖 Main Orchestrator ┃
2416
+ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛"
2417
+ fi
2418
+
2419
+ # Build stop criteria message - MUST be clear for each agent (v2.4)
1940
2420
  STOP_CRITERIA=""
1941
- if [ "$TDD_MODE" = "true" ]; then
1942
- STOP_CRITERIA="
2421
+ if [ "$AGENT_TYPE" = "orchestrator" ]; then
2422
+ # Main orchestrator stop criteria
2423
+ if [ "$TDD_MODE" = "true" ]; then
2424
+ STOP_CRITERIA="
1943
2425
  ╔══════════════════════════════════════════════════════════════╗
1944
- ║ 🎯 AGENT STOP CONDITION (TDD STRICT MODE)
2426
+ ║ 🎯 SESSION STOP CONDITION (TDD STRICT MODE)
1945
2427
  ╠══════════════════════════════════════════════════════════════╣
1946
2428
  ║ ✅ ALL tasks in tasks.md marked [x] completed ║
1947
2429
  ║ ✅ ALL tests pass (0 failures required) ║
1948
2430
  ║ ✅ Test execution detected in output ║
1949
2431
  ╠══════════════════════════════════════════════════════════════╣
1950
- ║ Source: $(printf '%-45s' "$(get_tdd_source)")║
2432
+ TDD Source: $(printf '%-42s' "$(get_tdd_source)")║
1951
2433
  ╚══════════════════════════════════════════════════════════════╝"
1952
- else
1953
- STOP_CRITERIA="
2434
+ else
2435
+ STOP_CRITERIA="
1954
2436
  ╔══════════════════════════════════════════════════════════════╗
1955
- ║ 🎯 AGENT STOP CONDITION
2437
+ ║ 🎯 SESSION STOP CONDITION
1956
2438
  ╠══════════════════════════════════════════════════════════════╣
1957
2439
  ║ ✅ ALL tasks in tasks.md marked [x] completed ║
1958
2440
  ║ ✅ Tests executed and passing ║
1959
2441
  ╚══════════════════════════════════════════════════════════════╝"
2442
+ fi
2443
+ else
2444
+ # Subagent stop criteria - simpler, just complete the task
2445
+ STOP_CRITERIA="
2446
+ ╔══════════════════════════════════════════════════════════════╗
2447
+ ║ 🎯 SUBAGENT STOP CONDITION ║
2448
+ ╠══════════════════════════════════════════════════════════════╣
2449
+ ║ ✅ Complete the assigned task ║
2450
+ ║ ↩️ Then return control to Main Orchestrator ║
2451
+ ╚══════════════════════════════════════════════════════════════╝"
2452
+ fi
2453
+
2454
+ # Build subagent info if any were spawned (NEW - v2.4)
2455
+ SUBAGENT_INFO=""
2456
+ if [ "$SUBAGENT_COUNT" -gt 0 ] && [ "$AGENT_TYPE" = "orchestrator" ]; then
2457
+ SUBAGENT_TYPES=$(echo "$SUBAGENT_ACTIVITY" | jq -r '.types | join(", ")' 2>/dev/null || echo "various")
2458
+ SUBAGENT_INFO="
2459
+ 📦 SUBAGENT ACTIVITY (this session):
2460
+ ├─ Spawned: $SUBAGENT_COUNT subagent(s)
2461
+ └─ Types: $SUBAGENT_TYPES"
1960
2462
  fi
1961
2463
 
1962
- # Build full context message
1963
- CONTEXT="🤖 AUTO MODE ACTIVE - Iteration $NEXT_ITERATION/$MAX_ITERATIONS
2464
+ # Build full context message (v2.4)
2465
+ CONTEXT="$AGENT_HEADER
1964
2466
  $STOP_CRITERIA
1965
2467
 
1966
2468
  📊 CURRENT PROGRESS:
1967
2469
  $PROGRESS
1968
2470
  $TEST_STATUS
2471
+ $SUBAGENT_INFO
1969
2472
 
1970
2473
  $TEST_INSTRUCTIONS
1971
2474
 
@@ -1981,8 +2484,9 @@ $TEST_INSTRUCTIONS
1981
2484
  ⚠️ This agent will NOT stop until the STOP CONDITION above is met!"
1982
2485
  fi
1983
2486
 
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"
2487
+ # Log iteration with agent info (v2.4)
2488
+ AGENT_SHORT=$(get_agent_type_short "$AGENT_TYPE")
2489
+ 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
2490
 
1987
2491
  # Block exit and re-feed prompt
1988
2492
  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