@polycode-projects/seonix 0.7.3 → 0.8.0

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
@@ -284,9 +284,8 @@ Flags: `--focus <sym>` (default: highest-degree module), `--depth N` (default 2)
284
284
  f.html` (default `seon-graph.html`), `--data-out f.json` (split viewer + sidecar
285
285
  data file instead of embedding), `--graph <path>` (override the graph.json
286
286
  location), `--repo-url <url> --ref <branch>` (source links), `--site-nav`
287
- (add the site's absolute home entry), `--nlp` (include the optional wink-nlp
288
- lemma tier in the chat panel), `--force-inline` (embed the full graph inline even
289
- past the size gate that would otherwise split it out with a message).
287
+ (add the site's absolute home entry), `--force-inline` (embed the full graph inline
288
+ even past the size gate that would otherwise split it out with a message).
290
289
 
291
290
  By default `viz` also writes a `code-browser.html` (Chronograph) and a
292
291
  `timeline.html` next to `--out` with a working header nav; override their paths
@@ -302,15 +301,15 @@ N of M" header note when truncated).
302
301
  `/timeline.html`; live re-annotates on HEAD change via `/code-browser-version`) —
303
302
  port 0 (default) picks a free port.
304
303
 
305
- ### `chat` — interactive client over the mechanical ask engine
304
+ ### `chat` — interactive tmct chat over the graph
306
305
 
307
306
  ```bash
308
- seonix chat [--repo /abs/path] [--with-claude|--with-copilot]
307
+ seonix chat [--repo /abs/path]
309
308
  ```
310
309
 
311
- See [Chat](#chat) below for the full behaviour (slash-commands, focus tracking,
312
- conversational manners, session logging, the opt-in `--with-claude`/
313
- `--with-copilot` LLM fallback).
310
+ Opens tmct's natural-language client over seonix's graph (see [Chat](#chat) below).
311
+ The conversation layer is tmct (`@polycode-projects/the-mechanical-code-talker`);
312
+ seonix supplies the graph through tmct's provider seam.
314
313
 
315
314
  ### `SEONIX_STORE` / `SEONIX_GRAPH_FORMAT`
316
315
 
@@ -388,114 +387,25 @@ for just the single self-contained graph file.
388
387
  ## Chat
389
388
 
390
389
  ```bash
