scip-cli 1.2.0__tar.gz → 2.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. scip_cli-2.0.0/.cursor/rules/agent.mdc +133 -0
  2. scip_cli-2.0.0/.gitignore +53 -0
  3. scip_cli-2.0.0/.pre-commit-config.yaml +15 -0
  4. scip_cli-2.0.0/PKG-INFO +330 -0
  5. scip_cli-2.0.0/README.md +310 -0
  6. scip_cli-2.0.0/pyproject.toml +59 -0
  7. scip_cli-2.0.0/scip_cli/SKILL.md +135 -0
  8. {scip_cli-1.2.0 → scip_cli-2.0.0}/scip_cli/__init__.py +2 -1
  9. scip_cli-2.0.0/scip_cli/__main__.py +180 -0
  10. scip_cli-2.0.0/scip_cli/analyze/__init__.py +1 -0
  11. scip_cli-2.0.0/scip_cli/analyze/common.py +110 -0
  12. scip_cli-2.0.0/scip_cli/analyze/file.py +354 -0
  13. scip_cli-2.0.0/scip_cli/analyze/project.py +435 -0
  14. scip_cli-2.0.0/scip_cli/analyze/sections.py +77 -0
  15. scip_cli-2.0.0/scip_cli/analyze/symbol.py +184 -0
  16. scip_cli-2.0.0/scip_cli/analyze/targets.py +97 -0
  17. scip_cli-2.0.0/scip_cli/cache.py +93 -0
  18. {scip_cli-1.2.0 → scip_cli-2.0.0}/scip_cli/cli_args.py +26 -1
  19. scip_cli-2.0.0/scip_cli/commands/analyze.py +142 -0
  20. scip_cli-2.0.0/scip_cli/commands/code.py +123 -0
  21. {scip_cli-1.2.0 → scip_cli-2.0.0}/scip_cli/commands/members.py +13 -14
  22. {scip_cli-1.2.0 → scip_cli-2.0.0}/scip_cli/commands/rdeps.py +5 -13
  23. scip_cli-2.0.0/scip_cli/commands/refs.py +166 -0
  24. scip_cli-2.0.0/scip_cli/commands/reindex.py +69 -0
  25. {scip_cli-1.2.0 → scip_cli-2.0.0}/scip_cli/commands/search.py +99 -58
  26. {scip_cli-1.2.0 → scip_cli-2.0.0}/scip_cli/commands/skill.py +7 -3
  27. {scip_cli-1.2.0 → scip_cli-2.0.0}/scip_cli/commands/symbols.py +4 -6
  28. {scip_cli-1.2.0 → scip_cli-2.0.0}/scip_cli/config.py +3 -3
  29. scip_cli-2.0.0/scip_cli/debug.py +10 -0
  30. {scip_cli-1.2.0 → scip_cli-2.0.0}/scip_cli/discover.py +14 -21
  31. scip_cli-2.0.0/scip_cli/indexing.py +520 -0
  32. scip_cli-2.0.0/scip_cli/merge.py +108 -0
  33. {scip_cli-1.2.0 → scip_cli-2.0.0}/scip_cli/output.py +56 -26
  34. {scip_cli-1.2.0 → scip_cli-2.0.0}/scip_cli/paths.py +37 -3
  35. scip_cli-2.0.0/scip_cli/project.py +42 -0
  36. {scip_cli-1.2.0 → scip_cli-2.0.0}/scip_cli/queries.py +86 -75
  37. {scip_cli-1.2.0 → scip_cli-2.0.0}/scip_cli/scip_tool.py +25 -10
  38. scip_cli-2.0.0/scip_cli/scope.py +69 -0
  39. {scip_cli-1.2.0 → scip_cli-2.0.0}/scip_cli/session.py +2 -1
  40. {scip_cli-1.2.0 → scip_cli-2.0.0}/scip_cli/source.py +12 -3
  41. {scip_cli-1.2.0 → scip_cli-2.0.0}/scip_cli/sql.py +7 -4
  42. {scip_cli-1.2.0 → scip_cli-2.0.0}/scip_cli/symbols.py +39 -12
  43. scip_cli-2.0.0/scip_cli/targets.py +17 -0
  44. scip_cli-2.0.0/scripts/build.sh +13 -0
  45. scip_cli-2.0.0/scripts/publish.sh +32 -0
  46. scip_cli-2.0.0/scripts/test.sh +9 -0
  47. scip_cli-2.0.0/tests/analyze_db.py +204 -0
  48. scip_cli-2.0.0/tests/conftest.py +25 -0
  49. scip_cli-2.0.0/tests/e2e_harness.py +101 -0
  50. scip_cli-2.0.0/tests/fixture_catalog.py +17 -0
  51. scip_cli-2.0.0/tests/fixtures/sample-project/package.json +5 -0
  52. scip_cli-2.0.0/tests/fixtures/sample-project/src/app/handler.ts +3 -0
  53. scip_cli-2.0.0/tests/fixtures/sample-project/src/config.ts +1 -0
  54. scip_cli-2.0.0/tests/fixtures/sample-project/src/consumer.ts +4 -0
  55. scip_cli-2.0.0/tests/fixtures/sample-project/src/dead.ts +1 -0
  56. scip_cli-2.0.0/tests/fixtures/sample-project/src/helper.ts +7 -0
  57. scip_cli-2.0.0/tests/fixtures/sample-project/src/index.ts +3 -0
  58. scip_cli-2.0.0/tests/fixtures/sample-project/src/user.ts +6 -0
  59. scip_cli-2.0.0/tests/fixtures/sample-project/src/widget.ts +7 -0
  60. scip_cli-2.0.0/tests/fixtures/sample-project/tsconfig.json +8 -0
  61. scip_cli-2.0.0/tests/test_analyze.py +223 -0
  62. scip_cli-2.0.0/tests/test_cache.py +54 -0
  63. {scip_cli-1.2.0 → scip_cli-2.0.0}/tests/test_composability.py +44 -49
  64. {scip_cli-1.2.0 → scip_cli-2.0.0}/tests/test_config.py +1 -0
  65. {scip_cli-1.2.0 → scip_cli-2.0.0}/tests/test_discover.py +1 -0
  66. scip_cli-2.0.0/tests/test_e2e.py +174 -0
  67. scip_cli-2.0.0/tests/test_index_prune.py +122 -0
  68. {scip_cli-1.2.0 → scip_cli-2.0.0}/tests/test_indexer_env.py +2 -1
  69. scip_cli-2.0.0/tests/test_merge.py +227 -0
  70. scip_cli-2.0.0/tests/test_multi_symbol.py +205 -0
  71. {scip_cli-1.2.0 → scip_cli-2.0.0}/tests/test_pure_functions.py +294 -266
  72. scip_cli-2.0.0/tests/test_qualified_symbols.py +100 -0
  73. scip_cli-2.0.0/tests/test_reindex.py +53 -0
  74. {scip_cli-1.2.0 → scip_cli-2.0.0}/tests/test_scip_tool.py +1 -0
  75. scip_cli-2.0.0/tests/test_scope.py +62 -0
  76. scip_cli-2.0.0/tests/test_targets.py +17 -0
  77. {scip_cli-1.2.0 → scip_cli-2.0.0}/tests/test_typescript_projects.py +1 -0
  78. scip_cli-1.2.0/MANIFEST.in +0 -4
  79. scip_cli-1.2.0/PKG-INFO +0 -267
  80. scip_cli-1.2.0/README.md +0 -243
  81. scip_cli-1.2.0/pyproject.toml +0 -13
  82. scip_cli-1.2.0/scip_cli/SKILL.md +0 -108
  83. scip_cli-1.2.0/scip_cli/__main__.py +0 -127
  84. scip_cli-1.2.0/scip_cli/cache.py +0 -34
  85. scip_cli-1.2.0/scip_cli/commands/def_cmd.py +0 -57
  86. scip_cli-1.2.0/scip_cli/commands/refs.py +0 -136
  87. scip_cli-1.2.0/scip_cli/commands/reindex.py +0 -28
  88. scip_cli-1.2.0/scip_cli/constants.py +0 -7
  89. scip_cli-1.2.0/scip_cli/indexing.py +0 -292
  90. scip_cli-1.2.0/scip_cli/lib.py +0 -64
  91. scip_cli-1.2.0/scip_cli/merge.py +0 -174
  92. scip_cli-1.2.0/scip_cli/project.py +0 -29
  93. scip_cli-1.2.0/scip_cli.egg-info/PKG-INFO +0 -267
  94. scip_cli-1.2.0/scip_cli.egg-info/SOURCES.txt +0 -49
  95. scip_cli-1.2.0/scip_cli.egg-info/entry_points.txt +0 -2
  96. scip_cli-1.2.0/scip_cli.egg-info/top_level.txt +0 -1
  97. scip_cli-1.2.0/setup.cfg +0 -4
  98. scip_cli-1.2.0/setup.py +0 -37
  99. scip_cli-1.2.0/tests/test_cache.py +0 -22
  100. scip_cli-1.2.0/tests/test_merge.py +0 -136
  101. scip_cli-1.2.0/tests/test_smoke_cli.py +0 -157
  102. {scip_cli-1.2.0 → scip_cli-2.0.0}/LICENSE +0 -0
  103. {scip_cli-1.2.0 → scip_cli-2.0.0}/scip_cli/commands/__init__.py +0 -0
  104. /scip_cli-1.2.0/scip_cli.egg-info/dependency_links.txt → /scip_cli-2.0.0/tests/__init__.py +0 -0
