symbol-level code reads

Read the function, not the file.

Once a whole file is in an agent's context it is replayed on every turn, and nothing that compresses it afterwards can undo that without breaking the prompt cache. sym keeps the file out in the first place: a skeleton, one symbol, or a budgeted map of a directory, parsed with tree-sitter.

-22%
median session cost with the plugin vs without, cache reads billed (the whole table)
11
languages: Rust, Lua, Python, TypeScript/TSX, JavaScript, Go, C, C++, Java, Ruby
3
fronts on one core: CLI, MCP over stdio, HTTP for a gateway
install

One binary. Three verbs.

$ cargo install starlab-sym            # the binary is `sym`
$ sym ls src/ops.rs                # every symbol: line range + signature
src/ops.rs — 271 lines, 14 symbols
   10-15    pub struct LsOut
   58-104   pub fn read(file: &Path, symbol: &str) -> Result<ReadOut, String>
   ...
$ sym read src/ops.rs read         # one symbol, with its doc block
$ sym map src/ --budget 800        # files ranked by PageRank over the imports, cut at 800 tokens

Add --json for structured output and --est for a token estimate. Nested symbols are addressed by leaf or qualified path (Widget::new, Runner.helper, Server.Serve).

Claude Code plugin

A skill that says when to use which verb, the MCP server, and a hook that nudges any whole-file Read of a big source file toward the skeleton. Silent when sym is not installed.

$ claude plugin marketplace add https://github.com/codylwalker/sym
$ claude plugin install sym@starlab

Any MCP client

Tools sym_ls, sym_read, sym_map over stdio. Same arguments as the CLI; json: true for structured results.

{ "sym": { "command": "sym", "args": ["mcp"] } }
for agents that cannot install a binary

The same verbs over any public repo, paid per call.

Managed agents, web agents and sandboxes without cargo get map and read over a git URL. The server clones shallowly, caches for an hour, and answers in the same shape as the CLI. It is also an MCP server over HTTP, one line in any client:

$ claude mcp add s2ar --transport http https://api.s2ar.dev/mcp --header "Authorization: Bearer <key>"
$ claude plugin install s2ar@starlab        # or: the same server as a plugin, with skills and free reminders; no key needed to start
tools: sym_map_repo $0.01 · sym_ls_repo · sym_read_repo · sym_find_repo $0.005 · sym_where_repo $0.01 · score_text $0.005 · assert_output $0.005 · inspect_x402 $0.005 · certify_image $0.01 · buy_credits
every paid tool takes an optional x402_payment argument (USDC on Base per call, no key); ten languages: Rust, Python, TypeScript/TSX, JavaScript, Go, Lua, C, C++, Java, Ruby

Over plain HTTP every route takes GET with query parameters or POST with a JSON body, and repo is owner/repo, github.com/owner/repo or the URL. Payment is the 402 handshake, in three rails ordered by how autonomous the agent can be.

GET https://api.s2ar.dev/v1/sym/read?repo=BurntSushi/ripgrep&ref=14.1.1&file=crates/core/flags/defs.rs&symbol=Glob%20as%20Flag

← 402 Payment Required
{ "x402Version": 1, "accepts": [ … ] }           # x402: USDC on Base, no account; v2 in the PAYMENT-REQUIRED header
{ "credits_card": { "5": "https://buy.stripe.com/…", … } }   # a human buys a pack; the key is shown once, then Authorization: Bearer
{ "how_to_pay": "…", "docs": "…", "example_get": "…" }   # every quote says how, and where the worked example is

← 200, paid
{ "ok": true, "text": "impl Glob as Flag (crates/core/flags/defs.rs, lines 2459-2512) …", "charged_usd": 0.005,
  "receipt": { "evidence_hash": "sha256:…", "cost_usd": 0.005, "determinism": "replayable" } }
railwho paysautonomyminimum
Authorization: Bearer sk-…a human bought credits oncefull, after the first purchase$5 pack
PAYMENT-SIGNATURE / X-PAYMENT (x402)the agent's USDC wallet on Base; over MCP, the x402_payment argumentfull$0.005
Authorization: Payment (MPP, card) → /v1/credits/buythe human's card through the Link agent wallet; buys a $5/$25/$100 pack and returns a keyone approval tap per pack, by Link's design; then full$5 pack (cards need $0.50+, a call costs cents)
Authorization: Payment (MPP, Tempo)the agent's USDC wallet on Tempo, settled by Stripefull$0.01 · when Stripe's crypto capability is enabled on our account (not yet)

