kibi-mcp 0.20.1 → 0.22.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/diagnostics.js +92 -0
- package/dist/server/docs.js +10 -6
- package/dist/server/json-schema-to-zod.js +12 -0
- package/dist/server/kb-freshness.js +12 -2
- package/dist/server/session.js +33 -10
- package/dist/server/tool-registration.js +2 -2
- package/dist/server/tools-runtime.js +26 -0
- package/dist/tools/check-prolog.js +1 -0
- package/dist/tools/coverage.js +4 -12
- package/dist/tools/discovery-adapter.js +11 -4
- package/dist/tools/status.js +2 -2
- package/package.json +4 -5
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";
|
|
@@ -63,6 +64,14 @@ export const DIAGNOSTIC_TELEMETRY_SCHEMA = {
|
|
|
63
64
|
type: "string",
|
|
64
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.",
|
|
65
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
|
+
},
|
|
66
75
|
},
|
|
67
76
|
};
|
|
68
77
|
function isRecord(value) {
|
|
@@ -103,6 +112,69 @@ function appendSemanticAdvisorFields(fields, receipt) {
|
|
|
103
112
|
fields.semantic_suggestion_kinds = suggestionKinds;
|
|
104
113
|
fields.semantic_suggestion_count = suggestionKinds.length;
|
|
105
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
|
+
}
|
|
106
178
|
}
|
|
107
179
|
function appendPredicateSuggestionFields(fields, structuredContent) {
|
|
108
180
|
const candidates = Array.isArray(structuredContent.candidates)
|
|
@@ -155,6 +227,12 @@ export function deriveDiagnosticFields(toolName, args, telemetry, result) {
|
|
|
155
227
|
fields.telemetry_is_autonomous = telemetry.is_autonomous ?? null;
|
|
156
228
|
fields.telemetry_confidence_score = telemetry.confidence_score ?? null;
|
|
157
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
|
+
}
|
|
158
236
|
}
|
|
159
237
|
const structuredContent = structuredContentFrom(result);
|
|
160
238
|
if (toolName === "kb_query" || toolName === "kb_search") {
|
|
@@ -171,6 +249,10 @@ export function deriveDiagnosticFields(toolName, args, telemetry, result) {
|
|
|
171
249
|
fields.result_summary =
|
|
172
250
|
violationCount === 0 ? "0 violations" : `${violationCount} violations`;
|
|
173
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
|
+
}
|
|
174
256
|
if (toolName === "kb_semantic_advisor" && structuredContent) {
|
|
175
257
|
appendSemanticAdvisorFields(fields, structuredContent.receipt);
|
|
176
258
|
const readiness = fields.semantic_logic_readiness;
|
|
@@ -189,6 +271,7 @@ export function deriveDiagnosticFields(toolName, args, telemetry, result) {
|
|
|
189
271
|
: `${count} predicate candidates`;
|
|
190
272
|
}
|
|
191
273
|
if (toolName === "kb_upsert" && structuredContent) {
|
|
274
|
+
appendMutationFields(fields, args);
|
|
192
275
|
const created = Number(structuredContent.created ?? 0);
|
|
193
276
|
const updated = Number(structuredContent.updated ?? 0);
|
|
194
277
|
fields.upsert_created = created;
|
|
@@ -201,6 +284,15 @@ export function deriveDiagnosticFields(toolName, args, telemetry, result) {
|
|
|
201
284
|
fields.result_summary = `upsert ${created > 0 ? "created" : "updated"}; semantic ${readiness}`;
|
|
202
285
|
}
|
|
203
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
|
+
}
|
|
204
296
|
if (!fields.result_summary) {
|
|
205
297
|
fields.result_summary = `${toolName} completed`;
|
|
206
298
|
}
|
package/dist/server/docs.js
CHANGED
|
@@ -104,7 +104,7 @@ export const PROMPTS = [
|
|
|
104
104
|
"- `kb_query`: Exact lookup of entities by type, ID, tags, or source file",
|
|
105
105
|
"- `kb_status`: Branch, snapshot, and freshness inspection",
|
|
106
106
|
"- `kb_find_gaps`: Bulk missing/present relationship analysis",
|
|
107
|
-
"- `kb_coverage`: Curated
|
|
107
|
+
"- `kb_coverage`: Curated proof reporting plus deterministic read-only repair batches for requirements",
|
|
108
108
|
"- `kb_graph`: Bounded graph traversal from seed IDs",
|
|
109
109
|
"- `kb_upsert`: Create or update entities and their relationships",
|
|
110
110
|
"- `kb_delete`: Remove entities by ID (with dependency safety checks)",
|
|
@@ -138,9 +138,11 @@ export const PROMPTS = [
|
|
|
138
138
|
"4. **Validate intent**: If creating links, call `kb_query` for both endpoint IDs first to ensure they exist.",
|
|
139
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`).",
|
|
140
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.",
|
|
141
|
-
"7. **
|
|
142
|
-
"8. **
|
|
143
|
-
"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.",
|
|
144
146
|
"",
|
|
145
147
|
"If a tool returns empty results, do not assume failure. Re-check filters (type, id, tags, sourceFile, limit, or offset).",
|
|
146
148
|
].join("\n"),
|
|
@@ -217,8 +219,10 @@ function registerDocResources() {
|
|
|
217
219
|
"",
|
|
218
220
|
"## Find missing coverage",
|
|
219
221
|
'1. `kb_find_gaps` with `{ "type": "req", "missingRelationships": ["specified_by", "verified_by"] }` to find under-linked requirements',
|
|
220
|
-
'2. `kb_coverage` with `{ "by": "req", "includePassing": false }` to review
|
|
221
|
-
|
|
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',
|
|
222
226
|
"",
|
|
223
227
|
"## Add a requirement and link it to a test",
|
|
224
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" ||
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { stat } from "node:fs/promises";
|
|
1
|
+
import { readFile, stat } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
export class KbRefreshError extends Error {
|
|
4
4
|
constructor(message) {
|
|
@@ -21,7 +21,17 @@ export async function readBranchKbStamp(branchPath) {
|
|
|
21
21
|
stamp.dirMissing = true;
|
|
22
22
|
errors.push(formatStatError(branchPath, error));
|
|
23
23
|
}
|
|
24
|
-
const
|
|
24
|
+
const markerPath = path.join(branchPath, "storage.json");
|
|
25
|
+
let journaled = false;
|
|
26
|
+
try {
|
|
27
|
+
journaled = (await readFile(markerPath, "utf8")).includes("kibi.rdf-journal.v1");
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
journaled = false;
|
|
31
|
+
}
|
|
32
|
+
// Journaled stores expose CURRENT as their cheap, atomic freshness marker;
|
|
33
|
+
// legacy stores retain kb.rdf for compatibility.
|
|
34
|
+
const rdfPath = path.join(branchPath, journaled ? "CURRENT" : "kb.rdf");
|
|
25
35
|
try {
|
|
26
36
|
const rdfStat = await stat(rdfPath);
|
|
27
37
|
stamp.rdfDev = rdfStat.dev;
|
package/dist/server/session.js
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
import fs from "node:fs";
|
|
19
19
|
import { createRequire } from "node:module";
|
|
20
20
|
import process from "node:process";
|
|
21
|
+
import { EngineClient } from "kibi-cli/engine";
|
|
21
22
|
import { PrologProcess } from "kibi-cli/prolog";
|
|
22
23
|
import { copyCleanSnapshot, getBranchDiagnostic, isValidBranchName, resolveActiveBranch, } from "kibi-cli/public/branch-resolver";
|
|
23
24
|
import { getBranchOverride, isMcpDebugEnabled } from "../env.js";
|
|
@@ -202,6 +203,7 @@ function usesBranchKbPath(kbPath) {
|
|
|
202
203
|
async function ensurePrologUnsafe() {
|
|
203
204
|
const generationAtStart = prologResetGeneration;
|
|
204
205
|
const workspaceRoot = sessionDeps.resolveWorkspaceRoot();
|
|
206
|
+
const useEngine = sessionDeps.PrologProcess === defaultSessionDeps.PrologProcess;
|
|
205
207
|
const assertGeneration = async () => {
|
|
206
208
|
if (generationAtStart !== prologResetGeneration) {
|
|
207
209
|
const current = prologProcess;
|
|
@@ -236,6 +238,19 @@ async function ensurePrologUnsafe() {
|
|
|
236
238
|
targetBranch = branchResult.branch;
|
|
237
239
|
}
|
|
238
240
|
// Check if we need to switch branches
|
|
241
|
+
if (isInitialized && useEngine && prologProcess?.isRunning()) {
|
|
242
|
+
const kbPath = sessionDeps.resolveKbPath(workspaceRoot, targetBranch);
|
|
243
|
+
if (targetBranch === activeBranchName) {
|
|
244
|
+
attachedBranchKbPath = kbPath;
|
|
245
|
+
attachedBranchStamp = await readBranchKbStamp(kbPath);
|
|
246
|
+
return prologProcess;
|
|
247
|
+
}
|
|
248
|
+
await prologProcess.terminate();
|
|
249
|
+
prologProcess = null;
|
|
250
|
+
isInitialized = false;
|
|
251
|
+
attachedBranchKbPath = null;
|
|
252
|
+
attachedBranchStamp = null;
|
|
253
|
+
}
|
|
239
254
|
if (isInitialized && prologProcess?.isRunning()) {
|
|
240
255
|
const kbPath = sessionDeps.resolveKbPath(workspaceRoot, targetBranch);
|
|
241
256
|
if (targetBranch === activeBranchName) {
|
|
@@ -298,7 +313,13 @@ async function ensurePrologUnsafe() {
|
|
|
298
313
|
}
|
|
299
314
|
// First initialization
|
|
300
315
|
debugLog("[KIBI-MCP] Initializing Prolog process...");
|
|
301
|
-
prologProcess =
|
|
316
|
+
prologProcess = useEngine
|
|
317
|
+
? new EngineClient({
|
|
318
|
+
workspaceRoot,
|
|
319
|
+
branch: targetBranch,
|
|
320
|
+
timeout: 120000,
|
|
321
|
+
})
|
|
322
|
+
: new sessionDeps.PrologProcess({ timeout: 120000 });
|
|
302
323
|
await prologProcess.start();
|
|
303
324
|
await assertGeneration();
|
|
304
325
|
// Startup debug: resolve which kibi-cli is being used and its version (best-effort).
|
|
@@ -340,16 +361,18 @@ async function ensurePrologUnsafe() {
|
|
|
340
361
|
activeBranchName = targetBranch;
|
|
341
362
|
const createdEmptyBranch = ensureBranchKbExists(workspaceRoot, targetBranch);
|
|
342
363
|
const kbPath = sessionDeps.resolveKbPath(workspaceRoot, targetBranch);
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
if (!attachResult.success) {
|
|
346
|
-
throw new Error(`Failed to attach KB: ${attachResult.error || "Unknown error"}`);
|
|
347
|
-
}
|
|
348
|
-
if (createdEmptyBranch) {
|
|
349
|
-
const initialSaveResult = await prologProcess.query("kb_save");
|
|
364
|
+
if (!useEngine) {
|
|
365
|
+
const attachResult = await prologProcess.query(`kb_attach('${kbPath}')`);
|
|
350
366
|
await assertGeneration();
|
|
351
|
-
if (!
|
|
352
|
-
throw new Error(`Failed to
|
|
367
|
+
if (!attachResult.success) {
|
|
368
|
+
throw new Error(`Failed to attach KB: ${attachResult.error || "Unknown error"}`);
|
|
369
|
+
}
|
|
370
|
+
if (createdEmptyBranch) {
|
|
371
|
+
const initialSaveResult = await prologProcess.query("kb_save");
|
|
372
|
+
await assertGeneration();
|
|
373
|
+
if (!initialSaveResult.success) {
|
|
374
|
+
throw new Error(`Failed to initialize empty branch KB: ${initialSaveResult.error || "Unknown error"}`);
|
|
375
|
+
}
|
|
353
376
|
}
|
|
354
377
|
}
|
|
355
378
|
attachedBranchKbPath = kbPath;
|
|
@@ -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";
|
|
@@ -61,6 +62,30 @@ function adaptProlog(prolog) {
|
|
|
61
62
|
return result;
|
|
62
63
|
},
|
|
63
64
|
save: () => prolog.query("kb_save"),
|
|
65
|
+
...(typeof prolog.queryEntities ===
|
|
66
|
+
"function"
|
|
67
|
+
? {
|
|
68
|
+
queryEntities: (input) => prolog.queryEntities(input),
|
|
69
|
+
}
|
|
70
|
+
: {}),
|
|
71
|
+
...(typeof prolog.searchEntities ===
|
|
72
|
+
"function"
|
|
73
|
+
? {
|
|
74
|
+
searchEntities: (input) => prolog.searchEntities(input),
|
|
75
|
+
}
|
|
76
|
+
: {}),
|
|
77
|
+
...(typeof prolog.storageStatus ===
|
|
78
|
+
"function"
|
|
79
|
+
? {
|
|
80
|
+
storageStatus: () => prolog.storageStatus(),
|
|
81
|
+
}
|
|
82
|
+
: {}),
|
|
83
|
+
...(typeof prolog.queryStatusJson ===
|
|
84
|
+
"function"
|
|
85
|
+
? {
|
|
86
|
+
queryStatusJson: () => prolog.queryStatusJson(),
|
|
87
|
+
}
|
|
88
|
+
: {}),
|
|
64
89
|
};
|
|
65
90
|
prologPorts.set(prolog, port);
|
|
66
91
|
return port;
|
|
@@ -71,6 +96,7 @@ const operationRuntime = createMcpRuntime({
|
|
|
71
96
|
attachedBranchKbPath: async () => (await getSessionModule()).attachedBranchKbPath,
|
|
72
97
|
ensureProlog: async () => (await getSessionModule()).ensureProlog(),
|
|
73
98
|
adaptProlog,
|
|
99
|
+
git: nodeGit,
|
|
74
100
|
net: { fetch: (input, init) => globalThis.fetch(input, init) },
|
|
75
101
|
refreshAttachedBranchStamp: async () => {
|
|
76
102
|
const session = await getSessionModule();
|
|
@@ -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 = {
|
|
@@ -13,11 +13,18 @@ export function createDiscoveryContext(prolog) {
|
|
|
13
13
|
return result;
|
|
14
14
|
},
|
|
15
15
|
save: () => prolog.query("kb_save"),
|
|
16
|
+
...(typeof prolog.queryStatusJson ===
|
|
17
|
+
"function"
|
|
18
|
+
? {
|
|
19
|
+
queryStatusJson: () => prolog.queryStatusJson(),
|
|
20
|
+
}
|
|
21
|
+
: {}),
|
|
16
22
|
};
|
|
17
23
|
return {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
24
|
+
...(baseContext ?? {}),
|
|
25
|
+
workspaceRoot: baseContext?.workspaceRoot ?? resolveWorkspaceRoot(),
|
|
26
|
+
signal: baseContext?.signal ?? new AbortController().signal,
|
|
27
|
+
clock: baseContext?.clock ?? (() => new Date()),
|
|
21
28
|
prolog: port,
|
|
22
29
|
};
|
|
23
30
|
}
|
package/dist/tools/status.js
CHANGED
|
@@ -1,10 +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
6
|
// cache so kb_status always reflects the current workspace state rather than
|
|
7
7
|
// a stale earlier-in-session result.
|
|
8
8
|
prolog.invalidateCache();
|
|
9
|
-
return statusSpec.execute(args, createDiscoveryContext(prolog));
|
|
9
|
+
return statusSpec.execute(args, createDiscoveryContext(prolog, context));
|
|
10
10
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kibi-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.0",
|
|
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.19.0",
|
|
13
|
+
"kibi-core": "^0.10.0",
|
|
14
14
|
"mcpcat": "^0.1.12",
|
|
15
15
|
"ts-morph": "^23.0.0",
|
|
16
16
|
"zod": "^4.3.6"
|
|
@@ -30,8 +30,7 @@
|
|
|
30
30
|
},
|
|
31
31
|
"files": ["dist", "bin"],
|
|
32
32
|
"engines": {
|
|
33
|
-
"node": ">=18"
|
|
34
|
-
"bun": ">=1.0"
|
|
33
|
+
"node": ">=18"
|
|
35
34
|
},
|
|
36
35
|
"exports": {
|
|
37
36
|
".": "./dist/server.js"
|