kibi-cli 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/cli.d.ts +4 -1
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +31 -16
  4. package/dist/commands/check.d.ts +5 -8
  5. package/dist/commands/check.d.ts.map +1 -1
  6. package/dist/commands/check.js +41 -62
  7. package/dist/commands/discovery-shared.d.ts.map +1 -1
  8. package/dist/commands/discovery-shared.js +23 -22
  9. package/dist/commands/doctor.d.ts +3 -1
  10. package/dist/commands/doctor.d.ts.map +1 -1
  11. package/dist/commands/doctor.js +4 -5
  12. package/dist/commands/init.d.ts +3 -1
  13. package/dist/commands/init.d.ts.map +1 -1
  14. package/dist/commands/init.js +4 -3
  15. package/dist/commands/query.d.ts +3 -1
  16. package/dist/commands/query.d.ts.map +1 -1
  17. package/dist/commands/query.js +9 -20
  18. package/dist/commands/search.d.ts.map +1 -1
  19. package/dist/commands/search.js +1 -1
  20. package/dist/commands/sync/persistence.d.ts.map +1 -1
  21. package/dist/commands/sync/persistence.js +79 -12
  22. package/dist/commands/sync.d.ts +4 -1
  23. package/dist/commands/sync.d.ts.map +1 -1
  24. package/dist/commands/sync.js +79 -31
  25. package/dist/extractors/markdown.d.ts +17 -0
  26. package/dist/extractors/markdown.d.ts.map +1 -1
  27. package/dist/extractors/markdown.js +104 -14
  28. package/dist/prolog/codec.d.ts +32 -5
  29. package/dist/prolog/codec.d.ts.map +1 -1
  30. package/dist/prolog/codec.js +95 -58
  31. package/dist/prolog.d.ts.map +1 -1
  32. package/dist/prolog.js +12 -2
  33. package/dist/public/check-types.d.ts +7 -0
  34. package/dist/public/check-types.d.ts.map +1 -0
  35. package/dist/public/check-types.js +18 -0
  36. package/dist/public/schemas/entity.d.ts +68 -0
  37. package/dist/public/schemas/entity.d.ts.map +1 -1
  38. package/dist/public/schemas/entity.js +52 -0
  39. package/dist/relationships/shards.d.ts.map +1 -1
  40. package/dist/relationships/shards.js +6 -3
  41. package/dist/schemas/entity.schema.json +120 -0
  42. package/dist/search-ranking.d.ts.map +1 -1
  43. package/dist/search-ranking.js +9 -3
  44. package/dist/traceability/symbol-extract.d.ts.map +1 -1
  45. package/dist/traceability/symbol-extract.js +16 -8
  46. package/dist/types/entities.d.ts +19 -1
  47. package/dist/types/entities.d.ts.map +1 -1
  48. package/dist/utils/prolog-cleanup.d.ts +10 -0
  49. package/dist/utils/prolog-cleanup.d.ts.map +1 -0
  50. package/dist/utils/prolog-cleanup.js +39 -0
  51. package/dist/utils/rule-registry.d.ts +8 -0
  52. package/dist/utils/rule-registry.d.ts.map +1 -1
  53. package/dist/utils/rule-registry.js +14 -12
  54. package/package.json +6 -2
  55. package/schema/config.json +7 -1
  56. package/schema/entities.pl +18 -0
  57. package/schema/validation.pl +115 -4
  58. package/src/public/check-types.ts +37 -0
  59. package/src/public/schemas/entity.ts +58 -0
  60. package/src/schemas/entity.schema.json +56 -1
  61. package/dist/kb/target-resolver.d.ts +0 -80
  62. package/dist/kb/target-resolver.d.ts.map +0 -1
  63. package/dist/kb/target-resolver.js +0 -313
package/dist/cli.d.ts CHANGED
@@ -1,2 +1,5 @@
1
- export {};
1
+ /** All command handlers return this instead of calling process.exit(). */
2
+ export interface CommandResult {
3
+ exitCode?: number;
4
+ }
2
5
  //# sourceMappingURL=cli.d.ts.map
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAsCA,0EAA0E;AAC1E,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB"}
package/dist/cli.js CHANGED
@@ -31,6 +31,16 @@ import { statusCommand } from "./commands/status.js";
31
31
  import { syncCommand } from "./commands/sync.js";
32
32
  const packageJson = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
33
33
  const VERSION = packageJson.version ?? "0.1.0";