391
- seonix chat [--repo /abs/path] [--with-claude | --with-copilot]
390
+ seonix chat [--repo /abs/path]
392
391
  ```
393
392
 
394
- An interactive client over the typed code-graph (deterministic, no model calls).
395
- With no `--repo` the target defaults to the **git root** (`git rev-parse
396
- --show-toplevel`), so running it from a nested package dir still indexes the whole
397
- repo; pass `--repo <path>` to override. Any **bare line** is a plain-English
398
- question dispatched through the mechanical `seonix_ask` engine e.g. `which
399
- modules import walk.mjs` — and the graph answers. Aggregate questions are answered
400
- straight off the graph header: `how many classes are there` → `20 classes.`
401
- (`how many <classes|functions|modules|methods|commits>`, `count the …`, `number of
402
- …`). A near-miss structural question still gets the precise rephrase hint.
403
-
404
- **Conversational manners** (deterministic, zero-model, recognised *before* any graph
405
- dispatch). A small closed set of everyday expressions gets a templated reply rather
406
- than a grammar hint:
407
-
408
- | you say | you get |
409
- | --- | --- |
410
- | `hi` / `hello` / `hey` / `yo` / `g'day` / `good morning` (and `hello there`, with a small nod) | a short greeting + a nudge to ask or `/help` |
411
- | `thanks` / `ta` / `cheers` / `ty` | an "any time" acknowledgement |
412
- | `help` / `what can you do` / `who are you` / `?` | the orientation (the friendly help text; see also `/help`, `/stats`) |
413
- | `bye` / `goodbye` / `quit` / `see ya` | a farewell **and a clean end of session** (flushes the log, closes — the `/exit` path) |
414
- | `why` / `how` / `explain` / `say more` / `go on` / `elaborate` | **re-renders the last answer verbosely** — repeats it with the ask engine's traversal receipt and the full matched-entity list the terse render trims (no previous answer → it says so plainly) |
415
-
416
- These turns resolve no code entity, so — like `/help` — they are recorded as plain
417
- turns and never add `mgx:asksAbout` edges to the session graph.
418
-
419
- **Slash-commands** reach every other query tool in one line each: `/find <query>`
420
- (or `/search`) for a lexical search; `/context <symbol>` for the sized edit bundle
421
- (start here to change code) and `/snippet <symbol>` for exact source; `/describe`,
422
- `/signature`, `/members <class>`, `/subclasses <class>`, `/impact <module>`,
423
- `/callers`, `/callees`, `/tests <symbol>`, `/untested`, `/history <symbol>`,
424
- `/exports <module>` and `/arch [package]`, plus `/stats` for a one-screen overview
425
- (entity counts, relationship counts, package totals). An unknown `/command` just
426
- nudges you to `/help`; a bad symbol prints the tool's own clean error, never a stack.
427
-
428
- The client tracks a **focus** entity for multi-turn context. A command or an answer
429
- that resolves a primary entity remembers it (the prompt shows it, `seon(walk.mjs)>`);
430
- a bare `it`/`this`/`that` in a question then binds to it, and a no-argument entity
431
- command (`/context`, `/impact`, …) reuses it — so `what calls it` works straight
432
- after `/describe walk.mjs`. Set it explicitly with `/focus <symbol>`. With no focus
433
- set the client stays single-shot. `/help` lists the commands and the question
434
- shapes; `/exit` (or Ctrl+C/Ctrl+D) leaves.
435
-
436
- Each session is appended per turn to `<repo>/.seonix/session-<uuidv7>.log`, and
437
- becomes first-class graph data like a commit — **slash-command turns included**: a
438
- structured sidecar (`.seonix/sessions/*.jsonl`) records each turn's query, the
439
- command used, and the entities it resolved/answered; the session enters `graph.json`
440
- as a `Session` individual with `mgx:asksAbout` edges wherever a turn resolved an
441
- entity (re-attached on every re-index), and it appears on the `seonix viz` timeline.
442
-
443
- ### Opt-in LLM fallback (`--with-claude` / `--with-copilot`)
444
-
445
- **Default OFF. Chat-only. Never in the benchmark, digest, or MCP path.** With a flag
446
- set, and *only* when the mechanical engine returns a genuine **miss** on a bare
447
- question (after the conversational layer and slash-commands have had their turn),
448
- chat consults an external LLM CLI — spawned in the target repo as cwd, with a
449
- timeout. To keep answers **graph-grounded** it asks the model to *compile one seonix
450
- command* (given the tool surface), then runs that command mechanically and shows the
451
- grounded result, **labelled `[LLM-assisted via …]`**. If the model can't produce a
452
- valid command, its raw text is shown instead, clearly marked `[answer from external
453
- model … — NOT the deterministic seonix engine]`; the two are never silently blended.
454
-
455
- - `--with-claude` → `claude -p "<compile-prompt>" --model haiku` (cheapest model).
456
- - `--with-copilot` → `gh copilot -- -p "<compile-prompt>" --allow-all-tools --no-color`.
457
- **Limitation, honestly:** the GitHub Copilot CLI is an *agentic shell assistant*
458
- (its native verbs are running commands and editing files), not a graph Q&A tool —
459
- it has no knowledge of the seonix query surface beyond what the prompt hands it, so
460
- it's a poorer fit than `claude -p` and is wired best-effort. Prefer `--with-claude`.
461
-
462
- Any failure — tool binary absent, non-zero exit, unparseable output, or timeout —
463
- degrades to the normal honest-miss hint; the flag never crashes or hangs the chat. If
464
- the flag is set but the binary is missing, chat says so **once at startup** and
465
- continues mechanical-only. No LLM call is ever made unless the flag is set **and** the
466
- mechanical engine missed — the hot path (and every benchmark run) stays zero-model.
467
-
468
- A real transcript against this repository's own index (verified answers):
469
-
470
- ```text
471
- seon> where is buildContextBundle defined
472
- function buildContextBundle() is defined in packages/seonix/src/server.mjs at lines 138-260.
473
-
474
- seon> what uses codegraph.mjs
475
- bench/arms.mjs and bench/run.mjs and packages/seonix/src/ask.mjs and
476
- packages/seonix/src/chat.mjs and packages/seonix/src/extract.mjs and
477
- packages/seonix/src/server.mjs and packages/seonix/src/viz.mjs and scripts/rank-gate.mjs.
478
-
479
- seon> when did walk.mjs change
480
- packages/seonix/src/walk.mjs was last touched by commit a73ac1ba8bfb on 2026-07-01
481
- ("feat(seonix): .seonixignore excludes […]"); 1 earlier commit recorded.
482
-
483
- seon> which changes touch commit 85073031
484
- commit 85073031ff05 touched modules packages/seonix/src/viz.mjs and
485
- packages/seonix/test/viz.test.mjs; functions buildViewerData() and cytoscapeSource().
486
-
487
- seon> which functions call bulidContextBundle
488
- assuming you meant buildContextBundle: in packages/seonix/src/server.mjs
489
- there is function dispatchTool().
490
- ```
491
-
492
- Question shapes: `which <functions|classes|modules> <import|call|use|test|touch> <name>`,
493
- `what does <module> <import|export>`, `what uses <name>`, `where is <name> defined`,
494
- `where is <name> mentioned`, `when did <name> change`, `which changes touch commit <sha>`,
495
- `what does <term> mean`. Misspellings are corrected from a curated table, close
496
- near-misses resolve through a bounded edit-distance pass and are announced
497
- ("assuming you meant"), and anything the graph cannot answer is a stated miss with
498
- a rephrase hint, never a guess.
393
+ `seonix chat` opens an interactive natural-language client over the code graph. The
394
+ conversation engine is **tmct** (`@polycode-projects/the-mechanical-code-talker`), a
395
+ deterministic, no-LLM chatbot that seonix runs over its own graph through tmct's
396
+ provider seam. Every answer is grounded in the graph or an honest miss. With no
397
+ `--repo` the target defaults to the git root, so running it from a nested package dir
398
+ still covers the whole repo.
399
+
400
+ Ask structural questions in plain English "which modules import walk.mjs", "where
401
+ is buildContextBundle defined", "when did walk.mjs change" and route slash-commands
402
+ (`/context`, `/impact`, `/callers`, `/arch`, …) to the graph tools. On top of the
403
+ query layer, tmct adds conversational memory, provenance and trust scoring,
404
+ teach-and-recall, and multi-strategy interpretation. See the tmct docs for the full
405
+ command set.
406
+
407
+ seonix supplies the graph; tmct owns the conversation. The old opt-in
408
+ `--with-claude`/`--with-copilot` fallback is gone — tmct is no-LLM by contract.
499
409
 
