Stop paying context to find code.

A live code knowledge graph that lets an agent ask in symbols instead of reading files: a location, a signature, an outline, a blast radius, a context pack sized to a budget you set — and, after every edit, what that edit broke elsewhere.

Currently v0.1.0 · Local · SQLite · CLI, MCP, hooks and a web UI

Bun 1.4+ and git. Indexing, analysis and the UI are entirely local. The GitHub sync is the only code that reaches the network, and SGX_OFFLINE=1 turns it off.

sgx context login --budget 200captured output
context · ~197/200 tok · 7 symbols in 3 files · epoch 2

src/auth/login.ts
@f0daw fn validate  :9
@twtp1 fn login  :14-25
/**
 * Log a user in and remember the session.
 */
export function login(user: User, opts: LoginOpts = {}): Session {
  if (!validate(user)) {
    throw new Error("invalid user");
  }
  const token = `${formatName(user.name)}-${opts.remember ? "long" : "short"}`;
  const session: Session = { token, user };
  store.set(session);
  return session;
}
@5kx7a fn logout  :27

src/auth/types.ts
@rxqer iface User  :1
@89nj2 iface LoginOpts  :6-8
export interface LoginOpts {
  remember?: boolean;
}
@sn2v2 iface Session  :10

tests/login.ts
@jwcqd export function testLogin(): void { … 5 lines }

not shown (12): @atvdc 0.32 · @b0q5q 0.29 · … — expand: sgx show <handle>
A budget you set, and it reports what it spent~197/200 tok
Handles outlive the edits that rot file:line@twtp1
What did not fit is named, with how to expand itnot shown (12)

Real output, captured in a temporary copy of test/fixtures/mini with git history.

The cost of finding

Most of a session is spent locating code, not changing it.

An agent without an index greps, gets every mention back — definition, import, comment — and reads whole files to tell them apart. It pays for that again after every compaction. find, outline and show --level are the replacement, and each one prints what it cost.

sgx find login --limit 3captured output
@twtp1 fn    login(user: User, opts: LoginOpts = {}): Session   src/auth/login.ts:17
@89nj2 iface interface LoginOpts                                src/auth/types.ts:6
@jwcqd fn    testLogin(): void                                  tests/login.ts:3

Locate

A symbol, not a list of files.

One line per hit: kind, signature, path and line, and a handle to expand it later. find searches names, paths and docs, and boosts exact and prefix names before the limit applies.

sgx outline src/auth/store.tscaptured output
src/auth/store.ts · 23 lines · outline ~53 tok (full ~112)
@atvdc export class SessionStore {
  @rtj1r get(id: string): Session | undefined { … 3 lines }
  @f5zfk set(session: Session): void { … 3 lines }
  @8czjn clear(): void { … 3 lines }
}
@hwz8t export const store

Read

A skeleton that prices itself.

The header carries the outline's estimate against the whole file, so the saving is on the page rather than in the pitch. Bodies collapse to { … 3 lines }, each still addressable.

sgx show validate --level 1captured output
@f0daw fn validate(user: User): boolean  src/auth/login.ts:9

Expand

Only the symbol the task needs.

show --level moves one symbol up the ladder — location, signature, docs and members, full source. The rest of the file never enters the context window.

How it works

Index once. Then ask in symbols, not files.

One pass builds .sgx/graph.db, a SQLite graph of symbols, edges, git history and notes. Everything after it — the CLI, the MCP server, the Claude Code hooks and the web UI — reads that one graph.

  1. 01

    Index the repository.

    init creates .sgx/graph.db and runs the first index. --claude merges the MCP server and the two hooks into the repository's own config, keeping whatever is already there. watch keeps it current; read commands refresh first.

    sgx init --claude

  2. 02

    Ask at the fidelity you need.

    find, outline, show and map answer at four levels, from a location to full source. context packs a task into a token budget. Each symbol carries a handle that survives edits to its body.

    sgx outline src/auth/store.ts

  3. 03

    Guard the edit as it happens.

    check reports removed exports, stale callers of changed signatures, cycles, architecture-rule violations and untested changes. The post-edit hook runs it on the file just written and prints nothing when clean.

    sgx check --all

Context

A token budget, not a pile of files.

