@saasontools/strauss-kb 0.1.20 → 0.1.22
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 +15 -8
- package/dist/{chunk-QQLPJO4R.js → chunk-AEO5U42S.js} +2407 -2131
- package/dist/chunk-AEO5U42S.js.map +1 -0
- package/dist/{chunk-XDH6J6CH.js → chunk-DVSPHL4B.js} +2 -2
- package/dist/{chunk-EXZQZJU2.js → chunk-TSE4YQEG.js} +2 -2
- package/dist/cli-main.cjs +1386 -1120
- package/dist/cli-main.cjs.map +1 -1
- package/dist/cli-main.js +2 -2
- package/dist/index.cjs +497 -212
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +295 -17
- package/dist/index.d.ts +295 -17
- package/dist/index.js +17 -3
- package/dist/mcp-main.cjs +1386 -1120
- package/dist/mcp-main.cjs.map +1 -1
- package/dist/mcp-main.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-QQLPJO4R.js.map +0 -1
- /package/dist/{chunk-XDH6J6CH.js.map → chunk-DVSPHL4B.js.map} +0 -0
- /package/dist/{chunk-EXZQZJU2.js.map → chunk-TSE4YQEG.js.map} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -60,6 +60,7 @@ __export(index_exports, {
|
|
|
60
60
|
KB_RECORD_STATUSES: () => KB_RECORD_STATUSES,
|
|
61
61
|
KB_RECORD_TYPES: () => KB_RECORD_TYPES,
|
|
62
62
|
KB_SLUG_PATTERN: () => KB_SLUG_PATTERN,
|
|
63
|
+
KbAnchorSetDuplicateError: () => KbAnchorSetDuplicateError,
|
|
63
64
|
KbBaseFrozenError: () => KbBaseFrozenError,
|
|
64
65
|
KbClassifyInputError: () => KbClassifyInputError,
|
|
65
66
|
KbInvalidConceptIdError: () => KbInvalidConceptIdError,
|
|
@@ -90,6 +91,8 @@ __export(index_exports, {
|
|
|
90
91
|
adjudicate: () => adjudicate,
|
|
91
92
|
anchorFilePath: () => anchorFilePath,
|
|
92
93
|
anchorOnHunk: () => anchorOnHunk,
|
|
94
|
+
anchorSetInputSchema: () => anchorSetInputSchema,
|
|
95
|
+
applyAnchorSet: () => applyAnchorSet,
|
|
93
96
|
assertBaseNotFrozen: () => assertBaseNotFrozen,
|
|
94
97
|
backlinks: () => backlinks,
|
|
95
98
|
buildContext: () => buildContext,
|
|
@@ -123,19 +126,23 @@ __export(index_exports, {
|
|
|
123
126
|
isNoDecisionRecord: () => isNoDecisionRecord,
|
|
124
127
|
isReviewTag: () => isReviewTag,
|
|
125
128
|
kbActorStampSchema: () => kbActorStampSchema,
|
|
129
|
+
kbAnchorLocatorSchema: () => kbAnchorLocatorSchema,
|
|
126
130
|
kbAnchorSchema: () => kbAnchorSchema,
|
|
127
131
|
kbAnchorSpanSchema: () => kbAnchorSpanSchema,
|
|
128
132
|
kbAnchorWriteSchema: () => kbAnchorWriteSchema,
|
|
129
133
|
kbConceptIdSchema: () => kbConceptIdSchema,
|
|
130
134
|
kbJsonSchemas: () => kbJsonSchemas,
|
|
131
135
|
kbLinkSchema: () => kbLinkSchema,
|
|
136
|
+
kbLogAnchorChangeSchema: () => kbLogAnchorChangeSchema,
|
|
132
137
|
kbLogEntrySchema: () => kbLogEntrySchema,
|
|
138
|
+
kbLogEntryWriteSchema: () => kbLogEntryWriteSchema,
|
|
133
139
|
kbRecordFrontmatterSchema: () => kbRecordFrontmatterSchema,
|
|
134
140
|
kbSourceSchema: () => kbSourceSchema,
|
|
135
141
|
kbVerifiedEventSchema: () => kbVerifiedEventSchema,
|
|
136
142
|
languageForFile: () => languageForFile,
|
|
137
143
|
listPins: () => listPins,
|
|
138
144
|
loadQmd: () => loadQmd,
|
|
145
|
+
locatorOf: () => locatorOf,
|
|
139
146
|
matchToDiff: () => matchToDiff,
|
|
140
147
|
matchesTags: () => matchesTags,
|
|
141
148
|
mergedContextBudgets: () => mergedContextBudgets,
|
|
@@ -347,6 +354,14 @@ var kbAnchorWriteSchema = kbAnchorSchema.superRefine((anchor, ctx) => {
|
|
|
347
354
|
});
|
|
348
355
|
}
|
|
349
356
|
});
|
|
357
|
+
var kbAnchorLocatorSchema = kbAnchorSchema.pick({
|
|
358
|
+
file: true,
|
|
359
|
+
symbol: true,
|
|
360
|
+
span: true,
|
|
361
|
+
side: true,
|
|
362
|
+
repo: true,
|
|
363
|
+
ref: true
|
|
364
|
+
});
|
|
350
365
|
var kbLinkSchema = import_zod.z.object({
|
|
351
366
|
target: import_zod.z.string().min(1),
|
|
352
367
|
rel: import_zod.z.string().min(1)
|
|
@@ -433,7 +448,10 @@ var Fault = /* @__PURE__ */ ((Fault2) => {
|
|
|
433
448
|
})(Fault || {});
|
|
434
449
|
var ErrorTypes = /* @__PURE__ */ ((ErrorTypes2) => {
|
|
435
450
|
ErrorTypes2["KbRecordAlreadyExists"] = "KbRecordAlreadyExists";
|
|
451
|
+
ErrorTypes2["KbAnchorSetDuplicate"] = "KbAnchorSetDuplicate";
|
|
436
452
|
ErrorTypes2["KbClassifyInput"] = "KbClassifyInput";
|
|
453
|
+
ErrorTypes2["KbFlagConflict"] = "KbFlagConflict";
|
|
454
|
+
ErrorTypes2["KbInvalidActor"] = "KbInvalidActor";
|
|
437
455
|
ErrorTypes2["KbInvalidConceptId"] = "KbInvalidConceptId";
|
|
438
456
|
ErrorTypes2["KbMatchInput"] = "KbMatchInput";
|
|
439
457
|
ErrorTypes2["KbMissingFlagValue"] = "KbMissingFlagValue";
|
|
@@ -534,6 +552,38 @@ var KbSelfVerificationError = class extends BaseError {
|
|
|
534
552
|
actor;
|
|
535
553
|
generatedBy;
|
|
536
554
|
};
|
|
555
|
+
var KbInvalidActorError = class extends BaseError {
|
|
556
|
+
constructor(actor, reason) {
|
|
557
|
+
super({
|
|
558
|
+
message: `kb: actor ${JSON.stringify(actor)} ${reason} \u2014 set STRAUSS_KB_ACTOR, e.g. human:alice or agent:reviewer`,
|
|
559
|
+
errorType: "KbInvalidActor" /* KbInvalidActor */,
|
|
560
|
+
code: 400,
|
|
561
|
+
fault: "User" /* User */,
|
|
562
|
+
retriable: false,
|
|
563
|
+
reportToUser: true,
|
|
564
|
+
details: { actor, reason, action: "refused" }
|
|
565
|
+
});
|
|
566
|
+
this.actor = actor;
|
|
567
|
+
this.reason = reason;
|
|
568
|
+
}
|
|
569
|
+
actor;
|
|
570
|
+
reason;
|
|
571
|
+
};
|
|
572
|
+
var KbFlagConflictError = class extends BaseError {
|
|
573
|
+
constructor(flags) {
|
|
574
|
+
super({
|
|
575
|
+
message: `kb: ${flags.join(" and ")} cannot be combined`,
|
|
576
|
+
errorType: "KbFlagConflict" /* KbFlagConflict */,
|
|
577
|
+
code: 400,
|
|
578
|
+
fault: "User" /* User */,
|
|
579
|
+
retriable: false,
|
|
580
|
+
reportToUser: true,
|
|
581
|
+
details: { flags }
|
|
582
|
+
});
|
|
583
|
+
this.flags = flags;
|
|
584
|
+
}
|
|
585
|
+
flags;
|
|
586
|
+
};
|
|
537
587
|
var KbPackBudgetExceededError = class extends BaseError {
|
|
538
588
|
constructor(recordCount, approxTokens2, budgetTokens, excluded) {
|
|
539
589
|
super({
|
|
@@ -2619,7 +2669,12 @@ var import_node_path6 = require("path");
|
|
|
2619
2669
|
// src/kb-log.ts
|
|
2620
2670
|
var import_zod3 = require("zod");
|
|
2621
2671
|
var LOG_FILE = "log.jsonl";
|
|
2622
|
-
var
|
|
2672
|
+
var kbLogAnchorChangeSchema = import_zod3.z.object({
|
|
2673
|
+
op: import_zod3.z.enum(["move", "add", "drop"]),
|
|
2674
|
+
from: kbAnchorLocatorSchema.optional(),
|
|
2675
|
+
to: kbAnchorLocatorSchema.optional()
|
|
2676
|
+
}).strict();
|
|
2677
|
+
var kbLogEntryFields = import_zod3.z.object({
|
|
2623
2678
|
// Validated, not just `min(1)`: `at` is a sort key (see `parseLog`
|
|
2624
2679
|
// below), and a value that isn't actually chronological — a Unix
|
|
2625
2680
|
// timestamp, a human-typed date, garbage — would sort wrong without
|
|
@@ -2636,10 +2691,20 @@ var kbLogEntrySchema = import_zod3.z.object({
|
|
|
2636
2691
|
* The operation's other end, where it has one: a second concept id for
|
|
2637
2692
|
* supersession, the other base's path for promotion.
|
|
2638
2693
|
*/
|
|
2639
|
-
target: import_zod3.z.string().min(1).optional()
|
|
2640
|
-
|
|
2694
|
+
target: import_zod3.z.string().min(1).optional(),
|
|
2695
|
+
/**
|
|
2696
|
+
* Why the operation was performed, where the operation demands one.
|
|
2697
|
+
* `anchor-set` does: a pointer moved by a reader is only auditable if
|
|
2698
|
+
* the reading is recorded beside it.
|
|
2699
|
+
*/
|
|
2700
|
+
reason: import_zod3.z.string().min(1).optional(),
|
|
2701
|
+
/** What `anchor-set` changed, derived from the record before and after. */
|
|
2702
|
+
anchors: import_zod3.z.array(kbLogAnchorChangeSchema).optional()
|
|
2703
|
+
});
|
|
2704
|
+
var kbLogEntrySchema = kbLogEntryFields.passthrough();
|
|
2705
|
+
var kbLogEntryWriteSchema = kbLogEntryFields.strict();
|
|
2641
2706
|
function renderLogEntry(entry) {
|
|
2642
|
-
return `${JSON.stringify(
|
|
2707
|
+
return `${JSON.stringify(kbLogEntryWriteSchema.parse(entry))}
|
|
2643
2708
|
`;
|
|
2644
2709
|
}
|
|
2645
2710
|
var CONFLICT_MARKER = /^(<{7}|\|{7}|={7}|>{7})/;
|
|
@@ -3361,6 +3426,7 @@ var KbStore = class {
|
|
|
3361
3426
|
* its contents.
|
|
3362
3427
|
*/
|
|
3363
3428
|
async write(bundlePath2, input, actor = "unknown") {
|
|
3429
|
+
assertActor(actor);
|
|
3364
3430
|
if (!KB_SLUG_PATTERN.test(input.slug)) {
|
|
3365
3431
|
throw new KbInvalidConceptIdError("slug must be kebab-case", {
|
|
3366
3432
|
slug: input.slug
|
|
@@ -3462,6 +3528,7 @@ var KbStore = class {
|
|
|
3462
3528
|
* timeouts.
|
|
3463
3529
|
*/
|
|
3464
3530
|
async setStatus(bundlePath2, conceptId2, status, actor = "unknown") {
|
|
3531
|
+
assertActor(actor);
|
|
3465
3532
|
return this.mutate(
|
|
3466
3533
|
bundlePath2,
|
|
3467
3534
|
conceptId2,
|
|
@@ -3470,22 +3537,31 @@ var KbStore = class {
|
|
|
3470
3537
|
);
|
|
3471
3538
|
}
|
|
3472
3539
|
/**
|
|
3473
|
-
* Replaces a record's anchors
|
|
3474
|
-
*
|
|
3475
|
-
*
|
|
3476
|
-
*
|
|
3477
|
-
* stale entries the resolution pass deliberately dropped.
|
|
3478
|
-
*
|
|
3479
|
-
* Through the write schema: this is a write, and a defect a hand-edit put in
|
|
3480
|
-
* the frontmatter must not be published back out under an actor stamp.
|
|
3540
|
+
* Replaces a record's anchors, preserving everything else. An array is the
|
|
3541
|
+
* whole set; a function is a patch and runs inside the mutation, against
|
|
3542
|
+
* the anchors the record holds then — see
|
|
3543
|
+
* `decision.anchor-update-patch-inside-mutation`.
|
|
3481
3544
|
*/
|
|
3482
3545
|
async updateAnchors(bundlePath2, conceptId2, anchors, actor = "unknown") {
|
|
3483
|
-
|
|
3546
|
+
assertActor(actor);
|
|
3547
|
+
let entry = {
|
|
3548
|
+
operation: "anchor-resolve",
|
|
3549
|
+
by: actor
|
|
3550
|
+
};
|
|
3484
3551
|
return this.mutate(
|
|
3485
3552
|
bundlePath2,
|
|
3486
3553
|
conceptId2,
|
|
3487
|
-
(frontmatter) =>
|
|
3488
|
-
|
|
3554
|
+
(frontmatter) => {
|
|
3555
|
+
const write = typeof anchors === "function" ? anchors(frontmatter.strauss_anchors ?? []) : { anchors };
|
|
3556
|
+
if (write.log) entry = { ...write.log, by: actor };
|
|
3557
|
+
return {
|
|
3558
|
+
...frontmatter,
|
|
3559
|
+
strauss_anchors: write.anchors.map(
|
|
3560
|
+
(anchor) => kbAnchorWriteSchema.parse(anchor)
|
|
3561
|
+
)
|
|
3562
|
+
};
|
|
3563
|
+
},
|
|
3564
|
+
() => entry
|
|
3489
3565
|
);
|
|
3490
3566
|
}
|
|
3491
3567
|
/**
|
|
@@ -3500,6 +3576,7 @@ var KbStore = class {
|
|
|
3500
3576
|
* logs what it publishes.
|
|
3501
3577
|
*/
|
|
3502
3578
|
async verify(bundlePath2, conceptId2, note, actor = "unknown", at2 = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
3579
|
+
assertActor(actor, { named: true });
|
|
3503
3580
|
const event = kbVerifiedEventSchema.parse({ by: actor, at: at2, note });
|
|
3504
3581
|
const existing = await this.read(bundlePath2, conceptId2);
|
|
3505
3582
|
if (!existing) throw new KbRecordNotFoundError(conceptId2);
|
|
@@ -3530,6 +3607,7 @@ var KbStore = class {
|
|
|
3530
3607
|
* in normal use, and validation drops to catching hand-edits.
|
|
3531
3608
|
*/
|
|
3532
3609
|
async supersede(bundlePath2, conceptId2, replacementId, actor = "unknown") {
|
|
3610
|
+
assertActor(actor);
|
|
3533
3611
|
const replacement = await this.read(bundlePath2, replacementId);
|
|
3534
3612
|
if (!replacement) throw new KbRecordNotFoundError(replacementId);
|
|
3535
3613
|
const superseded = await this.markSuperseded(
|
|
@@ -3553,6 +3631,7 @@ var KbStore = class {
|
|
|
3553
3631
|
}
|
|
3554
3632
|
/** Resolves an open question, stamping who answered and when. */
|
|
3555
3633
|
async answer(bundlePath2, conceptId2, answer, actor = "unknown", at2 = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
3634
|
+
assertActor(actor);
|
|
3556
3635
|
return this.mutate(
|
|
3557
3636
|
bundlePath2,
|
|
3558
3637
|
conceptId2,
|
|
@@ -3579,6 +3658,7 @@ ${answer}
|
|
|
3579
3658
|
* terminal status since the caller listed it is reported, not removed.
|
|
3580
3659
|
*/
|
|
3581
3660
|
async deleteRecord(bundlePath2, conceptId2, expected, actor = "unknown") {
|
|
3661
|
+
assertActor(actor);
|
|
3582
3662
|
const target = this.recordPath(bundlePath2, conceptId2);
|
|
3583
3663
|
const witness = await this.read(bundlePath2, conceptId2);
|
|
3584
3664
|
if (!witness) throw new KbRecordNotFoundError(conceptId2);
|
|
@@ -3882,6 +3962,7 @@ ${answer}
|
|
|
3882
3962
|
* base too, where nothing was written.
|
|
3883
3963
|
*/
|
|
3884
3964
|
async note(bundlePath2, entry) {
|
|
3965
|
+
assertActor(entry.by);
|
|
3885
3966
|
await this.record(this.root(bundlePath2), entry);
|
|
3886
3967
|
}
|
|
3887
3968
|
/**
|
|
@@ -3934,7 +4015,10 @@ ${answer}
|
|
|
3934
4015
|
throw new KbWriteConflictError(conceptId2);
|
|
3935
4016
|
}
|
|
3936
4017
|
await this.publish(target, contents, true, conceptId2);
|
|
3937
|
-
await this.record(this.root(bundlePath2), {
|
|
4018
|
+
await this.record(this.root(bundlePath2), {
|
|
4019
|
+
...typeof entry === "function" ? entry() : entry,
|
|
4020
|
+
conceptId: conceptId2
|
|
4021
|
+
});
|
|
3938
4022
|
return { conceptId: conceptId2, frontmatter, body };
|
|
3939
4023
|
}
|
|
3940
4024
|
/**
|
|
@@ -4139,9 +4223,119 @@ function normalizeActor(id) {
|
|
|
4139
4223
|
if (colon === -1) return id.toLowerCase();
|
|
4140
4224
|
return id.slice(0, colon + 1).toLowerCase() + id.slice(colon + 1);
|
|
4141
4225
|
}
|
|
4226
|
+
var KB_ACTOR_PATTERN = /^[A-Za-z][\w-]*(?::[\p{L}\p{M}\p{N}_.@+/-]+)?$/u;
|
|
4227
|
+
function assertActor(actor, { named = false } = {}) {
|
|
4228
|
+
if (!KB_ACTOR_PATTERN.test(actor)) {
|
|
4229
|
+
throw new KbInvalidActorError(actor, "is not kind or kind:name");
|
|
4230
|
+
}
|
|
4231
|
+
if (named && normalizeActor(actor) === "unknown") {
|
|
4232
|
+
throw new KbInvalidActorError(actor, "cannot verify: name who checked");
|
|
4233
|
+
}
|
|
4234
|
+
}
|
|
4142
4235
|
|
|
4143
4236
|
// src/compose.ts
|
|
4144
4237
|
var import_zod4 = require("zod");
|
|
4238
|
+
|
|
4239
|
+
// src/anchors/errors.ts
|
|
4240
|
+
function locatorText(locator) {
|
|
4241
|
+
const span2 = locator.span ? `:${locator.span.start}-${locator.span.end}` : "";
|
|
4242
|
+
const symbol = locator.symbol ? `:${cap(locator.symbol)}` : "";
|
|
4243
|
+
const repo = locator.repo ? `${cap(locator.repo)}@` : "";
|
|
4244
|
+
const ref = locator.ref ? `@${cap(locator.ref)}` : "";
|
|
4245
|
+
return `${repo}${cap(locator.file)}${symbol}${span2}${ref}`;
|
|
4246
|
+
}
|
|
4247
|
+
var FIELD_CAP = 120;
|
|
4248
|
+
function cap(value) {
|
|
4249
|
+
return value.length > FIELD_CAP ? `${value.slice(0, FIELD_CAP - 1)}\u2026` : value;
|
|
4250
|
+
}
|
|
4251
|
+
var KbAnchorSetDuplicateError = class extends BaseError {
|
|
4252
|
+
constructor(locator) {
|
|
4253
|
+
super({
|
|
4254
|
+
message: `kb: ${locator} appears twice in this set \u2014 a record holds each pointer once`,
|
|
4255
|
+
errorType: "KbAnchorSetDuplicate" /* KbAnchorSetDuplicate */,
|
|
4256
|
+
code: 400,
|
|
4257
|
+
fault: "User" /* User */,
|
|
4258
|
+
retriable: false,
|
|
4259
|
+
reportToUser: true,
|
|
4260
|
+
details: { locator, action: "refused" }
|
|
4261
|
+
});
|
|
4262
|
+
this.locator = locator;
|
|
4263
|
+
}
|
|
4264
|
+
locator;
|
|
4265
|
+
};
|
|
4266
|
+
|
|
4267
|
+
// src/anchors/apply.ts
|
|
4268
|
+
var LOCATOR_FIELDS = [
|
|
4269
|
+
"file",
|
|
4270
|
+
"symbol",
|
|
4271
|
+
"span",
|
|
4272
|
+
"side",
|
|
4273
|
+
"repo",
|
|
4274
|
+
"ref"
|
|
4275
|
+
];
|
|
4276
|
+
function applyAnchorSet(current, incoming) {
|
|
4277
|
+
const anchors = incoming.map((anchor) => ({ ...anchor }));
|
|
4278
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4279
|
+
for (const anchor of anchors) {
|
|
4280
|
+
const key2 = locatorKey(anchor);
|
|
4281
|
+
if (seen.has(key2)) {
|
|
4282
|
+
throw new KbAnchorSetDuplicateError(locatorText(locatorOf(anchor)));
|
|
4283
|
+
}
|
|
4284
|
+
seen.add(key2);
|
|
4285
|
+
}
|
|
4286
|
+
return { anchors, changes: diff(current, anchors) };
|
|
4287
|
+
}
|
|
4288
|
+
function diff(current, next) {
|
|
4289
|
+
const before = new Map(
|
|
4290
|
+
current.filter((anchor) => anchor.hash).map((a) => [a.hash, a])
|
|
4291
|
+
);
|
|
4292
|
+
const beforeLocators = new Map(current.map((a) => [locatorKey(a), a]));
|
|
4293
|
+
const afterLocators = new Set(next.map((anchor) => locatorKey(anchor)));
|
|
4294
|
+
const moved = /* @__PURE__ */ new Set();
|
|
4295
|
+
const changes = [];
|
|
4296
|
+
for (const anchor of next) {
|
|
4297
|
+
const source = anchor.hash ? before.get(anchor.hash) : void 0;
|
|
4298
|
+
if (source) {
|
|
4299
|
+
if (locatorKey(source) === locatorKey(anchor)) continue;
|
|
4300
|
+
moved.add(locatorKey(source));
|
|
4301
|
+
changes.push({
|
|
4302
|
+
op: "move",
|
|
4303
|
+
from: locatorOf(source),
|
|
4304
|
+
to: locatorOf(anchor)
|
|
4305
|
+
});
|
|
4306
|
+
continue;
|
|
4307
|
+
}
|
|
4308
|
+
if (beforeLocators.has(locatorKey(anchor))) continue;
|
|
4309
|
+
changes.push({ op: "add", to: locatorOf(anchor) });
|
|
4310
|
+
}
|
|
4311
|
+
for (const anchor of current) {
|
|
4312
|
+
const key2 = locatorKey(anchor);
|
|
4313
|
+
if (afterLocators.has(key2) || moved.has(key2)) continue;
|
|
4314
|
+
changes.push({ op: "drop", from: locatorOf(anchor) });
|
|
4315
|
+
}
|
|
4316
|
+
return changes;
|
|
4317
|
+
}
|
|
4318
|
+
function locatorOf(anchor) {
|
|
4319
|
+
return kbAnchorLocatorSchema.parse(
|
|
4320
|
+
Object.fromEntries(
|
|
4321
|
+
LOCATOR_FIELDS.flatMap(
|
|
4322
|
+
(field) => anchor[field] === void 0 ? [] : [[field, anchor[field]]]
|
|
4323
|
+
)
|
|
4324
|
+
)
|
|
4325
|
+
);
|
|
4326
|
+
}
|
|
4327
|
+
function locatorKey(anchor) {
|
|
4328
|
+
return JSON.stringify([
|
|
4329
|
+
anchor.file,
|
|
4330
|
+
anchor.symbol ?? "",
|
|
4331
|
+
anchor.span ? `${anchor.span.start}-${anchor.span.end}` : "",
|
|
4332
|
+
anchor.side ?? "new",
|
|
4333
|
+
anchor.repo === void 0 ? "" : normalizeRepoUrl(anchor.repo),
|
|
4334
|
+
anchor.ref ?? ""
|
|
4335
|
+
]);
|
|
4336
|
+
}
|
|
4337
|
+
|
|
4338
|
+
// src/compose.ts
|
|
4145
4339
|
var composeLinkSchema = import_zod4.z.object({
|
|
4146
4340
|
target: kbConceptIdSchema,
|
|
4147
4341
|
rel: import_zod4.z.enum(KB_LINK_RELS)
|
|
@@ -4213,7 +4407,9 @@ function composeRecord(type, input, writtenBy, writtenAt) {
|
|
|
4213
4407
|
strauss_status: spec.initialStatus
|
|
4214
4408
|
};
|
|
4215
4409
|
if (parsed.stale_after) frontmatter.stale_after = parsed.stale_after;
|
|
4216
|
-
if (parsed.anchors?.length)
|
|
4410
|
+
if (parsed.anchors?.length) {
|
|
4411
|
+
frontmatter.strauss_anchors = applyAnchorSet([], parsed.anchors).anchors;
|
|
4412
|
+
}
|
|
4217
4413
|
if (parsed.verify?.length) frontmatter.strauss_verify = parsed.verify;
|
|
4218
4414
|
if (parsed.tags?.length) frontmatter.tags = parsed.tags;
|
|
4219
4415
|
if (parsed.sources?.length) frontmatter.sources = parsed.sources;
|
|
@@ -5639,19 +5835,11 @@ function argvPositional(argv, ...names) {
|
|
|
5639
5835
|
}
|
|
5640
5836
|
|
|
5641
5837
|
// src/commands/anchor-resolve.ts
|
|
5642
|
-
function resolverSummary(results) {
|
|
5643
|
-
const names = [
|
|
5644
|
-
...new Set(
|
|
5645
|
-
results.flatMap((entry) => entry.resolver ? [entry.resolver] : [])
|
|
5646
|
-
)
|
|
5647
|
-
].sort();
|
|
5648
|
-
return names.length ? `${names.join(" + ")} resolver` : "whole-file";
|
|
5649
|
-
}
|
|
5650
5838
|
var anchorResolveCommand = define({
|
|
5651
5839
|
name: "anchor-resolve",
|
|
5652
5840
|
tool: "kb_anchor_resolve",
|
|
5653
|
-
usage: "anchor-resolve <concept-id> [--repo-root <path>] [--offline] [--rebaseline] [--restamp]",
|
|
5654
|
-
description: "Resolve a record's anchors: stamp a hash onto anchors that lack one, report drift where the code moved. An anchor naming another repository is read from that remote through a bare cache; --offline uses the cache only.
|
|
5841
|
+
usage: "anchor-resolve <concept-id> [--repo-root <path>] [--offline] [--rebaseline] [--restamp] [--check]",
|
|
5842
|
+
description: "Resolve a record's anchors: stamp a hash onto anchors that lack one, report drift where the code moved. An anchor naming another repository is read from that remote through a bare cache; --offline uses the cache only. Never writes verified[]; a judgment is kb_verify. Exits non-zero on drift.",
|
|
5655
5843
|
input: import_zod9.z.object({
|
|
5656
5844
|
bundlePath,
|
|
5657
5845
|
conceptId,
|
|
@@ -5664,6 +5852,9 @@ var anchorResolveCommand = define({
|
|
|
5664
5852
|
),
|
|
5665
5853
|
restamp: import_zod9.z.boolean().optional().describe(
|
|
5666
5854
|
"Refresh `resolved_at` on anchors that already match. Off by default, so a green run writes nothing."
|
|
5855
|
+
),
|
|
5856
|
+
check: import_zod9.z.boolean().optional().describe(
|
|
5857
|
+
"Resolve and report only: no hash, no `resolved_at`, no log entry."
|
|
5667
5858
|
)
|
|
5668
5859
|
}),
|
|
5669
5860
|
fromArgv: (argv, path) => ({
|
|
@@ -5672,9 +5863,24 @@ var anchorResolveCommand = define({
|
|
|
5672
5863
|
repoRoot: argvFlag(argv, "--repo-root"),
|
|
5673
5864
|
offline: argv.includes("--offline"),
|
|
5674
5865
|
rebaseline: argv.includes("--rebaseline"),
|
|
5675
|
-
restamp: argv.includes("--restamp")
|
|
5866
|
+
restamp: argv.includes("--restamp"),
|
|
5867
|
+
check: argv.includes("--check")
|
|
5676
5868
|
}),
|
|
5677
|
-
run: async ({ store, actor, now }, {
|
|
5869
|
+
run: async ({ store, actor, now }, {
|
|
5870
|
+
bundlePath: path,
|
|
5871
|
+
conceptId: id,
|
|
5872
|
+
repoRoot,
|
|
5873
|
+
offline,
|
|
5874
|
+
rebaseline,
|
|
5875
|
+
restamp,
|
|
5876
|
+
check
|
|
5877
|
+
}) => {
|
|
5878
|
+
if (check && (rebaseline || restamp)) {
|
|
5879
|
+
throw new KbFlagConflictError([
|
|
5880
|
+
"check",
|
|
5881
|
+
rebaseline ? "rebaseline" : "restamp"
|
|
5882
|
+
]);
|
|
5883
|
+
}
|
|
5678
5884
|
const root = repoRoot ?? process.cwd();
|
|
5679
5885
|
const record = await store.read(path, id);
|
|
5680
5886
|
if (!record) throw new KbRecordNotFoundError(id);
|
|
@@ -5683,7 +5889,6 @@ var anchorResolveCommand = define({
|
|
|
5683
5889
|
return {
|
|
5684
5890
|
conceptId: id,
|
|
5685
5891
|
results: [],
|
|
5686
|
-
verified: false,
|
|
5687
5892
|
note: "record has no anchors"
|
|
5688
5893
|
};
|
|
5689
5894
|
}
|
|
@@ -5741,7 +5946,7 @@ var anchorResolveCommand = define({
|
|
|
5741
5946
|
if (!anchor.hash) {
|
|
5742
5947
|
results.push({
|
|
5743
5948
|
...base2,
|
|
5744
|
-
state: "stamped",
|
|
5949
|
+
state: check ? "unstamped" : "stamped",
|
|
5745
5950
|
currentHash: stampedHash,
|
|
5746
5951
|
hashKind: stampedKind,
|
|
5747
5952
|
...producedBy ? { resolver: producedBy } : {}
|
|
@@ -5793,7 +5998,7 @@ var anchorResolveCommand = define({
|
|
|
5793
5998
|
if (refresh) dirty = true;
|
|
5794
5999
|
}
|
|
5795
6000
|
let frozen = false;
|
|
5796
|
-
if (dirty) {
|
|
6001
|
+
if (dirty && !check) {
|
|
5797
6002
|
try {
|
|
5798
6003
|
await assertBaseNotFrozen(process.cwd(), path);
|
|
5799
6004
|
} catch (error) {
|
|
@@ -5808,42 +6013,11 @@ var anchorResolveCommand = define({
|
|
|
5808
6013
|
const unreachable = results.filter(
|
|
5809
6014
|
(entry) => isUncheckedReason(entry.reason)
|
|
5810
6015
|
).length;
|
|
5811
|
-
const checked = results.length - unreachable;
|
|
5812
6016
|
const matches3 = results.filter((entry) => entry.state === "match").length;
|
|
5813
|
-
const note = `${matches3}/${
|
|
5814
|
-
const clean = checked > 0 && matches3 === checked && unreachable === 0;
|
|
5815
|
-
if (clean) {
|
|
5816
|
-
try {
|
|
5817
|
-
await store.verify(
|
|
5818
|
-
path,
|
|
5819
|
-
id,
|
|
5820
|
-
`anchor-resolve: ${note} (${resolverSummary(results)})`,
|
|
5821
|
-
actor,
|
|
5822
|
-
now()
|
|
5823
|
-
);
|
|
5824
|
-
} catch (error) {
|
|
5825
|
-
if (!(error instanceof KbSelfVerificationError)) throw error;
|
|
5826
|
-
return {
|
|
5827
|
-
conceptId: id,
|
|
5828
|
-
results,
|
|
5829
|
-
verified: false,
|
|
5830
|
-
verifyRefused: "self-verification",
|
|
5831
|
-
...frozenNote,
|
|
5832
|
-
...hintNote
|
|
5833
|
-
};
|
|
5834
|
-
}
|
|
5835
|
-
return {
|
|
5836
|
-
conceptId: id,
|
|
5837
|
-
results,
|
|
5838
|
-
verified: true,
|
|
5839
|
-
...frozenNote,
|
|
5840
|
-
...hintNote
|
|
5841
|
-
};
|
|
5842
|
-
}
|
|
6017
|
+
const note = `${matches3}/${results.length - unreachable} anchors match, ${unreachable} unreachable`;
|
|
5843
6018
|
return {
|
|
5844
6019
|
conceptId: id,
|
|
5845
6020
|
results,
|
|
5846
|
-
verified: false,
|
|
5847
6021
|
...unreachable ? { note } : {},
|
|
5848
6022
|
...frozenNote,
|
|
5849
6023
|
...hintNote
|
|
@@ -5930,14 +6104,117 @@ async function readSources(anchors, root, offline) {
|
|
|
5930
6104
|
return sources;
|
|
5931
6105
|
}
|
|
5932
6106
|
|
|
5933
|
-
// src/commands/
|
|
6107
|
+
// src/commands/anchor-set/model.ts
|
|
5934
6108
|
var import_zod10 = require("zod");
|
|
6109
|
+
var anchorSetInputSchema = import_zod10.z.object({
|
|
6110
|
+
reason: import_zod10.z.string().refine((text) => text.trim().length > 0, {
|
|
6111
|
+
message: "reason must say what was reviewed"
|
|
6112
|
+
}).describe(
|
|
6113
|
+
"What the reviewer read that makes these the right pointers. Recorded in the log."
|
|
6114
|
+
),
|
|
6115
|
+
anchors: import_zod10.z.array(kbAnchorWriteSchema).min(1).describe(
|
|
6116
|
+
"The complete new anchor set. Carry an anchor's hash forward to keep drift visible until the new code is read."
|
|
6117
|
+
)
|
|
6118
|
+
}).strict();
|
|
6119
|
+
var anchorSetCommandInput = import_zod10.z.object({
|
|
6120
|
+
bundlePath,
|
|
6121
|
+
conceptId,
|
|
6122
|
+
input: anchorSetInputSchema,
|
|
6123
|
+
resolve: import_zod10.z.boolean().optional().describe(
|
|
6124
|
+
"Also resolve and stamp every anchor against the current code, as anchor-resolve --rebaseline does."
|
|
6125
|
+
),
|
|
6126
|
+
repoRoot: import_zod10.z.string().min(1).optional().describe(
|
|
6127
|
+
"Where the anchored source lives, for resolve. Defaults to the working directory."
|
|
6128
|
+
),
|
|
6129
|
+
offline: import_zod10.z.boolean().optional().describe("With resolve, read foreign anchors from the repo cache only.")
|
|
6130
|
+
});
|
|
6131
|
+
|
|
6132
|
+
// src/commands/anchor-set/command.ts
|
|
6133
|
+
var NOTE = "pointers only: nothing was resolved or verified. Run anchor-resolve to check the new pointers, --rebaseline to accept the code, or pass resolve to do both here.";
|
|
6134
|
+
var STAMPED_NOTE = "pointers set and stamped against the current code. Not verification: run verify separately if someone reviewed it.";
|
|
6135
|
+
var anchorSetCommand = define({
|
|
6136
|
+
name: "anchor-set",
|
|
6137
|
+
tool: "kb_anchor_set",
|
|
6138
|
+
usage: "anchor-set <concept-id> [--resolve] [--repo-root <path>] [--offline] < anchors.json",
|
|
6139
|
+
description: "Set a record's code anchors after a reviewed refactor, with a reason. The array is the whole set. With resolve, every anchor is stamped against the current code in the same call; without it, run kb_anchor_resolve next. Recorded in the log, never verification.",
|
|
6140
|
+
input: anchorSetCommandInput,
|
|
6141
|
+
fromArgv: async (argv, path, stdin) => ({
|
|
6142
|
+
bundlePath: path,
|
|
6143
|
+
conceptId: argv[1],
|
|
6144
|
+
input: JSON.parse(await stdin()),
|
|
6145
|
+
resolve: argv.includes("--resolve"),
|
|
6146
|
+
repoRoot: argvFlag(argv, "--repo-root"),
|
|
6147
|
+
offline: argv.includes("--offline")
|
|
6148
|
+
}),
|
|
6149
|
+
run: async (ctx, { bundlePath: path, conceptId: id, input, resolve: resolve7, repoRoot, offline }) => {
|
|
6150
|
+
const { store, actor } = ctx;
|
|
6151
|
+
await assertBaseNotFrozen(process.cwd(), path);
|
|
6152
|
+
let applied;
|
|
6153
|
+
const record = await store.updateAnchors(
|
|
6154
|
+
path,
|
|
6155
|
+
id,
|
|
6156
|
+
(current) => {
|
|
6157
|
+
applied = applyAnchorSet(current, input.anchors);
|
|
6158
|
+
return {
|
|
6159
|
+
anchors: applied.anchors,
|
|
6160
|
+
log: {
|
|
6161
|
+
operation: "anchor-set",
|
|
6162
|
+
reason: input.reason,
|
|
6163
|
+
anchors: applied.changes
|
|
6164
|
+
}
|
|
6165
|
+
};
|
|
6166
|
+
},
|
|
6167
|
+
actor
|
|
6168
|
+
);
|
|
6169
|
+
const changes = applied?.changes ?? [];
|
|
6170
|
+
if (!resolve7) {
|
|
6171
|
+
return {
|
|
6172
|
+
conceptId: id,
|
|
6173
|
+
reason: input.reason,
|
|
6174
|
+
changes,
|
|
6175
|
+
anchors: record.frontmatter.strauss_anchors ?? [],
|
|
6176
|
+
baseline: "unchanged",
|
|
6177
|
+
note: NOTE
|
|
6178
|
+
};
|
|
6179
|
+
}
|
|
6180
|
+
const resolved = await anchorResolveCommand.run(
|
|
6181
|
+
ctx,
|
|
6182
|
+
anchorResolveCommand.input.parse({
|
|
6183
|
+
bundlePath: path,
|
|
6184
|
+
conceptId: id,
|
|
6185
|
+
rebaseline: true,
|
|
6186
|
+
...repoRoot ? { repoRoot } : {},
|
|
6187
|
+
...offline ? { offline } : {}
|
|
6188
|
+
})
|
|
6189
|
+
);
|
|
6190
|
+
const after = await store.read(path, id);
|
|
6191
|
+
return {
|
|
6192
|
+
conceptId: id,
|
|
6193
|
+
reason: input.reason,
|
|
6194
|
+
changes,
|
|
6195
|
+
anchors: after?.frontmatter.strauss_anchors ?? [],
|
|
6196
|
+
baseline: "stamped",
|
|
6197
|
+
resolved: resolved.results,
|
|
6198
|
+
note: STAMPED_NOTE
|
|
6199
|
+
};
|
|
6200
|
+
},
|
|
6201
|
+
// With `resolve`, a pointer that names nothing is a failed set, not a
|
|
6202
|
+
// finding to read later. A remote nothing could reach was never checked, so
|
|
6203
|
+
// it does not fail — the same line anchor-resolve draws.
|
|
6204
|
+
failsWhen: (result) => (result.resolved ?? []).some((entry) => {
|
|
6205
|
+
const { state, reason } = entry;
|
|
6206
|
+
return state === "unresolved" && !isUncheckedReason(reason);
|
|
6207
|
+
})
|
|
6208
|
+
});
|
|
6209
|
+
|
|
6210
|
+
// src/commands/answer.ts
|
|
6211
|
+
var import_zod11 = require("zod");
|
|
5935
6212
|
var answerCommand = define({
|
|
5936
6213
|
name: "answer",
|
|
5937
6214
|
tool: "kb_answer",
|
|
5938
6215
|
usage: "answer <concept-id> <answer...>",
|
|
5939
6216
|
description: "Resolve an open question: set status, stamp who and when, append an Answer section. If the answer overturns a decision or assumption, supersede that record explicitly.",
|
|
5940
|
-
input:
|
|
6217
|
+
input: import_zod11.z.object({ bundlePath, conceptId, answer: import_zod11.z.string().min(1) }),
|
|
5941
6218
|
fromArgv: (argv, path) => ({
|
|
5942
6219
|
bundlePath: path,
|
|
5943
6220
|
conceptId: argv[1],
|
|
@@ -5951,27 +6228,27 @@ var answerCommand = define({
|
|
|
5951
6228
|
});
|
|
5952
6229
|
|
|
5953
6230
|
// src/commands/backlinks.ts
|
|
5954
|
-
var
|
|
6231
|
+
var import_zod12 = require("zod");
|
|
5955
6232
|
var backlinksCommand = define({
|
|
5956
6233
|
name: "backlinks",
|
|
5957
6234
|
tool: "kb_backlinks",
|
|
5958
6235
|
usage: "backlinks <concept-id>",
|
|
5959
6236
|
description: "Who points at this record: every inbound typed causal link (`strauss_links`), one hop, every rel including `related_to`, each with its rel and the standing of the record that made it. Use it when you need the exact edges \u2014 reviewing or renaming a record.",
|
|
5960
|
-
input:
|
|
6237
|
+
input: import_zod12.z.object({ bundlePath, conceptId }),
|
|
5961
6238
|
fromArgv: (argv, path) => ({ bundlePath: path, conceptId: argv[1] }),
|
|
5962
6239
|
run: async ({ store }, { bundlePath: path, conceptId: id }) => store.backlinks(path, id)
|
|
5963
6240
|
});
|
|
5964
6241
|
|
|
5965
6242
|
// src/commands/catalog.ts
|
|
5966
|
-
var
|
|
6243
|
+
var import_zod13 = require("zod");
|
|
5967
6244
|
var catalogCommand = define({
|
|
5968
6245
|
name: "catalog",
|
|
5969
6246
|
tool: "kb_catalog",
|
|
5970
6247
|
usage: "catalog [type] [--tag T]...",
|
|
5971
6248
|
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.",
|
|
5972
|
-
input:
|
|
6249
|
+
input: import_zod13.z.object({
|
|
5973
6250
|
bundlePath,
|
|
5974
|
-
type:
|
|
6251
|
+
type: import_zod13.z.enum(KB_RECORD_TYPES).optional(),
|
|
5975
6252
|
tags: TAGS
|
|
5976
6253
|
}),
|
|
5977
6254
|
fromArgv: (argv, path) => {
|
|
@@ -6043,7 +6320,7 @@ function count(value, noun) {
|
|
|
6043
6320
|
var import_node_buffer = require("buffer");
|
|
6044
6321
|
var import_promises10 = require("fs/promises");
|
|
6045
6322
|
var import_node_path12 = require("path");
|
|
6046
|
-
var
|
|
6323
|
+
var import_zod16 = require("zod");
|
|
6047
6324
|
|
|
6048
6325
|
// src/drift/moved.ts
|
|
6049
6326
|
var import_promises9 = require("fs/promises");
|
|
@@ -6380,7 +6657,7 @@ function claimOf(record) {
|
|
|
6380
6657
|
}
|
|
6381
6658
|
|
|
6382
6659
|
// src/commands/match/command.ts
|
|
6383
|
-
var
|
|
6660
|
+
var import_zod15 = require("zod");
|
|
6384
6661
|
|
|
6385
6662
|
// src/commands/match/errors.ts
|
|
6386
6663
|
var KbMatchInputError = class extends BaseError {
|
|
@@ -6400,21 +6677,21 @@ var KbMatchInputError = class extends BaseError {
|
|
|
6400
6677
|
};
|
|
6401
6678
|
|
|
6402
6679
|
// src/commands/match/model.ts
|
|
6403
|
-
var
|
|
6404
|
-
var diffHunkSchema =
|
|
6405
|
-
startLine:
|
|
6406
|
-
endLine:
|
|
6407
|
-
side:
|
|
6680
|
+
var import_zod14 = require("zod");
|
|
6681
|
+
var diffHunkSchema = import_zod14.z.object({
|
|
6682
|
+
startLine: import_zod14.z.number().int().positive(),
|
|
6683
|
+
endLine: import_zod14.z.number().int().positive(),
|
|
6684
|
+
side: import_zod14.z.enum(["old", "new"]).optional()
|
|
6408
6685
|
}).passthrough();
|
|
6409
|
-
var diffFileSchema =
|
|
6410
|
-
filePath:
|
|
6411
|
-
hunks:
|
|
6686
|
+
var diffFileSchema = import_zod14.z.object({
|
|
6687
|
+
filePath: import_zod14.z.string().min(1).describe("Repo-relative, spelled the way anchors are."),
|
|
6688
|
+
hunks: import_zod14.z.array(diffHunkSchema)
|
|
6412
6689
|
});
|
|
6413
|
-
var symbolRangeSchema =
|
|
6414
|
-
file:
|
|
6415
|
-
symbol:
|
|
6416
|
-
startLine:
|
|
6417
|
-
endLine:
|
|
6690
|
+
var symbolRangeSchema = import_zod14.z.object({
|
|
6691
|
+
file: import_zod14.z.string().min(1),
|
|
6692
|
+
symbol: import_zod14.z.string().min(1),
|
|
6693
|
+
startLine: import_zod14.z.number().int().positive(),
|
|
6694
|
+
endLine: import_zod14.z.number().int().positive()
|
|
6418
6695
|
});
|
|
6419
6696
|
|
|
6420
6697
|
// src/commands/match/parse-unified-diff.ts
|
|
@@ -6663,17 +6940,17 @@ var matchCommand = define({
|
|
|
6663
6940
|
tool: "kb_match",
|
|
6664
6941
|
usage: "match --git <base>..<head> | --stdin [--repo-root <path>] [--offline] [--include-non-current]",
|
|
6665
6942
|
description: "Which records sit on each changed hunk: the anchored records per file range, current first, each with its standing and the anchor that matched. kb_load hands over a whole base; this narrows a diff. Symbol ranges resolve from repoRoot when omitted; non-current records need includeNonCurrent.",
|
|
6666
|
-
input:
|
|
6943
|
+
input: import_zod15.z.object({
|
|
6667
6944
|
bundlePath,
|
|
6668
|
-
files:
|
|
6669
|
-
symbolRanges:
|
|
6945
|
+
files: import_zod15.z.array(diffFileSchema).describe("The changed files, each with its post-change line ranges."),
|
|
6946
|
+
symbolRanges: import_zod15.z.array(symbolRangeSchema).optional().describe(
|
|
6670
6947
|
"Symbol spans the caller already has. Resolved from repoRoot when omitted."
|
|
6671
6948
|
),
|
|
6672
6949
|
repoRoot: REPO_ROOT,
|
|
6673
|
-
offline:
|
|
6950
|
+
offline: import_zod15.z.boolean().optional().describe(
|
|
6674
6951
|
"Resolve symbol ranges from what is already on disk, never fetching a grammar."
|
|
6675
6952
|
),
|
|
6676
|
-
includeNonCurrent:
|
|
6953
|
+
includeNonCurrent: import_zod15.z.boolean().optional().describe(
|
|
6677
6954
|
"Return superseded, rejected and unsettled records too, each carrying its standing."
|
|
6678
6955
|
)
|
|
6679
6956
|
}),
|
|
@@ -6687,11 +6964,11 @@ var matchCommand = define({
|
|
|
6687
6964
|
...argv.includes("--include-non-current") ? { includeNonCurrent: true } : {}
|
|
6688
6965
|
};
|
|
6689
6966
|
if (range !== void 0) {
|
|
6690
|
-
const
|
|
6691
|
-
if (!
|
|
6692
|
-
throw new KbMatchInputError(`--git ${range} ${REFUSED[
|
|
6967
|
+
const diff2 = await readRangeDiff(repoRoot ?? process.cwd(), range);
|
|
6968
|
+
if (!diff2.ok) {
|
|
6969
|
+
throw new KbMatchInputError(`--git ${range} ${REFUSED[diff2.reason]}`);
|
|
6693
6970
|
}
|
|
6694
|
-
return { ...base2, files: parseUnifiedDiff(
|
|
6971
|
+
return { ...base2, files: parseUnifiedDiff(diff2.text) };
|
|
6695
6972
|
}
|
|
6696
6973
|
if (!argv.includes("--stdin")) {
|
|
6697
6974
|
throw new KbMatchInputError(
|
|
@@ -6777,22 +7054,22 @@ function project(match, ranges, all) {
|
|
|
6777
7054
|
|
|
6778
7055
|
// src/commands/classify.ts
|
|
6779
7056
|
var classifyFileSchema = diffFileSchema.extend({
|
|
6780
|
-
hunks:
|
|
6781
|
-
diffHunkSchema.extend({ lines:
|
|
7057
|
+
hunks: import_zod16.z.array(
|
|
7058
|
+
diffHunkSchema.extend({ lines: import_zod16.z.array(import_zod16.z.string()).optional() })
|
|
6782
7059
|
),
|
|
6783
|
-
renamedFrom:
|
|
6784
|
-
similarity:
|
|
7060
|
+
renamedFrom: import_zod16.z.string().min(1).optional().describe("Where `git diff -M` says the path came from."),
|
|
7061
|
+
similarity: import_zod16.z.number().min(0).max(100).optional()
|
|
6785
7062
|
});
|
|
6786
7063
|
var classifyCommand = define({
|
|
6787
7064
|
name: "classify",
|
|
6788
7065
|
tool: "kb_classify",
|
|
6789
7066
|
usage: "classify --git <base>..<head> | --stdin [--repo-root <path>] [--offline]",
|
|
6790
7067
|
description: "What kind of change each file carries: test, config, ci, docs, lockfile, generated, boilerplate, rename or source, with the rule that decided it. Derived from the diff and never stored; a `review:generated`, `review:boilerplate` or `review:move` fact anchored on a file overrides the heuristic. kb_match says what sits on a hunk; this says whether to read it.",
|
|
6791
|
-
input:
|
|
7068
|
+
input: import_zod16.z.object({
|
|
6792
7069
|
bundlePath,
|
|
6793
|
-
files:
|
|
7070
|
+
files: import_zod16.z.array(classifyFileSchema).describe("The changed files, each with its line ranges."),
|
|
6794
7071
|
repoRoot: REPO_ROOT,
|
|
6795
|
-
offline:
|
|
7072
|
+
offline: import_zod16.z.boolean().optional().describe(
|
|
6796
7073
|
"Resolve symbol ranges from what is already on disk, never fetching a grammar."
|
|
6797
7074
|
)
|
|
6798
7075
|
}),
|
|
@@ -6805,15 +7082,15 @@ var classifyCommand = define({
|
|
|
6805
7082
|
...argv.includes("--offline") ? { offline: true } : {}
|
|
6806
7083
|
};
|
|
6807
7084
|
if (range !== void 0) {
|
|
6808
|
-
const
|
|
6809
|
-
if (!
|
|
7085
|
+
const diff2 = await readRangeDiff(repoRoot ?? process.cwd(), range);
|
|
7086
|
+
if (!diff2.ok) {
|
|
6810
7087
|
throw new KbClassifyInputError(
|
|
6811
|
-
`--git ${range} ${REFUSED2[
|
|
7088
|
+
`--git ${range} ${REFUSED2[diff2.reason]}`
|
|
6812
7089
|
);
|
|
6813
7090
|
}
|
|
6814
7091
|
return {
|
|
6815
7092
|
...base2,
|
|
6816
|
-
files: parseUnifiedDiff(
|
|
7093
|
+
files: parseUnifiedDiff(diff2.text, {
|
|
6817
7094
|
keepEmpty: true,
|
|
6818
7095
|
withLines: true
|
|
6819
7096
|
})
|
|
@@ -6903,29 +7180,29 @@ function renderClassify(result) {
|
|
|
6903
7180
|
}
|
|
6904
7181
|
|
|
6905
7182
|
// src/commands/context.ts
|
|
6906
|
-
var
|
|
7183
|
+
var import_zod17 = require("zod");
|
|
6907
7184
|
var contextCommand = define({
|
|
6908
7185
|
name: "context",
|
|
6909
7186
|
tool: "kb_context",
|
|
6910
7187
|
usage: "context [--profile NAME] [--budget N] [--full-under N] [--exclude-tag T]... [--format json] [--event NAME]",
|
|
6911
7188
|
description: "Index block of pinned bases (ids, titles, standing) for injection at context birth. Takes no bundlePath \u2014 reads the workspace pin manifests. Empty when nothing is pinned; refuses over budget rather than truncating. Budget precedence: flags, then the manifest `context[profile]` over `context.default`, then the built-in profile, then package defaults.",
|
|
6912
|
-
input:
|
|
6913
|
-
budgetTokens:
|
|
7189
|
+
input: import_zod17.z.object({
|
|
7190
|
+
budgetTokens: import_zod17.z.number().int().positive().optional().describe(
|
|
6914
7191
|
"Ceiling on the whole emitted block; past it the command refuses with a list of bases rather than truncating. Defaults to 4000."
|
|
6915
7192
|
),
|
|
6916
|
-
fullUnderTokens:
|
|
7193
|
+
fullUnderTokens: import_zod17.z.number().int().positive().optional().describe(
|
|
6917
7194
|
"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."
|
|
6918
7195
|
),
|
|
6919
|
-
profile:
|
|
7196
|
+
profile: import_zod17.z.string().optional().describe(
|
|
6920
7197
|
"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."
|
|
6921
7198
|
),
|
|
6922
|
-
excludeTags:
|
|
7199
|
+
excludeTags: import_zod17.z.array(import_zod17.z.string().min(1)).optional().describe(
|
|
6923
7200
|
"Frontmatter tags whose records stay out of the block. The base stays pinned and stays readable by tool; resolved like the budgets."
|
|
6924
7201
|
),
|
|
6925
|
-
format:
|
|
7202
|
+
format: import_zod17.z.enum(["markdown", "json"]).optional().describe(
|
|
6926
7203
|
"CLI envelope for hook protocols that require strict JSON on stdout. MCP callers omit this \u2014 the block itself is identical."
|
|
6927
7204
|
),
|
|
6928
|
-
event:
|
|
7205
|
+
event: import_zod17.z.string().optional().describe(
|
|
6929
7206
|
"hookEventName stamped into the JSON envelope. Only meaningful with format=json."
|
|
6930
7207
|
)
|
|
6931
7208
|
}),
|
|
@@ -6964,20 +7241,20 @@ var contextCommand = define({
|
|
|
6964
7241
|
});
|
|
6965
7242
|
|
|
6966
7243
|
// src/commands/doctor.ts
|
|
6967
|
-
var
|
|
7244
|
+
var import_zod19 = require("zod");
|
|
6968
7245
|
|
|
6969
7246
|
// src/commands/reassess.ts
|
|
6970
|
-
var
|
|
7247
|
+
var import_zod18 = require("zod");
|
|
6971
7248
|
var reassessCommand = define({
|
|
6972
7249
|
name: "reassess",
|
|
6973
7250
|
tool: "kb_reassess",
|
|
6974
7251
|
usage: "reassess <concept-id> [--repo-root <path>] [--with-diff]",
|
|
6975
7252
|
description: "One drifted record, as something to judge: its claim, each anchor's drift class, the old-vs-new span diff, and the records that depend on it. Formatting-only drift is dropped. Empty when there is nothing to reassess. Writes: relocates moved anchors, keeping their hash; never verifies, supersedes, or changes standing.",
|
|
6976
|
-
input:
|
|
7253
|
+
input: import_zod18.z.object({
|
|
6977
7254
|
bundlePath,
|
|
6978
7255
|
conceptId,
|
|
6979
7256
|
repoRoot: REPO_ROOT,
|
|
6980
|
-
withDiff:
|
|
7257
|
+
withDiff: import_zod18.z.boolean().optional().describe(
|
|
6981
7258
|
"Recover each anchor's committed span and render the diff. Reads git history."
|
|
6982
7259
|
)
|
|
6983
7260
|
}),
|
|
@@ -7122,13 +7399,13 @@ function at(file, symbol) {
|
|
|
7122
7399
|
}
|
|
7123
7400
|
|
|
7124
7401
|
// src/commands/doctor.ts
|
|
7125
|
-
var days = (what, fallback) =>
|
|
7402
|
+
var days = (what, fallback) => import_zod19.z.number().int().positive().optional().describe(`${what} Defaults to ${fallback}.`);
|
|
7126
7403
|
var doctorCommand = define({
|
|
7127
7404
|
name: "doctor",
|
|
7128
7405
|
tool: "kb_doctor",
|
|
7129
7406
|
usage: "doctor [--expiring-days N] [--unverified-days N] [--aging-days N] [--repo-root PATH] [--offline] [--strict] [--drifted [--with-diff]]",
|
|
7130
7407
|
description: "Read-only health sweep: expired, expiring, unverified, aging, orphaned, broken-supersession, superseded-but-cited, drifted and unchecked anchors. Every group is reported even when empty; nothing is written or re-stamped. `drifted` narrows it to a reassessment packet per drifted record, `with_diff` adding each anchor's old-vs-new span.",
|
|
7131
|
-
input:
|
|
7408
|
+
input: import_zod19.z.object({
|
|
7132
7409
|
bundlePath,
|
|
7133
7410
|
repoRoot: REPO_ROOT,
|
|
7134
7411
|
expiringDays: days(
|
|
@@ -7143,16 +7420,16 @@ var doctorCommand = define({
|
|
|
7143
7420
|
"How long a record may stay `open` or `proposed` before `aging` reports it, in days.",
|
|
7144
7421
|
DEFAULT_AGING_DAYS
|
|
7145
7422
|
),
|
|
7146
|
-
offline:
|
|
7423
|
+
offline: import_zod19.z.boolean().optional().describe(
|
|
7147
7424
|
"Read foreign anchors from the local repo cache only, never fetching."
|
|
7148
7425
|
),
|
|
7149
|
-
strict:
|
|
7426
|
+
strict: import_zod19.z.boolean().optional().describe(
|
|
7150
7427
|
"Turn an expired record into a non-zero exit for the CLI. No effect on the report itself."
|
|
7151
7428
|
),
|
|
7152
|
-
drifted:
|
|
7429
|
+
drifted: import_zod19.z.boolean().optional().describe(
|
|
7153
7430
|
"Report only drift, as a reassessment packet per record: claim, per-anchor class, and what depends on it."
|
|
7154
7431
|
),
|
|
7155
|
-
withDiff:
|
|
7432
|
+
withDiff: import_zod19.z.boolean().optional().describe(
|
|
7156
7433
|
"With `drifted`: recover each anchor's committed span and render the old-vs-new diff. Reads git history."
|
|
7157
7434
|
)
|
|
7158
7435
|
}),
|
|
@@ -7329,7 +7606,7 @@ function renderPackets(result) {
|
|
|
7329
7606
|
// src/commands/export.ts
|
|
7330
7607
|
var import_promises11 = require("fs/promises");
|
|
7331
7608
|
var import_node_path13 = require("path");
|
|
7332
|
-
var
|
|
7609
|
+
var import_zod20 = require("zod");
|
|
7333
7610
|
var NUMBERED = /^(\d{4})-(.+)\.md$/;
|
|
7334
7611
|
var MARKER = "<!-- strauss-kb export: ";
|
|
7335
7612
|
var exportCommand = define({
|
|
@@ -7337,10 +7614,10 @@ var exportCommand = define({
|
|
|
7337
7614
|
tool: "kb_export",
|
|
7338
7615
|
usage: "export --format madr --to <dir>",
|
|
7339
7616
|
description: "Write the base's decisions out as numbered MADR files, one per decision, for a repository that keeps ADRs of its own. Numbering is by slug, so a re-run rewrites its own files in place. A superseded decision is exported with what replaced it.",
|
|
7340
|
-
input:
|
|
7617
|
+
input: import_zod20.z.object({
|
|
7341
7618
|
bundlePath,
|
|
7342
|
-
format:
|
|
7343
|
-
to:
|
|
7619
|
+
format: import_zod20.z.enum(["madr"]).describe("Output layout. `madr` is the only one so far."),
|
|
7620
|
+
to: import_zod20.z.string().min(1).describe("Directory the ADR files are written into.")
|
|
7344
7621
|
}),
|
|
7345
7622
|
fromArgv: (argv, path) => ({
|
|
7346
7623
|
bundlePath: path,
|
|
@@ -7462,19 +7739,19 @@ function bodySections(body) {
|
|
|
7462
7739
|
}
|
|
7463
7740
|
|
|
7464
7741
|
// src/commands/impact.ts
|
|
7465
|
-
var
|
|
7742
|
+
var import_zod21 = require("zod");
|
|
7466
7743
|
var impactCommand = define({
|
|
7467
7744
|
name: "impact",
|
|
7468
7745
|
tool: "kb_impact",
|
|
7469
7746
|
usage: "impact <concept-id> [--depth N] [--rels a,b]",
|
|
7470
7747
|
description: "What breaks if this record changes: its transitive set of dependants, each with its standing. Each rel declares which of its ends depends on the other, and the walk follows each rel in its own direction. Naming `related_to` or an unknown rel in `rels` is an error. kb_backlinks gives one flat hop.",
|
|
7471
|
-
input:
|
|
7748
|
+
input: import_zod21.z.object({
|
|
7472
7749
|
bundlePath,
|
|
7473
7750
|
conceptId,
|
|
7474
|
-
depth:
|
|
7751
|
+
depth: import_zod21.z.number().int().positive().optional().describe(
|
|
7475
7752
|
"Hops out from the record. Unbounded when omitted; a walk this cuts reports truncated: true."
|
|
7476
7753
|
),
|
|
7477
|
-
rels:
|
|
7754
|
+
rels: import_zod21.z.array(import_zod21.z.enum(KB_CAUSAL_LINK_RELS)).optional().describe(
|
|
7478
7755
|
"Narrow which rels the walk follows. Defaults to every rel that carries a dependence \u2014 all but related_to."
|
|
7479
7756
|
)
|
|
7480
7757
|
}),
|
|
@@ -7495,15 +7772,15 @@ var impactCommand = define({
|
|
|
7495
7772
|
});
|
|
7496
7773
|
|
|
7497
7774
|
// src/commands/list.ts
|
|
7498
|
-
var
|
|
7775
|
+
var import_zod22 = require("zod");
|
|
7499
7776
|
var listCommand = define({
|
|
7500
7777
|
name: "list",
|
|
7501
7778
|
tool: "kb_list",
|
|
7502
7779
|
usage: "list [type] [--tag T]...",
|
|
7503
7780
|
description: "Every record, optionally one type or tag. For enumerating; use kb_query for a question.",
|
|
7504
|
-
input:
|
|
7781
|
+
input: import_zod22.z.object({
|
|
7505
7782
|
bundlePath,
|
|
7506
|
-
type:
|
|
7783
|
+
type: import_zod22.z.enum(KB_RECORD_TYPES).optional(),
|
|
7507
7784
|
tags: TAGS
|
|
7508
7785
|
}),
|
|
7509
7786
|
fromArgv: (argv, path) => {
|
|
@@ -7527,17 +7804,17 @@ var listCommand = define({
|
|
|
7527
7804
|
});
|
|
7528
7805
|
|
|
7529
7806
|
// src/commands/load.ts
|
|
7530
|
-
var
|
|
7807
|
+
var import_zod23 = require("zod");
|
|
7531
7808
|
var loadCommand = define({
|
|
7532
7809
|
name: "load",
|
|
7533
7810
|
tool: "kb_load",
|
|
7534
7811
|
usage: "load [type] [--budget N | --all] [--repo-root PATH]",
|
|
7535
7812
|
description: "Load the whole base, each record with its standing \u2014 call it first, at the point of use, since compaction drops it. Superseded records arrive as stubs; kb_trace has the history. Over budget it refuses: kb_catalog, then kb_pack, or narrow with `type`; `all` bypasses. Never read record files directly \u2014 only kb_* tools resolve supersession. `digest` stamps the base's content, so hooks know when to reload.",
|
|
7536
|
-
input:
|
|
7813
|
+
input: import_zod23.z.object({
|
|
7537
7814
|
bundlePath,
|
|
7538
|
-
type:
|
|
7539
|
-
budgetTokens:
|
|
7540
|
-
all:
|
|
7815
|
+
type: import_zod23.z.enum(KB_RECORD_TYPES).optional(),
|
|
7816
|
+
budgetTokens: import_zod23.z.number().int().positive().optional().describe("Approximate token ceiling. Defaults to 25000."),
|
|
7817
|
+
all: import_zod23.z.boolean().optional().describe(
|
|
7541
7818
|
"Loads the entire base regardless of size, bypassing the token budget; mutually exclusive with budgetTokens."
|
|
7542
7819
|
),
|
|
7543
7820
|
repoRoot: REPO_ROOT
|
|
@@ -7579,25 +7856,25 @@ var loadCommand = define({
|
|
|
7579
7856
|
});
|
|
7580
7857
|
|
|
7581
7858
|
// src/commands/log.ts
|
|
7582
|
-
var
|
|
7859
|
+
var import_zod24 = require("zod");
|
|
7583
7860
|
var logCommand = define({
|
|
7584
7861
|
name: "log",
|
|
7585
7862
|
tool: "kb_log",
|
|
7586
7863
|
usage: "log",
|
|
7587
7864
|
description: "Who touched what, and when. Append-only; malformed lines are reported, never repaired.",
|
|
7588
|
-
input:
|
|
7865
|
+
input: import_zod24.z.object({ bundlePath }),
|
|
7589
7866
|
fromArgv: (_argv, path) => ({ bundlePath: path }),
|
|
7590
7867
|
run: ({ store }, { bundlePath: path }) => store.readLog(path)
|
|
7591
7868
|
});
|
|
7592
7869
|
|
|
7593
7870
|
// src/commands/no-decision.ts
|
|
7594
|
-
var
|
|
7871
|
+
var import_zod25 = require("zod");
|
|
7595
7872
|
var noDecisionCommand = define({
|
|
7596
7873
|
name: "no-decision",
|
|
7597
7874
|
tool: "kb_no_decision",
|
|
7598
7875
|
usage: "no-decision <reason...>",
|
|
7599
7876
|
description: "Record in one sentence that a piece of work had nothing to decide. Idempotent.",
|
|
7600
|
-
input:
|
|
7877
|
+
input: import_zod25.z.object({ bundlePath, reason: import_zod25.z.string().min(1) }),
|
|
7601
7878
|
fromArgv: (argv, path) => ({
|
|
7602
7879
|
bundlePath: path,
|
|
7603
7880
|
reason: argv.slice(1).join(" ").trim()
|
|
@@ -7614,20 +7891,20 @@ var noDecisionCommand = define({
|
|
|
7614
7891
|
});
|
|
7615
7892
|
|
|
7616
7893
|
// src/commands/pack.ts
|
|
7617
|
-
var
|
|
7894
|
+
var import_zod26 = require("zod");
|
|
7618
7895
|
var packCommand = define({
|
|
7619
7896
|
name: "pack",
|
|
7620
7897
|
tool: "kb_pack",
|
|
7621
7898
|
usage: "pack <conceptId> [--hops N] [--max-nodes N] [--budget N]",
|
|
7622
7899
|
description: "Bounded neighbourhood around one record: within `hops`, ranked, cut to `maxNodes`, with every cut record named under Excluded. Use when the base is over kb_load's budget and the work centres on a record you can name. Refuses over budget rather than truncating. Everything below the header is byte-stable across runs. Resolves supersession like kb_load.",
|
|
7623
|
-
input:
|
|
7900
|
+
input: import_zod26.z.object({
|
|
7624
7901
|
bundlePath,
|
|
7625
7902
|
conceptId,
|
|
7626
|
-
hops:
|
|
7627
|
-
maxNodes:
|
|
7903
|
+
hops: import_zod26.z.number().int().positive().optional().describe("How far from the root the walk may reach. Defaults to 2."),
|
|
7904
|
+
maxNodes: import_zod26.z.number().int().positive().optional().describe(
|
|
7628
7905
|
"How many records the pack may hold, root included. Defaults to 20."
|
|
7629
7906
|
),
|
|
7630
|
-
budgetTokens:
|
|
7907
|
+
budgetTokens: import_zod26.z.number().int().positive().optional().describe(
|
|
7631
7908
|
"Approximate token ceiling over what is actually emitted. Defaults to 25000."
|
|
7632
7909
|
)
|
|
7633
7910
|
}),
|
|
@@ -7714,22 +7991,22 @@ function warningLabel(warning) {
|
|
|
7714
7991
|
}
|
|
7715
7992
|
|
|
7716
7993
|
// src/commands/pin.ts
|
|
7717
|
-
var
|
|
7994
|
+
var import_zod27 = require("zod");
|
|
7718
7995
|
var pinCommand = define({
|
|
7719
7996
|
name: "pin",
|
|
7720
7997
|
tool: "kb_pin",
|
|
7721
7998
|
usage: "pin [bundle-path] [--mode full|index] [--profiles a,b] [--local|--user] [--frozen|--unfreeze]",
|
|
7722
7999
|
description: "Pin a base into a workspace manifest so kb_context surfaces it. Layers, nearest wins: project `.strauss/kb-pins.json` (default), `--local` (personal, gitignored), `--user` (`~/.strauss`). Idempotent; `--mode full|index`, `--profiles`, `--frozen`/`--unfreeze` update only those fields. A path with no records pins with a warning. Never touches the base itself.",
|
|
7723
|
-
input:
|
|
8000
|
+
input: import_zod27.z.object({
|
|
7724
8001
|
bundlePath,
|
|
7725
|
-
mode:
|
|
8002
|
+
mode: import_zod27.z.enum(["full", "index"]).optional().describe(
|
|
7726
8003
|
"full: always emit this base's records whole (still under the block budget); index: never upgrade. Absent: the profile's full-under threshold decides."
|
|
7727
8004
|
),
|
|
7728
|
-
profiles:
|
|
7729
|
-
layer:
|
|
8005
|
+
profiles: import_zod27.z.array(import_zod27.z.string()).optional().describe("Context profiles this pin surfaces in. Absent: all of them."),
|
|
8006
|
+
layer: import_zod27.z.enum(["project", "local", "user"]).optional().describe(
|
|
7730
8007
|
"Which manifest to write: project (committed, default), local (personal, gitignored), user (~/.strauss, every workspace)."
|
|
7731
8008
|
),
|
|
7732
|
-
frozen:
|
|
8009
|
+
frozen: import_zod27.z.boolean().optional().describe(
|
|
7733
8010
|
"true: the base is concluded \u2014 writes against it refuse while pinned. false: lift a freeze."
|
|
7734
8011
|
)
|
|
7735
8012
|
}),
|
|
@@ -7758,13 +8035,13 @@ var pinCommand = define({
|
|
|
7758
8035
|
});
|
|
7759
8036
|
|
|
7760
8037
|
// src/commands/pins.ts
|
|
7761
|
-
var
|
|
8038
|
+
var import_zod28 = require("zod");
|
|
7762
8039
|
var pinsCommand = define({
|
|
7763
8040
|
name: "pins",
|
|
7764
8041
|
tool: "kb_pins",
|
|
7765
8042
|
usage: "pins",
|
|
7766
8043
|
description: "Every pinned base across the manifest layers, with its layer and whether it resolves to records. Takes no bundlePath.",
|
|
7767
|
-
input:
|
|
8044
|
+
input: import_zod28.z.object({}),
|
|
7768
8045
|
fromArgv: () => ({}),
|
|
7769
8046
|
run: ({ store }) => listPins(store, process.cwd())
|
|
7770
8047
|
});
|
|
@@ -7900,16 +8177,16 @@ function recordType(conceptId2) {
|
|
|
7900
8177
|
}
|
|
7901
8178
|
|
|
7902
8179
|
// src/commands/promote/model.ts
|
|
7903
|
-
var
|
|
7904
|
-
var promoteInputSchema =
|
|
8180
|
+
var import_zod29 = require("zod");
|
|
8181
|
+
var promoteInputSchema = import_zod29.z.object({
|
|
7905
8182
|
bundlePath,
|
|
7906
|
-
conceptIds:
|
|
7907
|
-
to:
|
|
7908
|
-
source:
|
|
8183
|
+
conceptIds: import_zod29.z.array(conceptId).max(64).optional().describe("Records to copy into the target base. Omit with `list`."),
|
|
8184
|
+
to: import_zod29.z.string().min(1).optional().describe("Absolute path to the base being promoted into."),
|
|
8185
|
+
source: import_zod29.z.string().min(1).optional().describe(
|
|
7909
8186
|
"Where the promotion came from, usually the pull request URL. Recorded on each copy as a source."
|
|
7910
8187
|
),
|
|
7911
|
-
force:
|
|
7912
|
-
list:
|
|
8188
|
+
force: import_zod29.z.boolean().optional().describe("Overwrite a record the target base already holds."),
|
|
8189
|
+
list: import_zod29.z.boolean().optional().describe("List the source base's candidates instead of promoting.")
|
|
7913
8190
|
}).refine((input) => input.list === true || input.to !== void 0, {
|
|
7914
8191
|
message: "promote needs a target base \u2014 pass --to <bundle>, or --list",
|
|
7915
8192
|
path: ["to"]
|
|
@@ -8046,17 +8323,17 @@ function renderPromote(result) {
|
|
|
8046
8323
|
}
|
|
8047
8324
|
|
|
8048
8325
|
// src/commands/query.ts
|
|
8049
|
-
var
|
|
8326
|
+
var import_zod30 = require("zod");
|
|
8050
8327
|
var queryCommand = define({
|
|
8051
8328
|
name: "query",
|
|
8052
8329
|
tool: "kb_query",
|
|
8053
8330
|
usage: "query <text...> [--tag T]... [--repo-root PATH]",
|
|
8054
8331
|
description: "Search; every hit carries its standing. Flagged, never filtered: a superseded hit returns with its replacement, a rejected one is marked. Prefer kb_load when the base fits its budget \u2014 a full read beats search. Results are volatile: place them at the tail, not the cached prefix. Never read record files directly.",
|
|
8055
|
-
input:
|
|
8332
|
+
input: import_zod30.z.object({
|
|
8056
8333
|
bundlePath,
|
|
8057
|
-
text:
|
|
8058
|
-
type:
|
|
8059
|
-
includeNonCurrent:
|
|
8334
|
+
text: import_zod30.z.string().optional(),
|
|
8335
|
+
type: import_zod30.z.enum(KB_RECORD_TYPES).optional(),
|
|
8336
|
+
includeNonCurrent: import_zod30.z.boolean().optional(),
|
|
8060
8337
|
tags: TAGS,
|
|
8061
8338
|
repoRoot: REPO_ROOT
|
|
8062
8339
|
}),
|
|
@@ -8090,43 +8367,43 @@ var queryCommand = define({
|
|
|
8090
8367
|
});
|
|
8091
8368
|
|
|
8092
8369
|
// src/commands/read-index.ts
|
|
8093
|
-
var
|
|
8370
|
+
var import_zod31 = require("zod");
|
|
8094
8371
|
var readIndexCommand = define({
|
|
8095
8372
|
name: "index",
|
|
8096
8373
|
tool: "kb_index",
|
|
8097
8374
|
usage: "index",
|
|
8098
8375
|
description: "The index \u2014 title, type, status, description per record \u2014 rebuilt if stale. Cheapest re-orientation after compaction: call it (or kb_context) first, then kb_load or fetch by id.",
|
|
8099
|
-
input:
|
|
8376
|
+
input: import_zod31.z.object({ bundlePath }),
|
|
8100
8377
|
fromArgv: (_argv, path) => ({ bundlePath: path }),
|
|
8101
8378
|
run: ({ store }, { bundlePath: path }) => store.readIndex(path)
|
|
8102
8379
|
});
|
|
8103
8380
|
|
|
8104
8381
|
// src/commands/schema.ts
|
|
8105
|
-
var
|
|
8382
|
+
var import_zod32 = require("zod");
|
|
8106
8383
|
var schemaCommand = define({
|
|
8107
8384
|
name: "schema",
|
|
8108
8385
|
tool: "kb_schema",
|
|
8109
8386
|
usage: "schema",
|
|
8110
8387
|
description: "JSON Schema for frontmatter, write input, and log entries, generated from the enforcing code.",
|
|
8111
|
-
input:
|
|
8388
|
+
input: import_zod32.z.object({}),
|
|
8112
8389
|
fromArgv: () => ({}),
|
|
8113
8390
|
run: () => Promise.resolve(kbJsonSchemas())
|
|
8114
8391
|
});
|
|
8115
8392
|
|
|
8116
8393
|
// src/commands/stamp.ts
|
|
8117
8394
|
var import_promises12 = require("fs/promises");
|
|
8118
|
-
var
|
|
8395
|
+
var import_zod33 = require("zod");
|
|
8119
8396
|
var DIGEST = /^[0-9a-f]{64}$/;
|
|
8120
8397
|
var stampCommand = define({
|
|
8121
8398
|
name: "stamp",
|
|
8122
8399
|
tool: "kb_stamp",
|
|
8123
8400
|
usage: "stamp [--bundle PATH] [--since DIGEST|FILE]",
|
|
8124
8401
|
description: "Content stamp of a base \u2014 `load`'s digest, record counts, per-record digests, how many records have drifted anchors \u2014 without any bodies. Takes no bundlePath to stamp every pinned base. With `since`, reports only the bases that moved, naming the changed ids. Reads, never writes.",
|
|
8125
|
-
input:
|
|
8126
|
-
bundlePath:
|
|
8402
|
+
input: import_zod33.z.object({
|
|
8403
|
+
bundlePath: import_zod33.z.string().min(1).optional().describe(
|
|
8127
8404
|
"Absolute path to one knowledge base. Omit to stamp every pinned base."
|
|
8128
8405
|
),
|
|
8129
|
-
since:
|
|
8406
|
+
since: import_zod33.z.string().min(1).optional().describe(
|
|
8130
8407
|
"Prior digest, or path to a prior `stamp --json`; only moved bases return, with changed ids when the baseline is a file."
|
|
8131
8408
|
)
|
|
8132
8409
|
}),
|
|
@@ -8212,16 +8489,16 @@ async function readBaseline(since) {
|
|
|
8212
8489
|
}
|
|
8213
8490
|
|
|
8214
8491
|
// src/commands/status.ts
|
|
8215
|
-
var
|
|
8492
|
+
var import_zod34 = require("zod");
|
|
8216
8493
|
var statusCommand = define({
|
|
8217
8494
|
name: "status",
|
|
8218
8495
|
tool: "kb_status",
|
|
8219
8496
|
usage: "status <concept-id> <status>",
|
|
8220
8497
|
description: "Move a record's status. Compare-and-swap: a concurrent change fails instead of being overwritten.",
|
|
8221
|
-
input:
|
|
8498
|
+
input: import_zod34.z.object({
|
|
8222
8499
|
bundlePath,
|
|
8223
8500
|
conceptId,
|
|
8224
|
-
status:
|
|
8501
|
+
status: import_zod34.z.enum(KB_RECORD_STATUSES)
|
|
8225
8502
|
}),
|
|
8226
8503
|
fromArgv: (argv, path) => ({
|
|
8227
8504
|
bundlePath: path,
|
|
@@ -8236,13 +8513,13 @@ var statusCommand = define({
|
|
|
8236
8513
|
});
|
|
8237
8514
|
|
|
8238
8515
|
// src/commands/supersede.ts
|
|
8239
|
-
var
|
|
8516
|
+
var import_zod35 = require("zod");
|
|
8240
8517
|
var supersedeCommand = define({
|
|
8241
8518
|
name: "supersede",
|
|
8242
8519
|
tool: "kb_supersede",
|
|
8243
8520
|
usage: "supersede <concept-id> <replacement-id>",
|
|
8244
8521
|
description: "Mark a record superseded by another, linked in both directions. Use instead of editing a record whose meaning changed.",
|
|
8245
|
-
input:
|
|
8522
|
+
input: import_zod35.z.object({ bundlePath, conceptId, replacementId: conceptId }),
|
|
8246
8523
|
fromArgv: (argv, path) => ({
|
|
8247
8524
|
bundlePath: path,
|
|
8248
8525
|
conceptId: argv[1],
|
|
@@ -8256,7 +8533,7 @@ var supersedeCommand = define({
|
|
|
8256
8533
|
});
|
|
8257
8534
|
|
|
8258
8535
|
// src/commands/sweep.ts
|
|
8259
|
-
var
|
|
8536
|
+
var import_zod36 = require("zod");
|
|
8260
8537
|
var TERMINAL = [
|
|
8261
8538
|
"resolved",
|
|
8262
8539
|
"rejected",
|
|
@@ -8267,15 +8544,15 @@ var sweepCommand = define({
|
|
|
8267
8544
|
tool: "kb_sweep",
|
|
8268
8545
|
usage: "sweep --tag <tag> --terminal [--dry-run]",
|
|
8269
8546
|
description: "Delete tagged records that are resolved, rejected or superseded. Refuses without --tag, keeps any record a surviving record still points at, and logs each deletion.",
|
|
8270
|
-
input:
|
|
8547
|
+
input: import_zod36.z.object({
|
|
8271
8548
|
bundlePath,
|
|
8272
|
-
tag:
|
|
8273
|
-
terminal:
|
|
8549
|
+
tag: import_zod36.z.string({ error: "sweep needs --tag: it never sweeps a whole base" }).min(1).describe("Only records carrying this tag are considered."),
|
|
8550
|
+
terminal: import_zod36.z.literal(true, {
|
|
8274
8551
|
error: "sweep needs --terminal: it deletes only settled records"
|
|
8275
8552
|
}).describe(
|
|
8276
8553
|
"Required. Names the only scope sweep deletes: resolved, rejected and superseded records."
|
|
8277
8554
|
),
|
|
8278
|
-
dryRun:
|
|
8555
|
+
dryRun: import_zod36.z.boolean().optional().describe("Report what would go, and delete nothing.")
|
|
8279
8556
|
}),
|
|
8280
8557
|
fromArgv: (argv, path) => ({
|
|
8281
8558
|
bundlePath: path,
|
|
@@ -8392,16 +8669,16 @@ function renderSweep(result) {
|
|
|
8392
8669
|
}
|
|
8393
8670
|
|
|
8394
8671
|
// src/commands/sync-instructions.ts
|
|
8395
|
-
var
|
|
8672
|
+
var import_zod37 = require("zod");
|
|
8396
8673
|
var syncInstructionsCommand = define({
|
|
8397
8674
|
name: "sync-instructions",
|
|
8398
8675
|
usage: "sync-instructions <file> [--profile NAME] [--budget N] [--full-under N]",
|
|
8399
8676
|
description: "CLI-only: plant the kb_context block between sentinel comments in AGENTS.md or CLAUDE.md, idempotently.",
|
|
8400
|
-
input:
|
|
8401
|
-
file:
|
|
8402
|
-
budgetTokens:
|
|
8403
|
-
fullUnderTokens:
|
|
8404
|
-
profile:
|
|
8677
|
+
input: import_zod37.z.object({
|
|
8678
|
+
file: import_zod37.z.string().min(1).describe("The instruction file to edit in place."),
|
|
8679
|
+
budgetTokens: import_zod37.z.number().int().positive().optional(),
|
|
8680
|
+
fullUnderTokens: import_zod37.z.number().int().positive().optional(),
|
|
8681
|
+
profile: import_zod37.z.string().optional()
|
|
8405
8682
|
}),
|
|
8406
8683
|
fromArgv: (argv) => {
|
|
8407
8684
|
const budget = argvFlag(argv, "--budget");
|
|
@@ -8427,17 +8704,17 @@ var syncInstructionsCommand = define({
|
|
|
8427
8704
|
});
|
|
8428
8705
|
|
|
8429
8706
|
// src/commands/trace.ts
|
|
8430
|
-
var
|
|
8707
|
+
var import_zod38 = require("zod");
|
|
8431
8708
|
var traceCommand = define({
|
|
8432
8709
|
name: "trace",
|
|
8433
8710
|
tool: "kb_trace",
|
|
8434
8711
|
usage: "trace <concept-id> [edges...]",
|
|
8435
8712
|
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".',
|
|
8436
|
-
input:
|
|
8713
|
+
input: import_zod38.z.object({
|
|
8437
8714
|
bundlePath,
|
|
8438
8715
|
conceptId,
|
|
8439
|
-
edges:
|
|
8440
|
-
depth:
|
|
8716
|
+
edges: import_zod38.z.array(import_zod38.z.enum(TRACE_EDGES)).optional(),
|
|
8717
|
+
depth: import_zod38.z.number().int().positive().optional()
|
|
8441
8718
|
}),
|
|
8442
8719
|
fromArgv: (argv, path) => ({
|
|
8443
8720
|
bundlePath: path,
|
|
@@ -8459,37 +8736,37 @@ var traceCommand = define({
|
|
|
8459
8736
|
});
|
|
8460
8737
|
|
|
8461
8738
|
// src/commands/types.ts
|
|
8462
|
-
var
|
|
8739
|
+
var import_zod39 = require("zod");
|
|
8463
8740
|
var typesCommand = define({
|
|
8464
8741
|
name: "types",
|
|
8465
8742
|
tool: "kb_types",
|
|
8466
8743
|
usage: "types",
|
|
8467
8744
|
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.",
|
|
8468
|
-
input:
|
|
8745
|
+
input: import_zod39.z.object({}),
|
|
8469
8746
|
fromArgv: () => ({}),
|
|
8470
8747
|
run: () => Promise.resolve(RECORD_TYPES)
|
|
8471
8748
|
});
|
|
8472
8749
|
|
|
8473
8750
|
// src/commands/unpin.ts
|
|
8474
|
-
var
|
|
8751
|
+
var import_zod40 = require("zod");
|
|
8475
8752
|
var unpinCommand = define({
|
|
8476
8753
|
name: "unpin",
|
|
8477
8754
|
tool: "kb_unpin",
|
|
8478
8755
|
usage: "unpin [bundle-path]",
|
|
8479
8756
|
description: "Remove a base from every manifest layer that holds it. Reports the layers touched.",
|
|
8480
|
-
input:
|
|
8757
|
+
input: import_zod40.z.object({ bundlePath }),
|
|
8481
8758
|
fromArgv: (argv, path) => ({ bundlePath: argv[1] ?? path }),
|
|
8482
8759
|
run: (_ctx, { bundlePath: path }) => unpinBase(process.cwd(), path)
|
|
8483
8760
|
});
|
|
8484
8761
|
|
|
8485
8762
|
// src/commands/validate.ts
|
|
8486
|
-
var
|
|
8763
|
+
var import_zod41 = require("zod");
|
|
8487
8764
|
var validateCommand = define({
|
|
8488
8765
|
name: "validate",
|
|
8489
8766
|
tool: "kb_validate",
|
|
8490
8767
|
usage: "validate",
|
|
8491
8768
|
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.",
|
|
8492
|
-
input:
|
|
8769
|
+
input: import_zod41.z.object({ bundlePath }),
|
|
8493
8770
|
fromArgv: (_argv, path) => ({ bundlePath: path }),
|
|
8494
8771
|
run: async ({ store }, { bundlePath: path }) => validateBundle(await store.list(path)),
|
|
8495
8772
|
// Warnings never fail the exit code; every other severity does.
|
|
@@ -8499,16 +8776,16 @@ var validateCommand = define({
|
|
|
8499
8776
|
});
|
|
8500
8777
|
|
|
8501
8778
|
// src/commands/verify.ts
|
|
8502
|
-
var
|
|
8779
|
+
var import_zod42 = require("zod");
|
|
8503
8780
|
var verifyCommand = define({
|
|
8504
8781
|
name: "verify",
|
|
8505
8782
|
tool: "kb_verify",
|
|
8506
8783
|
usage: "verify <concept-id> --note <text>",
|
|
8507
8784
|
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.",
|
|
8508
|
-
input:
|
|
8785
|
+
input: import_zod42.z.object({
|
|
8509
8786
|
bundlePath,
|
|
8510
8787
|
conceptId,
|
|
8511
|
-
note:
|
|
8788
|
+
note: import_zod42.z.string().refine((s) => s.trim().length > 0, {
|
|
8512
8789
|
message: "note must say what the check found"
|
|
8513
8790
|
})
|
|
8514
8791
|
}),
|
|
@@ -8528,15 +8805,15 @@ var verifyCommand = define({
|
|
|
8528
8805
|
});
|
|
8529
8806
|
|
|
8530
8807
|
// src/commands/write.ts
|
|
8531
|
-
var
|
|
8808
|
+
var import_zod43 = require("zod");
|
|
8532
8809
|
var writeCommand = define({
|
|
8533
8810
|
name: "write",
|
|
8534
8811
|
tool: "kb_write",
|
|
8535
8812
|
usage: "write <type> < record.json",
|
|
8536
8813
|
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.",
|
|
8537
|
-
input:
|
|
8814
|
+
input: import_zod43.z.object({
|
|
8538
8815
|
bundlePath,
|
|
8539
|
-
type:
|
|
8816
|
+
type: import_zod43.z.enum(KB_RECORD_TYPES),
|
|
8540
8817
|
input: composeInputSchema
|
|
8541
8818
|
}),
|
|
8542
8819
|
fromArgv: async (argv, path, stdin) => ({
|
|
@@ -8560,13 +8837,13 @@ var writeCommand = define({
|
|
|
8560
8837
|
});
|
|
8561
8838
|
|
|
8562
8839
|
// src/commands/write-decision.ts
|
|
8563
|
-
var
|
|
8840
|
+
var import_zod44 = require("zod");
|
|
8564
8841
|
var writeDecisionCommand = define({
|
|
8565
8842
|
name: "write-decision",
|
|
8566
8843
|
tool: "kb_write_decision",
|
|
8567
8844
|
usage: "write-decision < decision.json",
|
|
8568
8845
|
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.",
|
|
8569
|
-
input:
|
|
8846
|
+
input: import_zod44.z.object({ bundlePath, input: decisionInputSchema }),
|
|
8570
8847
|
fromArgv: async (_argv, path, stdin) => ({
|
|
8571
8848
|
bundlePath: path,
|
|
8572
8849
|
input: JSON.parse(await stdin())
|
|
@@ -8596,6 +8873,7 @@ var KB_COMMANDS = [
|
|
|
8596
8873
|
answerCommand,
|
|
8597
8874
|
verifyCommand,
|
|
8598
8875
|
anchorResolveCommand,
|
|
8876
|
+
anchorSetCommand,
|
|
8599
8877
|
reassessCommand,
|
|
8600
8878
|
promoteCommand,
|
|
8601
8879
|
loadCommand,
|
|
@@ -8632,7 +8910,7 @@ var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
|
8632
8910
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
8633
8911
|
|
|
8634
8912
|
// src/version.ts
|
|
8635
|
-
var VERSION = true ? "0.1.
|
|
8913
|
+
var VERSION = true ? "0.1.22" : "0.0.0-dev";
|
|
8636
8914
|
|
|
8637
8915
|
// src/mcp.ts
|
|
8638
8916
|
function createKbMcpServer() {
|
|
@@ -8813,6 +9091,7 @@ function usage() {
|
|
|
8813
9091
|
KB_RECORD_STATUSES,
|
|
8814
9092
|
KB_RECORD_TYPES,
|
|
8815
9093
|
KB_SLUG_PATTERN,
|
|
9094
|
+
KbAnchorSetDuplicateError,
|
|
8816
9095
|
KbBaseFrozenError,
|
|
8817
9096
|
KbClassifyInputError,
|
|
8818
9097
|
KbInvalidConceptIdError,
|
|
@@ -8843,6 +9122,8 @@ function usage() {
|
|
|
8843
9122
|
adjudicate,
|
|
8844
9123
|
anchorFilePath,
|
|
8845
9124
|
anchorOnHunk,
|
|
9125
|
+
anchorSetInputSchema,
|
|
9126
|
+
applyAnchorSet,
|
|
8846
9127
|
assertBaseNotFrozen,
|
|
8847
9128
|
backlinks,
|
|
8848
9129
|
buildContext,
|
|
@@ -8876,19 +9157,23 @@ function usage() {
|
|
|
8876
9157
|
isNoDecisionRecord,
|
|
8877
9158
|
isReviewTag,
|
|
8878
9159
|
kbActorStampSchema,
|
|
9160
|
+
kbAnchorLocatorSchema,
|
|
8879
9161
|
kbAnchorSchema,
|
|
8880
9162
|
kbAnchorSpanSchema,
|
|
8881
9163
|
kbAnchorWriteSchema,
|
|
8882
9164
|
kbConceptIdSchema,
|
|
8883
9165
|
kbJsonSchemas,
|
|
8884
9166
|
kbLinkSchema,
|
|
9167
|
+
kbLogAnchorChangeSchema,
|
|
8885
9168
|
kbLogEntrySchema,
|
|
9169
|
+
kbLogEntryWriteSchema,
|
|
8886
9170
|
kbRecordFrontmatterSchema,
|
|
8887
9171
|
kbSourceSchema,
|
|
8888
9172
|
kbVerifiedEventSchema,
|
|
8889
9173
|
languageForFile,
|
|
8890
9174
|
listPins,
|
|
8891
9175
|
loadQmd,
|
|
9176
|
+
locatorOf,
|
|
8892
9177
|
matchToDiff,
|
|
8893
9178
|
matchesTags,
|
|
8894
9179
|
mergedContextBudgets,
|