kibi-mcp 0.15.0 → 0.15.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/tools/upsert.js +114 -1
- package/dist/tools-config.js +14 -0
- package/package.json +6 -3
package/dist/tools/upsert.js
CHANGED
|
@@ -16,6 +16,9 @@
|
|
|
16
16
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
17
|
*/
|
|
18
18
|
import Ajv from "ajv";
|
|
19
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
20
|
+
import path from "node:path";
|
|
21
|
+
import { Project, ScriptKind } from "ts-morph";
|
|
19
22
|
import { escapeAtom, toPrologAtom, toPrologString, } from "kibi-cli/prolog/codec";
|
|
20
23
|
import entitySchema from "kibi-cli/schemas/entity";
|
|
21
24
|
import relationshipSchema from "kibi-cli/schemas/relationship";
|
|
@@ -23,8 +26,40 @@ import { isMcpDebugEnabled } from "../env.js";
|
|
|
23
26
|
import { refreshCoordinatesForSymbolId } from "./symbols.js";
|
|
24
27
|
let refreshCoordinatesForSymbolIdImpl = refreshCoordinatesForSymbolId;
|
|
25
28
|
const ajv = new Ajv({ strict: false });
|
|
26
|
-
const
|
|
29
|
+
const entitySchemaRecord = entitySchema;
|
|
30
|
+
const entitySchemaProperties = entitySchemaRecord.properties;
|
|
31
|
+
const normalizedEntitySchemaProperties = entitySchemaProperties !== null &&
|
|
32
|
+
typeof entitySchemaProperties === "object" &&
|
|
33
|
+
!Array.isArray(entitySchemaProperties)
|
|
34
|
+
? entitySchemaProperties
|
|
35
|
+
: {};
|
|
36
|
+
const validateEntity = ajv.compile({
|
|
37
|
+
...entitySchemaRecord,
|
|
38
|
+
properties: {
|
|
39
|
+
...normalizedEntitySchemaProperties,
|
|
40
|
+
granularity_reason: {
|
|
41
|
+
type: "string",
|
|
42
|
+
enum: [
|
|
43
|
+
"config-artifact",
|
|
44
|
+
"module-level-behavior",
|
|
45
|
+
"extractor-miss",
|
|
46
|
+
"legacy-link",
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
});
|
|
27
51
|
const validateRelationship = ajv.compile(relationshipSchema);
|
|
52
|
+
const TRACEABILITY_RELATIONSHIP_TYPES = new Set([
|
|
53
|
+
"implements",
|
|
54
|
+
"covered_by",
|
|
55
|
+
"executable_for",
|
|
56
|
+
]);
|
|
57
|
+
const ALLOWED_GRANULARITY_REASONS = new Set([
|
|
58
|
+
"config-artifact",
|
|
59
|
+
"module-level-behavior",
|
|
60
|
+
"extractor-miss",
|
|
61
|
+
"legacy-link",
|
|
62
|
+
]);
|
|
28
63
|
/**
|
|
29
64
|
* Handle kb.upsert tool calls
|
|
30
65
|
* Accepts { type, id, properties } — the flat format matching the tool schema.
|
|
@@ -76,6 +111,7 @@ export async function handleKbUpsert(prolog, args) {
|
|
|
76
111
|
}
|
|
77
112
|
}
|
|
78
113
|
validateRelationshipSources(id, relationships);
|
|
114
|
+
validateSymbolGranularity(entity, relationships);
|
|
79
115
|
// Validate strict-lane fact_kind pairing for constrains/requires_property
|
|
80
116
|
// implements REQ-011
|
|
81
117
|
await validateStrictLanePairing(prolog, relationships);
|
|
@@ -185,6 +221,83 @@ export async function handleKbUpsert(prolog, args) {
|
|
|
185
221
|
throw new Error(`Upsert execution failed: ${message}`);
|
|
186
222
|
}
|
|
187
223
|
}
|
|
224
|
+
function chooseScriptKind(filePath) {
|
|
225
|
+
const lower = filePath.toLowerCase();
|
|
226
|
+
if (lower.endsWith(".tsx"))
|
|
227
|
+
return ScriptKind.TSX;
|
|
228
|
+
if (lower.endsWith(".ts") || lower.endsWith(".mts") || lower.endsWith(".cts")) {
|
|
229
|
+
return ScriptKind.TS;
|
|
230
|
+
}
|
|
231
|
+
if (lower.endsWith(".jsx"))
|
|
232
|
+
return ScriptKind.JSX;
|
|
233
|
+
return ScriptKind.JS;
|
|
234
|
+
}
|
|
235
|
+
function hasTraceabilityRelationship(relationships) {
|
|
236
|
+
return relationships.some((relationship) => typeof relationship.type === "string" &&
|
|
237
|
+
TRACEABILITY_RELATIONSHIP_TYPES.has(relationship.type));
|
|
238
|
+
}
|
|
239
|
+
function hasAllowedGranularityReason(entity) {
|
|
240
|
+
const reason = entity.granularity_reason;
|
|
241
|
+
return (typeof reason === "string" && ALLOWED_GRANULARITY_REASONS.has(reason));
|
|
242
|
+
}
|
|
243
|
+
function collectNarrowExportNames(filePath, content) {
|
|
244
|
+
const project = new Project({ skipAddingFilesFromTsConfig: true });
|
|
245
|
+
const sourceFile = project.createSourceFile(`${filePath}::granularity`, content, {
|
|
246
|
+
overwrite: true,
|
|
247
|
+
scriptKind: chooseScriptKind(filePath),
|
|
248
|
+
});
|
|
249
|
+
const names = new Set();
|
|
250
|
+
for (const fn of sourceFile.getFunctions()) {
|
|
251
|
+
if (fn.isExported()) {
|
|
252
|
+
const name = fn.getName();
|
|
253
|
+
if (name)
|
|
254
|
+
names.add(name);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
for (const cls of sourceFile.getClasses()) {
|
|
258
|
+
if (cls.isExported()) {
|
|
259
|
+
const name = cls.getName();
|
|
260
|
+
if (name)
|
|
261
|
+
names.add(name);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
for (const iface of sourceFile.getInterfaces()) {
|
|
265
|
+
if (iface.isExported())
|
|
266
|
+
names.add(iface.getName());
|
|
267
|
+
}
|
|
268
|
+
for (const alias of sourceFile.getTypeAliases()) {
|
|
269
|
+
if (alias.isExported())
|
|
270
|
+
names.add(alias.getName());
|
|
271
|
+
}
|
|
272
|
+
for (const en of sourceFile.getEnums()) {
|
|
273
|
+
if (en.isExported())
|
|
274
|
+
names.add(en.getName());
|
|
275
|
+
}
|
|
276
|
+
return [...names].sort();
|
|
277
|
+
}
|
|
278
|
+
function validateSymbolGranularity(entity, relationships) {
|
|
279
|
+
if (entity.type !== "symbol")
|
|
280
|
+
return;
|
|
281
|
+
if (!hasTraceabilityRelationship(relationships))
|
|
282
|
+
return;
|
|
283
|
+
if (hasAllowedGranularityReason(entity))
|
|
284
|
+
return;
|
|
285
|
+
if (typeof entity.sourceFile !== "string")
|
|
286
|
+
return;
|
|
287
|
+
if (typeof entity.title !== "string")
|
|
288
|
+
return;
|
|
289
|
+
const sourcePath = path.isAbsolute(entity.sourceFile)
|
|
290
|
+
? entity.sourceFile
|
|
291
|
+
: path.resolve(process.cwd(), entity.sourceFile);
|
|
292
|
+
if (!existsSync(sourcePath))
|
|
293
|
+
return;
|
|
294
|
+
const narrowNames = collectNarrowExportNames(entity.sourceFile, readFileSync(sourcePath, "utf8"));
|
|
295
|
+
if (narrowNames.length === 0)
|
|
296
|
+
return;
|
|
297
|
+
if (narrowNames.includes(entity.title))
|
|
298
|
+
return;
|
|
299
|
+
throw new Error(`Symbol ${String(entity.id)} links ${entity.sourceFile} coarsely while granular symbols are available: ${narrowNames.join(", ")}. Move relationships to the narrow symbol or set granularity_reason to config-artifact, module-level-behavior, extractor-miss, or legacy-link.`);
|
|
300
|
+
}
|
|
188
301
|
export const __test__ = {
|
|
189
302
|
// implements REQ-vscode-traceability
|
|
190
303
|
setRefreshCoordinatesForSymbolIdForTests(fn) {
|
package/dist/tools-config.js
CHANGED
|
@@ -337,6 +337,20 @@ const BASE_TOOLS = [
|
|
|
337
337
|
type: "string",
|
|
338
338
|
description: "Optional text anchor/reference. Example: 'requirements.md#L40'.",
|
|
339
339
|
},
|
|
340
|
+
sourceFile: {
|
|
341
|
+
type: "string",
|
|
342
|
+
description: "Optional code source file for symbol entities. Example: 'src/auth/login.ts'.",
|
|
343
|
+
},
|
|
344
|
+
granularity_reason: {
|
|
345
|
+
type: "string",
|
|
346
|
+
enum: [
|
|
347
|
+
"config-artifact",
|
|
348
|
+
"module-level-behavior",
|
|
349
|
+
"extractor-miss",
|
|
350
|
+
"legacy-link",
|
|
351
|
+
],
|
|
352
|
+
description: "Optional justification for a coarse file/module-level symbol traceability relationship when narrower function/class/type symbols exist.",
|
|
353
|
+
},
|
|
340
354
|
},
|
|
341
355
|
required: ["title", "status"],
|
|
342
356
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kibi-mcp",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.1",
|
|
4
4
|
"dependencies": {
|
|
5
5
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
6
6
|
"ajv": "^8.18.0",
|
|
@@ -9,7 +9,7 @@
|
|
|
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.12.
|
|
12
|
+
"kibi-cli": "^0.12.1",
|
|
13
13
|
"kibi-core": "^0.6.0",
|
|
14
14
|
"mcpcat": "^0.1.12",
|
|
15
15
|
"ts-morph": "^23.0.0",
|
|
@@ -27,7 +27,10 @@
|
|
|
27
27
|
"build": "tsc -p tsconfig.json",
|
|
28
28
|
"prepack": "npm run build"
|
|
29
29
|
},
|
|
30
|
-
"files": [
|
|
30
|
+
"files": [
|
|
31
|
+
"dist",
|
|
32
|
+
"bin"
|
|
33
|
+
],
|
|
31
34
|
"engines": {
|
|
32
35
|
"node": ">=18",
|
|
33
36
|
"bun": ">=1.0"
|