scip-cli 2.3.0__tar.gz → 2.5.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 (131) hide show
  1. {scip_cli-2.3.0 → scip_cli-2.5.0}/.cursor/rules/agent.mdc +22 -1
  2. scip_cli-2.5.0/.github/workflows/ci.yml +22 -0
  3. {scip_cli-2.3.0 → scip_cli-2.5.0}/.pre-commit-config.yaml +5 -0
  4. {scip_cli-2.3.0 → scip_cli-2.5.0}/PKG-INFO +42 -23
  5. {scip_cli-2.3.0 → scip_cli-2.5.0}/README.md +40 -22
  6. {scip_cli-2.3.0 → scip_cli-2.5.0}/pyproject.toml +18 -0
  7. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/SKILL.md +4 -4
  8. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/__init__.py +1 -1
  9. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/__main__.py +6 -3
  10. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/analyze/common.py +1 -1
  11. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/analyze/file.py +1 -1
  12. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/analyze/live.py +16 -1
  13. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/analyze/project.py +17 -3
  14. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/analyze/sections.py +9 -7
  15. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/analyze/symbol.py +2 -2
  16. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/cache.py +4 -2
  17. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/commands/analyze.py +12 -10
  18. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/commands/deps.py +1 -1
  19. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/commands/members.py +1 -1
  20. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/commands/reindex.py +14 -6
  21. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/commands/search.py +5 -3
  22. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/discover.py +58 -2
  23. scip_cli-2.5.0/scip_cli/indexing/__init__.py +29 -0
  24. scip_cli-2.5.0/scip_cli/indexing/constants.py +13 -0
  25. scip_cli-2.5.0/scip_cli/indexing/convert.py +94 -0
  26. scip_cli-2.5.0/scip_cli/indexing/core.py +210 -0
  27. scip_cli-2.5.0/scip_cli/indexing/languages.py +85 -0
  28. scip_cli-2.5.0/scip_cli/indexing/orchestrate.py +158 -0
  29. scip_cli-2.5.0/scip_cli/indexing/postprocess.py +114 -0
  30. scip_cli-2.5.0/scip_cli/indexing/runners.py +116 -0
  31. scip_cli-2.5.0/scip_cli/indexing/typescript.py +165 -0
  32. scip_cli-2.5.0/scip_cli/merge.py +168 -0
  33. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/output.py +4 -2
  34. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/paths.py +5 -4
  35. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/project.py +6 -0
  36. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/queries.py +25 -9
  37. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/scip_tool.py +8 -7
  38. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/source.py +20 -5
  39. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/sql.py +18 -3
  40. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/symbols.py +11 -9
  41. scip_cli-2.5.0/scripts/bench_postprocess.py +236 -0
  42. {scip_cli-2.3.0 → scip_cli-2.5.0}/scripts/publish.sh +6 -0
  43. scip_cli-2.5.0/scripts/test.sh +29 -0
  44. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/test_analyze.py +2 -1
  45. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/test_analyze_perf.py +5 -3
  46. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/test_discover.py +77 -0
  47. scip_cli-2.5.0/tests/test_index_batching.py +59 -0
  48. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/test_index_prune.py +40 -3
  49. scip_cli-2.5.0/tests/test_indexer_fallback.py +315 -0
  50. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/test_merge.py +36 -0
  51. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/test_pure_functions.py +2 -0
  52. scip_cli-2.5.0/tests/test_python_index_integration.py +88 -0
  53. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/test_reindex.py +15 -1
  54. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/test_scope.py +3 -2
  55. scip_cli-2.3.0/scip_cli/indexing.py +0 -520
  56. scip_cli-2.3.0/scip_cli/merge.py +0 -108
  57. scip_cli-2.3.0/scripts/test.sh +0 -9
  58. {scip_cli-2.3.0 → scip_cli-2.5.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  59. {scip_cli-2.3.0 → scip_cli-2.5.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  60. {scip_cli-2.3.0 → scip_cli-2.5.0}/.github/ISSUE_TEMPLATE/question.md +0 -0
  61. {scip_cli-2.3.0 → scip_cli-2.5.0}/.github/pull_request_template.md +0 -0
  62. {scip_cli-2.3.0 → scip_cli-2.5.0}/.gitignore +0 -0
  63. {scip_cli-2.3.0 → scip_cli-2.5.0}/CODE_OF_CONDUCT.md +0 -0
  64. {scip_cli-2.3.0 → scip_cli-2.5.0}/CONTRIBUTING.md +0 -0
  65. {scip_cli-2.3.0 → scip_cli-2.5.0}/LICENSE +0 -0
  66. {scip_cli-2.3.0 → scip_cli-2.5.0}/SECURITY.md +0 -0
  67. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/analyze/__init__.py +0 -0
  68. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/analyze/graph.py +0 -0
  69. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/analyze/targets.py +0 -0
  70. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/cli_args.py +0 -0
  71. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/commands/__init__.py +0 -0
  72. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/commands/code.py +0 -0
  73. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/commands/rdeps.py +0 -0
  74. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/commands/refs.py +0 -0
  75. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/commands/skill.py +0 -0
  76. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/commands/symbols.py +0 -0
  77. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/config.py +0 -0
  78. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/debug.py +0 -0
  79. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/scope.py +0 -0
  80. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/session.py +0 -0
  81. {scip_cli-2.3.0 → scip_cli-2.5.0}/scip_cli/targets.py +0 -0
  82. {scip_cli-2.3.0 → scip_cli-2.5.0}/scripts/build.sh +0 -0
  83. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/__init__.py +0 -0
  84. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/analyze_db.py +0 -0
  85. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/conftest.py +0 -0
  86. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/e2e_harness.py +0 -0
  87. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/fixture_catalog.py +0 -0
  88. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/fixtures/sample-project/package.json +0 -0
  89. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/fixtures/sample-project/src/app/handler.ts +0 -0
  90. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/fixtures/sample-project/src/config.ts +0 -0
  91. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/fixtures/sample-project/src/consumer.ts +0 -0
  92. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/fixtures/sample-project/src/dead.ts +0 -0
  93. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/fixtures/sample-project/src/domain/i18n/en.ts +0 -0
  94. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/fixtures/sample-project/src/domain/i18n/index.ts +0 -0
  95. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/fixtures/sample-project/src/domain/i18n/useLocale.ts +0 -0
  96. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/fixtures/sample-project/src/domain/labels/index.ts +0 -0
  97. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/fixtures/sample-project/src/events/streamTypes.ts +0 -0
  98. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/fixtures/sample-project/src/gateway/index.ts +0 -0
  99. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/fixtures/sample-project/src/helper.ts +0 -0
  100. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/fixtures/sample-project/src/hooks/useHookA.ts +0 -0
  101. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/fixtures/sample-project/src/hooks/useHookB.ts +0 -0
  102. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/fixtures/sample-project/src/hooks/useItems.ts +0 -0
  103. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/fixtures/sample-project/src/index.ts +0 -0
  104. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/fixtures/sample-project/src/integrations/inferenceClient.ts +0 -0
  105. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/fixtures/sample-project/src/loaders/useInference.ts +0 -0
  106. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/fixtures/sample-project/src/pages/panelPage.ts +0 -0
  107. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/fixtures/sample-project/src/rules/applyRule.ts +0 -0
  108. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/fixtures/sample-project/src/types/a.ts +0 -0
  109. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/fixtures/sample-project/src/types/b.ts +0 -0
  110. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/fixtures/sample-project/src/ui/Button.ts +0 -0
  111. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/fixtures/sample-project/src/ui/LazyPanel.ts +0 -0
  112. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/fixtures/sample-project/src/ui/buttonConsumer.ts +0 -0
  113. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/fixtures/sample-project/src/ui/menuModule.ts +0 -0
  114. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/fixtures/sample-project/src/user.ts +0 -0
  115. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/fixtures/sample-project/src/widget.ts +0 -0
  116. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/fixtures/sample-project/src/widgets/OrphanWidget.ts +0 -0
  117. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/fixtures/sample-project/tsconfig.json +0 -0
  118. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/perf_util.py +0 -0
  119. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/test_analyze_graph.py +0 -0
  120. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/test_cache.py +0 -0
  121. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/test_composability.py +0 -0
  122. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/test_config.py +0 -0
  123. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/test_e2e.py +0 -0
  124. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/test_e2e_analyze_patterns.py +0 -0
  125. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/test_e2e_perf.py +0 -0
  126. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/test_indexer_env.py +0 -0
  127. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/test_multi_symbol.py +0 -0
  128. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/test_qualified_symbols.py +0 -0
  129. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/test_scip_tool.py +0 -0
  130. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/test_targets.py +0 -0
  131. {scip_cli-2.3.0 → scip_cli-2.5.0}/tests/test_typescript_projects.py +0 -0
@@ -30,11 +30,15 @@ Node.js + `npx` required for integration tests (`scip-typescript`). Optional: `S
30
30
 
31
31
  | Task | Command |
32
32
  |------|---------|
33
+ | **All checks** | `scripts/test.sh` (lint + format + types + tests) |
33
34
  | Full suite | `pytest` |
34
35
  | E2e loop | `pytest tests/test_e2e.py tests/test_e2e_analyze_patterns.py` |
35
36
  | Lint / format | `ruff check .` / `ruff format .` |
37
+ | Typecheck | `basedpyright scip_cli/` |
36
38
 
37
- Pre-commit: ruff + ruff-format + full pytest. E2e before commit on command changes.
39
+ Pre-commit: ruff + ruff-format + basedpyright + full pytest. E2e before commit on command changes.
40
+
41
+ **Use `scripts/test.sh`** before committing to catch all issues (lint, format, types, tests). This matches what pre-commit runs.
38
42
 
39
43
  ## Improving this repo (do this before review subagents)
40
44
 
@@ -186,3 +190,20 @@ If both layers help during development, **keep both until e2e passes**, then del
186
190
  - User install + analyze guide → `README.md`
187
191
  - Agent/user CLI reference → `scip_cli/SKILL.md` (must match `__main__.py` flags)
188
192
  - After flag changes: update SKILL + README command list; run `scip-cli skill` to verify output
193
+
194
+
195
+ ## Dogfood Notes
196
+
197
+ Future improvements from using the CLI on real projects.
198
+
199
+ ### Workflow
200
+
201
+ Requires bare `scip-cli` mapped to local (`pip install -e ".[dev]"` from repo root).
202
+
203
+ ```bash
204
+ scip-cli reindex
205
+ scip-cli analyze --limit 25
206
+ scip-cli analyze scip_cli/queries.py --limit 20 # drill into hubs
207
+ ```
208
+
209
+ Project-wide analyze skips test paths by default; `--include-tests` to include. See README **Finding easy wins with analyze**.
@@ -0,0 +1,22 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ matrix:
13
+ python-version: ["3.10", "3.11", "3.12"]
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - uses: actions/setup-python@v5
17
+ with:
18
+ python-version: ${{ matrix.python-version }}
19
+ - name: Install dependencies
20
+ run: pip install -e ".[dev]"
21
+ - name: Run publish gate
22
+ run: ./scripts/test.sh
@@ -5,11 +5,16 @@ repos:
5
5
  - id: ruff
6
6
  args: [--fix]
7
7
  - id: ruff-format
8
+ - repo: https://github.com/DetachHead/basedpyright-pre-commit-mirror
9
+ rev: 1.39.9
10
+ hooks:
11
+ - id: basedpyright
8
12
  - repo: local
9
13
  hooks:
10
14
  - id: pytest
11
15
  name: pytest
12
16
  entry: pytest
13
17
  language: python
18
+ additional_dependencies: [pytest>=7]
14
19
  pass_filenames: false
15
20
  always_run: true
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scip-cli
3
- Version: 2.3.0
3
+ Version: 2.5.0
4
4
  Summary: Fast code intelligence via SCIP indexes
5
5
  Project-URL: Homepage, https://github.com/flesler/scip-cli
6
6
  Author: Ariel Flesler
@@ -12,6 +12,7 @@ Classifier: Programming Language :: Python :: 3
12
12
  Classifier: Topic :: Software Development :: Code Generators
13
13
  Requires-Python: >=3.9
14
14
  Provides-Extra: dev
15
+ Requires-Dist: basedpyright>=1.39.9; extra == 'dev'
15
16
  Requires-Dist: build>=1; extra == 'dev'
16
17
  Requires-Dist: pre-commit>=4.0; extra == 'dev'
17
18
  Requires-Dist: pytest>=7; extra == 'dev'
@@ -23,7 +24,7 @@ Description-Content-Type: text/markdown
23
24
  [![PyPI version](https://badge.fury.io/py/scip-cli.svg)](https://badge.fury.io/py/scip-cli)
24
25
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
25
26
 
26
- Token-efficient code intelligence for AI agents. Precise refs, definitions, and repo health analysis via SCIP indexes — TypeScript/JavaScript and Python.
27
+ Token-efficient code intelligence for AI agents. Precise refs, definitions, and repo health analysis via SCIP indexes — TypeScript/JavaScript, Python, Go, and Rust.
27
28
 
28
29
  ## Why
29
30
 
@@ -79,7 +80,7 @@ python -m venv .venv && source .venv/bin/activate
79
80
  pip install -e ".[dev]"
80
81
  ```
81
82
 
82
- Tests and lint: `pytest`, `ruff check .` from the same environment.
83
+ Tests and lint: `pytest`, `ruff check .`, `basedpyright scip_cli/` from the same environment (or `scripts/test.sh` for the publish gate).
83
84
 
84
85
  ### 2. Install prerequisites (optional)
85
86
 
@@ -89,7 +90,7 @@ On **first index**, scip-cli runs language indexers and builds a SQLite cache. Y
89
90
 
90
91
  Install `scip-cli` and run it. On first index, scip-cli will:
91
92
 
92
- - Download `scip-typescript` / `scip-python` via `npx` when not already on PATH
93
+ - Download `scip-typescript` / `scip-python` via `npx`, `scip-go` via `go install`, or `rust-analyzer` via `rustup` when not already on PATH
93
94
  - 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
94
95
  - 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`
95
96
 
@@ -106,6 +107,12 @@ npm install -g @sourcegraph/scip-typescript
106
107
  # Python indexer
107
108
  npm install -g @sourcegraph/scip-python
108
109
 
110
+ # Go indexer
111
+ go install github.com/scip-code/scip-go/cmd/scip-go@latest
112
+
113
+ # Rust indexer (rust-analyzer with SCIP support)
114
+ rustup component add rust-analyzer
115
+
109
116
  # SCIP CLI for index conversion (GitHub release — not on npm)
110
117
  # https://github.com/scip-code/scip/releases (v0.8.1+ recommended)
111
118
  ```
@@ -199,9 +206,9 @@ scip-cli deps greet --paths-only | sort -u
199
206
 
200
207
  ## How It Works
201
208
 
202
- 1. On first query, automatically detects project language from `package.json` (TS/JS) or `pyproject.toml`/`setup.py` (Python)
209
+ 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)
203
210
  2. For TypeScript monorepos, walks the repository for `tsconfig*.json` project roots (nested ancestors deduped; root included only when its `include` is broad)
204
- 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
211
+ 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
205
212
  4. Converts each SCIP output to SQLite with `scip expt-convert`, then merges partial databases when needed
206
213
  5. Caches the result in `~/.cache/scip-cli/projects/<dirname>-<hash>/index.db` (e.g. `my-monorepo-1a3f7a`)
207
214
  6. Subsequent queries are SQLite lookups against that cache (not re-indexing)
@@ -218,11 +225,23 @@ Optional `.scip-cli.json` in the project root:
218
225
  }
