kibi-cli 0.2.8 → 0.4.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/cli.d.ts +4 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +91 -14
- package/dist/commands/check.d.ts +5 -8
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +41 -62
- package/dist/commands/coverage.d.ts +12 -0
- package/dist/commands/coverage.d.ts.map +1 -0
- package/dist/commands/coverage.js +24 -0
- package/dist/commands/discovery-shared.d.ts +11 -0
- package/dist/commands/discovery-shared.d.ts.map +1 -0
- package/dist/commands/discovery-shared.js +281 -0
- package/dist/commands/doctor.d.ts +3 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +12 -13
- package/dist/commands/gaps.d.ts +12 -0
- package/dist/commands/gaps.d.ts.map +1 -0
- package/dist/commands/gaps.js +28 -0
- package/dist/commands/graph.d.ts +13 -0
- package/dist/commands/graph.d.ts.map +1 -0
- package/dist/commands/graph.js +35 -0
- package/dist/commands/init.d.ts +3 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +4 -3
- package/dist/commands/query.d.ts +3 -1
- package/dist/commands/query.d.ts.map +1 -1
- package/dist/commands/query.js +9 -20
- package/dist/commands/search.d.ts +9 -0
- package/dist/commands/search.d.ts.map +1 -0
- package/dist/commands/search.js +38 -0
- package/dist/commands/status.d.ts +6 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +9 -0
- package/dist/commands/sync/persistence.d.ts.map +1 -1
- package/dist/commands/sync/persistence.js +79 -12
- package/dist/commands/sync.d.ts +4 -1
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +79 -31
- package/dist/extractors/markdown.d.ts +17 -0
- package/dist/extractors/markdown.d.ts.map +1 -1
- package/dist/extractors/markdown.js +104 -14
- package/dist/prolog/codec.d.ts +32 -5
- package/dist/prolog/codec.d.ts.map +1 -1
- package/dist/prolog/codec.js +95 -58
- package/dist/prolog.d.ts.map +1 -1
- package/dist/prolog.js +12 -2
- package/dist/public/check-types.d.ts +7 -0
- package/dist/public/check-types.d.ts.map +1 -0
- package/dist/public/check-types.js +18 -0
- package/dist/public/schemas/entity.d.ts +68 -0
- package/dist/public/schemas/entity.d.ts.map +1 -1
- package/dist/public/schemas/entity.js +52 -0
- package/dist/relationships/shards.d.ts.map +1 -1
- package/dist/relationships/shards.js +6 -3
- package/dist/schemas/entity.schema.json +120 -0
- package/dist/search-ranking.d.ts +9 -0
- package/dist/search-ranking.d.ts.map +1 -0
- package/dist/search-ranking.js +149 -0
- package/dist/traceability/symbol-extract.d.ts.map +1 -1
- package/dist/traceability/symbol-extract.js +16 -8
- package/dist/types/entities.d.ts +19 -1
- package/dist/types/entities.d.ts.map +1 -1
- package/dist/utils/prolog-cleanup.d.ts +10 -0
- package/dist/utils/prolog-cleanup.d.ts.map +1 -0
- package/dist/utils/prolog-cleanup.js +39 -0
- package/dist/utils/rule-registry.d.ts +8 -0
- package/dist/utils/rule-registry.d.ts.map +1 -1
- package/dist/utils/rule-registry.js +14 -12
- package/package.json +10 -2
- package/schema/config.json +7 -1
- package/schema/entities.pl +18 -0
- package/schema/validation.pl +115 -4
- package/src/public/check-types.ts +37 -0
- package/src/public/schemas/entity.ts +58 -0
- package/src/schemas/entity.schema.json +56 -1
- package/dist/kb/target-resolver.d.ts +0 -80
- package/dist/kb/target-resolver.d.ts.map +0 -1
- package/dist/kb/target-resolver.js +0 -313
package/dist/prolog/codec.js
CHANGED
|
@@ -28,6 +28,21 @@ export function toPrologAtom(value) {
|
|
|
28
28
|
? value
|
|
29
29
|
: `'${value.replace(/'/g, "''")}'`;
|
|
30
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Escape a string value for use inside a Prolog double-quoted string literal.
|
|
33
|
+
* Escapes: backslash, double-quote, newline, carriage-return, tab.
|
|
34
|
+
* Returns the full quoted literal including surrounding double-quotes.
|
|
35
|
+
*/
|
|
36
|
+
export function toPrologString(value) {
|
|
37
|
+
// implements REQ-009
|
|
38
|
+
const escaped = value
|
|
39
|
+
.replace(/\\/g, "\\\\")
|
|
40
|
+
.replace(/"/g, '\\"')
|
|
41
|
+
.replace(/\n/g, "\\n")
|
|
42
|
+
.replace(/\r/g, "\\r")
|
|
43
|
+
.replace(/\t/g, "\\t");
|
|
44
|
+
return `"${escaped}"`;
|
|
45
|
+
}
|
|
31
46
|
/**
|
|
32
47
|
* Escape a string for embedding inside a single-quoted Prolog atom.
|
|
33
48
|
* Alias for escapeAtom for semantic clarity.
|
|
@@ -35,56 +50,6 @@ export function toPrologAtom(value) {
|
|
|
35
50
|
export function escapeAtomContent(value) {
|
|
36
51
|
return value.replace(/'/g, "''");
|
|
37
52
|
}
|
|
38
|
-
/**
|
|
39
|
-
* Split a string by delimiter at the top level (not inside brackets or quotes).
|
|
40
|
-
* This is the general-purpose version that splits at depth 0.
|
|
41
|
-
*/
|
|
42
|
-
export function splitTopLevel(str, delimiter) {
|
|
43
|
-
const results = [];
|
|
44
|
-
let current = "";
|
|
45
|
-
let depth = 0;
|
|
46
|
-
let inDoubleQuotes = false;
|
|
47
|
-
let inSingleQuotes = false;
|
|
48
|
-
for (let i = 0; i < str.length; i++) {
|
|
49
|
-
const char = str[i];
|
|
50
|
-
const prev = i > 0 ? str[i - 1] : "";
|
|
51
|
-
if (char === '"' && !inSingleQuotes && prev !== "\\") {
|
|
52
|
-
inDoubleQuotes = !inDoubleQuotes;
|
|
53
|
-
current += char;
|
|
54
|
-
continue;
|
|
55
|
-
}
|
|
56
|
-
if (char === "'" && !inDoubleQuotes && prev !== "\\") {
|
|
57
|
-
inSingleQuotes = !inSingleQuotes;
|
|
58
|
-
current += char;
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
if (!inSingleQuotes && !inDoubleQuotes && (char === "[" || char === "(")) {
|
|
62
|
-
depth++;
|
|
63
|
-
current += char;
|
|
64
|
-
continue;
|
|
65
|
-
}
|
|
66
|
-
if (!inSingleQuotes && !inDoubleQuotes && (char === "]" || char === ")")) {
|
|
67
|
-
depth--;
|
|
68
|
-
current += char;
|
|
69
|
-
continue;
|
|
70
|
-
}
|
|
71
|
-
if (!inSingleQuotes &&
|
|
72
|
-
!inDoubleQuotes &&
|
|
73
|
-
depth === 0 &&
|
|
74
|
-
char === delimiter) {
|
|
75
|
-
if (current.length > 0) {
|
|
76
|
-
results.push(current);
|
|
77
|
-
}
|
|
78
|
-
current = "";
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
81
|
-
current += char;
|
|
82
|
-
}
|
|
83
|
-
if (current.length > 0) {
|
|
84
|
-
results.push(current);
|
|
85
|
-
}
|
|
86
|
-
return results;
|
|
87
|
-
}
|
|
88
53
|
/**
|
|
89
54
|
* Parse a Prolog list of lists into a JavaScript array.
|
|
90
55
|
* Input: "[[a,b,c],[d,e,f]]"
|
|
@@ -199,6 +164,7 @@ export function parsePropertyList(propsStr) {
|
|
|
199
164
|
* Parse a single Prolog value, handling typed literals and URIs.
|
|
200
165
|
*/
|
|
201
166
|
export function parsePrologValue(valueInput) {
|
|
167
|
+
// implements REQ-009
|
|
202
168
|
const value = valueInput.trim();
|
|
203
169
|
// Handle typed literal: ^^("value", type)
|
|
204
170
|
if (value.startsWith("^^(")) {
|
|
@@ -220,10 +186,21 @@ export function parsePrologValue(valueInput) {
|
|
|
220
186
|
const parts = splitTopLevelGeneral(innerContent, ",");
|
|
221
187
|
if (parts.length >= 2) {
|
|
222
188
|
let literalValue = parts[0].trim();
|
|
189
|
+
const datatype = parts[1].trim();
|
|
223
190
|
if (literalValue.startsWith('"') && literalValue.endsWith('"')) {
|
|
224
191
|
literalValue = literalValue.substring(1, literalValue.length - 1);
|
|
225
192
|
}
|
|
226
|
-
//
|
|
193
|
+
// Parse typed literals based on datatype
|
|
194
|
+
if (datatype.includes("#integer")) {
|
|
195
|
+
return Number.parseInt(literalValue, 10);
|
|
196
|
+
}
|
|
197
|
+
if (datatype.includes("#decimal") || datatype.includes("#double")) {
|
|
198
|
+
return Number.parseFloat(literalValue);
|
|
199
|
+
}
|
|
200
|
+
if (datatype.includes("#boolean")) {
|
|
201
|
+
return literalValue === "true";
|
|
202
|
+
}
|
|
203
|
+
// Handle array notation for string values
|
|
227
204
|
if (literalValue.startsWith("[") && literalValue.endsWith("]")) {
|
|
228
205
|
const listContent = literalValue.substring(1, literalValue.length - 1);
|
|
229
206
|
if (listContent === "") {
|
|
@@ -267,27 +244,40 @@ export function parsePrologValue(valueInput) {
|
|
|
267
244
|
* General-purpose split at top level (not inside brackets or quotes).
|
|
268
245
|
* More robust version used by property parsing.
|
|
269
246
|
*/
|
|
270
|
-
function splitTopLevelGeneral(str, delimiter) {
|
|
247
|
+
export function splitTopLevelGeneral(str, delimiter) {
|
|
248
|
+
// implements REQ-009
|
|
271
249
|
const results = [];
|
|
272
250
|
let current = "";
|
|
273
251
|
let depth = 0;
|
|
274
|
-
let
|
|
252
|
+
let inDoubleQuotes = false;
|
|
253
|
+
let inSingleQuotes = false;
|
|
275
254
|
for (let i = 0; i < str.length; i++) {
|
|
276
255
|
const char = str[i];
|
|
277
256
|
const prevChar = i > 0 ? str[i - 1] : "";
|
|
278
|
-
if (char === '"' && prevChar !== "\\") {
|
|
279
|
-
|
|
257
|
+
if (char === '"' && !inSingleQuotes && prevChar !== "\\") {
|
|
258
|
+
inDoubleQuotes = !inDoubleQuotes;
|
|
280
259
|
current += char;
|
|
281
260
|
}
|
|
282
|
-
else if (
|
|
261
|
+
else if (char === "'" && !inDoubleQuotes && prevChar !== "\\") {
|
|
262
|
+
inSingleQuotes = !inSingleQuotes;
|
|
263
|
+
current += char;
|
|
264
|
+
}
|
|
265
|
+
else if (!inDoubleQuotes &&
|
|
266
|
+
!inSingleQuotes &&
|
|
267
|
+
(char === "[" || char === "(")) {
|
|
283
268
|
depth++;
|
|
284
269
|
current += char;
|
|
285
270
|
}
|
|
286
|
-
else if (!
|
|
271
|
+
else if (!inDoubleQuotes &&
|
|
272
|
+
!inSingleQuotes &&
|
|
273
|
+
(char === "]" || char === ")")) {
|
|
287
274
|
depth--;
|
|
288
275
|
current += char;
|
|
289
276
|
}
|
|
290
|
-
else if (!
|
|
277
|
+
else if (!inDoubleQuotes &&
|
|
278
|
+
!inSingleQuotes &&
|
|
279
|
+
depth === 0 &&
|
|
280
|
+
char === delimiter) {
|
|
291
281
|
if (current) {
|
|
292
282
|
results.push(current);
|
|
293
283
|
current = "";
|
|
@@ -302,6 +292,14 @@ function splitTopLevelGeneral(str, delimiter) {
|
|
|
302
292
|
}
|
|
303
293
|
return results;
|
|
304
294
|
}
|
|
295
|
+
/**
|
|
296
|
+
* Split a string by delimiter at the top level (not inside brackets or quotes).
|
|
297
|
+
* @see splitTopLevelGeneral
|
|
298
|
+
*/
|
|
299
|
+
export function splitTopLevel(str, delimiter) {
|
|
300
|
+
// implements REQ-009
|
|
301
|
+
return splitTopLevelGeneral(str, delimiter);
|
|
302
|
+
}
|
|
305
303
|
/**
|
|
306
304
|
* Strip outer quotes from a string value.
|
|
307
305
|
*/
|
|
@@ -420,6 +418,45 @@ function unwrapList(value) {
|
|
|
420
418
|
}
|
|
421
419
|
return value;
|
|
422
420
|
}
|
|
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
|
+
export function parseViolationRows(raw) {
|
|
427
|
+
// implements REQ-006
|
|
428
|
+
const trimmed = raw.trim();
|
|
429
|
+
if (trimmed === "[]" || trimmed.length === 0) {
|
|
430
|
+
return [];
|
|
431
|
+
}
|
|
432
|
+
const violations = [];
|
|
433
|
+
// Unwrap outer list
|
|
434
|
+
const content = trimmed.startsWith("[") && trimmed.endsWith("]")
|
|
435
|
+
? trimmed.slice(1, -1)
|
|
436
|
+
: trimmed;
|
|
437
|
+
// Split at top-level commas to get individual violation(...) terms
|
|
438
|
+
const terms = splitTopLevelGeneral(content, ",");
|
|
439
|
+
for (const term of terms) {
|
|
440
|
+
const t = term.trim();
|
|
441
|
+
if (!t.startsWith("violation(") || !t.endsWith(")"))
|
|
442
|
+
continue;
|
|
443
|
+
// Strip "violation(" prefix and trailing ")"
|
|
444
|
+
const inner = t.slice("violation(".length, -1);
|
|
445
|
+
// Split the 5 arguments at top-level commas
|
|
446
|
+
const parts = splitTopLevelGeneral(inner, ",");
|
|
447
|
+
if (parts.length < 4)
|
|
448
|
+
continue;
|
|
449
|
+
const rule = parts[0].trim().replace(/^'|'$/g, "");
|
|
450
|
+
const entityId = parts[1].trim().replace(/^'|'$/g, "");
|
|
451
|
+
const description = parts[2].trim().replace(/^"|"$/g, "");
|
|
452
|
+
const suggestion = parts[3].trim().replace(/^"|"$/g, "");
|
|
453
|
+
const source = parts.length >= 5
|
|
454
|
+
? parts[4].trim().replace(/^'|'$/g, "") || undefined
|
|
455
|
+
: undefined;
|
|
456
|
+
violations.push({ rule, entityId, description, suggestion, source });
|
|
457
|
+
}
|
|
458
|
+
return violations;
|
|
459
|
+
}
|
|
423
460
|
/**
|
|
424
461
|
* Strip quotes from a value (single or double).
|
|
425
462
|
*/
|
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,CAyCxC;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;YAoCd,YAAY;IAyCpB,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;IAkC1B,OAAO,CAAC,WAAW;IAsGnB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,eAAe;IAevB,OAAO,CAAC,cAAc;IA8BtB,SAAS,IAAI,OAAO;IAIpB,MAAM,IAAI,MAAM;IAIV,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;CAyBjC"}
|
package/dist/prolog.js
CHANGED
|
@@ -23,6 +23,7 @@ import { fileURLToPath } from "node:url";
|
|
|
23
23
|
const importMetaDir = path.dirname(fileURLToPath(import.meta.url));
|
|
24
24
|
const require = createRequire(import.meta.url);
|
|
25
25
|
export function resolveKbPlPath() {
|
|
26
|
+
// implements REQ-009
|
|
26
27
|
const overrideKbPath = process.env.KIBI_KB_PL_PATH;
|
|
27
28
|
if (overrideKbPath && existsSync(overrideKbPath)) {
|
|
28
29
|
return overrideKbPath;
|
|
@@ -32,7 +33,9 @@ export function resolveKbPlPath() {
|
|
|
32
33
|
if (existsSync(installedKbPl))
|
|
33
34
|
return installedKbPl;
|
|
34
35
|
}
|
|
35
|
-
catch {
|
|
36
|
+
catch {
|
|
37
|
+
// require.resolve not available or package not installed
|
|
38
|
+
}
|
|
36
39
|
const startDirs = [importMetaDir, process.cwd()];
|
|
37
40
|
for (const startDir of startDirs) {
|
|
38
41
|
let currentDir = path.resolve(startDir);
|
|
@@ -363,10 +366,17 @@ export class PrologProcess {
|
|
|
363
366
|
bindings: this.extractBindings(clean),
|
|
364
367
|
};
|
|
365
368
|
}
|
|
369
|
+
if (stdout.includes("__KIBI_FALSE__")) {
|
|
370
|
+
return {
|
|
371
|
+
success: false,
|
|
372
|
+
bindings: {},
|
|
373
|
+
error: "Query returned false",
|
|
374
|
+
};
|
|
375
|
+
}
|
|
366
376
|
return {
|
|
367
377
|
success: false,
|
|
368
378
|
bindings: {},
|
|
369
|
-
error:
|
|
379
|
+
error: `Query failed - stdout: ${stdout.substring(0, 200)}, stderr: ${stderr.substring(0, 200)}`,
|
|
370
380
|
};
|
|
371
381
|
}
|
|
372
382
|
normalizeGoal(goal) {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public re-export barrel for shared check types.
|
|
3
|
+
* Import from "kibi-cli/public/check-types" in MCP or external consumers.
|
|
4
|
+
*/
|
|
5
|
+
export type { ChecksConfig, RuleDefinition, SymbolTraceabilityOptions, Violation, } from "../utils/rule-registry.js";
|
|
6
|
+
export { DEFAULT_CHECKS_CONFIG, RULE_NAMES, RULES, getEffectiveRules, mergeChecksConfig, validateRuleName, } from "../utils/rule-registry.js";
|
|
7
|
+
//# sourceMappingURL=check-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-types.d.ts","sourceRoot":"","sources":["../../src/public/check-types.ts"],"names":[],"mappings":"AAkBA;;;GAGG;AACH,YAAY,EACV,YAAY,EACZ,cAAc,EACd,yBAAyB,EACzB,SAAS,GACV,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,qBAAqB,EACrB,UAAU,EACV,KAAK,EACL,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,2BAA2B,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Kibi — repo-local, per-branch, queryable long-term memory for software projects
|
|
3
|
+
Copyright (C) 2026 Piotr Franczyk
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU Affero General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
export { DEFAULT_CHECKS_CONFIG, RULE_NAMES, RULES, getEffectiveRules, mergeChecksConfig, validateRuleName, } from "../utils/rule-registry.js";
|
|
@@ -50,8 +50,76 @@ declare const entitySchema: {
|
|
|
50
50
|
type: string;
|
|
51
51
|
enum: string[];
|
|
52
52
|
};
|
|
53
|
+
fact_kind: {
|
|
54
|
+
type: string;
|
|
55
|
+
enum: string[];
|
|
56
|
+
};
|
|
57
|
+
subject_key: {
|
|
58
|
+
type: string;
|
|
59
|
+
};
|
|
60
|
+
property_key: {
|
|
61
|
+
type: string;
|
|
62
|
+
};
|
|
63
|
+
operator: {
|
|
64
|
+
type: string;
|
|
65
|
+
enum: string[];
|
|
66
|
+
};
|
|
67
|
+
value_type: {
|
|
68
|
+
type: string;
|
|
69
|
+
enum: string[];
|
|
70
|
+
};
|
|
71
|
+
value_string: {
|
|
72
|
+
type: string;
|
|
73
|
+
};
|
|
74
|
+
value_int: {
|
|
75
|
+
type: string;
|
|
76
|
+
};
|
|
77
|
+
value_number: {
|
|
78
|
+
type: string;
|
|
79
|
+
};
|
|
80
|
+
value_bool: {
|
|
81
|
+
type: string;
|
|
82
|
+
};
|
|
83
|
+
unit: {
|
|
84
|
+
type: string;
|
|
85
|
+
};
|
|
86
|
+
scope: {
|
|
87
|
+
type: string;
|
|
88
|
+
};
|
|
89
|
+
polarity: {
|
|
90
|
+
type: string;
|
|
91
|
+
enum: string[];
|
|
92
|
+
};
|
|
93
|
+
closed_world: {
|
|
94
|
+
type: string;
|
|
95
|
+
};
|
|
96
|
+
valid_from: {
|
|
97
|
+
type: string;
|
|
98
|
+
};
|
|
99
|
+
valid_to: {
|
|
100
|
+
type: string;
|
|
101
|
+
};
|
|
102
|
+
canonical_key: {
|
|
103
|
+
type: string;
|
|
104
|
+
};
|
|
53
105
|
};
|
|
54
106
|
required: string[];
|
|
107
|
+
allOf: {
|
|
108
|
+
if: {
|
|
109
|
+
properties: {
|
|
110
|
+
type: {
|
|
111
|
+
const: string;
|
|
112
|
+
};
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
else: {
|
|
116
|
+
not: {
|
|
117
|
+
anyOf: {
|
|
118
|
+
required: string[];
|
|
119
|
+
}[];
|
|
120
|
+
};
|
|
121
|
+
};
|
|
122
|
+
}[];
|
|
55
123
|
additionalProperties: boolean;
|
|
56
124
|
};
|
|
57
125
|
export default entitySchema;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity.d.ts","sourceRoot":"","sources":["../../../src/public/schemas/entity.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"entity.d.ts","sourceRoot":"","sources":["../../../src/public/schemas/entity.ts"],"names":[],"mappings":"AAwBA,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoHjB,CAAC;AAEF,eAAe,YAAY,CAAC"}
|
|
@@ -68,6 +68,26 @@ const entitySchema = {
|
|
|
68
68
|
"fact",
|
|
69
69
|
],
|
|
70
70
|
},
|
|
71
|
+
// Typed fact fields - only valid when type === "fact"
|
|
72
|
+
fact_kind: {
|
|
73
|
+
type: "string",
|
|
74
|
+
enum: ["subject", "property_value", "observation", "meta"],
|
|
75
|
+
},
|
|
76
|
+
subject_key: { type: "string" },
|
|
77
|
+
property_key: { type: "string" },
|
|
78
|
+
operator: { type: "string", enum: ["eq", "neq", "lt", "lte", "gt", "gte"] },
|
|
79
|
+
value_type: { type: "string", enum: ["string", "int", "number", "bool"] },
|
|
80
|
+
value_string: { type: "string" },
|
|
81
|
+
value_int: { type: "integer" },
|
|
82
|
+
value_number: { type: "number" },
|
|
83
|
+
value_bool: { type: "boolean" },
|
|
84
|
+
unit: { type: "string" },
|
|
85
|
+
scope: { type: "string" },
|
|
86
|
+
polarity: { type: "string", enum: ["require", "forbid"] },
|
|
87
|
+
closed_world: { type: "boolean" },
|
|
88
|
+
valid_from: { type: "string" },
|
|
89
|
+
valid_to: { type: "string" },
|
|
90
|
+
canonical_key: { type: "string" },
|
|
71
91
|
},
|
|
72
92
|
required: [
|
|
73
93
|
"id",
|
|
@@ -78,6 +98,38 @@ const entitySchema = {
|
|
|
78
98
|
"source",
|
|
79
99
|
"type",
|
|
80
100
|
],
|
|
101
|
+
allOf: [
|
|
102
|
+
// Forbid fact-only fields on non-fact entities
|
|
103
|
+
{
|
|
104
|
+
if: {
|
|
105
|
+
properties: { type: { const: "fact" } },
|
|
106
|
+
},
|
|
107
|
+
// Fact entities can have fact fields (no restriction)
|
|
108
|
+
// Non-fact entities cannot have fact fields
|
|
109
|
+
else: {
|
|
110
|
+
not: {
|
|
111
|
+
anyOf: [
|
|
112
|
+
{ required: ["fact_kind"] },
|
|
113
|
+
{ required: ["subject_key"] },
|
|
114
|
+
{ required: ["property_key"] },
|
|
115
|
+
{ required: ["operator"] },
|
|
116
|
+
{ required: ["value_type"] },
|
|
117
|
+
{ required: ["value_string"] },
|
|
118
|
+
{ required: ["value_int"] },
|
|
119
|
+
{ required: ["value_number"] },
|
|
120
|
+
{ required: ["value_bool"] },
|
|
121
|
+
{ required: ["unit"] },
|
|
122
|
+
{ required: ["scope"] },
|
|
123
|
+
{ required: ["polarity"] },
|
|
124
|
+
{ required: ["closed_world"] },
|
|
125
|
+
{ required: ["valid_from"] },
|
|
126
|
+
{ required: ["valid_to"] },
|
|
127
|
+
{ required: ["canonical_key"] },
|
|
128
|
+
],
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
],
|
|
81
133
|
additionalProperties: false,
|
|
82
134
|
};
|
|
83
135
|
export default entitySchema;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shards.d.ts","sourceRoot":"","sources":["../../src/relationships/shards.ts"],"names":[],"mappings":"AAuBA;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAGrD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGzE;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,kBAAkB,EAAE,
|
|
1
|
+
{"version":3,"file":"shards.d.ts","sourceRoot":"","sources":["../../src/relationships/shards.ts"],"names":[],"mappings":"AAuBA;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAGrD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGzE;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAsFjE;AAED;;;GAGG;AACH,wBAAgB,UAAU,CACxB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,kBAAkB,EAAE,GAC5B,IAAI,CAoBN;AAqCD;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,IAAI,CAAC,kBAAkB,EAAE,IAAI,CAAC,GAC3C;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CA4BzC;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAUnD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAUlE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,kBAAkB,EAAE,EAC7B,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,GAC1B,kBAAkB,EAAE,CAKtB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,GACT,MAAM,CAMR;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAE1B,QAAQ,EAAE,kBAAkB,EAAE,EAC9B,QAAQ,EAAE,kBAAkB,EAAE,GAC7B,kBAAkB,EAAE,CAgCtB"}
|
|
@@ -46,6 +46,7 @@ export function computeShardPath(kbRoot, entityId) {
|
|
|
46
46
|
* Throws on parse errors.
|
|
47
47
|
*/
|
|
48
48
|
export function readShard(shardPath) {
|
|
49
|
+
// implements REQ-005
|
|
49
50
|
if (!fs.existsSync(shardPath)) {
|
|
50
51
|
return [];
|
|
51
52
|
}
|
|
@@ -78,7 +79,7 @@ export function readShard(shardPath) {
|
|
|
78
79
|
// Handle created_at - YAML may auto-convert ISO dates to Date objects
|
|
79
80
|
let createdAt;
|
|
80
81
|
if (rec.created_at instanceof Date) {
|
|
81
|
-
createdAt = rec.created_at.toISOString().replace(/\.000Z$/,
|
|
82
|
+
createdAt = rec.created_at.toISOString().replace(/\.000Z$/, "Z");
|
|
82
83
|
}
|
|
83
84
|
else if (typeof rec.created_at === "string" && rec.created_at) {
|
|
84
85
|
createdAt = rec.created_at;
|
|
@@ -150,7 +151,7 @@ function serializeToYAML(records) {
|
|
|
150
151
|
lines.push(` confidence: ${record.confidence}`);
|
|
151
152
|
}
|
|
152
153
|
}
|
|
153
|
-
return lines.join("\n")
|
|
154
|
+
return `${lines.join("\n")}\n`;
|
|
154
155
|
}
|
|
155
156
|
/**
|
|
156
157
|
* Sorts records deterministically by from, then type, then to.
|
|
@@ -234,7 +235,9 @@ export function relationshipIdFor(type, from, to) {
|
|
|
234
235
|
* Deduplicates by (type, from, to) tuple.
|
|
235
236
|
* On conflict, keeps the record with newer created_at timestamp.
|
|
236
237
|
*/
|
|
237
|
-
export function mergeRecords(
|
|
238
|
+
export function mergeRecords(
|
|
239
|
+
// implements REQ-005
|
|
240
|
+
existing, incoming) {
|
|
238
241
|
const recordMap = new Map();
|
|
239
242
|
// Helper to create key from record
|
|
240
243
|
const makeKey = (r) => `${r.type}|${r.from}|${r.to}`;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$id": "entity.schema.json",
|
|
3
|
+
"title": "Entity",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"properties": {
|
|
6
|
+
"id": { "type": "string" },
|
|
7
|
+
"title": { "type": "string" },
|
|
8
|
+
"status": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"enum": [
|
|
11
|
+
"active",
|
|
12
|
+
"inactive",
|
|
13
|
+
"draft",
|
|
14
|
+
"archived",
|
|
15
|
+
"deleted",
|
|
16
|
+
"approved",
|
|
17
|
+
"rejected",
|
|
18
|
+
"pending",
|
|
19
|
+
"in_progress",
|
|
20
|
+
"superseded",
|
|
21
|
+
"open",
|
|
22
|
+
"closed",
|
|
23
|
+
"deprecated",
|
|
24
|
+
"passing",
|
|
25
|
+
"failing",
|
|
26
|
+
"skipped",
|
|
27
|
+
"proposed",
|
|
28
|
+
"accepted",
|
|
29
|
+
"removed"
|
|
30
|
+
]
|
|
31
|
+
},
|
|
32
|
+
"created_at": { "type": "string" },
|
|
33
|
+
"updated_at": { "type": "string" },
|
|
34
|
+
"source": { "type": "string" },
|
|
35
|
+
"tags": { "type": "array", "items": { "type": "string" } },
|
|
36
|
+
"owner": { "type": "string" },
|
|
37
|
+
"priority": { "type": "string" },
|
|
38
|
+
"severity": { "type": "string" },
|
|
39
|
+
"links": { "type": "array", "items": { "type": "string" } },
|
|
40
|
+
"text_ref": { "type": "string" },
|
|
41
|
+
"type": {
|
|
42
|
+
"type": "string",
|
|
43
|
+
"enum": [
|
|
44
|
+
"req",
|
|
45
|
+
"scenario",
|
|
46
|
+
"test",
|
|
47
|
+
"adr",
|
|
48
|
+
"flag",
|
|
49
|
+
"event",
|
|
50
|
+
"symbol",
|
|
51
|
+
"fact"
|
|
52
|
+
]
|
|
53
|
+
},
|
|
54
|
+
"fact_kind": {
|
|
55
|
+
"type": "string",
|
|
56
|
+
"enum": ["subject", "property_value", "observation", "meta"]
|
|
57
|
+
},
|
|
58
|
+
"subject_key": { "type": "string" },
|
|
59
|
+
"property_key": { "type": "string" },
|
|
60
|
+
"operator": {
|
|
61
|
+
"type": "string",
|
|
62
|
+
"enum": ["eq", "neq", "lt", "lte", "gt", "gte"]
|
|
63
|
+
},
|
|
64
|
+
"value_type": {
|
|
65
|
+
"type": "string",
|
|
66
|
+
"enum": ["string", "int", "number", "bool"]
|
|
67
|
+
},
|
|
68
|
+
"value_string": { "type": "string" },
|
|
69
|
+
"value_int": { "type": "integer" },
|
|
70
|
+
"value_number": { "type": "number" },
|
|
71
|
+
"value_bool": { "type": "boolean" },
|
|
72
|
+
"unit": { "type": "string" },
|
|
73
|
+
"scope": { "type": "string" },
|
|
74
|
+
"polarity": { "type": "string", "enum": ["require", "forbid"] },
|
|
75
|
+
"closed_world": { "type": "boolean" },
|
|
76
|
+
"valid_from": { "type": "string" },
|
|
77
|
+
"valid_to": { "type": "string" },
|
|
78
|
+
"canonical_key": { "type": "string" }
|
|
79
|
+
},
|
|
80
|
+
"required": [
|
|
81
|
+
"id",
|
|
82
|
+
"title",
|
|
83
|
+
"status",
|
|
84
|
+
"created_at",
|
|
85
|
+
"updated_at",
|
|
86
|
+
"source",
|
|
87
|
+
"type"
|
|
88
|
+
],
|
|
89
|
+
"allOf": [
|
|
90
|
+
{
|
|
91
|
+
"if": {
|
|
92
|
+
"properties": { "type": { "const": "fact" } }
|
|
93
|
+
},
|
|
94
|
+
"then": {},
|
|
95
|
+
"else": {
|
|
96
|
+
"not": {
|
|
97
|
+
"anyOf": [
|
|
98
|
+
{ "required": ["fact_kind"] },
|
|
99
|
+
{ "required": ["subject_key"] },
|
|
100
|
+
{ "required": ["property_key"] },
|
|
101
|
+
{ "required": ["operator"] },
|
|
102
|
+
{ "required": ["value_type"] },
|
|
103
|
+
{ "required": ["value_string"] },
|
|
104
|
+
{ "required": ["value_int"] },
|
|
105
|
+
{ "required": ["value_number"] },
|
|
106
|
+
{ "required": ["value_bool"] },
|
|
107
|
+
{ "required": ["unit"] },
|
|
108
|
+
{ "required": ["scope"] },
|
|
109
|
+
{ "required": ["polarity"] },
|
|
110
|
+
{ "required": ["closed_world"] },
|
|
111
|
+
{ "required": ["valid_from"] },
|
|
112
|
+
{ "required": ["valid_to"] },
|
|
113
|
+
{ "required": ["canonical_key"] }
|
|
114
|
+
]
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
],
|
|
119
|
+
"additionalProperties": false
|
|
120
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface SearchMatch {
|
|
2
|
+
entity: Record<string, unknown>;
|
|
3
|
+
score: number;
|
|
4
|
+
reasons: string[];
|
|
5
|
+
snippet?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function rankEntities(entities: Record<string, unknown>[], query: string, workspaceRoot: string): Promise<SearchMatch[]>;
|
|
8
|
+
export declare function loadMarkdownBody(source: string, workspaceRoot: string): Promise<string | null>;
|
|
9
|
+
//# sourceMappingURL=search-ranking.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-ranking.d.ts","sourceRoot":"","sources":["../src/search-ranking.ts"],"names":[],"mappings":"AAIA,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"}
|