@atlashub/smartstack-cli 2.7.2 → 2.7.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/.documentation/agents.html +0 -4
  2. package/.documentation/business-analyse.html +0 -4
  3. package/.documentation/cli-commands.html +0 -4
  4. package/.documentation/commands.html +0 -77
  5. package/.documentation/css/styles.css +0 -8
  6. package/.documentation/efcore.html +0 -4
  7. package/.documentation/gitflow.html +0 -4
  8. package/.documentation/hooks.html +0 -4
  9. package/.documentation/index.html +2 -28
  10. package/.documentation/init.html +8 -14
  11. package/.documentation/installation.html +0 -11
  12. package/.documentation/js/app.js +2 -16
  13. package/.documentation/ralph-loop.html +0 -4
  14. package/.documentation/test-web.html +0 -4
  15. package/README.md +0 -1
  16. package/dist/index.js +3 -7
  17. package/dist/index.js.map +1 -1
  18. package/package.json +2 -3
  19. package/templates/agents/docs-sync-checker.md +2 -2
  20. package/templates/agents/efcore/squash.md +3 -1
  21. package/templates/hooks/docs-drift-check.md +4 -5
  22. package/templates/skills/_resources/context-digest-template.md +2 -2
  23. package/templates/skills/_resources/doc-context-cache.md +0 -2
  24. package/templates/skills/_resources/docs-manifest-schema.md +1 -3
  25. package/templates/skills/_resources/mcp-validate-documentation-spec.md +1 -3
  26. package/templates/skills/_shared.md +24 -25
  27. package/templates/skills/application/steps/step-04-backend.md +185 -11
  28. package/templates/skills/application/steps/step-06-migration.md +41 -2
  29. package/templates/skills/application/templates-seed.md +151 -0
  30. package/templates/skills/business-analyse/steps/step-05-handoff.md +59 -17
  31. package/templates/skills/controller/steps/step-01-analyze.md +1 -1
  32. package/templates/skills/efcore/steps/squash/step-03-create.md +39 -0
  33. package/templates/skills/ralph-loop/SKILL.md +35 -3
  34. package/templates/skills/ralph-loop/steps/step-00-init.md +93 -0
  35. package/templates/skills/ralph-loop/steps/step-01-task.md +202 -3
  36. package/templates/skills/ralph-loop/steps/step-02-execute.md +75 -3
  37. package/templates/skills/ralph-loop/steps/step-03-commit.md +8 -1
  38. package/templates/skills/ralph-loop/steps/step-04-check.md +77 -13
  39. package/templates/skills/ralph-loop/steps/step-05-report.md +79 -0
  40. package/.documentation/apex.html +0 -1027
  41. package/templates/skills/apex/SKILL.md +0 -297
  42. package/templates/skills/apex/steps/step-00-init.md +0 -212
  43. package/templates/skills/apex/steps/step-01-analyze.md +0 -263
  44. package/templates/skills/apex/steps/step-02-plan.md +0 -255
  45. package/templates/skills/apex/steps/step-03-execute.md +0 -217
  46. package/templates/skills/apex/steps/step-04-validate.md +0 -273
  47. package/templates/skills/apex/steps/step-04b-doc-sync.md +0 -162
  48. package/templates/skills/apex/steps/step-05-examine.md +0 -214
  49. package/templates/skills/apex/steps/step-06-resolve.md +0 -181
  50. package/templates/skills/apex/steps/step-07-tests.md +0 -206
  51. package/templates/skills/apex/steps/step-08-run-tests.md +0 -207
  52. package/templates/skills/apex/templates/00-context.md +0 -46
  53. package/templates/skills/apex/templates/01-analyze.md +0 -63
  54. package/templates/skills/apex/templates/02-plan.md +0 -63
  55. package/templates/skills/apex/templates/03-execute.md +0 -34
  56. package/templates/skills/apex/templates/04-validate.md +0 -61
  57. package/templates/skills/apex/templates/04b-doc-sync.md +0 -31
  58. package/templates/skills/apex/templates/05-examine.md +0 -58
  59. package/templates/skills/apex/templates/06-resolve.md +0 -39
  60. package/templates/skills/apex/templates/07-tests.md +0 -56
  61. package/templates/skills/apex/templates/08-run-tests.md +0 -41
  62. package/templates/skills/apex/templates/README.md +0 -69
  63. package/templates/skills/apex/templates/context-digest.md +0 -35
