scip-cli 1.3.0__tar.gz → 2.1.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 (102) hide show
  1. scip_cli-2.1.0/.cursor/rules/agent.mdc +165 -0
  2. scip_cli-2.1.0/.gitignore +53 -0
  3. scip_cli-2.1.0/.pre-commit-config.yaml +15 -0
  4. {scip_cli-1.3.0/scip_cli.egg-info → scip_cli-2.1.0}/PKG-INFO +101 -45
  5. scip_cli-1.3.0/PKG-INFO → scip_cli-2.1.0/README.md +92 -56
  6. scip_cli-2.1.0/pyproject.toml +59 -0
  7. scip_cli-2.1.0/scip_cli/SKILL.md +137 -0
  8. {scip_cli-1.3.0 → scip_cli-2.1.0}/scip_cli/__init__.py +2 -1
  9. {scip_cli-1.3.0 → scip_cli-2.1.0}/scip_cli/__main__.py +75 -28
  10. scip_cli-2.1.0/scip_cli/analyze/__init__.py +1 -0
  11. scip_cli-2.1.0/scip_cli/analyze/common.py +110 -0
  12. scip_cli-2.1.0/scip_cli/analyze/file.py +354 -0
  13. scip_cli-2.1.0/scip_cli/analyze/graph.py +130 -0
  14. scip_cli-2.1.0/scip_cli/analyze/project.py +403 -0
  15. scip_cli-2.1.0/scip_cli/analyze/sections.py +77 -0
  16. scip_cli-2.1.0/scip_cli/analyze/symbol.py +184 -0
  17. scip_cli-2.1.0/scip_cli/analyze/targets.py +97 -0
  18. {scip_cli-1.3.0 → scip_cli-2.1.0}/scip_cli/cache.py +28 -5
  19. {scip_cli-1.3.0 → scip_cli-2.1.0}/scip_cli/cli_args.py +26 -1
  20. scip_cli-2.1.0/scip_cli/commands/analyze.py +142 -0
  21. scip_cli-2.1.0/scip_cli/commands/code.py +123 -0
  22. {scip_cli-1.3.0 → scip_cli-2.1.0}/scip_cli/commands/members.py +13 -14
  23. {scip_cli-1.3.0 → scip_cli-2.1.0}/scip_cli/commands/rdeps.py +5 -13
  24. scip_cli-2.1.0/scip_cli/commands/refs.py +166 -0
  25. scip_cli-2.1.0/scip_cli/commands/reindex.py +69 -0
  26. {scip_cli-1.3.0 → scip_cli-2.1.0}/scip_cli/commands/search.py +98 -57
  27. {scip_cli-1.3.0 → scip_cli-2.1.0}/scip_cli/commands/skill.py +7 -3
  28. {scip_cli-1.3.0 → scip_cli-2.1.0}/scip_cli/commands/symbols.py +4 -6
  29. {scip_cli-1.3.0 → scip_cli-2.1.0}/scip_cli/config.py +3 -3
  30. scip_cli-2.1.0/scip_cli/debug.py +10 -0
  31. {scip_cli-1.3.0 → scip_cli-2.1.0}/scip_cli/discover.py +14 -21
  32. {scip_cli-1.3.0 → scip_cli-2.1.0}/scip_cli/indexing.py +224 -74
  33. scip_cli-2.1.0/scip_cli/merge.py +108 -0
  34. {scip_cli-1.3.0 → scip_cli-2.1.0}/scip_cli/output.py +56 -26
  35. {scip_cli-1.3.0 → scip_cli-2.1.0}/scip_cli/paths.py +37 -3
  36. scip_cli-2.1.0/scip_cli/project.py +42 -0
  37. {scip_cli-1.3.0 → scip_cli-2.1.0}/scip_cli/queries.py +86 -75
  38. {scip_cli-1.3.0 → scip_cli-2.1.0}/scip_cli/scip_tool.py +25 -10
  39. {scip_cli-1.3.0 → scip_cli-2.1.0}/scip_cli/scope.py +1 -6
  40. {scip_cli-1.3.0 → scip_cli-2.1.0}/scip_cli/session.py +2 -1
  41. {scip_cli-1.3.0 → scip_cli-2.1.0}/scip_cli/source.py +12 -3
  42. {scip_cli-1.3.0 → scip_cli-2.1.0}/scip_cli/sql.py +7 -4
  43. {scip_cli-1.3.0 → scip_cli-2.1.0}/scip_cli/symbols.py +39 -12
  44. scip_cli-2.1.0/scip_cli/targets.py +17 -0
  45. scip_cli-2.1.0/scripts/build.sh +13 -0
  46. scip_cli-2.1.0/scripts/publish.sh +32 -0
  47. scip_cli-2.1.0/scripts/test.sh +9 -0
  48. scip_cli-2.1.0/tests/analyze_db.py +204 -0
  49. scip_cli-2.1.0/tests/conftest.py +25 -0
  50. scip_cli-2.1.0/tests/e2e_harness.py +101 -0
  51. scip_cli-2.1.0/tests/fixture_catalog.py +17 -0
  52. scip_cli-2.1.0/tests/fixtures/sample-project/package.json +5 -0
  53. scip_cli-2.1.0/tests/fixtures/sample-project/src/app/handler.ts +3 -0
  54. scip_cli-2.1.0/tests/fixtures/sample-project/src/config.ts +1 -0
  55. scip_cli-2.1.0/tests/fixtures/sample-project/src/consumer.ts +4 -0
  56. scip_cli-2.1.0/tests/fixtures/sample-project/src/dead.ts +1 -0
  57. scip_cli-2.1.0/tests/fixtures/sample-project/src/helper.ts +7 -0
  58. scip_cli-2.1.0/tests/fixtures/sample-project/src/index.ts +3 -0
  59. scip_cli-2.1.0/tests/fixtures/sample-project/src/user.ts +6 -0
  60. scip_cli-2.1.0/tests/fixtures/sample-project/src/widget.ts +7 -0
  61. scip_cli-2.1.0/tests/fixtures/sample-project/tsconfig.json +8 -0
  62. scip_cli-2.1.0/tests/perf_util.py +45 -0
  63. scip_cli-2.1.0/tests/test_analyze.py +223 -0
  64. scip_cli-2.1.0/tests/test_analyze_graph.py +30 -0
  65. scip_cli-2.1.0/tests/test_analyze_perf.py +39 -0
  66. {scip_cli-1.3.0 → scip_cli-2.1.0}/tests/test_cache.py +2 -2
  67. {scip_cli-1.3.0 → scip_cli-2.1.0}/tests/test_composability.py +44 -49
  68. {scip_cli-1.3.0 → scip_cli-2.1.0}/tests/test_config.py +1 -0
  69. {scip_cli-1.3.0 → scip_cli-2.1.0}/tests/test_discover.py +1 -0
  70. scip_cli-2.1.0/tests/test_e2e.py +174 -0
  71. scip_cli-2.1.0/tests/test_e2e_perf.py +53 -0
  72. scip_cli-2.1.0/tests/test_index_prune.py +122 -0
  73. {scip_cli-1.3.0 → scip_cli-2.1.0}/tests/test_indexer_env.py +2 -1
  74. {scip_cli-1.3.0 → scip_cli-2.1.0}/tests/test_merge.py +40 -21
  75. scip_cli-2.1.0/tests/test_multi_symbol.py +205 -0
  76. {scip_cli-1.3.0 → scip_cli-2.1.0}/tests/test_pure_functions.py +277 -270
  77. scip_cli-2.1.0/tests/test_qualified_symbols.py +100 -0
  78. scip_cli-2.1.0/tests/test_reindex.py +53 -0
  79. {scip_cli-1.3.0 → scip_cli-2.1.0}/tests/test_scip_tool.py +1 -0
  80. {scip_cli-1.3.0 → scip_cli-2.1.0}/tests/test_scope.py +7 -6
  81. scip_cli-2.1.0/tests/test_targets.py +17 -0
  82. {scip_cli-1.3.0 → scip_cli-2.1.0}/tests/test_typescript_projects.py +1 -0
  83. scip_cli-1.3.0/MANIFEST.in +0 -4
  84. scip_cli-1.3.0/README.md +0 -257
  85. scip_cli-1.3.0/pyproject.toml +0 -13
  86. scip_cli-1.3.0/scip_cli/SKILL.md +0 -108
  87. scip_cli-1.3.0/scip_cli/commands/def_cmd.py +0 -57
  88. scip_cli-1.3.0/scip_cli/commands/refs.py +0 -136
  89. scip_cli-1.3.0/scip_cli/commands/reindex.py +0 -55
  90. scip_cli-1.3.0/scip_cli/constants.py +0 -14
  91. scip_cli-1.3.0/scip_cli/lib.py +0 -64
  92. scip_cli-1.3.0/scip_cli/merge.py +0 -175
  93. scip_cli-1.3.0/scip_cli/project.py +0 -29
  94. scip_cli-1.3.0/scip_cli.egg-info/SOURCES.txt +0 -51
  95. scip_cli-1.3.0/scip_cli.egg-info/entry_points.txt +0 -2
  96. scip_cli-1.3.0/scip_cli.egg-info/top_level.txt +0 -1
  97. scip_cli-1.3.0/setup.cfg +0 -4
  98. scip_cli-1.3.0/setup.py +0 -37
  99. scip_cli-1.3.0/tests/test_smoke_cli.py +0 -157
  100. {scip_cli-1.3.0 → scip_cli-2.1.0}/LICENSE +0 -0
  101. {scip_cli-1.3.0 → scip_cli-2.1.0}/scip_cli/commands/__init__.py +0 -0
  102. /scip_cli-1.3.0/scip_cli.egg-info/dependency_links.txt → /scip_cli-2.1.0/tests/__init__.py +0 -0
