@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.
- package/.documentation/agents.html +0 -4
- package/.documentation/business-analyse.html +0 -4
- package/.documentation/cli-commands.html +0 -4
- package/.documentation/commands.html +0 -77
- package/.documentation/css/styles.css +0 -8
- package/.documentation/efcore.html +0 -4
- package/.documentation/gitflow.html +0 -4
- package/.documentation/hooks.html +0 -4
- package/.documentation/index.html +2 -28
- package/.documentation/init.html +8 -14
- package/.documentation/installation.html +0 -11
- package/.documentation/js/app.js +2 -16
- package/.documentation/ralph-loop.html +0 -4
- package/.documentation/test-web.html +0 -4
- package/README.md +0 -1
- package/dist/index.js +3 -7
- package/dist/index.js.map +1 -1
- package/package.json +2 -3
- package/templates/agents/docs-sync-checker.md +2 -2
- package/templates/agents/efcore/squash.md +3 -1
- package/templates/hooks/docs-drift-check.md +4 -5
- package/templates/skills/_resources/context-digest-template.md +2 -2
- package/templates/skills/_resources/doc-context-cache.md +0 -2
- package/templates/skills/_resources/docs-manifest-schema.md +1 -3
- package/templates/skills/_resources/mcp-validate-documentation-spec.md +1 -3
- package/templates/skills/_shared.md +24 -25
- package/templates/skills/application/steps/step-04-backend.md +185 -11
- package/templates/skills/application/steps/step-06-migration.md +41 -2
- package/templates/skills/application/templates-seed.md +151 -0
- package/templates/skills/business-analyse/steps/step-05-handoff.md +59 -17
- package/templates/skills/controller/steps/step-01-analyze.md +1 -1
- package/templates/skills/efcore/steps/squash/step-03-create.md +39 -0
- package/templates/skills/ralph-loop/SKILL.md +35 -3
- package/templates/skills/ralph-loop/steps/step-00-init.md +93 -0
- package/templates/skills/ralph-loop/steps/step-01-task.md +202 -3
- package/templates/skills/ralph-loop/steps/step-02-execute.md +75 -3
- package/templates/skills/ralph-loop/steps/step-03-commit.md +8 -1
- package/templates/skills/ralph-loop/steps/step-04-check.md +77 -13
- package/templates/skills/ralph-loop/steps/step-05-report.md +79 -0
- package/.documentation/apex.html +0 -1027
- package/templates/skills/apex/SKILL.md +0 -297
- package/templates/skills/apex/steps/step-00-init.md +0 -212
- package/templates/skills/apex/steps/step-01-analyze.md +0 -263
- package/templates/skills/apex/steps/step-02-plan.md +0 -255
- package/templates/skills/apex/steps/step-03-execute.md +0 -217
- package/templates/skills/apex/steps/step-04-validate.md +0 -273
- package/templates/skills/apex/steps/step-04b-doc-sync.md +0 -162
- package/templates/skills/apex/steps/step-05-examine.md +0 -214
- package/templates/skills/apex/steps/step-06-resolve.md +0 -181
- package/templates/skills/apex/steps/step-07-tests.md +0 -206
- package/templates/skills/apex/steps/step-08-run-tests.md +0 -207
- package/templates/skills/apex/templates/00-context.md +0 -46
- package/templates/skills/apex/templates/01-analyze.md +0 -63
- package/templates/skills/apex/templates/02-plan.md +0 -63
- package/templates/skills/apex/templates/03-execute.md +0 -34
- package/templates/skills/apex/templates/04-validate.md +0 -61
- package/templates/skills/apex/templates/04b-doc-sync.md +0 -31
- package/templates/skills/apex/templates/05-examine.md +0 -58
- package/templates/skills/apex/templates/06-resolve.md +0 -39
- package/templates/skills/apex/templates/07-tests.md +0 -56
- package/templates/skills/apex/templates/08-run-tests.md +0 -41
- package/templates/skills/apex/templates/README.md +0 -69
- 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
|
-
#
|
|
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
|
|
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
|
|
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
|
|
168
|
-
│ Report
|
|
169
|
-
│ step-05
|
|
170
|
-
│ status:
|
|
171
|
-
│ "partial"
|
|
172
|
-
|
|
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
|
|
246
|
-
- If tasks
|
|
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
|
|