@@ -16,6 +16,193 @@ Load the current task from prd.json or create initial task breakdown with catego
16
16
 
17
17
  ## EXECUTION SEQUENCE:
18
18
 
19
+ ### 0. Multi-Module Queue Check
20
+
21
+ **Before any other logic, check for modules-queue.json:**
22
+
23
+ ```javascript
24
+ const queuePath = '.ralph/modules-queue.json';
25
+ const hasQueue = fileExists(queuePath);
26
+
27
+ if (hasQueue) {
28
+ const queue = readJSON(queuePath);
29
+ const currentModule = queue.modules[queue.currentIndex];
30
+
31
+ console.log(`Module ${queue.currentIndex + 1}/${queue.totalModules}: ${currentModule.code}`);
32
+
33
+ // Copy current module's prd file to prd.json (overwrite)
34
+ const modulePrd = readJSON(currentModule.prdFile);
35
+
36
+ // Transform PrdJson format to Ralph v2 if needed
37
+ if (modulePrd.project && modulePrd.requirements && !modulePrd.$version) {
38
+ // This is a PrdJson format from ss derive-prd → transform to Ralph v2
39
+ const transformedPrd = transformPrdJsonToRalphV2(modulePrd, currentModule.code);
40
+ writeJSON('.ralph/prd.json', transformedPrd);
41
+ } else {
42
+ // Already Ralph v2 format
43
+ writeJSON('.ralph/prd.json', modulePrd);
44
+ }
45
+
46
+ console.log(`Loaded PRD for module: ${currentModule.code}`);
47
+ }
48
+ ```
49
+
50
+ **PrdJson → Ralph v2 transformation:**
51
+
52
+ The PrdJson format (from `ss derive-prd`) has this structure:
53
+ ```
54
+ { version, source, project, requirements: { useCases[], functionalRequirements[] },
55
+ businessRules[], architecture: { entities[], apiEndpoints[], sections[] },
56
+ implementation: { filesToCreate: { domain[], application[], infrastructure[], api[], frontend[], tests[] } },
57
+ seedData }
58
+ ```
59
+
60
+ ```javascript
61
+ function transformPrdJsonToRalphV2(prdJson, moduleCode) {
62
+ const tasks = [];
63
+ let taskId = 1;
64
+
65
+ // Layer processing order (SmartStack convention)
66
+ const layerOrder = [
67
+ { key: "domain", category: "domain" },
68
+ { key: "infrastructure", category: "infrastructure" },
69
+ { key: "application", category: "application" },
70
+ { key: "api", category: "api" },
71
+ { key: "frontend", category: "frontend" },
72
+ { key: "seedData", category: "infrastructure" },
73
+ { key: "tests", category: "test" }
74
+ ];
75
+
76
+ const filesToCreate = prdJson.implementation?.filesToCreate || {};
77
+
78
+ // 1. Generate tasks from implementation.filesToCreate (primary source)
79
+ const lastIdByCategory = {};
80
+
81
+ for (const layer of layerOrder) {
82
+ const files = filesToCreate[layer.key] || [];
83
+ for (const fileSpec of files) {
84
+ const depIds = [];
85
+ // Depend on last task in same category (sequential within layer)
86
+ if (lastIdByCategory[layer.category]) {
87
+ depIds.push(lastIdByCategory[layer.category]);
88
+ }
89
+ // Cross-layer: api depends on last application task, frontend depends on last api task
90
+ if (layer.category === "api" && lastIdByCategory["application"]) {
91
+ depIds.push(lastIdByCategory["application"]);
92
+ }
93
+ if (layer.category === "frontend" && lastIdByCategory["api"]) {
94
+ depIds.push(lastIdByCategory["api"]);
95
+ }
96
+ if (layer.category === "test" && lastIdByCategory["api"]) {
97
+ depIds.push(lastIdByCategory["api"]);
98
+ }
99
+
100
+ // Build acceptance criteria from linked FRs and UCs
101
+ const linkedFRs = (fileSpec.linkedFRs || [])
102
+ .map(frId => {
103
+ const fr = prdJson.requirements?.functionalRequirements?.find(f => f.id === frId);
104
+ return fr ? fr.statement : frId;
105
+ });
106
+ const criteria = linkedFRs.length > 0
107
+ ? `Implements: ${linkedFRs.join("; ")}`
108
+ : `File ${fileSpec.path} created and compiles correctly`;
109
+
110
+ tasks.push({
111
+ id: taskId,
112
+ description: fileSpec.description
113
+ ? `[${layer.category}] ${fileSpec.description} → ${fileSpec.path}`
114
+ : `[${layer.category}] Create ${fileSpec.type}: ${fileSpec.path}`,
115
+ status: "pending",
116
+ category: layer.category,
117
+ dependencies: [...new Set(depIds)],
118
+ acceptance_criteria: criteria,
119
+ started_at: null,
120
+ completed_at: null,
121
+ iteration: null,
122
+ commit_hash: null,
123
+ files_changed: { created: [fileSpec.path], modified: [] },
124
+ validation: null,
125
+ error: null,
126
+ module: moduleCode
127
+ });
128
+
129
+ lastIdByCategory[layer.category] = taskId;
130
+ taskId++;
131
+ }
132
+ }
133
+
134
+ // 2. Add IClientSeedDataProvider task for client projects (ExtensionsDbContext)
135
+ // This is MANDATORY - without it, core seed data (navigation, permissions, roles) is dead code
136
+ const hasClientSeedData = filesToCreate["seedData"]?.some(f =>
137
+ f.type === "IClientSeedDataProvider" || f.path?.includes("SeedDataProvider"));
138
+ if (!hasClientSeedData && filesToCreate["seedData"]?.length > 0) {
139
+ tasks.push({
140
+ id: taskId,
141
+ description: `[infrastructure] Create IClientSeedDataProvider to inject core seed data at runtime`,
142
+ status: "pending",
143
+ category: "infrastructure",
144
+ dependencies: [lastIdByCategory["infrastructure"] || lastIdByCategory["domain"]].filter(Boolean),
145
+ acceptance_criteria: "IClientSeedDataProvider implements SeedNavigationAsync, SeedPermissionsAsync, SeedRolePermissionsAsync; registered in DI",
146
+ started_at: null, completed_at: null, iteration: null, commit_hash: null,
147
+ files_changed: { created: ["src/Infrastructure/Persistence/Seeding/{AppPascalName}SeedDataProvider.cs"], modified: ["src/Infrastructure/DependencyInjection.cs"] },
148
+ validation: null, error: null, module: moduleCode
149
+ });
150
+ lastIdByCategory["infrastructure"] = taskId;
151
+ taskId++;
152
+ }
153
+
154
+ // 3. Add a final validation task
155
+ tasks.push({
156
+ id: taskId,
157
+ description: `[validation] Build, test, and MCP validate module ${moduleCode}`,
158
+ status: "pending",
159
+ category: "validation",
160
+ dependencies: Object.values(lastIdByCategory),
161
+ acceptance_criteria: "dotnet build succeeds, dotnet test passes, MCP validate_conventions clean",
162
+ started_at: null,
163
+ completed_at: null,
164
+ iteration: null,
165
+ commit_hash: null,
166
+ files_changed: { created: [], modified: [] },
167
+ validation: null,
168
+ error: null,
169
+ module: moduleCode
170
+ });
171
+
172
+ return {
173
+ $version: "2.0.0",
174
+ feature: `${prdJson.project?.module || moduleCode} (${prdJson.project?.application || "app"})`,
175
+ status: "in_progress",
176
+ created: new Date().toISOString(),
177
+ updated_at: new Date().toISOString(),
178
+ metadata: {
179
+ cli_version: "unknown",
180
+ branch: getCurrentBranch(),
181
+ project_path: process.cwd(),
182
+ mcp_servers: { smartstack: true, context7: true },
183
+ module: moduleCode
184
+ },
185
+ config: {
186
+ max_iterations: {max_iterations},
187
+ completion_promise: "{completion_promise}",
188
+ current_iteration: 1
189
+ },
190
+ source: {
191
+ type: "ba-handoff",
192
+ handoff_path: null,
193
+ feature_json_path: prdJson.source?.featureJson || null,
194
+ module: moduleCode
195
+ },
196
+ tasks: tasks,
197
+ history: []
198
+ };
199
+ }
200
+ ```
201
+
202
+ **If no queue exists:** proceed with standard logic below (backward compatible).
203
+
204
+ ---
205
+
19
206
  ### 1. Check for Existing prd.json
