@saasontools/strauss-kb 0.1.20 → 0.1.21

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/dist/cli-main.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runKbCli
4
- } from "./chunk-XDH6J6CH.js";
5
- import "./chunk-QQLPJO4R.js";
4
+ } from "./chunk-VXWN62HT.js";
5
+ import "./chunk-XBZ7G5R5.js";
6
6
 
7
7
  // src/cli-main.ts
8
8
  runKbCli(process.argv.slice(2)).catch((error) => {
package/dist/index.cjs CHANGED
@@ -434,6 +434,8 @@ var Fault = /* @__PURE__ */ ((Fault2) => {
434
434
  var ErrorTypes = /* @__PURE__ */ ((ErrorTypes2) => {
435
435
  ErrorTypes2["KbRecordAlreadyExists"] = "KbRecordAlreadyExists";
436
436
  ErrorTypes2["KbClassifyInput"] = "KbClassifyInput";
437
+ ErrorTypes2["KbFlagConflict"] = "KbFlagConflict";
438
+ ErrorTypes2["KbInvalidActor"] = "KbInvalidActor";
437
439
  ErrorTypes2["KbInvalidConceptId"] = "KbInvalidConceptId";
438
440
  ErrorTypes2["KbMatchInput"] = "KbMatchInput";
439
441
  ErrorTypes2["KbMissingFlagValue"] = "KbMissingFlagValue";
@@ -534,6 +536,38 @@ var KbSelfVerificationError = class extends BaseError {
534
536
  actor;
535
537
  generatedBy;
536
538
  };
539
+ var KbInvalidActorError = class extends BaseError {
540
+ constructor(actor, reason) {
541
+ super({
542
+ message: `kb: actor ${JSON.stringify(actor)} ${reason} \u2014 set STRAUSS_KB_ACTOR, e.g. human:alice or agent:reviewer`,
543
+ errorType: "KbInvalidActor" /* KbInvalidActor */,
544
+ code: 400,
545
+ fault: "User" /* User */,
546
+ retriable: false,
547
+ reportToUser: true,
548
+ details: { actor, reason, action: "refused" }
549
+ });
550
+ this.actor = actor;
551
+ this.reason = reason;
552
+ }
553
+ actor;
554
+ reason;
555
+ };
556
+ var KbFlagConflictError = class extends BaseError {
557
+ constructor(flags) {
558
+ super({
559
+ message: `kb: ${flags.join(" and ")} cannot be combined`,
560
+ errorType: "KbFlagConflict" /* KbFlagConflict */,
561
+ code: 400,
562
+ fault: "User" /* User */,
563
+ retriable: false,
564
+ reportToUser: true,
565
+ details: { flags }
566
+ });
567
+ this.flags = flags;
568
+ }
569
+ flags;
570
+ };
537
571
  var KbPackBudgetExceededError = class extends BaseError {
538
572
  constructor(recordCount, approxTokens2, budgetTokens, excluded) {
539
573
  super({
@@ -3361,6 +3395,7 @@ var KbStore = class {
3361
3395
  * its contents.
3362
3396
  */
3363
3397
  async write(bundlePath2, input, actor = "unknown") {
3398
+ assertActor(actor);
3364
3399
  if (!KB_SLUG_PATTERN.test(input.slug)) {
3365
3400
  throw new KbInvalidConceptIdError("slug must be kebab-case", {
3366
3401
  slug: input.slug
@@ -3462,6 +3497,7 @@ var KbStore = class {
3462
3497
  * timeouts.
3463
3498
  */
3464
3499
  async setStatus(bundlePath2, conceptId2, status, actor = "unknown") {
3500
+ assertActor(actor);
3465
3501
  return this.mutate(
3466
3502
  bundlePath2,
3467
3503
  conceptId2,
@@ -3480,6 +3516,7 @@ var KbStore = class {
3480
3516
  * the frontmatter must not be published back out under an actor stamp.
3481
3517
  */
3482
3518
  async updateAnchors(bundlePath2, conceptId2, anchors, actor = "unknown") {
3519
+ assertActor(actor);
3483
3520
  const checked = anchors.map((anchor) => kbAnchorWriteSchema.parse(anchor));
3484
3521
  return this.mutate(
3485
3522
  bundlePath2,
@@ -3500,6 +3537,7 @@ var KbStore = class {
3500
3537
  * logs what it publishes.
3501
3538
  */
3502
3539
  async verify(bundlePath2, conceptId2, note, actor = "unknown", at2 = (/* @__PURE__ */ new Date()).toISOString()) {
3540
+ assertActor(actor, { named: true });
3503
3541
  const event = kbVerifiedEventSchema.parse({ by: actor, at: at2, note });
3504
3542
  const existing = await this.read(bundlePath2, conceptId2);
3505
3543
  if (!existing) throw new KbRecordNotFoundError(conceptId2);
@@ -3530,6 +3568,7 @@ var KbStore = class {
3530
3568
  * in normal use, and validation drops to catching hand-edits.
3531
3569
  */
3532
3570
  async supersede(bundlePath2, conceptId2, replacementId, actor = "unknown") {
3571
+ assertActor(actor);
3533
3572
  const replacement = await this.read(bundlePath2, replacementId);
3534
3573
  if (!replacement) throw new KbRecordNotFoundError(replacementId);
3535
3574
  const superseded = await this.markSuperseded(
@@ -3553,6 +3592,7 @@ var KbStore = class {
3553
3592
  }
3554
3593
  /** Resolves an open question, stamping who answered and when. */
3555
3594
  async answer(bundlePath2, conceptId2, answer, actor = "unknown", at2 = (/* @__PURE__ */ new Date()).toISOString()) {
3595
+ assertActor(actor);
3556
3596
  return this.mutate(
3557
3597
  bundlePath2,
3558
3598
  conceptId2,
@@ -3579,6 +3619,7 @@ ${answer}
3579
3619
  * terminal status since the caller listed it is reported, not removed.
3580
3620
  */
3581
3621
  async deleteRecord(bundlePath2, conceptId2, expected, actor = "unknown") {
3622
+ assertActor(actor);
3582
3623
  const target = this.recordPath(bundlePath2, conceptId2);
3583
3624
  const witness = await this.read(bundlePath2, conceptId2);
3584
3625
  if (!witness) throw new KbRecordNotFoundError(conceptId2);
@@ -3882,6 +3923,7 @@ ${answer}
3882
3923
  * base too, where nothing was written.
3883
3924
  */
3884
3925
  async note(bundlePath2, entry) {
3926
+ assertActor(entry.by);
3885
3927
  await this.record(this.root(bundlePath2), entry);
3886
3928
  }
3887
3929
  /**
@@ -4139,6 +4181,15 @@ function normalizeActor(id) {
4139
4181
  if (colon === -1) return id.toLowerCase();
4140
4182
  return id.slice(0, colon + 1).toLowerCase() + id.slice(colon + 1);
4141
4183
  }
4184
+ var KB_ACTOR_PATTERN = /^[A-Za-z][\w-]*(?::[\p{L}\p{M}\p{N}_.@+/-]+)?$/u;
4185
+ function assertActor(actor, { named = false } = {}) {
4186
+ if (!KB_ACTOR_PATTERN.test(actor)) {
4187
+ throw new KbInvalidActorError(actor, "is not kind or kind:name");
4188
+ }
4189
+ if (named && normalizeActor(actor) === "unknown") {
4190
+ throw new KbInvalidActorError(actor, "cannot verify: name who checked");
4191
+ }
4192
+ }
4142
4193
 
4143
4194
  // src/compose.ts
4144
4195
  var import_zod4 = require("zod");
@@ -5639,19 +5690,11 @@ function argvPositional(argv, ...names) {
5639
5690
  }
5640
5691
 
5641
5692
  // 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
5693
  var anchorResolveCommand = define({
5651
5694
  name: "anchor-resolve",
5652
5695
  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. kb_verify's mechanical counterpart \u2014 reach for it when the question is whether the code still is what it was. Exits non-zero on drift.",
5696
+ 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. Exits non-zero on drift.",
5655
5698
  input: import_zod9.z.object({
5656
5699
  bundlePath,
5657
5700
  conceptId,
@@ -5664,6 +5707,9 @@ var anchorResolveCommand = define({
5664
5707
  ),
5665
5708
  restamp: import_zod9.z.boolean().optional().describe(
5666
5709
  "Refresh `resolved_at` on anchors that already match. Off by default, so a green run writes nothing."
5710
+ ),
5711
+ check: import_zod9.z.boolean().optional().describe(
5712
+ "Resolve and report only: no hash, no `resolved_at`, no log entry."
5667
5713
  )
5668
5714
  }),
5669
5715
  fromArgv: (argv, path) => ({
@@ -5672,9 +5718,24 @@ var anchorResolveCommand = define({
5672
5718
  repoRoot: argvFlag(argv, "--repo-root"),
5673
5719
  offline: argv.includes("--offline"),
5674
5720
  rebaseline: argv.includes("--rebaseline"),
5675
- restamp: argv.includes("--restamp")
5721
+ restamp: argv.includes("--restamp"),
5722
+ check: argv.includes("--check")
5676
5723
  }),
5677
- run: async ({ store, actor, now }, { bundlePath: path, conceptId: id, repoRoot, offline, rebaseline, restamp }) => {
5724
+ run: async ({ store, actor, now }, {
5725
+ bundlePath: path,
5726
+ conceptId: id,
5727
+ repoRoot,
5728
+ offline,
5729
+ rebaseline,
5730
+ restamp,
5731
+ check
5732
+ }) => {
5733
+ if (check && (rebaseline || restamp)) {
5734
+ throw new KbFlagConflictError([
5735
+ "check",
5736
+ rebaseline ? "rebaseline" : "restamp"
5737
+ ]);
5738
+ }
5678
5739
  const root = repoRoot ?? process.cwd();
5679
5740
  const record = await store.read(path, id);
5680
5741
  if (!record) throw new KbRecordNotFoundError(id);
@@ -5683,7 +5744,6 @@ var anchorResolveCommand = define({
5683
5744
  return {
5684
5745
  conceptId: id,
5685
5746
  results: [],
5686
- verified: false,
5687
5747
  note: "record has no anchors"
5688
5748
  };
5689
5749
  }
@@ -5741,7 +5801,7 @@ var anchorResolveCommand = define({
5741
5801
  if (!anchor.hash) {
5742
5802
  results.push({
5743
5803
  ...base2,
5744
- state: "stamped",
5804
+ state: check ? "unstamped" : "stamped",
5745
5805
  currentHash: stampedHash,
5746
5806
  hashKind: stampedKind,
5747
5807
  ...producedBy ? { resolver: producedBy } : {}
@@ -5793,7 +5853,7 @@ var anchorResolveCommand = define({
5793
5853
  if (refresh) dirty = true;
5794
5854
  }
5795
5855
  let frozen = false;
5796
- if (dirty) {
5856
+ if (dirty && !check) {
5797
5857
  try {
5798
5858
  await assertBaseNotFrozen(process.cwd(), path);
5799
5859
  } catch (error) {
@@ -5808,42 +5868,11 @@ var anchorResolveCommand = define({
5808
5868
  const unreachable = results.filter(
5809
5869
  (entry) => isUncheckedReason(entry.reason)
5810
5870
  ).length;
5811
- const checked = results.length - unreachable;
5812
5871
  const matches3 = results.filter((entry) => entry.state === "match").length;
5813
- const note = `${matches3}/${checked} anchors match${unreachable ? `, ${unreachable} unreachable` : ""}`;
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
- }
5872
+ const note = `${matches3}/${results.length - unreachable} anchors match, ${unreachable} unreachable`;
5843
5873
  return {
5844
5874
  conceptId: id,
5845
5875
  results,
5846
- verified: false,
5847
5876
  ...unreachable ? { note } : {},
5848
5877
  ...frozenNote,
5849
5878
  ...hintNote
@@ -8632,7 +8661,7 @@ var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
8632
8661
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
8633
8662
 
8634
8663
  // src/version.ts
8635
- var VERSION = true ? "0.1.20" : "0.0.0-dev";
8664
+ var VERSION = true ? "0.1.21" : "0.0.0-dev";
8636
8665
 
8637
8666
  // src/mcp.ts
8638
8667
  function createKbMcpServer() {