@saasontools/strauss-kb 0.1.6 → 0.1.7

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
@@ -216,6 +216,8 @@ strauss-kb [--bundle PATH] <command> [args]
216
216
  answer <concept-id> <answer...> Resolve an open question and append the answer.
217
217
  verify <concept-id> --note <text> Append a verified[] event — who checked, when, and what the check found.
218
218
  load [type] [--budget N | --all] Hand over the whole base, each record with its standing.
219
+ pack <conceptId> [--hops N] [--max-nodes N] [--budget N]
220
+ The bounded neighbourhood around one record, every cut named.
219
221
  query <text...> Search; every match arrives flagged with its standing.
220
222
  trace <concept-id> [edges...] How a position was arrived at, as a timeline.
221
223
  list [type] Every record, optionally narrowed to one type.
@@ -234,7 +236,8 @@ strauss-kb [--bundle PATH] <command> [args]
234
236
  STRAUSS_KB_ACTOR names the writer in the log
235
237
  ```
236
238
 
237
- Results go to stdout as JSON — `index` is markdown, which is what it is. Errors
239
+ Results go to stdout as JSON — `index` and `pack` are markdown, which is what
240
+ they are. Errors
238
241
  go to stderr and exit 1. `validate` is the one command whose exit code is not
239
242
  just "did it run": a check that reports a problem succeeded as a command and
240
243
  failed as a check, so it exits 1 with its findings on stdout.
@@ -258,7 +261,7 @@ strauss-kb validate || echo "problems above"
258
261
 
259
262
  `strauss-kb-mcp` speaks stdio and takes no API key and no required environment.
260
263
  Every CLI verb is a tool: `kb_write`, `kb_write_decision`, `kb_no_decision`,
261
- `kb_status`, `kb_supersede`, `kb_answer`, `kb_verify`, `kb_load`, `kb_query`,
264
+ `kb_status`, `kb_supersede`, `kb_answer`, `kb_verify`, `kb_load`, `kb_pack`, `kb_query`,
262
265
  `kb_trace`, `kb_list`, `kb_index`, `kb_log`, `kb_validate`, `kb_schema`, `kb_types`,
263
266
  `kb_pin`, `kb_unpin`, `kb_pins`, `kb_context`. Most tools take a `bundlePath`;
264
267
  `kb_schema` and `kb_types` describe the format rather than any one base, and
@@ -362,6 +365,22 @@ setting to reach for by default. A reader that does not actually need every
362
365
  record is better served by a narrower `type` filter or a `query` than by
363
366
  turning the guardrail off.
364
367
 
368
+ **Pack is the middle rung.** Under budget, load the base whole — perfect
369
+ recall beats any ranking. Over budget, when the work centres on a record you
370
+ can name, `pack` hands over that record's bounded neighbourhood instead:
371
+ everything within `--hops` of the root, walked over the base's edges — body
372
+ links (a `relatedConceptIds` entry is stored as one), supersession in both
373
+ directions, shared code anchors, and shared sources — ranked and cut to
374
+ `--max-nodes`. Standing travels with it: superseded neighbours arrive as the
375
+ same name, replacement and date stubs `load` emits. Every record the cut
376
+ dropped is named under Excluded, because a named gap is knowable and a silent
377
+ one is not, and past its own token budget `pack` refuses exactly as `load`
378
+ does — naming what was already cut, so the caller can narrow the walk or
379
+ raise the ceiling. Below the header, the only place a timestamp appears, the
380
+ output is byte-identical across runs over an unchanged base: two packs diff,
381
+ and a changed byte means changed knowledge. With neither a budget problem nor
382
+ a root record in hand, the question is a point lookup, and that is `query`.
383
+
365
384
  **Flag, never filter.** `query` returns every hit with its standing, because a
366
385
  filtered result set is invisible — the caller cannot tell it missed anything.
367
386
  The single exception is narrow: a superseded record is dropped only when its
@@ -2,8 +2,9 @@ import {
2
2
  KB_COMMANDS,
3
3
  KB_COMMANDS_BY_NAME,
4
4
  KB_DIR,
5
- KbStore
6
- } from "./chunk-PNSRTKYN.js";
5
+ KbStore,
6
+ VERSION
7
+ } from "./chunk-GKUQOJEK.js";
7
8
 
8
9
  // src/cli.ts
9
10
  import { join } from "path";
@@ -14,6 +15,11 @@ async function runKbCli(argv) {
14
15
  process.stdout.write(usage());
15
16
  return;
16
17
  }
18
+ if (name === "--version" || name === "-v") {
19
+ process.stdout.write(`${VERSION}
20
+ `);
21
+ return;
22
+ }
17
23
  const command = KB_COMMANDS_BY_NAME.get(name);
18
24
  if (!command) die(`unknown command ${name}`);
19
25
  const raw = await command.fromArgv(rest, bundle, readStdin);
@@ -83,6 +89,7 @@ function usage() {
83
89
  ),
84
90
  "",
85
91
  ` --bundle PATH defaults to ./${KB_DIR}`,
92
+ " --version the installed package version",
86
93
  " STRAUSS_KB_ACTOR names the writer in the log",
87
94
  ""
88
95
  ].join("\n");
@@ -91,4 +98,4 @@ function usage() {
91
98
  export {
92
99
  runKbCli
93
100
  };
94
- //# sourceMappingURL=chunk-V7TRZ2ER.js.map
101
+ //# sourceMappingURL=chunk-GKCG4P3L.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["/**\n * strauss-kb — a knowledge base's command line.\n *\n * A dispatcher over `KB_COMMANDS`, which the MCP server also projects. Nothing\n * command-specific lives here beyond turning argv into the object both\n * surfaces pass.\n */\nimport { join } from \"node:path\";\nimport { KB_COMMANDS, KB_COMMANDS_BY_NAME } from \"./commands/index.js\";\nimport { KB_DIR, KbStore } from \"./kb-store.js\";\nimport { VERSION } from \"./version.js\";\n\nexport async function runKbCli(argv: string[]): Promise<void> {\n const { bundle, rest } = takeBundle(argv);\n const name = rest[0] ?? \"\";\n\n if (!name || name === \"-h\" || name === \"--help\") {\n process.stdout.write(usage());\n return;\n }\n\n // The plugin in front of this CLI updates from a marketplace while the CLI\n // updates from npm, and neither prompts for the other. Answering \"which one\n // is installed\" is what makes that skew diagnosable instead of mysterious.\n if (name === \"--version\" || name === \"-v\") {\n process.stdout.write(`${VERSION}\\n`);\n return;\n }\n\n const command = KB_COMMANDS_BY_NAME.get(name);\n if (!command) die(`unknown command ${name}`);\n\n const raw = await command.fromArgv(rest, bundle, readStdin);\n const parsed = command.input.safeParse(raw);\n if (!parsed.success) {\n die(\n `${name}: ${parsed.error.issues\n .map((issue) => `${issue.path.join(\".\") || \"(root)\"}: ${issue.message}`)\n .join(\"; \")}`,\n );\n }\n\n const store = new KbStore({\n warn: (entry) => process.stderr.write(`${JSON.stringify(entry)}\\n`),\n });\n const result = await command.run(\n {\n store,\n actor: process.env.STRAUSS_KB_ACTOR ?? \"unknown\",\n now: () => new Date().toISOString(),\n },\n parsed.data,\n );\n\n // A check reporting a problem succeeded as a command and failed as a check;\n // the command says which, rather than the dispatcher knowing their names.\n if (command.failsWhen?.(result)) process.exitCode = 1;\n // An empty string is deliberate silence — `context` with nothing pinned\n // runs from hooks at every session start, and even a bare newline is noise\n // injected into a fresh context.\n if (result === \"\") return;\n process.stdout.write(\n typeof result === \"string\"\n ? result.endsWith(\"\\n\")\n ? result\n : `${result}\\n`\n : `${JSON.stringify(result, null, 2)}\\n`,\n );\n}\n\n/**\n * `--bundle` addresses a base directly; without it the command works on the\n * one under the current directory. A base belongs to whatever prompted it, so\n * the default cannot be the only option.\n */\nfunction takeBundle(argv: string[]): { bundle: string; rest: string[] } {\n const at = argv.indexOf(\"--bundle\");\n if (at === -1) {\n return { bundle: join(process.cwd(), KB_DIR), rest: argv };\n }\n const bundle = argv[at + 1];\n if (!bundle) die(\"--bundle requires a path\");\n return { bundle, rest: [...argv.slice(0, at), ...argv.slice(at + 2)] };\n}\n\nfunction readStdin(): Promise<string> {\n return new Promise((resolve, reject) => {\n let text = \"\";\n process.stdin.setEncoding(\"utf8\");\n process.stdin.on(\"data\", (chunk) => (text += chunk));\n process.stdin.on(\"end\", () => resolve(text));\n process.stdin.on(\"error\", reject);\n });\n}\n\nfunction die(message: string): never {\n process.stderr.write(`strauss-kb: error: ${message}\\n`);\n process.exit(1);\n}\n\n/** First sentence, capped — the full text is what an MCP client shows. */\nfunction summarise(description: string): string {\n const first = description.split(\"\\n\")[0] ?? \"\";\n const sentence = first.includes(\". \")\n ? `${first.slice(0, first.indexOf(\". \"))}.`\n : first;\n return sentence.length > 78 ? `${sentence.slice(0, 75)}…` : sentence;\n}\n\nfunction usage(): string {\n const width = Math.max(...KB_COMMANDS.map((command) => command.usage.length));\n return [\n \"strauss-kb — knowledge base commands\",\n \"\",\n \"Usage: strauss-kb [--bundle PATH] <command> [args]\",\n \"\",\n ...KB_COMMANDS.map(\n (command) =>\n ` ${command.usage.padEnd(width)} ${summarise(command.description)}`,\n ),\n \"\",\n ` --bundle PATH defaults to ./${KB_DIR}`,\n \" --version the installed package version\",\n \" STRAUSS_KB_ACTOR names the writer in the log\",\n \"\",\n ].join(\"\\n\");\n}\n"],"mappings":"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","names":[]}