@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/index.d.cts CHANGED
@@ -1501,6 +1501,8 @@ declare enum Fault {
1501
1501
  declare enum ErrorTypes {
1502
1502
  KbRecordAlreadyExists = "KbRecordAlreadyExists",
1503
1503
  KbClassifyInput = "KbClassifyInput",
1504
+ KbFlagConflict = "KbFlagConflict",
1505
+ KbInvalidActor = "KbInvalidActor",
1504
1506
  KbInvalidConceptId = "KbInvalidConceptId",
1505
1507
  KbMatchInput = "KbMatchInput",
1506
1508
  KbMissingFlagValue = "KbMissingFlagValue",
package/dist/index.d.ts CHANGED
@@ -1501,6 +1501,8 @@ declare enum Fault {
1501
1501
  declare enum ErrorTypes {
1502
1502
  KbRecordAlreadyExists = "KbRecordAlreadyExists",
1503
1503
  KbClassifyInput = "KbClassifyInput",
1504
+ KbFlagConflict = "KbFlagConflict",
1505
+ KbInvalidActor = "KbInvalidActor",
1504
1506
  KbInvalidConceptId = "KbInvalidConceptId",
1505
1507
  KbMatchInput = "KbMatchInput",
1506
1508
  KbMissingFlagValue = "KbMissingFlagValue",
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  runKbCli
3
- } from "./chunk-XDH6J6CH.js";
3
+ } from "./chunk-VXWN62HT.js";
4
4
  import {
5
5
  createKbMcpServer,
6
6
  runKbMcpServer
7
- } from "./chunk-EXZQZJU2.js";
7
+ } from "./chunk-WVJULVGY.js";
8
8
  import {
9
9
  BaseError,
10
10
  CONTEXT_BEGIN,
@@ -149,7 +149,7 @@ import {
149
149
  unifiedDiff,
150
150
  unpinBase,
151
151
  validateBundle
152
- } from "./chunk-QQLPJO4R.js";
152
+ } from "./chunk-XBZ7G5R5.js";
153
153
  export {
154
154
  BaseError,
155
155
  CONTEXT_BEGIN,
package/dist/mcp-main.cjs CHANGED
@@ -2349,6 +2349,38 @@ var KbSelfVerificationError = class extends BaseError {
2349
2349
  actor;
2350
2350
  generatedBy;
2351
2351
  };
2352
+ var KbInvalidActorError = class extends BaseError {
2353
+ constructor(actor, reason) {
2354
+ super({
2355
+ message: `kb: actor ${JSON.stringify(actor)} ${reason} \u2014 set STRAUSS_KB_ACTOR, e.g. human:alice or agent:reviewer`,
2356
+ errorType: "KbInvalidActor" /* KbInvalidActor */,
2357
+ code: 400,
2358
+ fault: "User" /* User */,
2359
+ retriable: false,
2360
+ reportToUser: true,
2361
+ details: { actor, reason, action: "refused" }
2362
+ });
2363
+ this.actor = actor;
2364
+ this.reason = reason;
2365
+ }
2366
+ actor;
2367
+ reason;
2368
+ };
2369
+ var KbFlagConflictError = class extends BaseError {
2370
+ constructor(flags) {
2371
+ super({
2372
+ message: `kb: ${flags.join(" and ")} cannot be combined`,
2373
+ errorType: "KbFlagConflict" /* KbFlagConflict */,
2374
+ code: 400,
2375
+ fault: "User" /* User */,
2376
+ retriable: false,
2377
+ reportToUser: true,
2378
+ details: { flags }
2379
+ });
2380
+ this.flags = flags;
2381
+ }
2382
+ flags;
2383
+ };
2352
2384
  var KbPackBudgetExceededError = class extends BaseError {
2353
2385
  constructor(recordCount, approxTokens2, budgetTokens, excluded) {
2354
2386
  super({
@@ -2892,19 +2924,11 @@ function argvPositional(argv, ...names) {
2892
2924
  }
2893
2925
 
2894
2926
  // src/commands/anchor-resolve.ts
2895
- function resolverSummary(results) {
2896
- const names = [
2897
- ...new Set(
2898
- results.flatMap((entry) => entry.resolver ? [entry.resolver] : [])
2899
- )
2900
- ].sort();
2901
- return names.length ? `${names.join(" + ")} resolver` : "whole-file";
2902
- }
2903
2927
  var anchorResolveCommand = define({
2904
2928
  name: "anchor-resolve",
2905
2929
  tool: "kb_anchor_resolve",
2906
- usage: "anchor-resolve <concept-id> [--repo-root <path>] [--offline] [--rebaseline] [--restamp]",
2907
- 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.",
2930
+ usage: "anchor-resolve <concept-id> [--repo-root <path>] [--offline] [--rebaseline] [--restamp] [--check]",
2931
+ 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.",
2908
2932
  input: import_zod7.z.object({
2909
2933
  bundlePath,
2910
2934
  conceptId,
@@ -2917,6 +2941,9 @@ var anchorResolveCommand = define({
2917
2941
  ),
2918
2942
  restamp: import_zod7.z.boolean().optional().describe(
2919
2943
  "Refresh `resolved_at` on anchors that already match. Off by default, so a green run writes nothing."
2944
+ ),
2945
+ check: import_zod7.z.boolean().optional().describe(
2946
+ "Resolve and report only: no hash, no `resolved_at`, no log entry."
2920
2947
  )
2921
2948
  }),
2922
2949
  fromArgv: (argv, path) => ({
@@ -2925,9 +2952,24 @@ var anchorResolveCommand = define({
2925
2952
  repoRoot: argvFlag(argv, "--repo-root"),
2926
2953
  offline: argv.includes("--offline"),
2927
2954
  rebaseline: argv.includes("--rebaseline"),
2928
- restamp: argv.includes("--restamp")
2955
+ restamp: argv.includes("--restamp"),
2956
+ check: argv.includes("--check")
2929
2957
  }),
2930
- run: async ({ store, actor, now }, { bundlePath: path, conceptId: id, repoRoot, offline, rebaseline, restamp }) => {
2958
+ run: async ({ store, actor, now }, {
2959
+ bundlePath: path,
2960
+ conceptId: id,
2961
+ repoRoot,
2962
+ offline,
2963
+ rebaseline,
2964
+ restamp,
2965
+ check
2966
+ }) => {
2967
+ if (check && (rebaseline || restamp)) {
2968
+ throw new KbFlagConflictError([
2969
+ "check",
2970
+ rebaseline ? "rebaseline" : "restamp"
2971
+ ]);
2972
+ }
2931
2973
  const root = repoRoot ?? process.cwd();
2932
2974
  const record = await store.read(path, id);
2933
2975
  if (!record) throw new KbRecordNotFoundError(id);
@@ -2936,7 +2978,6 @@ var anchorResolveCommand = define({
2936
2978
  return {
2937
2979
  conceptId: id,
2938
2980
  results: [],
2939
- verified: false,
2940
2981
  note: "record has no anchors"
2941
2982
  };
2942
2983
  }
@@ -2994,7 +3035,7 @@ var anchorResolveCommand = define({
2994
3035
  if (!anchor.hash) {
2995
3036
  results.push({
2996
3037
  ...base2,
2997
- state: "stamped",
3038
+ state: check ? "unstamped" : "stamped",
2998
3039
  currentHash: stampedHash,
2999
3040
  hashKind: stampedKind,
3000
3041
  ...producedBy ? { resolver: producedBy } : {}
@@ -3046,7 +3087,7 @@ var anchorResolveCommand = define({
3046
3087
  if (refresh) dirty = true;
3047
3088
  }
3048
3089
  let frozen = false;
3049
- if (dirty) {
3090
+ if (dirty && !check) {
3050
3091
  try {
3051
3092
  await assertBaseNotFrozen(process.cwd(), path);
3052
3093
  } catch (error) {
@@ -3061,42 +3102,11 @@ var anchorResolveCommand = define({
3061
3102
  const unreachable = results.filter(
3062
3103
  (entry) => isUncheckedReason(entry.reason)
3063
3104
  ).length;
3064
- const checked = results.length - unreachable;
3065
3105
  const matches3 = results.filter((entry) => entry.state === "match").length;
3066
- const note = `${matches3}/${checked} anchors match${unreachable ? `, ${unreachable} unreachable` : ""}`;
3067
- const clean = checked > 0 && matches3 === checked && unreachable === 0;
3068
- if (clean) {
3069
- try {
3070
- await store.verify(
3071
- path,
3072
- id,
3073
- `anchor-resolve: ${note} (${resolverSummary(results)})`,
3074
- actor,
3075
- now()
3076
- );
3077
- } catch (error) {
3078
- if (!(error instanceof KbSelfVerificationError)) throw error;
3079
- return {
3080
- conceptId: id,
3081
- results,
3082
- verified: false,
3083
- verifyRefused: "self-verification",
3084
- ...frozenNote,
3085
- ...hintNote
3086
- };
3087
- }
3088
- return {
3089
- conceptId: id,
3090
- results,
3091
- verified: true,
3092
- ...frozenNote,
3093
- ...hintNote
3094
- };
3095
- }
3106
+ const note = `${matches3}/${results.length - unreachable} anchors match, ${unreachable} unreachable`;
3096
3107
  return {
3097
3108
  conceptId: id,
3098
3109
  results,
3099
- verified: false,
3100
3110
  ...unreachable ? { note } : {},
3101
3111
  ...frozenNote,
3102
3112
  ...hintNote
@@ -7653,6 +7663,7 @@ var KbStore = class {
7653
7663
  * its contents.
7654
7664
  */
7655
7665
  async write(bundlePath2, input, actor = "unknown") {
7666
+ assertActor(actor);
7656
7667
  if (!KB_SLUG_PATTERN.test(input.slug)) {
7657
7668
  throw new KbInvalidConceptIdError("slug must be kebab-case", {
7658
7669
  slug: input.slug
@@ -7754,6 +7765,7 @@ var KbStore = class {
7754
7765
  * timeouts.
7755
7766
  */
7756
7767
  async setStatus(bundlePath2, conceptId2, status, actor = "unknown") {
7768
+ assertActor(actor);
7757
7769
  return this.mutate(
7758
7770
  bundlePath2,
7759
7771
  conceptId2,
@@ -7772,6 +7784,7 @@ var KbStore = class {
7772
7784
  * the frontmatter must not be published back out under an actor stamp.
7773
7785
  */
7774
7786
  async updateAnchors(bundlePath2, conceptId2, anchors, actor = "unknown") {
7787
+ assertActor(actor);
7775
7788
  const checked = anchors.map((anchor) => kbAnchorWriteSchema.parse(anchor));
7776
7789
  return this.mutate(
7777
7790
  bundlePath2,
@@ -7792,6 +7805,7 @@ var KbStore = class {
7792
7805
  * logs what it publishes.
7793
7806
  */
7794
7807
  async verify(bundlePath2, conceptId2, note, actor = "unknown", at2 = (/* @__PURE__ */ new Date()).toISOString()) {
7808
+ assertActor(actor, { named: true });
7795
7809
  const event = kbVerifiedEventSchema.parse({ by: actor, at: at2, note });
7796
7810
  const existing = await this.read(bundlePath2, conceptId2);
7797
7811
  if (!existing) throw new KbRecordNotFoundError(conceptId2);
@@ -7822,6 +7836,7 @@ var KbStore = class {
7822
7836
  * in normal use, and validation drops to catching hand-edits.
7823
7837
  */
7824
7838
  async supersede(bundlePath2, conceptId2, replacementId, actor = "unknown") {
7839
+ assertActor(actor);
7825
7840
  const replacement = await this.read(bundlePath2, replacementId);
7826
7841
  if (!replacement) throw new KbRecordNotFoundError(replacementId);
7827
7842
  const superseded = await this.markSuperseded(
@@ -7845,6 +7860,7 @@ var KbStore = class {
7845
7860
  }
7846
7861
  /** Resolves an open question, stamping who answered and when. */
7847
7862
  async answer(bundlePath2, conceptId2, answer, actor = "unknown", at2 = (/* @__PURE__ */ new Date()).toISOString()) {
7863
+ assertActor(actor);
7848
7864
  return this.mutate(
7849
7865
  bundlePath2,
7850
7866
  conceptId2,
@@ -7871,6 +7887,7 @@ ${answer}
7871
7887
  * terminal status since the caller listed it is reported, not removed.
7872
7888
  */
7873
7889
  async deleteRecord(bundlePath2, conceptId2, expected, actor = "unknown") {
7890
+ assertActor(actor);
7874
7891
  const target = this.recordPath(bundlePath2, conceptId2);
7875
7892
  const witness = await this.read(bundlePath2, conceptId2);
7876
7893
  if (!witness) throw new KbRecordNotFoundError(conceptId2);
@@ -8174,6 +8191,7 @@ ${answer}
8174
8191
  * base too, where nothing was written.
8175
8192
  */
8176
8193
  async note(bundlePath2, entry) {
8194
+ assertActor(entry.by);
8177
8195
  await this.record(this.root(bundlePath2), entry);
8178
8196
  }
8179
8197
  /**
@@ -8431,9 +8449,18 @@ function normalizeActor(id) {
8431
8449
  if (colon === -1) return id.toLowerCase();
8432
8450
  return id.slice(0, colon + 1).toLowerCase() + id.slice(colon + 1);
8433
8451
  }
8452
+ var KB_ACTOR_PATTERN = /^[A-Za-z][\w-]*(?::[\p{L}\p{M}\p{N}_.@+/-]+)?$/u;
8453
+ function assertActor(actor, { named = false } = {}) {
8454
+ if (!KB_ACTOR_PATTERN.test(actor)) {
8455
+ throw new KbInvalidActorError(actor, "is not kind or kind:name");
8456
+ }
8457
+ if (named && normalizeActor(actor) === "unknown") {
8458
+ throw new KbInvalidActorError(actor, "cannot verify: name who checked");
8459
+ }
8460
+ }
8434
8461
 
8435
8462
  // src/version.ts
8436
- var VERSION = true ? "0.1.20" : "0.0.0-dev";
8463
+ var VERSION = true ? "0.1.21" : "0.0.0-dev";
8437
8464
 
8438
8465
  // src/mcp.ts
8439
8466
  function createKbMcpServer() {