34
+ // implements REQ-003
35
+ /** Wraps an async command handler to apply its returned exitCode to process.exitCode. */
36
+ function withExitCode(fn) {
37
+ return async (...args) => {
38
+ const result = await fn(...args);
39
+ if (result && typeof result.exitCode === "number") {
40
+ process.exitCode = result.exitCode;
41
+ }
42
+ };
43
+ }
34
44
  const program = new Command();
35
45
  program
36
46
  .name("kibi")
@@ -40,17 +50,17 @@ program
40
50
  .command("init")
41
51
  .description("Initialize .kb/ directory")
42
52
  .option("--no-hooks", "Do not install git hooks (hooks installed by default)")
43
- .action(async (options) => {
44
- await initCommand(options);
45
- });
53
+ .action(withExitCode(async (options) => {
54
+ return initCommand(options);
55
+ }));
46
56
  program
47
57
  .command("sync")
48
58
  .description("Sync entities from documents")
49
59
  .option("--validate-only", "Perform validation without mutations")
50
60
  .option("--rebuild", "Rebuild branch snapshot from scratch (discards current KB)")
51
- .action(async (options) => {
52
- await syncCommand(options);
53
- });
61
+ .action(withExitCode(async (options) => {
62
+ return syncCommand(options);
63
+ }));
54
64
  program
55
65
  .command("query [type]")
56
66
  .description("Query knowledge base")
@@ -61,9 +71,9 @@ program
61
71
  .option("--format <format>", "Output format: json|table", "json")
62
72
  .option("--limit <n>", "Limit results", "100")
63
73
  .option("--offset <n>", "Skip results", "0")
64
- .action(async (type, options) => {
65
- await queryCommand(type, options);
66
- });
74
+ .action(withExitCode(async (type, options) => {
75
+ return queryCommand(type, options);
76
+ }));
67
77
  program
68
78
  .command("search [query]")
69
79
  .description("Search knowledge base metadata and markdown content")
@@ -130,9 +140,9 @@ program
130
140
  .option("--staged", "Run check only against staged changes (experimental)")
131
141
  .option("--min-links <n>", "Minimum number of links required for symbol coverage", "1")
132
142
  .option("--dry-run", "Do not modify files; only print what would happen")
133
- .action(async (options) => {
134
- await checkCommand(options);
135
- });
143
+ .action(withExitCode(async (options) => {
144
+ return checkCommand(options);
145
+ }));
136
146
  program
137
147
  .command("gc")
138
148
  .description("Garbage collect stale branch KBs")
@@ -145,9 +155,7 @@ program
145
155
  program
146
156
  .command("doctor")
147
157
  .description("Diagnose KB setup and configuration")
148
- .action(async () => {
149
- await doctorCommand();
150
- });
158
+ .action(withExitCode(async () => doctorCommand()));
151
159
  program
152
160
  .command("branch")
153
161
  .description("Manage branch KBs")
@@ -158,4 +166,11 @@ program
158
166
  await branchEnsureCommand(options);
159
167
  }
160
168
  });
161
- program.parse(process.argv);
169
+ program
170
+ .parseAsync(process.argv)
171
+ .then(() => process.exit(process.exitCode ?? 0))
172
+ .catch((error) => {
173
+ const message = error instanceof Error ? error.message : String(error);
174
+ console.error(message);
175
+ process.exit(1);
176
+ });
@@ -1,3 +1,5 @@
1
+ import { type Violation } from "../utils/rule-registry.js";
2
+ export type { Violation };
1
3
  export interface CheckOptions {
2
4
  fix?: boolean;
3
5
  kbPath?: string;
@@ -6,12 +8,7 @@ export interface CheckOptions {
6
8
  minLinks?: string | number;
7
9
  dryRun?: boolean;
8
10
  }
9
- export interface Violation {
10
- rule: string;
11
- entityId: string;
12
- description: string;
13
- suggestion?: string;
14
- source?: string;
15
- }
16
- export declare function checkCommand(options: CheckOptions): Promise<void>;
11
+ export declare function checkCommand(options: CheckOptions): Promise<{
12
+ exitCode: number;
13
+ }>;
17
14
  //# sourceMappingURL=check.d.ts.map
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"check.d.ts","sourceRoot":"","sources":["../../src/commands/check.ts"],"names":[],"mappings":"AA4CA,OAAO,EAGL,KAAK,SAAS,EAEf,MAAM,2BAA2B,CAAC;AAEnC,YAAY,EAAE,SAAS,EAAE,CAAC;AAK1B,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;AAGD,wBAAsB,YAAY,CAChC,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CA0R/B"}
@@ -18,13 +18,14 @@
18
18
  import * as path from "node:path";
