@saasontools/strauss-kb 0.1.9 → 0.1.10

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 CHANGED
@@ -256,7 +256,9 @@ strauss-kb [--bundle PATH] <command> [args]
256
256
  supersede <concept-id> <replacement-id> Mark a record superseded, linking both directions.
257
257
  answer <concept-id> <answer...> Resolve an open question and append the answer.
258
258
  verify <concept-id> --note <text> Append a verified[] event — who checked, when, and what the check found.
259
- load [type] [--budget N | --all] Hand over the whole base, each record with its standing.
259
+ load [type] [--budget N] [--all]
260
+ Hand over the whole base, each record with its standing.
261
+ catalog [type] Every record in one line — id, type, title, standing, stale flag.
260
262
  pack <conceptId> [--hops N] [--max-nodes N] [--budget N]
261
263
  The bounded neighbourhood around one record, every cut named.
262
264
  query <text...> Search; every match arrives flagged with its standing.
@@ -281,7 +283,7 @@ strauss-kb [--bundle PATH] <command> [args]
281
283
  STRAUSS_KB_ACTOR names the writer in the log
282
284
  ```
283
285
 
284
- Results go to stdout as JSON — `index` and `pack` are markdown, which is what
286
+ Results go to stdout as JSON — `index`, `catalog` and `pack` are markdown, which is what
285
287
  they are, and `doctor` prints a table unless `--json` asks for the object
286
288
  behind it. `--json` is refused rather than ignored on the commands that have
287
289
  only one form, since a flag that quietly does nothing reads as one that
@@ -291,6 +293,10 @@ whose exit code is not just "did it run": a check that reports a problem
291
293
  succeeded as a command and failed as a check, so it exits 1 with its findings
292
294
  on stdout.
293
295
 
296
+ A flag accepts either spelling — `--budget 4000` or `--budget=4000` — and a
297
+ flag given no value is an error rather than a silent fallback to the default,
298
+ so a trailing typo cannot look like success.
299
+
294
300
  ```bash
295
301
  strauss-kb --bundle .strauss/kb write fact <<'JSON'
