@kaddo/mcp 3.115.0 → 3.117.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 +674 -156
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -371,6 +371,9 @@ function exists(p) {
|
|
|
371
371
|
function readFile(p) {
|
|
372
372
|
return fs2.readFileSync(p, "utf-8");
|
|
373
373
|
}
|
|
374
|
+
function ensureDir(dirPath) {
|
|
375
|
+
fs2.mkdirSync(dirPath, { recursive: true });
|
|
376
|
+
}
|
|
374
377
|
function readDir(dirPath) {
|
|
375
378
|
if (!exists(dirPath)) return [];
|
|
376
379
|
return fs2.readdirSync(dirPath);
|
|
@@ -390,13 +393,15 @@ import matter10 from "gray-matter";
|
|
|
390
393
|
|
|
391
394
|
// ../cli/src/core/config.ts
|
|
392
395
|
import { z } from "zod";
|
|
393
|
-
import { parse as parseYaml2 } from "yaml";
|
|
396
|
+
import { parse as parseYaml2, stringify as stringifyYaml } from "yaml";
|
|
394
397
|
var PROJECT_STATES = ["new", "pre-ai", "legacy", "ai-assisted"];
|
|
398
|
+
var PROJECT_MODES = ["standard", "poc"];
|
|
395
399
|
var TEAM_SIZES = ["indie", "small", "medium", "enterprise"];
|
|
396
400
|
var REPOSITORY_STRUCTURES = ["monorepo", "multirepo"];
|
|
397
401
|
var PROJECT_LANGUAGES = ["en", "es"];
|
|
398
402
|
var DEFAULTS = {
|
|
399
403
|
state: "pre-ai",
|
|
404
|
+
mode: "standard",
|
|
400
405
|
structure: "monorepo",
|
|
401
406
|
teamSize: "indie",
|
|
402
407
|
language: "en"
|
|
@@ -408,6 +413,9 @@ function projectLanguage(config) {
|
|
|
408
413
|
const lang = config.project.language;
|
|
409
414
|
return lang === "es" ? "es" : "en";
|
|
410
415
|
}
|
|
416
|
+
function projectMode(config) {
|
|
417
|
+
return config.project.mode === "poc" ? "poc" : "standard";
|
|
418
|
+
}
|
|
411
419
|
var ConfigError = class extends Error {
|
|
412
420
|
constructor(message) {
|
|
413
421
|
super(message);
|
|
@@ -419,6 +427,11 @@ var projectStateSchema = z.enum(["new", "pre-ai", "legacy", "ai-assisted"], {
|
|
|
419
427
|
message: `project.state must be one of: ${PROJECT_STATES.join(", ")}`
|
|
420
428
|
})
|
|
421
429
|
});
|
|
430
|
+
var projectModeSchema = z.enum(["standard", "poc"], {
|
|
431
|
+
errorMap: () => ({
|
|
432
|
+
message: `project.mode must be one of: ${PROJECT_MODES.join(", ")}`
|
|
433
|
+
})
|
|
434
|
+
});
|
|
422
435
|
var teamSizeSchema = z.enum(["indie", "small", "medium", "enterprise"], {
|
|
423
436
|
errorMap: () => ({
|
|
424
437
|
message: `team.size must be one of: ${TEAM_SIZES.join(", ")}`
|
|
@@ -440,6 +453,7 @@ var configSchema = z.object({
|
|
|
440
453
|
project: z.object({
|
|
441
454
|
name: z.string().default("project"),
|
|
442
455
|
state: projectStateSchema.default(DEFAULTS.state),
|
|
456
|
+
mode: projectModeSchema.default(DEFAULTS.mode),
|
|
443
457
|
structure: structureSchema.default(DEFAULTS.structure),
|
|
444
458
|
language: languageSchema.default(DEFAULTS.language),
|
|
445
459
|
role: multirepoRoleSchema,
|
|
@@ -485,8 +499,8 @@ function loadConfig(dir) {
|
|
|
485
499
|
const parsed = configSchema.safeParse(raw ?? {});
|
|
486
500
|
if (!parsed.success) {
|
|
487
501
|
const issues = parsed.error.issues.map((i) => {
|
|
488
|
-
const
|
|
489
|
-
return
|
|
502
|
+
const path9 = i.path.join(".");
|
|
503
|
+
return path9 ? ` - ${path9}: ${i.message}` : ` - ${i.message}`;
|
|
490
504
|
}).join("\n");
|
|
491
505
|
throw new ConfigError(`Invalid .kaddo/config.yml:
|
|
492
506
|
${issues}`);
|
|
@@ -888,7 +902,7 @@ function assessPhase(input) {
|
|
|
888
902
|
|
|
889
903
|
// ../cli/src/core/capsule.ts
|
|
890
904
|
import matter4 from "gray-matter";
|
|
891
|
-
import { parse as parseYaml7, stringify as
|
|
905
|
+
import { parse as parseYaml7, stringify as stringifyYaml4 } from "yaml";
|
|
892
906
|
|
|
893
907
|
// ../cli/src/services/owners.ts
|
|
894
908
|
import { parse as parseYaml3 } from "yaml";
|
|
@@ -927,11 +941,11 @@ function moduleArtifactCoverage(dir, id) {
|
|
|
927
941
|
};
|
|
928
942
|
}
|
|
929
943
|
function loadMappedModules(dir) {
|
|
930
|
-
const
|
|
931
|
-
if (!exists(
|
|
944
|
+
const path9 = join(dir, DESCRIPTOR_PATH);
|
|
945
|
+
if (!exists(path9)) return [];
|
|
932
946
|
let parsed;
|
|
933
947
|
try {
|
|
934
|
-
parsed = parseYaml4(readFile(
|
|
948
|
+
parsed = parseYaml4(readFile(path9));
|
|
935
949
|
} catch {
|
|
936
950
|
return [];
|
|
937
951
|
}
|
|
@@ -1142,7 +1156,7 @@ var Q2 = o("\u25B2", "!");
|
|
|
1142
1156
|
var ee = o("\u25A0", "x");
|
|
1143
1157
|
|
|
1144
1158
|
// ../cli/src/commands/modules-map.ts
|
|
1145
|
-
import { parse as parseYaml6, stringify as
|
|
1159
|
+
import { parse as parseYaml6, stringify as stringifyYaml3 } from "yaml";
|
|
1146
1160
|
|
|
1147
1161
|
// ../cli/src/templates/registry.ts
|
|
1148
1162
|
var QUALITY = "## Quality checklist";
|
|
@@ -2306,7 +2320,7 @@ ${QUALITY}
|
|
|
2306
2320
|
`;
|
|
2307
2321
|
|
|
2308
2322
|
// ../cli/src/commands/modules-discover.ts
|
|
2309
|
-
import { parse as parseYaml5, stringify as
|
|
2323
|
+
import { parse as parseYaml5, stringify as stringifyYaml2 } from "yaml";
|
|
2310
2324
|
|
|
2311
2325
|
// ../cli/src/core/capsule.ts
|
|
2312
2326
|
var KNOWLEDGE2 = "knowledge";
|
|
@@ -2438,7 +2452,7 @@ function sectionParagraph2(md, title) {
|
|
|
2438
2452
|
}
|
|
2439
2453
|
return "";
|
|
2440
2454
|
}
|
|
2441
|
-
function parseCapsule(id,
|
|
2455
|
+
function parseCapsule(id, path9, md) {
|
|
2442
2456
|
const { data } = matter4(md);
|
|
2443
2457
|
const updatedAt = data.updated_at ? String(data.updated_at) : void 0;
|
|
2444
2458
|
let ageDays = null;
|
|
@@ -2448,7 +2462,7 @@ function parseCapsule(id, path7, md) {
|
|
|
2448
2462
|
}
|
|
2449
2463
|
return {
|
|
2450
2464
|
id,
|
|
2451
|
-
path:
|
|
2465
|
+
path: path9,
|
|
2452
2466
|
system: data.system ? String(data.system) : id,
|
|
2453
2467
|
owner: data.owner ? String(data.owner) : void 0,
|
|
2454
2468
|
updatedAt,
|
|
@@ -2593,6 +2607,18 @@ function discoverInitiatives(dir) {
|
|
|
2593
2607
|
// ../cli/src/core/resources.ts
|
|
2594
2608
|
import matter6 from "gray-matter";
|
|
2595
2609
|
var RESOURCE_ARTIFACT_TYPE = "project-resource";
|
|
2610
|
+
var RESOURCE_TYPES = [
|
|
2611
|
+
"database",
|
|
2612
|
+
"cloud",
|
|
2613
|
+
"api",
|
|
2614
|
+
"queue",
|
|
2615
|
+
"storage",
|
|
2616
|
+
"repository",
|
|
2617
|
+
"platform",
|
|
2618
|
+
"service",
|
|
2619
|
+
"other"
|
|
2620
|
+
];
|
|
2621
|
+
var INTERFACE_TYPES = ["cli", "mcp", "api", "sql", "sdk", "iac", "console", "other"];
|
|
2596
2622
|
function str(v) {
|
|
2597
2623
|
return typeof v === "string" && v.trim() ? v.trim() : null;
|
|
2598
2624
|
}
|
|
@@ -2636,6 +2662,18 @@ function parseAuth(v) {
|
|
|
2636
2662
|
const refs = strArray(o2.refs).filter((r2) => !r2.includes("=") && /^[A-Za-z0-9_.-]+$/.test(r2));
|
|
2637
2663
|
return { mode: str(o2.mode), refs };
|
|
2638
2664
|
}
|
|
2665
|
+
function parseScope(v) {
|
|
2666
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) return null;
|
|
2667
|
+
const o2 = v;
|
|
2668
|
+
const type = str(o2.type);
|
|
2669
|
+
if (!type) return null;
|
|
2670
|
+
return { type, module: str(o2.module) };
|
|
2671
|
+
}
|
|
2672
|
+
function purposeFromBody(body) {
|
|
2673
|
+
const m2 = body.match(/^#{1,2}\s+Purpose\s*\n+([\s\S]*?)(?=\n#{1,2}\s|\s*$)/m);
|
|
2674
|
+
const text3 = m2 ? m2[1].trim() : "";
|
|
2675
|
+
return text3 || null;
|
|
2676
|
+
}
|
|
2639
2677
|
function toDetail(filePath, relPath, raw) {
|
|
2640
2678
|
const parsed = matter6(raw);
|
|
2641
2679
|
const fm = parsed.data ?? {};
|
|
@@ -2647,8 +2685,10 @@ function toDetail(filePath, relPath, raw) {
|
|
|
2647
2685
|
resourceType: str(fm.resource_type),
|
|
2648
2686
|
provider: str(fm.provider),
|
|
2649
2687
|
environments: strArray(fm.environments),
|
|
2688
|
+
scope: parseScope(fm.scope),
|
|
2689
|
+
modules: strArray(fm.modules),
|
|
2650
2690
|
path: relPath,
|
|
2651
|
-
purpose: str(fm.purpose),
|
|
2691
|
+
purpose: str(fm.purpose) ?? purposeFromBody(body),
|
|
2652
2692
|
interfaces: parseInterfaces(fm.access_interfaces),
|
|
2653
2693
|
boundaries: parseBoundaries(fm.access_boundaries),
|
|
2654
2694
|
...(() => {
|
|
@@ -2667,6 +2707,8 @@ function getResources(dir) {
|
|
|
2667
2707
|
resourceType: str(fm.resource_type),
|
|
2668
2708
|
provider: str(fm.provider),
|
|
2669
2709
|
environments: strArray(fm.environments),
|
|
2710
|
+
scope: parseScope(fm.scope),
|
|
2711
|
+
modules: strArray(fm.modules),
|
|
2670
2712
|
path: a2.relPath
|
|
2671
2713
|
};
|
|
2672
2714
|
}).sort((x2, y) => x2.id.localeCompare(y.id));
|
|
@@ -2737,9 +2779,9 @@ function buildGraph(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
2737
2779
|
];
|
|
2738
2780
|
const presentLayers = [];
|
|
2739
2781
|
for (const layer2 of layerDocs) {
|
|
2740
|
-
const
|
|
2741
|
-
if (
|
|
2742
|
-
addNode({ id: layer2.id, type: layer2.type, label: layer2.label, path:
|
|
2782
|
+
const path9 = layer2.files.map((f3) => `${KNOWLEDGE3}/${f3}`).find((rel) => exists(join(dir, rel)));
|
|
2783
|
+
if (path9) {
|
|
2784
|
+
addNode({ id: layer2.id, type: layer2.type, label: layer2.label, path: path9 });
|
|
2743
2785
|
presentLayers.push(layer2.id);
|
|
2744
2786
|
}
|
|
2745
2787
|
}
|
|
@@ -2782,12 +2824,20 @@ function buildGraph(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
2782
2824
|
const resourceTitleById = /* @__PURE__ */ new Map();
|
|
2783
2825
|
for (const r2 of getResources(dir)) {
|
|
2784
2826
|
resourceTitleById.set(r2.id, r2.title);
|
|
2827
|
+
const resId = `resource:${r2.id}`;
|
|
2785
2828
|
addNode({
|
|
2786
|
-
id:
|
|
2829
|
+
id: resId,
|
|
2787
2830
|
type: "project-resource",
|
|
2788
2831
|
label: r2.title ? `${r2.id} ${r2.title}`.trim() : r2.id,
|
|
2789
2832
|
path: r2.path
|
|
2790
2833
|
});
|
|
2834
|
+
const depModules = new Set(r2.modules);
|
|
2835
|
+
if (r2.scope?.type === "module" && r2.scope.module) depModules.add(r2.scope.module);
|
|
2836
|
+
for (const mod of depModules) {
|
|
2837
|
+
const modId = `module:${mod}`;
|
|
2838
|
+
addNode({ id: modId, type: "module", label: mod });
|
|
2839
|
+
addEdge(modId, resId, "depends_on");
|
|
2840
|
+
}
|
|
2791
2841
|
}
|
|
2792
2842
|
for (const wi of selectedWIs) {
|
|
2793
2843
|
const id = wi.id || wi.title;
|
|
@@ -4273,6 +4323,43 @@ function buildTechDecisions(dir) {
|
|
|
4273
4323
|
};
|
|
4274
4324
|
}
|
|
4275
4325
|
|
|
4326
|
+
// ../cli/src/core/poc.ts
|
|
4327
|
+
var POC_ARTIFACT_PATH = "knowledge/delivery/poc.md";
|
|
4328
|
+
var PLACEHOLDER = /^[_<].*[_>]$|^(describe|list|document|what|how)\b/i;
|
|
4329
|
+
function section2(markdown, heading) {
|
|
4330
|
+
const match = markdown.match(new RegExp(`^##\\s+${heading}\\s*$([\\s\\S]*?)(?=^##\\s+|(?![\\s\\S]))`, "im"));
|
|
4331
|
+
return match?.[1] ?? "";
|
|
4332
|
+
}
|
|
4333
|
+
function meaningful(text3) {
|
|
4334
|
+
return text3.split(/\r?\n/).some((line) => {
|
|
4335
|
+
const value = line.trim().replace(/^[-*]\s+(?:\[[^\]]+\]\s*)?/, "");
|
|
4336
|
+
return Boolean(value) && !PLACEHOLDER.test(value) && !/^status:\s*(pending|tbd)$/i.test(value);
|
|
4337
|
+
});
|
|
4338
|
+
}
|
|
4339
|
+
function readPocSummary(dir) {
|
|
4340
|
+
const path9 = join(dir, POC_ARTIFACT_PATH);
|
|
4341
|
+
if (!exists(path9)) {
|
|
4342
|
+
return { exists: false, hypothesisDefined: false, successCriteriaCount: 0, constraintsDefined: false, nonGoalsDefined: false, conclusion: "pending" };
|
|
4343
|
+
}
|
|
4344
|
+
let markdown = "";
|
|
4345
|
+
try {
|
|
4346
|
+
markdown = readFile(path9);
|
|
4347
|
+
} catch {
|
|
4348
|
+
return { exists: false, hypothesisDefined: false, successCriteriaCount: 0, constraintsDefined: false, nonGoalsDefined: false, conclusion: "pending" };
|
|
4349
|
+
}
|
|
4350
|
+
const criteria = section2(markdown, "Success Criteria").split(/\r?\n/).filter((line) => /^\s*[-*]\s+(?:\[[ xX]\]\s+)?/.test(line)).filter((line) => meaningful(line)).length;
|
|
4351
|
+
const conclusionBody = section2(markdown, "Conclusion");
|
|
4352
|
+
const conclusionMatch = conclusionBody.match(/\b(?:status|outcome)\s*:\s*(validated|rejected|inconclusive)\b/i);
|
|
4353
|
+
return {
|
|
4354
|
+
exists: true,
|
|
4355
|
+
hypothesisDefined: meaningful(section2(markdown, "Hypothesis")),
|
|
4356
|
+
successCriteriaCount: criteria,
|
|
4357
|
+
constraintsDefined: meaningful(section2(markdown, "Constraints")),
|
|
4358
|
+
nonGoalsDefined: meaningful(section2(markdown, "Non-goals")),
|
|
4359
|
+
conclusion: conclusionMatch ? conclusionMatch[1].toLowerCase() : "pending"
|
|
4360
|
+
};
|
|
4361
|
+
}
|
|
4362
|
+
|
|
4276
4363
|
// ../cli/src/core/next-step.ts
|
|
4277
4364
|
function buildDeliveryState(dir) {
|
|
4278
4365
|
const wis = discoverWorkItems(dir);
|
|
@@ -4360,13 +4447,47 @@ function resolveNextStep(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
4360
4447
|
return { id: "init", phase: "Setup", label: "Run `kaddo init` to initialize Kaddo.", command: "kaddo init", reason: "Kaddo is not initialized in this project." };
|
|
4361
4448
|
}
|
|
4362
4449
|
const state = config.project.state;
|
|
4450
|
+
if (projectMode(config) === "poc") {
|
|
4451
|
+
const poc = readPocSummary(dir);
|
|
4452
|
+
if (!poc.exists) {
|
|
4453
|
+
return { id: "bootstrap", phase: "Setup", label: "Run `kaddo bootstrap` to create the POC knowledge baseline.", command: "kaddo bootstrap", reason: `Missing ${POC_ARTIFACT_PATH}.` };
|
|
4454
|
+
}
|
|
4455
|
+
if (!poc.hypothesisDefined) {
|
|
4456
|
+
return { id: "poc-hypothesis", phase: "POC Definition", label: "Define the POC hypothesis.", target: POC_ARTIFACT_PATH, reason: "The POC needs a testable hypothesis before work begins." };
|
|
4457
|
+
}
|
|
4458
|
+
if (poc.successCriteriaCount === 0) {
|
|
4459
|
+
return { id: "poc-success-criteria", phase: "POC Definition", label: "Define observable POC success criteria.", target: POC_ARTIFACT_PATH, reason: "No success criteria are defined for the hypothesis." };
|
|
4460
|
+
}
|
|
4461
|
+
const qCurrentState2 = analyzeKnowledgeArtifact(dir, CS);
|
|
4462
|
+
const qCodebase2 = analyzeKnowledgeArtifact(dir, CB);
|
|
4463
|
+
if (qCurrentState2 !== "useful" || qCodebase2 !== "useful") {
|
|
4464
|
+
return { id: "poc-technical-context", phase: "POC Discovery", label: "Document the minimal technical context for the experiment.", target: qCurrentState2 !== "useful" ? CS : CB, agent: "architecture-agent", reason: "The experiment surface and constraints are not yet sufficiently documented." };
|
|
4465
|
+
}
|
|
4466
|
+
if (!exists(join(dir, "knowledge", "resources"))) {
|
|
4467
|
+
return { id: "poc-resources", phase: "POC Discovery", label: "Identify the project resources relevant to the experiment.", command: "kaddo resources list", reason: "No project resource catalog is available for the experiment context." };
|
|
4468
|
+
}
|
|
4469
|
+
const delivery = buildDeliveryState(dir);
|
|
4470
|
+
if (delivery.total_work_items === 0) {
|
|
4471
|
+
return { id: "poc-create-experiment", phase: "Experiment", label: "Create the first experiment Work Item (prefer `spike`).", command: "kaddo create spike", reason: "The POC has no Work Item to produce evidence." };
|
|
4472
|
+
}
|
|
4473
|
+
if (delivery.in_progress_work_items > 0) {
|
|
4474
|
+
return { id: "guard", phase: "Experiment", label: "Run `kaddo guard` and capture evidence from the experiment.", command: "kaddo guard", reason: `${delivery.in_progress_work_items} experiment Work Item(s) are in progress.` };
|
|
4475
|
+
}
|
|
4476
|
+
if (delivery.ready_work_items > 0 || delivery.draft_work_items > 0 || delivery.blocked_work_items > 0) {
|
|
4477
|
+
return { id: delivery.ready_work_items > 0 ? "implement-work-item" : "refine-work-item", phase: "Experiment", label: delivery.ready_work_items > 0 ? "Implement the ready experiment Work Item and collect evidence." : "Refine the experiment Work Item before running it.", agent: "work-item-agent", reason: "The POC evidence Work Item is not complete yet." };
|
|
4478
|
+
}
|
|
4479
|
+
if (poc.conclusion === "pending") {
|
|
4480
|
+
return { id: "poc-evaluate", phase: "Evaluation", label: "Evaluate the experiment evidence and record the POC conclusion.", target: POC_ARTIFACT_PATH, reason: "Experiment Work Items are complete but the POC conclusion is pending." };
|
|
4481
|
+
}
|
|
4482
|
+
return { id: "poc-complete", phase: "Conclusion", label: `POC concluded: ${poc.conclusion}. Decide whether to graduate it to standard delivery.`, target: POC_ARTIFACT_PATH, reason: "The POC conclusion is recorded from experiment evidence." };
|
|
4483
|
+
}
|
|
4363
4484
|
const q3 = (rel) => analyzeKnowledgeArtifact(dir, rel);
|
|
4364
4485
|
const resolveAgent = (agent) => {
|
|
4365
4486
|
const file = agent.endsWith(".md") ? agent : `${agent}.md`;
|
|
4366
|
-
const
|
|
4367
|
-
const installed = isFile(join(dir,
|
|
4487
|
+
const path9 = agentInstallPath(file);
|
|
4488
|
+
const installed = isFile(join(dir, path9));
|
|
4368
4489
|
const group = agentGroupOf(file);
|
|
4369
|
-
return { agentPath:
|
|
4490
|
+
return { agentPath: path9, agentInstalled: installed, installCommand: installed ? void 0 : `kaddo add agents --group ${group}` };
|
|
4370
4491
|
};
|
|
4371
4492
|
const moduleRepo = isModule(config);
|
|
4372
4493
|
const coreRepo = isCore(config);
|
|
@@ -6121,6 +6242,16 @@ supporting notes as \`knowledge/tech/discovery/architecture-notes.md\` and decis
|
|
|
6121
6242
|
under \`knowledge/tech/decisions/\`. Kaddo still reads the legacy root locations for backward
|
|
6122
6243
|
compatibility, but new output should use \`discovery/\`.
|
|
6123
6244
|
|
|
6245
|
+
## Project Resource candidates
|
|
6246
|
+
|
|
6247
|
+
When the knowledge describes an external system the project depends on (a database, cloud account,
|
|
6248
|
+
API, queue, storage, service\u2026), you may propose it as a **candidate** Project Resource \u2014 never create
|
|
6249
|
+
it silently. Discovery \u2260 creation: present candidates for human review and materialize only through
|
|
6250
|
+
\`kaddo_create_resource\` / \`kaddo resources create\` with explicit confirmation. Ground every
|
|
6251
|
+
candidate in the knowledge (no guessing from ambiguous names). Model the stable system, not its access
|
|
6252
|
+
interface (CLI/MCP/SQL/API are interfaces); keep \`resources\` separate from \`affected_modules\`; and
|
|
6253
|
+
never include secret values \u2014 only reference names.
|
|
6254
|
+
|
|
6124
6255
|
## Quality Checklist
|
|
6125
6256
|
|
|
6126
6257
|
- Every component is backed by evidence from the context pack.
|
|
@@ -8601,6 +8732,47 @@ var LEGACY_STEPS = [
|
|
|
8601
8732
|
runGuard,
|
|
8602
8733
|
captureLearning
|
|
8603
8734
|
];
|
|
8735
|
+
var definePocHypothesis = {
|
|
8736
|
+
id: "poc-hypothesis",
|
|
8737
|
+
label: "Define hypothesis",
|
|
8738
|
+
evaluate: (ctx) => {
|
|
8739
|
+
const poc = readPocSummary(ctx.dir);
|
|
8740
|
+
return { status: poc.hypothesisDefined ? "done" : "current", evidence: poc.hypothesisDefined ? [POC_ARTIFACT_PATH] : void 0, reason: poc.hypothesisDefined ? void 0 : "A POC starts with one testable hypothesis." };
|
|
8741
|
+
}
|
|
8742
|
+
};
|
|
8743
|
+
var definePocCriteria = {
|
|
8744
|
+
id: "poc-success-criteria",
|
|
8745
|
+
label: "Define success criteria",
|
|
8746
|
+
evaluate: (ctx) => {
|
|
8747
|
+
const poc = readPocSummary(ctx.dir);
|
|
8748
|
+
return { status: poc.successCriteriaCount > 0 ? "done" : "pending", evidence: poc.successCriteriaCount > 0 ? [POC_ARTIFACT_PATH] : void 0, reason: poc.successCriteriaCount > 0 ? void 0 : "Define observable evidence for the hypothesis." };
|
|
8749
|
+
}
|
|
8750
|
+
};
|
|
8751
|
+
var describePocTech = { ...describeArchitecture, id: "poc-technical-context", label: "Document minimal technical context" };
|
|
8752
|
+
var identifyPocResources = {
|
|
8753
|
+
id: "poc-resources",
|
|
8754
|
+
label: "Identify relevant resources",
|
|
8755
|
+
evaluate: (ctx) => exists(join(ctx.dir, "knowledge/resources")) ? { status: "done", evidence: ["knowledge/resources/"] } : { status: "pending", command: "kaddo resources list" }
|
|
8756
|
+
};
|
|
8757
|
+
var createPocExperiment = {
|
|
8758
|
+
id: "poc-create-experiment",
|
|
8759
|
+
label: "Create experiment Work Item",
|
|
8760
|
+
evaluate: (ctx) => ctx.totalWorkItems > 0 ? { status: "done", evidence: ["knowledge/delivery/work-items/"] } : { status: "pending", command: "kaddo create spike", reason: "Prefer a spike for a focused experiment." }
|
|
8761
|
+
};
|
|
8762
|
+
var capturePocEvidence = {
|
|
8763
|
+
id: "poc-evidence",
|
|
8764
|
+
label: "Capture experiment evidence",
|
|
8765
|
+
evaluate: (ctx) => ctx.completedWorkItems > 0 ? { status: "done", evidence: ["knowledge/delivery/work-items/"] } : { status: "pending", reason: "Complete an experiment Work Item and link its evidence in the POC artifact." }
|
|
8766
|
+
};
|
|
8767
|
+
var concludePoc = {
|
|
8768
|
+
id: "poc-evaluate",
|
|
8769
|
+
label: "Evaluate and conclude",
|
|
8770
|
+
evaluate: (ctx) => {
|
|
8771
|
+
const conclusion = readPocSummary(ctx.dir).conclusion;
|
|
8772
|
+
return conclusion === "pending" ? { status: "pending", reason: "Record validated, rejected, or inconclusive from the evidence." } : { status: "done", evidence: [POC_ARTIFACT_PATH], reason: `Conclusion: ${conclusion}.` };
|
|
8773
|
+
}
|
|
8774
|
+
};
|
|
8775
|
+
var POC_STEPS = [enableKaddo, bootstrapBaseline, definePocHypothesis, definePocCriteria, describePocTech, identifyPocResources, createPocExperiment, capturePocEvidence, concludePoc];
|
|
8604
8776
|
function stepsForState(state) {
|
|
8605
8777
|
switch (state) {
|
|
8606
8778
|
case "new":
|
|
@@ -8697,14 +8869,21 @@ function mapNextStepId(id) {
|
|
|
8697
8869
|
"init-module-context": "enable-kaddo",
|
|
8698
8870
|
"module-ready": "ready-for-core-orchestration",
|
|
8699
8871
|
"validate-work-item-modules": "validate-module-knowledge",
|
|
8700
|
-
"modules-discover": "enable-kaddo"
|
|
8872
|
+
"modules-discover": "enable-kaddo",
|
|
8873
|
+
"poc-hypothesis": "poc-hypothesis",
|
|
8874
|
+
"poc-success-criteria": "poc-success-criteria",
|
|
8875
|
+
"poc-technical-context": "poc-technical-context",
|
|
8876
|
+
"poc-resources": "poc-resources",
|
|
8877
|
+
"poc-create-experiment": "poc-create-experiment",
|
|
8878
|
+
"poc-evaluate": "poc-evaluate",
|
|
8879
|
+
"poc-complete": "poc-evaluate"
|
|
8701
8880
|
};
|
|
8702
8881
|
return MAP[id] ?? id;
|
|
8703
8882
|
}
|
|
8704
8883
|
function buildProjectRoute(dir) {
|
|
8705
8884
|
const config = loadConfig(dir);
|
|
8706
8885
|
const state = config?.project.state ?? "pre-ai";
|
|
8707
|
-
const defs = config && isModule(config) ? MODULE_STEPS : stepsForState(state);
|
|
8886
|
+
const defs = config && projectMode(config) === "poc" ? POC_STEPS : config && isModule(config) ? MODULE_STEPS : stepsForState(state);
|
|
8708
8887
|
const ctx = buildRouteContext(dir);
|
|
8709
8888
|
const steps = defs.map((d3) => {
|
|
8710
8889
|
const result = d3.evaluate(ctx);
|
|
@@ -9701,7 +9880,7 @@ function buildGuardHistory(dir) {
|
|
|
9701
9880
|
const d3 = hotspotDir(t.code_path);
|
|
9702
9881
|
hotspotMap.set(d3, (hotspotMap.get(d3) ?? 0) + 1);
|
|
9703
9882
|
}
|
|
9704
|
-
const hotspots = [...hotspotMap.entries()].map(([
|
|
9883
|
+
const hotspots = [...hotspotMap.entries()].map(([path9, warnings]) => ({ path: path9, warnings })).sort((a2, b2) => b2.warnings - a2.warnings);
|
|
9705
9884
|
return {
|
|
9706
9885
|
available: true,
|
|
9707
9886
|
total_runs: sorted.length,
|
|
@@ -10777,6 +10956,18 @@ var RESOURCES = [
|
|
|
10777
10956
|
"Roadmap not found. Create knowledge/delivery/roadmap.md (e.g. via the roadmap-agent) first."
|
|
10778
10957
|
)
|
|
10779
10958
|
},
|
|
10959
|
+
{
|
|
10960
|
+
uri: "kaddo://poc",
|
|
10961
|
+
name: "Kaddo proof of concept",
|
|
10962
|
+
description: "Canonical POC hypothesis, success criteria, evidence, and conclusion.",
|
|
10963
|
+
mimeType: "text/markdown",
|
|
10964
|
+
read: (root) => fileOrHint(
|
|
10965
|
+
root,
|
|
10966
|
+
"kaddo://poc",
|
|
10967
|
+
"knowledge/delivery/poc.md",
|
|
10968
|
+
"POC artifact not found. Set `project.mode: poc` and run `kaddo bootstrap` first."
|
|
10969
|
+
)
|
|
10970
|
+
},
|
|
10780
10971
|
{
|
|
10781
10972
|
uri: "kaddo://capsules",
|
|
10782
10973
|
name: "Kaddo knowledge capsules",
|
|
@@ -11060,20 +11251,20 @@ function getPrompt(root, name) {
|
|
|
11060
11251
|
}
|
|
11061
11252
|
|
|
11062
11253
|
// src/tools.ts
|
|
11063
|
-
import
|
|
11254
|
+
import matter15 from "gray-matter";
|
|
11064
11255
|
|
|
11065
11256
|
// ../cli/dist/core.js
|
|
11066
11257
|
import fs3 from "fs";
|
|
11067
11258
|
import path3 from "path";
|
|
11068
11259
|
import { z as z3 } from "zod";
|
|
11069
|
-
import { parse as parseYaml10 } from "yaml";
|
|
11260
|
+
import { parse as parseYaml10, stringify as stringifyYaml5 } from "yaml";
|
|
11070
11261
|
import matter12 from "gray-matter";
|
|
11071
11262
|
import matter22 from "gray-matter";
|
|
11072
|
-
import { parse as parseYaml62, stringify as
|
|
11263
|
+
import { parse as parseYaml62, stringify as stringifyYaml42 } from "yaml";
|
|
11073
11264
|
import { parse as parseYaml22 } from "yaml";
|
|
11074
11265
|
import { parse as parseYaml32 } from "yaml";
|
|
11075
|
-
import { parse as parseYaml52, stringify as
|
|
11076
|
-
import { parse as parseYaml42, stringify as
|
|
11266
|
+
import { parse as parseYaml52, stringify as stringifyYaml32 } from "yaml";
|
|
11267
|
+
import { parse as parseYaml42, stringify as stringifyYaml22 } from "yaml";
|
|
11077
11268
|
import matter32 from "gray-matter";
|
|
11078
11269
|
import matter42 from "gray-matter";
|
|
11079
11270
|
import matter52 from "gray-matter";
|
|
@@ -11084,18 +11275,19 @@ import { dirname as dirname2, join as join22, parse as parse2 } from "path";
|
|
|
11084
11275
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
11085
11276
|
import matter72 from "gray-matter";
|
|
11086
11277
|
import matter82 from "gray-matter";
|
|
11087
|
-
import { parse as parseYaml82, stringify as stringifyYaml42 } from "yaml";
|
|
11088
|
-
import fs32 from "fs";
|
|
11089
|
-
import path32 from "path";
|
|
11090
|
-
import crypto2 from "crypto";
|
|
11091
11278
|
import matter92 from "gray-matter";
|
|
11092
|
-
import
|
|
11279
|
+
import { parse as parseYaml82, stringify as stringifyYaml52 } from "yaml";
|
|
11093
11280
|
import fs4 from "fs";
|
|
11281
|
+
import path4 from "path";
|
|
11282
|
+
import crypto2 from "crypto";
|
|
11094
11283
|
import matter102 from "gray-matter";
|
|
11284
|
+
import crypto3 from "crypto";
|
|
11285
|
+
import fs5 from "fs";
|
|
11095
11286
|
import matter112 from "gray-matter";
|
|
11096
|
-
import { parse as parseYaml92, stringify as stringifyYaml5 } from "yaml";
|
|
11097
|
-
import { parse as parseYaml102, stringify as stringifyYaml6 } from "yaml";
|
|
11098
11287
|
import matter122 from "gray-matter";
|
|
11288
|
+
import { parse as parseYaml92, stringify as stringifyYaml6 } from "yaml";
|
|
11289
|
+
import { parse as parseYaml102, stringify as stringifyYaml7 } from "yaml";
|
|
11290
|
+
import matter13 from "gray-matter";
|
|
11099
11291
|
function exists2(p) {
|
|
11100
11292
|
return fs3.existsSync(p);
|
|
11101
11293
|
}
|
|
@@ -11120,11 +11312,13 @@ function join3(...parts) {
|
|
|
11120
11312
|
return path3.join(...parts);
|
|
11121
11313
|
}
|
|
11122
11314
|
var PROJECT_STATES2 = ["new", "pre-ai", "legacy", "ai-assisted"];
|
|
11315
|
+
var PROJECT_MODES2 = ["standard", "poc"];
|
|
11123
11316
|
var TEAM_SIZES2 = ["indie", "small", "medium", "enterprise"];
|
|
11124
11317
|
var REPOSITORY_STRUCTURES2 = ["monorepo", "multirepo"];
|
|
11125
11318
|
var PROJECT_LANGUAGES2 = ["en", "es"];
|
|
11126
11319
|
var DEFAULTS2 = {
|
|
11127
11320
|
state: "pre-ai",
|
|
11321
|
+
mode: "standard",
|
|
11128
11322
|
structure: "monorepo",
|
|
11129
11323
|
teamSize: "indie",
|
|
11130
11324
|
language: "en"
|
|
@@ -11140,6 +11334,11 @@ var projectStateSchema2 = z3.enum(["new", "pre-ai", "legacy", "ai-assisted"], {
|
|
|
11140
11334
|
message: `project.state must be one of: ${PROJECT_STATES2.join(", ")}`
|
|
11141
11335
|
})
|
|
11142
11336
|
});
|
|
11337
|
+
var projectModeSchema2 = z3.enum(["standard", "poc"], {
|
|
11338
|
+
errorMap: () => ({
|
|
11339
|
+
message: `project.mode must be one of: ${PROJECT_MODES2.join(", ")}`
|
|
11340
|
+
})
|
|
11341
|
+
});
|
|
11143
11342
|
var teamSizeSchema2 = z3.enum(["indie", "small", "medium", "enterprise"], {
|
|
11144
11343
|
errorMap: () => ({
|
|
11145
11344
|
message: `team.size must be one of: ${TEAM_SIZES2.join(", ")}`
|
|
@@ -11161,6 +11360,7 @@ var configSchema2 = z3.object({
|
|
|
11161
11360
|
project: z3.object({
|
|
11162
11361
|
name: z3.string().default("project"),
|
|
11163
11362
|
state: projectStateSchema2.default(DEFAULTS2.state),
|
|
11363
|
+
mode: projectModeSchema2.default(DEFAULTS2.mode),
|
|
11164
11364
|
structure: structureSchema2.default(DEFAULTS2.structure),
|
|
11165
11365
|
language: languageSchema2.default(DEFAULTS2.language),
|
|
11166
11366
|
role: multirepoRoleSchema2,
|
|
@@ -11206,8 +11406,8 @@ function loadConfig2(dir) {
|
|
|
11206
11406
|
const parsed = configSchema2.safeParse(raw ?? {});
|
|
11207
11407
|
if (!parsed.success) {
|
|
11208
11408
|
const issues = parsed.error.issues.map((i) => {
|
|
11209
|
-
const
|
|
11210
|
-
return
|
|
11409
|
+
const path52 = i.path.join(".");
|
|
11410
|
+
return path52 ? ` - ${path52}: ${i.message}` : ` - ${i.message}`;
|
|
11211
11411
|
}).join("\n");
|
|
11212
11412
|
throw new ConfigError2(`Invalid .kaddo/config.yml:
|
|
11213
11413
|
${issues}`);
|
|
@@ -11411,11 +11611,11 @@ function moduleArtifactCoverage2(dir, id) {
|
|
|
11411
11611
|
};
|
|
11412
11612
|
}
|
|
11413
11613
|
function loadMappedModules2(dir) {
|
|
11414
|
-
const
|
|
11415
|
-
if (!exists2(
|
|
11614
|
+
const path52 = join3(dir, DESCRIPTOR_PATH2);
|
|
11615
|
+
if (!exists2(path52)) return [];
|
|
11416
11616
|
let parsed;
|
|
11417
11617
|
try {
|
|
11418
|
-
parsed = parseYaml32(readFile2(
|
|
11618
|
+
parsed = parseYaml32(readFile2(path52));
|
|
11419
11619
|
} catch {
|
|
11420
11620
|
return [];
|
|
11421
11621
|
}
|
|
@@ -12861,6 +13061,18 @@ function parseAuth2(v) {
|
|
|
12861
13061
|
const refs = strArray3(o2.refs).filter((r2) => !r2.includes("=") && /^[A-Za-z0-9_.-]+$/.test(r2));
|
|
12862
13062
|
return { mode: str2(o2.mode), refs };
|
|
12863
13063
|
}
|
|
13064
|
+
function parseScope2(v) {
|
|
13065
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) return null;
|
|
13066
|
+
const o2 = v;
|
|
13067
|
+
const type = str2(o2.type);
|
|
13068
|
+
if (!type) return null;
|
|
13069
|
+
return { type, module: str2(o2.module) };
|
|
13070
|
+
}
|
|
13071
|
+
function purposeFromBody2(body) {
|
|
13072
|
+
const m2 = body.match(/^#{1,2}\s+Purpose\s*\n+([\s\S]*?)(?=\n#{1,2}\s|\s*$)/m);
|
|
13073
|
+
const text22 = m2 ? m2[1].trim() : "";
|
|
13074
|
+
return text22 || null;
|
|
13075
|
+
}
|
|
12864
13076
|
function toDetail2(filePath, relPath, raw) {
|
|
12865
13077
|
const parsed = matter42(raw);
|
|
12866
13078
|
const fm = parsed.data ?? {};
|
|
@@ -12872,8 +13084,10 @@ function toDetail2(filePath, relPath, raw) {
|
|
|
12872
13084
|
resourceType: str2(fm.resource_type),
|
|
12873
13085
|
provider: str2(fm.provider),
|
|
12874
13086
|
environments: strArray3(fm.environments),
|
|
13087
|
+
scope: parseScope2(fm.scope),
|
|
13088
|
+
modules: strArray3(fm.modules),
|
|
12875
13089
|
path: relPath,
|
|
12876
|
-
purpose: str2(fm.purpose),
|
|
13090
|
+
purpose: str2(fm.purpose) ?? purposeFromBody2(body),
|
|
12877
13091
|
interfaces: parseInterfaces2(fm.access_interfaces),
|
|
12878
13092
|
boundaries: parseBoundaries2(fm.access_boundaries),
|
|
12879
13093
|
...(() => {
|
|
@@ -12892,6 +13106,8 @@ function getResources2(dir) {
|
|
|
12892
13106
|
resourceType: str2(fm.resource_type),
|
|
12893
13107
|
provider: str2(fm.provider),
|
|
12894
13108
|
environments: strArray3(fm.environments),
|
|
13109
|
+
scope: parseScope2(fm.scope),
|
|
13110
|
+
modules: strArray3(fm.modules),
|
|
12895
13111
|
path: a2.relPath
|
|
12896
13112
|
};
|
|
12897
13113
|
}).sort((x2, y) => x2.id.localeCompare(y.id));
|
|
@@ -12960,9 +13176,9 @@ function buildGraph2(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
12960
13176
|
];
|
|
12961
13177
|
const presentLayers = [];
|
|
12962
13178
|
for (const layer2 of layerDocs) {
|
|
12963
|
-
const
|
|
12964
|
-
if (
|
|
12965
|
-
addNode({ id: layer2.id, type: layer2.type, label: layer2.label, path:
|
|
13179
|
+
const path52 = layer2.files.map((f3) => `${KNOWLEDGE22}/${f3}`).find((rel) => exists2(join3(dir, rel)));
|
|
13180
|
+
if (path52) {
|
|
13181
|
+
addNode({ id: layer2.id, type: layer2.type, label: layer2.label, path: path52 });
|
|
12966
13182
|
presentLayers.push(layer2.id);
|
|
12967
13183
|
}
|
|
12968
13184
|
}
|
|
@@ -13005,12 +13221,20 @@ function buildGraph2(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
13005
13221
|
const resourceTitleById = /* @__PURE__ */ new Map();
|
|
13006
13222
|
for (const r2 of getResources2(dir)) {
|
|
13007
13223
|
resourceTitleById.set(r2.id, r2.title);
|
|
13224
|
+
const resId = `resource:${r2.id}`;
|
|
13008
13225
|
addNode({
|
|
13009
|
-
id:
|
|
13226
|
+
id: resId,
|
|
13010
13227
|
type: "project-resource",
|
|
13011
13228
|
label: r2.title ? `${r2.id} ${r2.title}`.trim() : r2.id,
|
|
13012
13229
|
path: r2.path
|
|
13013
13230
|
});
|
|
13231
|
+
const depModules = new Set(r2.modules);
|
|
13232
|
+
if (r2.scope?.type === "module" && r2.scope.module) depModules.add(r2.scope.module);
|
|
13233
|
+
for (const mod of depModules) {
|
|
13234
|
+
const modId = `module:${mod}`;
|
|
13235
|
+
addNode({ id: modId, type: "module", label: mod });
|
|
13236
|
+
addEdge(modId, resId, "depends_on");
|
|
13237
|
+
}
|
|
13014
13238
|
}
|
|
13015
13239
|
for (const wi of selectedWIs) {
|
|
13016
13240
|
const id = wi.id || wi.title;
|
|
@@ -14698,6 +14922,16 @@ supporting notes as \`knowledge/tech/discovery/architecture-notes.md\` and decis
|
|
|
14698
14922
|
under \`knowledge/tech/decisions/\`. Kaddo still reads the legacy root locations for backward
|
|
14699
14923
|
compatibility, but new output should use \`discovery/\`.
|
|
14700
14924
|
|
|
14925
|
+
## Project Resource candidates
|
|
14926
|
+
|
|
14927
|
+
When the knowledge describes an external system the project depends on (a database, cloud account,
|
|
14928
|
+
API, queue, storage, service\u2026), you may propose it as a **candidate** Project Resource \u2014 never create
|
|
14929
|
+
it silently. Discovery \u2260 creation: present candidates for human review and materialize only through
|
|
14930
|
+
\`kaddo_create_resource\` / \`kaddo resources create\` with explicit confirmation. Ground every
|
|
14931
|
+
candidate in the knowledge (no guessing from ambiguous names). Model the stable system, not its access
|
|
14932
|
+
interface (CLI/MCP/SQL/API are interfaces); keep \`resources\` separate from \`affected_modules\`; and
|
|
14933
|
+
never include secret values \u2014 only reference names.
|
|
14934
|
+
|
|
14701
14935
|
## Quality Checklist
|
|
14702
14936
|
|
|
14703
14937
|
- Every component is backed by evidence from the context pack.
|
|
@@ -16904,6 +17138,7 @@ var LEGACY_STEPS2 = [
|
|
|
16904
17138
|
runGuard2,
|
|
16905
17139
|
captureLearning2
|
|
16906
17140
|
];
|
|
17141
|
+
var describePocTech2 = { ...describeArchitecture2, id: "poc-technical-context", label: "Document minimal technical context" };
|
|
16907
17142
|
var USER_FACING_KEYWORDS_EN = [
|
|
16908
17143
|
"user",
|
|
16909
17144
|
"visitor",
|
|
@@ -17251,8 +17486,8 @@ function parseGraphReason(v) {
|
|
|
17251
17486
|
if (!v || typeof v !== "object") return null;
|
|
17252
17487
|
const o2 = v;
|
|
17253
17488
|
const relationship = typeof o2.relationship === "string" && o2.relationship.trim() ? o2.relationship.trim() : null;
|
|
17254
|
-
const
|
|
17255
|
-
return relationship ||
|
|
17489
|
+
const path52 = Array.isArray(o2.path) ? o2.path.map(String).filter(Boolean) : [];
|
|
17490
|
+
return relationship || path52.length ? { relationship, path: path52 } : null;
|
|
17256
17491
|
}
|
|
17257
17492
|
function parseExplain(o2) {
|
|
17258
17493
|
const refs = Array.isArray(o2.evidence) ? o2.evidence : Array.isArray(o2.evidence_refs) ? o2.evidence_refs : [];
|
|
@@ -17307,7 +17542,7 @@ function parseOriginalSnapshot(fm) {
|
|
|
17307
17542
|
function readBody(filePath) {
|
|
17308
17543
|
try {
|
|
17309
17544
|
const raw = readFile2(filePath);
|
|
17310
|
-
return
|
|
17545
|
+
return matter92(raw).content;
|
|
17311
17546
|
} catch {
|
|
17312
17547
|
return "";
|
|
17313
17548
|
}
|
|
@@ -17509,7 +17744,7 @@ function nextWorkItemId(dir) {
|
|
|
17509
17744
|
let max = 0;
|
|
17510
17745
|
const walk = (d3) => {
|
|
17511
17746
|
if (!exists2(d3)) return;
|
|
17512
|
-
for (const entry of
|
|
17747
|
+
for (const entry of fs4.readdirSync(d3)) {
|
|
17513
17748
|
const full = join3(d3, entry);
|
|
17514
17749
|
if (isFile2(full)) {
|
|
17515
17750
|
const m2 = entry.match(/WI-(\d+)/);
|
|
@@ -17522,15 +17757,15 @@ function nextWorkItemId(dir) {
|
|
|
17522
17757
|
walk(wiDir);
|
|
17523
17758
|
return `WI-${String(max + 1).padStart(3, "0")}`;
|
|
17524
17759
|
}
|
|
17525
|
-
function
|
|
17526
|
-
|
|
17760
|
+
function atomicWrite3(filePath, content) {
|
|
17761
|
+
fs4.mkdirSync(path4.dirname(filePath), { recursive: true });
|
|
17527
17762
|
const tmp = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
17528
|
-
|
|
17763
|
+
fs4.writeFileSync(tmp, content, "utf-8");
|
|
17529
17764
|
try {
|
|
17530
|
-
|
|
17765
|
+
fs4.renameSync(tmp, filePath);
|
|
17531
17766
|
} catch (err) {
|
|
17532
17767
|
try {
|
|
17533
|
-
|
|
17768
|
+
fs4.rmSync(tmp, { force: true });
|
|
17534
17769
|
} catch {
|
|
17535
17770
|
}
|
|
17536
17771
|
throw err;
|
|
@@ -17591,8 +17826,8 @@ function ensureH1(body, title) {
|
|
|
17591
17826
|
|
|
17592
17827
|
${trimmed}`;
|
|
17593
17828
|
}
|
|
17594
|
-
function
|
|
17595
|
-
return
|
|
17829
|
+
function serialize2(data, body) {
|
|
17830
|
+
return matter102.stringify(`
|
|
17596
17831
|
${body.trim()}
|
|
17597
17832
|
`, data);
|
|
17598
17833
|
}
|
|
@@ -17660,8 +17895,8 @@ function createWorkItem(dir, opts) {
|
|
|
17660
17895
|
const relPath = `${WORK_ITEMS_DIR}/draft/${id}-${slugify22(title)}.md`;
|
|
17661
17896
|
const filePath = join3(dir, relPath);
|
|
17662
17897
|
if (exists2(filePath)) throw new WorkItemWriteError("INVALID_INPUT", `Work Item file already exists: ${relPath}`);
|
|
17663
|
-
const raw =
|
|
17664
|
-
|
|
17898
|
+
const raw = serialize2(data, body);
|
|
17899
|
+
atomicWrite3(filePath, raw);
|
|
17665
17900
|
return { id, path: relPath, revision: revisionOf(raw) };
|
|
17666
17901
|
}
|
|
17667
17902
|
function dimensionOf(type) {
|
|
@@ -18383,7 +18618,7 @@ function detectFormat(raw) {
|
|
|
18383
18618
|
if (!trimmed) throw new ImportError("EMPTY_INPUT", "Import content is empty.");
|
|
18384
18619
|
if (trimmed.startsWith("---")) {
|
|
18385
18620
|
try {
|
|
18386
|
-
const { data } =
|
|
18621
|
+
const { data } = matter112(trimmed);
|
|
18387
18622
|
if (data && typeof data === "object" && Object.keys(data).length > 0) {
|
|
18388
18623
|
const d3 = data;
|
|
18389
18624
|
if (d3.type === "work-item" || d3.id && typeof d3.id === "string" && /^WI-\d+/.test(d3.id)) {
|
|
@@ -18405,7 +18640,7 @@ function parseContent(raw) {
|
|
|
18405
18640
|
let bodyText = trimmed;
|
|
18406
18641
|
if (format === "kaddo-frontmatter" || format === "markdown-frontmatter") {
|
|
18407
18642
|
try {
|
|
18408
|
-
const parsed =
|
|
18643
|
+
const parsed = matter112(trimmed);
|
|
18409
18644
|
frontmatterFields = parsed.data ?? {};
|
|
18410
18645
|
bodyText = parsed.content.trim();
|
|
18411
18646
|
} catch {
|
|
@@ -18502,7 +18737,7 @@ function findDuplicateByHash(dir, hash) {
|
|
|
18502
18737
|
const raw = readFile2(item.filePath);
|
|
18503
18738
|
if (!raw) continue;
|
|
18504
18739
|
try {
|
|
18505
|
-
const { data } =
|
|
18740
|
+
const { data } = matter112(raw);
|
|
18506
18741
|
const source = data?.source;
|
|
18507
18742
|
if (source && typeof source === "object" && !Array.isArray(source)) {
|
|
18508
18743
|
const s = source;
|
|
@@ -18569,7 +18804,7 @@ function importWorkItem(dir, opts) {
|
|
|
18569
18804
|
};
|
|
18570
18805
|
}
|
|
18571
18806
|
if (opts.onConflict === "replace") {
|
|
18572
|
-
|
|
18807
|
+
fs5.rmSync(existing.filePath, { force: true });
|
|
18573
18808
|
replacedWorkItem = existing.workItemId;
|
|
18574
18809
|
}
|
|
18575
18810
|
}
|
|
@@ -19011,8 +19246,8 @@ async function handleJiraResponse(res) {
|
|
|
19011
19246
|
}
|
|
19012
19247
|
return await res.json();
|
|
19013
19248
|
}
|
|
19014
|
-
async function jiraFetch(baseUrl,
|
|
19015
|
-
const url = new URL(
|
|
19249
|
+
async function jiraFetch(baseUrl, path52, auth, timeoutMs, params) {
|
|
19250
|
+
const url = new URL(path52, baseUrl + "/");
|
|
19016
19251
|
if (params) for (const [k2, v] of Object.entries(params)) url.searchParams.set(k2, v);
|
|
19017
19252
|
const controller = new AbortController();
|
|
19018
19253
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
@@ -19030,8 +19265,8 @@ async function jiraFetch(baseUrl, path42, auth, timeoutMs, params) {
|
|
|
19030
19265
|
clearTimeout(timer);
|
|
19031
19266
|
}
|
|
19032
19267
|
}
|
|
19033
|
-
async function jiraPost(baseUrl,
|
|
19034
|
-
const url = new URL(
|
|
19268
|
+
async function jiraPost(baseUrl, path52, auth, timeoutMs, body) {
|
|
19269
|
+
const url = new URL(path52, baseUrl + "/");
|
|
19035
19270
|
const controller = new AbortController();
|
|
19036
19271
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
19037
19272
|
try {
|
|
@@ -19222,7 +19457,7 @@ function persistConsent(dir, consent) {
|
|
|
19222
19457
|
const prev = doc.telemetry ?? {};
|
|
19223
19458
|
delete prev.enabled;
|
|
19224
19459
|
doc.telemetry = { ...prev, consent, consentVersion: CURRENT_CONSENT_VERSION };
|
|
19225
|
-
writeFile2(configPath,
|
|
19460
|
+
writeFile2(configPath, stringifyYaml7(doc));
|
|
19226
19461
|
}
|
|
19227
19462
|
function isDeferralActive(dir) {
|
|
19228
19463
|
const p = join3(dir, ".kaddo", "telemetry-deferred.json");
|
|
@@ -19397,7 +19632,7 @@ function buildInitiativeContent(id, input) {
|
|
|
19397
19632
|
if (input.priority) data.priority = input.priority;
|
|
19398
19633
|
if (input.source) data.source = input.source;
|
|
19399
19634
|
if (input.sourceId) data.source_id = input.sourceId;
|
|
19400
|
-
return
|
|
19635
|
+
return matter13.stringify("\n" + (input.body ?? defaultBody(input.title.trim())), data);
|
|
19401
19636
|
}
|
|
19402
19637
|
function createInitiative(dir, input) {
|
|
19403
19638
|
if (!input.title?.trim()) throw new InitiativeWriteError("Initiative title is required.");
|
|
@@ -19421,10 +19656,10 @@ function transitionInitiative(dir, id, to) {
|
|
|
19421
19656
|
throw new InitiativeWriteError(`Invalid transition ${ini.status} \u2192 ${to}.`);
|
|
19422
19657
|
}
|
|
19423
19658
|
const raw = readFile2(ini.filePath);
|
|
19424
|
-
const { data, content } =
|
|
19659
|
+
const { data, content } = matter13(raw, {});
|
|
19425
19660
|
data.status = to;
|
|
19426
19661
|
if (to === "completed") data.completed_at = today2();
|
|
19427
|
-
const newContent =
|
|
19662
|
+
const newContent = matter13.stringify(content, data);
|
|
19428
19663
|
writeFile2(ini.filePath, newContent);
|
|
19429
19664
|
const updated = parseInitiative2(dir, ini.filePath, newContent);
|
|
19430
19665
|
if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after transition.");
|
|
@@ -19434,7 +19669,7 @@ function addCandidate(dir, id, candidate) {
|
|
|
19434
19669
|
const ini = getInitiative(dir, id);
|
|
19435
19670
|
if (!ini) throw new InitiativeWriteError(`Initiative ${id} not found.`);
|
|
19436
19671
|
const raw = readFile2(ini.filePath);
|
|
19437
|
-
const { data, content } =
|
|
19672
|
+
const { data, content } = matter13(raw, {});
|
|
19438
19673
|
const existing = Array.isArray(data.candidates) ? data.candidates : [];
|
|
19439
19674
|
const fallbackId = `WI-CANDIDATE-${String(existing.length + 1).padStart(3, "0")}`;
|
|
19440
19675
|
const normalized = normalizeCandidate(candidate, fallbackId);
|
|
@@ -19442,7 +19677,7 @@ function addCandidate(dir, id, candidate) {
|
|
|
19442
19677
|
throw new InitiativeWriteError(`Candidate ${normalized.id} already exists in ${id}.`);
|
|
19443
19678
|
}
|
|
19444
19679
|
data.candidates = [...existing, candidateToData(normalized)];
|
|
19445
|
-
const newContent =
|
|
19680
|
+
const newContent = matter13.stringify(content, data);
|
|
19446
19681
|
writeFile2(ini.filePath, newContent);
|
|
19447
19682
|
const updated = parseInitiative2(dir, ini.filePath, newContent);
|
|
19448
19683
|
if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after adding a candidate.");
|
|
@@ -19452,7 +19687,7 @@ function updateInitiative(dir, id, patch) {
|
|
|
19452
19687
|
const ini = getInitiative(dir, id);
|
|
19453
19688
|
if (!ini) throw new InitiativeWriteError(`Initiative ${id} not found.`);
|
|
19454
19689
|
const raw = readFile2(ini.filePath);
|
|
19455
|
-
const { data, content } =
|
|
19690
|
+
const { data, content } = matter13(raw, {});
|
|
19456
19691
|
if (patch.title !== void 0) {
|
|
19457
19692
|
if (!patch.title.trim()) throw new InitiativeWriteError("Initiative title cannot be empty.");
|
|
19458
19693
|
data.title = patch.title.trim();
|
|
@@ -19462,7 +19697,7 @@ function updateInitiative(dir, id, patch) {
|
|
|
19462
19697
|
if (patch.knowledgeLevel !== void 0) data.knowledge_level = patch.knowledgeLevel;
|
|
19463
19698
|
if (patch.domains !== void 0) data.domains = patch.domains;
|
|
19464
19699
|
if (patch.relatedCapabilities !== void 0) data.related_capabilities = patch.relatedCapabilities;
|
|
19465
|
-
const newContent =
|
|
19700
|
+
const newContent = matter13.stringify(content, data);
|
|
19466
19701
|
writeFile2(ini.filePath, newContent);
|
|
19467
19702
|
const updated = parseInitiative2(dir, ini.filePath, newContent);
|
|
19468
19703
|
if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after update.");
|
|
@@ -19480,10 +19715,10 @@ function addExternalLink(dir, id, link) {
|
|
|
19480
19715
|
throw new InitiativeWriteError(`External link ${link.integration}:${link.externalId} already exists on ${id}.`);
|
|
19481
19716
|
}
|
|
19482
19717
|
const raw = readFile2(ini.filePath);
|
|
19483
|
-
const { data, content } =
|
|
19718
|
+
const { data, content } = matter13(raw, {});
|
|
19484
19719
|
const existing = Array.isArray(data.external_links) ? data.external_links : [];
|
|
19485
19720
|
data.external_links = [...existing, externalLinkToData(link)];
|
|
19486
|
-
const newContent =
|
|
19721
|
+
const newContent = matter13.stringify(content, data);
|
|
19487
19722
|
writeFile2(ini.filePath, newContent);
|
|
19488
19723
|
const updated = parseInitiative2(dir, ini.filePath, newContent);
|
|
19489
19724
|
if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after adding an external link.");
|
|
@@ -19552,7 +19787,7 @@ function buildWorkItemContent(wiId, iniId, candidate) {
|
|
|
19552
19787
|
"_What did we learn? Update after completion._",
|
|
19553
19788
|
""
|
|
19554
19789
|
].join("\n");
|
|
19555
|
-
return
|
|
19790
|
+
return matter13.stringify("\n" + body, data);
|
|
19556
19791
|
}
|
|
19557
19792
|
function materializeCandidate(dir, id, candidateId) {
|
|
19558
19793
|
const ini = getInitiative(dir, id);
|
|
@@ -19569,13 +19804,13 @@ function materializeCandidate(dir, id, candidateId) {
|
|
|
19569
19804
|
ensureDir2(join3(dir, DRAFT_DIR));
|
|
19570
19805
|
writeFile2(join3(dir, DRAFT_DIR, fileName), buildWorkItemContent(wiId, ini.id, candidate));
|
|
19571
19806
|
const raw = readFile2(ini.filePath);
|
|
19572
|
-
const { data, content } =
|
|
19807
|
+
const { data, content } = matter13(raw, {});
|
|
19573
19808
|
const cands = Array.isArray(data.candidates) ? data.candidates : [];
|
|
19574
19809
|
for (const c of cands) {
|
|
19575
19810
|
if (c && typeof c === "object" && String(c.id) === candidateId) c.materialized_as = wiId;
|
|
19576
19811
|
}
|
|
19577
19812
|
data.candidates = cands;
|
|
19578
|
-
const newContent =
|
|
19813
|
+
const newContent = matter13.stringify(content, data);
|
|
19579
19814
|
writeFile2(ini.filePath, newContent);
|
|
19580
19815
|
const updated = parseInitiative2(dir, ini.filePath, newContent);
|
|
19581
19816
|
if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after materialization.");
|
|
@@ -19686,6 +19921,164 @@ function evaluateInitiativeCompletion(dir, ini) {
|
|
|
19686
19921
|
};
|
|
19687
19922
|
}
|
|
19688
19923
|
|
|
19924
|
+
// ../cli/src/core/resource-write.ts
|
|
19925
|
+
import fs6 from "fs";
|
|
19926
|
+
import path5 from "path";
|
|
19927
|
+
import matter14 from "gray-matter";
|
|
19928
|
+
var RESOURCES_DIR = "knowledge/tech/resources";
|
|
19929
|
+
var ResourceWriteError = class extends Error {
|
|
19930
|
+
constructor(code, message) {
|
|
19931
|
+
super(message);
|
|
19932
|
+
this.code = code;
|
|
19933
|
+
this.name = "ResourceWriteError";
|
|
19934
|
+
}
|
|
19935
|
+
code;
|
|
19936
|
+
};
|
|
19937
|
+
function slug4(s) {
|
|
19938
|
+
return s.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60);
|
|
19939
|
+
}
|
|
19940
|
+
function atomicWrite(filePath, content) {
|
|
19941
|
+
const tmp = `${filePath}.tmp-${process.pid}-${Date.now()}`;
|
|
19942
|
+
fs6.writeFileSync(tmp, content, "utf-8");
|
|
19943
|
+
fs6.renameSync(tmp, filePath);
|
|
19944
|
+
}
|
|
19945
|
+
function sanitizeRefs(refs) {
|
|
19946
|
+
if (!Array.isArray(refs)) return [];
|
|
19947
|
+
return refs.map((r2) => String(r2).trim()).filter((r2) => r2 && !r2.includes("=") && /^[A-Za-z0-9_.-]+$/.test(r2));
|
|
19948
|
+
}
|
|
19949
|
+
function allResourceArtifacts(dir) {
|
|
19950
|
+
return discoverKnowledge(dir).filter((a2) => a2.type === RESOURCE_ARTIFACT_TYPE);
|
|
19951
|
+
}
|
|
19952
|
+
function nextResourceId(dir, title, explicit) {
|
|
19953
|
+
const existing = new Set(allResourceArtifacts(dir).map((a2) => (a2.id || "").toUpperCase()));
|
|
19954
|
+
if (explicit && explicit.trim()) return explicit.trim();
|
|
19955
|
+
const base = `RES-${slug4(title) || "resource"}`;
|
|
19956
|
+
if (!existing.has(base.toUpperCase())) return base;
|
|
19957
|
+
for (let i = 2; i < 1e3; i++) {
|
|
19958
|
+
const candidate = `${base}-${i}`;
|
|
19959
|
+
if (!existing.has(candidate.toUpperCase())) return candidate;
|
|
19960
|
+
}
|
|
19961
|
+
throw new ResourceWriteError("ID_EXHAUSTED", `Could not derive a unique id for "${title}".`);
|
|
19962
|
+
}
|
|
19963
|
+
function validateResource(dir, input) {
|
|
19964
|
+
const findings = [];
|
|
19965
|
+
if (input.resourceType && !RESOURCE_TYPES.includes(input.resourceType)) {
|
|
19966
|
+
findings.push({ level: "warning", message: `Unknown resource_type "${input.resourceType}" (not in the known set).` });
|
|
19967
|
+
}
|
|
19968
|
+
for (const i of input.accessInterfaces ?? []) {
|
|
19969
|
+
if (i.type && !INTERFACE_TYPES.includes(i.type)) {
|
|
19970
|
+
findings.push({ level: "warning", message: `Unknown access interface type "${i.type}".` });
|
|
19971
|
+
}
|
|
19972
|
+
}
|
|
19973
|
+
const mapped = loadMappedModules(dir).map((m2) => m2.id);
|
|
19974
|
+
if (mapped.length > 0) {
|
|
19975
|
+
const refs = /* @__PURE__ */ new Set();
|
|
19976
|
+
if (input.scope?.type === "module" && input.scope.module) refs.add(input.scope.module);
|
|
19977
|
+
for (const m2 of input.modules ?? []) refs.add(m2);
|
|
19978
|
+
for (const m2 of refs) {
|
|
19979
|
+
if (!mapped.includes(m2)) findings.push({ level: "warning", message: `Module "${m2}" is not in .kaddo/modules.yml.` });
|
|
19980
|
+
}
|
|
19981
|
+
}
|
|
19982
|
+
if (input.scope && input.scope.type === "module" && !input.scope.module) {
|
|
19983
|
+
findings.push({ level: "error", message: 'scope.type "module" requires a module id.' });
|
|
19984
|
+
}
|
|
19985
|
+
return findings;
|
|
19986
|
+
}
|
|
19987
|
+
function buildFrontmatter(id, input, base = {}) {
|
|
19988
|
+
const data = { ...base };
|
|
19989
|
+
data.type = RESOURCE_ARTIFACT_TYPE;
|
|
19990
|
+
data.id = id;
|
|
19991
|
+
if (input.title != null) data.title = input.title;
|
|
19992
|
+
if (input.resourceType != null) data.resource_type = input.resourceType;
|
|
19993
|
+
if (input.provider != null) data.provider = input.provider;
|
|
19994
|
+
if (input.environments != null) data.environments = input.environments;
|
|
19995
|
+
if (input.scope != null) data.scope = input.scope.module ? { type: input.scope.type, module: input.scope.module } : { type: input.scope.type };
|
|
19996
|
+
if (input.modules != null) data.modules = input.modules;
|
|
19997
|
+
if (input.accessInterfaces != null) data.access_interfaces = input.accessInterfaces;
|
|
19998
|
+
if (input.accessBoundaries != null) data.access_boundaries = input.accessBoundaries;
|
|
19999
|
+
if (input.authentication != null) {
|
|
20000
|
+
const mode = input.authentication.mode;
|
|
20001
|
+
const refs = sanitizeRefs(input.authentication.refs);
|
|
20002
|
+
data.authentication = { ...mode ? { mode } : {}, ...refs.length ? { refs } : {} };
|
|
20003
|
+
}
|
|
20004
|
+
return data;
|
|
20005
|
+
}
|
|
20006
|
+
function serialize(data, body) {
|
|
20007
|
+
return matter14.stringify(`
|
|
20008
|
+
${body.trim()}
|
|
20009
|
+
`, data);
|
|
20010
|
+
}
|
|
20011
|
+
function createResource(dir, input) {
|
|
20012
|
+
const title = (input.title ?? "").trim();
|
|
20013
|
+
if (!title) throw new ResourceWriteError("INVALID_INPUT", "A resource title is required.");
|
|
20014
|
+
const resourceType = (input.resourceType ?? "").trim();
|
|
20015
|
+
if (!resourceType) throw new ResourceWriteError("INVALID_INPUT", "A resource_type is required.");
|
|
20016
|
+
const findings = validateResource(dir, input);
|
|
20017
|
+
if (findings.some((f3) => f3.level === "error")) {
|
|
20018
|
+
throw new ResourceWriteError("VALIDATION_FAILED", findings.filter((f3) => f3.level === "error").map((f3) => f3.message).join("; "));
|
|
20019
|
+
}
|
|
20020
|
+
const existing = allResourceArtifacts(dir);
|
|
20021
|
+
if (input.id && existing.some((a2) => (a2.id || "").toUpperCase() === input.id.toUpperCase())) {
|
|
20022
|
+
throw new ResourceWriteError("DUPLICATE", `A resource with id "${input.id}" already exists.`);
|
|
20023
|
+
}
|
|
20024
|
+
if (existing.some((a2) => (a2.title || "").toLowerCase() === title.toLowerCase())) {
|
|
20025
|
+
throw new ResourceWriteError("DUPLICATE", `A resource titled "${title}" already exists.`);
|
|
20026
|
+
}
|
|
20027
|
+
const id = nextResourceId(dir, title, input.id);
|
|
20028
|
+
const data = buildFrontmatter(id, input);
|
|
20029
|
+
const body = `# Purpose
|
|
20030
|
+
|
|
20031
|
+
${(input.purpose ?? "").trim() || "TODO: describe what this resource is for."}`;
|
|
20032
|
+
const relPath = `${RESOURCES_DIR}/${slug4(title) || slug4(id)}.md`;
|
|
20033
|
+
const filePath = join(dir, relPath);
|
|
20034
|
+
if (exists(filePath)) throw new ResourceWriteError("DUPLICATE", `Resource file already exists: ${relPath}`);
|
|
20035
|
+
ensureDir(path5.dirname(filePath));
|
|
20036
|
+
atomicWrite(filePath, serialize(data, body));
|
|
20037
|
+
return { id, path: relPath, findings };
|
|
20038
|
+
}
|
|
20039
|
+
function updateResource(dir, id, input) {
|
|
20040
|
+
const match = allResourceArtifacts(dir).find((a2) => a2.id === id || a2.title === id);
|
|
20041
|
+
if (!match) throw new ResourceWriteError("NOT_FOUND", `Project Resource "${id}" not found.`);
|
|
20042
|
+
const findings = validateResource(dir, input);
|
|
20043
|
+
if (findings.some((f3) => f3.level === "error")) {
|
|
20044
|
+
throw new ResourceWriteError("VALIDATION_FAILED", findings.filter((f3) => f3.level === "error").map((f3) => f3.message).join("; "));
|
|
20045
|
+
}
|
|
20046
|
+
const raw = readFile(match.filePath);
|
|
20047
|
+
const parsed = matter14(raw);
|
|
20048
|
+
const base = parsed.data ?? {};
|
|
20049
|
+
const data = buildFrontmatter(match.id || id, input, base);
|
|
20050
|
+
let body = parsed.content.trim();
|
|
20051
|
+
if (input.purpose != null) {
|
|
20052
|
+
const purpose = `# Purpose
|
|
20053
|
+
|
|
20054
|
+
${input.purpose.trim()}`;
|
|
20055
|
+
body = /^#\s+Purpose/m.test(body) ? body.replace(/#\s+Purpose[\s\S]*?(?=\n#\s|\n*$)/, purpose + "\n") : `${purpose}
|
|
20056
|
+
|
|
20057
|
+
${body}`.trim();
|
|
20058
|
+
}
|
|
20059
|
+
atomicWrite(match.filePath, serialize(data, body));
|
|
20060
|
+
return { id: match.id || id, path: match.relPath, findings };
|
|
20061
|
+
}
|
|
20062
|
+
function getResourceReferences(dir, id) {
|
|
20063
|
+
const refs = [];
|
|
20064
|
+
for (const wi of discoverWorkItems(dir)) {
|
|
20065
|
+
for (const rel of parseWorkItemResources(wi.rawFrontmatter)) {
|
|
20066
|
+
if (rel.id === id) refs.push({ kind: "work-item", id: wi.id || wi.title, role: rel.role, path: wi.relPath });
|
|
20067
|
+
}
|
|
20068
|
+
}
|
|
20069
|
+
return refs;
|
|
20070
|
+
}
|
|
20071
|
+
function deleteResource(dir, id, opts = {}) {
|
|
20072
|
+
const match = allResourceArtifacts(dir).find((a2) => a2.id === id || a2.title === id);
|
|
20073
|
+
if (!match) throw new ResourceWriteError("NOT_FOUND", `Project Resource "${id}" not found.`);
|
|
20074
|
+
const references = getResourceReferences(dir, match.id || id);
|
|
20075
|
+
if (!opts.confirm) {
|
|
20076
|
+
return { deleted: false, preview: { id: match.id || id, references } };
|
|
20077
|
+
}
|
|
20078
|
+
fs6.rmSync(match.filePath, { force: true });
|
|
20079
|
+
return { deleted: true, path: match.relPath };
|
|
20080
|
+
}
|
|
20081
|
+
|
|
19689
20082
|
// src/tools.ts
|
|
19690
20083
|
var ok = (data) => ({ ok: true, data });
|
|
19691
20084
|
var fail = (message) => ({ ok: false, message });
|
|
@@ -19781,12 +20174,12 @@ function markWorkItemReady(root, id, confirm2) {
|
|
|
19781
20174
|
}
|
|
19782
20175
|
const raw = readText(root, item.path);
|
|
19783
20176
|
if (!raw) return fail(`Could not read Work Item file: ${item.path}`);
|
|
19784
|
-
const parsed =
|
|
20177
|
+
const parsed = matter15(raw);
|
|
19785
20178
|
const data = { ...parsed.data };
|
|
19786
20179
|
const today3 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
19787
20180
|
data.status = "ready";
|
|
19788
20181
|
data.ready_at = today3;
|
|
19789
|
-
const newContent =
|
|
20182
|
+
const newContent = matter15.stringify(parsed.content, data);
|
|
19790
20183
|
const posixPath = item.path.replace(/\\/g, "/");
|
|
19791
20184
|
let newRelPath = item.path;
|
|
19792
20185
|
if (posixPath.includes("/work-items/draft/")) {
|
|
@@ -19957,12 +20350,65 @@ function getResourceTool(root, id) {
|
|
|
19957
20350
|
if (!resource) return fail(`Project Resource "${id}" not found.`);
|
|
19958
20351
|
return ok(resource);
|
|
19959
20352
|
}
|
|
20353
|
+
function toResourceInput(args) {
|
|
20354
|
+
const input = {};
|
|
20355
|
+
if (args.title != null) input.title = args.title;
|
|
20356
|
+
if (args.resource_type != null) input.resourceType = args.resource_type;
|
|
20357
|
+
if (args.provider != null) input.provider = args.provider;
|
|
20358
|
+
if (args.environments != null) input.environments = args.environments;
|
|
20359
|
+
if (args.scope != null || args.module != null) {
|
|
20360
|
+
const type = args.scope || (args.module ? "module" : "system");
|
|
20361
|
+
input.scope = type === "module" ? { type: "module", module: args.module ?? null } : { type: "system" };
|
|
20362
|
+
}
|
|
20363
|
+
if (args.modules != null) input.modules = args.modules;
|
|
20364
|
+
if (args.purpose != null) input.purpose = args.purpose;
|
|
20365
|
+
if (args.access_interfaces != null) input.accessInterfaces = args.access_interfaces;
|
|
20366
|
+
if (args.authentication != null) input.authentication = args.authentication;
|
|
20367
|
+
return input;
|
|
20368
|
+
}
|
|
20369
|
+
function createResourceTool(root, args, confirm2) {
|
|
20370
|
+
const input = toResourceInput(args);
|
|
20371
|
+
const findings = validateResource(root, input);
|
|
20372
|
+
if (!confirm2) {
|
|
20373
|
+
return ok({ status: "needs_confirmation", action: "create", proposal: input, findings, message: "Re-call with confirm=true to create this resource." });
|
|
20374
|
+
}
|
|
20375
|
+
try {
|
|
20376
|
+
return ok({ status: "created", ...createResource(root, input) });
|
|
20377
|
+
} catch (e) {
|
|
20378
|
+
return fail(e instanceof ResourceWriteError ? e.message : String(e));
|
|
20379
|
+
}
|
|
20380
|
+
}
|
|
20381
|
+
function updateResourceTool(root, id, args, confirm2) {
|
|
20382
|
+
if (!getResource(root, id)) return fail(`Project Resource "${id}" not found.`);
|
|
20383
|
+
const input = toResourceInput(args);
|
|
20384
|
+
const findings = validateResource(root, input);
|
|
20385
|
+
if (!confirm2) {
|
|
20386
|
+
return ok({ status: "needs_confirmation", action: "update", id, changes: input, findings, message: "Re-call with confirm=true to apply this update." });
|
|
20387
|
+
}
|
|
20388
|
+
try {
|
|
20389
|
+
return ok({ status: "updated", ...updateResource(root, id, input) });
|
|
20390
|
+
} catch (e) {
|
|
20391
|
+
return fail(e instanceof ResourceWriteError ? e.message : String(e));
|
|
20392
|
+
}
|
|
20393
|
+
}
|
|
20394
|
+
function deleteResourceTool(root, id, confirm2) {
|
|
20395
|
+
if (!getResource(root, id)) return fail(`Project Resource "${id}" not found.`);
|
|
20396
|
+
if (!confirm2) {
|
|
20397
|
+
const references = getResourceReferences(root, id);
|
|
20398
|
+
return ok({ status: "needs_confirmation", action: "delete", id, references, message: "Re-call with confirm=true to delete. References in other artifacts are NOT removed." });
|
|
20399
|
+
}
|
|
20400
|
+
try {
|
|
20401
|
+
return ok({ status: "deleted", ...deleteResource(root, id, { confirm: true }) });
|
|
20402
|
+
} catch (e) {
|
|
20403
|
+
return fail(e instanceof ResourceWriteError ? e.message : String(e));
|
|
20404
|
+
}
|
|
20405
|
+
}
|
|
19960
20406
|
|
|
19961
20407
|
// src/multirepo.ts
|
|
19962
|
-
import
|
|
19963
|
-
import
|
|
19964
|
-
import
|
|
19965
|
-
import { parse as parseYaml11, stringify as
|
|
20408
|
+
import fs7 from "fs";
|
|
20409
|
+
import path6 from "path";
|
|
20410
|
+
import matter16 from "gray-matter";
|
|
20411
|
+
import { parse as parseYaml11, stringify as stringifyYaml8 } from "yaml";
|
|
19966
20412
|
var ok2 = (data) => ({ ok: true, data });
|
|
19967
20413
|
var fail2 = (message) => ({ ok: false, message });
|
|
19968
20414
|
function moduleRepoPath(m2) {
|
|
@@ -19981,24 +20427,24 @@ function isCore2(config) {
|
|
|
19981
20427
|
return projectRole2(config) === "core";
|
|
19982
20428
|
}
|
|
19983
20429
|
function readModuleFile(root, repoPath, relFile) {
|
|
19984
|
-
const moduleDir =
|
|
19985
|
-
const abs =
|
|
20430
|
+
const moduleDir = path6.resolve(root, repoPath);
|
|
20431
|
+
const abs = path6.resolve(moduleDir, relFile);
|
|
19986
20432
|
if (!abs.startsWith(moduleDir)) return null;
|
|
19987
|
-
if (!
|
|
19988
|
-
return
|
|
20433
|
+
if (!fs7.existsSync(abs) || !fs7.statSync(abs).isFile()) return null;
|
|
20434
|
+
return fs7.readFileSync(abs, "utf-8");
|
|
19989
20435
|
}
|
|
19990
20436
|
function detectModuleStatus2(root, repoPath) {
|
|
19991
|
-
const moduleDir =
|
|
19992
|
-
if (!
|
|
20437
|
+
const moduleDir = path6.resolve(root, repoPath);
|
|
20438
|
+
if (!fs7.existsSync(moduleDir)) {
|
|
19993
20439
|
return { status: "missing", configured: false, moduleContext: false, currentState: false, codebase: false, warning: `Directory \`${repoPath}\` does not exist.` };
|
|
19994
20440
|
}
|
|
19995
|
-
const configPath =
|
|
19996
|
-
if (!
|
|
20441
|
+
const configPath = path6.join(moduleDir, ".kaddo", "config.yml");
|
|
20442
|
+
if (!fs7.existsSync(configPath)) {
|
|
19997
20443
|
return { status: "not_configured", configured: false, moduleContext: false, currentState: false, codebase: false, warning: `Run \`kaddo init\` inside \`${repoPath}\` and select multirepo \u2192 module.` };
|
|
19998
20444
|
}
|
|
19999
20445
|
let parsed = null;
|
|
20000
20446
|
try {
|
|
20001
|
-
parsed = parseYaml11(
|
|
20447
|
+
parsed = parseYaml11(fs7.readFileSync(configPath, "utf-8"));
|
|
20002
20448
|
} catch {
|
|
20003
20449
|
}
|
|
20004
20450
|
const role = parsed?.project?.role ?? parsed?.multirepo?.role;
|
|
@@ -20009,14 +20455,14 @@ function detectModuleStatus2(root, repoPath) {
|
|
|
20009
20455
|
status: "configured",
|
|
20010
20456
|
configured: true,
|
|
20011
20457
|
role: "module",
|
|
20012
|
-
moduleContext:
|
|
20013
|
-
currentState:
|
|
20014
|
-
codebase:
|
|
20458
|
+
moduleContext: fs7.existsSync(path6.join(moduleDir, "knowledge", "tech", "module", "module-context.md")) || fs7.existsSync(path6.join(moduleDir, "knowledge", "module", "module-context.md")),
|
|
20459
|
+
currentState: fs7.existsSync(path6.join(moduleDir, "knowledge", "tech", "current-state.md")),
|
|
20460
|
+
codebase: fs7.existsSync(path6.join(moduleDir, "knowledge", "tech", "codebase.md"))
|
|
20015
20461
|
};
|
|
20016
20462
|
}
|
|
20017
20463
|
function resolveModuleContextPath(root, repoPath) {
|
|
20018
|
-
const moduleDir =
|
|
20019
|
-
if (
|
|
20464
|
+
const moduleDir = path6.resolve(root, repoPath);
|
|
20465
|
+
if (fs7.existsSync(path6.join(moduleDir, "knowledge", "tech", "module", "module-context.md"))) {
|
|
20020
20466
|
return "knowledge/tech/module/module-context.md";
|
|
20021
20467
|
}
|
|
20022
20468
|
return "knowledge/module/module-context.md";
|
|
@@ -20113,7 +20559,7 @@ function validateWorkItemModulesTool(root, args) {
|
|
|
20113
20559
|
if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
|
|
20114
20560
|
const raw = readText(root, wi.path);
|
|
20115
20561
|
if (!raw) return fail2(`Could not read Work Item file.`);
|
|
20116
|
-
const parsed =
|
|
20562
|
+
const parsed = matter16(raw);
|
|
20117
20563
|
const data = parsed.data;
|
|
20118
20564
|
const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
|
|
20119
20565
|
const codeEntries = Array.isArray(data.code) ? data.code.filter((c) => typeof c === "object" && c !== null).map((c) => ({ repo: typeof c.repo === "string" ? c.repo : void 0, paths: Array.isArray(c.paths) ? c.paths.map(String) : [] })) : [];
|
|
@@ -20166,7 +20612,7 @@ function getWorkItemContextTool(root, args) {
|
|
|
20166
20612
|
if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
|
|
20167
20613
|
const raw = readText(root, wi.path);
|
|
20168
20614
|
if (!raw) return fail2(`Could not read Work Item file.`);
|
|
20169
|
-
const parsed =
|
|
20615
|
+
const parsed = matter16(raw);
|
|
20170
20616
|
const data = parsed.data;
|
|
20171
20617
|
const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
|
|
20172
20618
|
const codeEntries = Array.isArray(data.code) ? data.code.filter((c) => typeof c === "object" && c !== null).map((c) => ({ repo: typeof c.repo === "string" ? c.repo : void 0, paths: Array.isArray(c.paths) ? c.paths.map(String) : [] })) : [];
|
|
@@ -20220,15 +20666,15 @@ function extractSection(content, heading) {
|
|
|
20220
20666
|
return out;
|
|
20221
20667
|
}
|
|
20222
20668
|
function buildBranchStrategy(wi, affectedModules, codeEntries, descriptor) {
|
|
20223
|
-
const
|
|
20669
|
+
const slug6 = slugify4(wi.title);
|
|
20224
20670
|
const wiLower = wi.id.toLowerCase();
|
|
20225
|
-
const coreBranch = `feature/${wiLower}-${
|
|
20671
|
+
const coreBranch = `feature/${wiLower}-${slug6}`;
|
|
20226
20672
|
const moduleBranches = {};
|
|
20227
20673
|
const commitMessages = {};
|
|
20228
20674
|
for (const modId of affectedModules) {
|
|
20229
20675
|
const mod = (descriptor.modules ?? []).find((m2) => m2.id === modId);
|
|
20230
20676
|
const repo = mod ? moduleRepoPath(mod) || modId : modId;
|
|
20231
|
-
moduleBranches[repo] = `feature/${wiLower}-${modId}-${
|
|
20677
|
+
moduleBranches[repo] = `feature/${wiLower}-${modId}-${slug6.slice(0, 20)}`;
|
|
20232
20678
|
commitMessages[repo] = `fix(${wiLower}): update ${modId}`;
|
|
20233
20679
|
}
|
|
20234
20680
|
commitMessages["core"] = `docs(${wiLower}): capture cross-repo implementation summary`;
|
|
@@ -20252,7 +20698,7 @@ function suggestBranchStrategyTool(root, args) {
|
|
|
20252
20698
|
if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
|
|
20253
20699
|
const raw = readText(root, wi.path);
|
|
20254
20700
|
if (!raw) return fail2(`Could not read Work Item file.`);
|
|
20255
|
-
const parsed =
|
|
20701
|
+
const parsed = matter16(raw);
|
|
20256
20702
|
const data = parsed.data;
|
|
20257
20703
|
const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
|
|
20258
20704
|
const codeEntries = Array.isArray(data.code) ? data.code.filter((c) => typeof c === "object" && c !== null).map((c) => ({ repo: typeof c.repo === "string" ? c.repo : void 0, paths: Array.isArray(c.paths) ? c.paths.map(String) : [] })) : [];
|
|
@@ -20401,31 +20847,31 @@ function modulesDiscoverTool(root, args) {
|
|
|
20401
20847
|
const sysName = config.system?.name ?? config.project?.name ?? "";
|
|
20402
20848
|
const workspaceRoots2 = config.multirepo?.workspace_roots ?? [".."];
|
|
20403
20849
|
const IGNORED = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", "coverage", "vendor", ".next", "out", "tmp", "temp", ".kaddo"]);
|
|
20404
|
-
const coreAbs =
|
|
20850
|
+
const coreAbs = path6.resolve(root);
|
|
20405
20851
|
const discovered = [];
|
|
20406
20852
|
for (const wr of workspaceRoots2) {
|
|
20407
|
-
const rootAbs =
|
|
20408
|
-
if (!
|
|
20853
|
+
const rootAbs = path6.resolve(root, wr);
|
|
20854
|
+
if (!fs7.existsSync(rootAbs) || !fs7.statSync(rootAbs).isDirectory()) continue;
|
|
20409
20855
|
let entries;
|
|
20410
20856
|
try {
|
|
20411
|
-
entries =
|
|
20857
|
+
entries = fs7.readdirSync(rootAbs);
|
|
20412
20858
|
} catch {
|
|
20413
20859
|
continue;
|
|
20414
20860
|
}
|
|
20415
20861
|
for (const entry of entries) {
|
|
20416
20862
|
if (IGNORED.has(entry)) continue;
|
|
20417
|
-
const candidateAbs =
|
|
20418
|
-
if (!
|
|
20863
|
+
const candidateAbs = path6.resolve(rootAbs, entry);
|
|
20864
|
+
if (!fs7.statSync(candidateAbs).isDirectory()) continue;
|
|
20419
20865
|
if (candidateAbs === coreAbs) continue;
|
|
20420
|
-
const relPath =
|
|
20421
|
-
const cfgPath =
|
|
20422
|
-
if (!
|
|
20866
|
+
const relPath = path6.relative(root, candidateAbs).replace(/\\/g, "/");
|
|
20867
|
+
const cfgPath = path6.join(candidateAbs, ".kaddo", "config.yml");
|
|
20868
|
+
if (!fs7.existsSync(cfgPath)) {
|
|
20423
20869
|
discovered.push({ id: entry, path: relPath, status: "not_configured", eligibleForMapping: false });
|
|
20424
20870
|
continue;
|
|
20425
20871
|
}
|
|
20426
20872
|
let parsed = null;
|
|
20427
20873
|
try {
|
|
20428
|
-
parsed = parseYaml11(
|
|
20874
|
+
parsed = parseYaml11(fs7.readFileSync(cfgPath, "utf-8"));
|
|
20429
20875
|
} catch {
|
|
20430
20876
|
}
|
|
20431
20877
|
const role = parsed?.project?.role ?? parsed?.multirepo?.role;
|
|
@@ -20452,7 +20898,7 @@ function modulesDiscoverTool(root, args) {
|
|
|
20452
20898
|
descriptor.modules.push({ id: d3.id, name: d3.id, path: d3.path, parent_system: sysName, status: "configured" });
|
|
20453
20899
|
}
|
|
20454
20900
|
}
|
|
20455
|
-
const ymlContent =
|
|
20901
|
+
const ymlContent = stringifyYaml8({ version: descriptor.version ?? 1, system: sysName, workspace_roots: workspaceRoots2, modules: descriptor.modules ?? [] });
|
|
20456
20902
|
writeDerived(root, ".kaddo/modules.yml", ymlContent);
|
|
20457
20903
|
return ok2({ discovered, applied: true });
|
|
20458
20904
|
}
|
|
@@ -20498,9 +20944,9 @@ ${list3(data.owners)}
|
|
|
20498
20944
|
}
|
|
20499
20945
|
|
|
20500
20946
|
// ../cli/src/core/system-topology.ts
|
|
20501
|
-
import { parse as parseYaml12, stringify as
|
|
20502
|
-
import
|
|
20503
|
-
import
|
|
20947
|
+
import { parse as parseYaml12, stringify as stringifyYaml9 } from "yaml";
|
|
20948
|
+
import fs8 from "fs";
|
|
20949
|
+
import path7 from "path";
|
|
20504
20950
|
import crypto from "crypto";
|
|
20505
20951
|
var TOPOLOGY_FILE2 = "knowledge/tech/system-topology.yml";
|
|
20506
20952
|
var SYSTEM_ENTITY_KINDS2 = /* @__PURE__ */ new Set([
|
|
@@ -20872,16 +21318,16 @@ function findSystemPaths(dir, source, target, opts = {}) {
|
|
|
20872
21318
|
out.get(r2.source).push(r2.target);
|
|
20873
21319
|
}
|
|
20874
21320
|
const paths = [];
|
|
20875
|
-
const walk = (node,
|
|
21321
|
+
const walk = (node, path9) => {
|
|
20876
21322
|
if (paths.length >= 20) return;
|
|
20877
|
-
if (node === target &&
|
|
20878
|
-
paths.push([...
|
|
21323
|
+
if (node === target && path9.length > 1) {
|
|
21324
|
+
paths.push([...path9]);
|
|
20879
21325
|
return;
|
|
20880
21326
|
}
|
|
20881
|
-
if (
|
|
21327
|
+
if (path9.length > maxDepth) return;
|
|
20882
21328
|
for (const nxt of out.get(node) ?? []) {
|
|
20883
|
-
if (
|
|
20884
|
-
walk(nxt, [...
|
|
21329
|
+
if (path9.includes(nxt)) continue;
|
|
21330
|
+
walk(nxt, [...path9, nxt]);
|
|
20885
21331
|
}
|
|
20886
21332
|
};
|
|
20887
21333
|
walk(source, [source]);
|
|
@@ -20906,14 +21352,14 @@ function getImpactCandidates(dir, seedIds, opts = {}) {
|
|
|
20906
21352
|
existing.reason.push(reason);
|
|
20907
21353
|
continue;
|
|
20908
21354
|
}
|
|
20909
|
-
const
|
|
21355
|
+
const path9 = findSystemPaths(dir, seed, n.id, { maxDepth: opts.maxDepth ?? DEFAULT_MAX_DEPTH })[0];
|
|
20910
21356
|
candidates.set(n.id, {
|
|
20911
21357
|
nodeId: n.id,
|
|
20912
21358
|
label: n.label,
|
|
20913
21359
|
kind: n.type,
|
|
20914
21360
|
...n.moduleId ? { moduleId: n.moduleId } : {},
|
|
20915
21361
|
reason: [reason],
|
|
20916
|
-
...
|
|
21362
|
+
...path9 ? { graphPath: path9.map((id) => byId.get(id)?.label ?? id) } : {},
|
|
20917
21363
|
...n.implementationRefs?.length ? { implementationRefs: n.implementationRefs } : {},
|
|
20918
21364
|
...n.knowledgeRefs?.length ? { knowledgeRefs: n.knowledgeRefs } : {},
|
|
20919
21365
|
status: "candidate",
|
|
@@ -21012,14 +21458,14 @@ function systemImpactCandidatesTool(root, args) {
|
|
|
21012
21458
|
}
|
|
21013
21459
|
|
|
21014
21460
|
// ../cli/src/services/integrations.ts
|
|
21015
|
-
import
|
|
21016
|
-
import { parse as parseYaml13, stringify as
|
|
21461
|
+
import matter18 from "gray-matter";
|
|
21462
|
+
import { parse as parseYaml13, stringify as stringifyYaml10 } from "yaml";
|
|
21017
21463
|
|
|
21018
21464
|
// ../cli/src/core/work-item-write.ts
|
|
21019
|
-
import
|
|
21020
|
-
import
|
|
21465
|
+
import fs9 from "fs";
|
|
21466
|
+
import path8 from "path";
|
|
21021
21467
|
import crypto4 from "crypto";
|
|
21022
|
-
import
|
|
21468
|
+
import matter17 from "gray-matter";
|
|
21023
21469
|
var SECTION_ORDER2 = [
|
|
21024
21470
|
"Actor",
|
|
21025
21471
|
"Outcome",
|
|
@@ -21687,8 +22133,8 @@ async function handleJiraResponse2(res) {
|
|
|
21687
22133
|
}
|
|
21688
22134
|
return await res.json();
|
|
21689
22135
|
}
|
|
21690
|
-
async function jiraFetch2(baseUrl,
|
|
21691
|
-
const url = new URL(
|
|
22136
|
+
async function jiraFetch2(baseUrl, path9, auth, timeoutMs, params) {
|
|
22137
|
+
const url = new URL(path9, baseUrl + "/");
|
|
21692
22138
|
if (params) for (const [k2, v] of Object.entries(params)) url.searchParams.set(k2, v);
|
|
21693
22139
|
const controller = new AbortController();
|
|
21694
22140
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
@@ -21706,8 +22152,8 @@ async function jiraFetch2(baseUrl, path7, auth, timeoutMs, params) {
|
|
|
21706
22152
|
clearTimeout(timer);
|
|
21707
22153
|
}
|
|
21708
22154
|
}
|
|
21709
|
-
async function jiraPost2(baseUrl,
|
|
21710
|
-
const url = new URL(
|
|
22155
|
+
async function jiraPost2(baseUrl, path9, auth, timeoutMs, body) {
|
|
22156
|
+
const url = new URL(path9, baseUrl + "/");
|
|
21711
22157
|
const controller = new AbortController();
|
|
21712
22158
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
21713
22159
|
try {
|
|
@@ -22067,7 +22513,7 @@ async function integrationsWorkItemTool(root, args) {
|
|
|
22067
22513
|
}
|
|
22068
22514
|
|
|
22069
22515
|
// ../cli/src/core/context-pack.ts
|
|
22070
|
-
import
|
|
22516
|
+
import matter19 from "gray-matter";
|
|
22071
22517
|
var CONTEXT_PACK_VERSION = "1";
|
|
22072
22518
|
var ARCH_DIR4 = "knowledge";
|
|
22073
22519
|
function readScanJson(dir) {
|
|
@@ -22080,7 +22526,7 @@ function readScanJson(dir) {
|
|
|
22080
22526
|
}
|
|
22081
22527
|
}
|
|
22082
22528
|
function firstParagraph4(markdown) {
|
|
22083
|
-
const body =
|
|
22529
|
+
const body = matter19(markdown).content.trim();
|
|
22084
22530
|
const para = body.split("\n\n").map((p) => p.trim()).find((p) => p && !p.startsWith("#"));
|
|
22085
22531
|
return para ?? "";
|
|
22086
22532
|
}
|
|
@@ -22186,6 +22632,7 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
|
|
|
22186
22632
|
missing.push("No work items found.");
|
|
22187
22633
|
}
|
|
22188
22634
|
const state = config.project.state;
|
|
22635
|
+
const mode = projectMode(config);
|
|
22189
22636
|
const roadmapPath = join(dir, ARCH_DIR4, "delivery", "roadmap.md");
|
|
22190
22637
|
const materialized = allArtifacts.filter(
|
|
22191
22638
|
(a2) => a2.filePath.replace(/\\/g, "/").includes("/delivery/work-items/") && Boolean(a2.type)
|
|
@@ -22278,6 +22725,7 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
|
|
|
22278
22725
|
project: {
|
|
22279
22726
|
name: config.project.name,
|
|
22280
22727
|
state,
|
|
22728
|
+
mode,
|
|
22281
22729
|
teamSize: config.team.size,
|
|
22282
22730
|
structure: config.project.structure,
|
|
22283
22731
|
language: languageLabel(projectLanguage(config))
|
|
@@ -22303,6 +22751,7 @@ function buildContextPack(dir, config, now = /* @__PURE__ */ new Date()) {
|
|
|
22303
22751
|
allWorkItems: allWorkItemArtifacts.map(toContextWorkItem),
|
|
22304
22752
|
artifacts: allArtifacts.filter((a2) => a2.codeGlobs.length > 0).map(toContextArtifact)
|
|
22305
22753
|
},
|
|
22754
|
+
...mode === "poc" ? { poc: readPocSummary(dir) } : {},
|
|
22306
22755
|
layers,
|
|
22307
22756
|
knowledgeQuality,
|
|
22308
22757
|
roadmap,
|
|
@@ -22406,11 +22855,23 @@ function renderContextPack(pack) {
|
|
|
22406
22855
|
[
|
|
22407
22856
|
`- Name: ${project.name}`,
|
|
22408
22857
|
`- State: ${project.state}`,
|
|
22858
|
+
`- Mode: ${project.mode}`,
|
|
22409
22859
|
`- Team size: ${project.teamSize}`,
|
|
22410
22860
|
`- Structure: ${project.structure}`,
|
|
22411
22861
|
`- Language: ${project.language}`
|
|
22412
22862
|
].join("\n") + "\n"
|
|
22413
22863
|
);
|
|
22864
|
+
if (pack.poc) {
|
|
22865
|
+
parts.push("## Proof of Concept\n");
|
|
22866
|
+
parts.push([
|
|
22867
|
+
`- Hypothesis: ${pack.poc.hypothesisDefined ? "defined" : "missing"}`,
|
|
22868
|
+
`- Success criteria: ${pack.poc.successCriteriaCount}`,
|
|
22869
|
+
`- Constraints: ${pack.poc.constraintsDefined ? "defined" : "missing"}`,
|
|
22870
|
+
`- Non-goals: ${pack.poc.nonGoalsDefined ? "defined" : "missing"}`,
|
|
22871
|
+
`- Conclusion: ${pack.poc.conclusion}`,
|
|
22872
|
+
"- Canonical artifact: `knowledge/delivery/poc.md`"
|
|
22873
|
+
].join("\n") + "\n");
|
|
22874
|
+
}
|
|
22414
22875
|
parts.push("## Current Phase\n");
|
|
22415
22876
|
parts.push(`Phase: ${pack.phase.phase}
|
|
22416
22877
|
`);
|
|
@@ -22494,27 +22955,29 @@ function renderContextPack(pack) {
|
|
|
22494
22955
|
}
|
|
22495
22956
|
parts.push("## Current Knowledge\n");
|
|
22496
22957
|
parts.push((knowledge.summary || "No project knowledge summary found yet.") + "\n");
|
|
22497
|
-
|
|
22498
|
-
|
|
22499
|
-
|
|
22500
|
-
[
|
|
22501
|
-
`- Initiatives: ${pack.roadmap.initiatives}`,
|
|
22502
|
-
`- Work Item candidates: ${pack.roadmap.work_item_candidates}`,
|
|
22503
|
-
`- Materialized Work Items: ${pack.roadmap.materialized_work_items}`,
|
|
22504
|
-
`- Remaining Work Item candidates: ${pack.roadmap.remaining_work_item_candidates}`
|
|
22505
|
-
].join("\n") + "\n"
|
|
22506
|
-
);
|
|
22507
|
-
if (pack.roadmap.remaining_work_item_candidates > 0) {
|
|
22958
|
+
if (project.mode !== "poc") {
|
|
22959
|
+
parts.push("## Roadmap Status\n");
|
|
22960
|
+
if (pack.roadmap.present) {
|
|
22508
22961
|
parts.push(
|
|
22509
|
-
|
|
22962
|
+
[
|
|
22963
|
+
`- Initiatives: ${pack.roadmap.initiatives}`,
|
|
22964
|
+
`- Work Item candidates: ${pack.roadmap.work_item_candidates}`,
|
|
22965
|
+
`- Materialized Work Items: ${pack.roadmap.materialized_work_items}`,
|
|
22966
|
+
`- Remaining Work Item candidates: ${pack.roadmap.remaining_work_item_candidates}`
|
|
22967
|
+
].join("\n") + "\n"
|
|
22510
22968
|
);
|
|
22969
|
+
if (pack.roadmap.remaining_work_item_candidates > 0) {
|
|
22970
|
+
parts.push(
|
|
22971
|
+
"Work Item candidates are not yet Work Items. Materialize them with `kaddo create --from roadmap`.\n"
|
|
22972
|
+
);
|
|
22973
|
+
}
|
|
22511
22974
|
}
|
|
22975
|
+
parts.push((knowledge.roadmapSummary || "No roadmap baseline found.") + "\n");
|
|
22512
22976
|
}
|
|
22513
|
-
parts.push((knowledge.roadmapSummary || "No roadmap baseline found.") + "\n");
|
|
22514
22977
|
const rq = pack.roadmapQuality;
|
|
22515
22978
|
const rqi = rq.initiatives;
|
|
22516
22979
|
const rqw = rq.work_item_candidates;
|
|
22517
|
-
if (rqi.total > 0 || rqw.total > 0) {
|
|
22980
|
+
if (project.mode !== "poc" && (rqi.total > 0 || rqw.total > 0)) {
|
|
22518
22981
|
parts.push("## Roadmap Quality\n");
|
|
22519
22982
|
if (rqi.total > 0) {
|
|
22520
22983
|
parts.push(
|
|
@@ -22729,7 +23192,13 @@ function renderContextPack(pack) {
|
|
|
22729
23192
|
function agentIsInstalled(dir, fileName) {
|
|
22730
23193
|
return exists(join(dir, agentInstallPath(fileName))) || exists(join(dir, "knowledge", "agents", fileName));
|
|
22731
23194
|
}
|
|
22732
|
-
function flowForState(state) {
|
|
23195
|
+
function flowForState(state, mode) {
|
|
23196
|
+
if (mode === "poc") {
|
|
23197
|
+
return [
|
|
23198
|
+
{ agent: "architecture-agent.md", output: "knowledge/tech/current-state.md" },
|
|
23199
|
+
{ agent: "work-item-agent.md", output: "knowledge/delivery/poc.md" }
|
|
23200
|
+
];
|
|
23201
|
+
}
|
|
22733
23202
|
switch (state) {
|
|
22734
23203
|
case "new":
|
|
22735
23204
|
return [
|
|
@@ -22754,9 +23223,10 @@ function flowForState(state) {
|
|
|
22754
23223
|
}
|
|
22755
23224
|
function buildUnderstandPlan(dir, config) {
|
|
22756
23225
|
const state = config.project.state;
|
|
23226
|
+
const mode = projectMode(config);
|
|
22757
23227
|
if (isModule(config)) {
|
|
22758
23228
|
return {
|
|
22759
|
-
project: { name: config.project.name, state, teamSize: config.team.size, structure: config.project.structure, language: "English" },
|
|
23229
|
+
project: { name: config.project.name, state, mode, teamSize: config.team.size, structure: config.project.structure, language: "English" },
|
|
22760
23230
|
steps: [],
|
|
22761
23231
|
missingAgents: [],
|
|
22762
23232
|
agentsInstalled: true,
|
|
@@ -22764,7 +23234,7 @@ function buildUnderstandPlan(dir, config) {
|
|
|
22764
23234
|
contextPackPath: ".kaddo/context-pack.md"
|
|
22765
23235
|
};
|
|
22766
23236
|
}
|
|
22767
|
-
const flow = flowForState(state).filter((s) => !exists(join(dir, s.output)));
|
|
23237
|
+
const flow = flowForState(state, mode).filter((s) => !exists(join(dir, s.output)));
|
|
22768
23238
|
const steps = flow.map((s) => {
|
|
22769
23239
|
const installed = agentIsInstalled(dir, s.agent);
|
|
22770
23240
|
return { agent: s.agent, output: s.output, installed };
|
|
@@ -22775,6 +23245,7 @@ function buildUnderstandPlan(dir, config) {
|
|
|
22775
23245
|
project: {
|
|
22776
23246
|
name: config.project.name,
|
|
22777
23247
|
state,
|
|
23248
|
+
mode,
|
|
22778
23249
|
teamSize: config.team.size,
|
|
22779
23250
|
structure: config.project.structure,
|
|
22780
23251
|
language: "English"
|
|
@@ -23047,7 +23518,7 @@ function requireKnowledge(root) {
|
|
|
23047
23518
|
throw new KaddoMcpError("Knowledge repository not found. Run `kaddo bootstrap` first.");
|
|
23048
23519
|
}
|
|
23049
23520
|
}
|
|
23050
|
-
function
|
|
23521
|
+
function slug5(s) {
|
|
23051
23522
|
return s.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "project";
|
|
23052
23523
|
}
|
|
23053
23524
|
function generateContext(root) {
|
|
@@ -23180,7 +23651,7 @@ function generateCapsuleDraft(root) {
|
|
|
23180
23651
|
const config = requireConfig(root);
|
|
23181
23652
|
requireKnowledge(root);
|
|
23182
23653
|
const capsule = buildCapsule(root, config);
|
|
23183
|
-
const name =
|
|
23654
|
+
const name = slug5(config.project.name);
|
|
23184
23655
|
const md = `.kaddo/exports/${name}.capsule.md`;
|
|
23185
23656
|
const json = `.kaddo/exports/${name}.capsule.json`;
|
|
23186
23657
|
writeDerived(root, md, renderCapsuleMarkdown(capsule));
|
|
@@ -23478,6 +23949,53 @@ function createServer(root) {
|
|
|
23478
23949
|
{ title: "Get Project Resource", description: "Get one Project Resource by ID: purpose, environments, access interfaces, boundaries and auth references (never values).", inputSchema: { id: z4.string() } },
|
|
23479
23950
|
async (args) => toolText(guarded(root, () => getResourceTool(root, args.id)))
|
|
23480
23951
|
);
|
|
23952
|
+
const resourceFields = {
|
|
23953
|
+
title: z4.string().optional(),
|
|
23954
|
+
resource_type: z4.string().optional(),
|
|
23955
|
+
provider: z4.string().optional(),
|
|
23956
|
+
environments: z4.array(z4.string()).optional(),
|
|
23957
|
+
scope: z4.string().optional(),
|
|
23958
|
+
module: z4.string().optional(),
|
|
23959
|
+
modules: z4.array(z4.string()).optional(),
|
|
23960
|
+
purpose: z4.string().optional(),
|
|
23961
|
+
access_interfaces: z4.array(z4.object({
|
|
23962
|
+
type: z4.string(),
|
|
23963
|
+
tool: z4.string().optional(),
|
|
23964
|
+
provider: z4.string().optional(),
|
|
23965
|
+
purpose: z4.string().optional(),
|
|
23966
|
+
operations: z4.array(z4.string()).optional(),
|
|
23967
|
+
environments: z4.array(z4.string()).optional(),
|
|
23968
|
+
constraints: z4.string().optional()
|
|
23969
|
+
})).optional(),
|
|
23970
|
+
authentication: z4.object({ mode: z4.string().optional(), refs: z4.array(z4.string()).optional() }).optional()
|
|
23971
|
+
};
|
|
23972
|
+
server.registerTool(
|
|
23973
|
+
"kaddo_create_resource",
|
|
23974
|
+
{
|
|
23975
|
+
title: "Create Project Resource",
|
|
23976
|
+
description: "Propose creating a Project Resource. Without confirm, returns a preview + validation findings (writes nothing). With confirm=true, Core persists it as Markdown. Never include credential values \u2014 only reference names.",
|
|
23977
|
+
inputSchema: { ...resourceFields, confirm: z4.boolean().optional() }
|
|
23978
|
+
},
|
|
23979
|
+
async (args) => toolText(guarded(root, () => createResourceTool(root, args, args.confirm)))
|
|
23980
|
+
);
|
|
23981
|
+
server.registerTool(
|
|
23982
|
+
"kaddo_update_resource",
|
|
23983
|
+
{
|
|
23984
|
+
title: "Update Project Resource",
|
|
23985
|
+
description: "Propose updating a Project Resource (no-lossy). Without confirm, returns a preview; with confirm=true, Core applies it. Never include credential values.",
|
|
23986
|
+
inputSchema: { id: z4.string(), ...resourceFields, confirm: z4.boolean().optional() }
|
|
23987
|
+
},
|
|
23988
|
+
async (args) => toolText(guarded(root, () => updateResourceTool(root, args.id, args, args.confirm)))
|
|
23989
|
+
);
|
|
23990
|
+
server.registerTool(
|
|
23991
|
+
"kaddo_delete_resource",
|
|
23992
|
+
{
|
|
23993
|
+
title: "Delete Project Resource",
|
|
23994
|
+
description: "Delete a Project Resource. Without confirm, returns what references it (writes nothing); with confirm=true, Core removes only the resource \u2014 references in other artifacts are left intact.",
|
|
23995
|
+
inputSchema: { id: z4.string(), confirm: z4.boolean().optional() }
|
|
23996
|
+
},
|
|
23997
|
+
async (args) => toolText(guarded(root, () => deleteResourceTool(root, args.id, args.confirm)))
|
|
23998
|
+
);
|
|
23481
23999
|
server.registerTool(
|
|
23482
24000
|
"kaddo_list_capsules",
|
|
23483
24001
|
{ title: "List Knowledge Capsules", description: "List registered external Knowledge Capsules.", inputSchema: {} },
|