20
207
 
21
208
  **If `.ralph/prd.json` exists:**
@@ -38,16 +225,27 @@ Load the current task from prd.json or create initial task breakdown with catego
38
225
  - What layer does each task belong to?
39
226
  - What dependencies exist between tasks?
40
227
 
41
- **Check for BA handoff source:**
228
+ **Check for BA handoff source (priority order):**
42
229
 
43
230
  ```bash
44
- # If a handoff document exists, use it to derive tasks
231
+ # Priority 1: Per-module PRD files (from ss derive-prd / BA handoff)
232
+ # These are already handled by Section 0 if modules-queue.json exists
233
+ PRD_MODULE=$(ls .ralph/prd-*.json 2>/dev/null | head -1)
234
+
235
+ # Priority 2: Single prd.json already present (from BA or previous run)
236
+ PRD_SINGLE=".ralph/prd.json"
237
+
238
+ # Priority 3: Markdown handoff document
45
239
  HANDOFF=$(find . -path "*development-handoff*" -name "*.md" | head -1)
46
240
  BA_OUTPUT=$(find . -path ".claude/output/ba/*" -name "4-*.md" | head -1)
47
241
  SOURCE_PATH=${HANDOFF:-$BA_OUTPUT}
48
242
  ```
49
243
 
50
- **If handoff found:**
244
+ **If prd-*.json files exist but no modules-queue.json:**
245
+ - This means step-00 didn't create a queue (single prd file)
246
+ - Copy the single `prd-*.json` to `prd.json` and transform if needed
247
+
248
+ **If markdown handoff found:**
51
249
  - Read the handoff document