19
19
  import { extractFromManifest } from "../extractors/manifest.js";
20
20
  import { PrologProcess } from "../prolog.js";
21
- import { escapeAtom } from "../prolog/codec.js";
21
+ import { escapeAtom, parseTriples, parseViolationRows, } from "../prolog/codec.js";
22
22
  import { getStagedFiles } from "../traceability/git-staged.js";
23
23
  import { validateStagedMarkdown } from "../traceability/markdown-validate.js";
24
24
  import { extractSymbolsFromStagedFile, } from "../traceability/symbol-extract.js";
25
25
  import { cleanupTempKb, consultOverlay, createOverlayFacts, createTempKb, } from "../traceability/temp-kb.js";
26
26
  import { formatViolations as formatStagedViolations, validateStagedSymbols, } from "../traceability/validate.js";
27
27
  import { loadConfig } from "../utils/config.js";
28
+ import { safeCleanupProlog } from "../utils/prolog-cleanup.js";
28
29
  import { RULES, getEffectiveRules, } from "../utils/rule-registry.js";
29
30
  import { runAggregatedChecks } from "./aggregated-checks.js";
30
31
  import { getCurrentBranch } from "./init-helpers.js";
@@ -83,7 +84,7 @@ export async function checkCommand(options) {
83
84
  const stagedFiles = getStagedFiles();
84
85
  if (!stagedFiles || stagedFiles.length === 0) {
85
86
  console.log("No staged files found.");
86
- process.exit(0);
87
+ return { exitCode: 0 };
87
88
  }
88
89
  const codeFiles = stagedFiles.filter((f) => !f.path.endsWith(".md"));
89
90
  const markdownFiles = stagedFiles.filter((f) => f.path.endsWith(".md"));
@@ -101,9 +102,9 @@ export async function checkCommand(options) {
101
102
  console.log();
102
103
  }
103
104
  if (options.dryRun) {
104
- process.exit(0);
105
+ return { exitCode: 0 };
105
106
  }
106
- process.exit(1);
107
+ return { exitCode: 1 };
107
108
  }
108
109
  const allSymbols = [];
109
110
  for (const f of codeFiles) {
@@ -119,11 +120,11 @@ export async function checkCommand(options) {
119
120
  }
120
121
  if (allSymbols.length === 0 && markdownFiles.length === 0) {
121
122
  console.log("No exported symbols or markdown entities found in staged files.");
122
- process.exit(0);
123
+ return { exitCode: 0 };
123
124
  }
124
125
  if (allSymbols.length === 0) {
125
126
  console.log("✓ No violations found in staged files.");
126
- process.exit(0);
127
+ return { exitCode: 0 };
127
128
  }
128
129
  // Create temp KB
129
130
  tempCtx = await createTempKb(resolvedKbPath);
@@ -140,13 +141,13 @@ export async function checkCommand(options) {
140
141
  console.log(violationsFormatted);
141
142
  await cleanupTempKb(tempCtx.tempDir);
142
143
  if (options.dryRun) {
143
- process.exit(0);
144
+ return { exitCode: 0 };
144
145
  }
145
- process.exit(1);
146
+ return { exitCode: 1 };
146
147
  }
147
148
  console.log("✓ No violations found in staged symbols.");
148
149
  await cleanupTempKb(tempCtx.tempDir);
149
- process.exit(0);
150
+ return { exitCode: 0 };
150
151
  }
151
152
  catch (err) {
152
153
  console.error(`Error running staged validation: ${err instanceof Error ? err.message : String(err)}`);
@@ -154,9 +155,11 @@ export async function checkCommand(options) {
154
155
  try {
155
156
  await cleanupTempKb(tempCtx.tempDir);
156
157
  }
157
- catch { }
158
+ catch {
159
+ // best-effort: temp directory may already be cleaned up
160
+ }
158
161
  }
159
- process.exit(1);
162
+ return { exitCode: 1 };
160
163
  }
161
164
  }
162
165
  prolog = new PrologProcess({ timeout: 120000 });