@@ -0,0 +1,165 @@
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
+ **Almost always:** bare `scip-cli` on PATH must be **this checkout live** (`pip install -e ".[dev]"`). All examples use `scip-cli` — never `.venv/bin/scip-cli`, `python -m scip_cli`, or other paths. Only swap to PyPI briefly to smoke-test a release.
11
+
12
+ ## Bootstrap
13
+
14
+ From repo root — keep this mapping for the whole dev session:
15
+
16
+ ```bash
17
+ pip install -e ".[dev]"
18
+ scip-cli --version # must match scip_cli/__init__.py
19
+ pre-commit install
20
+ scip-cli skill ~/.claude/skills/scip-cli/
21
+ ```
22
+
23
+ After publishing: `pip install scip-cli` → smoke-test → `pip install -e ".[dev]"` to restore local.
24
+
25
+ Optional venv for isolation only: create/activate, then same `pip install -e ".[dev]"` — `scip-cli` still runs from PATH, not a venv-relative path.
26
+
27
+ Node.js + `npx` required for integration tests (`scip-typescript`). Optional: `SCIP_CLI_DEBUG=1` (SQL to stderr, truncated).
28
+
29
+ ## Lint / test
30
+
31
+ | Task | Command |
32
+ |------|---------|
33
+ | Full suite | `pytest` |
34
+ | E2e loop | `pytest tests/test_e2e.py` |
35
+ | Lint / format | `ruff check .` / `ruff format .` |
36
+
37
+ Pre-commit: ruff + ruff-format + full pytest. E2e before commit on command changes.
38
+
39
+ ## Improving this repo (do this before review subagents)
40
+
41
+ **Dogfood on the repo itself** — faster and more honest than spawning reviewers:
42
+
43
+ ```bash
44
+ scip-cli reindex
45
+ scip-cli analyze --limit 25
46
+ scip-cli analyze --priority high --limit 25 # dead code & cycles only
47
+ scip-cli analyze scip_cli --limit 15 # directory
48
+ scip-cli analyze scip_cli/queries.py --limit 20 # file (+ scoped project)
49
+ ```
50
+
51
+ Interpretation: README § *Finding easy wins with analyze*. Target = omit (project), directory, file, or symbol — not `--path` for scope.
52
+
53
+ | Priority | Section | Action |
54
+ |----------|---------|--------|
55
+ | 1 | **Cycles** | Break production file dependency cycles (`cache`↔`scope` was one) |
56
+ | 2 | **Unreferenced / dead exports** | No ref usage / no external refs — verify with `rg`, delete or `_` prefix |
57
+ | 3 | **Same-file only** | Module-private by usage — rename to `_` |
58
+ | 4 | **Stale types** | Low-consumer classes — merge or justify |
59
+
60
+ `analyze` skips test paths by default (`tests/`, `*.test.*`, `*.spec.*`); `--include-tests` to include. Reindex after large edits.
61
+
62
+ Do **not** spawn broad code-review agents until this pass is done (or explain why skipped).
63
+
64
+ ## Analyze feedback loop (mandatory)
65
+
66
+ When you or a review subagent finds a repo issue — bug, dead code, cycle, risky hub, stale type, unused import — **stop and ask first:**
67
+
68
+ > *Could our SQLite index have predicted this, and `analyze` just doesn't surface it yet?*
69
+
70
+ If **yes** (the signal is in `documents`, `mentions`, `global_symbols`, `defn_enclosing_ranges`):
71
+
72
+ 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`.
73
+ 2. **Dogfood** — `reindex` → confirm the issue appears (or the false positive is filtered).
74
+ 3. **Then** fix the production code (or document a known blind spot).
75
+
76
+ Only skip step 1 when the issue is **outside the index** (runtime, CLI UX, docs drift, git, env, concurrency, test harness).
77
+
78
+ ### Retrospective: caught manually, SQLite already had it
79
+
80
+ | Issue we hit | Index signal | Analyze today | Gap / next check |
81
+ |--------------|--------------|---------------|------------------|
82
+ | `cache` ↔ `scope` import cycle | cross-file `mentions` edges | `[high] Cycles` | **Caught** — keep dogfooding cycles |
83
+ | Cross-file dead export (`get_refs_for_symbols`) | def with no external `mentions` | `[high] Dead exports` | **Caught** |
84
+ | Same-file-only dead code | def, `role=0` only in def file | `[high] Unreferenced` or `[medium] Same-file only` | **Caught** (since extended) |
85
+ | Same-file helper without `_` (`run_with_fallback`) | same-file `role=0` or none | `[medium] Same-file only` / unreferenced | **Caught** — rename to `_` |
86
+ | 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` |
87
+ | `analyze/*` section runners “dead” | no external refs | noise filter in `common.py` | **Filtered** — framework pattern |
88
+ | Unused import | `role=2` mention, no in-file use | `[high] Unused imports` (per file) | **Caught** on file target |
89
+ | 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 |
90
+ | 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` |
91
+ | 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 |
92
+ | Duplicate / parallel helpers | — | — | **Not in index** — `rg`, not analyze |
93
+
94
+ Add new rows to `dogfood.md` when you find another gap; implement the SQL check before the next manual review pass.
95
+
96
+ ### Size / complexity checks (assessed — don’t promote to high/medium)
97
+
98
+ SQLite has **line ranges** (`defn_enclosing_ranges`) so LOC is cheap. That does **not** mean “big function” belongs next to dead exports.
99
+
100
+ | Idea | In index? | Verdict |
101
+ |------|-----------|---------|
102
+ | **LOC ≥ N** alone | Yes | **LOW at best** — noisy (`main`, argparse, legitimate parsers). Not a nuke candidate. |
103
+ | **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. |
104
+ | **LOC + consumers** on file target | Yes | **Change surface** (medium) already sorts by consumers with line ranges — drill there. |
105
+ | **Symbol pressure** | Yes | **Per-symbol** `analyze Foo` shows `loc × fan_in × fan_out`. |
106
+ | **God class / many members** | Yes | Use `members Class` — not analyze. |
107
+ | **File byte size / cyclomatic complexity** | No | Out of scope for SCIP index. |
108
+ | **Fat file (many symbols)** | Yes | Low value; coupling/hotspots approximate module heat. |
109
+
110
+ **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.
111
+
112
+ ## SQL / analyze benchmarks
113
+
114
+ The e2e fixture and this repo's own index are **small** — every query looks fast here. That hides blowups on real projects (40MB+ indexes, dense graphs).
115
+
116
+ **Before merging new or heavily changed SQL** (`queries.py`, `analyze/*`):
117
+
118
+ 1. Time it against **1–2 existing checks** on a **large cached index** you already have (not the fixture):
119
+ ```bash
120
+ python3 -u -c "
121
+ import sqlite3, time, sys
122
+ from pathlib import Path
123
+ sys.path.insert(0, '.')
124
+ from scip_cli.analyze import project as p
125
+ from scip_cli.sql import configure_read_connection
126
+ db_path = Path.home() / '.cache/scip-cli/projects/<project-slug>/index.db'
127
+ db = sqlite3.connect(f'file:{db_path}?mode=ro', uri=True)
128
+ configure_read_connection(db)
129
+ for name, fn in [('hotspots', p.hotspots), ('cycles', p.cycles), ('dead_exports', p.dead_exports)]:
130
+ t = time.perf_counter(); fn(db, limit=25); print(f'{name}: {(time.perf_counter()-t)*1000:.0f}ms')
131
+ "
132
+ ```
133
+ 2. Flag anything **notably slower** than peers — orders of magnitude or multi-second, not a few ms drift.
134
+ 3. Swap in your new/changed check for one of the rows; keep a fast peer (e.g. `hotspots`) and a heavy peer (e.g. `dead_exports`).
135
+
136
+ **In CI:** `tests/test_e2e_perf.py` (whole commands) and `tests/test_analyze_perf.py` (per-check on mini DB) catch gross regressions on the tiny fixture only — they **will not** catch large-repo OOMs. Use the manual benchmark above for that.
137
+
138
+ ## TDD
139
+
140
+ 1. **Red** — `tests/test_e2e.py` + `fixture_catalog` constants.
141
+ 2. **Green** — `scip_cli/commands/*.py`.
142
+ 3. **Refactor** — unit tests for pure helpers only.
143
+
144
+ - **E2e** (`cli` fixture): in-process `main()`, TS project indexed once per session — default for behavior.
145
+ - **Unit**: `:memory:` SQLite, `tests/analyze_db.py`, merge/index prune tests.
146
+ - **No subprocess** for CLI tests; no `reindex` in e2e (mutates `~/.cache/scip-cli`).
147
+ - New symbols: extend `tests/fixtures/sample-project/` + `fixture_catalog.py`.
148
+
149
+ ## Codebase map (contributors)
150
+
151
+ | Area | Path | Notes |
152
+ |------|------|-------|
153
+ | Commands | `scip_cli/commands/` | Thin; SQL in `queries.py` / `analyze/` |
154
+ | Index build | `scip_cli/indexing.py` | `index.db.next` → promote; flock; `>10` tsconfigs log progress |
155
+ | Variable prune | postprocess + `merge.py` | Omit const/let/var on **COPY**, not DELETE — no `variable` kind |
156
+ | Session | `scip_cli/session.py` | `setup()`, `resolve_one_symbol` (limit 2 for ambiguity) |
157
+ | Analyze filters | `scip_cli/analyze/common.py` | `is_test_path`, `analyze_noise` (`_` helpers) |
158
+
159
+ `todo.md` = tracked work; `dogfood.md` = future ideas only. Wishlist/backlog in `docs/` per workspace rules.
160
+
161
+ ## Docs touch points
162
+
163
+ - User install + analyze guide → `README.md`
164
+ - Agent/user CLI reference → `scip_cli/SKILL.md` (must match `__main__.py` flags)
165
+ - 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
@@ -1,82 +1,86 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scip-cli
3
- Version: 1.3.0
3
+ Version: 2.1.0
4
4
  Summary: Fast code intelligence via SCIP indexes
5
- Home-page: https://github.com/flesler/scip-cli
5
+ Project-URL: Homepage, https://github.com/flesler/scip-cli
6
6
  Author: Ariel Flesler
7
7
  License: MIT
8
- Classifier: Programming Language :: Python :: 3
8
+ License-File: LICENSE
9
9
  Classifier: License :: OSI Approved :: MIT License
10
10
  Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: Python :: 3
11
12
  Classifier: Topic :: Software Development :: Code Generators
12
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'
13
19
  Description-Content-Type: text/markdown
14
- License-File: LICENSE
15
- Dynamic: author
16
- Dynamic: classifier
17
- Dynamic: description
18
- Dynamic: description-content-type
19
- Dynamic: home-page
20
- Dynamic: license
21
- Dynamic: license-file
22
- Dynamic: requires-python
23
- Dynamic: summary
24
20
 
25
21
  # scip-cli
26
22
 
27
23
  [![PyPI version](https://badge.fury.io/py/scip-cli.svg)](https://badge.fury.io/py/scip-cli)
28
24
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
29
25
 
30
- Fast code intelligence CLI for TypeScript/JavaScript and Python projects. Query SCIP indexes directly via SQLite for instant results.
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
31
 
32
32
  ## Features
33
33
 
34
- - **Fast**: Direct SQLite queries, eliminating skippable overhead
35
- - **Simple**: Single binary with subcommands
36
- - **Auto-indexing**: Automatically indexes projects on first query
37
- - **Token-efficient**: Clean, minimal output optimized for AI consumption
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
38
39
 
39
40
  ## For AI Agents
40
41
 
41
- If you're an AI agent, run this to see the quick reference:
42
+ Install as a reusable skill so your agent always knows how to navigate the codebase:
42
43
 
43
44
  ```bash
44
- scip-cli skill
45
+ scip-cli skill ~/.claude/skills/scip-cli/ # Claude Code
46
+ scip-cli skill ~/.cursor/skills/scip-cli/ # Cursor
45
47
  ```
46
48
 
47
- Or install it to your skills folder:
49
+ Or dump the quick reference for one-off use:
48
50
 
49
51
  ```bash
50
- scip-cli skill ~/.claude/skills/scip-cli/SKILL.md
52
+ scip-cli skill
51
53
  ```
52
54
 
53
- This enables commands like `def`, `refs`, `search`, `symbols`, `rdeps`, and `members` - just ask "where is X?" or "find references to X".
54
-
55
55
  ## Installation
56
56
 
57
57
  ### 1. Install scip-cli
58
58
 
59
- **From PyPI:**
59
+ **From PyPI** (end users):
60
60
 
61
61
  ```bash
62
62
  pip install scip-cli
63
63
  ```
64
64
 
65
- **From source (local development):**
65
+ **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.
66
66
 
67
67
  ```bash
68
- git clone https://github.com/flesler/scip-cli.git
69
68
  cd scip-cli
70
- pip install .
69
+ pip install -e ".[dev]"
70
+ scip-cli --version
71
71
  ```
72
72
 
73
- For editable development (where `pip install -e .` fails due to permissions):
73
+ 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.
74
+
75
+ Optional venv for isolation — activate it, run the same editable install, still invoke `scip-cli` on PATH:
74
76
 
75
77
  ```bash
76
- export PYTHONPATH=/path/to/scip-cli:$PYTHONPATH
77
- python -m scip_cli --help
78
+ python -m venv .venv && source .venv/bin/activate
79
+ pip install -e ".[dev]"
78
80
  ```
79
81
 
82
+ Tests and lint: `pytest`, `ruff check .` from the same environment.
83
+
80
84
  ### 2. Install prerequisites (optional)
81
85
 
82
86
  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`:
@@ -125,11 +129,12 @@ scip-cli <command> [arguments]
125
129
  ### Commands
126
130
 
127
131
  - `refs <symbol>` - Find all references to a symbol (`--path` to scope)
128
- - `def <symbol>` - Find symbol definition with source code (`--path`, `--max-lines`)
132
+ - `code <symbol>` - Find symbol definition with source code (`--path`, `--max-lines`, `--full`, `--offset`, `--snippet`, `--line-numbers`)
129
133
  - `search <pattern>` - Search symbols by name pattern (`--path`)
130
134
  - `symbols <file>` - List all symbols in a file (`--path`; bare filename OK)
131
135
  - `rdeps <file>` - Find files that depend on a file (`--path`)
132
136
  - `members <symbol>` - List members of a class/interface (`--path`)
137
+ - `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).
133
138
  - `reindex` - Force re-indexing of the current project (`--path` to limit scope; repeatable)
134
139
  - `skill [path]` - Install or dump the SKILL.md
135
140
 
@@ -140,13 +145,13 @@ scip-cli <command> [arguments]
140
145
  scip-cli refs greet
141
146
 
142
147
  # Get definition of greet
143
- scip-cli def greet
148
+ scip-cli code greet
144
149
 
145
150
  # Search for symbols matching "Widget"
146
151
  scip-cli search Widget
147
152
 
148
153
  # Scope to a subdirectory
149
- scip-cli def greet --path packages/api
154
+ scip-cli code greet --path packages/api
150
155
 
151
156
  # List symbols by bare filename
152
157
  scip-cli symbols helper.ts
@@ -157,13 +162,16 @@ scip-cli rdeps src/helper.ts
157
162
  # List members of a class
158
163
  scip-cli members Widget
159
164
 
165
+ # Project health dashboard (or: scip-cli analyze src/foo.ts / scip-cli analyze greet)
166
+ scip-cli analyze
167
+
160
168
  # Install skill file
161
169
  scip-cli skill ~/.claude/skills/scip-cli/SKILL.md
162
170
  ```
163
171
 
164
172
  ### Pipelines
165
173
 
166
- Stdout is one record per line; stderr carries warnings and ambiguity notices. Kinds are lowercase (`function`, `class`, `method`, `property`, `variable`). Pipe-friendly flags: `refs --paths-only`, `search --names-only` / `--paths-only`, `members --names-only`. `rdeps` already prints bare file paths.
174
+ 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.
167
175
 
168
176
  ```bash
169
177
  # What do importers of this file export?
@@ -176,7 +184,7 @@ scip-cli refs greet --paths-only
176
184
  scip-cli search Handler --kind class --names-only | xargs -I{} scip-cli members {}
177
185
 
178
186
  # Walk class members to their definitions
179
- scip-cli members Widget --names-only | xargs -I{} scip-cli def Widget.{}
187
+ scip-cli members Widget --names-only | xargs -I{} scip-cli code Widget.{}
180
188
  ```
181
189
 
182
190
  ## How It Works
@@ -206,10 +214,12 @@ Optional `.scip-cli.json` in the project root:
206
214
 
207
215
  `SCIP_CLI_INDEX_WORKERS` controls parallel `scip-typescript` runs during first index (default: up to 8). Merge into one database is always serial.
208
216
 
217
+ Large monorepos (>10 tsconfig projects) log per-project progress to stderr during indexing; smaller repos stay quiet aside from the final `Indexed … (size)` line.
218
+
209
219
  Scoped indexing without editing `.scip-cli.json`:
210
220
 
211
221
  ```bash
212
- scip-cli reindex --path entrypoints/server
222
+ scip-cli reindex --path packages/server
213
223
  scip-cli reindex --path packages/api --path packages/worker
214
224
  ```
215
225
 
@@ -217,20 +227,63 @@ scip-cli reindex --path packages/api --path packages/worker
217
227
 
218
228
  Run `scip-cli reindex` after changing scope, `.scip-cli.json` index settings, or when you want a fresh index.
219
229
 
220
- This is separate from `.scipquery.json`, which belongs to [scip-query](https://github.com/PlunderStruck/scip-query) and configures its analyzers, watch mode, and diff-gate — not read by scip-cli.
230
+ ## Finding easy wins with `analyze`
231
+
232
+ Use `analyze` on the repo itself before broad refactors or agent review — it surfaces cross-file issues from the SCIP index (not Python `vulture`).
233
+
234
+ **Quick pass** (after `scip-cli reindex`):
235
+
236
+ ```bash
237
+ scip-cli analyze --limit 25
238
+ scip-cli analyze --priority high --limit 25 # dead exports & cycles only
239
+ ```
240
+
241
+ Sections are tagged `[high]`, `[medium]`, `[low]` and listed in that order.
242
+
243
+ | Tier | Project sections | Action |
244
+ | ---- | ---------------- | ------ |
245
+ | **high** | Cycles, unreferenced, dead exports, stale types | Nuke or fix cycles; delete unused; `_` prefix |
246
+ | **medium** | Same-file only, change surface (file target) | Module-private by usage |
247
+ | **low** | Test-only consumers, coupling, bottlenecks, hotspots | Noisy on Python (index omits many same-file calls); verify with `rg` |
248
+
249
+ Use `--priority high` for a quick gate; `--priority high,medium` adds context. File drill-down adds change surface and unused imports.
250
+
251
+ **What to look at first**
252
+
253
+ | Section | Easy pickings |
254
+ | ------- | ------------- |
255
+ | **Cycles** | Import/mention cycles between production files — break the edge or extract shared code |
256
+ | **Unreferenced** | No usage in the index at all — delete |
257
+ | **Dead exports** | No external refs — delete or `_` prefix |
258
+ | **Stale types** | Classes/types with ≤1 external consumer — merge, inline, or document why they stay |
259
+ | **Same-file only** | Used only inside defining file — rename to `_` |
260
+ | **Test-only consumers** | Cross-file refs are all from tests — promote to e2e or accept as internal |
261
+
262
+ **Per-file or package drill-down** on hubs or suspects:
263
+
264
+ ```bash
265
+ scip-cli analyze scip_cli/queries.py --limit 20 # file: scoped project + per-file + top symbols
266
+ scip-cli analyze scip_cli --limit 15 # directory: scoped project + each file under it
267
+ ```
268
+
269
+ `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.
270
+
271
+ **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.
272
+
273
+ **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.
221
274
 
222
275
  ## Performance
223
276
 
224
- Inspired by [scip-query](https://github.com/PlunderStruck/scip-query), scip-cli is a lightweight Python reimplementation optimized for speed. Compared to the original bash wrapper scripts:
277
+ 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:
225
278
 
226
279
  - `refs`: 6.4s → 0.03s (213x faster)
227
- - `def`: 2.8s → 0.05s (56x faster)
280
+ - `code`: 2.8s → 0.05s (56x faster)
228
281
  - `search`: 2.6s → 0.03s (87x faster)
229
282
  - `symbols`: 0.3s → 0.02s (15x faster)
230
283
  - `rdeps`: 0.2s → 0.02s (10x faster)
231
284
  - `members`: 3.1s → 0.03s (103x faster)
232
285
 
233
- The speedup comes from direct SQLite queries instead of shell command chains, eliminating subprocess overhead.
286
+ The speedup comes from using optimized direct SQLite queries and cutting some nice but very slow goodies (like ts-morph).
234
287
 
235
288
  ## Architecture
236
289
 
@@ -243,38 +296,41 @@ scip_cli/
243
296
  ├── discover.py # TypeScript project discovery
244
297
  ├── merge.py # SQLite index merging
245
298
  ├── scip_tool.py # scip binary download
246
- ├── constants.py # Shared constants
247
299
  ├── sql.py # SQLite helpers
248
300
  ├── paths.py # --path scope filtering
249
301
  ├── project.py # Project root + language detection
250
302
  ├── cache.py # Index cache paths
303
+ ├── scope.py # Persisted reindex scope (index-scope.json)
304
+ ├── debug.py # SCIP_CLI_DEBUG stderr helpers
251
305
  ├── indexing.py # SCIP index build + get_db
252
306
  ├── symbols.py # Symbol parsing and kinds
253
307
  ├── queries.py # Symbol/file SQL queries
254
308
  ├── source.py # Filesystem source reads
255
309
  ├── output.py # CLI formatting helpers
256
310
  ├── session.py # setup() and single-match resolution
311
+ ├── targets.py # file-path heuristics (tests; analyze uses analyze/targets.py)
312
+ ├── analyze/ # SQL dashboard queries (project/file/symbol)
257
313
  └── commands/ # Subcommand implementations
258
314
  ```
259
315
 
260
316
  ## Development
261
317
 
262
318
  ```bash
263
- pip install -e .
319
+ pip install -e ".[dev]"
264
320
  pytest tests/ -q
265
321
  pytest tests/ -m integration -q # indexes tests/fixtures/sample-project (needs scip-typescript)
266
322
  ```
267
323
 
268
324
  ### Debug Logging
269
325
 
270
- Set `SCIP_CLI_DEBUG=1` to enable SQL query logging to stderr:
326
+ Set `SCIP_CLI_DEBUG=1` to enable SQL query logging to stderr (statements truncated to 200 chars):
271
327
 
272
328
  ```bash
273
329
  SCIP_CLI_DEBUG=1 scip-cli refs MyFunction
274
330
  # Shows: SQL: SELECT ... | params: (...)
275
331
  ```
276
332
 
277
- This is useful for testing and debugging SQL queries without exposing a `--debug` flag to users.
333
+ This is useful for testing and debugging SQL queries
278
334
 
279
335
  ## License
280
336