The expensive part of an agent is not finding code, it is sending too much of it. context takes a task and a budget and returns the code worth paying for, in a form that is stable enough to cache.

The fidelity ladder

L0 is a location and a name, L1 a signature, L2 documentation and a member outline, L3 the source. show --level picks one; outline elides bodies and prints its own estimate against the full file.

Seeded, then ranked

Explicit handles, paths, identifier terms and optionally the git working set seed a personalized PageRank. The strongest seeds receive source before the remaining space goes to their neighbours; generic terms carry less weight.

What it dropped

The footer lists the candidates that did not fit with their scores and the command to expand any of them. Output is deterministic for the same graph, sources and ledger.

Sent once

show and context remember the body hashes sent in a session. Unchanged source comes back as ≡ unchanged since sent; changed bodies are resent and marked changed. On a replayed run the ledger saved 58% of bytes.

sgx map --budget 200captured output
repo map · ~193/200 tok · 25 files · 12 symbols · epoch 2

entry: go/cmd/main.go · py/app/service.py · src/cli.ts

go/greeter/ (1 file)
  @bpayd type Greeter struct  greeter.go:4
  @b32tr fn New() *Greeter  greeter.go:14

A repository map ranked into a 200-token estimate.

Edit-time checks

It tells you before the tests do.

Three findings an agent cannot see from the file it is editing. All three are real output, and all three are what the PostToolUse hook prints when it is not silent.

sgx check --allcaptured output
warn cycle src/core/a.ts import cycle src/core/a.ts -> src/core/b.ts -> src/core/a.ts

Cycles

An import cycle you did not mean to add.

check exits 1 for errors and 0 for warnings, so it drops into a pre-commit hook. Architecture rules and the test globs live in a repository-local sgx.json.

sgx changes --since 2captured output
changes since epoch 2 · 1 symbol change in 1 file · epoch 3
M sig  @twtp1 login(user: User, opts: LoginOpts = {}): Session → login(user: User, opts: LoginOpts = {}, required: boolean): Session  src/auth/login.ts:17

Signatures

A signature that moved under its callers.

changes prints committed symbol changes since an epoch, old signature to new. An agent resuming after a compaction reads what happened while it was away.

sgx checkcaptured output
warn  open-pr  src/auth/login.ts  also changed by open pull request #42 (dana)

In flight

A file someone else has already changed.

The warning reads the pull request cache, never the network, so it costs the post-edit hook nothing. sgx github review <n> prints the same findings scoped to one pull request.

Documents

The prose goes stale silently. Now it does not.

A repository is not only code. A document declares what it governs in its front matter, the graph carries an edge from it to every file it covers, and check reports the ones your change has left behind — in the same hook that reads the code.

A document says what it governs

covers: in the front matter takes globs or paths. Every other top-level key becomes a searchable symbol, scalars included, and owner rides along on every finding.

Three sources, three confidences

A covers: glob is 1.0 because the document said so, a resolved link 0.9, a fenced block naming a path 0.7. A path mentioned in prose is never a source — inference that noisy would make the finding worthless.

Behind is a commit count

How many commits touched a covered file after the document last moved. sgx docs --stale sweeps the repository and exits 1, so it works as a CI gate; a deprecated status never fires.

A claim, not a measurement

A glob can be wrong, a link can point at the wrong file, and a document can be current with an old timestamp. The finding names the document, the file and the count, and lets you judge.

sgx docs --stalecaptured output
docs/auth.md  2 commits behind src/auth/login.ts  owner platform
1 stale document
sgx checkcaptured output
warn stale-doc docs/auth.md documents src/auth/login.ts, which you changed; the document is 2 commits behind it (owner: platform)
0 errors · 1 warning · 0 info

The same warning reaches the agent through the post-edit hook.

Blast radius

Read a change as its consequences.

impact combines three kinds of evidence for one target or a whole working diff, and labels which is which rather than merging them into a single confident answer.

sgx impact login --depth 1captured output
impact @twtp1 login · src/auth/login.ts:17
2 callers in 3 files · 1 test · 1 co-change partner
direct:
  @jb7pz module src/cli.ts:1  imports
  @eqdyv function main  src/cli.ts:3  calls
  @2pnq2 module src/index.ts:1  imports
  @kd19m module tests/login.ts:1  imports
  @jwcqd function testLogin  tests/login.ts:3  calls
