Codebase intelligence layer

Your codebase, as a graph your agent can read.

sciogen indexes your code once into a queryable knowledge graph — every symbol, every call, resolved to its exact definition. Then a coding agent reads structure instead of raw files, for a fraction of the tokens.

$ pip install sciogen
See how it works
100+languages parsed
0.2sincremental re-index
0LLM calls inside sciogen

The problem

Reading files is the wrong granularity.

To learn one function's callers, an agent pulls thousands of lines of source into its context window and infers the call graph itself. sciogen precomputes that structure, so the same answer is a handful of typed records with exact file:line locations.

Without sciogen

0 tok
Open service.py, app.py, the test file — read every line, guess the call graph.

With sciogen

0 tok
Ask get_callers("login") — get exactly what's affected, where, and how confident.
handle_login · src/app.py:4 · hops 1 · conf 0.4
test_login_flow · tests/test_login.py:9 · TEST_FOR · 1.0

Phase 1 · Indexing

Five stages turn source into a resolved graph.

Every file flows through the same pipeline. Only the normalizer knows languages; everything downstream is language-agnostic — and only changed files are ever re-processed.

01 · parse

Tree-sitter

Language-specific ASTs from 100+ grammars behind a single API.

02 · normalize

Universal IR

Python, JS, Go all become the same typed nodes. The only layer with language knowledge.

03 · resolve

Symbol resolver

Every reference traced to its exact definition, with a confidence score.

04 · build

KuzuDB graph

Typed nodes and edges materialized into an embedded graph store via Cypher.

05 · diff

SHA256 differ

Only changed files re-run. A no-op re-index is instant; one file, under a second.

Phase 2 · Querying

Deterministic queries. Typed answers. Never raw source.

Real output, from sciogen indexing its own source. Structural queries hit the graph; semantic queries hit local vectors; hybrid uses vector hits as seeds for graph expansion.

sciogen — your project
$ sciogen callers resolve_file

6 symbols → resolve_file
hops  conf  via    symbol                     location
   1  0.90  CALLS  Pipeline._link             sciogen/index/pipeline.py:335
   1  0.60  CALLS  edges_of                   tests/test_resolver.py:19
   1  0.60  CALLS  test_extends_resolution    tests/test_resolver.py:124
   1  0.60  CALLS  test_local_call_full…      tests/test_resolver.py:24
   1  0.60  CALLS  test_test_for_naming…      tests/test_resolver.py:136
$ sciogen impact GraphStore --depth 3

5 symbols → GraphStore
hops  conf  via      symbol                       location
   1  0.90  CALLS    SciogenGraph.graph_store     sciogen/api.py:40
   1  0.90  MUTATES  GraphStore.__init__          sciogen/stores/graph.py:76
   2  0.90  CALLS    SciogenGraph.snapshot        sciogen/api.py:121
   3  0.40  CALLS    explore                      sciogen/cli/main.py:262
   3  0.40  CALLS    test_explore_html_generation tests/test_pipeline…
$ sciogen search "deduplicate embeddings by content hash"

score  kind          symbol                   location
 0.71  FunctionNode  Pipeline._embed          sciogen/index/pipeline.py:210
 0.66  FunctionNode  MetaStore.embedding_hashes sciogen/stores/meta.py:243
 0.58  ChunkNode     _embed · part 2          sciogen/index/pipeline.py:216
 0.52  FunctionNode  HashingEmbedder.embed    sciogen/embed/hashing.py:34
$ sciogen index .

 Scanning project structure...
 Parsing Python...
 Building knowledge graph...
 Embedding 380 symbols...
 Linking dependencies...
✓ Done! 521 nodes · 1,217 edges · indexed in 25.4s
  Ready. Your codebase is now queryable.

$ sciogen index .   # nothing changed
✓ Done! 521 nodes · 1,217 edges · indexed in 0.2s

The honest part

Every edge carries a confidence score.

Static analysis is incomplete, not wrong. A call through dynamic dispatch can't be proven — so sciogen keeps the edge and scores it low, instead of dropping it and faking a clean graph. You set the threshold at query time.

1.0Direct & local. A call in the same file, a sibling method, an import resolved to an indexed file.
0.8–0.9Resolved via import. Through a module alias or an imported class, to a definition sciogen indexed.
0.2–0.5Dynamic or external. A method on an unknown receiver, or a target outside the index — kept, never dropped.

Path confidence

On a multi-hop traversal, a chain is only as trustworthy as its weakest link. sciogen reports the minimum edge confidence along the best path — so a two-hop caller through one shaky edge is scored honestly, not averaged into false certainty.

min(0.9, 0.4, 1.0) → 0.4

Two ways it's used

You read it in the terminal. Your agent reads it over MCP.

The graph lives in a .sciogen/ directory inside the repo. Nobody reads those binary stores directly — the query layer turns a question into a small typed answer.

You · CLI

Index & explore

Build the graph, then open it as an interactive browser GUI — collapsed to files, expanding into classes, functions, and call edges on click.

$ sciogen index . $ sciogen explore # opens the graph in your browser
Your agent · MCP

Query, don't crawl

Eight tools over the Model Context Protocol. The agent calls analyze_impact or get_context_for_task and gets typed records — then opens only the files it points to.

{ "mcpServers": { "sciogen": { "command": "sciogen", "args": ["mcp", "."] } } }
KuzuDB
graph topology

Nodes, typed edges, and confidence scores. One embedded graph, queried with Cypher — no server.

ChromaDB
vector search

Embeddings at four granularities — file, class, function, chunk — of normalized summaries, never raw code.

SQLite
operational metadata

File hashes, the symbol table, and reference records that make incremental re-resolution possible.

Index once. Query forever.

One command builds the graph. Everything after is a sub-second lookup — for you, and for the agent working beside you.

$ pip install sciogen
Star on GitHub
Copied to clipboard