500
410
  ## Languages
501
411
 
package/bin/cli.mjs CHANGED
@@ -33,7 +33,7 @@
33
33
  // seonix viz [--focus <sym>] [--depth N] [--out f.html] [--data-out f.json] [--repo-url <gitlab url> --ref main] [--site-nav] → render a focused sub-graph to HTML (one shared viewer; data embedded, or split out with --data-out). By default also writes code-browser.html + timeline.html next to --out with a working header nav; --graph-only suppresses the siblings
34
34
  // seonix viz --browser-out f.html [--browser-data-out f.json] [--timeline-out f.html] [--scope product|<prefixes>] → override the sibling artifact paths (Chronograph code browser: temporal scrub + two-cursor diff + narration + ghost-branch merges + keyboard nav; archive/PLAN_CODE_BROWSER.md)
35
35
  // seonix viz --serve [--port N] [--focus <sym>] → serve the same viewer against THIS repo's index (the local equivalent of the site's #viz; code browser at /code-browser.html, live-reannotating on HEAD change via /code-browser-version)
36
- // seonix chat [--repo <abs>] [--with-claude|--with-copilot] → interactive prompt over the mechanical seonix_ask engine; /exit to leave; session log → <repo>/.seonix/session-<uuidv7>.log. The optional --with-* flags add an opt-in LLM fallback used ONLY when the mechanical engine misses a bare question (chat-only, never in the benchmark path)
36
+ // seonix chat [--repo <abs>] → interactive tmct chat over the graph (@polycode-projects/the-mechanical-code-talker, a deterministic no-LLM chatbot run through tmct's provider seam); /exit to leave
37
37
  //
38
38
  // The index step is offline and model-free (Python ast + git). It writes the
39
39
  // graph artifact to <repo_path>/.seonix/graph.json; the server (started with
@@ -643,15 +643,12 @@ async function main() {
643
643
  }
644
644
 
645
645
  if (mode === "chat") {
646
- const { runChat } = await import("../src/chat.mjs");
646
+ // The chat surface is tmct's now (PLAN_CHAT_EXTRACTION.md — the inversion): run tmct's chat
647
+ // over seonix's native graph via tmct's provider seam. The old --with-claude/--with-copilot
648
+ // LLM fallback does not port (tmct is no-LLM by contract).
649
+ const { runSeonixChat } = await import("../src/chat-shim.mjs");
647
650
  const i = process.argv.indexOf("--repo");
648
- // Opt-in LLM fallback (chat-only, default OFF, miss-gated never in the benchmark
649
- // path): --with-claude shells `claude -p`, --with-copilot shells `gh copilot -- -p`.
650
- await runChat({
651
- repoPath: i !== -1 ? process.argv[i + 1] : undefined,
652
- withClaude: process.argv.includes("--with-claude"),
653
- withCopilot: process.argv.includes("--with-copilot"),
654
- });
651
+ await runSeonixChat({ repoPath: i !== -1 ? process.argv[i + 1] : undefined });
655
652
  return;
656
653
  }