@@ -0,0 +1,133 @@
1
+ ---
2
+ description: scip-cli dev workflow and TDD with the shared fixture
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # scip-cli agent
7
+
8
+ **Work from repo root.** CLI usage, flags, and user gotchas: `scip-cli skill` (source: `scip_cli/SKILL.md`). Read once per task — do not duplicate SKILL here.
9
+
10
+ Use **`.venv/bin/scip-cli`** / **`.venv/bin/pytest`**. Uncommitted code: `PYTHONPATH=$PWD python -m scip_cli …` if the venv binary is stale.
11
+
12
+ ## Bootstrap
13
+
14
+ ```bash
15
+ python -m venv .venv && source .venv/bin/activate
16
+ pip install -e ".[dev]"
17
+ pre-commit install
18
+ scip-cli skill
19
+ ```
20
+
21
+ Node.js + `npx` required for integration tests (`scip-typescript`). Optional: `SCIP_CLI_DEBUG=1` (SQL to stderr, truncated).
22
+
23
+ ## Lint / test
24
+
25
+ | Task | Command |
26
+ |------|---------|
27
+ | Full suite | `.venv/bin/pytest` |
28
+ | E2e loop | `.venv/bin/pytest tests/test_e2e.py` |
29
+ | Lint / format | `.venv/bin/ruff check .` / `ruff format .` |
30
+
31
+ Pre-commit: ruff + ruff-format + full pytest. E2e before commit on command changes.
32
+
33
+ ## Improving this repo (do this before review subagents)
34
+
35
+ **Dogfood on the repo itself** — faster and more honest than spawning reviewers:
36
+
37
+ ```bash
38
+ .venv/bin/scip-cli reindex
39
+ .venv/bin/scip-cli analyze --limit 25
40
+ .venv/bin/scip-cli analyze --priority high --limit 25 # dead code & cycles only
41
+ .venv/bin/scip-cli analyze scip_cli --limit 15 # directory
42
+ .venv/bin/scip-cli analyze scip_cli/queries.py --limit 20 # file (+ scoped project)
43
+ ```
44
+
45
+ Interpretation: README § *Finding easy wins with analyze*. Target = omit (project), directory, file, or symbol — not `--path` for scope.
46
+
47
+ | Priority | Section | Action |
48
+ |----------|---------|--------|
49
+ | 1 | **Cycles** | Break production file dependency cycles (`cache`↔`scope` was one) |
50
+ | 2 | **Unreferenced / dead exports** | No ref usage / no external refs — verify with `rg`, delete or `_` prefix |
51
+ | 3 | **Same-file only** | Module-private by usage — rename to `_` |
52
+ | 4 | **Stale types** | Low-consumer classes — merge or justify |
53
+
54
+ `analyze` skips test paths by default (`tests/`, `*.test.*`, `*.spec.*`); `--include-tests` to include. Reindex after large edits.
55
+
56
+ Do **not** spawn broad code-review agents until this pass is done (or explain why skipped).
57
+
58
+ ## Analyze feedback loop (mandatory)
59
+
60
+ When you or a review subagent finds a repo issue — bug, dead code, cycle, risky hub, stale type, unused import — **stop and ask first:**
61
+
62
+ > *Could our SQLite index have predicted this, and `analyze` just doesn't surface it yet?*
63
+
64
+ If **yes** (the signal is in `documents`, `mentions`, `global_symbols`, `defn_enclosing_ranges`):
65
+
66
+ 1. **Extend `analyze` first** — new or tuned SQL check in `scip_cli/analyze/`, priority in `sections.py`, tests in `tests/test_analyze.py` + `analyze_db.py`.
67
+ 2. **Dogfood** — `reindex` → confirm the issue appears (or the false positive is filtered).
68
+ 3. **Then** fix the production code (or document a known blind spot).
69
+
70
+ Only skip step 1 when the issue is **outside the index** (runtime, CLI UX, docs drift, git, env, concurrency, test harness).
71
+
72
+ ### Retrospective: caught manually, SQLite already had it
73
+
74
+ | Issue we hit | Index signal | Analyze today | Gap / next check |
75
+ |--------------|--------------|---------------|------------------|
76
+ | `cache` ↔ `scope` import cycle | cross-file `mentions` edges | `[high] Cycles` | **Caught** — keep dogfooding cycles |
77
+ | Cross-file dead export (`get_refs_for_symbols`) | def with no external `mentions` | `[high] Dead exports` | **Caught** |
78
+ | Same-file-only dead code | def, `role=0` only in def file | `[high] Unreferenced` or `[medium] Same-file only` | **Caught** (since extended) |
79
+ | Same-file helper without `_` (`run_with_fallback`) | same-file `role=0` or none | `[medium] Same-file only` / unreferenced | **Caught** — rename to `_` |
80
+ | Typing-only dataclasses (`ProjectSettings`, `IndexScope`) | type `#` symbol, 0 consumer files | `[high] Stale types` + `stale_type_noise` | **Filtered** — index can't see annotations; document or optional `--types` |
81
+ | `analyze/*` section runners “dead” | no external refs | noise filter in `common.py` | **Filtered** — framework pattern |
82
+ | Unused import | `role=2` mention, no in-file use | `[high] Unused imports` (per file) | **Caught** on file target |
83
+ | Hub before editing (`resolve_symbol`, `queries.py`) | fan-in / consumer count | `[medium] Change surface` / `[low] Hotspots` | **Caught** on file target; project hubs are low tier |
84
+ | Symbol only used from tests | consumers all under `tests/` | `[low] Test-only consumers` | **Partial** — Python index often omits same-file `role=0`; verify with `rg` |
85
+ | Indexer omits same-file Python calls | no `role=0` for in-module use | unreferenced / test-only false positives | **Index gap** — not fixable in SQL alone; demoted test-only to low |
86
+ | Duplicate / parallel helpers | — | — | **Not in index** — `rg`, not analyze |
87
+
88
+ Add new rows to `dogfood.md` when you find another gap; implement the SQL check before the next manual review pass.
89
+
90
+ ### Size / complexity checks (assessed — don’t promote to high/medium)
91
+
92
+ SQLite has **line ranges** (`defn_enclosing_ranges`) so LOC is cheap. That does **not** mean “big function” belongs next to dead exports.
93
+
94
+ | Idea | In index? | Verdict |
95
+ |------|-----------|---------|
96
+ | **LOC ≥ N** alone | Yes | **LOW at best** — noisy (`main`, argparse, legitimate parsers). Not a nuke candidate. |
97
+ | **LOC + fan-in** (large hub) | Yes | **Already covered** by `[low] Bottlenecks` (fan-in × fan-out). Add `loc=` to that output if helpful; don’t duplicate as medium. |
98
+ | **LOC + consumers** on file target | Yes | **Change surface** (medium) already sorts by consumers with line ranges — drill there. |
99
+ | **Symbol pressure** | Yes | **Per-symbol** `analyze Foo` shows `loc × fan_in × fan_out`. |
100
+ | **God class / many members** | Yes | Use `members Class` — not analyze. |
101
+ | **File byte size / cyclomatic complexity** | No | Out of scope for SCIP index. |
102
+ | **Fat file (many symbols)** | Yes | Low value; coupling/hotspots approximate module heat. |
103
+
104
+ **Rule:** size-only checks stay **low or file-drill-down**; never high/medium unless paired with a **structural smell** (cycle, dead export, zero production consumers). Prefer enriching existing low-tier lines (e.g. show `loc` on bottlenecks) over a new dashboard section.
105
+
106
+ ## TDD
107
+
108
+ 1. **Red** — `tests/test_e2e.py` + `fixture_catalog` constants.
109
+ 2. **Green** — `scip_cli/commands/*.py`.
110
+ 3. **Refactor** — unit tests for pure helpers only.
111
+
112
+ - **E2e** (`cli` fixture): in-process `main()`, TS project indexed once per session — default for behavior.
113
+ - **Unit**: `:memory:` SQLite, `tests/analyze_db.py`, merge/index prune tests.
114
+ - **No subprocess** for CLI tests; no `reindex` in e2e (mutates `~/.cache/scip-cli`).
115
+ - New symbols: extend `tests/fixtures/sample-project/` + `fixture_catalog.py`.
116
+
117
+ ## Codebase map (contributors)
118
+
119
+ | Area | Path | Notes |
120
+ |------|------|-------|
121
+ | Commands | `scip_cli/commands/` | Thin; SQL in `queries.py` / `analyze/` |
122
+ | Index build | `scip_cli/indexing.py` | `index.db.next` → promote; flock; `>10` tsconfigs log progress |
123
+ | Variable prune | postprocess + `merge.py` | Omit const/let/var on **COPY**, not DELETE — no `variable` kind |
124
+ | Session | `scip_cli/session.py` | `setup()`, `resolve_one_symbol` (limit 2 for ambiguity) |
125
+ | Analyze filters | `scip_cli/analyze/common.py` | `is_test_path`, `analyze_noise` (`_` helpers) |
126
+
127
+ `todo.md` = tracked work; `dogfood.md` = future ideas only. Wishlist/backlog in `docs/` per workspace rules.
128
+
129
+ ## Docs touch points
130
+
131
+ - User install + analyze guide → `README.md`
132
+ - Agent/user CLI reference → `scip_cli/SKILL.md` (must match `__main__.py` flags)
133
+ - After flag changes: update SKILL + README command list; run `scip-cli skill` to verify output
@@ -0,0 +1,53 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ build/
8
+ develop-eggs/
9
+ dist/
10
+ downloads/
11
+ eggs/
12
+ .eggs/
13
+ lib/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ wheels/
19
+ *.egg-info/
20
+ .installed.cfg
21
+ *.egg
22
+
23
+ # Virtual environments
24
+ venv/
25
+ ENV/
26
+ env/
27
+ .venv
28
+
29
+ # IDE
30
+ .vscode/
31
+ .idea/
32
+ *.swp
33
+ *.swo
34
+ *~
35
+
36
+ # OS
37
+ .DS_Store
38
+ Thumbs.db
39
+
40
+ # Env/secrets
41
+ .env
42
+
43
+ # Pytest
44
+ .pytest_cache/
45
+
46
+ # Project specific
47
+ TODO.md
48
+ todo.md
49
+ dogfood.md
50
+
51
+ # Local-only dev scripts (repo-specific smoke harnesses)
52
+ scripts/deep-smoke.sh
53
+ tmp/
@@ -0,0 +1,15 @@
1
+ repos:
2
+ - repo: https://github.com/astral-sh/ruff-pre-commit
3
+ rev: v0.11.0
4
+ hooks:
5
+ - id: ruff
6
+ args: [--fix]
7
+ - id: ruff-format
8
+ - repo: local
9
+ hooks:
10
+ - id: pytest
11
+ name: pytest
12
+ entry: pytest
13
+ language: python
14
+ pass_filenames: false
15
+ always_run: true
@@ -0,0 +1,330 @@
1
+ Metadata-Version: 2.4
2
+ Name: scip-cli
3
+ Version: 2.0.0
4
+ Summary: Fast code intelligence via SCIP indexes
5
+ Project-URL: Homepage, https://github.com/flesler/scip-cli
6
+ Author: Ariel Flesler
7
+ License: MIT
8
+ License-File: LICENSE
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Topic :: Software Development :: Code Generators
13
+ Requires-Python: >=3.9
14
+ Provides-Extra: dev
15
+ Requires-Dist: build>=1; extra == 'dev'
16
+ Requires-Dist: pre-commit>=4.0; extra == 'dev'
17
+ Requires-Dist: pytest>=7; extra == 'dev'
18
+ Requires-Dist: ruff>=0.11; extra == 'dev'
19
+ Description-Content-Type: text/markdown
20
+
21
+ # scip-cli
22
+
23
+ [![PyPI version](https://badge.fury.io/py/scip-cli.svg)](https://badge.fury.io/py/scip-cli)
24
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
25
+
26
+ Token-efficient code intelligence for AI agents. Precise refs, definitions, and repo health analysis via SCIP indexes — TypeScript/JavaScript and Python.
27
+
28
+ ## Why
29
+
30
+ 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.
31
+
32
+ ## Features
33
+
34
+ - **Agent-first**: Install as a skill for Claude Code, Cursor, or any AI agent — precise code navigation without burning context
35
+ - **Token-efficient**: One record per line, stderr for warnings, pipe-friendly output
36
+ - **Fast**: Direct SQLite queries — 10x to 213x faster than alternatives
37
+ - **`analyze`**: Find dead exports, import cycles, stale types, coupling hotspots — actionable health dashboards at project, file, or symbol scope
38
+ - **Auto-indexing**: Indexes on first query, caches in SQLite, zero config
39
+
40
+ ## For AI Agents
41
+
42
+ Install as a reusable skill so your agent always knows how to navigate the codebase:
43
+
44
+ ```bash
45
+ scip-cli skill ~/.claude/skills/scip-cli/ # Claude Code
46
+ scip-cli skill ~/.cursor/skills/scip-cli/ # Cursor
47
+ ```
48
+
49
+ Or dump the quick reference for one-off use:
50
+
51
+ ```bash
52
+ scip-cli skill
53
+ ```
54
+
55
+ ## Installation
56
+
57
+ ### 1. Install scip-cli
58
+
59
+ **From PyPI** (end users):
60
+
61
+ ```bash
62
+ pip install scip-cli
63
+ ```
64
+
65
+ **Local development** (use a project venv — do not rely on global `pip`):
66
+
67
+ ```bash
68
+ cd scip-cli
69
+ python -m venv .venv
70
+ source .venv/bin/activate # Windows: .venv\Scripts\activate
71
+ pip install -e ".[dev]"
72
+ scip-cli --version
73
+ ```
74
+
75
+ `pip install -e .` keeps `scip-cli` on your PATH inside the venv while you edit the repo. Run tests with `pytest` from the same venv.
76
+
77
+ ### 2. Install prerequisites (optional)
78
+
79
+ 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`:
80
+
81
+ **Option A: Zero extra setup (recommended)**
82
+
83
+ Install `scip-cli` and run it. On first index, scip-cli will:
84
+
85
+ - Download `scip-typescript` / `scip-python` via `npx` when not already on PATH
86
+ - Download the `scip` converter binary from [GitHub releases](https://github.com/scip-code/scip/releases) into `~/.cache/scip-cli/bin/` when not already on PATH
87
+ - 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`
88
+
89
+ No `.scip-cli.json` required for discovery. Subsequent queries read the cached database only.
90
+
91
+ **Option B: Install indexers globally ahead of time**
92
+
93
+ Same indexing steps as Option A; this only avoids `npx` download on the first run:
94
+
95
+ ```bash
96
+ # TypeScript/JavaScript indexer (also handles plain JS via --infer-tsconfig)
97
+ npm install -g @sourcegraph/scip-typescript
98
+
99
+ # Python indexer
100
+ npm install -g @sourcegraph/scip-python
101
+
102
+ # SCIP CLI for index conversion (GitHub release — not on npm)
103
+ # https://github.com/scip-code/scip/releases (v0.8.1+ recommended)
104
+ ```
105
+
106
+ **Verify installation:**
107
+
108
+ ```bash
109
+ scip-cli --help
110
+ scip-typescript --version # Only if you chose Option B
111
+ scip --version # Install from GitHub releases; v0.8.1+ recommended
112
+ ```
113
+
114
+ ## Usage
115
+
116
+ All commands are subcommands of `scip-cli`:
117
+
118
+ ```bash
119
+ scip-cli <command> [arguments]
120
+ ```
121
+
122
+ ### Commands
123
+
124
+ - `refs <symbol>` - Find all references to a symbol (`--path` to scope)
125
+ - `code <symbol>` - Find symbol definition with source code (`--path`, `--max-lines`, `--full`, `--offset`, `--snippet`, `--line-numbers`)
126
+ - `search <pattern>` - Search symbols by name pattern (`--path`)
127
+ - `symbols <file>` - List all symbols in a file (`--path`; bare filename OK)
128
+ - `rdeps <file>` - Find files that depend on a file (`--path`)
129
+ - `members <symbol>` - List members of a class/interface (`--path`)
130
+ - `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`](#finding-easy-wins-with-analyze).
131
+ - `reindex` - Force re-indexing of the current project (`--path` to limit scope; repeatable)
132
+ - `skill [path]` - Install or dump the SKILL.md
133
+
134
+ ### Examples
135
+
136
+ ```bash
137
+ # Find where greet is used
138
+ scip-cli refs greet
139
+
140
+ # Get definition of greet
141
+ scip-cli code greet
142
+
143
+ # Search for symbols matching "Widget"
144
+ scip-cli search Widget
145
+
146
+ # Scope to a subdirectory
147
+ scip-cli code greet --path packages/api
148
+
149
+ # List symbols by bare filename
150
+ scip-cli symbols helper.ts
151
+
152
+ # Find files that import from a module
153
+ scip-cli rdeps src/helper.ts
154
+
155
+ # List members of a class
156
+ scip-cli members Widget
157
+
158
+ # Project health dashboard (or: scip-cli analyze src/foo.ts / scip-cli analyze greet)
159
+ scip-cli analyze
160
+
161
+ # Install skill file
162
+ scip-cli skill ~/.claude/skills/scip-cli/SKILL.md
163
+ ```
164
+
165
+ ### Pipelines
166
+
167
+ 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`. `rdeps` already prints bare file paths.
168
+
169
+ ```bash
170
+ # What do importers of this file export?
171
+ scip-cli rdeps src/helper.ts | xargs -I{} scip-cli symbols {}
172
+
173
+ # Which files reference a symbol?
174
+ scip-cli refs greet --paths-only
175
+
176
+ # Classes matching a name → list their members
177
+ scip-cli search Handler --kind class --names-only | xargs -I{} scip-cli members {}
178
+
179
+ # Walk class members to their definitions
180
+ scip-cli members Widget --names-only | xargs -I{} scip-cli code Widget.{}
181
+ ```
182
+
183
+ ## How It Works
184
+
185
+ 1. On first query, automatically detects project language from `package.json` (TS/JS) or `pyproject.toml`/`setup.py` (Python)
186
+ 2. For TypeScript monorepos, walks the repository for `tsconfig*.json` project roots (nested ancestors deduped; root included only when its `include` is broad)
187
+ 3. Runs `scip-typescript` per project (in parallel when there are multiple projects; set `SCIP_CLI_INDEX_WORKERS=1` to force serial), or `scip-python` for Python
188
+ 4. Converts each SCIP output to SQLite with `scip expt-convert`, then merges partial databases when needed
189
+ 5. Caches the result in `~/.cache/scip-cli/projects/<dirname>-<hash>/index.db` (e.g. `my-monorepo-1a3f7a`)
190
+ 6. Subsequent queries are SQLite lookups against that cache (not re-indexing)
191
+
192
+ ## Configuration
193
+
194
+ Optional `.scip-cli.json` in the project root:
195
+
196
+ ```json
197
+ {
198
+ "maxHeapMb": 8192,
199
+ "indexRoots": ["packages/core", "apps/worker"],
200
+ "onlyIndexRoots": false
201
+ }
202
+ ```
203
+
204
+ - `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.
205
+ - `indexRoots` — extra TypeScript project directories to include on **first index**, merged with auto-discovered projects.
206
+ - `onlyIndexRoots` — skip auto-discovery and index **only** `indexRoots` (smaller initial index when you only care about part of a monorepo).
207
+
208
+ `SCIP_CLI_INDEX_WORKERS` controls parallel `scip-typescript` runs during first index (default: up to 8). Merge into one database is always serial.
209
+
210
+ Large monorepos (>10 tsconfig projects) log per-project progress to stderr during indexing; smaller repos stay quiet aside from the final `Indexed … (size)` line.
211
+
212
+ Scoped indexing without editing `.scip-cli.json`:
213
+
214
+ ```bash
215
+ scip-cli reindex --path packages/server
216
+ scip-cli reindex --path packages/api --path packages/worker
217
+ ```
218
+
219
+ `--path` limits which discovered tsconfig projects are indexed (prefix match, same idea as query `--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`.
220
+
221
+ Run `scip-cli reindex` after changing scope, `.scip-cli.json` index settings, or when you want a fresh index.
222
+
223
+ ## Finding easy wins with `analyze`
224
+
225
+ Use `analyze` on the repo itself before broad refactors or agent review — it surfaces cross-file issues from the SCIP index (not Python `vulture`).
226
+
227
+ **Quick pass** (after `scip-cli reindex`):
228
+
229
+ ```bash
230
+ scip-cli analyze --limit 25
231
+ scip-cli analyze --priority high --limit 25 # dead exports & cycles only
232
+ ```
233
+
234
+ Sections are tagged `[high]`, `[medium]`, `[low]` and listed in that order.
235
+
236
+ | Tier | Project sections | Action |
237
+ | ---- | ---------------- | ------ |
238
+ | **high** | Cycles, unreferenced, dead exports, stale types | Nuke or fix cycles; delete unused; `_` prefix |
239
+ | **medium** | Same-file only, change surface (file target) | Module-private by usage |
240
+ | **low** | Test-only consumers, coupling, bottlenecks, hotspots | Noisy on Python (index omits many same-file calls); verify with `rg` |
241
+
242
+ Use `--priority high` for a quick gate; `--priority high,medium` adds context. File drill-down adds change surface and unused imports.
243
+
244
+ **What to look at first**
245
+
246
+ | Section | Easy pickings |
247
+ | ------- | ------------- |
248
+ | **Cycles** | Import/mention cycles between production files — break the edge or extract shared code |
249
+ | **Unreferenced** | No usage in the index at all — delete |
250
+ | **Dead exports** | No external refs — delete or `_` prefix |
251
+ | **Stale types** | Classes/types with ≤1 external consumer — merge, inline, or document why they stay |
252
+ | **Same-file only** | Used only inside defining file — rename to `_` |
253
+ | **Test-only consumers** | Cross-file refs are all from tests — promote to e2e or accept as internal |
254
+
255
+ **Per-file or package drill-down** on hubs or suspects:
256
+
257
+ ```bash
258
+ scip-cli analyze scip_cli/queries.py --limit 20 # file: scoped project + per-file + top symbols
259
+ scip-cli analyze scip_cli --limit 15 # directory: scoped project + each file under it
260
+ ```
261
+
262
+ `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.
263
+
264
+ **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.
265
+
266
+ **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.
267
+
268
+ ## Performance
269
+
270
+ Inspired by [scip-query](https://github.com/PlunderStruck/scip-query), scip-cli is a lightweight Python partial reimplementation optimized for speed. Compared to the original:
271
+
272
+ - `refs`: 6.4s → 0.03s (213x faster)
273
+ - `code`: 2.8s → 0.05s (56x faster)
274
+ - `search`: 2.6s → 0.03s (87x faster)
275
+ - `symbols`: 0.3s → 0.02s (15x faster)
276
+ - `rdeps`: 0.2s → 0.02s (10x faster)
277
+ - `members`: 3.1s → 0.03s (103x faster)
278
+
279
+ The speedup comes from using optimized direct SQLite queries and cutting some nice but very slow goodies (like ts-morph).
280
+
281
+ ## Architecture
282
+
283
+ ```
284
+ scip_cli/
285
+ ├── __init__.py
286
+ ├── __main__.py # CLI entry point
287
+ ├── cli_args.py # Shared argparse helpers
288
+ ├── config.py # .scip-cli.json loader
289
+ ├── discover.py # TypeScript project discovery
290
+ ├── merge.py # SQLite index merging
291
+ ├── scip_tool.py # scip binary download
292
+ ├── sql.py # SQLite helpers
293
+ ├── paths.py # --path scope filtering
294
+ ├── project.py # Project root + language detection
295
+ ├── cache.py # Index cache paths
296
+ ├── scope.py # Persisted reindex scope (index-scope.json)
297
+ ├── debug.py # SCIP_CLI_DEBUG stderr helpers
298
+ ├── indexing.py # SCIP index build + get_db
299
+ ├── symbols.py # Symbol parsing and kinds
300
+ ├── queries.py # Symbol/file SQL queries
301
+ ├── source.py # Filesystem source reads
302
+ ├── output.py # CLI formatting helpers
303
+ ├── session.py # setup() and single-match resolution
304
+ ├── targets.py # file-path heuristics (tests; analyze uses analyze/targets.py)
305
+ ├── analyze/ # SQL dashboard queries (project/file/symbol)
306
+ └── commands/ # Subcommand implementations
307
+ ```
308
+
309
+ ## Development
310
+
311
+ ```bash
312
+ pip install -e ".[dev]"
313
+ pytest tests/ -q
314
+ pytest tests/ -m integration -q # indexes tests/fixtures/sample-project (needs scip-typescript)
315
+ ```
316
+
317
+ ### Debug Logging
318
+
319
+ Set `SCIP_CLI_DEBUG=1` to enable SQL query logging to stderr (statements truncated to 200 chars):
320
+
321
+ ```bash
322
+ SCIP_CLI_DEBUG=1 scip-cli refs MyFunction
323
+ # Shows: SQL: SELECT ... | params: (...)
324
+ ```
325
+
326
+ This is useful for testing and debugging SQL queries
327
+
328
+ ## License
329
+
330
+ MIT