@kaddo/mcp 3.81.0 → 3.82.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 +530 -26
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -262,8 +262,8 @@ function sectionParagraph(md, title) {
|
|
|
262
262
|
return "";
|
|
263
263
|
}
|
|
264
264
|
function listCapsules(root) {
|
|
265
|
-
const
|
|
266
|
-
const entries = Array.isArray(
|
|
265
|
+
const registry2 = readYaml(root, ".kaddo/external.yml");
|
|
266
|
+
const entries = Array.isArray(registry2?.external) ? registry2.external : [];
|
|
267
267
|
return entries.map((e) => {
|
|
268
268
|
const md = e.path ? readText(root, e.path) : null;
|
|
269
269
|
let system;
|
|
@@ -480,8 +480,8 @@ function loadConfig(dir) {
|
|
|
480
480
|
const parsed = configSchema.safeParse(raw ?? {});
|
|
481
481
|
if (!parsed.success) {
|
|
482
482
|
const issues = parsed.error.issues.map((i) => {
|
|
483
|
-
const
|
|
484
|
-
return
|
|
483
|
+
const path6 = i.path.join(".");
|
|
484
|
+
return path6 ? ` - ${path6}: ${i.message}` : ` - ${i.message}`;
|
|
485
485
|
}).join("\n");
|
|
486
486
|
throw new ConfigError(`Invalid .kaddo/config.yml:
|
|
487
487
|
${issues}`);
|
|
@@ -922,11 +922,11 @@ function moduleArtifactCoverage(dir, id) {
|
|
|
922
922
|
};
|
|
923
923
|
}
|
|
924
924
|
function loadMappedModules(dir) {
|
|
925
|
-
const
|
|
926
|
-
if (!exists(
|
|
925
|
+
const path6 = join(dir, DESCRIPTOR_PATH);
|
|
926
|
+
if (!exists(path6)) return [];
|
|
927
927
|
let parsed;
|
|
928
928
|
try {
|
|
929
|
-
parsed = parseYaml4(readFile(
|
|
929
|
+
parsed = parseYaml4(readFile(path6));
|
|
930
930
|
} catch {
|
|
931
931
|
return [];
|
|
932
932
|
}
|
|
@@ -2433,7 +2433,7 @@ function sectionParagraph2(md, title) {
|
|
|
2433
2433
|
}
|
|
2434
2434
|
return "";
|
|
2435
2435
|
}
|
|
2436
|
-
function parseCapsule(id,
|
|
2436
|
+
function parseCapsule(id, path6, md) {
|
|
2437
2437
|
const { data } = matter4(md);
|
|
2438
2438
|
const updatedAt = data.updated_at ? String(data.updated_at) : void 0;
|
|
2439
2439
|
let ageDays = null;
|
|
@@ -2443,7 +2443,7 @@ function parseCapsule(id, path5, md) {
|
|
|
2443
2443
|
}
|
|
2444
2444
|
return {
|
|
2445
2445
|
id,
|
|
2446
|
-
path:
|
|
2446
|
+
path: path6,
|
|
2447
2447
|
system: data.system ? String(data.system) : id,
|
|
2448
2448
|
owner: data.owner ? String(data.owner) : void 0,
|
|
2449
2449
|
updatedAt,
|
|
@@ -2505,9 +2505,9 @@ function buildGraph(dir, config, opts = {}, now = /* @__PURE__ */ new Date()) {
|
|
|
2505
2505
|
];
|
|
2506
2506
|
const presentLayers = [];
|
|
2507
2507
|
for (const layer2 of layerDocs) {
|
|
2508
|
-
const
|
|
2509
|
-
if (
|
|
2510
|
-
addNode({ id: layer2.id, type: layer2.type, label: layer2.label, path:
|
|
2508
|
+
const path6 = layer2.files.map((f3) => `${KNOWLEDGE3}/${f3}`).find((rel) => exists(join(dir, rel)));
|
|
2509
|
+
if (path6) {
|
|
2510
|
+
addNode({ id: layer2.id, type: layer2.type, label: layer2.label, path: path6 });
|
|
2511
2511
|
presentLayers.push(layer2.id);
|
|
2512
2512
|
}
|
|
2513
2513
|
}
|
|
@@ -4088,10 +4088,10 @@ function resolveNextStep(dir, now = /* @__PURE__ */ new Date()) {
|
|
|
4088
4088
|
const q3 = (rel) => analyzeKnowledgeArtifact(dir, rel);
|
|
4089
4089
|
const resolveAgent = (agent) => {
|
|
4090
4090
|
const file = agent.endsWith(".md") ? agent : `${agent}.md`;
|
|
4091
|
-
const
|
|
4092
|
-
const installed = isFile(join(dir,
|
|
4091
|
+
const path6 = agentInstallPath(file);
|
|
4092
|
+
const installed = isFile(join(dir, path6));
|
|
4093
4093
|
const group = agentGroupOf(file);
|
|
4094
|
-
return { agentPath:
|
|
4094
|
+
return { agentPath: path6, agentInstalled: installed, installCommand: installed ? void 0 : `kaddo add agents --group ${group}` };
|
|
4095
4095
|
};
|
|
4096
4096
|
const moduleRepo = isModule(config);
|
|
4097
4097
|
const coreRepo = isCore(config);
|
|
@@ -7643,6 +7643,7 @@ function parseWorkItemSource(frontmatter) {
|
|
|
7643
7643
|
title: optStr(obj.title) ?? optStr(frontmatter.source_title),
|
|
7644
7644
|
context: optStr(obj.context) ?? optStr(frontmatter.source_context),
|
|
7645
7645
|
provider: optStr(obj.provider) ?? optStr(frontmatter.source_provider),
|
|
7646
|
+
integration: optStr(obj.integration) ?? optStr(frontmatter.source_integration),
|
|
7646
7647
|
url: optStr(obj.url) ?? optStr(frontmatter.source_url),
|
|
7647
7648
|
imported_at: optStr(obj.imported_at) ?? optStr(frontmatter.source_imported_at),
|
|
7648
7649
|
synced_at: optStr(obj.synced_at) ?? optStr(frontmatter.source_synced_at),
|
|
@@ -9131,7 +9132,7 @@ function buildGuardHistory(dir) {
|
|
|
9131
9132
|
const d3 = hotspotDir(t.code_path);
|
|
9132
9133
|
hotspotMap.set(d3, (hotspotMap.get(d3) ?? 0) + 1);
|
|
9133
9134
|
}
|
|
9134
|
-
const hotspots = [...hotspotMap.entries()].map(([
|
|
9135
|
+
const hotspots = [...hotspotMap.entries()].map(([path6, warnings]) => ({ path: path6, warnings })).sort((a2, b2) => b2.warnings - a2.warnings);
|
|
9135
9136
|
return {
|
|
9136
9137
|
available: true,
|
|
9137
9138
|
total_runs: sorted.length,
|
|
@@ -11515,16 +11516,16 @@ function findSystemPaths(dir, source, target, opts = {}) {
|
|
|
11515
11516
|
out.get(r2.source).push(r2.target);
|
|
11516
11517
|
}
|
|
11517
11518
|
const paths = [];
|
|
11518
|
-
const walk = (node,
|
|
11519
|
+
const walk = (node, path6) => {
|
|
11519
11520
|
if (paths.length >= 20) return;
|
|
11520
|
-
if (node === target &&
|
|
11521
|
-
paths.push([...
|
|
11521
|
+
if (node === target && path6.length > 1) {
|
|
11522
|
+
paths.push([...path6]);
|
|
11522
11523
|
return;
|
|
11523
11524
|
}
|
|
11524
|
-
if (
|
|
11525
|
+
if (path6.length > maxDepth) return;
|
|
11525
11526
|
for (const nxt of out.get(node) ?? []) {
|
|
11526
|
-
if (
|
|
11527
|
-
walk(nxt, [...
|
|
11527
|
+
if (path6.includes(nxt)) continue;
|
|
11528
|
+
walk(nxt, [...path6, nxt]);
|
|
11528
11529
|
}
|
|
11529
11530
|
};
|
|
11530
11531
|
walk(source, [source]);
|
|
@@ -11549,14 +11550,14 @@ function getImpactCandidates(dir, seedIds, opts = {}) {
|
|
|
11549
11550
|
existing.reason.push(reason);
|
|
11550
11551
|
continue;
|
|
11551
11552
|
}
|
|
11552
|
-
const
|
|
11553
|
+
const path6 = findSystemPaths(dir, seed, n.id, { maxDepth: opts.maxDepth ?? DEFAULT_MAX_DEPTH })[0];
|
|
11553
11554
|
candidates.set(n.id, {
|
|
11554
11555
|
nodeId: n.id,
|
|
11555
11556
|
label: n.label,
|
|
11556
11557
|
kind: n.type,
|
|
11557
11558
|
...n.moduleId ? { moduleId: n.moduleId } : {},
|
|
11558
11559
|
reason: [reason],
|
|
11559
|
-
...
|
|
11560
|
+
...path6 ? { graphPath: path6.map((id) => byId.get(id)?.label ?? id) } : {},
|
|
11560
11561
|
...n.implementationRefs?.length ? { implementationRefs: n.implementationRefs } : {},
|
|
11561
11562
|
...n.knowledgeRefs?.length ? { knowledgeRefs: n.knowledgeRefs } : {},
|
|
11562
11563
|
status: "candidate",
|
|
@@ -11654,8 +11655,463 @@ function systemImpactCandidatesTool(root, args) {
|
|
|
11654
11655
|
});
|
|
11655
11656
|
}
|
|
11656
11657
|
|
|
11657
|
-
// ../cli/src/
|
|
11658
|
+
// ../cli/src/services/integrations.ts
|
|
11659
|
+
import matter13 from "gray-matter";
|
|
11660
|
+
import { parse as parseYaml12 } from "yaml";
|
|
11661
|
+
|
|
11662
|
+
// ../cli/src/core/work-item-write.ts
|
|
11663
|
+
import fs5 from "fs";
|
|
11664
|
+
import path5 from "path";
|
|
11665
|
+
import crypto2 from "crypto";
|
|
11658
11666
|
import matter12 from "gray-matter";
|
|
11667
|
+
var SECTION_ORDER = [
|
|
11668
|
+
"Actor",
|
|
11669
|
+
"Outcome",
|
|
11670
|
+
"Current behavior",
|
|
11671
|
+
"Target behavior",
|
|
11672
|
+
"Entry points",
|
|
11673
|
+
"End-to-end flow",
|
|
11674
|
+
"Scope unknowns",
|
|
11675
|
+
"Acceptance criteria"
|
|
11676
|
+
];
|
|
11677
|
+
var KNOWN_HEADINGS = new Set(SECTION_ORDER.map((s) => s.toLowerCase()));
|
|
11678
|
+
|
|
11679
|
+
// ../integrations/src/errors.ts
|
|
11680
|
+
var RETRYABLE = /* @__PURE__ */ new Set([
|
|
11681
|
+
"INTEGRATION_RATE_LIMITED",
|
|
11682
|
+
"INTEGRATION_UNAVAILABLE",
|
|
11683
|
+
"INTEGRATION_TIMEOUT"
|
|
11684
|
+
]);
|
|
11685
|
+
var IntegrationError = class extends Error {
|
|
11686
|
+
code;
|
|
11687
|
+
/** True for transient conditions (rate limit / unavailable / timeout). */
|
|
11688
|
+
retryable;
|
|
11689
|
+
/** A safe, provider-agnostic reason. Never the raw SDK message. */
|
|
11690
|
+
safeMessage;
|
|
11691
|
+
constructor(code, message) {
|
|
11692
|
+
super(message);
|
|
11693
|
+
this.name = "IntegrationError";
|
|
11694
|
+
this.code = code;
|
|
11695
|
+
this.retryable = RETRYABLE.has(code);
|
|
11696
|
+
this.safeMessage = message;
|
|
11697
|
+
}
|
|
11698
|
+
};
|
|
11699
|
+
function defaultMessageFor(code) {
|
|
11700
|
+
switch (code) {
|
|
11701
|
+
case "INTEGRATION_CONFIG_INVALID":
|
|
11702
|
+
return "The integration configuration is invalid.";
|
|
11703
|
+
case "INTEGRATION_UNAUTHORIZED":
|
|
11704
|
+
return "Authentication failed. Check the configured credentials.";
|
|
11705
|
+
case "INTEGRATION_FORBIDDEN":
|
|
11706
|
+
return "The configured credentials lack permission for this operation.";
|
|
11707
|
+
case "INTEGRATION_NOT_FOUND":
|
|
11708
|
+
return "The requested external resource was not found.";
|
|
11709
|
+
case "INTEGRATION_RATE_LIMITED":
|
|
11710
|
+
return "The external provider is rate limiting requests. Try again later.";
|
|
11711
|
+
case "INTEGRATION_UNAVAILABLE":
|
|
11712
|
+
return "The external provider is temporarily unavailable.";
|
|
11713
|
+
case "INTEGRATION_TIMEOUT":
|
|
11714
|
+
return "The external provider did not respond in time.";
|
|
11715
|
+
case "INTEGRATION_PROVIDER_ERROR":
|
|
11716
|
+
return "The external provider returned an error.";
|
|
11717
|
+
case "UNSUPPORTED_CAPABILITY":
|
|
11718
|
+
return "This adapter does not support the requested capability.";
|
|
11719
|
+
}
|
|
11720
|
+
}
|
|
11721
|
+
function integrationError(code, message) {
|
|
11722
|
+
return new IntegrationError(code, message ?? defaultMessageFor(code));
|
|
11723
|
+
}
|
|
11724
|
+
|
|
11725
|
+
// ../integrations/src/config.ts
|
|
11726
|
+
var ENV_SUFFIX = "_env";
|
|
11727
|
+
var SECRET_KEY = /(token|secret|password|key|pat|apikey|api_key)/i;
|
|
11728
|
+
function parseCredentials(raw, id, findings) {
|
|
11729
|
+
const creds = {};
|
|
11730
|
+
if (raw == null) return creds;
|
|
11731
|
+
if (typeof raw !== "object" || Array.isArray(raw)) {
|
|
11732
|
+
findings.push({ level: "blocking", id, message: `Integration "${id}": credentials must be a mapping of secret references.` });
|
|
11733
|
+
return creds;
|
|
11734
|
+
}
|
|
11735
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
11736
|
+
if (key.endsWith(ENV_SUFFIX)) {
|
|
11737
|
+
const name = key.slice(0, -ENV_SUFFIX.length);
|
|
11738
|
+
if (typeof value === "string" && value.trim()) creds[name] = { env: value.trim() };
|
|
11739
|
+
else findings.push({ level: "blocking", id, message: `Integration "${id}": credential "${key}" must name an environment variable.` });
|
|
11740
|
+
continue;
|
|
11741
|
+
}
|
|
11742
|
+
if (SECRET_KEY.test(key) && typeof value === "string") {
|
|
11743
|
+
findings.push({ level: "blocking", id, message: `Integration "${id}": secret "${key}" must not be stored in config. Use "${key}${ENV_SUFFIX}: <ENV_VAR_NAME>".` });
|
|
11744
|
+
continue;
|
|
11745
|
+
}
|
|
11746
|
+
if (value && typeof value === "object" && typeof value.env === "string") {
|
|
11747
|
+
creds[key] = { env: String(value.env) };
|
|
11748
|
+
continue;
|
|
11749
|
+
}
|
|
11750
|
+
findings.push({ level: "warning", id, message: `Integration "${id}": ignoring unrecognized credential entry "${key}".` });
|
|
11751
|
+
}
|
|
11752
|
+
return creds;
|
|
11753
|
+
}
|
|
11754
|
+
function parseIntegrationsConfig(raw, opts) {
|
|
11755
|
+
const findings = [];
|
|
11756
|
+
const integrations = [];
|
|
11757
|
+
const list2 = raw && typeof raw === "object" && Array.isArray(raw.integrations) ? raw.integrations : Array.isArray(raw) ? raw : [];
|
|
11758
|
+
const seen = /* @__PURE__ */ new Set();
|
|
11759
|
+
for (const entry of list2) {
|
|
11760
|
+
if (!entry || typeof entry !== "object") {
|
|
11761
|
+
findings.push({ level: "blocking", message: "Each integration must be a mapping." });
|
|
11762
|
+
continue;
|
|
11763
|
+
}
|
|
11764
|
+
const o2 = entry;
|
|
11765
|
+
const id = typeof o2.id === "string" ? o2.id.trim() : "";
|
|
11766
|
+
const adapter = typeof o2.adapter === "string" ? o2.adapter.trim() : "";
|
|
11767
|
+
if (!id) {
|
|
11768
|
+
findings.push({ level: "blocking", message: 'An integration is missing a required "id".' });
|
|
11769
|
+
continue;
|
|
11770
|
+
}
|
|
11771
|
+
if (seen.has(id)) {
|
|
11772
|
+
findings.push({ level: "blocking", id, message: `Duplicate integration id "${id}".` });
|
|
11773
|
+
continue;
|
|
11774
|
+
}
|
|
11775
|
+
seen.add(id);
|
|
11776
|
+
if (!adapter) {
|
|
11777
|
+
findings.push({ level: "blocking", id, message: `Integration "${id}" is missing a required "adapter".` });
|
|
11778
|
+
continue;
|
|
11779
|
+
}
|
|
11780
|
+
if (!opts.adapterIds.has(adapter)) {
|
|
11781
|
+
findings.push({ level: "blocking", id, message: `Integration "${id}" references unknown adapter "${adapter}".` });
|
|
11782
|
+
}
|
|
11783
|
+
const enabled = o2.enabled === void 0 ? true : o2.enabled === true || o2.enabled === "true";
|
|
11784
|
+
const config = o2.config && typeof o2.config === "object" && !Array.isArray(o2.config) ? o2.config : {};
|
|
11785
|
+
const credentials = parseCredentials(o2.credentials, id, findings);
|
|
11786
|
+
const timeoutMs = typeof o2.timeout_ms === "number" ? o2.timeout_ms : typeof o2.timeoutMs === "number" ? o2.timeoutMs : void 0;
|
|
11787
|
+
integrations.push({ id, adapter, enabled, config, credentials, timeoutMs });
|
|
11788
|
+
}
|
|
11789
|
+
return { integrations, findings };
|
|
11790
|
+
}
|
|
11791
|
+
function resolveCredentials(integration, env) {
|
|
11792
|
+
const credentials = {};
|
|
11793
|
+
const missing = [];
|
|
11794
|
+
for (const [name, ref] of Object.entries(integration.credentials)) {
|
|
11795
|
+
const value = env[ref.env];
|
|
11796
|
+
if (value && value.length > 0) credentials[name] = value;
|
|
11797
|
+
else missing.push(ref.env);
|
|
11798
|
+
}
|
|
11799
|
+
return { credentials, missing };
|
|
11800
|
+
}
|
|
11801
|
+
|
|
11802
|
+
// ../integrations/src/registry.ts
|
|
11803
|
+
var DuplicateAdapterError = class extends Error {
|
|
11804
|
+
constructor(id) {
|
|
11805
|
+
super(`An integration adapter with id "${id}" is already registered.`);
|
|
11806
|
+
this.name = "DuplicateAdapterError";
|
|
11807
|
+
}
|
|
11808
|
+
};
|
|
11809
|
+
var IntegrationRegistry = class {
|
|
11810
|
+
adapters = /* @__PURE__ */ new Map();
|
|
11811
|
+
register(adapter) {
|
|
11812
|
+
if (this.adapters.has(adapter.id)) throw new DuplicateAdapterError(adapter.id);
|
|
11813
|
+
this.adapters.set(adapter.id, adapter);
|
|
11814
|
+
}
|
|
11815
|
+
get(id) {
|
|
11816
|
+
return this.adapters.get(id);
|
|
11817
|
+
}
|
|
11818
|
+
has(id) {
|
|
11819
|
+
return this.adapters.has(id);
|
|
11820
|
+
}
|
|
11821
|
+
list() {
|
|
11822
|
+
return [...this.adapters.values()].sort((a2, b2) => a2.id.localeCompare(b2.id));
|
|
11823
|
+
}
|
|
11824
|
+
ids() {
|
|
11825
|
+
return new Set(this.adapters.keys());
|
|
11826
|
+
}
|
|
11827
|
+
};
|
|
11828
|
+
|
|
11829
|
+
// ../integrations/src/mock-adapter.ts
|
|
11830
|
+
var MOCK_ADAPTER_ID = "mock";
|
|
11831
|
+
var DEFAULT_ITEMS = [
|
|
11832
|
+
{
|
|
11833
|
+
externalId: "EXT-001",
|
|
11834
|
+
provider: MOCK_ADAPTER_ID,
|
|
11835
|
+
title: "Open public registration to everyone",
|
|
11836
|
+
description: "Open self-service registration to the public once the private beta ends.",
|
|
11837
|
+
type: "Feature",
|
|
11838
|
+
status: "Open",
|
|
11839
|
+
url: "https://example.test/mock/EXT-001",
|
|
11840
|
+
labels: ["registration", "beta"],
|
|
11841
|
+
createdAt: "2026-01-05T10:00:00.000Z",
|
|
11842
|
+
updatedAt: "2026-02-01T09:30:00.000Z",
|
|
11843
|
+
rawMetadata: { board: "delivery" }
|
|
11844
|
+
},
|
|
11845
|
+
{
|
|
11846
|
+
externalId: "EXT-002",
|
|
11847
|
+
provider: MOCK_ADAPTER_ID,
|
|
11848
|
+
title: "Personalize onboarding steps",
|
|
11849
|
+
description: "The onboarding checklist should adapt to what the user has already completed.",
|
|
11850
|
+
type: "Task",
|
|
11851
|
+
status: "To Do",
|
|
11852
|
+
url: "https://example.test/mock/EXT-002",
|
|
11853
|
+
labels: ["onboarding"],
|
|
11854
|
+
createdAt: "2026-01-08T12:00:00.000Z",
|
|
11855
|
+
updatedAt: "2026-01-20T15:00:00.000Z"
|
|
11856
|
+
}
|
|
11857
|
+
];
|
|
11858
|
+
function simulationOf(context, fallback) {
|
|
11859
|
+
const fromConfig = context.config?.simulate;
|
|
11860
|
+
return typeof fromConfig === "string" ? fromConfig : fallback;
|
|
11861
|
+
}
|
|
11862
|
+
function readFailure(sim) {
|
|
11863
|
+
switch (sim) {
|
|
11864
|
+
case "unauthorized":
|
|
11865
|
+
throw integrationError("INTEGRATION_UNAUTHORIZED");
|
|
11866
|
+
case "rate-limited":
|
|
11867
|
+
throw integrationError("INTEGRATION_RATE_LIMITED");
|
|
11868
|
+
case "unavailable":
|
|
11869
|
+
throw integrationError("INTEGRATION_UNAVAILABLE");
|
|
11870
|
+
case "timeout":
|
|
11871
|
+
throw integrationError("INTEGRATION_TIMEOUT");
|
|
11872
|
+
case "available":
|
|
11873
|
+
break;
|
|
11874
|
+
}
|
|
11875
|
+
}
|
|
11876
|
+
function createMockAdapter(opts = {}) {
|
|
11877
|
+
const items = opts.items ?? DEFAULT_ITEMS;
|
|
11878
|
+
const defaultSim = opts.simulate ?? "available";
|
|
11879
|
+
const defaultPageSize = opts.pageSize ?? 50;
|
|
11880
|
+
return {
|
|
11881
|
+
id: MOCK_ADAPTER_ID,
|
|
11882
|
+
metadata: {
|
|
11883
|
+
id: MOCK_ADAPTER_ID,
|
|
11884
|
+
displayName: "Mock Work Source",
|
|
11885
|
+
version: "1.0.0",
|
|
11886
|
+
description: "Deterministic offline reference adapter for validating the integration foundation."
|
|
11887
|
+
},
|
|
11888
|
+
capabilities: {
|
|
11889
|
+
workItems: { list: true, read: true, import: true, write: false, statusSync: false, comments: false, webhooks: false }
|
|
11890
|
+
},
|
|
11891
|
+
async verifyConnection(context) {
|
|
11892
|
+
const sim = simulationOf(context, defaultSim);
|
|
11893
|
+
const checkedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
11894
|
+
switch (sim) {
|
|
11895
|
+
case "available":
|
|
11896
|
+
return { status: "available", checkedAt };
|
|
11897
|
+
case "unauthorized":
|
|
11898
|
+
return { status: "unauthorized", message: "Check the configured credentials.", checkedAt };
|
|
11899
|
+
case "rate-limited":
|
|
11900
|
+
case "unavailable":
|
|
11901
|
+
case "timeout":
|
|
11902
|
+
return { status: "unavailable", message: "The mock provider is temporarily unavailable.", checkedAt };
|
|
11903
|
+
}
|
|
11904
|
+
},
|
|
11905
|
+
async listWorkItems(request) {
|
|
11906
|
+
readFailure(simulationOf(request.context, defaultSim));
|
|
11907
|
+
let pool = items;
|
|
11908
|
+
const f3 = request.filters;
|
|
11909
|
+
if (f3?.status) pool = pool.filter((i) => (i.status ?? "").toLowerCase() === f3.status.toLowerCase());
|
|
11910
|
+
if (f3?.query) pool = pool.filter((i) => `${i.title} ${i.description ?? ""}`.toLowerCase().includes(f3.query.toLowerCase()));
|
|
11911
|
+
if (f3?.updatedSince) pool = pool.filter((i) => (i.updatedAt ?? "") >= f3.updatedSince);
|
|
11912
|
+
const size = Math.max(1, request.pageSize ?? defaultPageSize);
|
|
11913
|
+
const start = request.cursor ? Math.max(0, Number.parseInt(request.cursor, 10) || 0) : 0;
|
|
11914
|
+
const slice = pool.slice(start, start + size);
|
|
11915
|
+
const end = start + slice.length;
|
|
11916
|
+
const hasMore = end < pool.length;
|
|
11917
|
+
return { items: slice, hasMore, ...hasMore ? { nextCursor: String(end) } : {} };
|
|
11918
|
+
},
|
|
11919
|
+
async getWorkItem(request) {
|
|
11920
|
+
readFailure(simulationOf(request.context, defaultSim));
|
|
11921
|
+
return items.find((i) => i.externalId === request.externalId) ?? null;
|
|
11922
|
+
}
|
|
11923
|
+
};
|
|
11924
|
+
}
|
|
11925
|
+
|
|
11926
|
+
// ../integrations/src/index.ts
|
|
11927
|
+
function createDefaultRegistry() {
|
|
11928
|
+
const registry2 = new IntegrationRegistry();
|
|
11929
|
+
registry2.register(createMockAdapter());
|
|
11930
|
+
return registry2;
|
|
11931
|
+
}
|
|
11932
|
+
|
|
11933
|
+
// ../cli/src/services/integrations.ts
|
|
11934
|
+
var INTEGRATIONS_FILE = ".kaddo/integrations.yml";
|
|
11935
|
+
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
11936
|
+
var registry = createDefaultRegistry();
|
|
11937
|
+
var IntegrationServiceError = class extends Error {
|
|
11938
|
+
code;
|
|
11939
|
+
constructor(code, message) {
|
|
11940
|
+
super(message);
|
|
11941
|
+
this.name = "IntegrationServiceError";
|
|
11942
|
+
this.code = code;
|
|
11943
|
+
}
|
|
11944
|
+
};
|
|
11945
|
+
function loadRaw(dir) {
|
|
11946
|
+
const abs = join(dir, INTEGRATIONS_FILE);
|
|
11947
|
+
if (!exists(abs)) return { integrations: [] };
|
|
11948
|
+
try {
|
|
11949
|
+
return parseYaml12(readFile(abs)) ?? { integrations: [] };
|
|
11950
|
+
} catch {
|
|
11951
|
+
return { integrations: [] };
|
|
11952
|
+
}
|
|
11953
|
+
}
|
|
11954
|
+
function loadIntegrations(dir) {
|
|
11955
|
+
return parseIntegrationsConfig(loadRaw(dir), { adapterIds: registry.ids() });
|
|
11956
|
+
}
|
|
11957
|
+
function configStatus(integration, findings) {
|
|
11958
|
+
if (!integration.enabled) return "disabled";
|
|
11959
|
+
const blocking = findings.some((f3) => f3.id === integration.id && f3.level === "blocking");
|
|
11960
|
+
if (blocking || !registry.has(integration.adapter)) return "invalid-config";
|
|
11961
|
+
return "configured";
|
|
11962
|
+
}
|
|
11963
|
+
function listIntegrations(dir) {
|
|
11964
|
+
const { integrations, findings } = loadIntegrations(dir);
|
|
11965
|
+
return integrations.map((integration) => {
|
|
11966
|
+
const adapter = registry.get(integration.adapter);
|
|
11967
|
+
return {
|
|
11968
|
+
id: integration.id,
|
|
11969
|
+
adapter: integration.adapter,
|
|
11970
|
+
enabled: integration.enabled,
|
|
11971
|
+
status: configStatus(integration, findings),
|
|
11972
|
+
displayName: adapter?.metadata.displayName ?? integration.adapter,
|
|
11973
|
+
capabilities: adapter?.capabilities ?? null,
|
|
11974
|
+
metadata: adapter?.metadata ?? null,
|
|
11975
|
+
credentialRefs: Object.values(integration.credentials).map((c) => c.env),
|
|
11976
|
+
findings: findings.filter((f3) => f3.id === integration.id)
|
|
11977
|
+
};
|
|
11978
|
+
});
|
|
11979
|
+
}
|
|
11980
|
+
function requireIntegration(dir, id) {
|
|
11981
|
+
const { integrations, findings } = loadIntegrations(dir);
|
|
11982
|
+
const integration = integrations.find((i) => i.id === id);
|
|
11983
|
+
if (!integration) throw new IntegrationServiceError("INTEGRATION_NOT_CONFIGURED", `No integration "${id}" is configured in this project.`);
|
|
11984
|
+
return { integration, findings };
|
|
11985
|
+
}
|
|
11986
|
+
function resolveAdapter(integration) {
|
|
11987
|
+
const adapter = registry.get(integration.adapter);
|
|
11988
|
+
if (!adapter) throw new IntegrationServiceError("ADAPTER_NOT_FOUND", `Unknown integration adapter "${integration.adapter}".`);
|
|
11989
|
+
return adapter;
|
|
11990
|
+
}
|
|
11991
|
+
function buildContext(integration, env) {
|
|
11992
|
+
const { credentials, missing } = resolveCredentials(integration, env);
|
|
11993
|
+
return {
|
|
11994
|
+
context: { integrationId: integration.id, config: integration.config, credentials, timeoutMs: integration.timeoutMs ?? DEFAULT_TIMEOUT_MS },
|
|
11995
|
+
missing
|
|
11996
|
+
};
|
|
11997
|
+
}
|
|
11998
|
+
async function withTimeout(op, ms) {
|
|
11999
|
+
let timer;
|
|
12000
|
+
const timeout = new Promise((_3, reject) => {
|
|
12001
|
+
timer = setTimeout(() => reject(integrationError("INTEGRATION_TIMEOUT")), ms);
|
|
12002
|
+
});
|
|
12003
|
+
try {
|
|
12004
|
+
return await Promise.race([op, timeout]);
|
|
12005
|
+
} finally {
|
|
12006
|
+
if (timer) clearTimeout(timer);
|
|
12007
|
+
}
|
|
12008
|
+
}
|
|
12009
|
+
async function verifyIntegration(dir, id, env = process.env) {
|
|
12010
|
+
const { integration, findings } = requireIntegration(dir, id);
|
|
12011
|
+
const cfgStatus = configStatus(integration, findings);
|
|
12012
|
+
if (cfgStatus === "disabled") return { id, status: "disabled", connection: null, missingCredentials: [] };
|
|
12013
|
+
if (cfgStatus === "invalid-config") return { id, status: "invalid-config", connection: null, missingCredentials: [] };
|
|
12014
|
+
const adapter = resolveAdapter(integration);
|
|
12015
|
+
const { context, missing } = buildContext(integration, env);
|
|
12016
|
+
try {
|
|
12017
|
+
const connection = await withTimeout(adapter.verifyConnection(context), context.timeoutMs);
|
|
12018
|
+
return { id, status: statusOf(connection), connection, missingCredentials: missing, message: connection.message };
|
|
12019
|
+
} catch (err) {
|
|
12020
|
+
const e = err instanceof IntegrationError ? err : integrationError("INTEGRATION_PROVIDER_ERROR");
|
|
12021
|
+
return { id, status: e.code === "INTEGRATION_UNAUTHORIZED" ? "unauthorized" : "unavailable", connection: null, missingCredentials: missing, message: e.safeMessage };
|
|
12022
|
+
}
|
|
12023
|
+
}
|
|
12024
|
+
function statusOf(connection) {
|
|
12025
|
+
switch (connection.status) {
|
|
12026
|
+
case "available":
|
|
12027
|
+
return "available";
|
|
12028
|
+
case "unauthorized":
|
|
12029
|
+
return "unauthorized";
|
|
12030
|
+
case "unavailable":
|
|
12031
|
+
return "unavailable";
|
|
12032
|
+
case "invalid-config":
|
|
12033
|
+
return "invalid-config";
|
|
12034
|
+
}
|
|
12035
|
+
}
|
|
12036
|
+
async function listExternalWorkItems(dir, id, opts = {}, env = process.env) {
|
|
12037
|
+
const { integration } = requireIntegration(dir, id);
|
|
12038
|
+
const adapter = resolveAdapter(integration);
|
|
12039
|
+
if (!adapter.capabilities.workItems.list) throw integrationError("UNSUPPORTED_CAPABILITY", `Adapter "${adapter.id}" cannot list work items.`);
|
|
12040
|
+
const { context } = buildContext(integration, env);
|
|
12041
|
+
return withTimeout(adapter.listWorkItems({ context, cursor: opts.cursor, pageSize: opts.pageSize, filters: opts.filters }), context.timeoutMs);
|
|
12042
|
+
}
|
|
12043
|
+
async function getExternalWorkItem(dir, id, externalId, env = process.env) {
|
|
12044
|
+
const { integration } = requireIntegration(dir, id);
|
|
12045
|
+
const adapter = resolveAdapter(integration);
|
|
12046
|
+
if (!adapter.capabilities.workItems.read) throw integrationError("UNSUPPORTED_CAPABILITY", `Adapter "${adapter.id}" cannot read work items.`);
|
|
12047
|
+
const { context } = buildContext(integration, env);
|
|
12048
|
+
return withTimeout(adapter.getWorkItem({ context, externalId }), context.timeoutMs);
|
|
12049
|
+
}
|
|
12050
|
+
|
|
12051
|
+
// src/integrations.ts
|
|
12052
|
+
var ok4 = (data) => ({ ok: true, data });
|
|
12053
|
+
var fail3 = (message) => ({ ok: false, message });
|
|
12054
|
+
function safeError(err) {
|
|
12055
|
+
if (err instanceof IntegrationError) return fail3(`[${err.code}] ${err.safeMessage}`);
|
|
12056
|
+
return fail3("The integration operation failed.");
|
|
12057
|
+
}
|
|
12058
|
+
function integrationsListTool(root) {
|
|
12059
|
+
return ok4({
|
|
12060
|
+
integrations: listIntegrations(root).map((i) => ({
|
|
12061
|
+
id: i.id,
|
|
12062
|
+
adapter: i.adapter,
|
|
12063
|
+
enabled: i.enabled,
|
|
12064
|
+
status: i.status,
|
|
12065
|
+
displayName: i.displayName,
|
|
12066
|
+
capabilities: i.capabilities,
|
|
12067
|
+
// Only credential *reference* names — never values.
|
|
12068
|
+
credentialRefs: i.credentialRefs
|
|
12069
|
+
})),
|
|
12070
|
+
note: "Read-only. Reading external items never creates a Kaddo Work Item; import requires human confirmation."
|
|
12071
|
+
});
|
|
12072
|
+
}
|
|
12073
|
+
async function integrationsStatusTool(root, args) {
|
|
12074
|
+
try {
|
|
12075
|
+
const summaries = listIntegrations(root).filter((s) => !args.id || s.id === args.id);
|
|
12076
|
+
const results = await Promise.all(summaries.map(async (s) => {
|
|
12077
|
+
if (s.status === "disabled" || s.status === "invalid-config") return { id: s.id, status: s.status };
|
|
12078
|
+
const v = await verifyIntegration(root, s.id);
|
|
12079
|
+
return { id: v.id, status: v.status, message: v.message, missingCredentials: v.missingCredentials };
|
|
12080
|
+
}));
|
|
12081
|
+
return ok4({ statuses: results });
|
|
12082
|
+
} catch (err) {
|
|
12083
|
+
return safeError(err);
|
|
12084
|
+
}
|
|
12085
|
+
}
|
|
12086
|
+
async function integrationsWorkItemsTool(root, args) {
|
|
12087
|
+
try {
|
|
12088
|
+
const page = await listExternalWorkItems(root, args.id, {
|
|
12089
|
+
cursor: args.cursor,
|
|
12090
|
+
pageSize: args.pageSize,
|
|
12091
|
+
filters: { status: args.status, query: args.query }
|
|
12092
|
+
});
|
|
12093
|
+
return ok4({
|
|
12094
|
+
items: page.items,
|
|
12095
|
+
hasMore: page.hasMore,
|
|
12096
|
+
nextCursor: page.nextCursor ?? null,
|
|
12097
|
+
note: "Read-only external items. Import them explicitly (with human confirmation) to create a Kaddo Draft."
|
|
12098
|
+
});
|
|
12099
|
+
} catch (err) {
|
|
12100
|
+
return safeError(err);
|
|
12101
|
+
}
|
|
12102
|
+
}
|
|
12103
|
+
async function integrationsWorkItemTool(root, args) {
|
|
12104
|
+
try {
|
|
12105
|
+
const item = await getExternalWorkItem(root, args.id, args.externalId);
|
|
12106
|
+
if (!item) return ok4({ found: false });
|
|
12107
|
+
return ok4({ found: true, item });
|
|
12108
|
+
} catch (err) {
|
|
12109
|
+
return safeError(err);
|
|
12110
|
+
}
|
|
12111
|
+
}
|
|
12112
|
+
|
|
12113
|
+
// ../cli/src/core/context-pack.ts
|
|
12114
|
+
import matter14 from "gray-matter";
|
|
11659
12115
|
var CONTEXT_PACK_VERSION = "1";
|
|
11660
12116
|
var ARCH_DIR3 = "knowledge";
|
|
11661
12117
|
function readScanJson(dir) {
|
|
@@ -11668,7 +12124,7 @@ function readScanJson(dir) {
|
|
|
11668
12124
|
}
|
|
11669
12125
|
}
|
|
11670
12126
|
function firstParagraph4(markdown) {
|
|
11671
|
-
const body =
|
|
12127
|
+
const body = matter14(markdown).content.trim();
|
|
11672
12128
|
const para = body.split("\n\n").map((p) => p.trim()).find((p) => p && !p.startsWith("#"));
|
|
11673
12129
|
return para ?? "";
|
|
11674
12130
|
}
|
|
@@ -12803,6 +13259,15 @@ function guarded(root, fn) {
|
|
|
12803
13259
|
throw err;
|
|
12804
13260
|
}
|
|
12805
13261
|
}
|
|
13262
|
+
async function guardedAsync(root, fn) {
|
|
13263
|
+
try {
|
|
13264
|
+
assertKaddoProject(root);
|
|
13265
|
+
return await fn();
|
|
13266
|
+
} catch (err) {
|
|
13267
|
+
if (err instanceof KaddoMcpError) return { ok: false, message: err.message };
|
|
13268
|
+
throw err;
|
|
13269
|
+
}
|
|
13270
|
+
}
|
|
12806
13271
|
function generated(root, label, fn) {
|
|
12807
13272
|
try {
|
|
12808
13273
|
assertKaddoProject(root);
|
|
@@ -13044,6 +13509,45 @@ function createServer(root) {
|
|
|
13044
13509
|
},
|
|
13045
13510
|
async (args) => toolText(guarded(root, () => systemImpactCandidatesTool(root, args)))
|
|
13046
13511
|
);
|
|
13512
|
+
const INTEGRATION_READ_NOTE = "Read-only. Reading external items never creates a Kaddo Work Item; import is a separate, human-confirmed action.";
|
|
13513
|
+
server.registerTool(
|
|
13514
|
+
"kaddo_integrations_list",
|
|
13515
|
+
{ title: "List integrations", description: `List configured external-work-system integrations and their capabilities. ${INTEGRATION_READ_NOTE}`, inputSchema: {} },
|
|
13516
|
+
async () => toolText(guarded(root, () => integrationsListTool(root)))
|
|
13517
|
+
);
|
|
13518
|
+
server.registerTool(
|
|
13519
|
+
"kaddo_integrations_status",
|
|
13520
|
+
{
|
|
13521
|
+
title: "Integration status",
|
|
13522
|
+
description: `Verify integrations and report connection status (available/unauthorized/unavailable/\u2026). Never returns secrets. ${INTEGRATION_READ_NOTE}`,
|
|
13523
|
+
inputSchema: { id: z3.string().optional() }
|
|
13524
|
+
},
|
|
13525
|
+
async (args) => toolText(await guardedAsync(root, () => integrationsStatusTool(root, args)))
|
|
13526
|
+
);
|
|
13527
|
+
server.registerTool(
|
|
13528
|
+
"kaddo_integrations_work_items",
|
|
13529
|
+
{
|
|
13530
|
+
title: "List external work items",
|
|
13531
|
+
description: `List external work items from an integration (paginated). ${INTEGRATION_READ_NOTE}`,
|
|
13532
|
+
inputSchema: {
|
|
13533
|
+
id: z3.string(),
|
|
13534
|
+
cursor: z3.string().optional(),
|
|
13535
|
+
pageSize: z3.number().int().positive().optional(),
|
|
13536
|
+
status: z3.string().optional(),
|
|
13537
|
+
query: z3.string().optional()
|
|
13538
|
+
}
|
|
13539
|
+
},
|
|
13540
|
+
async (args) => toolText(await guardedAsync(root, () => integrationsWorkItemsTool(root, args)))
|
|
13541
|
+
);
|
|
13542
|
+
server.registerTool(
|
|
13543
|
+
"kaddo_integrations_work_item",
|
|
13544
|
+
{
|
|
13545
|
+
title: "Read an external work item",
|
|
13546
|
+
description: `Read a single external work item by id. ${INTEGRATION_READ_NOTE}`,
|
|
13547
|
+
inputSchema: { id: z3.string(), externalId: z3.string() }
|
|
13548
|
+
},
|
|
13549
|
+
async (args) => toolText(await guardedAsync(root, () => integrationsWorkItemTool(root, args)))
|
|
13550
|
+
);
|
|
13047
13551
|
let installedSkills = [];
|
|
13048
13552
|
try {
|
|
13049
13553
|
assertKaddoProject(root);
|
package/package.json
CHANGED