@polymerix-labs/facts-extract
v0.17.0
Published
Local CLI that walks a repository and emits FactsBundle JSON (symbols, imports, and references as written in source). Installs the native binary for your OS.
Maintainers
Readme
@polymerix-labs/facts-extract
Local CLI that walks a repository and emits FactsBundle JSON.
Walk a repository and write a FactsBundle JSON file — one entry per supported source file. Parsing is local (tree-sitter); nothing is uploaded and nothing is downloaded at runtime.
The native CLI for your platform is installed automatically (same pattern as esbuild).
Install
npm install @polymerix-labs/facts-extractRequires Node.js 16+. Platform binaries are also attached to GitHub Releases.
Languages
Every programming language below is parsed with tree-sitter. Facts are collected as written. Call targets, inheritance graphs, and include graphs are not resolved.
| | Parse | Imports | Symbols | Calls as written | Exports | Linked specifiers |
|---|:---:|:---:|:---:|:---:|:---:|:---:|
| Java .java | ✓ | ✓ | ✓ | ✓ | — | — |
| Kotlin .kt .kts | ✓ | ✓ | ✓ | ✓ | — | — |
| TypeScript .ts .tsx .mts .cts | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| JavaScript .js .jsx .mjs .cjs | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Python .py .pyi | ✓ | ✓ | ✓ | ✓ | — | — |
| Go .go | ✓ | ✓ | ✓ | ✓ | — | — |
| Rust .rs | ✓ | ✓ | ✓ | ✓ | — | — |
| C# .cs | ✓ | ✓ | ✓ | ✓ | — | — |
| C .c .h | ✓ | ✓ | ✓ | ✓ | — | — |
| C++ .cpp .cc .cxx .hpp .hh .hxx | ✓ | ✓ | ✓ | ✓ | — | — |
| Swift .swift | ✓ | ✓ | ✓ | ✓ | — | — |
| Ruby .rb | ✓ | ✓ | ✓ | ✓ | — | — |
| PHP .php | ✓ | ✓ | ✓ | ✓ | — | — |
| Scala .scala | ✓ | ✓ | ✓ | ✓ | — | — |
| Groovy .groovy | ✓ | ✓ | ✓ | ✓ | — | — |
| Lua .lua | ✓ | ✓ | ✓ | ✓ | — | — |
| Elixir .ex .exs | ✓ | ✓ | ✓ | ✓ | — | — |
| Julia .jl | ✓ | ✓ | ✓ | ✓ | — | — |
| Zig .zig | ✓ | ✓ | ✓ | ✓ | — | — |
| Bash .sh .bash | ✓ | ✓ | ✓ | ✓ | — | — |
| PowerShell .ps1 .psm1 | ✓ | ✓ | ✓ | ✓ | — | — |
| SQL .sql | ✓ | — | ✓ | — | — | — |
| MSBuild .csproj .sln | ✓ | ✓ | — | — | — | — |
| Cargo Cargo.toml | ✓ | ✓ | — | — | — | — |
| npm package.json | ✓ | ✓ | — | — | — | — |
What the columns mean
| Column | Meaning |
|--------|---------|
| Parse | Tree-sitter CST for that file |
| Imports | import / use / #include / require / source as written |
| Symbols | Classes, functions, methods, fields, … with names and source positions |
| Calls as written | Call / type / inherit mentions collected, not bound to a target |
| Exports | ES export / re-export (TypeScript and JavaScript only) |
| Linked specifiers | TS/JS import paths rewritten to repo module ids (tsconfig paths, barrels) |
Limits
- C / C++ —
#includepaths are imports. No include graph, no macros, no overload resolution..his C unless the file looks like C++. - Python — relative imports are turned into repo paths at extract time. No
super()MRO. - Go — no embedding promotion.
- Rust — no trait coherence.
- C# — no extension methods.
- SQL — tables / columns / functions as symbols. No invented
CALLresolution. .csproj/.sln/Cargo.toml/package.json— dependency edges only. No symbols, no calls.
Supported platforms
| OS | Arch | Package |
|----|------|---------|
| Linux | x64 | @polymerix-labs/facts-extract-linux-x64 |
| Linux | arm64 | @polymerix-labs/facts-extract-linux-arm64 |
| macOS | x64 (Intel) | @polymerix-labs/facts-extract-darwin-x64 |
| macOS | arm64 (Apple Silicon) | @polymerix-labs/facts-extract-darwin-arm64 |
| Windows | x64 | @polymerix-labs/facts-extract-win32-x64 |
Usage
Resolve the binary path (spawn it yourself)
const { binaryPath } = require("@polymerix-labs/facts-extract");
const { spawnSync } = require("child_process");
const out = spawnSync(binaryPath, ["--repo-root", "/abs/path/to/project"], {
encoding: "utf8",
});
const bundle = JSON.parse(out.stdout); // FactsBundleFull repository
npx @polymerix-labs/facts-extract --repo-root /abs/path/to/project -o bundle.jsonOne file
npx @polymerix-labs/facts-extract --repo-root /abs/path/to/project --file src/Foo.javaStdout is one FactsFileEntry JSON (entry, facts, content_fingerprint).
Subset of files
npx @polymerix-labs/facts-extract --repo-root /abs/path/to/project --files src/A.ts,src/B.tsFlags
| Flag | Effect |
|------|--------|
| --repo-root <PATH> | Absolute path to the repository root (required) |
| --file <REL_PATH> | Extract one repo-relative file as a FactsFileEntry JSON |
| --files <A,B,...> | Extract only these repo-relative files as a partial FactsBundle |
| --list | Walk only: emit the discovered file listing JSON, without parsing |
| --scan | Walk and blake3-hash every file's raw content, without parsing |
| --stream | Full extraction as NDJSON on stdout (header, one FactsFileEntry per file, then an end line) |
| --output, -o <FILE> | Write JSON to a file (default: stdout) |
| --verbose | Real-time logs on stderr |
| --progress | Progress bar on stderr during the parallel parse pass |
Exit codes: 0 success, 1 usage error, 2 repo not found, 3 extraction/serialize error.
Output
The CLI writes a FactsBundle JSON (schema_version, repo_root, and a files array). Each file entry includes:
| Field | What it contains |
|-------|------------------|
| entry | File path, language, extension |
| facts.imports | Import declarations as written in source |
| facts.exports | Export / re-export declarations (JS/TS) |
| facts.symbols | Classes, methods, functions, fields… with names, types, signatures, and source positions |
| facts.refs | Calls, type uses, inheritance, annotations — collected as written, not resolved to targets |
| facts.local_bindings | Local variables and their declared or inferred types |
| facts.has_syntax_errors | Whether the file had parse errors |
The bundle format is versioned (schema_version is currently 4).
Example
Given src/Greeter.java:
package demo;
public class Greeter {
public void hello() {
System.out.println("hi");
}
}facts-extract --file src/Greeter.java writes one file entry (pretty-printed here; the CLI emits compact JSON):
{
"entry": {
"absolute_path": "/abs/path/to/project/src/Greeter.java",
"relative_path": "src/Greeter.java",
"filename": "Greeter.java",
"extension": ".java",
"language": "java"
},
"facts": {
"package": "demo",
"symbols": [
{
"kind": "Class",
"name": "Greeter",
"fqn": "demo.Greeter",
"visibility": "Public",
"modifiers": ["public"],
"decl_span": [15, 105, 3, 0, 7, 1],
"name_span": [28, 35, 3, 13, 3, 20],
"signature": "public class Greeter"
},
{
"kind": "Method",
"name": "hello",
"fqn": "demo.Greeter.hello",
"visibility": "Public",
"modifiers": ["public"],
"return_type": "void",
"parent": 0,
"decl_span": [42, 103, 4, 4, 6, 5],
"name_span": [54, 59, 4, 16, 4, 21],
"signature": "public void hello()"
}
],
"refs": [
{
"kind": "Call",
"name": "println",
"qualifier": "System.out",
"arity": 1,
"arg_types": ["String"],
"from": 1,
"span": [72, 96, 5, 8, 5, 32]
}
]
},
"content_fingerprint": "f09d5fa0dad7d794",
"content_hash": "8e80ece5b5e240b915e8c1583ce99ac8699bec807fe6a3bdcb3d9495407c978e"
}println is recorded as written (qualifier + name). It is not resolved to a target. Spans are [start_byte, end_byte, start_line, start_col, end_line, end_col]. from is the index of the enclosing symbol (hello). A full-repo run wraps entries like this in a FactsBundle: { "schema_version": 4, "repo_root": "...", "files": [ ... ] }.
License
MIT. See LICENSE.