52
250
  - Derive tasks from its specifications (per-layer breakdown)
53
251
  - Set `source.type = "ba-handoff"` and `source.handoff_path`
@@ -329,6 +527,7 @@ Read `.ralph/progress.txt` to understand:
329
527
  ```
330
528
  ╔══════════════════════════════════════════════════════════════════╗
331
529
  ║ ITERATION {current_iteration} / {max_iterations} ║
530
+ ║ {modules_queue ? "Module " + (currentIndex+1) + "/" + totalModules + ": " + current_module : ""} ║
332
531
  ╠══════════════════════════════════════════════════════════════════╣
333
532
  ║ Progress: {tasks_completed} / {tasks_total} tasks complete ║
334
533
  ║ Blocked: {tasks_blocked} ║
@@ -74,13 +74,85 @@ writeJSON('.ralph/prd.json', prd);
74
74
  |----------|-----------|----------|
75
75
  | `domain` | `validate_conventions` | Entity base class, IHasData, audit fields |
76
76
  | `application` | `validate_conventions`, `scaffold_extension` | CQRS, MediatR, FluentValidation |
77
- | `infrastructure` | `check_migrations`, `suggest_migration` | EF Core config, HasData seeding |
78
- | `api` | `scaffold_routes`, `validate_security` | Controller conventions, authorization |
79
- | `frontend` | `validate_frontend_routes`, `scaffold_frontend_extension` | React patterns, hooks |
77
+ | `infrastructure` | `check_migrations`, `suggest_migration` | EF Core config, HasData seeding, IClientSeedDataProvider, SqlObjects |
78
+ | `api` | `scaffold_routes`, `validate_security` | Controller conventions, authorization, context-based folder hierarchy |
79
+ | `frontend` | `validate_frontend_routes`, `scaffold_frontend_extension` | React patterns, hooks, Layout wrapper |
80
80
  | `i18n` | - | 4-language JSON structure |
81
81
  | `test` | `scaffold_tests`, `analyze_test_coverage` | xUnit, test naming conventions |
82
82
  | `validation` | `validate_conventions` | Build, test, lint checks |
83
83
 
84
+ **Infrastructure task guidance (MANDATORY directory conventions):**
85
+
86
+ When executing `infrastructure` category tasks, follow these rules strictly:
87
+
88
+ 1. **Seed data files** MUST go under `Infrastructure/Persistence/Seeding/Data/{Module}/`
89
+ - NEVER use `Infrastructure/Data/SeedData/` or `Data/SeedData/`
90
+ - Each module gets 5 core files: NavigationModuleSeedData.cs, PermissionsSeedData.cs, RolesSeedData.cs, TenantSeedData.cs, UserSeedData.cs
91
+
92
+ 2. **IClientSeedDataProvider** (client projects with `extensions` DbContext):
93
+ - Generate at `Infrastructure/Persistence/Seeding/{AppPascalName}SeedDataProvider.cs`
94
+ - Register in DI: `services.AddScoped<IClientSeedDataProvider, {AppPascalName}SeedDataProvider>()`
95
+ - Must implement: SeedNavigationAsync, SeedPermissionsAsync, SeedRolePermissionsAsync
96
+ - See `/application` skill step-03b-provider for the full pattern
97
+
98
+ 3. **DevDataSeeder** for domain entity seed data:
99
+ - Located at `Infrastructure/Persistence/Seeding/DevDataSeeder.cs`
100
+ - Implements `IDevDataSeeder`, registered in DI
101
+
102
+ 4. **Migrations** MUST all be in `Infrastructure/Persistence/Migrations/`
103
+ - NEVER create subdirectories under Migrations/
104
+ - Always use `-o Persistence/Migrations` flag with `dotnet ef migrations add`
105
+
106
+ 5. **SQL Objects** go under `Infrastructure/Persistence/SqlObjects/`
107
+ - `Functions/` for TVFs and scalar functions (.sql files)
108
+ - Use `SqlObjectHelper.cs` for embedded resource loading
109
+ - Use `CREATE OR ALTER` in SQL files for idempotency
110
+
111
+ **Frontend task guidance (MANDATORY Layout wrapper):**
112
+
113
+ When executing `frontend` category tasks, follow these rules strictly:
114
+
115
+ 1. **Routes MUST be inside Layout wrapper** (CRITICAL - violating this breaks the entire UI shell)
116
+ - All routes MUST be nested inside the appropriate context Layout component
117
+ - `platform.*` → `<Route path="/platform" element={<AdminLayout />}>` children
118
+ - `business.*` → `<Route path="/business" element={<BusinessLayout />}>` children
119
+ - `personal.*` → `<Route path="/personal/myspace" element={<UserLayout />}>` children
120
+ - **If routes are placed OUTSIDE the layout wrapper: header, sidebar, and AvatarMenu will NOT render**
121
+ - NEVER create flat routes at the top level of `<Routes>`
122
+
123
+ 2. **Nested routes, NOT flat routes**
124
+ - Use `<Route path="application"><Route path="module" element={...} /></Route>` pattern
125
+ - NEVER use `<Route path="/context/application/module" element={...} />` (flat)
126
+ - Use `<Route index element={<Navigate to="default" replace />} />` for default redirects
127
+
128
+ 3. **Tenant-prefixed routes** - If `/t/:slug/` block exists, add routes there too
129
+
130
+ 4. **See `/application` skill templates-frontend.md** for full template patterns
131
+
132
+ **API/Controller task guidance (MANDATORY folder hierarchy):**
133
+
134
+ When executing `api` category tasks, follow these rules strictly:
135
+
136
+ 1. **Controllers MUST be organized by context and application folders:**
137
+ - Path: `Api/Controllers/{ContextShort}/{Application}/{EntityName}Controller.cs`
138
+ - If no application sub-level: `Api/Controllers/{ContextShort}/{EntityName}Controller.cs`
139
+
140
+ 2. **Context-to-folder mapping (from navRoute):**
141
+
142
+ | NavRoute Prefix | Controller Folder |
143
+ |-----------------|-------------------|
144
+ | `platform.administration` | `Admin` |
145
+ | `platform.support` | `Support` |
146
+ | `business.*` | `Business` |
147
+ | `personal.*` | `User` |
148
+
149
+ 3. **Sub-folders for applications/modules with multiple controllers:**
150
+ - `Admin/Tenants/` for tenant-related controllers
151
+ - `Admin/AI/` for AI-related controllers
152
+ - `Admin/Communications/` for communication controllers
153
+
154
+ 4. **Reference:** SmartStack.app `Api/Controllers/` for the canonical structure
155
+
84
156
  ### 4. Explore Context (if needed)
85
157
 
86
158
  **Use subagents sparingly:**
@@ -42,13 +42,14 @@ git commit -m "$(cat <<'EOF'
42
42
 
43
43
  Task {current_task_id}/{tasks_total} - Iteration {current_iteration}
44
44
  Category: {current_task_category}
45
+ {current_module ? "Module: " + current_module : ""}
45
46
 
46
47
  Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
47
48
  EOF
48
49
  )"
49
50
  ```
