CLI reference
ctx-optimize gathers your repo — code, docs, manifests, git history, and optionally your database, bucket, queue or API — into a deterministic graph on disk, then answers questions about it with a resolved symbol, its signature and its file:line. One static Go binary: no database, no server, no LLM, no embeddings, no MCP.
It is configured in exactly three places — the committed repo config (.ctxoptimize/config.json), a machine-global config (~/ctxoptimize/config.json), and one environment variable. Everything else is a command. Use the sidebar; everything is on these five pages.
npm, Go, or a plain binary.
Section titled “npm, Go, or a plain binary.”# npm — thin launcher + prebuilt platform binary (no postinstall, no download step)npm install -g @muthuishere/ctx-optimize
# Gogo install github.com/muthuishere/ctx-optimize/cmd/ctx-optimize@latest
# plain binaries (macOS / Linux / Windows) — GitHub Releases# https://github.com/muthuishere/ctx-optimize/releases
# then give your agents the skill + prompt hook (Claude Code, Codex, Copilot, Devin)ctx-optimize install --skills
# later — one command updates the binary AND every installed surfacectx-optimize update # npm installs update via npm; standalone binaries from # GitHub Releases, sha256-verified against checksums.txt, # swapped atomically — then skills + hooks refreshctx-optimize update --check # report only, touch nothinginstall detects which agent CLIs are on the machine and reports per platform. Narrow with --claude --codex --copilot --devin, or with --skills / --hooks to install only one mechanism. Remove everything with plain uninstall — no flags needed; stores and committed repo pointers stay. update’s network call happens only when you run it — the binary never checks for updates in the background.
One verb. Bare repo, monorepo, fresh clone, CI — up decides.
Section titled “One verb. Bare repo, monorepo, fresh clone, CI — up decides.”# the whole getting-started story — run it whenever, it's idempotentctx-optimize up # no config → bootstraps it (monorepos via scan; curate # .ctxoptimize/config.json after) and gathers # config declares a remote.pull + no local store → pulls the # team's prebuilt graph (falls back to gathering, loudly) # no remote → gathers · stale vs git HEAD → fast re-gather # fresh → no-op
# author-side, when you want control instead of up's defaultsctx-optimize init # scaffolds .ctxoptimize/ (commit it): config, adapter + transport # samples, remote.example.md, agent pointer blockctx-optimize scan # monorepo, READ-ONLY: lists every project + the config it would writectx-optimize init --scan --yes # writes the FULL module list to config.json — yours to edit afterctx-optimize add . # gather explicitly (parallel fan-out per module + a navigator)
# then askctx-optimize query "where is auth"ctx-optimize card HandleCheckout # signature + doc + callers + callees, no file readctx-optimize change-plan PaymentService # about to EDIT: callers + blast radius + which tests to run, one callctx-optimize affected PaymentService --depth 2How your agent learns to use the store — a skill, not an MCP server.
Section titled “How your agent learns to use the store — a skill, not an MCP server.”There is no MCP server, by choice. install writes an agent skill (plus an optional prompt hook and a committed pointer block) that teaches the agent when and how to reach for the store — the query craft, the verify discipline, the verb-by-intent ladder — instead of handing it a flat tool list.
ctx-optimize install --skills # one-time per machine: detect every agent CLI, write the skillctx-optimize install # skills + prompt hooks for every platform detectedctx-optimize install --claude --codex # narrow to specific platformsctx-optimize uninstall # remove everything install wrote (stores + repo config stay)| what lands | where, and what reads it |
|---|---|
| the skill | ~/.claude/skills/ (Claude Code, Copilot) and/or ~/.agents/skills/ (the cross-CLI SKILL.md standard). Controlled by the skills setting. |
| the prompt hook | ~/.claude/settings.json (Claude) and the AGENTS-family hook files (~/.codex/hooks.json, copilot). Controlled by the hooks setting. Devin needs no hook file — it reads the Claude hook and AGENTS.md natively. |
| the repo pointer block | Written by init into CLAUDE.md / AGENTS.md, marker-fenced so your surrounding text is never touched and a re-init never duplicates it. Controlled by the instructions setting. |
.ctxoptimize/instructions.md |
The committed usage card — the deep doc the pointer block references. Version-stamped managed block, refreshed upgrade-only by init/up; anything your team writes outside the markers is left alone. |
The pointer block is gated: it opens by telling the agent to check that ctx-optimize is actually installed, and to read the code normally if it isn’t. A teammate who never installs the binary is never worse off. All four surfaces refresh together on ctx-optimize update.
Not “search the codebase” — say what you’re about to do.
Section titled “Not “search the codebase” — say what you’re about to do.”Every verb answers a different question, and picking by intent is most of the value. This is the table to read first; the full flag-by-flag reference is below.
| intent | verb | why this one |
|---|---|---|
| Find — you have words, want locations | query "<2-4 terms>" |
ranked, cited hits with signatures, complete under a token budget |
| Inspect a known symbol | card X |
signature + doc + callers + callees, without opening the file |
| About to EDIT | change-plan X |
ONE call: signature + callers + blast radius + which tests to run + co-change history |
| Blast radius only | affected X |
reverse impact — what breaks if X changes |
| Connection between two things | path "A" "B" |
shortest chain of real edges |
| Orient in a new repo | hubs --top 10 · report |
most-connected nodes; report adds subsystems, seams, and what could NOT be resolved |
| Explain a node | explain X |
plain-language node + neighborhood |
| Check a citation before acting | verify "file.go:L10-L20" |
exit 0 only when every claim holds |
| List / filter without jq | nodes · edges · deps |
native table or JSON output over the graph |
| Can I trust the store right now | fresh · status |
freshness against git HEAD, scriptable exit codes |
Settling an abstention for good — .ctxoptimize/resolutions.json
Section titled “Settling an abstention for good — .ctxoptimize/resolutions.json”--include-ambiguous lets you look at a shortlist. This lets the repo settle one, so the same question isn’t re-derived by every agent forever:
{ "external_methods": ["Error", "String", "Close"] }Bare method names whose receivers are never types you own. Listing the name retires that shortlist — on this repo one declared line retired 98 of them. It is deliberately the one key that cannot make the graph wrong: it is consulted only on the abstention path, never deletes a resolved edge (MyErr.Error(), which names its own receiver, still resolves), and applies only to receiver-qualified calls. Malformed is a hard error, never a warning — bad JSON, an unknown key, a qualified name, an empty entry — because a silently ignored declaration is the worst outcome. A declared name matching no call site is reported on every gather, so the file cannot rot in silence. init scaffolds an inert resolutions.json.sample.
The whole verb set.
Section titled “The whole verb set.”Every verb below: when to run it, what to type, what happens. Global flags: --path DIR (default cwd), --store DIR (default $CTX_OPTIMIZE_STORE or ~/ctxoptimize). Most read verbs take --json.
When. First time in a repo, or you don’t want to think. Fresh clone, CI, monorepo, already gathered — one verb.
Do. ctx-optimize up
Does. No config → writes .ctxoptimize/ (monorepos via scan) and gathers. Empty store + remote.pull → pull (failed pull gathers locally). No remote → gather. Stale vs HEAD → fast re-gather, adapters skipped. Fresh → no-op. Idempotent.
When. You want to edit the config before the first gather, or write the pointer into AGENTS.md / CLAUDE.md.
Do. ctx-optimize init · init --scan [--yes] [--depth N] [--modules "globs"]
Does. Scaffolds .ctxoptimize/ (config, adapter sample, push.js.sample / pull.js.sample). Does not clobber an existing config. --scan writes modules[]. A pull-declaring clone redirects to up. If you don’t need control, use up.
When. You want to see what init --scan would write, without writing it.
Do. ctx-optimize scan [--depth N] [--json]
Does. Prints every project found. Changes nothing.
When. Gather or refresh this tree. Also the door for adapters and native sources.
Do.
ctx-optimize add [<path>] [--force] [--jobs N] [--no-adapters]ctx-optimize add --json -|FILEctx-optimize add --rebuildctx-optimize add BILLING_DB_URLDoes. Walks code, markdown, manifests, adapters (honors .gitignore). Incremental: prunes stale nodes; >50% shrink refused without --force. --json upserts a validated Batch (fail-closed). --rebuild drops the store first (retired producers otherwise survive). add NAME dials the env var’s URL, merges, records the name in config — never a raw URL on argv. Multi-module root: one worker per module.
When. You edited code and want the store current. Fast path.
Do. ctx-optimize sync
Does. Re-gathers code, docs, manifests, git. Skips adapters. Adapter nodes stay. Report says how many were skipped.
adapters
Section titled “adapters”When. The external system changed (schema, topics) or you want to see what’s dropped in.
Do. ctx-optimize adapters list · adapters run · adapters run postgres · adapters help postgres
Does. list shows scripts. run re-runs all or one. Never touches the code graph. help prints the source setup card.
capture
Section titled “capture”When. You want to see what a source would emit before it lands in the store.
Do. ctx-optimize capture BILLING_DB_URL
Does. One connector → Batch JSON on stdout. No write. Adapters call this back with their own env.
When. You turned "wiki": false on gather but want the map now.
Do. ctx-optimize wiki
Does. Writes a complete wiki/ from the graph. add already does this unless wiki is off.
When. You have words, not a symbol name.
Do. ctx-optimize query "refund tax" [--budget N] [--modules all|a,b] [--root]
Does. Ranks nodes (IDF / prefix / trigram), attaches 1-hop neighbours, stops at the token budget (default 2000). Alias: ask. Scope follows cwd; zero hits escalate repo-wide. How scoring works.
When. You have the name. You want callers without opening the file.
Do. ctx-optimize card Store.Merge
Does. Signature, doc, file:line, callers, callees. Id, exact label, or fuzzy. Strips invented qualifiers. Miss → nearest labels.
change-plan
Section titled “change-plan”When. You are about to edit.
Do. ctx-optimize change-plan Store.Merge [--json]
Does. Signature + callers + blast + tests to run + co-change, with a confidence footer. Alias: plan.
explain
Section titled “explain”When. You want a sentence, not a card.
Do. ctx-optimize explain PaymentService
Does. Plain-language node + neighbourhood.
affected
Section titled “affected”When. Blast radius only.
Do. ctx-optimize affected Store.Merge [--depth N] [--relation R]
Does. Reverse impact. AMBIGUOUS callers excluded unless --include-ambiguous. Floor, not a census.
When. You know two names and want the connection.
Do. ctx-optimize path cmdAdd Store.Merge
Does. Shortest path on real edges.
When. Unfamiliar repo. Where to start.
Do. ctx-optimize hubs [--top N]
Does. Most-connected nodes.
report
Section titled “report”When. “Explain this repo” as one artifact.
Do. ctx-optimize report [--json]
Does. Subsystems, hubs, seams, and what could not resolve (with the grep that settles it). Never counts an AMBIGUOUS edge as structure.
verify
Section titled “verify”When. A human is about to act on a citation.
Do. ctx-optimize verify "pay.go:L1-L5" · verify Store.Merge
Does. Node exists (exact id or label, never fuzzy), file exists, range in bounds, no drift vs gather HEAD. Exit 0 only if every claim holds.
status
Section titled “status”When. You want the store’s facts.
Do. ctx-optimize status [--json]
Does. Counts, freshness vs HEAD, answers served. Names failed lanes if partial.
When. A hook or script must decide whether to trust the store.
Do. ctx-optimize fresh [--json] · check $?
Does. 0 fresh · 1 stale · 2 unknown (no git) · 3 partial (a producer failed). up retries a partial store in full, adapters included.
When. You want the picture.
Do. ctx-optimize serve [--port 4747] [--host H]
Does. Dashboard on 127.0.0.1:4747. Alias: dashboard. Writes stay loopback + token. Lock.
When. List/filter without jq.
Do. ctx-optimize nodes --kind port --where transport=network.http
Does. Table of nodes. --kind --file-type --id-prefix --label --scope --where k=v --select. Federates at a monorepo root.
When. Same, for edges.
Do. ctx-optimize edges --relation calls --confidence AMBIGUOUS --to <id>
Does. Table of edges. --relation --confidence --from --to --where.
When. You want dependency scope, or who imports a package.
Do. ctx-optimize deps --scope runtime · deps --importers
Does. dep: nodes with scope. --importers adds the files.
A filter value that exists nowhere in the store is disclosed (no node has kind "route"; kinds present: …). Exit 0. A real value narrowed to empty is silent. --json: disclosure on stderr.
config
Section titled “config”When. Read or set a setting.
Do. ctx-optimize config · config <key> · config <key> <value> [--project]
Does. Machine-global by default. --project writes .ctxoptimize/config.json. Project overrides global. Settings.
remote
Section titled “remote”When. Push or pull the store. The binary has no transport.
Do. ctx-optimize remote push · remote pull
Does. Runs the command in config (remote.push / remote.pull). Env: CTX_STORE_DIR CTX_STORE_KEY CTX_SCOPE_PREFIX CTX_DIRECTION. Non-zero exit fails the verb. No URL argument.
When. You want one combined view of named modules.
Do. ctx-optimize merge api worker --into everything
Does. Writes a merged store. Opt-in, never automatic.
export
Section titled “export”When. Another tool needs the graph. Not team sharing (that’s remote).
Do. ctx-optimize export [--format json|dot|graphml|csv|obsidian|all] [--out F|DIR]
Does. Dumps nodes/edges. csv / obsidian / all need --out DIR.
When. Delete this repo’s stores.
Do. ctx-optimize store delete [--yes]
Does. Root + every module store. Prints the blast radius, then [y/N]. Off a TTY: no ask, no delete. .ctxoptimize/ stays. Audited.
When. What mutated the store.
Do. ctx-optimize log [--json]
Does. Prints audit.ndjson: ts, actor (cli|dashboard), action, target, before/after sha256.
languages
Section titled “languages”When. Add a language that is not embedded.
Do. ctx-optimize languages add|list|remove
Does. Grammar packs. Languages.
routes
Section titled “routes”When. A framework the core recognizers don’t cover.
Do. ctx-optimize routes add|list|remove
Does. Route packs. Packs.
manifests
Section titled “manifests”When. A build tool the core recognizers don’t cover.
Do. ctx-optimize manifests add|list|remove
Does. Manifest packs. Packs.
install
Section titled “install”When. Teach the agent CLI.
Do. ctx-optimize install [--claude|--codex|--copilot|--devin] · uninstall
Does. Writes skill + hooks. uninstall removes what install wrote. Stores and committed pointers stay.
update
Section titled “update”When. You want the new binary.
Do. ctx-optimize update · update --check
Does. npm channel → npm i -g. Standalone → GitHub Release, sha256 vs checksums.txt. Then refreshes skills/hooks. --check reports only. No background check.
version
Section titled “version”When. Which binary.
Do. ctx-optimize version
Does. Prints the version.
.ctxoptimize/config.json — the only thing we put in your repo.
Section titled “.ctxoptimize/config.json — the only thing we put in your repo.”Scaffolded by init — alongside an inert example.js.sample adapter, the git-lane transport samples push.js.sample / pull.js.sample, and remote.example.md — owned by you, committed so the whole team’s agents inherit it. Every field:
{ "name": "my-service", // store key override (default: repo basename) "remote": { // YOUR transport — any shell line (js, py, sh, inline) "push": "node .ctxoptimize/push.js", // run by `remote push`; gets CTX_STORE_DIR, "pull": "node .ctxoptimize/pull.js" // CTX_STORE_KEY, CTX_SCOPE_PREFIX, CTX_DIRECTION in env }, "adapters": [ // optional explicit list; scripts dropped in {"name": "kafka", "run": "adapters/kafka.js"} // adapters/ are auto-discovered anyway ], "modules": [ // PRESENT ⇒ this is a multi-module ROOT {"path": "services/api"}, // single-path: one dir, mirrored store {"path": "services/worker", "name": "worker"}, {"name": "Billing", "paths": ["src/Billing", "tests/Billing.Tests"]} // multi-path: scattered folders → ONE store ], "scan": { // tunes scan / init --scan "depth": 5, // max depth below root (default 5) "markers": ["BUILD.bazel"], // extra marker filenames "include": ["tools/gen"], // globs force-added as modules "exclude": ["experiments/*"] // globs pruned from the walk }}| field | meaning |
|---|---|
| name | Overrides the store folder under ~/ctxoptimize/. Use when two repos share a basename or you want a custom key. |
| remote | The push/pull commands — plain shell lines the binary runs as-is (cwd = repo root, one env contract: CTX_STORE_DIR, CTX_STORE_KEY, CTX_SCOPE_PREFIX, CTX_DIRECTION). The binary ships no transport of its own. A legacy v0.3 URL-shaped value still loads but is inert — push/pull print the migration pointer. |
| adapters | Optional explicit adapter list. Any .js/.mjs/.py/.sh dropped into .ctxoptimize/adapters/ is discovered automatically on add and must print one batch JSON to stdout. init seeds an inert example.js.sample. |
| modules | The generated, owned module list of a monorepo root. Written by init --scan, hand-editable, globs allowed. Present ⇒ add fans out and queries resolve scope against it. Two shapes: {"path": "…"} for a single directory, or {"name": "X", "paths": ["…", "…"]} to gather several scattered folders into ONE store — the .NET / Gradle case where a module’s source and tests live in separate top-level dirs (src/Billing + tests/Billing.Tests). Multi-path modules extract in a single pass, so test→source calls resolve across the folder split. |
| module_of | (child configs only) Marks an opt-in config inside a module dir; the value is the root store key. Written automatically when you run init inside a declared module — never a shadow store. |
| scan | Generator tuning: depth, extra markers, include/exclude globs. Built-in markers: go.mod, go.work, package.json, pom.xml, build.gradle(.kts), settings.gradle(.kts), Cargo.toml, pyproject.toml, setup.py. |
| instructions · skills · hooks | Per-project overrides of the machine-global settings — set with config <key> <value> --project, committed so the whole team inherits them. Empty = inherit global. |
Two levels, git-style: machine-global, and --project overrides it.
Section titled “Two levels, git-style: machine-global, and --project overrides it.”Managed by ctx-optimize config. Global lives in ~/ctxoptimize/config.json (never committed); --project writes the same keys into the repo’s .ctxoptimize/config.json (committable — a team pins a repo’s behavior). Project beats global. Keys are flat artifact nouns; values name who gets the artifact.
ctx-optimize config # list effective values + which level set themctx-optimize config instructions # get one key (effective)ctx-optimize config instructions CLAUDE # set globally — scriptable (CI, npm setup, dotfiles)ctx-optimize config instructions AGENTS --project # pin for THIS repo — commit .ctxoptimize/| key | values | controls |
|---|---|---|
| instructions | CLAUDE · AGENTS · ALL (default) · NONE |
Which instruction files init writes the pointer block into: CLAUDE.md, AGENTS.md, both, or don’t touch the repo at all. The block is marker-fenced — your surrounding content is never modified, and re-init never duplicates it. |
| skills | CLAUDE · AGENTS · ALL (default) |
Which skill directories install --skills writes: ~/.claude/skills (Claude Code, Copilot), ~/.agents/skills (the cross-CLI SKILL.md standard), or both. |
| hooks | CLAUDE · AGENTS · ALL (default) · NONE |
Which platforms’ prompt-hook files install writes: the Claude hook (~/.claude/settings.json), the AGENTS-family hooks (codex ~/.codex/hooks.json, copilot), both, or none. Devin never needs a hook file — it reads the Claude hook and AGENTS.md natively, and the install report says which lane covers it. |
Typos are refused with the valid options — a bad value never silently falls back to writing files. BOTH is accepted as an alias for ALL.
One variable in. Four handed to your remote scripts.
Section titled “One variable in. Four handed to your remote scripts.”| variable | meaning |
|---|---|
| CTX_OPTIMIZE_STORE | Store root override (default ~/ctxoptimize). Also how tests stay hermetic. |
| CTX_STORE_DIR · CTX_STORE_KEY · CTX_SCOPE_PREFIX · CTX_DIRECTION | Set by the binary in the environment of your declared remote push/pull command: the local store tree (pull pre-creates it), the store key, the module scope, and push/pull — so one script can serve both directions. |
| $VAR in your scripts | Your own secrets stay env-var names — the shell expands them when your command runs; nothing is ever persisted or printed. |
One store per module, a navigator on top — never a giant merged graph.
Section titled “One store per module, a navigator on top — never a giant merged graph.”Stores mirror your repo layout under ~/ctxoptimize/<root>/<module-path>/. Nested modules (a Maven archetype inside a module) get their own store and are never double-extracted. A module can also be a set of scattered folders — {"name": "Billing", "paths": ["src/Billing", "tests/Billing.Tests"]} — gathered into one name-keyed store so source and tests that live in separate top-level dirs (the.NET / Gradle convention) share a graph and resolve calls across the split.
Scope follows where you ask
Section titled “Scope follows where you ask”| you ask from | what answers |
|---|---|
| a module directory | That module’s store. Zero hits escalate repo-wide automatically — a miss is never a dead end. |
| the repo root | Federation across all module stores (--modules a,b narrows, --root forces the root residual only). |
| anywhere else inside the repo | The nearest config wins — resolution walks upward exactly like git finds .git. |
The navigator (navigator.md + modules.json in the root store) is the map: every module, node/edge counts, top hubs, README summary, wiki links. It refreshes on every root add. Code that lives outside any declared module lands in the root residual graph, so nothing in the repo is invisible. Stores on disk that your config no longer declares are inert — federation is config-driven — and add tells you they’re safe to delete. merge stays an explicit verb.
Push the graph, not the gather time. The remote is your script.
Section titled “Push the graph, not the gather time. The remote is your script.”The binary never moves bytes to a host it chose. remote push / remote pull run the commands you declare in the committed config — any shell line (js, py, sh, or inline). Same trust model as adapters and npm scripts.
# .ctxoptimize/config.json — committed, the whole team inherits it{ "remote": { "push": "node .ctxoptimize/push.js", "pull": "node .ctxoptimize/pull.js" }}
# arm the scaffolded zero-dependency GIT LANE (a private repo hosts every store —# artifacts are sorted ndjson, so git diffs and merges them cleanly)gh repo create your-org/ctx-stores --private # once per teammv .ctxoptimize/push.js.sample .ctxoptimize/push.jsmv .ctxoptimize/pull.js.sample .ctxoptimize/pull.js# set STORE_REPO_URL in both, add the "remote" block to config.json, commitctx-optimize remote push
# teammate, fresh clone — ONE command, donectx-optimize up # runs the declared pull; falls back to a local gather, loudly
# after you gather new workctx-optimize add . && ctx-optimize remote pushYour command gets the store context in env — CTX_STORE_DIR (pull pre-creates it), CTX_STORE_KEY, CTX_SCOPE_PREFIX (module scope: push from a monorepo root shares the whole tree, from a module dir only that module’s prefix), CTX_DIRECTION — and a non-zero exit fails the verb. S3/R2/MinIO is a small aws-CLI script over the same contract; GCS, Artifactory, rsync-over-ssh, anything — write the script that copies CTX_STORE_DIR to and from your host and declare it. Recipes live in the scaffolded .ctxoptimize/remote.example.md. Secrets stay env-var names, expanded by the shell at run time — never in config or scripts, never printed. Vocabulary: sync = keep the graph matching the code (up, sync, add., fresh); share = remote push/pull.
Your database, bucket, queue and API — in the same graph, by env-var name.
Section titled “Your database, bucket, queue and API — in the same graph, by env-var name.”Nine wire-protocol connectors. You pass the name of an environment variable; the scheme in that variable’s value picks the connector. Tables, collections, topics, buckets and endpoints land as nodes beside your code, so one question spans all of it.
ctx-optimize adapters help postgres # setup card: value format, credential/cert params, paste-ready commandctx-optimize adapters help # every scheme it can dial
export BILLING_DB_URL='postgres://reader:$PG_PASS@db.internal:5432/billing'ctx-optimize add BILLING_DB_URL # resolve → dial → capture → merge → record the NAME in configctx-optimize capture BILLING_DB_URL # same dial, Batch JSON to stdout, nothing written
ctx-optimize up # recorded sources re-capture after the gather (24h TTL)ctx-optimize up --sources=always|never # force or skip re-capturectx-optimize up --strict # fail instead of skipping when a var is unsetctx-optimize up --prune-sources # drop producers no longer declared in configpostgres
Section titled “postgres”When. You want tables, columns, FKs in the graph.
Do. adapters help postgres then add $BILLING_DB_URL (postgres:// or postgresql://).
Does. System schemas skipped. Partitions collapse to partitions: N.
When. Same, MySQL.
Do. add $MYSQL_URL (mysql://).
Does. Tables, columns, FKs. System schemas skipped.
When. Same, SQL Server.
Do. add $MSSQL_URL (mssql:// or sqlserver://).
Does. Tables, columns, FKs.
mongodb
Section titled “mongodb”When. Collections and document shape.
Do. add $MONGO_URL (mongodb:// or mongodb+srv://).
Does. Bounded sample. Not a dump of every document.
When. Key-space shape, not values.
Do. add $REDIS_URL (redis:// or rediss://).
Does. Patterns and types.
When. Topics and partitions.
Do. add $KAFKA_URL (kafka://).
Does. Topic nodes, partition counts.
When. Subjects.
Do. add $NATS_URL (nats://).
Does. Subject nodes.
When. Buckets and prefixes.
Do. add $S3_URL (s3://).
Does. Stdlib SigV4. No SDK. Prefixes, not objects.
openapi
Section titled “openapi”When. An HTTP API spec.
Do. add $SPEC_URL (http:// / https://) or add ./openapi.json.
Does. Operations, paths, schemas.
drop-in
Section titled “drop-in”When. No connector for it (tickets, Firebase, a log).
Do. Drop .js / .py / .sh in .ctxoptimize/adapters/. Print one Batch.
Does. Runs on add. Invalid batch rejected whole. sync skips it. adapters run refreshes it.
The logical-shape rule
Section titled “The logical-shape rule”A connector captures the shape a developer actually reasons about, not a dump: system schemas are skipped, partitions collapse to their parent, samples are bounded, and every cap it hit is reported in the output rather than silently truncating. A real Postgres capture is milliseconds, not minutes.
Credentials are structural, not a policy you have to remember
Section titled “Credentials are structural, not a policy you have to remember”Argv and committed config carry env-var names only. A literal password in a committed entry is a hard error at config load, not a warning. The value is resolved at dial time through a ladder — process environment → repo-root .env → machine-global ~/.config/ctx-optimize/.env — and is choked out of every output path, so it cannot appear in a log, an error message, or the store. A tracked .env earns a warning. A teammate without the credential still runs up cleanly: one skip line, prior nodes stay, the gather succeeds.
Freshness is per source: <store>/sources.json stamps each one, dialling is parallel while the merge is serial, and each source reports captured / skipped / failed on its own — one unreachable database never fails the gather. Producer ids are source:<VAR_NAME>, so re-capture replaces exactly that slice.
Anything becomes nodes: databases, topics, logs, documents.
Section titled “Anything becomes nodes: databases, topics, logs, documents.”The binary never grows drivers. An adapter is any script in .ctxoptimize/adapters/ (.js/.mjs → node, .py → python3, .sh → sh) that prints one batch to stdout. It runs on every add; an invalid batch is rejected whole. Adapters can be arbitrarily slow (DB dumps, doc converters), so they get their own lanes: sync re-gathers the repo and skips them, adapters run [name] re-runs all or one on demand, add --no-adapters is the fast lane spelled long.
{ "producer": "kafka", "nodes": [ {"id": "kafka://orders", "label": "orders", "kind": "topic", "file_type": "messaging", "source": "kafka://orders", "location": "L1"} ], "edges": [ {"source": "kafka://orders", "target": "svc://billing", "relation": "consumed_by", "confidence": "EXTRACTED"} ]}Replacement is producer-scoped: each adapter owns its slice of the graph; re-runs prune what it no longer emits, and other producers’ nodes are untouched. confidence is honest provenance: EXTRACTED = parsed fact, INFERRED = name-matched. Cross-batch edges are the point — code ↔ docs ↔ schema link by node id. Read secrets from the environment inside your script; never hardcode values.
12 embedded, everything else is a drop-in pack.
Section titled “12 embedded, everything else is a drop-in pack.”Embedded (tree-sitter compiled to WASM, zero setup): Go, Python, JavaScript, TypeScript/TSX, Java, C, C++, C#, Rust, Zig, SQL. Any other language is a grammar pack — <name>.wasm + <name>.json mapping — in ~/ctxoptimize/grammars/ (machine) or .ctxoptimize/grammars/ (repo, committable; kotlin/swift/dart ship in the repo’s grammars/). Packs are discovered at add-time and override embedded grammars for their extensions; a malformed pack fails loudly.
ctx-optimize languages list # embedded + installed packs + names addable by namectx-optimize languages add kotlin # known name — builds the pack in pure Go (zig auto-fetched once, sha256-verified)ctx-optimize languages add https://github.com/tree-sitter/tree-sitter-rubyctx-optimize languages remove kotlinA pack is built in pure Go: no toolchain to install, no shell script in your path. A Zig compiler is taken from PATH or auto-downloaded once — sha256-verified against ziglang.org’s own index — into ~/ctxoptimize/toolchain/ and cached. The node-type mapping is drafted for you from the grammar’s node-types.json and marked _review so you know what to check. That download is the second of the two user-invoked network calls the binary makes; the other is update.
Teach it your framework, without forking it.
Section titled “Teach it your framework, without forking it.”Core recognizers stay embedded; everything else is a drop-in JSON pack, discovered at add time from .ctxoptimize/routes/ or .ctxoptimize/manifests/ in the repo and ~/ctxoptimize/routes|manifests/ on the machine. Repo wins on collision; a malformed pack fails loudly. The file existing IS the registration — commit it and the whole team’s agents inherit it.
| axis | core, embedded | what a pack adds |
|---|---|---|
| routes | FastAPI, Flask, Express, NestJS, Angular, React Router, Vue Router (from source); OpenAPI, Drupal *.routing.yml, Kubernetes Ingress (from YAML) |
Any call-shaped convention of your own — registerRoute(path, handler) — declared as selectors in JSON. |
| manifests | package.json, pom.xml, *.csproj/*.sln, go.mod, build.gradle, Kubernetes |
Declarative path selectors over any json/xml/yaml build file. |
| grammars | 12 embedded languages | Grammar packs — <name>.wasm + mapping. |
| adapters | — | Any script that prints one validated batch. |
ctx-optimize routes add myframework # scaffold .ctxoptimize/routes/myframework.jsonctx-optimize routes add https://github.com/org/route-packsctx-optimize routes list # core channels + discovered packsctx-optimize routes remove myframework # repo first, then global
ctx-optimize manifests add internal-deps # --global writes to ~/ctxoptimize/manifests/ctx-optimize manifests listRoute edges are emitted as INFERRED with a synthesized_by channel tag — honest provenance about which recognizer produced them.
Plain files. Sorted output. Diffable forever.
Section titled “Plain files. Sorted output. Diffable forever.”~/ctxoptimize/├── config.json # machine-global settings (instructions, skills)├── grammars/ # machine-wide grammar packs└── <repo-name>/ # one store per repo ("name" in repo config overrides) ├── graph/nodes.ndjson # the graph — sorted, atomic-rename writes ├── graph/edges.ndjson ├── manifest.json # content hashes → incremental add ├── source.json # git HEAD at gather time → the fresh gate ├── wiki/index.md # deterministic wiki, regenerated on every add ├── reflections/LESSONS.md # the learning loop's output ├── navigator.md # (multi-module roots) the module map ├── modules.json ├── graph/index/ # machine-local lookup index — see below; never transported ├── audit.ndjson # append-only mutation trail (ctx-optimize log) ├── sources.json # per-source freshness stamps (native sources, 24h TTL) └── services/api/ # (multi-module) one nested store per module, mirroring the repocard on the Linux kernel: 1.8 s → under 20 ms.
Section titled “card on the Linux kernel: 1.8 s → under 20 ms.”Shipped 2026-08-05. Every verb used to materialize the whole graph to answer about one symbol — reading all 2.1 GB of a kernel store costs 0.12 s, but json.Unmarshal of it costs 3.19 s, so ~97% of a lookup was deserialization paid before the question was even known.
<store>/graph/index/ labels.idx # plain sorted text — binary-searched with 8KB ReadAt windows ids.idx edges-by-source.idx edges-by-target.idx # only the matching records are ever parsed| fact | measured |
|---|---|
exact card <symbol>, Linux kernel |
1.73–1.83 s → 0.00–0.02 s |
| fuzzy / ambiguous names | unchanged, ~7.4 s — deliberately: they rank against every node, and refusing to guess is the point |
| index size | 0.43 GB, 20% of the graph (CodeGraph’s index is 54% of their DB), built in ~6 s |
| added to a gather | ~5% on the kernel, ~5% on small corpora |
| equivalence proof | 2,002 of 2,002 kernel resolutions identical through both paths |
Wired into card only so far, and only for exact-id / exact-label resolution. change-plan, affected and path are follow-ups — each needs its own equivalence proof before it ships.
The store remembers how its answers worked out.
Section titled “The store remembers how its answers worked out.”# after an answer proves useful (or doesn't) — the agent records itctx-optimize save-result --question "where is auth" --answer "internal/auth" \ --type query --nodes "auth.go::login" --outcome useful # or dead_end | corrected
# at session start — aggregate with recency decay into reflections/LESSONS.mdctx-optimize reflectDeterministic, no model anywhere: the agent is the judge, the binary only tallies — preferred nodes (corroborated, recency-weighted), dead ends to avoid, verbatim corrections. status shows the running tally of answers served. It is a local counter, not a savings claim — we do not claim token savings anywhere (CRITIQUE.md).
A lookup index — and a round of corrections to our own benchmarks.
Section titled “A lookup index — and a round of corrections to our own benchmarks.”Tagged and published to npm on 2026-08-05.
The lookup index
Section titled “The lookup index”card on the Linux kernel went from 1.8 s to under 20 ms, because ~97% of a lookup was deserializing the whole graph before the question was even known. Plain sorted text files under <store>/graph/index/, binary-searched with 8 KB ReadAt windows. It fails safe — any mismatch falls back to the full scan — and it is machine-local, so it is never transported. Full detail, sizes and the equivalence proof: the lookup index.
Benchmarks re-run, and several published numbers corrected
Section titled “Benchmarks re-run, and several published numbers corrected”The 2026-07-24 harness was a scratch script that was never committed, so its numbers could not be reproduced from the repo. It now lives in the repo at benchmarks/bench_multi.py with pinned, shallow clones under benchmarks/suite/. Two corrections, both of which had flattered us or a competitor: CodeGraph’s kernel query published as 536 ms → 880 ms (the harness passed CodeGraph only the first word while every other tool answered the full phrase), and CodeGraph’s flask query published as 416 ms → 102 ms (it resolves its store from the working directory, and the harness invoked it from the wrong one). The whole 2026-07-24 head-to-head was retired rather than patched. Both corrections stay printed next to the tables on the comparison page.
The remote is your script. Onboarding is one verb.
Section titled “The remote is your script. Onboarding is one verb.”Same contract as ever — deterministic binary, no LLM, no DB — and the network surface is now spelled exactly: only when you ask (update, grammar build’s one-time zig fetch, and whatever your own remote scripts do).
Scripted remotes — the binary ships no transport (breaking)
Section titled “Scripted remotes — the binary ships no transport (breaking)”remote push / remote pull now run the commands you declare in the committed .ctxoptimize/config.json — any shell line (js, py, sh, inline). The built-in file:// + s3:// transports and remote init are gone; a legacy URL-shaped config still loads but is inert, and push/pull print the migration pointer. Your command receives the store context in env — CTX_STORE_DIR, CTX_STORE_KEY, CTX_SCOPE_PREFIX, CTX_DIRECTION — and a non-zero exit fails the verb. init scaffolds a complete zero-dependency git lane as inert samples (push.js.sample / pull.js.sample) plus remote.example.md with git / s3 / custom recipes. Details in Remotes & sharing.
up — the one onboarding verb, bare repo included
Section titled “up — the one onboarding verb, bare repo included”Bare repo, fresh clone, teammate machine, CI: ctx-optimize up makes the store exist and be current, whatever it takes. No config at all → bootstraps it (monorepos via scan; curate .ctxoptimize/config.json after) and gathers; empty store + declared remote.pull → run it (a failed pull falls back to a local gather, loudly); no remote → gather; stale vs git HEAD → fast re-gather (adapter scripts skipped); fresh → no-op. Idempotent — run it whenever. init stays the author-control door (review module lists, pick pointer targets, transport samples); on a pull-declaring clone it just redirects to up. CI gate: ctx-optimize up && ctx-optimize fresh.
Fast lane / slow lane — sync and adapters run
Section titled “Fast lane / slow lane — sync and adapters run”Adapter scripts can be arbitrarily slow (DB dumps, doc converters), so they get their own lanes. sync re-gathers the repo you’re in — code, docs, manifests, git — and skips adapters (safe: replacement is producer-scoped, adapter nodes stay put). adapters list shows them; adapters run [name] re-runs all or one on demand when the external system changed. add --no-adapters is the fast lane spelled long.
update — the whole tool, one command
Section titled “update — the whole tool, one command”Updates the binary (npm installs via npm; standalone binaries from GitHub Releases, sha256-verified against the release’s checksums.txt, swapped atomically — dev builds left alone) and then refreshes skills, hooks and the global rule from the binary that is now current. update --check reports without touching anything. User-invoked network only — never a background check.
change-plan — the composed “I’m about to change X” answer
Section titled “change-plan — the composed “I’m about to change X” answer”One bounded call returns the signature, callers, blast radius, which tests to run (affected filtered to test declarations), historical co-changes, and a confidence footer separating extracted from inferred evidence. One call in place of the query → card → affected chain it replaces. Alias: plan. (We do not claim it saves you tokens — that claim was measured and killed; see CRITIQUE.md and the graded run, where the store arm made 15.0 tool calls per run against the shell arm’s 42.7.)
Routes, dependencies, subsystems, co-change — and a real dashboard.
Section titled “Routes, dependencies, subsystems, co-change — and a real dashboard.”Every one of these is deterministic pure-Go extraction over the ASTs and git history we already hold — no model, no network, same contract.
v0.3.5 — scattered modules, one-step clones, a viewer that never white-screens
Section titled “v0.3.5 — scattered modules, one-step clones, a viewer that never white-screens”Multi-path modules. A module can be a set of folders, not one subtree: {"name": "Billing", "paths": ["src/Billing", "tests/Billing.Tests"]}. The.NET / Gradle case — source in src/, tests in tests/ — gathers into one store and extracts in a single pass, so test→source calls resolve across the split instead of breaking at a folder boundary. One-step clones. A fresh clone fetches the team’s prebuilt graph instead of rebuilding from source — since v0.4 the verb for this is ctx-optimize up (init on a pull-declaring clone just redirects to it). The agent-instruction block init writes is gated — if the binary isn’t installed, agents ignore it and read code normally. A crash-proof viewer. One malformed graph node used to blank the whole dashboard; now a bad node is sanitized or dropped alone and every healthy node still renders, behind an error boundary — covered by unit tests.
Framework routes — the edge grep can’t produce
Section titled “Framework routes — the edge grep can’t produce”Routing declarations become route nodes linked by handles edges to their handlers, so affected <handler> surfaces the URL that binds it and query "GET /users" finds the endpoint. Core recognizers: FastAPI, Flask, Express, NestJS, Angular, React Router, Vue Router (from source) and OpenAPI, Drupal *.routing.yml, Kubernetes Ingress (from YAML). Edges are INFERRED with a synthesized_by channel tag — honest provenance. Anything call-shaped that isn’t core is a route pack (see below).
The manifest lane — dependencies + K8s as graph
Section titled “The manifest lane — dependencies + K8s as graph”Build manifests become a dependency graph: package.json, pom.xml, .csproj/.sln (ProjectReference = the.NET module graph), go.mod, build.gradle. A dependency is a version-free node (dep:maven/org.apache.kafka:kafka-clients) with the version on the edge — so the same library declared by a Maven module and a Gradle module federates to one node, and affected dep:npm/express lists every module that uses it. Kubernetes manifests become topology: resource nodes (k8s://ns/kind/name), Service→Deployment selects (label match), Ingress→Service, ConfigMap mounts, image refs. Secret resources: node only, data never read; Helm templates skipped.
Community-detected subsystems
Section titled “Community-detected subsystems”The wiki now opens with a Subsystems table — deterministic community detection clusters the graph into architecture neighborhoods (“this repo is 6 subsystems”), each with its top hubs and dominant directories. Regenerated on every add, byte-stable.
Git-history co-change edges
Section titled “Git-history co-change edges”From git log alone (zero parsing): co_changed_with edges between files that historically change together, with support counts. Answers what no AST can — “what usually breaks with this?” — and the strongest signals are often code↔doc couplings a parser could never see.
Pack doctrine — extend routes and manifests like grammars
Section titled “Pack doctrine — extend routes and manifests like grammars”Core stays embedded; everything else is a drop-in JSON pack, discovered at add time from .ctxoptimize/routes/ (or manifests/) in the repo and ~/ctxoptimize/routes/ on the machine — repo wins on collision, malformed packs fail loudly.
# route pack: teach it your team's own registerRoute(path, handler) conventionctx-optimize routes add myframework # scaffold .ctxoptimize/routes/myframework.jsonctx-optimize routes add https://github.com/org/route-packsctx-optimize routes list # core channels + discovered packs
# manifest packs work the same way (declarative path selectors over json/xml/yaml)ctx-optimize manifests add internal-depsctx-optimize manifests listFirst-class dashboard
Section titled “First-class dashboard”ctx-optimize serve is now a full React app (embedded, zero external requests): Repos (freshness, counts), Onboard (scan → confirm → add with live progress), Query, Viewer (force-directed graph, budgeted, click-to-expand neighborhoods), Settings (every config key + pack across all four axes, editable), Changes (the audit feed). All mutations run through the CLI’s own validated doors, loopback-only even if --host is widened, and every change is written to an append-only audit.ndjson — read it with ctx-optimize log.
What the binary will never do.
Section titled “What the binary will never do.”No LLM calls. No database drivers. No embeddings. No MCP server. No network — except when you ask: update (npm / GitHub Releases, sha256-verified), languages add’s one-time zig fetch, and whatever your own remote scripts do. No telemetry. The host agent is the only intelligence; the binary is deterministic and its artifacts are plain, sorted, git-diffable files. Full reasoning: VISION.md and the standing counter-weight CRITIQUE.md.