@kaddo/cli 3.61.0 → 3.63.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 +609 -274
- 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 = {
|
|
@@ -6550,10 +6587,10 @@ function analyzeGuard(touchedFiles, artifacts, silentWithoutOwnership) {
|
|
|
6550
6587
|
import { parse as parseYaml4, stringify as stringifyYaml2 } from "yaml";
|
|
6551
6588
|
var IGNORE_FILE = ".kaddo/ignores.yml";
|
|
6552
6589
|
function loadIgnores(dir) {
|
|
6553
|
-
const
|
|
6554
|
-
if (!exists(
|
|
6590
|
+
const path8 = join(dir, IGNORE_FILE);
|
|
6591
|
+
if (!exists(path8)) return [];
|
|
6555
6592
|
try {
|
|
6556
|
-
const raw = readFile(
|
|
6593
|
+
const raw = readFile(path8);
|
|
6557
6594
|
const parsed = parseYaml4(raw);
|
|
6558
6595
|
return Array.isArray(parsed) ? parsed : [];
|
|
6559
6596
|
} catch {
|
|
@@ -6561,7 +6598,7 @@ function loadIgnores(dir) {
|
|
|
6561
6598
|
}
|
|
6562
6599
|
}
|
|
6563
6600
|
function saveIgnore(dir, entry) {
|
|
6564
|
-
const
|
|
6601
|
+
const path8 = join(dir, IGNORE_FILE);
|
|
6565
6602
|
const existing = loadIgnores(dir);
|
|
6566
6603
|
const idx = existing.findIndex((e) => e.artifact_id === entry.artifact_id);
|
|
6567
6604
|
if (idx >= 0) {
|
|
@@ -6569,17 +6606,17 @@ function saveIgnore(dir, entry) {
|
|
|
6569
6606
|
} else {
|
|
6570
6607
|
existing.push(entry);
|
|
6571
6608
|
}
|
|
6572
|
-
writeFile(
|
|
6609
|
+
writeFile(path8, stringifyYaml2(existing));
|
|
6573
6610
|
}
|
|
6574
6611
|
function isIgnored(ignores, artifactId) {
|
|
6575
6612
|
return ignores.find((e) => e.artifact_id === artifactId);
|
|
6576
6613
|
}
|
|
6577
6614
|
function removeIgnore(dir, artifactId) {
|
|
6578
|
-
const
|
|
6615
|
+
const path8 = join(dir, IGNORE_FILE);
|
|
6579
6616
|
const existing = loadIgnores(dir);
|
|
6580
6617
|
const filtered = existing.filter((e) => e.artifact_id !== artifactId);
|
|
6581
6618
|
if (filtered.length === existing.length) return false;
|
|
6582
|
-
writeFile(
|
|
6619
|
+
writeFile(path8, stringifyYaml2(filtered));
|
|
6583
6620
|
return true;
|
|
6584
6621
|
}
|
|
6585
6622
|
|
|
@@ -6601,11 +6638,11 @@ function moduleArtifactCoverage(dir, id) {
|
|
|
6601
6638
|
};
|
|
6602
6639
|
}
|
|
6603
6640
|
function loadMappedModules(dir) {
|
|
6604
|
-
const
|
|
6605
|
-
if (!exists(
|
|
6641
|
+
const path8 = join(dir, DESCRIPTOR_PATH);
|
|
6642
|
+
if (!exists(path8)) return [];
|
|
6606
6643
|
let parsed;
|
|
6607
6644
|
try {
|
|
6608
|
-
parsed = parseYaml5(readFile(
|
|
6645
|
+
parsed = parseYaml5(readFile(path8));
|
|
6609
6646
|
} catch {
|
|
6610
6647
|
return [];
|
|
6611
6648
|
}
|
|
@@ -6619,7 +6656,7 @@ function loadMappedModules(dir) {
|
|
|
6619
6656
|
modules.push({
|
|
6620
6657
|
id,
|
|
6621
6658
|
name: typeof m.name === "string" ? m.name : void 0,
|
|
6622
|
-
repoPath: typeof m.repoPath === "string" ? m.repoPath : "",
|
|
6659
|
+
repoPath: typeof m.repoPath === "string" ? m.repoPath : typeof m.path === "string" ? m.path : "",
|
|
6623
6660
|
type: typeof m.type === "string" ? m.type : void 0,
|
|
6624
6661
|
mainTechnology: typeof m.mainTechnology === "string" ? m.mainTechnology : void 0,
|
|
6625
6662
|
owner: typeof m.owner === "string" ? m.owner : void 0,
|
|
@@ -6668,13 +6705,15 @@ function detectModuleStatus(coreDir, repoPath) {
|
|
|
6668
6705
|
status: "configured",
|
|
6669
6706
|
configured: true,
|
|
6670
6707
|
role: "module",
|
|
6671
|
-
moduleContext: exists(join(moduleDir2, "knowledge", "module", "module-context.md")),
|
|
6708
|
+
moduleContext: exists(join(moduleDir2, "knowledge", "tech", "module", "module-context.md")) || exists(join(moduleDir2, "knowledge", "module", "module-context.md")),
|
|
6672
6709
|
currentState: exists(join(moduleDir2, "knowledge", "tech", "current-state.md")),
|
|
6673
6710
|
codebase: exists(join(moduleDir2, "knowledge", "tech", "codebase.md"))
|
|
6674
6711
|
};
|
|
6675
6712
|
}
|
|
6676
6713
|
function readModuleContext(coreDir, repoPath) {
|
|
6677
|
-
const
|
|
6714
|
+
const newPath = join(coreDir, repoPath, "knowledge", "tech", "module", "module-context.md");
|
|
6715
|
+
const legacyPath = join(coreDir, repoPath, "knowledge", "module", "module-context.md");
|
|
6716
|
+
const p2 = exists(newPath) ? newPath : legacyPath;
|
|
6678
6717
|
if (!exists(p2)) return null;
|
|
6679
6718
|
try {
|
|
6680
6719
|
return readFile(p2);
|
|
@@ -7085,7 +7124,7 @@ function buildGuardHistory(dir) {
|
|
|
7085
7124
|
const d = hotspotDir(t.code_path);
|
|
7086
7125
|
hotspotMap.set(d, (hotspotMap.get(d) ?? 0) + 1);
|
|
7087
7126
|
}
|
|
7088
|
-
const hotspots = [...hotspotMap.entries()].map(([
|
|
7127
|
+
const hotspots = [...hotspotMap.entries()].map(([path8, warnings]) => ({ path: path8, warnings })).sort((a, b) => b.warnings - a.warnings);
|
|
7089
7128
|
return {
|
|
7090
7129
|
available: true,
|
|
7091
7130
|
total_runs: sorted.length,
|
|
@@ -7487,7 +7526,7 @@ function runIgnoreRemove(artifactId) {
|
|
|
7487
7526
|
|
|
7488
7527
|
// src/commands/explain.ts
|
|
7489
7528
|
import matter6 from "gray-matter";
|
|
7490
|
-
import { parse as
|
|
7529
|
+
import { parse as parseYaml12 } from "yaml";
|
|
7491
7530
|
|
|
7492
7531
|
// src/core/delivery-phase.ts
|
|
7493
7532
|
function layer(layers, name) {
|
|
@@ -7653,10 +7692,10 @@ function assessPhase(input) {
|
|
|
7653
7692
|
|
|
7654
7693
|
// src/core/capsule.ts
|
|
7655
7694
|
import matter3 from "gray-matter";
|
|
7656
|
-
import { parse as
|
|
7695
|
+
import { parse as parseYaml10, stringify as stringifyYaml5 } from "yaml";
|
|
7657
7696
|
|
|
7658
7697
|
// src/commands/modules-map.ts
|
|
7659
|
-
import { parse as
|
|
7698
|
+
import { parse as parseYaml9, stringify as stringifyYaml4 } from "yaml";
|
|
7660
7699
|
|
|
7661
7700
|
// src/templates/registry.ts
|
|
7662
7701
|
var QUALITY = "## Quality checklist";
|
|
@@ -9143,9 +9182,373 @@ function getTemplate(id) {
|
|
|
9143
9182
|
return KADDO_TEMPLATES.find((t) => t.id === id);
|
|
9144
9183
|
}
|
|
9145
9184
|
|
|
9185
|
+
// src/commands/modules-discover.ts
|
|
9186
|
+
import path5 from "path";
|
|
9187
|
+
import { parse as parseYaml8, stringify as stringifyYaml3 } from "yaml";
|
|
9188
|
+
var CONFIG_PATH3 = ".kaddo/config.yml";
|
|
9189
|
+
var MODULES_YML = ".kaddo/modules.yml";
|
|
9190
|
+
var MODULES_MD = "knowledge/tech/modules/modules.md";
|
|
9191
|
+
var IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
9192
|
+
".git",
|
|
9193
|
+
"node_modules",
|
|
9194
|
+
"dist",
|
|
9195
|
+
"build",
|
|
9196
|
+
"coverage",
|
|
9197
|
+
"vendor",
|
|
9198
|
+
".next",
|
|
9199
|
+
"out",
|
|
9200
|
+
"tmp",
|
|
9201
|
+
"temp",
|
|
9202
|
+
".kaddo"
|
|
9203
|
+
]);
|
|
9204
|
+
function readModulesYml(dir) {
|
|
9205
|
+
const p2 = join(dir, MODULES_YML);
|
|
9206
|
+
if (!exists(p2)) return { version: 1, system: "", workspace_roots: [".."], modules: [] };
|
|
9207
|
+
try {
|
|
9208
|
+
const parsed = parseYaml8(readFile(p2));
|
|
9209
|
+
return {
|
|
9210
|
+
version: parsed.version ?? 1,
|
|
9211
|
+
system: parsed.system ?? "",
|
|
9212
|
+
workspace_roots: parsed.workspace_roots ?? [".."],
|
|
9213
|
+
modules: Array.isArray(parsed.modules) ? parsed.modules : []
|
|
9214
|
+
};
|
|
9215
|
+
} catch {
|
|
9216
|
+
return { version: 1, system: "", workspace_roots: [".."], modules: [] };
|
|
9217
|
+
}
|
|
9218
|
+
}
|
|
9219
|
+
function writeModulesYml(dir, yml) {
|
|
9220
|
+
writeFile(join(dir, MODULES_YML), stringifyYaml3(yml));
|
|
9221
|
+
}
|
|
9222
|
+
function readRepoConfig(repoDir) {
|
|
9223
|
+
const configPath = join(repoDir, CONFIG_PATH3);
|
|
9224
|
+
if (!exists(configPath)) return null;
|
|
9225
|
+
try {
|
|
9226
|
+
return parseYaml8(readFile(configPath));
|
|
9227
|
+
} catch {
|
|
9228
|
+
return null;
|
|
9229
|
+
}
|
|
9230
|
+
}
|
|
9231
|
+
function hasKnowledgeFile(repoDir, ...segments) {
|
|
9232
|
+
return exists(join(repoDir, "knowledge", ...segments));
|
|
9233
|
+
}
|
|
9234
|
+
function hasModuleContext(repoDir) {
|
|
9235
|
+
return hasKnowledgeFile(repoDir, "tech", "module", "module-context.md") || hasKnowledgeFile(repoDir, "module", "module-context.md");
|
|
9236
|
+
}
|
|
9237
|
+
function hasCurrentState(repoDir) {
|
|
9238
|
+
return hasKnowledgeFile(repoDir, "tech", "current-state.md");
|
|
9239
|
+
}
|
|
9240
|
+
function hasCodebase(repoDir) {
|
|
9241
|
+
return hasKnowledgeFile(repoDir, "tech", "codebase.md");
|
|
9242
|
+
}
|
|
9243
|
+
function buildModuleContextPaths(repoDir) {
|
|
9244
|
+
const ctx = {};
|
|
9245
|
+
if (hasKnowledgeFile(repoDir, "tech", "module", "module-context.md")) {
|
|
9246
|
+
ctx.module = "knowledge/tech/module/module-context.md";
|
|
9247
|
+
} else if (hasKnowledgeFile(repoDir, "module", "module-context.md")) {
|
|
9248
|
+
ctx.module = "knowledge/module/module-context.md";
|
|
9249
|
+
}
|
|
9250
|
+
if (hasCurrentState(repoDir)) ctx.current_state = "knowledge/tech/current-state.md";
|
|
9251
|
+
if (hasCodebase(repoDir)) ctx.codebase = "knowledge/tech/codebase.md";
|
|
9252
|
+
return ctx;
|
|
9253
|
+
}
|
|
9254
|
+
function discoverModules(dir) {
|
|
9255
|
+
const config = loadConfig(dir);
|
|
9256
|
+
if (!config || !isCore(config)) return [];
|
|
9257
|
+
const sysName = systemName(config) ?? config.project.name;
|
|
9258
|
+
const roots = workspaceRoots(config);
|
|
9259
|
+
const existing = readModulesYml(dir);
|
|
9260
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
9261
|
+
const seenPaths = /* @__PURE__ */ new Set();
|
|
9262
|
+
const results = [];
|
|
9263
|
+
const coreAbsPath = path5.resolve(dir);
|
|
9264
|
+
for (const root of roots) {
|
|
9265
|
+
const rootAbs = path5.resolve(dir, root);
|
|
9266
|
+
if (!exists(rootAbs) || !isDir(rootAbs)) continue;
|
|
9267
|
+
let entries;
|
|
9268
|
+
try {
|
|
9269
|
+
entries = readDir(rootAbs);
|
|
9270
|
+
} catch {
|
|
9271
|
+
continue;
|
|
9272
|
+
}
|
|
9273
|
+
for (const entry of entries) {
|
|
9274
|
+
if (IGNORED_DIRS.has(entry)) continue;
|
|
9275
|
+
const candidateAbs = path5.resolve(rootAbs, entry);
|
|
9276
|
+
if (!isDir(candidateAbs)) continue;
|
|
9277
|
+
if (candidateAbs === coreAbsPath) continue;
|
|
9278
|
+
const relPath = path5.relative(dir, candidateAbs).replace(/\\/g, "/");
|
|
9279
|
+
const repoConfig = readRepoConfig(candidateAbs);
|
|
9280
|
+
if (!repoConfig) {
|
|
9281
|
+
results.push({
|
|
9282
|
+
id: entry,
|
|
9283
|
+
name: entry,
|
|
9284
|
+
path: relPath.startsWith(".") ? relPath : `./${relPath}`,
|
|
9285
|
+
parentSystem: "",
|
|
9286
|
+
status: "not_configured",
|
|
9287
|
+
eligibleForMapping: false,
|
|
9288
|
+
moduleContext: false,
|
|
9289
|
+
currentState: false,
|
|
9290
|
+
codebase: false
|
|
9291
|
+
});
|
|
9292
|
+
continue;
|
|
9293
|
+
}
|
|
9294
|
+
const project = repoConfig.project;
|
|
9295
|
+
const multirepo = repoConfig.multirepo;
|
|
9296
|
+
const module = repoConfig.module;
|
|
9297
|
+
const role = project?.role ?? multirepo?.role;
|
|
9298
|
+
if (role !== "module") {
|
|
9299
|
+
results.push({
|
|
9300
|
+
id: entry,
|
|
9301
|
+
name: project?.name ?? entry,
|
|
9302
|
+
path: relPath.startsWith(".") ? relPath : `./${relPath}`,
|
|
9303
|
+
parentSystem: "",
|
|
9304
|
+
status: "invalid",
|
|
9305
|
+
eligibleForMapping: false,
|
|
9306
|
+
warning: "Repository has Kaddo but is not configured as a module.",
|
|
9307
|
+
moduleContext: false,
|
|
9308
|
+
currentState: false,
|
|
9309
|
+
codebase: false
|
|
9310
|
+
});
|
|
9311
|
+
continue;
|
|
9312
|
+
}
|
|
9313
|
+
const modId = module?.id ?? entry;
|
|
9314
|
+
const modParent = multirepo?.parent_system ?? "";
|
|
9315
|
+
const modName = project?.name ?? entry;
|
|
9316
|
+
const modPath = relPath.startsWith(".") ? relPath : `./${relPath}`;
|
|
9317
|
+
if (modParent && modParent !== sysName) {
|
|
9318
|
+
results.push({
|
|
9319
|
+
id: modId,
|
|
9320
|
+
name: modName,
|
|
9321
|
+
path: modPath,
|
|
9322
|
+
parentSystem: modParent,
|
|
9323
|
+
status: "foreign_system",
|
|
9324
|
+
eligibleForMapping: false,
|
|
9325
|
+
warning: `parent system: ${modParent}`,
|
|
9326
|
+
moduleContext: hasModuleContext(candidateAbs),
|
|
9327
|
+
currentState: hasCurrentState(candidateAbs),
|
|
9328
|
+
codebase: hasCodebase(candidateAbs)
|
|
9329
|
+
});
|
|
9330
|
+
continue;
|
|
9331
|
+
}
|
|
9332
|
+
let status = "configured";
|
|
9333
|
+
let warning;
|
|
9334
|
+
let eligible = true;
|
|
9335
|
+
if (seenIds.has(modId)) {
|
|
9336
|
+
status = "duplicate";
|
|
9337
|
+
warning = `duplicate module id: ${modId}`;
|
|
9338
|
+
eligible = false;
|
|
9339
|
+
} else if (seenPaths.has(modPath)) {
|
|
9340
|
+
status = "duplicate";
|
|
9341
|
+
warning = `duplicate path: ${modPath}`;
|
|
9342
|
+
eligible = false;
|
|
9343
|
+
}
|
|
9344
|
+
seenIds.add(modId);
|
|
9345
|
+
seenPaths.add(modPath);
|
|
9346
|
+
results.push({
|
|
9347
|
+
id: modId,
|
|
9348
|
+
name: modName,
|
|
9349
|
+
path: modPath,
|
|
9350
|
+
parentSystem: modParent || sysName,
|
|
9351
|
+
status,
|
|
9352
|
+
eligibleForMapping: eligible,
|
|
9353
|
+
warning,
|
|
9354
|
+
moduleContext: hasModuleContext(candidateAbs),
|
|
9355
|
+
currentState: hasCurrentState(candidateAbs),
|
|
9356
|
+
codebase: hasCodebase(candidateAbs)
|
|
9357
|
+
});
|
|
9358
|
+
}
|
|
9359
|
+
}
|
|
9360
|
+
for (const m of existing.modules) {
|
|
9361
|
+
const absPath = path5.resolve(dir, m.path);
|
|
9362
|
+
if (!exists(absPath) && !results.some((r) => r.path === m.path)) {
|
|
9363
|
+
results.push({
|
|
9364
|
+
id: m.id,
|
|
9365
|
+
name: m.name,
|
|
9366
|
+
path: m.path,
|
|
9367
|
+
parentSystem: m.parent_system,
|
|
9368
|
+
status: "missing",
|
|
9369
|
+
eligibleForMapping: false,
|
|
9370
|
+
warning: "Previously registered but path no longer exists.",
|
|
9371
|
+
moduleContext: false,
|
|
9372
|
+
currentState: false,
|
|
9373
|
+
codebase: false
|
|
9374
|
+
});
|
|
9375
|
+
}
|
|
9376
|
+
}
|
|
9377
|
+
return results;
|
|
9378
|
+
}
|
|
9379
|
+
function applyDiscovery(dir, discovered) {
|
|
9380
|
+
const config = loadConfig(dir);
|
|
9381
|
+
const sysName = systemName(config) ?? config.project.name;
|
|
9382
|
+
const yml = readModulesYml(dir);
|
|
9383
|
+
yml.system = sysName;
|
|
9384
|
+
const eligible = discovered.filter((d) => d.eligibleForMapping && d.status === "configured");
|
|
9385
|
+
for (const d of eligible) {
|
|
9386
|
+
const existingIdx = yml.modules.findIndex((m) => m.id === d.id);
|
|
9387
|
+
const entry = {
|
|
9388
|
+
id: d.id,
|
|
9389
|
+
name: d.name,
|
|
9390
|
+
path: d.path,
|
|
9391
|
+
parent_system: d.parentSystem,
|
|
9392
|
+
status: d.status,
|
|
9393
|
+
context: buildModuleContextPaths(path5.resolve(dir, d.path))
|
|
9394
|
+
};
|
|
9395
|
+
if (existingIdx >= 0) {
|
|
9396
|
+
yml.modules[existingIdx] = entry;
|
|
9397
|
+
} else {
|
|
9398
|
+
yml.modules.push(entry);
|
|
9399
|
+
}
|
|
9400
|
+
}
|
|
9401
|
+
writeModulesYml(dir, yml);
|
|
9402
|
+
updateModulesMd(dir, yml);
|
|
9403
|
+
return { mapped: eligible.length };
|
|
9404
|
+
}
|
|
9405
|
+
function updateModulesMd(dir, yml) {
|
|
9406
|
+
const lines = [
|
|
9407
|
+
"---",
|
|
9408
|
+
"type: modules-map",
|
|
9409
|
+
`system: ${yml.system}`,
|
|
9410
|
+
"generated_by: kaddo-modules-discover",
|
|
9411
|
+
"template_version: 2",
|
|
9412
|
+
"---",
|
|
9413
|
+
"",
|
|
9414
|
+
`# ${yml.system} \u2014 Modules`,
|
|
9415
|
+
""
|
|
9416
|
+
];
|
|
9417
|
+
if (yml.modules.length === 0) {
|
|
9418
|
+
lines.push("_No modules mapped yet. Run `kaddo modules discover` to find sibling modules._");
|
|
9419
|
+
} else {
|
|
9420
|
+
for (const m of yml.modules) {
|
|
9421
|
+
lines.push(`## ${m.id}`);
|
|
9422
|
+
lines.push("");
|
|
9423
|
+
lines.push(`- Repository: \`${m.path}\``);
|
|
9424
|
+
lines.push(`- Status: ${m.status}`);
|
|
9425
|
+
lines.push(`- Parent system: ${m.parent_system}`);
|
|
9426
|
+
const ctxPaths = m.context ?? {};
|
|
9427
|
+
lines.push(`- Module context: ${ctxPaths.module ? "available" : "missing"}`);
|
|
9428
|
+
lines.push("");
|
|
9429
|
+
}
|
|
9430
|
+
}
|
|
9431
|
+
writeFile(join(dir, MODULES_MD), lines.join("\n"));
|
|
9432
|
+
}
|
|
9433
|
+
function runModulesValidate(dir = cwd()) {
|
|
9434
|
+
const yml = readModulesYml(dir);
|
|
9435
|
+
if (yml.modules.length === 0) {
|
|
9436
|
+
console.log("No modules registered in .kaddo/modules.yml.");
|
|
9437
|
+
return;
|
|
9438
|
+
}
|
|
9439
|
+
const config = loadConfig(dir);
|
|
9440
|
+
const sysName = systemName(config) ?? config.project.name;
|
|
9441
|
+
const legacy = detectLegacyPaths(dir);
|
|
9442
|
+
if (legacy.length > 0) {
|
|
9443
|
+
console.log("");
|
|
9444
|
+
console.log("Legacy multirepo knowledge paths detected.");
|
|
9445
|
+
console.log("");
|
|
9446
|
+
console.log("Recommended migration:");
|
|
9447
|
+
for (const l of legacy) {
|
|
9448
|
+
console.log(`- ${l.from}`);
|
|
9449
|
+
console.log(` \u2192 ${l.to}`);
|
|
9450
|
+
console.log("");
|
|
9451
|
+
}
|
|
9452
|
+
}
|
|
9453
|
+
console.log("");
|
|
9454
|
+
for (const m of yml.modules) {
|
|
9455
|
+
const absPath = path5.resolve(dir, m.path);
|
|
9456
|
+
if (!exists(absPath)) {
|
|
9457
|
+
console.log(`${m.id.padEnd(16)} missing`);
|
|
9458
|
+
continue;
|
|
9459
|
+
}
|
|
9460
|
+
const repoConfig = readRepoConfig(absPath);
|
|
9461
|
+
if (!repoConfig) {
|
|
9462
|
+
console.log(`${m.id.padEnd(16)} not_configured`);
|
|
9463
|
+
continue;
|
|
9464
|
+
}
|
|
9465
|
+
const project = repoConfig.project;
|
|
9466
|
+
const multirepo = repoConfig.multirepo;
|
|
9467
|
+
const role = project?.role ?? multirepo?.role;
|
|
9468
|
+
if (role !== "module") {
|
|
9469
|
+
console.log(`${m.id.padEnd(16)} invalid`);
|
|
9470
|
+
continue;
|
|
9471
|
+
}
|
|
9472
|
+
const modParent = multirepo?.parent_system;
|
|
9473
|
+
if (modParent && modParent !== sysName) {
|
|
9474
|
+
console.log(`${m.id.padEnd(16)} foreign_system`);
|
|
9475
|
+
continue;
|
|
9476
|
+
}
|
|
9477
|
+
console.log(`${m.id.padEnd(16)} valid`);
|
|
9478
|
+
}
|
|
9479
|
+
console.log("");
|
|
9480
|
+
}
|
|
9481
|
+
function detectLegacyPaths(dir) {
|
|
9482
|
+
const migrations = [];
|
|
9483
|
+
if (exists(join(dir, "knowledge", "system", "system-context.md"))) {
|
|
9484
|
+
migrations.push({ from: "knowledge/system/system-context.md", to: "knowledge/tech/system/system-context.md" });
|
|
9485
|
+
}
|
|
9486
|
+
if (exists(join(dir, "knowledge", "modules", "modules.md"))) {
|
|
9487
|
+
migrations.push({ from: "knowledge/modules/modules.md", to: "knowledge/tech/modules/modules.md" });
|
|
9488
|
+
}
|
|
9489
|
+
if (exists(join(dir, "knowledge", "module", "module-context.md"))) {
|
|
9490
|
+
migrations.push({ from: "knowledge/module/module-context.md", to: "knowledge/tech/module/module-context.md" });
|
|
9491
|
+
}
|
|
9492
|
+
return migrations;
|
|
9493
|
+
}
|
|
9494
|
+
async function runModulesDiscover(opts = {}) {
|
|
9495
|
+
const dir = cwd();
|
|
9496
|
+
intro2("kaddo modules discover");
|
|
9497
|
+
const config = loadConfig(dir);
|
|
9498
|
+
if (!config) {
|
|
9499
|
+
console.error("Kaddo is not initialized. Run `kaddo init` first.");
|
|
9500
|
+
process.exit(1);
|
|
9501
|
+
}
|
|
9502
|
+
if (!isCore(config)) {
|
|
9503
|
+
console.error("Module discovery is only available from a core repository.");
|
|
9504
|
+
process.exit(1);
|
|
9505
|
+
}
|
|
9506
|
+
const discovered = discoverModules(dir);
|
|
9507
|
+
if (discovered.length === 0) {
|
|
9508
|
+
log2.info("No sibling repositories found in workspace roots.");
|
|
9509
|
+
outro2("Discovery complete.");
|
|
9510
|
+
return;
|
|
9511
|
+
}
|
|
9512
|
+
console.log("");
|
|
9513
|
+
console.log("Workspace module discovery");
|
|
9514
|
+
console.log("");
|
|
9515
|
+
for (const d of discovered) {
|
|
9516
|
+
const icon = d.status === "configured" ? "\u2713" : d.status === "not_configured" ? "\u25CB" : d.status === "foreign_system" ? "!" : d.status === "duplicate" ? "!" : d.status === "missing" ? "\u2717" : "\u2717";
|
|
9517
|
+
console.log(`${icon} ${d.id}`);
|
|
9518
|
+
console.log(` ${d.path}`);
|
|
9519
|
+
console.log(` status: ${d.status}`);
|
|
9520
|
+
if (d.warning) console.log(` ${d.warning}`);
|
|
9521
|
+
console.log("");
|
|
9522
|
+
}
|
|
9523
|
+
const eligible = discovered.filter((d) => d.eligibleForMapping && d.status === "configured");
|
|
9524
|
+
console.log(`Eligible for mapping: ${eligible.length}`);
|
|
9525
|
+
if (eligible.length === 0) {
|
|
9526
|
+
outro2("Discovery complete.");
|
|
9527
|
+
return;
|
|
9528
|
+
}
|
|
9529
|
+
if (opts.apply) {
|
|
9530
|
+
const confirmed = await confirm2({
|
|
9531
|
+
message: `Map ${eligible.length} configured Kaddo modules?`,
|
|
9532
|
+
initialValue: true
|
|
9533
|
+
});
|
|
9534
|
+
if (!confirmed) {
|
|
9535
|
+
outro2("No changes applied.");
|
|
9536
|
+
return;
|
|
9537
|
+
}
|
|
9538
|
+
const result = applyDiscovery(dir, discovered);
|
|
9539
|
+
log2.success(`Mapped ${result.mapped} configured modules.`);
|
|
9540
|
+
log2.success("Updated .kaddo/modules.yml.");
|
|
9541
|
+
log2.success("Updated knowledge/tech/modules/modules.md.");
|
|
9542
|
+
outro2("Discovery applied.");
|
|
9543
|
+
} else {
|
|
9544
|
+
log2.info("Run `kaddo modules discover --apply` to persist eligible modules.");
|
|
9545
|
+
outro2("Discovery complete.");
|
|
9546
|
+
}
|
|
9547
|
+
}
|
|
9548
|
+
|
|
9146
9549
|
// src/commands/modules-map.ts
|
|
9147
9550
|
var DESCRIPTOR_PATH2 = ".kaddo/modules.yml";
|
|
9148
|
-
var
|
|
9551
|
+
var CONFIG_PATH4 = ".kaddo/config.yml";
|
|
9149
9552
|
var MODULE_TYPES = [
|
|
9150
9553
|
"frontend",
|
|
9151
9554
|
"backend",
|
|
@@ -9160,17 +9563,17 @@ function slugify2(name) {
|
|
|
9160
9563
|
return name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
9161
9564
|
}
|
|
9162
9565
|
function readModulesDescriptor(dir) {
|
|
9163
|
-
const
|
|
9164
|
-
if (!exists(
|
|
9566
|
+
const path8 = join(dir, DESCRIPTOR_PATH2);
|
|
9567
|
+
if (!exists(path8)) return { version: 1, modules: [] };
|
|
9165
9568
|
try {
|
|
9166
|
-
const parsed =
|
|
9569
|
+
const parsed = parseYaml9(readFile(path8));
|
|
9167
9570
|
return { version: parsed.version ?? 1, modules: parsed.modules ?? [] };
|
|
9168
9571
|
} catch {
|
|
9169
9572
|
return { version: 1, modules: [] };
|
|
9170
9573
|
}
|
|
9171
9574
|
}
|
|
9172
9575
|
function writeModulesDescriptor(dir, descriptor) {
|
|
9173
|
-
writeFile(join(dir, DESCRIPTOR_PATH2),
|
|
9576
|
+
writeFile(join(dir, DESCRIPTOR_PATH2), stringifyYaml4(descriptor));
|
|
9174
9577
|
}
|
|
9175
9578
|
function moduleDir(id) {
|
|
9176
9579
|
return `knowledge/tech/modules/${id}`;
|
|
@@ -9301,70 +9704,74 @@ function mapModule(dir, input) {
|
|
|
9301
9704
|
}
|
|
9302
9705
|
return { module: mod, written, skipped, alreadyRegistered };
|
|
9303
9706
|
}
|
|
9304
|
-
async function runModulesMap(dir = cwd()) {
|
|
9707
|
+
async function runModulesMap(dir = cwd(), modulePath) {
|
|
9305
9708
|
intro2("kaddo modules map");
|
|
9306
|
-
if (!exists(join(dir,
|
|
9709
|
+
if (!exists(join(dir, CONFIG_PATH4))) {
|
|
9307
9710
|
console.error("Kaddo is not initialized in this project.");
|
|
9308
9711
|
console.error("Run `kaddo init` first.");
|
|
9309
9712
|
process.exit(1);
|
|
9310
9713
|
}
|
|
9311
|
-
|
|
9714
|
+
let repoPath;
|
|
9715
|
+
if (modulePath) {
|
|
9716
|
+
repoPath = modulePath;
|
|
9717
|
+
} else {
|
|
9718
|
+
repoPath = await text2({
|
|
9719
|
+
message: "Repository path (relative to this repo)",
|
|
9720
|
+
placeholder: "e.g. ../frontend",
|
|
9721
|
+
validate: (v) => v.trim().length === 0 ? "Repository path is required." : void 0
|
|
9722
|
+
});
|
|
9723
|
+
}
|
|
9724
|
+
const targetDir = join(dir, repoPath.trim());
|
|
9725
|
+
if (!exists(targetDir)) {
|
|
9726
|
+
log2.warn(`Path "${repoPath.trim()}" does not exist.`);
|
|
9727
|
+
} else {
|
|
9728
|
+
const st = detectModuleStatus(dir, repoPath.trim());
|
|
9729
|
+
if (st.status === "not_configured") {
|
|
9730
|
+
log2.warn(`${repoPath.trim()} does not have Kaddo configured. Run \`kaddo init\` inside that repository first.`);
|
|
9731
|
+
} else if (st.status === "invalid") {
|
|
9732
|
+
log2.warn(`${repoPath.trim()} has Kaddo but is not configured as a module.`);
|
|
9733
|
+
}
|
|
9734
|
+
}
|
|
9312
9735
|
const name = await text2({
|
|
9313
9736
|
message: "Module name",
|
|
9314
9737
|
placeholder: "e.g. Frontend Web",
|
|
9315
9738
|
validate: (v) => v.trim().length === 0 ? "Name is required." : void 0
|
|
9316
9739
|
});
|
|
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
9740
|
const type = await select2({
|
|
9323
9741
|
message: "Module type",
|
|
9324
9742
|
options: MODULE_TYPES.map((t) => ({ value: t, label: t }))
|
|
9325
9743
|
});
|
|
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
9744
|
const result = mapModule(dir, {
|
|
9342
9745
|
name,
|
|
9343
9746
|
repoPath,
|
|
9344
|
-
type
|
|
9345
|
-
mainTechnology,
|
|
9346
|
-
owner,
|
|
9347
|
-
capabilities: capabilitiesRaw.split(",").map((c) => c.trim()).filter(Boolean)
|
|
9747
|
+
type
|
|
9348
9748
|
});
|
|
9349
9749
|
for (const p2 of result.written) log2.success(`Created ${p2}`);
|
|
9350
9750
|
for (const p2 of result.skipped) log2.info(`Kept existing ${p2}`);
|
|
9351
9751
|
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
|
-
);
|
|
9752
|
+
outro2("Module mapped.");
|
|
9355
9753
|
}
|
|
9356
9754
|
function runModulesList(dir = cwd()) {
|
|
9357
|
-
const
|
|
9358
|
-
if (
|
|
9359
|
-
console.log("
|
|
9755
|
+
const yml = readModulesYml(dir);
|
|
9756
|
+
if (yml.modules.length === 0) {
|
|
9757
|
+
console.log("");
|
|
9758
|
+
console.log("No modules are mapped for this core repository.");
|
|
9759
|
+
console.log("");
|
|
9760
|
+
console.log("Run:");
|
|
9761
|
+
console.log(" kaddo modules discover");
|
|
9762
|
+
console.log("");
|
|
9763
|
+
console.log("Or map one manually:");
|
|
9764
|
+
console.log(" kaddo modules map <path>");
|
|
9765
|
+
console.log("");
|
|
9360
9766
|
return;
|
|
9361
9767
|
}
|
|
9362
9768
|
console.log("");
|
|
9363
9769
|
console.log("Mapped modules:");
|
|
9364
|
-
for (const m of
|
|
9365
|
-
const
|
|
9770
|
+
for (const m of yml.modules) {
|
|
9771
|
+
const mPath = m.path ?? m.repoPath ?? "";
|
|
9772
|
+
const st = detectModuleStatus(dir, mPath);
|
|
9366
9773
|
const statusLabel = st.status === "configured" ? "\u2713 configured" : st.status === "not_configured" ? "\u25CB not configured" : "\u2717 invalid";
|
|
9367
|
-
console.log(` ${m.id.padEnd(16)} ${
|
|
9774
|
+
console.log(` ${m.id.padEnd(16)} ${statusLabel.padEnd(18)} ${mPath}`);
|
|
9368
9775
|
if (st.warning) console.log(` \u26A0 ${st.warning}`);
|
|
9369
9776
|
}
|
|
9370
9777
|
console.log("");
|
|
@@ -9505,14 +9912,14 @@ function loadExternalRegistry(dir) {
|
|
|
9505
9912
|
const p2 = join(dir, EXTERNAL_PATH);
|
|
9506
9913
|
if (!exists(p2)) return [];
|
|
9507
9914
|
try {
|
|
9508
|
-
const parsed =
|
|
9915
|
+
const parsed = parseYaml10(readFile(p2));
|
|
9509
9916
|
return Array.isArray(parsed?.external) ? parsed.external : [];
|
|
9510
9917
|
} catch {
|
|
9511
9918
|
return [];
|
|
9512
9919
|
}
|
|
9513
9920
|
}
|
|
9514
9921
|
function serializeExternalRegistry(entries) {
|
|
9515
|
-
return
|
|
9922
|
+
return stringifyYaml5({ external: entries });
|
|
9516
9923
|
}
|
|
9517
9924
|
function sectionList(md, title) {
|
|
9518
9925
|
const re = new RegExp(`^##\\s+${title}\\s*$`, "im");
|
|
@@ -9539,7 +9946,7 @@ function sectionParagraph(md, title) {
|
|
|
9539
9946
|
}
|
|
9540
9947
|
return "";
|
|
9541
9948
|
}
|
|
9542
|
-
function parseCapsule(id,
|
|
9949
|
+
function parseCapsule(id, path8, md) {
|
|
9543
9950
|
const { data } = matter3(md);
|
|
9544
9951
|
const updatedAt = data.updated_at ? String(data.updated_at) : void 0;
|
|
9545
9952
|
let ageDays = null;
|
|
@@ -9549,7 +9956,7 @@ function parseCapsule(id, path7, md) {
|
|
|
9549
9956
|
}
|
|
9550
9957
|
return {
|
|
9551
9958
|
id,
|
|
9552
|
-
path:
|
|
9959
|
+
path: path8,
|
|
9553
9960
|
system: data.system ? String(data.system) : id,
|
|
9554
9961
|
owner: data.owner ? String(data.owner) : void 0,
|
|
9555
9962
|
updatedAt,
|
|
@@ -9635,9 +10042,9 @@ function buildGraph(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
9635
10042
|
];
|
|
9636
10043
|
const presentLayers = [];
|
|
9637
10044
|
for (const layer2 of layerDocs) {
|
|
9638
|
-
const
|
|
9639
|
-
if (
|
|
9640
|
-
addNode({ id: layer2.id, type: layer2.type, label: layer2.label, path:
|
|
10045
|
+
const path8 = layer2.files.map((f) => `${KNOWLEDGE3}/${f}`).find((rel) => exists(join(dir, rel)));
|
|
10046
|
+
if (path8) {
|
|
10047
|
+
addNode({ id: layer2.id, type: layer2.type, label: layer2.label, path: path8 });
|
|
9641
10048
|
presentLayers.push(layer2.id);
|
|
9642
10049
|
}
|
|
9643
10050
|
}
|
|
@@ -10965,6 +11372,9 @@ function buildSharedFileStatuses(statuses) {
|
|
|
10965
11372
|
}));
|
|
10966
11373
|
}
|
|
10967
11374
|
|
|
11375
|
+
// src/core/next-step.ts
|
|
11376
|
+
import { parse as parseYaml11 } from "yaml";
|
|
11377
|
+
|
|
10968
11378
|
// src/core/decisions.ts
|
|
10969
11379
|
var CANDIDATES_DISCOVERY = "knowledge/tech/discovery/decision-candidates.md";
|
|
10970
11380
|
var CANDIDATES_LEGACY = "knowledge/tech/decision-candidates.md";
|
|
@@ -11069,6 +11479,7 @@ function buildDeliveryState(dir) {
|
|
|
11069
11479
|
refined_draft_ids: wis.filter((w) => w.lifecycle === "draft" && w.rawFrontmatter.refined_by).map((w) => w.id),
|
|
11070
11480
|
ready_work_items: byState("ready"),
|
|
11071
11481
|
ready_work_item_ids: wis.filter((w) => w.lifecycle === "ready").map((w) => w.id),
|
|
11482
|
+
ready_multirepo_wi_ids: wis.filter((w) => w.lifecycle === "ready" && Array.isArray(w.rawFrontmatter?.affected_modules) && w.rawFrontmatter.affected_modules.length > 0).map((w) => w.id),
|
|
11072
11483
|
in_progress_work_items: byState("in-progress"),
|
|
11073
11484
|
blocked_work_items: byState("blocked"),
|
|
11074
11485
|
total_work_items: total,
|
|
@@ -11079,6 +11490,14 @@ function buildDeliveryState(dir) {
|
|
|
11079
11490
|
adapters_installed: adapters.length
|
|
11080
11491
|
};
|
|
11081
11492
|
}
|
|
11493
|
+
function hasRegisteredModules(modulesYmlPath) {
|
|
11494
|
+
try {
|
|
11495
|
+
const parsed = parseYaml11(readFile(modulesYmlPath));
|
|
11496
|
+
return Array.isArray(parsed?.modules) && parsed.modules.length > 0;
|
|
11497
|
+
} catch {
|
|
11498
|
+
return false;
|
|
11499
|
+
}
|
|
11500
|
+
}
|
|
11082
11501
|
function safeRead(p2) {
|
|
11083
11502
|
try {
|
|
11084
11503
|
return readFile(p2);
|
|
@@ -11129,11 +11548,12 @@ function resolveNextStep(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
11129
11548
|
const state = config.project.state;
|
|
11130
11549
|
const q = (rel) => analyzeKnowledgeArtifact(dir, rel);
|
|
11131
11550
|
const moduleRepo = isModule(config);
|
|
11132
|
-
const
|
|
11551
|
+
const coreRepo = isCore(config);
|
|
11552
|
+
const MC = exists(join(dir, "knowledge/tech/module/module-context.md")) ? "knowledge/tech/module/module-context.md" : "knowledge/module/module-context.md";
|
|
11133
11553
|
if (moduleRepo) {
|
|
11134
11554
|
const qMC = q(MC);
|
|
11135
11555
|
if (qMC === "missing") {
|
|
11136
|
-
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." };
|
|
11556
|
+
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." };
|
|
11137
11557
|
}
|
|
11138
11558
|
if ((state === "pre-ai" || state === "legacy") && !exists(join(dir, ".kaddo", "scan.json"))) {
|
|
11139
11559
|
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." };
|
|
@@ -11182,9 +11602,15 @@ function resolveNextStep(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
11182
11602
|
return { id: "module-ready", phase: "Active Delivery", label: "Module context is complete. Create and manage Work Items from the core repository.", reason: "This module's knowledge is ready. Work Items are created and tracked in the core." };
|
|
11183
11603
|
}
|
|
11184
11604
|
const qBusiness = q(B), qProduct = q(P), qCap = q(CAP), qCurrentState = q(CS), qCodebase = q(CB);
|
|
11185
|
-
if (qBusiness === "missing" || qProduct === "missing") {
|
|
11605
|
+
if (!coreRepo && (qBusiness === "missing" || qProduct === "missing")) {
|
|
11186
11606
|
return { id: "bootstrap", phase: "Setup", label: "Run `kaddo bootstrap` to create the project knowledge baseline.", command: "kaddo bootstrap", reason: "The knowledge baseline is incomplete." };
|
|
11187
11607
|
}
|
|
11608
|
+
if (coreRepo) {
|
|
11609
|
+
const modulesYmlPath = join(dir, ".kaddo", "modules.yml");
|
|
11610
|
+
if (!exists(modulesYmlPath) || !hasRegisteredModules(modulesYmlPath)) {
|
|
11611
|
+
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." };
|
|
11612
|
+
}
|
|
11613
|
+
}
|
|
11188
11614
|
const ctx = buildCodexAdapterContext(dir);
|
|
11189
11615
|
if (!ctx.hasAgents) {
|
|
11190
11616
|
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." };
|
|
@@ -11201,10 +11627,10 @@ function resolveNextStep(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
11201
11627
|
const discovery = state === "pre-ai" || state === "legacy";
|
|
11202
11628
|
const resolveAgent = (agent) => {
|
|
11203
11629
|
const file = agent.endsWith(".md") ? agent : `${agent}.md`;
|
|
11204
|
-
const
|
|
11205
|
-
const installed = isFile(join(dir,
|
|
11630
|
+
const path8 = agentInstallPath(file);
|
|
11631
|
+
const installed = isFile(join(dir, path8));
|
|
11206
11632
|
const group = agentGroupOf(file);
|
|
11207
|
-
return { agentPath:
|
|
11633
|
+
return { agentPath: path8, agentInstalled: installed, installCommand: installed ? void 0 : `kaddo add agents --group ${group}` };
|
|
11208
11634
|
};
|
|
11209
11635
|
const refine = (id, agent, target, quality, verb = "complete") => {
|
|
11210
11636
|
const ar = resolveAgent(agent);
|
|
@@ -11261,6 +11687,13 @@ function resolveNextStep(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
11261
11687
|
reason: "The roadmap has no candidates yet, but ready Work Items exist."
|
|
11262
11688
|
});
|
|
11263
11689
|
}
|
|
11690
|
+
if (st.ready_multirepo_wi_ids.length > 0) {
|
|
11691
|
+
sec.push({
|
|
11692
|
+
id: "validate-work-item-modules",
|
|
11693
|
+
label: "Validate affected modules for multirepo Work Items before implementation.",
|
|
11694
|
+
reason: `${st.ready_multirepo_wi_ids.length} ready Work Item(s) reference affected_modules.`
|
|
11695
|
+
});
|
|
11696
|
+
}
|
|
11264
11697
|
if (adapters === 0) {
|
|
11265
11698
|
return {
|
|
11266
11699
|
id: "prepare-implementation",
|
|
@@ -12079,7 +12512,9 @@ function mapNextStepId(id) {
|
|
|
12079
12512
|
"implement-work-item": "prepare-implementation",
|
|
12080
12513
|
"refine-module-context": "refine-work-item",
|
|
12081
12514
|
"init-module-context": "enable-kaddo",
|
|
12082
|
-
"module-ready": "prepare-implementation"
|
|
12515
|
+
"module-ready": "prepare-implementation",
|
|
12516
|
+
"validate-work-item-modules": "prepare-implementation",
|
|
12517
|
+
"modules-discover": "enable-kaddo"
|
|
12083
12518
|
};
|
|
12084
12519
|
return MAP[id] ?? id;
|
|
12085
12520
|
}
|
|
@@ -12750,7 +13185,7 @@ function renderExplanationAgent(exp) {
|
|
|
12750
13185
|
|
|
12751
13186
|
// src/commands/explain.ts
|
|
12752
13187
|
var ARCH_DIR5 = "knowledge";
|
|
12753
|
-
var
|
|
13188
|
+
var CONFIG_PATH5 = ".kaddo/config.yml";
|
|
12754
13189
|
function readKnowledge(dir) {
|
|
12755
13190
|
const knowledgePath = join(dir, ARCH_DIR5, "knowledge.md");
|
|
12756
13191
|
if (!exists(knowledgePath)) return null;
|
|
@@ -12774,10 +13209,10 @@ function readRoadmap(dir) {
|
|
|
12774
13209
|
}
|
|
12775
13210
|
}
|
|
12776
13211
|
function readConfig(dir) {
|
|
12777
|
-
const configPath = join(dir,
|
|
13212
|
+
const configPath = join(dir, CONFIG_PATH5);
|
|
12778
13213
|
if (!exists(configPath)) return {};
|
|
12779
13214
|
try {
|
|
12780
|
-
return
|
|
13215
|
+
return parseYaml12(readFile(configPath));
|
|
12781
13216
|
} catch {
|
|
12782
13217
|
return {};
|
|
12783
13218
|
}
|
|
@@ -12888,7 +13323,7 @@ function explainForAgent(dir, artifacts, opts) {
|
|
|
12888
13323
|
console.log(JSON.stringify(output, null, 2));
|
|
12889
13324
|
}
|
|
12890
13325
|
function explainProject(dir, mode) {
|
|
12891
|
-
if (!exists(join(dir,
|
|
13326
|
+
if (!exists(join(dir, CONFIG_PATH5))) {
|
|
12892
13327
|
console.error("No .kaddo/config.yml found. Run `kaddo init` first.");
|
|
12893
13328
|
process.exit(1);
|
|
12894
13329
|
}
|
|
@@ -13960,7 +14395,7 @@ function renderUnderstandTerminal(plan) {
|
|
|
13960
14395
|
}
|
|
13961
14396
|
|
|
13962
14397
|
// src/core/delivery.ts
|
|
13963
|
-
import { parse as
|
|
14398
|
+
import { parse as parseYaml13 } from "yaml";
|
|
13964
14399
|
function slugify4(s) {
|
|
13965
14400
|
return s.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
13966
14401
|
}
|
|
@@ -14194,7 +14629,7 @@ function runUnderstand() {
|
|
|
14194
14629
|
}
|
|
14195
14630
|
|
|
14196
14631
|
// src/core/classifier.ts
|
|
14197
|
-
import
|
|
14632
|
+
import path6 from "path";
|
|
14198
14633
|
var MIGRATION_PATTERNS = [
|
|
14199
14634
|
/supabase\/migrations\//,
|
|
14200
14635
|
/prisma\/migrations\//,
|
|
@@ -14233,7 +14668,7 @@ var DEP_PATTERNS = [
|
|
|
14233
14668
|
/^pom\.xml$/
|
|
14234
14669
|
];
|
|
14235
14670
|
function normalizePath2(p2) {
|
|
14236
|
-
return p2.split(
|
|
14671
|
+
return p2.split(path6.sep).join("/");
|
|
14237
14672
|
}
|
|
14238
14673
|
function matchesAny2(file, patterns) {
|
|
14239
14674
|
const normalized = normalizePath2(file);
|
|
@@ -14411,14 +14846,14 @@ async function runClassify(opts = {}) {
|
|
|
14411
14846
|
}
|
|
14412
14847
|
|
|
14413
14848
|
// src/commands/status.ts
|
|
14414
|
-
import { parse as
|
|
14849
|
+
import { parse as parseYaml14 } from "yaml";
|
|
14415
14850
|
var ARCH_DIR8 = "knowledge";
|
|
14416
|
-
var
|
|
14851
|
+
var CONFIG_PATH6 = ".kaddo/config.yml";
|
|
14417
14852
|
function loadConfig3(dir) {
|
|
14418
|
-
const p2 = join(dir,
|
|
14853
|
+
const p2 = join(dir, CONFIG_PATH6);
|
|
14419
14854
|
if (!exists(p2)) return {};
|
|
14420
14855
|
try {
|
|
14421
|
-
return
|
|
14856
|
+
return parseYaml14(readFile(p2));
|
|
14422
14857
|
} catch {
|
|
14423
14858
|
return {};
|
|
14424
14859
|
}
|
|
@@ -14620,13 +15055,13 @@ function runHistory(opts = {}) {
|
|
|
14620
15055
|
}
|
|
14621
15056
|
|
|
14622
15057
|
// src/commands/add.ts
|
|
14623
|
-
import { parse as
|
|
14624
|
-
var
|
|
15058
|
+
import { parse as parseYaml15, stringify as stringifyYaml6 } from "yaml";
|
|
15059
|
+
var CONFIG_PATH7 = ".kaddo/config.yml";
|
|
14625
15060
|
function readProjectState(dir) {
|
|
14626
|
-
const configPath = join(dir,
|
|
15061
|
+
const configPath = join(dir, CONFIG_PATH7);
|
|
14627
15062
|
if (!exists(configPath)) return void 0;
|
|
14628
15063
|
try {
|
|
14629
|
-
const config =
|
|
15064
|
+
const config = parseYaml15(readFile(configPath));
|
|
14630
15065
|
return config.project?.state;
|
|
14631
15066
|
} catch {
|
|
14632
15067
|
return void 0;
|
|
@@ -14647,23 +15082,23 @@ function selectSkillModuleFiles(files, opts) {
|
|
|
14647
15082
|
return { files: selected, label };
|
|
14648
15083
|
}
|
|
14649
15084
|
function markModuleInstalled(dir, configKey, moduleName) {
|
|
14650
|
-
const configPath = join(dir,
|
|
15085
|
+
const configPath = join(dir, CONFIG_PATH7);
|
|
14651
15086
|
if (!exists(configPath)) return;
|
|
14652
15087
|
try {
|
|
14653
|
-
const config =
|
|
15088
|
+
const config = parseYaml15(readFile(configPath));
|
|
14654
15089
|
config[configKey] = { installed: true, installed_at: (/* @__PURE__ */ new Date()).toISOString().split("T")[0] };
|
|
14655
15090
|
const modules = config.modules ?? [];
|
|
14656
15091
|
if (!modules.includes(moduleName)) modules.push(moduleName);
|
|
14657
15092
|
config.modules = modules;
|
|
14658
|
-
writeFile(configPath,
|
|
15093
|
+
writeFile(configPath, stringifyYaml6(config));
|
|
14659
15094
|
} catch {
|
|
14660
15095
|
}
|
|
14661
15096
|
}
|
|
14662
15097
|
function isModuleInstalled(dir, configKey) {
|
|
14663
|
-
const configPath = join(dir,
|
|
15098
|
+
const configPath = join(dir, CONFIG_PATH7);
|
|
14664
15099
|
if (!exists(configPath)) return false;
|
|
14665
15100
|
try {
|
|
14666
|
-
const config =
|
|
15101
|
+
const config = parseYaml15(readFile(configPath));
|
|
14667
15102
|
const moduleConfig = config[configKey];
|
|
14668
15103
|
return moduleConfig?.installed === true;
|
|
14669
15104
|
} catch {
|
|
@@ -14687,7 +15122,7 @@ function runAdd(moduleName, opts = {}, dir = cwd()) {
|
|
|
14687
15122
|
process.exit(1);
|
|
14688
15123
|
}
|
|
14689
15124
|
intro2(`kaddo add ${mod.name}`);
|
|
14690
|
-
if (!exists(join(dir,
|
|
15125
|
+
if (!exists(join(dir, CONFIG_PATH7))) {
|
|
14691
15126
|
console.error("Kaddo is not initialized in this project.");
|
|
14692
15127
|
console.error("Run `kaddo init` first.");
|
|
14693
15128
|
process.exit(1);
|
|
@@ -14892,11 +15327,11 @@ function applyOwnership(raw, globs, mode = "replace") {
|
|
|
14892
15327
|
}
|
|
14893
15328
|
|
|
14894
15329
|
// src/commands/owners.ts
|
|
14895
|
-
var
|
|
15330
|
+
var CONFIG_PATH8 = ".kaddo/config.yml";
|
|
14896
15331
|
var CUSTOM_GLOB = "__custom__";
|
|
14897
15332
|
function runOwners(opts) {
|
|
14898
15333
|
const dir = cwd();
|
|
14899
|
-
if (!exists(join(dir,
|
|
15334
|
+
if (!exists(join(dir, CONFIG_PATH8))) {
|
|
14900
15335
|
console.error("No .kaddo/config.yml found. Run `kaddo init` first.");
|
|
14901
15336
|
process.exit(1);
|
|
14902
15337
|
}
|
|
@@ -14986,7 +15421,7 @@ async function chooseGlobs(suggested) {
|
|
|
14986
15421
|
}
|
|
14987
15422
|
async function runOwnersSuggest() {
|
|
14988
15423
|
const dir = cwd();
|
|
14989
|
-
if (!exists(join(dir,
|
|
15424
|
+
if (!exists(join(dir, CONFIG_PATH8))) {
|
|
14990
15425
|
console.error("No .kaddo/config.yml found. Run `kaddo init` first.");
|
|
14991
15426
|
process.exit(1);
|
|
14992
15427
|
}
|
|
@@ -15061,113 +15496,6 @@ ${globs.map((g) => ` - ${g}`).join("\n")}`);
|
|
|
15061
15496
|
outro2("Ownership declared.");
|
|
15062
15497
|
}
|
|
15063
15498
|
|
|
15064
|
-
// src/commands/module-descriptor.ts
|
|
15065
|
-
import { parse as parseYaml14, stringify as stringifyYaml6 } from "yaml";
|
|
15066
|
-
var DESCRIPTOR_PATH3 = "knowledge/module.yml";
|
|
15067
|
-
function readDescriptor(dir) {
|
|
15068
|
-
const path7 = join(dir, DESCRIPTOR_PATH3);
|
|
15069
|
-
if (!exists(path7)) return null;
|
|
15070
|
-
try {
|
|
15071
|
-
return parseYaml14(readFile(path7));
|
|
15072
|
-
} catch {
|
|
15073
|
-
return null;
|
|
15074
|
-
}
|
|
15075
|
-
}
|
|
15076
|
-
function writeDescriptor(dir, descriptor) {
|
|
15077
|
-
writeFile(join(dir, DESCRIPTOR_PATH3), stringifyYaml6(descriptor));
|
|
15078
|
-
}
|
|
15079
|
-
async function runModuleDescriptor(opts) {
|
|
15080
|
-
const dir = cwd();
|
|
15081
|
-
if (opts.show || !opts.init && exists(join(dir, DESCRIPTOR_PATH3))) {
|
|
15082
|
-
const descriptor2 = readDescriptor(dir);
|
|
15083
|
-
if (!descriptor2) {
|
|
15084
|
-
console.log("No module descriptor found. Run `kaddo module --init` to create one.");
|
|
15085
|
-
return;
|
|
15086
|
-
}
|
|
15087
|
-
printDescriptor(descriptor2);
|
|
15088
|
-
return;
|
|
15089
|
-
}
|
|
15090
|
-
intro2("kaddo module --init");
|
|
15091
|
-
log2.info("Creates knowledge/module.yml \u2014 declares this repository's identity for multirepo contexts.");
|
|
15092
|
-
const name = await text2({
|
|
15093
|
-
message: "Module name (this repository)",
|
|
15094
|
-
placeholder: "e.g. payments-service",
|
|
15095
|
-
validate: (v) => v.trim().length === 0 ? "Name is required." : void 0
|
|
15096
|
-
});
|
|
15097
|
-
const purpose = await text2({
|
|
15098
|
-
message: "Purpose \u2014 what does this module do?",
|
|
15099
|
-
placeholder: "e.g. Handles subscription billing, invoicing, and payment processing",
|
|
15100
|
-
validate: (v) => v.trim().length === 0 ? "Purpose is required." : void 0
|
|
15101
|
-
});
|
|
15102
|
-
const stack = await text2({
|
|
15103
|
-
message: "Stack (comma-separated)",
|
|
15104
|
-
placeholder: "e.g. TypeScript, Node.js, PostgreSQL, Stripe"
|
|
15105
|
-
});
|
|
15106
|
-
const responsibilities = await text2({
|
|
15107
|
-
message: "Key responsibilities (comma-separated)",
|
|
15108
|
-
placeholder: "e.g. Charge customers, Issue refunds, Manage subscriptions"
|
|
15109
|
-
});
|
|
15110
|
-
const contracts = await text2({
|
|
15111
|
-
message: "Contracts exposed (APIs, events, comma-separated)",
|
|
15112
|
-
placeholder: "e.g. POST /charge, event:payment.succeeded, event:payment.failed"
|
|
15113
|
-
});
|
|
15114
|
-
const dependencies = await text2({
|
|
15115
|
-
message: "Dependencies on other modules (comma-separated)",
|
|
15116
|
-
placeholder: "e.g. auth-service, user-service"
|
|
15117
|
-
});
|
|
15118
|
-
const boundaries = await text2({
|
|
15119
|
-
message: "What does this module NOT own?",
|
|
15120
|
-
placeholder: "e.g. User identity, Email delivery, Inventory"
|
|
15121
|
-
});
|
|
15122
|
-
const ownership = await text2({
|
|
15123
|
-
message: "Team or owner",
|
|
15124
|
-
placeholder: "e.g. platform-team"
|
|
15125
|
-
});
|
|
15126
|
-
const splitTrimmed = (s) => s.split(",").map((x) => x.trim()).filter(Boolean);
|
|
15127
|
-
const descriptor = {
|
|
15128
|
-
name: name.trim(),
|
|
15129
|
-
purpose: purpose.trim(),
|
|
15130
|
-
stack: splitTrimmed(stack),
|
|
15131
|
-
responsibilities: splitTrimmed(responsibilities),
|
|
15132
|
-
contracts: splitTrimmed(contracts),
|
|
15133
|
-
dependencies: splitTrimmed(dependencies),
|
|
15134
|
-
boundaries: splitTrimmed(boundaries),
|
|
15135
|
-
ownership: ownership.trim(),
|
|
15136
|
-
related_artifacts: []
|
|
15137
|
-
};
|
|
15138
|
-
writeDescriptor(dir, descriptor);
|
|
15139
|
-
log2.success(`Created ${DESCRIPTOR_PATH3}`);
|
|
15140
|
-
outro2(`Module descriptor ready. Share knowledge/module.yml with dependent repositories.`);
|
|
15141
|
-
}
|
|
15142
|
-
function printDescriptor(d) {
|
|
15143
|
-
console.log("");
|
|
15144
|
-
console.log(`Module: ${d.name}`);
|
|
15145
|
-
console.log(`Purpose: ${d.purpose}`);
|
|
15146
|
-
if (d.stack?.length) console.log(`Stack: ${d.stack.join(", ")}`);
|
|
15147
|
-
if (d.ownership) console.log(`Owner: ${d.ownership}`);
|
|
15148
|
-
if (d.responsibilities?.length) {
|
|
15149
|
-
console.log("Responsibilities:");
|
|
15150
|
-
d.responsibilities.forEach((r) => console.log(` - ${r}`));
|
|
15151
|
-
}
|
|
15152
|
-
if (d.contracts?.length) {
|
|
15153
|
-
console.log("Contracts:");
|
|
15154
|
-
d.contracts.forEach((c) => console.log(` - ${c}`));
|
|
15155
|
-
}
|
|
15156
|
-
if (d.dependencies?.length) {
|
|
15157
|
-
console.log("Dependencies:");
|
|
15158
|
-
d.dependencies.forEach((dep) => console.log(` - ${dep}`));
|
|
15159
|
-
}
|
|
15160
|
-
if (d.boundaries?.length) {
|
|
15161
|
-
console.log("Boundaries (not owned):");
|
|
15162
|
-
d.boundaries.forEach((b) => console.log(` - ${b}`));
|
|
15163
|
-
}
|
|
15164
|
-
if (d.related_artifacts?.length) {
|
|
15165
|
-
console.log("Related artifacts:");
|
|
15166
|
-
d.related_artifacts.forEach((a) => console.log(` - ${a}`));
|
|
15167
|
-
}
|
|
15168
|
-
console.log("");
|
|
15169
|
-
}
|
|
15170
|
-
|
|
15171
15499
|
// src/core/bootstrap-templates.ts
|
|
15172
15500
|
function fm(type, state) {
|
|
15173
15501
|
return `---
|
|
@@ -15599,7 +15927,7 @@ function baselineTemplate(kind, state) {
|
|
|
15599
15927
|
}
|
|
15600
15928
|
|
|
15601
15929
|
// src/commands/bootstrap.ts
|
|
15602
|
-
var
|
|
15930
|
+
var CONFIG_PATH9 = ".kaddo/config.yml";
|
|
15603
15931
|
var FILE_TARGETS = [
|
|
15604
15932
|
{ path: "knowledge/business/business.md", kind: "business" },
|
|
15605
15933
|
{ path: "knowledge/product/product.md", kind: "product" },
|
|
@@ -15656,7 +15984,7 @@ function bootstrap(dir) {
|
|
|
15656
15984
|
}
|
|
15657
15985
|
async function runBootstrap(dir = cwd()) {
|
|
15658
15986
|
intro2("kaddo bootstrap");
|
|
15659
|
-
if (!exists(join(dir,
|
|
15987
|
+
if (!exists(join(dir, CONFIG_PATH9))) {
|
|
15660
15988
|
console.error("Kaddo is not initialized in this project.");
|
|
15661
15989
|
console.error("Run `kaddo init` first.");
|
|
15662
15990
|
process.exit(1);
|
|
@@ -16248,7 +16576,7 @@ function runReportImpact(opts = {}) {
|
|
|
16248
16576
|
}
|
|
16249
16577
|
|
|
16250
16578
|
// src/core/savings.ts
|
|
16251
|
-
import { parse as
|
|
16579
|
+
import { parse as parseYaml16 } from "yaml";
|
|
16252
16580
|
var DEFAULT_ASSUMPTIONS = {
|
|
16253
16581
|
currency: "USD",
|
|
16254
16582
|
hourly_cost: 40,
|
|
@@ -16266,7 +16594,7 @@ function loadAssumptions(dir) {
|
|
|
16266
16594
|
const p2 = join(dir, SAVINGS_PATH);
|
|
16267
16595
|
if (!exists(p2)) return { assumptions: { ...DEFAULT_ASSUMPTIONS }, source: "default" };
|
|
16268
16596
|
try {
|
|
16269
|
-
const raw =
|
|
16597
|
+
const raw = parseYaml16(readFile(p2));
|
|
16270
16598
|
const a = raw?.assumptions ?? {};
|
|
16271
16599
|
const t = raw?.team ?? {};
|
|
16272
16600
|
const num = (v, d) => typeof v === "number" && Number.isFinite(v) ? v : d;
|
|
@@ -17048,7 +17376,7 @@ function runAssetsUpdate(kind, opts = {}) {
|
|
|
17048
17376
|
|
|
17049
17377
|
// src/commands/ready.ts
|
|
17050
17378
|
import fs4 from "fs";
|
|
17051
|
-
import
|
|
17379
|
+
import path7 from "path";
|
|
17052
17380
|
import matter11 from "gray-matter";
|
|
17053
17381
|
function validateReadiness(content, frontmatter) {
|
|
17054
17382
|
const warnings = [];
|
|
@@ -17111,10 +17439,10 @@ function transitionToReady(filePath) {
|
|
|
17111
17439
|
const posix = filePath.replace(/\\/g, "/");
|
|
17112
17440
|
let newPath = filePath;
|
|
17113
17441
|
if (posix.includes("/work-items/draft/")) {
|
|
17114
|
-
const readyDir =
|
|
17442
|
+
const readyDir = path7.dirname(filePath).replace(/[/\\]draft$/, path7.sep + "ready");
|
|
17115
17443
|
ensureDir(readyDir);
|
|
17116
|
-
const fileName =
|
|
17117
|
-
newPath =
|
|
17444
|
+
const fileName = path7.basename(filePath);
|
|
17445
|
+
newPath = path7.join(readyDir, fileName);
|
|
17118
17446
|
}
|
|
17119
17447
|
return { newPath, newContent };
|
|
17120
17448
|
}
|
|
@@ -17185,8 +17513,8 @@ async function runReady(id, opts = {}) {
|
|
|
17185
17513
|
fs4.unlinkSync(wi.filePath);
|
|
17186
17514
|
log2.success(`Updated status: ready`);
|
|
17187
17515
|
log2.success(`Moved file:`);
|
|
17188
|
-
log2.info(` ${
|
|
17189
|
-
log2.info(` \u2192 ${
|
|
17516
|
+
log2.info(` ${path7.relative(dir, wi.filePath)}`);
|
|
17517
|
+
log2.info(` \u2192 ${path7.relative(dir, newPath)}`);
|
|
17190
17518
|
} else {
|
|
17191
17519
|
writeFile(wi.filePath, newContent);
|
|
17192
17520
|
log2.success(`Updated status: ready`);
|
|
@@ -17220,8 +17548,8 @@ var capsuleCmd = program.command("capsule").description("Export this project as
|
|
|
17220
17548
|
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) => {
|
|
17221
17549
|
runCapsuleExport(opts);
|
|
17222
17550
|
});
|
|
17223
|
-
capsuleCmd.command("add <path>").description("Register an external Knowledge Capsule as project context (.kaddo/external.yml)").action((
|
|
17224
|
-
runCapsuleAdd(
|
|
17551
|
+
capsuleCmd.command("add <path>").description("Register an external Knowledge Capsule as project context (.kaddo/external.yml)").action((path8) => {
|
|
17552
|
+
runCapsuleAdd(path8);
|
|
17225
17553
|
});
|
|
17226
17554
|
var graphCmd = program.command("graph").description("Export the lightweight, file-based knowledge graph of the project");
|
|
17227
17555
|
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) => {
|
|
@@ -17319,16 +17647,23 @@ program.command("owners [action]").description("List domain owners, or run `owne
|
|
|
17319
17647
|
runOwners(opts);
|
|
17320
17648
|
}
|
|
17321
17649
|
});
|
|
17322
|
-
program.command("module").description("
|
|
17323
|
-
|
|
17650
|
+
program.command("module").description("(deprecated) Use `kaddo modules` instead").allowUnknownOption().action(() => {
|
|
17651
|
+
console.log("Unknown command `module`.");
|
|
17652
|
+
console.log("Did you mean `kaddo modules list`?");
|
|
17324
17653
|
});
|
|
17325
|
-
var modulesCmd = program.command("modules").description("
|
|
17326
|
-
modulesCmd.command("
|
|
17327
|
-
await
|
|
17654
|
+
var modulesCmd = program.command("modules").description("Discover, map, list and validate multirepo modules");
|
|
17655
|
+
modulesCmd.command("discover").description("Discover sibling repositories configured as Kaddo modules").option("--apply", "Persist discovered modules to .kaddo/modules.yml").action(async (opts) => {
|
|
17656
|
+
await runModulesDiscover(opts);
|
|
17328
17657
|
});
|
|
17329
|
-
modulesCmd.command("
|
|
17658
|
+
modulesCmd.command("map [path]").description("Register a repository as a module manually").action(async (modulePath) => {
|
|
17659
|
+
await runModulesMap(void 0, modulePath);
|
|
17660
|
+
});
|
|
17661
|
+
modulesCmd.command("list").description("List mapped modules from .kaddo/modules.yml").action(() => {
|
|
17330
17662
|
runModulesList();
|
|
17331
17663
|
});
|
|
17664
|
+
modulesCmd.command("validate").description("Validate registered modules against their current configuration").action(() => {
|
|
17665
|
+
runModulesValidate();
|
|
17666
|
+
});
|
|
17332
17667
|
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) => {
|
|
17333
17668
|
runAdd(moduleName ?? "", { all: opts.all, group: opts.group });
|
|
17334
17669
|
});
|