@@ -166,7 +169,7 @@ export async function checkCommand(options) {
166
169
  if (!attachResult.success) {
167
170
  await prolog.terminate();
168
171
  console.error(`Error: Failed to attach KB: ${attachResult.error}`);
169
- process.exit(1);
172
+ return { exitCode: 1 };
170
173
  }
171
174
  attached = true;
172
175
  const violations = [];
@@ -203,6 +206,7 @@ export async function checkCommand(options) {
203
206
  "required-fields",
204
207
  "deprecated-adr-no-successor",
205
208
  "domain-contradictions",
209
+ "strict-fact-shape",
206
210
  ];
207
211
  const canUseAggregated = Array.from(effectiveRules).every((r) => supportedRules.includes(r));
208
212
  if (canUseAggregated) {
@@ -225,10 +229,11 @@ export async function checkCommand(options) {
225
229
  }
226
230
  await runCheck("deprecated-adr-no-successor", checkDeprecatedAdrs);
227
231
  await runCheck("domain-contradictions", checkDomainContradictions);
232
+ await runCheck("strict-fact-shape", checkStrictFactShape);
228
233
  }
229
234
  if (violations.length === 0) {
230
235
  console.log("✓ No violations found. KB is valid.");
231
- process.exit(0);
236
+ return { exitCode: 0 };
232
237
  }
233
238
  console.log(`Found ${violations.length} violation(s):`);
234
239
  console.log();
@@ -241,26 +246,15 @@ export async function checkCommand(options) {
241
246
  }
242
247
  console.log();
243
248
  }
244
- process.exit(1);
249
+ return { exitCode: 1 };
245
250
  }
246
251
  catch (error) {
247
252
  const message = error instanceof Error ? error.message : String(error);
248
253
  console.error(`Error: ${message}`);
249
- process.exit(1);
254
+ return { exitCode: 1 };
250
255
  }
251
256
  finally {
252
- if (prolog) {
253
- if (attached) {
254
- try {
255
- await prolog.query("kb_detach");
256
- }
257
- catch { }
258
- }
259
- try {
260
- await prolog.terminate();
261
- }
262
- catch { }
263
- }
257
+ await safeCleanupProlog(prolog);
264
258
  }
265
259
  }