657
654
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polycode-projects/seonix",
3
- "version": "0.7.3",
3
+ "version": "0.8.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "SEONIX — a deterministic, offline, $0 code-graph tool that makes a cheap coding agent edit like an expensive one. Indexes a repo with Python ast + git (zero model calls) and renders a bounded edit-digest.",
@@ -44,14 +44,13 @@
44
44
  },
45
45
  "dependencies": {
46
46
  "@modelcontextprotocol/sdk": "^1.29.0",
47
+ "@polycode-projects/the-mechanical-code-talker": "^0.6.0",
47
48
  "cytoscape": "^3.30.0",
48
49
  "smol-toml": "^1.7.0",
49
50
  "tree-sitter": "^0.21.1",
50
51
  "tree-sitter-c-sharp": "^0.21.3",
51
52
  "tree-sitter-java": "^0.21.0",
52
- "typescript": "~5.6.2",
53
- "wink-eng-lite-web-model": "^1.8.1",
54
- "wink-nlp": "^2.4.0"
53
+ "typescript": "~5.6.2"
55
54
  },
56
55
  "scripts": {
57
56
  "test": "node --test \"test/**/*.test.mjs\"",
@@ -0,0 +1,43 @@
1
+ // ask-browser-entry.mjs — the esbuild entry for the website "Ask the graph" panel.
2
+ //
3
+ // PLAN_CHAT_EXTRACTION.md, the inversion: the panel's mechanical NL engine is now tmct's
4
+ // (@polycode-projects/the-mechanical-code-talker), bundled for the browser. It runs adapter-less
5
+ // (no wink model in the browser — tmct's ask degrades to its curated + fuzzy tiers), so the ~4 MB
6
+ // wink bundle is dropped from the site.
7
+ //
8
+ // This entry is bundled by scripts/build-ask-bundle.mjs into src/ask-browser.bundle.js (an IIFE),
9
+ // which viz.mjs inlines into the viewer page. It exposes two globals the panel's inline JS uses:
10
+ // parseEntities(payload) → the graph object tmct's ask consumes
11
+ // ask(graph, query, opts) → { content, seonix_ask } (tmct's tmct_ask, renamed for the panel)
12
+ import { ask as tmctAsk } from "@polycode-projects/the-mechanical-code-talker/ask";
13
+
14
+ // A self-contained parseEntities, byte-identical in output to seonix's and tmct's (the seonix-ask
15
+ // data file is already expanded by source.fetchEntities, so there is no v2 interning to undo here).
16
+ // Kept inline so the browser bundle pulls in nothing from seonix's Node-side codegraph stack.
17
+ function parseEntities(payload) {
18
+ const individuals = Array.isArray(payload?.individuals) ? payload.individuals : [];
19
+ const byId = new Map();
20
+ for (const ind of individuals) if (ind && ind.id) byId.set(ind.id, ind);
21
+ const relations = (Array.isArray(payload?.objectProperties) ? payload.objectProperties : []).map((g) => {
22
+ const edges = (Array.isArray(g?.examples) ? g.examples : []).filter((e) => e && e.subject && e.object);
23
+ return { predicate: String(g?.predicate || ""), prop: g?.prop ?? null, count: Number(g?.count) || edges.length, edges };
24
+ });
25
+ return {
26
+ individuals,
27
+ byId,
28
+ relations,
29
+ truncated: Boolean(payload?.truncated),
30
+ generatedAt: payload?.generated_at || "",
31
+ proseIndex: payload?.proseIndex || {},
32
+ };
33
+ }
34
+
35
+ // Panel-compatible wrapper: tmct's ask returns { content, tmct_ask }; the panel's inline JS reads
36
+ // `seonix_ask.matches` / `.miss`, so rename the envelope. The match/miss shape is shared.
37
+ function ask(graph, query, opts) {
38
+ const r = tmctAsk(graph, query, opts);
39
+ return { content: r.content, seonix_ask: r.tmct_ask };
40
+ }
41
+
42
+ globalThis.parseEntities = parseEntities;
43
+ globalThis.ask = ask;