@kaddo/mcp 3.62.0 → 3.63.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +173 -44
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -168,7 +168,8 @@ var DERIVED_WRITE_FILES = /* @__PURE__ */ new Set([
|
|
|
168
168
|
".kaddo/graph.json",
|
|
169
169
|
".kaddo/graph.mmd",
|
|
170
170
|
".kaddo/graph-hints.md",
|
|
171
|
-
".kaddo/graph-hints.json"
|
|
171
|
+
".kaddo/graph-hints.json",
|
|
172
|
+
".kaddo/modules.yml"
|
|
172
173
|
]);
|
|
173
174
|
var DERIVED_EXPORTS_RE = /^\.kaddo\/exports\/[^/]+\.capsule\.(md|json)$/;
|
|
174
175
|
var DERIVED_REPORTS_RE = /^\.kaddo\/reports\/[^/]+\.(md|json)$/;
|
|
@@ -447,10 +448,14 @@ var configSchema = z.object({
|
|
|
447
448
|
team: z.object({
|
|
448
449
|
size: teamSizeSchema.default(DEFAULTS.teamSize)
|
|
449
450
|
}).passthrough().default({}),
|
|
451
|
+
system: z.object({
|
|
452
|
+
name: z.string().optional()
|
|
453
|
+
}).passthrough().optional(),
|
|
450
454
|
multirepo: z.object({
|
|
451
455
|
role: multirepoRoleSchema,
|
|
452
456
|
modules_file: z.string().optional(),
|
|
453
|
-
parent_system: z.string().optional()
|
|
457
|
+
parent_system: z.string().optional(),
|
|
458
|
+
workspace_roots: z.array(z.string()).optional()
|
|
454
459
|
}).passthrough().optional(),
|
|
455
460
|
module: z.object({
|
|
456
461
|
id: z.string().optional()
|
|
@@ -489,6 +494,9 @@ function projectRole(config) {
|
|
|
489
494
|
function isModule(config) {
|
|
490
495
|
return projectRole(config) === "module";
|
|
491
496
|
}
|
|
497
|
+
function isCore(config) {
|
|
498
|
+
return projectRole(config) === "core";
|
|
499
|
+
}
|
|
492
500
|
|
|
493
501
|
// ../cli/src/services/artifact-reader.ts
|
|
494
502
|
import matter3 from "gray-matter";
|
|
@@ -790,7 +798,7 @@ function assessPhase(input) {
|
|
|
790
798
|
|
|
791
799
|
// ../cli/src/core/capsule.ts
|
|
792
800
|
import matter4 from "gray-matter";
|
|
793
|
-
import { parse as
|
|
801
|
+
import { parse as parseYaml7, stringify as stringifyYaml3 } from "yaml";
|
|
794
802
|
|
|
795
803
|
// ../cli/src/services/owners.ts
|
|
796
804
|
import { parse as parseYaml3 } from "yaml";
|
|
@@ -847,7 +855,7 @@ function loadMappedModules(dir) {
|
|
|
847
855
|
modules.push({
|
|
848
856
|
id,
|
|
849
857
|
name: typeof m2.name === "string" ? m2.name : void 0,
|
|
850
|
-
repoPath: typeof m2.repoPath === "string" ? m2.repoPath : "",
|
|
858
|
+
repoPath: typeof m2.repoPath === "string" ? m2.repoPath : typeof m2.path === "string" ? m2.path : "",
|
|
851
859
|
type: typeof m2.type === "string" ? m2.type : void 0,
|
|
852
860
|
mainTechnology: typeof m2.mainTechnology === "string" ? m2.mainTechnology : void 0,
|
|
853
861
|
owner: typeof m2.owner === "string" ? m2.owner : void 0,
|
|
@@ -896,13 +904,15 @@ function detectModuleStatus(coreDir, repoPath) {
|
|
|
896
904
|
status: "configured",
|
|
897
905
|
configured: true,
|
|
898
906
|
role: "module",
|
|
899
|
-
moduleContext: exists(join(moduleDir, "knowledge", "module", "module-context.md")),
|
|
907
|
+
moduleContext: exists(join(moduleDir, "knowledge", "tech", "module", "module-context.md")) || exists(join(moduleDir, "knowledge", "module", "module-context.md")),
|
|
900
908
|
currentState: exists(join(moduleDir, "knowledge", "tech", "current-state.md")),
|
|
901
909
|
codebase: exists(join(moduleDir, "knowledge", "tech", "codebase.md"))
|
|
902
910
|
};
|
|
903
911
|
}
|
|
904
912
|
function readModuleContext(coreDir, repoPath) {
|
|
905
|
-
const
|
|
913
|
+
const newPath = join(coreDir, repoPath, "knowledge", "tech", "module", "module-context.md");
|
|
914
|
+
const legacyPath = join(coreDir, repoPath, "knowledge", "module", "module-context.md");
|
|
915
|
+
const p = exists(newPath) ? newPath : legacyPath;
|
|
906
916
|
if (!exists(p)) return null;
|
|
907
917
|
try {
|
|
908
918
|
return readFile(p);
|
|
@@ -1042,7 +1052,7 @@ var Q2 = o("\u25B2", "!");
|
|
|
1042
1052
|
var ee = o("\u25A0", "x");
|
|
1043
1053
|
|
|
1044
1054
|
// ../cli/src/commands/modules-map.ts
|
|
1045
|
-
import { parse as
|
|
1055
|
+
import { parse as parseYaml6, stringify as stringifyYaml2 } from "yaml";
|
|
1046
1056
|
|
|
1047
1057
|
// ../cli/src/templates/registry.ts
|
|
1048
1058
|
var QUALITY = "## Quality checklist";
|
|
@@ -2153,6 +2163,9 @@ ${QUALITY}
|
|
|
2153
2163
|
- [ ] The next step is clear.
|
|
2154
2164
|
`;
|
|
2155
2165
|
|
|
2166
|
+
// ../cli/src/commands/modules-discover.ts
|
|
2167
|
+
import { parse as parseYaml5, stringify as stringifyYaml } from "yaml";
|
|
2168
|
+
|
|
2156
2169
|
// ../cli/src/core/capsule.ts
|
|
2157
2170
|
var KNOWLEDGE2 = "knowledge";
|
|
2158
2171
|
function today() {
|
|
@@ -2252,7 +2265,7 @@ function loadExternalRegistry(dir) {
|
|
|
2252
2265
|
const p = join(dir, EXTERNAL_PATH);
|
|
2253
2266
|
if (!exists(p)) return [];
|
|
2254
2267
|
try {
|
|
2255
|
-
const parsed =
|
|
2268
|
+
const parsed = parseYaml7(readFile(p));
|
|
2256
2269
|
return Array.isArray(parsed?.external) ? parsed.external : [];
|
|
2257
2270
|
} catch {
|
|
2258
2271
|
return [];
|
|
@@ -3693,6 +3706,9 @@ function buildAdapterStatuses(dir) {
|
|
|
3693
3706
|
});
|
|
3694
3707
|
}
|
|
3695
3708
|
|
|
3709
|
+
// ../cli/src/core/next-step.ts
|
|
3710
|
+
import { parse as parseYaml8 } from "yaml";
|
|
3711
|
+
|
|
3696
3712
|
// ../cli/src/agents/groups.ts
|
|
3697
3713
|
var AGENT_GROUPS = {
|
|
3698
3714
|
business: ["business-agent.md"],
|
|
@@ -3845,6 +3861,14 @@ function buildDeliveryState(dir) {
|
|
|
3845
3861
|
adapters_installed: adapters.length
|
|
3846
3862
|
};
|
|
3847
3863
|
}
|
|
3864
|
+
function hasRegisteredModules(modulesYmlPath) {
|
|
3865
|
+
try {
|
|
3866
|
+
const parsed = parseYaml8(readFile(modulesYmlPath));
|
|
3867
|
+
return Array.isArray(parsed?.modules) && parsed.modules.length > 0;
|
|
3868
|
+
} catch {
|
|
3869
|
+
return false;
|
|
3870
|
+
}
|
|
3871
|
+
}
|
|
3848
3872
|
function safeRead(p) {
|
|
3849
3873
|
try {
|
|
3850
3874
|
return readFile(p);
|
|
@@ -3895,11 +3919,12 @@ function resolveNextStep(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
3895
3919
|
const state = config.project.state;
|
|
3896
3920
|
const q3 = (rel) => analyzeKnowledgeArtifact(dir, rel);
|
|
3897
3921
|
const moduleRepo = isModule(config);
|
|
3898
|
-
const
|
|
3922
|
+
const coreRepo = isCore(config);
|
|
3923
|
+
const MC = exists(join(dir, "knowledge/tech/module/module-context.md")) ? "knowledge/tech/module/module-context.md" : "knowledge/module/module-context.md";
|
|
3899
3924
|
if (moduleRepo) {
|
|
3900
3925
|
const qMC = q3(MC);
|
|
3901
3926
|
if (qMC === "missing") {
|
|
3902
|
-
return { id: "init-module-context", phase: "Setup", label: "Create `knowledge/module/module-context.md` for this module.", command: "kaddo init", reason: "Module context file is missing. Re-run `kaddo init` as a multirepo module." };
|
|
3927
|
+
return { id: "init-module-context", phase: "Setup", label: "Create `knowledge/tech/module/module-context.md` for this module.", command: "kaddo init", reason: "Module context file is missing. Re-run `kaddo init` as a multirepo module." };
|
|
3903
3928
|
}
|
|
3904
3929
|
if ((state === "pre-ai" || state === "legacy") && !exists(join(dir, ".kaddo", "scan.json"))) {
|
|
3905
3930
|
return { id: "scan", phase: "Discovery", label: "Run `kaddo scan` to capture deterministic signals from the existing code.", command: "kaddo scan", reason: "No scan baseline exists for this existing project yet." };
|
|
@@ -3948,9 +3973,15 @@ function resolveNextStep(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
3948
3973
|
return { id: "module-ready", phase: "Active Delivery", label: "Module context is complete. Create and manage Work Items from the core repository.", reason: "This module's knowledge is ready. Work Items are created and tracked in the core." };
|
|
3949
3974
|
}
|
|
3950
3975
|
const qBusiness = q3(B2), qProduct = q3(P2), qCap = q3(CAP), qCurrentState = q3(CS), qCodebase = q3(CB);
|
|
3951
|
-
if (qBusiness === "missing" || qProduct === "missing") {
|
|
3976
|
+
if (!coreRepo && (qBusiness === "missing" || qProduct === "missing")) {
|
|
3952
3977
|
return { id: "bootstrap", phase: "Setup", label: "Run `kaddo bootstrap` to create the project knowledge baseline.", command: "kaddo bootstrap", reason: "The knowledge baseline is incomplete." };
|
|
3953
3978
|
}
|
|
3979
|
+
if (coreRepo) {
|
|
3980
|
+
const modulesYmlPath = join(dir, ".kaddo", "modules.yml");
|
|
3981
|
+
if (!exists(modulesYmlPath) || !hasRegisteredModules(modulesYmlPath)) {
|
|
3982
|
+
return { id: "modules-discover", phase: "Setup", label: "Run `kaddo modules discover` to find sibling module repositories.", command: "kaddo modules discover", reason: "No modules are mapped for this core repository." };
|
|
3983
|
+
}
|
|
3984
|
+
}
|
|
3954
3985
|
const ctx = buildCodexAdapterContext(dir);
|
|
3955
3986
|
if (!ctx.hasAgents) {
|
|
3956
3987
|
return { id: "add-agents", phase: "Setup", label: "Run `kaddo add agents` to install the Kaddo agent prompt packs.", command: "kaddo add agents", reason: "No Kaddo agents are installed yet." };
|
|
@@ -7714,7 +7745,8 @@ function mapNextStepId(id) {
|
|
|
7714
7745
|
"refine-module-context": "refine-work-item",
|
|
7715
7746
|
"init-module-context": "enable-kaddo",
|
|
7716
7747
|
"module-ready": "prepare-implementation",
|
|
7717
|
-
"validate-work-item-modules": "prepare-implementation"
|
|
7748
|
+
"validate-work-item-modules": "prepare-implementation",
|
|
7749
|
+
"modules-discover": "enable-kaddo"
|
|
7718
7750
|
};
|
|
7719
7751
|
return MAP[id] ?? id;
|
|
7720
7752
|
}
|
|
@@ -8981,7 +9013,7 @@ function serializeImpactJson(r2) {
|
|
|
8981
9013
|
}
|
|
8982
9014
|
|
|
8983
9015
|
// ../cli/src/core/savings.ts
|
|
8984
|
-
import { parse as
|
|
9016
|
+
import { parse as parseYaml9 } from "yaml";
|
|
8985
9017
|
var DEFAULT_ASSUMPTIONS = {
|
|
8986
9018
|
currency: "USD",
|
|
8987
9019
|
hourly_cost: 40,
|
|
@@ -8999,7 +9031,7 @@ function loadAssumptions(dir) {
|
|
|
8999
9031
|
const p = join(dir, SAVINGS_PATH);
|
|
9000
9032
|
if (!exists(p)) return { assumptions: { ...DEFAULT_ASSUMPTIONS }, source: "default" };
|
|
9001
9033
|
try {
|
|
9002
|
-
const raw =
|
|
9034
|
+
const raw = parseYaml9(readFile(p));
|
|
9003
9035
|
const a2 = raw?.assumptions ?? {};
|
|
9004
9036
|
const t = raw?.team ?? {};
|
|
9005
9037
|
const num = (v, d3) => typeof v === "number" && Number.isFinite(v) ? v : d3;
|
|
@@ -9837,7 +9869,7 @@ function getWorkItem(root, id) {
|
|
|
9837
9869
|
const content = readText(root, item.path);
|
|
9838
9870
|
return ok({ ...item, content: content ?? "" });
|
|
9839
9871
|
}
|
|
9840
|
-
function markWorkItemReady(root, id,
|
|
9872
|
+
function markWorkItemReady(root, id, confirm2) {
|
|
9841
9873
|
const item = listWorkItems(root).find((w3) => w3.id === id);
|
|
9842
9874
|
if (!item) return fail(`Work Item "${id}" not found.`);
|
|
9843
9875
|
if (item.status === "ready") {
|
|
@@ -9847,7 +9879,7 @@ function markWorkItemReady(root, id, confirm) {
|
|
|
9847
9879
|
return fail(`Work Item ${item.id} is in state "${item.status}", not "draft". Only draft Work Items can be marked as ready.`);
|
|
9848
9880
|
}
|
|
9849
9881
|
const warnings = item.readiness?.warnings ?? [];
|
|
9850
|
-
if (!
|
|
9882
|
+
if (!confirm2) {
|
|
9851
9883
|
return ok({
|
|
9852
9884
|
status: "needs_confirmation",
|
|
9853
9885
|
workItem: {
|
|
@@ -9937,9 +9969,12 @@ function listGraphHints(root, filter = {}) {
|
|
|
9937
9969
|
import fs3 from "fs";
|
|
9938
9970
|
import path3 from "path";
|
|
9939
9971
|
import matter11 from "gray-matter";
|
|
9940
|
-
import { parse as
|
|
9972
|
+
import { parse as parseYaml10, stringify as stringifyYaml4 } from "yaml";
|
|
9941
9973
|
var ok2 = (data) => ({ ok: true, data });
|
|
9942
9974
|
var fail2 = (message) => ({ ok: false, message });
|
|
9975
|
+
function moduleRepoPath(m2) {
|
|
9976
|
+
return m2.path ?? m2.repoPath ?? "";
|
|
9977
|
+
}
|
|
9943
9978
|
function loadModulesDescriptor(root) {
|
|
9944
9979
|
return readYaml(root, ".kaddo/modules.yml") ?? { modules: [] };
|
|
9945
9980
|
}
|
|
@@ -9949,7 +9984,7 @@ function loadConfig2(root) {
|
|
|
9949
9984
|
function projectRole2(config) {
|
|
9950
9985
|
return config.project?.role ?? config.multirepo?.role;
|
|
9951
9986
|
}
|
|
9952
|
-
function
|
|
9987
|
+
function isCore2(config) {
|
|
9953
9988
|
return projectRole2(config) === "core";
|
|
9954
9989
|
}
|
|
9955
9990
|
function readModuleFile(root, repoPath, relFile) {
|
|
@@ -9970,7 +10005,7 @@ function detectModuleStatus2(root, repoPath) {
|
|
|
9970
10005
|
}
|
|
9971
10006
|
let parsed = null;
|
|
9972
10007
|
try {
|
|
9973
|
-
parsed =
|
|
10008
|
+
parsed = parseYaml10(fs3.readFileSync(configPath, "utf-8"));
|
|
9974
10009
|
} catch {
|
|
9975
10010
|
}
|
|
9976
10011
|
const role = parsed?.project?.role ?? parsed?.multirepo?.role;
|
|
@@ -9981,11 +10016,18 @@ function detectModuleStatus2(root, repoPath) {
|
|
|
9981
10016
|
status: "configured",
|
|
9982
10017
|
configured: true,
|
|
9983
10018
|
role: "module",
|
|
9984
|
-
moduleContext: fs3.existsSync(path3.join(moduleDir, "knowledge", "module", "module-context.md")),
|
|
10019
|
+
moduleContext: fs3.existsSync(path3.join(moduleDir, "knowledge", "tech", "module", "module-context.md")) || fs3.existsSync(path3.join(moduleDir, "knowledge", "module", "module-context.md")),
|
|
9985
10020
|
currentState: fs3.existsSync(path3.join(moduleDir, "knowledge", "tech", "current-state.md")),
|
|
9986
10021
|
codebase: fs3.existsSync(path3.join(moduleDir, "knowledge", "tech", "codebase.md"))
|
|
9987
10022
|
};
|
|
9988
10023
|
}
|
|
10024
|
+
function resolveModuleContextPath(root, repoPath) {
|
|
10025
|
+
const moduleDir = path3.resolve(root, repoPath);
|
|
10026
|
+
if (fs3.existsSync(path3.join(moduleDir, "knowledge", "tech", "module", "module-context.md"))) {
|
|
10027
|
+
return "knowledge/tech/module/module-context.md";
|
|
10028
|
+
}
|
|
10029
|
+
return "knowledge/module/module-context.md";
|
|
10030
|
+
}
|
|
9989
10031
|
function isPlaceholder(content) {
|
|
9990
10032
|
const body = content.replace(/^---[\s\S]*?---\n?/, "");
|
|
9991
10033
|
return /^_[^_]+_$/m.test(body) && !body.split("\n").some((l2) => l2.trim().length > 0 && !l2.startsWith("#") && !l2.startsWith("_") && !l2.startsWith("-") && !l2.startsWith(">"));
|
|
@@ -10004,13 +10046,14 @@ function slugify2(s) {
|
|
|
10004
10046
|
function modulesListTool(root, args) {
|
|
10005
10047
|
const config = loadConfig2(root);
|
|
10006
10048
|
if (!config) return fail2("No Kaddo config found. Run `kaddo init` first.");
|
|
10007
|
-
if (!
|
|
10049
|
+
if (!isCore2(config)) return fail2("kaddo_modules_list can only be called from a core repository (project.role: core).");
|
|
10008
10050
|
const descriptor = loadModulesDescriptor(root);
|
|
10009
10051
|
const modules = (descriptor.modules ?? []).map((m2) => {
|
|
10010
|
-
const
|
|
10052
|
+
const rp = moduleRepoPath(m2);
|
|
10053
|
+
const st = detectModuleStatus2(root, rp);
|
|
10011
10054
|
const entry = {
|
|
10012
10055
|
id: m2.id,
|
|
10013
|
-
repo:
|
|
10056
|
+
repo: rp,
|
|
10014
10057
|
status: st.status,
|
|
10015
10058
|
kaddo: {
|
|
10016
10059
|
configured: st.configured,
|
|
@@ -10034,7 +10077,8 @@ function getModuleContextTool(root, args) {
|
|
|
10034
10077
|
const descriptor = loadModulesDescriptor(root);
|
|
10035
10078
|
const mod = (descriptor.modules ?? []).find((m2) => m2.id === args.module);
|
|
10036
10079
|
if (!mod) return fail2(`Module "${args.module}" not found in .kaddo/modules.yml.`);
|
|
10037
|
-
const
|
|
10080
|
+
const rp = moduleRepoPath(mod);
|
|
10081
|
+
const st = detectModuleStatus2(root, rp);
|
|
10038
10082
|
const warnings = [];
|
|
10039
10083
|
if (st.status !== "configured") {
|
|
10040
10084
|
if (st.warning) warnings.push(st.warning);
|
|
@@ -10045,26 +10089,27 @@ function getModuleContextTool(root, args) {
|
|
|
10045
10089
|
recommendedNextStep: { label: "Initialize module repository with Kaddo.", command: "kaddo init" }
|
|
10046
10090
|
});
|
|
10047
10091
|
}
|
|
10048
|
-
const
|
|
10092
|
+
const mcRelPath = resolveModuleContextPath(root, rp);
|
|
10093
|
+
const mcContent = readModuleFile(root, rp, mcRelPath);
|
|
10049
10094
|
const context = {
|
|
10050
|
-
moduleContextPath: `${
|
|
10095
|
+
moduleContextPath: `${rp}/${mcRelPath}`,
|
|
10051
10096
|
moduleContext: mcContent ?? null
|
|
10052
10097
|
};
|
|
10053
10098
|
if (mcContent && isPlaceholder(mcContent)) {
|
|
10054
10099
|
warnings.push("module-context.md still contains placeholder sections.");
|
|
10055
10100
|
}
|
|
10056
10101
|
if (args.includeTech) {
|
|
10057
|
-
const cs = readModuleFile(root,
|
|
10058
|
-
const cb = readModuleFile(root,
|
|
10059
|
-
context.currentStatePath = `${
|
|
10102
|
+
const cs = readModuleFile(root, rp, "knowledge/tech/current-state.md");
|
|
10103
|
+
const cb = readModuleFile(root, rp, "knowledge/tech/codebase.md");
|
|
10104
|
+
context.currentStatePath = `${rp}/knowledge/tech/current-state.md`;
|
|
10060
10105
|
context.currentStateSummary = cs ? firstParagraph3(cs) : null;
|
|
10061
|
-
context.codebasePath = `${
|
|
10106
|
+
context.codebasePath = `${rp}/knowledge/tech/codebase.md`;
|
|
10062
10107
|
context.codebaseSummary = cb ? firstParagraph3(cb) : null;
|
|
10063
10108
|
if (!cs) warnings.push("current-state.md not found in module repository.");
|
|
10064
10109
|
if (!cb) warnings.push("codebase.md not found in module repository.");
|
|
10065
10110
|
}
|
|
10066
10111
|
return ok2({
|
|
10067
|
-
module: { id: mod.id, repo:
|
|
10112
|
+
module: { id: mod.id, repo: rp, status: "configured" },
|
|
10068
10113
|
context,
|
|
10069
10114
|
warnings: args.includeWarnings ? warnings : []
|
|
10070
10115
|
});
|
|
@@ -10092,15 +10137,17 @@ function validateWorkItemModulesTool(root, args) {
|
|
|
10092
10137
|
return { id: modId, status: "not_mapped", hasModuleContext: false, hasCurrentState: false, hasCodebase: false, hasOwnership: false };
|
|
10093
10138
|
}
|
|
10094
10139
|
const mod = (descriptor.modules ?? []).find((m2) => m2.id === modId);
|
|
10095
|
-
const
|
|
10140
|
+
const rp = moduleRepoPath(mod);
|
|
10141
|
+
const st = detectModuleStatus2(root, rp);
|
|
10096
10142
|
if (st.status !== "configured") {
|
|
10097
10143
|
warnings.push(`Module "${modId}" is ${st.status}.`);
|
|
10098
10144
|
}
|
|
10099
|
-
const
|
|
10145
|
+
const mcRelPath = resolveModuleContextPath(root, rp);
|
|
10146
|
+
const mcContent = st.moduleContext ? readModuleFile(root, rp, mcRelPath) : null;
|
|
10100
10147
|
if (mcContent && isPlaceholder(mcContent)) {
|
|
10101
10148
|
warnings.push(`Module "${modId}" has placeholder module-context.`);
|
|
10102
10149
|
}
|
|
10103
|
-
const hasOwnership = codeEntries.some((c) => c.repo && (c.repo ===
|
|
10150
|
+
const hasOwnership = codeEntries.some((c) => c.repo && (c.repo === rp || c.repo.endsWith(`/${modId}`) || c.repo.includes(modId)));
|
|
10104
10151
|
return { id: modId, status: st.status, hasModuleContext: st.moduleContext, hasCurrentState: st.currentState, hasCodebase: st.codebase, hasOwnership };
|
|
10105
10152
|
});
|
|
10106
10153
|
for (const ce of codeEntries) {
|
|
@@ -10138,12 +10185,14 @@ function getWorkItemContextTool(root, args) {
|
|
|
10138
10185
|
warnings.push(`Module "${modId}" not in modules.yml.`);
|
|
10139
10186
|
return { id: modId, repo: null, moduleContext: null, currentStateSummary: null, codebaseSummary: null };
|
|
10140
10187
|
}
|
|
10141
|
-
const
|
|
10188
|
+
const rp = moduleRepoPath(mod);
|
|
10189
|
+
const st = detectModuleStatus2(root, rp);
|
|
10142
10190
|
if (st.status !== "configured") warnings.push(`Module "${modId}" is ${st.status}.`);
|
|
10143
|
-
const
|
|
10144
|
-
const
|
|
10145
|
-
const
|
|
10146
|
-
|
|
10191
|
+
const mcRelPath = resolveModuleContextPath(root, rp);
|
|
10192
|
+
const mc = args.includeModuleContexts !== false ? readModuleFile(root, rp, mcRelPath) : null;
|
|
10193
|
+
const cs = readModuleFile(root, rp, "knowledge/tech/current-state.md");
|
|
10194
|
+
const cb = readModuleFile(root, rp, "knowledge/tech/codebase.md");
|
|
10195
|
+
return { id: modId, repo: rp, moduleContext: mc, currentStateSummary: cs ? firstParagraph3(cs) : null, codebaseSummary: cb ? firstParagraph3(cb) : null };
|
|
10147
10196
|
});
|
|
10148
10197
|
const ownership = { crossRepo: codeEntries.length > 1, items: codeEntries.filter((c) => c.repo).map((c) => ({ repo: c.repo, paths: c.paths ?? [] })) };
|
|
10149
10198
|
const result = {
|
|
@@ -10185,7 +10234,7 @@ function buildBranchStrategy(wi, affectedModules, codeEntries, descriptor) {
|
|
|
10185
10234
|
const commitMessages = {};
|
|
10186
10235
|
for (const modId of affectedModules) {
|
|
10187
10236
|
const mod = (descriptor.modules ?? []).find((m2) => m2.id === modId);
|
|
10188
|
-
const repo = mod
|
|
10237
|
+
const repo = mod ? moduleRepoPath(mod) || modId : modId;
|
|
10189
10238
|
moduleBranches[repo] = `feature/${wiLower}-${modId}-${slug4.slice(0, 20)}`;
|
|
10190
10239
|
commitMessages[repo] = `fix(${wiLower}): update ${modId}`;
|
|
10191
10240
|
}
|
|
@@ -10235,7 +10284,9 @@ function exportCapsuleTool(root, args) {
|
|
|
10235
10284
|
const descriptor = loadModulesDescriptor(root);
|
|
10236
10285
|
const mod = (descriptor.modules ?? []).find((m2) => m2.id === args.module);
|
|
10237
10286
|
if (!mod) return fail2(`Module "${args.module}" not found in .kaddo/modules.yml.`);
|
|
10238
|
-
const
|
|
10287
|
+
const rp = moduleRepoPath(mod);
|
|
10288
|
+
const mcRelPath = resolveModuleContextPath(root, rp);
|
|
10289
|
+
const mc = readModuleFile(root, rp, mcRelPath);
|
|
10239
10290
|
if (!mc) warnings.push("module-context.md not found for this module.");
|
|
10240
10291
|
if (mc && isPlaceholder(mc)) warnings.push("module-context.md contains placeholder sections.");
|
|
10241
10292
|
capsuleType = "module";
|
|
@@ -10256,7 +10307,7 @@ function exportCapsuleTool(root, args) {
|
|
|
10256
10307
|
owners: mod.owner ? [mod.owner] : []
|
|
10257
10308
|
};
|
|
10258
10309
|
} else if (role === "module") {
|
|
10259
|
-
const mc = readText(root, "knowledge/module/module-context.md");
|
|
10310
|
+
const mc = readText(root, "knowledge/tech/module/module-context.md") ?? readText(root, "knowledge/module/module-context.md");
|
|
10260
10311
|
if (!mc) warnings.push("module-context.md not found.");
|
|
10261
10312
|
if (mc && isPlaceholder(mc)) warnings.push("module-context.md contains placeholder sections.");
|
|
10262
10313
|
const modId = config.module?.id ?? args.module;
|
|
@@ -10283,7 +10334,9 @@ function exportCapsuleTool(root, args) {
|
|
|
10283
10334
|
const descriptor = loadModulesDescriptor(root);
|
|
10284
10335
|
const responsibilities = [];
|
|
10285
10336
|
for (const m2 of descriptor.modules ?? []) {
|
|
10286
|
-
const
|
|
10337
|
+
const rp = moduleRepoPath(m2);
|
|
10338
|
+
const mcRelPath = resolveModuleContextPath(root, rp);
|
|
10339
|
+
const mc = readModuleFile(root, rp, mcRelPath);
|
|
10287
10340
|
responsibilities.push(mc ? `[${m2.id}] ${firstParagraph3(mc) || m2.name || m2.id}` : `[${m2.id}] ${m2.name || m2.id} (no module-context)`);
|
|
10288
10341
|
}
|
|
10289
10342
|
capsuleType = "system";
|
|
@@ -10303,7 +10356,7 @@ function exportCapsuleTool(root, args) {
|
|
|
10303
10356
|
};
|
|
10304
10357
|
} else {
|
|
10305
10358
|
if (role === "module") {
|
|
10306
|
-
const mc = readText(root, "knowledge/module/module-context.md");
|
|
10359
|
+
const mc = readText(root, "knowledge/tech/module/module-context.md") ?? readText(root, "knowledge/module/module-context.md");
|
|
10307
10360
|
if (!mc) warnings.push("module-context.md not found.");
|
|
10308
10361
|
if (mc && isPlaceholder(mc)) warnings.push("module-context.md contains placeholder sections.");
|
|
10309
10362
|
const modId = config.module?.id ?? "module";
|
|
@@ -10348,6 +10401,70 @@ function exportCapsuleTool(root, args) {
|
|
|
10348
10401
|
writeDerived(root, mdPath, mdContent);
|
|
10349
10402
|
return ok2({ exported: true, type: capsuleType, module: args.module ?? void 0, paths: { json: jsonPath, md: mdPath }, warnings });
|
|
10350
10403
|
}
|
|
10404
|
+
function modulesDiscoverTool(root, args) {
|
|
10405
|
+
const config = loadConfig2(root);
|
|
10406
|
+
if (!config) return fail2("No Kaddo config found.");
|
|
10407
|
+
if (!isCore2(config)) return fail2("Module discovery is only available from a core repository.");
|
|
10408
|
+
const sysName = config.system?.name ?? config.project?.name ?? "";
|
|
10409
|
+
const workspaceRoots2 = config.multirepo?.workspace_roots ?? [".."];
|
|
10410
|
+
const IGNORED = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", "coverage", "vendor", ".next", "out", "tmp", "temp", ".kaddo"]);
|
|
10411
|
+
const coreAbs = path3.resolve(root);
|
|
10412
|
+
const discovered = [];
|
|
10413
|
+
for (const wr of workspaceRoots2) {
|
|
10414
|
+
const rootAbs = path3.resolve(root, wr);
|
|
10415
|
+
if (!fs3.existsSync(rootAbs) || !fs3.statSync(rootAbs).isDirectory()) continue;
|
|
10416
|
+
let entries;
|
|
10417
|
+
try {
|
|
10418
|
+
entries = fs3.readdirSync(rootAbs);
|
|
10419
|
+
} catch {
|
|
10420
|
+
continue;
|
|
10421
|
+
}
|
|
10422
|
+
for (const entry of entries) {
|
|
10423
|
+
if (IGNORED.has(entry)) continue;
|
|
10424
|
+
const candidateAbs = path3.resolve(rootAbs, entry);
|
|
10425
|
+
if (!fs3.statSync(candidateAbs).isDirectory()) continue;
|
|
10426
|
+
if (candidateAbs === coreAbs) continue;
|
|
10427
|
+
const relPath = path3.relative(root, candidateAbs).replace(/\\/g, "/");
|
|
10428
|
+
const cfgPath = path3.join(candidateAbs, ".kaddo", "config.yml");
|
|
10429
|
+
if (!fs3.existsSync(cfgPath)) {
|
|
10430
|
+
discovered.push({ id: entry, path: relPath, status: "not_configured", eligibleForMapping: false });
|
|
10431
|
+
continue;
|
|
10432
|
+
}
|
|
10433
|
+
let parsed = null;
|
|
10434
|
+
try {
|
|
10435
|
+
parsed = parseYaml10(fs3.readFileSync(cfgPath, "utf-8"));
|
|
10436
|
+
} catch {
|
|
10437
|
+
}
|
|
10438
|
+
const role = parsed?.project?.role ?? parsed?.multirepo?.role;
|
|
10439
|
+
if (role !== "module") {
|
|
10440
|
+
discovered.push({ id: entry, path: relPath, status: "invalid", eligibleForMapping: false, warning: "Not configured as module." });
|
|
10441
|
+
continue;
|
|
10442
|
+
}
|
|
10443
|
+
const modParent = parsed?.multirepo?.parent_system ?? "";
|
|
10444
|
+
if (modParent && modParent !== sysName) {
|
|
10445
|
+
discovered.push({ id: parsed?.module?.id ?? entry, path: relPath, status: "foreign_system", eligibleForMapping: false, parentSystem: modParent });
|
|
10446
|
+
continue;
|
|
10447
|
+
}
|
|
10448
|
+
discovered.push({ id: parsed?.module?.id ?? entry, path: relPath, status: "configured", eligibleForMapping: true });
|
|
10449
|
+
}
|
|
10450
|
+
}
|
|
10451
|
+
if (args.apply) {
|
|
10452
|
+
if (!args.confirm) {
|
|
10453
|
+
return ok2({ discovered, applied: false, message: "Set confirm: true to apply discovered modules." });
|
|
10454
|
+
}
|
|
10455
|
+
const descriptor = loadModulesDescriptor(root);
|
|
10456
|
+
for (const d3 of discovered.filter((dd) => dd.eligibleForMapping)) {
|
|
10457
|
+
if (!(descriptor.modules ?? []).some((m2) => m2.id === d3.id)) {
|
|
10458
|
+
if (!descriptor.modules) descriptor.modules = [];
|
|
10459
|
+
descriptor.modules.push({ id: d3.id, name: d3.id, path: d3.path, parent_system: sysName, status: "configured" });
|
|
10460
|
+
}
|
|
10461
|
+
}
|
|
10462
|
+
const ymlContent = stringifyYaml4({ version: descriptor.version ?? 1, system: sysName, workspace_roots: workspaceRoots2, modules: descriptor.modules ?? [] });
|
|
10463
|
+
writeDerived(root, ".kaddo/modules.yml", ymlContent);
|
|
10464
|
+
return ok2({ discovered, applied: true });
|
|
10465
|
+
}
|
|
10466
|
+
return ok2({ discovered, applied: false });
|
|
10467
|
+
}
|
|
10351
10468
|
function renderCapsuleMd(data) {
|
|
10352
10469
|
const fm = ["---", `type: knowledge-capsule`, `system: ${data.system}`, `version: ${data.version}`, `updated_at: ${data.updated_at}`, "---", ""].join("\n");
|
|
10353
10470
|
const list2 = (items) => items.length === 0 ? "_To be completed._" : items.map((i) => `- ${i}`).join("\n");
|
|
@@ -11641,6 +11758,18 @@ function createServer(root) {
|
|
|
11641
11758
|
},
|
|
11642
11759
|
async (args) => toolText(guarded(root, () => exportCapsuleTool(root, args)))
|
|
11643
11760
|
);
|
|
11761
|
+
server.registerTool(
|
|
11762
|
+
"kaddo_modules_discover",
|
|
11763
|
+
{
|
|
11764
|
+
title: "Discover multirepo modules",
|
|
11765
|
+
description: "Discover sibling repositories configured as Kaddo modules. Does not persist without apply+confirm. Core only.",
|
|
11766
|
+
inputSchema: {
|
|
11767
|
+
apply: z3.boolean().optional(),
|
|
11768
|
+
confirm: z3.boolean().optional()
|
|
11769
|
+
}
|
|
11770
|
+
},
|
|
11771
|
+
async (args) => toolText(guarded(root, () => modulesDiscoverTool(root, args)))
|
|
11772
|
+
);
|
|
11644
11773
|
let installedSkills = [];
|
|
11645
11774
|
try {
|
|
11646
11775
|
assertKaddoProject(root);
|
package/package.json
CHANGED