Python CLI for AI agents. Queries SCIP indexes for precise refs, definitions, and repo health analysis — TypeScript/JavaScript, Python, Go, Rust.
AI agents waste tokens on grep and file scanning. scip-cli gives them precise, type-aware code navigation in milliseconds — and analyze surfaces dead code, cycles, and coupling so agents (and humans) can fix real problems fast.
- Agent-first: Install as a skill for Claude Code, Cursor, or any AI agent — precise code navigation without burning context
- Token-efficient: One record per line, stderr for warnings, pipe-friendly output
- Fast: Direct SQLite queries — 10x to 213x faster than alternatives
analyze: Find dead exports, import cycles, stale types, coupling hotspots — actionable health dashboards at project, file, or symbol scope- Auto-indexing: Indexes on first query, caches in SQLite, zero config
Install as a reusable skill so your agent always knows how to navigate the codebase:
scip-cli skill ~/.claude/skills/scip-cli/ # Claude Code
scip-cli skill ~/.cursor/skills/scip-cli/ # CursorOr dump the quick reference for one-off use:
scip-cli skillFrom PyPI (end users):
pip install scip-cliLocal development — almost always keep bare scip-cli mapped to this checkout via editable install. You edit the repo; the CLI you run is live code, not a frozen PyPI copy.
cd scip-cli
pip install -e ".[dev]"
scip-cli --versionAll docs and examples use scip-cli (never .venv/bin/scip-cli or python -m scip_cli). After publishing, briefly pip install scip-cli to smoke-test PyPI, then pip install -e ".[dev]" again.
Optional venv for isolation — activate it, run the same editable install, still invoke scip-cli on PATH:
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"Tests and lint: pytest, ruff check ., basedpyright scip_cli/ from the same environment (or scripts/test.sh for the publish gate).
On first index, scip-cli runs language indexers and builds a SQLite cache. You can let it fetch tools on demand, or install them globally ahead of time so the first run does not download via npx:
Option A: Zero extra setup (recommended)
Install scip-cli and run it. On first index, scip-cli will:
- Download
scip-typescript/scip-pythonvianpx,scip-goviago install, orrust-analyzerviarustupwhen not already on PATH - Download the
scipconverter binary from GitHub releases into~/.cache/scip-cli/bin/when not already on PATH - Walk the repo for
tsconfig*.jsonproject roots (TypeScript monorepos), runscip-typescriptper project (parallel by default), convert each partial index, then merge into oneindex.db
No .scip-cli.json required for discovery. Subsequent queries read the cached database only.
Option B: Install indexers globally ahead of time
Same indexing steps as Option A; this only avoids npx download on the first run:
# TypeScript/JavaScript indexer (also handles plain JS via --infer-tsconfig)
npm install -g @sourcegraph/scip-typescript
# Python indexer
npm install -g @sourcegraph/scip-python
# Go indexer
go install github.com/scip-code/scip-go/cmd/scip-go@latest
# Rust indexer (rust-analyzer with SCIP support)
rustup component add rust-analyzer
# SCIP CLI for index conversion (GitHub release — not on npm)
# https://github.com/scip-code/scip/releases (v0.8.1+ recommended)Verify installation:
scip-cli --help
scip-typescript --version # Only if you chose Option B
scip --version # Install from GitHub releases; v0.8.1+ recommendedAll commands are subcommands of scip-cli:
scip-cli <command> [arguments]refs <symbol>- Find all references to a symbol (--pathto scope)code <symbol>- Find symbol definition with source code (--path,--max-lines,--full,--offset,--snippet,--line-numbers)search <pattern>- Search symbols by name pattern (--path)symbols <file>- List all symbols in a file (--path; bare filename OK)rdeps <file>- Find files that depend on a file (--path)deps <symbol|file>- Find outbound dependencies (what a symbol or file calls) (--path,--paths-only)members <symbol>- List members of a class/interface (--path)analyze [target]- SQL health dashboards (--limit,--priority,--include-tests). No target: project-wide; directory or file path; symbol name. See Finding easy wins withanalyze.reindex- Force re-indexing of the current project (--pathto limit scope; repeatable)skill [path]- Install or dump the SKILL.md
# Find where greet is used
scip-cli refs greet
# Get definition of greet
scip-cli code greet
# Search for symbols matching "Widget"
scip-cli search Widget
# Scope to a subdirectory
scip-cli code greet --path packages/api
# List symbols by bare filename
scip-cli symbols helper.ts
# Find files that import from a module
scip-cli rdeps src/helper.ts
# Find what a function calls
scip-cli deps greet
# Find what a file depends on
scip-cli deps src/helper.ts --paths-only
# List members of a class
scip-cli members Widget
# Project health dashboard (or: scip-cli analyze src/foo.ts / scip-cli analyze greet)
scip-cli analyze
# Install skill file
scip-cli skill ~/.claude/skills/scip-cli/SKILL.mdStdout is one record per line; stderr carries warnings and ambiguity notices. Kinds are lowercase (function, class, method, property). Pipe-friendly flags: refs --paths-only, search --names-only / --paths-only, members --names-only, deps --paths-only. rdeps already prints bare file paths.
# What do importers of this file export?
scip-cli rdeps src/helper.ts | xargs -I{} scip-cli symbols {}
# Which files reference a symbol?
scip-cli refs greet --paths-only
# Classes matching a name → list their members
scip-cli search Handler --kind class --names-only | xargs -I{} scip-cli members {}
# Walk class members to their definitions
scip-cli members Widget --names-only | xargs -I{} scip-cli code Widget.{}
# Find all files that a symbol depends on (outbound dependencies)
scip-cli deps greet --paths-only | sort -u- On first query, automatically detects project language from
package.json(TS/JS),pyproject.toml/setup.py(Python),go.mod(Go), orCargo.toml(Rust) - For TypeScript monorepos, walks the repository for
tsconfig*.jsonproject roots (nested ancestors deduped; root included only when itsincludeis broad) - Runs
scip-typescriptper project (in parallel when there are multiple projects; setSCIP_CLI_INDEX_WORKERS=1to force serial),scip-pythonfor Python,scip-gofor Go, orrust-analyzer scipfor Rust - Converts each SCIP output to SQLite with
scip expt-convert, then merges partial databases when needed - Caches the result in
~/.cache/scip-cli/projects/<dirname>-<hash>/index.db(e.g.my-monorepo-1a3f7a) - Subsequent queries are SQLite lookups against that cache (not re-indexing)
Optional .scip-cli.json in the project root:
{
"maxHeapMb": 8192,
"indexRoots": ["packages/core", "apps/worker"],
"onlyIndexRoots": false
}maxHeapMb— Node heap forscip-typescript/scip-python(default 8192 MB when omitted). Overridden bySCIP_CLI_MAX_HEAP_MB. This is the V8 heap cap, not total RAM usage. Does not affectscip-go.indexRoots— extra TypeScript project directories to include on first index, merged with auto-discovered projects.onlyIndexRoots— skip auto-discovery and index onlyindexRoots(smaller initial index when you only care about part of a monorepo).
SCIP_CLI_INDEX_WORKERS controls parallel indexer runs during first index (default: up to 8). Merge into one database is always serial.
Other environment variables:
| Variable | Purpose |
|---|---|
SCIP_CLI_MAX_HEAP_MB |
Node heap for scip-typescript / scip-python (overrides maxHeapMb in config) |
SCIP_CLI_TS_INDEX_BATCH_SIZE |
Split large TS repos into multiple scip-typescript runs (default: all tsconfigs in one run) |
SCIP_CLI_MERGE_BATCH_SIZE |
SQLite ATTACH batch size when merging part DBs (max 9) |
SCIP_CLI_MAX_DEF_LINES |
Max definition lines in code output |
SCIP_CLI_DEBUG |
Log SQL queries to stderr |
Version policy: only the scip converter (expt-convert) is pinned to the 0.8.x release line because it defines the SQLite schema. Language indexers (scip-typescript, scip-python via npx; scip-go via go install @latest) install at latest on first use. rust-analyzer installs via rustup component add.
Large monorepos (>10 tsconfig projects) log per-project progress to stderr during indexing; smaller repos stay quiet aside from the final Indexed … (size) line.
Scoped indexing without editing .scip-cli.json:
scip-cli reindex --path packages/server
scip-cli reindex --path packages/api --path packages/worker--path limits which discovered tsconfig projects are indexed (prefix match, same idea as query --path). TypeScript only — other languages reject reindex --path. The scope is saved as index-scope.json next to index.db and reused until you run a full scip-cli reindex with no --path.
Run scip-cli reindex after changing scope, .scip-cli.json index settings, or when you want a fresh index.
Use analyze on the repo itself before broad refactors or agent review — it surfaces cross-file issues from the SCIP index (not Python vulture).
Quick pass (after scip-cli reindex):
scip-cli analyze --limit 25
scip-cli analyze --priority high --limit 25 # dead exports & cycles onlySections are tagged [high], [medium], [low] and listed in that order.
| Tier | Project sections | Action |
|---|---|---|
| high | Cycles, unreferenced, dead exports, stale types | Nuke or fix cycles; delete unused; _ prefix |
| medium | Same-file only, change surface (file target) | Module-private by usage |
| low | Test-only consumers, coupling, bottlenecks, hotspots | Noisy on Python (index omits many same-file calls); verify with rg |
Use --priority high for a quick gate; --priority high,medium adds context. File drill-down adds change surface and unused imports.
What to look at first
| Section | Easy pickings |
|---|---|
| Cycles | Import/mention cycles between production files — break the edge or extract shared code |
| Unreferenced | No usage in the index at all — delete |
| Dead exports | No external refs — delete or _ prefix |
| Stale types | Classes/types with no external consumer in the index — verify in-file or type-only use before removing |
| Same-file only | Used only inside defining file — rename to _ |
| Test-only consumers | Cross-file refs are all from tests — promote to e2e or accept as internal |
Per-file or package drill-down on hubs or suspects:
scip-cli analyze scip_cli/queries.py --limit 20 # file: scoped project + per-file + top symbols
scip-cli analyze scip_cli --limit 15 # directory: scoped project + each file under itDead exports in file lists same-module symbols with no external refs — module-private _helpers are filtered out. Remaining rows are worth a manual rg check.
Defaults: project-wide and directory analyze skip tests/, *.test.*, *.spec.*, conftest.py, and __tests__/. Pass --include-tests to include them. File-target analyze always includes that file.
Limits: "Dead export" means no cross-file mentions in the index — not unreachable code. Same-file private helpers are expected. Re-run reindex after large changes; the index is a snapshot.
Inspired by scip-query, scip-cli is a lightweight Python partial reimplementation optimized for speed. Compared to the original:
refs: 6.4s → 0.03s (213x faster)code: 2.8s → 0.05s (56x faster)search: 2.6s → 0.03s (87x faster)symbols: 0.3s → 0.02s (15x faster)rdeps: 0.2s → 0.02s (10x faster)members: 3.1s → 0.03s (103x faster)
The speedup comes from using optimized direct SQLite queries and cutting some nice but very slow goodies (like ts-morph).
scip_cli/
├── __init__.py
├── __main__.py # CLI entry point
├── cli_args.py # Shared argparse helpers
├── config.py # .scip-cli.json loader
├── discover.py # TypeScript project discovery
├── merge.py # SQLite index merging
├── scip_tool.py # scip binary download
├── sql.py # SQLite helpers
├── paths.py # --path scope filtering
├── project.py # Project root + language detection
├── cache.py # Index cache paths
├── scope.py # Persisted reindex scope (index-scope.json)
├── debug.py # SCIP_CLI_DEBUG stderr helpers
├── indexing.py # SCIP index build + get_db
├── symbols.py # Symbol parsing and kinds
├── queries.py # Symbol/file SQL queries
├── source.py # Filesystem source reads
├── output.py # CLI formatting helpers
├── session.py # setup() and single-match resolution
├── targets.py # file-path heuristics (tests; analyze uses analyze/targets.py)
├── analyze/ # SQL dashboard queries (project/file/symbol)
└── commands/ # Subcommand implementations
pip install -e ".[dev]"
pytest tests/ -q
pytest tests/ -m integration -q # indexes tests/fixtures/typescript-project (needs scip-typescript)Set SCIP_CLI_DEBUG=1 to enable SQL query logging to stderr (statements truncated to 200 chars):
SCIP_CLI_DEBUG=1 scip-cli refs MyFunction
# Shows: SQL: SELECT ... | params: (...)This is useful for testing and debugging SQL queries
Contributions are welcome! See CONTRIBUTING.md for guidelines. By participating, you agree to the Code of Conduct.
To report a security vulnerability, see SECURITY.md.
MIT