kibi-mcp 0.17.5 → 0.19.2

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.
Files changed (43) hide show
  1. package/bin/kibi-mcp +1 -1
  2. package/dist/diagnostics-helpers.js +22 -0
  3. package/dist/diagnostics.js +113 -4
  4. package/dist/runtime/mcp-runtime.js +43 -0
  5. package/dist/server/docs.js +11 -10
  6. package/dist/server/json-schema-to-zod.js +73 -2
  7. package/dist/server/session.js +33 -15
  8. package/dist/server/tool-registration.js +35 -59
  9. package/dist/server/tools-runtime.js +52 -2
  10. package/dist/server/tools.js +19 -5
  11. package/dist/server/transport.js +9 -5
  12. package/dist/tools/autopilot-generate.js +37 -646
  13. package/dist/tools/autopilot-test-compat.js +88 -0
  14. package/dist/tools/autopilot-test-parsers.js +120 -0
  15. package/dist/tools/check-config.js +67 -0
  16. package/dist/tools/check-format.js +92 -0
  17. package/dist/tools/check-impact.js +45 -0
  18. package/dist/tools/check-prolog.js +51 -0
  19. package/dist/tools/check.js +13 -190
  20. package/dist/tools/coverage.js +11 -25
  21. package/dist/tools/delete.js +5 -112
  22. package/dist/tools/discovery-adapter.js +23 -0
  23. package/dist/tools/entity-query.js +1 -111
  24. package/dist/tools/find-gaps.js +11 -26
  25. package/dist/tools/graph.js +11 -24
  26. package/dist/tools/model-requirement.js +8 -347
  27. package/dist/tools/mutation-context.js +28 -0
  28. package/dist/tools/query-plan-safety.js +58 -0
  29. package/dist/tools/query.js +5 -50
  30. package/dist/tools/relationship-validation.js +11 -0
  31. package/dist/tools/search.js +4 -37
  32. package/dist/tools/semantic-advisor.js +2 -40
  33. package/dist/tools/skills.js +25 -47
  34. package/dist/tools/sparql.js +12 -93
  35. package/dist/tools/status.js +5 -19
  36. package/dist/tools/suggest-predicates.js +11 -2448
  37. package/dist/tools/upsert.js +21 -794
  38. package/dist/tools/validate-upsert.js +16 -40
  39. package/dist/tools-config.js +75 -764
  40. package/package.json +4 -3
  41. package/dist/semantic-advisor/analyze-prose.js +0 -1367
  42. package/dist/semantic-advisor/prose-coverage-evaluator.js +0 -66
  43. /package/dist/{semantic-advisor/types.js → tools/check-types.js} +0 -0
@@ -1,28 +1,14 @@
1
- import { runJsonModuleQuery, toPrologList } from "./core-module.js";
1
+ import { executeCoverage } from "kibi-cli/operations";
2
2
  // implements REQ-002, REQ-013
3
3
  export async function handleKbCoverage(prolog, args) {
4
- const by = args.by ?? "req";
5
- const limit = args.limit ?? 100;
6
- const offset = args.offset ?? 0;
7
- const includePassing = args.includePassing ?? false;
8
- const includeTransitive = args.includeTransitive ?? true;
9
- try {
10
- const payload = await runJsonModuleQuery(prolog, "discovery.pl", `discovery:coverage_report_json('${by}', ${toPrologList(args.tags)}, ${includePassing}, ${includeTransitive}, ${limit}, ${offset}, JsonString)`, "Coverage execution");
11
- const summary = payload?.summary ?? {};
12
- const fullyCovered = Number(summary.fullyCovered ?? 0);
13
- const total = Number(summary.total ?? 0);
14
- return {
15
- content: [
16
- {
17
- type: "text",
18
- text: `Coverage summary: ${fullyCovered} fully covered out of ${total}.`,
19
- },
20
- ],
21
- ...(payload !== undefined ? { structuredContent: payload } : {}),
22
- };
23
- }
24
- catch (error) {
25
- const message = error instanceof Error ? error.message : String(error);
26
- throw new Error(`Coverage execution failed: ${message}`);
27
- }
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
+ });
28
14
  }