The agent-side skill for cards is npx skills add stripe/link-cli: it answers the 402 on /v1/credits/buy, and the key in the response pays for every call after that. Nothing is charged for a failed request.

measured, not claimed

Session cost, cache reads billed.

Token-reduction numbers measured on one payload say little about what a session costs; several context compressors cost more than running nothing once caching is counted. So the number on this page is the median cost of a fixed task list run as fresh sessions with and without the plugin, on a pinned public repo, cache reads billed, losses included. The first full run says something we did not expect and will not hide: Claude Code already avoids whole-file reads on its own (it greps, then reads a range), so on file-anchored questions the plugin is close to a wash and the agent reached for sym in one session out of twenty-four. Where sym earns its keep is orientation (map), agents without grep or ranged reads (the hosted tier), and the measurement itself.

taskarmcost USDinputcache readturns
defs-colorsmod0.23612410641
defs-colorsmod0.12292039420910
defs-colorsmod0.13022243615811
defs-colorsplain0.1363162837698
defs-colorsplain0.0813122231236
defs-colorsplain0.14802241280211
defs-colorsplugin0.13072243692311
defs-colorsplugin0.12772039255710
defs-colorsplugin0.1121183552379
defs-globmod0.038681508014
defs-globmod0.029461128723
defs-globmod0.032461127243
defs-globplain0.045081427604
defs-globplain0.035961072173
defs-globplain0.0554101801915
defs-globplugin0.038681508024
defs-globplugin0.031261129433
defs-globplugin0.0965101782875
defs-sortmod0.031661128653
defs-sortmod0.034061126463
defs-sortmod0.039881507314
defs-sortplain0.044881420004
defs-sortplain0.083181331884
defs-sortplain0.037661069113
defs-sortplugin0.034461126473
defs-sortplugin0.033461128793
defs-sortplugin0.030861128313
gitignore-matchedmod0.042361140094
gitignore-matchedmod0.042461152395
gitignore-matchedmod0.047961138605
gitignore-matchedplain0.042761063563
gitignore-matchedplain0.088681330454
gitignore-matchedplain0.053461101913
gitignore-matchedplugin0.086961031643
gitignore-matchedplugin0.043461139964
gitignore-matchedplugin0.041461152393
glue-fillmod0.039361127894
glue-fillmod0.032561142414
glue-fillmod0.054581534145
glue-fillplain0.085581330754
glue-fillplain0.1100122052856
glue-fillplain0.0970101693395
glue-fillplugin0.080461021204
glue-fillplugin0.040361127874
glue-fillplugin0.032461142454
json-beginmod0.0849142755489
json-beginmod0.052381539176
json-beginmod0.038961137625
json-beginplain0.079781338754
json-beginplain0.036961070723
json-beginplain0.0966122056306
json-beginplugin0.093581432216
json-beginplugin0.052081539026
json-beginplugin0.039461137595
orient-flag-archmod0.12051428934011
orient-flag-archmod0.0963122419868
orient-flag-archmod0.11581428673912
orient-flag-archplain0.16872244458312
orient-flag-archplain0.14971429100012
orient-flag-archplain0.18762042320316
orient-flag-archplugin0.20232244709413
orient-flag-archplugin0.1260142823779
orient-flag-archplugin0.10331224434310
orient-printermod0.0962101965828
orient-printermod0.0624101921575
orient-printermod0.0968101997138
orient-printerplain0.11852036542216
orient-printerplain0.0985183332689
orient-printerplain0.1160142437557
orient-printerplugin0.1309101812967
orient-printerplugin0.13791634233011
orient-printerplugin0.043661137503
orient-testsmod0.02644749463
orient-testsmod0.0545101898295
orient-testsmod0.0540101897795
orient-testsplain0.0473101777955
orient-testsplain0.0596122143676
orient-testsplain0.0459101776475
orient-testsplugin0.087881401826
orient-testsplugin0.044581506054
orient-testsplugin0.035761127614
orient-walkermod0.04064749762
orient-walkermod0.055981504864
orient-walkermod0.04064749762
orient-walkerplain0.036561064123
orient-walkerplain0.06404614643
orient-walkerplain0.047481422756
orient-walkerplugin0.08184642843
orient-walkerplugin0.055261138683
orient-walkerplugin0.0693101896056
standard-sepmod0.042881647075
standard-sepmod0.0738102031936
standard-sepmod0.049761171963
standard-sepplain0.0645101834705
standard-sepplain0.049781457834
standard-sepplain0.07596987104
standard-sepplugin0.061781599675
standard-sepplugin0.051661171944
standard-sepplugin0.062381602334
walk-skipmod0.031661126053
walk-skipmod0.028261135703
walk-skipmod0.02024757322
walk-skipplain0.07066970333
walk-skipplain0.034261061873
walk-skipplain0.035161062393
walk-skipplugin0.072661019373
walk-skipplugin0.02264749462
walk-skipplugin0.032761126233

