@saasontools/strauss-kb 0.1.9 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +71 -39
- package/dist/{chunk-OFDWRMY6.js → chunk-EJQPZWN5.js} +397 -233
- package/dist/chunk-EJQPZWN5.js.map +1 -0
- package/dist/{chunk-MWWDD23L.js → chunk-NMTP7V7E.js} +2 -2
- package/dist/{chunk-KVEEISYQ.js → chunk-RGK3K6LN.js} +2 -2
- package/dist/cli-main.cjs +386 -222
- package/dist/cli-main.cjs.map +1 -1
- package/dist/cli-main.js +2 -2
- package/dist/index.cjs +255 -88
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +104 -4
- package/dist/index.d.ts +104 -4
- package/dist/index.js +9 -3
- package/dist/index.js.map +1 -1
- package/dist/mcp-main.cjs +386 -222
- package/dist/mcp-main.cjs.map +1 -1
- package/dist/mcp-main.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-OFDWRMY6.js.map +0 -1
- /package/dist/{chunk-MWWDD23L.js.map → chunk-NMTP7V7E.js.map} +0 -0
- /package/dist/{chunk-KVEEISYQ.js.map → chunk-RGK3K6LN.js.map} +0 -0
package/dist/cli-main.cjs
CHANGED
|
@@ -619,14 +619,162 @@ async function unpinBase(workspaceDir, bundlePath2) {
|
|
|
619
619
|
|
|
620
620
|
// src/commands/model.ts
|
|
621
621
|
var import_zod5 = require("zod");
|
|
622
|
+
|
|
623
|
+
// src/errors.ts
|
|
624
|
+
var BaseError = class extends Error {
|
|
625
|
+
code;
|
|
626
|
+
errorType;
|
|
627
|
+
fault;
|
|
628
|
+
retriable;
|
|
629
|
+
reportToUser;
|
|
630
|
+
details;
|
|
631
|
+
constructor(props) {
|
|
632
|
+
super(props.message);
|
|
633
|
+
this.name = props.name ?? this.constructor.name;
|
|
634
|
+
this.code = props.code ?? 500;
|
|
635
|
+
this.errorType = props.errorType;
|
|
636
|
+
this.fault = props.fault;
|
|
637
|
+
this.retriable = props.retriable ?? true;
|
|
638
|
+
this.reportToUser = props.reportToUser ?? false;
|
|
639
|
+
this.details = props.details;
|
|
640
|
+
}
|
|
641
|
+
};
|
|
642
|
+
|
|
643
|
+
// src/kb-errors.ts
|
|
644
|
+
var KbRecordAlreadyExistsError = class extends BaseError {
|
|
645
|
+
constructor(conceptId2) {
|
|
646
|
+
super({
|
|
647
|
+
message: `kb: ${conceptId2} already exists \u2014 choose a more specific slug, or write with overwrite`,
|
|
648
|
+
errorType: "KbRecordAlreadyExists" /* KbRecordAlreadyExists */,
|
|
649
|
+
code: 409,
|
|
650
|
+
fault: "User" /* User */,
|
|
651
|
+
retriable: false,
|
|
652
|
+
reportToUser: true,
|
|
653
|
+
details: { conceptId: conceptId2, action: "refused" }
|
|
654
|
+
});
|
|
655
|
+
this.conceptId = conceptId2;
|
|
656
|
+
}
|
|
657
|
+
conceptId;
|
|
658
|
+
};
|
|
659
|
+
var KbRecordNotFoundError = class extends BaseError {
|
|
660
|
+
constructor(conceptId2) {
|
|
661
|
+
super({
|
|
662
|
+
message: `kb: ${conceptId2} does not exist`,
|
|
663
|
+
errorType: "KbRecordNotFound" /* KbRecordNotFound */,
|
|
664
|
+
code: 404,
|
|
665
|
+
fault: "User" /* User */,
|
|
666
|
+
retriable: false,
|
|
667
|
+
reportToUser: true,
|
|
668
|
+
details: { conceptId: conceptId2 }
|
|
669
|
+
});
|
|
670
|
+
this.conceptId = conceptId2;
|
|
671
|
+
}
|
|
672
|
+
conceptId;
|
|
673
|
+
};
|
|
674
|
+
var KbWriteConflictError = class extends BaseError {
|
|
675
|
+
constructor(conceptId2) {
|
|
676
|
+
super({
|
|
677
|
+
message: `kb: ${conceptId2} changed while it was being updated \u2014 re-read and retry`,
|
|
678
|
+
errorType: "KbWriteConflict" /* KbWriteConflict */,
|
|
679
|
+
code: 409,
|
|
680
|
+
fault: "System" /* System */,
|
|
681
|
+
retriable: true,
|
|
682
|
+
reportToUser: true,
|
|
683
|
+
details: { conceptId: conceptId2 }
|
|
684
|
+
});
|
|
685
|
+
this.conceptId = conceptId2;
|
|
686
|
+
}
|
|
687
|
+
conceptId;
|
|
688
|
+
};
|
|
689
|
+
var KbSelfVerificationError = class extends BaseError {
|
|
690
|
+
constructor(conceptId2, actor, generatedBy) {
|
|
691
|
+
super({
|
|
692
|
+
message: `kb: ${conceptId2} was generated by ${generatedBy}, and a record's generator cannot verify it \u2014 only a human or a different actor can`,
|
|
693
|
+
errorType: "KbSelfVerification" /* KbSelfVerification */,
|
|
694
|
+
code: 400,
|
|
695
|
+
fault: "User" /* User */,
|
|
696
|
+
retriable: false,
|
|
697
|
+
reportToUser: true,
|
|
698
|
+
details: { conceptId: conceptId2, actor, generatedBy, action: "refused" }
|
|
699
|
+
});
|
|
700
|
+
this.conceptId = conceptId2;
|
|
701
|
+
this.actor = actor;
|
|
702
|
+
this.generatedBy = generatedBy;
|
|
703
|
+
}
|
|
704
|
+
conceptId;
|
|
705
|
+
actor;
|
|
706
|
+
generatedBy;
|
|
707
|
+
};
|
|
708
|
+
var KbPackBudgetExceededError = class extends BaseError {
|
|
709
|
+
constructor(recordCount, approxTokens2, budgetTokens, excluded) {
|
|
710
|
+
super({
|
|
711
|
+
message: `kb: a pack of ${recordCount} records is ~${approxTokens2} tokens against a budget of ${budgetTokens} \u2014 lower hops or maxNodes, or raise the budget`,
|
|
712
|
+
errorType: "KbPackBudgetExceeded" /* KbPackBudgetExceeded */,
|
|
713
|
+
code: 400,
|
|
714
|
+
fault: "User" /* User */,
|
|
715
|
+
retriable: false,
|
|
716
|
+
reportToUser: true,
|
|
717
|
+
details: { recordCount, approxTokens: approxTokens2, budgetTokens, excluded }
|
|
718
|
+
});
|
|
719
|
+
this.recordCount = recordCount;
|
|
720
|
+
this.approxTokens = approxTokens2;
|
|
721
|
+
this.budgetTokens = budgetTokens;
|
|
722
|
+
this.excluded = excluded;
|
|
723
|
+
}
|
|
724
|
+
recordCount;
|
|
725
|
+
approxTokens;
|
|
726
|
+
budgetTokens;
|
|
727
|
+
excluded;
|
|
728
|
+
};
|
|
729
|
+
var KbMissingFlagValueError = class extends BaseError {
|
|
730
|
+
constructor(flag) {
|
|
731
|
+
super({
|
|
732
|
+
message: `kb: ${flag} needs a value \u2014 pass ${flag} <value> or ${flag}=<value>`,
|
|
733
|
+
errorType: "KbMissingFlagValue" /* KbMissingFlagValue */,
|
|
734
|
+
code: 400,
|
|
735
|
+
fault: "User" /* User */,
|
|
736
|
+
retriable: false,
|
|
737
|
+
reportToUser: true,
|
|
738
|
+
details: { flag }
|
|
739
|
+
});
|
|
740
|
+
this.flag = flag;
|
|
741
|
+
}
|
|
742
|
+
flag;
|
|
743
|
+
};
|
|
744
|
+
var KbInvalidConceptIdError = class extends BaseError {
|
|
745
|
+
constructor(message, details) {
|
|
746
|
+
super({
|
|
747
|
+
message: `kb: ${message}`,
|
|
748
|
+
errorType: "KbInvalidConceptId" /* KbInvalidConceptId */,
|
|
749
|
+
code: 400,
|
|
750
|
+
fault: "User" /* User */,
|
|
751
|
+
retriable: false,
|
|
752
|
+
reportToUser: true,
|
|
753
|
+
details
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
};
|
|
757
|
+
|
|
758
|
+
// src/commands/model.ts
|
|
622
759
|
var bundlePath = import_zod5.z.string().min(1).describe("Absolute path to the knowledge base directory.");
|
|
623
760
|
var conceptId = import_zod5.z.string().min(1).describe("e.g. decision.cursor-v2");
|
|
624
761
|
function define(command) {
|
|
625
762
|
return command;
|
|
626
763
|
}
|
|
627
764
|
function argvFlag(argv, name) {
|
|
765
|
+
const joined = argv.find((arg) => arg.startsWith(`${name}=`));
|
|
766
|
+
if (joined !== void 0) {
|
|
767
|
+
const value2 = joined.slice(name.length + 1);
|
|
768
|
+
if (!value2) throw new KbMissingFlagValueError(name);
|
|
769
|
+
return value2;
|
|
770
|
+
}
|
|
628
771
|
const at = argv.indexOf(name);
|
|
629
|
-
|
|
772
|
+
if (at === -1) return void 0;
|
|
773
|
+
const value = argv[at + 1];
|
|
774
|
+
if (value === void 0 || value.startsWith("--")) {
|
|
775
|
+
throw new KbMissingFlagValueError(name);
|
|
776
|
+
}
|
|
777
|
+
return value;
|
|
630
778
|
}
|
|
631
779
|
|
|
632
780
|
// src/commands/answer.ts
|
|
@@ -648,12 +796,9 @@ var answerCommand = define({
|
|
|
648
796
|
}
|
|
649
797
|
});
|
|
650
798
|
|
|
651
|
-
// src/commands/
|
|
799
|
+
// src/commands/catalog.ts
|
|
652
800
|
var import_zod7 = require("zod");
|
|
653
801
|
|
|
654
|
-
// src/kb-context.ts
|
|
655
|
-
var import_promises2 = require("fs/promises");
|
|
656
|
-
|
|
657
802
|
// src/adjudicate.ts
|
|
658
803
|
var STANDING = {
|
|
659
804
|
accepted: "current",
|
|
@@ -746,6 +891,120 @@ function successors(record, byId) {
|
|
|
746
891
|
return { records, missing };
|
|
747
892
|
}
|
|
748
893
|
|
|
894
|
+
// src/catalog.ts
|
|
895
|
+
var EMPTY_STANDINGS = {
|
|
896
|
+
current: 0,
|
|
897
|
+
superseded: 0,
|
|
898
|
+
rejected: 0,
|
|
899
|
+
unsettled: 0,
|
|
900
|
+
open: 0
|
|
901
|
+
};
|
|
902
|
+
function catalog(bundle, options = {}) {
|
|
903
|
+
const wanted = options.type ? bundle.filter((record) => record.frontmatter.type === options.type) : bundle;
|
|
904
|
+
const entries = adjudicate(wanted, bundle, options.now ?? /* @__PURE__ */ new Date()).map((hit) => ({
|
|
905
|
+
conceptId: hit.record.conceptId,
|
|
906
|
+
type: hit.record.frontmatter.type,
|
|
907
|
+
title: hit.record.frontmatter.title ?? null,
|
|
908
|
+
standing: hit.standing,
|
|
909
|
+
supersededBy: hit.heads.map((head) => head.conceptId),
|
|
910
|
+
stale: hit.warnings.some((warning) => warning.kind === "stale")
|
|
911
|
+
})).sort(byTypeThenTitle);
|
|
912
|
+
const standings = { ...EMPTY_STANDINGS };
|
|
913
|
+
for (const entry of entries) standings[entry.standing] += 1;
|
|
914
|
+
return {
|
|
915
|
+
entries,
|
|
916
|
+
recordCount: entries.length,
|
|
917
|
+
standings,
|
|
918
|
+
currentCount: standings.current,
|
|
919
|
+
supersededCount: standings.superseded,
|
|
920
|
+
staleCount: entries.filter((entry) => entry.stale).length
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
function byTypeThenTitle(left, right) {
|
|
924
|
+
return byCodeUnit(left.type, right.type) || byCodeUnit(left.title ?? "", right.title ?? "") || byCodeUnit(left.conceptId, right.conceptId);
|
|
925
|
+
}
|
|
926
|
+
function byCodeUnit(left, right) {
|
|
927
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
928
|
+
}
|
|
929
|
+
function renderCatalogLine(entry) {
|
|
930
|
+
const parts = [
|
|
931
|
+
entry.conceptId,
|
|
932
|
+
entry.type,
|
|
933
|
+
entry.title ?? "(untitled)",
|
|
934
|
+
entry.standing === "superseded" ? `superseded \u2192 ${entry.supersededBy.join(", ") || "(no surviving head)"}` : entry.standing
|
|
935
|
+
];
|
|
936
|
+
if (entry.stale) parts.push("stale");
|
|
937
|
+
return `- ${parts.join(" \xB7 ")}`;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
// src/commands/catalog.ts
|
|
941
|
+
var catalogCommand = define({
|
|
942
|
+
name: "catalog",
|
|
943
|
+
tool: "kb_catalog",
|
|
944
|
+
usage: "catalog [type]",
|
|
945
|
+
description: "Lists every record as one line \u2014 concept id, type, title, standing, and a stale flag \u2014 at roughly thirty tokens each. Pick this over kb_load once kb_load refuses: kb_catalog never refuses. Superseded records show only their replacement; fetch bodies with kb_load, kb_pack, kb_query, or kb_trace.",
|
|
946
|
+
input: import_zod7.z.object({
|
|
947
|
+
bundlePath,
|
|
948
|
+
type: import_zod7.z.enum(KB_RECORD_TYPES).optional()
|
|
949
|
+
}),
|
|
950
|
+
fromArgv: (argv, path) => ({
|
|
951
|
+
bundlePath: path,
|
|
952
|
+
...argv[1] && !argv[1].startsWith("--") ? { type: argv[1] } : {}
|
|
953
|
+
}),
|
|
954
|
+
run: async ({ store }, { bundlePath: path, type }) => render(
|
|
955
|
+
await store.catalog(path, { ...type ? { type } : {} }),
|
|
956
|
+
path,
|
|
957
|
+
type
|
|
958
|
+
)
|
|
959
|
+
});
|
|
960
|
+
function render(result, bundle, type) {
|
|
961
|
+
const lines = [
|
|
962
|
+
`# KB Catalog${type ? ` \u2014 ${type}` : ""}`,
|
|
963
|
+
`bundle: ${bundle}`,
|
|
964
|
+
`${count(result.recordCount, "record")}: ${standingCounts(result)}`
|
|
965
|
+
];
|
|
966
|
+
if (result.staleCount) {
|
|
967
|
+
lines.push(
|
|
968
|
+
`${result.staleCount} stale \u2014 a flag over the standings above, not one of them`
|
|
969
|
+
);
|
|
970
|
+
}
|
|
971
|
+
lines.push("");
|
|
972
|
+
if (!result.entries.length) {
|
|
973
|
+
lines.push(
|
|
974
|
+
type ? `(no records of type ${type})` : "(no records \u2014 this base is empty)"
|
|
975
|
+
);
|
|
976
|
+
} else {
|
|
977
|
+
for (const entry of result.entries) lines.push(renderCatalogLine(entry));
|
|
978
|
+
}
|
|
979
|
+
lines.push(
|
|
980
|
+
"",
|
|
981
|
+
"Bodies are not here: kb_pack <conceptId> for the neighbourhood around one record, kb_load for the whole base when it fits the budget, kb_query for a lookup by wording, kb_trace <conceptId> for how a position was arrived at."
|
|
982
|
+
);
|
|
983
|
+
return lines.join("\n");
|
|
984
|
+
}
|
|
985
|
+
function standingCounts(result) {
|
|
986
|
+
const ORDER = [
|
|
987
|
+
"current",
|
|
988
|
+
"open",
|
|
989
|
+
"unsettled",
|
|
990
|
+
"rejected",
|
|
991
|
+
"superseded"
|
|
992
|
+
];
|
|
993
|
+
const parts = ORDER.filter((standing) => result.standings[standing]).map(
|
|
994
|
+
(standing) => `${result.standings[standing]} ${standing}`
|
|
995
|
+
);
|
|
996
|
+
return parts.length ? parts.join(" \xB7 ") : "none";
|
|
997
|
+
}
|
|
998
|
+
function count(value, noun) {
|
|
999
|
+
return `${value} ${value === 1 ? noun : `${noun}s`}`;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
// src/commands/context.ts
|
|
1003
|
+
var import_zod8 = require("zod");
|
|
1004
|
+
|
|
1005
|
+
// src/kb-context.ts
|
|
1006
|
+
var import_promises2 = require("fs/promises");
|
|
1007
|
+
|
|
749
1008
|
// src/kb-index.ts
|
|
750
1009
|
var INDEX_FILE = "INDEX.md";
|
|
751
1010
|
var HEADING = "# KB Index";
|
|
@@ -1007,20 +1266,20 @@ var contextCommand = define({
|
|
|
1007
1266
|
tool: "kb_context",
|
|
1008
1267
|
usage: "context [--profile NAME] [--budget N] [--full-under N] [--format json] [--event NAME]",
|
|
1009
1268
|
description: "The pinned-base index block, for injection at every context birth \u2014 startup, clear, resume, and after compaction. An index, not the content: concept ids, titles and standing, with the bodies left behind kb_load at the point of use. Emits nothing when nothing is pinned. Refuses with the list of bases and their sizes rather than truncating past its budget. Budgets resolve most-specific-first: explicit flags, then the workspace manifests' `context` tables (per profile, over their `default`), then the built-in profile (session-start, compact, turn), then package defaults \u2014 so a repo tunes its own numbers in .strauss/kb-pins.json without touching hook commands. Like kb_schema and kb_types this takes no bundlePath \u2014 it reads the workspace pin manifests, because which bases a session should see is workspace state, not a property of one base.",
|
|
1010
|
-
input:
|
|
1011
|
-
budgetTokens:
|
|
1269
|
+
input: import_zod8.z.object({
|
|
1270
|
+
budgetTokens: import_zod8.z.number().int().positive().optional().describe(
|
|
1012
1271
|
"Ceiling on the whole emitted block; past it the command refuses with a list of bases rather than truncating. Defaults to 4000."
|
|
1013
1272
|
),
|
|
1014
|
-
fullUnderTokens:
|
|
1273
|
+
fullUnderTokens: import_zod8.z.number().int().positive().optional().describe(
|
|
1015
1274
|
"Per-base rendering threshold, applied before the budget: a base whose complete load fits under this arrives as full records instead of index lines, and the whole block still answers to budgetTokens. Off by default \u2014 index-only is the safe default at a context birth, because injected bodies outlive the qualifiers on them; the session-start profile opts tiny bases in at 1500."
|
|
1016
1275
|
),
|
|
1017
|
-
profile:
|
|
1276
|
+
profile: import_zod8.z.string().optional().describe(
|
|
1018
1277
|
"Named budget set: built-ins are session-start (full-under 1500), compact and turn (budget 2500); the manifests' `context` tables override per repo. Unknown names fall through to defaults rather than failing."
|
|
1019
1278
|
),
|
|
1020
|
-
format:
|
|
1279
|
+
format: import_zod8.z.enum(["markdown", "json"]).optional().describe(
|
|
1021
1280
|
"CLI envelope for hook protocols that require strict JSON on stdout. MCP callers omit this \u2014 the block itself is identical."
|
|
1022
1281
|
),
|
|
1023
|
-
event:
|
|
1282
|
+
event: import_zod8.z.string().optional().describe(
|
|
1024
1283
|
"hookEventName stamped into the JSON envelope. Only meaningful with format=json."
|
|
1025
1284
|
)
|
|
1026
1285
|
}),
|
|
@@ -1056,7 +1315,7 @@ var contextCommand = define({
|
|
|
1056
1315
|
});
|
|
1057
1316
|
|
|
1058
1317
|
// src/commands/doctor.ts
|
|
1059
|
-
var
|
|
1318
|
+
var import_zod9 = require("zod");
|
|
1060
1319
|
|
|
1061
1320
|
// src/kb-edges.ts
|
|
1062
1321
|
var KB_EDGE_KINDS = [
|
|
@@ -1413,13 +1672,13 @@ function ageInDays(record, now) {
|
|
|
1413
1672
|
}
|
|
1414
1673
|
|
|
1415
1674
|
// src/commands/doctor.ts
|
|
1416
|
-
var days = (what, fallback) =>
|
|
1675
|
+
var days = (what, fallback) => import_zod9.z.number().int().positive().optional().describe(`${what} Defaults to ${fallback}.`);
|
|
1417
1676
|
var doctorCommand = define({
|
|
1418
1677
|
name: "doctor",
|
|
1419
1678
|
tool: "kb_doctor",
|
|
1420
1679
|
usage: "doctor [--expiring-days N] [--unverified-days N] [--aging-days N] [--strict]",
|
|
1421
1680
|
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.",
|
|
1422
|
-
input:
|
|
1681
|
+
input: import_zod9.z.object({
|
|
1423
1682
|
bundlePath,
|
|
1424
1683
|
expiringDays: days(
|
|
1425
1684
|
"How far ahead `expiring` looks, in days.",
|
|
@@ -1433,7 +1692,7 @@ var doctorCommand = define({
|
|
|
1433
1692
|
"How long a record may stay `open` or `proposed` before `aging` reports it, in days.",
|
|
1434
1693
|
DEFAULT_AGING_DAYS
|
|
1435
1694
|
),
|
|
1436
|
-
strict:
|
|
1695
|
+
strict: import_zod9.z.boolean().optional().describe(
|
|
1437
1696
|
"Turn an expired record into a non-zero exit for the CLI. No effect on the report itself."
|
|
1438
1697
|
)
|
|
1439
1698
|
}),
|
|
@@ -1463,14 +1722,14 @@ var doctorCommand = define({
|
|
|
1463
1722
|
});
|
|
1464
1723
|
return { bundlePath: path, checkedAt, ...report };
|
|
1465
1724
|
},
|
|
1466
|
-
render: (result) =>
|
|
1725
|
+
render: (result) => render2(result),
|
|
1467
1726
|
// Only expiry, and only under --strict. The other six checks report debt a
|
|
1468
1727
|
// reader decides about; an expired record is the base asserting something it
|
|
1469
1728
|
// already said it would stop standing behind, which is the one finding a
|
|
1470
1729
|
// pipeline can act on without a judgment call.
|
|
1471
1730
|
failsWhen: (result, input) => input.strict === true && result.counts.expired > 0
|
|
1472
1731
|
});
|
|
1473
|
-
function
|
|
1732
|
+
function render2(result) {
|
|
1474
1733
|
const { thresholds } = result;
|
|
1475
1734
|
const lines = [
|
|
1476
1735
|
`# KB Doctor \u2014 ${result.bundlePath}`,
|
|
@@ -1502,13 +1761,13 @@ function render(result) {
|
|
|
1502
1761
|
}
|
|
1503
1762
|
|
|
1504
1763
|
// src/commands/list.ts
|
|
1505
|
-
var
|
|
1764
|
+
var import_zod10 = require("zod");
|
|
1506
1765
|
var listCommand = define({
|
|
1507
1766
|
name: "list",
|
|
1508
1767
|
tool: "kb_list",
|
|
1509
1768
|
usage: "list [type]",
|
|
1510
1769
|
description: "Every record, optionally narrowed to one type. Use kb_query when you have a question; this is for enumerating.",
|
|
1511
|
-
input:
|
|
1770
|
+
input: import_zod10.z.object({ bundlePath, type: import_zod10.z.enum(KB_RECORD_TYPES).optional() }),
|
|
1512
1771
|
fromArgv: (argv, path) => ({ bundlePath: path, type: argv[1] }),
|
|
1513
1772
|
run: async ({ store }, { bundlePath: path, type }) => (await store.list(path, type)).map((record) => ({
|
|
1514
1773
|
conceptId: record.conceptId,
|
|
@@ -1520,21 +1779,21 @@ var listCommand = define({
|
|
|
1520
1779
|
});
|
|
1521
1780
|
|
|
1522
1781
|
// src/commands/load.ts
|
|
1523
|
-
var
|
|
1782
|
+
var import_zod11 = require("zod");
|
|
1524
1783
|
var loadCommand = define({
|
|
1525
1784
|
name: "load",
|
|
1526
1785
|
tool: "kb_load",
|
|
1527
|
-
usage: "load [type] [--budget N
|
|
1528
|
-
description: "
|
|
1529
|
-
input:
|
|
1786
|
+
usage: "load [type] [--budget N] [--all]",
|
|
1787
|
+
description: "Loads the whole knowledge base at once, each record with its standing. Superseded records arrive as stubs (name, replacement, date); rejected and open records arrive whole. Refuses past the token budget rather than truncating \u2014 call kb_catalog, then kb_pack on the record that matters, or narrow with `type`; kb_query for a lookup by wording. `all` bypasses the budget.",
|
|
1788
|
+
input: import_zod11.z.object({
|
|
1530
1789
|
bundlePath,
|
|
1531
|
-
type:
|
|
1532
|
-
budgetTokens:
|
|
1533
|
-
all:
|
|
1534
|
-
"
|
|
1790
|
+
type: import_zod11.z.enum(KB_RECORD_TYPES).optional(),
|
|
1791
|
+
budgetTokens: import_zod11.z.number().int().positive().optional().describe("Approximate token ceiling. Defaults to 25000."),
|
|
1792
|
+
all: import_zod11.z.boolean().optional().describe(
|
|
1793
|
+
"Loads the entire base regardless of size, bypassing the token budget; mutually exclusive with budgetTokens."
|
|
1535
1794
|
)
|
|
1536
1795
|
}).refine((value) => !(value.all && value.budgetTokens !== void 0), {
|
|
1537
|
-
message: "all
|
|
1796
|
+
message: "all is mutually exclusive with budgetTokens: pass a ceiling or none, not both."
|
|
1538
1797
|
}),
|
|
1539
1798
|
fromArgv: (argv, path) => {
|
|
1540
1799
|
const budget = argvFlag(argv, "--budget");
|
|
@@ -1568,25 +1827,25 @@ var loadCommand = define({
|
|
|
1568
1827
|
});
|
|
1569
1828
|
|
|
1570
1829
|
// src/commands/log.ts
|
|
1571
|
-
var
|
|
1830
|
+
var import_zod12 = require("zod");
|
|
1572
1831
|
var logCommand = define({
|
|
1573
1832
|
name: "log",
|
|
1574
1833
|
tool: "kb_log",
|
|
1575
1834
|
usage: "log",
|
|
1576
1835
|
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.",
|
|
1577
|
-
input:
|
|
1836
|
+
input: import_zod12.z.object({ bundlePath }),
|
|
1578
1837
|
fromArgv: (_argv, path) => ({ bundlePath: path }),
|
|
1579
1838
|
run: ({ store }, { bundlePath: path }) => store.readLog(path)
|
|
1580
1839
|
});
|
|
1581
1840
|
|
|
1582
1841
|
// src/commands/no-decision.ts
|
|
1583
|
-
var
|
|
1842
|
+
var import_zod13 = require("zod");
|
|
1584
1843
|
var noDecisionCommand = define({
|
|
1585
1844
|
name: "no-decision",
|
|
1586
1845
|
tool: "kb_no_decision",
|
|
1587
1846
|
usage: "no-decision <reason...>",
|
|
1588
1847
|
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.',
|
|
1589
|
-
input:
|
|
1848
|
+
input: import_zod13.z.object({ bundlePath, reason: import_zod13.z.string().min(1) }),
|
|
1590
1849
|
fromArgv: (argv, path) => ({
|
|
1591
1850
|
bundlePath: path,
|
|
1592
1851
|
reason: argv.slice(1).join(" ").trim()
|
|
@@ -1603,20 +1862,20 @@ var noDecisionCommand = define({
|
|
|
1603
1862
|
});
|
|
1604
1863
|
|
|
1605
1864
|
// src/commands/pack.ts
|
|
1606
|
-
var
|
|
1865
|
+
var import_zod14 = require("zod");
|
|
1607
1866
|
var packCommand = define({
|
|
1608
1867
|
name: "pack",
|
|
1609
1868
|
tool: "kb_pack",
|
|
1610
1869
|
usage: "pack <conceptId> [--hops N] [--max-nodes N] [--budget N]",
|
|
1611
1870
|
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.",
|
|
1612
|
-
input:
|
|
1871
|
+
input: import_zod14.z.object({
|
|
1613
1872
|
bundlePath,
|
|
1614
1873
|
conceptId,
|
|
1615
|
-
hops:
|
|
1616
|
-
maxNodes:
|
|
1874
|
+
hops: import_zod14.z.number().int().positive().optional().describe("How far from the root the walk may reach. Defaults to 2."),
|
|
1875
|
+
maxNodes: import_zod14.z.number().int().positive().optional().describe(
|
|
1617
1876
|
"How many records the pack may hold, root included. Defaults to 20."
|
|
1618
1877
|
),
|
|
1619
|
-
budgetTokens:
|
|
1878
|
+
budgetTokens: import_zod14.z.number().int().positive().optional().describe(
|
|
1620
1879
|
"Approximate token ceiling over what is actually emitted. Defaults to 25000."
|
|
1621
1880
|
)
|
|
1622
1881
|
}),
|
|
@@ -1638,10 +1897,10 @@ var packCommand = define({
|
|
|
1638
1897
|
...maxNodes !== void 0 ? { maxNodes } : {},
|
|
1639
1898
|
...budgetTokens !== void 0 ? { budgetTokens } : {}
|
|
1640
1899
|
});
|
|
1641
|
-
return
|
|
1900
|
+
return render3(result, path, now());
|
|
1642
1901
|
}
|
|
1643
1902
|
});
|
|
1644
|
-
function
|
|
1903
|
+
function render3(result, bundle, at) {
|
|
1645
1904
|
const lines = [
|
|
1646
1905
|
`# KB Pack \u2014 ${result.root}`,
|
|
1647
1906
|
`bundle: ${bundle}`,
|
|
@@ -1703,22 +1962,22 @@ function warningLabel(warning) {
|
|
|
1703
1962
|
}
|
|
1704
1963
|
|
|
1705
1964
|
// src/commands/pin.ts
|
|
1706
|
-
var
|
|
1965
|
+
var import_zod15 = require("zod");
|
|
1707
1966
|
var pinCommand = define({
|
|
1708
1967
|
name: "pin",
|
|
1709
1968
|
tool: "kb_pin",
|
|
1710
1969
|
usage: "pin [bundle-path] [--mode full|index] [--profiles a,b] [--local|--user] [--frozen|--unfreeze]",
|
|
1711
1970
|
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.",
|
|
1712
|
-
input:
|
|
1971
|
+
input: import_zod15.z.object({
|
|
1713
1972
|
bundlePath,
|
|
1714
|
-
mode:
|
|
1973
|
+
mode: import_zod15.z.enum(["full", "index"]).optional().describe(
|
|
1715
1974
|
"full: always emit this base's records whole (still under the block budget); index: never upgrade. Absent: the profile's full-under threshold decides."
|
|
1716
1975
|
),
|
|
1717
|
-
profiles:
|
|
1718
|
-
layer:
|
|
1976
|
+
profiles: import_zod15.z.array(import_zod15.z.string()).optional().describe("Context profiles this pin surfaces in. Absent: all of them."),
|
|
1977
|
+
layer: import_zod15.z.enum(["project", "local", "user"]).optional().describe(
|
|
1719
1978
|
"Which manifest to write: project (committed, default), local (personal, gitignored), user (~/.strauss, every workspace)."
|
|
1720
1979
|
),
|
|
1721
|
-
frozen:
|
|
1980
|
+
frozen: import_zod15.z.boolean().optional().describe(
|
|
1722
1981
|
"true: the base is concluded \u2014 writes against it refuse while pinned. false: lift a freeze."
|
|
1723
1982
|
)
|
|
1724
1983
|
}),
|
|
@@ -1747,29 +2006,29 @@ var pinCommand = define({
|
|
|
1747
2006
|
});
|
|
1748
2007
|
|
|
1749
2008
|
// src/commands/pins.ts
|
|
1750
|
-
var
|
|
2009
|
+
var import_zod16 = require("zod");
|
|
1751
2010
|
var pinsCommand = define({
|
|
1752
2011
|
name: "pins",
|
|
1753
2012
|
tool: "kb_pins",
|
|
1754
2013
|
usage: "pins",
|
|
1755
2014
|
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.",
|
|
1756
|
-
input:
|
|
2015
|
+
input: import_zod16.z.object({}),
|
|
1757
2016
|
fromArgv: () => ({}),
|
|
1758
2017
|
run: ({ store }) => listPins(store, process.cwd())
|
|
1759
2018
|
});
|
|
1760
2019
|
|
|
1761
2020
|
// src/commands/query.ts
|
|
1762
|
-
var
|
|
2021
|
+
var import_zod17 = require("zod");
|
|
1763
2022
|
var queryCommand = define({
|
|
1764
2023
|
name: "query",
|
|
1765
2024
|
tool: "kb_query",
|
|
1766
2025
|
usage: "query <text...>",
|
|
1767
|
-
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.
|
|
1768
|
-
input:
|
|
2026
|
+
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. This is the lookup-by-wording rung, and the narrowest of the three: use it when you know roughly what the record says. The decision rule around it \u2014 while the base fits kb_load's token budget, kb_load it whole, because 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; once kb_load refuses, kb_catalog for one line per record and then kb_pack on the record the work centres on; and kb_query when the question is a point lookup rather than a neighbourhood. A query cannot tell you that nothing was decided \u2014 it returns its nearest hit whatever the distance \u2014 so reach for kb_catalog when the question is what exists. Never read record files directly: this tool (with kb_load, kb_catalog, kb_pack 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.",
|
|
2027
|
+
input: import_zod17.z.object({
|
|
1769
2028
|
bundlePath,
|
|
1770
|
-
text:
|
|
1771
|
-
type:
|
|
1772
|
-
includeNonCurrent:
|
|
2029
|
+
text: import_zod17.z.string().optional(),
|
|
2030
|
+
type: import_zod17.z.enum(KB_RECORD_TYPES).optional(),
|
|
2031
|
+
includeNonCurrent: import_zod17.z.boolean().optional()
|
|
1773
2032
|
}),
|
|
1774
2033
|
fromArgv: (argv, path) => ({
|
|
1775
2034
|
bundlePath: path,
|
|
@@ -1791,27 +2050,27 @@ var queryCommand = define({
|
|
|
1791
2050
|
});
|
|
1792
2051
|
|
|
1793
2052
|
// src/commands/read-index.ts
|
|
1794
|
-
var
|
|
2053
|
+
var import_zod18 = require("zod");
|
|
1795
2054
|
var readIndexCommand = define({
|
|
1796
2055
|
name: "index",
|
|
1797
2056
|
tool: "kb_index",
|
|
1798
2057
|
usage: "index",
|
|
1799
2058
|
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.",
|
|
1800
|
-
input:
|
|
2059
|
+
input: import_zod18.z.object({ bundlePath }),
|
|
1801
2060
|
fromArgv: (_argv, path) => ({ bundlePath: path }),
|
|
1802
2061
|
run: ({ store }, { bundlePath: path }) => store.readIndex(path)
|
|
1803
2062
|
});
|
|
1804
2063
|
|
|
1805
2064
|
// src/commands/schema.ts
|
|
1806
|
-
var
|
|
2065
|
+
var import_zod21 = require("zod");
|
|
1807
2066
|
|
|
1808
2067
|
// src/json-schema.ts
|
|
1809
|
-
var
|
|
2068
|
+
var import_zod20 = require("zod");
|
|
1810
2069
|
|
|
1811
2070
|
// src/kb-log.ts
|
|
1812
|
-
var
|
|
2071
|
+
var import_zod19 = require("zod");
|
|
1813
2072
|
var LOG_FILE = "log.jsonl";
|
|
1814
|
-
var kbLogEntrySchema =
|
|
2073
|
+
var kbLogEntrySchema = import_zod19.z.object({
|
|
1815
2074
|
// Validated, not just `min(1)`: `at` is a sort key (see `parseLog`
|
|
1816
2075
|
// below), and a value that isn't actually chronological — a Unix
|
|
1817
2076
|
// timestamp, a human-typed date, garbage — would sort wrong without
|
|
@@ -1820,12 +2079,12 @@ var kbLogEntrySchema = import_zod18.z.object({
|
|
|
1820
2079
|
// and rejects everything else, including a non-`Z` offset — so a
|
|
1821
2080
|
// malformed `at` is reported the same way a malformed line already is,
|
|
1822
2081
|
// rather than silently sorting into the wrong place.
|
|
1823
|
-
at:
|
|
1824
|
-
by:
|
|
1825
|
-
operation:
|
|
1826
|
-
conceptId:
|
|
2082
|
+
at: import_zod19.z.iso.datetime(),
|
|
2083
|
+
by: import_zod19.z.string().min(1),
|
|
2084
|
+
operation: import_zod19.z.string().min(1),
|
|
2085
|
+
conceptId: import_zod19.z.string().min(1),
|
|
1827
2086
|
/** Second concept id, where the operation relates two — supersession. */
|
|
1828
|
-
target:
|
|
2087
|
+
target: import_zod19.z.string().min(1).optional()
|
|
1829
2088
|
}).strict();
|
|
1830
2089
|
function renderLogEntry(entry) {
|
|
1831
2090
|
return `${JSON.stringify(kbLogEntrySchema.parse(entry))}
|
|
@@ -1863,11 +2122,11 @@ function parseLog(raw) {
|
|
|
1863
2122
|
// src/json-schema.ts
|
|
1864
2123
|
function kbJsonSchemas() {
|
|
1865
2124
|
return {
|
|
1866
|
-
recordFrontmatter:
|
|
2125
|
+
recordFrontmatter: import_zod20.z.toJSONSchema(kbRecordFrontmatterSchema, {
|
|
1867
2126
|
io: "input"
|
|
1868
2127
|
}),
|
|
1869
|
-
composeInput:
|
|
1870
|
-
logEntry:
|
|
2128
|
+
composeInput: import_zod20.z.toJSONSchema(composeInputSchema, { io: "input" }),
|
|
2129
|
+
logEntry: import_zod20.z.toJSONSchema(kbLogEntrySchema, { io: "input" })
|
|
1871
2130
|
};
|
|
1872
2131
|
}
|
|
1873
2132
|
|
|
@@ -1877,22 +2136,22 @@ var schemaCommand = define({
|
|
|
1877
2136
|
tool: "kb_schema",
|
|
1878
2137
|
usage: "schema",
|
|
1879
2138
|
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.",
|
|
1880
|
-
input:
|
|
2139
|
+
input: import_zod21.z.object({}),
|
|
1881
2140
|
fromArgv: () => ({}),
|
|
1882
2141
|
run: () => Promise.resolve(kbJsonSchemas())
|
|
1883
2142
|
});
|
|
1884
2143
|
|
|
1885
2144
|
// src/commands/status.ts
|
|
1886
|
-
var
|
|
2145
|
+
var import_zod22 = require("zod");
|
|
1887
2146
|
var statusCommand = define({
|
|
1888
2147
|
name: "status",
|
|
1889
2148
|
tool: "kb_status",
|
|
1890
2149
|
usage: "status <concept-id> <status>",
|
|
1891
2150
|
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.",
|
|
1892
|
-
input:
|
|
2151
|
+
input: import_zod22.z.object({
|
|
1893
2152
|
bundlePath,
|
|
1894
2153
|
conceptId,
|
|
1895
|
-
status:
|
|
2154
|
+
status: import_zod22.z.enum(KB_RECORD_STATUSES)
|
|
1896
2155
|
}),
|
|
1897
2156
|
fromArgv: (argv, path) => ({
|
|
1898
2157
|
bundlePath: path,
|
|
@@ -1907,13 +2166,13 @@ var statusCommand = define({
|
|
|
1907
2166
|
});
|
|
1908
2167
|
|
|
1909
2168
|
// src/commands/supersede.ts
|
|
1910
|
-
var
|
|
2169
|
+
var import_zod23 = require("zod");
|
|
1911
2170
|
var supersedeCommand = define({
|
|
1912
2171
|
name: "supersede",
|
|
1913
2172
|
tool: "kb_supersede",
|
|
1914
2173
|
usage: "supersede <concept-id> <replacement-id>",
|
|
1915
2174
|
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.",
|
|
1916
|
-
input:
|
|
2175
|
+
input: import_zod23.z.object({ bundlePath, conceptId, replacementId: conceptId }),
|
|
1917
2176
|
fromArgv: (argv, path) => ({
|
|
1918
2177
|
bundlePath: path,
|
|
1919
2178
|
conceptId: argv[1],
|
|
@@ -1927,16 +2186,16 @@ var supersedeCommand = define({
|
|
|
1927
2186
|
});
|
|
1928
2187
|
|
|
1929
2188
|
// src/commands/sync-instructions.ts
|
|
1930
|
-
var
|
|
2189
|
+
var import_zod24 = require("zod");
|
|
1931
2190
|
var syncInstructionsCommand = define({
|
|
1932
2191
|
name: "sync-instructions",
|
|
1933
2192
|
usage: "sync-instructions <file> [--profile NAME] [--budget N] [--full-under N]",
|
|
1934
2193
|
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.",
|
|
1935
|
-
input:
|
|
1936
|
-
file:
|
|
1937
|
-
budgetTokens:
|
|
1938
|
-
fullUnderTokens:
|
|
1939
|
-
profile:
|
|
2194
|
+
input: import_zod24.z.object({
|
|
2195
|
+
file: import_zod24.z.string().min(1).describe("The instruction file to edit in place."),
|
|
2196
|
+
budgetTokens: import_zod24.z.number().int().positive().optional(),
|
|
2197
|
+
fullUnderTokens: import_zod24.z.number().int().positive().optional(),
|
|
2198
|
+
profile: import_zod24.z.string().optional()
|
|
1940
2199
|
}),
|
|
1941
2200
|
fromArgv: (argv) => {
|
|
1942
2201
|
const budget = argvFlag(argv, "--budget");
|
|
@@ -1962,7 +2221,7 @@ var syncInstructionsCommand = define({
|
|
|
1962
2221
|
});
|
|
1963
2222
|
|
|
1964
2223
|
// src/commands/trace.ts
|
|
1965
|
-
var
|
|
2224
|
+
var import_zod25 = require("zod");
|
|
1966
2225
|
|
|
1967
2226
|
// src/trace.ts
|
|
1968
2227
|
var TRACE_EDGES = ["supersession", "anchor", "source"];
|
|
@@ -2008,11 +2267,11 @@ var traceCommand = define({
|
|
|
2008
2267
|
tool: "kb_trace",
|
|
2009
2268
|
usage: "trace <concept-id> [edges...]",
|
|
2010
2269
|
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.',
|
|
2011
|
-
input:
|
|
2270
|
+
input: import_zod25.z.object({
|
|
2012
2271
|
bundlePath,
|
|
2013
2272
|
conceptId,
|
|
2014
|
-
edges:
|
|
2015
|
-
depth:
|
|
2273
|
+
edges: import_zod25.z.array(import_zod25.z.enum(TRACE_EDGES)).optional(),
|
|
2274
|
+
depth: import_zod25.z.number().int().positive().optional()
|
|
2016
2275
|
}),
|
|
2017
2276
|
fromArgv: (argv, path) => ({
|
|
2018
2277
|
bundlePath: path,
|
|
@@ -2034,53 +2293,53 @@ var traceCommand = define({
|
|
|
2034
2293
|
});
|
|
2035
2294
|
|
|
2036
2295
|
// src/commands/types.ts
|
|
2037
|
-
var
|
|
2296
|
+
var import_zod26 = require("zod");
|
|
2038
2297
|
var typesCommand = define({
|
|
2039
2298
|
name: "types",
|
|
2040
2299
|
tool: "kb_types",
|
|
2041
2300
|
usage: "types",
|
|
2042
2301
|
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.",
|
|
2043
|
-
input:
|
|
2302
|
+
input: import_zod26.z.object({}),
|
|
2044
2303
|
fromArgv: () => ({}),
|
|
2045
2304
|
run: () => Promise.resolve(RECORD_TYPES)
|
|
2046
2305
|
});
|
|
2047
2306
|
|
|
2048
2307
|
// src/commands/unpin.ts
|
|
2049
|
-
var
|
|
2308
|
+
var import_zod27 = require("zod");
|
|
2050
2309
|
var unpinCommand = define({
|
|
2051
2310
|
name: "unpin",
|
|
2052
2311
|
tool: "kb_unpin",
|
|
2053
2312
|
usage: "unpin [bundle-path]",
|
|
2054
2313
|
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.",
|
|
2055
|
-
input:
|
|
2314
|
+
input: import_zod27.z.object({ bundlePath }),
|
|
2056
2315
|
fromArgv: (argv, path) => ({ bundlePath: argv[1] ?? path }),
|
|
2057
2316
|
run: (_ctx, { bundlePath: path }) => unpinBase(process.cwd(), path)
|
|
2058
2317
|
});
|
|
2059
2318
|
|
|
2060
2319
|
// src/commands/validate.ts
|
|
2061
|
-
var
|
|
2320
|
+
var import_zod28 = require("zod");
|
|
2062
2321
|
var validateCommand = define({
|
|
2063
2322
|
name: "validate",
|
|
2064
2323
|
tool: "kb_validate",
|
|
2065
2324
|
usage: "validate",
|
|
2066
2325
|
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.",
|
|
2067
|
-
input:
|
|
2326
|
+
input: import_zod28.z.object({ bundlePath }),
|
|
2068
2327
|
fromArgv: (_argv, path) => ({ bundlePath: path }),
|
|
2069
2328
|
run: async ({ store }, { bundlePath: path }) => validateBundle(await store.list(path)),
|
|
2070
2329
|
failsWhen: (result) => Array.isArray(result) && result.length > 0
|
|
2071
2330
|
});
|
|
2072
2331
|
|
|
2073
2332
|
// src/commands/verify.ts
|
|
2074
|
-
var
|
|
2333
|
+
var import_zod29 = require("zod");
|
|
2075
2334
|
var verifyCommand = define({
|
|
2076
2335
|
name: "verify",
|
|
2077
2336
|
tool: "kb_verify",
|
|
2078
2337
|
usage: "verify <concept-id> --note <text>",
|
|
2079
2338
|
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.",
|
|
2080
|
-
input:
|
|
2339
|
+
input: import_zod29.z.object({
|
|
2081
2340
|
bundlePath,
|
|
2082
2341
|
conceptId,
|
|
2083
|
-
note:
|
|
2342
|
+
note: import_zod29.z.string().refine((s) => s.trim().length > 0, {
|
|
2084
2343
|
message: "note must say what the check found"
|
|
2085
2344
|
})
|
|
2086
2345
|
}),
|
|
@@ -2100,7 +2359,7 @@ var verifyCommand = define({
|
|
|
2100
2359
|
});
|
|
2101
2360
|
|
|
2102
2361
|
// src/commands/write.ts
|
|
2103
|
-
var
|
|
2362
|
+
var import_zod30 = require("zod");
|
|
2104
2363
|
var writeCommand = define({
|
|
2105
2364
|
name: "write",
|
|
2106
2365
|
tool: "kb_write",
|
|
@@ -2114,9 +2373,9 @@ var writeCommand = define({
|
|
|
2114
2373
|
"- Prefer a new record over overloading an existing one, and keep each short. A record nobody finishes reading is not durable memory.",
|
|
2115
2374
|
"- Records are never deleted; supersede instead, so the earlier reasoning stays inspectable."
|
|
2116
2375
|
].join("\n"),
|
|
2117
|
-
input:
|
|
2376
|
+
input: import_zod30.z.object({
|
|
2118
2377
|
bundlePath,
|
|
2119
|
-
type:
|
|
2378
|
+
type: import_zod30.z.enum(KB_RECORD_TYPES),
|
|
2120
2379
|
input: composeInputSchema
|
|
2121
2380
|
}),
|
|
2122
2381
|
fromArgv: async (argv, path, stdin) => ({
|
|
@@ -2140,7 +2399,7 @@ var writeCommand = define({
|
|
|
2140
2399
|
});
|
|
2141
2400
|
|
|
2142
2401
|
// src/commands/write-decision.ts
|
|
2143
|
-
var
|
|
2402
|
+
var import_zod31 = require("zod");
|
|
2144
2403
|
var writeDecisionCommand = define({
|
|
2145
2404
|
name: "write-decision",
|
|
2146
2405
|
tool: "kb_write_decision",
|
|
@@ -2153,7 +2412,7 @@ var writeDecisionCommand = define({
|
|
|
2153
2412
|
"- `alternative` is what you turned down and why, not a list of everything considered.",
|
|
2154
2413
|
"- A reference to material you read goes in `sources`; a reference to code goes in `anchors`; a reference to another record goes in `relatedConceptIds`."
|
|
2155
2414
|
].join("\n"),
|
|
2156
|
-
input:
|
|
2415
|
+
input: import_zod31.z.object({ bundlePath, input: decisionInputSchema }),
|
|
2157
2416
|
fromArgv: async (_argv, path, stdin) => ({
|
|
2158
2417
|
bundlePath: path,
|
|
2159
2418
|
input: JSON.parse(await stdin())
|
|
@@ -2183,6 +2442,7 @@ var KB_COMMANDS = [
|
|
|
2183
2442
|
answerCommand,
|
|
2184
2443
|
verifyCommand,
|
|
2185
2444
|
loadCommand,
|
|
2445
|
+
catalogCommand,
|
|
2186
2446
|
packCommand,
|
|
2187
2447
|
queryCommand,
|
|
2188
2448
|
traceCommand,
|
|
@@ -2233,126 +2493,6 @@ function parseMarkdownWithFrontmatter(text, schema) {
|
|
|
2233
2493
|
};
|
|
2234
2494
|
}
|
|
2235
2495
|
|
|
2236
|
-
// src/errors.ts
|
|
2237
|
-
var BaseError = class extends Error {
|
|
2238
|
-
code;
|
|
2239
|
-
errorType;
|
|
2240
|
-
fault;
|
|
2241
|
-
retriable;
|
|
2242
|
-
reportToUser;
|
|
2243
|
-
details;
|
|
2244
|
-
constructor(props) {
|
|
2245
|
-
super(props.message);
|
|
2246
|
-
this.name = props.name ?? this.constructor.name;
|
|
2247
|
-
this.code = props.code ?? 500;
|
|
2248
|
-
this.errorType = props.errorType;
|
|
2249
|
-
this.fault = props.fault;
|
|
2250
|
-
this.retriable = props.retriable ?? true;
|
|
2251
|
-
this.reportToUser = props.reportToUser ?? false;
|
|
2252
|
-
this.details = props.details;
|
|
2253
|
-
}
|
|
2254
|
-
};
|
|
2255
|
-
|
|
2256
|
-
// src/kb-errors.ts
|
|
2257
|
-
var KbRecordAlreadyExistsError = class extends BaseError {
|
|
2258
|
-
constructor(conceptId2) {
|
|
2259
|
-
super({
|
|
2260
|
-
message: `kb: ${conceptId2} already exists \u2014 choose a more specific slug, or write with overwrite`,
|
|
2261
|
-
errorType: "KbRecordAlreadyExists" /* KbRecordAlreadyExists */,
|
|
2262
|
-
code: 409,
|
|
2263
|
-
fault: "User" /* User */,
|
|
2264
|
-
retriable: false,
|
|
2265
|
-
reportToUser: true,
|
|
2266
|
-
details: { conceptId: conceptId2, action: "refused" }
|
|
2267
|
-
});
|
|
2268
|
-
this.conceptId = conceptId2;
|
|
2269
|
-
}
|
|
2270
|
-
conceptId;
|
|
2271
|
-
};
|
|
2272
|
-
var KbRecordNotFoundError = class extends BaseError {
|
|
2273
|
-
constructor(conceptId2) {
|
|
2274
|
-
super({
|
|
2275
|
-
message: `kb: ${conceptId2} does not exist`,
|
|
2276
|
-
errorType: "KbRecordNotFound" /* KbRecordNotFound */,
|
|
2277
|
-
code: 404,
|
|
2278
|
-
fault: "User" /* User */,
|
|
2279
|
-
retriable: false,
|
|
2280
|
-
reportToUser: true,
|
|
2281
|
-
details: { conceptId: conceptId2 }
|
|
2282
|
-
});
|
|
2283
|
-
this.conceptId = conceptId2;
|
|
2284
|
-
}
|
|
2285
|
-
conceptId;
|
|
2286
|
-
};
|
|
2287
|
-
var KbWriteConflictError = class extends BaseError {
|
|
2288
|
-
constructor(conceptId2) {
|
|
2289
|
-
super({
|
|
2290
|
-
message: `kb: ${conceptId2} changed while it was being updated \u2014 re-read and retry`,
|
|
2291
|
-
errorType: "KbWriteConflict" /* KbWriteConflict */,
|
|
2292
|
-
code: 409,
|
|
2293
|
-
fault: "System" /* System */,
|
|
2294
|
-
retriable: true,
|
|
2295
|
-
reportToUser: true,
|
|
2296
|
-
details: { conceptId: conceptId2 }
|
|
2297
|
-
});
|
|
2298
|
-
this.conceptId = conceptId2;
|
|
2299
|
-
}
|
|
2300
|
-
conceptId;
|
|
2301
|
-
};
|
|
2302
|
-
var KbSelfVerificationError = class extends BaseError {
|
|
2303
|
-
constructor(conceptId2, actor, generatedBy) {
|
|
2304
|
-
super({
|
|
2305
|
-
message: `kb: ${conceptId2} was generated by ${generatedBy}, and a record's generator cannot verify it \u2014 only a human or a different actor can`,
|
|
2306
|
-
errorType: "KbSelfVerification" /* KbSelfVerification */,
|
|
2307
|
-
code: 400,
|
|
2308
|
-
fault: "User" /* User */,
|
|
2309
|
-
retriable: false,
|
|
2310
|
-
reportToUser: true,
|
|
2311
|
-
details: { conceptId: conceptId2, actor, generatedBy, action: "refused" }
|
|
2312
|
-
});
|
|
2313
|
-
this.conceptId = conceptId2;
|
|
2314
|
-
this.actor = actor;
|
|
2315
|
-
this.generatedBy = generatedBy;
|
|
2316
|
-
}
|
|
2317
|
-
conceptId;
|
|
2318
|
-
actor;
|
|
2319
|
-
generatedBy;
|
|
2320
|
-
};
|
|
2321
|
-
var KbPackBudgetExceededError = class extends BaseError {
|
|
2322
|
-
constructor(recordCount, approxTokens2, budgetTokens, excluded) {
|
|
2323
|
-
super({
|
|
2324
|
-
message: `kb: a pack of ${recordCount} records is ~${approxTokens2} tokens against a budget of ${budgetTokens} \u2014 lower hops or maxNodes, or raise the budget`,
|
|
2325
|
-
errorType: "KbPackBudgetExceeded" /* KbPackBudgetExceeded */,
|
|
2326
|
-
code: 400,
|
|
2327
|
-
fault: "User" /* User */,
|
|
2328
|
-
retriable: false,
|
|
2329
|
-
reportToUser: true,
|
|
2330
|
-
details: { recordCount, approxTokens: approxTokens2, budgetTokens, excluded }
|
|
2331
|
-
});
|
|
2332
|
-
this.recordCount = recordCount;
|
|
2333
|
-
this.approxTokens = approxTokens2;
|
|
2334
|
-
this.budgetTokens = budgetTokens;
|
|
2335
|
-
this.excluded = excluded;
|
|
2336
|
-
}
|
|
2337
|
-
recordCount;
|
|
2338
|
-
approxTokens;
|
|
2339
|
-
budgetTokens;
|
|
2340
|
-
excluded;
|
|
2341
|
-
};
|
|
2342
|
-
var KbInvalidConceptIdError = class extends BaseError {
|
|
2343
|
-
constructor(message, details) {
|
|
2344
|
-
super({
|
|
2345
|
-
message: `kb: ${message}`,
|
|
2346
|
-
errorType: "KbInvalidConceptId" /* KbInvalidConceptId */,
|
|
2347
|
-
code: 400,
|
|
2348
|
-
fault: "User" /* User */,
|
|
2349
|
-
retriable: false,
|
|
2350
|
-
reportToUser: true,
|
|
2351
|
-
details
|
|
2352
|
-
});
|
|
2353
|
-
}
|
|
2354
|
-
};
|
|
2355
|
-
|
|
2356
2496
|
// src/search-index.ts
|
|
2357
2497
|
var import_promises3 = require("fs/promises");
|
|
2358
2498
|
var import_node_path5 = require("path");
|
|
@@ -2803,9 +2943,16 @@ ${answer}
|
|
|
2803
2943
|
* is indistinguishable from a complete one, so a caller would answer "that
|
|
2804
2944
|
* was never decided" from a slice it did not know was a slice.
|
|
2805
2945
|
*
|
|
2806
|
-
*
|
|
2807
|
-
*
|
|
2808
|
-
*
|
|
2946
|
+
* A token budget decides that, measured over what is actually handed back.
|
|
2947
|
+
* The refusal names the estimate and the budget, because a caller told only
|
|
2948
|
+
* "too big" cannot tell whether to narrow the type filter, raise the budget,
|
|
2949
|
+
* or stop loading the base whole altogether. Past the budget the answer is
|
|
2950
|
+
* the catalog and then a pack, which is what the refusal says.
|
|
2951
|
+
*
|
|
2952
|
+
* That refusal is the default guardrail. `all` bypasses the budget outright
|
|
2953
|
+
* and always hands back the whole bundle: an explicit, never-accidental
|
|
2954
|
+
* escape hatch for an operator who has the budget to spend, not a wider
|
|
2955
|
+
* default.
|
|
2809
2956
|
*/
|
|
2810
2957
|
async load(bundlePath2, options = {}) {
|
|
2811
2958
|
const budgetTokens = options.budgetTokens ?? DEFAULT_LOAD_BUDGET;
|
|
@@ -2820,7 +2967,12 @@ ${answer}
|
|
|
2820
2967
|
loaded: false,
|
|
2821
2968
|
recordCount: wanted.length,
|
|
2822
2969
|
approxTokens: approxTokens2,
|
|
2823
|
-
budgetTokens
|
|
2970
|
+
budgetTokens,
|
|
2971
|
+
message: refusalMessage({
|
|
2972
|
+
approxTokens: approxTokens2,
|
|
2973
|
+
budgetTokens,
|
|
2974
|
+
type: options.type
|
|
2975
|
+
})
|
|
2824
2976
|
};
|
|
2825
2977
|
}
|
|
2826
2978
|
return {
|
|
@@ -2836,6 +2988,10 @@ ${answer}
|
|
|
2836
2988
|
async trace(bundlePath2, seedId, options = {}) {
|
|
2837
2989
|
return trace(seedId, await this.list(bundlePath2), options);
|
|
2838
2990
|
}
|
|
2991
|
+
/** Every record named in one line each. See `catalog.ts`. */
|
|
2992
|
+
async catalog(bundlePath2, options = {}) {
|
|
2993
|
+
return catalog(await this.list(bundlePath2), options);
|
|
2994
|
+
}
|
|
2839
2995
|
/** A bounded neighbourhood around one record. See `pack.ts`. */
|
|
2840
2996
|
async pack(bundlePath2, rootId, options = {}) {
|
|
2841
2997
|
return pack(await this.list(bundlePath2), rootId, options);
|
|
@@ -3102,6 +3258,14 @@ function estimateTokens(record) {
|
|
|
3102
3258
|
function estimateStubTokens(entry) {
|
|
3103
3259
|
return Math.ceil(JSON.stringify(entry).length / 4);
|
|
3104
3260
|
}
|
|
3261
|
+
function refusalMessage(refusal) {
|
|
3262
|
+
const scope = refusal.type ? ` of type ${refusal.type}` : "";
|
|
3263
|
+
return [
|
|
3264
|
+
`Refusing to load this base whole: ~${refusal.approxTokens} tokens is past the ${refusal.budgetTokens}-token budget.`,
|
|
3265
|
+
`Call kb_catalog for one line per record${scope} (id, type, title, standing), then kb_pack on the record that matters; kb_query works for a lookup by wording.`,
|
|
3266
|
+
`To load anyway: raise budgetTokens (currently ${refusal.budgetTokens}), or all=true to bypass the budget.`
|
|
3267
|
+
].join(" ");
|
|
3268
|
+
}
|
|
3105
3269
|
function stub(hit) {
|
|
3106
3270
|
return {
|
|
3107
3271
|
conceptId: hit.record.conceptId,
|
|
@@ -3126,7 +3290,7 @@ function digest(contents) {
|
|
|
3126
3290
|
}
|
|
3127
3291
|
|
|
3128
3292
|
// src/version.ts
|
|
3129
|
-
var VERSION = true ? "0.1.
|
|
3293
|
+
var VERSION = true ? "0.1.10" : "0.0.0-dev";
|
|
3130
3294
|
|
|
3131
3295
|
// src/cli.ts
|
|
3132
3296
|
async function runKbCli(argv) {
|