@kaddo/cli 3.62.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 +600 -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";
|
|
@@ -11080,6 +11490,14 @@ function buildDeliveryState(dir) {
|
|
|
11080
11490
|
adapters_installed: adapters.length
|
|
11081
11491
|
};
|
|
11082
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
|
+
}
|
|
11083
11501
|
function safeRead(p2) {
|
|
11084
11502
|
try {
|
|
11085
11503
|
return readFile(p2);
|
|
@@ -11130,11 +11548,12 @@ function resolveNextStep(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
11130
11548
|
const state = config.project.state;
|
|
11131
11549
|
const q = (rel) => analyzeKnowledgeArtifact(dir, rel);
|
|
11132
11550
|
const moduleRepo = isModule(config);
|
|
11133
|
-
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";
|
|
11134
11553
|
if (moduleRepo) {
|
|
11135
11554
|
const qMC = q(MC);
|
|
11136
11555
|
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." };
|
|
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." };
|
|
11138
11557
|
}
|
|
11139
11558
|
if ((state === "pre-ai" || state === "legacy") && !exists(join(dir, ".kaddo", "scan.json"))) {
|
|
11140
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." };
|
|
@@ -11183,9 +11602,15 @@ function resolveNextStep(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
11183
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." };
|
|
11184
11603
|
}
|
|
11185
11604
|
const qBusiness = q(B), qProduct = q(P), qCap = q(CAP), qCurrentState = q(CS), qCodebase = q(CB);
|
|
11186
|
-
if (qBusiness === "missing" || qProduct === "missing") {
|
|
11605
|
+
if (!coreRepo && (qBusiness === "missing" || qProduct === "missing")) {
|
|
11187
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." };
|
|
11188
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
|
+
}
|
|
11189
11614
|
const ctx = buildCodexAdapterContext(dir);
|
|
11190
11615
|
if (!ctx.hasAgents) {
|
|
11191
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." };
|
|
@@ -11202,10 +11627,10 @@ function resolveNextStep(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
11202
11627
|
const discovery = state === "pre-ai" || state === "legacy";
|
|
11203
11628
|
const resolveAgent = (agent) => {
|
|
11204
11629
|
const file = agent.endsWith(".md") ? agent : `${agent}.md`;
|
|
11205
|
-
const
|
|
11206
|
-
const installed = isFile(join(dir,
|
|
11630
|
+
const path8 = agentInstallPath(file);
|
|
11631
|
+
const installed = isFile(join(dir, path8));
|
|
11207
11632
|
const group = agentGroupOf(file);
|
|
11208
|
-
return { agentPath:
|
|
11633
|
+
return { agentPath: path8, agentInstalled: installed, installCommand: installed ? void 0 : `kaddo add agents --group ${group}` };
|
|
11209
11634
|
};
|
|
11210
11635
|
const refine = (id, agent, target, quality, verb = "complete") => {
|
|
11211
11636
|
const ar = resolveAgent(agent);
|
|
@@ -12088,7 +12513,8 @@ function mapNextStepId(id) {
|
|
|
12088
12513
|
"refine-module-context": "refine-work-item",
|
|
12089
12514
|
"init-module-context": "enable-kaddo",
|
|
12090
12515
|
"module-ready": "prepare-implementation",
|
|
12091
|
-
"validate-work-item-modules": "prepare-implementation"
|
|
12516
|
+
"validate-work-item-modules": "prepare-implementation",
|
|
12517
|
+
"modules-discover": "enable-kaddo"
|
|
12092
12518
|
};
|
|
12093
12519
|
return MAP[id] ?? id;
|
|
12094
12520
|
}
|
|
@@ -12759,7 +13185,7 @@ function renderExplanationAgent(exp) {
|
|
|
12759
13185
|
|
|
12760
13186
|
// src/commands/explain.ts
|
|
12761
13187
|
var ARCH_DIR5 = "knowledge";
|
|
12762
|
-
var
|
|
13188
|
+
var CONFIG_PATH5 = ".kaddo/config.yml";
|
|
12763
13189
|
function readKnowledge(dir) {
|
|
12764
13190
|
const knowledgePath = join(dir, ARCH_DIR5, "knowledge.md");
|
|
12765
13191
|
if (!exists(knowledgePath)) return null;
|
|
@@ -12783,10 +13209,10 @@ function readRoadmap(dir) {
|
|
|
12783
13209
|
}
|
|
12784
13210
|
}
|
|
12785
13211
|
function readConfig(dir) {
|
|
12786
|
-
const configPath = join(dir,
|
|
13212
|
+
const configPath = join(dir, CONFIG_PATH5);
|
|
12787
13213
|
if (!exists(configPath)) return {};
|
|
12788
13214
|
try {
|
|
12789
|
-
return
|
|
13215
|
+
return parseYaml12(readFile(configPath));
|
|
12790
13216
|
} catch {
|
|
12791
13217
|
return {};
|
|
12792
13218
|
}
|
|
@@ -12897,7 +13323,7 @@ function explainForAgent(dir, artifacts, opts) {
|
|
|
12897
13323
|
console.log(JSON.stringify(output, null, 2));
|
|
12898
13324
|
}
|
|
12899
13325
|
function explainProject(dir, mode) {
|
|
12900
|
-
if (!exists(join(dir,
|
|
13326
|
+
if (!exists(join(dir, CONFIG_PATH5))) {
|
|
12901
13327
|
console.error("No .kaddo/config.yml found. Run `kaddo init` first.");
|
|
12902
13328
|
process.exit(1);
|
|
12903
13329
|
}
|
|
@@ -13969,7 +14395,7 @@ function renderUnderstandTerminal(plan) {
|
|
|
13969
14395
|
}
|
|
13970
14396
|
|
|
13971
14397
|
// src/core/delivery.ts
|
|
13972
|
-
import { parse as
|
|
14398
|
+
import { parse as parseYaml13 } from "yaml";
|
|
13973
14399
|
function slugify4(s) {
|
|
13974
14400
|
return s.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
13975
14401
|
}
|
|
@@ -14203,7 +14629,7 @@ function runUnderstand() {
|
|
|
14203
14629
|
}
|
|
14204
14630
|
|
|
14205
14631
|
// src/core/classifier.ts
|
|
14206
|
-
import
|
|
14632
|
+
import path6 from "path";
|
|
14207
14633
|
var MIGRATION_PATTERNS = [
|
|
14208
14634
|
/supabase\/migrations\//,
|
|
14209
14635
|
/prisma\/migrations\//,
|
|
@@ -14242,7 +14668,7 @@ var DEP_PATTERNS = [
|
|
|
14242
14668
|
/^pom\.xml$/
|
|
14243
14669
|
];
|
|
14244
14670
|
function normalizePath2(p2) {
|
|
14245
|
-
return p2.split(
|
|
14671
|
+
return p2.split(path6.sep).join("/");
|
|
14246
14672
|
}
|
|
14247
14673
|
function matchesAny2(file, patterns) {
|
|
14248
14674
|
const normalized = normalizePath2(file);
|
|
@@ -14420,14 +14846,14 @@ async function runClassify(opts = {}) {
|
|
|
14420
14846
|
}
|
|
14421
14847
|
|
|
14422
14848
|
// src/commands/status.ts
|
|
14423
|
-
import { parse as
|
|
14849
|
+
import { parse as parseYaml14 } from "yaml";
|
|
14424
14850
|
var ARCH_DIR8 = "knowledge";
|
|
14425
|
-
var
|
|
14851
|
+
var CONFIG_PATH6 = ".kaddo/config.yml";
|
|
14426
14852
|
function loadConfig3(dir) {
|
|
14427
|
-
const p2 = join(dir,
|
|
14853
|
+
const p2 = join(dir, CONFIG_PATH6);
|
|
14428
14854
|
if (!exists(p2)) return {};
|
|
14429
14855
|
try {
|
|
14430
|
-
return
|
|
14856
|
+
return parseYaml14(readFile(p2));
|
|
14431
14857
|
} catch {
|
|
14432
14858
|
return {};
|
|
14433
14859
|
}
|
|
@@ -14629,13 +15055,13 @@ function runHistory(opts = {}) {
|
|
|
14629
15055
|
}
|
|
14630
15056
|
|
|
14631
15057
|
// src/commands/add.ts
|
|
14632
|
-
import { parse as
|
|
14633
|
-
var
|
|
15058
|
+
import { parse as parseYaml15, stringify as stringifyYaml6 } from "yaml";
|
|
15059
|
+
var CONFIG_PATH7 = ".kaddo/config.yml";
|
|
14634
15060
|
function readProjectState(dir) {
|
|
14635
|
-
const configPath = join(dir,
|
|
15061
|
+
const configPath = join(dir, CONFIG_PATH7);
|
|
14636
15062
|
if (!exists(configPath)) return void 0;
|
|
14637
15063
|
try {
|
|
14638
|
-
const config =
|
|
15064
|
+
const config = parseYaml15(readFile(configPath));
|
|
14639
15065
|
return config.project?.state;
|
|
14640
15066
|
} catch {
|
|
14641
15067
|
return void 0;
|
|
@@ -14656,23 +15082,23 @@ function selectSkillModuleFiles(files, opts) {
|
|
|
14656
15082
|
return { files: selected, label };
|
|
14657
15083
|
}
|
|
14658
15084
|
function markModuleInstalled(dir, configKey, moduleName) {
|
|
14659
|
-
const configPath = join(dir,
|
|
15085
|
+
const configPath = join(dir, CONFIG_PATH7);
|
|
14660
15086
|
if (!exists(configPath)) return;
|
|
14661
15087
|
try {
|
|
14662
|
-
const config =
|
|
15088
|
+
const config = parseYaml15(readFile(configPath));
|
|
14663
15089
|
config[configKey] = { installed: true, installed_at: (/* @__PURE__ */ new Date()).toISOString().split("T")[0] };
|
|
14664
15090
|
const modules = config.modules ?? [];
|
|
14665
15091
|
if (!modules.includes(moduleName)) modules.push(moduleName);
|
|
14666
15092
|
config.modules = modules;
|
|
14667
|
-
writeFile(configPath,
|
|
15093
|
+
writeFile(configPath, stringifyYaml6(config));
|
|
14668
15094
|
} catch {
|
|
14669
15095
|
}
|
|
14670
15096
|
}
|
|
14671
15097
|
function isModuleInstalled(dir, configKey) {
|
|
14672
|
-
const configPath = join(dir,
|
|
15098
|
+
const configPath = join(dir, CONFIG_PATH7);
|
|
14673
15099
|
if (!exists(configPath)) return false;
|
|
14674
15100
|
try {
|
|
14675
|
-
const config =
|
|
15101
|
+
const config = parseYaml15(readFile(configPath));
|
|
14676
15102
|
const moduleConfig = config[configKey];
|
|
14677
15103
|
return moduleConfig?.installed === true;
|
|
14678
15104
|
} catch {
|
|
@@ -14696,7 +15122,7 @@ function runAdd(moduleName, opts = {}, dir = cwd()) {
|
|
|
14696
15122
|
process.exit(1);
|
|
14697
15123
|
}
|
|
14698
15124
|
intro2(`kaddo add ${mod.name}`);
|
|
14699
|
-
if (!exists(join(dir,
|
|
15125
|
+
if (!exists(join(dir, CONFIG_PATH7))) {
|
|
14700
15126
|
console.error("Kaddo is not initialized in this project.");
|
|
14701
15127
|
console.error("Run `kaddo init` first.");
|
|
14702
15128
|
process.exit(1);
|
|
@@ -14901,11 +15327,11 @@ function applyOwnership(raw, globs, mode = "replace") {
|
|
|
14901
15327
|
}
|
|
14902
15328
|
|
|
14903
15329
|
// src/commands/owners.ts
|
|
14904
|
-
var
|
|
15330
|
+
var CONFIG_PATH8 = ".kaddo/config.yml";
|
|
14905
15331
|
var CUSTOM_GLOB = "__custom__";
|
|
14906
15332
|
function runOwners(opts) {
|
|
14907
15333
|
const dir = cwd();
|
|
14908
|
-
if (!exists(join(dir,
|
|
15334
|
+
if (!exists(join(dir, CONFIG_PATH8))) {
|
|
14909
15335
|
console.error("No .kaddo/config.yml found. Run `kaddo init` first.");
|
|
14910
15336
|
process.exit(1);
|
|
14911
15337
|
}
|
|
@@ -14995,7 +15421,7 @@ async function chooseGlobs(suggested) {
|
|
|
14995
15421
|
}
|
|
14996
15422
|
async function runOwnersSuggest() {
|
|
14997
15423
|
const dir = cwd();
|
|
14998
|
-
if (!exists(join(dir,
|
|
15424
|
+
if (!exists(join(dir, CONFIG_PATH8))) {
|
|
14999
15425
|
console.error("No .kaddo/config.yml found. Run `kaddo init` first.");
|
|
15000
15426
|
process.exit(1);
|
|
15001
15427
|
}
|
|
@@ -15070,113 +15496,6 @@ ${globs.map((g) => ` - ${g}`).join("\n")}`);
|
|
|
15070
15496
|
outro2("Ownership declared.");
|
|
15071
15497
|
}
|
|
15072
15498
|
|
|
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
15499
|
// src/core/bootstrap-templates.ts
|
|
15181
15500
|
function fm(type, state) {
|
|
15182
15501
|
return `---
|
|
@@ -15608,7 +15927,7 @@ function baselineTemplate(kind, state) {
|
|
|
15608
15927
|
}
|
|
15609
15928
|
|
|
15610
15929
|
// src/commands/bootstrap.ts
|
|
15611
|
-
var
|
|
15930
|
+
var CONFIG_PATH9 = ".kaddo/config.yml";
|
|
15612
15931
|
var FILE_TARGETS = [
|
|
15613
15932
|
{ path: "knowledge/business/business.md", kind: "business" },
|
|
15614
15933
|
{ path: "knowledge/product/product.md", kind: "product" },
|
|
@@ -15665,7 +15984,7 @@ function bootstrap(dir) {
|
|
|
15665
15984
|
}
|
|
15666
15985
|
async function runBootstrap(dir = cwd()) {
|
|
15667
15986
|
intro2("kaddo bootstrap");
|
|
15668
|
-
if (!exists(join(dir,
|
|
15987
|
+
if (!exists(join(dir, CONFIG_PATH9))) {
|
|
15669
15988
|
console.error("Kaddo is not initialized in this project.");
|
|
15670
15989
|
console.error("Run `kaddo init` first.");
|
|
15671
15990
|
process.exit(1);
|
|
@@ -16257,7 +16576,7 @@ function runReportImpact(opts = {}) {
|
|
|
16257
16576
|
}
|
|
16258
16577
|
|
|
16259
16578
|
// src/core/savings.ts
|
|
16260
|
-
import { parse as
|
|
16579
|
+
import { parse as parseYaml16 } from "yaml";
|
|
16261
16580
|
var DEFAULT_ASSUMPTIONS = {
|
|
16262
16581
|
currency: "USD",
|
|
16263
16582
|
hourly_cost: 40,
|
|
@@ -16275,7 +16594,7 @@ function loadAssumptions(dir) {
|
|
|
16275
16594
|
const p2 = join(dir, SAVINGS_PATH);
|
|
16276
16595
|
if (!exists(p2)) return { assumptions: { ...DEFAULT_ASSUMPTIONS }, source: "default" };
|
|
16277
16596
|
try {
|
|
16278
|
-
const raw =
|
|
16597
|
+
const raw = parseYaml16(readFile(p2));
|
|
16279
16598
|
const a = raw?.assumptions ?? {};
|
|
16280
16599
|
const t = raw?.team ?? {};
|
|
16281
16600
|
const num = (v, d) => typeof v === "number" && Number.isFinite(v) ? v : d;
|
|
@@ -17057,7 +17376,7 @@ function runAssetsUpdate(kind, opts = {}) {
|
|
|
17057
17376
|
|
|
17058
17377
|
// src/commands/ready.ts
|
|
17059
17378
|
import fs4 from "fs";
|
|
17060
|
-
import
|
|
17379
|
+
import path7 from "path";
|
|
17061
17380
|
import matter11 from "gray-matter";
|
|
17062
17381
|
function validateReadiness(content, frontmatter) {
|
|
17063
17382
|
const warnings = [];
|
|
@@ -17120,10 +17439,10 @@ function transitionToReady(filePath) {
|
|
|
17120
17439
|
const posix = filePath.replace(/\\/g, "/");
|
|
17121
17440
|
let newPath = filePath;
|
|
17122
17441
|
if (posix.includes("/work-items/draft/")) {
|
|
17123
|
-
const readyDir =
|
|
17442
|
+
const readyDir = path7.dirname(filePath).replace(/[/\\]draft$/, path7.sep + "ready");
|
|
17124
17443
|
ensureDir(readyDir);
|
|
17125
|
-
const fileName =
|
|
17126
|
-
newPath =
|
|
17444
|
+
const fileName = path7.basename(filePath);
|
|
17445
|
+
newPath = path7.join(readyDir, fileName);
|
|
17127
17446
|
}
|
|
17128
17447
|
return { newPath, newContent };
|
|
17129
17448
|
}
|
|
@@ -17194,8 +17513,8 @@ async function runReady(id, opts = {}) {
|
|
|
17194
17513
|
fs4.unlinkSync(wi.filePath);
|
|
17195
17514
|
log2.success(`Updated status: ready`);
|
|
17196
17515
|
log2.success(`Moved file:`);
|
|
17197
|
-
log2.info(` ${
|
|
17198
|
-
log2.info(` \u2192 ${
|
|
17516
|
+
log2.info(` ${path7.relative(dir, wi.filePath)}`);
|
|
17517
|
+
log2.info(` \u2192 ${path7.relative(dir, newPath)}`);
|
|
17199
17518
|
} else {
|
|
17200
17519
|
writeFile(wi.filePath, newContent);
|
|
17201
17520
|
log2.success(`Updated status: ready`);
|
|
@@ -17229,8 +17548,8 @@ var capsuleCmd = program.command("capsule").description("Export this project as
|
|
|
17229
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) => {
|
|
17230
17549
|
runCapsuleExport(opts);
|
|
17231
17550
|
});
|
|
17232
|
-
capsuleCmd.command("add <path>").description("Register an external Knowledge Capsule as project context (.kaddo/external.yml)").action((
|
|
17233
|
-
runCapsuleAdd(
|
|
17551
|
+
capsuleCmd.command("add <path>").description("Register an external Knowledge Capsule as project context (.kaddo/external.yml)").action((path8) => {
|
|
17552
|
+
runCapsuleAdd(path8);
|
|
17234
17553
|
});
|
|
17235
17554
|
var graphCmd = program.command("graph").description("Export the lightweight, file-based knowledge graph of the project");
|
|
17236
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) => {
|
|
@@ -17328,16 +17647,23 @@ program.command("owners [action]").description("List domain owners, or run `owne
|
|
|
17328
17647
|
runOwners(opts);
|
|
17329
17648
|
}
|
|
17330
17649
|
});
|
|
17331
|
-
program.command("module").description("
|
|
17332
|
-
|
|
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`?");
|
|
17333
17653
|
});
|
|
17334
|
-
var modulesCmd = program.command("modules").description("
|
|
17335
|
-
modulesCmd.command("
|
|
17336
|
-
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);
|
|
17337
17657
|
});
|
|
17338
|
-
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(() => {
|
|
17339
17662
|
runModulesList();
|
|
17340
17663
|
});
|
|
17664
|
+
modulesCmd.command("validate").description("Validate registered modules against their current configuration").action(() => {
|
|
17665
|
+
runModulesValidate();
|
|
17666
|
+
});
|
|
17341
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) => {
|
|
17342
17668
|
runAdd(moduleName ?? "", { all: opts.all, group: opts.group });
|
|
17343
17669
|
});
|