deterministic · offline · $0 to index

Stop your coding agent reading whole files.

seonix turns a repository into a typed code graph your agent can query, then hands it a bounded edit digest at the start of a session. Your agent edits from turn 1 instead of fishing through the repo. Built offline, $0.

2 cells clear the ≥50% input-token bar under the shipped default, both on exploration-bound complex tasks (59.6% and 53.5%)
R1b score-gap selection: shipped 2026-07-02, the one lever positive on every task tested across two paid probes
$0 to build the graph: no API keys, no cloud, no telemetry

Status, 2026-07-02. Under the shipped default, two tasks save more than half the input tokens (hono·response-time on Haiku 59.6%, eshoponweb·order-service-total on Sonnet 53.5%), with more in the 35 to 48% range. Tasks whose edit site is already obvious save little. Full grid, method, and negative results in BENCHMARK_017 ↗.

quickstart →
Star

Paired-arm benchmark: same model pin, N≥3 runs, mean input tokens over the tasks both arms solved, index cost counted separately, no drop in solve rate. Any win depends on the model and the codebase. A leaner agent, or a repo small enough to hold in context, has less to displace. Full method: BENCHMARKS ↗.

How it works

seonix reads your repository into a typed graph: the modules, classes, functions, methods and attributes a change touches, and how they connect. It follows the published seonix schema.

  1. 1 · Build the graph

    Point seonix at a repository and it derives that typed graph offline. Same commit → the same graph every time. Nothing leaves your machine.

  2. 2 · Find the change site

    From your task description, seonix ranks the parts of the graph the change touches, with no human-chosen target. Already know the file? Hand it in directly.

  3. 3 · Hand over the digest

    seonix renders a bounded edit digest (the signatures, call sites and insertion regions your agent needs) and hands it over at the start of the session. The agent edits from turn 1 instead of exploring; the render is deterministic and costs $0.

See it run

One real task, run twice on the same rig that produces every number here. It shows the mechanism on one cell. The grid below is the evidence.

=== proof-demo: otb vs seonix, one real task, one cheap model, n=3 (mean) ===
suite=eshoponweb  task=order-service-total  model=claude-haiku-4-5

$ node bench/run.mjs --suite eshoponweb --arms otb --models claude-haiku-4-5 --runs 3 --instance order-service-total
  mean turns=9.0  mean input_tokens=288,796  mean cost=$0.0660  errors=0/3
  run 1 actions (representative):
  · Agent()
  · TaskOutput()
  · Bash("find <repo> -type f -name "*.cs" | grep …")
  · Read(Interfaces/IOrderService.cs)
  · Read(Services/OrderService.cs)
  · Read(Specifications/OrderWithItemsByIdSpec.cs)
  · Read(Interfaces/IOrderService.cs)
  · Read(Services/OrderService.cs)
  · Edit(Interfaces/IOrderService.cs)
  · Edit(Services/OrderService.cs)

$ node bench/run.mjs --suite eshoponweb --arms seonix --models claude-haiku-4-5 --runs 3 --instance order-service-total
  mean turns=6.0  mean input_tokens=263,790  mean cost=$0.0659  errors=0/3
  run 1 actions (representative):
  · Edit(Interfaces/IOrderService.cs)
  · Read(Interfaces/IOrderService.cs)
  · Edit(Interfaces/IOrderService.cs)
  · Read(Services/OrderService.cs)
  · Edit(Services/OrderService.cs)

--- headline (mean of 3 runs per arm) ---
input tokens:  otb 288,796  →  seonix 263,790  (-8.7% fewer)
turns:         otb 9.0  →  seonix 6.0
billed cost:   otb $0.0660  →  seonix $0.0659

(one task, n=3 per arm, one cheap model — illustrative, not the full benchmark grid; solve-status is graded by the real pipeline, not this quick script — see the results table above for the measured, paired-arm numbers this site actually cites.)

Repeatable: npm run proof reruns both arms for real and regenerates this capture. Nothing here is staged or hand-edited.

Ask the graph a question, from a terminal, with no model in the loop:

$ node packages/seonix/bin/cli.mjs cli seonix_ask '{"query":"which modules import walk.mjs"}' | jq -r '.matches[].label'
packages/seonix/src/cs_treesitter.mjs
packages/seonix/src/extract.mjs
packages/seonix/src/java_treesitter.mjs
packages/seonix/src/jsts_tsc.mjs
packages/seonix/src/manifest.mjs
scripts/verify-counts.mjs

seonix_ask runs tmct's deterministic, no-LLM engine over the graph. It returns JSON, so it pipes straight into jq, a script, or another tool.

Or hold a conversation. A real seonix chat transcript against this repo's own index:

tmct> which modules import walk.mjs
packages/seonix/src/cs_treesitter.mjs and packages/seonix/src/extract.mjs and packages/seonix/src/java_treesitter.mjs and packages/seonix/src/jsts_tsc.mjs and packages/seonix/src/manifest.mjs and scripts/verify-counts.mjs.

tmct> where is buildContextBundle defined
function buildContextBundle() is defined in packages/seonix/src/server.mjs at lines 139-261.

tmct> when did walk.mjs change
packages/seonix/src/walk.mjs was last touched by commit 541d91db47f3 on 2026-07-04 ("feat(seonix): seonix.toml config loader […]"); 2 earlier commits recorded.

seonix chat runs tmct (@polycode-projects/the-mechanical-code-talker), a deterministic no-LLM chatbot, over seonix's graph. Every answer is grounded in the graph or an honest miss — no model in the loop.

Benchmark 015 — task × configuration

B015 tested adaptive digest sizing across five languages and three models; restated under the current contract it cleared the ≥50% bar in 0 of 15 cells. Trace analysis found the cause (the digest too often named the wrong modules) and a score-gap fix (R1b) now ships as the default. Under R1b, two cells clear the bar, both on exploration-bound complex tasks (hono·response-time on Haiku 59.6%, eshoponweb·order-service-total on Sonnet 53.5%), with more in the 35 to 48% range. Tasks whose edit site is already obvious change little. Full detail, including the negative query-time MCP and beam-search results, in BENCHMARK_017 ↗.

Mean total_input_tokens per solved run (thousands, incl. cache reads). otb = out-of-the-box agent; seo = seonix, the shipped no-MCP default. N=3 for cells re-run this cycle (B017/B018), N=5 for reused B015 baselines. Each seo cell shows % input tokens saved vs the same-row otb (negative = seonix used more), the mean token count beneath, and a numbered link to the sourcing benchmark doc. · = no scored seo cell (hover for which of three honest reasons); (2/3) = solved fewer than the full run count; = pre-R1b config, not yet re-tested; = solve-rate regression, not a pass. Full method and per-cell provenance in 17 BENCHMARK_017 ↗ and 18 BENCHMARK_018 ↗.
Task (S/C) Opus 4.8 Sonnet 4.6 Haiku 4.5
otbseo otbseo otbseo
Python — django-lh
py-django-slugify-separator (S) 307k6%289k16407k−20%487k161948k6%1822k16
py-django-initials-filter (C) 428k·616k9%561k162698k59%1106k16
py-django-truncatelines-filter (C) 610k·970k41%574k(2/3)162218k(3/5)48%1164k(2/3)17
TypeScript — hono (B017; replaces cdk-examples)
ts-hono-utils-helper (S) 135k·117k·120k·
ts-hono-response-time-middleware (C) 345k·647k14%555k17978k60%395k17
ts-hono-json-envelope (C) 1102k·940k8%864k171580k0%1577k17
C# — eShopOnWeb
cs-eshoponweb-basket-item-subtotal (S) 153k0%153k16149k−5%158k16153k−13%173k(4/5)16
cs-eshoponweb-basket-service-total (C) 353k·401k44%222k18197k(2/5)·(0/3)
cs-eshoponweb-order-service-total (C) 318k·332k53%154k17311k18%254k17
JavaScript — commander.js + express
js-commander-option-takes-value (S) 140k−9%152k16249k−97%490k16654k21%516k16
js-express-json-envelope (C) 174k·187k1%185k17564k46%302k17
js-express-request-id (C) 330k·231k−16%267k17873k25%659k(2/3)17
Java — gson
java-gson-jsonarray-get-first-last (S) 265k14%228k16378k−5%396k16974k2%949k16
java-gson-formatting-space-before-colon (C) 800k·863k20%688k161463k2%1427k16
java-gson-formatting-space-before-comma (C) 807k·668k3%647k161613k−7%1727k16