50
51
 
51
- **Determine commit prefix from category:**
52
+ **Determine commit prefix and scope:**
52
53
 
53
54
  | Category | Prefix | Scope |
54
55
  |----------|--------|-------|
@@ -62,6 +63,8 @@ EOF
62
63
  | `validation` | `chore` | validation |
63
64
  | `other` | `chore` | ralph |
64
65
 
66
+ **Multi-module scope:** When `{current_module}` is set (from modules-queue.json), prefix the scope with the module code: `{PREFIX}({module}/{scope})`. Example: `feat(orders/OrderController): Create REST endpoints`.
67
+
65
68
  **Get commit hash:**
66
69
  ```bash
67
70
  COMMIT_HASH=$(git rev-parse --short HEAD)
@@ -176,11 +179,15 @@ Result: ✅ Met / ❌ Not met
176
179
 
177
180
  ```bash
178
181
  git add .ralph/prd.json .ralph/progress.txt
182
+ # Also stage modules-queue.json if it exists (multi-module tracking)
183
+ [ -f .ralph/modules-queue.json ] && git add .ralph/modules-queue.json
184
+
179
185
  git commit -m "$(cat <<'EOF'
180
186
  chore(ralph): update progress - task {current_task_id}/{tasks_total}
181
187
 
182
188
  Iteration {current_iteration} complete
