Skip to content

flesler/scip-cli

Repository files navigation

scip-cli

PyPI version License: MIT

Python CLI for AI agents. Queries SCIP indexes for precise refs, definitions, and repo health analysis — TypeScript/JavaScript, Python, Go, Rust.

Why

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.

Features

  • 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

Ports

CLI/output parity ports of this project (Python is the reference):

Language Repository
Python (reference) flesler/scip-cli
Go flesler/scip-cli-go
Rust flesler/scip-cli-rust
Zig flesler/scip-cli-zig

For AI Agents

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/   # Cursor

Or dump the quick reference for one-off use:

scip-cli skill

Installation

1. Install scip-cli

From PyPI (end users):

pip install scip-cli

Local 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 --version

All 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).

2. Install prerequisites (optional)

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-python via npx, scip-go via go install, or rust-analyzer via rustup when not already on PATH
  • Download the scip converter binary from GitHub releases into ~/.cache/scip-cli/bin/ when not already on PATH
  • Walk the repo for tsconfig*.json project roots (TypeScript monorepos), run scip-typescript per project (parallel by default), convert each partial index, then merge into one index.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+ recommended

Usage

All commands are subcommands of scip-cli:

scip-cli <command> [arguments]

Commands

  • refs <symbol> - Find all references to a symbol (--path to 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 with analyze.
  • reindex - Force re-indexing of the current project (--path to limit scope; repeatable)
  • skill [path] - Install or dump the SKILL.md

Examples

# 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.md

Pipelines

Stdout 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

How It Works

  1. On first query, automatically detects project language from package.json (TS/JS), pyproject.toml/setup.py (Python), go.mod (Go), or Cargo.toml (Rust)
  2. For TypeScript monorepos, walks the repository for tsconfig*.json project roots (nested ancestors deduped; root included only when its include is broad)
  3. Runs scip-typescript per project (in parallel when there are multiple projects; set SCIP_CLI_INDEX_WORKERS=1 to force serial), scip-python for Python, scip-go for Go, or rust-analyzer scip for Rust
  4. Converts each SCIP output to SQLite with scip expt-convert, then merges partial databases when needed
  5. Caches the result in ~/.cache/scip-cli/projects/<dirname>-<hash>/index.db (e.g. my-monorepo-1a3f7a)
  6. Subsequent queries are SQLite lookups against that cache (not re-indexing)

Configuration

Optional .scip-cli.json in the project root:

{
  "maxHeapMb": 8192,
  "indexRoots": ["packages/core", "apps/worker"],
  "onlyIndexRoots": false
}
  • maxHeapMb — Node heap for scip-typescript / scip-python (default 8192 MB when omitted). Overridden by SCIP_CLI_MAX_HEAP_MB. This is the V8 heap cap, not total RAM usage. Does not affect scip-go.
  • indexRoots — extra TypeScript project directories to include on first index, merged with auto-discovered projects.
  • onlyIndexRoots — skip auto-discovery and index only indexRoots (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.

Finding easy wins with analyze

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 only

Sections 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 it

Dead 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.

Performance

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).

Architecture

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

Development

pip install -e ".[dev]"
pytest tests/ -q
pytest tests/ -m integration -q   # indexes tests/fixtures/typescript-project (needs scip-typescript)

Debug Logging

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

Contributing

Contributions are welcome! See CONTRIBUTING.md for guidelines. By participating, you agree to the Code of Conduct.

Security

To report a security vulnerability, see SECURITY.md.

License

MIT

About

Python CLI for AI agents. Queries SCIP indexes for precise refs, definitions, and repo health analysis.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Packages

 
 
 

Contributors