tests:
  tests/login.ts
co-change:
  src/auth/store.ts  support 1.00 (4 commits)
  1. 1
    Callers and importers come from the graph. Syntactic resolution with explicit confidence; inferred relationships print ~. This is not a type-checked call graph and does not claim to be one.
  2. 2
    Tests are the ones that reach the target. sgx tests --diff | xargs bun test runs only the tests a working diff can affect. The edge is three-hop reachability, which is generous — enrich --edges exists to prune it.
  3. 3
    Co-change is evidence git has and the parser does not. Files that keep moving together, with support and commit count, mined from one git log --raw --numstat pass over the last 2,000 commits when HEAD moves.
  4. 4
    A pull request is a blast radius. sgx github pr <n> resolves the changed paths into the graph and answers with the same report, plus the reviewers the file history suggests and any downstream repositories.

Semantics

Find the code whose names never say it.

The graph is syntactic, so sgx find auth misses SessionStore and validateCredentials. sgx enrich judges every eligible symbol into a concept — one request per symbol, three questions each, every answer carrying the model's own confidence.

A vocabulary from the repository

Half the concepts are universal roles; half are derived from the repository's own directories, package names and detected flavors. Nothing is invented, and the list is reproducible from the graph.

Priced before it runs

enrich --estimate prices a pass first, and the UI's button carries the same number. sgx's own 1,694 symbols took 21 seconds and about ten cents. It is incremental by body hash, so a second pass sends only what changed.

A miss says it missed

Concept matching is lexical and weighted by how many descriptions share a word. A question that matches nothing says so rather than guessing, and --route spends one model call to try again.

A judgement, not ground truth

Assignments are one model's opinion with its own confidence, reported as such. Recorded judgements in .sgx/semantic.json replace the model entirely, and are how the test suite and the end-to-end run work.

sgx where "caching"captured output
cache (10) · 4 symbols
@h8kcy 100% cache          perEpoch      src/ui/api.ts:155
@frevc  99% cache          getCsr        src/context/csr.ts:39
@hkymv  88% cache          ensureSynced  src/github/sync.ts:88
@m0s0e  87% cache          REFRESH_MS    src/github/sync.ts:14
sgx enrich --estimatecaptured output
~1,245 symbols × 40 concepts ≈ 1,718,100 input tokens ≈ $0.07 and ~39s (estimates)

None of those four say cache in their name.

Languages

Deep where it can be, structural everywhere else.

Nine languages get full symbol extraction with calls, imports and tests edges. Nine more get a compact structural outline, and their local imports, document links, resources and schema references still become edges where the target resolves.

TypeScriptDeep
JavaScriptDeep
PythonDeep
GoDeep
RustDeep
C#Deep
JavaDeep
PHPDeep
RubyDeep
SQLOutline
LuaOutline
RacketOutline
MarkdownOutline
JSONOutline
YAMLOutline
HTML / HTMXOutline
CSSOutline
GraphQLOutline

SQL is dialect-agnostic: CREATE TABLE becomes a table with a column per definition, and REFERENCES, ALTER TABLE, FROM and JOIN become ordinary references, so a foreign key has a blast radius like anything else. YAML scalars are indexed two levels deep, which is where runs-on: and image: live. Other files stay searchable by path and still count towards churn and co-change. Package-scoped flavors are detected separately — React, Next.js, Vite, Svelte, SvelteKit, FastAPI, GraphQL and HTMX.

Surfaces

One graph, four ways in.

The same answers reach a person and an agent: sgx <command>, sgx mcp, sgx hook and sgx ui. Nothing is a wrapper around the CLI — every surface opens the graph directly.

CLI

Every command takes --root and --json, and the scope flags widen it past one repository. Read commands refresh the index first unless you pass --no-refresh.

MCP server

sgx mcp is a stdio server whose stdout carries only protocol messages. Fifteen tools, and --schema-cost measures what the tool list actually costs in the model's context before you pay for it.

Agent hooks

SessionStart emits the repo map at up to 1,500 estimated tokens plus open errors, and resets the ledger on a compaction. PostToolUse reindexes the written file and checks it against HEAD. Claude Code and Codex share the contract; fx, which has no command hooks, gets the server through .mcp.json and an AGENTS.md block.

Web UI