183
189
  Status: {task.status}
190
+ {current_module ? "Module: " + current_module : ""}
184
191
 
185
192
  Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
186
193
  EOF
@@ -67,6 +67,69 @@ writeJSON('.ralph/prd.json', prd);
67
67
 
68
68
  **If allDone = true:**
69
69
 
70
+ **First, check for multi-module queue:**
71
+
72
+ ```javascript
73
+ const queuePath = '.ralph/modules-queue.json';
74
+ const hasQueue = fileExists(queuePath);
75
+
76
+ if (hasQueue) {
77
+ const queue = readJSON(queuePath);
78
+ const currentModule = queue.modules[queue.currentIndex];
79
+
80
+ // Mark current module as completed
81
+ currentModule.status = 'completed';
82
+ queue.completedModules++;
83
+
84
+ // Check if more modules remain
85
+ const nextIndex = queue.currentIndex + 1;
86
+
87
+ if (nextIndex < queue.totalModules) {
88
+ // ADVANCE TO NEXT MODULE
89
+ queue.currentIndex = nextIndex;
90
+ queue.modules[nextIndex].status = 'in-progress';
91
+ writeJSON(queuePath, queue);
92
+
93
+ // Mark current prd.json as completed
94
+ prd.status = 'completed';
95
+ prd.updated_at = new Date().toISOString();
96
+ writeJSON('.ralph/prd.json', prd);
97
+
98
+ // Reset iteration counter for next module
99
+ // (preserve max_iterations from config)
100
+
101
+ console.log(`
102
+ ╔══════════════════════════════════════════════════════════════════╗
103
+ ║ ✅ MODULE COMPLETE: ${currentModule.code} ║
104
+ ╠══════════════════════════════════════════════════════════════════╣
105
+ ║ Tasks: ${tasksCompleted} completed, ${tasksSkipped} skipped ║
106
+ ║ Modules: ${queue.completedModules} / ${queue.totalModules} ║
107
+ ╠══════════════════════════════════════════════════════════════════╣
108
+ ║ ADVANCING TO NEXT MODULE: ${queue.modules[nextIndex].code} ║
109
+ ╚══════════════════════════════════════════════════════════════════╝
110
+ `);
111
+
112
+ // Loop back to step-01 which will load next module's prd
113
+ // -> Proceed to step-01-task.md
114
+ return;
115
+ }
116
+
117
+ // ALL MODULES COMPLETE - fall through to completion below
118
+ writeJSON(queuePath, queue);
119
+
120
+ console.log(`
121
+ ╔══════════════════════════════════════════════════════════════════╗
122
+ ║ ✅ ALL MODULES COMPLETE ║
123
+ ╠══════════════════════════════════════════════════════════════════╣
124
+ ║ Modules: ${queue.completedModules} / ${queue.totalModules} ║
125
+ ║ ${queue.modules.map(m => m.code + ": " + m.status).join("\n║ ")} ║
126
+ ╚══════════════════════════════════════════════════════════════════╝
127
+ `);
128
+ }
129
+ ```
130
+
131
+ **Then output completion (single module or all modules done):**
132
+
70
133
  ```
71
134
  ╔══════════════════════════════════════════════════════════════════╗
72
135
  ║ ✅ ALL TASKS COMPLETE ║
@@ -160,17 +223,16 @@ Continuing to next iteration...
160
223
  │ Reached │ │ Done │ │ Remaining │
161
224
  └──────┬───────┘ └──────┬───────┘ └──────┬───────┘
162
225
  │ │ │
163
- ┌────────┴────────┐
164
-
165
-
166
- ┌──────────────┐ ┌──────────────┐ ┌─────────────┐ ┌─────────────┐
167
- │ Partial Output │ │ Dead-End │ │ Loop to
168
- │ Report Promise │ │ (all blocked│ │ step-01
169
- │ step-05 │ step-05 │ │ or failed) │ │
170
- │ status: status: │ │ step-05 │ │ │
171
- │ "partial" │ │ "completed" status: │ │ │
172
- └──────────────┘ └──────────────┘ │ "failed" │ └─────────────┘
173
- └─────────────┘
226
+ ┌────────┴────────┐ ┌───────┴────────┐
227
+ │ │ │ │
228
+ ▼ ▼ ▼ ▼
229
+ ┌────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
230
+ │ Partial More modules│ │ No more │ │ Dead-End /
231
+ │ Report in queue? │ │ modules │ │ Loop to
232
+ │ step-05 step-01 │ │ Promise │ │ step-01
233
+ │ status: (next mod) │ │ step-05 │ │ │
234
+ │ "partial" │ └─────────────┘ └─────────────┘ └─────────────┘
235
+ └────────────┘
174
236
  ```