2026-10-08T2020Z on https://github.com/BurntSushi/ripgrep@14.1.1, 3 runs per cell, model claude-sonnet-5, Claude Code 2.1.294. mod vs plain: -21.8% (median of per-task paired deltas; cheaper on 11 of 12 tasks; pooled -32.8%). plugin vs plain: -10.5% (median of per-task paired deltas; cheaper on 10 of 12 tasks; pooled -13.4%). The headline above is the mod arm (the install on Claude Code 2.1.287+). Answers, judged by haiku against the plain arm's: mod agreed with plain on 5 of 12 tasks (partial 6, disagree 1); plugin agreed with plain on 4 of 12 tasks (partial 7, disagree 1). Answers in this result were stored cut at 600 characters; most "partial" verdicts cite the cut, not a different answer. Later results store 2,000. Ablation (run 2026-10-09T0418Z, same tasks, 3 runs per cell): the mod -26.9% (cheaper on 8 of 12); the mod without its repo map +16.5% (cheaper on 5 of 12); the mod with Haiku file summaries on the map -2.3% (cheaper on 6 of 12); answers agreed with plain (full-length, haiku judge): mod 10/12, mod-nomap 8/12, mod-sum 10/12. The repo map with the first message is where the saving comes from; summaries cost more tokens per turn than they save here, so they stay opt-in. Full file: bench/results/2026-10-08T2020Z.json.

The harness is bench/ in the repo, with a README on how to run it on any plugin or mod and how to read the numbers; every run is published there, losses included. Run it on your own repo; the table above regenerates from bench/results/latest.json.

pricing

Free where it runs on your machine. Cents where we run it.

Local: free

The CLI, the Claude Code plugin and the MCP server. Source-available under PolyForm Shield 1.0.0: read it, build it, self-host it.

Hosted: per call

/v1/sym/map $0.01 · /v1/sym/read, /ls, /find $0.005 · /v1/sym/where (code by meaning, over a semantic index we build once per checkout on our own GPU) $0.01 · /v1/score (a text against a form, programmatic verifiers) $0.005 · /v1/assert (deterministic checks over an agent's output; a signed record) $0.005 · /v1/x402/inspect (what an x402 endpoint declares, checked and signed) $0.005 · /v1/certify (compress an image, prove the model still reads it; a signed record) $0.01. Three keyless calls a day are free on each route. Credit packs of $5, $25 and $100 by card, or USDC on Base per call over x402. One ledger for every s2ar API, and every answer carries a receipt. The pay-per-call walkthrough → · where this sits in the market → · today's proceedings →

docs

What each front returns.

frontcallreturns
CLIsym ls | read | map [--json] [--est]the legacy text, or JSON with tokens_est
MCP (hosted, HTTP)claude mcp add s2ar --transport http https://api.s2ar.dev/mcp …sym_map_repo, sym_ls_repo, sym_read_repo, sym_find_repo, sym_where_repo, score_text, certify_image, buy_credits; a keyless call answers "payment required" with the x402 accepts, the card packs and how_to_pay
MCP (stdio)sym_ls {file} · sym_read {file,symbol} · sym_map {dir,budget}one text content block; failures are isError, never protocol errors
HTTP (local)sym serve --port 8431 --root DIR → POST /ls /read /map{ok, text, data, tokens_est}; every path jailed under the root
HTTP (hosted)GET /v1/sym/map?repo=&ref=&budget= · /v1/sym/read?repo=&file=&symbol= (or POST the same fields as JSON)the same, plus charged_usd and a receipt; 402 with every rail and how_to_pay when unpaid
hooksym hook prePreToolUse additionalContext for a big-file Read; exit 0 always

The repo map resolves each file's imports to files in the tree (Rust mod/use, Python, JS/TS relative paths, Go packages, Lua require, C includes, Java imports, Ruby requires), runs PageRank over that graph with test files voting at a discount, and emits top-level signatures (twelve per file, then "+N more") until the budget is spent.