sgx ui binds 127.0.0.1 only, indexes, watches and serves fourteen screens — search, symbols, files, a bounded graph, architecture and flow lenses, module matrix, hotspots, context, concepts, workspace, git and GitHub. Every page refetches on an SSE epoch event.

sgx mcp --schema-costcaptured output
sgx mcp schema · 15 tools · 6,049 bytes · ~1,513 tok (est.)
lookup_candidates     532 B  ~133 tok
lookup_materialize    697 B  ~175 tok
find                  360 B  ~90 tok
flavors               301 B  ~76 tok
outline               291 B  ~73 tok
show                  403 B  ~101 tok
context               422 B  ~106 tok
impact                404 B  ~101 tok
docs                  389 B  ~98 tok
changes               224 B  ~56 tok
check                 310 B  ~78 tok
where                 488 B  ~122 tok
git                   425 B  ~107 tok
pr                    434 B  ~109 tok
note                  353 B  ~89 tok

Measured from the SDK's own tools/list response, not estimated from the source.

Remote sources

Mirror what the repository depends on.

A vendor's API reference or a sibling checkout is not in your tree, so nothing checks your code against it. sgx source add <uri> --as <name> --covers '<glob>' mirrors it into .sgx/sources/, indexes it with the ordinary pipeline, and gives it the same covers: edge a local document gets.

A mirrored page is ordinary graph content

Its headings become symbols with handles, so find, context and show answer from it. A path or file:// mirrors a file or a directory of text files; an https:// URL serving Markdown or plain text is fetched with ETag; HTML and git+https:// are refused by name rather than half-supported.

The mirror is what everything reads

source add and source sync are the only commands in sgx that touch the network. check, context, find, the MCP server and the hooks all read the mirror, so a source that is unreachable today still answers and a flaky CDN can never block an edit-time guard.

--ttl marks, it does not schedule

It marks a source due, and sgx source sync --stale fetches those and nothing else — an unchanged page is a conditional request answered 304. sgx watch --sync-sources and sgx init --claude --sync-sources are the two ways to run it automatically, and neither is on by default.

Then it governs code

The manifest's covers: becomes a documents edge, so the vendor's reference goes stale against your billing code exactly as docs/auth.md would, and check says so. A mirrored file has no commits of its own, so its last fetch stands in for the timestamp.

sgx source add ../vendor-docs --as vendor --covers 'src/billing/**' --ttl 7d2 commands
vendor: fetched · 1 file · 131 B

$ sgx source list
1 source

