Skip to main content

strauss-kb

@saasontools/strauss-kb is a durable project knowledge base: a directory of small markdown records, each carrying not just a claim but its standing — whether it is still what the project holds. Copy the directory and you have the whole thing.

The package is that directory's library, its command line, and its MCP server. All three project one command table, so a capability exists in every surface or in none.

The problem​

An agent session forgets: attention decays, and compaction summarises away both the material loaded early and the instruction that said to consult it. Two failures follow:

  • Re-deciding. A constraint settled last week is invisible this week, so it gets "simplified" away, and the diff does not record which alternative was rejected.
  • Acting on stale conclusions. A superseded record is usually the older, longer, more general one, so any similarity measure favours the record that is no longer true.

So every result arrives flagged, never filtered: a superseded record comes back alongside whatever replaced it, and a rejected one is marked as explicitly not adopted.

What a base looks like​

<kb>/
<type>.<slug>.md records
INDEX.md index derived, store-owned
log.jsonl history primary, append-only
.gitattributes merge store-owned, written on first write
.index.sqlite search derived, gitignored

The default base is .strauss/kb relative to the working directory; every command takes --bundle PATH to address another. Records are OKF concepts, and the filename is the identity: fact.auth-retries.md has concept id fact.auth-retries.

The three surfaces​

SurfaceEntry pointWho uses it
CLIstrauss-kbagent skills shelling out by name, and humans
MCP serverstrauss-kb-mcp (stdio)any MCP client
Libraryimport { KbStore } from "@saasontools/strauss-kb"programmatic callers, diff annotation

The table holds 24 verbs and projects 23 of them as MCP tools; the one verb with no tool is sync-instructions, whose capability is kb_context.

npm install -g @saasontools/strauss-kb

Global install is the supported path: the consumers of the CLI are agent skills that invoke strauss-kb by name from whatever directory the work happens to be in. npx -y @saasontools/strauss-kb@0.1 and a project-local pnpm add -D both work, at the cost of per-call resolution latency.

Read for a question, not for a session​

The counter-intuitive default: load the whole base rather than searching it. These bases run to a few thousand tokens, and on nine questions whose wording appears in no record, a reader holding the whole base answered eight where embedding search over the same records answered four. Read it at the point of use: reloading costs about three thousand tokens.

The three rungs, in one rule​

load the base whole. When it refuses on budget, catalog then pack the record that matters. For a lookup by wording, query.

strauss-kb load # everything, with standing
strauss-kb catalog # if load refuses: one line per record, ~30 tokens each
strauss-kb pack decision.cursor-v2 # then the neighbourhood around the one that matters
strauss-kb query cursor pagination # or a point lookup by wording

A whole read gives perfect recall and can say no record answers this. A catalog keeps that second property and gives up the bodies. A query gives up both: it returns its nearest hit whatever the distance. load holds a 25,000-token budget and refuses rather than truncating past it.

Where to go next​

  • Specification — the record model, the bundle layout, and the validation rules.
  • Architecture — why it is shaped this way, and which alternatives were dropped.
  • Use cases — recording, querying, superseding, verifying, tracing, loading into agent context, packing a subgraph.
  • CLI reference — every verb, its flags, and an example.
  • MCP reference — every tool and its parameters.