Values are mean input tokens per solved run (incl. cache reads). S = simple task, C = complex. Read the table by task shape, not as one mean. Tasks that need exploration (find a sibling pattern, a target module, a multi-file edit site) win big: response-time-middleware Haiku 60% and order-service-total Sonnet 53% both save more than half. Edits to a known large file barely move: json-envelope injects the right file first and still saves nothing, because the cost was editing an obvious file, not finding it. Cells marked are pre-R1b, not yet re-tested; Opus complex cells are · this cycle. Full write-ups: BENCHMARK_017 ↗ · BENCHMARK_018 ↗.

A cheaper model, matched

Sonnet with the digest solved our tasks at Opus's rate for 20–58% less billed cost.

On six of eight tasks, seonix on the cheaper Sonnet 4.6 used fewer input tokens than out-of-the-box Opus 4.8, at equal correctness. This crosses model tier and the digest together, so it illustrates practical value rather than the paired same-model measurement the grid uses. The full cross-model table is in BENCHMARKS ↗.

What predicts the saving: turns, not task type

Across all 13 measured seonix cells the token cut is 0.91-correlated (Pearson) with the turn cut: seonix saves tokens by letting the agent skip exploration turns, so where it removes no turns there is little to save. The per-cell turns-vs-tokens table is in BENCHMARKS ↗.

Quickstart with Claude Code

seonix is a Node package (ESM, Node ≥ 24) that shells out to Python 3 and git, both on PATH. Four steps take Claude Code from grepping the tree to editing from turn 1.

  1. Install the seonix CLI:

    npm i -g @polycode-projects/seonix
  2. Build the graph (deterministic, offline, $0):

    seonix cli index_repository '{"repo_path":"/abs/path/to/repo"}'

    This writes the typed code graph to .seonix/graph.json (plus a .seonix/TOOLS.md tool catalog). Same commit → byte-for-byte identical graph.

  3. Render a bounded edit-digest for the modules a task touches and save it where the agent can read it:

    # which modules does the change touch? (ranked, no LLM)
    seonix cli seonix_locate '{"repo_path":"/abs/path/to/repo","query":"<your task>"}'
    
    # render the digest for those modules → save it under .seonix/
    seonix cli digest '{"repo_path":"/abs/path/to/repo","modules":["src/pkg/module.py"]}' > /abs/path/to/repo/.seonix/digest.txt
  4. Put the digest in front of the agent at session start. Wire a Claude Code SessionStart hook that adds the rendered digest to the session context (or paste it at the top of a claude session):

    # .claude/settings.json — inject the digest once, at session start
    { "hooks": { "SessionStart": [ { "hooks": [
      { "type": "command", "command": "cat .seonix/digest.txt" }
    ] } ] } }

    The digest is in context from turn 1, so the agent edits without exploring first. No MCP server, no tool loop: the saving comes from the digest arriving up front rather than from an interactive round-trip.

Copy-paste setup

The same four steps as one block per tool, with the permission rules that let the agent run seonix without prompts.

Claude Code

# install + index (offline, $0)
npm i -g @polycode-projects/seonix
cd /path/to/your/repo
seonix cli index_repository "{\"repo_path\":\"$PWD\"}"

# locate the change site, render the digest
seonix cli seonix_locate "{\"repo_path\":\"$PWD\",\"query\":\"<your task>\"}"
seonix cli digest "{\"repo_path\":\"$PWD\",\"modules\":[\"src/pkg/module.py\"]}" \
  > .seonix/digest.txt

