kibi-cli 0.2.5 → 0.2.7

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.
@@ -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;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAuPvE"}
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"}
@@ -27,7 +27,10 @@ import { loadConfig } from "../utils/config.js";
27
27
  import { RULES, getEffectiveRules, } from "../utils/rule-registry.js";
28
28
  import { runAggregatedChecks } from "./aggregated-checks.js";
29
29
  import { getCurrentBranch } from "./init-helpers.js";
30
+ // implements REQ-006
30
31
  export async function checkCommand(options) {
32
+ let prolog = null;
33
+ let attached = false;
31
34
  try {
32
35
  // Resolve KB path with priority:
33
36
  // --kb-path > git branch --show-current > KIBI_BRANCH env > develop > main
@@ -139,7 +142,7 @@ export async function checkCommand(options) {
139
142
  process.exit(1);
140
143
  }
141
144
  }
142
- const prolog = new PrologProcess({ timeout: 120000 });
145
+ prolog = new PrologProcess({ timeout: 120000 });
143
146
  await prolog.start();
144
147
  const kbPathEscaped = escapeAtom(resolvedKbPath);
145
148
  const attachResult = await prolog.query(`kb_attach('${kbPathEscaped}')`);
@@ -148,6 +151,7 @@ export async function checkCommand(options) {
148
151
  console.error(`Error: Failed to attach KB: ${attachResult.error}`);
149
152
  process.exit(1);
150
153
  }
154
+ attached = true;
151
155
  const violations = [];
152
156
  // Load config to get rule enablement settings
153
157
  const config = loadConfig(process.cwd());
@@ -161,10 +165,17 @@ export async function checkCommand(options) {
161
165
  async function runCheck(name, fn, ...args) {
162
166
  if (!effectiveRules.has(name))
163
167
  return;
168
+ if (!prolog) {
169
+ throw new Error("Prolog process not initialized");
170
+ }
164
171
  const res = await fn(prolog, ...args);
165
172
  if (res?.length)
166
173
  violations.push(...res);
167
174
  }
175
+ if (!prolog) {
176
+ throw new Error("Prolog process not initialized");
177
+ }
178
+ const activeProlog = prolog;
168
179
  // Use aggregated checks (single Prolog call) when possible for better performance
169
180
  // This is significantly faster in Bun/Docker environments where one-shot mode
170
181
  // spawns a new Prolog process for each query
@@ -182,7 +193,7 @@ export async function checkCommand(options) {
182
193
  if (canUseAggregated) {
183
194
  // Fast path: single Prolog call returning all violations
184
195
  // Pass the requireAdr option for symbol-traceability
185
- const aggregatedViolations = await runAggregatedChecks(prolog, effectiveRules, checksConfig.symbolTraceability?.requireAdr ?? false);
196
+ const aggregatedViolations = await runAggregatedChecks(activeProlog, effectiveRules, checksConfig.symbolTraceability?.requireAdr ?? false);
186
197
  violations.push(...aggregatedViolations);
187
198
  }
188
199
  else {
@@ -192,16 +203,14 @@ export async function checkCommand(options) {
192
203
  await runCheck("symbol-traceability", (p) => checkSymbolTraceability(p, checksConfig.symbolTraceability?.requireAdr ?? false));
193
204
  await runCheck("no-dangling-refs", checkNoDanglingRefs);
194
205
  await runCheck("no-cycles", checkNoCycles);
195
- const allEntityIds = await getAllEntityIds(prolog);
206
+ const allEntityIds = await getAllEntityIds(activeProlog);
196
207
  if (effectiveRules.has("required-fields")) {
197
- const requiredViolations = await checkRequiredFields(prolog, allEntityIds);
208
+ const requiredViolations = await checkRequiredFields(activeProlog, allEntityIds);
198
209
  violations.push(...requiredViolations);
199
210
  }
200
211
  await runCheck("deprecated-adr-no-successor", checkDeprecatedAdrs);
201
212
  await runCheck("domain-contradictions", checkDomainContradictions);
202
213
  }
203
- await prolog.query("kb_detach");
204
- await prolog.terminate();
205
214
  if (violations.length === 0) {
206
215
  console.log("✓ No violations found. KB is valid.");
207
216
  process.exit(0);
@@ -224,6 +233,20 @@ export async function checkCommand(options) {
224
233
  console.error(`Error: ${message}`);
225
234
  process.exit(1);
226
235
  }
236
+ finally {
237
+ if (prolog) {
238
+ if (attached) {
239
+ try {
240
+ await prolog.query("kb_detach");
241
+ }
242
+ catch { }
243
+ }
244
+ try {
245
+ await prolog.terminate();
246
+ }
247
+ catch { }
248
+ }
249
+ }
227
250
  }
228
251
  async function checkMustPriorityCoverage(prolog) {
229
252
  const violations = [];
@@ -505,7 +528,7 @@ async function checkDeprecatedAdrs(prolog) {
505
528
  violations.push({
506
529
  rule: "deprecated-adr-no-successor",
507
530
  entityId: adrId,
508
- description: "Archived/deprecated ADR has no successor — add a supersedes link from the replacement ADR",
531
+ description: "Superseded/deprecated ADR has no successor — add a supersedes link from the replacement ADR",
509
532
  suggestion: `Create a new ADR and add: links: [{type: supersedes, target: ${adrId}}]`,
510
533
  source,
511
534
  });
@@ -1 +1 @@
1
- {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../src/commands/query.ts"],"names":[],"mappings":"AAmCA,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;AAED,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":"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"}
@@ -23,9 +23,12 @@ 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
25
  const REL_TYPES = relationshipSchema.properties.type.enum;
26
+ // implements REQ-003
26
27
  export async function queryCommand(type, options) {
28
+ let prolog = null;
29
+ let attached = false;
27
30
  try {
28
- const prolog = new PrologProcess({ timeout: 120000 });
31
+ prolog = new PrologProcess({ timeout: 120000 });
29
32
  await prolog.start();
30
33
  await prolog.query("set_prolog_flag(answer_write_options, [max_depth(0), spacing(next_argument)])");
31
34
  // Resolve branch: allow non-git repos to use default "main" for query
@@ -56,6 +59,7 @@ export async function queryCommand(type, options) {
56
59
  console.error(`Error: Failed to attach KB: ${attachResult.error || "Unknown error"}`);
57
60
  process.exit(1);
58
61
  }
62
+ attached = true;
59
63
  let results = [];
60
64
  // Query relationships mode
61
65
  if (options.relationships) {
@@ -85,10 +89,9 @@ export async function queryCommand(type, options) {
85
89
  else if (type || options.source) {
86
90
  // Validate type if provided
87
91
  if (type && !VALID_ENTITY_TYPES.includes(type)) {
88
- await prolog.query("kb_detach");
89
- await prolog.terminate();
90
92
  console.error(`Error: Invalid type '${type}'. Valid types: ${VALID_ENTITY_TYPES.join(", ")}`);
91
- process.exit(1);
93
+ process.exitCode = 1;
94
+ return;
92
95
  }
93
96
  let goal;
94
97
  if (options.source) {
@@ -134,13 +137,10 @@ export async function queryCommand(type, options) {
134
137
  }
135
138
  }
136
139
  else {
137
- await prolog.query("kb_detach");
138
- await prolog.terminate();
139
140
  console.error("Error: Must specify entity type, --source, or --relationships option");
140
- process.exit(1);
141
+ process.exitCode = 1;
142
+ return;
141
143
  }
142
- await prolog.query("kb_detach");
143
- await prolog.terminate();
144
144
  // Apply pagination
145
145
  const limit = Number.parseInt(options.limit || "100");
146
146
  const offset = Number.parseInt(options.offset || "0");
@@ -152,7 +152,7 @@ export async function queryCommand(type, options) {
152
152
  else {
153
153
  console.log("No entities found");
154
154
  }
155
- process.exit(0);
155
+ return;
156
156
  }
157
157
  // Format output
158
158
  if (options.format === "table") {
@@ -161,12 +161,25 @@ export async function queryCommand(type, options) {
161
161
  else {
162
162
  console.log(JSON.stringify(paginated, null, 2));
163
163
  }
164
- process.exit(0);
165
164
  }
166
165
  catch (error) {
167
166
  const message = error instanceof Error ? error.message : String(error);
168
167
  console.error(`Error: ${message}`);
169
- process.exit(1);
168
+ process.exitCode = 1;
169
+ }
170
+ finally {
171
+ if (prolog) {
172
+ if (attached) {
173
+ try {
174
+ await prolog.query("kb_detach");
175
+ }
176
+ catch { }
177
+ }
178
+ try {
179
+ await prolog.terminate();
180
+ }
181
+ catch { }
182
+ }
170
183
  }
171
184
  }
172
185
  /**
@@ -183,10 +196,11 @@ function outputTable(items, isRelationships) {
183
196
  colWidths: [20, 18, 18],
184
197
  });
185
198
  for (const item of items) {
199
+ const rel = item;
186
200
  table.push([
187
- item.type || "N/A",
188
- item.from?.substring(0, 16) || "N/A",
189
- item.to?.substring(0, 16) || "N/A",
201
+ rel.type || "N/A",
202
+ rel.from.substring(0, 16) || "N/A",
203
+ rel.to.substring(0, 16) || "N/A",
190
204
  ]);
191
205
  }
192
206
  console.log(table.toString());
@@ -197,12 +211,22 @@ function outputTable(items, isRelationships) {
197
211
  colWidths: [18, 10, 40, 12, 30],
198
212
  });
199
213
  for (const entity of items) {
214
+ const record = entity;
215
+ const id = typeof record.id === "string" ? record.id : "N/A";
216
+ const entityType = typeof record.type === "string" ? record.type : "N/A";
217
+ const title = typeof record.title === "string" ? record.title : "N/A";
218
+ const status = typeof record.status === "string" ? record.status : "N/A";
219
+ const tags = Array.isArray(record.tags)
220
+ ? record.tags
221
+ .filter((tag) => typeof tag === "string")
222
+ .join(", ")
223
+ : "";
200
224
  table.push([
201
- entity.id?.substring(0, 16) || "N/A",
202
- entity.type || "N/A",
203
- (entity.title || "N/A").substring(0, 38),
204
- entity.status || "N/A",
205
- (entity.tags || []).join(", ").substring(0, 28) || "",
225
+ id.substring(0, 16),
226
+ entityType,
227
+ title.substring(0, 38),
228
+ status,
229
+ tags.substring(0, 28),
206
230
  ]);
207
231
  }
208
232
  console.log(table.toString());
@@ -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;AAED,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;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,CA0WtB;AAGD,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC"}
@@ -35,6 +35,7 @@ export class SyncError extends Error {
35
35
  this.name = "SyncError";
36
36
  }
37
37
  }
38
+ // implements REQ-003, REQ-007
38
39
  export async function syncCommand(options = {}) {
39
40
  const validateOnly = options.validateOnly ?? false;
40
41
  const rebuild = options.rebuild ?? false;
@@ -106,9 +107,9 @@ export async function syncCommand(options = {}) {
106
107
  const hash = hashFile(file);
107
108
  const lastSeen = syncCache.seenAt[key];
108
109
  const lastSeenMs = lastSeen ? Date.parse(lastSeen) : Number.NaN;
109
- const expired = Number.isNaN(lastSeenMs)
110
- ? false
111
- : nowMs - lastSeenMs > SYNC_CACHE_TTL_MS;
110
+ const expired = !lastSeen ||
111
+ Number.isNaN(lastSeenMs) ||
112
+ nowMs - lastSeenMs > SYNC_CACHE_TTL_MS;
112
113
  nextHashes[key] = hash;
113
114
  nextSeenAt[key] = nowIso;
114
115
  const isChanged = expired || syncCache.hashes[key] !== hash || validateOnly || rebuild;
@@ -126,6 +127,8 @@ export async function syncCommand(options = {}) {
126
127
  console.warn(`Warning: Failed to hash ${file}: ${message}`);
127
128
  }
128
129
  }
130
+ const performedFullReindex = changedMarkdownFiles.length === markdownFiles.length &&
131
+ changedManifestFiles.length === manifestFiles.length;
129
132
  // Content extraction
130
133
  const { results, failedCacheKeys, errors } = await processExtractions(changedMarkdownFiles, changedManifestFiles, validateOnly);
131
134
  // Collect INVALID_AUTHORING diagnostics
@@ -176,7 +179,7 @@ export async function syncCommand(options = {}) {
176
179
  continue;
177
180
  }
178
181
  evictedHashes[key] = hash;
179
- evictedSeenAt[key] = nextHashes[key] ?? nowIso;
182
+ evictedSeenAt[key] = nextSeenAt[key] ?? nowIso;
180
183
  }
181
184
  writeSyncCache(cachePath, {
182
185
  version: SYNC_CACHE_VERSION,
@@ -204,7 +207,13 @@ export async function syncCommand(options = {}) {
204
207
  throw new SyncError(`Failed to attach to staging KB: ${attachResult.error || "Unknown error"}`);
205
208
  }
206
209
  const entityIds = new Set();
207
- // Validate and filter dangling relationships
210
+ // Track entity counts by type
211
+ for (const { entity } of results) {
212
+ entityCounts[entity.type] = (entityCounts[entity.type] || 0) + 1;
213
+ }
214
+ // Persist entities
215
+ const { entityCount, kbModified: entitiesModified } = await persistEntities(prolog, results, entityIds);
216
+ // Validate and filter dangling relationships after entity IDs are known.
208
217
  const validationErrors = validateRelationships(allRelationships, entityIds);
209
218
  if (validationErrors.length > 0) {
210
219
  console.warn(`Warning: ${validationErrors.length} dangling relationship(s) found`);
@@ -214,19 +223,16 @@ export async function syncCommand(options = {}) {
214
223
  }
215
224
  const danglingKeys = new Set(validationErrors.map(({ relationship: r }) => `${r.type}|${r.from}|${r.to}`));
216
225
  const validRelationships = allRelationships.filter((r) => !danglingKeys.has(`${r.type}|${r.from}|${r.to}`));
217
- // Track entity counts by type
218
- for (const { entity } of results) {
219
- entityCounts[entity.type] = (entityCounts[entity.type] || 0) + 1;
220
- }
221
- // Persist entities
222
- const { entityCount, kbModified: entitiesModified } = await persistEntities(prolog, results, entityIds);
223
226
  // Persist relationships
224
227
  const { relationshipCount, kbModified: relationshipsModified } = await persistRelationships(prolog, results, validRelationships);
225
228
  const kbModified = entitiesModified || relationshipsModified;
226
229
  if (kbModified) {
227
230
  prolog.invalidateCache();
228
231
  }
229
- await prolog.query("kb_save");
232
+ const saveResult = await prolog.query("kb_save");
233
+ if (!saveResult.success) {
234
+ throw new SyncError(`Failed to save staging KB: ${saveResult.error || "Unknown error"}`);
235
+ }
230
236
  await prolog.query("kb_detach");
231
237
  await prolog.terminate();
232
238
  // Publish staging to live
@@ -239,7 +245,7 @@ export async function syncCommand(options = {}) {
239
245
  continue;
240
246
  }
241
247
  evictedHashes[key] = hash;
242
- evictedSeenAt[key] = nextHashes[key] ?? nowIso;
248
+ evictedSeenAt[key] = nextSeenAt[key] ?? nowIso;
243
249
  }
244
250
  const liveCachePath = path.join(livePath, "sync-cache.json");
245
251
  writeSyncCache(liveCachePath, {
@@ -248,7 +254,9 @@ export async function syncCommand(options = {}) {
248
254
  seenAt: evictedSeenAt,
249
255
  });
250
256
  published = true;
251
- if (markdownFiles.length > 0 && entityCount < markdownFiles.length) {
257
+ if (performedFullReindex &&
258
+ markdownFiles.length > 0 &&
259
+ entityCount < markdownFiles.length) {
252
260
  diagnostics.push(createDocsNotIndexedDiagnostic(markdownFiles.length, entityCount));
253
261
  }
254
262
  console.log(`✓ Imported ${entityCount} entities, ${relationshipCount} relationships`);
@@ -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;AAsBD,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;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"}
@@ -26,6 +26,7 @@ export class ManifestError extends Error {
26
26
  this.name = "ManifestError";
27
27
  }
28
28
  }
29
+ // implements REQ-007
29
30
  export function extractFromManifest(filePath) {
30
31
  try {
31
32
  const content = readFileSync(filePath, "utf8");
@@ -82,7 +83,7 @@ export function extractFromManifest(filePath) {
82
83
  id,
83
84
  type: "symbol",
84
85
  title: symbol.title,
85
- status: symbol.status || "draft",
86
+ status: symbol.status || "active",
86
87
  created_at: symbol.created_at || new Date().toISOString(),
87
88
  updated_at: symbol.updated_at || new Date().toISOString(),
88
89
  source: filePath,
@@ -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;AAED,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;AAED,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,CAmJtE;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CASjE"}
@@ -18,6 +18,16 @@
18
18
  import { createHash } from "node:crypto";
19
19
  import { readFileSync } from "node:fs";
20
20
  import matter from "gray-matter";
21
+ const DEFAULT_STATUS_BY_TYPE = {
22
+ req: "open",
23
+ scenario: "draft",
24
+ test: "pending",
25
+ adr: "proposed",
26
+ flag: "active",
27
+ event: "active",
28
+ symbol: "active",
29
+ fact: "active",
30
+ };
21
31
  export class FrontmatterError extends Error {
22
32
  filePath;
23
33
  classification;
@@ -81,6 +91,7 @@ export function detectEmbeddedEntities(data, entityType) {
81
91
  }
82
92
  return detected;
83
93
  }
94
+ // implements REQ-007, REQ-004
84
95
  export function extractFromMarkdown(filePath) {
85
96
  let content;
86
97
  try {
@@ -142,7 +153,7 @@ export function extractFromMarkdown(filePath) {
142
153
  id,
143
154
  type,
144
155
  title: data.title,
145
- status: data.status || "draft",
156
+ status: data.status || DEFAULT_STATUS_BY_TYPE[String(type)] || "active",
146
157
  created_at: data.created_at || new Date().toISOString(),
147
158
  updated_at: data.updated_at || new Date().toISOString(),
148
159
  source: filePath,
package/dist/prolog.d.ts CHANGED
@@ -25,6 +25,8 @@ export declare class PrologProcess {
25
25
  query(goal: string | string[]): Promise<QueryResult>;
26
26
  invalidateCache(): void;
27
27
  private isCacheableGoal;
28
+ private isMutatingGoal;
29
+ private isExplicitSaveGoal;
28
30
  private queryOneShot;
29
31
  private execOneShot;
30
32
  private normalizeGoal;
@@ -1 +1 @@
1
- {"version":3,"file":"prolog.d.ts","sourceRoot":"","sources":["../src/prolog.ts"],"names":[],"mappings":"AA4BA,wBAAgB,eAAe,IAAI,MAAM,CAsCxC;AACD,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,WAAW,CAAM;IACzB,OAAO,CAAC,KAAK,CAAuC;IACpD,OAAO,CAAC,oBAAoB,CAAoC;IAChE,OAAO,CAAC,cAAc,CACyC;IAC/D,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,aAAa,CAA6B;gBAEtC,OAAO,GAAE,aAAkB;IAKjC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAoCd,YAAY;IAyCpB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC;IAuJ1D,eAAe,IAAI,IAAI;IAIvB,OAAO,CAAC,eAAe;YAYT,YAAY;IA0B1B,OAAO,CAAC,WAAW;IA8EnB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,eAAe;IAevB,OAAO,CAAC,cAAc;IA8BtB,SAAS,IAAI,OAAO;IAIpB,MAAM,IAAI,MAAM;IAIV,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;CAyBjC"}
1
+ {"version":3,"file":"prolog.d.ts","sourceRoot":"","sources":["../src/prolog.ts"],"names":[],"mappings":"AA4BA,wBAAgB,eAAe,IAAI,MAAM,CAsCxC;AACD,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,WAAW,CAAM;IACzB,OAAO,CAAC,KAAK,CAAuC;IACpD,OAAO,CAAC,oBAAoB,CAAoC;IAChE,OAAO,CAAC,cAAc,CACyC;IAC/D,OAAO,CAAC,cAAc,CAAuB;IAC7C,OAAO,CAAC,aAAa,CAA6B;gBAEtC,OAAO,GAAE,aAAkB;IAKjC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAoCd,YAAY;IAyCpB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC;IAuJ1D,eAAe,IAAI,IAAI;IAIvB,OAAO,CAAC,eAAe;IAavB,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,kBAAkB;YAIZ,YAAY;IAkC1B,OAAO,CAAC,WAAW;IA8FnB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,eAAe;IAevB,OAAO,CAAC,cAAc;IA8BtB,SAAS,IAAI,OAAO;IAIpB,MAAM,IAAI,MAAM;IAIV,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;CAyBjC"}
package/dist/prolog.js CHANGED
@@ -73,7 +73,7 @@ export class PrologProcess {
73
73
  const kbPath = resolveKbPlPath();
74
74
  this.process = spawn(this.swiplPath, [
75
75
  "-g",
76
- `use_module('${kbPath}'), set_prolog_flag(answer_write_options, [max_depth(0), quoted(true)])`,
76
+ `use_module('${kbPath}'), use_module(library(semweb/rdf_db)), set_prolog_flag(answer_write_options, [max_depth(0), quoted(true)])`,
77
77
  "--quiet",
78
78
  ]);
79
79
  if (!this.process.stdout || !this.process.stderr || !this.process.stdin) {
@@ -256,6 +256,16 @@ export class PrologProcess {
256
256
  trimmed.startsWith("kb_delete_") ||
257
257
  trimmed.startsWith("kb_retract_"));
258
258
  }
259
+ // implements REQ-009
260
+ isMutatingGoal(goal) {
261
+ return (goal.includes("kb_assert_") ||
262
+ goal.includes("kb_delete_") ||
263
+ goal.includes("kb_retract_"));
264
+ }
265
+ // implements REQ-009
266
+ isExplicitSaveGoal(goal) {
267
+ return goal.trim().startsWith("kb_save");
268
+ }
259
269
  async queryOneShot(goal) {
260
270
  if (Array.isArray(goal)) {
261
271
  return this.execOneShot(goal, this.attachedKbPath);
@@ -269,6 +279,13 @@ export class PrologProcess {
269
279
  }
270
280
  const attachMatch = trimmedGoal.match(/^kb_attach\('(.+)'\)$/);
271
281
  if (attachMatch) {
282
+ if (this.attachedKbPath !== null) {
283
+ return {
284
+ success: false,
285
+ bindings: {},
286
+ error: "KB already attached",
287
+ };
288
+ }
272
289
  const attachResult = this.execOneShot(trimmedGoal, null);
273
290
  if (attachResult.success) {
274
291
  this.attachedKbPath = attachMatch[1];
@@ -278,11 +295,21 @@ export class PrologProcess {
278
295
  return this.execOneShot(trimmedGoal, this.attachedKbPath);
279
296
  }
280
297
  execOneShot(goal, kbPath) {
281
- const goalList = Array.isArray(goal)
298
+ const originalGoalList = Array.isArray(goal)
282
299
  ? goal.map((item) => this.normalizeGoal(item))
283
300
  : [this.normalizeGoal(goal)];
301
+ const explicitSaveRequested = Array.isArray(goal)
302
+ ? originalGoalList.some((item) => this.isExplicitSaveGoal(item))
303
+ : false;
304
+ const goalList = explicitSaveRequested
305
+ ? originalGoalList.filter((item) => !this.isExplicitSaveGoal(item))
306
+ : originalGoalList;
284
307
  const isBatch = goalList.length > 1;
285
- const combinedGoal = goalList.length === 1 ? goalList[0] : `(${goalList.join(", ")})`;
308
+ const combinedGoal = goalList.length === 0
309
+ ? "true"
310
+ : goalList.length === 1
311
+ ? goalList[0]
312
+ : `(${goalList.join(", ")})`;
286
313
  const kbModulePath = resolveKbPlPath();
287
314
  const prologGoal = [
288
315
  `use_module('${kbModulePath}')`,
@@ -292,8 +319,14 @@ export class PrologProcess {
292
319
  "read_term_from_atom(GoalAtom, Goal, [variable_names(Vars)])",
293
320
  kbPath ? "getenv('KIBI_KB_PATH', KBPath), kb_attach(KBPath)" : "true",
294
321
  isBatch ? "WrappedGoal = rdf_transaction(Goal)" : "WrappedGoal = Goal",
295
- "(catch(call(WrappedGoal), E, (print_message(error, E), fail)) -> (forall(member(Name=Value, Vars), (write(Name), write('='), write_term(Value, [quoted(true), max_depth(0)]), writeln('.'))), writeln('__KIBI_TRUE__.')) ; writeln('__KIBI_FALSE__.'))",
296
- kbPath ? "kb_save, kb_detach" : "true",
322
+ "(catch(call(WrappedGoal), E, (print_message(error, E), fail)) -> QuerySucceeded = true ; QuerySucceeded = false)",
323
+ kbPath &&
324
+ (explicitSaveRequested ||
325
+ goalList.some((item) => this.isMutatingGoal(item)))
326
+ ? "(QuerySucceeded == true -> kb_save ; true)"
327
+ : "true",
328
+ kbPath ? "kb_detach" : "true",
329
+ "(QuerySucceeded == true -> (forall(member(Name=Value, Vars), (write(Name), write('='), write_term(Value, [quoted(true), max_depth(0)]), writeln('.'))), writeln('__KIBI_TRUE__.')) ; writeln('__KIBI_FALSE__.'))",
297
330
  ].join(", ");
298
331
  const result = spawnSync(this.swiplPath, ["-q", "-g", prologGoal, "-t", "halt"], {
299
332
  encoding: "utf8",
@@ -313,6 +346,13 @@ export class PrologProcess {
313
346
  }
314
347
  const stdout = result.stdout ?? "";
315
348
  const stderr = result.stderr ?? "";
349
+ if (stderr.includes("ERROR")) {
350
+ return {
351
+ success: false,
352
+ bindings: {},
353
+ error: this.translateError(stderr),
354
+ };
355
+ }
316
356
  if (stdout.includes("__KIBI_TRUE__")) {
317
357
  const clean = stdout
318
358
  .split("\n")
@@ -323,13 +363,6 @@ export class PrologProcess {
323
363
  bindings: this.extractBindings(clean),
324
364
  };
325
365
  }
326
- if (stderr.includes("ERROR")) {
327
- return {
328
- success: false,
329
- bindings: {},
330
- error: this.translateError(stderr),
331
- };
332
- }
333
366
  return {
334
367
  success: false,
335
368
  bindings: {},
@@ -1 +1 @@
1
- {"version":3,"file":"entity.d.ts","sourceRoot":"","sources":["../../../src/public/schemas/entity.ts"],"names":[],"mappings":"AAoBA,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsDjB,CAAC;AAEF,eAAe,YAAY,CAAC"}
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"}
@@ -28,6 +28,7 @@ const entitySchema = {
28
28
  type: "string",
29
29
  enum: [
30
30
  "active",
31
+ "inactive",
31
32
  "draft",
32
33
  "archived",
33
34
  "deleted",
@@ -36,6 +37,15 @@ const entitySchema = {
36
37
  "pending",
37
38
  "in_progress",
38
39
  "superseded",
40
+ "open",
41
+ "closed",
42
+ "deprecated",
43
+ "passing",
44
+ "failing",
45
+ "skipped",
46
+ "proposed",
47
+ "accepted",
48
+ "removed",
39
49
  ],
40
50
  },
41
51
  created_at: { type: "string" },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kibi-cli",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "type": "module",
5
5
  "description": "Kibi CLI for knowledge base management",
6
6
  "engines": {
@@ -68,7 +68,7 @@
68
68
  "fast-glob": "^3.2.12",
69
69
  "gray-matter": "^4.0.3",
70
70
  "js-yaml": "^4.1.0",
71
- "kibi-core": "^0.1.7",
71
+ "kibi-core": "^0.1.10",
72
72
  "ts-morph": "^23.0.0"
73
73
  },
74
74
  "license": "AGPL-3.0-or-later",
@@ -5,6 +5,11 @@
5
5
  "description": "Configuration schema for Kibi knowledge base",
6
6
  "type": "object",
7
7
  "properties": {
8
+ "$schema": {
9
+ "type": "string",
10
+ "format": "uri",
11
+ "description": "Optional JSON Schema identifier for editor/schema tooling"
12
+ },
8
13
  "paths": {
9
14
  "type": "object",
10
15
  "description": "Directory paths for entity documentation",
@@ -29,6 +29,7 @@ const entitySchema = {
29
29
  type: "string",
30
30
  enum: [
31
31
  "active",
32
+ "inactive",
32
33
  "draft",
33
34
  "archived",
34
35
  "deleted",
@@ -37,6 +38,15 @@ const entitySchema = {
37
38
  "pending",
38
39
  "in_progress",
39
40
  "superseded",
41
+ "open",
42
+ "closed",
43
+ "deprecated",
44
+ "passing",
45
+ "failing",
46
+ "skipped",
47
+ "proposed",
48
+ "accepted",
49
+ "removed",
40
50
  ],
41
51
  },
42
52
  created_at: { type: "string" },
@@ -9,6 +9,7 @@
9
9
  "type": "string",
10
10
  "enum": [
11
11
  "active",
12
+ "inactive",
12
13
  "draft",
13
14
  "archived",
14
15
  "deleted",
@@ -16,7 +17,16 @@
16
17
  "rejected",
17
18
  "pending",
18
19
  "in_progress",
19
- "superseded"
20
+ "superseded",
21
+ "open",
22
+ "closed",
23
+ "deprecated",
24
+ "passing",
25
+ "failing",
26
+ "skipped",
27
+ "proposed",
28
+ "accepted",
29
+ "removed"
20
30
  ]
21
31
  },
22
32
  "created_at": { "type": "string" },