vendor  ../vendor-docs
  1 file · 131 B · synced 0s ago · trusted · ttl 1w
  covers src/billing/**
sgx find charge --limit 52 commands
@qdsrj fn      chargeCustomer(id: string, cents: number)   src/billing/charge.ts:1
@y5mzv section ## charge.failed                            .sgx/sources/vendor/webhooks.md:7
@3kekz section ## charge.succeeded                         .sgx/sources/vendor/webhooks.md:3

$ sgx source show vendor
vendor · ../vendor-docs
trusted · text · ttl 1w · synced 3m ago · changed 3m ago
1 file · 131 B

  .sgx/sources/vendor/webhooks.md · 9 lines

covers src/billing/charge.ts

The last line is the point: a vendor's page now governs billing code.

Telemetry

See how the agent actually used it.

Every tool call is recorded in the repository's own .sgx/graph.db — which tool, with what arguments, how long it took, how many tokens came back, and what the agent called next. SGX_USAGE=0 turns it off; nothing is ever sent anywhere.

sgx usagecaptured output
sgx usage · 8 calls · 2 sessions · 0s ago → 0s ago
~371 tok returned · 87 tok saved by the ledger · 25.0% errors · 1 finding · p50 15ms · p95 75ms

tool     calls  err  find   p50   p95  tok  share  saved  last
find         2    —     —  17ms  18ms  212    57%      —  0s ago
show         2    —     —   0ms   1ms  114    31%     87  0s ago
impact       1    1     —  15ms  15ms    0     0%      —  0s ago
index        1    —     —  75ms  75ms   23     6%      —  0s ago
outline      1    1     —   0ms   0ms   11     3%      —  0s ago
where        1    —     1  15ms  15ms   11     3%      —  0s ago

what follows what:
  find → where  ×1
  find → show  ×1
  index → find  ×1
  show → show  ×1
  show → outline  ×1
  where → impact  ×1

most repeated arguments:
  2× show {"handles":["@twtp1"],"fresh":false}
  1× outline {"path":"nope/nope.ts"}
  1× find {"query":"login","limit":20}
  1× where how do we log in
  1× find login

errors:
  1× outline: nothing indexed at 'nope/nope.ts'
  1× impact: sgx impact: needs a target (handle, path#name, name or file) or --diff

sessions:
  session   client           calls  err  find  tok  last    tools
  e4132ffc  mcp/claude-code      4    1     —  232  0s ago  show outline find
  cli:3420  cli                  4    1     1  139  0s ago  where index impact find
  1. 1
    A finding is not a failure. check exits 1 because it found drift, where because there is no semantic layer yet. Those get their own column and stay out of the error rate, which is reserved for misuse, missing preconditions and crashes.
  2. 2
    share is what a tool costs the context. The tool's share of every token sgx returned in the window, next to saved — what the session ledger kept out of the response. It is the fastest way to see which tool is spending the agent's budget.
  3. 3
    --trace prints the calls themselves. Newest first, errors marked ! and findings ~, each carrying the arguments and the message the agent received. --sessions, --since 24h, --tool, --surface mcp|cli and --json filter the same records.
  4. 4
    The same numbers have a screen. /usage in the web UI draws calls over time, latency against tokens returned, what follows what, and a live trace. Retention is 50,000 calls, and --clear forgets everything.

Self-improvement

Then change the instruction that allowed it.

sgx usage cannot see what the agent did instead — an agent that never calls sgx looks perfect there. sgx improve reads the agent's own transcript beside the telemetry, ranks the workarounds by what they cost, and with --apply rewrites the guidance the next session is given.

Displaced, with the price attached

Every grep -rn and whole-file cat the transcript records, joined to the tool that answers the same question and what each one costs a call. The report names the commands, so the accusation is checkable.

Only when the claim holds

A call counts as displaced only when sgx indexes the file and has a tool for the question. A grep outside the repository, a read of something nothing indexed, a | grep over another command's output: all left alone, because a wrong accusation teaches an agent to distrust the report.

--apply writes one generated block

.sgx/instructions.md, regenerated in full each time and capped at 1,200 characters. Two readers pick it up, both already places an agent is told about sgx: the MCP server appends it to its instructions, and sgx hook session-start prints it under the repo map.

Nothing read leaves the machine

Transcripts are read, never written. --no-transcripts runs on telemetry alone, --since 24h|7d narrows the window, and --json emits the ranking.

sgx improvecaptured output
sgx improve · 52 sgx calls · 948 agent tool calls across 10 sessions
361 of them were questions sgx answers, costing ~195k tok of the agent's context

! displaced: the agent read a whole file 196× here, costing ~152k tok (~774 a call) · `show` cost ~1.4k tok a call here
  → route it to `show` / `outline`
    cat docs/telemetry.md && ls src/telemetry/
    git diff --stat && sed -n '1,80p' src/telemetry/record.ts

! displaced: the agent searched with grep 145× here, costing ~36k tok (~251 a call) · `where` cost ~145 tok a call here
  → route it to `where` / `find`

! stale: `where` was called 5× with no semantic layer built (1,532 symbols eligible)
  → `sgx enrich`

· costly: `check` returned 62% of every token sgx gave the agent (~37k tok over 14 calls)
  → tune its defaults, or give it a smaller budget when the agent calls it
.sgx/instructions.mdtext
<!-- generated by `sgx improve --apply` · 2026-09-20 · do not edit -->
Learned in this repository from 10 sessions:

- Do not read a source file whole to find something in it: 196 whole-file reads here cost ~152k tok. `show` / `outline` answer the same question.
- Do not grep this repository: 145 searches here cost ~37k tok. `where` / `find` answer the same question.
- `impact` keeps failing with: impact needs a target (handle, path#name, name or file) or diff=true

The block the next session reads, written from the run above it.

Workspace

Ask one repository, or everything you work on.

Repositories group into projects and projects into workspaces, and --repo, --project, --workspace or --all widens any command across them. Each repository still keeps its own graph and still works entirely on its own — the registry above them is one SQLite file holding no symbols. Delete it and nothing breaks.

Scope is a flag

--repo, --project, --workspace and --all widen a command. With no flag the ordinary single-repository path runs, unchanged. sgx repo scan registers every checkout under a directory.

Federated, not joined

No query crosses SQLite files. find merges by score, hotspots and changes by score and epoch, and context splits the budget by how well each repository probes against the task, so an unrelated one costs a header and nothing more.

Links are package names

A repository publishes the names in its manifests and consumes the imports that resolved to nothing inside it. Matching is exact: @acme/ui matches @acme/ui/button and never @acme/ui-kit. sgx says which repository imports the package, never which symbol resolves where.

Downstream is the point

impact --all adds the repositories a change reaches past its own boundary, and where --all --shared lists the concepts two repositories share — which is the relationship an import graph cannot see.

sgx index --all3 commands
app  indexed 2 of 2 files (+2 ~0 -0) · 2 files · 1 symbols · 0 edges · epoch 1 · 58 ms · history mined
ui   indexed 2 of 2 files (+2 ~0 -0) · 2 files · 1 symbols · 0 edges · epoch 1 · 57 ms · history mined

$ sgx repo link
2 repositories · 2 published packages · 1 cross-repo links

$ sgx workspace show
workspace acme · 1 project · 2 repositories · 4 files · 2 symbols · 0 edges

platform/
  app                       2 files       1 symbols  main           epoch 1
                       publishes @acme/app
  ui                        2 files       1 symbols  main           epoch 1
                       publishes @acme/ui

cross-repo imports
  app → ui  @acme/ui  1 file

Two repositories in one project, with the cross-repo import between them.

Measured

Fast enough for the edit loop, cheap enough to keep asking.

A deterministic corpus of 2,000 source files and 100,000 lines, TypeScript, Python and Go, with cross-file calls and 160 test files. bun run bench --check exits nonzero if any latency reaches twice its target.

522.45 msCold index, 2,000 files
40.64 msNo-op refresh, p50
1.66 msContext pack at 4k, p50
140.20 msPost-edit hook, start to exit
89.0%Outlines against full files, 2,000 files
58.0%A replayed session ledger
bun run bench --checktext
machine: Tims-MacBook-Pro-2.local · Apple M5 Pro · darwin/arm64 · Bun 1.4.2
corpus: 2,000 source files · 100,000 lines · TS 1,600 / Python 200 / Go 200 · 160 TS test files · cross-file calls and classes
timings: core library over one graph handle; cold includes parsing, inserts, resolution and git; hook includes binary startup; no warmup samples discarded
cold index: 522.45 ms · target 4000 ms · check < 8000 ms PASS
graph: 2001 files · 5200 symbols · 5500 edges
no-op refresh: 40.64 ms p50 (n=21) · target 150 ms · check < 300 ms PASS
one-file edit → committed epoch: 28.20 ms p50 (n=21) · target 25 ms · check < 50 ms PASS
find: 0.16 ms p50 (n=21) · target 5 ms · check < 10 ms PASS
outline: 0.05 ms p50 (n=21) · target 5 ms · check < 10 ms PASS
show: 0.03 ms p50 (n=21) · target 5 ms · check < 10 ms PASS
impact depth 3: 0.10 ms p50 (n=21) · target 20 ms · check < 40 ms PASS
context 4k: 1.66 ms p50 (n=21) · target 60 ms · check < 120 ms PASS
full files vs outlines (entire corpus): 3637333 → 398650 bytes · saved 3238683 bytes (89.0%) · estimated tokens 910381 → 97890 (~4 chars/token)
naive target + callers vs context (20 targets, depth 3, 4k budget): 145331 → 150653 bytes · saved -5322 bytes (-3.7%) · estimated tokens 36337 → 37393 (~4 chars/token)
30-call show replay (10 targets, one body edit), without vs with ledger: 18933 → 7945 bytes · saved 10988 bytes (58.0%) · estimated tokens 4749 → 1988 (~4 chars/token)
post-edit hook process start → exit: 140.20 ms p50 (n=9) · target 150 ms · check < 300 ms PASS

Token counts are estimates at roughly four characters per token. The two savings above are bytes on this corpus; the third comparison is not a saving, and the page says so: the 4k context pack is 3.7% larger than naively reading the caller files, because it reaches related context those files do not. CI records these numbers on a shared runner but does not gate on them — the targets are wall-clock on hardware we control.

Boundaries

What holds, what is a judgement, what is not here.

Two things leave the machine and nothing else does: the GitHub sync, and sgx enrich with where --route. Both are explicit, both stop at SGX_OFFLINE=1, and both replay from recorded files in the test suite. The columns below are the same list the boundaries section keeps.

Holds10

  • Graph, analysis and UI are local
  • The UI binds 127.0.0.1 only
  • SGX_OFFLINE=1 stops both network paths
  • No home-directory Claude config is written
  • GitHub tokens are never stored, logged or printed
  • The only header sgx sends is User-Agent: sgx
  • Request paths cannot escape the repository
  • Notes render as text
  • context output is deterministic
  • Tests and the e2e run use recorded fixtures

A judgement5

  • Edges are syntactic, with explicit confidence
  • tests edges are three-hop reachability
  • Concept assignments are one model's opinion
  • Cross-repo links are package-name matches
  • Token counts are ~4-characters estimates

Not in v0.16

  • Type-accurate LSP/SCIP resolution
  • External-package nodes
  • Embeddings
  • Hosted or team servers
  • UI authentication
  • A session-ledger retention policy

Read the boundaries, including where the spec still disagrees with the code

Questions

Before you index anything.

Does anything leave my machine?

Only the GitHub sync and sgx enrich (including where --route). Never during indexing, watching, the post-edit hook, check or context. SGX_OFFLINE=1 stops both and answers GitHub reads from the cache.

Do I need an API key?

Not to index, search, outline, pack context, check or run the UI. enrich and where --route need $SGX_TYPESAFE_API_KEY. GitHub reads take the first of $SGX_GITHUB_TOKEN, $GITHUB_TOKEN, $GH_TOKEN or gh auth token, so if you already use gh there is nothing to configure.

What does it write into my repository?

.sgx/graph.db and .sgx/.gitignore. It does not edit your root .gitignore. --claude merges .mcp.json and .claude/settings.json, adding only missing sgx entries and rejecting any configuration path that resolves outside the repository.

Is this a type-checked call graph?

No. Resolution is syntactic and every edge carries a confidence; inferred relationships print ~. False positives and negatives remain possible. sgx enrich --edges asks the model about the edges the graph already admits it is guessing at, and stores the verdicts by handle so they survive re-indexing.

What happens when the budget runs out?

The strongest seeds get source, the rest get lines, and the footer names what was dropped with its score and the command to expand it. Nothing is silently truncated, and the same inputs pack the same way twice.

Does it work across repositories?

index, find, stats, hotspots, changes, impact, context and ui federate over a project, a workspace or --all. Every other command says it works on one repository at a time rather than quietly answering for one.

What happens when I rename something?

A rename or a move creates a new handle; history belongs to the path it was made under, because git history is mined without following renames. Notes anchored to a symbol go stale after a body edit and say so.

Start

Install it, index something, ask it a question.

From a checkout: bun link registers sgx, and sgx init --claude indexes and wires the agent up. The package is private and installed from source, so there is no registry release to pull.

Use the binary

bun run build produces a standalone dist/sgx with the WASM parsers, workers and the UI embedded. bun link registers sgx against this checkout instead; use the absolute path to dist/sgx to run the compiled binary.

Check it yourself

The commands on the right are the full gate, and CI runs every one of them on push except the benchmark. The end-to-end run drives the compiled binary in temporary fixture copies with its own registry home and the network off.

Install from a checkoutsh
bun install
bun run build                 # standalone binary: dist/sgx, with WASM, workers and UI embedded
bun link                      # registers sgx -> src/cli.ts; requires this checkout and Bun
cd /path/to/your/repository
sgx init --claude              # index and merge repository-local Claude Code configuration
sgx init --codex               # the same, for Codex (.codex/config.toml)
sgx init --fx                  # the same, for fx (.mcp.json and AGENTS.md)
sgx find login
sgx source add https://docs.example.com/api.md --covers 'src/api/**'
sgx ui
Run the whole gatesh
bun run typecheck
bun test
bun run build
bun run e2e
bun run eval:context --min-recall 0.75
bun run bench --check