# .claude/settings.json — inject at session start; allow seonix
# to run un-prompted via the permissions framework
cat > .claude/settings.json <<'JSON'
{
  "permissions": {
    "allow": ["Bash(seonix:*)", "Read(.seonix/**)"]
  },
  "hooks": {
    "SessionStart": [
      { "hooks": [ { "type": "command",
          "command": "cat \"$CLAUDE_PROJECT_DIR/.seonix/digest.txt\" 2>/dev/null || true" } ] }
    ]
  }
}
JSON

The hook puts the digest in context from turn 1; the permissions.allow rule lets the agent re-run seonix (re-index, re-locate, re-render) without an approval prompt.

GitHub Copilot (VS Code agent mode)

# install + index + digest — same commands
npm i -g @polycode-projects/seonix
cd /path/to/your/repo
seonix cli index_repository "{\"repo_path\":\"$PWD\"}"
seonix cli digest "{\"repo_path\":\"$PWD\",\"modules\":[\"src/pkg/module.py\"]}" \
  > .seonix/digest.txt

# .github/copilot-instructions.md — Copilot reads this file
# automatically at the start of every chat/agent task
cat >> .github/copilot-instructions.md <<'MD'
Before exploring the codebase, read `.seonix/digest.txt` — it is a
pre-built edit digest (signatures, call sites, insertion regions) for
this repository. Re-render it with:
`seonix cli digest '{"repo_path":"<abs repo path>","modules":[...]}'`
MD

# VS Code settings.json — allow seonix in the terminal
# tool without a confirmation prompt
"chat.tools.terminal.autoApprove": { "seonix": true }

Copilot has no session-start hook, so injection is by reference: the instructions file (read automatically) points the agent at the digest, and the terminal auto-approve setting lets it run seonix itself without a prompt. Check the setting name against your VS Code version; the terminal allow-list has been renamed between releases.

Or paste one prompt into chat. It works for Claude Code, Copilot, or any tool-calling agent; the agent reads its own capabilities and skips whatever doesn't apply:

Install seonix globally (`npm i -g @polycode-projects/seonix`), then index
this repository by running `seonix cli index_repository` with
`repo_path` set to this repo's absolute path. Render its edit digest to
`.seonix/digest.txt` with `seonix cli digest`.

Then configure yourself to load that digest automatically, using
whichever of these applies to you — check your own docs/settings and
skip what doesn't exist:
- If you support a session-start hook (e.g. Claude Code's
  `.claude/settings.json` → `hooks.SessionStart`), add one that `cat`s
  `.seonix/digest.txt` into context (no-op if the file is missing), and
  add `"Bash(seonix:*)"` + `"Read(.seonix/**)"` to `permissions.allow`
  so you can re-index/re-locate/re-render yourself without a prompt.
- If you don't have a hook but read a standing instructions file (e.g.
  Copilot's `.github/copilot-instructions.md`), append a note there
  telling yourself to read `.seonix/digest.txt` before exploring the
  codebase, and enable running `seonix` in your terminal/tool-call
  allow-list without a confirmation prompt (e.g. VS Code's
  `chat.tools.terminal.autoApprove`).

Verify by starting a fresh session and confirming the digest text is
present in context or referenced correctly.

Prefer an interactive tool? seonix also runs as a local server. The digest path above is the recommended one, and the one the benchmarks measure. Everything runs against your local checkout with no network calls. The graph rebuilds from source, so keep it out of version control.

Configure & tune

No server to run

The recommended path hands the agent one pre-rendered digest and runs it with no server attached. That is the shape the benchmarks measure and the one that deploys cleanly. An interactive mode is there if you want it.

Automatic, or point it yourself

seonix picks the change site straight from your task description, with no human in the loop. Already know the file? Name it directly and skip the search.

Deterministic & cache-stable

The same commit always produces the same graph, so rebuilds are reproducible and the digest stays cache-stable; prompt caching keeps repeat turns cheap.

Cost & privacy

Building the graph and querying it cost $0 beyond CPU and disk: no model calls, no API keys, no cloud. seonix runs entirely on your machine and never phones home.

seonix, indexing itself

The graph below is a live seonix viz render of seonix's own code graph, the same typed-edge structure it builds for any repo you point it at.

Visualisation loads here. If it doesn't, open the graph directly ↗ or view the commit timeline.

Install seonix