@saasontools/strauss-kb 0.1.15 → 0.1.16
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 +17 -11
- package/dist/{chunk-KNIUBCZY.js → chunk-H5W53NVU.js} +225 -60
- package/dist/chunk-H5W53NVU.js.map +1 -0
- package/dist/{chunk-LACCRB2Y.js → chunk-MEZCF646.js} +2 -2
- package/dist/{chunk-MK7GU4DX.js → chunk-RINBOAQZ.js} +15 -6
- package/dist/chunk-RINBOAQZ.js.map +1 -0
- package/dist/cli-main.cjs +247 -75
- package/dist/cli-main.cjs.map +1 -1
- package/dist/cli-main.js +2 -2
- package/dist/index.cjs +231 -57
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +30 -2
- package/dist/index.d.ts +30 -2
- package/dist/index.js +3 -3
- package/dist/mcp-main.cjs +234 -71
- package/dist/mcp-main.cjs.map +1 -1
- package/dist/mcp-main.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-KNIUBCZY.js.map +0 -1
- package/dist/chunk-MK7GU4DX.js.map +0 -1
- /package/dist/{chunk-LACCRB2Y.js.map → chunk-MEZCF646.js.map} +0 -0
package/dist/mcp-main.cjs
CHANGED
|
@@ -1440,6 +1440,36 @@ var KbInvalidConceptIdError = class extends BaseError {
|
|
|
1440
1440
|
});
|
|
1441
1441
|
}
|
|
1442
1442
|
};
|
|
1443
|
+
var KbStampBaselineError = class extends BaseError {
|
|
1444
|
+
constructor(since) {
|
|
1445
|
+
super({
|
|
1446
|
+
message: `kb: --since ${since} is neither a 64-character digest nor a readable stamp file`,
|
|
1447
|
+
errorType: "KbStampBaselineUnreadable" /* KbStampBaselineUnreadable */,
|
|
1448
|
+
code: 400,
|
|
1449
|
+
fault: "User" /* User */,
|
|
1450
|
+
retriable: false,
|
|
1451
|
+
reportToUser: true,
|
|
1452
|
+
details: { since }
|
|
1453
|
+
});
|
|
1454
|
+
this.since = since;
|
|
1455
|
+
}
|
|
1456
|
+
since;
|
|
1457
|
+
};
|
|
1458
|
+
var KbStampDigestBaselineError = class extends BaseError {
|
|
1459
|
+
constructor(since) {
|
|
1460
|
+
super({
|
|
1461
|
+
message: `kb: --since ${since} is a digest, which needs --bundle (one base) \u2014 a file baseline works for many`,
|
|
1462
|
+
errorType: "KbStampDigestBaselineAmbiguous" /* KbStampDigestBaselineAmbiguous */,
|
|
1463
|
+
code: 400,
|
|
1464
|
+
fault: "User" /* User */,
|
|
1465
|
+
retriable: false,
|
|
1466
|
+
reportToUser: true,
|
|
1467
|
+
details: { since }
|
|
1468
|
+
});
|
|
1469
|
+
this.since = since;
|
|
1470
|
+
}
|
|
1471
|
+
since;
|
|
1472
|
+
};
|
|
1443
1473
|
|
|
1444
1474
|
// src/kb-pins/budgets.ts
|
|
1445
1475
|
function asBudgets(value) {
|
|
@@ -3578,17 +3608,115 @@ var schemaCommand = define({
|
|
|
3578
3608
|
run: () => Promise.resolve(kbJsonSchemas())
|
|
3579
3609
|
});
|
|
3580
3610
|
|
|
3581
|
-
// src/commands/
|
|
3611
|
+
// src/commands/stamp.ts
|
|
3612
|
+
var import_promises5 = require("fs/promises");
|
|
3582
3613
|
var import_zod25 = require("zod");
|
|
3614
|
+
var DIGEST = /^[0-9a-f]{64}$/;
|
|
3615
|
+
var stampCommand = define({
|
|
3616
|
+
name: "stamp",
|
|
3617
|
+
tool: "kb_stamp",
|
|
3618
|
+
usage: "stamp [--bundle PATH] [--since DIGEST|FILE]",
|
|
3619
|
+
description: "Content stamp of a base \u2014 `load`'s digest, record counts, per-record digests \u2014 without any bodies. Takes no bundlePath to stamp every pinned base. With `since`, reports only the bases that moved, naming the changed ids when the baseline is a prior stamp; silent when nothing changed. Reads, never writes.",
|
|
3620
|
+
input: import_zod25.z.object({
|
|
3621
|
+
bundlePath: import_zod25.z.string().min(1).optional().describe(
|
|
3622
|
+
"Absolute path to one knowledge base. Omit to stamp every pinned base."
|
|
3623
|
+
),
|
|
3624
|
+
since: import_zod25.z.string().min(1).optional().describe(
|
|
3625
|
+
"Prior digest, or path to a prior `stamp --json`; only moved bases return, with changed ids when the baseline is a file."
|
|
3626
|
+
)
|
|
3627
|
+
}),
|
|
3628
|
+
fromArgv: (argv, path, _stdin, bundleExplicit) => {
|
|
3629
|
+
const since = argvFlag(argv, "--since");
|
|
3630
|
+
return {
|
|
3631
|
+
...bundleExplicit ? { bundlePath: path } : {},
|
|
3632
|
+
...since !== void 0 ? { since } : {}
|
|
3633
|
+
};
|
|
3634
|
+
},
|
|
3635
|
+
run: async ({ store }, { bundlePath: bundlePath2, since }) => {
|
|
3636
|
+
const targets = bundlePath2 ? [bundlePath2] : (await readMergedPins(process.cwd())).pins.map(
|
|
3637
|
+
(pin) => pin.absolutePath
|
|
3638
|
+
);
|
|
3639
|
+
if (since !== void 0 && DIGEST.test(since) && targets.length > 1) {
|
|
3640
|
+
throw new KbStampDigestBaselineError(since);
|
|
3641
|
+
}
|
|
3642
|
+
const stamps = await Promise.all(
|
|
3643
|
+
targets.map((target) => store.stamp(target))
|
|
3644
|
+
);
|
|
3645
|
+
if (since === void 0) {
|
|
3646
|
+
return stamps.map((stamp) => ({ ...stamp, changed: null }));
|
|
3647
|
+
}
|
|
3648
|
+
const baseline = await readBaseline(since);
|
|
3649
|
+
const reports = [];
|
|
3650
|
+
for (const stamp of stamps) {
|
|
3651
|
+
const before = baseline.byPath.get(stamp.path);
|
|
3652
|
+
if (baseline.digest !== null) {
|
|
3653
|
+
if (baseline.digest === stamp.digest) continue;
|
|
3654
|
+
reports.push({ ...stamp, changed: null });
|
|
3655
|
+
continue;
|
|
3656
|
+
}
|
|
3657
|
+
if (before && before.digest === stamp.digest) continue;
|
|
3658
|
+
reports.push({ ...stamp, changed: changedIds(before?.records, stamp) });
|
|
3659
|
+
}
|
|
3660
|
+
return reports;
|
|
3661
|
+
},
|
|
3662
|
+
render: (result) => result.map((report) => {
|
|
3663
|
+
const counts = `${report.recordCount} record(s), ${report.superseded} superseded`;
|
|
3664
|
+
const head = `${report.path} ${report.digest} ${counts}${report.newestAt ? ` newest ${report.newestAt}` : ""}`;
|
|
3665
|
+
return report.changed?.length ? `${head}
|
|
3666
|
+
changed: ${report.changed.join(", ")}` : head;
|
|
3667
|
+
}).join("\n")
|
|
3668
|
+
});
|
|
3669
|
+
function changedIds(before, stamp) {
|
|
3670
|
+
const now = new Map(
|
|
3671
|
+
stamp.records.map((record) => [record.conceptId, record.digest])
|
|
3672
|
+
);
|
|
3673
|
+
const ids = /* @__PURE__ */ new Set();
|
|
3674
|
+
for (const [conceptId2, digest] of now) {
|
|
3675
|
+
if (before?.get(conceptId2) !== digest) ids.add(conceptId2);
|
|
3676
|
+
}
|
|
3677
|
+
for (const conceptId2 of before?.keys() ?? []) {
|
|
3678
|
+
if (!now.has(conceptId2)) ids.add(conceptId2);
|
|
3679
|
+
}
|
|
3680
|
+
return [...ids].sort();
|
|
3681
|
+
}
|
|
3682
|
+
async function readBaseline(since) {
|
|
3683
|
+
if (DIGEST.test(since)) return { digest: since, byPath: /* @__PURE__ */ new Map() };
|
|
3684
|
+
let parsed;
|
|
3685
|
+
try {
|
|
3686
|
+
parsed = JSON.parse(await (0, import_promises5.readFile)(since, "utf8"));
|
|
3687
|
+
} catch {
|
|
3688
|
+
throw new KbStampBaselineError(since);
|
|
3689
|
+
}
|
|
3690
|
+
const entries = Array.isArray(parsed) ? parsed : parsed?.stamps ?? [];
|
|
3691
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
3692
|
+
for (const entry of entries) {
|
|
3693
|
+
if (typeof entry?.path !== "string" || typeof entry?.digest !== "string") {
|
|
3694
|
+
continue;
|
|
3695
|
+
}
|
|
3696
|
+
byPath.set(entry.path, {
|
|
3697
|
+
digest: entry.digest,
|
|
3698
|
+
records: new Map(
|
|
3699
|
+
(entry.records ?? []).map((record) => [
|
|
3700
|
+
record.conceptId,
|
|
3701
|
+
record.digest
|
|
3702
|
+
])
|
|
3703
|
+
)
|
|
3704
|
+
});
|
|
3705
|
+
}
|
|
3706
|
+
return { digest: null, byPath };
|
|
3707
|
+
}
|
|
3708
|
+
|
|
3709
|
+
// src/commands/status.ts
|
|
3710
|
+
var import_zod26 = require("zod");
|
|
3583
3711
|
var statusCommand = define({
|
|
3584
3712
|
name: "status",
|
|
3585
3713
|
tool: "kb_status",
|
|
3586
3714
|
usage: "status <concept-id> <status>",
|
|
3587
3715
|
description: "Move a record's status. Compare-and-swap: a concurrent change fails instead of being overwritten.",
|
|
3588
|
-
input:
|
|
3716
|
+
input: import_zod26.z.object({
|
|
3589
3717
|
bundlePath,
|
|
3590
3718
|
conceptId,
|
|
3591
|
-
status:
|
|
3719
|
+
status: import_zod26.z.enum(KB_RECORD_STATUSES)
|
|
3592
3720
|
}),
|
|
3593
3721
|
fromArgv: (argv, path) => ({
|
|
3594
3722
|
bundlePath: path,
|
|
@@ -3603,13 +3731,13 @@ var statusCommand = define({
|
|
|
3603
3731
|
});
|
|
3604
3732
|
|
|
3605
3733
|
// src/commands/supersede.ts
|
|
3606
|
-
var
|
|
3734
|
+
var import_zod27 = require("zod");
|
|
3607
3735
|
var supersedeCommand = define({
|
|
3608
3736
|
name: "supersede",
|
|
3609
3737
|
tool: "kb_supersede",
|
|
3610
3738
|
usage: "supersede <concept-id> <replacement-id>",
|
|
3611
3739
|
description: "Mark a record superseded by another, linked in both directions. Use instead of editing a record whose meaning changed.",
|
|
3612
|
-
input:
|
|
3740
|
+
input: import_zod27.z.object({ bundlePath, conceptId, replacementId: conceptId }),
|
|
3613
3741
|
fromArgv: (argv, path) => ({
|
|
3614
3742
|
bundlePath: path,
|
|
3615
3743
|
conceptId: argv[1],
|
|
@@ -3623,16 +3751,16 @@ var supersedeCommand = define({
|
|
|
3623
3751
|
});
|
|
3624
3752
|
|
|
3625
3753
|
// src/commands/sync-instructions.ts
|
|
3626
|
-
var
|
|
3754
|
+
var import_zod28 = require("zod");
|
|
3627
3755
|
var syncInstructionsCommand = define({
|
|
3628
3756
|
name: "sync-instructions",
|
|
3629
3757
|
usage: "sync-instructions <file> [--profile NAME] [--budget N] [--full-under N]",
|
|
3630
3758
|
description: "CLI-only: plant the kb_context block between sentinel comments in AGENTS.md or CLAUDE.md, idempotently.",
|
|
3631
|
-
input:
|
|
3632
|
-
file:
|
|
3633
|
-
budgetTokens:
|
|
3634
|
-
fullUnderTokens:
|
|
3635
|
-
profile:
|
|
3759
|
+
input: import_zod28.z.object({
|
|
3760
|
+
file: import_zod28.z.string().min(1).describe("The instruction file to edit in place."),
|
|
3761
|
+
budgetTokens: import_zod28.z.number().int().positive().optional(),
|
|
3762
|
+
fullUnderTokens: import_zod28.z.number().int().positive().optional(),
|
|
3763
|
+
profile: import_zod28.z.string().optional()
|
|
3636
3764
|
}),
|
|
3637
3765
|
fromArgv: (argv) => {
|
|
3638
3766
|
const budget = argvFlag(argv, "--budget");
|
|
@@ -3658,7 +3786,7 @@ var syncInstructionsCommand = define({
|
|
|
3658
3786
|
});
|
|
3659
3787
|
|
|
3660
3788
|
// src/commands/trace.ts
|
|
3661
|
-
var
|
|
3789
|
+
var import_zod29 = require("zod");
|
|
3662
3790
|
|
|
3663
3791
|
// src/trace.ts
|
|
3664
3792
|
var TRACE_EDGES = [
|
|
@@ -3714,11 +3842,11 @@ var traceCommand = define({
|
|
|
3714
3842
|
tool: "kb_trace",
|
|
3715
3843
|
usage: "trace <concept-id> [edges...]",
|
|
3716
3844
|
description: 'Timeline of how a position was reached, ordered by write time, following supersession, shared anchors and shared sources. Includes rejected, draft and superseded records \u2014 in a history they are the content. For "why is it like this"; kb_load answers "what holds now".',
|
|
3717
|
-
input:
|
|
3845
|
+
input: import_zod29.z.object({
|
|
3718
3846
|
bundlePath,
|
|
3719
3847
|
conceptId,
|
|
3720
|
-
edges:
|
|
3721
|
-
depth:
|
|
3848
|
+
edges: import_zod29.z.array(import_zod29.z.enum(TRACE_EDGES)).optional(),
|
|
3849
|
+
depth: import_zod29.z.number().int().positive().optional()
|
|
3722
3850
|
}),
|
|
3723
3851
|
fromArgv: (argv, path) => ({
|
|
3724
3852
|
bundlePath: path,
|
|
@@ -3740,37 +3868,37 @@ var traceCommand = define({
|
|
|
3740
3868
|
});
|
|
3741
3869
|
|
|
3742
3870
|
// src/commands/types.ts
|
|
3743
|
-
var
|
|
3871
|
+
var import_zod30 = require("zod");
|
|
3744
3872
|
var typesCommand = define({
|
|
3745
3873
|
name: "types",
|
|
3746
3874
|
tool: "kb_types",
|
|
3747
3875
|
usage: "types",
|
|
3748
3876
|
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.",
|
|
3749
|
-
input:
|
|
3877
|
+
input: import_zod30.z.object({}),
|
|
3750
3878
|
fromArgv: () => ({}),
|
|
3751
3879
|
run: () => Promise.resolve(RECORD_TYPES)
|
|
3752
3880
|
});
|
|
3753
3881
|
|
|
3754
3882
|
// src/commands/unpin.ts
|
|
3755
|
-
var
|
|
3883
|
+
var import_zod31 = require("zod");
|
|
3756
3884
|
var unpinCommand = define({
|
|
3757
3885
|
name: "unpin",
|
|
3758
3886
|
tool: "kb_unpin",
|
|
3759
3887
|
usage: "unpin [bundle-path]",
|
|
3760
3888
|
description: "Remove a base from every manifest layer that holds it. Reports the layers touched.",
|
|
3761
|
-
input:
|
|
3889
|
+
input: import_zod31.z.object({ bundlePath }),
|
|
3762
3890
|
fromArgv: (argv, path) => ({ bundlePath: argv[1] ?? path }),
|
|
3763
3891
|
run: (_ctx, { bundlePath: path }) => unpinBase(process.cwd(), path)
|
|
3764
3892
|
});
|
|
3765
3893
|
|
|
3766
3894
|
// src/commands/validate.ts
|
|
3767
|
-
var
|
|
3895
|
+
var import_zod32 = require("zod");
|
|
3768
3896
|
var validateCommand = define({
|
|
3769
3897
|
name: "validate",
|
|
3770
3898
|
tool: "kb_validate",
|
|
3771
3899
|
usage: "validate",
|
|
3772
3900
|
description: "Check pointers no single record can see: supersession links that disagree between the two records, typed causal links, and assumptions that cite sources. Each finding carries a severity: errors fail the exit code, warnings do not.",
|
|
3773
|
-
input:
|
|
3901
|
+
input: import_zod32.z.object({ bundlePath }),
|
|
3774
3902
|
fromArgv: (_argv, path) => ({ bundlePath: path }),
|
|
3775
3903
|
run: async ({ store }, { bundlePath: path }) => validateBundle(await store.list(path)),
|
|
3776
3904
|
// Warnings never fail the exit code; every other severity does.
|
|
@@ -3780,16 +3908,16 @@ var validateCommand = define({
|
|
|
3780
3908
|
});
|
|
3781
3909
|
|
|
3782
3910
|
// src/commands/verify.ts
|
|
3783
|
-
var
|
|
3911
|
+
var import_zod33 = require("zod");
|
|
3784
3912
|
var verifyCommand = define({
|
|
3785
3913
|
name: "verify",
|
|
3786
3914
|
tool: "kb_verify",
|
|
3787
3915
|
usage: "verify <concept-id> --note <text>",
|
|
3788
3916
|
description: "Append a verified[] event: who checked, when, and what was found. Append-only. A record's own generator is refused unless the actor is `human:`-prefixed.",
|
|
3789
|
-
input:
|
|
3917
|
+
input: import_zod33.z.object({
|
|
3790
3918
|
bundlePath,
|
|
3791
3919
|
conceptId,
|
|
3792
|
-
note:
|
|
3920
|
+
note: import_zod33.z.string().refine((s) => s.trim().length > 0, {
|
|
3793
3921
|
message: "note must say what the check found"
|
|
3794
3922
|
})
|
|
3795
3923
|
}),
|
|
@@ -3809,15 +3937,15 @@ var verifyCommand = define({
|
|
|
3809
3937
|
});
|
|
3810
3938
|
|
|
3811
3939
|
// src/commands/write.ts
|
|
3812
|
-
var
|
|
3940
|
+
var import_zod34 = require("zod");
|
|
3813
3941
|
var writeCommand = define({
|
|
3814
3942
|
name: "write",
|
|
3815
3943
|
tool: "kb_write",
|
|
3816
3944
|
usage: "write <type> < record.json",
|
|
3817
3945
|
description: "Write one record. Search first \u2014 a duplicate concept id is rejected, not overwritten; kb_types lists each type's sections. An unsourced claim is an `assumption` with assumption: true, never a vague `fact`. Conflicting records get a `risk`, `open-question`, or superseding `decision`. Prefer a new short record over overloading one. Never delete; supersede.",
|
|
3818
|
-
input:
|
|
3946
|
+
input: import_zod34.z.object({
|
|
3819
3947
|
bundlePath,
|
|
3820
|
-
type:
|
|
3948
|
+
type: import_zod34.z.enum(KB_RECORD_TYPES),
|
|
3821
3949
|
input: composeInputSchema
|
|
3822
3950
|
}),
|
|
3823
3951
|
fromArgv: async (argv, path, stdin) => ({
|
|
@@ -3841,13 +3969,13 @@ var writeCommand = define({
|
|
|
3841
3969
|
});
|
|
3842
3970
|
|
|
3843
3971
|
// src/commands/write-decision.ts
|
|
3844
|
-
var
|
|
3972
|
+
var import_zod35 = require("zod");
|
|
3845
3973
|
var writeDecisionCommand = define({
|
|
3846
3974
|
name: "write-decision",
|
|
3847
3975
|
tool: "kb_write_decision",
|
|
3848
3976
|
usage: "write-decision < decision.json",
|
|
3849
3977
|
description: "Write a decision, with `alternative` (what was rejected and why) and `impact` as fields. Record one when a later reader would otherwise simplify the constraint away; skip when the diff already answers it. `sources` for material read, `anchors` for code, `relatedConceptIds` for records.",
|
|
3850
|
-
input:
|
|
3978
|
+
input: import_zod35.z.object({ bundlePath, input: decisionInputSchema }),
|
|
3851
3979
|
fromArgv: async (_argv, path, stdin) => ({
|
|
3852
3980
|
bundlePath: path,
|
|
3853
3981
|
input: JSON.parse(await stdin())
|
|
@@ -3887,6 +4015,7 @@ var KB_COMMANDS = [
|
|
|
3887
4015
|
listCommand,
|
|
3888
4016
|
readIndexCommand,
|
|
3889
4017
|
logCommand,
|
|
4018
|
+
stampCommand,
|
|
3890
4019
|
validateCommand,
|
|
3891
4020
|
doctorCommand,
|
|
3892
4021
|
schemaCommand,
|
|
@@ -3902,8 +4031,7 @@ var KB_COMMANDS_BY_NAME = new Map(
|
|
|
3902
4031
|
);
|
|
3903
4032
|
|
|
3904
4033
|
// src/kb-store.ts
|
|
3905
|
-
var
|
|
3906
|
-
var import_promises6 = require("fs/promises");
|
|
4034
|
+
var import_promises7 = require("fs/promises");
|
|
3907
4035
|
var import_node_path8 = require("path");
|
|
3908
4036
|
|
|
3909
4037
|
// src/markdown.ts
|
|
@@ -3931,8 +4059,40 @@ function parseMarkdownWithFrontmatter(text, schema) {
|
|
|
3931
4059
|
};
|
|
3932
4060
|
}
|
|
3933
4061
|
|
|
4062
|
+
// src/kb-stamp.ts
|
|
4063
|
+
var import_node_crypto2 = require("crypto");
|
|
4064
|
+
function sha256(contents) {
|
|
4065
|
+
return (0, import_node_crypto2.createHash)("sha256").update(contents).digest("hex");
|
|
4066
|
+
}
|
|
4067
|
+
function bundleStamp(records, superseded) {
|
|
4068
|
+
const entries = [
|
|
4069
|
+
...records.map((hit) => ({
|
|
4070
|
+
conceptId: hit.record.conceptId,
|
|
4071
|
+
digest: `current:${sha256(
|
|
4072
|
+
stringifyMarkdownWithFrontmatter(
|
|
4073
|
+
hit.record.body,
|
|
4074
|
+
hit.record.frontmatter
|
|
4075
|
+
)
|
|
4076
|
+
)}`
|
|
4077
|
+
})),
|
|
4078
|
+
...superseded.map((entry) => ({
|
|
4079
|
+
conceptId: entry.conceptId,
|
|
4080
|
+
digest: `superseded:${sha256(JSON.stringify(entry))}`
|
|
4081
|
+
}))
|
|
4082
|
+
].sort((a, b) => a.conceptId < b.conceptId ? -1 : 1);
|
|
4083
|
+
return {
|
|
4084
|
+
digest: sha256(
|
|
4085
|
+
entries.map((entry) => `${entry.conceptId}:${entry.digest}`).join("\n")
|
|
4086
|
+
),
|
|
4087
|
+
records: entries
|
|
4088
|
+
};
|
|
4089
|
+
}
|
|
4090
|
+
function bundleDigest(records, superseded) {
|
|
4091
|
+
return bundleStamp(records, superseded).digest;
|
|
4092
|
+
}
|
|
4093
|
+
|
|
3934
4094
|
// src/search-index.ts
|
|
3935
|
-
var
|
|
4095
|
+
var import_promises6 = require("fs/promises");
|
|
3936
4096
|
var import_node_path7 = require("path");
|
|
3937
4097
|
var SEARCH_INDEX_FILE = ".index.sqlite";
|
|
3938
4098
|
var COLLECTION = "kb";
|
|
@@ -3977,7 +4137,7 @@ async function searchBase(bundlePath2, query, options = {}) {
|
|
|
3977
4137
|
}
|
|
3978
4138
|
}
|
|
3979
4139
|
async function isStale(bundlePath2) {
|
|
3980
|
-
const indexAt = await (0,
|
|
4140
|
+
const indexAt = await (0, import_promises6.stat)((0, import_node_path7.join)(bundlePath2, SEARCH_INDEX_FILE)).then((s) => s.mtimeMs).catch(() => 0);
|
|
3981
4141
|
if (!indexAt) return true;
|
|
3982
4142
|
const { readdir: readdir2 } = await import("fs/promises");
|
|
3983
4143
|
const names = (await readdir2(bundlePath2).catch(() => [])).filter(
|
|
@@ -3986,7 +4146,7 @@ async function isStale(bundlePath2) {
|
|
|
3986
4146
|
let stale = false;
|
|
3987
4147
|
await mapLimit(names, DEFAULT_IO_CONCURRENCY, async (name) => {
|
|
3988
4148
|
if (stale) return;
|
|
3989
|
-
const at = await (0,
|
|
4149
|
+
const at = await (0, import_promises6.stat)((0, import_node_path7.join)(bundlePath2, name)).then((s) => s.mtimeMs).catch(() => 0);
|
|
3990
4150
|
if (at > indexAt) stale = true;
|
|
3991
4151
|
});
|
|
3992
4152
|
return stale;
|
|
@@ -4295,7 +4455,7 @@ var KbStore = class {
|
|
|
4295
4455
|
const conceptId2 = `${input.type}.${input.slug}`;
|
|
4296
4456
|
const root = this.root(bundlePath2);
|
|
4297
4457
|
const target = this.recordPath(bundlePath2, conceptId2);
|
|
4298
|
-
await (0,
|
|
4458
|
+
await (0, import_promises7.mkdir)(root, { recursive: true });
|
|
4299
4459
|
await this.publish(
|
|
4300
4460
|
target,
|
|
4301
4461
|
stringifyMarkdownWithFrontmatter(input.body, frontmatter),
|
|
@@ -4334,7 +4494,7 @@ var KbStore = class {
|
|
|
4334
4494
|
const target = this.recordPath(bundlePath2, conceptId2);
|
|
4335
4495
|
let raw;
|
|
4336
4496
|
try {
|
|
4337
|
-
raw = await (0,
|
|
4497
|
+
raw = await (0, import_promises7.readFile)(target, "utf8");
|
|
4338
4498
|
} catch {
|
|
4339
4499
|
return null;
|
|
4340
4500
|
}
|
|
@@ -4351,7 +4511,7 @@ var KbStore = class {
|
|
|
4351
4511
|
const root = this.root(bundlePath2);
|
|
4352
4512
|
let names;
|
|
4353
4513
|
try {
|
|
4354
|
-
names = await (0,
|
|
4514
|
+
names = await (0, import_promises7.readdir)(root);
|
|
4355
4515
|
} catch {
|
|
4356
4516
|
return [];
|
|
4357
4517
|
}
|
|
@@ -4359,7 +4519,7 @@ var KbStore = class {
|
|
|
4359
4519
|
const records = await mapLimit(
|
|
4360
4520
|
wanted,
|
|
4361
4521
|
DEFAULT_IO_CONCURRENCY,
|
|
4362
|
-
async ({ name, conceptId: conceptId2 }) => this.parse(conceptId2, await (0,
|
|
4522
|
+
async ({ name, conceptId: conceptId2 }) => this.parse(conceptId2, await (0, import_promises7.readFile)((0, import_node_path8.join)(root, name), "utf8"))
|
|
4363
4523
|
);
|
|
4364
4524
|
return records.filter((record) => record !== null);
|
|
4365
4525
|
}
|
|
@@ -4634,6 +4794,28 @@ ${answer}
|
|
|
4634
4794
|
digest: bundleDigestValue
|
|
4635
4795
|
};
|
|
4636
4796
|
}
|
|
4797
|
+
/**
|
|
4798
|
+
* `load`'s digest without `load`'s bodies — the same records, adjudicated
|
|
4799
|
+
* the same way, handed back as a stamp. Skips the anchor drift pass, which
|
|
4800
|
+
* reads source files and only ever adds warnings: no warning reaches the
|
|
4801
|
+
* digest, so the value is identical to the one `load` returns.
|
|
4802
|
+
*/
|
|
4803
|
+
async stamp(bundlePath2) {
|
|
4804
|
+
const bundle = await this.list(bundlePath2);
|
|
4805
|
+
const adjudicated = adjudicate(bundle, bundle, /* @__PURE__ */ new Date());
|
|
4806
|
+
const current = adjudicated.filter((hit) => hit.standing !== "superseded");
|
|
4807
|
+
const superseded = adjudicated.filter((hit) => hit.standing === "superseded").map(stub);
|
|
4808
|
+
const stamped = bundleStamp(current, superseded);
|
|
4809
|
+
const dates = bundle.map((record) => record.frontmatter.generated?.at ?? null).filter((at) => typeof at === "string").sort();
|
|
4810
|
+
return {
|
|
4811
|
+
path: bundlePath2,
|
|
4812
|
+
digest: stamped.digest,
|
|
4813
|
+
recordCount: bundle.length,
|
|
4814
|
+
superseded: superseded.length,
|
|
4815
|
+
newestAt: dates.at(-1) ?? null,
|
|
4816
|
+
records: stamped.records
|
|
4817
|
+
};
|
|
4818
|
+
}
|
|
4637
4819
|
/** How a position was arrived at, as a timeline. See `trace.ts`. */
|
|
4638
4820
|
async trace(bundlePath2, seedId, options = {}) {
|
|
4639
4821
|
return trace(seedId, await this.list(bundlePath2), options);
|
|
@@ -4664,7 +4846,7 @@ ${answer}
|
|
|
4664
4846
|
async readIndex(bundlePath2) {
|
|
4665
4847
|
const root = this.root(bundlePath2);
|
|
4666
4848
|
const expected = renderIndex(await this.list(bundlePath2));
|
|
4667
|
-
const stored = await (0,
|
|
4849
|
+
const stored = await (0, import_promises7.readFile)((0, import_node_path8.join)(root, INDEX_FILE), "utf8").catch(
|
|
4668
4850
|
() => null
|
|
4669
4851
|
);
|
|
4670
4852
|
if (indexIsStale(stored, expected)) {
|
|
@@ -4685,7 +4867,7 @@ ${answer}
|
|
|
4685
4867
|
* knows which agent touched what. So a bad line is surfaced and left alone.
|
|
4686
4868
|
*/
|
|
4687
4869
|
async readLog(bundlePath2) {
|
|
4688
|
-
const raw = await (0,
|
|
4870
|
+
const raw = await (0, import_promises7.readFile)(
|
|
4689
4871
|
(0, import_node_path8.join)(this.root(bundlePath2), LOG_FILE),
|
|
4690
4872
|
"utf8"
|
|
4691
4873
|
).catch(() => "");
|
|
@@ -4737,15 +4919,15 @@ ${answer}
|
|
|
4737
4919
|
}
|
|
4738
4920
|
async mutate(bundlePath2, conceptId2, change, entry, changeBody = (body) => body) {
|
|
4739
4921
|
const target = this.recordPath(bundlePath2, conceptId2);
|
|
4740
|
-
const before = await (0,
|
|
4922
|
+
const before = await (0, import_promises7.readFile)(target, "utf8").catch(() => null);
|
|
4741
4923
|
if (before === null) throw new KbRecordNotFoundError(conceptId2);
|
|
4742
4924
|
const parsed = this.parse(conceptId2, before);
|
|
4743
4925
|
if (!parsed) throw new KbRecordNotFoundError(conceptId2);
|
|
4744
4926
|
const frontmatter = change(parsed.frontmatter);
|
|
4745
4927
|
const body = changeBody(parsed.body);
|
|
4746
4928
|
const contents = stringifyMarkdownWithFrontmatter(body, frontmatter);
|
|
4747
|
-
const witness = await (0,
|
|
4748
|
-
if (witness === null ||
|
|
4929
|
+
const witness = await (0, import_promises7.readFile)(target, "utf8").catch(() => null);
|
|
4930
|
+
if (witness === null || sha256(witness) !== sha256(before)) {
|
|
4749
4931
|
throw new KbWriteConflictError(conceptId2);
|
|
4750
4932
|
}
|
|
4751
4933
|
await this.publish(target, contents, true, conceptId2);
|
|
@@ -4770,20 +4952,20 @@ ${answer}
|
|
|
4770
4952
|
*/
|
|
4771
4953
|
async publish(target, contents, overwrite, conceptId2) {
|
|
4772
4954
|
const staging = `${target}.${process.pid}.tmp`;
|
|
4773
|
-
await (0,
|
|
4955
|
+
await (0, import_promises7.writeFile)(staging, contents, "utf8");
|
|
4774
4956
|
try {
|
|
4775
4957
|
if (overwrite) {
|
|
4776
|
-
await (0,
|
|
4958
|
+
await (0, import_promises7.rename)(staging, target);
|
|
4777
4959
|
return;
|
|
4778
4960
|
}
|
|
4779
|
-
await (0,
|
|
4961
|
+
await (0, import_promises7.link)(staging, target);
|
|
4780
4962
|
} catch (error) {
|
|
4781
4963
|
if (error.code === "EEXIST") {
|
|
4782
4964
|
throw new KbRecordAlreadyExistsError(conceptId2);
|
|
4783
4965
|
}
|
|
4784
4966
|
throw error;
|
|
4785
4967
|
} finally {
|
|
4786
|
-
await (0,
|
|
4968
|
+
await (0, import_promises7.unlink)(staging).catch(() => void 0);
|
|
4787
4969
|
}
|
|
4788
4970
|
}
|
|
4789
4971
|
/**
|
|
@@ -4831,14 +5013,14 @@ ${answer}
|
|
|
4831
5013
|
try {
|
|
4832
5014
|
let existing;
|
|
4833
5015
|
try {
|
|
4834
|
-
existing = await (0,
|
|
5016
|
+
existing = await (0, import_promises7.readFile)(target, "utf8");
|
|
4835
5017
|
} catch (error) {
|
|
4836
5018
|
if (error.code !== "ENOENT") throw error;
|
|
4837
5019
|
existing = null;
|
|
4838
5020
|
}
|
|
4839
5021
|
if (existing === null) {
|
|
4840
5022
|
try {
|
|
4841
|
-
await (0,
|
|
5023
|
+
await (0, import_promises7.writeFile)(target, appendUnionMergeLine(""), {
|
|
4842
5024
|
encoding: "utf8",
|
|
4843
5025
|
flag: "wx"
|
|
4844
5026
|
});
|
|
@@ -4859,7 +5041,7 @@ ${answer}
|
|
|
4859
5041
|
return;
|
|
4860
5042
|
}
|
|
4861
5043
|
if (!hasMergeDeclaration(existing)) {
|
|
4862
|
-
await (0,
|
|
5044
|
+
await (0, import_promises7.appendFile)(target, appendUnionMergeLine(existing), "utf8");
|
|
4863
5045
|
this.logger.info?.({
|
|
4864
5046
|
operation: "kb.gitattributes.ensure",
|
|
4865
5047
|
bundlePath: root,
|
|
@@ -4878,7 +5060,7 @@ ${answer}
|
|
|
4878
5060
|
async record(root, entry) {
|
|
4879
5061
|
await this.ensureGitattributes(root);
|
|
4880
5062
|
const line = renderLogEntry({ at: (/* @__PURE__ */ new Date()).toISOString(), ...entry });
|
|
4881
|
-
await (0,
|
|
5063
|
+
await (0, import_promises7.appendFile)((0, import_node_path8.join)(root, LOG_FILE), line, "utf8").catch((error) => {
|
|
4882
5064
|
this.logger.warn?.({
|
|
4883
5065
|
operation: "kb.log.append",
|
|
4884
5066
|
outcome: "failed",
|
|
@@ -4953,28 +5135,9 @@ function normalizeActor(id) {
|
|
|
4953
5135
|
if (colon === -1) return id.toLowerCase();
|
|
4954
5136
|
return id.slice(0, colon + 1).toLowerCase() + id.slice(colon + 1);
|
|
4955
5137
|
}
|
|
4956
|
-
function digest(contents) {
|
|
4957
|
-
return (0, import_node_crypto2.createHash)("sha256").update(contents).digest("hex");
|
|
4958
|
-
}
|
|
4959
|
-
function bundleDigest(records, superseded) {
|
|
4960
|
-
const entries = [
|
|
4961
|
-
...records.map(
|
|
4962
|
-
(hit) => `${hit.record.conceptId}:current:${digest(
|
|
4963
|
-
stringifyMarkdownWithFrontmatter(
|
|
4964
|
-
hit.record.body,
|
|
4965
|
-
hit.record.frontmatter
|
|
4966
|
-
)
|
|
4967
|
-
)}`
|
|
4968
|
-
),
|
|
4969
|
-
...superseded.map(
|
|
4970
|
-
(entry) => `${entry.conceptId}:superseded:${digest(JSON.stringify(entry))}`
|
|
4971
|
-
)
|
|
4972
|
-
].sort();
|
|
4973
|
-
return digest(entries.join("\n"));
|
|
4974
|
-
}
|
|
4975
5138
|
|
|
4976
5139
|
// src/version.ts
|
|
4977
|
-
var VERSION = true ? "0.1.
|
|
5140
|
+
var VERSION = true ? "0.1.16" : "0.0.0-dev";
|
|
4978
5141
|
|
|
4979
5142
|
// src/mcp.ts
|
|
4980
5143
|
function createKbMcpServer() {
|