296
302
  {
@@ -310,9 +316,9 @@ strauss-kb validate || echo "problems above"
310
316
 
311
317
  `strauss-kb-mcp` speaks stdio and takes no API key and no required environment.
312
318
  Every CLI verb is a tool: `kb_write`, `kb_write_decision`, `kb_no_decision`,
313
- `kb_status`, `kb_supersede`, `kb_answer`, `kb_verify`, `kb_load`, `kb_pack`, `kb_query`,
314
- `kb_trace`, `kb_list`, `kb_index`, `kb_log`, `kb_validate`, `kb_doctor`,
315
- `kb_schema`, `kb_types`,
319
+ `kb_status`, `kb_supersede`, `kb_answer`, `kb_verify`, `kb_load`, `kb_catalog`,
320
+ `kb_pack`, `kb_query`, `kb_trace`, `kb_list`, `kb_index`, `kb_log`, `kb_validate`,
321
+ `kb_doctor`, `kb_schema`, `kb_types`,
316
322
  `kb_pin`, `kb_unpin`, `kb_pins`, `kb_context`. Most tools take a `bundlePath`;
317
323
  `kb_schema` and `kb_types` describe the format rather than any one base, and
318
324
  `kb_pins` and `kb_context` read the workspace pin manifests instead. The one
@@ -396,40 +402,66 @@ Read for a question, not for a session: a base loaded at the start of a long
396
402
  conversation is summarised away by the end of it, and reloading costs about
397
403
  three thousand tokens. Read it again at the point of use.
398
404
 
399
- `load` refuses rather than truncating when a base exceeds its budget (25,000
400
- tokens by default). A truncated base is indistinguishable from a complete one,
401
- so a caller would answer "that was never decided" from a slice it did not know
402
- was a slice. `context` refuses the same way at its own, tighter budget (4,000
403
- by default). Superseded records come back as name, replacement and date only —
404
- their bodies no longer hold, and a body read later in a long session outlives
405
- the qualifier that said so. `trace` still reaches them by id.
406
-
407
- `--all` (`all: true` over MCP) is the escape hatch: it bypasses the refusal
408
- outright and hands back the entire bundle whatever its size. A loaded result
409
- carries `tokensLoaded`, the same estimate the budget is held against, and
410
- `budgetTokens: null` marks that no ceiling was applied; `--all` is mutually
411
- exclusive with `--budget`. That refusal is the guardrail an agent needs so a
412
- wide base does not silently consume its whole context; `--all` is for a
413
- deliberate operator who has decided the size is worth the tokens, not a
414
- setting to reach for by default. A reader that does not actually need every
415
- record is better served by a narrower `type` filter or a `query` than by
416
- turning the guardrail off.
417
-
418
- **Pack is the middle rung.** Under budget, load the base whole perfect
419
- recall beats any ranking. Over budget, when the work centres on a record you
420
- can name, `pack` hands over that record's bounded neighbourhood instead:
421
- everything within `--hops` of the root, walked over the base's edges — body
422
- links (a `relatedConceptIds` entry is stored as one), supersession in both
423
- directions, shared code anchors, and shared sources ranked and cut to
424
- `--max-nodes`. Standing travels with it: superseded neighbours arrive as the
425
- same name, replacement and date stubs `load` emits. Every record the cut
426
- dropped is named under Excluded, because a named gap is knowable and a silent
427
- one is not, and past its own token budget `pack` refuses exactly as `load`
428
- does naming what was already cut, so the caller can narrow the walk or
429
- raise the ceiling. Below the header, the only place a timestamp appears, the
430
- output is byte-identical across runs over an unchanged base: two packs diff,
431
- and a changed byte means changed knowledge. With neither a budget problem nor
432
- a root record in hand, the question is a point lookup, and that is `query`.
405
+ **The three rungs, in one rule.** While the base fits the budget, `load` it
406
+ whole. Once `load` refuses, `catalog` then `pack` the record that matters. For
407
+ a lookup by wording, `query`.
408
+
409
+ ```bash
410
+ strauss-kb load # under the budget: everything, with standing
411
+ strauss-kb catalog # past it: one line per record, ~30 tokens each
412
+ strauss-kb pack decision.cursor-v2 # then the neighbourhood around the one that matters
413
+ strauss-kb query cursor pagination # or a point lookup by wording
414
+ ```
415
+
416
+ A whole read gives perfect recall and can say _no record answers this_, which
417
+ no ranker can. `catalog` keeps that at a fraction of the cost by naming every
418
+ record instead of every body; `query` gives up both, returning its nearest hit
419
+ whatever the distance.
420
+
421
+ `load` refuses rather than truncating past its token budget (`--budget` /
422
+ `budgetTokens`, 25,000 by default, held against the estimated size of what is
423
+ handed back) — a truncated base reads as a complete one, so a caller would
424
+ answer "never decided" from a slice it did not know was a slice. `context`
425
+ refuses the same way at its own, tighter budget (4,000 by default). Superseded
426
+ records come back as name, replacement and date only; `trace` still reaches
427
+ them by id.
428
+
429
+ A refusal reports `approxTokens` against `budgetTokens` and carries a `message`
430
+ naming the budget and the next calls. A successful load reports `budgetTokens`
431
+ too, so a caller can see how close it came before crossing the line.
432
+
433
+ `--all` (`all: true` over MCP) bypasses the budget and hands back the entire
434
+ bundle regardless of size. A loaded result carries `tokensLoaded`, and
435
+ `budgetTokens: null` marks that no ceiling applied; `--all` is mutually
436
+ exclusive with `--budget`. It is for an operator who has decided the size is
437
+ worth the tokens a narrower `type` filter, `catalog`, or `query` fits better
438
+ when it is not.
439
+
440
+ **Catalog is the rung that keeps the base knowable.** One line per record —
441
+ concept id, type, title, standing, stale flag — sorted by type then title, at
442
+ roughly thirty tokens each, so a base far past `load`'s budget still fits in
443
+ one call. Superseded records show the replacement in place of a body. The
444
+ header sums record counts by standing and reports staleness separately (a
445
+ current record can be stale). Bodies live in `load`, `pack`, and `trace`.
446
+
447
+ `catalog` alone has no ceiling and never refuses — cost is linear at roughly
448
+ thirty tokens a record (a thousand-record base is about 30k, five thousand
449
+ about 150k); narrow with `type` at that scale. Output is deterministic given
450
+ a fixed clock — no timestamp, ordering total down to the concept id — so two
451
+ catalogs of an unchanged base diff to nothing except a stale flag flipping as
452
+ `stale_after` passes. Pass an explicit `now` (library callers) to hold
453
+ byte-equality across that boundary.
454
+
455
+ **Pack is the middle rung.** Under the budget, load the base whole. Past it, when the work centres on a record you can name (`catalog` is how you
456
+ name it), `pack` hands over that record's bounded neighbourhood: everything
457
+ within `--hops` of the root, walked over the base's edges — body links (a
458
+ `relatedConceptIds` entry is one), supersession in both directions, shared
459
+ code anchors, and shared sources — ranked and cut to `--max-nodes`. Standing
460
+ travels with it: superseded neighbours arrive as the same stubs `load` emits.
461
+ Every dropped record is named under Excluded, and past its own token budget
462
+ `pack` refuses exactly as `load` does. Output is byte-identical across runs
463
+ over an unchanged base below the header. With neither a size problem nor a
464
+ root record in hand, the question is a point lookup — `query`.
433
465
 
434
466
  **Flag, never filter.** `query` returns every hit with its standing, because a
435
467
  filtered result set is invisible — the caller cannot tell it missed anything.