219
226
  ```
220
227
 
221
- - `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.
228
+ - `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`.
222
229
  - `indexRoots` — extra TypeScript project directories to include on **first index**, merged with auto-discovered projects.
223
230
  - `onlyIndexRoots` — skip auto-discovery and index **only** `indexRoots` (smaller initial index when you only care about part of a monorepo).
224
231
 
225
- `SCIP_CLI_INDEX_WORKERS` controls parallel `scip-typescript` runs during first index (default: up to 8). Merge into one database is always serial.
232
+ `SCIP_CLI_INDEX_WORKERS` controls parallel indexer runs during first index (default: up to 8). Merge into one database is always serial.
233
+
234
+ Other environment variables:
235
+
236
+ | Variable | Purpose |
237
+ | ------------------------------ | --------------------------------------------------------------------------------------------- |
238
+ | `SCIP_CLI_MAX_HEAP_MB` | Node heap for `scip-typescript` / `scip-python` (overrides `maxHeapMb` in config) |
239
+ | `SCIP_CLI_TS_INDEX_BATCH_SIZE` | Split large TS repos into multiple `scip-typescript` runs (default: all tsconfigs in one run) |
240
+ | `SCIP_CLI_MERGE_BATCH_SIZE` | SQLite ATTACH batch size when merging part DBs (max 9) |
241
+ | `SCIP_CLI_MAX_DEF_LINES` | Max definition lines in `code` output |
242
+ | `SCIP_CLI_DEBUG` | Log SQL queries to stderr |
243
+
244
+ **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`.
226
245
 
227
246
  Large monorepos (>10 tsconfig projects) log per-project progress to stderr during indexing; smaller repos stay quiet aside from the final `Indexed … (size)` line.
228
247
 
@@ -233,7 +252,7 @@ scip-cli reindex --path packages/server
233
252
  scip-cli reindex --path packages/api --path packages/worker
234
253
  ```
