npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@polycode-projects/seonix

v0.10.18

Published

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.

Readme

@polycode-projects/seonix

A deterministic, offline, $0 code-graph tool that makes a cheap coding agent edit like an expensive one. SEONIX indexes a repository with Python's ast and git log, with zero model calls, into a typed code graph, then renders a bounded edit digest you inject once at session start. Your agent edits from turn 1 without fishing through the repo.

Headline. Measured as mean input tokens per both-solved task (paired arms, identical model pin). Under the shipped score-gap default, two cells clear the ≥50% bar, both on exploration-bound complex tasks (a TypeScript middleware task on Haiku 4.5 and a C# service task on Sonnet 4.6), with several more in the 35 to 48% range. The token cut tracks the turn cut: seonix saves tokens by removing exploration turns, so tasks whose edit site is obvious change little. Indexing costs $0. The win is task- and model-conditional. Full method, numbers, and the measurement contract are in BENCHMARKS.md.

  • Website / live demo: https://seonix.polycode.co.uk
  • Source + benchmarks: https://gitlab.com/polycode-projects/seonix
  • Licence: AGPL-3.0-only

A cheaper model, matched

seonix on the cheaper Sonnet 4.6 used fewer input tokens than out-of-the-box Opus 4.8 on six of eight tasks, at equal correctness. The full cross-model table and the turns correlation (Pearson 0.91) are in BENCHMARKS.md.

Install

npm i -g @polycode-projects/seonix        # global — gives you the `seonix` bin on PATH
# or one-shot, no install:
npx @polycode-projects/seonix cli index_repository '{"repo_path":"/abs/path/to/repo"}'

Runtime prerequisites: Node ≥ 24, Python 3, and git on PATH (the indexer shells out to Python's ast and git log). A Java runtime is needed only to index Java through JavaParser, and the .NET SDK only to index C# through Roslyn; without them, indexing for that language falls back to the bundled tree-sitter parser. Indexing is fully local and deterministic: no API keys, no network calls, $0.

Quickstart

# 1. index your repo (deterministic, offline) — writes a machine-local .seonix/
cd /path/to/repo && seonix cli index_repository

# 2. self-locate the touched modules from the task text (no human-chosen target)
seonix cli seonix_search '{"query":"<your task>"}'

# 3. render the bounded edit digest for those modules
seonix cli digest '{"modules":["<top hits>"]}' > digest.txt

# 4. inject digest.txt once at session start, then run your coding agent
claude --prompt "Use the seonix digest below to localise and make the change. $(cat digest.txt)"

Add .seonix/ to your .gitignore; it is machine-local and rebuilt from source.

The JSON argument is optional on every cli command: an omitted repo_path resolves to the nearest ancestor of the current directory (current directory first) containing a .seonix/, so query commands work from any subdirectory of an indexed repo. index_repository falls back to the current directory when nothing is indexed yet; query commands print a one-line error instead. An explicit repo_path always wins.

Indexing multiple repositories

index_repository takes one of three argument forms:

# one repo (unchanged): artifacts to <repo_path>/.seonix/, module ids unprefixed
seonix cli index_repository '{"repo_path":"/abs/repo"}'

# n explicit repos, merged into ONE graph at <out_root>/.seonix/
seonix cli index_repository '{"repo_paths":["/abs/repo1","/abs/repo2"],"out_root":"/abs/dir"}'

# estate discovery: every immediate child directory of multi_root that contains a
# .git (dir or file) is indexed as a repo; artifacts to <multi_root>/.seonix/
seonix cli index_repository '{"multi_root":"/abs/estate"}'

The forms are mutually exclusive. out_root defaults to the deepest common ancestor directory of the paths (or the current directory if that ancestor is the filesystem root). In the merged graph every module id and label is prefixed with the repo's directory basename (mod:<repoName>/<relpath>); when two repos share a basename the first in path sort order keeps the bare name and later ones get -2, -3, … appended. Each repo's git history and .seonixignore are handled per repo, exactly as in single mode — a .seonixignore at the multi_root parent is NOT read (each sub-repo loads only its own). For an estate-wide exclude, put it in a seonix.toml at the directory you index from ([index].exclude / secret_exclude); that propagates to every sub-repo. Cross-repo call edges only arise where a symbol name resolves uniquely across the whole merged graph; ambiguous names are dropped, as within one repo. Query a merged graph by passing the merge root as repo_path (e.g. seonix cli seonix_search '{"repo_path":"/abs/estate","query":"…"}').

The shape that ships

The product is the no-MCP digest path: render the digest, inject it once, run with no server. The stdio MCP server still ships and is fully supported (bare seonix starts it) — it's just not the recommended default, because even mitigated the interactive tool loop never cleared the 50% bar: the cost lies in the round-trip count rather than the resident schema. Use seonix_search (locate, self-derives the target) in production; pass a known target (oracle) only to measure the headroom.

CLI reference

Every invocation shape bin/cli.mjs dispatches on, verified against the current source.

Bare invocation — MCP stdio server

seonix

No flags, no config file. Starts the stdio MCP server against <cwd>/.seonix/graph.json (override with SEONIX_GRAPH_FILE). MCP launchers should start it with cwd = the indexed worktree.

cli index_repository — build the graph

seonix cli index_repository                       # no arg: index the repo you are in
seonix cli index_repository '{"repo_path":"/abs/repo"}'
seonix cli index_repository '{"repo_paths":["/abs/repo1","/abs/repo2"],"out_root":"/abs/dir"}'
seonix cli index_repository '{"multi_root":"/abs/estate"}'

repo_path / repo_paths+out_root / multi_root are mutually exclusive — pass at most one. When none is given, seonix.toml's repositories supplies the set if present; otherwise the target defaults to the nearest ancestor of the current directory carrying a .seonix/ (re-index), else the current directory itself (first index). Shared JSON options:

  • ignores (bool, default true) — honour the repo's .seonixignore. Pass "ignores":false to index everything regardless (the bench harness always passes this, so a subject repo's own .seonixignore never shapes an arm's index).
  • config (bool, default true) — read seonix.toml from the target dir. Pass "config":false to skip it entirely (byte-identical to no-file behaviour; the bench harness's index/locate/digest calls always pass this).
  • sync (bool) — on a multi_root call, "sync":true routes through the same incremental path as cli sync instead of a full re-index.
  • history_depth (non-negative integer) — cap on the git-history pass (both the commit walk and the history-symbol pass). 0 skips git history entirely; absent uses the default depth. Precedence: this arg > seonix.toml [index].history_depth > default.

Writes <out_root-or-repo_path>/.seonix/graph.json (+ manifest for multi_root/sync), prints indexed counts to stderr and a human summary MD to stdout.

cli sync — incremental estate re-index

seonix cli sync '{"multi_root":"/abs/estate"}'

Fingerprints every child repo of multi_root and re-extracts only the ones that changed since the last run (per-repo cache under .seonix/cache/); one union rebuild is exact-equal to a full re-index, and an unchanged estate is a byte-stable no-op. Accepts ignores and history_depth as above. Writes .seonix/manifest.json.

cli digest — the no-MCP injection payload

seonix cli digest '{"repo_path":"/abs/repo","modules":["path/to/mod.mjs","other/mod.py"]}'
seonix cli digest '{"repo_path":"/abs/repo","query":"add a truncate filter to the article view"}'

Two ways to pick the modules: an explicit modules array, or a query string — query auto-locates and score-gap-selects the modules in one call (modules wins if both are given). Prints an architecture map + a size-adaptive context bundle per module to stdout, headed by a machine-readable line the rig parses: # seonix-digest tier=<NONE|TINY|MID|LARGE|FULL> topup=<bool> modules=<N> [selected=<comma-list>] (selected= only appears in query mode).

Tuning flags (all optional, precedence explicit arg > seonix.toml [tune] > shipped default):

  • top_k (int, query mode only, default 2) — how many modules query mode selects.
  • score_gap (number, or false to disable) — the score-gap cutoff for query-mode selection.
  • literal_mention (bool, default true) — credit verbatim dotted-name/path mentions in the raw query text.
  • min (bool) — force the leanest bundle (no auto top-up).
  • untuned (bool) — the pre-tuning escalation behaviour.
  • max (bool) — the injection ceiling: every requested module gets a FULL (untrimmed) bundle instead of primary-full + capped trimmed secondaries.

cli seonix_locate — ranked module list (recall probe)

seonix cli seonix_locate '{"query":"…","repo_path":"/abs/repo"}'

Prints <relpath>\t<score> per line, highest first — the raw ranking a caller (or the rig) can threshold itself. Recall/precision levers, all optional (precedence arg > seonix.toml [tune] > default, all default false unless noted):

  • demote_nonprod — demote examples//fixtures//test-* paths.
  • call_adjacency — resolved-call adjacency bonus.
  • impl_of_interface — C# impl-of-interface boost.
  • beam_search (+ beam_width) — multi-ply discriminative beam expansion.
  • literal_mention (default true) — verbatim dotted-name/path mention matching against the raw query.
  • raw_query — forward a separately-normalized raw problem text for literal matching (else derived from query).
  • 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).

cli browser_link — NL request → Chronograph URL

seonix cli browser_link '{"query":"classes that change with render","base":"https://example.com"}'

Maps a natural-language-ish request to the code-browser query grammar deterministically (no model call), self-tests the resulting link against the local temporal graph (must match ≥1 node and every cursor must resolve), then prints the URL — or exits 1 with the failure reason(s) rather than printing a dead link. Accepts repo_path (which repo's graph to test against), base (the URL prefix the link is built on), at / b (the two temporal cursors — single cursor / diff-cursor), scope (restrict the graph to a package-prefix set, same values as viz --scope), and grammar:true (take query as grammar verbatim, skipping NL mapping).

cli store_rebuild — force-rebuild the sqlite store

seonix cli store_rebuild '{"repo_path":"/abs/repo"}'

Force-(re)builds the opt-in node:sqlite resident store (see SEONIX_STORE below) from the current graph.json. The store is a derived, rebuildable companion — read paths never auto-build it, so this is the explicit way to (re)materialize it after an index or a manual graph.json edit.

cli <toolName> — generic MCP tool fallback

seonix cli seonix_ask '{"query":"which modules import codegraph"}'
seonix cli seonix_context '{"symbol":"buildContextBundle","repo_path":"/abs/repo"}'

Any sub-command name that isn't one of the above routes straight to the MCP tool dispatcher, so any tool in the server's surface — cold or hot — is invokable from Bash with no MCP connection. repo_path in the JSON arg selects the target graph; omitted, it defaults to the nearest ancestor of the current directory containing a .seonix/ (a clean one-line error when there is none). Every other key is passed through as the tool's own arguments.

hook-augment — PreToolUse Grep/Glob augmenter

seonix hook-augment   # reads a PreToolUse hook event JSON from stdin

Reads the hook event from stdin, extracts the Grep/Glob pattern, and — if the local graph has a hit — writes {hookSpecificOutput:{hookEventName, additionalContext}} to stdout so the agent sees seon typed-graph hits alongside its own search. Silent (exit 0, no output) on any failure — empty/malformed input, no graph, no hit — the never-block contract.

init [--dotnet] — seed a seonix.toml

seonix init             # single-repo template
seonix init --dotnet     # estate/.NET template (repositories + broader include_text/exclude)
seonix init --force      # overwrite an existing seonix.toml

Copies the shipped template to ./seonix.toml (byte-for-byte). Refuses to overwrite an existing file unless --force (exit 2).

config --effective [--repo <abs>]

seonix config --effective
seonix config --effective --repo /abs/repo

Prints the merged {effective, sources} config (sorted keys) as JSON — every knob's resolved value plus which source won it ("arg" | "seonix.toml" | "default"). --repo defaults to cwd. Requires --effective; without it, prints usage and exits 2.

viz — render the code-map / code-browser / timeline

seonix viz --focus <Symbol> --out graph.html
seonix viz --serve --port 8080 --focus <Symbol>

Flags: --focus <sym> (default: highest-degree module), --depth N (default 2), --hub N (hub-degree cap, default 40), --max N (node cap, default 200), --out f.html (default seon-graph.html), --data-out f.json (split viewer + sidecar data file instead of embedding), --graph <path> (override the graph.json location), --repo-url <url> --ref <branch> (source links), --site-nav (add the site's absolute home entry), --force-inline (embed the full graph inline even past the size gate that would otherwise split it out with a message).

By default viz also writes a code-browser.html (Chronograph) and a timeline.html next to --out with a working header nav; override their paths with --browser-out f.html / --browser-data-out f.json / --timeline-out f.html, or suppress both siblings with --graph-only. --scope product|<prefixes> filters the Chronograph browser/timeline to a package prefix set. --limit N caps the commit timeline to the newest N commits (0 = unlimited; default 100 — an estate-scale timeline stays openable, with a "newest N of M" header note when truncated).

--serve [--port N] serves the same viewer live against this repo's own index (code browser at /code-browser.html, alias /browser; timeline at /timeline.html; live re-annotates on HEAD change via /code-browser-version) — port 0 (default) picks a free port.

chat — interactive tmct chat over the graph

seonix chat [--repo /abs/path]

Opens tmct's natural-language client over seonix's graph (see Chat below). The conversation layer is tmct (@polycode-projects/the-mechanical-code-talker); seonix supplies the graph through tmct's provider seam.

SEONIX_STORE / SEONIX_GRAPH_FORMAT

Two opt-in/opt-out environment knobs, orthogonal to everything above: SEONIX_STORE=sqlite turns on the resident node:sqlite store as a derived, rebuildable companion to graph.json (rebuild it explicitly with cli store_rebuild; unset, nothing sqlite loads). The variable must be exported (export SEONIX_STORE=sqlite) — a plain shell assignment is invisible to the seonix child process, and the JSON path runs instead. The index summary's store: line states which mode actually ran (store: sqlite (graph.db + graph.json) only when graph.db was built). SEONIX_GRAPH_FORMAT controls the graph's on-disk wire format — the interned v2 form is the shipped default; set SEONIX_GRAPH_FORMAT=1 to opt back out to the legacy v1 form.

Configuration (seonix.toml)

An optional seonix.toml at a repo (or estate) root steers indexing and scoring. Absent file = shipped defaults, byte-for-byte today's behaviour. seonix init (or seonix init --dotnet for an estate) seeds a commented template — see templates/seonix.toml and templates/seonix-dotnet.toml for the full annotated reference.

  • repositories — the repo set for an estate config: an inline array of paths, or a string naming a newline-delimited file (both resolved relative to the config file). Only consulted when no explicit repo_path / repo_paths / multi_root arg is given.
  • out_root — where the merged graph artifact is written (default .).
  • [index]
    • languages — the AST languages to parse (javascript, typescript, python, csharp, java, …).
    • exclude — additive glob excludes on top of .seonixignore (ordered; a leading ! re-includes).
    • 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.
    • history_depth — cap on the git-history pass (0 = skip entirely). Precedence: CLI arg > this key > default.
    • include_text, include_structure, respect_gitignore, markdown_sections, vue — recognized and normalized, but not yet wired to a consumer (see "unwired keys" below).
  • [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.
  • [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).
  • [interfaces] — HTTP route↔handler edges the AST graph can't see on its own: a Route node per endpoint, linked to its handler by a serves edge, and (JS/TS + tree-sitter C#) an HTTP client call linked to the route it targets by a callsHttp edge. Off by default; turn it on with enabled = true or SEONIX_INTERFACES=1 (env wins). Tunable: frameworks (default ["aspnet", "express"]), verbs, combine_controller_prefix, prefix_strip, match_threshold.
  • [telemetry]enabled = true opts in (see Telemetry below).

Precedence, everywhere: explicit CLI arg > seonix.toml value > shipped default. seonix cli … '{"config":false}' (always set on the bench path) skips reading seonix.toml entirely, so a subject repo's config file can never shape a benchmark arm. A seonix.toml key that parses and normalizes but has no wired consumer yet (index.include_text, index.include_structure, index.respect_gitignore, index.markdown_sections, index.vue) triggers a one-time stderr warning per process rather than silently doing nothing. seonix config --effective [--repo <abs>] prints the fully merged config plus, per key, which source won it.

Telemetry

Opt-in, off by default, and the OFF path is byte-identical to no telemetry existing at all — no file, no stdout/stderr change. Turn it on with SEONIX_TELEMETRY=1 (env always wins both directions — SEONIX_TELEMETRY=0 force-disables even if the toml turns it on) or [telemetry] enabled = true in seonix.toml.

When enabled, each cli invocation (digest / locate / the generic tool fallback) appends fire-and-forget JSONL lines to <repo>/.seonix/seonix-<uuidv7>.log — one line per query, schema-versioned (v: 1) with an id (the invocation id, reusable across a host's own trace via SEONIX_INVOCATION_ID), a monotonic seq, and per-surface fields (query, response, perf, cost). A structural redactor drops any text/content/snippet field at any depth and truncates long strings — telemetry never records file contents, only ids/paths/scores/sizes/counts. Writes are swallowed on error: telemetry can never throw, block a query, or touch stdout (stdout is the MCP transport / the digest injected into the no-MCP arm).

Visualise

seonix viz --focus <Symbol> --out graph.html

Renders a focused, portable Cytoscape sub-graph of the typed code-map, the same view that runs live on the website. By default it also writes a code-browser.html and a timeline.html beside it with a working header nav; pass --graph-only for just the single self-contained graph file.

Chat

seonix chat [--repo /abs/path]

seonix chat opens an interactive natural-language client over the code graph. The conversation engine is tmct (@polycode-projects/the-mechanical-code-talker), a deterministic, no-LLM chatbot that seonix runs over its own graph through tmct's provider seam. Every answer is grounded in the graph or an honest miss. With no --repo the target defaults to the git root, so running it from a nested package dir still covers the whole repo.

Ask structural questions in plain English — "which modules import walk.mjs", "where is buildContextBundle defined", "when did walk.mjs change" — and route slash-commands (/context, /impact, /callers, /arch, …) to the graph tools. On top of the query layer, tmct adds conversational memory, provenance and trust scoring, teach-and-recall, and multi-strategy interpretation. See the tmct docs for the full command set.

seonix supplies the graph; tmct owns the conversation. The old opt-in --with-claude/--with-copilot fallback is gone — tmct is no-LLM by contract.

Languages

Python (stdlib ast), JS/TS (the TypeScript compiler API, including CommonJS require/module.exports), C# (Roslyn, with a tree-sitter fallback) and Java (JavaParser, with a tree-sitter fallback), all merged into the one typed graph. The C# Roslyn helper's source ships in the package under roslyn/. If the .NET SDK is on your PATH, seonix builds it the first time it indexes a C# repo, one build, cached after that. No dotnet on PATH just means it uses the tree-sitter fallback, silently, no error. Run npm run build:roslyn yourself first if you want the dll ready ahead of time, e.g. in a Docker image build. The Java JavaParser helper is a jar built from the repository's java/ directory (mvn package) and is not included in the npm package, so a package install indexes Java with the tree-sitter fallback.

Licence & safety

AGPL-3.0-only. The network clause is intended for a hosted MCP tool, so that hosted derivatives stay open. The seonix: ontology vocabulary is CC-BY-4.0 (NOTICE credits SEON + FAMIX). SEONIX never writes to your global agent config and never phones home; everything it writes lives under .seonix/.