@saasontools/strauss-kb 0.1.7 → 0.1.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.
@@ -934,55 +934,6 @@ ${CONTEXT_END}` : null;
934
934
  return { file, action: "appended" };
935
935
  }
936
936
 
937
- // src/kb-log.ts
938
- import { z as z5 } from "zod";
939
- var LOG_FILE = "log.jsonl";
940
- var kbLogEntrySchema = z5.object({
941
- at: z5.string().min(1),
942
- by: z5.string().min(1),
943
- operation: z5.string().min(1),
944
- conceptId: z5.string().min(1),
945
- /** Second concept id, where the operation relates two — supersession. */
946
- target: z5.string().min(1).optional()
947
- }).strict();
948
- function renderLogEntry(entry) {
949
- return `${JSON.stringify(kbLogEntrySchema.parse(entry))}
950
- `;
951
- }
952
- function parseLog(raw) {
953
- const entries = [];
954
- const malformed = [];
955
- raw.split("\n").forEach((text, index) => {
956
- if (!text.trim()) return;
957
- let value;
958
- try {
959
- value = JSON.parse(text);
960
- } catch {
961
- malformed.push({ line: index + 1, text });
962
- return;
963
- }
964
- const parsed = kbLogEntrySchema.safeParse(value);
965
- if (!parsed.success) {
966
- malformed.push({ line: index + 1, text });
967
- return;
968
- }
969
- entries.push(parsed.data);
970
- });
971
- return { entries, malformed };
972
- }
973
-
974
- // src/json-schema.ts
975
- import { z as z6 } from "zod";
976
- function kbJsonSchemas() {
977
- return {
978
- recordFrontmatter: z6.toJSONSchema(kbRecordFrontmatterSchema, {
979
- io: "input"
980
- }),
981
- composeInput: z6.toJSONSchema(composeInputSchema, { io: "input" }),
982
- logEntry: z6.toJSONSchema(kbLogEntrySchema, { io: "input" })
983
- };
984
- }
985
-
986
937
  // src/kb-edges.ts
987
938
  var KB_EDGE_KINDS = [
988
939
  "body-link",
@@ -1059,6 +1010,342 @@ function anchorsTouch(left, right) {
1059
1010
  return left.symbol === right.symbol;
1060
1011
  }
1061
1012
 
1013
+ // src/validate.ts
1014
+ function validateBundle(records) {
1015
+ const byId = new Map(records.map((record) => [record.conceptId, record]));
1016
+ const problems = [];
1017
+ const report = (check, conceptId2, note) => problems.push({ check, conceptId: conceptId2, note });
1018
+ for (const record of records) {
1019
+ const { conceptId: conceptId2, frontmatter: fm } = record;
1020
+ if (!isKbRecordType(fm.type)) {
1021
+ report("type", conceptId2, `unrecognised type "${fm.type}"`);
1022
+ }
1023
+ if (fm.strauss_status === "superseded") {
1024
+ const by = fm.strauss_superseded_by;
1025
+ if (!by) {
1026
+ report("superseded_by", conceptId2, "superseded with no replacement");
1027
+ } else if (!byId.has(by)) {
1028
+ report("superseded_by", conceptId2, `replacement ${by} is missing`);
1029
+ } else if (!byId.get(by)?.frontmatter.strauss_supersedes?.includes(conceptId2)) {
1030
+ report("backlink", by, `does not list ${conceptId2} in supersedes`);
1031
+ }
1032
+ }
1033
+ for (const old of fm.strauss_supersedes ?? []) {
1034
+ const previous = byId.get(old);
1035
+ if (!previous) {
1036
+ report("supersedes", conceptId2, `target ${old} is missing`);
1037
+ } else if (previous.frontmatter.strauss_status !== "superseded") {
1038
+ report("supersedes", conceptId2, `${old} is not marked superseded`);
1039
+ }
1040
+ }
1041
+ if (fm.strauss_assumption && fm.sources?.length) {
1042
+ report("assumption", conceptId2, "marked an assumption but cites sources");
1043
+ }
1044
+ }
1045
+ return problems;
1046
+ }
1047
+
1048
+ // src/doctor.ts
1049
+ var DEFAULT_EXPIRING_DAYS = 30;
1050
+ var DEFAULT_UNVERIFIED_DAYS = 90;
1051
+ var DEFAULT_AGING_DAYS = 90;
1052
+ var KB_DOCTOR_CHECKS = [
1053
+ "expired",
1054
+ "expiring",
1055
+ "unverified",
1056
+ "aging",
1057
+ "orphaned",
1058
+ "broken-supersession",
1059
+ "superseded-but-cited"
1060
+ ];
1061
+ var CHECK_HEADLINES = {
1062
+ expired: "past its stale_after date",
1063
+ expiring: "stale_after falls within the window",
1064
+ unverified: "nobody has ever confirmed it, and it is old enough to matter",
1065
+ aging: "still open or still proposed long after it was written",
1066
+ orphaned: "no other record links to it",
1067
+ "broken-supersession": "the supersession pointers do not resolve",
1068
+ "superseded-but-cited": "a live record's body links to one that no longer holds"
1069
+ };
1070
+ var DAY_MS = 864e5;
1071
+ function doctor(bundle, options = {}) {
1072
+ const thresholds = {
1073
+ expiringDays: options.expiringDays ?? DEFAULT_EXPIRING_DAYS,
1074
+ unverifiedDays: options.unverifiedDays ?? DEFAULT_UNVERIFIED_DAYS,
1075
+ agingDays: options.agingDays ?? DEFAULT_AGING_DAYS
1076
+ };
1077
+ const now = options.now ?? /* @__PURE__ */ new Date();
1078
+ const adjudicated = adjudicate(bundle, bundle, now);
1079
+ const standings = new Map(
1080
+ adjudicated.map((hit) => [hit.record.conceptId, hit.standing])
1081
+ );
1082
+ const inForce = adjudicated.filter(
1083
+ (hit) => hit.standing !== "superseded" && hit.standing !== "rejected"
1084
+ );
1085
+ const groups = [
1086
+ group("expired", expired(inForce, now)),
1087
+ group("expiring", expiring(inForce, now, thresholds.expiringDays)),
1088
+ group("unverified", unverified(inForce, now, thresholds.unverifiedDays)),
1089
+ group("aging", aging(inForce, now, thresholds.agingDays)),
1090
+ group("orphaned", orphaned(bundle)),
1091
+ group("broken-supersession", brokenSupersession(bundle, adjudicated)),
1092
+ group("superseded-but-cited", supersededButCited(bundle, standings))
1093
+ ];
1094
+ const counts = Object.fromEntries(
1095
+ groups.map((entry) => [entry.check, entry.count])
1096
+ );
1097
+ const findingCount = groups.reduce((total, entry) => total + entry.count, 0);
1098
+ return {
1099
+ recordCount: bundle.length,
1100
+ thresholds,
1101
+ counts,
1102
+ groups,
1103
+ findingCount,
1104
+ healthy: findingCount === 0
1105
+ };
1106
+ }
1107
+ function group(check, findings) {
1108
+ return {
1109
+ check,
1110
+ headline: CHECK_HEADLINES[check],
1111
+ count: findings.length,
1112
+ findings
1113
+ };
1114
+ }
1115
+ function expired(hits, now) {
1116
+ const findings = [];
1117
+ for (const hit of hits) {
1118
+ const raw = hit.record.frontmatter.stale_after;
1119
+ if (!raw) continue;
1120
+ const at = Date.parse(raw);
1121
+ if (Number.isNaN(at)) {
1122
+ findings.push(
1123
+ finding(hit.record, `stale_after "${raw}" is not a readable date`)
1124
+ );
1125
+ continue;
1126
+ }
1127
+ if (at < now.getTime()) {
1128
+ findings.push(
1129
+ finding(
1130
+ hit.record,
1131
+ `stale since ${raw} (${daysBetween(at, now.getTime())} days ago)`
1132
+ )
1133
+ );
1134
+ }
1135
+ }
1136
+ return findings;
1137
+ }
1138
+ function expiring(hits, now, withinDays) {
1139
+ const horizon = now.getTime() + withinDays * DAY_MS;
1140
+ const findings = [];
1141
+ for (const hit of hits) {
1142
+ const raw = hit.record.frontmatter.stale_after;
1143
+ if (!raw) continue;
1144
+ const at = Date.parse(raw);
1145
+ if (Number.isNaN(at) || at < now.getTime() || at > horizon) continue;
1146
+ findings.push(
1147
+ finding(
1148
+ hit.record,
1149
+ `goes stale ${raw} (in ${daysBetween(now.getTime(), at)} days)`
1150
+ )
1151
+ );
1152
+ }
1153
+ return findings;
1154
+ }
1155
+ function unverified(hits, now, olderThanDays) {
1156
+ const findings = [];
1157
+ for (const hit of hits) {
1158
+ if (hit.record.frontmatter.verified?.length) continue;
1159
+ const age = ageInDays(hit.record, now);
1160
+ if (age === null || age <= olderThanDays) continue;
1161
+ findings.push(
1162
+ finding(hit.record, `never verified, written ${age} days ago`)
1163
+ );
1164
+ }
1165
+ return findings;
1166
+ }
1167
+ function aging(hits, now, olderThanDays) {
1168
+ const findings = [];
1169
+ for (const hit of hits) {
1170
+ const status = hit.record.frontmatter.strauss_status;
1171
+ if (status !== "open" && status !== "proposed") continue;
1172
+ const age = ageInDays(hit.record, now);
1173
+ if (age === null || age <= olderThanDays) continue;
1174
+ findings.push(
1175
+ finding(
1176
+ hit.record,
1177
+ status === "open" ? `open for ${age} days` : `proposed ${age} days ago and still unsettled`
1178
+ )
1179
+ );
1180
+ }
1181
+ return findings.sort(
1182
+ (left, right) => left.conceptId.localeCompare(right.conceptId)
1183
+ );
1184
+ }
1185
+ function orphaned(bundle) {
1186
+ const present = new Set(bundle.map((record) => record.conceptId));
1187
+ const referenced = /* @__PURE__ */ new Set();
1188
+ for (const record of bundle) {
1189
+ for (const neighbour of edgeNeighbours(record, bundle, "body-link")) {
1190
+ referenced.add(neighbour.conceptId);
1191
+ }
1192
+ for (const replaced of record.frontmatter.strauss_supersedes ?? []) {
1193
+ referenced.add(replaced);
1194
+ }
1195
+ const replacement = record.frontmatter.strauss_superseded_by;
1196
+ if (replacement && present.has(replacement)) {
1197
+ referenced.add(record.conceptId);
1198
+ }
1199
+ }
1200
+ return bundle.filter((record) => !referenced.has(record.conceptId)).map((record) => finding(record, "no other record links to it"));
1201
+ }
1202
+ var SUPERSESSION_CHECKS = /* @__PURE__ */ new Set([
1203
+ "superseded_by",
1204
+ "supersedes",
1205
+ "backlink"
1206
+ ]);
1207
+ function brokenSupersession(bundle, adjudicated) {
1208
+ const byId = new Map(bundle.map((record) => [record.conceptId, record]));
1209
+ const findings = [];
1210
+ const seen = /* @__PURE__ */ new Set();
1211
+ const add = (record, note) => {
1212
+ const key = `${record.conceptId}\0${note}`;
1213
+ if (seen.has(key)) return;
1214
+ seen.add(key);
1215
+ findings.push(finding(record, note));
1216
+ };
1217
+ for (const problem of validateBundle(bundle)) {
1218
+ if (!SUPERSESSION_CHECKS.has(problem.check)) continue;
1219
+ const record = byId.get(problem.conceptId);
1220
+ if (record) add(record, problem.note);
1221
+ }
1222
+ for (const record of bundle) {
1223
+ const replacement = record.frontmatter.strauss_superseded_by;
1224
+ if (!replacement) continue;
1225
+ if (!byId.has(replacement)) {
1226
+ add(record, `replacement ${replacement} is missing`);
1227
+ } else if (record.frontmatter.strauss_status !== "superseded") {
1228
+ add(
1229
+ record,
1230
+ `names ${replacement} as its replacement but is not marked superseded`
1231
+ );
1232
+ }
1233
+ }
1234
+ for (const hit of adjudicated) {
1235
+ for (const warning of hit.warnings) {
1236
+ if (warning.kind === "broken-chain") {
1237
+ add(hit.record, `replacement ${warning.missing} is missing`);
1238
+ } else if (warning.kind === "chain-cycle") {
1239
+ add(
1240
+ hit.record,
1241
+ `supersession chain cycles through ${warning.through.join(" \u2192 ")}`
1242
+ );
1243
+ } else if (warning.kind === "forked-chain") {
1244
+ add(
1245
+ hit.record,
1246
+ `two records claim to replace it: ${warning.heads.join(", ")}`
1247
+ );
1248
+ }
1249
+ }
1250
+ }
1251
+ return findings.sort(
1252
+ (left, right) => left.conceptId.localeCompare(right.conceptId)
1253
+ );
1254
+ }
1255
+ function supersededButCited(bundle, standings) {
1256
+ const byId = new Map(bundle.map((record) => [record.conceptId, record]));
1257
+ const findings = [];
1258
+ for (const record of bundle) {
1259
+ const standing = standings.get(record.conceptId);
1260
+ if (standing === "superseded" || standing === "rejected") continue;
1261
+ for (const target of edgeNeighbours(record, bundle, "body-link")) {
1262
+ const targetStanding = standings.get(target.conceptId);
1263
+ if (targetStanding !== "superseded" && targetStanding !== "rejected") {
1264
+ continue;
1265
+ }
1266
+ if (replaces(record, target)) continue;
1267
+ const replacement = target.frontmatter.strauss_superseded_by;
1268
+ findings.push(
1269
+ finding(
1270
+ record,
1271
+ `cites ${targetStanding} ${target.conceptId}${targetStanding === "superseded" && replacement && byId.has(replacement) ? ` \u2014 replaced by ${replacement}` : ""}`
1272
+ )
1273
+ );
1274
+ }
1275
+ }
1276
+ return findings;
1277
+ }
1278
+ function replaces(later, earlier) {
1279
+ return (later.frontmatter.strauss_supersedes ?? []).includes(earlier.conceptId) || earlier.frontmatter.strauss_superseded_by === later.conceptId;
1280
+ }
1281
+ function finding(record, note) {
1282
+ return {
1283
+ conceptId: record.conceptId,
1284
+ title: record.frontmatter.title ?? null,
1285
+ status: record.frontmatter.strauss_status,
1286
+ note
1287
+ };
1288
+ }
1289
+ function daysBetween(from, to) {
1290
+ return Math.max(0, Math.floor((to - from) / DAY_MS));
1291
+ }
1292
+ function ageInDays(record, now) {
1293
+ const at = record.frontmatter.generated?.at;
1294
+ if (!at) return null;
1295
+ const written = Date.parse(at);
1296
+ if (Number.isNaN(written)) return null;
1297
+ return daysBetween(written, now.getTime());
1298
+ }
1299
+
1300
+ // src/kb-log.ts
1301
+ import { z as z5 } from "zod";
1302
+ var LOG_FILE = "log.jsonl";
1303
+ var kbLogEntrySchema = z5.object({
1304
+ at: z5.string().min(1),
1305
+ by: z5.string().min(1),
1306
+ operation: z5.string().min(1),
1307
+ conceptId: z5.string().min(1),
1308
+ /** Second concept id, where the operation relates two — supersession. */
1309
+ target: z5.string().min(1).optional()
1310
+ }).strict();
1311
+ function renderLogEntry(entry) {
1312
+ return `${JSON.stringify(kbLogEntrySchema.parse(entry))}
1313
+ `;
1314
+ }
1315
+ function parseLog(raw) {
1316
+ const entries = [];
1317
+ const malformed = [];
1318
+ raw.split("\n").forEach((text, index) => {
1319
+ if (!text.trim()) return;
1320
+ let value;
1321
+ try {
1322
+ value = JSON.parse(text);
1323
+ } catch {
1324
+ malformed.push({ line: index + 1, text });
1325
+ return;
1326
+ }
1327
+ const parsed = kbLogEntrySchema.safeParse(value);
1328
+ if (!parsed.success) {
1329
+ malformed.push({ line: index + 1, text });
1330
+ return;
1331
+ }
1332
+ entries.push(parsed.data);
1333
+ });
1334
+ return { entries, malformed };
1335
+ }
1336
+
1337
+ // src/json-schema.ts
1338
+ import { z as z6 } from "zod";
1339
+ function kbJsonSchemas() {
1340
+ return {
1341
+ recordFrontmatter: z6.toJSONSchema(kbRecordFrontmatterSchema, {
1342
+ io: "input"
1343
+ }),
1344
+ composeInput: z6.toJSONSchema(composeInputSchema, { io: "input" }),
1345
+ logEntry: z6.toJSONSchema(kbLogEntrySchema, { io: "input" })
1346
+ };
1347
+ }
1348
+
1062
1349
  // src/trace.ts
1063
1350
  var TRACE_EDGES = ["supersession", "anchor", "source"];
1064
1351
  function trace(seedId, bundle, options = {}) {
@@ -1097,41 +1384,6 @@ function byGeneratedAt(left, right) {
1097
1384
  return at(left).localeCompare(at(right)) || left.depth - right.depth;
1098
1385
  }
1099
1386
 
1100
- // src/validate.ts
1101
- function validateBundle(records) {
1102
- const byId = new Map(records.map((record) => [record.conceptId, record]));
1103
- const problems = [];
1104
- const report = (check, conceptId2, note) => problems.push({ check, conceptId: conceptId2, note });
1105
- for (const record of records) {
1106
- const { conceptId: conceptId2, frontmatter: fm } = record;
1107
- if (!isKbRecordType(fm.type)) {
1108
- report("type", conceptId2, `unrecognised type "${fm.type}"`);
1109
- }
1110
- if (fm.strauss_status === "superseded") {
1111
- const by = fm.strauss_superseded_by;
1112
- if (!by) {
1113
- report("superseded_by", conceptId2, "superseded with no replacement");
1114
- } else if (!byId.has(by)) {
1115
- report("superseded_by", conceptId2, `replacement ${by} is missing`);
1116
- } else if (!byId.get(by)?.frontmatter.strauss_supersedes?.includes(conceptId2)) {
1117
- report("backlink", by, `does not list ${conceptId2} in supersedes`);
1118
- }
1119
- }
1120
- for (const old of fm.strauss_supersedes ?? []) {
1121
- const previous = byId.get(old);
1122
- if (!previous) {
1123
- report("supersedes", conceptId2, `target ${old} is missing`);
1124
- } else if (previous.frontmatter.strauss_status !== "superseded") {
1125
- report("supersedes", conceptId2, `${old} is not marked superseded`);
1126
- }
1127
- }
1128
- if (fm.strauss_assumption && fm.sources?.length) {
1129
- report("assumption", conceptId2, "marked an assumption but cites sources");
1130
- }
1131
- }
1132
- return problems;
1133
- }
1134
-
1135
1387
  // src/commands/answer.ts
1136
1388
  import { z as z8 } from "zod";
1137
1389
 
@@ -1221,14 +1473,104 @@ var contextCommand = define({
1221
1473
  }
1222
1474
  });
1223
1475
 
1224
- // src/commands/list.ts
1476
+ // src/commands/doctor.ts
1225
1477
  import { z as z10 } from "zod";
1478
+ var days = (what, fallback) => z10.number().int().positive().optional().describe(`${what} Defaults to ${fallback}.`);
1479
+ var doctorCommand = define({
1480
+ name: "doctor",
1481
+ tool: "kb_doctor",
1482
+ usage: "doctor [--expiring-days N] [--unverified-days N] [--aging-days N] [--strict]",
1483
+ description: "A health sweep over a whole base: what the calendar has already retired, what nobody ever confirmed, what has been open or proposed long enough that the status is now the answer, and what the graph has dropped on the floor. Read-only \u2014 it never writes, never supersedes, and never re-dates anything; every finding names a record for a person to repair. Seven checks, grouped and counted: expired (past `stale_after`), expiring (inside the window), unverified (an empty `verified[]` on a record old enough to matter), aging (still `open` or `proposed`), orphaned (no other record links to it), broken supersession (a chain that does not resolve), and superseded-but-cited (a live record whose body links to a record that no longer holds). Every group is reported even when empty, because a check that found nothing and a check that never ran look identical in a report that only lists findings.\n\nThis is the question no reader thinks to ask, which is why it needs a command: decay is invisible from inside a single record \u2014 a stale one reads exactly like a live one, and a question nobody answered reads exactly like one nobody asked. Reach for it when picking up a base someone else kept, before trusting a base you have not touched in months, or on a schedule; kb_validate is the narrower neighbour, checking only whether pointers between records agree.",
1484
+ input: z10.object({
1485
+ bundlePath,
1486
+ expiringDays: days(
1487
+ "How far ahead `expiring` looks, in days.",
1488
+ DEFAULT_EXPIRING_DAYS
1489
+ ),
1490
+ unverifiedDays: days(
1491
+ "How old an unconfirmed record must be before `unverified` reports it, in days.",
1492
+ DEFAULT_UNVERIFIED_DAYS
1493
+ ),
1494
+ agingDays: days(
1495
+ "How long a record may stay `open` or `proposed` before `aging` reports it, in days.",
1496
+ DEFAULT_AGING_DAYS
1497
+ ),
1498
+ strict: z10.boolean().optional().describe(
1499
+ "Turn an expired record into a non-zero exit for the CLI. No effect on the report itself."
1500
+ )
1501
+ }),
1502
+ // Presence, not truthiness: `--expiring-days ""` is a caller who meant
1503
+ // something and mistyped it, and a falsy test would answer by quietly
1504
+ // sweeping at the default. Passed through as given, the schema rejects it
1505
+ // and says which field.
1506
+ fromArgv: (argv, path) => {
1507
+ const expiring2 = argvFlag(argv, "--expiring-days");
1508
+ const unverified2 = argvFlag(argv, "--unverified-days");
1509
+ const agingDays = argvFlag(argv, "--aging-days");
1510
+ return {
1511
+ bundlePath: path,
1512
+ ...expiring2 !== void 0 ? { expiringDays: Number(expiring2) } : {},
1513
+ ...unverified2 !== void 0 ? { unverifiedDays: Number(unverified2) } : {},
1514
+ ...agingDays !== void 0 ? { agingDays: Number(agingDays) } : {},
1515
+ ...argv.includes("--strict") ? { strict: true } : {}
1516
+ };
1517
+ },
1518
+ run: async ({ store, now }, { bundlePath: path, expiringDays, unverifiedDays, agingDays }) => {
1519
+ const checkedAt = now();
1520
+ const report = doctor(await store.list(path), {
1521
+ ...expiringDays !== void 0 ? { expiringDays } : {},
1522
+ ...unverifiedDays !== void 0 ? { unverifiedDays } : {},
1523
+ ...agingDays !== void 0 ? { agingDays } : {},
1524
+ now: new Date(checkedAt)
1525
+ });
1526
+ return { bundlePath: path, checkedAt, ...report };
1527
+ },
1528
+ render: (result) => render(result),
1529
+ // Only expiry, and only under --strict. The other six checks report debt a
1530
+ // reader decides about; an expired record is the base asserting something it
1531
+ // already said it would stop standing behind, which is the one finding a
1532
+ // pipeline can act on without a judgment call.
1533
+ failsWhen: (result, input) => input.strict === true && result.counts.expired > 0
1534
+ });
1535
+ function render(result) {
1536
+ const { thresholds } = result;
1537
+ const lines = [
1538
+ `# KB Doctor \u2014 ${result.bundlePath}`,
1539
+ `records: ${result.recordCount}`,
1540
+ `thresholds: expiring within ${thresholds.expiringDays}d, unverified over ${thresholds.unverifiedDays}d, aging over ${thresholds.agingDays}d`,
1541
+ `checked: ${result.checkedAt}`,
1542
+ ""
1543
+ ];
1544
+ const width = Math.max(...result.groups.map((group2) => group2.check.length));
1545
+ for (const group2 of result.groups) {
1546
+ lines.push(
1547
+ ` ${group2.check.padEnd(width)} ${String(group2.count).padStart(3)} ${group2.headline}`
1548
+ );
1549
+ }
1550
+ for (const group2 of result.groups) {
1551
+ if (!group2.count) continue;
1552
+ lines.push("", `## ${group2.check} (${group2.count})`);
1553
+ for (const found of group2.findings) {
1554
+ lines.push(
1555
+ `- ${found.conceptId}${found.title ? ` \u2014 ${found.title}` : ""}: ${found.note}`
1556
+ );
1557
+ }
1558
+ }
1559
+ lines.push(
1560
+ "",
1561
+ result.healthy ? "Nothing to repair." : `${result.findingCount} finding${result.findingCount === 1 ? "" : "s"} across ${result.groups.filter((group2) => group2.count).length} of ${result.groups.length} checks.`
1562
+ );
1563
+ return lines.join("\n");
1564
+ }
1565
+
1566
+ // src/commands/list.ts
1567
+ import { z as z11 } from "zod";
1226
1568
  var listCommand = define({
1227
1569
  name: "list",
1228
1570
  tool: "kb_list",
1229
1571
  usage: "list [type]",
1230
1572
  description: "Every record, optionally narrowed to one type. Use kb_query when you have a question; this is for enumerating.",
1231
- input: z10.object({ bundlePath, type: z10.enum(KB_RECORD_TYPES).optional() }),
1573
+ input: z11.object({ bundlePath, type: z11.enum(KB_RECORD_TYPES).optional() }),
1232
1574
  fromArgv: (argv, path) => ({ bundlePath: path, type: argv[1] }),
1233
1575
  run: async ({ store }, { bundlePath: path, type }) => (await store.list(path, type)).map((record) => ({
1234
1576
  conceptId: record.conceptId,
@@ -1240,17 +1582,17 @@ var listCommand = define({
1240
1582
  });
1241
1583
 
1242
1584
  // src/commands/load.ts
1243
- import { z as z11 } from "zod";
1585
+ import { z as z12 } from "zod";
1244
1586
  var loadCommand = define({
1245
1587
  name: "load",
1246
1588
  tool: "kb_load",
1247
1589
  usage: "load [type] [--budget N | --all]",
1248
1590
  description: "Load the whole knowledge base at once, each record with its standing. Prefer this over searching: these bases run to a few thousand tokens, and a reader holding all of it has perfect recall and knows why it is asking, which no ranker does. Superseded records arrive under `superseded` as name, replacement and date only \u2014 their bodies no longer hold, and reading one later in a long session is the mistake this prevents; pass the id to kb_trace when you need the history. Rejected and unresolved records arrive whole: what was turned down, and what is still open, is the part a diff cannot show you. Refuses with a count rather than truncating when the base is too large \u2014 a truncated base is indistinguishable from a complete one, and would have you conclude something was never decided from a slice you did not know was a slice. Call at the point of use, not once per session: a base loaded early is summarised away by compaction, so if the visible context holds no records from this base and the question at hand is one it might govern, load before answering \u2014 never conclude nothing was decided from a context with no KB content in it. This tool (with kb_query and kb_trace) is the only supported way to read a base; a raw file read bypasses supersession resolution and returns replaced records as if current.\n\nThat refusal is the default guardrail, meant for an agent that would otherwise burn its whole context on one call. `all` bypasses it and loads everything regardless of size: a deliberate operator with the budget to spend, not something to reach for automatically. It is mutually exclusive with `budgetTokens`. When the reader does not need everything, kb_query or a narrower `type` filter is the better fit than either.",
1249
- input: z11.object({
1591
+ input: z12.object({
1250
1592
  bundlePath,
1251
- type: z11.enum(KB_RECORD_TYPES).optional(),
1252
- budgetTokens: z11.number().int().positive().optional().describe("Approximate token ceiling. Defaults to 25000."),
1253
- all: z11.boolean().optional().describe(
1593
+ type: z12.enum(KB_RECORD_TYPES).optional(),
1594
+ budgetTokens: z12.number().int().positive().optional().describe("Approximate token ceiling. Defaults to 25000."),
1595
+ all: z12.boolean().optional().describe(
1254
1596
  "Load the entire base regardless of size. The deliberate-operator escape hatch; mutually exclusive with budgetTokens."
1255
1597
  )
1256
1598
  }).refine((value) => !(value.all && value.budgetTokens !== void 0), {
@@ -1288,25 +1630,25 @@ var loadCommand = define({
1288
1630
  });
1289
1631
 
1290
1632
  // src/commands/log.ts
1291
- import { z as z12 } from "zod";
1633
+ import { z as z13 } from "zod";
1292
1634
  var logCommand = define({
1293
1635
  name: "log",
1294
1636
  tool: "kb_log",
1295
1637
  usage: "log",
1296
1638
  description: "What touched what, and when. The only artifact here that cannot be reconstructed from the records, so malformed lines are reported rather than repaired.",
1297
- input: z12.object({ bundlePath }),
1639
+ input: z13.object({ bundlePath }),
1298
1640
  fromArgv: (_argv, path) => ({ bundlePath: path }),
1299
1641
  run: ({ store }, { bundlePath: path }) => store.readLog(path)
1300
1642
  });
1301
1643
 
1302
1644
  // src/commands/no-decision.ts
1303
- import { z as z13 } from "zod";
1645
+ import { z as z14 } from "zod";
1304
1646
  var noDecisionCommand = define({
1305
1647
  name: "no-decision",
1306
1648
  tool: "kb_no_decision",
1307
1649
  usage: "no-decision <reason...>",
1308
1650
  description: 'Claim in one sentence that there was nothing to decide. Gating on "did you write a decision?" rewards writing a junk one; gating on "did you answer?" does not, so silence has to be expressible. Idempotent \u2014 restating it is not a collision.',
1309
- input: z13.object({ bundlePath, reason: z13.string().min(1) }),
1651
+ input: z14.object({ bundlePath, reason: z14.string().min(1) }),
1310
1652
  fromArgv: (argv, path) => ({
1311
1653
  bundlePath: path,
1312
1654
  reason: argv.slice(1).join(" ").trim()
@@ -1323,20 +1665,20 @@ var noDecisionCommand = define({
1323
1665
  });
1324
1666
 
1325
1667
  // src/commands/pack.ts
1326
- import { z as z14 } from "zod";
1668
+ import { z as z15 } from "zod";
1327
1669
  var packCommand = define({
1328
1670
  name: "pack",
1329
1671
  tool: "kb_pack",
1330
1672
  usage: "pack <conceptId> [--hops N] [--max-nodes N] [--budget N]",
1331
1673
  description: "The bounded neighbourhood around one record: everything within `hops` of the root, ranked and cut to `maxNodes`, with every cut record named under Excluded \u2014 a named gap is knowable, a silent one is not. Prefer this over kb_load when the base is too large to hold whole and the work centres on one record; prefer it over kb_query when the question needs the governed neighbourhood \u2014 what was settled and what binds near this record \u2014 rather than a lookup by wording. Superseded records arrive as name, replacement and date stubs exactly as kb_load emits them: their bodies no longer hold, and kb_trace has the history. Refuses outright rather than truncating when the pack would exceed its token budget \u2014 a partial pack is indistinguishable from a complete one \u2014 reporting the record count and every already-cut id so the caller can lower hops or maxNodes, or raise the budget. The header carries the bundle, root, budget and a timestamp; everything below the header is byte-identical across runs over an unchanged base, so two packs can be diffed and a changed byte means changed knowledge. This tool (with kb_load, kb_query and kb_trace) is the only supported way to read a base; a raw file read bypasses supersession resolution and returns replaced records as if current.",
1332
- input: z14.object({
1674
+ input: z15.object({
1333
1675
  bundlePath,
1334
1676
  conceptId,
1335
- hops: z14.number().int().positive().optional().describe("How far from the root the walk may reach. Defaults to 2."),
1336
- maxNodes: z14.number().int().positive().optional().describe(
1677
+ hops: z15.number().int().positive().optional().describe("How far from the root the walk may reach. Defaults to 2."),
1678
+ maxNodes: z15.number().int().positive().optional().describe(
1337
1679
  "How many records the pack may hold, root included. Defaults to 20."
1338
1680
  ),
1339
- budgetTokens: z14.number().int().positive().optional().describe(
1681
+ budgetTokens: z15.number().int().positive().optional().describe(
1340
1682
  "Approximate token ceiling over what is actually emitted. Defaults to 25000."
1341
1683
  )
1342
1684
  }),
@@ -1358,10 +1700,10 @@ var packCommand = define({
1358
1700
  ...maxNodes !== void 0 ? { maxNodes } : {},
1359
1701
  ...budgetTokens !== void 0 ? { budgetTokens } : {}
1360
1702
  });
1361
- return render(result, path, now());
1703
+ return render2(result, path, now());
1362
1704
  }
1363
1705
  });
1364
- function render(result, bundle, at) {
1706
+ function render2(result, bundle, at) {
1365
1707
  const lines = [
1366
1708
  `# KB Pack \u2014 ${result.root}`,
1367
1709
  `bundle: ${bundle}`,
@@ -1423,22 +1765,22 @@ function warningLabel(warning) {
1423
1765
  }
1424
1766
 
1425
1767
  // src/commands/pin.ts
1426
- import { z as z15 } from "zod";
1768
+ import { z as z16 } from "zod";
1427
1769
  var pinCommand = define({
1428
1770
  name: "pin",
1429
1771
  tool: "kb_pin",
1430
1772
  usage: "pin [bundle-path] [--mode full|index] [--profiles a,b] [--local|--user] [--frozen|--unfreeze]",
1431
1773
  description: "Pin a base into a workspace pin manifest, so `context` surfaces it at every context birth. Three layers, nearest wins: the committed project manifest (.strauss/kb-pins.json, the default), `--local` (.strauss/kb-pins.local.json, personal and gitignored), and `--user` (~/.strauss/kb-pins.json, every workspace). Idempotent \u2014 re-pinning changes nothing unless --mode, --profiles, or --frozen/--unfreeze are given, which update just those fields. `--mode full` preloads the whole base into the block regardless of the full-under threshold; `--mode index` never upgrades. `--profiles` scopes the pin to named context profiles. `--frozen` marks the base concluded: write commands against it refuse and `context` labels it read-only. A path with no records yet succeeds with a warning; bases are routinely pinned before they are populated. Pins are workspace state: the pinned base itself is never touched.",
1432
- input: z15.object({
1774
+ input: z16.object({
1433
1775
  bundlePath,
1434
- mode: z15.enum(["full", "index"]).optional().describe(
1776
+ mode: z16.enum(["full", "index"]).optional().describe(
1435
1777
  "full: always emit this base's records whole (still under the block budget); index: never upgrade. Absent: the profile's full-under threshold decides."
1436
1778
  ),
1437
- profiles: z15.array(z15.string()).optional().describe("Context profiles this pin surfaces in. Absent: all of them."),
1438
- layer: z15.enum(["project", "local", "user"]).optional().describe(
1779
+ profiles: z16.array(z16.string()).optional().describe("Context profiles this pin surfaces in. Absent: all of them."),
1780
+ layer: z16.enum(["project", "local", "user"]).optional().describe(
1439
1781
  "Which manifest to write: project (committed, default), local (personal, gitignored), user (~/.strauss, every workspace)."
1440
1782
  ),
1441
- frozen: z15.boolean().optional().describe(
1783
+ frozen: z16.boolean().optional().describe(
1442
1784
  "true: the base is concluded \u2014 writes against it refuse while pinned. false: lift a freeze."
1443
1785
  )
1444
1786
  }),
@@ -1467,29 +1809,29 @@ var pinCommand = define({
1467
1809
  });
1468
1810
 
1469
1811
  // src/commands/pins.ts
1470
- import { z as z16 } from "zod";
1812
+ import { z as z17 } from "zod";
1471
1813
  var pinsCommand = define({
1472
1814
  name: "pins",
1473
1815
  tool: "kb_pins",
1474
1816
  usage: "pins",
1475
1817
  description: "Every pinned base across the manifest layers, each with its layer and whether it currently resolves to readable records. Reads the workspace manifests rather than any one base, like kb_context.",
1476
- input: z16.object({}),
1818
+ input: z17.object({}),
1477
1819
  fromArgv: () => ({}),
1478
1820
  run: ({ store }) => listPins(store, process.cwd())
1479
1821
  });
1480
1822
 
1481
1823
  // src/commands/query.ts
1482
- import { z as z17 } from "zod";
1824
+ import { z as z18 } from "zod";
1483
1825
  var queryCommand = define({
1484
1826
  name: "query",
1485
1827
  tool: "kb_query",
1486
1828
  usage: "query <text...>",
1487
1829
  description: "Search and return each match with its standing. Results are flagged, never filtered: a superseded record comes back alongside whatever replaced it, and a rejected one is marked as something explicitly not adopted. Prefer kb_load when the base fits its budget: on this package's measurements, a reader holding the whole base answered eight of nine questions whose wording appears in no record, where embedding search answered four. Never read record files directly \u2014 this tool (with kb_load and kb_trace) is the only supported way to read a base; a file read bypasses supersession resolution and returns replaced records as if current.",
1488
- input: z17.object({
1830
+ input: z18.object({
1489
1831
  bundlePath,
1490
- text: z17.string().optional(),
1491
- type: z17.enum(KB_RECORD_TYPES).optional(),
1492
- includeNonCurrent: z17.boolean().optional()
1832
+ text: z18.string().optional(),
1833
+ type: z18.enum(KB_RECORD_TYPES).optional(),
1834
+ includeNonCurrent: z18.boolean().optional()
1493
1835
  }),
1494
1836
  fromArgv: (argv, path) => ({
1495
1837
  bundlePath: path,
@@ -1511,40 +1853,40 @@ var queryCommand = define({
1511
1853
  });
1512
1854
 
1513
1855
  // src/commands/read-index.ts
1514
- import { z as z18 } from "zod";
1856
+ import { z as z19 } from "zod";
1515
1857
  var readIndexCommand = define({
1516
1858
  name: "index",
1517
1859
  tool: "kb_index",
1518
1860
  usage: "index",
1519
1861
  description: "The index, rebuilt if it disagrees with the records. One call gives the whole shape of the base: title, type, status, and description per record. The cheap re-orientation call after compaction or deep in a long session \u2014 a few hundred tokens; call it (or kb_context, when bases are pinned) first, then kb_load or fetch by concept id.",
1520
- input: z18.object({ bundlePath }),
1862
+ input: z19.object({ bundlePath }),
1521
1863
  fromArgv: (_argv, path) => ({ bundlePath: path }),
1522
1864
  run: ({ store }, { bundlePath: path }) => store.readIndex(path)
1523
1865
  });
1524
1866
 
1525
1867
  // src/commands/schema.ts
1526
- import { z as z19 } from "zod";
1868
+ import { z as z20 } from "zod";
1527
1869
  var schemaCommand = define({
1528
1870
  name: "schema",
1529
1871
  tool: "kb_schema",
1530
1872
  usage: "schema",
1531
1873
  description: "JSON Schema for the frontmatter, the write input, and log entries \u2014 generated from the code that enforces them, so it cannot drift from what a write will accept.",
1532
- input: z19.object({}),
1874
+ input: z20.object({}),
1533
1875
  fromArgv: () => ({}),
1534
1876
  run: () => Promise.resolve(kbJsonSchemas())
1535
1877
  });
1536
1878
 
1537
1879
  // src/commands/status.ts
1538
- import { z as z20 } from "zod";
1880
+ import { z as z21 } from "zod";
1539
1881
  var statusCommand = define({
1540
1882
  name: "status",
1541
1883
  tool: "kb_status",
1542
1884
  usage: "status <concept-id> <status>",
1543
1885
  description: "Move a record's status, leaving everything else alone. Uses a compare-and-swap, so a concurrent change fails loudly rather than being overwritten.",
1544
- input: z20.object({
1886
+ input: z21.object({
1545
1887
  bundlePath,
1546
1888
  conceptId,
1547
- status: z20.enum(KB_RECORD_STATUSES)
1889
+ status: z21.enum(KB_RECORD_STATUSES)
1548
1890
  }),
1549
1891
  fromArgv: (argv, path) => ({
1550
1892
  bundlePath: path,
@@ -1559,13 +1901,13 @@ var statusCommand = define({
1559
1901
  });
1560
1902
 
1561
1903
  // src/commands/supersede.ts
1562
- import { z as z21 } from "zod";
1904
+ import { z as z22 } from "zod";
1563
1905
  var supersedeCommand = define({
1564
1906
  name: "supersede",
1565
1907
  tool: "kb_supersede",
1566
1908
  usage: "supersede <concept-id> <replacement-id>",
1567
1909
  description: "Mark a record superseded by another, linking both directions. Use this rather than editing a record whose meaning changed \u2014 a record that quietly becomes something else invalidates every reference to it, and the earlier understanding is what a later trace needs.",
1568
- input: z21.object({ bundlePath, conceptId, replacementId: conceptId }),
1910
+ input: z22.object({ bundlePath, conceptId, replacementId: conceptId }),
1569
1911
  fromArgv: (argv, path) => ({
1570
1912
  bundlePath: path,
1571
1913
  conceptId: argv[1],
@@ -1579,16 +1921,16 @@ var supersedeCommand = define({
1579
1921
  });
1580
1922
 
1581
1923
  // src/commands/sync-instructions.ts
1582
- import { z as z22 } from "zod";
1924
+ import { z as z23 } from "zod";
1583
1925
  var syncInstructionsCommand = define({
1584
1926
  name: "sync-instructions",
1585
1927
  usage: "sync-instructions <file> [--profile NAME] [--budget N] [--full-under N]",
1586
1928
  description: "Idempotently plant the `context` block between sentinel comments in an instruction file (AGENTS.md, CLAUDE.md), creating the block when absent and leaving everything outside the sentinels alone. CLI-only: this is file plumbing for runtimes whose instruction files are re-read where their conversations are not, not an agent capability \u2014 the capability is kb_context.",
1587
- input: z22.object({
1588
- file: z22.string().min(1).describe("The instruction file to edit in place."),
1589
- budgetTokens: z22.number().int().positive().optional(),
1590
- fullUnderTokens: z22.number().int().positive().optional(),
1591
- profile: z22.string().optional()
1929
+ input: z23.object({
1930
+ file: z23.string().min(1).describe("The instruction file to edit in place."),
1931
+ budgetTokens: z23.number().int().positive().optional(),
1932
+ fullUnderTokens: z23.number().int().positive().optional(),
1933
+ profile: z23.string().optional()
1592
1934
  }),
1593
1935
  fromArgv: (argv) => {
1594
1936
  const budget = argvFlag(argv, "--budget");
@@ -1614,17 +1956,17 @@ var syncInstructionsCommand = define({
1614
1956
  });
1615
1957
 
1616
1958
  // src/commands/trace.ts
1617
- import { z as z23 } from "zod";
1959
+ import { z as z24 } from "zod";
1618
1960
  var traceCommand = define({
1619
1961
  name: "trace",
1620
1962
  tool: "kb_trace",
1621
1963
  usage: "trace <concept-id> [edges...]",
1622
1964
  description: 'How a position was arrived at, as a timeline ordered by when each record was written. Deliberately includes rejected, draft, and superseded records \u2014 in a history those are the content, not noise. Follows supersession, shared code anchors, and shared sources. Use when the question is "why is this the way it is" rather than "what do we hold now". This tool (with kb_load and kb_query) is the only supported way to read a base; a raw file read bypasses supersession resolution and returns replaced records as if current.',
1623
- input: z23.object({
1965
+ input: z24.object({
1624
1966
  bundlePath,
1625
1967
  conceptId,
1626
- edges: z23.array(z23.enum(TRACE_EDGES)).optional(),
1627
- depth: z23.number().int().positive().optional()
1968
+ edges: z24.array(z24.enum(TRACE_EDGES)).optional(),
1969
+ depth: z24.number().int().positive().optional()
1628
1970
  }),
1629
1971
  fromArgv: (argv, path) => ({
1630
1972
  bundlePath: path,
@@ -1646,53 +1988,53 @@ var traceCommand = define({
1646
1988
  });
1647
1989
 
1648
1990
  // src/commands/types.ts
1649
- import { z as z24 } from "zod";
1991
+ import { z as z25 } from "zod";
1650
1992
  var typesCommand = define({
1651
1993
  name: "types",
1652
1994
  tool: "kb_types",
1653
1995
  usage: "types",
1654
1996
  description: "The twelve record types with their purpose, body sections, and starting status. Read this before writing rather than guessing headings \u2014 a section the type does not define is rejected.",
1655
- input: z24.object({}),
1997
+ input: z25.object({}),
1656
1998
  fromArgv: () => ({}),
1657
1999
  run: () => Promise.resolve(RECORD_TYPES)
1658
2000
  });
1659
2001
 
1660
2002
  // src/commands/unpin.ts
1661
- import { z as z25 } from "zod";
2003
+ import { z as z26 } from "zod";
1662
2004
  var unpinCommand = define({
1663
2005
  name: "unpin",
1664
2006
  tool: "kb_unpin",
1665
2007
  usage: "unpin [bundle-path]",
1666
2008
  description: "Remove a base from every pin manifest layer that holds it \u2014 project, local, and user \u2014 because unpinned means gone, not still injected from another file. Reports which layers were touched.",
1667
- input: z25.object({ bundlePath }),
2009
+ input: z26.object({ bundlePath }),
1668
2010
  fromArgv: (argv, path) => ({ bundlePath: argv[1] ?? path }),
1669
2011
  run: (_ctx, { bundlePath: path }) => unpinBase(process.cwd(), path)
1670
2012
  });
1671
2013
 
1672
2014
  // src/commands/validate.ts
1673
- import { z as z26 } from "zod";
2015
+ import { z as z27 } from "zod";
1674
2016
  var validateCommand = define({
1675
2017
  name: "validate",
1676
2018
  tool: "kb_validate",
1677
2019
  usage: "validate",
1678
2020
  description: "Check pointers no single record can see: supersession links that disagree between the two records, and assumptions that cite sources. Per-record shape is enforced on every read, so a problem here means someone edited a file by hand.",
1679
- input: z26.object({ bundlePath }),
2021
+ input: z27.object({ bundlePath }),
1680
2022
  fromArgv: (_argv, path) => ({ bundlePath: path }),
1681
2023
  run: async ({ store }, { bundlePath: path }) => validateBundle(await store.list(path)),
1682
2024
  failsWhen: (result) => Array.isArray(result) && result.length > 0
1683
2025
  });
1684
2026
 
1685
2027
  // src/commands/verify.ts
1686
- import { z as z27 } from "zod";
2028
+ import { z as z28 } from "zod";
1687
2029
  var verifyCommand = define({
1688
2030
  name: "verify",
1689
2031
  tool: "kb_verify",
1690
2032
  usage: "verify <concept-id> --note <text>",
1691
2033
  description: "Append one verified[] event \u2014 who checked the record, when, and what the check found. Appends only; prior events are never rewritten. A record's own generator is refused unless the actor is human: re-reading your own output is not an independent check.",
1692
- input: z27.object({
2034
+ input: z28.object({
1693
2035
  bundlePath,
1694
2036
  conceptId,
1695
- note: z27.string().refine((s) => s.trim().length > 0, {
2037
+ note: z28.string().refine((s) => s.trim().length > 0, {
1696
2038
  message: "note must say what the check found"
1697
2039
  })
1698
2040
  }),
@@ -1712,7 +2054,7 @@ var verifyCommand = define({
1712
2054
  });
1713
2055
 
1714
2056
  // src/commands/write.ts
1715
- import { z as z28 } from "zod";
2057
+ import { z as z29 } from "zod";
1716
2058
  var writeCommand = define({
1717
2059
  name: "write",
1718
2060
  tool: "kb_write",
@@ -1726,9 +2068,9 @@ var writeCommand = define({
1726
2068
  "- Prefer a new record over overloading an existing one, and keep each short. A record nobody finishes reading is not durable memory.",
1727
2069
  "- Records are never deleted; supersede instead, so the earlier reasoning stays inspectable."
1728
2070
  ].join("\n"),
1729
- input: z28.object({
2071
+ input: z29.object({
1730
2072
  bundlePath,
1731
- type: z28.enum(KB_RECORD_TYPES),
2073
+ type: z29.enum(KB_RECORD_TYPES),
1732
2074
  input: composeInputSchema
1733
2075
  }),
1734
2076
  fromArgv: async (argv, path, stdin) => ({
@@ -1752,7 +2094,7 @@ var writeCommand = define({
1752
2094
  });
1753
2095
 
1754
2096
  // src/commands/write-decision.ts
1755
- import { z as z29 } from "zod";
2097
+ import { z as z30 } from "zod";
1756
2098
  var writeDecisionCommand = define({
1757
2099
  name: "write-decision",
1758
2100
  tool: "kb_write_decision",
@@ -1765,7 +2107,7 @@ var writeDecisionCommand = define({
1765
2107
  "- `alternative` is what you turned down and why, not a list of everything considered.",
1766
2108
  "- A reference to material you read goes in `sources`; a reference to code goes in `anchors`; a reference to another record goes in `relatedConceptIds`."
1767
2109
  ].join("\n"),
1768
- input: z29.object({ bundlePath, input: decisionInputSchema }),
2110
+ input: z30.object({ bundlePath, input: decisionInputSchema }),
1769
2111
  fromArgv: async (_argv, path, stdin) => ({
1770
2112
  bundlePath: path,
1771
2113
  input: JSON.parse(await stdin())
@@ -1802,6 +2144,7 @@ var KB_COMMANDS = [
1802
2144
  readIndexCommand,
1803
2145
  logCommand,
1804
2146
  validateCommand,
2147
+ doctorCommand,
1805
2148
  schemaCommand,
1806
2149
  pinCommand,
1807
2150
  unpinCommand,
@@ -2656,7 +2999,7 @@ function typeRank(record) {
2656
2999
  }
2657
3000
 
2658
3001
  // src/version.ts
2659
- var VERSION = true ? "0.1.7" : "0.0.0-dev";
3002
+ var VERSION = true ? "0.1.8" : "0.0.0-dev";
2660
3003
 
2661
3004
  export {
2662
3005
  kbSourceSchema,
@@ -2708,17 +3051,22 @@ export {
2708
3051
  CONTEXT_BEGIN,
2709
3052
  CONTEXT_END,
2710
3053
  syncInstructions,
3054
+ KB_EDGE_KINDS,
3055
+ neighbours,
3056
+ edgeNeighbours,
3057
+ validateBundle,
3058
+ DEFAULT_EXPIRING_DAYS,
3059
+ DEFAULT_UNVERIFIED_DAYS,
3060
+ DEFAULT_AGING_DAYS,
3061
+ KB_DOCTOR_CHECKS,
3062
+ doctor,
2711
3063
  LOG_FILE,
2712
3064
  kbLogEntrySchema,
2713
3065
  renderLogEntry,
2714
3066
  parseLog,
2715
3067
  kbJsonSchemas,
2716
- KB_EDGE_KINDS,
2717
- neighbours,
2718
- edgeNeighbours,
2719
3068
  TRACE_EDGES,
2720
3069
  trace,
2721
- validateBundle,
2722
3070
  KB_COMMANDS,
2723
3071
  KB_COMMANDS_BY_NAME,
2724
3072
  stringifyMarkdownWithFrontmatter,
@@ -2745,4 +3093,4 @@ export {
2745
3093
  KbStore,
2746
3094
  VERSION
2747
3095
  };
2748
- //# sourceMappingURL=chunk-GKUQOJEK.js.map
3096
+ //# sourceMappingURL=chunk-YJK7KGHN.js.map