235
254
 
236
- `--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`.
255
+ `--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`.
237
256
 
238
257
  Run `scip-cli reindex` after changing scope, `.scip-cli.json` index settings, or when you want a fresh index.
239
258
 
@@ -250,24 +269,24 @@ scip-cli analyze --priority high --limit 25 # dead exports & cycles only
250
269
 
251
270
  Sections are tagged `[high]`, `[medium]`, `[low]` and listed in that order.
252
271
 
253
- | Tier | Project sections | Action |
254
- | ---- | ---------------- | ------ |
255
- | **high** | Cycles, unreferenced, dead exports, stale types | Nuke or fix cycles; delete unused; `_` prefix |
256
- | **medium** | Same-file only, change surface (file target) | Module-private by usage |
257
- | **low** | Test-only consumers, coupling, bottlenecks, hotspots | Noisy on Python (index omits many same-file calls); verify with `rg` |
272
+ | Tier | Project sections | Action |
273
+ | ---------- | ---------------------------------------------------- | -------------------------------------------------------------------- |
274
+ | **high** | Cycles, unreferenced, dead exports, stale types | Nuke or fix cycles; delete unused; `_` prefix |
275
+ | **medium** | Same-file only, change surface (file target) | Module-private by usage |
276
+ | **low** | Test-only consumers, coupling, bottlenecks, hotspots | Noisy on Python (index omits many same-file calls); verify with `rg` |
258
277
 
