kibi-cli 0.5.1 → 0.6.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/commands/aggregated-checks.d.ts.map +1 -1
- package/dist/commands/aggregated-checks.js +3 -2
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +64 -53
- package/dist/commands/discovery-shared.d.ts +12 -5
- package/dist/commands/discovery-shared.d.ts.map +1 -1
- package/dist/commands/discovery-shared.js +21 -13
- package/dist/commands/doctor.js +9 -1
- package/dist/commands/init-helpers.d.ts.map +1 -1
- package/dist/commands/init-helpers.js +2 -3
- package/dist/commands/query.d.ts.map +1 -1
- package/dist/commands/query.js +15 -5
- package/dist/commands/search.js +1 -1
- package/dist/commands/sync/cache.d.ts +14 -4
- package/dist/commands/sync/cache.d.ts.map +1 -1
- package/dist/commands/sync/cache.js +36 -14
- package/dist/commands/sync/extraction.d.ts.map +1 -1
- package/dist/commands/sync/extraction.js +4 -9
- package/dist/commands/sync/manifest.d.ts +14 -3
- package/dist/commands/sync/manifest.d.ts.map +1 -1
- package/dist/commands/sync/manifest.js +29 -10
- package/dist/commands/sync/persistence.d.ts.map +1 -1
- package/dist/commands/sync/persistence.js +9 -5
- package/dist/commands/sync/staging.d.ts +19 -3
- package/dist/commands/sync/staging.d.ts.map +1 -1
- package/dist/commands/sync/staging.js +50 -27
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +16 -20
- package/dist/diagnostics.d.ts +1 -10
- package/dist/diagnostics.d.ts.map +1 -1
- package/dist/diagnostics.js +6 -12
- package/dist/env.d.ts +7 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +41 -0
- package/dist/extractors/manifest.d.ts.map +1 -1
- package/dist/extractors/manifest.js +17 -15
- package/dist/extractors/markdown.d.ts +2 -0
- package/dist/extractors/markdown.d.ts.map +1 -1
- package/dist/extractors/markdown.js +124 -30
- package/dist/extractors/relationships.d.ts.map +1 -1
- package/dist/extractors/relationships.js +10 -4
- package/dist/extractors/symbols-coordinator.d.ts +5 -2
- package/dist/extractors/symbols-coordinator.d.ts.map +1 -1
- package/dist/extractors/symbols-coordinator.js +5 -2
- package/dist/extractors/symbols-ts.d.ts.map +1 -1
- package/dist/extractors/symbols-ts.js +56 -10
- package/dist/prolog/codec.d.ts +0 -43
- package/dist/prolog/codec.d.ts.map +1 -1
- package/dist/prolog/codec.js +68 -74
- package/dist/prolog.d.ts.map +1 -1
- package/dist/prolog.js +39 -25
- package/dist/public/schemas/relationship.d.ts.map +1 -1
- package/dist/public/schemas/relationship.js +1 -0
- package/dist/query/service.d.ts +9 -0
- package/dist/query/service.d.ts.map +1 -1
- package/dist/query/service.js +27 -10
- package/dist/schemas/entity.schema.json +22 -0
- package/dist/search-ranking.d.ts.map +1 -1
- package/dist/search-ranking.js +19 -3
- package/dist/traceability/git-staged.d.ts.map +1 -1
- package/dist/traceability/git-staged.js +22 -6
- package/dist/traceability/symbol-extract.d.ts.map +1 -1
- package/dist/traceability/symbol-extract.js +10 -4
- package/dist/traceability/temp-kb.d.ts +12 -0
- package/dist/traceability/temp-kb.d.ts.map +1 -1
- package/dist/traceability/temp-kb.js +42 -8
- package/dist/traceability/validate.d.ts.map +1 -1
- package/dist/traceability/validate.js +11 -2
- package/dist/utils/branch-resolver.d.ts +4 -0
- package/dist/utils/branch-resolver.d.ts.map +1 -1
- package/dist/utils/branch-resolver.js +29 -12
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +8 -2
- package/dist/utils/rule-registry.js +2 -2
- package/package.json +2 -2
- package/src/public/schemas/relationship.ts +1 -0
- package/src/schemas/entity.schema.json +22 -0
- package/src/schemas/relationship.schema.json +1 -0
package/dist/prolog/codec.js
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
* Doubles single-quote characters per ISO Prolog standard.
|
|
17
17
|
*/
|
|
18
18
|
export function escapeAtom(value) {
|
|
19
|
+
// implements REQ-009
|
|
19
20
|
return value.replace(/'/g, "''");
|
|
20
21
|
}
|
|
21
22
|
/**
|
|
@@ -23,6 +24,7 @@ export function escapeAtom(value) {
|
|
|
23
24
|
* Simple atoms (lowercase start, alphanumeric + underscore) pass through.
|
|
24
25
|
*/
|
|
25
26
|
export function toPrologAtom(value) {
|
|
27
|
+
// implements REQ-009
|
|
26
28
|
const simplePrologAtom = /^[a-z][a-zA-Z0-9_]*$/;
|
|
27
29
|
return simplePrologAtom.test(value)
|
|
28
30
|
? value
|
|
@@ -48,14 +50,11 @@ export function toPrologString(value) {
|
|
|
48
50
|
* Alias for escapeAtom for semantic clarity.
|
|
49
51
|
*/
|
|
50
52
|
export function escapeAtomContent(value) {
|
|
53
|
+
// implements REQ-009
|
|
51
54
|
return value.replace(/'/g, "''");
|
|
52
55
|
}
|
|
53
|
-
/**
|
|
54
|
-
* Parse a Prolog list of lists into a JavaScript array.
|
|
55
|
-
* Input: "[[a,b,c],[d,e,f]]"
|
|
56
|
-
* Output: [["a", "b", "c"], ["d", "e", "f"]]
|
|
57
|
-
*/
|
|
58
56
|
export function parseListOfLists(listStr) {
|
|
57
|
+
// implements REQ-009
|
|
59
58
|
const cleaned = listStr.trim().replace(/^\[/, "").replace(/\]$/, "");
|
|
60
59
|
if (cleaned === "") {
|
|
61
60
|
return [];
|
|
@@ -102,41 +101,44 @@ export function parseListOfLists(listStr) {
|
|
|
102
101
|
}
|
|
103
102
|
return results;
|
|
104
103
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
*/
|
|
109
|
-
export function parseEntityFromBinding(bindingStr) {
|
|
104
|
+
export function parseEntityFromBinding(
|
|
105
|
+
// implements REQ-009
|
|
106
|
+
bindingStr) {
|
|
110
107
|
const cleaned = bindingStr.trim().replace(/^\[/, "").replace(/\]$/, "");
|
|
111
108
|
const parts = splitTopLevelGeneral(cleaned, ",");
|
|
112
109
|
if (parts.length < 3) {
|
|
113
110
|
return {};
|
|
114
111
|
}
|
|
115
|
-
const
|
|
116
|
-
const
|
|
112
|
+
const idPart = parts[0];
|
|
113
|
+
const typePart = parts[1];
|
|
114
|
+
if (idPart === undefined || typePart === undefined) {
|
|
115
|
+
return {};
|
|
116
|
+
}
|
|
117
|
+
const id = idPart.trim();
|
|
118
|
+
const type = typePart.trim();
|
|
117
119
|
const propsStr = parts.slice(2).join(",").trim();
|
|
118
120
|
const props = parsePropertyList(propsStr);
|
|
119
121
|
return { ...props, id: normalizeEntityId(stripOuterQuotes(id)), type };
|
|
120
122
|
}
|
|
121
|
-
/**
|
|
122
|
-
* Parse entity from array returned by parseListOfLists.
|
|
123
|
-
* Input: ["abc123", "req", "[id=abc123, title=\"Test\", ...]"]
|
|
124
|
-
*/
|
|
125
123
|
export function parseEntityFromList(data) {
|
|
124
|
+
// implements REQ-009
|
|
126
125
|
if (data.length < 3) {
|
|
127
126
|
return {};
|
|
128
127
|
}
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
128
|
+
const [idPart, typePart, propsPart] = data;
|
|
129
|
+
if (idPart === undefined ||
|
|
130
|
+
typePart === undefined ||
|
|
131
|
+
propsPart === undefined) {
|
|
132
|
+
return {};
|
|
133
|
+
}
|
|
134
|
+
const id = idPart.trim();
|
|
135
|
+
const type = typePart.trim();
|
|
136
|
+
const propsStr = propsPart.trim();
|
|
132
137
|
const props = parsePropertyList(propsStr);
|
|
133
138
|
return { ...props, id: normalizeEntityId(stripOuterQuotes(id)), type };
|
|
134
139
|
}
|
|
135
|
-
/**
|
|
136
|
-
* Parse Prolog property list into JavaScript object.
|
|
137
|
-
* Input: "[id=abc123, title=\"Test\"]"
|
|
138
|
-
*/
|
|
139
140
|
export function parsePropertyList(propsStr) {
|
|
141
|
+
// implements REQ-009
|
|
140
142
|
const props = {};
|
|
141
143
|
let cleaned = propsStr.trim();
|
|
142
144
|
if (cleaned.startsWith("[")) {
|
|
@@ -160,9 +162,6 @@ export function parsePropertyList(propsStr) {
|
|
|
160
162
|
}
|
|
161
163
|
return props;
|
|
162
164
|
}
|
|
163
|
-
/**
|
|
164
|
-
* Parse a single Prolog value, handling typed literals and URIs.
|
|
165
|
-
*/
|
|
166
165
|
export function parsePrologValue(valueInput) {
|
|
167
166
|
// implements REQ-009
|
|
168
167
|
const value = valueInput.trim();
|
|
@@ -185,8 +184,13 @@ export function parsePrologValue(valueInput) {
|
|
|
185
184
|
const innerContent = value.substring(innerStart, innerEnd);
|
|
186
185
|
const parts = splitTopLevelGeneral(innerContent, ",");
|
|
187
186
|
if (parts.length >= 2) {
|
|
188
|
-
|
|
189
|
-
const
|
|
187
|
+
const literalPart = parts[0];
|
|
188
|
+
const datatypePart = parts[1];
|
|
189
|
+
if (literalPart === undefined || datatypePart === undefined) {
|
|
190
|
+
return value;
|
|
191
|
+
}
|
|
192
|
+
let literalValue = literalPart.trim();
|
|
193
|
+
const datatype = datatypePart.trim();
|
|
190
194
|
if (literalValue.startsWith('"') && literalValue.endsWith('"')) {
|
|
191
195
|
literalValue = literalValue.substring(1, literalValue.length - 1);
|
|
192
196
|
}
|
|
@@ -240,10 +244,6 @@ export function parsePrologValue(valueInput) {
|
|
|
240
244
|
}
|
|
241
245
|
return value;
|
|
242
246
|
}
|
|
243
|
-
/**
|
|
244
|
-
* General-purpose split at top level (not inside brackets or quotes).
|
|
245
|
-
* More robust version used by property parsing.
|
|
246
|
-
*/
|
|
247
247
|
export function splitTopLevelGeneral(str, delimiter) {
|
|
248
248
|
// implements REQ-009
|
|
249
249
|
const results = [];
|
|
@@ -292,17 +292,10 @@ export function splitTopLevelGeneral(str, delimiter) {
|
|
|
292
292
|
}
|
|
293
293
|
return results;
|
|
294
294
|
}
|
|
295
|
-
/**
|
|
296
|
-
* Split a string by delimiter at the top level (not inside brackets or quotes).
|
|
297
|
-
* @see splitTopLevelGeneral
|
|
298
|
-
*/
|
|
299
295
|
export function splitTopLevel(str, delimiter) {
|
|
300
296
|
// implements REQ-009
|
|
301
297
|
return splitTopLevelGeneral(str, delimiter);
|
|
302
298
|
}
|
|
303
|
-
/**
|
|
304
|
-
* Strip outer quotes from a string value.
|
|
305
|
-
*/
|
|
306
299
|
function stripOuterQuotes(value) {
|
|
307
300
|
if (value.startsWith("'") && value.endsWith("'")) {
|
|
308
301
|
return value.slice(1, -1);
|
|
@@ -312,9 +305,6 @@ function stripOuterQuotes(value) {
|
|
|
312
305
|
}
|
|
313
306
|
return value;
|
|
314
307
|
}
|
|
315
|
-
/**
|
|
316
|
-
* Normalize entity ID by extracting filename from file:// URI.
|
|
317
|
-
*/
|
|
318
308
|
function normalizeEntityId(value) {
|
|
319
309
|
if (!value.startsWith("file:///")) {
|
|
320
310
|
return value;
|
|
@@ -322,11 +312,8 @@ function normalizeEntityId(value) {
|
|
|
322
312
|
const idx = value.lastIndexOf("/");
|
|
323
313
|
return idx === -1 ? value : value.slice(idx + 1);
|
|
324
314
|
}
|
|
325
|
-
/**
|
|
326
|
-
* Parse an atom list from Prolog response.
|
|
327
|
-
* Input: "[a, b, c]" or atom string
|
|
328
|
-
*/
|
|
329
315
|
export function parseAtomList(raw) {
|
|
316
|
+
// implements REQ-009
|
|
330
317
|
const trimmed = raw.trim();
|
|
331
318
|
if (trimmed === "[]" || trimmed.length === 0) {
|
|
332
319
|
return [];
|
|
@@ -339,37 +326,39 @@ export function parseAtomList(raw) {
|
|
|
339
326
|
.map((token) => stripQuotes(token.trim()))
|
|
340
327
|
.filter((token) => token.length > 0);
|
|
341
328
|
}
|
|
342
|
-
/**
|
|
343
|
-
* Parse a list of pairs from Prolog response.
|
|
344
|
-
*/
|
|
345
329
|
export function parsePairList(raw) {
|
|
330
|
+
// implements REQ-009
|
|
346
331
|
const rows = parseListRows(raw);
|
|
347
332
|
const pairs = [];
|
|
348
333
|
for (const row of rows) {
|
|
349
334
|
const parts = splitTopLevelGeneral(row, ",").map((part) => stripQuotes(part.trim()));
|
|
350
335
|
if (parts.length >= 2) {
|
|
351
|
-
|
|
336
|
+
const first = parts[0];
|
|
337
|
+
const second = parts[1];
|
|
338
|
+
if (first !== undefined && second !== undefined) {
|
|
339
|
+
pairs.push([first, second]);
|
|
340
|
+
}
|
|
352
341
|
}
|
|
353
342
|
}
|
|
354
343
|
return pairs;
|
|
355
344
|
}
|
|
356
|
-
/**
|
|
357
|
-
* Parse a list of triples from Prolog response.
|
|
358
|
-
*/
|
|
359
345
|
export function parseTriples(raw) {
|
|
346
|
+
// implements REQ-009
|
|
360
347
|
const rows = parseListRows(raw);
|
|
361
348
|
const triples = [];
|
|
362
349
|
for (const row of rows) {
|
|
363
350
|
const parts = splitTopLevelGeneral(row, ",").map((part) => stripQuotes(part.trim()));
|
|
364
351
|
if (parts.length >= 3) {
|
|
365
|
-
|
|
352
|
+
const first = parts[0];
|
|
353
|
+
const second = parts[1];
|
|
354
|
+
const third = parts[2];
|
|
355
|
+
if (first !== undefined && second !== undefined && third !== undefined) {
|
|
356
|
+
triples.push([first, second, third]);
|
|
357
|
+
}
|
|
366
358
|
}
|
|
367
359
|
}
|
|
368
360
|
return triples;
|
|
369
361
|
}
|
|
370
|
-
/**
|
|
371
|
-
* Parse list rows from Prolog response.
|
|
372
|
-
*/
|
|
373
362
|
function parseListRows(raw) {
|
|
374
363
|
const trimmed = raw.trim();
|
|
375
364
|
if (trimmed === "[]" || trimmed.length === 0) {
|
|
@@ -409,20 +398,12 @@ function parseListRows(raw) {
|
|
|
409
398
|
}
|
|
410
399
|
return rows;
|
|
411
400
|
}
|
|
412
|
-
/**
|
|
413
|
-
* Unwrap outer list brackets.
|
|
414
|
-
*/
|
|
415
401
|
function unwrapList(value) {
|
|
416
402
|
if (value.startsWith("[") && value.endsWith("]")) {
|
|
417
403
|
return value.slice(1, -1).trim();
|
|
418
404
|
}
|
|
419
405
|
return value;
|
|
420
406
|
}
|
|
421
|
-
/**
|
|
422
|
-
* Parse a Prolog list of violation/5 terms into JavaScript objects.
|
|
423
|
-
* Handles descriptions and suggestions that contain commas or nested parens.
|
|
424
|
-
* Input: "[violation(rule,'EntityId',\"Desc\",\"Sugg\",'src')]"
|
|
425
|
-
*/
|
|
426
407
|
export function parseViolationRows(raw) {
|
|
427
408
|
// implements REQ-006
|
|
428
409
|
const trimmed = raw.trim();
|
|
@@ -446,20 +427,33 @@ export function parseViolationRows(raw) {
|
|
|
446
427
|
const parts = splitTopLevelGeneral(inner, ",");
|
|
447
428
|
if (parts.length < 4)
|
|
448
429
|
continue;
|
|
449
|
-
const
|
|
450
|
-
const
|
|
451
|
-
const
|
|
452
|
-
const
|
|
430
|
+
const rulePart = parts[0];
|
|
431
|
+
const entityIdPart = parts[1];
|
|
432
|
+
const descriptionPart = parts[2];
|
|
433
|
+
const suggestionPart = parts[3];
|
|
434
|
+
if (rulePart === undefined ||
|
|
435
|
+
entityIdPart === undefined ||
|
|
436
|
+
descriptionPart === undefined ||
|
|
437
|
+
suggestionPart === undefined) {
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
440
|
+
const rule = rulePart.trim().replace(/^'|'$/g, "");
|
|
441
|
+
const entityId = entityIdPart.trim().replace(/^'|'$/g, "");
|
|
442
|
+
const description = descriptionPart.trim().replace(/^"|"$/g, "");
|
|
443
|
+
const suggestion = suggestionPart.trim().replace(/^"|"$/g, "");
|
|
453
444
|
const source = parts.length >= 5
|
|
454
|
-
? parts[4]
|
|
445
|
+
? parts[4]?.trim().replace(/^'|'$/g, "") || undefined
|
|
455
446
|
: undefined;
|
|
456
|
-
violations.push({
|
|
447
|
+
violations.push({
|
|
448
|
+
rule,
|
|
449
|
+
entityId,
|
|
450
|
+
description,
|
|
451
|
+
suggestion,
|
|
452
|
+
...(source !== undefined ? { source } : {}),
|
|
453
|
+
});
|
|
457
454
|
}
|
|
458
455
|
return violations;
|
|
459
456
|
}
|
|
460
|
-
/**
|
|
461
|
-
* Strip quotes from a value (single or double).
|
|
462
|
-
*/
|
|
463
457
|
function stripQuotes(value) {
|
|
464
458
|
if (value.startsWith("'") && value.endsWith("'")) {
|
|
465
459
|
return value.slice(1, -1);
|
package/dist/prolog.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prolog.d.ts","sourceRoot":"","sources":["../src/prolog.ts"],"names":[],"mappings":"AA4BA,wBAAgB,eAAe,IAAI,MAAM,
|
|
1
|
+
{"version":3,"file":"prolog.d.ts","sourceRoot":"","sources":["../src/prolog.ts"],"names":[],"mappings":"AA4BA,wBAAgB,eAAe,IAAI,MAAM,CAsCxC;AACD,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,WAAW,CAAM;IACzB,OAAO,CAAC,KAAK,CAAuC;IACpD,OAAO,CAAC,oBAAoB,CAAoC;IAChE,OAAO,CAAC,cAAc,CACyC;IAC/D,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,aAAa,CAA6B;gBAEtC,OAAO,GAAE,aAAkB;IAKjC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAsCd,YAAY;IA0CpB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC;IAuJ1D,eAAe,IAAI,IAAI;IAIvB,OAAO,CAAC,eAAe;IAavB,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,kBAAkB;YAIZ,YAAY;IA2C1B,OAAO,CAAC,WAAW;IAsGnB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,eAAe;IAiBvB,OAAO,CAAC,cAAc;IA+BtB,SAAS,IAAI,OAAO;IAIpB,MAAM,IAAI,MAAM;IAIV,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;CAyBjC"}
|
package/dist/prolog.js
CHANGED
|
@@ -20,36 +20,41 @@ import { existsSync } from "node:fs";
|
|
|
20
20
|
import { createRequire } from "node:module";
|
|
21
21
|
import path from "node:path";
|
|
22
22
|
import { fileURLToPath } from "node:url";
|
|
23
|
+
import { getKbPlPathOverride, isPrologDebugEnabled } from "./env.js";
|
|
23
24
|
const importMetaDir = path.dirname(fileURLToPath(import.meta.url));
|
|
24
25
|
const require = createRequire(import.meta.url);
|
|
25
26
|
export function resolveKbPlPath() {
|
|
26
27
|
// implements REQ-009
|
|
27
|
-
const overrideKbPath =
|
|
28
|
-
if (overrideKbPath
|
|
28
|
+
const overrideKbPath = getKbPlPathOverride();
|
|
29
|
+
if (overrideKbPath) {
|
|
29
30
|
return overrideKbPath;
|
|
30
31
|
}
|
|
32
|
+
// Strategy 1: Resolve kibi-core package and derive the source file path.
|
|
33
|
+
// This works in npm workspaces where kibi-core is a direct dependency of kibi-cli.
|
|
31
34
|
try {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return
|
|
35
|
+
try {
|
|
36
|
+
// First try: resolve as a file within the package
|
|
37
|
+
return require.resolve("kibi-core/src/kb.pl");
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// Fall back: resolve package entry point and derive path
|
|
41
|
+
const coreMain = require.resolve("kibi-core");
|
|
42
|
+
const coreDir = path.dirname(coreMain);
|
|
43
|
+
return path.join(coreDir, "src", "kb.pl");
|
|
44
|
+
}
|
|
35
45
|
}
|
|
36
46
|
catch {
|
|
37
|
-
//
|
|
47
|
+
// Both resolution strategies failed
|
|
38
48
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
const parentDir = path.dirname(currentDir);
|
|
48
|
-
if (parentDir === currentDir) {
|
|
49
|
-
break;
|
|
50
|
-
}
|
|
51
|
-
currentDir = parentDir;
|
|
49
|
+
// Strategy 2: Walk up from importMetaDir looking for packages/core/src/kb.pl.
|
|
50
|
+
// This works when running from the source tree (e.g., during development).
|
|
51
|
+
let currentDir = importMetaDir;
|
|
52
|
+
while (currentDir !== path.dirname(currentDir)) {
|
|
53
|
+
const candidate = path.join(currentDir, "packages", "core", "src", "kb.pl");
|
|
54
|
+
if (existsSync(candidate)) {
|
|
55
|
+
return candidate;
|
|
52
56
|
}
|
|
57
|
+
currentDir = path.dirname(currentDir);
|
|
53
58
|
}
|
|
54
59
|
throw new Error("Unable to resolve kb.pl. Expected kibi-core to be installed (node_modules) " +
|
|
55
60
|
"or to be running inside the monorepo checkout.");
|
|
@@ -156,7 +161,7 @@ export class PrologProcess {
|
|
|
156
161
|
const runInteractiveQuery = async () => {
|
|
157
162
|
this.outputBuffer = "";
|
|
158
163
|
this.errorBuffer = "";
|
|
159
|
-
const debug =
|
|
164
|
+
const debug = isPrologDebugEnabled();
|
|
160
165
|
const normalizedGoal = this.normalizeGoal(goal);
|
|
161
166
|
const wrappedGoal = /^once\s*\(/.test(normalizedGoal)
|
|
162
167
|
? normalizedGoal
|
|
@@ -284,6 +289,14 @@ export class PrologProcess {
|
|
|
284
289
|
}
|
|
285
290
|
const attachMatch = trimmedGoal.match(/^kb_attach\('(.+)'\)$/);
|
|
286
291
|
if (attachMatch) {
|
|
292
|
+
const attachPath = attachMatch[1] ?? null;
|
|
293
|
+
if (!attachPath) {
|
|
294
|
+
return {
|
|
295
|
+
success: false,
|
|
296
|
+
bindings: {},
|
|
297
|
+
error: "Invalid KB attach path",
|
|
298
|
+
};
|
|
299
|
+
}
|
|
287
300
|
if (this.attachedKbPath !== null) {
|
|
288
301
|
return {
|
|
289
302
|
success: false,
|
|
@@ -293,7 +306,7 @@ export class PrologProcess {
|
|
|
293
306
|
}
|
|
294
307
|
const attachResult = this.execOneShot(trimmedGoal, null);
|
|
295
308
|
if (attachResult.success) {
|
|
296
|
-
this.attachedKbPath =
|
|
309
|
+
this.attachedKbPath = attachPath;
|
|
297
310
|
}
|
|
298
311
|
return attachResult;
|
|
299
312
|
}
|
|
@@ -391,7 +404,9 @@ export class PrologProcess {
|
|
|
391
404
|
const match = line.match(/^([A-Z_][A-Za-z0-9_]*)\s*=\s*(.+)\.?\s*$/);
|
|
392
405
|
if (match) {
|
|
393
406
|
const [, varName, value] = match;
|
|
394
|
-
|
|
407
|
+
if (varName !== undefined && value !== undefined) {
|
|
408
|
+
bindings[varName] = value.trim().replace(/\.$/, "").replace(/,$/, "");
|
|
409
|
+
}
|
|
395
410
|
}
|
|
396
411
|
}
|
|
397
412
|
return bindings;
|
|
@@ -411,12 +426,11 @@ export class PrologProcess {
|
|
|
411
426
|
if (errorText.includes("timeout_error")) {
|
|
412
427
|
return `Operation exceeded ${this.timeout / 1000}s timeout`;
|
|
413
428
|
}
|
|
414
|
-
const simpleError = errorText
|
|
429
|
+
const simpleError = (errorText
|
|
415
430
|
.replace(/ERROR:\s*/g, "")
|
|
416
431
|
.replace(/^\*\*.*\*\*$/gm, "")
|
|
417
432
|
.replace(/^\s+/gm, "")
|
|
418
|
-
.split("\n")[0]
|
|
419
|
-
.trim();
|
|
433
|
+
.split("\n")[0] ?? "").trim();
|
|
420
434
|
return simpleError || "Unknown error";
|
|
421
435
|
}
|
|
422
436
|
isRunning() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relationship.d.ts","sourceRoot":"","sources":["../../../src/public/schemas/relationship.ts"],"names":[],"mappings":"AAkBA,QAAA,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"relationship.d.ts","sourceRoot":"","sources":["../../../src/public/schemas/relationship.ts"],"names":[],"mappings":"AAkBA,QAAA,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkCvB,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
|
package/dist/query/service.d.ts
CHANGED
|
@@ -11,6 +11,14 @@ export interface QueryResult {
|
|
|
11
11
|
entities: Array<Record<string, unknown>>;
|
|
12
12
|
totalCount: number;
|
|
13
13
|
}
|
|
14
|
+
interface QueryCodecDeps {
|
|
15
|
+
escapeAtom: (value: string) => string;
|
|
16
|
+
parseListOfLists: (value: string) => string[][];
|
|
17
|
+
parseEntityFromList: (value: string[]) => Record<string, unknown>;
|
|
18
|
+
parseEntityFromBinding: (value: string) => Record<string, unknown>;
|
|
19
|
+
}
|
|
20
|
+
export declare function _setQueryCodecDepsForTests(deps: QueryCodecDeps): void;
|
|
21
|
+
export declare function _resetQueryCodecDepsForTests(): void;
|
|
14
22
|
export declare const VALID_ENTITY_TYPES: string[];
|
|
15
23
|
/**
|
|
16
24
|
* Build a Prolog query goal from filters.
|
|
@@ -32,4 +40,5 @@ export declare function getInvalidTypeError(type: string): string;
|
|
|
32
40
|
* Build human-readable summary text for query results.
|
|
33
41
|
*/
|
|
34
42
|
export declare function buildQuerySummaryText(result: QueryResult, filters: QueryFilters): string;
|
|
43
|
+
export {};
|
|
35
44
|
//# sourceMappingURL=service.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/query/service.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAQlD,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACzC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,eAAO,MAAM,kBAAkB,UAS9B,CAAC;AAEF;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/query/service.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAQlD,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACzC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,cAAc;IACtB,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACtC,gBAAgB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,EAAE,EAAE,CAAC;IAChD,mBAAmB,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClE,sBAAsB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpE;AAWD,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI,CAGrE;AAED,wBAAgB,4BAA4B,IAAI,IAAI,CAGnD;AAED,eAAO,MAAM,kBAAkB,UAS9B,CAAC;AAEF;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,CAsClE;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,WAAW,CAAC,CAwCtB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAExD;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,WAAW,EACnB,OAAO,EAAE,YAAY,GACpB,MAAM,CAkBR"}
|
package/dist/query/service.js
CHANGED
|
@@ -8,6 +8,21 @@
|
|
|
8
8
|
* (at your option) any later version.
|
|
9
9
|
*/
|
|
10
10
|
import { escapeAtom, parseEntityFromBinding, parseEntityFromList, parseListOfLists, } from "../prolog/codec.js";
|
|
11
|
+
const defaultCodecDeps = {
|
|
12
|
+
escapeAtom,
|
|
13
|
+
parseListOfLists,
|
|
14
|
+
parseEntityFromList,
|
|
15
|
+
parseEntityFromBinding,
|
|
16
|
+
};
|
|
17
|
+
let codecDeps = defaultCodecDeps;
|
|
18
|
+
export function _setQueryCodecDepsForTests(deps) {
|
|
19
|
+
// implements REQ-003
|
|
20
|
+
codecDeps = deps;
|
|
21
|
+
}
|
|
22
|
+
export function _resetQueryCodecDepsForTests() {
|
|
23
|
+
// implements REQ-003
|
|
24
|
+
codecDeps = defaultCodecDeps;
|
|
25
|
+
}
|
|
11
26
|
export const VALID_ENTITY_TYPES = [
|
|
12
27
|
"req",
|
|
13
28
|
"scenario",
|
|
@@ -22,33 +37,34 @@ export const VALID_ENTITY_TYPES = [
|
|
|
22
37
|
* Build a Prolog query goal from filters.
|
|
23
38
|
*/
|
|
24
39
|
export function buildEntityQueryGoal(filters) {
|
|
40
|
+
// implements REQ-003
|
|
25
41
|
const { type, id, sourceFile, tags } = filters;
|
|
26
42
|
if (sourceFile) {
|
|
27
|
-
const safeSource = escapeAtom(sourceFile);
|
|
43
|
+
const safeSource = codecDeps.escapeAtom(sourceFile);
|
|
28
44
|
if (type) {
|
|
29
|
-
const safeType = escapeAtom(type);
|
|
45
|
+
const safeType = codecDeps.escapeAtom(type);
|
|
30
46
|
return `findall([Id,'${safeType}',Props], (kb_entities_by_source('${safeSource}', SourceIds), member(Id, SourceIds), kb_entity(Id, '${safeType}', Props)), Results)`;
|
|
31
47
|
}
|
|
32
48
|
return `findall([Id,Type,Props], (kb_entities_by_source('${safeSource}', SourceIds), member(Id, SourceIds), kb_entity(Id, Type, Props)), Results)`;
|
|
33
49
|
}
|
|
34
50
|
if (id && type) {
|
|
35
|
-
const safeId = escapeAtom(id);
|
|
36
|
-
const safeType = escapeAtom(type);
|
|
51
|
+
const safeId = codecDeps.escapeAtom(id);
|
|
52
|
+
const safeType = codecDeps.escapeAtom(type);
|
|
37
53
|
return `findall(['${safeId}','${safeType}',Props], kb_entity('${safeId}', '${safeType}', Props), Results)`;
|
|
38
54
|
}
|
|
39
55
|
if (id) {
|
|
40
|
-
const safeId = escapeAtom(id);
|
|
56
|
+
const safeId = codecDeps.escapeAtom(id);
|
|
41
57
|
return `findall(['${safeId}',Type,Props], kb_entity('${safeId}', Type, Props), Results)`;
|
|
42
58
|
}
|
|
43
59
|
if (tags && tags.length > 0) {
|
|
44
60
|
if (type) {
|
|
45
|
-
const safeType = escapeAtom(type);
|
|
61
|
+
const safeType = codecDeps.escapeAtom(type);
|
|
46
62
|
return `findall([Id,'${safeType}',Props], kb_entity(Id, '${safeType}', Props), Results)`;
|
|
47
63
|
}
|
|
48
64
|
return "findall([Id,Type,Props], kb_entity(Id, Type, Props), Results)";
|
|
49
65
|
}
|
|
50
66
|
if (type) {
|
|
51
|
-
const safeType = escapeAtom(type);
|
|
67
|
+
const safeType = codecDeps.escapeAtom(type);
|
|
52
68
|
return `findall([Id,'${safeType}',Props], kb_entity(Id, '${safeType}', Props), Results)`;
|
|
53
69
|
}
|
|
54
70
|
return "findall([Id,Type,Props], kb_entity(Id, Type, Props), Results)";
|
|
@@ -57,20 +73,21 @@ export function buildEntityQueryGoal(filters) {
|
|
|
57
73
|
* Execute a filtered entity query against the KB.
|
|
58
74
|
*/
|
|
59
75
|
export async function queryEntities(prolog, filters) {
|
|
76
|
+
// implements REQ-003
|
|
60
77
|
const { tags, limit = 100, offset = 0 } = filters;
|
|
61
78
|
const goal = buildEntityQueryGoal(filters);
|
|
62
79
|
const queryResult = await prolog.query(goal);
|
|
63
80
|
let entities = [];
|
|
64
81
|
if (queryResult.success) {
|
|
65
82
|
if (queryResult.bindings.Results) {
|
|
66
|
-
const entitiesData = parseListOfLists(queryResult.bindings.Results);
|
|
83
|
+
const entitiesData = codecDeps.parseListOfLists(queryResult.bindings.Results);
|
|
67
84
|
for (const data of entitiesData) {
|
|
68
|
-
const entity = parseEntityFromList(data);
|
|
85
|
+
const entity = codecDeps.parseEntityFromList(data);
|
|
69
86
|
entities.push(entity);
|
|
70
87
|
}
|
|
71
88
|
}
|
|
72
89
|
else if (queryResult.bindings.Result) {
|
|
73
|
-
const entity = parseEntityFromBinding(queryResult.bindings.Result);
|
|
90
|
+
const entity = codecDeps.parseEntityFromBinding(queryResult.bindings.Result);
|
|
74
91
|
entities = [entity];
|
|
75
92
|
}
|
|
76
93
|
}
|
|
@@ -39,6 +39,14 @@
|
|
|
39
39
|
"links": { "type": "array", "items": { "type": "string" } },
|
|
40
40
|
"text_ref": { "type": "string" },
|
|
41
41
|
"sourceFile": { "type": "string" },
|
|
42
|
+
"verification_scope": {
|
|
43
|
+
"type": "string",
|
|
44
|
+
"enum": ["unit", "integration", "end_to_end"]
|
|
45
|
+
},
|
|
46
|
+
"verification_perspective": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"enum": ["internal", "consumer"]
|
|
49
|
+
},
|
|
42
50
|
"type": {
|
|
43
51
|
"type": "string",
|
|
44
52
|
"enum": [
|
|
@@ -88,6 +96,20 @@
|
|
|
88
96
|
"type"
|
|
89
97
|
],
|
|
90
98
|
"allOf": [
|
|
99
|
+
{
|
|
100
|
+
"if": {
|
|
101
|
+
"properties": { "type": { "const": "test" } }
|
|
102
|
+
},
|
|
103
|
+
"then": {},
|
|
104
|
+
"else": {
|
|
105
|
+
"not": {
|
|
106
|
+
"anyOf": [
|
|
107
|
+
{ "required": ["verification_scope"] },
|
|
108
|
+
{ "required": ["verification_perspective"] }
|
|
109
|
+
]
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
},
|
|
91
113
|
{
|
|
92
114
|
"if": {
|
|
93
115
|
"properties": { "type": { "const": "fact" } }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search-ranking.d.ts","sourceRoot":"","sources":["../src/search-ranking.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"search-ranking.d.ts","sourceRoot":"","sources":["../src/search-ranking.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAGD,wBAAsB,YAAY,CAChC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EACnC,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,WAAW,EAAE,CAAC,CAyBxB;AA+FD,wBAAsB,gBAAgB,CAEpC,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA6BxB"}
|
package/dist/search-ranking.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import matter from "gray-matter";
|
|
4
3
|
// implements REQ-mcp-search-discovery, REQ-002, REQ-003
|
|
5
4
|
export async function rankEntities(entities, query, workspaceRoot) {
|
|
6
5
|
const matches = [];
|
|
@@ -97,7 +96,7 @@ async function rankEntity(entity, query, workspaceRoot) {
|
|
|
97
96
|
entity,
|
|
98
97
|
score,
|
|
99
98
|
reasons: Array.from(new Set(reasons)),
|
|
100
|
-
snippet,
|
|
99
|
+
...(snippet !== undefined ? { snippet } : {}),
|
|
101
100
|
};
|
|
102
101
|
}
|
|
103
102
|
export async function loadMarkdownBody(
|
|
@@ -121,12 +120,29 @@ source, workspaceRoot) {
|
|
|
121
120
|
}
|
|
122
121
|
try {
|
|
123
122
|
const fileContent = await fs.readFile(resolved, "utf8");
|
|
124
|
-
return
|
|
123
|
+
return stripFrontmatter(fileContent);
|
|
125
124
|
}
|
|
126
125
|
catch {
|
|
127
126
|
return null;
|
|
128
127
|
}
|
|
129
128
|
}
|
|
129
|
+
function stripFrontmatter(content) {
|
|
130
|
+
const trimmedContent = content.trimStart();
|
|
131
|
+
if (!trimmedContent.startsWith("---")) {
|
|
132
|
+
return content;
|
|
133
|
+
}
|
|
134
|
+
const openingDelimiter = /^---[^\S\r\n]*(?:\r?\n|$)/.exec(trimmedContent);
|
|
135
|
+
if (!openingDelimiter) {
|
|
136
|
+
return content;
|
|
137
|
+
}
|
|
138
|
+
const closingDelimiter = /(?:\r?\n)---[^\S\r\n]*(?:\r?\n|$)/g;
|
|
139
|
+
closingDelimiter.lastIndex = openingDelimiter[0].length;
|
|
140
|
+
const match = closingDelimiter.exec(trimmedContent);
|
|
141
|
+
if (!match) {
|
|
142
|
+
return content;
|
|
143
|
+
}
|
|
144
|
+
return trimmedContent.slice(match.index + match[0].length);
|
|
145
|
+
}
|
|
130
146
|
function normalize(value) {
|
|
131
147
|
return value.trim().toLowerCase();
|
|
132
148
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git-staged.d.ts","sourceRoot":"","sources":["../../src/traceability/git-staged.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"git-staged.d.ts","sourceRoot":"","sources":["../../src/traceability/git-staged.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,MAAM,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AAE3C,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,KAAK,MAAM,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,KAAK,MAAM,CAAC;AAalE;;GAEG;AAEH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,GACZ,KAAK,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAwB5C;AAiDD;;GAEG;AACH,wBAAgB,kBAAkB,CAEhC,QAAQ,EAAE,MAAM,EAChB,SAAS,UAAQ,GAChB,SAAS,EAAE,CAuBb;AAED;;GAEG;AAEH,wBAAgB,cAAc,CAAC,IAAI,GAAE,MAAiB,GAAG,UAAU,EAAE,CAuGpE;AAED,eAAe,cAAc,CAAC"}
|