kibi-cli 0.2.7 → 0.2.8
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.js +4 -6
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +26 -24
- package/dist/commands/query.d.ts.map +1 -1
- package/dist/commands/query.js +2 -8
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +0 -15
- package/dist/diagnostics.d.ts.map +1 -1
- package/dist/diagnostics.js +0 -2
- package/dist/extractors/manifest.d.ts +2 -0
- package/dist/extractors/manifest.d.ts.map +1 -1
- package/dist/extractors/manifest.js +1 -0
- package/dist/extractors/markdown.d.ts.map +1 -1
- package/dist/extractors/markdown.js +9 -1
- package/dist/public/extractors/symbols-coordinator.d.ts.map +1 -1
- package/dist/public/extractors/symbols-coordinator.js +1 -2
- package/dist/public/prolog/index.d.ts.map +1 -1
- package/dist/public/prolog/index.js +1 -2
- package/dist/public/schemas/entity.d.ts.map +1 -1
- package/dist/public/schemas/entity.js +1 -3
- package/dist/public/schemas/relationship.d.ts.map +1 -1
- package/dist/public/schemas/relationship.js +1 -3
- package/dist/traceability/git-staged.d.ts.map +1 -1
- package/dist/traceability/git-staged.js +33 -7
- package/dist/traceability/symbol-extract.d.ts +6 -1
- package/dist/traceability/symbol-extract.d.ts.map +1 -1
- package/dist/traceability/symbol-extract.js +62 -34
- package/dist/traceability/temp-kb.d.ts.map +1 -1
- package/dist/traceability/temp-kb.js +4 -3
- package/dist/traceability/validate.d.ts.map +1 -1
- package/dist/traceability/validate.js +8 -7
- package/package.json +5 -1
- package/src/public/extractors/symbols-coordinator.ts +1 -2
- package/src/public/prolog/index.ts +1 -2
- package/src/public/schemas/entity.ts +1 -3
- package/src/public/schemas/relationship.ts +1 -3
|
@@ -23,8 +23,7 @@ export async function runAggregatedChecks(prolog, rulesAllowlist, requireAdr = f
|
|
|
23
23
|
try {
|
|
24
24
|
const result = await prolog.query(query);
|
|
25
25
|
if (!result.success) {
|
|
26
|
-
|
|
27
|
-
return [];
|
|
26
|
+
throw new Error(`Aggregated checks query failed: ${result.error || "Unknown error"}`);
|
|
28
27
|
}
|
|
29
28
|
let violationsDict;
|
|
30
29
|
try {
|
|
@@ -39,8 +38,7 @@ export async function runAggregatedChecks(prolog, rulesAllowlist, requireAdr = f
|
|
|
39
38
|
violationsDict = parsed;
|
|
40
39
|
}
|
|
41
40
|
catch (parseError) {
|
|
42
|
-
|
|
43
|
-
return [];
|
|
41
|
+
throw new Error(`Failed to parse violations JSON: ${parseError instanceof Error ? parseError.message : String(parseError)}`);
|
|
44
42
|
}
|
|
45
43
|
for (const ruleViolations of Object.values(violationsDict)) {
|
|
46
44
|
for (const v of ruleViolations) {
|
|
@@ -59,7 +57,7 @@ export async function runAggregatedChecks(prolog, rulesAllowlist, requireAdr = f
|
|
|
59
57
|
return violations;
|
|
60
58
|
}
|
|
61
59
|
catch (error) {
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
61
|
+
throw new Error(`Error running aggregated checks: ${message}`);
|
|
64
62
|
}
|
|
65
63
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/commands/check.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/commands/check.ts"],"names":[],"mappings":"AAgDA,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC3B,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAGD,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA+RvE"}
|
package/dist/commands/check.js
CHANGED
|
@@ -16,24 +16,24 @@
|
|
|
16
16
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
17
|
*/
|
|
18
18
|
import * as path from "node:path";
|
|
19
|
+
import { extractFromManifest } from "../extractors/manifest.js";
|
|
19
20
|
import { PrologProcess } from "../prolog.js";
|
|
20
21
|
import { escapeAtom } from "../prolog/codec.js";
|
|
21
22
|
import { getStagedFiles } from "../traceability/git-staged.js";
|
|
22
23
|
import { validateStagedMarkdown } from "../traceability/markdown-validate.js";
|
|
23
|
-
import { extractSymbolsFromStagedFile } from "../traceability/symbol-extract.js";
|
|
24
|
+
import { extractSymbolsFromStagedFile, } from "../traceability/symbol-extract.js";
|
|
24
25
|
import { cleanupTempKb, consultOverlay, createOverlayFacts, createTempKb, } from "../traceability/temp-kb.js";
|
|
25
26
|
import { formatViolations as formatStagedViolations, validateStagedSymbols, } from "../traceability/validate.js";
|
|
26
27
|
import { loadConfig } from "../utils/config.js";
|
|
27
28
|
import { RULES, getEffectiveRules, } from "../utils/rule-registry.js";
|
|
28
29
|
import { runAggregatedChecks } from "./aggregated-checks.js";
|
|
29
30
|
import { getCurrentBranch } from "./init-helpers.js";
|
|
31
|
+
import { discoverSourceFiles } from "./sync/discovery.js";
|
|
30
32
|
// implements REQ-006
|
|
31
33
|
export async function checkCommand(options) {
|
|
32
34
|
let prolog = null;
|
|
33
35
|
let attached = false;
|
|
34
36
|
try {
|
|
35
|
-
// Resolve KB path with priority:
|
|
36
|
-
// --kb-path > git branch --show-current > KIBI_BRANCH env > develop > main
|
|
37
37
|
let resolvedKbPath = "";
|
|
38
38
|
if (options.kbPath) {
|
|
39
39
|
resolvedKbPath = options.kbPath;
|
|
@@ -54,13 +54,32 @@ export async function checkCommand(options) {
|
|
|
54
54
|
// fallback to main if develop isn't present? keep path consistent
|
|
55
55
|
resolvedKbPath = path.join(process.cwd(), ".kb/branches", branch || "main");
|
|
56
56
|
}
|
|
57
|
-
// If --staged mode requested, run staged-symbol traceability gate.
|
|
58
|
-
// We skip creating the main prolog session entirely in this path.
|
|
59
57
|
if (options.staged) {
|
|
60
58
|
const minLinks = options.minLinks ? Number(options.minLinks) : 1;
|
|
61
59
|
let tempCtx = null;
|
|
62
60
|
try {
|
|
63
|
-
|
|
61
|
+
const config = loadConfig(process.cwd());
|
|
62
|
+
const manifestLookup = new Map();
|
|
63
|
+
const { manifestFiles } = await discoverSourceFiles(process.cwd(), config.paths);
|
|
64
|
+
for (const manifestPath of manifestFiles) {
|
|
65
|
+
try {
|
|
66
|
+
const entries = extractFromManifest(manifestPath);
|
|
67
|
+
for (const entry of entries) {
|
|
68
|
+
// Prefer the per-symbol sourceFile; fall back to entity.source or manifest path
|
|
69
|
+
const sourceFile = entry.sourceFile || entry.entity.source || manifestPath;
|
|
70
|
+
const key = `${sourceFile}:${entry.entity.title}`;
|
|
71
|
+
// Extract requirement links (implements relationships to REQ-*)
|
|
72
|
+
const links = entry.relationships
|
|
73
|
+
.filter((r) => r.type === "implements" &&
|
|
74
|
+
r.to.match(/^[A-Z][A-Z0-9\-_]*$/))
|
|
75
|
+
.map((r) => r.to);
|
|
76
|
+
manifestLookup.set(key, { id: entry.entity.id, links });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
// Ignore manifest parsing errors
|
|
81
|
+
}
|
|
82
|
+
}
|
|
64
83
|
const stagedFiles = getStagedFiles();
|
|
65
84
|
if (!stagedFiles || stagedFiles.length === 0) {
|
|
66
85
|
console.log("No staged files found.");
|
|
@@ -89,7 +108,7 @@ export async function checkCommand(options) {
|
|
|
89
108
|
const allSymbols = [];
|
|
90
109
|
for (const f of codeFiles) {
|
|
91
110
|
try {
|
|
92
|
-
const symbols = extractSymbolsFromStagedFile(f);
|
|
111
|
+
const symbols = extractSymbolsFromStagedFile(f, manifestLookup);
|
|
93
112
|
if (symbols?.length) {
|
|
94
113
|
allSymbols.push(...symbols);
|
|
95
114
|
}
|
|
@@ -108,12 +127,10 @@ export async function checkCommand(options) {
|
|
|
108
127
|
}
|
|
109
128
|
// Create temp KB
|
|
110
129
|
tempCtx = await createTempKb(resolvedKbPath);
|
|
111
|
-
// Write overlay facts THEN consult so Prolog sees the changed_symbol facts
|
|
112
130
|
const overlayFacts = createOverlayFacts(allSymbols);
|
|
113
131
|
const fs = await import("node:fs/promises");
|
|
114
132
|
await fs.writeFile(tempCtx.overlayPath, overlayFacts, "utf8");
|
|
115
133
|
await consultOverlay(tempCtx);
|
|
116
|
-
// Validate staged symbols using the temp KB prolog session
|
|
117
134
|
const violationsRaw = await validateStagedSymbols({
|
|
118
135
|
minLinks,
|
|
119
136
|
prolog: tempCtx.prolog,
|
|
@@ -153,13 +170,11 @@ export async function checkCommand(options) {
|
|
|
153
170
|
}
|
|
154
171
|
attached = true;
|
|
155
172
|
const violations = [];
|
|
156
|
-
// Load config to get rule enablement settings
|
|
157
173
|
const config = loadConfig(process.cwd());
|
|
158
174
|
const checksConfig = config.checks ?? {
|
|
159
175
|
rules: Object.fromEntries(RULES.map((r) => [r.name, true])),
|
|
160
176
|
symbolTraceability: { requireAdr: false },
|
|
161
177
|
};
|
|
162
|
-
// Get effective rules based on config and CLI --rules filter
|
|
163
178
|
const effectiveRules = getEffectiveRules(checksConfig.rules, options.rules);
|
|
164
179
|
// Helper to conditionally run a check by name
|
|
165
180
|
async function runCheck(name, fn, ...args) {
|
|
@@ -250,7 +265,6 @@ export async function checkCommand(options) {
|
|
|
250
265
|
}
|
|
251
266
|
async function checkMustPriorityCoverage(prolog) {
|
|
252
267
|
const violations = [];
|
|
253
|
-
// Find all must-priority requirements
|
|
254
268
|
const mustReqs = await findMustPriorityReqs(prolog);
|
|
255
269
|
for (const reqId of mustReqs) {
|
|
256
270
|
const entityResult = await prolog.query(`kb_entity('${reqId}', req, Props)`);
|
|
@@ -324,9 +338,7 @@ async function getAllEntityIds(prolog, type) {
|
|
|
324
338
|
}
|
|
325
339
|
async function checkNoDanglingRefs(prolog) {
|
|
326
340
|
const violations = [];
|
|
327
|
-
// Get all entity IDs once
|
|
328
341
|
const allEntityIds = new Set(await getAllEntityIds(prolog));
|
|
329
|
-
// Get all relationships by querying all known relationship types
|
|
330
342
|
const relTypes = [
|
|
331
343
|
"depends_on",
|
|
332
344
|
"verified_by",
|
|
@@ -379,7 +391,6 @@ async function checkNoDanglingRefs(prolog) {
|
|
|
379
391
|
}
|
|
380
392
|
async function checkNoCycles(prolog) {
|
|
381
393
|
const violations = [];
|
|
382
|
-
// Get all depends_on relationships
|
|
383
394
|
const depsResult = await prolog.query("findall([From,To], kb_relationship(depends_on, From, To), Deps)");
|
|
384
395
|
if (!depsResult.success || !depsResult.bindings.Deps) {
|
|
385
396
|
return violations;
|
|
@@ -393,7 +404,6 @@ async function checkNoCycles(prolog) {
|
|
|
393
404
|
if (!content) {
|
|
394
405
|
return violations;
|
|
395
406
|
}
|
|
396
|
-
// Build adjacency map
|
|
397
407
|
const graph = new Map();
|
|
398
408
|
const depMatches = content.matchAll(/\[([^,]+),([^\]]+)\]/g);
|
|
399
409
|
for (const depMatch of depMatches) {
|
|
@@ -407,7 +417,6 @@ async function checkNoCycles(prolog) {
|
|
|
407
417
|
fromList.push(to);
|
|
408
418
|
}
|
|
409
419
|
}
|
|
410
|
-
// DFS to detect cycles
|
|
411
420
|
const visited = new Set();
|
|
412
421
|
const recStack = new Set();
|
|
413
422
|
function hasCycleDFS(node, path) {
|
|
@@ -429,7 +438,6 @@ async function checkNoCycles(prolog) {
|
|
|
429
438
|
recStack.delete(node);
|
|
430
439
|
return null;
|
|
431
440
|
}
|
|
432
|
-
// Check each node for cycles
|
|
433
441
|
for (const node of graph.keys()) {
|
|
434
442
|
if (!visited.has(node)) {
|
|
435
443
|
const cyclePath = hasCycleDFS(node, []);
|
|
@@ -472,15 +480,12 @@ async function checkRequiredFields(prolog, allEntityIds) {
|
|
|
472
480
|
for (const entityId of allEntityIds) {
|
|
473
481
|
const result = await prolog.query(`kb_entity('${entityId}', Type, Props)`);
|
|
474
482
|
if (result.success && result.bindings.Props) {
|
|
475
|
-
// Parse properties list: [key1=value1, key2=value2, ...]
|
|
476
483
|
const propsStr = result.bindings.Props;
|
|
477
484
|
const propKeys = new Set();
|
|
478
|
-
// Extract keys from Props
|
|
479
485
|
const keyMatches = propsStr.matchAll(/(\w+)\s*=/g);
|
|
480
486
|
for (const match of keyMatches) {
|
|
481
487
|
propKeys.add(match[1]);
|
|
482
488
|
}
|
|
483
|
-
// Check for missing required fields
|
|
484
489
|
for (const field of required) {
|
|
485
490
|
if (!propKeys.has(field)) {
|
|
486
491
|
violations.push({
|
|
@@ -515,7 +520,6 @@ async function checkDeprecatedAdrs(prolog) {
|
|
|
515
520
|
.split(",")
|
|
516
521
|
.map((id) => id.trim().replace(/^'|'$/g, ""));
|
|
517
522
|
for (const adrId of adrIds) {
|
|
518
|
-
// Get source for better error message
|
|
519
523
|
const entityResult = await prolog.query(`kb_entity('${adrId}', adr, Props)`);
|
|
520
524
|
let source = "";
|
|
521
525
|
if (entityResult.success && entityResult.bindings.Props) {
|
|
@@ -585,10 +589,8 @@ async function checkSymbolTraceability(prolog, requireAdr) {
|
|
|
585
589
|
if (!result.success || !result.bindings.Violations) {
|
|
586
590
|
return violations;
|
|
587
591
|
}
|
|
588
|
-
// Parse the violations from Prolog format
|
|
589
592
|
const violationsStr = result.bindings.Violations;
|
|
590
593
|
if (violationsStr && violationsStr !== "[]") {
|
|
591
|
-
// Parse each violation term
|
|
592
594
|
const violationRegex = /violation\(([^,]+),'?([^',]+)'?,([^,]+),([^,]+),'?([^']*)'?\)/g;
|
|
593
595
|
let match;
|
|
594
596
|
do {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../src/commands/query.ts"],"names":[],"mappings":"AA2CA,UAAU,YAAY;IACpB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAGD,wBAAsB,YAAY,CAChC,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../src/commands/query.ts"],"names":[],"mappings":"AA2CA,UAAU,YAAY;IACpB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAGD,wBAAsB,YAAY,CAChC,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,IAAI,CAAC,CAyKf"}
|
package/dist/commands/query.js
CHANGED
|
@@ -31,7 +31,6 @@ export async function queryCommand(type, options) {
|
|
|
31
31
|
prolog = new PrologProcess({ timeout: 120000 });
|
|
32
32
|
await prolog.start();
|
|
33
33
|
await prolog.query("set_prolog_flag(answer_write_options, [max_depth(0), spacing(next_argument)])");
|
|
34
|
-
// Resolve branch: allow non-git repos to use default "main" for query
|
|
35
34
|
let currentBranch;
|
|
36
35
|
const branchResult = resolveActiveBranch();
|
|
37
36
|
if ("error" in branchResult) {
|
|
@@ -61,12 +60,11 @@ export async function queryCommand(type, options) {
|
|
|
61
60
|
}
|
|
62
61
|
attached = true;
|
|
63
62
|
let results = [];
|
|
64
|
-
// Query relationships mode
|
|
65
63
|
if (options.relationships) {
|
|
66
64
|
const fromId = String(options.relationships);
|
|
67
65
|
const safeFromId = fromId.replace(/'/g, "''");
|
|
68
|
-
|
|
69
|
-
const goal = `findall([Type,From,To], (
|
|
66
|
+
const relTypesList = REL_TYPES.join(", ");
|
|
67
|
+
const goal = `findall([Type,From,To], (member(Type, [${relTypesList}]), kb_relationship(Type, '${safeFromId}', To), From='${safeFromId}'), Results)`;
|
|
70
68
|
const queryResult = await prolog.query(goal);
|
|
71
69
|
if (queryResult.success && queryResult.bindings.Results) {
|
|
72
70
|
const rows = parseListOfLists(queryResult.bindings.Results);
|
|
@@ -85,9 +83,7 @@ export async function queryCommand(type, options) {
|
|
|
85
83
|
REL_TYPES.includes(rel.type));
|
|
86
84
|
}
|
|
87
85
|
}
|
|
88
|
-
// Query entities mode
|
|
89
86
|
else if (type || options.source) {
|
|
90
|
-
// Validate type if provided
|
|
91
87
|
if (type && !VALID_ENTITY_TYPES.includes(type)) {
|
|
92
88
|
console.error(`Error: Invalid type '${type}'. Valid types: ${VALID_ENTITY_TYPES.join(", ")}`);
|
|
93
89
|
process.exitCode = 1;
|
|
@@ -95,7 +91,6 @@ export async function queryCommand(type, options) {
|
|
|
95
91
|
}
|
|
96
92
|
let goal;
|
|
97
93
|
if (options.source) {
|
|
98
|
-
// Query by source path (substring match)
|
|
99
94
|
const safeSource = String(options.source).replace(/'/g, "\\'");
|
|
100
95
|
if (type) {
|
|
101
96
|
goal = `findall([Id,'${type}',Props], (kb_entities_by_source('${safeSource}', SourceIds), member(Id, SourceIds), kb_entity(Id, '${type}', Props)), Results)`;
|
|
@@ -141,7 +136,6 @@ export async function queryCommand(type, options) {
|
|
|
141
136
|
process.exitCode = 1;
|
|
142
137
|
return;
|
|
143
138
|
}
|
|
144
|
-
// Apply pagination
|
|
145
139
|
const limit = Number.parseInt(options.limit || "100");
|
|
146
140
|
const offset = Number.parseInt(options.offset || "0");
|
|
147
141
|
const paginated = results.slice(offset, offset + limit);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAc,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAyCjE,qBAAa,SAAU,SAAQ,KAAK;gBACtB,OAAO,EAAE,MAAM;CAI5B;AAGD,wBAAsB,WAAW,CAC/B,OAAO,GAAE;IACP,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;CACd,GACL,OAAO,CAAC,WAAW,CAAC,
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAc,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAyCjE,qBAAa,SAAU,SAAQ,KAAK;gBACtB,OAAO,EAAE,MAAM;CAI5B;AAGD,wBAAsB,WAAW,CAC/B,OAAO,GAAE;IACP,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;CACd,GACL,OAAO,CAAC,WAAW,CAAC,CA4VtB;AAED,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC"}
|
package/dist/commands/sync.js
CHANGED
|
@@ -74,10 +74,8 @@ export async function syncCommand(options = {}) {
|
|
|
74
74
|
}
|
|
75
75
|
catch { }
|
|
76
76
|
}
|
|
77
|
-
// Load config
|
|
78
77
|
const config = loadSyncConfig(process.cwd());
|
|
79
78
|
const paths = config.paths;
|
|
80
|
-
// File discovery
|
|
81
79
|
const { markdownFiles, manifestFiles, relationshipsDir } = await discoverSourceFiles(process.cwd(), paths);
|
|
82
80
|
if (process.env.KIBI_DEBUG) {
|
|
83
81
|
try {
|
|
@@ -95,12 +93,10 @@ export async function syncCommand(options = {}) {
|
|
|
95
93
|
const nowMs = Date.now();
|
|
96
94
|
const nextHashes = {};
|
|
97
95
|
const nextSeenAt = {};
|
|
98
|
-
// Extract relationships from shard files
|
|
99
96
|
const shardResults = extractFromRelationshipShards(relationshipsDir);
|
|
100
97
|
const allRelationships = flattenRelationships(shardResults);
|
|
101
98
|
const changedMarkdownFiles = [];
|
|
102
99
|
const changedManifestFiles = [];
|
|
103
|
-
// Detect changed files
|
|
104
100
|
for (const file of sourceFiles) {
|
|
105
101
|
try {
|
|
106
102
|
const key = toCacheKey(file);
|
|
@@ -129,7 +125,6 @@ export async function syncCommand(options = {}) {
|
|
|
129
125
|
}
|
|
130
126
|
const performedFullReindex = changedMarkdownFiles.length === markdownFiles.length &&
|
|
131
127
|
changedManifestFiles.length === manifestFiles.length;
|
|
132
|
-
// Content extraction
|
|
133
128
|
const { results, failedCacheKeys, errors } = await processExtractions(changedMarkdownFiles, changedManifestFiles, validateOnly);
|
|
134
129
|
// Collect INVALID_AUTHORING diagnostics
|
|
135
130
|
for (const err of errors) {
|
|
@@ -160,7 +155,6 @@ export async function syncCommand(options = {}) {
|
|
|
160
155
|
process.exit(0);
|
|
161
156
|
}
|
|
162
157
|
}
|
|
163
|
-
// Refresh symbol manifest coordinates
|
|
164
158
|
for (const file of manifestFiles) {
|
|
165
159
|
try {
|
|
166
160
|
await refreshManifestCoordinates(file, process.cwd());
|
|
@@ -170,7 +164,6 @@ export async function syncCommand(options = {}) {
|
|
|
170
164
|
console.warn(`Warning: Failed to refresh symbol coordinates in ${file}: ${message}`);
|
|
171
165
|
}
|
|
172
166
|
}
|
|
173
|
-
// Early exit if no changes
|
|
174
167
|
if (results.length === 0 && allRelationships.length === 0 && !rebuild) {
|
|
175
168
|
const evictedHashes = {};
|
|
176
169
|
const evictedSeenAt = {};
|
|
@@ -189,7 +182,6 @@ export async function syncCommand(options = {}) {
|
|
|
189
182
|
console.log("✓ Imported 0 entities, 0 relationships (no changes)");
|
|
190
183
|
process.exit(0);
|
|
191
184
|
}
|
|
192
|
-
// Staging setup
|
|
193
185
|
const livePath = path.join(process.cwd(), `.kb/branches/${currentBranch}`);
|
|
194
186
|
const kbExists = existsSync(livePath);
|
|
195
187
|
if (!kbExists && !rebuild) {
|
|
@@ -197,7 +189,6 @@ export async function syncCommand(options = {}) {
|
|
|
197
189
|
}
|
|
198
190
|
const stagingPath = path.join(process.cwd(), `.kb/branches/${currentBranch}.staging`);
|
|
199
191
|
await prepareStagingEnvironment(stagingPath, livePath, rebuild);
|
|
200
|
-
// Persistence to KB
|
|
201
192
|
try {
|
|
202
193
|
const prolog = new PrologProcess({ timeout: 120000 });
|
|
203
194
|
await prolog.start();
|
|
@@ -207,13 +198,10 @@ export async function syncCommand(options = {}) {
|
|
|
207
198
|
throw new SyncError(`Failed to attach to staging KB: ${attachResult.error || "Unknown error"}`);
|
|
208
199
|
}
|
|
209
200
|
const entityIds = new Set();
|
|
210
|
-
// Track entity counts by type
|
|
211
201
|
for (const { entity } of results) {
|
|
212
202
|
entityCounts[entity.type] = (entityCounts[entity.type] || 0) + 1;
|
|
213
203
|
}
|
|
214
|
-
// Persist entities
|
|
215
204
|
const { entityCount, kbModified: entitiesModified } = await persistEntities(prolog, results, entityIds);
|
|
216
|
-
// Validate and filter dangling relationships after entity IDs are known.
|
|
217
205
|
const validationErrors = validateRelationships(allRelationships, entityIds);
|
|
218
206
|
if (validationErrors.length > 0) {
|
|
219
207
|
console.warn(`Warning: ${validationErrors.length} dangling relationship(s) found`);
|
|
@@ -235,9 +223,7 @@ export async function syncCommand(options = {}) {
|
|
|
235
223
|
}
|
|
236
224
|
await prolog.query("kb_detach");
|
|
237
225
|
await prolog.terminate();
|
|
238
|
-
// Publish staging to live
|
|
239
226
|
atomicPublish(stagingPath, livePath);
|
|
240
|
-
// Update cache
|
|
241
227
|
const evictedHashes = {};
|
|
242
228
|
const evictedSeenAt = {};
|
|
243
229
|
for (const [key, hash] of Object.entries(nextHashes)) {
|
|
@@ -304,5 +290,4 @@ export async function syncCommand(options = {}) {
|
|
|
304
290
|
throw error;
|
|
305
291
|
}
|
|
306
292
|
}
|
|
307
|
-
// Export for use by modules that need these functions
|
|
308
293
|
export { normalizeMarkdownPath } from "./sync/discovery.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../src/diagnostics.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,kBAAkB,GAC1B,2BAA2B,GAC3B,YAAY,GACZ,kBAAkB,GAClB,mBAAmB,GACnB,YAAY,GACZ,kBAAkB,GAClB,oBAAoB,CAAC;AAEzB,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,MAAM,eAAe,GACvB,eAAe,GACf,eAAe,GACf,mBAAmB,GACnB,gBAAgB,GAChB,cAAc,GACd,eAAe,CAAC;AAEpB;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,eAAe,EACrB,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,MAAM,GACd,UAAU,CASZ;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,GACX,UAAU,CAOZ;AAED;;GAEG;AACH,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,GAClB,UAAU,CAQZ;AAED;;GAEG;AACH,wBAAgB,gCAAgC,CAC9C,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EAAE,GACtB,UAAU,CAQZ;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../src/diagnostics.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,kBAAkB,GAC1B,2BAA2B,GAC3B,YAAY,GACZ,kBAAkB,GAClB,mBAAmB,GACnB,YAAY,GACZ,kBAAkB,GAClB,oBAAoB,CAAC;AAEzB,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,MAAM,eAAe,GACvB,eAAe,GACf,eAAe,GACf,mBAAmB,GACnB,gBAAgB,GAChB,cAAc,GACd,eAAe,CAAC;AAEpB;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,eAAe,EACrB,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,MAAM,GACd,UAAU,CASZ;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,GACX,UAAU,CAOZ;AAED;;GAEG;AACH,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,GAClB,UAAU,CAQZ;AAED;;GAEG;AACH,wBAAgB,gCAAgC,CAC9C,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,EAAE,GACtB,UAAU,CAQZ;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,CAiD9D;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,UAAU,EAAE,GAAG,KAAK,CAAC;IACxE,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC,CAQD"}
|
package/dist/diagnostics.js
CHANGED
|
@@ -77,13 +77,11 @@ export function formatSyncSummary(summary) {
|
|
|
77
77
|
}
|
|
78
78
|
lines.push(`Total Relationships: ${summary.relationshipCount}`);
|
|
79
79
|
lines.push("");
|
|
80
|
-
// Status
|
|
81
80
|
lines.push(`Status: ${summary.success ? "✓ Success" : "✗ Failed"}`);
|
|
82
81
|
lines.push(`Published: ${summary.published ? "Yes" : "No"}`);
|
|
83
82
|
if (summary.durationMs !== undefined) {
|
|
84
83
|
lines.push(`Duration: ${summary.durationMs}ms`);
|
|
85
84
|
}
|
|
86
|
-
// Failures
|
|
87
85
|
if (summary.failures.length > 0) {
|
|
88
86
|
lines.push("");
|
|
89
87
|
lines.push(`Failures (${summary.failures.length}):`);
|
|
@@ -20,6 +20,8 @@ export interface ExtractedRelationship {
|
|
|
20
20
|
export interface ExtractionResult {
|
|
21
21
|
entity: ExtractedEntity;
|
|
22
22
|
relationships: ExtractedRelationship[];
|
|
23
|
+
/** The per-symbol source code file, distinct from the manifest file path. */
|
|
24
|
+
sourceFile?: string;
|
|
23
25
|
}
|
|
24
26
|
export declare class ManifestError extends Error {
|
|
25
27
|
filePath: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/extractors/manifest.ts"],"names":[],"mappings":"AAsBA,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,eAAe,CAAC;IACxB,aAAa,EAAE,qBAAqB,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/extractors/manifest.ts"],"names":[],"mappings":"AAsBA,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,eAAe,CAAC;IACxB,aAAa,EAAE,qBAAqB,EAAE,CAAC;IACvC,6EAA6E;IAC7E,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,aAAc,SAAQ,KAAK;IAG7B,QAAQ,EAAE,MAAM;gBADvB,OAAO,EAAE,MAAM,EACR,QAAQ,EAAE,MAAM;CAK1B;AAwBD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,EAAE,CA6FxE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/extractors/markdown.ts"],"names":[],"mappings":"AAsBA,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,eAAe,CAAC;IACxB,aAAa,EAAE,qBAAqB,EAAE,CAAC;CACxC;AAaD,qBAAa,gBAAiB,SAAQ,KAAK;IAOhC,QAAQ,EAAE,MAAM;IANlB,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,MAAM,CAAC;gBAG5B,OAAO,EAAE,MAAM,EACR,QAAQ,EAAE,MAAM,EACvB,OAAO,CAAC,EAAE;QACR,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,aAAa,CAAC,EAAE,MAAM,CAAC;KACxB;IASM,QAAQ;CAUlB;AAED,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,UAAU,EAAE,MAAM,GACjB,MAAM,EAAE,CA8CV;AAGD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,
|
|
1
|
+
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../src/extractors/markdown.ts"],"names":[],"mappings":"AAsBA,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,eAAe,CAAC;IACxB,aAAa,EAAE,qBAAqB,EAAE,CAAC;CACxC;AAaD,qBAAa,gBAAiB,SAAQ,KAAK;IAOhC,QAAQ,EAAE,MAAM;IANlB,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,MAAM,CAAC;gBAG5B,OAAO,EAAE,MAAM,EACR,QAAQ,EAAE,MAAM,EACvB,OAAO,CAAC,EAAE;QACR,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,aAAa,CAAC,EAAE,MAAM,CAAC;KACxB;IASM,QAAQ;CAUlB;AAED,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,UAAU,EAAE,MAAM,GACjB,MAAM,EAAE,CA8CV;AAGD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CA4JtE;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CASjE"}
|
|
@@ -101,7 +101,7 @@ export function extractFromMarkdown(filePath) {
|
|
|
101
101
|
throw new FrontmatterError(`Failed to read file: ${error instanceof Error ? error.message : String(error)}`, filePath, { classification: "File Read Error" });
|
|
102
102
|
}
|
|
103
103
|
try {
|
|
104
|
-
const { data
|
|
104
|
+
const { data } = matter(content);
|
|
105
105
|
if (content.trim().startsWith("---")) {
|
|
106
106
|
const parts = content.split("---");
|
|
107
107
|
if (parts.length < 3) {
|
|
@@ -136,6 +136,14 @@ export function extractFromMarkdown(filePath) {
|
|
|
136
136
|
const relationships = [];
|
|
137
137
|
if (Array.isArray(data.links)) {
|
|
138
138
|
for (const link of data.links) {
|
|
139
|
+
if (typeof link === "string") {
|
|
140
|
+
relationships.push({
|
|
141
|
+
type: "relates_to",
|
|
142
|
+
from: id,
|
|
143
|
+
to: link,
|
|
144
|
+
});
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
139
147
|
if (link &&
|
|
140
148
|
typeof link === "object" &&
|
|
141
149
|
typeof link.type === "string" &&
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"symbols-coordinator.d.ts","sourceRoot":"","sources":["../../../src/public/extractors/symbols-coordinator.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"symbols-coordinator.d.ts","sourceRoot":"","sources":["../../../src/public/extractors/symbols-coordinator.ts"],"names":[],"mappings":"AAkBA,OAAO,EACL,uBAAuB,EACvB,KAAK,mBAAmB,GACzB,MAAM,yCAAyC,CAAC"}
|
|
@@ -14,6 +14,5 @@
|
|
|
14
14
|
|
|
15
15
|
You should have received a copy of the GNU Affero General Public License
|
|
16
16
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
-
*/
|
|
18
|
-
// Public re-export of symbols coordinator
|
|
17
|
+
*/
|
|
19
18
|
export { enrichSymbolCoordinates, } from "../../extractors/symbols-coordinator.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/public/prolog/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/public/prolog/index.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -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":"AAkBA,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgEjB,CAAC;AAEF,eAAe,YAAY,CAAC"}
|
|
@@ -14,9 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
You should have received a copy of the GNU Affero General Public License
|
|
16
16
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
-
*/
|
|
18
|
-
// Public export of entity schema
|
|
19
|
-
// Generated from entity.schema.json
|
|
17
|
+
*/
|
|
20
18
|
const entitySchema = {
|
|
21
19
|
$id: "entity.schema.json",
|
|
22
20
|
title: "Entity",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relationship.d.ts","sourceRoot":"","sources":["../../../src/public/schemas/relationship.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"relationship.d.ts","sourceRoot":"","sources":["../../../src/public/schemas/relationship.ts"],"names":[],"mappings":"AAkBA,QAAA,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiCvB,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
|
|
@@ -14,9 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
You should have received a copy of the GNU Affero General Public License
|
|
16
16
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
-
*/
|
|
18
|
-
// Public export of relationship schema
|
|
19
|
-
// Generated from relationship.schema.json
|
|
17
|
+
*/
|
|
20
18
|
const relationshipSchema = {
|
|
21
19
|
$id: "relationship.schema.json",
|
|
22
20
|
title: "Relationship",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git-staged.d.ts","sourceRoot":"","sources":["../../src/traceability/git-staged.ts"],"names":[],"mappings":"AAEA,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;
|
|
1
|
+
{"version":3,"file":"git-staged.d.ts","sourceRoot":"","sources":["../../src/traceability/git-staged.ts"],"names":[],"mappings":"AAEA,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,CAChC,QAAQ,EAAE,MAAM,EAChB,SAAS,UAAQ,GAChB,SAAS,EAAE,CAoBb;AAED;;GAEG;AAEH,wBAAgB,cAAc,CAAC,IAAI,GAAE,MAAiB,GAAG,UAAU,EAAE,CA6FpE;AAED,eAAe,cAAc,CAAC"}
|
|
@@ -13,16 +13,28 @@ function runGit(cmd, exec) {
|
|
|
13
13
|
/**
|
|
14
14
|
* Parse null-separated name-status output from git
|
|
15
15
|
*/
|
|
16
|
+
// implements REQ-014
|
|
16
17
|
export function parseNameStatusNull(input) {
|
|
17
18
|
if (!input)
|
|
18
19
|
return [];
|
|
19
20
|
const entries = input.split("\0").filter(Boolean);
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
const rows = [];
|
|
22
|
+
for (let i = 0; i < entries.length;) {
|
|
23
|
+
const entry = entries[i] ?? "";
|
|
24
|
+
if (entry.includes("\t")) {
|
|
25
|
+
const cols = entry.split("\t");
|
|
26
|
+
rows.push({ status: cols[0] ?? "", parts: cols.slice(1) });
|
|
27
|
+
i += 1;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const status = entry;
|
|
31
|
+
const isRenameOrCopy = /^[RC]\d*$/.test(status);
|
|
32
|
+
const partCount = isRenameOrCopy ? 2 : 1;
|
|
33
|
+
const parts = entries.slice(i + 1, i + 1 + partCount);
|
|
34
|
+
rows.push({ status, parts });
|
|
35
|
+
i += 1 + partCount;
|
|
36
|
+
}
|
|
37
|
+
return rows;
|
|
26
38
|
}
|
|
27
39
|
const SUPPORTED_EXT = new Set([
|
|
28
40
|
".ts",
|
|
@@ -34,6 +46,7 @@ const SUPPORTED_EXT = new Set([
|
|
|
34
46
|
".mjs",
|
|
35
47
|
".cjs",
|
|
36
48
|
]);
|
|
49
|
+
const SUPPORTED_MANIFEST = new Set(["symbols.yaml", "symbols.yml"]);
|
|
37
50
|
const ENTITY_MARKDOWN_DIRS = ["/requirements/", "/scenarios/", "/tests/"];
|
|
38
51
|
function shouldLogTraceDebug() {
|
|
39
52
|
return Boolean(process.env.KIBI_TRACE || process.env.KIBI_DEBUG);
|
|
@@ -57,6 +70,16 @@ function isEntityMarkdown(p) {
|
|
|
57
70
|
}
|
|
58
71
|
return false;
|
|
59
72
|
}
|
|
73
|
+
function isManifestFile(p) {
|
|
74
|
+
const base = p.split(/[\/]/).pop();
|
|
75
|
+
if (!base)
|
|
76
|
+
return false;
|
|
77
|
+
for (const name of SUPPORTED_MANIFEST) {
|
|
78
|
+
if (base === name)
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
60
83
|
/**
|
|
61
84
|
* Parse unified diff hunks (new-file coordinates) from git diff output
|
|
62
85
|
*/
|
|
@@ -86,6 +109,7 @@ export function parseHunksFromDiff(diffText, isNewFile = false) {
|
|
|
86
109
|
/**
|
|
87
110
|
* Get staged files with statuses, hunks and content.
|
|
88
111
|
*/
|
|
112
|
+
// implements REQ-014
|
|
89
113
|
export function getStagedFiles(exec = execSync) {
|
|
90
114
|
// 1. get staged name-status -z
|
|
91
115
|
let nameStatus;
|
|
@@ -115,7 +139,9 @@ export function getStagedFiles(exec = execSync) {
|
|
|
115
139
|
path = entry.parts[1];
|
|
116
140
|
}
|
|
117
141
|
}
|
|
118
|
-
if (!hasSupportedExt(path) &&
|
|
142
|
+
if (!hasSupportedExt(path) &&
|
|
143
|
+
!isEntityMarkdown(path) &&
|
|
144
|
+
!isManifestFile(path)) {
|
|
119
145
|
if (shouldLogTraceDebug()) {
|
|
120
146
|
console.debug(`Skipping unsupported extension: ${path}`);
|
|
121
147
|
}
|
|
@@ -11,5 +11,10 @@ export interface ExtractedSymbol {
|
|
|
11
11
|
hunkRanges: HunkRange[];
|
|
12
12
|
reqLinks: string[];
|
|
13
13
|
}
|
|
14
|
-
export
|
|
14
|
+
export interface ManifestLookupEntry {
|
|
15
|
+
id: string;
|
|
16
|
+
links?: string[];
|
|
17
|
+
}
|
|
18
|
+
export type ManifestLookup = Map<string, ManifestLookupEntry>;
|
|
19
|
+
export declare function extractSymbolsFromStagedFile(stagedFile: StagedFile, manifestLookup?: ManifestLookup): ExtractedSymbol[];
|
|
15
20
|
//# sourceMappingURL=symbol-extract.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"symbol-extract.d.ts","sourceRoot":"","sources":["../../src/traceability/symbol-extract.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7D,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,GAAG,SAAS,CAAC;IAC7D,QAAQ,EAAE;QACR,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;
|
|
1
|
+
{"version":3,"file":"symbol-extract.d.ts","sourceRoot":"","sources":["../../src/traceability/symbol-extract.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7D,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,GAAG,SAAS,CAAC;IAC7D,QAAQ,EAAE;QACR,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,MAAM,cAAc,GAAG,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;AA4D9D,wBAAgB,4BAA4B,CAC1C,UAAU,EAAE,UAAU,EACtB,cAAc,CAAC,EAAE,cAAc,GAC9B,eAAe,EAAE,CA+MnB"}
|
|
@@ -28,7 +28,10 @@ function parseReqDirectives(text) {
|
|
|
28
28
|
const regex = new RegExp(`implements\\s*:?\\s*(${REQ_ID}(?:\\s*,\\s*${REQ_ID})*)\\s*$`, "gim");
|
|
29
29
|
const reqs = new Set();
|
|
30
30
|
let m;
|
|
31
|
-
while (
|
|
31
|
+
while (true) {
|
|
32
|
+
m = regex.exec(text);
|
|
33
|
+
if (!m)
|
|
34
|
+
break;
|
|
32
35
|
const list = m[1];
|
|
33
36
|
for (const part of list.split(/[,\s]+/)) {
|
|
34
37
|
const p = part.trim();
|
|
@@ -42,9 +45,9 @@ function parseReqDirectives(text) {
|
|
|
42
45
|
function rangesIntersect(aStart, aEnd, bStart, bEnd) {
|
|
43
46
|
return aStart <= bEnd && bStart <= aEnd;
|
|
44
47
|
}
|
|
45
|
-
export function extractSymbolsFromStagedFile(stagedFile) {
|
|
48
|
+
export function extractSymbolsFromStagedFile(stagedFile, manifestLookup) {
|
|
46
49
|
const content = stagedFile.content ?? "";
|
|
47
|
-
const sha = computeContentSha(content
|
|
50
|
+
const sha = computeContentSha(`${content}|${stagedFile.path}`);
|
|
48
51
|
// TTL cache lookup
|
|
49
52
|
const now = Date.now();
|
|
50
53
|
let cached = sourceFileCache.get(sha);
|
|
@@ -52,7 +55,7 @@ export function extractSymbolsFromStagedFile(stagedFile) {
|
|
|
52
55
|
// create or recreate SourceFile in project (in-memory)
|
|
53
56
|
try {
|
|
54
57
|
const scriptKind = chooseScriptKind(stagedFile.path);
|
|
55
|
-
const sf = project.createSourceFile(stagedFile.path
|
|
58
|
+
const sf = project.createSourceFile(`${stagedFile.path}::staged`, content, {
|
|
56
59
|
overwrite: true,
|
|
57
60
|
scriptKind,
|
|
58
61
|
});
|
|
@@ -85,13 +88,13 @@ export function extractSymbolsFromStagedFile(stagedFile) {
|
|
|
85
88
|
const start = nameNode ? nameNode.getStart() : fn.getStart();
|
|
86
89
|
const end = fn.getEnd();
|
|
87
90
|
const span = getSpan(start, end);
|
|
88
|
-
const reqLinks = parseReqDirectives(fn.getFullText()
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const
|
|
91
|
+
const reqLinks = parseReqDirectives(`${fn.getFullText()}\n${fn
|
|
92
|
+
.getJsDocs()
|
|
93
|
+
.map((d) => d.getFullText())
|
|
94
|
+
.join("\n")}`);
|
|
95
|
+
const { id, reqLinks: manifestLinks } = resolveSymbolId(stagedFile.path, name, manifestLookup);
|
|
96
|
+
// Merge manifest links with inline directive links (manifest links as fallback)
|
|
97
|
+
const mergedReqLinks = reqLinks.length > 0 ? reqLinks : (manifestLinks ?? []);
|
|
95
98
|
results.push({
|
|
96
99
|
id,
|
|
97
100
|
name,
|
|
@@ -102,7 +105,7 @@ export function extractSymbolsFromStagedFile(stagedFile) {
|
|
|
102
105
|
endLine: span.endLine,
|
|
103
106
|
},
|
|
104
107
|
hunkRanges: intersectingHunks(span.startLine, span.endLine, stagedFile.hunkRanges),
|
|
105
|
-
reqLinks,
|
|
108
|
+
reqLinks: mergedReqLinks,
|
|
106
109
|
});
|
|
107
110
|
}
|
|
108
111
|
catch { }
|
|
@@ -116,13 +119,13 @@ export function extractSymbolsFromStagedFile(stagedFile) {
|
|
|
116
119
|
const start = cls.getNameNode()?.getStart() ?? cls.getStart();
|
|
117
120
|
const end = cls.getEnd();
|
|
118
121
|
const span = getSpan(start, end);
|
|
119
|
-
const reqLinks = parseReqDirectives(cls.getText()
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const
|
|
122
|
+
const reqLinks = parseReqDirectives(`${cls.getText()}\n${cls
|
|
123
|
+
.getJsDocs()
|
|
124
|
+
.map((d) => d.getFullText())
|
|
125
|
+
.join("\n")}`);
|
|
126
|
+
const { id, reqLinks: manifestLinks } = resolveSymbolId(stagedFile.path, name, manifestLookup);
|
|
127
|
+
// Merge manifest links with inline directive links (manifest links as fallback)
|
|
128
|
+
const mergedReqLinks = reqLinks.length > 0 ? reqLinks : (manifestLinks ?? []);
|
|
126
129
|
results.push({
|
|
127
130
|
id,
|
|
128
131
|
name,
|
|
@@ -133,7 +136,7 @@ export function extractSymbolsFromStagedFile(stagedFile) {
|
|
|
133
136
|
endLine: span.endLine,
|
|
134
137
|
},
|
|
135
138
|
hunkRanges: intersectingHunks(span.startLine, span.endLine, stagedFile.hunkRanges),
|
|
136
|
-
reqLinks,
|
|
139
|
+
reqLinks: mergedReqLinks,
|
|
137
140
|
});
|
|
138
141
|
}
|
|
139
142
|
catch { }
|
|
@@ -148,7 +151,9 @@ export function extractSymbolsFromStagedFile(stagedFile) {
|
|
|
148
151
|
const end = en.getEnd();
|
|
149
152
|
const span = getSpan(start, end);
|
|
150
153
|
const reqLinks = parseReqDirectives(en.getText());
|
|
151
|
-
const id = resolveSymbolId(stagedFile.path, name);
|
|
154
|
+
const { id, reqLinks: manifestLinks } = resolveSymbolId(stagedFile.path, name, manifestLookup);
|
|
155
|
+
// Merge manifest links with inline directive links (manifest links as fallback)
|
|
156
|
+
const mergedReqLinks = reqLinks.length > 0 ? reqLinks : (manifestLinks ?? []);
|
|
152
157
|
results.push({
|
|
153
158
|
id,
|
|
154
159
|
name,
|
|
@@ -159,7 +164,7 @@ export function extractSymbolsFromStagedFile(stagedFile) {
|
|
|
159
164
|
endLine: span.endLine,
|
|
160
165
|
},
|
|
161
166
|
hunkRanges: intersectingHunks(span.startLine, span.endLine, stagedFile.hunkRanges),
|
|
162
|
-
reqLinks,
|
|
167
|
+
reqLinks: mergedReqLinks,
|
|
163
168
|
});
|
|
164
169
|
}
|
|
165
170
|
catch { }
|
|
@@ -175,7 +180,9 @@ export function extractSymbolsFromStagedFile(stagedFile) {
|
|
|
175
180
|
const end = decl.getEnd();
|
|
176
181
|
const span = getSpan(start, end);
|
|
177
182
|
const reqLinks = parseReqDirectives(decl.getText());
|
|
178
|
-
const id = resolveSymbolId(stagedFile.path, name);
|
|
183
|
+
const { id, reqLinks: manifestLinks } = resolveSymbolId(stagedFile.path, name, manifestLookup);
|
|
184
|
+
// Merge manifest links with inline directive links (manifest links as fallback)
|
|
185
|
+
const mergedReqLinks = reqLinks.length > 0 ? reqLinks : (manifestLinks ?? []);
|
|
179
186
|
results.push({
|
|
180
187
|
id,
|
|
181
188
|
name,
|
|
@@ -186,7 +193,7 @@ export function extractSymbolsFromStagedFile(stagedFile) {
|
|
|
186
193
|
endLine: span.endLine,
|
|
187
194
|
},
|
|
188
195
|
hunkRanges: intersectingHunks(span.startLine, span.endLine, stagedFile.hunkRanges),
|
|
189
|
-
reqLinks,
|
|
196
|
+
reqLinks: mergedReqLinks,
|
|
190
197
|
});
|
|
191
198
|
}
|
|
192
199
|
catch { }
|
|
@@ -208,21 +215,42 @@ function intersectingHunks(startLine, endLine, hunks) {
|
|
|
208
215
|
}
|
|
209
216
|
return out;
|
|
210
217
|
}
|
|
211
|
-
function resolveSymbolId(filePath, name) {
|
|
218
|
+
function resolveSymbolId(filePath, name, manifestLookup) {
|
|
219
|
+
// First, check the provided manifest lookup if available
|
|
220
|
+
if (manifestLookup) {
|
|
221
|
+
// Normalize the source file path for consistent lookup
|
|
222
|
+
const normalizedSource = filePath.startsWith("/")
|
|
223
|
+
? filePath
|
|
224
|
+
: `${filePath}`;
|
|
225
|
+
const lookupKey = `${normalizedSource}:${name}`;
|
|
226
|
+
const entry = manifestLookup.get(lookupKey);
|
|
227
|
+
if (entry) {
|
|
228
|
+
return { id: entry.id, reqLinks: entry.links };
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// Fallback: attempt to read manifest entries from a local symbols.yaml (best-effort)
|
|
212
232
|
try {
|
|
213
|
-
//
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
233
|
+
// Try to find a symbols.yaml in the same directory as the file
|
|
234
|
+
const dir = filePath.substring(0, filePath.lastIndexOf("/"));
|
|
235
|
+
if (dir) {
|
|
236
|
+
const manifestPath = `${dir}/symbols.yaml`;
|
|
237
|
+
const ents = extractFromManifest(manifestPath);
|
|
238
|
+
for (const e of ents) {
|
|
239
|
+
if (e.entity.title === name) {
|
|
240
|
+
// Extract requirement links from relationships
|
|
241
|
+
const reqLinks = e.relationships
|
|
242
|
+
.filter((r) => r.type === "implements" && r.to.match(/^[A-Z][A-Z0-9\-_]*$/))
|
|
243
|
+
.map((r) => r.to);
|
|
244
|
+
return { id: e.entity.id, reqLinks };
|
|
245
|
+
}
|
|
246
|
+
}
|
|
219
247
|
}
|
|
220
248
|
}
|
|
221
249
|
catch {
|
|
222
|
-
// ignore
|
|
250
|
+
// ignore - no local manifest or parse error
|
|
223
251
|
}
|
|
224
|
-
// deterministic id: sha(file:path:name)
|
|
252
|
+
// Final fallback: deterministic id: sha(file:path:name)
|
|
225
253
|
const h = createHash("sha256");
|
|
226
254
|
h.update(`${filePath}:${name}`);
|
|
227
|
-
return h.digest("hex").slice(0, 16);
|
|
255
|
+
return { id: h.digest("hex").slice(0, 16) };
|
|
228
256
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"temp-kb.d.ts","sourceRoot":"","sources":["../../src/traceability/temp-kb.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,aAAa,CAAC;CACvB;AAmDD,iBAAe,cAAc,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAe/D;AAED,OAAO,EAAE,cAAc,EAAE,CAAC;AAE1B,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CA2C7E;
|
|
1
|
+
{"version":3,"file":"temp-kb.d.ts","sourceRoot":"","sources":["../../src/traceability/temp-kb.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,aAAa,CAAC;CACvB;AAmDD,iBAAe,cAAc,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAe/D;AAED,OAAO,EAAE,cAAc,EAAE,CAAC;AAE1B,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CA2C7E;AAGD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,eAAe,EAAE,GAAG,MAAM,CAkBrE;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA2BlE"}
|
|
@@ -82,14 +82,15 @@ export async function createTempKb(baseKbPath) {
|
|
|
82
82
|
trace(`temporary KB ready at ${kbPath}`);
|
|
83
83
|
return ctx;
|
|
84
84
|
}
|
|
85
|
+
// implements REQ-014
|
|
85
86
|
export function createOverlayFacts(symbols) {
|
|
86
87
|
const lines = [];
|
|
87
88
|
for (const symbol of symbols) {
|
|
88
|
-
lines.push(`changed_symbol(${escapePrologAtom(symbol.id)}).`);
|
|
89
|
-
lines.push(`changed_symbol_loc(${escapePrologAtom(symbol.id)}, ${escapePrologAtom(symbol.location.file)}, ${symbol.location.startLine}, 0, ${escapePrologAtom(symbol.name)}).`);
|
|
89
|
+
lines.push(`kb:changed_symbol(${escapePrologAtom(symbol.id)}).`);
|
|
90
|
+
lines.push(`kb:changed_symbol_loc(${escapePrologAtom(symbol.id)}, ${escapePrologAtom(symbol.location.file)}, ${symbol.location.startLine}, 0, ${escapePrologAtom(symbol.name)}).`);
|
|
90
91
|
// Emit overlay facts for requirement links from code-comment directives.
|
|
91
92
|
for (const reqId of symbol.reqLinks) {
|
|
92
|
-
lines.push(`changed_symbol_req(${escapePrologAtom(symbol.id)}, ${escapePrologAtom(reqId)}).`);
|
|
93
|
+
lines.push(`kb:changed_symbol_req(${escapePrologAtom(symbol.id)}, ${escapePrologAtom(reqId)}).`);
|
|
93
94
|
}
|
|
94
95
|
}
|
|
95
96
|
return lines.join("\n");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/traceability/validate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,aAAa,CAAC;CACvB;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;CACvB;
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/traceability/validate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,aAAa,CAAC;CACvB;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;CACvB;AAiHD,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,SAAS,EAAE,CAAC,CAqCtB;AAED,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,MAAM,CAiBhE"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
function unquoteAtom(
|
|
1
|
+
function unquoteAtom(value) {
|
|
2
2
|
// remove surrounding single quotes and unescape doubled quotes
|
|
3
|
-
|
|
4
|
-
if (
|
|
5
|
-
|
|
3
|
+
const trimmed = value.trim();
|
|
4
|
+
if (trimmed.startsWith("'") && trimmed.endsWith("'")) {
|
|
5
|
+
return trimmed.slice(1, -1).replace(/''/g, "'");
|
|
6
6
|
}
|
|
7
|
-
return
|
|
7
|
+
return trimmed;
|
|
8
8
|
}
|
|
9
9
|
function splitTopLevelComma(s) {
|
|
10
10
|
const parts = [];
|
|
@@ -98,14 +98,15 @@ function parsePrologListOfLists(value) {
|
|
|
98
98
|
}
|
|
99
99
|
return out;
|
|
100
100
|
}
|
|
101
|
+
// implements REQ-014
|
|
101
102
|
export async function validateStagedSymbols(options) {
|
|
102
103
|
const { minLinks, prolog } = options;
|
|
103
|
-
const goal = `findall([Sym,Count,File,Line,Col,Name], changed_symbol_violation(Sym, ${minLinks}, Count, File, Line, Col, Name), Rows)`;
|
|
104
|
+
const goal = `findall([Sym,Count,File,Line,Col,Name], kb:changed_symbol_violation(Sym, ${minLinks}, Count, File, Line, Col, Name), Rows)`;
|
|
104
105
|
const res = await prolog.query(goal);
|
|
105
106
|
if (!res.success) {
|
|
106
107
|
throw new Error(`Prolog query failed: ${res.error || "unknown error"}`);
|
|
107
108
|
}
|
|
108
|
-
const rowsRaw = res.bindings
|
|
109
|
+
const rowsRaw = res.bindings.Rows ?? "[]";
|
|
109
110
|
const lists = parsePrologListOfLists(rowsRaw);
|
|
110
111
|
const violations = [];
|
|
111
112
|
for (const row of lists) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kibi-cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.8",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Kibi CLI for knowledge base management",
|
|
6
6
|
"engines": {
|
|
@@ -71,6 +71,10 @@
|
|
|
71
71
|
"kibi-core": "^0.1.10",
|
|
72
72
|
"ts-morph": "^23.0.0"
|
|
73
73
|
},
|
|
74
|
+
"devDependencies": {
|
|
75
|
+
"@types/node": "latest",
|
|
76
|
+
"typescript": "^5.7.0"
|
|
77
|
+
},
|
|
74
78
|
"license": "AGPL-3.0-or-later",
|
|
75
79
|
"author": "Piotr Franczyk",
|
|
76
80
|
"repository": {
|
|
@@ -14,9 +14,8 @@
|
|
|
14
14
|
|
|
15
15
|
You should have received a copy of the GNU Affero General Public License
|
|
16
16
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
-
*/
|
|
17
|
+
*/
|
|
18
18
|
|
|
19
|
-
// Public re-export of symbols coordinator
|
|
20
19
|
export {
|
|
21
20
|
enrichSymbolCoordinates,
|
|
22
21
|
type ManifestSymbolEntry,
|
|
@@ -14,10 +14,8 @@
|
|
|
14
14
|
|
|
15
15
|
You should have received a copy of the GNU Affero General Public License
|
|
16
16
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
-
*/
|
|
17
|
+
*/
|
|
18
18
|
|
|
19
|
-
// Public export of entity schema
|
|
20
|
-
// Generated from entity.schema.json
|
|
21
19
|
const entitySchema = {
|
|
22
20
|
$id: "entity.schema.json",
|
|
23
21
|
title: "Entity",
|
|
@@ -14,10 +14,8 @@
|
|
|
14
14
|
|
|
15
15
|
You should have received a copy of the GNU Affero General Public License
|
|
16
16
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
-
*/
|
|
17
|
+
*/
|
|
18
18
|
|
|
19
|
-
// Public export of relationship schema
|
|
20
|
-
// Generated from relationship.schema.json
|
|
21
19
|
const relationshipSchema = {
|
|
22
20
|
$id: "relationship.schema.json",
|
|
23
21
|
title: "Relationship",
|