@kaddo/cli 3.62.0 → 3.64.0
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/dist/index.js +753 -292
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -114,9 +114,15 @@ guard:
|
|
|
114
114
|
`;
|
|
115
115
|
if (meta.role === "core") {
|
|
116
116
|
yml += `
|
|
117
|
+
system:
|
|
118
|
+
name: ${meta.systemName ?? meta.name}
|
|
119
|
+
`;
|
|
120
|
+
yml += `
|
|
117
121
|
multirepo:
|
|
118
122
|
role: core
|
|
119
|
-
modules_file:
|
|
123
|
+
modules_file: .kaddo/modules.yml
|
|
124
|
+
workspace_roots:
|
|
125
|
+
- ..
|
|
120
126
|
`;
|
|
121
127
|
}
|
|
122
128
|
if (meta.role === "module") {
|
|
@@ -212,21 +218,27 @@ _How the system is deployed: regions, environments, infrastructure._
|
|
|
212
218
|
_Auth, observability, error handling, compliance, and other shared concerns._
|
|
213
219
|
`;
|
|
214
220
|
}
|
|
215
|
-
function buildModulesFile(
|
|
221
|
+
function buildModulesFile(systemName3) {
|
|
216
222
|
return `---
|
|
217
223
|
type: modules-map
|
|
218
|
-
system: ${
|
|
224
|
+
system: ${systemName3}
|
|
219
225
|
generated_by: kaddo-init
|
|
220
|
-
template_version:
|
|
226
|
+
template_version: 2
|
|
221
227
|
---
|
|
222
228
|
|
|
223
|
-
# ${
|
|
229
|
+
# ${systemName3} \u2014 Modules
|
|
224
230
|
|
|
225
|
-
|
|
231
|
+
_No modules mapped yet. Run \`kaddo modules discover\` to find sibling modules, or \`kaddo modules map <path>\` to register one manually._
|
|
232
|
+
`;
|
|
233
|
+
}
|
|
234
|
+
function buildModulesYml(systemName3) {
|
|
235
|
+
return `version: 1
|
|
236
|
+
system: ${systemName3}
|
|
226
237
|
|
|
227
|
-
|
|
238
|
+
workspace_roots:
|
|
239
|
+
- ..
|
|
228
240
|
|
|
229
|
-
|
|
241
|
+
modules: []
|
|
230
242
|
`;
|
|
231
243
|
}
|
|
232
244
|
function buildKnowledge(projectName) {
|
|
@@ -302,25 +314,6 @@ async function runInit() {
|
|
|
302
314
|
initialValue: inferredName,
|
|
303
315
|
validate: (v) => v.trim().length === 0 ? "Project name is required." : void 0
|
|
304
316
|
});
|
|
305
|
-
const state = await select2({
|
|
306
|
-
message: "What kind of project is this?",
|
|
307
|
-
options: [
|
|
308
|
-
{ value: "new", label: "New", hint: "Starting from scratch" },
|
|
309
|
-
{ value: "pre-ai", label: "Pre-AI", hint: "Existing code, not prepared for agents" },
|
|
310
|
-
{ value: "legacy", label: "Legacy", hint: "Knowledge lives in people's heads" }
|
|
311
|
-
],
|
|
312
|
-
initialValue: "pre-ai"
|
|
313
|
-
});
|
|
314
|
-
const teamSize = await select2({
|
|
315
|
-
message: "Team size",
|
|
316
|
-
options: [
|
|
317
|
-
{ value: "indie", label: "Indie", hint: "1 person" },
|
|
318
|
-
{ value: "small", label: "Small", hint: "2-5 people" },
|
|
319
|
-
{ value: "medium", label: "Medium", hint: "6-20 people" },
|
|
320
|
-
{ value: "enterprise", label: "Enterprise", hint: "20+ people" }
|
|
321
|
-
],
|
|
322
|
-
initialValue: "small"
|
|
323
|
-
});
|
|
324
317
|
const structure = await select2({
|
|
325
318
|
message: "Repository structure",
|
|
326
319
|
options: [
|
|
@@ -329,38 +322,65 @@ async function runInit() {
|
|
|
329
322
|
],
|
|
330
323
|
initialValue: "monorepo"
|
|
331
324
|
});
|
|
332
|
-
const language = await select2({
|
|
333
|
-
message: "Project knowledge language (the CLI stays in English)",
|
|
334
|
-
options: [
|
|
335
|
-
{ value: "en", label: "English" },
|
|
336
|
-
{ value: "es", label: "Spanish" }
|
|
337
|
-
],
|
|
338
|
-
initialValue: "en"
|
|
339
|
-
});
|
|
340
325
|
let role;
|
|
326
|
+
let systemName3;
|
|
341
327
|
let parentSystem;
|
|
342
328
|
let modId;
|
|
343
329
|
if (structure === "multirepo") {
|
|
344
330
|
role = await select2({
|
|
345
|
-
message: "
|
|
331
|
+
message: "What is the role of this repository?",
|
|
346
332
|
options: [
|
|
347
333
|
{ value: "core", label: "Core / system orchestrator" },
|
|
348
334
|
{ value: "module", label: "Module of an existing system" }
|
|
349
335
|
]
|
|
350
336
|
});
|
|
337
|
+
if (role === "core") {
|
|
338
|
+
systemName3 = await text2({
|
|
339
|
+
message: "System name",
|
|
340
|
+
placeholder: "e.g. tryckers",
|
|
341
|
+
validate: (v) => v.trim().length === 0 ? "System name is required." : void 0
|
|
342
|
+
});
|
|
343
|
+
}
|
|
351
344
|
if (role === "module") {
|
|
352
345
|
parentSystem = await text2({
|
|
353
346
|
message: "Parent system name",
|
|
354
|
-
placeholder: "e.g.
|
|
347
|
+
placeholder: "e.g. tryckers",
|
|
355
348
|
validate: (v) => v.trim().length === 0 ? "Parent system name is required." : void 0
|
|
356
349
|
});
|
|
357
350
|
modId = await text2({
|
|
358
351
|
message: "Module ID",
|
|
359
|
-
placeholder: "e.g.
|
|
352
|
+
placeholder: "e.g. frontend",
|
|
360
353
|
validate: (v) => v.trim().length === 0 ? "Module ID is required." : void 0
|
|
361
354
|
});
|
|
362
355
|
}
|
|
363
356
|
}
|
|
357
|
+
const state = await select2({
|
|
358
|
+
message: "What kind of project is this?",
|
|
359
|
+
options: [
|
|
360
|
+
{ value: "new", label: "New", hint: "Starting from scratch" },
|
|
361
|
+
{ value: "pre-ai", label: "Pre-AI", hint: "Existing code, not prepared for agents" },
|
|
362
|
+
{ value: "legacy", label: "Legacy", hint: "Knowledge lives in people's heads" }
|
|
363
|
+
],
|
|
364
|
+
initialValue: "pre-ai"
|
|
365
|
+
});
|
|
366
|
+
const language = await select2({
|
|
367
|
+
message: "Project knowledge language (the CLI stays in English)",
|
|
368
|
+
options: [
|
|
369
|
+
{ value: "en", label: "English" },
|
|
370
|
+
{ value: "es", label: "Spanish" }
|
|
371
|
+
],
|
|
372
|
+
initialValue: "en"
|
|
373
|
+
});
|
|
374
|
+
const teamSize = await select2({
|
|
375
|
+
message: "Team size",
|
|
376
|
+
options: [
|
|
377
|
+
{ value: "indie", label: "Indie", hint: "1 person" },
|
|
378
|
+
{ value: "small", label: "Small", hint: "2-5 people" },
|
|
379
|
+
{ value: "medium", label: "Medium", hint: "6-20 people" },
|
|
380
|
+
{ value: "enterprise", label: "Enterprise", hint: "20+ people" }
|
|
381
|
+
],
|
|
382
|
+
initialValue: "small"
|
|
383
|
+
});
|
|
364
384
|
const meta = {
|
|
365
385
|
name: projectName.trim(),
|
|
366
386
|
state,
|
|
@@ -368,6 +388,7 @@ async function runInit() {
|
|
|
368
388
|
structure,
|
|
369
389
|
language,
|
|
370
390
|
role,
|
|
391
|
+
systemName: systemName3?.trim(),
|
|
371
392
|
parentSystem: parentSystem?.trim(),
|
|
372
393
|
moduleId: modId?.trim()
|
|
373
394
|
};
|
|
@@ -375,36 +396,39 @@ async function runInit() {
|
|
|
375
396
|
writeFile(kaddoConfigPath, buildConfig(meta));
|
|
376
397
|
log2.success("Created .kaddo/config.yml");
|
|
377
398
|
if (role === "module") {
|
|
378
|
-
ensureDir(join(dir, ARCH_DIR, "module"));
|
|
399
|
+
ensureDir(join(dir, ARCH_DIR, "tech", "module"));
|
|
379
400
|
writeFile(
|
|
380
|
-
join(dir, ARCH_DIR, "module", "module-context.md"),
|
|
401
|
+
join(dir, ARCH_DIR, "tech", "module", "module-context.md"),
|
|
381
402
|
buildModuleContext(modId.trim(), parentSystem.trim())
|
|
382
403
|
);
|
|
383
|
-
ensureDir(join(dir, ARCH_DIR, "tech"));
|
|
384
404
|
if (!exists(join(dir, ARCH_DIR, "tech", "current-state.md"))) {
|
|
385
405
|
writeFile(join(dir, ARCH_DIR, "tech", "current-state.md"), buildKnowledge(projectName.trim()));
|
|
386
406
|
}
|
|
387
407
|
if (!exists(join(dir, ARCH_DIR, "tech", "codebase.md"))) {
|
|
388
408
|
writeFile(join(dir, ARCH_DIR, "tech", "codebase.md"), buildKnowledge(projectName.trim()));
|
|
389
409
|
}
|
|
390
|
-
|
|
391
|
-
log2.success("Created knowledge/module/module-context.md");
|
|
410
|
+
log2.success("Created knowledge/tech/module/module-context.md");
|
|
392
411
|
log2.success("Created knowledge/tech/current-state.md");
|
|
393
412
|
log2.success("Created knowledge/tech/codebase.md");
|
|
394
413
|
log2.info("Next: run `kaddo scan` to detect your stack, then use module-context-agent to refine module-context.md.");
|
|
395
414
|
} else if (role === "core") {
|
|
415
|
+
const sysName = systemName3?.trim() ?? projectName.trim();
|
|
396
416
|
ensureDir(join(dir, ARCH_DIR, "delivery", "work-items"));
|
|
397
|
-
ensureDir(join(dir, ARCH_DIR, "system"));
|
|
398
|
-
ensureDir(join(dir, ARCH_DIR, "modules"));
|
|
417
|
+
ensureDir(join(dir, ARCH_DIR, "tech", "system"));
|
|
418
|
+
ensureDir(join(dir, ARCH_DIR, "tech", "modules"));
|
|
419
|
+
ensureDir(join(dir, ARCH_DIR, "agents"));
|
|
420
|
+
ensureDir(join(dir, ARCH_DIR, "skills"));
|
|
399
421
|
writeFile(join(dir, ARCH_DIR, "knowledge.md"), buildKnowledge(projectName.trim()));
|
|
400
422
|
writeFile(join(dir, ARCH_DIR, "delivery", "roadmap.md"), buildRoadmap(projectName.trim()));
|
|
401
|
-
writeFile(join(dir, ARCH_DIR, "system", "system-context.md"), buildSystemContext(
|
|
402
|
-
writeFile(join(dir, ARCH_DIR, "modules", "modules.md"), buildModulesFile(
|
|
423
|
+
writeFile(join(dir, ARCH_DIR, "tech", "system", "system-context.md"), buildSystemContext(sysName));
|
|
424
|
+
writeFile(join(dir, ARCH_DIR, "tech", "modules", "modules.md"), buildModulesFile(sysName));
|
|
425
|
+
writeFile(join(dir, KADDO_DIR, "modules.yml"), buildModulesYml(sysName));
|
|
403
426
|
log2.success("Created knowledge/knowledge.md");
|
|
404
427
|
log2.success("Created knowledge/delivery/roadmap.md");
|
|
405
|
-
log2.success("Created knowledge/system/system-context.md");
|
|
406
|
-
log2.success("Created knowledge/modules/modules.md");
|
|
407
|
-
log2.
|
|
428
|
+
log2.success("Created knowledge/tech/system/system-context.md");
|
|
429
|
+
log2.success("Created knowledge/tech/modules/modules.md");
|
|
430
|
+
log2.success("Created .kaddo/modules.yml");
|
|
431
|
+
log2.info("Next: run `kaddo scan` then `kaddo modules discover` to find sibling modules.");
|
|
408
432
|
} else {
|
|
409
433
|
ensureDir(join(dir, ARCH_DIR, "delivery", "work-items"));
|
|
410
434
|
writeFile(join(dir, ARCH_DIR, "knowledge.md"), buildKnowledge(projectName.trim()));
|
|
@@ -1305,10 +1329,14 @@ var configSchema = z.object({
|
|
|
1305
1329
|
team: z.object({
|
|
1306
1330
|
size: teamSizeSchema.default(DEFAULTS.teamSize)
|
|
1307
1331
|
}).passthrough().default({}),
|
|
1332
|
+
system: z.object({
|
|
1333
|
+
name: z.string().optional()
|
|
1334
|
+
}).passthrough().optional(),
|
|
1308
1335
|
multirepo: z.object({
|
|
1309
1336
|
role: multirepoRoleSchema,
|
|
1310
1337
|
modules_file: z.string().optional(),
|
|
1311
|
-
parent_system: z.string().optional()
|
|
1338
|
+
parent_system: z.string().optional(),
|
|
1339
|
+
workspace_roots: z.array(z.string()).optional()
|
|
1312
1340
|
}).passthrough().optional(),
|
|
1313
1341
|
module: z.object({
|
|
1314
1342
|
id: z.string().optional()
|
|
@@ -1333,8 +1361,8 @@ function loadConfig(dir) {
|
|
|
1333
1361
|
const parsed = configSchema.safeParse(raw ?? {});
|
|
1334
1362
|
if (!parsed.success) {
|
|
1335
1363
|
const issues = parsed.error.issues.map((i) => {
|
|
1336
|
-
const
|
|
1337
|
-
return
|
|
1364
|
+
const path8 = i.path.join(".");
|
|
1365
|
+
return path8 ? ` - ${path8}: ${i.message}` : ` - ${i.message}`;
|
|
1338
1366
|
}).join("\n");
|
|
1339
1367
|
throw new ConfigError(`Invalid .kaddo/config.yml:
|
|
1340
1368
|
${issues}`);
|
|
@@ -1391,6 +1419,15 @@ function projectRole(config) {
|
|
|
1391
1419
|
function isModule(config) {
|
|
1392
1420
|
return projectRole(config) === "module";
|
|
1393
1421
|
}
|
|
1422
|
+
function isCore(config) {
|
|
1423
|
+
return projectRole(config) === "core";
|
|
1424
|
+
}
|
|
1425
|
+
function systemName(config) {
|
|
1426
|
+
return config.system?.name;
|
|
1427
|
+
}
|
|
1428
|
+
function workspaceRoots(config) {
|
|
1429
|
+
return config.multirepo?.workspace_roots ?? [".."];
|
|
1430
|
+
}
|
|
1394
1431
|
|
|
1395
1432
|
// src/core/command-help.ts
|
|
1396
1433
|
var COMMAND_HELP = {
|
|
@@ -5030,7 +5067,8 @@ var RECOMMENDED_BY_STATE = {
|
|
|
5030
5067
|
"backlog-agent.md",
|
|
5031
5068
|
"work-item-agent.md",
|
|
5032
5069
|
"implementation-agent.md",
|
|
5033
|
-
"adr-agent.md"
|
|
5070
|
+
"adr-agent.md",
|
|
5071
|
+
"module-context-agent.md"
|
|
5034
5072
|
],
|
|
5035
5073
|
"pre-ai": [
|
|
5036
5074
|
"business-agent.md",
|
|
@@ -5040,7 +5078,8 @@ var RECOMMENDED_BY_STATE = {
|
|
|
5040
5078
|
"backlog-agent.md",
|
|
5041
5079
|
"work-item-agent.md",
|
|
5042
5080
|
"implementation-agent.md",
|
|
5043
|
-
"ownership-agent.md"
|
|
5081
|
+
"ownership-agent.md",
|
|
5082
|
+
"module-context-agent.md"
|
|
5044
5083
|
],
|
|
5045
5084
|
legacy: [
|
|
5046
5085
|
"legacy-agent.md",
|
|
@@ -6550,10 +6589,10 @@ function analyzeGuard(touchedFiles, artifacts, silentWithoutOwnership) {
|
|
|
6550
6589
|
import { parse as parseYaml4, stringify as stringifyYaml2 } from "yaml";
|
|
6551
6590
|
var IGNORE_FILE = ".kaddo/ignores.yml";
|
|
6552
6591
|
function loadIgnores(dir) {
|
|
6553
|
-
const
|
|
6554
|
-
if (!exists(
|
|
6592
|
+
const path8 = join(dir, IGNORE_FILE);
|
|
6593
|
+
if (!exists(path8)) return [];
|
|
6555
6594
|
try {
|
|
6556
|
-
const raw = readFile(
|
|
6595
|
+
const raw = readFile(path8);
|
|
6557
6596
|
const parsed = parseYaml4(raw);
|
|
6558
6597
|
return Array.isArray(parsed) ? parsed : [];
|
|
6559
6598
|
} catch {
|
|
@@ -6561,7 +6600,7 @@ function loadIgnores(dir) {
|
|
|
6561
6600
|
}
|
|
6562
6601
|
}
|
|
6563
6602
|
function saveIgnore(dir, entry) {
|
|
6564
|
-
const
|
|
6603
|
+
const path8 = join(dir, IGNORE_FILE);
|
|
6565
6604
|
const existing = loadIgnores(dir);
|
|
6566
6605
|
const idx = existing.findIndex((e) => e.artifact_id === entry.artifact_id);
|
|
6567
6606
|
if (idx >= 0) {
|
|
@@ -6569,17 +6608,17 @@ function saveIgnore(dir, entry) {
|
|
|
6569
6608
|
} else {
|
|
6570
6609
|
existing.push(entry);
|
|
6571
6610
|
}
|
|
6572
|
-
writeFile(
|
|
6611
|
+
writeFile(path8, stringifyYaml2(existing));
|
|
6573
6612
|
}
|
|
6574
6613
|
function isIgnored(ignores, artifactId) {
|
|
6575
6614
|
return ignores.find((e) => e.artifact_id === artifactId);
|
|
6576
6615
|
}
|
|
6577
6616
|
function removeIgnore(dir, artifactId) {
|
|
6578
|
-
const
|
|
6617
|
+
const path8 = join(dir, IGNORE_FILE);
|
|
6579
6618
|
const existing = loadIgnores(dir);
|
|
6580
6619
|
const filtered = existing.filter((e) => e.artifact_id !== artifactId);
|
|
6581
6620
|
if (filtered.length === existing.length) return false;
|
|
6582
|
-
writeFile(
|
|
6621
|
+
writeFile(path8, stringifyYaml2(filtered));
|
|
6583
6622
|
return true;
|
|
6584
6623
|
}
|
|
6585
6624
|
|
|
@@ -6601,11 +6640,11 @@ function moduleArtifactCoverage(dir, id) {
|
|
|
6601
6640
|
};
|
|
6602
6641
|
}
|
|
6603
6642
|
function loadMappedModules(dir) {
|
|
6604
|
-
const
|
|
6605
|
-
if (!exists(
|
|
6643
|
+
const path8 = join(dir, DESCRIPTOR_PATH);
|
|
6644
|
+
if (!exists(path8)) return [];
|
|
6606
6645
|
let parsed;
|
|
6607
6646
|
try {
|
|
6608
|
-
parsed = parseYaml5(readFile(
|
|
6647
|
+
parsed = parseYaml5(readFile(path8));
|
|
6609
6648
|
} catch {
|
|
6610
6649
|
return [];
|
|
6611
6650
|
}
|
|
@@ -6619,7 +6658,7 @@ function loadMappedModules(dir) {
|
|
|
6619
6658
|
modules.push({
|
|
6620
6659
|
id,
|
|
6621
6660
|
name: typeof m.name === "string" ? m.name : void 0,
|
|
6622
|
-
repoPath: typeof m.repoPath === "string" ? m.repoPath : "",
|
|
6661
|
+
repoPath: typeof m.repoPath === "string" ? m.repoPath : typeof m.path === "string" ? m.path : "",
|
|
6623
6662
|
type: typeof m.type === "string" ? m.type : void 0,
|
|
6624
6663
|
mainTechnology: typeof m.mainTechnology === "string" ? m.mainTechnology : void 0,
|
|
6625
6664
|
owner: typeof m.owner === "string" ? m.owner : void 0,
|
|
@@ -6668,13 +6707,15 @@ function detectModuleStatus(coreDir, repoPath) {
|
|
|
6668
6707
|
status: "configured",
|
|
6669
6708
|
configured: true,
|
|
6670
6709
|
role: "module",
|
|
6671
|
-
moduleContext: exists(join(moduleDir2, "knowledge", "module", "module-context.md")),
|
|
6710
|
+
moduleContext: exists(join(moduleDir2, "knowledge", "tech", "module", "module-context.md")) || exists(join(moduleDir2, "knowledge", "module", "module-context.md")),
|
|
6672
6711
|
currentState: exists(join(moduleDir2, "knowledge", "tech", "current-state.md")),
|
|
6673
6712
|
codebase: exists(join(moduleDir2, "knowledge", "tech", "codebase.md"))
|
|
6674
6713
|
};
|
|
6675
6714
|
}
|
|
6676
6715
|
function readModuleContext(coreDir, repoPath) {
|
|
6677
|
-
const
|
|
6716
|
+
const newPath = join(coreDir, repoPath, "knowledge", "tech", "module", "module-context.md");
|
|
6717
|
+
const legacyPath = join(coreDir, repoPath, "knowledge", "module", "module-context.md");
|
|
6718
|
+
const p2 = exists(newPath) ? newPath : legacyPath;
|
|
6678
6719
|
if (!exists(p2)) return null;
|
|
6679
6720
|
try {
|
|
6680
6721
|
return readFile(p2);
|
|
@@ -7085,7 +7126,7 @@ function buildGuardHistory(dir) {
|
|
|
7085
7126
|
const d = hotspotDir(t.code_path);
|
|
7086
7127
|
hotspotMap.set(d, (hotspotMap.get(d) ?? 0) + 1);
|
|
7087
7128
|
}
|
|
7088
|
-
const hotspots = [...hotspotMap.entries()].map(([
|
|
7129
|
+
const hotspots = [...hotspotMap.entries()].map(([path8, warnings]) => ({ path: path8, warnings })).sort((a, b) => b.warnings - a.warnings);
|
|
7089
7130
|
return {
|
|
7090
7131
|
available: true,
|
|
7091
7132
|
total_runs: sorted.length,
|
|
@@ -7487,7 +7528,7 @@ function runIgnoreRemove(artifactId) {
|
|
|
7487
7528
|
|
|
7488
7529
|
// src/commands/explain.ts
|
|
7489
7530
|
import matter6 from "gray-matter";
|
|
7490
|
-
import { parse as
|
|
7531
|
+
import { parse as parseYaml12 } from "yaml";
|
|
7491
7532
|
|
|
7492
7533
|
// src/core/delivery-phase.ts
|
|
7493
7534
|
function layer(layers, name) {
|
|
@@ -7548,6 +7589,34 @@ function firstUnrefinedLayerAgent(layers) {
|
|
|
7548
7589
|
return { agent: "architecture-agent", step: "Use architecture-agent to complete knowledge/tech/current-state.md" };
|
|
7549
7590
|
}
|
|
7550
7591
|
function assessPhase(input) {
|
|
7592
|
+
if (input.isModuleRepo) {
|
|
7593
|
+
const techStatus = layer(input.layers, "Tech");
|
|
7594
|
+
const techReady = !NOT_READY_LAYER.includes(techStatus);
|
|
7595
|
+
if (techReady) {
|
|
7596
|
+
return {
|
|
7597
|
+
phase: "Active Delivery",
|
|
7598
|
+
reasons: ["Module repo \u2014 Tech layer ready", "Business/Product managed by core"],
|
|
7599
|
+
recommendedAgents: [],
|
|
7600
|
+
nextStep: "Module knowledge is ready for core orchestration.",
|
|
7601
|
+
llmInstructions: [
|
|
7602
|
+
"This is a module repo. Business, Product, and Delivery are managed by the core repo.",
|
|
7603
|
+
"Do not create business, product, roadmap or Work Items here."
|
|
7604
|
+
]
|
|
7605
|
+
};
|
|
7606
|
+
}
|
|
7607
|
+
return {
|
|
7608
|
+
phase: "Knowledge Refinement",
|
|
7609
|
+
reasons: ["Module repo \u2014 Tech layer incomplete", "Business/Product managed by core"],
|
|
7610
|
+
recommendedAgents: ["module-context-agent"],
|
|
7611
|
+
nextStep: "Use module-context-agent to refine knowledge/tech/module/module-context.md",
|
|
7612
|
+
llmInstructions: [
|
|
7613
|
+
"Use the module-context-agent to refine the module context.",
|
|
7614
|
+
"Do not create business, product, roadmap or Work Items in this module.",
|
|
7615
|
+
"Do not install agents or skills.",
|
|
7616
|
+
"Do not write application code."
|
|
7617
|
+
]
|
|
7618
|
+
};
|
|
7619
|
+
}
|
|
7551
7620
|
const phase = determinePhase(input);
|
|
7552
7621
|
const reasons = buildReasons(input);
|
|
7553
7622
|
const bs = input.workItems.byState;
|
|
@@ -7653,10 +7722,10 @@ function assessPhase(input) {
|
|
|
7653
7722
|
|
|
7654
7723
|
// src/core/capsule.ts
|
|
7655
7724
|
import matter3 from "gray-matter";
|
|
7656
|
-
import { parse as
|
|
7725
|
+
import { parse as parseYaml10, stringify as stringifyYaml5 } from "yaml";
|
|
7657
7726
|
|
|
7658
7727
|
// src/commands/modules-map.ts
|
|
7659
|
-
import { parse as
|
|
7728
|
+
import { parse as parseYaml9, stringify as stringifyYaml4 } from "yaml";
|
|
7660
7729
|
|
|
7661
7730
|
// src/templates/registry.ts
|
|
7662
7731
|
var QUALITY = "## Quality checklist";
|
|
@@ -9143,9 +9212,373 @@ function getTemplate(id) {
|
|
|
9143
9212
|
return KADDO_TEMPLATES.find((t) => t.id === id);
|
|
9144
9213
|
}
|
|
9145
9214
|
|
|
9215
|
+
// src/commands/modules-discover.ts
|
|
9216
|
+
import path5 from "path";
|
|
9217
|
+
import { parse as parseYaml8, stringify as stringifyYaml3 } from "yaml";
|
|
9218
|
+
var CONFIG_PATH3 = ".kaddo/config.yml";
|
|
9219
|
+
var MODULES_YML = ".kaddo/modules.yml";
|
|
9220
|
+
var MODULES_MD = "knowledge/tech/modules/modules.md";
|
|
9221
|
+
var IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
9222
|
+
".git",
|
|
9223
|
+
"node_modules",
|
|
9224
|
+
"dist",
|
|
9225
|
+
"build",
|
|
9226
|
+
"coverage",
|
|
9227
|
+
"vendor",
|
|
9228
|
+
".next",
|
|
9229
|
+
"out",
|
|
9230
|
+
"tmp",
|
|
9231
|
+
"temp",
|
|
9232
|
+
".kaddo"
|
|
9233
|
+
]);
|
|
9234
|
+
function readModulesYml(dir) {
|
|
9235
|
+
const p2 = join(dir, MODULES_YML);
|
|
9236
|
+
if (!exists(p2)) return { version: 1, system: "", workspace_roots: [".."], modules: [] };
|
|
9237
|
+
try {
|
|
9238
|
+
const parsed = parseYaml8(readFile(p2));
|
|
9239
|
+
return {
|
|
9240
|
+
version: parsed.version ?? 1,
|
|
9241
|
+
system: parsed.system ?? "",
|
|
9242
|
+
workspace_roots: parsed.workspace_roots ?? [".."],
|
|
9243
|
+
modules: Array.isArray(parsed.modules) ? parsed.modules : []
|
|
9244
|
+
};
|
|
9245
|
+
} catch {
|
|
9246
|
+
return { version: 1, system: "", workspace_roots: [".."], modules: [] };
|
|
9247
|
+
}
|
|
9248
|
+
}
|
|
9249
|
+
function writeModulesYml(dir, yml) {
|
|
9250
|
+
writeFile(join(dir, MODULES_YML), stringifyYaml3(yml));
|
|
9251
|
+
}
|
|
9252
|
+
function readRepoConfig(repoDir) {
|
|
9253
|
+
const configPath = join(repoDir, CONFIG_PATH3);
|
|
9254
|
+
if (!exists(configPath)) return null;
|
|
9255
|
+
try {
|
|
9256
|
+
return parseYaml8(readFile(configPath));
|
|
9257
|
+
} catch {
|
|
9258
|
+
return null;
|
|
9259
|
+
}
|
|
9260
|
+
}
|
|
9261
|
+
function hasKnowledgeFile(repoDir, ...segments) {
|
|
9262
|
+
return exists(join(repoDir, "knowledge", ...segments));
|
|
9263
|
+
}
|
|
9264
|
+
function hasModuleContext(repoDir) {
|
|
9265
|
+
return hasKnowledgeFile(repoDir, "tech", "module", "module-context.md") || hasKnowledgeFile(repoDir, "module", "module-context.md");
|
|
9266
|
+
}
|
|
9267
|
+
function hasCurrentState(repoDir) {
|
|
9268
|
+
return hasKnowledgeFile(repoDir, "tech", "current-state.md");
|
|
9269
|
+
}
|
|
9270
|
+
function hasCodebase(repoDir) {
|
|
9271
|
+
return hasKnowledgeFile(repoDir, "tech", "codebase.md");
|
|
9272
|
+
}
|
|
9273
|
+
function buildModuleContextPaths(repoDir) {
|
|
9274
|
+
const ctx = {};
|
|
9275
|
+
if (hasKnowledgeFile(repoDir, "tech", "module", "module-context.md")) {
|
|
9276
|
+
ctx.module = "knowledge/tech/module/module-context.md";
|
|
9277
|
+
} else if (hasKnowledgeFile(repoDir, "module", "module-context.md")) {
|
|
9278
|
+
ctx.module = "knowledge/module/module-context.md";
|
|
9279
|
+
}
|
|
9280
|
+
if (hasCurrentState(repoDir)) ctx.current_state = "knowledge/tech/current-state.md";
|
|
9281
|
+
if (hasCodebase(repoDir)) ctx.codebase = "knowledge/tech/codebase.md";
|
|
9282
|
+
return ctx;
|
|
9283
|
+
}
|
|
9284
|
+
function discoverModules(dir) {
|
|
9285
|
+
const config = loadConfig(dir);
|
|
9286
|
+
if (!config || !isCore(config)) return [];
|
|
9287
|
+
const sysName = systemName(config) ?? config.project.name;
|
|
9288
|
+
const roots = workspaceRoots(config);
|
|
9289
|
+
const existing = readModulesYml(dir);
|
|
9290
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
9291
|
+
const seenPaths = /* @__PURE__ */ new Set();
|
|
9292
|
+
const results = [];
|
|
9293
|
+
const coreAbsPath = path5.resolve(dir);
|
|
9294
|
+
for (const root of roots) {
|
|
9295
|
+
const rootAbs = path5.resolve(dir, root);
|
|
9296
|
+
if (!exists(rootAbs) || !isDir(rootAbs)) continue;
|
|
9297
|
+
let entries;
|
|
9298
|
+
try {
|
|
9299
|
+
entries = readDir(rootAbs);
|
|
9300
|
+
} catch {
|
|
9301
|
+
continue;
|
|
9302
|
+
}
|
|
9303
|
+
for (const entry of entries) {
|
|
9304
|
+
if (IGNORED_DIRS.has(entry)) continue;
|
|
9305
|
+
const candidateAbs = path5.resolve(rootAbs, entry);
|
|
9306
|
+
if (!isDir(candidateAbs)) continue;
|
|
9307
|
+
if (candidateAbs === coreAbsPath) continue;
|
|
9308
|
+
const relPath = path5.relative(dir, candidateAbs).replace(/\\/g, "/");
|
|
9309
|
+
const repoConfig = readRepoConfig(candidateAbs);
|
|
9310
|
+
if (!repoConfig) {
|
|
9311
|
+
results.push({
|
|
9312
|
+
id: entry,
|
|
9313
|
+
name: entry,
|
|
9314
|
+
path: relPath.startsWith(".") ? relPath : `./${relPath}`,
|
|
9315
|
+
parentSystem: "",
|
|
9316
|
+
status: "not_configured",
|
|
9317
|
+
eligibleForMapping: false,
|
|
9318
|
+
moduleContext: false,
|
|
9319
|
+
currentState: false,
|
|
9320
|
+
codebase: false
|
|
9321
|
+
});
|
|
9322
|
+
continue;
|
|
9323
|
+
}
|
|
9324
|
+
const project = repoConfig.project;
|
|
9325
|
+
const multirepo = repoConfig.multirepo;
|
|
9326
|
+
const module = repoConfig.module;
|
|
9327
|
+
const role = project?.role ?? multirepo?.role;
|
|
9328
|
+
if (role !== "module") {
|
|
9329
|
+
results.push({
|
|
9330
|
+
id: entry,
|
|
9331
|
+
name: project?.name ?? entry,
|
|
9332
|
+
path: relPath.startsWith(".") ? relPath : `./${relPath}`,
|
|
9333
|
+
parentSystem: "",
|
|
9334
|
+
status: "invalid",
|
|
9335
|
+
eligibleForMapping: false,
|
|
9336
|
+
warning: "Repository has Kaddo but is not configured as a module.",
|
|
9337
|
+
moduleContext: false,
|
|
9338
|
+
currentState: false,
|
|
9339
|
+
codebase: false
|
|
9340
|
+
});
|
|
9341
|
+
continue;
|
|
9342
|
+
}
|
|
9343
|
+
const modId = module?.id ?? entry;
|
|
9344
|
+
const modParent = multirepo?.parent_system ?? "";
|
|
9345
|
+
const modName = project?.name ?? entry;
|
|
9346
|
+
const modPath = relPath.startsWith(".") ? relPath : `./${relPath}`;
|
|
9347
|
+
if (modParent && modParent !== sysName) {
|
|
9348
|
+
results.push({
|
|
9349
|
+
id: modId,
|
|
9350
|
+
name: modName,
|
|
9351
|
+
path: modPath,
|
|
9352
|
+
parentSystem: modParent,
|
|
9353
|
+
status: "foreign_system",
|
|
9354
|
+
eligibleForMapping: false,
|
|
9355
|
+
warning: `parent system: ${modParent}`,
|
|
9356
|
+
moduleContext: hasModuleContext(candidateAbs),
|
|
9357
|
+
currentState: hasCurrentState(candidateAbs),
|
|
9358
|
+
codebase: hasCodebase(candidateAbs)
|
|
9359
|
+
});
|
|
9360
|
+
continue;
|
|
9361
|
+
}
|
|
9362
|
+
let status = "configured";
|
|
9363
|
+
let warning;
|
|
9364
|
+
let eligible = true;
|
|
9365
|
+
if (seenIds.has(modId)) {
|
|
9366
|
+
status = "duplicate";
|
|
9367
|
+
warning = `duplicate module id: ${modId}`;
|
|
9368
|
+
eligible = false;
|
|
9369
|
+
} else if (seenPaths.has(modPath)) {
|
|
9370
|
+
status = "duplicate";
|
|
9371
|
+
warning = `duplicate path: ${modPath}`;
|
|
9372
|
+
eligible = false;
|
|
9373
|
+
}
|
|
9374
|
+
seenIds.add(modId);
|
|
9375
|
+
seenPaths.add(modPath);
|
|
9376
|
+
results.push({
|
|
9377
|
+
id: modId,
|
|
9378
|
+
name: modName,
|
|
9379
|
+
path: modPath,
|
|
9380
|
+
parentSystem: modParent || sysName,
|
|
9381
|
+
status,
|
|
9382
|
+
eligibleForMapping: eligible,
|
|
9383
|
+
warning,
|
|
9384
|
+
moduleContext: hasModuleContext(candidateAbs),
|
|
9385
|
+
currentState: hasCurrentState(candidateAbs),
|
|
9386
|
+
codebase: hasCodebase(candidateAbs)
|
|
9387
|
+
});
|
|
9388
|
+
}
|
|
9389
|
+
}
|
|
9390
|
+
for (const m of existing.modules) {
|
|
9391
|
+
const absPath = path5.resolve(dir, m.path);
|
|
9392
|
+
if (!exists(absPath) && !results.some((r) => r.path === m.path)) {
|
|
9393
|
+
results.push({
|
|
9394
|
+
id: m.id,
|
|
9395
|
+
name: m.name,
|
|
9396
|
+
path: m.path,
|
|
9397
|
+
parentSystem: m.parent_system,
|
|
9398
|
+
status: "missing",
|
|
9399
|
+
eligibleForMapping: false,
|
|
9400
|
+
warning: "Previously registered but path no longer exists.",
|
|
9401
|
+
moduleContext: false,
|
|
9402
|
+
currentState: false,
|
|
9403
|
+
codebase: false
|
|
9404
|
+
});
|
|
9405
|
+
}
|
|
9406
|
+
}
|
|
9407
|
+
return results;
|
|
9408
|
+
}
|
|
9409
|
+
function applyDiscovery(dir, discovered) {
|
|
9410
|
+
const config = loadConfig(dir);
|
|
9411
|
+
const sysName = systemName(config) ?? config.project.name;
|
|
9412
|
+
const yml = readModulesYml(dir);
|
|
9413
|
+
yml.system = sysName;
|
|
9414
|
+
const eligible = discovered.filter((d) => d.eligibleForMapping && d.status === "configured");
|
|
9415
|
+
for (const d of eligible) {
|
|
9416
|
+
const existingIdx = yml.modules.findIndex((m) => m.id === d.id);
|
|
9417
|
+
const entry = {
|
|
9418
|
+
id: d.id,
|
|
9419
|
+
name: d.name,
|
|
9420
|
+
path: d.path,
|
|
9421
|
+
parent_system: d.parentSystem,
|
|
9422
|
+
status: d.status,
|
|
9423
|
+
context: buildModuleContextPaths(path5.resolve(dir, d.path))
|
|
9424
|
+
};
|
|
9425
|
+
if (existingIdx >= 0) {
|
|
9426
|
+
yml.modules[existingIdx] = entry;
|
|
9427
|
+
} else {
|
|
9428
|
+
yml.modules.push(entry);
|
|
9429
|
+
}
|
|
9430
|
+
}
|
|
9431
|
+
writeModulesYml(dir, yml);
|
|
9432
|
+
updateModulesMd(dir, yml);
|
|
9433
|
+
return { mapped: eligible.length };
|
|
9434
|
+
}
|
|
9435
|
+
function updateModulesMd(dir, yml) {
|
|
9436
|
+
const lines = [
|
|
9437
|
+
"---",
|
|
9438
|
+
"type: modules-map",
|
|
9439
|
+
`system: ${yml.system}`,
|
|
9440
|
+
"generated_by: kaddo-modules-discover",
|
|
9441
|
+
"template_version: 2",
|
|
9442
|
+
"---",
|
|
9443
|
+
"",
|
|
9444
|
+
`# ${yml.system} \u2014 Modules`,
|
|
9445
|
+
""
|
|
9446
|
+
];
|
|
9447
|
+
if (yml.modules.length === 0) {
|
|
9448
|
+
lines.push("_No modules mapped yet. Run `kaddo modules discover` to find sibling modules._");
|
|
9449
|
+
} else {
|
|
9450
|
+
for (const m of yml.modules) {
|
|
9451
|
+
lines.push(`## ${m.id}`);
|
|
9452
|
+
lines.push("");
|
|
9453
|
+
lines.push(`- Repository: \`${m.path}\``);
|
|
9454
|
+
lines.push(`- Status: ${m.status}`);
|
|
9455
|
+
lines.push(`- Parent system: ${m.parent_system}`);
|
|
9456
|
+
const ctxPaths = m.context ?? {};
|
|
9457
|
+
lines.push(`- Module context: ${ctxPaths.module ? "available" : "missing"}`);
|
|
9458
|
+
lines.push("");
|
|
9459
|
+
}
|
|
9460
|
+
}
|
|
9461
|
+
writeFile(join(dir, MODULES_MD), lines.join("\n"));
|
|
9462
|
+
}
|
|
9463
|
+
function runModulesValidate(dir = cwd()) {
|
|
9464
|
+
const yml = readModulesYml(dir);
|
|
9465
|
+
if (yml.modules.length === 0) {
|
|
9466
|
+
console.log("No modules registered in .kaddo/modules.yml.");
|
|
9467
|
+
return;
|
|
9468
|
+
}
|
|
9469
|
+
const config = loadConfig(dir);
|
|
9470
|
+
const sysName = systemName(config) ?? config.project.name;
|
|
9471
|
+
const legacy = detectLegacyPaths(dir);
|
|
9472
|
+
if (legacy.length > 0) {
|
|
9473
|
+
console.log("");
|
|
9474
|
+
console.log("Legacy multirepo knowledge paths detected.");
|
|
9475
|
+
console.log("");
|
|
9476
|
+
console.log("Recommended migration:");
|
|
9477
|
+
for (const l of legacy) {
|
|
9478
|
+
console.log(`- ${l.from}`);
|
|
9479
|
+
console.log(` \u2192 ${l.to}`);
|
|
9480
|
+
console.log("");
|
|
9481
|
+
}
|
|
9482
|
+
}
|
|
9483
|
+
console.log("");
|
|
9484
|
+
for (const m of yml.modules) {
|
|
9485
|
+
const absPath = path5.resolve(dir, m.path);
|
|
9486
|
+
if (!exists(absPath)) {
|
|
9487
|
+
console.log(`${m.id.padEnd(16)} missing`);
|
|
9488
|
+
continue;
|
|
9489
|
+
}
|
|
9490
|
+
const repoConfig = readRepoConfig(absPath);
|
|
9491
|
+
if (!repoConfig) {
|
|
9492
|
+
console.log(`${m.id.padEnd(16)} not_configured`);
|
|
9493
|
+
continue;
|
|
9494
|
+
}
|
|
9495
|
+
const project = repoConfig.project;
|
|
9496
|
+
const multirepo = repoConfig.multirepo;
|
|
9497
|
+
const role = project?.role ?? multirepo?.role;
|
|
9498
|
+
if (role !== "module") {
|
|
9499
|
+
console.log(`${m.id.padEnd(16)} invalid`);
|
|
9500
|
+
continue;
|
|
9501
|
+
}
|
|
9502
|
+
const modParent = multirepo?.parent_system;
|
|
9503
|
+
if (modParent && modParent !== sysName) {
|
|
9504
|
+
console.log(`${m.id.padEnd(16)} foreign_system`);
|
|
9505
|
+
continue;
|
|
9506
|
+
}
|
|
9507
|
+
console.log(`${m.id.padEnd(16)} valid`);
|
|
9508
|
+
}
|
|
9509
|
+
console.log("");
|
|
9510
|
+
}
|
|
9511
|
+
function detectLegacyPaths(dir) {
|
|
9512
|
+
const migrations = [];
|
|
9513
|
+
if (exists(join(dir, "knowledge", "system", "system-context.md"))) {
|
|
9514
|
+
migrations.push({ from: "knowledge/system/system-context.md", to: "knowledge/tech/system/system-context.md" });
|
|
9515
|
+
}
|
|
9516
|
+
if (exists(join(dir, "knowledge", "modules", "modules.md"))) {
|
|
9517
|
+
migrations.push({ from: "knowledge/modules/modules.md", to: "knowledge/tech/modules/modules.md" });
|
|
9518
|
+
}
|
|
9519
|
+
if (exists(join(dir, "knowledge", "module", "module-context.md"))) {
|
|
9520
|
+
migrations.push({ from: "knowledge/module/module-context.md", to: "knowledge/tech/module/module-context.md" });
|
|
9521
|
+
}
|
|
9522
|
+
return migrations;
|
|
9523
|
+
}
|
|
9524
|
+
async function runModulesDiscover(opts = {}) {
|
|
9525
|
+
const dir = cwd();
|
|
9526
|
+
intro2("kaddo modules discover");
|
|
9527
|
+
const config = loadConfig(dir);
|
|
9528
|
+
if (!config) {
|
|
9529
|
+
console.error("Kaddo is not initialized. Run `kaddo init` first.");
|
|
9530
|
+
process.exit(1);
|
|
9531
|
+
}
|
|
9532
|
+
if (!isCore(config)) {
|
|
9533
|
+
console.error("Module discovery is only available from a core repository.");
|
|
9534
|
+
process.exit(1);
|
|
9535
|
+
}
|
|
9536
|
+
const discovered = discoverModules(dir);
|
|
9537
|
+
if (discovered.length === 0) {
|
|
9538
|
+
log2.info("No sibling repositories found in workspace roots.");
|
|
9539
|
+
outro2("Discovery complete.");
|
|
9540
|
+
return;
|
|
9541
|
+
}
|
|
9542
|
+
console.log("");
|
|
9543
|
+
console.log("Workspace module discovery");
|
|
9544
|
+
console.log("");
|
|
9545
|
+
for (const d of discovered) {
|
|
9546
|
+
const icon = d.status === "configured" ? "\u2713" : d.status === "not_configured" ? "\u25CB" : d.status === "foreign_system" ? "!" : d.status === "duplicate" ? "!" : d.status === "missing" ? "\u2717" : "\u2717";
|
|
9547
|
+
console.log(`${icon} ${d.id}`);
|
|
9548
|
+
console.log(` ${d.path}`);
|
|
9549
|
+
console.log(` status: ${d.status}`);
|
|
9550
|
+
if (d.warning) console.log(` ${d.warning}`);
|
|
9551
|
+
console.log("");
|
|
9552
|
+
}
|
|
9553
|
+
const eligible = discovered.filter((d) => d.eligibleForMapping && d.status === "configured");
|
|
9554
|
+
console.log(`Eligible for mapping: ${eligible.length}`);
|
|
9555
|
+
if (eligible.length === 0) {
|
|
9556
|
+
outro2("Discovery complete.");
|
|
9557
|
+
return;
|
|
9558
|
+
}
|
|
9559
|
+
if (opts.apply) {
|
|
9560
|
+
const confirmed = await confirm2({
|
|
9561
|
+
message: `Map ${eligible.length} configured Kaddo modules?`,
|
|
9562
|
+
initialValue: true
|
|
9563
|
+
});
|
|
9564
|
+
if (!confirmed) {
|
|
9565
|
+
outro2("No changes applied.");
|
|
9566
|
+
return;
|
|
9567
|
+
}
|
|
9568
|
+
const result = applyDiscovery(dir, discovered);
|
|
9569
|
+
log2.success(`Mapped ${result.mapped} configured modules.`);
|
|
9570
|
+
log2.success("Updated .kaddo/modules.yml.");
|
|
9571
|
+
log2.success("Updated knowledge/tech/modules/modules.md.");
|
|
9572
|
+
outro2("Discovery applied.");
|
|
9573
|
+
} else {
|
|
9574
|
+
log2.info("Run `kaddo modules discover --apply` to persist eligible modules.");
|
|
9575
|
+
outro2("Discovery complete.");
|
|
9576
|
+
}
|
|
9577
|
+
}
|
|
9578
|
+
|
|
9146
9579
|
// src/commands/modules-map.ts
|
|
9147
9580
|
var DESCRIPTOR_PATH2 = ".kaddo/modules.yml";
|
|
9148
|
-
var
|
|
9581
|
+
var CONFIG_PATH4 = ".kaddo/config.yml";
|
|
9149
9582
|
var MODULE_TYPES = [
|
|
9150
9583
|
"frontend",
|
|
9151
9584
|
"backend",
|
|
@@ -9160,17 +9593,17 @@ function slugify2(name) {
|
|
|
9160
9593
|
return name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
9161
9594
|
}
|
|
9162
9595
|
function readModulesDescriptor(dir) {
|
|
9163
|
-
const
|
|
9164
|
-
if (!exists(
|
|
9596
|
+
const path8 = join(dir, DESCRIPTOR_PATH2);
|
|
9597
|
+
if (!exists(path8)) return { version: 1, modules: [] };
|
|
9165
9598
|
try {
|
|
9166
|
-
const parsed =
|
|
9599
|
+
const parsed = parseYaml9(readFile(path8));
|
|
9167
9600
|
return { version: parsed.version ?? 1, modules: parsed.modules ?? [] };
|
|
9168
9601
|
} catch {
|
|
9169
9602
|
return { version: 1, modules: [] };
|
|
9170
9603
|
}
|
|
9171
9604
|
}
|
|
9172
9605
|
function writeModulesDescriptor(dir, descriptor) {
|
|
9173
|
-
writeFile(join(dir, DESCRIPTOR_PATH2),
|
|
9606
|
+
writeFile(join(dir, DESCRIPTOR_PATH2), stringifyYaml4(descriptor));
|
|
9174
9607
|
}
|
|
9175
9608
|
function moduleDir(id) {
|
|
9176
9609
|
return `knowledge/tech/modules/${id}`;
|
|
@@ -9301,70 +9734,74 @@ function mapModule(dir, input) {
|
|
|
9301
9734
|
}
|
|
9302
9735
|
return { module: mod, written, skipped, alreadyRegistered };
|
|
9303
9736
|
}
|
|
9304
|
-
async function runModulesMap(dir = cwd()) {
|
|
9737
|
+
async function runModulesMap(dir = cwd(), modulePath) {
|
|
9305
9738
|
intro2("kaddo modules map");
|
|
9306
|
-
if (!exists(join(dir,
|
|
9739
|
+
if (!exists(join(dir, CONFIG_PATH4))) {
|
|
9307
9740
|
console.error("Kaddo is not initialized in this project.");
|
|
9308
9741
|
console.error("Run `kaddo init` first.");
|
|
9309
9742
|
process.exit(1);
|
|
9310
9743
|
}
|
|
9311
|
-
|
|
9744
|
+
let repoPath;
|
|
9745
|
+
if (modulePath) {
|
|
9746
|
+
repoPath = modulePath;
|
|
9747
|
+
} else {
|
|
9748
|
+
repoPath = await text2({
|
|
9749
|
+
message: "Repository path (relative to this repo)",
|
|
9750
|
+
placeholder: "e.g. ../frontend",
|
|
9751
|
+
validate: (v) => v.trim().length === 0 ? "Repository path is required." : void 0
|
|
9752
|
+
});
|
|
9753
|
+
}
|
|
9754
|
+
const targetDir = join(dir, repoPath.trim());
|
|
9755
|
+
if (!exists(targetDir)) {
|
|
9756
|
+
log2.warn(`Path "${repoPath.trim()}" does not exist.`);
|
|
9757
|
+
} else {
|
|
9758
|
+
const st = detectModuleStatus(dir, repoPath.trim());
|
|
9759
|
+
if (st.status === "not_configured") {
|
|
9760
|
+
log2.warn(`${repoPath.trim()} does not have Kaddo configured. Run \`kaddo init\` inside that repository first.`);
|
|
9761
|
+
} else if (st.status === "invalid") {
|
|
9762
|
+
log2.warn(`${repoPath.trim()} has Kaddo but is not configured as a module.`);
|
|
9763
|
+
}
|
|
9764
|
+
}
|
|
9312
9765
|
const name = await text2({
|
|
9313
9766
|
message: "Module name",
|
|
9314
9767
|
placeholder: "e.g. Frontend Web",
|
|
9315
9768
|
validate: (v) => v.trim().length === 0 ? "Name is required." : void 0
|
|
9316
9769
|
});
|
|
9317
|
-
const repoPath = await text2({
|
|
9318
|
-
message: "Repository path (relative to this repo)",
|
|
9319
|
-
placeholder: "e.g. ../frontend",
|
|
9320
|
-
validate: (v) => v.trim().length === 0 ? "Repository path is required." : void 0
|
|
9321
|
-
});
|
|
9322
9770
|
const type = await select2({
|
|
9323
9771
|
message: "Module type",
|
|
9324
9772
|
options: MODULE_TYPES.map((t) => ({ value: t, label: t }))
|
|
9325
9773
|
});
|
|
9326
|
-
const mainTechnology = await text2({
|
|
9327
|
-
message: "Main technology (optional)",
|
|
9328
|
-
placeholder: "e.g. Next.js"
|
|
9329
|
-
});
|
|
9330
|
-
const owner = await text2({
|
|
9331
|
-
message: "Owner (optional)",
|
|
9332
|
-
placeholder: "e.g. web-team"
|
|
9333
|
-
});
|
|
9334
|
-
const capabilitiesRaw = await text2({
|
|
9335
|
-
message: "Related capabilities (comma-separated, optional)",
|
|
9336
|
-
placeholder: "e.g. customer-dashboard, loyalty-portal"
|
|
9337
|
-
});
|
|
9338
|
-
if (!exists(join(dir, repoPath.trim()))) {
|
|
9339
|
-
log2.warn(`Path "${repoPath.trim()}" does not exist yet \u2014 registering anyway.`);
|
|
9340
|
-
}
|
|
9341
9774
|
const result = mapModule(dir, {
|
|
9342
9775
|
name,
|
|
9343
9776
|
repoPath,
|
|
9344
|
-
type
|
|
9345
|
-
mainTechnology,
|
|
9346
|
-
owner,
|
|
9347
|
-
capabilities: capabilitiesRaw.split(",").map((c) => c.trim()).filter(Boolean)
|
|
9777
|
+
type
|
|
9348
9778
|
});
|
|
9349
9779
|
for (const p2 of result.written) log2.success(`Created ${p2}`);
|
|
9350
9780
|
for (const p2 of result.skipped) log2.info(`Kept existing ${p2}`);
|
|
9351
9781
|
log2.success(`Module "${result.module.id}" recorded in ${DESCRIPTOR_PATH2}`);
|
|
9352
|
-
outro2(
|
|
9353
|
-
"Module mapped. Use the `module-design-agent` in your LLM with `.kaddo/context-pack.md` to fill in module-design.md."
|
|
9354
|
-
);
|
|
9782
|
+
outro2("Module mapped.");
|
|
9355
9783
|
}
|
|
9356
9784
|
function runModulesList(dir = cwd()) {
|
|
9357
|
-
const
|
|
9358
|
-
if (
|
|
9359
|
-
console.log("
|
|
9785
|
+
const yml = readModulesYml(dir);
|
|
9786
|
+
if (yml.modules.length === 0) {
|
|
9787
|
+
console.log("");
|
|
9788
|
+
console.log("No modules are mapped for this core repository.");
|
|
9789
|
+
console.log("");
|
|
9790
|
+
console.log("Run:");
|
|
9791
|
+
console.log(" kaddo modules discover");
|
|
9792
|
+
console.log("");
|
|
9793
|
+
console.log("Or map one manually:");
|
|
9794
|
+
console.log(" kaddo modules map <path>");
|
|
9795
|
+
console.log("");
|
|
9360
9796
|
return;
|
|
9361
9797
|
}
|
|
9362
9798
|
console.log("");
|
|
9363
9799
|
console.log("Mapped modules:");
|
|
9364
|
-
for (const m of
|
|
9365
|
-
const
|
|
9800
|
+
for (const m of yml.modules) {
|
|
9801
|
+
const mPath = m.path ?? m.repoPath ?? "";
|
|
9802
|
+
const st = detectModuleStatus(dir, mPath);
|
|
9366
9803
|
const statusLabel = st.status === "configured" ? "\u2713 configured" : st.status === "not_configured" ? "\u25CB not configured" : "\u2717 invalid";
|
|
9367
|
-
console.log(` ${m.id.padEnd(16)} ${
|
|
9804
|
+
console.log(` ${m.id.padEnd(16)} ${statusLabel.padEnd(18)} ${mPath}`);
|
|
9368
9805
|
if (st.warning) console.log(` \u26A0 ${st.warning}`);
|
|
9369
9806
|
}
|
|
9370
9807
|
console.log("");
|
|
@@ -9505,14 +9942,14 @@ function loadExternalRegistry(dir) {
|
|
|
9505
9942
|
const p2 = join(dir, EXTERNAL_PATH);
|
|
9506
9943
|
if (!exists(p2)) return [];
|
|
9507
9944
|
try {
|
|
9508
|
-
const parsed =
|
|
9945
|
+
const parsed = parseYaml10(readFile(p2));
|
|
9509
9946
|
return Array.isArray(parsed?.external) ? parsed.external : [];
|
|
9510
9947
|
} catch {
|
|
9511
9948
|
return [];
|
|
9512
9949
|
}
|
|
9513
9950
|
}
|
|
9514
9951
|
function serializeExternalRegistry(entries) {
|
|
9515
|
-
return
|
|
9952
|
+
return stringifyYaml5({ external: entries });
|
|
9516
9953
|
}
|
|
9517
9954
|
function sectionList(md, title) {
|
|
9518
9955
|
const re = new RegExp(`^##\\s+${title}\\s*$`, "im");
|
|
@@ -9539,7 +9976,7 @@ function sectionParagraph(md, title) {
|
|
|
9539
9976
|
}
|
|
9540
9977
|
return "";
|
|
9541
9978
|
}
|
|
9542
|
-
function parseCapsule(id,
|
|
9979
|
+
function parseCapsule(id, path8, md) {
|
|
9543
9980
|
const { data } = matter3(md);
|
|
9544
9981
|
const updatedAt = data.updated_at ? String(data.updated_at) : void 0;
|
|
9545
9982
|
let ageDays = null;
|
|
@@ -9549,7 +9986,7 @@ function parseCapsule(id, path7, md) {
|
|
|
9549
9986
|
}
|
|
9550
9987
|
return {
|
|
9551
9988
|
id,
|
|
9552
|
-
path:
|
|
9989
|
+
path: path8,
|
|
9553
9990
|
system: data.system ? String(data.system) : id,
|
|
9554
9991
|
owner: data.owner ? String(data.owner) : void 0,
|
|
9555
9992
|
updatedAt,
|
|
@@ -9635,9 +10072,9 @@ function buildGraph(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
9635
10072
|
];
|
|
9636
10073
|
const presentLayers = [];
|
|
9637
10074
|
for (const layer2 of layerDocs) {
|
|
9638
|
-
const
|
|
9639
|
-
if (
|
|
9640
|
-
addNode({ id: layer2.id, type: layer2.type, label: layer2.label, path:
|
|
10075
|
+
const path8 = layer2.files.map((f) => `${KNOWLEDGE3}/${f}`).find((rel) => exists(join(dir, rel)));
|
|
10076
|
+
if (path8) {
|
|
10077
|
+
addNode({ id: layer2.id, type: layer2.type, label: layer2.label, path: path8 });
|
|
9641
10078
|
presentLayers.push(layer2.id);
|
|
9642
10079
|
}
|
|
9643
10080
|
}
|
|
@@ -10280,6 +10717,21 @@ function layerQuality(dir, layer2) {
|
|
|
10280
10717
|
return worst;
|
|
10281
10718
|
}
|
|
10282
10719
|
function knowledgeLayers(dir) {
|
|
10720
|
+
const config = loadConfig(dir);
|
|
10721
|
+
if (config && isModule(config)) {
|
|
10722
|
+
return [
|
|
10723
|
+
{ layer: "Business", status: "Not applicable", detected: [] },
|
|
10724
|
+
{ layer: "Product", status: "Not applicable", detected: [] },
|
|
10725
|
+
...discoverLayers(dir).filter((l) => l.layer === "Tech").map((l) => {
|
|
10726
|
+
if (l.status !== "Consolidated" && l.status !== "Structured") return l;
|
|
10727
|
+
const q = layerQuality(dir, l.layer);
|
|
10728
|
+
if (q === "placeholder") return { ...l, status: "Placeholder" };
|
|
10729
|
+
if (q === "weak") return { ...l, status: "Weak" };
|
|
10730
|
+
return l;
|
|
10731
|
+
}),
|
|
10732
|
+
{ layer: "Delivery", status: "Managed by core", detected: [] }
|
|
10733
|
+
];
|
|
10734
|
+
}
|
|
10283
10735
|
return discoverLayers(dir).map((l) => {
|
|
10284
10736
|
if (l.status !== "Consolidated" && l.status !== "Structured") return l;
|
|
10285
10737
|
const q = layerQuality(dir, l.layer);
|
|
@@ -10965,6 +11417,9 @@ function buildSharedFileStatuses(statuses) {
|
|
|
10965
11417
|
}));
|
|
10966
11418
|
}
|
|
10967
11419
|
|
|
11420
|
+
// src/core/next-step.ts
|
|
11421
|
+
import { parse as parseYaml11 } from "yaml";
|
|
11422
|
+
|
|
10968
11423
|
// src/core/decisions.ts
|
|
10969
11424
|
var CANDIDATES_DISCOVERY = "knowledge/tech/discovery/decision-candidates.md";
|
|
10970
11425
|
var CANDIDATES_LEGACY = "knowledge/tech/decision-candidates.md";
|
|
@@ -11080,6 +11535,14 @@ function buildDeliveryState(dir) {
|
|
|
11080
11535
|
adapters_installed: adapters.length
|
|
11081
11536
|
};
|
|
11082
11537
|
}
|
|
11538
|
+
function hasRegisteredModules(modulesYmlPath) {
|
|
11539
|
+
try {
|
|
11540
|
+
const parsed = parseYaml11(readFile(modulesYmlPath));
|
|
11541
|
+
return Array.isArray(parsed?.modules) && parsed.modules.length > 0;
|
|
11542
|
+
} catch {
|
|
11543
|
+
return false;
|
|
11544
|
+
}
|
|
11545
|
+
}
|
|
11083
11546
|
function safeRead(p2) {
|
|
11084
11547
|
try {
|
|
11085
11548
|
return readFile(p2);
|
|
@@ -11129,12 +11592,20 @@ function resolveNextStep(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
11129
11592
|
}
|
|
11130
11593
|
const state = config.project.state;
|
|
11131
11594
|
const q = (rel) => analyzeKnowledgeArtifact(dir, rel);
|
|
11595
|
+
const resolveAgent = (agent) => {
|
|
11596
|
+
const file = agent.endsWith(".md") ? agent : `${agent}.md`;
|
|
11597
|
+
const path8 = agentInstallPath(file);
|
|
11598
|
+
const installed = isFile(join(dir, path8));
|
|
11599
|
+
const group = agentGroupOf(file);
|
|
11600
|
+
return { agentPath: path8, agentInstalled: installed, installCommand: installed ? void 0 : `kaddo add agents --group ${group}` };
|
|
11601
|
+
};
|
|
11132
11602
|
const moduleRepo = isModule(config);
|
|
11133
|
-
const
|
|
11603
|
+
const coreRepo = isCore(config);
|
|
11604
|
+
const MC = exists(join(dir, "knowledge/tech/module/module-context.md")) ? "knowledge/tech/module/module-context.md" : "knowledge/module/module-context.md";
|
|
11134
11605
|
if (moduleRepo) {
|
|
11135
11606
|
const qMC = q(MC);
|
|
11136
11607
|
if (qMC === "missing") {
|
|
11137
|
-
return { id: "init-module-context", phase: "Setup", label: "Create `knowledge/module/module-context.md` for this module.", command: "kaddo init", reason: "Module context file is missing. Re-run `kaddo init` as a multirepo module." };
|
|
11608
|
+
return { id: "init-module-context", phase: "Setup", label: "Create `knowledge/tech/module/module-context.md` for this module.", command: "kaddo init", reason: "Module context file is missing. Re-run `kaddo init` as a multirepo module." };
|
|
11138
11609
|
}
|
|
11139
11610
|
if ((state === "pre-ai" || state === "legacy") && !exists(join(dir, ".kaddo", "scan.json"))) {
|
|
11140
11611
|
return { id: "scan", phase: "Discovery", label: "Run `kaddo scan` to capture deterministic signals from the existing code.", command: "kaddo scan", reason: "No scan baseline exists for this existing project yet." };
|
|
@@ -11143,15 +11614,26 @@ function resolveNextStep(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
11143
11614
|
return { id: "context", phase: "Discovery", label: "Run `kaddo context` to prepare the LLM context pack.", command: "kaddo context", reason: "No context pack has been generated yet." };
|
|
11144
11615
|
}
|
|
11145
11616
|
if (qMC !== "useful") {
|
|
11617
|
+
const ar = resolveAgent("module-context-agent");
|
|
11146
11618
|
return {
|
|
11147
11619
|
id: "refine-module-context",
|
|
11148
11620
|
phase: "Knowledge Refinement",
|
|
11149
|
-
label: "Use module-context-agent to refine `knowledge/module/module-context.md`.",
|
|
11621
|
+
label: "Use module-context-agent to refine `knowledge/tech/module/module-context.md`.",
|
|
11150
11622
|
agent: "module-context-agent",
|
|
11151
11623
|
skill: "module-context-refinement",
|
|
11152
11624
|
target: MC,
|
|
11625
|
+
agentPath: ar.agentPath,
|
|
11626
|
+
agentInstalled: ar.agentInstalled,
|
|
11627
|
+
installCommand: ar.installCommand,
|
|
11153
11628
|
reason: "Module context is still a placeholder.",
|
|
11154
|
-
instructions: [
|
|
11629
|
+
instructions: [
|
|
11630
|
+
"Refine the module context using evidence from this repository.",
|
|
11631
|
+
"Use the parent system context when available.",
|
|
11632
|
+
"Do not create business, product, roadmap or Work Items in this module.",
|
|
11633
|
+
"Do not install agents or skills in this module.",
|
|
11634
|
+
"Do not write application code.",
|
|
11635
|
+
"Preserve frontmatter and record unknowns as open questions."
|
|
11636
|
+
]
|
|
11155
11637
|
};
|
|
11156
11638
|
}
|
|
11157
11639
|
const qCurrentState2 = q(CS), qCodebase2 = q(CB);
|
|
@@ -11180,12 +11662,18 @@ function resolveNextStep(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
11180
11662
|
if (!exists(join(dir, ".kaddo", "understand.md"))) {
|
|
11181
11663
|
return { id: "understand", phase: "Discovery", label: "Run `kaddo understand` to summarize the project context.", command: "kaddo understand", reason: "No understand handoff has been generated yet." };
|
|
11182
11664
|
}
|
|
11183
|
-
return { id: "module-ready", phase: "
|
|
11665
|
+
return { id: "module-ready", phase: "Ready for Core Orchestration", label: "Module knowledge is complete. Create and manage Work Items from the core repository.", reason: "This module's knowledge is ready for core orchestration. Work Items are created and tracked in the core." };
|
|
11184
11666
|
}
|
|
11185
11667
|
const qBusiness = q(B), qProduct = q(P), qCap = q(CAP), qCurrentState = q(CS), qCodebase = q(CB);
|
|
11186
|
-
if (qBusiness === "missing" || qProduct === "missing") {
|
|
11668
|
+
if (!coreRepo && (qBusiness === "missing" || qProduct === "missing")) {
|
|
11187
11669
|
return { id: "bootstrap", phase: "Setup", label: "Run `kaddo bootstrap` to create the project knowledge baseline.", command: "kaddo bootstrap", reason: "The knowledge baseline is incomplete." };
|
|
11188
11670
|
}
|
|
11671
|
+
if (coreRepo) {
|
|
11672
|
+
const modulesYmlPath = join(dir, ".kaddo", "modules.yml");
|
|
11673
|
+
if (!exists(modulesYmlPath) || !hasRegisteredModules(modulesYmlPath)) {
|
|
11674
|
+
return { id: "modules-discover", phase: "Setup", label: "Run `kaddo modules discover` to find sibling module repositories.", command: "kaddo modules discover", reason: "No modules are mapped for this core repository." };
|
|
11675
|
+
}
|
|
11676
|
+
}
|
|
11189
11677
|
const ctx = buildCodexAdapterContext(dir);
|
|
11190
11678
|
if (!ctx.hasAgents) {
|
|
11191
11679
|
return { id: "add-agents", phase: "Setup", label: "Run `kaddo add agents` to install the Kaddo agent prompt packs.", command: "kaddo add agents", reason: "No Kaddo agents are installed yet." };
|
|
@@ -11200,13 +11688,6 @@ function resolveNextStep(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
11200
11688
|
return { id: "context", phase: "Discovery", label: "Run `kaddo context` to prepare the LLM context pack.", command: "kaddo context", reason: "No context pack has been generated yet." };
|
|
11201
11689
|
}
|
|
11202
11690
|
const discovery = state === "pre-ai" || state === "legacy";
|
|
11203
|
-
const resolveAgent = (agent) => {
|
|
11204
|
-
const file = agent.endsWith(".md") ? agent : `${agent}.md`;
|
|
11205
|
-
const path7 = agentInstallPath(file);
|
|
11206
|
-
const installed = isFile(join(dir, path7));
|
|
11207
|
-
const group = agentGroupOf(file);
|
|
11208
|
-
return { agentPath: path7, agentInstalled: installed, installCommand: installed ? void 0 : `kaddo add agents --group ${group}` };
|
|
11209
|
-
};
|
|
11210
11691
|
const refine = (id, agent, target, quality, verb = "complete") => {
|
|
11211
11692
|
const ar = resolveAgent(agent);
|
|
11212
11693
|
return {
|
|
@@ -11945,6 +12426,67 @@ var identifyRisks = {
|
|
|
11945
12426
|
return { status: hasLegacy ? "done" : "pending", agent: hasLegacy ? void 0 : "legacy-agent" };
|
|
11946
12427
|
}
|
|
11947
12428
|
};
|
|
12429
|
+
var refineModuleContext = {
|
|
12430
|
+
id: "refine-module-context",
|
|
12431
|
+
label: "Refine module context",
|
|
12432
|
+
evaluate: (ctx) => {
|
|
12433
|
+
const newPath = join(ctx.dir, "knowledge/tech/module/module-context.md");
|
|
12434
|
+
const legacyPath = join(ctx.dir, "knowledge/module/module-context.md");
|
|
12435
|
+
const mcPath = exists(newPath) ? "knowledge/tech/module/module-context.md" : exists(legacyPath) ? "knowledge/module/module-context.md" : null;
|
|
12436
|
+
if (!mcPath) return { status: "pending", agent: "module-context-agent", skill: "module-context-refinement" };
|
|
12437
|
+
const q = analyzeKnowledgeArtifact(ctx.dir, mcPath);
|
|
12438
|
+
if (q === "useful") return { status: "done", evidence: [mcPath] };
|
|
12439
|
+
return { status: "current", agent: "module-context-agent", skill: "module-context-refinement", evidence: [mcPath], reason: "Module context is still a placeholder." };
|
|
12440
|
+
}
|
|
12441
|
+
};
|
|
12442
|
+
var describeModuleCurrentState = {
|
|
12443
|
+
id: "describe-current-state",
|
|
12444
|
+
label: "Describe current state",
|
|
12445
|
+
evaluate: (ctx) => ({
|
|
12446
|
+
status: isUseful(ctx.qCurrentState) ? "done" : "pending",
|
|
12447
|
+
evidence: isUseful(ctx.qCurrentState) ? ["knowledge/tech/current-state.md"] : void 0,
|
|
12448
|
+
agent: isUseful(ctx.qCurrentState) ? void 0 : "architecture-agent"
|
|
12449
|
+
})
|
|
12450
|
+
};
|
|
12451
|
+
var mapModuleCodebase = {
|
|
12452
|
+
id: "map-codebase",
|
|
12453
|
+
label: "Map codebase",
|
|
12454
|
+
evaluate: (ctx) => ({
|
|
12455
|
+
status: isUseful(ctx.qCodebase) ? "done" : "pending",
|
|
12456
|
+
evidence: isUseful(ctx.qCodebase) ? ["knowledge/tech/codebase.md"] : void 0,
|
|
12457
|
+
agent: isUseful(ctx.qCodebase) ? void 0 : "architecture-agent"
|
|
12458
|
+
})
|
|
12459
|
+
};
|
|
12460
|
+
var validateModuleKnowledge = {
|
|
12461
|
+
id: "validate-module-knowledge",
|
|
12462
|
+
label: "Validate module knowledge",
|
|
12463
|
+
evaluate: (ctx) => {
|
|
12464
|
+
const hasUnderstand = exists(join(ctx.dir, ".kaddo/understand.md"));
|
|
12465
|
+
if (hasUnderstand) return { status: "done", evidence: [".kaddo/understand.md"] };
|
|
12466
|
+
return { status: "pending", command: "kaddo understand" };
|
|
12467
|
+
}
|
|
12468
|
+
};
|
|
12469
|
+
var readyForCoreOrchestration = {
|
|
12470
|
+
id: "ready-for-core-orchestration",
|
|
12471
|
+
label: "Ready for core orchestration",
|
|
12472
|
+
evaluate: (ctx) => {
|
|
12473
|
+
const newPath = join(ctx.dir, "knowledge/tech/module/module-context.md");
|
|
12474
|
+
const legacyPath = join(ctx.dir, "knowledge/module/module-context.md");
|
|
12475
|
+
const mcPath = exists(newPath) ? "knowledge/tech/module/module-context.md" : exists(legacyPath) ? "knowledge/module/module-context.md" : null;
|
|
12476
|
+
const mcUseful = mcPath ? analyzeKnowledgeArtifact(ctx.dir, mcPath) === "useful" : false;
|
|
12477
|
+
const allDone = mcUseful && isUseful(ctx.qCurrentState) && isUseful(ctx.qCodebase);
|
|
12478
|
+
return { status: allDone ? "done" : "pending", reason: allDone ? void 0 : "Module knowledge is not yet complete." };
|
|
12479
|
+
}
|
|
12480
|
+
};
|
|
12481
|
+
var MODULE_STEPS = [
|
|
12482
|
+
enableKaddo,
|
|
12483
|
+
scanRepository,
|
|
12484
|
+
refineModuleContext,
|
|
12485
|
+
describeModuleCurrentState,
|
|
12486
|
+
mapModuleCodebase,
|
|
12487
|
+
validateModuleKnowledge,
|
|
12488
|
+
readyForCoreOrchestration
|
|
12489
|
+
];
|
|
11948
12490
|
var NEW_STEPS = [
|
|
11949
12491
|
enableKaddo,
|
|
11950
12492
|
defineBusiness,
|
|
@@ -12085,17 +12627,18 @@ function mapNextStepId(id) {
|
|
|
12085
12627
|
"review-work-item": "refine-work-item",
|
|
12086
12628
|
"prepare-implementation": "prepare-implementation",
|
|
12087
12629
|
"implement-work-item": "prepare-implementation",
|
|
12088
|
-
"refine-module-context": "refine-
|
|
12630
|
+
"refine-module-context": "refine-module-context",
|
|
12089
12631
|
"init-module-context": "enable-kaddo",
|
|
12090
|
-
"module-ready": "
|
|
12091
|
-
"validate-work-item-modules": "
|
|
12632
|
+
"module-ready": "ready-for-core-orchestration",
|
|
12633
|
+
"validate-work-item-modules": "validate-module-knowledge",
|
|
12634
|
+
"modules-discover": "enable-kaddo"
|
|
12092
12635
|
};
|
|
12093
12636
|
return MAP[id] ?? id;
|
|
12094
12637
|
}
|
|
12095
12638
|
function buildProjectRoute(dir) {
|
|
12096
12639
|
const config = loadConfig(dir);
|
|
12097
12640
|
const state = config?.project.state ?? "pre-ai";
|
|
12098
|
-
const defs = stepsForState(state);
|
|
12641
|
+
const defs = config && isModule(config) ? MODULE_STEPS : stepsForState(state);
|
|
12099
12642
|
const ctx = buildRouteContext(dir);
|
|
12100
12643
|
const steps = defs.map((d) => {
|
|
12101
12644
|
const result = d.evaluate(ctx);
|
|
@@ -12434,7 +12977,8 @@ function buildProjectExplanation(dir) {
|
|
|
12434
12977
|
roadmapQuality: buildRoadmapQuality(dir),
|
|
12435
12978
|
projectRoute: buildProjectRoute(dir),
|
|
12436
12979
|
scanSignals: loadScanSignals(dir),
|
|
12437
|
-
metadataHealth: analyzeMetadataHealth(dir)
|
|
12980
|
+
metadataHealth: analyzeMetadataHealth(dir),
|
|
12981
|
+
isModuleRepo: config != null && isModule(config)
|
|
12438
12982
|
};
|
|
12439
12983
|
}
|
|
12440
12984
|
function stateLabel(state) {
|
|
@@ -12759,7 +13303,7 @@ function renderExplanationAgent(exp) {
|
|
|
12759
13303
|
|
|
12760
13304
|
// src/commands/explain.ts
|
|
12761
13305
|
var ARCH_DIR5 = "knowledge";
|
|
12762
|
-
var
|
|
13306
|
+
var CONFIG_PATH5 = ".kaddo/config.yml";
|
|
12763
13307
|
function readKnowledge(dir) {
|
|
12764
13308
|
const knowledgePath = join(dir, ARCH_DIR5, "knowledge.md");
|
|
12765
13309
|
if (!exists(knowledgePath)) return null;
|
|
@@ -12783,10 +13327,10 @@ function readRoadmap(dir) {
|
|
|
12783
13327
|
}
|
|
12784
13328
|
}
|
|
12785
13329
|
function readConfig(dir) {
|
|
12786
|
-
const configPath = join(dir,
|
|
13330
|
+
const configPath = join(dir, CONFIG_PATH5);
|
|
12787
13331
|
if (!exists(configPath)) return {};
|
|
12788
13332
|
try {
|
|
12789
|
-
return
|
|
13333
|
+
return parseYaml12(readFile(configPath));
|
|
12790
13334
|
} catch {
|
|
12791
13335
|
return {};
|
|
12792
13336
|
}
|
|
@@ -12897,7 +13441,7 @@ function explainForAgent(dir, artifacts, opts) {
|
|
|
12897
13441
|
console.log(JSON.stringify(output, null, 2));
|
|
12898
13442
|
}
|
|
12899
13443
|
function explainProject(dir, mode) {
|
|
12900
|
-
if (!exists(join(dir,
|
|
13444
|
+
if (!exists(join(dir, CONFIG_PATH5))) {
|
|
12901
13445
|
console.error("No .kaddo/config.yml found. Run `kaddo init` first.");
|
|
12902
13446
|
process.exit(1);
|
|
12903
13447
|
}
|
|
@@ -13028,15 +13572,16 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
|
|
|
13028
13572
|
if (!knowledgeSummary) {
|
|
13029
13573
|
missing.push("No project knowledge summary found yet.");
|
|
13030
13574
|
}
|
|
13575
|
+
const moduleRepo = config != null && isModule(config);
|
|
13031
13576
|
const roadmapSummary = readMarkdownSummary(dir, "delivery/roadmap.md") ?? "";
|
|
13032
|
-
if (!roadmapSummary) {
|
|
13577
|
+
if (!roadmapSummary && !moduleRepo) {
|
|
13033
13578
|
missing.push("No roadmap baseline found.");
|
|
13034
13579
|
}
|
|
13035
13580
|
const allArtifacts = discoverKnowledge(dir);
|
|
13036
13581
|
const workItems = allArtifacts.filter(
|
|
13037
13582
|
(a) => isDeliveryWorkItem(a) && isActiveState(lifecycleStateOf({ status: a.status, filePath: a.filePath }))
|
|
13038
13583
|
);
|
|
13039
|
-
if (workItems.length === 0) {
|
|
13584
|
+
if (workItems.length === 0 && !moduleRepo) {
|
|
13040
13585
|
missing.push("No work items found.");
|
|
13041
13586
|
}
|
|
13042
13587
|
const state = config.project.state;
|
|
@@ -13068,8 +13613,8 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
|
|
|
13068
13613
|
tech: { status: layerStatusOf("Tech"), artifacts: { "knowledge/tech/codebase.md": qCodebase, "knowledge/tech/current-state.md": qCurrentState } },
|
|
13069
13614
|
delivery: { status: layerStatusOf("Delivery"), artifacts: { "knowledge/delivery/roadmap.md": qRoadmap } }
|
|
13070
13615
|
};
|
|
13071
|
-
if (qBusiness === "placeholder") missing.push("Business context exists but still looks like a bootstrap placeholder.");
|
|
13072
|
-
if (qProduct === "placeholder" || qCapabilities === "placeholder") missing.push("Product capabilities exist but still look like a bootstrap placeholder.");
|
|
13616
|
+
if (qBusiness === "placeholder" && !moduleRepo) missing.push("Business context exists but still looks like a bootstrap placeholder.");
|
|
13617
|
+
if ((qProduct === "placeholder" || qCapabilities === "placeholder") && !moduleRepo) missing.push("Product capabilities exist but still look like a bootstrap placeholder.");
|
|
13073
13618
|
if (qCurrentState === "placeholder") missing.push("Current state exists but still looks like a bootstrap placeholder.");
|
|
13074
13619
|
if (qCodebase === "placeholder") missing.push("Codebase map exists but still looks like a bootstrap placeholder.");
|
|
13075
13620
|
const allWorkItems = allArtifacts.filter((a) => a.isWorkItem);
|
|
@@ -13092,7 +13637,8 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
|
|
|
13092
13637
|
workItemsTotal: allWorkItems.length,
|
|
13093
13638
|
workItemsWithOwnership: wiWithOwnership,
|
|
13094
13639
|
workItemsMissingOwnership: allWorkItems.length - wiWithOwnership
|
|
13095
|
-
}
|
|
13640
|
+
},
|
|
13641
|
+
isModuleRepo: config != null && isModule(config)
|
|
13096
13642
|
});
|
|
13097
13643
|
const nextStepRecommendation = resolveNextStep(dir, now);
|
|
13098
13644
|
const techDecisions = buildTechDecisions(dir);
|
|
@@ -13613,6 +14159,16 @@ function flowForState(state) {
|
|
|
13613
14159
|
}
|
|
13614
14160
|
function buildUnderstandPlan(dir, config) {
|
|
13615
14161
|
const state = config.project.state;
|
|
14162
|
+
if (isModule(config)) {
|
|
14163
|
+
return {
|
|
14164
|
+
project: { name: config.project.name, state, teamSize: config.team.size, structure: config.project.structure, language: "English" },
|
|
14165
|
+
steps: [],
|
|
14166
|
+
missingAgents: [],
|
|
14167
|
+
agentsInstalled: true,
|
|
14168
|
+
scanAvailable: exists(join(dir, ".kaddo", "scan.json")),
|
|
14169
|
+
contextPackPath: ".kaddo/context-pack.md"
|
|
14170
|
+
};
|
|
14171
|
+
}
|
|
13616
14172
|
const flow = flowForState(state).filter((s) => !exists(join(dir, s.output)));
|
|
13617
14173
|
const steps = flow.map((s) => {
|
|
13618
14174
|
const installed = agentIsInstalled(dir, s.agent);
|
|
@@ -13969,7 +14525,7 @@ function renderUnderstandTerminal(plan) {
|
|
|
13969
14525
|
}
|
|
13970
14526
|
|
|
13971
14527
|
// src/core/delivery.ts
|
|
13972
|
-
import { parse as
|
|
14528
|
+
import { parse as parseYaml13 } from "yaml";
|
|
13973
14529
|
function slugify4(s) {
|
|
13974
14530
|
return s.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
13975
14531
|
}
|
|
@@ -14203,7 +14759,7 @@ function runUnderstand() {
|
|
|
14203
14759
|
}
|
|
14204
14760
|
|
|
14205
14761
|
// src/core/classifier.ts
|
|
14206
|
-
import
|
|
14762
|
+
import path6 from "path";
|
|
14207
14763
|
var MIGRATION_PATTERNS = [
|
|
14208
14764
|
/supabase\/migrations\//,
|
|
14209
14765
|
/prisma\/migrations\//,
|
|
@@ -14242,7 +14798,7 @@ var DEP_PATTERNS = [
|
|
|
14242
14798
|
/^pom\.xml$/
|
|
14243
14799
|
];
|
|
14244
14800
|
function normalizePath2(p2) {
|
|
14245
|
-
return p2.split(
|
|
14801
|
+
return p2.split(path6.sep).join("/");
|
|
14246
14802
|
}
|
|
14247
14803
|
function matchesAny2(file, patterns) {
|
|
14248
14804
|
const normalized = normalizePath2(file);
|
|
@@ -14420,14 +14976,14 @@ async function runClassify(opts = {}) {
|
|
|
14420
14976
|
}
|
|
14421
14977
|
|
|
14422
14978
|
// src/commands/status.ts
|
|
14423
|
-
import { parse as
|
|
14979
|
+
import { parse as parseYaml14 } from "yaml";
|
|
14424
14980
|
var ARCH_DIR8 = "knowledge";
|
|
14425
|
-
var
|
|
14981
|
+
var CONFIG_PATH6 = ".kaddo/config.yml";
|
|
14426
14982
|
function loadConfig3(dir) {
|
|
14427
|
-
const p2 = join(dir,
|
|
14983
|
+
const p2 = join(dir, CONFIG_PATH6);
|
|
14428
14984
|
if (!exists(p2)) return {};
|
|
14429
14985
|
try {
|
|
14430
|
-
return
|
|
14986
|
+
return parseYaml14(readFile(p2));
|
|
14431
14987
|
} catch {
|
|
14432
14988
|
return {};
|
|
14433
14989
|
}
|
|
@@ -14629,13 +15185,13 @@ function runHistory(opts = {}) {
|
|
|
14629
15185
|
}
|
|
14630
15186
|
|
|
14631
15187
|
// src/commands/add.ts
|
|
14632
|
-
import { parse as
|
|
14633
|
-
var
|
|
15188
|
+
import { parse as parseYaml15, stringify as stringifyYaml6 } from "yaml";
|
|
15189
|
+
var CONFIG_PATH7 = ".kaddo/config.yml";
|
|
14634
15190
|
function readProjectState(dir) {
|
|
14635
|
-
const configPath = join(dir,
|
|
15191
|
+
const configPath = join(dir, CONFIG_PATH7);
|
|
14636
15192
|
if (!exists(configPath)) return void 0;
|
|
14637
15193
|
try {
|
|
14638
|
-
const config =
|
|
15194
|
+
const config = parseYaml15(readFile(configPath));
|
|
14639
15195
|
return config.project?.state;
|
|
14640
15196
|
} catch {
|
|
14641
15197
|
return void 0;
|
|
@@ -14656,23 +15212,23 @@ function selectSkillModuleFiles(files, opts) {
|
|
|
14656
15212
|
return { files: selected, label };
|
|
14657
15213
|
}
|
|
14658
15214
|
function markModuleInstalled(dir, configKey, moduleName) {
|
|
14659
|
-
const configPath = join(dir,
|
|
15215
|
+
const configPath = join(dir, CONFIG_PATH7);
|
|
14660
15216
|
if (!exists(configPath)) return;
|
|
14661
15217
|
try {
|
|
14662
|
-
const config =
|
|
15218
|
+
const config = parseYaml15(readFile(configPath));
|
|
14663
15219
|
config[configKey] = { installed: true, installed_at: (/* @__PURE__ */ new Date()).toISOString().split("T")[0] };
|
|
14664
15220
|
const modules = config.modules ?? [];
|
|
14665
15221
|
if (!modules.includes(moduleName)) modules.push(moduleName);
|
|
14666
15222
|
config.modules = modules;
|
|
14667
|
-
writeFile(configPath,
|
|
15223
|
+
writeFile(configPath, stringifyYaml6(config));
|
|
14668
15224
|
} catch {
|
|
14669
15225
|
}
|
|
14670
15226
|
}
|
|
14671
15227
|
function isModuleInstalled(dir, configKey) {
|
|
14672
|
-
const configPath = join(dir,
|
|
15228
|
+
const configPath = join(dir, CONFIG_PATH7);
|
|
14673
15229
|
if (!exists(configPath)) return false;
|
|
14674
15230
|
try {
|
|
14675
|
-
const config =
|
|
15231
|
+
const config = parseYaml15(readFile(configPath));
|
|
14676
15232
|
const moduleConfig = config[configKey];
|
|
14677
15233
|
return moduleConfig?.installed === true;
|
|
14678
15234
|
} catch {
|
|
@@ -14696,11 +15252,16 @@ function runAdd(moduleName, opts = {}, dir = cwd()) {
|
|
|
14696
15252
|
process.exit(1);
|
|
14697
15253
|
}
|
|
14698
15254
|
intro2(`kaddo add ${mod.name}`);
|
|
14699
|
-
if (!exists(join(dir,
|
|
15255
|
+
if (!exists(join(dir, CONFIG_PATH7))) {
|
|
14700
15256
|
console.error("Kaddo is not initialized in this project.");
|
|
14701
15257
|
console.error("Run `kaddo init` first.");
|
|
14702
15258
|
process.exit(1);
|
|
14703
15259
|
}
|
|
15260
|
+
const addConfig = loadConfig(dir);
|
|
15261
|
+
if ((mod.name === "agents" || mod.name === "skills") && addConfig && isModule(addConfig)) {
|
|
15262
|
+
console.error(`Module repos do not use ${mod.name}. ${mod.name === "agents" ? "Agents" : "Skills"} are managed by the core repo.`);
|
|
15263
|
+
process.exit(1);
|
|
15264
|
+
}
|
|
14704
15265
|
const alreadyInstalled = isModuleInstalled(dir, mod.configKey);
|
|
14705
15266
|
for (const d of mod.dirs) {
|
|
14706
15267
|
ensureDir(join(dir, d));
|
|
@@ -14901,11 +15462,11 @@ function applyOwnership(raw, globs, mode = "replace") {
|
|
|
14901
15462
|
}
|
|
14902
15463
|
|
|
14903
15464
|
// src/commands/owners.ts
|
|
14904
|
-
var
|
|
15465
|
+
var CONFIG_PATH8 = ".kaddo/config.yml";
|
|
14905
15466
|
var CUSTOM_GLOB = "__custom__";
|
|
14906
15467
|
function runOwners(opts) {
|
|
14907
15468
|
const dir = cwd();
|
|
14908
|
-
if (!exists(join(dir,
|
|
15469
|
+
if (!exists(join(dir, CONFIG_PATH8))) {
|
|
14909
15470
|
console.error("No .kaddo/config.yml found. Run `kaddo init` first.");
|
|
14910
15471
|
process.exit(1);
|
|
14911
15472
|
}
|
|
@@ -14995,7 +15556,7 @@ async function chooseGlobs(suggested) {
|
|
|
14995
15556
|
}
|
|
14996
15557
|
async function runOwnersSuggest() {
|
|
14997
15558
|
const dir = cwd();
|
|
14998
|
-
if (!exists(join(dir,
|
|
15559
|
+
if (!exists(join(dir, CONFIG_PATH8))) {
|
|
14999
15560
|
console.error("No .kaddo/config.yml found. Run `kaddo init` first.");
|
|
15000
15561
|
process.exit(1);
|
|
15001
15562
|
}
|
|
@@ -15070,113 +15631,6 @@ ${globs.map((g) => ` - ${g}`).join("\n")}`);
|
|
|
15070
15631
|
outro2("Ownership declared.");
|
|
15071
15632
|
}
|
|
15072
15633
|
|
|
15073
|
-
// src/commands/module-descriptor.ts
|
|
15074
|
-
import { parse as parseYaml14, stringify as stringifyYaml6 } from "yaml";
|
|
15075
|
-
var DESCRIPTOR_PATH3 = "knowledge/module.yml";
|
|
15076
|
-
function readDescriptor(dir) {
|
|
15077
|
-
const path7 = join(dir, DESCRIPTOR_PATH3);
|
|
15078
|
-
if (!exists(path7)) return null;
|
|
15079
|
-
try {
|
|
15080
|
-
return parseYaml14(readFile(path7));
|
|
15081
|
-
} catch {
|
|
15082
|
-
return null;
|
|
15083
|
-
}
|
|
15084
|
-
}
|
|
15085
|
-
function writeDescriptor(dir, descriptor) {
|
|
15086
|
-
writeFile(join(dir, DESCRIPTOR_PATH3), stringifyYaml6(descriptor));
|
|
15087
|
-
}
|
|
15088
|
-
async function runModuleDescriptor(opts) {
|
|
15089
|
-
const dir = cwd();
|
|
15090
|
-
if (opts.show || !opts.init && exists(join(dir, DESCRIPTOR_PATH3))) {
|
|
15091
|
-
const descriptor2 = readDescriptor(dir);
|
|
15092
|
-
if (!descriptor2) {
|
|
15093
|
-
console.log("No module descriptor found. Run `kaddo module --init` to create one.");
|
|
15094
|
-
return;
|
|
15095
|
-
}
|
|
15096
|
-
printDescriptor(descriptor2);
|
|
15097
|
-
return;
|
|
15098
|
-
}
|
|
15099
|
-
intro2("kaddo module --init");
|
|
15100
|
-
log2.info("Creates knowledge/module.yml \u2014 declares this repository's identity for multirepo contexts.");
|
|
15101
|
-
const name = await text2({
|
|
15102
|
-
message: "Module name (this repository)",
|
|
15103
|
-
placeholder: "e.g. payments-service",
|
|
15104
|
-
validate: (v) => v.trim().length === 0 ? "Name is required." : void 0
|
|
15105
|
-
});
|
|
15106
|
-
const purpose = await text2({
|
|
15107
|
-
message: "Purpose \u2014 what does this module do?",
|
|
15108
|
-
placeholder: "e.g. Handles subscription billing, invoicing, and payment processing",
|
|
15109
|
-
validate: (v) => v.trim().length === 0 ? "Purpose is required." : void 0
|
|
15110
|
-
});
|
|
15111
|
-
const stack = await text2({
|
|
15112
|
-
message: "Stack (comma-separated)",
|
|
15113
|
-
placeholder: "e.g. TypeScript, Node.js, PostgreSQL, Stripe"
|
|
15114
|
-
});
|
|
15115
|
-
const responsibilities = await text2({
|
|
15116
|
-
message: "Key responsibilities (comma-separated)",
|
|
15117
|
-
placeholder: "e.g. Charge customers, Issue refunds, Manage subscriptions"
|
|
15118
|
-
});
|
|
15119
|
-
const contracts = await text2({
|
|
15120
|
-
message: "Contracts exposed (APIs, events, comma-separated)",
|
|
15121
|
-
placeholder: "e.g. POST /charge, event:payment.succeeded, event:payment.failed"
|
|
15122
|
-
});
|
|
15123
|
-
const dependencies = await text2({
|
|
15124
|
-
message: "Dependencies on other modules (comma-separated)",
|
|
15125
|
-
placeholder: "e.g. auth-service, user-service"
|
|
15126
|
-
});
|
|
15127
|
-
const boundaries = await text2({
|
|
15128
|
-
message: "What does this module NOT own?",
|
|
15129
|
-
placeholder: "e.g. User identity, Email delivery, Inventory"
|
|
15130
|
-
});
|
|
15131
|
-
const ownership = await text2({
|
|
15132
|
-
message: "Team or owner",
|
|
15133
|
-
placeholder: "e.g. platform-team"
|
|
15134
|
-
});
|
|
15135
|
-
const splitTrimmed = (s) => s.split(",").map((x) => x.trim()).filter(Boolean);
|
|
15136
|
-
const descriptor = {
|
|
15137
|
-
name: name.trim(),
|
|
15138
|
-
purpose: purpose.trim(),
|
|
15139
|
-
stack: splitTrimmed(stack),
|
|
15140
|
-
responsibilities: splitTrimmed(responsibilities),
|
|
15141
|
-
contracts: splitTrimmed(contracts),
|
|
15142
|
-
dependencies: splitTrimmed(dependencies),
|
|
15143
|
-
boundaries: splitTrimmed(boundaries),
|
|
15144
|
-
ownership: ownership.trim(),
|
|
15145
|
-
related_artifacts: []
|
|
15146
|
-
};
|
|
15147
|
-
writeDescriptor(dir, descriptor);
|
|
15148
|
-
log2.success(`Created ${DESCRIPTOR_PATH3}`);
|
|
15149
|
-
outro2(`Module descriptor ready. Share knowledge/module.yml with dependent repositories.`);
|
|
15150
|
-
}
|
|
15151
|
-
function printDescriptor(d) {
|
|
15152
|
-
console.log("");
|
|
15153
|
-
console.log(`Module: ${d.name}`);
|
|
15154
|
-
console.log(`Purpose: ${d.purpose}`);
|
|
15155
|
-
if (d.stack?.length) console.log(`Stack: ${d.stack.join(", ")}`);
|
|
15156
|
-
if (d.ownership) console.log(`Owner: ${d.ownership}`);
|
|
15157
|
-
if (d.responsibilities?.length) {
|
|
15158
|
-
console.log("Responsibilities:");
|
|
15159
|
-
d.responsibilities.forEach((r) => console.log(` - ${r}`));
|
|
15160
|
-
}
|
|
15161
|
-
if (d.contracts?.length) {
|
|
15162
|
-
console.log("Contracts:");
|
|
15163
|
-
d.contracts.forEach((c) => console.log(` - ${c}`));
|
|
15164
|
-
}
|
|
15165
|
-
if (d.dependencies?.length) {
|
|
15166
|
-
console.log("Dependencies:");
|
|
15167
|
-
d.dependencies.forEach((dep) => console.log(` - ${dep}`));
|
|
15168
|
-
}
|
|
15169
|
-
if (d.boundaries?.length) {
|
|
15170
|
-
console.log("Boundaries (not owned):");
|
|
15171
|
-
d.boundaries.forEach((b) => console.log(` - ${b}`));
|
|
15172
|
-
}
|
|
15173
|
-
if (d.related_artifacts?.length) {
|
|
15174
|
-
console.log("Related artifacts:");
|
|
15175
|
-
d.related_artifacts.forEach((a) => console.log(` - ${a}`));
|
|
15176
|
-
}
|
|
15177
|
-
console.log("");
|
|
15178
|
-
}
|
|
15179
|
-
|
|
15180
15634
|
// src/core/bootstrap-templates.ts
|
|
15181
15635
|
function fm(type, state) {
|
|
15182
15636
|
return `---
|
|
@@ -15608,7 +16062,7 @@ function baselineTemplate(kind, state) {
|
|
|
15608
16062
|
}
|
|
15609
16063
|
|
|
15610
16064
|
// src/commands/bootstrap.ts
|
|
15611
|
-
var
|
|
16065
|
+
var CONFIG_PATH9 = ".kaddo/config.yml";
|
|
15612
16066
|
var FILE_TARGETS = [
|
|
15613
16067
|
{ path: "knowledge/business/business.md", kind: "business" },
|
|
15614
16068
|
{ path: "knowledge/product/product.md", kind: "product" },
|
|
@@ -15665,7 +16119,7 @@ function bootstrap(dir) {
|
|
|
15665
16119
|
}
|
|
15666
16120
|
async function runBootstrap(dir = cwd()) {
|
|
15667
16121
|
intro2("kaddo bootstrap");
|
|
15668
|
-
if (!exists(join(dir,
|
|
16122
|
+
if (!exists(join(dir, CONFIG_PATH9))) {
|
|
15669
16123
|
console.error("Kaddo is not initialized in this project.");
|
|
15670
16124
|
console.error("Run `kaddo init` first.");
|
|
15671
16125
|
process.exit(1);
|
|
@@ -16257,7 +16711,7 @@ function runReportImpact(opts = {}) {
|
|
|
16257
16711
|
}
|
|
16258
16712
|
|
|
16259
16713
|
// src/core/savings.ts
|
|
16260
|
-
import { parse as
|
|
16714
|
+
import { parse as parseYaml16 } from "yaml";
|
|
16261
16715
|
var DEFAULT_ASSUMPTIONS = {
|
|
16262
16716
|
currency: "USD",
|
|
16263
16717
|
hourly_cost: 40,
|
|
@@ -16275,7 +16729,7 @@ function loadAssumptions(dir) {
|
|
|
16275
16729
|
const p2 = join(dir, SAVINGS_PATH);
|
|
16276
16730
|
if (!exists(p2)) return { assumptions: { ...DEFAULT_ASSUMPTIONS }, source: "default" };
|
|
16277
16731
|
try {
|
|
16278
|
-
const raw =
|
|
16732
|
+
const raw = parseYaml16(readFile(p2));
|
|
16279
16733
|
const a = raw?.assumptions ?? {};
|
|
16280
16734
|
const t = raw?.team ?? {};
|
|
16281
16735
|
const num = (v, d) => typeof v === "number" && Number.isFinite(v) ? v : d;
|
|
@@ -17057,7 +17511,7 @@ function runAssetsUpdate(kind, opts = {}) {
|
|
|
17057
17511
|
|
|
17058
17512
|
// src/commands/ready.ts
|
|
17059
17513
|
import fs4 from "fs";
|
|
17060
|
-
import
|
|
17514
|
+
import path7 from "path";
|
|
17061
17515
|
import matter11 from "gray-matter";
|
|
17062
17516
|
function validateReadiness(content, frontmatter) {
|
|
17063
17517
|
const warnings = [];
|
|
@@ -17120,10 +17574,10 @@ function transitionToReady(filePath) {
|
|
|
17120
17574
|
const posix = filePath.replace(/\\/g, "/");
|
|
17121
17575
|
let newPath = filePath;
|
|
17122
17576
|
if (posix.includes("/work-items/draft/")) {
|
|
17123
|
-
const readyDir =
|
|
17577
|
+
const readyDir = path7.dirname(filePath).replace(/[/\\]draft$/, path7.sep + "ready");
|
|
17124
17578
|
ensureDir(readyDir);
|
|
17125
|
-
const fileName =
|
|
17126
|
-
newPath =
|
|
17579
|
+
const fileName = path7.basename(filePath);
|
|
17580
|
+
newPath = path7.join(readyDir, fileName);
|
|
17127
17581
|
}
|
|
17128
17582
|
return { newPath, newContent };
|
|
17129
17583
|
}
|
|
@@ -17194,8 +17648,8 @@ async function runReady(id, opts = {}) {
|
|
|
17194
17648
|
fs4.unlinkSync(wi.filePath);
|
|
17195
17649
|
log2.success(`Updated status: ready`);
|
|
17196
17650
|
log2.success(`Moved file:`);
|
|
17197
|
-
log2.info(` ${
|
|
17198
|
-
log2.info(` \u2192 ${
|
|
17651
|
+
log2.info(` ${path7.relative(dir, wi.filePath)}`);
|
|
17652
|
+
log2.info(` \u2192 ${path7.relative(dir, newPath)}`);
|
|
17199
17653
|
} else {
|
|
17200
17654
|
writeFile(wi.filePath, newContent);
|
|
17201
17655
|
log2.success(`Updated status: ready`);
|
|
@@ -17229,8 +17683,8 @@ var capsuleCmd = program.command("capsule").description("Export this project as
|
|
|
17229
17683
|
capsuleCmd.command("export").description("Write a Knowledge Capsule about this project to .kaddo/exports/").option("--scope <scope>", 'Export scope: "system" includes all mapped modules').option("--module <id>", "Export a capsule for a specific mapped module").action((opts) => {
|
|
17230
17684
|
runCapsuleExport(opts);
|
|
17231
17685
|
});
|
|
17232
|
-
capsuleCmd.command("add <path>").description("Register an external Knowledge Capsule as project context (.kaddo/external.yml)").action((
|
|
17233
|
-
runCapsuleAdd(
|
|
17686
|
+
capsuleCmd.command("add <path>").description("Register an external Knowledge Capsule as project context (.kaddo/external.yml)").action((path8) => {
|
|
17687
|
+
runCapsuleAdd(path8);
|
|
17234
17688
|
});
|
|
17235
17689
|
var graphCmd = program.command("graph").description("Export the lightweight, file-based knowledge graph of the project");
|
|
17236
17690
|
graphCmd.command("export").description("Write the knowledge graph to .kaddo/graph.json and .kaddo/graph.mmd").option("--scope <scope>", "Graph scope: active (default) or all").option("--format <format>", "Output format: json, mermaid (default: both)").action((opts) => {
|
|
@@ -17328,16 +17782,23 @@ program.command("owners [action]").description("List domain owners, or run `owne
|
|
|
17328
17782
|
runOwners(opts);
|
|
17329
17783
|
}
|
|
17330
17784
|
});
|
|
17331
|
-
program.command("module").description("
|
|
17332
|
-
|
|
17785
|
+
program.command("module").description("(deprecated) Use `kaddo modules` instead").allowUnknownOption().action(() => {
|
|
17786
|
+
console.log("Unknown command `module`.");
|
|
17787
|
+
console.log("Did you mean `kaddo modules list`?");
|
|
17333
17788
|
});
|
|
17334
|
-
var modulesCmd = program.command("modules").description("
|
|
17335
|
-
modulesCmd.command("
|
|
17336
|
-
await
|
|
17789
|
+
var modulesCmd = program.command("modules").description("Discover, map, list and validate multirepo modules");
|
|
17790
|
+
modulesCmd.command("discover").description("Discover sibling repositories configured as Kaddo modules").option("--apply", "Persist discovered modules to .kaddo/modules.yml").action(async (opts) => {
|
|
17791
|
+
await runModulesDiscover(opts);
|
|
17337
17792
|
});
|
|
17338
|
-
modulesCmd.command("
|
|
17793
|
+
modulesCmd.command("map [path]").description("Register a repository as a module manually").action(async (modulePath) => {
|
|
17794
|
+
await runModulesMap(void 0, modulePath);
|
|
17795
|
+
});
|
|
17796
|
+
modulesCmd.command("list").description("List mapped modules from .kaddo/modules.yml").action(() => {
|
|
17339
17797
|
runModulesList();
|
|
17340
17798
|
});
|
|
17799
|
+
modulesCmd.command("validate").description("Validate registered modules against their current configuration").action(() => {
|
|
17800
|
+
runModulesValidate();
|
|
17801
|
+
});
|
|
17341
17802
|
program.command("add [module]").description("Install an optional Kaddo module (adr, incident, rfc, migration, legacy, agents, skills, standards, security, stack, git-strategy)").option("--all", "For `add agents` / `add skills`: install every item (not just the recommended set)").option("--group <name>", "For `add agents`: business|product|tech|delivery|utilities. For `add skills`: delivery|tech|integration").action((moduleName, opts) => {
|
|
17342
17803
|
runAdd(moduleName ?? "", { all: opts.all, group: opts.group });
|
|
17343
17804
|
});
|