kibi-mcp 0.19.2 → 0.21.1
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/diagnostics.js +100 -1
- package/dist/server/docs.js +13 -8
- package/dist/server/json-schema-to-zod.js +12 -0
- package/dist/server/tool-registration.js +2 -2
- package/dist/server/tools-runtime.js +2 -0
- package/dist/startup-resolution.js +26 -9
- package/dist/tools/check-prolog.js +1 -0
- package/dist/tools/coverage.js +4 -12
- package/dist/tools/discovery-adapter.js +5 -4
- package/dist/tools/status.js +6 -2
- package/package.json +3 -3
package/dist/diagnostics.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* the Free Software Foundation, either version 3 of the License, or
|
|
8
8
|
* (at your option) any later version.
|
|
9
9
|
*/
|
|
10
|
+
import { createHash } from "node:crypto";
|
|
10
11
|
import fs from "node:fs";
|
|
11
12
|
import path from "node:path";
|
|
12
13
|
import { resolveWorkspaceRoot } from "./workspace.js";
|
|
@@ -21,7 +22,14 @@ export function initializeDiagnosticMode(enabled = DIAGNOSTIC_MODE_ENABLED) {
|
|
|
21
22
|
return;
|
|
22
23
|
}
|
|
23
24
|
const workspaceRoot = resolveWorkspaceRoot();
|
|
24
|
-
|
|
25
|
+
// Evaluators may run an independent read-only verification client in the
|
|
26
|
+
// same fixture workspace after the model. Allow that client to use a
|
|
27
|
+
// separate receipt so model-originated usage can be reconciled without
|
|
28
|
+
// accidentally treating verifier calls as model evidence. Normal Kibi
|
|
29
|
+
// operation remains rooted at `.kb/usage.log`.
|
|
30
|
+
diagnosticUsageLogPath =
|
|
31
|
+
process.env.KIBI_MCP_DIAGNOSTIC_USAGE_LOG_PATH ??
|
|
32
|
+
path.join(workspaceRoot, ".kb", "usage.log");
|
|
25
33
|
process.env.KIBI_MCP_DIAGNOSTIC_MODE = "1";
|
|
26
34
|
}
|
|
27
35
|
export function appendUsageLogLine(entry) {
|
|
@@ -56,6 +64,14 @@ export const DIAGNOSTIC_TELEMETRY_SCHEMA = {
|
|
|
56
64
|
type: "string",
|
|
57
65
|
description: "If you had to split your task into multiple steps because this tool lacks a specific filtering or querying capability, describe what parameter is missing. Otherwise, leave empty.",
|
|
58
66
|
},
|
|
67
|
+
session_id: {
|
|
68
|
+
type: "string",
|
|
69
|
+
description: "Optional opaque workflow-session identifier used to correlate advisor, preflight, mutation, coverage, and verification evidence. Do not put personal data in this field.",
|
|
70
|
+
},
|
|
71
|
+
actor_id: {
|
|
72
|
+
type: "string",
|
|
73
|
+
description: "Optional opaque actor identifier used to prevent evidence from a different actor being correlated with this call. Do not put personal data in this field.",
|
|
74
|
+
},
|
|
59
75
|
},
|
|
60
76
|
};
|
|
61
77
|
function isRecord(value) {
|
|
@@ -96,6 +112,69 @@ function appendSemanticAdvisorFields(fields, receipt) {
|
|
|
96
112
|
fields.semantic_suggestion_kinds = suggestionKinds;
|
|
97
113
|
fields.semantic_suggestion_count = suggestionKinds.length;
|
|
98
114
|
fields.semantic_next_tools = stringArray(receipt.suggested_next_tools);
|
|
115
|
+
const inventoryContract = receipt.inventory_contract;
|
|
116
|
+
if (isRecord(inventoryContract) &&
|
|
117
|
+
typeof inventoryContract.source_hash === "string") {
|
|
118
|
+
fields.semantic_source_hash = inventoryContract.source_hash;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function canonicalDiagnosticValue(value) {
|
|
122
|
+
if (Array.isArray(value))
|
|
123
|
+
return value.map(canonicalDiagnosticValue);
|
|
124
|
+
if (!isRecord(value))
|
|
125
|
+
return value;
|
|
126
|
+
return Object.fromEntries(Object.entries(value)
|
|
127
|
+
.filter(([key]) => key !== "_diagnostic_telemetry")
|
|
128
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
129
|
+
.map(([key, child]) => [key, canonicalDiagnosticValue(child)]));
|
|
130
|
+
}
|
|
131
|
+
function mutationFingerprint(args) {
|
|
132
|
+
return createHash("sha256")
|
|
133
|
+
.update(JSON.stringify(canonicalDiagnosticValue(args)))
|
|
134
|
+
.digest("hex");
|
|
135
|
+
}
|
|
136
|
+
function appendMutationFields(fields, args) {
|
|
137
|
+
fields.mutation_fingerprint = mutationFingerprint(args);
|
|
138
|
+
const type = typeof args.type === "string" ? args.type : "unknown";
|
|
139
|
+
const id = typeof args.id === "string" ? args.id : "unknown";
|
|
140
|
+
fields.mutation_target = `${type}:${id}`;
|
|
141
|
+
}
|
|
142
|
+
const RECEIPT_GAP_CODES = new Set([
|
|
143
|
+
"missing_verification_receipt",
|
|
144
|
+
"stale_verification_receipt",
|
|
145
|
+
"failed_verification_receipt",
|
|
146
|
+
"invalid_verification_receipt",
|
|
147
|
+
"verification_snapshot_unavailable",
|
|
148
|
+
]);
|
|
149
|
+
function appendCoverageFields(fields, args, structuredContent) {
|
|
150
|
+
const rows = Array.isArray(structuredContent.rows)
|
|
151
|
+
? structuredContent.rows.filter(isRecord)
|
|
152
|
+
: [];
|
|
153
|
+
const summary = isRecord(structuredContent.summary)
|
|
154
|
+
? structuredContent.summary
|
|
155
|
+
: {};
|
|
156
|
+
const gapCodes = rows.flatMap((row) => stringArray(row.proofGaps));
|
|
157
|
+
const uniqueGapCodes = [...new Set(gapCodes)].sort();
|
|
158
|
+
const repairPlan = isRecord(structuredContent.repairPlan)
|
|
159
|
+
? structuredContent.repairPlan
|
|
160
|
+
: undefined;
|
|
161
|
+
const scope = isRecord(repairPlan?.scope) ? repairPlan.scope : undefined;
|
|
162
|
+
fields.coverage_by = typeof args.by === "string" ? args.by : "req";
|
|
163
|
+
fields.coverage_requirement_count = Number(summary.total ?? rows.length);
|
|
164
|
+
fields.coverage_proven_count = Number(summary.proofProven ?? 0);
|
|
165
|
+
fields.coverage_proof_missing_count = Number(summary.proofMissing ?? 0);
|
|
166
|
+
fields.coverage_proof_gap_count = gapCodes.length;
|
|
167
|
+
fields.coverage_gap_codes = uniqueGapCodes;
|
|
168
|
+
fields.coverage_receipt_gap_count = gapCodes.filter((gap) => RECEIPT_GAP_CODES.has(gap)).length;
|
|
169
|
+
if (typeof scope?.complete === "boolean") {
|
|
170
|
+
fields.coverage_scope_complete = scope.complete;
|
|
171
|
+
}
|
|
172
|
+
const metadata = isRecord(structuredContent.meta)
|
|
173
|
+
? structuredContent.meta
|
|
174
|
+
: undefined;
|
|
175
|
+
if (typeof metadata?.verificationSnapshot === "string") {
|
|
176
|
+
fields.coverage_verification_snapshot = metadata.verificationSnapshot;
|
|
177
|
+
}
|
|
99
178
|
}
|
|
100
179
|
function appendPredicateSuggestionFields(fields, structuredContent) {
|
|
101
180
|
const candidates = Array.isArray(structuredContent.candidates)
|
|
@@ -148,6 +227,12 @@ export function deriveDiagnosticFields(toolName, args, telemetry, result) {
|
|
|
148
227
|
fields.telemetry_is_autonomous = telemetry.is_autonomous ?? null;
|
|
149
228
|
fields.telemetry_confidence_score = telemetry.confidence_score ?? null;
|
|
150
229
|
fields.telemetry_attempt_number = telemetry.attempt_number ?? null;
|
|
230
|
+
if (typeof telemetry.session_id === "string") {
|
|
231
|
+
fields.session_id = telemetry.session_id;
|
|
232
|
+
}
|
|
233
|
+
if (typeof telemetry.actor_id === "string") {
|
|
234
|
+
fields.actor_id = telemetry.actor_id;
|
|
235
|
+
}
|
|
151
236
|
}
|
|
152
237
|
const structuredContent = structuredContentFrom(result);
|
|
153
238
|
if (toolName === "kb_query" || toolName === "kb_search") {
|
|
@@ -164,6 +249,10 @@ export function deriveDiagnosticFields(toolName, args, telemetry, result) {
|
|
|
164
249
|
fields.result_summary =
|
|
165
250
|
violationCount === 0 ? "0 violations" : `${violationCount} violations`;
|
|
166
251
|
}
|
|
252
|
+
if (toolName === "kb_coverage" && structuredContent) {
|
|
253
|
+
appendCoverageFields(fields, args, structuredContent);
|
|
254
|
+
fields.result_summary = `${String(fields.coverage_proven_count)} proven; ${String(fields.coverage_proof_gap_count)} proof gaps`;
|
|
255
|
+
}
|
|
167
256
|
if (toolName === "kb_semantic_advisor" && structuredContent) {
|
|
168
257
|
appendSemanticAdvisorFields(fields, structuredContent.receipt);
|
|
169
258
|
const readiness = fields.semantic_logic_readiness;
|
|
@@ -182,6 +271,7 @@ export function deriveDiagnosticFields(toolName, args, telemetry, result) {
|
|
|
182
271
|
: `${count} predicate candidates`;
|
|
183
272
|
}
|
|
184
273
|
if (toolName === "kb_upsert" && structuredContent) {
|
|
274
|
+
appendMutationFields(fields, args);
|
|
185
275
|
const created = Number(structuredContent.created ?? 0);
|
|
186
276
|
const updated = Number(structuredContent.updated ?? 0);
|
|
187
277
|
fields.upsert_created = created;
|
|
@@ -194,6 +284,15 @@ export function deriveDiagnosticFields(toolName, args, telemetry, result) {
|
|
|
194
284
|
fields.result_summary = `upsert ${created > 0 ? "created" : "updated"}; semantic ${readiness}`;
|
|
195
285
|
}
|
|
196
286
|
}
|
|
287
|
+
if (toolName === "kb_validate_upsert" && structuredContent) {
|
|
288
|
+
appendMutationFields(fields, args);
|
|
289
|
+
fields.validation_valid = structuredContent.valid === true;
|
|
290
|
+
appendSemanticAdvisorFields(fields, structuredContent.semanticAdvisor);
|
|
291
|
+
fields.result_summary =
|
|
292
|
+
structuredContent.valid === true
|
|
293
|
+
? "upsert payload valid"
|
|
294
|
+
: "upsert payload invalid";
|
|
295
|
+
}
|
|
197
296
|
if (!fields.result_summary) {
|
|
198
297
|
fields.result_summary = `${toolName} completed`;
|
|
199
298
|
}
|
package/dist/server/docs.js
CHANGED
|
@@ -84,8 +84,9 @@ export const PROMPTS = [
|
|
|
84
84
|
"## Rules",
|
|
85
85
|
"- Never apply bootstrap writes without user-facing preview and explicit approval.",
|
|
86
86
|
"- `kb_autopilot_generate` is strictly read-only; synthesis is the backend, not the actor.",
|
|
87
|
-
"-
|
|
88
|
-
"- If Kibi MCP tools are visible
|
|
87
|
+
"- MCP tools and the trusted project-local CLI are peer surfaces; choose by what is visible and approved here.",
|
|
88
|
+
"- If Kibi MCP tools are visible and approved, use MCP.",
|
|
89
|
+
"- Otherwise, in a trusted workspace, use the project-local CLI's dedicated JSON routes with `--input <file|->`.",
|
|
89
90
|
"- If neither interface is available, Kibi operation is blocked; tell the operator to enable MCP or the trusted project-local CLI. Do not infer MCP availability from config file existence.",
|
|
90
91
|
"- Do not read or edit `.kb/` files directly. Query before mutate. Run `kb_upsert` sequentially. Run `kb_check` before completion.",
|
|
91
92
|
].join("\n"),
|
|
@@ -103,7 +104,7 @@ export const PROMPTS = [
|
|
|
103
104
|
"- `kb_query`: Exact lookup of entities by type, ID, tags, or source file",
|
|
104
105
|
"- `kb_status`: Branch, snapshot, and freshness inspection",
|
|
105
106
|
"- `kb_find_gaps`: Bulk missing/present relationship analysis",
|
|
106
|
-
"- `kb_coverage`: Curated
|
|
107
|
+
"- `kb_coverage`: Curated proof reporting plus deterministic read-only repair batches for requirements",
|
|
107
108
|
"- `kb_graph`: Bounded graph traversal from seed IDs",
|
|
108
109
|
"- `kb_upsert`: Create or update entities and their relationships",
|
|
109
110
|
"- `kb_delete`: Remove entities by ID (with dependency safety checks)",
|
|
@@ -137,9 +138,11 @@ export const PROMPTS = [
|
|
|
137
138
|
"4. **Validate intent**: If creating links, call `kb_query` for both endpoint IDs first to ensure they exist.",
|
|
138
139
|
"5. **Model requirements as facts**: For new/updated reqs, create/reuse fact entities first, then express req semantics with `constrains` + `requires_property` (automated via `kb_model_requirement`).",
|
|
139
140
|
"6. **Suggest predicates before prose**: For ontology-lane requirements, spell out the prose claim and call `kb_suggest_predicates` before writing `fact_kind: observation`. Apply the selected `fact_kind: predicate` applyPlan, then attach the returned `relationshipPlan` as `requires_predicate` while preserving existing req metadata; use the returned `review:ontology-gap` observation when no predicate fits.",
|
|
140
|
-
"7. **
|
|
141
|
-
"8. **
|
|
142
|
-
"9. **
|
|
141
|
+
"7. **Plan repairs**: Requirement `kb_coverage` returns `repairPlan`; require `repairPlan.scope.complete`, apply only a batch whose state is `ready`, and treat every batch as non-auto-applicable guidance.",
|
|
142
|
+
"8. **Mutate sequentially**: Query current endpoints, validate each payload, then call `kb_upsert` one at a time or `kb_delete` for explicit removals. Re-run `kb_coverage` after each batch rather than continuing from a stale plan.",
|
|
143
|
+
"9. **Targeted checks**: Run `kb_check` after meaningful mutations; specify only the rules you need so scoped validation stays fast and skips the full-KB advisory scan.",
|
|
144
|
+
"10. **Final check**: Run `kb_check` without `rules` before completion so hard `violations[]` and advisory full-KB `qualityDiagnostics[]`—including telemetry acceptance when usage evidence exists—are both reviewed.",
|
|
145
|
+
"11. **Telemetry gate**: When `.kb/usage.log` exists, run the CLI-only `kibi usage-metrics --format json --require-acceptance`; stale, partial, or insufficient `kibi.telemetry-acceptance.v1` evidence is not a pass.",
|
|
143
146
|
"",
|
|
144
147
|
"If a tool returns empty results, do not assume failure. Re-check filters (type, id, tags, sourceFile, limit, or offset).",
|
|
145
148
|
].join("\n"),
|
|
@@ -216,8 +219,10 @@ function registerDocResources() {
|
|
|
216
219
|
"",
|
|
217
220
|
"## Find missing coverage",
|
|
218
221
|
'1. `kb_find_gaps` with `{ "type": "req", "missingRelationships": ["specified_by", "verified_by"] }` to find under-linked requirements',
|
|
219
|
-
'2. `kb_coverage` with `{ "by": "req", "includePassing": false }` to review
|
|
220
|
-
|
|
222
|
+
'2. `kb_coverage` with `{ "by": "req", "includePassing": false }` to review proof rows and `repairPlan`; require `repairPlan.scope.complete` before treating it as a project-wide inventory',
|
|
223
|
+
"3. Apply only a `repairPlan` batch whose state is `ready`; batches are non-auto-applicable guidance, so query current endpoints, validate every write, and keep `kb_upsert` sequential",
|
|
224
|
+
"4. Re-run `kb_coverage` after each batch instead of continuing from a stale plan",
|
|
225
|
+
'5. `kb_graph` with `{ "seedIds": ["REQ-001"], "direction": "both", "depth": 2 }` to inspect neighboring entities',
|
|
221
226
|
"",
|
|
222
227
|
"## Add a requirement and link it to a test",
|
|
223
228
|
'1. `kb_query` with `{ "type": "test" }` to check for existing test IDs',
|
|
@@ -41,6 +41,18 @@ export function jsonSchemaToZod(schema) {
|
|
|
41
41
|
return z.any();
|
|
42
42
|
}
|
|
43
43
|
const obj = schema;
|
|
44
|
+
if (Object.hasOwn(obj, "const")) {
|
|
45
|
+
const description = typeof obj.description === "string" ? obj.description : undefined;
|
|
46
|
+
const value = obj.const;
|
|
47
|
+
if (typeof value === "string" ||
|
|
48
|
+
typeof value === "number" ||
|
|
49
|
+
typeof value === "boolean" ||
|
|
50
|
+
value === null) {
|
|
51
|
+
const literal = z.literal(value);
|
|
52
|
+
return description ? literal.describe(description) : literal;
|
|
53
|
+
}
|
|
54
|
+
return description ? z.any().describe(description) : z.any();
|
|
55
|
+
}
|
|
44
56
|
if (Array.isArray(obj.enum) && obj.enum.length > 0) {
|
|
45
57
|
const description = typeof obj.description === "string" ? obj.description : undefined;
|
|
46
58
|
const literals = obj.enum.filter((value) => typeof value === "string" ||
|
|
@@ -39,7 +39,7 @@ export function registerConfiguredTools(server, runtime, registerTool) {
|
|
|
39
39
|
});
|
|
40
40
|
register({
|
|
41
41
|
name: "kb_status",
|
|
42
|
-
execute: async (context, args) => runtime.handleKbStatus(prologFor(context), args),
|
|
42
|
+
execute: async (context, args) => runtime.handleKbStatus(prologFor(context), args, context),
|
|
43
43
|
});
|
|
44
44
|
register({
|
|
45
45
|
name: "kb_skills_list",
|
|
@@ -59,7 +59,7 @@ export function registerConfiguredTools(server, runtime, registerTool) {
|
|
|
59
59
|
});
|
|
60
60
|
register({
|
|
61
61
|
name: "kb_coverage",
|
|
62
|
-
execute: async (context, args) => runtime.handleKbCoverage(prologFor(context), args),
|
|
62
|
+
execute: async (context, args) => runtime.handleKbCoverage(prologFor(context), args, context),
|
|
63
63
|
});
|
|
64
64
|
register({
|
|
65
65
|
name: "kb_graph",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getSpec } from "kibi-cli/operations";
|
|
2
|
+
import { nodeGit } from "kibi-cli/operations/node-ports";
|
|
2
3
|
import { DIAGNOSTIC_MODE_ENABLED, appendUsageLogLine, classifyDiagnosticError, deriveDiagnosticFields, extractToolCallPayload, } from "../diagnostics.js";
|
|
3
4
|
import { isMcpDebugEnabled } from "../env.js";
|
|
4
5
|
import { createMcpRuntime } from "../runtime/mcp-runtime.js";
|
|
@@ -71,6 +72,7 @@ const operationRuntime = createMcpRuntime({
|
|
|
71
72
|
attachedBranchKbPath: async () => (await getSessionModule()).attachedBranchKbPath,
|
|
72
73
|
ensureProlog: async () => (await getSessionModule()).ensureProlog(),
|
|
73
74
|
adaptProlog,
|
|
75
|
+
git: nodeGit,
|
|
74
76
|
net: { fetch: (input, init) => globalThis.fetch(input, init) },
|
|
75
77
|
refreshAttachedBranchStamp: async () => {
|
|
76
78
|
const session = await getSessionModule();
|
|
@@ -44,6 +44,20 @@ function nearestPackageJson(startPath) {
|
|
|
44
44
|
current = parent;
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
|
+
function nearestPackageJsonFromDirectory(startPath) {
|
|
48
|
+
let current = path.resolve(startPath);
|
|
49
|
+
while (true) {
|
|
50
|
+
const candidate = path.join(current, "package.json");
|
|
51
|
+
if (existsSync(candidate)) {
|
|
52
|
+
return candidate;
|
|
53
|
+
}
|
|
54
|
+
const parent = path.dirname(current);
|
|
55
|
+
if (parent === current) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
current = parent;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
47
61
|
function packageInfoFromEntrypoint(entrypoint) {
|
|
48
62
|
const normalizedEntrypoint = path.resolve(entrypoint);
|
|
49
63
|
const packageJsonPath = nearestPackageJson(normalizedEntrypoint);
|
|
@@ -62,7 +76,11 @@ export function readRunningPackageInfo(entrypointUrl) {
|
|
|
62
76
|
}
|
|
63
77
|
export function resolveProjectLocalMcp(cwd) {
|
|
64
78
|
try {
|
|
65
|
-
const
|
|
79
|
+
const projectPackageJson = nearestPackageJsonFromDirectory(cwd);
|
|
80
|
+
if (!projectPackageJson) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
const projectRequire = createRequire(projectPackageJson);
|
|
66
84
|
const resolved = projectRequire.resolve(PACKAGE_NAME);
|
|
67
85
|
const entrypoint = realpathSync.native?.(resolved) ?? realpathSync(resolved);
|
|
68
86
|
return packageInfoFromEntrypoint(entrypoint);
|
|
@@ -76,6 +94,7 @@ export function resolveProjectLocalMcp(cwd) {
|
|
|
76
94
|
}
|
|
77
95
|
}
|
|
78
96
|
export function compareMcpResolution(running, projectLocal) {
|
|
97
|
+
// implements REQ-cursor-agent-plugin-standard-v1
|
|
79
98
|
const diagnosticText = JSON.stringify({ running, projectLocal });
|
|
80
99
|
const forbiddenVersionObserved = diagnosticText.includes(FORBIDDEN_VERSION);
|
|
81
100
|
if (!projectLocal) {
|
|
@@ -92,16 +111,14 @@ export function compareMcpResolution(running, projectLocal) {
|
|
|
92
111
|
forbiddenVersionObserved,
|
|
93
112
|
};
|
|
94
113
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
};
|
|
101
|
-
}
|
|
114
|
+
// Same version: the running build is the version the project pins. Re-entering
|
|
115
|
+
// a same-version copy (e.g. a bun-store published install) abandons the local
|
|
116
|
+
// dev build for an identical-version store copy, which defeats local
|
|
117
|
+
// dogfooding and can silently drop unreleased local fixes. Only re-enter on a
|
|
118
|
+
// genuine version mismatch.
|
|
102
119
|
return {
|
|
103
120
|
stale: false,
|
|
104
|
-
reason: "running kibi-mcp matches project-local
|
|
121
|
+
reason: "running kibi-mcp matches the project-local version",
|
|
105
122
|
forbiddenVersionObserved,
|
|
106
123
|
};
|
|
107
124
|
}
|
|
@@ -30,6 +30,7 @@ export async function runAggregatedChecks(prolog, rulesAllowlist, requireAdr) {
|
|
|
30
30
|
description: v.description,
|
|
31
31
|
...(v.suggestion ? { suggestion: v.suggestion } : {}),
|
|
32
32
|
...(v.source ? { source: v.source } : {}),
|
|
33
|
+
...(v.evidence ? { evidence: v.evidence } : {}),
|
|
33
34
|
});
|
|
34
35
|
}
|
|
35
36
|
}
|
package/dist/tools/coverage.js
CHANGED
|
@@ -1,14 +1,6 @@
|
|
|
1
|
-
import { executeCoverage } from "kibi-cli/operations";
|
|
1
|
+
import { executeCoverage, } from "kibi-cli/operations";
|
|
2
|
+
import { createDiscoveryContext } from "./discovery-adapter.js";
|
|
2
3
|
// implements REQ-002, REQ-013
|
|
3
|
-
export async function handleKbCoverage(prolog, args) {
|
|
4
|
-
return executeCoverage({ ...args },
|
|
5
|
-
workspaceRoot: process.cwd(),
|
|
6
|
-
signal: new AbortController().signal,
|
|
7
|
-
clock: () => new Date(),
|
|
8
|
-
prolog: {
|
|
9
|
-
query: (goal) => prolog.query(goal),
|
|
10
|
-
nextSolution: async () => null,
|
|
11
|
-
save: () => prolog.query("kb_save"),
|
|
12
|
-
},
|
|
13
|
-
});
|
|
4
|
+
export async function handleKbCoverage(prolog, args, context) {
|
|
5
|
+
return executeCoverage({ ...args }, createDiscoveryContext(prolog, context));
|
|
14
6
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { resolveWorkspaceRoot } from "../workspace.js";
|
|
2
|
-
export function createDiscoveryContext(prolog) {
|
|
2
|
+
export function createDiscoveryContext(prolog, baseContext) {
|
|
3
3
|
// implements REQ-kibi-operation-interface-parity
|
|
4
4
|
let lastResult = null;
|
|
5
5
|
const port = {
|
|
@@ -15,9 +15,10 @@ export function createDiscoveryContext(prolog) {
|
|
|
15
15
|
save: () => prolog.query("kb_save"),
|
|
16
16
|
};
|
|
17
17
|
return {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
...(baseContext ?? {}),
|
|
19
|
+
workspaceRoot: baseContext?.workspaceRoot ?? resolveWorkspaceRoot(),
|
|
20
|
+
signal: baseContext?.signal ?? new AbortController().signal,
|
|
21
|
+
clock: baseContext?.clock ?? (() => new Date()),
|
|
21
22
|
prolog: port,
|
|
22
23
|
};
|
|
23
24
|
}
|
package/dist/tools/status.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { statusSpec } from "kibi-cli/operations";
|
|
2
2
|
import { createDiscoveryContext } from "./discovery-adapter.js";
|
|
3
|
-
export async function handleKbStatus(prolog, args) {
|
|
3
|
+
export async function handleKbStatus(prolog, args, context) {
|
|
4
4
|
// implements REQ-kibi-operation-interface-parity
|
|
5
|
-
|
|
5
|
+
// Freshness is read-after-write sensitive: invalidate the PrologProcess query
|
|
6
|
+
// cache so kb_status always reflects the current workspace state rather than
|
|
7
|
+
// a stale earlier-in-session result.
|
|
8
|
+
prolog.invalidateCache();
|
|
9
|
+
return statusSpec.execute(args, createDiscoveryContext(prolog, context));
|
|
6
10
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kibi-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.1",
|
|
4
4
|
"dependencies": {
|
|
5
5
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
6
6
|
"ajv": "^8.18.0",
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
"fast-glob": "^3.2.12",
|
|
10
10
|
"gray-matter": "^4.0.3",
|
|
11
11
|
"js-yaml": "^4.1.0",
|
|
12
|
-
"kibi-cli": "^0.
|
|
13
|
-
"kibi-core": "^0.
|
|
12
|
+
"kibi-cli": "^0.18.1",
|
|
13
|
+
"kibi-core": "^0.9.1",
|
|
14
14
|
"mcpcat": "^0.1.12",
|
|
15
15
|
"ts-morph": "^23.0.0",
|
|
16
16
|
"zod": "^4.3.6"
|