259
278
  Use `--priority high` for a quick gate; `--priority high,medium` adds context. File drill-down adds change surface and unused imports.
260
279
 
261
280
  **What to look at first**
262
281
 
263
- | Section | Easy pickings |
264
- | ------- | ------------- |
265
- | **Cycles** | Import/mention cycles between production files — break the edge or extract shared code |
266
- | **Unreferenced** | No usage in the index at all — delete |
267
- | **Dead exports** | No external refs — delete or `_` prefix |
268
- | **Stale types** | Classes/types with no external consumer in the index — verify in-file or type-only use before removing |
269
- | **Same-file only** | Used only inside defining file — rename to `_` |
270
- | **Test-only consumers** | Cross-file refs are all from tests — promote to e2e or accept as internal |
282
+ | Section | Easy pickings |
283
+ | ----------------------- | ------------------------------------------------------------------------------------------------------ |
284
+ | **Cycles** | Import/mention cycles between production files — break the edge or extract shared code |
285
+ | **Unreferenced** | No usage in the index at all — delete |
286
+ | **Dead exports** | No external refs — delete or `_` prefix |
287
+ | **Stale types** | Classes/types with no external consumer in the index — verify in-file or type-only use before removing |
288
+ | **Same-file only** | Used only inside defining file — rename to `_` |
289
+ | **Test-only consumers** | Cross-file refs are all from tests — promote to e2e or accept as internal |
271
290
 
272
291
  **Per-file or package drill-down** on hubs or suspects:
273
292
 
@@ -276,7 +295,7 @@ scip-cli analyze scip_cli/queries.py --limit 20 # file: scoped project + per-f
276
295
  scip-cli analyze scip_cli --limit 15 # directory: scoped project + each file under it
277
296
  ```
278
297
 
279
- `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.
298
+ `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.
280
299
 
281
300
  **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.
282
301
 
@@ -3,7 +3,7 @@
3
3
  [![PyPI version](https://badge.fury.io/py/scip-cli.svg)](https://badge.fury.io/py/scip-cli)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
5
 
6
- Token-efficient code intelligence for AI agents. Precise refs, definitions, and repo health analysis via SCIP indexes — TypeScript/JavaScript and Python.
6
+ Token-efficient code intelligence for AI agents. Precise refs, definitions, and repo health analysis via SCIP indexes — TypeScript/JavaScript, Python, Go, and Rust.
7
7
 
8
8
  ## Why
9
9
 
@@ -59,7 +59,7 @@ python -m venv .venv && source .venv/bin/activate
59
59
  pip install -e ".[dev]"
60
60
  ```
61
61
 
62
- Tests and lint: `pytest`, `ruff check .` from the same environment.
62
+ Tests and lint: `pytest`, `ruff check .`, `basedpyright scip_cli/` from the same environment (or `scripts/test.sh` for the publish gate).
63
63
 
64
64
  ### 2. Install prerequisites (optional)