266
260
  async function checkMustPriorityCoverage(prolog) {
@@ -545,7 +539,7 @@ async function checkDomainContradictions(prolog) {
545
539
  if (!result.success || !result.bindings.Rows) {
546
540
  return violations;
547
541
  }
548
- const rows = parseTripleRows(result.bindings.Rows);
542
+ const rows = parseTriples(result.bindings.Rows);
549
543
  for (const [reqA, reqB, reason] of rows) {
550
544
  violations.push({
551
545
  rule: "domain-contradictions",
@@ -556,6 +550,22 @@ async function checkDomainContradictions(prolog) {
556
550
  }
557
551
  return violations;
558
552
  }
553
+ async function checkStrictFactShape(prolog) {
554
+ const violations = [];
555
+ const result = await prolog.query(`findall(violation(Rule, EntityId, Desc, Sugg, Src),
556
+ checks:strict_fact_shape_violation(violation(Rule, EntityId, Desc, Sugg, Src)),
557
+ Violations)`);
558
+ if (!result.success || !result.bindings.Violations) {
559
+ return violations;
560
+ }
561
+ const violationsStr = result.bindings.Violations;
562
+ if (violationsStr && violationsStr !== "[]") {
563
+ for (const v of parseViolationRows(violationsStr)) {
564
+ violations.push(v);
565
+ }
566
+ }
567
+ return violations;
568
+ }
559
569
  async function checkSymbolCoverage(prolog) {
560
570
  const violations = [];
561
571
  const uncoveredResult = await prolog.query("setof(Symbol, symbol_no_req_coverage(Symbol, _), Symbols)");
@@ -591,40 +601,9 @@ async function checkSymbolTraceability(prolog, requireAdr) {
591
601
  }
592
602
  const violationsStr = result.bindings.Violations;
593
603
  if (violationsStr && violationsStr !== "[]") {
594
- const violationRegex = /violation\(([^,]+),'?([^',]+)'?,([^,]+),([^,]+),'?([^']*)'?\)/g;
595
- let match;
596
- do {
597
- match = violationRegex.exec(violationsStr);
598
- if (match) {
599
- violations.push({
600
- rule: match[1].trim().replace(/^'|'$/g, ""),
601
- entityId: match[2].trim(),
602
- description: match[3].trim().replace(/^"|"$/g, ""),
603
- suggestion: match[4].trim().replace(/^"|"$/g, ""),
604
- source: match[5].trim() || undefined,
605
- });
606
- }
607
- } while (match);
604
+ for (const v of parseViolationRows(violationsStr)) {
605
+ violations.push(v);
606
+ }
608
607
  }
609
608
  return violations;
610
609
  }
611
- function parseTripleRows(raw) {
612
- const cleaned = raw.trim();
613
- if (cleaned === "[]" || cleaned.length === 0) {
614
- return [];
615
- }
616
- const rows = [];
617
- const rowRegex = /\[([^,]+),([^,]+),([^\]]+)\]/g;
618
- let match;
619
- do {
620
- match = rowRegex.exec(cleaned);
621
- if (match) {
622
- rows.push([
623
- match[1].trim().replace(/^'|'$/g, ""),
624
- match[2].trim().replace(/^'|'$/g, ""),
625
- match[3].trim().replace(/^'|'$/g, ""),
626
- ]);
627
- }
628
- } while (match);
629
- return rows;
630
- }
@@ -1 +1 @@
1
- {"version":3,"file":"discovery-shared.d.ts","sourceRoot":"","sources":["../../src/commands/discovery-shared.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAmB,MAAM,cAAc,CAAC;AAI9D,MAAM,WAAW,uBAAuB;IACtC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CAC3B;AAGD,wBAAsB,wBAAwB,CAAC,CAAC,EAC9C,QAAQ,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,OAAO,CAAC,CAAC,CAAC,GAC9C,OAAO,CAAC,CAAC,CAAC,CAsCZ;AAGD,wBAAsB,iBAAiB,CAAC,CAAC,EACvC,QAAQ,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,OAAO,CAAC,CAAC,CAAC,GAC9C,OAAO,CAAC,CAAC,CAAC,CAcZ;AAGD,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,MAAM,CAAC,CAS5D;AAGD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE9D;AAGD,wBAAsB,kBAAkB,CAAC,CAAC,EACxC,MAAM,EAAE,aAAa,EACrB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,CAAC,CAAC,CAsBZ;AAGD,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,EACpC,UAAU,EAAE,OAAO,EACnB,YAAY,EAAE,MAAM,GACnB,IAAI,CAQN"}
1
+ {"version":3,"file":"discovery-shared.d.ts","sourceRoot":"","sources":["../../src/commands/discovery-shared.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAmB,MAAM,cAAc,CAAC;AAK9D,MAAM,WAAW,uBAAuB;IACtC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CAC3B;AAGD,wBAAsB,wBAAwB,CAAC,CAAC,EAC9C,QAAQ,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,OAAO,CAAC,CAAC,CAAC,GAC9C,OAAO,CAAC,CAAC,CAAC,CAkCZ;AAGD,wBAAsB,iBAAiB,CAAC,CAAC,EACvC,QAAQ,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,OAAO,CAAC,CAAC,CAAC,GAC9C,OAAO,CAAC,CAAC,CAAC,CAYZ;AAGD,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,MAAM,CAAC,CAS5D;AAGD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE9D;AAGD,wBAAsB,kBAAkB,CAAC,CAAC,EACxC,MAAM,EAAE,aAAa,EACrB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,EAClB,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,CAAC,CAAC,CAwBZ;AAGD,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,EACpC,UAAU,EAAE,OAAO,EACnB,YAAY,EAAE,MAAM,GACnB,IAAI,CAQN"}
@@ -2,6 +2,7 @@ import path from "node:path";
2
2
  import Table from "cli-table3";
3
3
  import { PrologProcess, resolveKbPlPath } from "../prolog.js";
4
4
  import { escapeAtom } from "../prolog/codec.js";
5
+ import { safeCleanupProlog } from "../utils/prolog-cleanup.js";
5
6
  import { getCurrentBranch } from "./init-helpers.js";
6
7
  // implements REQ-003
7
8
  export async function withAttachedBranchProlog(callback) {
@@ -13,7 +14,8 @@ export async function withAttachedBranchProlog(callback) {
13
14
  await prolog.query("set_prolog_flag(answer_write_options, [max_depth(0), spacing(next_argument)])");
14
15
  let branch;
15
16
  try {
16
- branch = process.env.KIBI_BRANCH || (await getCurrentBranch(process.cwd()));
17
+ branch =
18
+ process.env.KIBI_BRANCH || (await getCurrentBranch(process.cwd()));
17
19
  }
18
20
  catch {
19
21
  branch = process.env.KIBI_BRANCH || "main";
@@ -27,18 +29,7 @@ export async function withAttachedBranchProlog(callback) {
27
29
  return await callback(prolog);
28
30
  }
29
31
  finally {
30
- if (prolog) {
31
- if (attached) {
32
- try {
33
- await prolog.query("kb_detach");
34
- }
35
- catch { }
36
- }
37
- try {
38
- await prolog.terminate();
39
- }
40
- catch { }
41
- }
32
+ await safeCleanupProlog(prolog);
42
33
  }
43
34
  }
44
35
  // implements REQ-003
@@ -46,16 +37,12 @@ export async function withPrologProcess(callback) {
46
37
  const prolog = new PrologProcess({ timeout: 120000 });
47
38
  try {
48
39
  await prolog.start();
49
- ;
50
40
  prolog.useOneShotMode = true;
51
41
  await prolog.query("set_prolog_flag(answer_write_options, [max_depth(0), spacing(next_argument)])");
52
42
  return await callback(prolog);
53
43
  }
54
44
  finally {
55
- try {
56
- await prolog.terminate();
57
- }
58
- catch { }
45
+ await safeCleanupProlog(prolog);
59
46
  }
60
47
  }
61
48
  // implements REQ-003
@@ -110,13 +97,16 @@ function renderDiscoveryTable(structured) {
110
97
  if (Array.isArray(payload.results)) {
111
98
  return renderSearchTable(payload);
112
99
  }
113
- if (typeof payload.branch === "string" && typeof payload.syncState === "string") {
100
+ if (typeof payload.branch === "string" &&
101
+ typeof payload.syncState === "string") {
114
102
  return renderStatusTable(payload);
115
103
  }
116
104
  if (Array.isArray(payload.nodes) && Array.isArray(payload.edges)) {
117
105
  return renderGraphTable(payload);
118
106
  }
119
- if (Array.isArray(payload.rows) && payload.summary && typeof payload.summary === "object") {
107
+ if (Array.isArray(payload.rows) &&
108
+ payload.summary &&
109
+ typeof payload.summary === "object") {
120
110
  return renderCoverageTable(payload);
121
111
  }
122
112
  if (Array.isArray(payload.rows)) {
@@ -134,7 +124,9 @@ function renderSearchTable(payload) {
134
124
  for (const row of rows) {
135
125
  const match = row;
136
126
  const entity = (match.entity ?? {});
137
- const reasons = Array.isArray(match.reasons) ? match.reasons.join(", ") : "";
127
+ const reasons = Array.isArray(match.reasons)
128
+ ? match.reasons.join(", ")
129
+ : "";
138
130
  table.push([
139
131
  stringifyCell(entity.id),
140
132
  stringifyCell(entity.type),
@@ -192,7 +184,16 @@ function renderCoverageTable(payload) {
192
184
  const isRequirementCoverage = firstRow && Object.hasOwn(firstRow, "scenarioCount");
193
185
  const table = isRequirementCoverage
194
186
  ? new Table({
195
- head: ["ID", "Status", "Priority", "Coverage", "Scen", "Tests", "Symbols", "Gaps"],
187
+ head: [
188
+ "ID",
189
+ "Status",
190
+ "Priority",
191
+ "Coverage",
192
+ "Scen",
193
+ "Tests",
194
+ "Symbols",
195
+ "Gaps",
196
+ ],
196
197
  colWidths: [20, 12, 12, 18, 8, 8, 10, 28],
197
198
  wordWrap: true,
198
199
  })
@@ -1,2 +1,4 @@
1
- export declare function doctorCommand(): Promise<void>;
1
+ export declare function doctorCommand(): Promise<{
2
+ exitCode: number;
3
+ }>;
2
4
  //# sourceMappingURL=doctor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AA2BA,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CA0DnD"}
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AA4BA,wBAAsB,aAAa,IAAI,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAyDnE"}
@@ -18,6 +18,7 @@
18
18
  import { execSync } from "node:child_process";
19
19
  import { existsSync, readFileSync, statSync } from "node:fs";
20
20
  import * as path from "node:path";
21
+ // implements REQ-003
21
22
  export async function doctorCommand() {
22
23
  const checks = [
23
24
  {
@@ -65,12 +66,10 @@ export async function doctorCommand() {
65
66
  console.log();
66
67
  if (allPassed) {
67
68
  console.log("All checks passed! Your environment is ready.");
68
- process.exit(0);
69
- }
70
- else {
71
- console.log("Some checks failed. Please address the issues above.");
72
- process.exit(1);
69
+ return { exitCode: 0 };
73
70
  }
71
+ console.log("Some checks failed. Please address the issues above.");
72
+ return { exitCode: 1 };
74
73
  }
75
74
  function checkSWIProlog() {
76
75
  try {
@@ -1,6 +1,8 @@
1
1
  interface InitOptions {
2
2
  hooks?: boolean;
3
3
  }
4
- export declare function initCommand(options: InitOptions): Promise<void>;
4
+ export declare function initCommand(options: InitOptions): Promise<{
5
+ exitCode: number;
6
+ }>;
5
7
  export {};
6
8
  //# sourceMappingURL=init.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAiCA,UAAU,WAAW;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,wBAAsB,WAAW,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CA6DrE"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAiCA,UAAU,WAAW;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAGD,wBAAsB,WAAW,CAC/B,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CA6D/B"}
@@ -22,6 +22,7 @@ import { resolveActiveBranch } from "../utils/branch-resolver.js";
22
22
  import { copySchemaFiles, createConfigFile, createKbDirectoryStructure, installGitHooks, updateGitIgnore, } from "./init-helpers.js";
23
23
  const __filename = fileURLToPath(import.meta.url);
24
24
  const __dirname = path.dirname(__filename);
25
+ // implements REQ-003
25
26
  export async function initCommand(options) {
26
27
  const kbDir = path.join(process.cwd(), ".kb");
27
28
  const kbExists = existsSync(kbDir);
@@ -40,7 +41,7 @@ export async function initCommand(options) {
40
41
  else {
41
42
  console.error("Error: Failed to resolve the active git branch.");
42
43
  console.error(result.error);
43
- process.exit(1);
44
+ return { exitCode: 1 };
44
45
  }
45
46
  }
46
47
  else {
@@ -70,10 +71,10 @@ export async function initCommand(options) {
70
71
  console.log("Next steps:");
71
72
  console.log(" 1. Run 'kibi doctor' to verify setup");
72
73
  console.log(" 2. Run 'kibi sync' to extract entities from documents");
73
- process.exit(0);
74
+ return { exitCode: 0 };
74
75
  }
75
76
  catch (error) {
76
77
  console.error("Error during initialization:", error);
77
- process.exit(1);
78
+ return { exitCode: 1 };
78
79
  }
79
80
  }
@@ -7,6 +7,8 @@ interface QueryOptions {
7
7
  limit?: string;
8
8
  offset?: string;
9
9
  }
10
- export declare function queryCommand(type: string | undefined, options: QueryOptions): Promise<void>;
10
+ export declare function queryCommand(type: string | undefined, options: QueryOptions): Promise<{
11
+ exitCode: number;
12
+ }>;
11
13
  export {};
12
14
  //# sourceMappingURL=query.d.ts.map
@@ -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,CAyKf"}
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../src/commands/query.ts"],"names":[],"mappings":"AA4CA,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;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CA+J/B"}
@@ -22,6 +22,7 @@ import { parseEntityFromBinding, parseEntityFromList, parseListOfLists, parsePro
22
22
  import relationshipSchema from "../public/schemas/relationship.js";
23
23
  import { VALID_ENTITY_TYPES } from "../query/service.js";
24
24
  import { resolveActiveBranch } from "../utils/branch-resolver.js";
25
+ import { safeCleanupProlog } from "../utils/prolog-cleanup.js";
25
26
  const REL_TYPES = relationshipSchema.properties.type.enum;
26
27
  // implements REQ-003
27
28
  export async function queryCommand(type, options) {
@@ -45,7 +46,7 @@ export async function queryCommand(type, options) {
45
46
  else {
46
47
  console.error(`Error: Failed to resolve active branch:\n${branchResult.error}`);
47
48
  await prolog.terminate();
48
- process.exit(1);
49
+ return { exitCode: 1 };
49
50
  }
50
51
  }
51
52
  else {
@@ -56,7 +57,7 @@ export async function queryCommand(type, options) {
56
57
  if (!attachResult.success) {
57
58
  await prolog.terminate();
58
59
  console.error(`Error: Failed to attach KB: ${attachResult.error || "Unknown error"}`);
59
- process.exit(1);
60
+ return { exitCode: 1 };
60
61
  }
61
62
  attached = true;
62
63
  let results = [];
@@ -86,8 +87,7 @@ export async function queryCommand(type, options) {
86
87
  else if (type || options.source) {
87
88
  if (type && !VALID_ENTITY_TYPES.includes(type)) {
88
89
  console.error(`Error: Invalid type '${type}'. Valid types: ${VALID_ENTITY_TYPES.join(", ")}`);
89
- process.exitCode = 1;
90
- return;
90
+ return { exitCode: 1 };
91
91
  }
92
92
  let goal;
93
93
  if (options.source) {
@@ -133,8 +133,7 @@ export async function queryCommand(type, options) {
133
133
  }
134
134
  else {
135
135
  console.error("Error: Must specify entity type, --source, or --relationships option");
136
- process.exitCode = 1;
137
- return;
136
+ return { exitCode: 1 };
138
137
  }
139
138
  const limit = Number.parseInt(options.limit || "100");
140
139
  const offset = Number.parseInt(options.offset || "0");
@@ -146,7 +145,7 @@ export async function queryCommand(type, options) {
146
145
  else {
147
146
  console.log("No entities found");
148
147
  }
149
- return;
148
+ return { exitCode: 0 };
150
149
  }
151
150
  // Format output
152
151
  if (options.format === "table") {
@@ -155,25 +154,15 @@ export async function queryCommand(type, options) {
155
154
  else {
156
155
  console.log(JSON.stringify(paginated, null, 2));
157
156
  }
157
+ return { exitCode: 0 };
158
158
  }
159
159
  catch (error) {
160
160
  const message = error instanceof Error ? error.message : String(error);
161
161
  console.error(`Error: ${message}`);
162
- process.exitCode = 1;
162
+ return { exitCode: 1 };
163
163
  }
164
164
  finally {
165
- if (prolog) {
166
- if (attached) {
167
- try {
168
- await prolog.query("kb_detach");
169
- }
170
- catch { }
171
- }
172
- try {
173
- await prolog.terminate();
174
- }
175
- catch { }
176
- }
165
+ await safeCleanupProlog(prolog);
177
166
  }
178
167
  }
179
168
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/commands/search.ts"],"names":[],"mappings":"AAYA,UAAU,aAAa;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAGD,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,IAAI,CAAC,CAqBf"}
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/commands/search.ts"],"names":[],"mappings":"AASA,UAAU,aAAa;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAGD,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,IAAI,CAAC,CA+Bf"}
@@ -1,5 +1,5 @@
1
+ import { VALID_ENTITY_TYPES, queryEntities } from "../query/service.js";
1
2
  import { rankEntities } from "../search-ranking.js";
2
- import { VALID_ENTITY_TYPES, queryEntities, } from "../query/service.js";
3
3
  import { printDiscoveryResult, withAttachedBranchProlog, } from "./discovery-shared.js";
4
4
  // implements REQ-mcp-search-discovery, REQ-003
5
5
  export async function searchCommand(query, options) {
@@ -1 +1 @@
1
- {"version":3,"file":"persistence.d.ts","sourceRoot":"","sources":["../../../src/commands/sync/persistence.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EACV,qBAAqB,EACrB,gBAAgB,EACjB,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAGrD,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,wBAAsB,eAAe,CACnC,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,gBAAgB,EAAE,EAC3B,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,GACrB,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,CAAC,CA0DvD;AAED,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,gBAAgB,EAAE,EAC3B,kBAAkB,EAAE,qBAAqB,EAAE,GAC1C,OAAO,CAAC;IAAE,iBAAiB,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,CAAC,CAiI7D"}
1
+ {"version":3,"file":"persistence.d.ts","sourceRoot":"","sources":["../../../src/commands/sync/persistence.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAEV,qBAAqB,EACrB,gBAAgB,EACjB,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAqErD,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,wBAAsB,eAAe,CAEnC,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,gBAAgB,EAAE,EAC3B,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,GACrB,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,CAAC,CAoEvD;AAED,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,aAAa,EACrB,OAAO,EAAE,gBAAgB,EAAE,EAC3B,kBAAkB,EAAE,qBAAqB,EAAE,GAC1C,OAAO,CAAC;IAAE,iBAAiB,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,CAAC,CAiI7D"}