@polycode-projects/seonix 0.6.1 → 0.7.1

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
@@ -110,10 +110,286 @@ within one repo. Query a merged graph by passing the merge root as `repo_path`
110
110
  ## The shape that ships
111
111
 
112
112
  The product is the **no-MCP digest path**: render the digest, inject it once, run
113
- with **no server**. The interactive MCP tool loop is retired. Even mitigated it
114
- never cleared the 50% bar, because the cost lies in the round-trip count rather
115
- than the resident schema. Use `seonix_search` (`locate`, self-derives the target)
116
- in production; pass a known target (`oracle`) only to measure the headroom.
113
+ with **no server**. The stdio MCP server still ships and is fully supported (bare
114
+ `seonix` starts it) it's just not the recommended default, because even
115
+ mitigated the interactive tool loop never cleared the 50% bar: the cost lies in
116
+ the round-trip count rather than the resident schema. Use `seonix_search`
117
+ (`locate`, self-derives the target) in production; pass a known target (`oracle`)
118
+ only to measure the headroom.
119
+
120
+ ## CLI reference
121
+
122
+ Every invocation shape `bin/cli.mjs` dispatches on, verified against the current source.
123
+
124
+ ### Bare invocation — MCP stdio server
125
+
126
+ ```bash
127
+ seonix
128
+ ```
129
+
130
+ No flags, no config file. Starts the stdio MCP server against
131
+ `<cwd>/.seonix/graph.json` (override with `SEONIX_GRAPH_FILE`). MCP launchers
132
+ should start it with cwd = the indexed worktree.
133
+
134
+ ### `cli index_repository` — build the graph
135
+
136
+ ```bash
137
+ seonix cli index_repository '{"repo_path":"/abs/repo"}'
138
+ seonix cli index_repository '{"repo_paths":["/abs/repo1","/abs/repo2"],"out_root":"/abs/dir"}'
139
+ seonix cli index_repository '{"multi_root":"/abs/estate"}'
140
+ ```
141
+
142
+ `repo_path` / `repo_paths`+`out_root` / `multi_root` are mutually exclusive — pass
143
+ exactly one (or let `seonix.toml`'s `repositories` supply the set when none is
144
+ given explicitly). Shared JSON options:
145
+
146
+ - `ignores` (bool, default `true`) — honour the repo's `.seonixignore`. Pass
147
+ `"ignores":false` to index everything regardless (the bench harness always
148
+ passes this, so a subject repo's own `.seonixignore` never shapes an arm's index).
149
+ - `config` (bool, default `true`) — read `seonix.toml` from the target dir. Pass
150
+ `"config":false` to skip it entirely (byte-identical to no-file behaviour; the
151
+ bench harness's index/locate/digest calls always pass this).
152
+ - `sync` (bool) — on a `multi_root` call, `"sync":true` routes through the same
153
+ incremental path as `cli sync` instead of a full re-index.
154
+ - `history_depth` (non-negative integer) — cap on the git-history pass (both the
155
+ commit walk and the history-symbol pass). `0` skips git history entirely;
156
+ absent uses the default depth. Precedence: this arg > `seonix.toml`
157
+ `[index].history_depth` > default.
158
+
159
+ Writes `<out_root-or-repo_path>/.seonix/graph.json` (+ manifest for
160
+ `multi_root`/`sync`), prints indexed counts to stderr and a human summary MD to
161
+ stdout.
162
+
163
+ ### `cli sync` — incremental estate re-index
164
+
165
+ ```bash
166
+ seonix cli sync '{"multi_root":"/abs/estate"}'
167
+ ```
168
+
169
+ Fingerprints every child repo of `multi_root` and re-extracts only the ones that
170
+ changed since the last run (per-repo cache under `.seonix/cache/`); one union
171
+ rebuild is exact-equal to a full re-index, and an unchanged estate is a byte-stable
172
+ no-op. Accepts `ignores` and `history_depth` as above. Writes
173
+ `.seonix/manifest.json`.
174
+
175
+ ### `cli digest` — the no-MCP injection payload
176
+
177
+ ```bash
178
+ seonix cli digest '{"repo_path":"/abs/repo","modules":["path/to/mod.mjs","other/mod.py"]}'
179
+ seonix cli digest '{"repo_path":"/abs/repo","query":"add a truncate filter to the article view"}'
180
+ ```
181
+
182
+ Two ways to pick the modules: an explicit `modules` array, or a `query` string —
183
+ `query` auto-locates and score-gap-selects the modules in one call (`modules`
184
+ wins if both are given). Prints an architecture map + a size-adaptive context
185
+ bundle per module to stdout, headed by a machine-readable line the rig parses:
186
+ `# seonix-digest tier=<NONE|TINY|MID|LARGE|FULL> topup=<bool> modules=<N>
187
+ [selected=<comma-list>]` (`selected=` only appears in query mode).
188
+
189
+ Tuning flags (all optional, precedence explicit arg > `seonix.toml [tune]` >
190
+ shipped default):
191
+
192
+ - `top_k` (int, query mode only, default 2) — how many modules `query` mode selects.
193
+ - `score_gap` (number, or `false` to disable) — the score-gap cutoff for query-mode selection.
194
+ - `literal_mention` (bool, default `true`) — credit verbatim dotted-name/path mentions in the raw query text.
195
+ - `min` (bool) — force the leanest bundle (no auto top-up).
196
+ - `untuned` (bool) — the pre-tuning escalation behaviour.
197
+ - `max` (bool) — the injection ceiling: every requested module gets a FULL (untrimmed) bundle instead of primary-full + capped trimmed secondaries.
198
+
199
+ ### `cli seonix_locate` — ranked module list (recall probe)
200
+
201
+ ```bash
202
+ seonix cli seonix_locate '{"query":"…","repo_path":"/abs/repo"}'
203
+ ```
204
+
205
+ Prints `<relpath>\t<score>` per line, highest first — the raw ranking a caller
206
+ (or the rig) can threshold itself. Recall/precision levers, all optional
207
+ (precedence arg > `seonix.toml [tune]` > default, all default `false` unless noted):
208
+
209
+ - `demote_nonprod` — demote `examples/`/`fixtures/`/`test-*` paths.
210
+ - `call_adjacency` — resolved-call adjacency bonus.
211
+ - `impl_of_interface` — C# impl-of-interface boost.
212
+ - `beam_search` (+ `beam_width`) — multi-ply discriminative beam expansion.
213
+ - `literal_mention` (default `true`) — verbatim dotted-name/path mention matching against the raw query.
214
+ - `raw_query` — forward a separately-normalized raw problem text for literal matching (else derived from `query`).
215
+ - `spiral` (+ `spiral_depth`, `limit`) — a bounded-radius, degree-ordered ego-neighbourhood walk from the lexical seeds that can surface lexically-invisible modules. `spiral_depth` is the hop radius (only honoured if `>0`); `limit` overrides how many newly-reached nodes the spiral may surface (default 100 — deliberately above the internal 12-node default, since this CLI surface is a recall probe, not a token budget).
216
+
217
+ ### `cli browser_link` — NL request → Chronograph URL
218
+
219
+ ```bash
220
+ seonix cli browser_link '{"query":"classes that change with render","base":"https://example.com"}'
221
+ ```
222
+
223
+ Maps a natural-language-ish request to the code-browser query grammar
224
+ deterministically (no model call), self-tests the resulting link against the
225
+ local temporal graph (must match ≥1 node and every cursor must resolve), then
226
+ prints the URL — or exits 1 with the failure reason(s) rather than printing a
227
+ dead link. Accepts `repo_path` (which repo's graph to test against), `base` (the
228
+ URL prefix the link is built on), `at` / `b` (the two temporal cursors — single
229
+ cursor / diff-cursor), `scope` (restrict the graph to a package-prefix set,
230
+ same values as `viz --scope`), and `grammar:true` (take `query` as grammar
231
+ verbatim, skipping NL mapping).
232
+
233
+ ### `cli store_rebuild` — force-rebuild the sqlite store
234
+
235
+ ```bash
236
+ seonix cli store_rebuild '{"repo_path":"/abs/repo"}'
237
+ ```
238
+
239
+ Force-(re)builds the opt-in `node:sqlite` resident store (see
240
+ [SEONIX_STORE](#seonix_store--seonix_graph_format) below) from the current
241
+ `graph.json`. The store is a derived, rebuildable companion — read paths never
242
+ auto-build it, so this is the explicit way to (re)materialize it after an index
243
+ or a manual `graph.json` edit.
244
+
245
+ ### `cli <toolName>` — generic MCP tool fallback
246
+
247
+ ```bash
248
+ seonix cli seonix_ask '{"query":"which modules import codegraph"}'
249
+ seonix cli seonix_context '{"symbol":"buildContextBundle","repo_path":"/abs/repo"}'
250
+ ```
251
+
252
+ Any sub-command name that isn't one of the above routes straight to the MCP tool
253
+ dispatcher, so any tool in the server's surface — cold or hot — is invokable from
254
+ Bash with no MCP connection. `repo_path` in the JSON arg selects the target
255
+ graph; every other key is passed through as the tool's own arguments.
256
+
257
+ ### `hook-augment` — PreToolUse Grep/Glob augmenter
258
+
259
+ ```bash
260
+ seonix hook-augment # reads a PreToolUse hook event JSON from stdin
261
+ ```
262
+
263
+ Reads the hook event from stdin, extracts the Grep/Glob pattern, and — if the
264
+ local graph has a hit — writes `{hookSpecificOutput:{hookEventName,
265
+ additionalContext}}` to stdout so the agent sees seon typed-graph hits alongside
266
+ its own search. Silent (exit 0, no output) on any failure — empty/malformed
267
+ input, no graph, no hit — the **never-block contract**.
268
+
269
+ ### `init [--dotnet]` — seed a seonix.toml
270
+
271
+ ```bash
272
+ seonix init # single-repo template
273
+ seonix init --dotnet # estate/.NET template (repositories + broader include_text/exclude)
274
+ seonix init --force # overwrite an existing seonix.toml
275
+ ```
276
+
277
+ Copies the shipped template to `./seonix.toml` (byte-for-byte). Refuses to
278
+ overwrite an existing file unless `--force` (exit 2).
279
+
280
+ ### `config --effective [--repo <abs>]`
281
+
282
+ ```bash
283
+ seonix config --effective
284
+ seonix config --effective --repo /abs/repo
285
+ ```
286
+
287
+ Prints the merged `{effective, sources}` config (sorted keys) as JSON — every
288
+ knob's resolved value plus which source won it (`"arg"` | `"seonix.toml"` |
289
+ `"default"`). `--repo` defaults to cwd. Requires `--effective`; without it,
290
+ prints usage and exits 2.
291
+
292
+ ### `viz` — render the code-map / code-browser / timeline
293
+
294
+ ```bash
295
+ seonix viz --focus <Symbol> --out graph.html
296
+ seonix viz --serve --port 8080 --focus <Symbol>
297
+ ```
298
+
299
+ Flags: `--focus <sym>` (default: highest-degree module), `--depth N` (default 2),
300
+ `--hub N` (hub-degree cap, default 40), `--max N` (node cap, default 200), `--out
301
+ f.html` (default `seon-graph.html`), `--data-out f.json` (split viewer + sidecar
302
+ data file instead of embedding), `--graph <path>` (override the graph.json
303
+ location), `--repo-url <url> --ref <branch>` (source links), `--site-nav`
304
+ (add the site's absolute home entry), `--nlp` (include the optional wink-nlp
305
+ lemma tier in the chat panel), `--force-inline` (embed the full graph inline even
306
+ past the size gate that would otherwise split it out with a message).
307
+
308
+ By default `viz` also writes a `code-browser.html` (Chronograph) and a
309
+ `timeline.html` next to `--out` with a working header nav; override their paths
310
+ with `--browser-out f.html` / `--browser-data-out f.json` / `--timeline-out
311
+ f.html`, or suppress both siblings with `--graph-only`. `--scope
312
+ product|<prefixes>` filters the Chronograph browser/timeline to a package
313
+ prefix set. `--limit N` caps the commit timeline to the newest N commits (0 =
314
+ unlimited; default 100 — an estate-scale timeline stays openable, with a "newest
315
+ N of M" header note when truncated).
316
+
317
+ `--serve [--port N]` serves the same viewer live against this repo's own index
318
+ (code browser at `/code-browser.html`, alias `/browser`; timeline at
319
+ `/timeline.html`; live re-annotates on HEAD change via `/code-browser-version`) —
320
+ port 0 (default) picks a free port.
321
+
322
+ ### `chat` — interactive client over the mechanical ask engine
323
+
324
+ ```bash
325
+ seonix chat [--repo /abs/path] [--with-claude|--with-copilot]
326
+ ```
327
+
328
+ See [Chat](#chat) below for the full behaviour (slash-commands, focus tracking,
329
+ conversational manners, session logging, the opt-in `--with-claude`/
330
+ `--with-copilot` LLM fallback).
331
+
332
+ ### `SEONIX_STORE` / `SEONIX_GRAPH_FORMAT`
333
+
334
+ Two opt-in/opt-out environment knobs, orthogonal to everything above:
335
+ `SEONIX_STORE=sqlite` turns on the resident `node:sqlite` store as a derived,
336
+ rebuildable companion to `graph.json` (rebuild it explicitly with `cli
337
+ store_rebuild`; unset, nothing sqlite loads). `SEONIX_GRAPH_FORMAT` controls the
338
+ graph's on-disk wire format — the interned v2 form is the shipped default;
339
+ set `SEONIX_GRAPH_FORMAT=1` to opt back out to the legacy v1 form.
340
+
341
+ ## Configuration (seonix.toml)
342
+
343
+ An optional `seonix.toml` at a repo (or estate) root steers indexing and
344
+ scoring. Absent file = shipped defaults, byte-for-byte today's behaviour.
345
+ `seonix init` (or `seonix init --dotnet` for an estate) seeds a commented
346
+ template — see `packages/seonix/templates/seonix.toml` and
347
+ `templates/seonix-dotnet.toml` for the full annotated reference.
348
+
349
+ - **`repositories`** — the repo set for an estate config: an inline array of
350
+ paths, or a string naming a newline-delimited file (both resolved relative
351
+ to the config file). Only consulted when no explicit `repo_path` /
352
+ `repo_paths` / `multi_root` arg is given.
353
+ - **`out_root`** — where the merged graph artifact is written (default `.`).
354
+ - **`[index]`**
355
+ - `languages` — the AST languages to parse (`javascript`, `typescript`, `python`, `csharp`, `java`, …).
356
+ - `exclude` — additive glob excludes on top of `.seonixignore` (ordered; a leading `!` re-includes).
357
+ - `secret_exclude` — a hard, fail-safe-ON glob list (env files, keys, certs) that is never indexed and never grabbed as text, even as a text/structure source.
358
+ - `history_depth` — cap on the git-history pass (0 = skip entirely). Precedence: CLI arg > this key > default.
359
+ - `include_text`, `include_structure`, `respect_gitignore`, `markdown_sections`, `vue` — recognized and normalized, but **not yet wired to a consumer** (see "unwired keys" below).
360
+ - **`[tune]`** — the locate/digest scoring knobs: `score_gap_k`, `literal_mention` (shipped default `true`), `demote_non_prod`, `call_adjacency`, `impl_of_interface`, `beam_search` (+ `beam_width`), `embed_rank`, `prose_layers`.
361
+ - **`[tune.expansion]`** — graph-traversal expansion mode used by locate/the viewer's navigation: `strategy` (`none` | `beam` | `spiral` | `ppr`; `none` is the byte-identical shipped default), `nodes` (breadth/token-budget cap), `q` (proportion of most-distinctive neighbours followed per step), `depth` (hop radius).
362
+ - **`[telemetry]`** — `enabled = true` opts in (see [Telemetry](#telemetry) below).
363
+
364
+ **Precedence, everywhere:** explicit CLI arg > `seonix.toml` value > shipped
365
+ default. `seonix cli … '{"config":false}'` (always set on the bench path) skips
366
+ reading `seonix.toml` entirely, so a subject repo's config file can never shape
367
+ a benchmark arm. A `seonix.toml` key that parses and normalizes but has no
368
+ wired consumer yet (`index.include_text`, `index.include_structure`,
369
+ `index.respect_gitignore`, `index.markdown_sections`, `index.vue`) triggers a
370
+ one-time stderr warning per process rather than silently doing nothing.
371
+ `seonix config --effective [--repo <abs>]` prints the fully merged config plus,
372
+ per key, which source won it.
373
+
374
+ ## Telemetry
375
+
376
+ Opt-in, off by default, and the OFF path is **byte-identical** to no telemetry
377
+ existing at all — no file, no stdout/stderr change. Turn it on with
378
+ `SEONIX_TELEMETRY=1` (env always wins both directions — `SEONIX_TELEMETRY=0`
379
+ force-disables even if the toml turns it on) or `[telemetry] enabled = true` in
380
+ `seonix.toml`.
381
+
382
+ When enabled, each `cli` invocation (digest / locate / the generic tool
383
+ fallback) appends fire-and-forget JSONL lines to
384
+ `<repo>/.seonix/seonix-<uuidv7>.log` — one line per query, schema-versioned
385
+ (`v: 1`) with an `id` (the invocation id, reusable across a host's own trace
386
+ via `SEONIX_INVOCATION_ID`), a monotonic `seq`, and per-surface fields
387
+ (`query`, `response`, `perf`, `cost`). A structural redactor drops any
388
+ `text`/`content`/`snippet` field at any depth and truncates long strings —
389
+ **telemetry never records file contents**, only ids/paths/scores/sizes/counts.
390
+ Writes are swallowed on error: telemetry can never throw, block a query, or
391
+ touch stdout (stdout is the MCP transport / the digest injected into the
392
+ no-MCP arm).
117
393
 
118
394
  ## Visualise
119
395