175
237
 
176
238
  ---
@@ -242,5 +304,7 @@ Completion Check:
242
304
 
243
305
  ## NEXT STEP:
244
306
 
245
- - If complete or max iterations or dead-end: `./step-05-report.md`
246
- - If tasks remaining: `./step-01-task.md`
307
+ - If all tasks complete AND more modules in queue: `./step-01-task.md` (next module)
308
+ - If all tasks complete AND no more modules (or no queue): `./step-05-report.md`
309
+ - If max iterations or dead-end: `./step-05-report.md`
310
+ - If tasks remaining in current module: `./step-01-task.md`
@@ -59,6 +59,70 @@ const filesModified = [...new Set(allFilesModified)];
59
59
  const totalDuration = prd.history.reduce((sum, h) => sum + (h.duration_seconds || 0), 0);
60
60
  ```
61
61
 
62
+ ### 1b. Multi-Module Aggregation (if modules-queue.json exists)
63
+
64
+ **Check for multi-module context:**
65
+
66
+ ```javascript
67
+ const queuePath = '.ralph/modules-queue.json';
68
+ const hasQueue = fileExists(queuePath);
69
+
70
+ let moduleStats = null;
71
+
72
+ if (hasQueue) {
73
+ const queue = readJSON(queuePath);
74
+
75
+ moduleStats = {
76
+ totalModules: queue.totalModules,
77
+ completedModules: queue.completedModules,
78
+ modules: []
79
+ };
80
+
81
+ // Aggregate stats from each module's prd file
82
+ for (const mod of queue.modules) {
83
+ const modPrd = readJSON(mod.prdFile);
84
+ const modTasks = modPrd.tasks || [];
85
+
86
+ const modData = {
87
+ code: mod.code,
88
+ status: mod.status,
89
+ tasks: {
90
+ total: modTasks.length,
91
+ completed: modTasks.filter(t => t.status === 'completed').length,
92
+ failed: modTasks.filter(t => t.status === 'failed').length,
93
+ blocked: modTasks.filter(t => t.status === 'blocked').length,
94
+ skipped: modTasks.filter(t => t.status === 'skipped').length
95
+ },
96
+ filesCreated: [],
97
+ filesModified: [],
98
+ commits: []
99
+ };
100
+
101
+ for (const task of modTasks) {
102
+ if (task.files_changed) {
103
+ modData.filesCreated.push(...task.files_changed.created);
104
+ modData.filesModified.push(...task.files_changed.modified);
105
+ }
106
+ if (task.commit_hash) {
107
+ modData.commits.push(task.commit_hash);
108
+ }
109
+ }
110
+
111
+ modData.filesCreated = [...new Set(modData.filesCreated)];
112
+ modData.filesModified = [...new Set(modData.filesModified)];
113
+ modData.commits = [...new Set(modData.commits)];
114
+
115
+ moduleStats.modules.push(modData);
116
+ }
117
+
118
+ // Merge all module files into main aggregation
119
+ for (const mod of moduleStats.modules) {
120
+ allFilesCreated.push(...mod.filesCreated);
121
+ allFilesModified.push(...mod.filesModified);
122
+ }
123
+ }
124
+ ```
125
+
62
126
  ### 2. Collect MCP Usage (from logs if available)
63
127
 
64
128
  **Parse from verbose logs:**
@@ -111,6 +175,18 @@ const validationStats = {
111
175
 
112
176
  {status_emoji mapping: completed=✅, failed=❌, blocked=🚫, skipped=⏭️, pending=⏳}
113
177
 
178
+ {if moduleStats:}
179
+ ## Module Progress
180
+
181
+ | Module | Status | Tasks | Completed | Failed | Files Created | Files Modified | Commits |
182
+ |--------|--------|-------|-----------|--------|---------------|----------------|---------|
183
+ {for each mod in moduleStats.modules:}
184
+ | {mod.code} | {mod.status} | {mod.tasks.total} | {mod.tasks.completed} | {mod.tasks.failed} | {mod.filesCreated.length} | {mod.filesModified.length} | {mod.commits.length} |
185
+ {end for}
186
+
187
+ **Modules: {moduleStats.completedModules}/{moduleStats.totalModules} completed**
188
+ {end if}
189
+
114
190
  ## Failed Tasks
115
191
 
116
192
  {if any failed tasks:}
@@ -216,6 +292,7 @@ writeJSON('.ralph/prd.json', prd);
216
292
  ║ Iterations: {iterations_used} ║
217
293
  ║ Tasks: {completed}/{total} completed ║
218
294
  ║ {failed} failed, {blocked} blocked ║
295
+ ║ {moduleStats ? "Modules: " + moduleStats.completedModules + "/" + moduleStats.totalModules + " completed" : ""} ║
219
296
  ╠══════════════════════════════════════════════════════════════════╣
220
297
  ║ Files Created: {filesCreated.length} ║
221
298
  ║ Files Modified: {filesModified.length} ║
@@ -262,6 +339,8 @@ Next steps:
262
339
  - Duration calculated from history entries
263
340
  - Final summary displayed
264
341
  - prd.json `status` and `updated_at` finalized
342
+ - **Multi-module:** Per-module statistics table included (if modules-queue.json exists)
343
+ - **Multi-module:** Cross-module file and commit aggregation
265
344
 
266
345
  ## COMPLETION:
267
346