65
65
 
@@ -69,7 +69,7 @@ On **first index**, scip-cli runs language indexers and builds a SQLite cache. Y
69
69
 
70
70
  Install `scip-cli` and run it. On first index, scip-cli will:
71
71
 
72
- - Download `scip-typescript` / `scip-python` via `npx` when not already on PATH
72
+ - Download `scip-typescript` / `scip-python` via `npx`, `scip-go` via `go install`, or `rust-analyzer` via `rustup` when not already on PATH
73
73
  - 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
74
74
  - 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`
75
75
 
@@ -86,6 +86,12 @@ npm install -g @sourcegraph/scip-typescript
86
86
  # Python indexer
87
87
  npm install -g @sourcegraph/scip-python
88
88
 
89
+ # Go indexer
90
+ go install github.com/scip-code/scip-go/cmd/scip-go@latest
91
+
92
+ # Rust indexer (rust-analyzer with SCIP support)
93
+ rustup component add rust-analyzer
94
+
89
95
  # SCIP CLI for index conversion (GitHub release — not on npm)
90
96
  # https://github.com/scip-code/scip/releases (v0.8.1+ recommended)
91
97
  ```
@@ -179,9 +185,9 @@ scip-cli deps greet --paths-only | sort -u
179
185
 
180
186
  ## How It Works
181
187
 
182
- 1. On first query, automatically detects project language from `package.json` (TS/JS) or `pyproject.toml`/`setup.py` (Python)
188
+ 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)
183
189
  2. For TypeScript monorepos, walks the repository for `tsconfig*.json` project roots (nested ancestors deduped; root included only when its `include` is broad)
184
- 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
190
+ 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
185
191
  4. Converts each SCIP output to SQLite with `scip expt-convert`, then merges partial databases when needed
186
192
  5. Caches the result in `~/.cache/scip-cli/projects/<dirname>-<hash>/index.db` (e.g. `my-monorepo-1a3f7a`)
187
193
  6. Subsequent queries are SQLite lookups against that cache (not re-indexing)
@@ -198,11 +204,23 @@ Optional `.scip-cli.json` in the project root:
198
204
  }
199
205
  ```
200
206
 
201
- - `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.
207
+ - `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`.
202
208
  - `indexRoots` — extra TypeScript project directories to include on **first index**, merged with auto-discovered projects.
203
209
  - `onlyIndexRoots` — skip auto-discovery and index **only** `indexRoots` (smaller initial index when you only care about part of a monorepo).
204
210
 
205
- `SCIP_CLI_INDEX_WORKERS` controls parallel `scip-typescript` runs during first index (default: up to 8). Merge into one database is always serial.
211
+ `SCIP_CLI_INDEX_WORKERS` controls parallel indexer runs during first index (default: up to 8). Merge into one database is always serial.
212
+
213
+ Other environment variables:
214
+
215
+ | Variable | Purpose |
216
+ | ------------------------------ | --------------------------------------------------------------------------------------------- |
217
+ | `SCIP_CLI_MAX_HEAP_MB` | Node heap for `scip-typescript` / `scip-python` (overrides `maxHeapMb` in config) |
218
+ | `SCIP_CLI_TS_INDEX_BATCH_SIZE` | Split large TS repos into multiple `scip-typescript` runs (default: all tsconfigs in one run) |
219
+ | `SCIP_CLI_MERGE_BATCH_SIZE` | SQLite ATTACH batch size when merging part DBs (max 9) |
220
+ | `SCIP_CLI_MAX_DEF_LINES` | Max definition lines in `code` output |
221
+ | `SCIP_CLI_DEBUG` | Log SQL queries to stderr |
222
+
223
+ **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`.
206
224
 
207
225
  Large monorepos (>10 tsconfig projects) log per-project progress to stderr during indexing; smaller repos stay quiet aside from the final `Indexed … (size)` line.
208
226
 
@@ -213,7 +231,7 @@ scip-cli reindex --path packages/server
213
231
  scip-cli reindex --path packages/api --path packages/worker
214
232
  ```
215
233
 
216
- `--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`.
234
+ `--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`.
217
235
 
218
236
  Run `scip-cli reindex` after changing scope, `.scip-cli.json` index settings, or when you want a fresh index.
219
237
 
@@ -230,24 +248,24 @@ scip-cli analyze --priority high --limit 25 # dead exports & cycles only
230
248
 
231
249
  Sections are tagged `[high]`, `[medium]`, `[low]` and listed in that order.
232
250
 
233
- | Tier | Project sections | Action |
234
- | ---- | ---------------- | ------ |
235
- | **high** | Cycles, unreferenced, dead exports, stale types | Nuke or fix cycles; delete unused; `_` prefix |
236
- | **medium** | Same-file only, change surface (file target) | Module-private by usage |
237
- | **low** | Test-only consumers, coupling, bottlenecks, hotspots | Noisy on Python (index omits many same-file calls); verify with `rg` |
251
+ | Tier | Project sections | Action |
252
+ | ---------- | ---------------------------------------------------- | -------------------------------------------------------------------- |
253
+ | **high** | Cycles, unreferenced, dead exports, stale types | Nuke or fix cycles; delete unused; `_` prefix |
254
+ | **medium** | Same-file only, change surface (file target) | Module-private by usage |
255
+ | **low** | Test-only consumers, coupling, bottlenecks, hotspots | Noisy on Python (index omits many same-file calls); verify with `rg` |
238
256
 
239
257
  Use `--priority high` for a quick gate; `--priority high,medium` adds context. File drill-down adds change surface and unused imports.