@@ -1,113 +1,6 @@
1
- import { escapeAtom, parseEntityFromList, parseListOfLists, } from "kibi-cli/prolog/codec";
2
- /**
3
- * Handle kb.delete tool calls
4
- * Prevents deletion of entities with dependents (referential integrity)
5
- */
6
- export async function handleKbDelete(
7
- // implements REQ-002, REQ-011
8
- prolog, args) {
9
- const { ids } = args;
10
- if (!ids || ids.length === 0) {
11
- throw new Error("At least one ID required for delete");
12
- }
13
- let deleted = 0;
14
- let skipped = 0;
15
- const errors = [];
16
- try {
17
- for (const id of ids) {
18
- const safeId = escapeAtom(id);
19
- // Check if entity exists
20
- const checkGoal = `once(kb_entity('${safeId}', _, _))`;
21
- const checkResult = await prolog.query(checkGoal);
22
- if (!checkResult.success) {
23
- errors.push(`Entity ${id} does not exist`);
24
- skipped++;
25
- continue;
26
- }
27
- // Check for dependents (entities that reference this one)
28
- const dependentsGoal = `findall([RelType,From], (member(RelType, [depends_on, verified_by, validates, specified_by, relates_to, guards, publishes, consumes]), kb_relationship(RelType, From, '${safeId}')), Dependents)`;
29
- const dependentsResult = await prolog.query(dependentsGoal);
30
- if (!dependentsResult.success) {
31
- errors.push(`Failed to inspect dependents for entity ${id}: ${dependentsResult.error ?? "Query failed"}`);
32
- skipped++;
33
- continue;
34
- }
35
- const hasDependents = dependentsResult.bindings.Dependents !== undefined &&
36
- dependentsResult.bindings.Dependents !== "[]";
37
- if (hasDependents) {
38
- errors.push(`Cannot delete entity ${id}: has dependents (other entities reference it)`);
39
- skipped++;
40
- }
41
- if (hasDependents) {
42
- continue;
43
- }
44
- // No dependents, safe to delete
45
- const entityMetadata = await loadEntityMetadataForDelete(prolog, id, safeId);
46
- const deleteGoal = buildDeleteGoal(safeId, entityMetadata);
47
- const deleteResult = await prolog.query(deleteGoal);
48
- if (!deleteResult.success) {
49
- errors.push(`Failed to delete entity ${id}: ${deleteResult.error || "Unknown error"}`);
50
- skipped++;
51
- }
52
- else {
53
- deleted++;
54
- }
55
- }
56
- // Save KB to disk
57
- const saveResult = await prolog.query("kb_save");
58
- if (!saveResult.success) {
59
- throw new Error(`Failed to save KB after delete: ${saveResult.error || "Unknown error"}`);
60
- }
61
- prolog.invalidateCache();
62
- return {
63
- content: [
64
- {
65
- type: "text",
66
- text: `Deleted ${deleted} entities. Skipped ${skipped}. ${errors.length > 0 ? `Errors: ${errors.join("; ")}` : ""}`,
67
- },
68
- ],
69
- structuredContent: {
70
- deleted,
71
- skipped,
72
- errors,
73
- },
74
- };
75
- }
76
- catch (error) {
77
- const message = error instanceof Error ? error.message : String(error);
78
- throw new Error(`Delete execution failed: ${message}`);
79
- }
80
- }
81
- async function loadEntityMetadataForDelete(prolog, id, safeId) {
82
- const result = await prolog.query(`findall(['${safeId}',Type,Props], kb_entity('${safeId}', Type, Props), Results)`);
83
- if (!result.success) {
84
- throw new Error(`Failed to load metadata for entity ${id}: ${result.error || "Unknown error"}`);
85
- }
86
- const rows = result.bindings.Results
87
- ? parseListOfLists(result.bindings.Results)
88
- : [];
89
- if (rows.length === 0) {
90
- throw new Error(`Failed to load metadata for entity ${id}: Entity not found`);
91
- }
92
- const entity = parseEntityFromList(rows[0] ?? []);
93
- const type = String(entity.type ?? "unknown");
94
- const { id: _entityId, type: _entityType, ...props } = entity;
95
- return { type, props };
96
- }
97
- function buildDeleteGoal(safeId, metadata) {
98
- const auditProps = [
99
- `id='${safeId}'`,
100
- ...serializeDeleteProps(metadata.props),
101
- ];
102
- return `kb_retract_entity('${safeId}', ${metadata.type}, [${auditProps.join(", ")}])`;
103
- }
104
- function serializeDeleteProps(props) {
105
- const orderedKeys = ["title", "source", "text_ref"];
106
- return orderedKeys.flatMap((key) => {
107
- const value = props[key];
108
- if (typeof value !== "string") {
109
- return [];
110
- }
111
- return `${key}=${JSON.stringify(value)}`;
112
- });
1
+ import { deleteSpec } from "kibi-cli/operations";
2
+ import { createMutationContext } from "./mutation-context.js";
3
+ // implements REQ-kibi-operation-interface-parity
4
+ export async function handleKbDelete(prolog, args) {
5
+ return deleteSpec.execute(args, createMutationContext(prolog));
113
6
  }
@@ -0,0 +1,23 @@
1
+ import { resolveWorkspaceRoot } from "../workspace.js";
2
+ export function createDiscoveryContext(prolog) {
3
+ // implements REQ-kibi-operation-interface-parity
4
+ let lastResult = null;
5
+ const port = {
6
+ query: async (goal) => {
7
+ lastResult = await prolog.query(goal);
8
+ return lastResult;
9
+ },
10
+ nextSolution: async () => {
11
+ const result = lastResult;
12
+ lastResult = null;
13
+ return result;
14
+ },
15
+ save: () => prolog.query("kb_save"),
16
+ };
17
+ return {
18
+ workspaceRoot: resolveWorkspaceRoot(),
19
+ signal: new AbortController().signal,
20
+ clock: () => new Date(),
21
+ prolog: port,
22
+ };
23
+ }
@@ -1,111 +1 @@
1
- import { escapeAtomContent, parseEntityFromBinding, parseEntityFromList, parseListOfLists, } from "kibi-cli/prolog/codec";
2
- export const VALID_ENTITY_TYPES = [
3
- // implements REQ-002
4
- "req",
5
- "scenario",
6
- "test",
7
- "adr",
8
- "flag",
9
- "event",
10
- "symbol",
11
- "fact",
12
- ];
13
- // implements REQ-002, REQ-013
14
- export function validateEntityType(type) {
15
- if (type && !VALID_ENTITY_TYPES.includes(type)) {
16
- throw new Error(`Invalid type '${type}'. Valid types: ${VALID_ENTITY_TYPES.join(", ")}. Use a single type value, or omit this parameter to query all entities.`);
17
- }
18
- }
19
- // implements REQ-002, REQ-013
20
- export function buildEntityGoal(args) {
21
- const { type, id, tags, sourceFile } = args;
22
- if (sourceFile) {
23
- const safeSource = escapeAtomContent(sourceFile);
24
- if (type) {
25
- const safeType = escapeAtomContent(type);
26
- return `findall([Id,'${safeType}',Props], (kb_entities_by_source('${safeSource}', SourceIds), member(Id, SourceIds), kb_entity(Id, '${safeType}', Props)), Results)`;
27
- }
28
- return `findall([Id,Type,Props], (kb_entities_by_source('${safeSource}', SourceIds), member(Id, SourceIds), kb_entity(Id, Type, Props)), Results)`;
29
- }
30
- if (id && type) {
31
- const safeId = escapeAtomContent(id);
32
- const safeType = escapeAtomContent(type);
33
- return `findall(['${safeId}','${safeType}',Props], kb_entity('${safeId}', '${safeType}', Props), Results)`;
34
- }
35
- if (id) {
36
- const safeId = escapeAtomContent(id);
37
- return `findall(['${safeId}',Type,Props], kb_entity('${safeId}', Type, Props), Results)`;
38
- }
39
- if (tags && tags.length > 0) {
40
- if (type) {
41
- const safeType = escapeAtomContent(type);
42
- return `findall([Id,'${safeType}',Props], kb_entity(Id, '${safeType}', Props), Results)`;
43
- }
44
- return "findall([Id,Type,Props], kb_entity(Id, Type, Props), Results)";
45
- }
46
- if (type) {
47
- const safeType = escapeAtomContent(type);
48
- return `findall([Id,'${safeType}',Props], kb_entity(Id, '${safeType}', Props), Results)`;
49
- }
50
- return "findall([Id,Type,Props], kb_entity(Id, Type, Props), Results)";
51
- }
52
- // implements REQ-002, REQ-013
53
- export async function loadEntities(prolog, args) {
54
- validateEntityType(args.type);
55
- const goal = buildEntityGoal(args);
56
- const queryResult = await prolog.query(goal);
57
- let results = [];
58
- if (!queryResult.success) {
59
- throw new Error(queryResult.error || "Query failed with unknown error");
60
- }
61
- if (queryResult.bindings.Results) {
62
- const entitiesData = parseListOfLists(queryResult.bindings.Results);
63
- for (const data of entitiesData) {
64
- results.push(parseEntityFromList(data));
65
- }
66
- }
67
- else if (queryResult.bindings.Result) {
68
- results = [parseEntityFromBinding(queryResult.bindings.Result)];
69
- }
70
- if (args.tags && args.tags.length > 0) {
71
- const requestedTags = args.tags;
72
- results = dedupeEntities(results.filter((entity) => hasAnyTag(entity, requestedTags)));
73
- }
74
- return dedupeEntities(results);
75
- }
76
- // implements REQ-002
77
- export function paginateResults(results, limit = 100, offset = 0) {
78
- return results.slice(offset, offset + limit);
79
- }
80
- function hasAnyTag(entity, requestedTags) {
81
- const expected = new Set(requestedTags.map(normalizeTagValue));
82
- const rawTags = entity.tags;
83
- if (!Array.isArray(rawTags) || rawTags.length === 0) {
84
- return false;
85
- }
86
- for (const tag of rawTags) {
87
- if (expected.has(normalizeTagValue(tag))) {
88
- return true;
89
- }
90
- }
91
- return false;
92
- }
93
- function normalizeTagValue(tag) {
94
- return String(tag).trim();
95
- }
96
- // implements REQ-002
97
- export function dedupeEntities(entities) {
98
- const seen = new Set();
99
- const deduped = [];
100
- for (const entity of entities) {
101
- const id = String(entity.id ?? "");
102
- const type = String(entity.type ?? "");
103
- const key = `${type}::${id}`;
104
- if (seen.has(key)) {
105
- continue;
106
- }
107
- seen.add(key);
108
- deduped.push(entity);
109
- }
110
- return deduped;
111
- }
1
+ export { VALID_ENTITY_TYPES, buildEntityGoal, dedupeEntities, loadEntities, paginateResults, validateEntityType, } from "kibi-cli/operations";
@@ -1,29 +1,14 @@
1
- import { runJsonModuleQuery, toPrologAtom, toPrologList, } from "./core-module.js";
2
- import { validateEntityType } from "./entity-query.js";
1
+ import { executeFindGaps } from "kibi-cli/operations";
3
2
  // implements REQ-002, REQ-013
4
3
  export async function handleKbFindGaps(prolog, args) {
5
- validateEntityType(args.type);
6
- const limit = args.limit ?? 100;
7
- const offset = args.offset ?? 0;
8
- try {
9
- const payload = await runJsonModuleQuery(prolog, "discovery.pl", `discovery:find_gaps_json(${toPrologAtom(args.type)}, ${toPrologList(args.missingRelationships)}, ${toPrologList(args.presentRelationships)}, ${toPrologList(args.tags)}, ${toPrologAtom(args.sourceFile)}, ${limit}, ${offset}, JsonString)`, "Find-gaps execution");
10
- const rows = payload?.rows ?? [];
11
- return {
12
- content: [
13
- {
14
- type: "text",
15
- text: rows.length === 0
16
- ? "No matching gaps found."
17
- : `Found ${payload?.count ?? rows.length} gap rows. Showing ${rows.length}: ${rows
18
- .map((row) => row.id)
19
- .join(", ")}`,
20
- },
21
- ],
22
- ...(payload !== undefined ? { structuredContent: payload } : {}),
23
- };
24
- }
25
- catch (error) {
26
- const message = error instanceof Error ? error.message : String(error);
27
- throw new Error(`Find-gaps execution failed: ${message}`);
28
- }
4
+ return executeFindGaps({ ...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
+ });
29
14
  }
@@ -1,27 +1,14 @@
1
- import { runJsonModuleQuery, toPrologList } from "./core-module.js";
1
+ import { executeGraph } from "kibi-cli/operations";
2
2
  // implements REQ-002, REQ-013
3
3
  export async function handleKbGraph(prolog, args) {
4
- const direction = args.direction ?? "outgoing";
5
- const depth = args.depth ?? 1;
6
- const maxNodes = args.maxNodes ?? 200;
7
- const maxEdges = args.maxEdges ?? 500;
8
- try {
9
- const payload = await runJsonModuleQuery(prolog, "discovery.pl", `discovery:graph_expand_json(${toPrologList(args.seedIds)}, ${toPrologList(args.relationships)}, '${direction}', ${depth}, ${toPrologList(args.entityTypes)}, ${maxNodes}, ${maxEdges}, JsonString)`, "Graph execution");
10
- const nodes = payload?.nodes ?? [];
11
- return {
12
- content: [
13
- {
14
- type: "text",
15
- text: nodes.length === 0
16
- ? "Graph traversal returned no nodes."
17
- : `Graph traversal returned ${nodes.length} nodes and ${(payload?.edges ?? []).length} edges from ${args.seedIds.join(", ")}.`,
18
- },
19
- ],
20
- ...(payload !== undefined ? { structuredContent: payload } : {}),
21
- };
22
- }
23
- catch (error) {
24
- const message = error instanceof Error ? error.message : String(error);
25
- throw new Error(`Graph execution failed: ${message}`);
26
- }
4
+ return executeGraph({ ...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
+ });
27
14
  }