@kaddo/mcp 3.115.0 → 3.116.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 +465 -123
- 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);
|
|
@@ -485,8 +488,8 @@ function loadConfig(dir) {
|
|
|
485
488
|
const parsed = configSchema.safeParse(raw ?? {});
|
|
486
489
|
if (!parsed.success) {
|
|
487
490
|
const issues = parsed.error.issues.map((i) => {
|
|
488
|
-
const
|
|
489
|
-
return
|
|
491
|
+
const path9 = i.path.join(".");
|
|
492
|
+
return path9 ? ` - ${path9}: ${i.message}` : ` - ${i.message}`;
|
|
490
493
|
}).join("\n");
|
|
491
494
|
throw new ConfigError(`Invalid .kaddo/config.yml:
|
|
492
495
|
${issues}`);
|
|
@@ -927,11 +930,11 @@ function moduleArtifactCoverage(dir, id) {
|
|
|
927
930
|
};
|
|
928
931
|
}
|
|
929
932
|
function loadMappedModules(dir) {
|
|
930
|
-
const
|
|
931
|
-
if (!exists(
|
|
933
|
+
const path9 = join(dir, DESCRIPTOR_PATH);
|
|
934
|
+
if (!exists(path9)) return [];
|
|
932
935
|
let parsed;
|
|
933
936
|
try {
|
|
934
|
-
parsed = parseYaml4(readFile(
|
|
937
|
+
parsed = parseYaml4(readFile(path9));
|
|
935
938
|
} catch {
|
|
936
939
|
return [];
|
|
937
940
|
}
|
|
@@ -2438,7 +2441,7 @@ function sectionParagraph2(md, title) {
|
|
|
2438
2441
|
}
|
|
2439
2442
|
return "";
|
|
2440
2443
|
}
|
|
2441
|
-
function parseCapsule(id,
|
|
2444
|
+
function parseCapsule(id, path9, md) {
|
|
2442
2445
|
const { data } = matter4(md);
|
|
2443
2446
|
const updatedAt = data.updated_at ? String(data.updated_at) : void 0;
|
|
2444
2447
|
let ageDays = null;
|
|
@@ -2448,7 +2451,7 @@ function parseCapsule(id, path7, md) {
|
|
|
2448
2451
|
}
|
|
2449
2452
|
return {
|
|
2450
2453
|
id,
|
|
2451
|
-
path:
|
|
2454
|
+
path: path9,
|
|
2452
2455
|
system: data.system ? String(data.system) : id,
|
|
2453
2456
|
owner: data.owner ? String(data.owner) : void 0,
|
|
2454
2457
|
updatedAt,
|
|
@@ -2593,6 +2596,18 @@ function discoverInitiatives(dir) {
|
|
|
2593
2596
|
// ../cli/src/core/resources.ts
|
|
2594
2597
|
import matter6 from "gray-matter";
|
|
2595
2598
|
var RESOURCE_ARTIFACT_TYPE = "project-resource";
|
|
2599
|
+
var RESOURCE_TYPES = [
|
|
2600
|
+
"database",
|
|
2601
|
+
"cloud",
|
|
2602
|
+
"api",
|
|
2603
|
+
"queue",
|
|
2604
|
+
"storage",
|
|
2605
|
+
"repository",
|
|
2606
|
+
"platform",
|
|
2607
|
+
"service",
|
|
2608
|
+
"other"
|
|
2609
|
+
];
|
|
2610
|
+
var INTERFACE_TYPES = ["cli", "mcp", "api", "sql", "sdk", "iac", "console", "other"];
|
|
2596
2611
|
function str(v) {
|
|
2597
2612
|
return typeof v === "string" && v.trim() ? v.trim() : null;
|
|
2598
2613
|
}
|
|
@@ -2636,6 +2651,18 @@ function parseAuth(v) {
|
|
|
2636
2651
|
const refs = strArray(o2.refs).filter((r2) => !r2.includes("=") && /^[A-Za-z0-9_.-]+$/.test(r2));
|
|
2637
2652
|
return { mode: str(o2.mode), refs };
|
|
2638
2653
|
}
|
|
2654
|
+
function parseScope(v) {
|
|
2655
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) return null;
|
|
2656
|
+
const o2 = v;
|
|
2657
|
+
const type = str(o2.type);
|
|
2658
|
+
if (!type) return null;
|
|
2659
|
+
return { type, module: str(o2.module) };
|
|
2660
|
+
}
|
|
2661
|
+
function purposeFromBody(body) {
|
|
2662
|
+
const m2 = body.match(/^#{1,2}\s+Purpose\s*\n+([\s\S]*?)(?=\n#{1,2}\s|\s*$)/m);
|
|
2663
|
+
const text3 = m2 ? m2[1].trim() : "";
|
|
2664
|
+
return text3 || null;
|
|
2665
|
+
}
|
|
2639
2666
|
function toDetail(filePath, relPath, raw) {
|
|
2640
2667
|
const parsed = matter6(raw);
|
|
2641
2668
|
const fm = parsed.data ?? {};
|
|
@@ -2647,8 +2674,10 @@ function toDetail(filePath, relPath, raw) {
|
|
|
2647
2674
|
resourceType: str(fm.resource_type),
|
|
2648
2675
|
provider: str(fm.provider),
|
|
2649
2676
|
environments: strArray(fm.environments),
|
|
2677
|
+
scope: parseScope(fm.scope),
|
|
2678
|
+
modules: strArray(fm.modules),
|
|
2650
2679
|
path: relPath,
|
|
2651
|
-
purpose: str(fm.purpose),
|
|
2680
|
+
purpose: str(fm.purpose) ?? purposeFromBody(body),
|
|
2652
2681
|
interfaces: parseInterfaces(fm.access_interfaces),
|
|
2653
2682
|
boundaries: parseBoundaries(fm.access_boundaries),
|
|
2654
2683
|
...(() => {
|
|
@@ -2667,6 +2696,8 @@ function getResources(dir) {
|
|
|
2667
2696
|
resourceType: str(fm.resource_type),
|
|
2668
2697
|
provider: str(fm.provider),
|
|
2669
2698
|
environments: strArray(fm.environments),
|
|
2699
|
+
scope: parseScope(fm.scope),
|
|
2700
|
+
modules: strArray(fm.modules),
|
|
2670
2701
|
path: a2.relPath
|
|
2671
2702
|
};
|
|
2672
2703
|
}).sort((x2, y) => x2.id.localeCompare(y.id));
|
|
@@ -2737,9 +2768,9 @@ function buildGraph(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
2737
2768
|
];
|
|
2738
2769
|
const presentLayers = [];
|
|
2739
2770
|
for (const layer2 of layerDocs) {
|
|
2740
|
-
const
|
|
2741
|
-
if (
|
|
2742
|
-
addNode({ id: layer2.id, type: layer2.type, label: layer2.label, path:
|
|
2771
|
+
const path9 = layer2.files.map((f3) => `${KNOWLEDGE3}/${f3}`).find((rel) => exists(join(dir, rel)));
|
|
2772
|
+
if (path9) {
|
|
2773
|
+
addNode({ id: layer2.id, type: layer2.type, label: layer2.label, path: path9 });
|
|
2743
2774
|
presentLayers.push(layer2.id);
|
|
2744
2775
|
}
|
|
2745
2776
|
}
|
|
@@ -2782,12 +2813,20 @@ function buildGraph(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
2782
2813
|
const resourceTitleById = /* @__PURE__ */ new Map();
|
|
2783
2814
|
for (const r2 of getResources(dir)) {
|
|
2784
2815
|
resourceTitleById.set(r2.id, r2.title);
|
|
2816
|
+
const resId = `resource:${r2.id}`;
|
|
2785
2817
|
addNode({
|
|
2786
|
-
id:
|
|
2818
|
+
id: resId,
|
|
2787
2819
|
type: "project-resource",
|
|
2788
2820
|
label: r2.title ? `${r2.id} ${r2.title}`.trim() : r2.id,
|
|
2789
2821
|
path: r2.path
|
|
2790
2822
|
});
|
|
2823
|
+
const depModules = new Set(r2.modules);
|
|
2824
|
+
if (r2.scope?.type === "module" && r2.scope.module) depModules.add(r2.scope.module);
|
|
2825
|
+
for (const mod of depModules) {
|
|
2826
|
+
const modId = `module:${mod}`;
|
|
2827
|
+
addNode({ id: modId, type: "module", label: mod });
|
|
2828
|
+
addEdge(modId, resId, "depends_on");
|
|
2829
|
+
}
|
|
2791
2830
|
}
|
|
2792
2831
|
for (const wi of selectedWIs) {
|
|
2793
2832
|
const id = wi.id || wi.title;
|
|
@@ -4363,10 +4402,10 @@ function resolveNextStep(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
4363
4402
|
const q3 = (rel) => analyzeKnowledgeArtifact(dir, rel);
|
|
4364
4403
|
const resolveAgent = (agent) => {
|
|
4365
4404
|
const file = agent.endsWith(".md") ? agent : `${agent}.md`;
|
|
4366
|
-
const
|
|
4367
|
-
const installed = isFile(join(dir,
|
|
4405
|
+
const path9 = agentInstallPath(file);
|
|
4406
|
+
const installed = isFile(join(dir, path9));
|
|
4368
4407
|
const group = agentGroupOf(file);
|
|
4369
|
-
return { agentPath:
|
|
4408
|
+
return { agentPath: path9, agentInstalled: installed, installCommand: installed ? void 0 : `kaddo add agents --group ${group}` };
|
|
4370
4409
|
};
|
|
4371
4410
|
const moduleRepo = isModule(config);
|
|
4372
4411
|
const coreRepo = isCore(config);
|
|
@@ -6121,6 +6160,16 @@ supporting notes as \`knowledge/tech/discovery/architecture-notes.md\` and decis
|
|
|
6121
6160
|
under \`knowledge/tech/decisions/\`. Kaddo still reads the legacy root locations for backward
|
|
6122
6161
|
compatibility, but new output should use \`discovery/\`.
|
|
6123
6162
|
|
|
6163
|
+
## Project Resource candidates
|
|
6164
|
+
|
|
6165
|
+
When the knowledge describes an external system the project depends on (a database, cloud account,
|
|
6166
|
+
API, queue, storage, service\u2026), you may propose it as a **candidate** Project Resource \u2014 never create
|
|
6167
|
+
it silently. Discovery \u2260 creation: present candidates for human review and materialize only through
|
|
6168
|
+
\`kaddo_create_resource\` / \`kaddo resources create\` with explicit confirmation. Ground every
|
|
6169
|
+
candidate in the knowledge (no guessing from ambiguous names). Model the stable system, not its access
|
|
6170
|
+
interface (CLI/MCP/SQL/API are interfaces); keep \`resources\` separate from \`affected_modules\`; and
|
|
6171
|
+
never include secret values \u2014 only reference names.
|
|
6172
|
+
|
|
6124
6173
|
## Quality Checklist
|
|
6125
6174
|
|
|
6126
6175
|
- Every component is backed by evidence from the context pack.
|
|
@@ -9701,7 +9750,7 @@ function buildGuardHistory(dir) {
|
|
|
9701
9750
|
const d3 = hotspotDir(t.code_path);
|
|
9702
9751
|
hotspotMap.set(d3, (hotspotMap.get(d3) ?? 0) + 1);
|
|
9703
9752
|
}
|
|
9704
|
-
const hotspots = [...hotspotMap.entries()].map(([
|
|
9753
|
+
const hotspots = [...hotspotMap.entries()].map(([path9, warnings]) => ({ path: path9, warnings })).sort((a2, b2) => b2.warnings - a2.warnings);
|
|
9705
9754
|
return {
|
|
9706
9755
|
available: true,
|
|
9707
9756
|
total_runs: sorted.length,
|
|
@@ -11060,7 +11109,7 @@ function getPrompt(root, name) {
|
|
|
11060
11109
|
}
|
|
11061
11110
|
|
|
11062
11111
|
// src/tools.ts
|
|
11063
|
-
import
|
|
11112
|
+
import matter15 from "gray-matter";
|
|
11064
11113
|
|
|
11065
11114
|
// ../cli/dist/core.js
|
|
11066
11115
|
import fs3 from "fs";
|
|
@@ -11084,18 +11133,19 @@ import { dirname as dirname2, join as join22, parse as parse2 } from "path";
|
|
|
11084
11133
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
11085
11134
|
import matter72 from "gray-matter";
|
|
11086
11135
|
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
11136
|
import matter92 from "gray-matter";
|
|
11092
|
-
import
|
|
11137
|
+
import { parse as parseYaml82, stringify as stringifyYaml42 } from "yaml";
|
|
11093
11138
|
import fs4 from "fs";
|
|
11139
|
+
import path4 from "path";
|
|
11140
|
+
import crypto2 from "crypto";
|
|
11094
11141
|
import matter102 from "gray-matter";
|
|
11142
|
+
import crypto3 from "crypto";
|
|
11143
|
+
import fs5 from "fs";
|
|
11095
11144
|
import matter112 from "gray-matter";
|
|
11145
|
+
import matter122 from "gray-matter";
|
|
11096
11146
|
import { parse as parseYaml92, stringify as stringifyYaml5 } from "yaml";
|
|
11097
11147
|
import { parse as parseYaml102, stringify as stringifyYaml6 } from "yaml";
|
|
11098
|
-
import
|
|
11148
|
+
import matter13 from "gray-matter";
|
|
11099
11149
|
function exists2(p) {
|
|
11100
11150
|
return fs3.existsSync(p);
|
|
11101
11151
|
}
|
|
@@ -11206,8 +11256,8 @@ function loadConfig2(dir) {
|
|
|
11206
11256
|
const parsed = configSchema2.safeParse(raw ?? {});
|
|
11207
11257
|
if (!parsed.success) {
|
|
11208
11258
|
const issues = parsed.error.issues.map((i) => {
|
|
11209
|
-
const
|
|
11210
|
-
return
|
|
11259
|
+
const path52 = i.path.join(".");
|
|
11260
|
+
return path52 ? ` - ${path52}: ${i.message}` : ` - ${i.message}`;
|
|
11211
11261
|
}).join("\n");
|
|
11212
11262
|
throw new ConfigError2(`Invalid .kaddo/config.yml:
|
|
11213
11263
|
${issues}`);
|
|
@@ -11411,11 +11461,11 @@ function moduleArtifactCoverage2(dir, id) {
|
|
|
11411
11461
|
};
|
|
11412
11462
|
}
|
|
11413
11463
|
function loadMappedModules2(dir) {
|
|
11414
|
-
const
|
|
11415
|
-
if (!exists2(
|
|
11464
|
+
const path52 = join3(dir, DESCRIPTOR_PATH2);
|
|
11465
|
+
if (!exists2(path52)) return [];
|
|
11416
11466
|
let parsed;
|
|
11417
11467
|
try {
|
|
11418
|
-
parsed = parseYaml32(readFile2(
|
|
11468
|
+
parsed = parseYaml32(readFile2(path52));
|
|
11419
11469
|
} catch {
|
|
11420
11470
|
return [];
|
|
11421
11471
|
}
|
|
@@ -12861,6 +12911,18 @@ function parseAuth2(v) {
|
|
|
12861
12911
|
const refs = strArray3(o2.refs).filter((r2) => !r2.includes("=") && /^[A-Za-z0-9_.-]+$/.test(r2));
|
|
12862
12912
|
return { mode: str2(o2.mode), refs };
|
|
12863
12913
|
}
|
|
12914
|
+
function parseScope2(v) {
|
|
12915
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) return null;
|
|
12916
|
+
const o2 = v;
|
|
12917
|
+
const type = str2(o2.type);
|
|
12918
|
+
if (!type) return null;
|
|
12919
|
+
return { type, module: str2(o2.module) };
|
|
12920
|
+
}
|
|
12921
|
+
function purposeFromBody2(body) {
|
|
12922
|
+
const m2 = body.match(/^#{1,2}\s+Purpose\s*\n+([\s\S]*?)(?=\n#{1,2}\s|\s*$)/m);
|
|
12923
|
+
const text22 = m2 ? m2[1].trim() : "";
|
|
12924
|
+
return text22 || null;
|
|
12925
|
+
}
|
|
12864
12926
|
function toDetail2(filePath, relPath, raw) {
|
|
12865
12927
|
const parsed = matter42(raw);
|
|
12866
12928
|
const fm = parsed.data ?? {};
|
|
@@ -12872,8 +12934,10 @@ function toDetail2(filePath, relPath, raw) {
|
|
|
12872
12934
|
resourceType: str2(fm.resource_type),
|
|
12873
12935
|
provider: str2(fm.provider),
|
|
12874
12936
|
environments: strArray3(fm.environments),
|
|
12937
|
+
scope: parseScope2(fm.scope),
|
|
12938
|
+
modules: strArray3(fm.modules),
|
|
12875
12939
|
path: relPath,
|
|
12876
|
-
purpose: str2(fm.purpose),
|
|
12940
|
+
purpose: str2(fm.purpose) ?? purposeFromBody2(body),
|
|
12877
12941
|
interfaces: parseInterfaces2(fm.access_interfaces),
|
|
12878
12942
|
boundaries: parseBoundaries2(fm.access_boundaries),
|
|
12879
12943
|
...(() => {
|
|
@@ -12892,6 +12956,8 @@ function getResources2(dir) {
|
|
|
12892
12956
|
resourceType: str2(fm.resource_type),
|
|
12893
12957
|
provider: str2(fm.provider),
|
|
12894
12958
|
environments: strArray3(fm.environments),
|
|
12959
|
+
scope: parseScope2(fm.scope),
|
|
12960
|
+
modules: strArray3(fm.modules),
|
|
12895
12961
|
path: a2.relPath
|
|
12896
12962
|
};
|
|
12897
12963
|
}).sort((x2, y) => x2.id.localeCompare(y.id));
|
|
@@ -12960,9 +13026,9 @@ function buildGraph2(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
12960
13026
|
];
|
|
12961
13027
|
const presentLayers = [];
|
|
12962
13028
|
for (const layer2 of layerDocs) {
|
|
12963
|
-
const
|
|
12964
|
-
if (
|
|
12965
|
-
addNode({ id: layer2.id, type: layer2.type, label: layer2.label, path:
|
|
13029
|
+
const path52 = layer2.files.map((f3) => `${KNOWLEDGE22}/${f3}`).find((rel) => exists2(join3(dir, rel)));
|
|
13030
|
+
if (path52) {
|
|
13031
|
+
addNode({ id: layer2.id, type: layer2.type, label: layer2.label, path: path52 });
|
|
12966
13032
|
presentLayers.push(layer2.id);
|
|
12967
13033
|
}
|
|
12968
13034
|
}
|
|
@@ -13005,12 +13071,20 @@ function buildGraph2(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
13005
13071
|
const resourceTitleById = /* @__PURE__ */ new Map();
|
|
13006
13072
|
for (const r2 of getResources2(dir)) {
|
|
13007
13073
|
resourceTitleById.set(r2.id, r2.title);
|
|
13074
|
+
const resId = `resource:${r2.id}`;
|
|
13008
13075
|
addNode({
|
|
13009
|
-
id:
|
|
13076
|
+
id: resId,
|
|
13010
13077
|
type: "project-resource",
|
|
13011
13078
|
label: r2.title ? `${r2.id} ${r2.title}`.trim() : r2.id,
|
|
13012
13079
|
path: r2.path
|
|
13013
13080
|
});
|
|
13081
|
+
const depModules = new Set(r2.modules);
|
|
13082
|
+
if (r2.scope?.type === "module" && r2.scope.module) depModules.add(r2.scope.module);
|
|
13083
|
+
for (const mod of depModules) {
|
|
13084
|
+
const modId = `module:${mod}`;
|
|
13085
|
+
addNode({ id: modId, type: "module", label: mod });
|
|
13086
|
+
addEdge(modId, resId, "depends_on");
|
|
13087
|
+
}
|
|
13014
13088
|
}
|
|
13015
13089
|
for (const wi of selectedWIs) {
|
|
13016
13090
|
const id = wi.id || wi.title;
|
|
@@ -14698,6 +14772,16 @@ supporting notes as \`knowledge/tech/discovery/architecture-notes.md\` and decis
|
|
|
14698
14772
|
under \`knowledge/tech/decisions/\`. Kaddo still reads the legacy root locations for backward
|
|
14699
14773
|
compatibility, but new output should use \`discovery/\`.
|
|
14700
14774
|
|
|
14775
|
+
## Project Resource candidates
|
|
14776
|
+
|
|
14777
|
+
When the knowledge describes an external system the project depends on (a database, cloud account,
|
|
14778
|
+
API, queue, storage, service\u2026), you may propose it as a **candidate** Project Resource \u2014 never create
|
|
14779
|
+
it silently. Discovery \u2260 creation: present candidates for human review and materialize only through
|
|
14780
|
+
\`kaddo_create_resource\` / \`kaddo resources create\` with explicit confirmation. Ground every
|
|
14781
|
+
candidate in the knowledge (no guessing from ambiguous names). Model the stable system, not its access
|
|
14782
|
+
interface (CLI/MCP/SQL/API are interfaces); keep \`resources\` separate from \`affected_modules\`; and
|
|
14783
|
+
never include secret values \u2014 only reference names.
|
|
14784
|
+
|
|
14701
14785
|
## Quality Checklist
|
|
14702
14786
|
|
|
14703
14787
|
- Every component is backed by evidence from the context pack.
|
|
@@ -17251,8 +17335,8 @@ function parseGraphReason(v) {
|
|
|
17251
17335
|
if (!v || typeof v !== "object") return null;
|
|
17252
17336
|
const o2 = v;
|
|
17253
17337
|
const relationship = typeof o2.relationship === "string" && o2.relationship.trim() ? o2.relationship.trim() : null;
|
|
17254
|
-
const
|
|
17255
|
-
return relationship ||
|
|
17338
|
+
const path52 = Array.isArray(o2.path) ? o2.path.map(String).filter(Boolean) : [];
|
|
17339
|
+
return relationship || path52.length ? { relationship, path: path52 } : null;
|
|
17256
17340
|
}
|
|
17257
17341
|
function parseExplain(o2) {
|
|
17258
17342
|
const refs = Array.isArray(o2.evidence) ? o2.evidence : Array.isArray(o2.evidence_refs) ? o2.evidence_refs : [];
|
|
@@ -17307,7 +17391,7 @@ function parseOriginalSnapshot(fm) {
|
|
|
17307
17391
|
function readBody(filePath) {
|
|
17308
17392
|
try {
|
|
17309
17393
|
const raw = readFile2(filePath);
|
|
17310
|
-
return
|
|
17394
|
+
return matter92(raw).content;
|
|
17311
17395
|
} catch {
|
|
17312
17396
|
return "";
|
|
17313
17397
|
}
|
|
@@ -17509,7 +17593,7 @@ function nextWorkItemId(dir) {
|
|
|
17509
17593
|
let max = 0;
|
|
17510
17594
|
const walk = (d3) => {
|
|
17511
17595
|
if (!exists2(d3)) return;
|
|
17512
|
-
for (const entry of
|
|
17596
|
+
for (const entry of fs4.readdirSync(d3)) {
|
|
17513
17597
|
const full = join3(d3, entry);
|
|
17514
17598
|
if (isFile2(full)) {
|
|
17515
17599
|
const m2 = entry.match(/WI-(\d+)/);
|
|
@@ -17522,15 +17606,15 @@ function nextWorkItemId(dir) {
|
|
|
17522
17606
|
walk(wiDir);
|
|
17523
17607
|
return `WI-${String(max + 1).padStart(3, "0")}`;
|
|
17524
17608
|
}
|
|
17525
|
-
function
|
|
17526
|
-
|
|
17609
|
+
function atomicWrite3(filePath, content) {
|
|
17610
|
+
fs4.mkdirSync(path4.dirname(filePath), { recursive: true });
|
|
17527
17611
|
const tmp = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
17528
|
-
|
|
17612
|
+
fs4.writeFileSync(tmp, content, "utf-8");
|
|
17529
17613
|
try {
|
|
17530
|
-
|
|
17614
|
+
fs4.renameSync(tmp, filePath);
|
|
17531
17615
|
} catch (err) {
|
|
17532
17616
|
try {
|
|
17533
|
-
|
|
17617
|
+
fs4.rmSync(tmp, { force: true });
|
|
17534
17618
|
} catch {
|
|
17535
17619
|
}
|
|
17536
17620
|
throw err;
|
|
@@ -17591,8 +17675,8 @@ function ensureH1(body, title) {
|
|
|
17591
17675
|
|
|
17592
17676
|
${trimmed}`;
|
|
17593
17677
|
}
|
|
17594
|
-
function
|
|
17595
|
-
return
|
|
17678
|
+
function serialize2(data, body) {
|
|
17679
|
+
return matter102.stringify(`
|
|
17596
17680
|
${body.trim()}
|
|
17597
17681
|
`, data);
|
|
17598
17682
|
}
|
|
@@ -17660,8 +17744,8 @@ function createWorkItem(dir, opts) {
|
|
|
17660
17744
|
const relPath = `${WORK_ITEMS_DIR}/draft/${id}-${slugify22(title)}.md`;
|
|
17661
17745
|
const filePath = join3(dir, relPath);
|
|
17662
17746
|
if (exists2(filePath)) throw new WorkItemWriteError("INVALID_INPUT", `Work Item file already exists: ${relPath}`);
|
|
17663
|
-
const raw =
|
|
17664
|
-
|
|
17747
|
+
const raw = serialize2(data, body);
|
|
17748
|
+
atomicWrite3(filePath, raw);
|
|
17665
17749
|
return { id, path: relPath, revision: revisionOf(raw) };
|
|
17666
17750
|
}
|
|
17667
17751
|
function dimensionOf(type) {
|
|
@@ -18383,7 +18467,7 @@ function detectFormat(raw) {
|
|
|
18383
18467
|
if (!trimmed) throw new ImportError("EMPTY_INPUT", "Import content is empty.");
|
|
18384
18468
|
if (trimmed.startsWith("---")) {
|
|
18385
18469
|
try {
|
|
18386
|
-
const { data } =
|
|
18470
|
+
const { data } = matter112(trimmed);
|
|
18387
18471
|
if (data && typeof data === "object" && Object.keys(data).length > 0) {
|
|
18388
18472
|
const d3 = data;
|
|
18389
18473
|
if (d3.type === "work-item" || d3.id && typeof d3.id === "string" && /^WI-\d+/.test(d3.id)) {
|
|
@@ -18405,7 +18489,7 @@ function parseContent(raw) {
|
|
|
18405
18489
|
let bodyText = trimmed;
|
|
18406
18490
|
if (format === "kaddo-frontmatter" || format === "markdown-frontmatter") {
|
|
18407
18491
|
try {
|
|
18408
|
-
const parsed =
|
|
18492
|
+
const parsed = matter112(trimmed);
|
|
18409
18493
|
frontmatterFields = parsed.data ?? {};
|
|
18410
18494
|
bodyText = parsed.content.trim();
|
|
18411
18495
|
} catch {
|
|
@@ -18502,7 +18586,7 @@ function findDuplicateByHash(dir, hash) {
|
|
|
18502
18586
|
const raw = readFile2(item.filePath);
|
|
18503
18587
|
if (!raw) continue;
|
|
18504
18588
|
try {
|
|
18505
|
-
const { data } =
|
|
18589
|
+
const { data } = matter112(raw);
|
|
18506
18590
|
const source = data?.source;
|
|
18507
18591
|
if (source && typeof source === "object" && !Array.isArray(source)) {
|
|
18508
18592
|
const s = source;
|
|
@@ -18569,7 +18653,7 @@ function importWorkItem(dir, opts) {
|
|
|
18569
18653
|
};
|
|
18570
18654
|
}
|
|
18571
18655
|
if (opts.onConflict === "replace") {
|
|
18572
|
-
|
|
18656
|
+
fs5.rmSync(existing.filePath, { force: true });
|
|
18573
18657
|
replacedWorkItem = existing.workItemId;
|
|
18574
18658
|
}
|
|
18575
18659
|
}
|
|
@@ -19011,8 +19095,8 @@ async function handleJiraResponse(res) {
|
|
|
19011
19095
|
}
|
|
19012
19096
|
return await res.json();
|
|
19013
19097
|
}
|
|
19014
|
-
async function jiraFetch(baseUrl,
|
|
19015
|
-
const url = new URL(
|
|
19098
|
+
async function jiraFetch(baseUrl, path52, auth, timeoutMs, params) {
|
|
19099
|
+
const url = new URL(path52, baseUrl + "/");
|
|
19016
19100
|
if (params) for (const [k2, v] of Object.entries(params)) url.searchParams.set(k2, v);
|
|
19017
19101
|
const controller = new AbortController();
|
|
19018
19102
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
@@ -19030,8 +19114,8 @@ async function jiraFetch(baseUrl, path42, auth, timeoutMs, params) {
|
|
|
19030
19114
|
clearTimeout(timer);
|
|
19031
19115
|
}
|
|
19032
19116
|
}
|
|
19033
|
-
async function jiraPost(baseUrl,
|
|
19034
|
-
const url = new URL(
|
|
19117
|
+
async function jiraPost(baseUrl, path52, auth, timeoutMs, body) {
|
|
19118
|
+
const url = new URL(path52, baseUrl + "/");
|
|
19035
19119
|
const controller = new AbortController();
|
|
19036
19120
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
19037
19121
|
try {
|
|
@@ -19397,7 +19481,7 @@ function buildInitiativeContent(id, input) {
|
|
|
19397
19481
|
if (input.priority) data.priority = input.priority;
|
|
19398
19482
|
if (input.source) data.source = input.source;
|
|
19399
19483
|
if (input.sourceId) data.source_id = input.sourceId;
|
|
19400
|
-
return
|
|
19484
|
+
return matter13.stringify("\n" + (input.body ?? defaultBody(input.title.trim())), data);
|
|
19401
19485
|
}
|
|
19402
19486
|
function createInitiative(dir, input) {
|
|
19403
19487
|
if (!input.title?.trim()) throw new InitiativeWriteError("Initiative title is required.");
|
|
@@ -19421,10 +19505,10 @@ function transitionInitiative(dir, id, to) {
|
|
|
19421
19505
|
throw new InitiativeWriteError(`Invalid transition ${ini.status} \u2192 ${to}.`);
|
|
19422
19506
|
}
|
|
19423
19507
|
const raw = readFile2(ini.filePath);
|
|
19424
|
-
const { data, content } =
|
|
19508
|
+
const { data, content } = matter13(raw, {});
|
|
19425
19509
|
data.status = to;
|
|
19426
19510
|
if (to === "completed") data.completed_at = today2();
|
|
19427
|
-
const newContent =
|
|
19511
|
+
const newContent = matter13.stringify(content, data);
|
|
19428
19512
|
writeFile2(ini.filePath, newContent);
|
|
19429
19513
|
const updated = parseInitiative2(dir, ini.filePath, newContent);
|
|
19430
19514
|
if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after transition.");
|
|
@@ -19434,7 +19518,7 @@ function addCandidate(dir, id, candidate) {
|
|
|
19434
19518
|
const ini = getInitiative(dir, id);
|
|
19435
19519
|
if (!ini) throw new InitiativeWriteError(`Initiative ${id} not found.`);
|
|
19436
19520
|
const raw = readFile2(ini.filePath);
|
|
19437
|
-
const { data, content } =
|
|
19521
|
+
const { data, content } = matter13(raw, {});
|
|
19438
19522
|
const existing = Array.isArray(data.candidates) ? data.candidates : [];
|
|
19439
19523
|
const fallbackId = `WI-CANDIDATE-${String(existing.length + 1).padStart(3, "0")}`;
|
|
19440
19524
|
const normalized = normalizeCandidate(candidate, fallbackId);
|
|
@@ -19442,7 +19526,7 @@ function addCandidate(dir, id, candidate) {
|
|
|
19442
19526
|
throw new InitiativeWriteError(`Candidate ${normalized.id} already exists in ${id}.`);
|
|
19443
19527
|
}
|
|
19444
19528
|
data.candidates = [...existing, candidateToData(normalized)];
|
|
19445
|
-
const newContent =
|
|
19529
|
+
const newContent = matter13.stringify(content, data);
|
|
19446
19530
|
writeFile2(ini.filePath, newContent);
|
|
19447
19531
|
const updated = parseInitiative2(dir, ini.filePath, newContent);
|
|
19448
19532
|
if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after adding a candidate.");
|
|
@@ -19452,7 +19536,7 @@ function updateInitiative(dir, id, patch) {
|
|
|
19452
19536
|
const ini = getInitiative(dir, id);
|
|
19453
19537
|
if (!ini) throw new InitiativeWriteError(`Initiative ${id} not found.`);
|
|
19454
19538
|
const raw = readFile2(ini.filePath);
|
|
19455
|
-
const { data, content } =
|
|
19539
|
+
const { data, content } = matter13(raw, {});
|
|
19456
19540
|
if (patch.title !== void 0) {
|
|
19457
19541
|
if (!patch.title.trim()) throw new InitiativeWriteError("Initiative title cannot be empty.");
|
|
19458
19542
|
data.title = patch.title.trim();
|
|
@@ -19462,7 +19546,7 @@ function updateInitiative(dir, id, patch) {
|
|
|
19462
19546
|
if (patch.knowledgeLevel !== void 0) data.knowledge_level = patch.knowledgeLevel;
|
|
19463
19547
|
if (patch.domains !== void 0) data.domains = patch.domains;
|
|
19464
19548
|
if (patch.relatedCapabilities !== void 0) data.related_capabilities = patch.relatedCapabilities;
|
|
19465
|
-
const newContent =
|
|
19549
|
+
const newContent = matter13.stringify(content, data);
|
|
19466
19550
|
writeFile2(ini.filePath, newContent);
|
|
19467
19551
|
const updated = parseInitiative2(dir, ini.filePath, newContent);
|
|
19468
19552
|
if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after update.");
|
|
@@ -19480,10 +19564,10 @@ function addExternalLink(dir, id, link) {
|
|
|
19480
19564
|
throw new InitiativeWriteError(`External link ${link.integration}:${link.externalId} already exists on ${id}.`);
|
|
19481
19565
|
}
|
|
19482
19566
|
const raw = readFile2(ini.filePath);
|
|
19483
|
-
const { data, content } =
|
|
19567
|
+
const { data, content } = matter13(raw, {});
|
|
19484
19568
|
const existing = Array.isArray(data.external_links) ? data.external_links : [];
|
|
19485
19569
|
data.external_links = [...existing, externalLinkToData(link)];
|
|
19486
|
-
const newContent =
|
|
19570
|
+
const newContent = matter13.stringify(content, data);
|
|
19487
19571
|
writeFile2(ini.filePath, newContent);
|
|
19488
19572
|
const updated = parseInitiative2(dir, ini.filePath, newContent);
|
|
19489
19573
|
if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after adding an external link.");
|
|
@@ -19552,7 +19636,7 @@ function buildWorkItemContent(wiId, iniId, candidate) {
|
|
|
19552
19636
|
"_What did we learn? Update after completion._",
|
|
19553
19637
|
""
|
|
19554
19638
|
].join("\n");
|
|
19555
|
-
return
|
|
19639
|
+
return matter13.stringify("\n" + body, data);
|
|
19556
19640
|
}
|
|
19557
19641
|
function materializeCandidate(dir, id, candidateId) {
|
|
19558
19642
|
const ini = getInitiative(dir, id);
|
|
@@ -19569,13 +19653,13 @@ function materializeCandidate(dir, id, candidateId) {
|
|
|
19569
19653
|
ensureDir2(join3(dir, DRAFT_DIR));
|
|
19570
19654
|
writeFile2(join3(dir, DRAFT_DIR, fileName), buildWorkItemContent(wiId, ini.id, candidate));
|
|
19571
19655
|
const raw = readFile2(ini.filePath);
|
|
19572
|
-
const { data, content } =
|
|
19656
|
+
const { data, content } = matter13(raw, {});
|
|
19573
19657
|
const cands = Array.isArray(data.candidates) ? data.candidates : [];
|
|
19574
19658
|
for (const c of cands) {
|
|
19575
19659
|
if (c && typeof c === "object" && String(c.id) === candidateId) c.materialized_as = wiId;
|
|
19576
19660
|
}
|
|
19577
19661
|
data.candidates = cands;
|
|
19578
|
-
const newContent =
|
|
19662
|
+
const newContent = matter13.stringify(content, data);
|
|
19579
19663
|
writeFile2(ini.filePath, newContent);
|
|
19580
19664
|
const updated = parseInitiative2(dir, ini.filePath, newContent);
|
|
19581
19665
|
if (!updated) throw new InitiativeWriteError("Failed to parse the Initiative after materialization.");
|
|
@@ -19686,6 +19770,164 @@ function evaluateInitiativeCompletion(dir, ini) {
|
|
|
19686
19770
|
};
|
|
19687
19771
|
}
|
|
19688
19772
|
|
|
19773
|
+
// ../cli/src/core/resource-write.ts
|
|
19774
|
+
import fs6 from "fs";
|
|
19775
|
+
import path5 from "path";
|
|
19776
|
+
import matter14 from "gray-matter";
|
|
19777
|
+
var RESOURCES_DIR = "knowledge/tech/resources";
|
|
19778
|
+
var ResourceWriteError = class extends Error {
|
|
19779
|
+
constructor(code, message) {
|
|
19780
|
+
super(message);
|
|
19781
|
+
this.code = code;
|
|
19782
|
+
this.name = "ResourceWriteError";
|
|
19783
|
+
}
|
|
19784
|
+
code;
|
|
19785
|
+
};
|
|
19786
|
+
function slug4(s) {
|
|
19787
|
+
return s.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60);
|
|
19788
|
+
}
|
|
19789
|
+
function atomicWrite(filePath, content) {
|
|
19790
|
+
const tmp = `${filePath}.tmp-${process.pid}-${Date.now()}`;
|
|
19791
|
+
fs6.writeFileSync(tmp, content, "utf-8");
|
|
19792
|
+
fs6.renameSync(tmp, filePath);
|
|
19793
|
+
}
|
|
19794
|
+
function sanitizeRefs(refs) {
|
|
19795
|
+
if (!Array.isArray(refs)) return [];
|
|
19796
|
+
return refs.map((r2) => String(r2).trim()).filter((r2) => r2 && !r2.includes("=") && /^[A-Za-z0-9_.-]+$/.test(r2));
|
|
19797
|
+
}
|
|
19798
|
+
function allResourceArtifacts(dir) {
|
|
19799
|
+
return discoverKnowledge(dir).filter((a2) => a2.type === RESOURCE_ARTIFACT_TYPE);
|
|
19800
|
+
}
|
|
19801
|
+
function nextResourceId(dir, title, explicit) {
|
|
19802
|
+
const existing = new Set(allResourceArtifacts(dir).map((a2) => (a2.id || "").toUpperCase()));
|
|
19803
|
+
if (explicit && explicit.trim()) return explicit.trim();
|
|
19804
|
+
const base = `RES-${slug4(title) || "resource"}`;
|
|
19805
|
+
if (!existing.has(base.toUpperCase())) return base;
|
|
19806
|
+
for (let i = 2; i < 1e3; i++) {
|
|
19807
|
+
const candidate = `${base}-${i}`;
|
|
19808
|
+
if (!existing.has(candidate.toUpperCase())) return candidate;
|
|
19809
|
+
}
|
|
19810
|
+
throw new ResourceWriteError("ID_EXHAUSTED", `Could not derive a unique id for "${title}".`);
|
|
19811
|
+
}
|
|
19812
|
+
function validateResource(dir, input) {
|
|
19813
|
+
const findings = [];
|
|
19814
|
+
if (input.resourceType && !RESOURCE_TYPES.includes(input.resourceType)) {
|
|
19815
|
+
findings.push({ level: "warning", message: `Unknown resource_type "${input.resourceType}" (not in the known set).` });
|
|
19816
|
+
}
|
|
19817
|
+
for (const i of input.accessInterfaces ?? []) {
|
|
19818
|
+
if (i.type && !INTERFACE_TYPES.includes(i.type)) {
|
|
19819
|
+
findings.push({ level: "warning", message: `Unknown access interface type "${i.type}".` });
|
|
19820
|
+
}
|
|
19821
|
+
}
|
|
19822
|
+
const mapped = loadMappedModules(dir).map((m2) => m2.id);
|
|
19823
|
+
if (mapped.length > 0) {
|
|
19824
|
+
const refs = /* @__PURE__ */ new Set();
|
|
19825
|
+
if (input.scope?.type === "module" && input.scope.module) refs.add(input.scope.module);
|
|
19826
|
+
for (const m2 of input.modules ?? []) refs.add(m2);
|
|
19827
|
+
for (const m2 of refs) {
|
|
19828
|
+
if (!mapped.includes(m2)) findings.push({ level: "warning", message: `Module "${m2}" is not in .kaddo/modules.yml.` });
|
|
19829
|
+
}
|
|
19830
|
+
}
|
|
19831
|
+
if (input.scope && input.scope.type === "module" && !input.scope.module) {
|
|
19832
|
+
findings.push({ level: "error", message: 'scope.type "module" requires a module id.' });
|
|
19833
|
+
}
|
|
19834
|
+
return findings;
|
|
19835
|
+
}
|
|
19836
|
+
function buildFrontmatter(id, input, base = {}) {
|
|
19837
|
+
const data = { ...base };
|
|
19838
|
+
data.type = RESOURCE_ARTIFACT_TYPE;
|
|
19839
|
+
data.id = id;
|
|
19840
|
+
if (input.title != null) data.title = input.title;
|
|
19841
|
+
if (input.resourceType != null) data.resource_type = input.resourceType;
|
|
19842
|
+
if (input.provider != null) data.provider = input.provider;
|
|
19843
|
+
if (input.environments != null) data.environments = input.environments;
|
|
19844
|
+
if (input.scope != null) data.scope = input.scope.module ? { type: input.scope.type, module: input.scope.module } : { type: input.scope.type };
|
|
19845
|
+
if (input.modules != null) data.modules = input.modules;
|
|
19846
|
+
if (input.accessInterfaces != null) data.access_interfaces = input.accessInterfaces;
|
|
19847
|
+
if (input.accessBoundaries != null) data.access_boundaries = input.accessBoundaries;
|
|
19848
|
+
if (input.authentication != null) {
|
|
19849
|
+
const mode = input.authentication.mode;
|
|
19850
|
+
const refs = sanitizeRefs(input.authentication.refs);
|
|
19851
|
+
data.authentication = { ...mode ? { mode } : {}, ...refs.length ? { refs } : {} };
|
|
19852
|
+
}
|
|
19853
|
+
return data;
|
|
19854
|
+
}
|
|
19855
|
+
function serialize(data, body) {
|
|
19856
|
+
return matter14.stringify(`
|
|
19857
|
+
${body.trim()}
|
|
19858
|
+
`, data);
|
|
19859
|
+
}
|
|
19860
|
+
function createResource(dir, input) {
|
|
19861
|
+
const title = (input.title ?? "").trim();
|
|
19862
|
+
if (!title) throw new ResourceWriteError("INVALID_INPUT", "A resource title is required.");
|
|
19863
|
+
const resourceType = (input.resourceType ?? "").trim();
|
|
19864
|
+
if (!resourceType) throw new ResourceWriteError("INVALID_INPUT", "A resource_type is required.");
|
|
19865
|
+
const findings = validateResource(dir, input);
|
|
19866
|
+
if (findings.some((f3) => f3.level === "error")) {
|
|
19867
|
+
throw new ResourceWriteError("VALIDATION_FAILED", findings.filter((f3) => f3.level === "error").map((f3) => f3.message).join("; "));
|
|
19868
|
+
}
|
|
19869
|
+
const existing = allResourceArtifacts(dir);
|
|
19870
|
+
if (input.id && existing.some((a2) => (a2.id || "").toUpperCase() === input.id.toUpperCase())) {
|
|
19871
|
+
throw new ResourceWriteError("DUPLICATE", `A resource with id "${input.id}" already exists.`);
|
|
19872
|
+
}
|
|
19873
|
+
if (existing.some((a2) => (a2.title || "").toLowerCase() === title.toLowerCase())) {
|
|
19874
|
+
throw new ResourceWriteError("DUPLICATE", `A resource titled "${title}" already exists.`);
|
|
19875
|
+
}
|
|
19876
|
+
const id = nextResourceId(dir, title, input.id);
|
|
19877
|
+
const data = buildFrontmatter(id, input);
|
|
19878
|
+
const body = `# Purpose
|
|
19879
|
+
|
|
19880
|
+
${(input.purpose ?? "").trim() || "TODO: describe what this resource is for."}`;
|
|
19881
|
+
const relPath = `${RESOURCES_DIR}/${slug4(title) || slug4(id)}.md`;
|
|
19882
|
+
const filePath = join(dir, relPath);
|
|
19883
|
+
if (exists(filePath)) throw new ResourceWriteError("DUPLICATE", `Resource file already exists: ${relPath}`);
|
|
19884
|
+
ensureDir(path5.dirname(filePath));
|
|
19885
|
+
atomicWrite(filePath, serialize(data, body));
|
|
19886
|
+
return { id, path: relPath, findings };
|
|
19887
|
+
}
|
|
19888
|
+
function updateResource(dir, id, input) {
|
|
19889
|
+
const match = allResourceArtifacts(dir).find((a2) => a2.id === id || a2.title === id);
|
|
19890
|
+
if (!match) throw new ResourceWriteError("NOT_FOUND", `Project Resource "${id}" not found.`);
|
|
19891
|
+
const findings = validateResource(dir, input);
|
|
19892
|
+
if (findings.some((f3) => f3.level === "error")) {
|
|
19893
|
+
throw new ResourceWriteError("VALIDATION_FAILED", findings.filter((f3) => f3.level === "error").map((f3) => f3.message).join("; "));
|
|
19894
|
+
}
|
|
19895
|
+
const raw = readFile(match.filePath);
|
|
19896
|
+
const parsed = matter14(raw);
|
|
19897
|
+
const base = parsed.data ?? {};
|
|
19898
|
+
const data = buildFrontmatter(match.id || id, input, base);
|
|
19899
|
+
let body = parsed.content.trim();
|
|
19900
|
+
if (input.purpose != null) {
|
|
19901
|
+
const purpose = `# Purpose
|
|
19902
|
+
|
|
19903
|
+
${input.purpose.trim()}`;
|
|
19904
|
+
body = /^#\s+Purpose/m.test(body) ? body.replace(/#\s+Purpose[\s\S]*?(?=\n#\s|\n*$)/, purpose + "\n") : `${purpose}
|
|
19905
|
+
|
|
19906
|
+
${body}`.trim();
|
|
19907
|
+
}
|
|
19908
|
+
atomicWrite(match.filePath, serialize(data, body));
|
|
19909
|
+
return { id: match.id || id, path: match.relPath, findings };
|
|
19910
|
+
}
|
|
19911
|
+
function getResourceReferences(dir, id) {
|
|
19912
|
+
const refs = [];
|
|
19913
|
+
for (const wi of discoverWorkItems(dir)) {
|
|
19914
|
+
for (const rel of parseWorkItemResources(wi.rawFrontmatter)) {
|
|
19915
|
+
if (rel.id === id) refs.push({ kind: "work-item", id: wi.id || wi.title, role: rel.role, path: wi.relPath });
|
|
19916
|
+
}
|
|
19917
|
+
}
|
|
19918
|
+
return refs;
|
|
19919
|
+
}
|
|
19920
|
+
function deleteResource(dir, id, opts = {}) {
|
|
19921
|
+
const match = allResourceArtifacts(dir).find((a2) => a2.id === id || a2.title === id);
|
|
19922
|
+
if (!match) throw new ResourceWriteError("NOT_FOUND", `Project Resource "${id}" not found.`);
|
|
19923
|
+
const references = getResourceReferences(dir, match.id || id);
|
|
19924
|
+
if (!opts.confirm) {
|
|
19925
|
+
return { deleted: false, preview: { id: match.id || id, references } };
|
|
19926
|
+
}
|
|
19927
|
+
fs6.rmSync(match.filePath, { force: true });
|
|
19928
|
+
return { deleted: true, path: match.relPath };
|
|
19929
|
+
}
|
|
19930
|
+
|
|
19689
19931
|
// src/tools.ts
|
|
19690
19932
|
var ok = (data) => ({ ok: true, data });
|
|
19691
19933
|
var fail = (message) => ({ ok: false, message });
|
|
@@ -19781,12 +20023,12 @@ function markWorkItemReady(root, id, confirm2) {
|
|
|
19781
20023
|
}
|
|
19782
20024
|
const raw = readText(root, item.path);
|
|
19783
20025
|
if (!raw) return fail(`Could not read Work Item file: ${item.path}`);
|
|
19784
|
-
const parsed =
|
|
20026
|
+
const parsed = matter15(raw);
|
|
19785
20027
|
const data = { ...parsed.data };
|
|
19786
20028
|
const today3 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
19787
20029
|
data.status = "ready";
|
|
19788
20030
|
data.ready_at = today3;
|
|
19789
|
-
const newContent =
|
|
20031
|
+
const newContent = matter15.stringify(parsed.content, data);
|
|
19790
20032
|
const posixPath = item.path.replace(/\\/g, "/");
|
|
19791
20033
|
let newRelPath = item.path;
|
|
19792
20034
|
if (posixPath.includes("/work-items/draft/")) {
|
|
@@ -19957,11 +20199,64 @@ function getResourceTool(root, id) {
|
|
|
19957
20199
|
if (!resource) return fail(`Project Resource "${id}" not found.`);
|
|
19958
20200
|
return ok(resource);
|
|
19959
20201
|
}
|
|
20202
|
+
function toResourceInput(args) {
|
|
20203
|
+
const input = {};
|
|
20204
|
+
if (args.title != null) input.title = args.title;
|
|
20205
|
+
if (args.resource_type != null) input.resourceType = args.resource_type;
|
|
20206
|
+
if (args.provider != null) input.provider = args.provider;
|
|
20207
|
+
if (args.environments != null) input.environments = args.environments;
|
|
20208
|
+
if (args.scope != null || args.module != null) {
|
|
20209
|
+
const type = args.scope || (args.module ? "module" : "system");
|
|
20210
|
+
input.scope = type === "module" ? { type: "module", module: args.module ?? null } : { type: "system" };
|
|
20211
|
+
}
|
|
20212
|
+
if (args.modules != null) input.modules = args.modules;
|
|
20213
|
+
if (args.purpose != null) input.purpose = args.purpose;
|
|
20214
|
+
if (args.access_interfaces != null) input.accessInterfaces = args.access_interfaces;
|
|
20215
|
+
if (args.authentication != null) input.authentication = args.authentication;
|
|
20216
|
+
return input;
|
|
20217
|
+
}
|
|
20218
|
+
function createResourceTool(root, args, confirm2) {
|
|
20219
|
+
const input = toResourceInput(args);
|
|
20220
|
+
const findings = validateResource(root, input);
|
|
20221
|
+
if (!confirm2) {
|
|
20222
|
+
return ok({ status: "needs_confirmation", action: "create", proposal: input, findings, message: "Re-call with confirm=true to create this resource." });
|
|
20223
|
+
}
|
|
20224
|
+
try {
|
|
20225
|
+
return ok({ status: "created", ...createResource(root, input) });
|
|
20226
|
+
} catch (e) {
|
|
20227
|
+
return fail(e instanceof ResourceWriteError ? e.message : String(e));
|
|
20228
|
+
}
|
|
20229
|
+
}
|
|
20230
|
+
function updateResourceTool(root, id, args, confirm2) {
|
|
20231
|
+
if (!getResource(root, id)) return fail(`Project Resource "${id}" not found.`);
|
|
20232
|
+
const input = toResourceInput(args);
|
|
20233
|
+
const findings = validateResource(root, input);
|
|
20234
|
+
if (!confirm2) {
|
|
20235
|
+
return ok({ status: "needs_confirmation", action: "update", id, changes: input, findings, message: "Re-call with confirm=true to apply this update." });
|
|
20236
|
+
}
|
|
20237
|
+
try {
|
|
20238
|
+
return ok({ status: "updated", ...updateResource(root, id, input) });
|
|
20239
|
+
} catch (e) {
|
|
20240
|
+
return fail(e instanceof ResourceWriteError ? e.message : String(e));
|
|
20241
|
+
}
|
|
20242
|
+
}
|
|
20243
|
+
function deleteResourceTool(root, id, confirm2) {
|
|
20244
|
+
if (!getResource(root, id)) return fail(`Project Resource "${id}" not found.`);
|
|
20245
|
+
if (!confirm2) {
|
|
20246
|
+
const references = getResourceReferences(root, id);
|
|
20247
|
+
return ok({ status: "needs_confirmation", action: "delete", id, references, message: "Re-call with confirm=true to delete. References in other artifacts are NOT removed." });
|
|
20248
|
+
}
|
|
20249
|
+
try {
|
|
20250
|
+
return ok({ status: "deleted", ...deleteResource(root, id, { confirm: true }) });
|
|
20251
|
+
} catch (e) {
|
|
20252
|
+
return fail(e instanceof ResourceWriteError ? e.message : String(e));
|
|
20253
|
+
}
|
|
20254
|
+
}
|
|
19960
20255
|
|
|
19961
20256
|
// src/multirepo.ts
|
|
19962
|
-
import
|
|
19963
|
-
import
|
|
19964
|
-
import
|
|
20257
|
+
import fs7 from "fs";
|
|
20258
|
+
import path6 from "path";
|
|
20259
|
+
import matter16 from "gray-matter";
|
|
19965
20260
|
import { parse as parseYaml11, stringify as stringifyYaml7 } from "yaml";
|
|
19966
20261
|
var ok2 = (data) => ({ ok: true, data });
|
|
19967
20262
|
var fail2 = (message) => ({ ok: false, message });
|
|
@@ -19981,24 +20276,24 @@ function isCore2(config) {
|
|
|
19981
20276
|
return projectRole2(config) === "core";
|
|
19982
20277
|
}
|
|
19983
20278
|
function readModuleFile(root, repoPath, relFile) {
|
|
19984
|
-
const moduleDir =
|
|
19985
|
-
const abs =
|
|
20279
|
+
const moduleDir = path6.resolve(root, repoPath);
|
|
20280
|
+
const abs = path6.resolve(moduleDir, relFile);
|
|
19986
20281
|
if (!abs.startsWith(moduleDir)) return null;
|
|
19987
|
-
if (!
|
|
19988
|
-
return
|
|
20282
|
+
if (!fs7.existsSync(abs) || !fs7.statSync(abs).isFile()) return null;
|
|
20283
|
+
return fs7.readFileSync(abs, "utf-8");
|
|
19989
20284
|
}
|
|
19990
20285
|
function detectModuleStatus2(root, repoPath) {
|
|
19991
|
-
const moduleDir =
|
|
19992
|
-
if (!
|
|
20286
|
+
const moduleDir = path6.resolve(root, repoPath);
|
|
20287
|
+
if (!fs7.existsSync(moduleDir)) {
|
|
19993
20288
|
return { status: "missing", configured: false, moduleContext: false, currentState: false, codebase: false, warning: `Directory \`${repoPath}\` does not exist.` };
|
|
19994
20289
|
}
|
|
19995
|
-
const configPath =
|
|
19996
|
-
if (!
|
|
20290
|
+
const configPath = path6.join(moduleDir, ".kaddo", "config.yml");
|
|
20291
|
+
if (!fs7.existsSync(configPath)) {
|
|
19997
20292
|
return { status: "not_configured", configured: false, moduleContext: false, currentState: false, codebase: false, warning: `Run \`kaddo init\` inside \`${repoPath}\` and select multirepo \u2192 module.` };
|
|
19998
20293
|
}
|
|
19999
20294
|
let parsed = null;
|
|
20000
20295
|
try {
|
|
20001
|
-
parsed = parseYaml11(
|
|
20296
|
+
parsed = parseYaml11(fs7.readFileSync(configPath, "utf-8"));
|
|
20002
20297
|
} catch {
|
|
20003
20298
|
}
|
|
20004
20299
|
const role = parsed?.project?.role ?? parsed?.multirepo?.role;
|
|
@@ -20009,14 +20304,14 @@ function detectModuleStatus2(root, repoPath) {
|
|
|
20009
20304
|
status: "configured",
|
|
20010
20305
|
configured: true,
|
|
20011
20306
|
role: "module",
|
|
20012
|
-
moduleContext:
|
|
20013
|
-
currentState:
|
|
20014
|
-
codebase:
|
|
20307
|
+
moduleContext: fs7.existsSync(path6.join(moduleDir, "knowledge", "tech", "module", "module-context.md")) || fs7.existsSync(path6.join(moduleDir, "knowledge", "module", "module-context.md")),
|
|
20308
|
+
currentState: fs7.existsSync(path6.join(moduleDir, "knowledge", "tech", "current-state.md")),
|
|
20309
|
+
codebase: fs7.existsSync(path6.join(moduleDir, "knowledge", "tech", "codebase.md"))
|
|
20015
20310
|
};
|
|
20016
20311
|
}
|
|
20017
20312
|
function resolveModuleContextPath(root, repoPath) {
|
|
20018
|
-
const moduleDir =
|
|
20019
|
-
if (
|
|
20313
|
+
const moduleDir = path6.resolve(root, repoPath);
|
|
20314
|
+
if (fs7.existsSync(path6.join(moduleDir, "knowledge", "tech", "module", "module-context.md"))) {
|
|
20020
20315
|
return "knowledge/tech/module/module-context.md";
|
|
20021
20316
|
}
|
|
20022
20317
|
return "knowledge/module/module-context.md";
|
|
@@ -20113,7 +20408,7 @@ function validateWorkItemModulesTool(root, args) {
|
|
|
20113
20408
|
if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
|
|
20114
20409
|
const raw = readText(root, wi.path);
|
|
20115
20410
|
if (!raw) return fail2(`Could not read Work Item file.`);
|
|
20116
|
-
const parsed =
|
|
20411
|
+
const parsed = matter16(raw);
|
|
20117
20412
|
const data = parsed.data;
|
|
20118
20413
|
const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
|
|
20119
20414
|
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 +20461,7 @@ function getWorkItemContextTool(root, args) {
|
|
|
20166
20461
|
if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
|
|
20167
20462
|
const raw = readText(root, wi.path);
|
|
20168
20463
|
if (!raw) return fail2(`Could not read Work Item file.`);
|
|
20169
|
-
const parsed =
|
|
20464
|
+
const parsed = matter16(raw);
|
|
20170
20465
|
const data = parsed.data;
|
|
20171
20466
|
const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
|
|
20172
20467
|
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 +20515,15 @@ function extractSection(content, heading) {
|
|
|
20220
20515
|
return out;
|
|
20221
20516
|
}
|
|
20222
20517
|
function buildBranchStrategy(wi, affectedModules, codeEntries, descriptor) {
|
|
20223
|
-
const
|
|
20518
|
+
const slug6 = slugify4(wi.title);
|
|
20224
20519
|
const wiLower = wi.id.toLowerCase();
|
|
20225
|
-
const coreBranch = `feature/${wiLower}-${
|
|
20520
|
+
const coreBranch = `feature/${wiLower}-${slug6}`;
|
|
20226
20521
|
const moduleBranches = {};
|
|
20227
20522
|
const commitMessages = {};
|
|
20228
20523
|
for (const modId of affectedModules) {
|
|
20229
20524
|
const mod = (descriptor.modules ?? []).find((m2) => m2.id === modId);
|
|
20230
20525
|
const repo = mod ? moduleRepoPath(mod) || modId : modId;
|
|
20231
|
-
moduleBranches[repo] = `feature/${wiLower}-${modId}-${
|
|
20526
|
+
moduleBranches[repo] = `feature/${wiLower}-${modId}-${slug6.slice(0, 20)}`;
|
|
20232
20527
|
commitMessages[repo] = `fix(${wiLower}): update ${modId}`;
|
|
20233
20528
|
}
|
|
20234
20529
|
commitMessages["core"] = `docs(${wiLower}): capture cross-repo implementation summary`;
|
|
@@ -20252,7 +20547,7 @@ function suggestBranchStrategyTool(root, args) {
|
|
|
20252
20547
|
if (!wi) return fail2(`Work Item "${args.workItemId}" not found.`);
|
|
20253
20548
|
const raw = readText(root, wi.path);
|
|
20254
20549
|
if (!raw) return fail2(`Could not read Work Item file.`);
|
|
20255
|
-
const parsed =
|
|
20550
|
+
const parsed = matter16(raw);
|
|
20256
20551
|
const data = parsed.data;
|
|
20257
20552
|
const affectedModules = Array.isArray(data.affected_modules) ? data.affected_modules.map(String) : [];
|
|
20258
20553
|
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 +20696,31 @@ function modulesDiscoverTool(root, args) {
|
|
|
20401
20696
|
const sysName = config.system?.name ?? config.project?.name ?? "";
|
|
20402
20697
|
const workspaceRoots2 = config.multirepo?.workspace_roots ?? [".."];
|
|
20403
20698
|
const IGNORED = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", "coverage", "vendor", ".next", "out", "tmp", "temp", ".kaddo"]);
|
|
20404
|
-
const coreAbs =
|
|
20699
|
+
const coreAbs = path6.resolve(root);
|
|
20405
20700
|
const discovered = [];
|
|
20406
20701
|
for (const wr of workspaceRoots2) {
|
|
20407
|
-
const rootAbs =
|
|
20408
|
-
if (!
|
|
20702
|
+
const rootAbs = path6.resolve(root, wr);
|
|
20703
|
+
if (!fs7.existsSync(rootAbs) || !fs7.statSync(rootAbs).isDirectory()) continue;
|
|
20409
20704
|
let entries;
|
|
20410
20705
|
try {
|
|
20411
|
-
entries =
|
|
20706
|
+
entries = fs7.readdirSync(rootAbs);
|
|
20412
20707
|
} catch {
|
|
20413
20708
|
continue;
|
|
20414
20709
|
}
|
|
20415
20710
|
for (const entry of entries) {
|
|
20416
20711
|
if (IGNORED.has(entry)) continue;
|
|
20417
|
-
const candidateAbs =
|
|
20418
|
-
if (!
|
|
20712
|
+
const candidateAbs = path6.resolve(rootAbs, entry);
|
|
20713
|
+
if (!fs7.statSync(candidateAbs).isDirectory()) continue;
|
|
20419
20714
|
if (candidateAbs === coreAbs) continue;
|
|
20420
|
-
const relPath =
|
|
20421
|
-
const cfgPath =
|
|
20422
|
-
if (!
|
|
20715
|
+
const relPath = path6.relative(root, candidateAbs).replace(/\\/g, "/");
|
|
20716
|
+
const cfgPath = path6.join(candidateAbs, ".kaddo", "config.yml");
|
|
20717
|
+
if (!fs7.existsSync(cfgPath)) {
|
|
20423
20718
|
discovered.push({ id: entry, path: relPath, status: "not_configured", eligibleForMapping: false });
|
|
20424
20719
|
continue;
|
|
20425
20720
|
}
|
|
20426
20721
|
let parsed = null;
|
|
20427
20722
|
try {
|
|
20428
|
-
parsed = parseYaml11(
|
|
20723
|
+
parsed = parseYaml11(fs7.readFileSync(cfgPath, "utf-8"));
|
|
20429
20724
|
} catch {
|
|
20430
20725
|
}
|
|
20431
20726
|
const role = parsed?.project?.role ?? parsed?.multirepo?.role;
|
|
@@ -20499,8 +20794,8 @@ ${list3(data.owners)}
|
|
|
20499
20794
|
|
|
20500
20795
|
// ../cli/src/core/system-topology.ts
|
|
20501
20796
|
import { parse as parseYaml12, stringify as stringifyYaml8 } from "yaml";
|
|
20502
|
-
import
|
|
20503
|
-
import
|
|
20797
|
+
import fs8 from "fs";
|
|
20798
|
+
import path7 from "path";
|
|
20504
20799
|
import crypto from "crypto";
|
|
20505
20800
|
var TOPOLOGY_FILE2 = "knowledge/tech/system-topology.yml";
|
|
20506
20801
|
var SYSTEM_ENTITY_KINDS2 = /* @__PURE__ */ new Set([
|
|
@@ -20872,16 +21167,16 @@ function findSystemPaths(dir, source, target, opts = {}) {
|
|
|
20872
21167
|
out.get(r2.source).push(r2.target);
|
|
20873
21168
|
}
|
|
20874
21169
|
const paths = [];
|
|
20875
|
-
const walk = (node,
|
|
21170
|
+
const walk = (node, path9) => {
|
|
20876
21171
|
if (paths.length >= 20) return;
|
|
20877
|
-
if (node === target &&
|
|
20878
|
-
paths.push([...
|
|
21172
|
+
if (node === target && path9.length > 1) {
|
|
21173
|
+
paths.push([...path9]);
|
|
20879
21174
|
return;
|
|
20880
21175
|
}
|
|
20881
|
-
if (
|
|
21176
|
+
if (path9.length > maxDepth) return;
|
|
20882
21177
|
for (const nxt of out.get(node) ?? []) {
|
|
20883
|
-
if (
|
|
20884
|
-
walk(nxt, [...
|
|
21178
|
+
if (path9.includes(nxt)) continue;
|
|
21179
|
+
walk(nxt, [...path9, nxt]);
|
|
20885
21180
|
}
|
|
20886
21181
|
};
|
|
20887
21182
|
walk(source, [source]);
|
|
@@ -20906,14 +21201,14 @@ function getImpactCandidates(dir, seedIds, opts = {}) {
|
|
|
20906
21201
|
existing.reason.push(reason);
|
|
20907
21202
|
continue;
|
|
20908
21203
|
}
|
|
20909
|
-
const
|
|
21204
|
+
const path9 = findSystemPaths(dir, seed, n.id, { maxDepth: opts.maxDepth ?? DEFAULT_MAX_DEPTH })[0];
|
|
20910
21205
|
candidates.set(n.id, {
|
|
20911
21206
|
nodeId: n.id,
|
|
20912
21207
|
label: n.label,
|
|
20913
21208
|
kind: n.type,
|
|
20914
21209
|
...n.moduleId ? { moduleId: n.moduleId } : {},
|
|
20915
21210
|
reason: [reason],
|
|
20916
|
-
...
|
|
21211
|
+
...path9 ? { graphPath: path9.map((id) => byId.get(id)?.label ?? id) } : {},
|
|
20917
21212
|
...n.implementationRefs?.length ? { implementationRefs: n.implementationRefs } : {},
|
|
20918
21213
|
...n.knowledgeRefs?.length ? { knowledgeRefs: n.knowledgeRefs } : {},
|
|
20919
21214
|
status: "candidate",
|
|
@@ -21012,14 +21307,14 @@ function systemImpactCandidatesTool(root, args) {
|
|
|
21012
21307
|
}
|
|
21013
21308
|
|
|
21014
21309
|
// ../cli/src/services/integrations.ts
|
|
21015
|
-
import
|
|
21310
|
+
import matter18 from "gray-matter";
|
|
21016
21311
|
import { parse as parseYaml13, stringify as stringifyYaml9 } from "yaml";
|
|
21017
21312
|
|
|
21018
21313
|
// ../cli/src/core/work-item-write.ts
|
|
21019
|
-
import
|
|
21020
|
-
import
|
|
21314
|
+
import fs9 from "fs";
|
|
21315
|
+
import path8 from "path";
|
|
21021
21316
|
import crypto4 from "crypto";
|
|
21022
|
-
import
|
|
21317
|
+
import matter17 from "gray-matter";
|
|
21023
21318
|
var SECTION_ORDER2 = [
|
|
21024
21319
|
"Actor",
|
|
21025
21320
|
"Outcome",
|
|
@@ -21687,8 +21982,8 @@ async function handleJiraResponse2(res) {
|
|
|
21687
21982
|
}
|
|
21688
21983
|
return await res.json();
|
|
21689
21984
|
}
|
|
21690
|
-
async function jiraFetch2(baseUrl,
|
|
21691
|
-
const url = new URL(
|
|
21985
|
+
async function jiraFetch2(baseUrl, path9, auth, timeoutMs, params) {
|
|
21986
|
+
const url = new URL(path9, baseUrl + "/");
|
|
21692
21987
|
if (params) for (const [k2, v] of Object.entries(params)) url.searchParams.set(k2, v);
|
|
21693
21988
|
const controller = new AbortController();
|
|
21694
21989
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
@@ -21706,8 +22001,8 @@ async function jiraFetch2(baseUrl, path7, auth, timeoutMs, params) {
|
|
|
21706
22001
|
clearTimeout(timer);
|
|
21707
22002
|
}
|
|
21708
22003
|
}
|
|
21709
|
-
async function jiraPost2(baseUrl,
|
|
21710
|
-
const url = new URL(
|
|
22004
|
+
async function jiraPost2(baseUrl, path9, auth, timeoutMs, body) {
|
|
22005
|
+
const url = new URL(path9, baseUrl + "/");
|
|
21711
22006
|
const controller = new AbortController();
|
|
21712
22007
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
21713
22008
|
try {
|
|
@@ -22067,7 +22362,7 @@ async function integrationsWorkItemTool(root, args) {
|
|
|
22067
22362
|
}
|
|
22068
22363
|
|
|
22069
22364
|
// ../cli/src/core/context-pack.ts
|
|
22070
|
-
import
|
|
22365
|
+
import matter19 from "gray-matter";
|
|
22071
22366
|
var CONTEXT_PACK_VERSION = "1";
|
|
22072
22367
|
var ARCH_DIR4 = "knowledge";
|
|
22073
22368
|
function readScanJson(dir) {
|
|
@@ -22080,7 +22375,7 @@ function readScanJson(dir) {
|
|
|
22080
22375
|
}
|
|
22081
22376
|
}
|
|
22082
22377
|
function firstParagraph4(markdown) {
|
|
22083
|
-
const body =
|
|
22378
|
+
const body = matter19(markdown).content.trim();
|
|
22084
22379
|
const para = body.split("\n\n").map((p) => p.trim()).find((p) => p && !p.startsWith("#"));
|
|
22085
22380
|
return para ?? "";
|
|
22086
22381
|
}
|
|
@@ -23047,7 +23342,7 @@ function requireKnowledge(root) {
|
|
|
23047
23342
|
throw new KaddoMcpError("Knowledge repository not found. Run `kaddo bootstrap` first.");
|
|
23048
23343
|
}
|
|
23049
23344
|
}
|
|
23050
|
-
function
|
|
23345
|
+
function slug5(s) {
|
|
23051
23346
|
return s.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "project";
|
|
23052
23347
|
}
|
|
23053
23348
|
function generateContext(root) {
|
|
@@ -23180,7 +23475,7 @@ function generateCapsuleDraft(root) {
|
|
|
23180
23475
|
const config = requireConfig(root);
|
|
23181
23476
|
requireKnowledge(root);
|
|
23182
23477
|
const capsule = buildCapsule(root, config);
|
|
23183
|
-
const name =
|
|
23478
|
+
const name = slug5(config.project.name);
|
|
23184
23479
|
const md = `.kaddo/exports/${name}.capsule.md`;
|
|
23185
23480
|
const json = `.kaddo/exports/${name}.capsule.json`;
|
|
23186
23481
|
writeDerived(root, md, renderCapsuleMarkdown(capsule));
|
|
@@ -23478,6 +23773,53 @@ function createServer(root) {
|
|
|
23478
23773
|
{ 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
23774
|
async (args) => toolText(guarded(root, () => getResourceTool(root, args.id)))
|
|
23480
23775
|
);
|
|
23776
|
+
const resourceFields = {
|
|
23777
|
+
title: z4.string().optional(),
|
|
23778
|
+
resource_type: z4.string().optional(),
|
|
23779
|
+
provider: z4.string().optional(),
|
|
23780
|
+
environments: z4.array(z4.string()).optional(),
|
|
23781
|
+
scope: z4.string().optional(),
|
|
23782
|
+
module: z4.string().optional(),
|
|
23783
|
+
modules: z4.array(z4.string()).optional(),
|
|
23784
|
+
purpose: z4.string().optional(),
|
|
23785
|
+
access_interfaces: z4.array(z4.object({
|
|
23786
|
+
type: z4.string(),
|
|
23787
|
+
tool: z4.string().optional(),
|
|
23788
|
+
provider: z4.string().optional(),
|
|
23789
|
+
purpose: z4.string().optional(),
|
|
23790
|
+
operations: z4.array(z4.string()).optional(),
|
|
23791
|
+
environments: z4.array(z4.string()).optional(),
|
|
23792
|
+
constraints: z4.string().optional()
|
|
23793
|
+
})).optional(),
|
|
23794
|
+
authentication: z4.object({ mode: z4.string().optional(), refs: z4.array(z4.string()).optional() }).optional()
|
|
23795
|
+
};
|
|
23796
|
+
server.registerTool(
|
|
23797
|
+
"kaddo_create_resource",
|
|
23798
|
+
{
|
|
23799
|
+
title: "Create Project Resource",
|
|
23800
|
+
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.",
|
|
23801
|
+
inputSchema: { ...resourceFields, confirm: z4.boolean().optional() }
|
|
23802
|
+
},
|
|
23803
|
+
async (args) => toolText(guarded(root, () => createResourceTool(root, args, args.confirm)))
|
|
23804
|
+
);
|
|
23805
|
+
server.registerTool(
|
|
23806
|
+
"kaddo_update_resource",
|
|
23807
|
+
{
|
|
23808
|
+
title: "Update Project Resource",
|
|
23809
|
+
description: "Propose updating a Project Resource (no-lossy). Without confirm, returns a preview; with confirm=true, Core applies it. Never include credential values.",
|
|
23810
|
+
inputSchema: { id: z4.string(), ...resourceFields, confirm: z4.boolean().optional() }
|
|
23811
|
+
},
|
|
23812
|
+
async (args) => toolText(guarded(root, () => updateResourceTool(root, args.id, args, args.confirm)))
|
|
23813
|
+
);
|
|
23814
|
+
server.registerTool(
|
|
23815
|
+
"kaddo_delete_resource",
|
|
23816
|
+
{
|
|
23817
|
+
title: "Delete Project Resource",
|
|
23818
|
+
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.",
|
|
23819
|
+
inputSchema: { id: z4.string(), confirm: z4.boolean().optional() }
|
|
23820
|
+
},
|
|
23821
|
+
async (args) => toolText(guarded(root, () => deleteResourceTool(root, args.id, args.confirm)))
|
|
23822
|
+
);
|
|
23481
23823
|
server.registerTool(
|
|
23482
23824
|
"kaddo_list_capsules",
|
|
23483
23825
|
{ title: "List Knowledge Capsules", description: "List registered external Knowledge Capsules.", inputSchema: {} },
|
package/package.json
CHANGED