@saasontools/strauss-kb 0.1.21 → 0.1.23
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 +14 -4
- package/dist/{chunk-WVJULVGY.js → chunk-3EZ3PAPA.js} +2 -2
- package/dist/{chunk-VXWN62HT.js → chunk-5IPQVXCM.js} +2 -2
- package/dist/{chunk-XBZ7G5R5.js → chunk-XENLCPL5.js} +2509 -2169
- package/dist/chunk-XENLCPL5.js.map +1 -0
- package/dist/cli-main.cjs +1516 -1184
- package/dist/cli-main.cjs.map +1 -1
- package/dist/cli-main.js +2 -2
- package/dist/index.cjs +714 -367
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +328 -17
- package/dist/index.d.ts +328 -17
- package/dist/index.js +17 -3
- package/dist/mcp-main.cjs +1516 -1184
- package/dist/mcp-main.cjs.map +1 -1
- package/dist/mcp-main.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-XBZ7G5R5.js.map +0 -1
- /package/dist/{chunk-WVJULVGY.js.map → chunk-3EZ3PAPA.js.map} +0 -0
- /package/dist/{chunk-VXWN62HT.js.map → chunk-5IPQVXCM.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,6 +448,7 @@ 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";
|
|
437
453
|
ErrorTypes2["KbFlagConflict"] = "KbFlagConflict";
|
|
438
454
|
ErrorTypes2["KbInvalidActor"] = "KbInvalidActor";
|
|
@@ -2653,7 +2669,12 @@ var import_node_path6 = require("path");
|
|
|
2653
2669
|
// src/kb-log.ts
|
|
2654
2670
|
var import_zod3 = require("zod");
|
|
2655
2671
|
var LOG_FILE = "log.jsonl";
|
|
2656
|
-
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({
|
|
2657
2678
|
// Validated, not just `min(1)`: `at` is a sort key (see `parseLog`
|
|
2658
2679
|
// below), and a value that isn't actually chronological — a Unix
|
|
2659
2680
|
// timestamp, a human-typed date, garbage — would sort wrong without
|
|
@@ -2670,10 +2691,20 @@ var kbLogEntrySchema = import_zod3.z.object({
|
|
|
2670
2691
|
* The operation's other end, where it has one: a second concept id for
|
|
2671
2692
|
* supersession, the other base's path for promotion.
|
|
2672
2693
|
*/
|
|
2673
|
-
target: import_zod3.z.string().min(1).optional()
|
|
2674
|
-
|
|
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();
|
|
2675
2706
|
function renderLogEntry(entry) {
|
|
2676
|
-
return `${JSON.stringify(
|
|
2707
|
+
return `${JSON.stringify(kbLogEntryWriteSchema.parse(entry))}
|
|
2677
2708
|
`;
|
|
2678
2709
|
}
|
|
2679
2710
|
var CONFLICT_MARKER = /^(<{7}|\|{7}|={7}|>{7})/;
|
|
@@ -3506,23 +3537,31 @@ var KbStore = class {
|
|
|
3506
3537
|
);
|
|
3507
3538
|
}
|
|
3508
3539
|
/**
|
|
3509
|
-
* Replaces a record's anchors
|
|
3510
|
-
*
|
|
3511
|
-
*
|
|
3512
|
-
*
|
|
3513
|
-
* stale entries the resolution pass deliberately dropped.
|
|
3514
|
-
*
|
|
3515
|
-
* Through the write schema: this is a write, and a defect a hand-edit put in
|
|
3516
|
-
* 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`.
|
|
3517
3544
|
*/
|
|
3518
3545
|
async updateAnchors(bundlePath2, conceptId2, anchors, actor = "unknown") {
|
|
3519
3546
|
assertActor(actor);
|
|
3520
|
-
|
|
3547
|
+
let entry = {
|
|
3548
|
+
operation: "anchor-resolve",
|
|
3549
|
+
by: actor
|
|
3550
|
+
};
|
|
3521
3551
|
return this.mutate(
|
|
3522
3552
|
bundlePath2,
|
|
3523
3553
|
conceptId2,
|
|
3524
|
-
(frontmatter) =>
|
|
3525
|
-
|
|
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
|
|
3526
3565
|
);
|
|
3527
3566
|
}
|
|
3528
3567
|
/**
|
|
@@ -3976,7 +4015,10 @@ ${answer}
|
|
|
3976
4015
|
throw new KbWriteConflictError(conceptId2);
|
|
3977
4016
|
}
|
|
3978
4017
|
await this.publish(target, contents, true, conceptId2);
|
|
3979
|
-
await this.record(this.root(bundlePath2), {
|
|
4018
|
+
await this.record(this.root(bundlePath2), {
|
|
4019
|
+
...typeof entry === "function" ? entry() : entry,
|
|
4020
|
+
conceptId: conceptId2
|
|
4021
|
+
});
|
|
3980
4022
|
return { conceptId: conceptId2, frontmatter, body };
|
|
3981
4023
|
}
|
|
3982
4024
|
/**
|
|
@@ -4193,6 +4235,107 @@ function assertActor(actor, { named = false } = {}) {
|
|
|
4193
4235
|
|
|
4194
4236
|
// src/compose.ts
|
|
4195
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
|
|
4196
4339
|
var composeLinkSchema = import_zod4.z.object({
|
|
4197
4340
|
target: kbConceptIdSchema,
|
|
4198
4341
|
rel: import_zod4.z.enum(KB_LINK_RELS)
|
|
@@ -4264,7 +4407,9 @@ function composeRecord(type, input, writtenBy, writtenAt) {
|
|
|
4264
4407
|
strauss_status: spec.initialStatus
|
|
4265
4408
|
};
|
|
4266
4409
|
if (parsed.stale_after) frontmatter.stale_after = parsed.stale_after;
|
|
4267
|
-
if (parsed.anchors?.length)
|
|
4410
|
+
if (parsed.anchors?.length) {
|
|
4411
|
+
frontmatter.strauss_anchors = applyAnchorSet([], parsed.anchors).anchors;
|
|
4412
|
+
}
|
|
4268
4413
|
if (parsed.verify?.length) frontmatter.strauss_verify = parsed.verify;
|
|
4269
4414
|
if (parsed.tags?.length) frontmatter.tags = parsed.tags;
|
|
4270
4415
|
if (parsed.sources?.length) frontmatter.sources = parsed.sources;
|
|
@@ -5622,8 +5767,8 @@ function selectDecisions(records) {
|
|
|
5622
5767
|
);
|
|
5623
5768
|
}
|
|
5624
5769
|
|
|
5625
|
-
// src/commands/anchor-resolve.ts
|
|
5626
|
-
var
|
|
5770
|
+
// src/commands/anchor-resolve/command.ts
|
|
5771
|
+
var import_zod10 = require("zod");
|
|
5627
5772
|
|
|
5628
5773
|
// src/commands/model.ts
|
|
5629
5774
|
var import_zod8 = require("zod");
|
|
@@ -5689,26 +5834,270 @@ function argvPositional(argv, ...names) {
|
|
|
5689
5834
|
);
|
|
5690
5835
|
}
|
|
5691
5836
|
|
|
5692
|
-
// src/commands/anchor-resolve.ts
|
|
5837
|
+
// src/commands/anchor-resolve/apply.ts
|
|
5838
|
+
var import_zod9 = require("zod");
|
|
5839
|
+
async function baseFrozen(cwd, bundlePath2) {
|
|
5840
|
+
try {
|
|
5841
|
+
await assertBaseNotFrozen(cwd, bundlePath2);
|
|
5842
|
+
return false;
|
|
5843
|
+
} catch (caught) {
|
|
5844
|
+
if (!(caught instanceof KbBaseFrozenError)) throw caught;
|
|
5845
|
+
return true;
|
|
5846
|
+
}
|
|
5847
|
+
}
|
|
5848
|
+
async function applyPlan(plans, target) {
|
|
5849
|
+
if (!plans.some((plan) => plan.write)) {
|
|
5850
|
+
return { results: plans.map((plan) => plan.finding) };
|
|
5851
|
+
}
|
|
5852
|
+
let failure = target.frozen ? "frozen" : void 0;
|
|
5853
|
+
let error;
|
|
5854
|
+
if (!failure) {
|
|
5855
|
+
try {
|
|
5856
|
+
await target.store.updateAnchors(
|
|
5857
|
+
target.bundlePath,
|
|
5858
|
+
target.conceptId,
|
|
5859
|
+
plans.map((plan) => plan.anchor),
|
|
5860
|
+
target.actor
|
|
5861
|
+
);
|
|
5862
|
+
} catch (caught) {
|
|
5863
|
+
if (caught instanceof BaseError || caught instanceof import_zod9.z.ZodError) {
|
|
5864
|
+
throw caught;
|
|
5865
|
+
}
|
|
5866
|
+
failure = "write-failed";
|
|
5867
|
+
error = clamp(caught instanceof Error ? caught.message : String(caught));
|
|
5868
|
+
}
|
|
5869
|
+
}
|
|
5870
|
+
return {
|
|
5871
|
+
results: plans.map((plan) => settle(plan, failure)),
|
|
5872
|
+
...error ? { error } : {}
|
|
5873
|
+
};
|
|
5874
|
+
}
|
|
5875
|
+
function settle(plan, failure) {
|
|
5876
|
+
if (!plan.write) return plan.finding;
|
|
5877
|
+
if (failure) {
|
|
5878
|
+
return { ...plan.finding, outcome: "failed", outcomeReason: failure };
|
|
5879
|
+
}
|
|
5880
|
+
if (plan.write === "refresh") return plan.finding;
|
|
5881
|
+
return plan.write === "stamp" ? { ...plan.finding, state: "stamped", outcome: "applied" } : { ...plan.finding, outcome: "applied", rebaselined: true };
|
|
5882
|
+
}
|
|
5883
|
+
function clamp(message) {
|
|
5884
|
+
const line = message.split("\n")[0] ?? "";
|
|
5885
|
+
return line.length > 200 ? `${line.slice(0, 199)}\u2026` : line;
|
|
5886
|
+
}
|
|
5887
|
+
|
|
5888
|
+
// src/commands/anchor-resolve/sources.ts
|
|
5889
|
+
async function readSources(anchors, root, offline) {
|
|
5890
|
+
const origin = new LazyOrigin(root);
|
|
5891
|
+
if (anchors.some((anchor) => anchor.repo)) await origin.prime();
|
|
5892
|
+
const foreign = new Map(
|
|
5893
|
+
anchors.map((anchor) => [anchor, origin.isForeign(anchor)])
|
|
5894
|
+
);
|
|
5895
|
+
const local = anchors.filter(
|
|
5896
|
+
(anchor) => !foreign.get(anchor) && anchor.side !== "old"
|
|
5897
|
+
);
|
|
5898
|
+
const committed = anchors.filter(
|
|
5899
|
+
(anchor) => !foreign.get(anchor) && anchor.side === "old"
|
|
5900
|
+
);
|
|
5901
|
+
const remote = anchors.filter((anchor) => foreign.get(anchor));
|
|
5902
|
+
const reads = await readAnchorFiles(
|
|
5903
|
+
local.map((anchor) => anchor.file),
|
|
5904
|
+
anchorFileReader(root)
|
|
5905
|
+
);
|
|
5906
|
+
const atRef = await readCommitted(root, committed);
|
|
5907
|
+
const blobs = await readRemoteAnchors(remote.flatMap(remoteWants), {
|
|
5908
|
+
offline
|
|
5909
|
+
});
|
|
5910
|
+
const sources = /* @__PURE__ */ new Map();
|
|
5911
|
+
for (const anchor of local) {
|
|
5912
|
+
const read = reads.get(anchor.file);
|
|
5913
|
+
sources.set(
|
|
5914
|
+
anchor,
|
|
5915
|
+
read.ok ? { ok: true, source: read.source } : { ok: false, reason: read.reason }
|
|
5916
|
+
);
|
|
5917
|
+
}
|
|
5918
|
+
for (const anchor of committed) {
|
|
5919
|
+
const read = atRef.get(atRefKey(anchor));
|
|
5920
|
+
sources.set(
|
|
5921
|
+
anchor,
|
|
5922
|
+
read.ok ? { ok: true, source: read.source } : { ok: false, reason: read.reason }
|
|
5923
|
+
);
|
|
5924
|
+
}
|
|
5925
|
+
for (const anchor of remote) {
|
|
5926
|
+
const repo = anchor.repo;
|
|
5927
|
+
const key2 = normalizeRepoUrl(repo);
|
|
5928
|
+
const atDefault = blobs.get(wantKey(key2, void 0, anchor.file));
|
|
5929
|
+
const primary = anchor.ref ? blobs.get(wantKey(key2, anchor.ref, anchor.file)) : atDefault;
|
|
5930
|
+
if (!primary?.ok) {
|
|
5931
|
+
sources.set(anchor, {
|
|
5932
|
+
ok: false,
|
|
5933
|
+
reason: primary?.ok === false ? primary.reason : "remote-unreachable",
|
|
5934
|
+
repo
|
|
5935
|
+
});
|
|
5936
|
+
continue;
|
|
5937
|
+
}
|
|
5938
|
+
sources.set(anchor, {
|
|
5939
|
+
ok: true,
|
|
5940
|
+
source: primary.source,
|
|
5941
|
+
repo,
|
|
5942
|
+
...anchor.ref && atDefault?.ok ? { head: atDefault.source } : {}
|
|
5943
|
+
});
|
|
5944
|
+
}
|
|
5945
|
+
return sources;
|
|
5946
|
+
}
|
|
5947
|
+
|
|
5948
|
+
// src/commands/anchor-resolve/plan.ts
|
|
5949
|
+
async function planAnchors(anchors, options) {
|
|
5950
|
+
const { root, offline, rebaseline, restamp, check, frozen, now } = options;
|
|
5951
|
+
const sources = await readSources(anchors, root, offline);
|
|
5952
|
+
const resolvers = defaultAnchorResolvers({ offline });
|
|
5953
|
+
await prepareResolvers(
|
|
5954
|
+
resolvers,
|
|
5955
|
+
anchors.map((anchor) => anchor.file)
|
|
5956
|
+
);
|
|
5957
|
+
const plans = [];
|
|
5958
|
+
for (const anchor of anchors) {
|
|
5959
|
+
const base2 = {
|
|
5960
|
+
file: anchor.file,
|
|
5961
|
+
...anchor.symbol ? { symbol: anchor.symbol } : {},
|
|
5962
|
+
...anchor.side === "old" ? { side: "old" } : {},
|
|
5963
|
+
// Carried onto unresolved findings too: an anchor that once hashed
|
|
5964
|
+
// and now resolves to nothing is a broken anchor, and the exit code
|
|
5965
|
+
// has to be able to tell it from one nobody ever stamped.
|
|
5966
|
+
...anchor.hash ? { storedHash: anchor.hash } : {}
|
|
5967
|
+
};
|
|
5968
|
+
const source = sources.get(anchor);
|
|
5969
|
+
if (source.repo) base2.repo = source.repo;
|
|
5970
|
+
if (!source.ok) {
|
|
5971
|
+
plans.push({
|
|
5972
|
+
finding: { ...base2, state: "unresolved", reason: source.reason },
|
|
5973
|
+
anchor
|
|
5974
|
+
});
|
|
5975
|
+
continue;
|
|
5976
|
+
}
|
|
5977
|
+
const outcome = resolveAnchorSpan(source.source, anchor, resolvers);
|
|
5978
|
+
if (!outcome.ok) {
|
|
5979
|
+
plans.push({
|
|
5980
|
+
finding: { ...base2, state: "unresolved", reason: outcome.reason },
|
|
5981
|
+
anchor
|
|
5982
|
+
});
|
|
5983
|
+
continue;
|
|
5984
|
+
}
|
|
5985
|
+
const resolved = outcome.span;
|
|
5986
|
+
const producedBy = outcome.resolver;
|
|
5987
|
+
const { hash: currentHash, kind } = anchorHashOf(anchor, outcome);
|
|
5988
|
+
const currentLines = resolved.endLine - resolved.startLine + 1;
|
|
5989
|
+
const stampedKind = outcome.normalized ? "ast" : "raw";
|
|
5990
|
+
const stampedHash = outcome.normalized ? anchorHashOf({ ...anchor, hash: void 0 }, outcome).hash : currentHash;
|
|
5991
|
+
const stamped = {
|
|
5992
|
+
...anchor,
|
|
5993
|
+
hash: stampedHash,
|
|
5994
|
+
hash_kind: stampedKind,
|
|
5995
|
+
lines: currentLines,
|
|
5996
|
+
resolved_at: now(),
|
|
5997
|
+
...producedBy ? { resolver: producedBy } : {}
|
|
5998
|
+
};
|
|
5999
|
+
const pinned = anchor.ref !== void 0 && source.repo !== void 0;
|
|
6000
|
+
if (!anchor.hash) {
|
|
6001
|
+
plans.push({
|
|
6002
|
+
finding: {
|
|
6003
|
+
...base2,
|
|
6004
|
+
state: "unstamped",
|
|
6005
|
+
currentHash: stampedHash,
|
|
6006
|
+
hashKind: stampedKind,
|
|
6007
|
+
...producedBy ? { resolver: producedBy } : {}
|
|
6008
|
+
},
|
|
6009
|
+
anchor: check ? anchor : stamped,
|
|
6010
|
+
...check ? {} : { write: "stamp" }
|
|
6011
|
+
});
|
|
6012
|
+
continue;
|
|
6013
|
+
}
|
|
6014
|
+
if (anchor.hash !== currentHash) {
|
|
6015
|
+
plans.push({
|
|
6016
|
+
finding: {
|
|
6017
|
+
...base2,
|
|
6018
|
+
state: "drifted",
|
|
6019
|
+
currentHash,
|
|
6020
|
+
hashKind: kind,
|
|
6021
|
+
diffSize: lineDelta(anchor, currentLines),
|
|
6022
|
+
...producedBy ? { resolver: producedBy } : {},
|
|
6023
|
+
// A regex-stamped anchor re-read by tree-sitter drifts because the
|
|
6024
|
+
// resolver changed, not because the code did.
|
|
6025
|
+
...resolverChanged(source.source, anchor, producedBy) ? { reason: "resolver-changed" } : {},
|
|
6026
|
+
...pinned ? { remoteState: "drifted-from-ref" } : {}
|
|
6027
|
+
},
|
|
6028
|
+
anchor: rebaseline && !check ? stamped : anchor,
|
|
6029
|
+
...rebaseline && !check ? { write: "rebaseline" } : {}
|
|
6030
|
+
});
|
|
6031
|
+
continue;
|
|
6032
|
+
}
|
|
6033
|
+
const onDefault = pinned ? headHash(source, anchor, resolvers) : void 0;
|
|
6034
|
+
if (onDefault && onDefault.hash !== anchor.hash) {
|
|
6035
|
+
plans.push({
|
|
6036
|
+
finding: {
|
|
6037
|
+
...base2,
|
|
6038
|
+
state: "drifted",
|
|
6039
|
+
currentHash: onDefault.hash,
|
|
6040
|
+
diffSize: lineDelta(anchor, onDefault.lines),
|
|
6041
|
+
remoteState: "drifted-on-default",
|
|
6042
|
+
...rebaseline ? {
|
|
6043
|
+
outcome: "skipped",
|
|
6044
|
+
outcomeReason: "pinned-ref"
|
|
6045
|
+
} : {}
|
|
6046
|
+
},
|
|
6047
|
+
anchor
|
|
6048
|
+
});
|
|
6049
|
+
continue;
|
|
6050
|
+
}
|
|
6051
|
+
const backfill = anchor.resolved_at === void 0 && !frozen;
|
|
6052
|
+
const refresh = !check && (restamp || backfill);
|
|
6053
|
+
plans.push({
|
|
6054
|
+
finding: {
|
|
6055
|
+
...base2,
|
|
6056
|
+
state: "match",
|
|
6057
|
+
currentHash,
|
|
6058
|
+
hashKind: kind,
|
|
6059
|
+
...producedBy ? { resolver: producedBy } : {},
|
|
6060
|
+
...pinned ? { remoteState: "matches-ref" } : {}
|
|
6061
|
+
},
|
|
6062
|
+
anchor: refresh ? { ...anchor, resolved_at: now() } : anchor,
|
|
6063
|
+
...refresh ? { write: "refresh" } : {}
|
|
6064
|
+
});
|
|
6065
|
+
}
|
|
6066
|
+
return plans;
|
|
6067
|
+
}
|
|
6068
|
+
function lineDelta(anchor, current) {
|
|
6069
|
+
return anchor.lines === void 0 ? null : Math.abs(current - anchor.lines);
|
|
6070
|
+
}
|
|
6071
|
+
function headHash(source, anchor, resolvers) {
|
|
6072
|
+
if (source.head === void 0) return void 0;
|
|
6073
|
+
const outcome = resolveAnchorSpan(source.head, anchor, resolvers);
|
|
6074
|
+
if (!outcome.ok) return void 0;
|
|
6075
|
+
return {
|
|
6076
|
+
hash: hashAnchorText(outcome.span.text),
|
|
6077
|
+
lines: outcome.span.endLine - outcome.span.startLine + 1
|
|
6078
|
+
};
|
|
6079
|
+
}
|
|
6080
|
+
|
|
6081
|
+
// src/commands/anchor-resolve/command.ts
|
|
5693
6082
|
var anchorResolveCommand = define({
|
|
5694
6083
|
name: "anchor-resolve",
|
|
5695
6084
|
tool: "kb_anchor_resolve",
|
|
5696
6085
|
usage: "anchor-resolve <concept-id> [--repo-root <path>] [--offline] [--rebaseline] [--restamp] [--check]",
|
|
5697
|
-
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.
|
|
5698
|
-
input:
|
|
6086
|
+
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. Each result says what it compared and whether the write applied.",
|
|
6087
|
+
input: import_zod10.z.object({
|
|
5699
6088
|
bundlePath,
|
|
5700
6089
|
conceptId,
|
|
5701
|
-
repoRoot:
|
|
5702
|
-
offline:
|
|
6090
|
+
repoRoot: import_zod10.z.string().min(1).optional(),
|
|
6091
|
+
offline: import_zod10.z.boolean().optional().describe(
|
|
5703
6092
|
"Resolve foreign anchors from the local repo cache only, never fetching."
|
|
5704
6093
|
),
|
|
5705
|
-
rebaseline:
|
|
6094
|
+
rebaseline: import_zod10.z.boolean().optional().describe(
|
|
5706
6095
|
"Accept the current code as the new baseline for anchors that drifted."
|
|
5707
6096
|
),
|
|
5708
|
-
restamp:
|
|
6097
|
+
restamp: import_zod10.z.boolean().optional().describe(
|
|
5709
6098
|
"Refresh `resolved_at` on anchors that already match. Off by default, so a green run writes nothing."
|
|
5710
6099
|
),
|
|
5711
|
-
check:
|
|
6100
|
+
check: import_zod10.z.boolean().optional().describe(
|
|
5712
6101
|
"Resolve and report only: no hash, no `resolved_at`, no log entry."
|
|
5713
6102
|
)
|
|
5714
6103
|
}),
|
|
@@ -5747,226 +6136,176 @@ var anchorResolveCommand = define({
|
|
|
5747
6136
|
note: "record has no anchors"
|
|
5748
6137
|
};
|
|
5749
6138
|
}
|
|
5750
|
-
const
|
|
5751
|
-
const
|
|
5752
|
-
|
|
5753
|
-
|
|
5754
|
-
|
|
5755
|
-
|
|
5756
|
-
|
|
5757
|
-
|
|
5758
|
-
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
|
|
5769
|
-
const source = sources.get(anchor);
|
|
5770
|
-
if (source.repo) base2.repo = source.repo;
|
|
5771
|
-
if (!source.ok) {
|
|
5772
|
-
results.push({ ...base2, state: "unresolved", reason: source.reason });
|
|
5773
|
-
updated.push(anchor);
|
|
5774
|
-
continue;
|
|
5775
|
-
}
|
|
5776
|
-
const outcome = resolveAnchorSpan(source.source, anchor, resolvers);
|
|
5777
|
-
if (!outcome.ok) {
|
|
5778
|
-
results.push({
|
|
5779
|
-
...base2,
|
|
5780
|
-
state: "unresolved",
|
|
5781
|
-
reason: outcome.reason
|
|
5782
|
-
});
|
|
5783
|
-
updated.push(anchor);
|
|
5784
|
-
continue;
|
|
5785
|
-
}
|
|
5786
|
-
const resolved = outcome.span;
|
|
5787
|
-
const producedBy = outcome.resolver;
|
|
5788
|
-
const { hash: currentHash, kind } = anchorHashOf(anchor, outcome);
|
|
5789
|
-
const currentLines = resolved.endLine - resolved.startLine + 1;
|
|
5790
|
-
const stampedKind = outcome.normalized ? "ast" : "raw";
|
|
5791
|
-
const stampedHash = outcome.normalized ? anchorHashOf({ ...anchor, hash: void 0 }, outcome).hash : currentHash;
|
|
5792
|
-
const stamped = {
|
|
5793
|
-
...anchor,
|
|
5794
|
-
hash: stampedHash,
|
|
5795
|
-
hash_kind: stampedKind,
|
|
5796
|
-
lines: currentLines,
|
|
5797
|
-
resolved_at: now(),
|
|
5798
|
-
...producedBy ? { resolver: producedBy } : {}
|
|
5799
|
-
};
|
|
5800
|
-
const pinned = anchor.ref !== void 0 && source.repo !== void 0;
|
|
5801
|
-
if (!anchor.hash) {
|
|
5802
|
-
results.push({
|
|
5803
|
-
...base2,
|
|
5804
|
-
state: check ? "unstamped" : "stamped",
|
|
5805
|
-
currentHash: stampedHash,
|
|
5806
|
-
hashKind: stampedKind,
|
|
5807
|
-
...producedBy ? { resolver: producedBy } : {}
|
|
5808
|
-
});
|
|
5809
|
-
updated.push(stamped);
|
|
5810
|
-
dirty = true;
|
|
5811
|
-
continue;
|
|
5812
|
-
}
|
|
5813
|
-
if (anchor.hash !== currentHash) {
|
|
5814
|
-
results.push({
|
|
5815
|
-
...base2,
|
|
5816
|
-
state: "drifted",
|
|
5817
|
-
currentHash,
|
|
5818
|
-
hashKind: kind,
|
|
5819
|
-
diffSize: lineDelta(anchor, currentLines),
|
|
5820
|
-
...producedBy ? { resolver: producedBy } : {},
|
|
5821
|
-
// A regex-stamped anchor re-read by tree-sitter drifts because the
|
|
5822
|
-
// resolver changed, not because the code did.
|
|
5823
|
-
...resolverChanged(source.source, anchor, producedBy) ? { reason: "resolver-changed" } : {},
|
|
5824
|
-
...pinned ? { remoteState: "drifted-from-ref" } : {},
|
|
5825
|
-
...rebaseline ? { rebaselined: true } : {}
|
|
5826
|
-
});
|
|
5827
|
-
updated.push(rebaseline ? stamped : anchor);
|
|
5828
|
-
if (rebaseline) dirty = true;
|
|
5829
|
-
continue;
|
|
5830
|
-
}
|
|
5831
|
-
const onDefault = pinned ? headHash(source, anchor, resolvers) : void 0;
|
|
5832
|
-
if (onDefault && onDefault.hash !== anchor.hash) {
|
|
5833
|
-
results.push({
|
|
5834
|
-
...base2,
|
|
5835
|
-
state: "drifted",
|
|
5836
|
-
currentHash: onDefault.hash,
|
|
5837
|
-
diffSize: lineDelta(anchor, onDefault.lines),
|
|
5838
|
-
remoteState: "drifted-on-default"
|
|
5839
|
-
});
|
|
5840
|
-
updated.push(anchor);
|
|
5841
|
-
continue;
|
|
5842
|
-
}
|
|
5843
|
-
results.push({
|
|
5844
|
-
...base2,
|
|
5845
|
-
state: "match",
|
|
5846
|
-
currentHash,
|
|
5847
|
-
hashKind: kind,
|
|
5848
|
-
...producedBy ? { resolver: producedBy } : {},
|
|
5849
|
-
...pinned ? { remoteState: "matches-ref" } : {}
|
|
5850
|
-
});
|
|
5851
|
-
const refresh = restamp || anchor.resolved_at === void 0;
|
|
5852
|
-
updated.push(refresh ? { ...anchor, resolved_at: now() } : anchor);
|
|
5853
|
-
if (refresh) dirty = true;
|
|
5854
|
-
}
|
|
5855
|
-
let frozen = false;
|
|
5856
|
-
if (dirty && !check) {
|
|
5857
|
-
try {
|
|
5858
|
-
await assertBaseNotFrozen(process.cwd(), path);
|
|
5859
|
-
} catch (error) {
|
|
5860
|
-
if (!(error instanceof KbBaseFrozenError)) throw error;
|
|
5861
|
-
frozen = true;
|
|
5862
|
-
}
|
|
5863
|
-
if (!frozen) await store.updateAnchors(path, id, updated, actor);
|
|
5864
|
-
}
|
|
5865
|
-
const frozenNote = frozen ? { frozen: true, note: "base is frozen: nothing was stamped" } : {};
|
|
6139
|
+
const frozen = check ? false : await baseFrozen(process.cwd(), path);
|
|
6140
|
+
const plans = await planAnchors(anchors, {
|
|
6141
|
+
root,
|
|
6142
|
+
offline: offline === true,
|
|
6143
|
+
rebaseline: rebaseline === true,
|
|
6144
|
+
restamp: restamp === true,
|
|
6145
|
+
check: check === true,
|
|
6146
|
+
frozen,
|
|
6147
|
+
now
|
|
6148
|
+
});
|
|
6149
|
+
const applied = await applyPlan(plans, {
|
|
6150
|
+
store,
|
|
6151
|
+
actor,
|
|
6152
|
+
bundlePath: path,
|
|
6153
|
+
conceptId: id,
|
|
6154
|
+
frozen
|
|
6155
|
+
});
|
|
6156
|
+
const results = applied.results;
|
|
6157
|
+
const refused = frozen && plans.some((plan) => plan.write);
|
|
5866
6158
|
const hints = grammarHints();
|
|
5867
6159
|
const hintNote = hints.length ? { hints } : {};
|
|
5868
6160
|
const unreachable = results.filter(
|
|
5869
6161
|
(entry) => isUncheckedReason(entry.reason)
|
|
5870
6162
|
).length;
|
|
5871
6163
|
const matches3 = results.filter((entry) => entry.state === "match").length;
|
|
5872
|
-
const note =
|
|
6164
|
+
const note = [
|
|
6165
|
+
unreachable ? `${matches3}/${results.length - unreachable} anchors match, ${unreachable} unreachable` : "",
|
|
6166
|
+
refused ? "base is frozen: nothing was stamped" : "",
|
|
6167
|
+
applied.error ? `nothing was written: ${applied.error}` : ""
|
|
6168
|
+
].filter(Boolean).join("; ");
|
|
5873
6169
|
return {
|
|
5874
6170
|
conceptId: id,
|
|
5875
6171
|
results,
|
|
5876
|
-
...
|
|
5877
|
-
...
|
|
6172
|
+
...note ? { note } : {},
|
|
6173
|
+
...refused ? { frozen: true } : {},
|
|
5878
6174
|
...hintNote
|
|
5879
6175
|
};
|
|
5880
6176
|
},
|
|
6177
|
+
// Drift is a finding until a write settles it: a rebaseline the base took is
|
|
6178
|
+
// the answer to the drift it reports, while one refused, skipped, or never
|
|
6179
|
+
// asked for leaves the gate exactly what it was meant to catch.
|
|
6180
|
+
//
|
|
5881
6181
|
// A stored hash that no longer resolves is a broken anchor, not an absence:
|
|
5882
|
-
// the file was deleted or the symbol renamed
|
|
5883
|
-
//
|
|
5884
|
-
//
|
|
5885
|
-
//
|
|
5886
|
-
// would gate on work this command did not do.
|
|
6182
|
+
// the file was deleted or the symbol renamed. An anchor nobody ever stamped
|
|
6183
|
+
// is still just unstamped, and one whose remote nothing could reach was
|
|
6184
|
+
// never checked — failing CI on either would gate on work this command did
|
|
6185
|
+
// not do.
|
|
5887
6186
|
failsWhen: (result) => result.results.some(
|
|
5888
|
-
(entry) => entry.state === "drifted" || entry.state === "unresolved" && entry.storedHash !== void 0 && !isUncheckedReason(entry.reason)
|
|
6187
|
+
(entry) => entry.outcome === "failed" || entry.outcome === "skipped" || entry.state === "drifted" && entry.outcome !== "applied" || entry.state === "unresolved" && entry.storedHash !== void 0 && !isUncheckedReason(entry.reason)
|
|
5889
6188
|
)
|
|
5890
6189
|
});
|
|
5891
|
-
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
)
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
)
|
|
5912
|
-
|
|
5913
|
-
|
|
5914
|
-
)
|
|
5915
|
-
|
|
5916
|
-
|
|
5917
|
-
|
|
5918
|
-
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
|
|
5922
|
-
|
|
5923
|
-
|
|
5924
|
-
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
)
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
6190
|
+
|
|
6191
|
+
// src/commands/anchor-set/model.ts
|
|
6192
|
+
var import_zod11 = require("zod");
|
|
6193
|
+
var anchorSetInputSchema = import_zod11.z.object({
|
|
6194
|
+
reason: import_zod11.z.string().refine((text) => text.trim().length > 0, {
|
|
6195
|
+
message: "reason must say what was reviewed"
|
|
6196
|
+
}).describe(
|
|
6197
|
+
"What the reviewer read that makes these the right pointers. Recorded in the log."
|
|
6198
|
+
),
|
|
6199
|
+
anchors: import_zod11.z.array(kbAnchorWriteSchema).min(1).describe(
|
|
6200
|
+
"The complete new anchor set. Carry an anchor's hash forward to keep drift visible until the new code is read."
|
|
6201
|
+
)
|
|
6202
|
+
}).strict();
|
|
6203
|
+
var anchorSetCommandInput = import_zod11.z.object({
|
|
6204
|
+
bundlePath,
|
|
6205
|
+
conceptId,
|
|
6206
|
+
input: anchorSetInputSchema,
|
|
6207
|
+
resolve: import_zod11.z.boolean().optional().describe(
|
|
6208
|
+
"Also resolve and stamp every anchor against the current code, as anchor-resolve --rebaseline does."
|
|
6209
|
+
),
|
|
6210
|
+
repoRoot: import_zod11.z.string().min(1).optional().describe(
|
|
6211
|
+
"Where the anchored source lives, for resolve. Defaults to the working directory."
|
|
6212
|
+
),
|
|
6213
|
+
offline: import_zod11.z.boolean().optional().describe("With resolve, read foreign anchors from the repo cache only.")
|
|
6214
|
+
});
|
|
6215
|
+
|
|
6216
|
+
// src/commands/anchor-set/command.ts
|
|
6217
|
+
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.";
|
|
6218
|
+
var STAMPED_NOTE = "pointers set and stamped against the current code. Not verification: run verify separately if someone reviewed it.";
|
|
6219
|
+
var INCOMPLETE_NOTE = "pointers set, but not every anchor was stamped: see each resolved entry's state and outcome.";
|
|
6220
|
+
var anchorSetCommand = define({
|
|
6221
|
+
name: "anchor-set",
|
|
6222
|
+
tool: "kb_anchor_set",
|
|
6223
|
+
usage: "anchor-set <concept-id> [--resolve] [--repo-root <path>] [--offline] < anchors.json",
|
|
6224
|
+
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.",
|
|
6225
|
+
input: anchorSetCommandInput,
|
|
6226
|
+
fromArgv: async (argv, path, stdin) => ({
|
|
6227
|
+
bundlePath: path,
|
|
6228
|
+
conceptId: argv[1],
|
|
6229
|
+
input: JSON.parse(await stdin()),
|
|
6230
|
+
resolve: argv.includes("--resolve"),
|
|
6231
|
+
repoRoot: argvFlag(argv, "--repo-root"),
|
|
6232
|
+
offline: argv.includes("--offline")
|
|
6233
|
+
}),
|
|
6234
|
+
run: async (ctx, { bundlePath: path, conceptId: id, input, resolve: resolve7, repoRoot, offline }) => {
|
|
6235
|
+
const { store, actor } = ctx;
|
|
6236
|
+
await assertBaseNotFrozen(process.cwd(), path);
|
|
6237
|
+
let applied;
|
|
6238
|
+
const record = await store.updateAnchors(
|
|
6239
|
+
path,
|
|
6240
|
+
id,
|
|
6241
|
+
(current) => {
|
|
6242
|
+
applied = applyAnchorSet(current, input.anchors);
|
|
6243
|
+
return {
|
|
6244
|
+
anchors: applied.anchors,
|
|
6245
|
+
log: {
|
|
6246
|
+
operation: "anchor-set",
|
|
6247
|
+
reason: input.reason,
|
|
6248
|
+
anchors: applied.changes
|
|
6249
|
+
}
|
|
6250
|
+
};
|
|
6251
|
+
},
|
|
6252
|
+
actor
|
|
5937
6253
|
);
|
|
5938
|
-
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
|
|
5946
|
-
|
|
5947
|
-
|
|
5948
|
-
repo
|
|
5949
|
-
});
|
|
5950
|
-
continue;
|
|
6254
|
+
const changes = applied?.changes ?? [];
|
|
6255
|
+
if (!resolve7) {
|
|
6256
|
+
return {
|
|
6257
|
+
conceptId: id,
|
|
6258
|
+
reason: input.reason,
|
|
6259
|
+
changes,
|
|
6260
|
+
anchors: record.frontmatter.strauss_anchors ?? [],
|
|
6261
|
+
baseline: "unchanged",
|
|
6262
|
+
note: NOTE
|
|
6263
|
+
};
|
|
5951
6264
|
}
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
6265
|
+
const resolved = await anchorResolveCommand.run(
|
|
6266
|
+
ctx,
|
|
6267
|
+
anchorResolveCommand.input.parse({
|
|
6268
|
+
bundlePath: path,
|
|
6269
|
+
conceptId: id,
|
|
6270
|
+
rebaseline: true,
|
|
6271
|
+
...repoRoot ? { repoRoot } : {},
|
|
6272
|
+
...offline ? { offline } : {}
|
|
6273
|
+
})
|
|
6274
|
+
);
|
|
6275
|
+
const after = await store.read(path, id);
|
|
6276
|
+
const stamped = resolved.results.every(
|
|
6277
|
+
(entry) => entry.state === "match" || entry.outcome === "applied"
|
|
6278
|
+
);
|
|
6279
|
+
return {
|
|
6280
|
+
conceptId: id,
|
|
6281
|
+
reason: input.reason,
|
|
6282
|
+
changes,
|
|
6283
|
+
anchors: after?.frontmatter.strauss_anchors ?? [],
|
|
6284
|
+
baseline: stamped ? "stamped" : "incomplete",
|
|
6285
|
+
resolved: resolved.results,
|
|
6286
|
+
note: stamped ? STAMPED_NOTE : INCOMPLETE_NOTE
|
|
6287
|
+
};
|
|
6288
|
+
},
|
|
6289
|
+
// With `resolve`, a pointer that names nothing is a failed set, not a
|
|
6290
|
+
// finding to read later, and a stamp that did not land fails as it does in
|
|
6291
|
+
// anchor-resolve. A remote nothing could reach was never checked, so it does
|
|
6292
|
+
// not fail — the same line anchor-resolve draws.
|
|
6293
|
+
failsWhen: (result, input) => {
|
|
6294
|
+
const resolved = result.resolved ?? [];
|
|
6295
|
+
return resolved.some(
|
|
6296
|
+
(entry) => entry.state === "unresolved" && !isUncheckedReason(entry.reason)
|
|
6297
|
+
) || anchorResolveCommand.failsWhen?.({ results: resolved }, input) === true;
|
|
5958
6298
|
}
|
|
5959
|
-
|
|
5960
|
-
}
|
|
6299
|
+
});
|
|
5961
6300
|
|
|
5962
6301
|
// src/commands/answer.ts
|
|
5963
|
-
var
|
|
6302
|
+
var import_zod12 = require("zod");
|
|
5964
6303
|
var answerCommand = define({
|
|
5965
6304
|
name: "answer",
|
|
5966
6305
|
tool: "kb_answer",
|
|
5967
6306
|
usage: "answer <concept-id> <answer...>",
|
|
5968
6307
|
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.",
|
|
5969
|
-
input:
|
|
6308
|
+
input: import_zod12.z.object({ bundlePath, conceptId, answer: import_zod12.z.string().min(1) }),
|
|
5970
6309
|
fromArgv: (argv, path) => ({
|
|
5971
6310
|
bundlePath: path,
|
|
5972
6311
|
conceptId: argv[1],
|
|
@@ -5980,27 +6319,27 @@ var answerCommand = define({
|
|
|
5980
6319
|
});
|
|
5981
6320
|
|
|
5982
6321
|
// src/commands/backlinks.ts
|
|
5983
|
-
var
|
|
6322
|
+
var import_zod13 = require("zod");
|
|
5984
6323
|
var backlinksCommand = define({
|
|
5985
6324
|
name: "backlinks",
|
|
5986
6325
|
tool: "kb_backlinks",
|
|
5987
6326
|
usage: "backlinks <concept-id>",
|
|
5988
6327
|
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.",
|
|
5989
|
-
input:
|
|
6328
|
+
input: import_zod13.z.object({ bundlePath, conceptId }),
|
|
5990
6329
|
fromArgv: (argv, path) => ({ bundlePath: path, conceptId: argv[1] }),
|
|
5991
6330
|
run: async ({ store }, { bundlePath: path, conceptId: id }) => store.backlinks(path, id)
|
|
5992
6331
|
});
|
|
5993
6332
|
|
|
5994
6333
|
// src/commands/catalog.ts
|
|
5995
|
-
var
|
|
6334
|
+
var import_zod14 = require("zod");
|
|
5996
6335
|
var catalogCommand = define({
|
|
5997
6336
|
name: "catalog",
|
|
5998
6337
|
tool: "kb_catalog",
|
|
5999
6338
|
usage: "catalog [type] [--tag T]...",
|
|
6000
6339
|
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.",
|
|
6001
|
-
input:
|
|
6340
|
+
input: import_zod14.z.object({
|
|
6002
6341
|
bundlePath,
|
|
6003
|
-
type:
|
|
6342
|
+
type: import_zod14.z.enum(KB_RECORD_TYPES).optional(),
|
|
6004
6343
|
tags: TAGS
|
|
6005
6344
|
}),
|
|
6006
6345
|
fromArgv: (argv, path) => {
|
|
@@ -6072,7 +6411,7 @@ function count(value, noun) {
|
|
|
6072
6411
|
var import_node_buffer = require("buffer");
|
|
6073
6412
|
var import_promises10 = require("fs/promises");
|
|
6074
6413
|
var import_node_path12 = require("path");
|
|
6075
|
-
var
|
|
6414
|
+
var import_zod17 = require("zod");
|
|
6076
6415
|
|
|
6077
6416
|
// src/drift/moved.ts
|
|
6078
6417
|
var import_promises9 = require("fs/promises");
|
|
@@ -6409,7 +6748,7 @@ function claimOf(record) {
|
|
|
6409
6748
|
}
|
|
6410
6749
|
|
|
6411
6750
|
// src/commands/match/command.ts
|
|
6412
|
-
var
|
|
6751
|
+
var import_zod16 = require("zod");
|
|
6413
6752
|
|
|
6414
6753
|
// src/commands/match/errors.ts
|
|
6415
6754
|
var KbMatchInputError = class extends BaseError {
|
|
@@ -6429,21 +6768,21 @@ var KbMatchInputError = class extends BaseError {
|
|
|
6429
6768
|
};
|
|
6430
6769
|
|
|
6431
6770
|
// src/commands/match/model.ts
|
|
6432
|
-
var
|
|
6433
|
-
var diffHunkSchema =
|
|
6434
|
-
startLine:
|
|
6435
|
-
endLine:
|
|
6436
|
-
side:
|
|
6771
|
+
var import_zod15 = require("zod");
|
|
6772
|
+
var diffHunkSchema = import_zod15.z.object({
|
|
6773
|
+
startLine: import_zod15.z.number().int().positive(),
|
|
6774
|
+
endLine: import_zod15.z.number().int().positive(),
|
|
6775
|
+
side: import_zod15.z.enum(["old", "new"]).optional()
|
|
6437
6776
|
}).passthrough();
|
|
6438
|
-
var diffFileSchema =
|
|
6439
|
-
filePath:
|
|
6440
|
-
hunks:
|
|
6777
|
+
var diffFileSchema = import_zod15.z.object({
|
|
6778
|
+
filePath: import_zod15.z.string().min(1).describe("Repo-relative, spelled the way anchors are."),
|
|
6779
|
+
hunks: import_zod15.z.array(diffHunkSchema)
|
|
6441
6780
|
});
|
|
6442
|
-
var symbolRangeSchema =
|
|
6443
|
-
file:
|
|
6444
|
-
symbol:
|
|
6445
|
-
startLine:
|
|
6446
|
-
endLine:
|
|
6781
|
+
var symbolRangeSchema = import_zod15.z.object({
|
|
6782
|
+
file: import_zod15.z.string().min(1),
|
|
6783
|
+
symbol: import_zod15.z.string().min(1),
|
|
6784
|
+
startLine: import_zod15.z.number().int().positive(),
|
|
6785
|
+
endLine: import_zod15.z.number().int().positive()
|
|
6447
6786
|
});
|
|
6448
6787
|
|
|
6449
6788
|
// src/commands/match/parse-unified-diff.ts
|
|
@@ -6692,17 +7031,17 @@ var matchCommand = define({
|
|
|
6692
7031
|
tool: "kb_match",
|
|
6693
7032
|
usage: "match --git <base>..<head> | --stdin [--repo-root <path>] [--offline] [--include-non-current]",
|
|
6694
7033
|
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.",
|
|
6695
|
-
input:
|
|
7034
|
+
input: import_zod16.z.object({
|
|
6696
7035
|
bundlePath,
|
|
6697
|
-
files:
|
|
6698
|
-
symbolRanges:
|
|
7036
|
+
files: import_zod16.z.array(diffFileSchema).describe("The changed files, each with its post-change line ranges."),
|
|
7037
|
+
symbolRanges: import_zod16.z.array(symbolRangeSchema).optional().describe(
|
|
6699
7038
|
"Symbol spans the caller already has. Resolved from repoRoot when omitted."
|
|
6700
7039
|
),
|
|
6701
7040
|
repoRoot: REPO_ROOT,
|
|
6702
|
-
offline:
|
|
7041
|
+
offline: import_zod16.z.boolean().optional().describe(
|
|
6703
7042
|
"Resolve symbol ranges from what is already on disk, never fetching a grammar."
|
|
6704
7043
|
),
|
|
6705
|
-
includeNonCurrent:
|
|
7044
|
+
includeNonCurrent: import_zod16.z.boolean().optional().describe(
|
|
6706
7045
|
"Return superseded, rejected and unsettled records too, each carrying its standing."
|
|
6707
7046
|
)
|
|
6708
7047
|
}),
|
|
@@ -6716,11 +7055,11 @@ var matchCommand = define({
|
|
|
6716
7055
|
...argv.includes("--include-non-current") ? { includeNonCurrent: true } : {}
|
|
6717
7056
|
};
|
|
6718
7057
|
if (range !== void 0) {
|
|
6719
|
-
const
|
|
6720
|
-
if (!
|
|
6721
|
-
throw new KbMatchInputError(`--git ${range} ${REFUSED[
|
|
7058
|
+
const diff2 = await readRangeDiff(repoRoot ?? process.cwd(), range);
|
|
7059
|
+
if (!diff2.ok) {
|
|
7060
|
+
throw new KbMatchInputError(`--git ${range} ${REFUSED[diff2.reason]}`);
|
|
6722
7061
|
}
|
|
6723
|
-
return { ...base2, files: parseUnifiedDiff(
|
|
7062
|
+
return { ...base2, files: parseUnifiedDiff(diff2.text) };
|
|
6724
7063
|
}
|
|
6725
7064
|
if (!argv.includes("--stdin")) {
|
|
6726
7065
|
throw new KbMatchInputError(
|
|
@@ -6806,22 +7145,22 @@ function project(match, ranges, all) {
|
|
|
6806
7145
|
|
|
6807
7146
|
// src/commands/classify.ts
|
|
6808
7147
|
var classifyFileSchema = diffFileSchema.extend({
|
|
6809
|
-
hunks:
|
|
6810
|
-
diffHunkSchema.extend({ lines:
|
|
7148
|
+
hunks: import_zod17.z.array(
|
|
7149
|
+
diffHunkSchema.extend({ lines: import_zod17.z.array(import_zod17.z.string()).optional() })
|
|
6811
7150
|
),
|
|
6812
|
-
renamedFrom:
|
|
6813
|
-
similarity:
|
|
7151
|
+
renamedFrom: import_zod17.z.string().min(1).optional().describe("Where `git diff -M` says the path came from."),
|
|
7152
|
+
similarity: import_zod17.z.number().min(0).max(100).optional()
|
|
6814
7153
|
});
|
|
6815
7154
|
var classifyCommand = define({
|
|
6816
7155
|
name: "classify",
|
|
6817
7156
|
tool: "kb_classify",
|
|
6818
7157
|
usage: "classify --git <base>..<head> | --stdin [--repo-root <path>] [--offline]",
|
|
6819
7158
|
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.",
|
|
6820
|
-
input:
|
|
7159
|
+
input: import_zod17.z.object({
|
|
6821
7160
|
bundlePath,
|
|
6822
|
-
files:
|
|
7161
|
+
files: import_zod17.z.array(classifyFileSchema).describe("The changed files, each with its line ranges."),
|
|
6823
7162
|
repoRoot: REPO_ROOT,
|
|
6824
|
-
offline:
|
|
7163
|
+
offline: import_zod17.z.boolean().optional().describe(
|
|
6825
7164
|
"Resolve symbol ranges from what is already on disk, never fetching a grammar."
|
|
6826
7165
|
)
|
|
6827
7166
|
}),
|
|
@@ -6834,15 +7173,15 @@ var classifyCommand = define({
|
|
|
6834
7173
|
...argv.includes("--offline") ? { offline: true } : {}
|
|
6835
7174
|
};
|
|
6836
7175
|
if (range !== void 0) {
|
|
6837
|
-
const
|
|
6838
|
-
if (!
|
|
7176
|
+
const diff2 = await readRangeDiff(repoRoot ?? process.cwd(), range);
|
|
7177
|
+
if (!diff2.ok) {
|
|
6839
7178
|
throw new KbClassifyInputError(
|
|
6840
|
-
`--git ${range} ${REFUSED2[
|
|
7179
|
+
`--git ${range} ${REFUSED2[diff2.reason]}`
|
|
6841
7180
|
);
|
|
6842
7181
|
}
|
|
6843
7182
|
return {
|
|
6844
7183
|
...base2,
|
|
6845
|
-
files: parseUnifiedDiff(
|
|
7184
|
+
files: parseUnifiedDiff(diff2.text, {
|
|
6846
7185
|
keepEmpty: true,
|
|
6847
7186
|
withLines: true
|
|
6848
7187
|
})
|
|
@@ -6932,29 +7271,29 @@ function renderClassify(result) {
|
|
|
6932
7271
|
}
|
|
6933
7272
|
|
|
6934
7273
|
// src/commands/context.ts
|
|
6935
|
-
var
|
|
7274
|
+
var import_zod18 = require("zod");
|
|
6936
7275
|
var contextCommand = define({
|
|
6937
7276
|
name: "context",
|
|
6938
7277
|
tool: "kb_context",
|
|
6939
7278
|
usage: "context [--profile NAME] [--budget N] [--full-under N] [--exclude-tag T]... [--format json] [--event NAME]",
|
|
6940
7279
|
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.",
|
|
6941
|
-
input:
|
|
6942
|
-
budgetTokens:
|
|
7280
|
+
input: import_zod18.z.object({
|
|
7281
|
+
budgetTokens: import_zod18.z.number().int().positive().optional().describe(
|
|
6943
7282
|
"Ceiling on the whole emitted block; past it the command refuses with a list of bases rather than truncating. Defaults to 4000."
|
|
6944
7283
|
),
|
|
6945
|
-
fullUnderTokens:
|
|
7284
|
+
fullUnderTokens: import_zod18.z.number().int().positive().optional().describe(
|
|
6946
7285
|
"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."
|
|
6947
7286
|
),
|
|
6948
|
-
profile:
|
|
7287
|
+
profile: import_zod18.z.string().optional().describe(
|
|
6949
7288
|
"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."
|
|
6950
7289
|
),
|
|
6951
|
-
excludeTags:
|
|
7290
|
+
excludeTags: import_zod18.z.array(import_zod18.z.string().min(1)).optional().describe(
|
|
6952
7291
|
"Frontmatter tags whose records stay out of the block. The base stays pinned and stays readable by tool; resolved like the budgets."
|
|
6953
7292
|
),
|
|
6954
|
-
format:
|
|
7293
|
+
format: import_zod18.z.enum(["markdown", "json"]).optional().describe(
|
|
6955
7294
|
"CLI envelope for hook protocols that require strict JSON on stdout. MCP callers omit this \u2014 the block itself is identical."
|
|
6956
7295
|
),
|
|
6957
|
-
event:
|
|
7296
|
+
event: import_zod18.z.string().optional().describe(
|
|
6958
7297
|
"hookEventName stamped into the JSON envelope. Only meaningful with format=json."
|
|
6959
7298
|
)
|
|
6960
7299
|
}),
|
|
@@ -6993,20 +7332,20 @@ var contextCommand = define({
|
|
|
6993
7332
|
});
|
|
6994
7333
|
|
|
6995
7334
|
// src/commands/doctor.ts
|
|
6996
|
-
var
|
|
7335
|
+
var import_zod20 = require("zod");
|
|
6997
7336
|
|
|
6998
7337
|
// src/commands/reassess.ts
|
|
6999
|
-
var
|
|
7338
|
+
var import_zod19 = require("zod");
|
|
7000
7339
|
var reassessCommand = define({
|
|
7001
7340
|
name: "reassess",
|
|
7002
7341
|
tool: "kb_reassess",
|
|
7003
7342
|
usage: "reassess <concept-id> [--repo-root <path>] [--with-diff]",
|
|
7004
7343
|
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.",
|
|
7005
|
-
input:
|
|
7344
|
+
input: import_zod19.z.object({
|
|
7006
7345
|
bundlePath,
|
|
7007
7346
|
conceptId,
|
|
7008
7347
|
repoRoot: REPO_ROOT,
|
|
7009
|
-
withDiff:
|
|
7348
|
+
withDiff: import_zod19.z.boolean().optional().describe(
|
|
7010
7349
|
"Recover each anchor's committed span and render the diff. Reads git history."
|
|
7011
7350
|
)
|
|
7012
7351
|
}),
|
|
@@ -7151,13 +7490,13 @@ function at(file, symbol) {
|
|
|
7151
7490
|
}
|
|
7152
7491
|
|
|
7153
7492
|
// src/commands/doctor.ts
|
|
7154
|
-
var days = (what, fallback) =>
|
|
7493
|
+
var days = (what, fallback) => import_zod20.z.number().int().positive().optional().describe(`${what} Defaults to ${fallback}.`);
|
|
7155
7494
|
var doctorCommand = define({
|
|
7156
7495
|
name: "doctor",
|
|
7157
7496
|
tool: "kb_doctor",
|
|
7158
7497
|
usage: "doctor [--expiring-days N] [--unverified-days N] [--aging-days N] [--repo-root PATH] [--offline] [--strict] [--drifted [--with-diff]]",
|
|
7159
7498
|
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.",
|
|
7160
|
-
input:
|
|
7499
|
+
input: import_zod20.z.object({
|
|
7161
7500
|
bundlePath,
|
|
7162
7501
|
repoRoot: REPO_ROOT,
|
|
7163
7502
|
expiringDays: days(
|
|
@@ -7172,16 +7511,16 @@ var doctorCommand = define({
|
|
|
7172
7511
|
"How long a record may stay `open` or `proposed` before `aging` reports it, in days.",
|
|
7173
7512
|
DEFAULT_AGING_DAYS
|
|
7174
7513
|
),
|
|
7175
|
-
offline:
|
|
7514
|
+
offline: import_zod20.z.boolean().optional().describe(
|
|
7176
7515
|
"Read foreign anchors from the local repo cache only, never fetching."
|
|
7177
7516
|
),
|
|
7178
|
-
strict:
|
|
7517
|
+
strict: import_zod20.z.boolean().optional().describe(
|
|
7179
7518
|
"Turn an expired record into a non-zero exit for the CLI. No effect on the report itself."
|
|
7180
7519
|
),
|
|
7181
|
-
drifted:
|
|
7520
|
+
drifted: import_zod20.z.boolean().optional().describe(
|
|
7182
7521
|
"Report only drift, as a reassessment packet per record: claim, per-anchor class, and what depends on it."
|
|
7183
7522
|
),
|
|
7184
|
-
withDiff:
|
|
7523
|
+
withDiff: import_zod20.z.boolean().optional().describe(
|
|
7185
7524
|
"With `drifted`: recover each anchor's committed span and render the old-vs-new diff. Reads git history."
|
|
7186
7525
|
)
|
|
7187
7526
|
}),
|
|
@@ -7358,7 +7697,7 @@ function renderPackets(result) {
|
|
|
7358
7697
|
// src/commands/export.ts
|
|
7359
7698
|
var import_promises11 = require("fs/promises");
|
|
7360
7699
|
var import_node_path13 = require("path");
|
|
7361
|
-
var
|
|
7700
|
+
var import_zod21 = require("zod");
|
|
7362
7701
|
var NUMBERED = /^(\d{4})-(.+)\.md$/;
|
|
7363
7702
|
var MARKER = "<!-- strauss-kb export: ";
|
|
7364
7703
|
var exportCommand = define({
|
|
@@ -7366,10 +7705,10 @@ var exportCommand = define({
|
|
|
7366
7705
|
tool: "kb_export",
|
|
7367
7706
|
usage: "export --format madr --to <dir>",
|
|
7368
7707
|
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.",
|
|
7369
|
-
input:
|
|
7708
|
+
input: import_zod21.z.object({
|
|
7370
7709
|
bundlePath,
|
|
7371
|
-
format:
|
|
7372
|
-
to:
|
|
7710
|
+
format: import_zod21.z.enum(["madr"]).describe("Output layout. `madr` is the only one so far."),
|
|
7711
|
+
to: import_zod21.z.string().min(1).describe("Directory the ADR files are written into.")
|
|
7373
7712
|
}),
|
|
7374
7713
|
fromArgv: (argv, path) => ({
|
|
7375
7714
|
bundlePath: path,
|
|
@@ -7491,19 +7830,19 @@ function bodySections(body) {
|
|
|
7491
7830
|
}
|
|
7492
7831
|
|
|
7493
7832
|
// src/commands/impact.ts
|
|
7494
|
-
var
|
|
7833
|
+
var import_zod22 = require("zod");
|
|
7495
7834
|
var impactCommand = define({
|
|
7496
7835
|
name: "impact",
|
|
7497
7836
|
tool: "kb_impact",
|
|
7498
7837
|
usage: "impact <concept-id> [--depth N] [--rels a,b]",
|
|
7499
7838
|
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.",
|
|
7500
|
-
input:
|
|
7839
|
+
input: import_zod22.z.object({
|
|
7501
7840
|
bundlePath,
|
|
7502
7841
|
conceptId,
|
|
7503
|
-
depth:
|
|
7842
|
+
depth: import_zod22.z.number().int().positive().optional().describe(
|
|
7504
7843
|
"Hops out from the record. Unbounded when omitted; a walk this cuts reports truncated: true."
|
|
7505
7844
|
),
|
|
7506
|
-
rels:
|
|
7845
|
+
rels: import_zod22.z.array(import_zod22.z.enum(KB_CAUSAL_LINK_RELS)).optional().describe(
|
|
7507
7846
|
"Narrow which rels the walk follows. Defaults to every rel that carries a dependence \u2014 all but related_to."
|
|
7508
7847
|
)
|
|
7509
7848
|
}),
|
|
@@ -7524,15 +7863,15 @@ var impactCommand = define({
|
|
|
7524
7863
|
});
|
|
7525
7864
|
|
|
7526
7865
|
// src/commands/list.ts
|
|
7527
|
-
var
|
|
7866
|
+
var import_zod23 = require("zod");
|
|
7528
7867
|
var listCommand = define({
|
|
7529
7868
|
name: "list",
|
|
7530
7869
|
tool: "kb_list",
|
|
7531
7870
|
usage: "list [type] [--tag T]...",
|
|
7532
7871
|
description: "Every record, optionally one type or tag. For enumerating; use kb_query for a question.",
|
|
7533
|
-
input:
|
|
7872
|
+
input: import_zod23.z.object({
|
|
7534
7873
|
bundlePath,
|
|
7535
|
-
type:
|
|
7874
|
+
type: import_zod23.z.enum(KB_RECORD_TYPES).optional(),
|
|
7536
7875
|
tags: TAGS
|
|
7537
7876
|
}),
|
|
7538
7877
|
fromArgv: (argv, path) => {
|
|
@@ -7556,17 +7895,17 @@ var listCommand = define({
|
|
|
7556
7895
|
});
|
|
7557
7896
|
|
|
7558
7897
|
// src/commands/load.ts
|
|
7559
|
-
var
|
|
7898
|
+
var import_zod24 = require("zod");
|
|
7560
7899
|
var loadCommand = define({
|
|
7561
7900
|
name: "load",
|
|
7562
7901
|
tool: "kb_load",
|
|
7563
7902
|
usage: "load [type] [--budget N | --all] [--repo-root PATH]",
|
|
7564
7903
|
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.",
|
|
7565
|
-
input:
|
|
7904
|
+
input: import_zod24.z.object({
|
|
7566
7905
|
bundlePath,
|
|
7567
|
-
type:
|
|
7568
|
-
budgetTokens:
|
|
7569
|
-
all:
|
|
7906
|
+
type: import_zod24.z.enum(KB_RECORD_TYPES).optional(),
|
|
7907
|
+
budgetTokens: import_zod24.z.number().int().positive().optional().describe("Approximate token ceiling. Defaults to 25000."),
|
|
7908
|
+
all: import_zod24.z.boolean().optional().describe(
|
|
7570
7909
|
"Loads the entire base regardless of size, bypassing the token budget; mutually exclusive with budgetTokens."
|
|
7571
7910
|
),
|
|
7572
7911
|
repoRoot: REPO_ROOT
|
|
@@ -7608,25 +7947,25 @@ var loadCommand = define({
|
|
|
7608
7947
|
});
|
|
7609
7948
|
|
|
7610
7949
|
// src/commands/log.ts
|
|
7611
|
-
var
|
|
7950
|
+
var import_zod25 = require("zod");
|
|
7612
7951
|
var logCommand = define({
|
|
7613
7952
|
name: "log",
|
|
7614
7953
|
tool: "kb_log",
|
|
7615
7954
|
usage: "log",
|
|
7616
7955
|
description: "Who touched what, and when. Append-only; malformed lines are reported, never repaired.",
|
|
7617
|
-
input:
|
|
7956
|
+
input: import_zod25.z.object({ bundlePath }),
|
|
7618
7957
|
fromArgv: (_argv, path) => ({ bundlePath: path }),
|
|
7619
7958
|
run: ({ store }, { bundlePath: path }) => store.readLog(path)
|
|
7620
7959
|
});
|
|
7621
7960
|
|
|
7622
7961
|
// src/commands/no-decision.ts
|
|
7623
|
-
var
|
|
7962
|
+
var import_zod26 = require("zod");
|
|
7624
7963
|
var noDecisionCommand = define({
|
|
7625
7964
|
name: "no-decision",
|
|
7626
7965
|
tool: "kb_no_decision",
|
|
7627
7966
|
usage: "no-decision <reason...>",
|
|
7628
7967
|
description: "Record in one sentence that a piece of work had nothing to decide. Idempotent.",
|
|
7629
|
-
input:
|
|
7968
|
+
input: import_zod26.z.object({ bundlePath, reason: import_zod26.z.string().min(1) }),
|
|
7630
7969
|
fromArgv: (argv, path) => ({
|
|
7631
7970
|
bundlePath: path,
|
|
7632
7971
|
reason: argv.slice(1).join(" ").trim()
|
|
@@ -7643,20 +7982,20 @@ var noDecisionCommand = define({
|
|
|
7643
7982
|
});
|
|
7644
7983
|
|
|
7645
7984
|
// src/commands/pack.ts
|
|
7646
|
-
var
|
|
7985
|
+
var import_zod27 = require("zod");
|
|
7647
7986
|
var packCommand = define({
|
|
7648
7987
|
name: "pack",
|
|
7649
7988
|
tool: "kb_pack",
|
|
7650
7989
|
usage: "pack <conceptId> [--hops N] [--max-nodes N] [--budget N]",
|
|
7651
7990
|
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.",
|
|
7652
|
-
input:
|
|
7991
|
+
input: import_zod27.z.object({
|
|
7653
7992
|
bundlePath,
|
|
7654
7993
|
conceptId,
|
|
7655
|
-
hops:
|
|
7656
|
-
maxNodes:
|
|
7994
|
+
hops: import_zod27.z.number().int().positive().optional().describe("How far from the root the walk may reach. Defaults to 2."),
|
|
7995
|
+
maxNodes: import_zod27.z.number().int().positive().optional().describe(
|
|
7657
7996
|
"How many records the pack may hold, root included. Defaults to 20."
|
|
7658
7997
|
),
|
|
7659
|
-
budgetTokens:
|
|
7998
|
+
budgetTokens: import_zod27.z.number().int().positive().optional().describe(
|
|
7660
7999
|
"Approximate token ceiling over what is actually emitted. Defaults to 25000."
|
|
7661
8000
|
)
|
|
7662
8001
|
}),
|
|
@@ -7743,22 +8082,22 @@ function warningLabel(warning) {
|
|
|
7743
8082
|
}
|
|
7744
8083
|
|
|
7745
8084
|
// src/commands/pin.ts
|
|
7746
|
-
var
|
|
8085
|
+
var import_zod28 = require("zod");
|
|
7747
8086
|
var pinCommand = define({
|
|
7748
8087
|
name: "pin",
|
|
7749
8088
|
tool: "kb_pin",
|
|
7750
8089
|
usage: "pin [bundle-path] [--mode full|index] [--profiles a,b] [--local|--user] [--frozen|--unfreeze]",
|
|
7751
8090
|
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.",
|
|
7752
|
-
input:
|
|
8091
|
+
input: import_zod28.z.object({
|
|
7753
8092
|
bundlePath,
|
|
7754
|
-
mode:
|
|
8093
|
+
mode: import_zod28.z.enum(["full", "index"]).optional().describe(
|
|
7755
8094
|
"full: always emit this base's records whole (still under the block budget); index: never upgrade. Absent: the profile's full-under threshold decides."
|
|
7756
8095
|
),
|
|
7757
|
-
profiles:
|
|
7758
|
-
layer:
|
|
8096
|
+
profiles: import_zod28.z.array(import_zod28.z.string()).optional().describe("Context profiles this pin surfaces in. Absent: all of them."),
|
|
8097
|
+
layer: import_zod28.z.enum(["project", "local", "user"]).optional().describe(
|
|
7759
8098
|
"Which manifest to write: project (committed, default), local (personal, gitignored), user (~/.strauss, every workspace)."
|
|
7760
8099
|
),
|
|
7761
|
-
frozen:
|
|
8100
|
+
frozen: import_zod28.z.boolean().optional().describe(
|
|
7762
8101
|
"true: the base is concluded \u2014 writes against it refuse while pinned. false: lift a freeze."
|
|
7763
8102
|
)
|
|
7764
8103
|
}),
|
|
@@ -7787,13 +8126,13 @@ var pinCommand = define({
|
|
|
7787
8126
|
});
|
|
7788
8127
|
|
|
7789
8128
|
// src/commands/pins.ts
|
|
7790
|
-
var
|
|
8129
|
+
var import_zod29 = require("zod");
|
|
7791
8130
|
var pinsCommand = define({
|
|
7792
8131
|
name: "pins",
|
|
7793
8132
|
tool: "kb_pins",
|
|
7794
8133
|
usage: "pins",
|
|
7795
8134
|
description: "Every pinned base across the manifest layers, with its layer and whether it resolves to records. Takes no bundlePath.",
|
|
7796
|
-
input:
|
|
8135
|
+
input: import_zod29.z.object({}),
|
|
7797
8136
|
fromArgv: () => ({}),
|
|
7798
8137
|
run: ({ store }) => listPins(store, process.cwd())
|
|
7799
8138
|
});
|
|
@@ -7929,16 +8268,16 @@ function recordType(conceptId2) {
|
|
|
7929
8268
|
}
|
|
7930
8269
|
|
|
7931
8270
|
// src/commands/promote/model.ts
|
|
7932
|
-
var
|
|
7933
|
-
var promoteInputSchema =
|
|
8271
|
+
var import_zod30 = require("zod");
|
|
8272
|
+
var promoteInputSchema = import_zod30.z.object({
|
|
7934
8273
|
bundlePath,
|
|
7935
|
-
conceptIds:
|
|
7936
|
-
to:
|
|
7937
|
-
source:
|
|
8274
|
+
conceptIds: import_zod30.z.array(conceptId).max(64).optional().describe("Records to copy into the target base. Omit with `list`."),
|
|
8275
|
+
to: import_zod30.z.string().min(1).optional().describe("Absolute path to the base being promoted into."),
|
|
8276
|
+
source: import_zod30.z.string().min(1).optional().describe(
|
|
7938
8277
|
"Where the promotion came from, usually the pull request URL. Recorded on each copy as a source."
|
|
7939
8278
|
),
|
|
7940
|
-
force:
|
|
7941
|
-
list:
|
|
8279
|
+
force: import_zod30.z.boolean().optional().describe("Overwrite a record the target base already holds."),
|
|
8280
|
+
list: import_zod30.z.boolean().optional().describe("List the source base's candidates instead of promoting.")
|
|
7942
8281
|
}).refine((input) => input.list === true || input.to !== void 0, {
|
|
7943
8282
|
message: "promote needs a target base \u2014 pass --to <bundle>, or --list",
|
|
7944
8283
|
path: ["to"]
|
|
@@ -8075,17 +8414,17 @@ function renderPromote(result) {
|
|
|
8075
8414
|
}
|
|
8076
8415
|
|
|
8077
8416
|
// src/commands/query.ts
|
|
8078
|
-
var
|
|
8417
|
+
var import_zod31 = require("zod");
|
|
8079
8418
|
var queryCommand = define({
|
|
8080
8419
|
name: "query",
|
|
8081
8420
|
tool: "kb_query",
|
|
8082
8421
|
usage: "query <text...> [--tag T]... [--repo-root PATH]",
|
|
8083
8422
|
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.",
|
|
8084
|
-
input:
|
|
8423
|
+
input: import_zod31.z.object({
|
|
8085
8424
|
bundlePath,
|
|
8086
|
-
text:
|
|
8087
|
-
type:
|
|
8088
|
-
includeNonCurrent:
|
|
8425
|
+
text: import_zod31.z.string().optional(),
|
|
8426
|
+
type: import_zod31.z.enum(KB_RECORD_TYPES).optional(),
|
|
8427
|
+
includeNonCurrent: import_zod31.z.boolean().optional(),
|
|
8089
8428
|
tags: TAGS,
|
|
8090
8429
|
repoRoot: REPO_ROOT
|
|
8091
8430
|
}),
|
|
@@ -8119,43 +8458,43 @@ var queryCommand = define({
|
|
|
8119
8458
|
});
|
|
8120
8459
|
|
|
8121
8460
|
// src/commands/read-index.ts
|
|
8122
|
-
var
|
|
8461
|
+
var import_zod32 = require("zod");
|
|
8123
8462
|
var readIndexCommand = define({
|
|
8124
8463
|
name: "index",
|
|
8125
8464
|
tool: "kb_index",
|
|
8126
8465
|
usage: "index",
|
|
8127
8466
|
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.",
|
|
8128
|
-
input:
|
|
8467
|
+
input: import_zod32.z.object({ bundlePath }),
|
|
8129
8468
|
fromArgv: (_argv, path) => ({ bundlePath: path }),
|
|
8130
8469
|
run: ({ store }, { bundlePath: path }) => store.readIndex(path)
|
|
8131
8470
|
});
|
|
8132
8471
|
|
|
8133
8472
|
// src/commands/schema.ts
|
|
8134
|
-
var
|
|
8473
|
+
var import_zod33 = require("zod");
|
|
8135
8474
|
var schemaCommand = define({
|
|
8136
8475
|
name: "schema",
|
|
8137
8476
|
tool: "kb_schema",
|
|
8138
8477
|
usage: "schema",
|
|
8139
8478
|
description: "JSON Schema for frontmatter, write input, and log entries, generated from the enforcing code.",
|
|
8140
|
-
input:
|
|
8479
|
+
input: import_zod33.z.object({}),
|
|
8141
8480
|
fromArgv: () => ({}),
|
|
8142
8481
|
run: () => Promise.resolve(kbJsonSchemas())
|
|
8143
8482
|
});
|
|
8144
8483
|
|
|
8145
8484
|
// src/commands/stamp.ts
|
|
8146
8485
|
var import_promises12 = require("fs/promises");
|
|
8147
|
-
var
|
|
8486
|
+
var import_zod34 = require("zod");
|
|
8148
8487
|
var DIGEST = /^[0-9a-f]{64}$/;
|
|
8149
8488
|
var stampCommand = define({
|
|
8150
8489
|
name: "stamp",
|
|
8151
8490
|
tool: "kb_stamp",
|
|
8152
8491
|
usage: "stamp [--bundle PATH] [--since DIGEST|FILE]",
|
|
8153
8492
|
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.",
|
|
8154
|
-
input:
|
|
8155
|
-
bundlePath:
|
|
8493
|
+
input: import_zod34.z.object({
|
|
8494
|
+
bundlePath: import_zod34.z.string().min(1).optional().describe(
|
|
8156
8495
|
"Absolute path to one knowledge base. Omit to stamp every pinned base."
|
|
8157
8496
|
),
|
|
8158
|
-
since:
|
|
8497
|
+
since: import_zod34.z.string().min(1).optional().describe(
|
|
8159
8498
|
"Prior digest, or path to a prior `stamp --json`; only moved bases return, with changed ids when the baseline is a file."
|
|
8160
8499
|
)
|
|
8161
8500
|
}),
|
|
@@ -8241,16 +8580,16 @@ async function readBaseline(since) {
|
|
|
8241
8580
|
}
|
|
8242
8581
|
|
|
8243
8582
|
// src/commands/status.ts
|
|
8244
|
-
var
|
|
8583
|
+
var import_zod35 = require("zod");
|
|
8245
8584
|
var statusCommand = define({
|
|
8246
8585
|
name: "status",
|
|
8247
8586
|
tool: "kb_status",
|
|
8248
8587
|
usage: "status <concept-id> <status>",
|
|
8249
8588
|
description: "Move a record's status. Compare-and-swap: a concurrent change fails instead of being overwritten.",
|
|
8250
|
-
input:
|
|
8589
|
+
input: import_zod35.z.object({
|
|
8251
8590
|
bundlePath,
|
|
8252
8591
|
conceptId,
|
|
8253
|
-
status:
|
|
8592
|
+
status: import_zod35.z.enum(KB_RECORD_STATUSES)
|
|
8254
8593
|
}),
|
|
8255
8594
|
fromArgv: (argv, path) => ({
|
|
8256
8595
|
bundlePath: path,
|
|
@@ -8265,13 +8604,13 @@ var statusCommand = define({
|
|
|
8265
8604
|
});
|
|
8266
8605
|
|
|
8267
8606
|
// src/commands/supersede.ts
|
|
8268
|
-
var
|
|
8607
|
+
var import_zod36 = require("zod");
|
|
8269
8608
|
var supersedeCommand = define({
|
|
8270
8609
|
name: "supersede",
|
|
8271
8610
|
tool: "kb_supersede",
|
|
8272
8611
|
usage: "supersede <concept-id> <replacement-id>",
|
|
8273
8612
|
description: "Mark a record superseded by another, linked in both directions. Use instead of editing a record whose meaning changed.",
|
|
8274
|
-
input:
|
|
8613
|
+
input: import_zod36.z.object({ bundlePath, conceptId, replacementId: conceptId }),
|
|
8275
8614
|
fromArgv: (argv, path) => ({
|
|
8276
8615
|
bundlePath: path,
|
|
8277
8616
|
conceptId: argv[1],
|
|
@@ -8285,7 +8624,7 @@ var supersedeCommand = define({
|
|
|
8285
8624
|
});
|
|
8286
8625
|
|
|
8287
8626
|
// src/commands/sweep.ts
|
|
8288
|
-
var
|
|
8627
|
+
var import_zod37 = require("zod");
|
|
8289
8628
|
var TERMINAL = [
|
|
8290
8629
|
"resolved",
|
|
8291
8630
|
"rejected",
|
|
@@ -8296,15 +8635,15 @@ var sweepCommand = define({
|
|
|
8296
8635
|
tool: "kb_sweep",
|
|
8297
8636
|
usage: "sweep --tag <tag> --terminal [--dry-run]",
|
|
8298
8637
|
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.",
|
|
8299
|
-
input:
|
|
8638
|
+
input: import_zod37.z.object({
|
|
8300
8639
|
bundlePath,
|
|
8301
|
-
tag:
|
|
8302
|
-
terminal:
|
|
8640
|
+
tag: import_zod37.z.string({ error: "sweep needs --tag: it never sweeps a whole base" }).min(1).describe("Only records carrying this tag are considered."),
|
|
8641
|
+
terminal: import_zod37.z.literal(true, {
|
|
8303
8642
|
error: "sweep needs --terminal: it deletes only settled records"
|
|
8304
8643
|
}).describe(
|
|
8305
8644
|
"Required. Names the only scope sweep deletes: resolved, rejected and superseded records."
|
|
8306
8645
|
),
|
|
8307
|
-
dryRun:
|
|
8646
|
+
dryRun: import_zod37.z.boolean().optional().describe("Report what would go, and delete nothing.")
|
|
8308
8647
|
}),
|
|
8309
8648
|
fromArgv: (argv, path) => ({
|
|
8310
8649
|
bundlePath: path,
|
|
@@ -8421,16 +8760,16 @@ function renderSweep(result) {
|
|
|
8421
8760
|
}
|
|
8422
8761
|
|
|
8423
8762
|
// src/commands/sync-instructions.ts
|
|
8424
|
-
var
|
|
8763
|
+
var import_zod38 = require("zod");
|
|
8425
8764
|
var syncInstructionsCommand = define({
|
|
8426
8765
|
name: "sync-instructions",
|
|
8427
8766
|
usage: "sync-instructions <file> [--profile NAME] [--budget N] [--full-under N]",
|
|
8428
8767
|
description: "CLI-only: plant the kb_context block between sentinel comments in AGENTS.md or CLAUDE.md, idempotently.",
|
|
8429
|
-
input:
|
|
8430
|
-
file:
|
|
8431
|
-
budgetTokens:
|
|
8432
|
-
fullUnderTokens:
|
|
8433
|
-
profile:
|
|
8768
|
+
input: import_zod38.z.object({
|
|
8769
|
+
file: import_zod38.z.string().min(1).describe("The instruction file to edit in place."),
|
|
8770
|
+
budgetTokens: import_zod38.z.number().int().positive().optional(),
|
|
8771
|
+
fullUnderTokens: import_zod38.z.number().int().positive().optional(),
|
|
8772
|
+
profile: import_zod38.z.string().optional()
|
|
8434
8773
|
}),
|
|
8435
8774
|
fromArgv: (argv) => {
|
|
8436
8775
|
const budget = argvFlag(argv, "--budget");
|
|
@@ -8456,17 +8795,17 @@ var syncInstructionsCommand = define({
|
|
|
8456
8795
|
});
|
|
8457
8796
|
|
|
8458
8797
|
// src/commands/trace.ts
|
|
8459
|
-
var
|
|
8798
|
+
var import_zod39 = require("zod");
|
|
8460
8799
|
var traceCommand = define({
|
|
8461
8800
|
name: "trace",
|
|
8462
8801
|
tool: "kb_trace",
|
|
8463
8802
|
usage: "trace <concept-id> [edges...]",
|
|
8464
8803
|
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".',
|
|
8465
|
-
input:
|
|
8804
|
+
input: import_zod39.z.object({
|
|
8466
8805
|
bundlePath,
|
|
8467
8806
|
conceptId,
|
|
8468
|
-
edges:
|
|
8469
|
-
depth:
|
|
8807
|
+
edges: import_zod39.z.array(import_zod39.z.enum(TRACE_EDGES)).optional(),
|
|
8808
|
+
depth: import_zod39.z.number().int().positive().optional()
|
|
8470
8809
|
}),
|
|
8471
8810
|
fromArgv: (argv, path) => ({
|
|
8472
8811
|
bundlePath: path,
|
|
@@ -8488,37 +8827,37 @@ var traceCommand = define({
|
|
|
8488
8827
|
});
|
|
8489
8828
|
|
|
8490
8829
|
// src/commands/types.ts
|
|
8491
|
-
var
|
|
8830
|
+
var import_zod40 = require("zod");
|
|
8492
8831
|
var typesCommand = define({
|
|
8493
8832
|
name: "types",
|
|
8494
8833
|
tool: "kb_types",
|
|
8495
8834
|
usage: "types",
|
|
8496
8835
|
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.",
|
|
8497
|
-
input:
|
|
8836
|
+
input: import_zod40.z.object({}),
|
|
8498
8837
|
fromArgv: () => ({}),
|
|
8499
8838
|
run: () => Promise.resolve(RECORD_TYPES)
|
|
8500
8839
|
});
|
|
8501
8840
|
|
|
8502
8841
|
// src/commands/unpin.ts
|
|
8503
|
-
var
|
|
8842
|
+
var import_zod41 = require("zod");
|
|
8504
8843
|
var unpinCommand = define({
|
|
8505
8844
|
name: "unpin",
|
|
8506
8845
|
tool: "kb_unpin",
|
|
8507
8846
|
usage: "unpin [bundle-path]",
|
|
8508
8847
|
description: "Remove a base from every manifest layer that holds it. Reports the layers touched.",
|
|
8509
|
-
input:
|
|
8848
|
+
input: import_zod41.z.object({ bundlePath }),
|
|
8510
8849
|
fromArgv: (argv, path) => ({ bundlePath: argv[1] ?? path }),
|
|
8511
8850
|
run: (_ctx, { bundlePath: path }) => unpinBase(process.cwd(), path)
|
|
8512
8851
|
});
|
|
8513
8852
|
|
|
8514
8853
|
// src/commands/validate.ts
|
|
8515
|
-
var
|
|
8854
|
+
var import_zod42 = require("zod");
|
|
8516
8855
|
var validateCommand = define({
|
|
8517
8856
|
name: "validate",
|
|
8518
8857
|
tool: "kb_validate",
|
|
8519
8858
|
usage: "validate",
|
|
8520
8859
|
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.",
|
|
8521
|
-
input:
|
|
8860
|
+
input: import_zod42.z.object({ bundlePath }),
|
|
8522
8861
|
fromArgv: (_argv, path) => ({ bundlePath: path }),
|
|
8523
8862
|
run: async ({ store }, { bundlePath: path }) => validateBundle(await store.list(path)),
|
|
8524
8863
|
// Warnings never fail the exit code; every other severity does.
|
|
@@ -8528,16 +8867,16 @@ var validateCommand = define({
|
|
|
8528
8867
|
});
|
|
8529
8868
|
|
|
8530
8869
|
// src/commands/verify.ts
|
|
8531
|
-
var
|
|
8870
|
+
var import_zod43 = require("zod");
|
|
8532
8871
|
var verifyCommand = define({
|
|
8533
8872
|
name: "verify",
|
|
8534
8873
|
tool: "kb_verify",
|
|
8535
8874
|
usage: "verify <concept-id> --note <text>",
|
|
8536
8875
|
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.",
|
|
8537
|
-
input:
|
|
8876
|
+
input: import_zod43.z.object({
|
|
8538
8877
|
bundlePath,
|
|
8539
8878
|
conceptId,
|
|
8540
|
-
note:
|
|
8879
|
+
note: import_zod43.z.string().refine((s) => s.trim().length > 0, {
|
|
8541
8880
|
message: "note must say what the check found"
|
|
8542
8881
|
})
|
|
8543
8882
|
}),
|
|
@@ -8557,15 +8896,15 @@ var verifyCommand = define({
|
|
|
8557
8896
|
});
|
|
8558
8897
|
|
|
8559
8898
|
// src/commands/write.ts
|
|
8560
|
-
var
|
|
8899
|
+
var import_zod44 = require("zod");
|
|
8561
8900
|
var writeCommand = define({
|
|
8562
8901
|
name: "write",
|
|
8563
8902
|
tool: "kb_write",
|
|
8564
8903
|
usage: "write <type> < record.json",
|
|
8565
8904
|
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.",
|
|
8566
|
-
input:
|
|
8905
|
+
input: import_zod44.z.object({
|
|
8567
8906
|
bundlePath,
|
|
8568
|
-
type:
|
|
8907
|
+
type: import_zod44.z.enum(KB_RECORD_TYPES),
|
|
8569
8908
|
input: composeInputSchema
|
|
8570
8909
|
}),
|
|
8571
8910
|
fromArgv: async (argv, path, stdin) => ({
|
|
@@ -8589,13 +8928,13 @@ var writeCommand = define({
|
|
|
8589
8928
|
});
|
|
8590
8929
|
|
|
8591
8930
|
// src/commands/write-decision.ts
|
|
8592
|
-
var
|
|
8931
|
+
var import_zod45 = require("zod");
|
|
8593
8932
|
var writeDecisionCommand = define({
|
|
8594
8933
|
name: "write-decision",
|
|
8595
8934
|
tool: "kb_write_decision",
|
|
8596
8935
|
usage: "write-decision < decision.json",
|
|
8597
8936
|
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.",
|
|
8598
|
-
input:
|
|
8937
|
+
input: import_zod45.z.object({ bundlePath, input: decisionInputSchema }),
|
|
8599
8938
|
fromArgv: async (_argv, path, stdin) => ({
|
|
8600
8939
|
bundlePath: path,
|
|
8601
8940
|
input: JSON.parse(await stdin())
|
|
@@ -8625,6 +8964,7 @@ var KB_COMMANDS = [
|
|
|
8625
8964
|
answerCommand,
|
|
8626
8965
|
verifyCommand,
|
|
8627
8966
|
anchorResolveCommand,
|
|
8967
|
+
anchorSetCommand,
|
|
8628
8968
|
reassessCommand,
|
|
8629
8969
|
promoteCommand,
|
|
8630
8970
|
loadCommand,
|
|
@@ -8661,7 +9001,7 @@ var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
|
8661
9001
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
8662
9002
|
|
|
8663
9003
|
// src/version.ts
|
|
8664
|
-
var VERSION = true ? "0.1.
|
|
9004
|
+
var VERSION = true ? "0.1.23" : "0.0.0-dev";
|
|
8665
9005
|
|
|
8666
9006
|
// src/mcp.ts
|
|
8667
9007
|
function createKbMcpServer() {
|
|
@@ -8842,6 +9182,7 @@ function usage() {
|
|
|
8842
9182
|
KB_RECORD_STATUSES,
|
|
8843
9183
|
KB_RECORD_TYPES,
|
|
8844
9184
|
KB_SLUG_PATTERN,
|
|
9185
|
+
KbAnchorSetDuplicateError,
|
|
8845
9186
|
KbBaseFrozenError,
|
|
8846
9187
|
KbClassifyInputError,
|
|
8847
9188
|
KbInvalidConceptIdError,
|
|
@@ -8872,6 +9213,8 @@ function usage() {
|
|
|
8872
9213
|
adjudicate,
|
|
8873
9214
|
anchorFilePath,
|
|
8874
9215
|
anchorOnHunk,
|
|
9216
|
+
anchorSetInputSchema,
|
|
9217
|
+
applyAnchorSet,
|
|
8875
9218
|
assertBaseNotFrozen,
|
|
8876
9219
|
backlinks,
|
|
8877
9220
|
buildContext,
|
|
@@ -8905,19 +9248,23 @@ function usage() {
|
|
|
8905
9248
|
isNoDecisionRecord,
|
|
8906
9249
|
isReviewTag,
|
|
8907
9250
|
kbActorStampSchema,
|
|
9251
|
+
kbAnchorLocatorSchema,
|
|
8908
9252
|
kbAnchorSchema,
|
|
8909
9253
|
kbAnchorSpanSchema,
|
|
8910
9254
|
kbAnchorWriteSchema,
|
|
8911
9255
|
kbConceptIdSchema,
|
|
8912
9256
|
kbJsonSchemas,
|
|
8913
9257
|
kbLinkSchema,
|
|
9258
|
+
kbLogAnchorChangeSchema,
|
|
8914
9259
|
kbLogEntrySchema,
|
|
9260
|
+
kbLogEntryWriteSchema,
|
|
8915
9261
|
kbRecordFrontmatterSchema,
|
|
8916
9262
|
kbSourceSchema,
|
|
8917
9263
|
kbVerifiedEventSchema,
|
|
8918
9264
|
languageForFile,
|
|
8919
9265
|
listPins,
|
|
8920
9266
|
loadQmd,
|
|
9267
|
+
locatorOf,
|
|
8921
9268
|
matchToDiff,
|
|
8922
9269
|
matchesTags,
|
|
8923
9270
|
mergedContextBudgets,
|