240
258
 
241
259
  **What to look at first**
242
260
 
243
- | Section | Easy pickings |
244
- | ------- | ------------- |
245
- | **Cycles** | Import/mention cycles between production files — break the edge or extract shared code |
246
- | **Unreferenced** | No usage in the index at all — delete |
247
- | **Dead exports** | No external refs — delete or `_` prefix |
248
- | **Stale types** | Classes/types with no external consumer in the index — verify in-file or type-only use before removing |
249
- | **Same-file only** | Used only inside defining file — rename to `_` |
250
- | **Test-only consumers** | Cross-file refs are all from tests — promote to e2e or accept as internal |
261
+ | Section | Easy pickings |
262
+ | ----------------------- | ------------------------------------------------------------------------------------------------------ |
263
+ | **Cycles** | Import/mention cycles between production files — break the edge or extract shared code |
264
+ | **Unreferenced** | No usage in the index at all — delete |
265
+ | **Dead exports** | No external refs — delete or `_` prefix |
266
+ | **Stale types** | Classes/types with no external consumer in the index — verify in-file or type-only use before removing |
267
+ | **Same-file only** | Used only inside defining file — rename to `_` |
268
+ | **Test-only consumers** | Cross-file refs are all from tests — promote to e2e or accept as internal |
251
269
 
252
270
  **Per-file or package drill-down** on hubs or suspects:
253
271
 
@@ -256,7 +274,7 @@ scip-cli analyze scip_cli/queries.py --limit 20 # file: scoped project + per-f
256
274
  scip-cli analyze scip_cli --limit 15 # directory: scoped project + each file under it
