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.
Files changed (36) hide show
  1. package/dist/commands/aggregated-checks.js +4 -6
  2. package/dist/commands/check.d.ts.map +1 -1
  3. package/dist/commands/check.js +26 -24
  4. package/dist/commands/query.d.ts.map +1 -1
  5. package/dist/commands/query.js +2 -8
  6. package/dist/commands/sync.d.ts.map +1 -1
  7. package/dist/commands/sync.js +0 -15
  8. package/dist/diagnostics.d.ts.map +1 -1
  9. package/dist/diagnostics.js +0 -2
  10. package/dist/extractors/manifest.d.ts +2 -0
  11. package/dist/extractors/manifest.d.ts.map +1 -1
  12. package/dist/extractors/manifest.js +1 -0
  13. package/dist/extractors/markdown.d.ts.map +1 -1
  14. package/dist/extractors/markdown.js +9 -1
  15. package/dist/public/extractors/symbols-coordinator.d.ts.map +1 -1
  16. package/dist/public/extractors/symbols-coordinator.js +1 -2
  17. package/dist/public/prolog/index.d.ts.map +1 -1
  18. package/dist/public/prolog/index.js +1 -2
  19. package/dist/public/schemas/entity.d.ts.map +1 -1
  20. package/dist/public/schemas/entity.js +1 -3
  21. package/dist/public/schemas/relationship.d.ts.map +1 -1
  22. package/dist/public/schemas/relationship.js +1 -3
  23. package/dist/traceability/git-staged.d.ts.map +1 -1
  24. package/dist/traceability/git-staged.js +33 -7
  25. package/dist/traceability/symbol-extract.d.ts +6 -1
  26. package/dist/traceability/symbol-extract.d.ts.map +1 -1
  27. package/dist/traceability/symbol-extract.js +62 -34
  28. package/dist/traceability/temp-kb.d.ts.map +1 -1
  29. package/dist/traceability/temp-kb.js +4 -3
  30. package/dist/traceability/validate.d.ts.map +1 -1
  31. package/dist/traceability/validate.js +8 -7
  32. package/package.json +5 -1
  33. package/src/public/extractors/symbols-coordinator.ts +1 -2
  34. package/src/public/prolog/index.ts +1 -2
  35. package/src/public/schemas/entity.ts +1 -3
  36. 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
- console.warn("Aggregated checks query failed, falling back to individual checks");
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
- console.warn("Failed to parse violations JSON:", parseError);
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
- console.warn("Error running aggregated checks:", error);
63
- return [];
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":"AA2CA,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,CA0QvE"}
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"}
@@ -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
- // Get staged files
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,CAgLf"}
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"}
@@ -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
- // Query all relationship types for the given source ID
69
- const goal = `findall([Type,From,To], (From='${safeFromId}', kb_relationship(Type, From, To)), Results)`;
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,CA0WtB;AAGD,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,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"}
@@ -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,CAmD9D;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,UAAU,EAAE,GACxB,KAAK,CAAC;IACP,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"}
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"}
@@ -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;CACxC;AAED,qBAAa,aAAc,SAAQ,KAAK;IAG7B,QAAQ,EAAE,MAAM;gBADvB,OAAO,EAAE,MAAM,EACR,QAAQ,EAAE,MAAM;CAK1B;AAuBD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,EAAE,CA4FxE"}
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"}
@@ -94,6 +94,7 @@ export function extractFromManifest(filePath) {
94
94
  text_ref: symbol.text_ref,
95
95
  },
96
96
  relationships,
97
+ sourceFile: symbol.sourceFile ?? symbol.source,
97
98
  };
98
99
  });
99
100
  }
@@ -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,CAmJtE;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CASjE"}
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, content: body } = matter(content);
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":"AAmBA,OAAO,EACL,uBAAuB,EACvB,KAAK,mBAAmB,GACzB,MAAM,yCAAyC,CAAC"}
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":"AAmBA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/public/prolog/index.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,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 PrologProcess
17
+ */
19
18
  export { PrologProcess } from "../../prolog.js";
@@ -1 +1 @@
1
- {"version":3,"file":"entity.d.ts","sourceRoot":"","sources":["../../../src/public/schemas/entity.ts"],"names":[],"mappings":"AAoBA,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgEjB,CAAC;AAEF,eAAe,YAAY,CAAC"}
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":"AAoBA,QAAA,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiCvB,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
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;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,GACZ,KAAK,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAS5C;AAsCD;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,SAAS,UAAQ,GAChB,SAAS,EAAE,CAoBb;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,GAAE,MAAiB,GAAG,UAAU,EAAE,CA4FpE;AAED,eAAe,cAAc,CAAC"}
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
- return entries.map((entry) => {
21
- const cols = entry.split("\t");
22
- const status = cols[0];
23
- const parts = cols.slice(1);
24
- return { status, parts };
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) && !isEntityMarkdown(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 declare function extractSymbolsFromStagedFile(stagedFile: StagedFile): ExtractedSymbol[];
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;AA0DD,wBAAgB,4BAA4B,CAC1C,UAAU,EAAE,UAAU,GACrB,eAAe,EAAE,CAuLnB"}
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 ((m = regex.exec(text))) {
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 + "|" + stagedFile.path);
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 + "::staged", content, {
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
- "\n" +
90
- fn
91
- .getJsDocs()
92
- .map((d) => d.getFullText())
93
- .join("\n"));
94
- const id = resolveSymbolId(stagedFile.path, name);
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
- "\n" +
121
- cls
122
- .getJsDocs()
123
- .map((d) => d.getFullText())
124
- .join("\n"));
125
- const id = resolveSymbolId(stagedFile.path, name);
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
- // attempt to read manifest entries for explicit id (best-effort)
214
- // extractFromManifest expects a file path; if manifest not present it will throw — catch it
215
- const ents = extractFromManifest(filePath);
216
- for (const e of ents) {
217
- if (e.entity.title === name)
218
- return e.entity.id;
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;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,eAAe,EAAE,GAAG,MAAM,CAkBrE;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA2BlE"}
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;AAgHD,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
+ {"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(v) {
1
+ function unquoteAtom(value) {
2
2
  // remove surrounding single quotes and unescape doubled quotes
3
- v = v.trim();
4
- if (v.startsWith("'") && v.endsWith("'")) {
5
- v = v.slice(1, -1).replace(/''/g, "'");
3
+ const trimmed = value.trim();
4
+ if (trimmed.startsWith("'") && trimmed.endsWith("'")) {
5
+ return trimmed.slice(1, -1).replace(/''/g, "'");
6
6
  }
7
- return v;
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["Rows"] ?? "[]";
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.7",
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,7 +14,6 @@
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 PrologProcess
20
19
  export { PrologProcess } from "../../prolog.js";
@@ -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",