257
275
  ```
258
276
 
259
- `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.
277
+ `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.
260
278
 
261
279
  **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.
262
280
 
@@ -32,6 +32,7 @@ packages = ["scip_cli"]
32
32
  [project.optional-dependencies]
33
33
  dev = [
34
34
  "ruff>=0.11",
35
+ "basedpyright>=1.39.9",
35
36
  "pytest>=7",
36
37
  "pre-commit>=4.0",
37
38
  "build>=1",
@@ -57,3 +58,20 @@ addopts = "-q --tb=line --no-header"
57
58
  markers = [
58
59
  "integration: uses the shared indexed TS fixture (requires npx / scip-typescript)",
59
60
  ]
61
+
62
+ [tool.basedpyright]
63
+ pythonVersion = "3.10"
64
+ venvPath = "."
65
+ venv = ".venv"
66
+ include = ["scip_cli"]
67
+ exclude = ["tests"]
68
+
69
+ # Gradual typing: basedpyright's unknown* rules fire hundreds of times on CLI/SQL glue
70
+ # pythonVersion=3.10 matches union syntax in annotations; package still supports 3.9 via __future__ annotations
71
+ reportAny = "none"
72
+ reportUnknownParameterType = "none"
73
+ reportUnknownArgumentType = "none"
74
+ reportUnknownVariableType = "none"
75
+ reportUnknownMemberType = "none"
76
+ reportMissingParameterType = "none"
77
+ reportUnusedCallResult = "none"
@@ -1,9 +1,9 @@
1
1
  ---
2
2
  name: scip-cli
3
- description: Read when needing symbols, definitions, refs, members, or SQL health dashboards in TS/JS or Python
3
+ description: Read when needing symbols, definitions, refs, members, or SQL health dashboards in TS/JS, Python, Go, or Rust
4
4
  ---
5
5
 
6
- TypeScript/JavaScript (.ts, .tsx, .js, .jsx) and Python (.py) — not GraphQL, CSS, or other files.
6
+ TypeScript/JavaScript (.ts, .tsx, .js, .jsx), Python (.py), Go (.go), and Rust (.rs) — not GraphQL, CSS, or other files.
7
7
 
8
8
  All commands are sub-commands of `scip-cli`. Run from the project root.
9
9
 
@@ -27,10 +27,10 @@ All commands are sub-commands of `scip-cli`. Run from the project root.
27
27
  - **Bare names** resolve functions, types (aliases + interfaces), and classes. Use dotted qualifiers to disambiguate members: `code Widget.run`, `refs Foo.setBar`, `search MyClass.myMethod`, `members pkg.MyClass`. Type/object fields use the same form: `search Options.verbose`, `code Options.verbose`. Consts/let/var are not kept in the index (too many rows, single-line defs) — use `rg` or read the file. Class methods need `members ClassName`, not bare `code methodName`.
28
28
  - **Ambiguous types** (e.g. `Opts` in multiple hooks) — `code`/`refs` return all matches up to `--limit`; `members` and `analyze` pick the first match with a stderr warning. Use dotted qualifiers or `--path` to narrow.
29
29
  - **Stale index** — the cache is a snapshot; run `scip-cli reindex` after substantive code changes (no automatic invalidation).
30
- - **Query `--path` vs `reindex --path`** — query `--path` filters results only. `reindex --path` persists scope and **replaces** the cached index with only those tsconfig projects; run full `reindex` (no `--path`) to restore.
30
+ - **Query `--path` vs `reindex --path`** — query `--path` filters results only. `reindex --path` is **TypeScript-only**: persists scope and **replaces** the cached index with only those tsconfig projects; run full `reindex` (no `--path`) to restore.
31
31
  - **First run** in a project may auto-index (one-time wait; large monorepos with many `tsconfig.json` files take longer). Projects index in parallel by default (`SCIP_CLI_INDEX_WORKERS`; merge is serial). Repos with more than 10 tsconfig projects log per-project progress to stderr. JS-only projects (no `tsconfig.json`) are supported automatically.
32
32
  - **Monorepos** are indexed by walking for `tsconfig*.json` under the repo (skips `node_modules`, `.git`, etc.). Nested parent/child projects are deduped. Add extra roots or limit indexing with `.scip-cli.json` (see README). Use query `--path packages/api` to scope lookups.
33
- - **Prerequisites**: Node.js (for `npx` indexers). The `scip` converter auto-downloads on first use if missing; `scip-typescript` / `scip-python` download via `npx`. Optional `.scip-cli.json` for extra index roots or heap tuning. `brew install scip` installs an unrelated optimization solver — scip-cli ignores it and downloads the real binary.
33
+ - **Prerequisites**: Node.js (for TypeScript/Python via `npx`), Go toolchain (for Go via `go install`), or Rust toolchain (for Rust via `rustup`). The `scip` converter auto-downloads on first use if missing; `scip-typescript` / `scip-python` download via `npx`; `scip-go` downloads via `go install` to `~/go/bin`; `rust-analyzer` installs via `rustup component add`. Optional `.scip-cli.json` for extra index roots or heap tuning. `brew install scip` installs an unrelated optimization solver — scip-cli ignores it and downloads the real binary.
34
34
 
35
35
  ## Details
36
36
 
@@ -1,3 +1,3 @@
1
1
  """scip-cli: Fast code intelligence via SCIP indexes."""
2
2
 
3
- __version__ = "2.3.0"
3
+ __version__ = "2.5.0"
@@ -1,11 +1,14 @@
1
1
  """CLI entry point for scip-cli."""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  import argparse
4
6
  import logging
5
7
  import os
6
8
  import shutil
7
9
  import sqlite3
8
10
  import sys
11
+ from collections.abc import Callable
9
12
 
10
13
  from . import __version__
11
14
  from .cli_args import add_limit_argument, add_names_only_argument, add_path_argument, add_paths_only_argument
@@ -19,7 +22,7 @@ else:
19
22
  logging.disable(logging.DEBUG)
20
23
 
21
24
 
22
- def main():
25
+ def main() -> None:
23
26
  parser = argparse.ArgumentParser(
24
27
  prog="scip-cli",
25
28
  description="Fast code intelligence via SCIP indexes",
@@ -45,7 +48,7 @@ def main():
45
48
  type=int,
46
49
  default=None,
47
50
  metavar="N",
48
- help=("Max source lines per definition body (default: 80, env SCIP_CLI_MAX_DEF_LINES). Use 0 for unlimited."),
51
+ help="Max source lines per definition body (default: 80, env SCIP_CLI_MAX_DEF_LINES). Use 0 for unlimited.",
49
52
  )
50
53
  code_parser.add_argument(
51
54
  "--full",
@@ -151,7 +154,7 @@ def main():
151
154
  return
152
155
 
153
156
  # Dispatch to command handlers
154
- dispatch = {
157
+ dispatch: dict[str, Callable[..., None]] = {
155
158
  "refs": refs.main,
156
159
  "code": code.main,
157
160
  "search": search.main,
@@ -16,7 +16,7 @@ SYM_DEF_JOIN = """
16
16
  """
17
17
 
18
18
 
19
- def fetch_all(db, sql: str, params=()) -> list:
19
+ def fetch_all(db, sql: str, params=()):
20
20
  return debug_execute(db, sql, params).fetchall()
21
21
 
22
22
 
@@ -298,7 +298,7 @@ def top_symbol_pressure(db, relative_path: str, limit: int = DEFAULT_LIMIT) -> l
298
298
 
299
299
 
300
300
  def _bind_path(fn, relative_path: str):
301
- def run(db, limit: int, **kwargs):
301
+ def run(db, limit: int, **_kwargs):
302
302
  return fn(db, relative_path, limit)
303
303
 
304
304
  return run
@@ -15,7 +15,7 @@ _EXTERNAL_MENTION = """
15
15
  """
16
16
 
17
17
 
18
- def _fetch_all(db, sql: str, params=()) -> list:
18
+ def _fetch_all(db, sql: str, params=()):
19
19
  return debug_execute(db, sql, params).fetchall()
20
20
 
21
21
 
@@ -155,6 +155,21 @@ def has_same_file_reference_usage(db, symbol_id: int, def_doc_id: int) -> bool:
155
155
  return row is not None
156
156
 
157
157
 
158
+ def has_same_file_usage_mention(db, symbol_id: int, def_doc_id: int) -> bool:
159
+ """Non-definition mention in the defining file (in-file callers)."""
160
+ row = _fetch_one(
161
+ db,
162
+ """
163
+ SELECT 1 FROM mentions m
164
+ JOIN chunks c ON m.chunk_id = c.id
165
+ WHERE m.symbol_id = ? AND c.document_id = ? AND m.role != 1
166
+ LIMIT 1
167
+ """,
168
+ (symbol_id, def_doc_id),
169
+ )
170
+ return row is not None
171
+
172
+
158
173
  def file_has_scip_importers(db, relative_path: str, *, live: LiveIndex, def_doc_id: int) -> bool:
159
174
  """True when the index shows another file importing this module or its symbols."""
160
175
  if def_doc_id in live.live_module_docs:
@@ -18,7 +18,12 @@ from .common import (
18
18
  stale_type_noise,
19
19
  )
20
20
  from .graph import FILE_EDGES_SQL, fetch_file_edges, find_longer_cycles
21
- from .live import LiveIndex, file_has_scip_importers, has_same_file_reference_usage
21
+ from .live import (
22
+ LiveIndex,
23
+ file_has_scip_importers,
24
+ has_same_file_reference_usage,
25
+ has_same_file_usage_mention,
26
+ )
22
27
  from .sections import Check, Priority, run_checks
23
28
 
24
29
 
@@ -165,6 +170,8 @@ def _format_dead_export_rows(
165
170
  continue
166
171
  if has_same_file_reference_usage(db, symbol_id, def_doc_id):
167
172
  continue
173
+ if has_same_file_usage_mention(db, symbol_id, def_doc_id):
174
+ continue
168
175
  if live.dead_export_noise(symbol, def_doc_id):
169
176
  continue
170
177
  lines.append(f"{short_name(symbol)} loc={loc} ({path})")
@@ -416,7 +423,6 @@ def run_all(
416
423
  budget=None,
417
424
  ) -> list[tuple[str, list[str], str | None]]:
418
425
  suffix = _scope_suffix(scope)
419
- opts = {"include_tests": include_tests, "scope": scope, "budget": budget}
420
426
  checks = [
421
427
  Check("cycles", Priority.HIGH, f"Cycles (file dependencies){suffix}", cycles),
422
428
  Check("unreferenced", Priority.HIGH, f"Unreferenced symbols (no refs){suffix}", unreferenced_symbols),
@@ -438,4 +444,12 @@ def run_all(
438
444
  Check("bottlenecks", Priority.LOW, f"Bottlenecks (fan-in x fan-out){suffix}", bottlenecks),
439
445
  Check("hotspots", Priority.LOW, f"Hotspots (most referenced){suffix}", hotspots),
440
446
  ]
441
- return run_checks(checks, db, limit, priorities, **opts)
447
+ return run_checks(
448
+ checks,
449
+ db,
450
+ limit,
451
+ priorities,
452
+ include_tests=include_tests,
453
+ scope=scope,
454
+ budget=budget,
455
+ )
@@ -5,7 +5,6 @@ from __future__ import annotations
5
5
  from collections.abc import Callable
6
6
  from dataclasses import dataclass
7
7
  from enum import Enum
8
- from typing import Any
9
8
 
10
9
  from .common import section
11
10
 
@@ -101,20 +100,23 @@ def run_checks(
101
100
  db,
102
101
  limit: int,
103
102
  priorities: set[Priority] | None,
104
- **kwargs: Any,
103
+ *,
104
+ include_tests: bool = False,
105
+ scope: str | None = None,
106
+ budget: RowBudget | None = None,
105
107
  ) -> list[tuple[str, list[str], str | None]]:
106
108
  """Run checks in priority order (high → low), optionally filtered."""
107
109
  selected = [check for check in checks if priorities is None or check.priority in priorities]
108
110
  selected.sort(key=lambda check: (_ORDER.index(check.priority), check.key))
109
- budget: RowBudget = kwargs.pop("budget", None) or RowBudget(remaining=limit)
111
+ budget_obj: RowBudget = budget or RowBudget(remaining=limit)
110
112
  sections: list[tuple[str, list[str], str | None]] = []
111
113
  for check in selected:
112
- if budget.exhausted():
114
+ if budget_obj.exhausted():
113
115
  break
114
- lines = check.run(db, budget.remaining, **kwargs)
116
+ lines = check.run(db, budget_obj.remaining, include_tests=include_tests, scope=scope)
115
117
  if lines != ["(none)"]:
116
- lines = lines[: budget.remaining]
117
- budget.remaining -= len(lines)
118
+ lines = lines[: budget_obj.remaining]
119
+ budget_obj.remaining -= len(lines)
118
120
  preface = check.false_positive_preface if check.false_positive_preface else _preface_for(check.key)
119
121
  sections.append(section(check.labeled_title(), lines, preface=preface))
120
122
  return sections
@@ -8,14 +8,14 @@ from .sections import Check, Priority, run_checks
8
8
 
9
9
 
10
10
  def _bind_symbol(fn, symbol_id: int):
11
- def run(db, limit: int, **kwargs):
11
+ def run(db, limit: int, **_kwargs):
12
12
  return fn(db, symbol_id, limit)
13
13
 
14
14
  return run
15
15
 
16
16
 
17
17
  def _bind_symbol0(fn, symbol_id: int):
18
- def run(db, limit: int, **kwargs):
18
+ def run(db, _limit: int, **_kwargs):
19
19
  return fn(db, symbol_id)
20
20
 
21
21
  return run
@@ -1,5 +1,7 @@
1
1
  """Index cache path resolution."""
2
2
 
3
+ from __future__ import annotations
4
+
3
5
  import contextlib
4
6
  import fcntl
5
7
  import hashlib
@@ -78,13 +80,13 @@ def project_cache_slug(project_root: Path) -> str:
78
80
  return f"{slug}-{digest}"
79
81
 
80
82
 
81
- def get_cache_dir(project_root):
83
+ def get_cache_dir(project_root: Path | str) -> Path:
82
84
  """Get the cache directory for a project (one dir per repo root)."""
83
85
  root = Path(project_root).resolve()
84
86
  return Path.home() / ".cache" / "scip-cli" / "projects" / project_cache_slug(root)
85
87
 
86
88
 
87
- def find_db(project_root=None):
89
+ def find_db(project_root: Path | str | None = None) -> Path | None:
88
90
  """Find the index.db for the given project (or cwd)."""
89
91
  root = project_root or find_project_root()
90
92
  if not root: