scip-cli 2.7.0__tar.gz → 2.9.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 (155) hide show
  1. {scip_cli-2.7.0 → scip_cli-2.9.0}/PKG-INFO +35 -31
  2. {scip_cli-2.7.0 → scip_cli-2.9.0}/README.md +33 -29
  3. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/SKILL.md +34 -30
  4. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/__init__.py +1 -1
  5. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/__main__.py +18 -2
  6. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/commands/reindex.py +21 -7
  7. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/config.py +6 -0
  8. scip_cli-2.9.0/scip_cli/exclude.py +90 -0
  9. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/indexing/convert.py +8 -2
  10. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/indexing/core.py +8 -1
  11. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/indexing/languages.py +6 -6
  12. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/indexing/orchestrate.py +3 -0
  13. scip_cli-2.9.0/scip_cli/indexing/postprocess.py +296 -0
  14. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/indexing/typescript.py +13 -3
  15. scip_cli-2.9.0/scip_cli/metadata.py +105 -0
  16. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/scope.py +11 -31
  17. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/e2e_harness.py +3 -1
  18. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixture_catalog.py +8 -0
  19. scip_cli-2.9.0/tests/fixtures/typescript-project/src/__tests__/fixtureOnly.spec.ts +5 -0
  20. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_config.py +1 -0
  21. scip_cli-2.9.0/tests/test_exclude_globs.py +162 -0
  22. scip_cli-2.9.0/tests/test_metadata.py +81 -0
  23. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_python_index_integration.py +1 -1
  24. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_reindex.py +88 -7
  25. scip_cli-2.7.0/scip_cli/indexing/postprocess.py +0 -152
  26. {scip_cli-2.7.0 → scip_cli-2.9.0}/.agents/commands/migration-problems.md +0 -0
  27. {scip_cli-2.7.0 → scip_cli-2.9.0}/.agents/rules/agent.mdc +0 -0
  28. {scip_cli-2.7.0 → scip_cli-2.9.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  29. {scip_cli-2.7.0 → scip_cli-2.9.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  30. {scip_cli-2.7.0 → scip_cli-2.9.0}/.github/ISSUE_TEMPLATE/question.md +0 -0
  31. {scip_cli-2.7.0 → scip_cli-2.9.0}/.github/pull_request_template.md +0 -0
  32. {scip_cli-2.7.0 → scip_cli-2.9.0}/.github/workflows/ci.yml +0 -0
  33. {scip_cli-2.7.0 → scip_cli-2.9.0}/.gitignore +0 -0
  34. {scip_cli-2.7.0 → scip_cli-2.9.0}/.pre-commit-config.yaml +0 -0
  35. {scip_cli-2.7.0 → scip_cli-2.9.0}/AGENTS.md +0 -0
  36. {scip_cli-2.7.0 → scip_cli-2.9.0}/CODE_OF_CONDUCT.md +0 -0
  37. {scip_cli-2.7.0 → scip_cli-2.9.0}/CONTRIBUTING.md +0 -0
  38. {scip_cli-2.7.0 → scip_cli-2.9.0}/LICENSE +0 -0
  39. {scip_cli-2.7.0 → scip_cli-2.9.0}/SECURITY.md +0 -0
  40. {scip_cli-2.7.0 → scip_cli-2.9.0}/docs/port.md +0 -0
  41. {scip_cli-2.7.0 → scip_cli-2.9.0}/docs/ports/conclusions.md +0 -0
  42. {scip_cli-2.7.0 → scip_cli-2.9.0}/docs/ports/experiment-summary.md +0 -0
  43. {scip_cli-2.7.0 → scip_cli-2.9.0}/docs/ports/go-experiment.md +0 -0
  44. {scip_cli-2.7.0 → scip_cli-2.9.0}/docs/ports/go.md +0 -0
  45. {scip_cli-2.7.0 → scip_cli-2.9.0}/docs/ports/plan.md +0 -0
  46. {scip_cli-2.7.0 → scip_cli-2.9.0}/docs/ports/rust-experiment.md +0 -0
  47. {scip_cli-2.7.0 → scip_cli-2.9.0}/docs/ports/rust.md +0 -0
  48. {scip_cli-2.7.0 → scip_cli-2.9.0}/docs/ports/summary.md +0 -0
  49. {scip_cli-2.7.0 → scip_cli-2.9.0}/docs/ports/zig-experiment.md +0 -0
  50. {scip_cli-2.7.0 → scip_cli-2.9.0}/docs/ports/zig.md +0 -0
  51. {scip_cli-2.7.0 → scip_cli-2.9.0}/pyproject.toml +0 -0
  52. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/analyze/__init__.py +0 -0
  53. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/analyze/common.py +0 -0
  54. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/analyze/file.py +0 -0
  55. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/analyze/graph.py +0 -0
  56. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/analyze/live.py +0 -0
  57. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/analyze/project.py +0 -0
  58. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/analyze/sections.py +0 -0
  59. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/analyze/symbol.py +0 -0
  60. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/analyze/targets.py +0 -0
  61. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/cache.py +0 -0
  62. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/cli_args.py +0 -0
  63. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/commands/__init__.py +0 -0
  64. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/commands/analyze.py +0 -0
  65. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/commands/code.py +0 -0
  66. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/commands/deps.py +0 -0
  67. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/commands/members.py +0 -0
  68. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/commands/rdeps.py +0 -0
  69. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/commands/refs.py +0 -0
  70. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/commands/search.py +0 -0
  71. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/commands/skill.py +0 -0
  72. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/commands/symbols.py +0 -0
  73. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/debug.py +0 -0
  74. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/discover.py +0 -0
  75. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/indexing/__init__.py +0 -0
  76. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/indexing/constants.py +0 -0
  77. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/indexing/runners.py +0 -0
  78. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/merge.py +0 -0
  79. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/output.py +0 -0
  80. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/paths.py +0 -0
  81. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/project.py +0 -0
  82. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/queries.py +0 -0
  83. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/scip_tool.py +0 -0
  84. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/session.py +0 -0
  85. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/source.py +0 -0
  86. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/sql.py +0 -0
  87. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/symbols.py +0 -0
  88. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/targets.py +0 -0
  89. {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/tsconfig.py +0 -0
  90. {scip_cli-2.7.0 → scip_cli-2.9.0}/scripts/bench.sh +0 -0
  91. {scip_cli-2.7.0 → scip_cli-2.9.0}/scripts/bench_postprocess.py +0 -0
  92. {scip_cli-2.7.0 → scip_cli-2.9.0}/scripts/build.sh +0 -0
  93. {scip_cli-2.7.0 → scip_cli-2.9.0}/scripts/publish.sh +0 -0
  94. {scip_cli-2.7.0 → scip_cli-2.9.0}/scripts/test.sh +0 -0
  95. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/__init__.py +0 -0
  96. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/analyze_db.py +0 -0
  97. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/bench_db.py +0 -0
  98. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/conftest.py +0 -0
  99. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/package.json +0 -0
  100. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/app/handler.ts +0 -0
  101. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/config.ts +0 -0
  102. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/consumer.ts +0 -0
  103. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/dead.ts +0 -0
  104. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/domain/i18n/en.ts +0 -0
  105. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/domain/i18n/index.ts +0 -0
  106. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/domain/i18n/useLocale.ts +0 -0
  107. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/domain/labels/index.ts +0 -0
  108. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/events/streamTypes.ts +0 -0
  109. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/gateway/index.ts +0 -0
  110. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/helper.ts +0 -0
  111. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/hooks/useHookA.ts +0 -0
  112. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/hooks/useHookB.ts +0 -0
  113. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/hooks/useItems.ts +0 -0
  114. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/index.ts +0 -0
  115. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/integrations/inferenceClient.ts +0 -0
  116. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/lib/handler.ts +0 -0
  117. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/loaders/useInference.ts +0 -0
  118. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/models/MutationAggregate.ts +0 -0
  119. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/pages/panelPage.ts +0 -0
  120. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/rules/applyRule.ts +0 -0
  121. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/types/a.ts +0 -0
  122. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/types/b.ts +0 -0
  123. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/ui/Button.ts +0 -0
  124. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/ui/LazyPanel.ts +0 -0
  125. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/ui/buttonConsumer.ts +0 -0
  126. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/ui/menuModule.ts +0 -0
  127. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/user.ts +0 -0
  128. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/widget.ts +0 -0
  129. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/widgets/OrphanWidget.ts +0 -0
  130. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/tsconfig.json +0 -0
  131. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/perf_util.py +0 -0
  132. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_analyze.py +0 -0
  133. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_analyze_graph.py +0 -0
  134. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_analyze_perf.py +0 -0
  135. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_bench_queries.py +0 -0
  136. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_cache.py +0 -0
  137. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_composability.py +0 -0
  138. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_discover.py +0 -0
  139. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_e2e.py +0 -0
  140. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_e2e_analyze_patterns.py +0 -0
  141. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_e2e_perf.py +0 -0
  142. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_index_batching.py +0 -0
  143. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_index_prune.py +0 -0
  144. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_indexer_env.py +0 -0
  145. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_indexer_fallback.py +0 -0
  146. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_merge.py +0 -0
  147. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_multi_symbol.py +0 -0
  148. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_pure_functions.py +0 -0
  149. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_qualified_symbols.py +0 -0
  150. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_scip_tool.py +0 -0
  151. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_scope.py +0 -0
  152. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_symbols_freq.py +0 -0
  153. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_targets.py +0 -0
  154. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_tsconfig.py +0 -0
  155. {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_typescript_projects.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: scip-cli
3
- Version: 2.7.0
3
+ Version: 2.9.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
@@ -35,19 +35,19 @@ AI agents waste tokens on grep and file scanning. scip-cli gives them precise, t
35
35
  - **Agent-first**: Install as a skill for Claude Code, Cursor, or any AI agent — precise code navigation without burning context
36
36
  - **Token-efficient**: One record per line, stderr for warnings, pipe-friendly output
37
37
  - **Fast**: Direct SQLite queries — 10x to 213x faster than alternatives
38
- - **`analyze`**: Find dead exports, import cycles, stale types, coupling hotspots — actionable health dashboards at project, file, or symbol scope
38
+ - `analyze`: Find dead exports, import cycles, stale types, coupling hotspots — actionable health dashboards at project, file, or symbol scope
39
39
  - **Auto-indexing**: Indexes on first query, caches in SQLite, zero config
40
40
 
41
41
  ## Ports
42
42
 
43
43
  CLI/output parity ports of this project (Python is the reference):
44
44
 
45
- | Language | Repository |
46
- | ------------------ | ----------------------------------------------------------------- |
47
- | Python (reference) | [flesler/scip-cli](https://github.com/flesler/scip-cli) |
48
- | Go | [flesler/scip-cli-go](https://github.com/flesler/scip-cli-go) |
49
- | Rust | [flesler/scip-cli-rust](https://github.com/flesler/scip-cli-rust) |
50
- | Zig | [flesler/scip-cli-zig](https://github.com/flesler/scip-cli-zig) |
45
+ |Language|Repository|
46
+ |---|---|
47
+ |Python (reference)|[flesler/scip-cli](https://github.com/flesler/scip-cli)|
48
+ |Go|[flesler/scip-cli-go](https://github.com/flesler/scip-cli-go)|
49
+ |Rust|[flesler/scip-cli-rust](https://github.com/flesler/scip-cli-rust)|
50
+ |Zig|[flesler/scip-cli-zig](https://github.com/flesler/scip-cli-zig)|
51
51
 
52
52
  ## For AI Agents
53
53
 
@@ -244,13 +244,13 @@ Optional `.scip-cli.json` in the project root:
244
244
 
245
245
  Other environment variables:
246
246
 
247
- | Variable | Purpose |
248
- | ------------------------------ | --------------------------------------------------------------------------------------------- |
249
- | `SCIP_CLI_MAX_HEAP_MB` | Node heap for `scip-typescript` / `scip-python` (overrides `maxHeapMb` in config) |
250
- | `SCIP_CLI_TS_INDEX_BATCH_SIZE` | Split large TS repos into multiple `scip-typescript` runs (default: all tsconfigs in one run) |
251
- | `SCIP_CLI_MERGE_BATCH_SIZE` | SQLite ATTACH batch size when merging part DBs (max 9) |
252
- | `SCIP_CLI_MAX_DEF_LINES` | Max definition lines in `code` output |
253
- | `SCIP_CLI_DEBUG` | Log SQL queries to stderr |
247
+ |Variable|Purpose|
248
+ |---|---|
249
+ |`SCIP_CLI_MAX_HEAP_MB`|Node heap for `scip-typescript` / `scip-python` (overrides `maxHeapMb` in config)|
250
+ |`SCIP_CLI_TS_INDEX_BATCH_SIZE`|Split large TS repos into multiple `scip-typescript` runs (default: all tsconfigs in one run)|
251
+ |`SCIP_CLI_MERGE_BATCH_SIZE`|SQLite ATTACH batch size when merging part DBs (max 9)|
252
+ |`SCIP_CLI_MAX_DEF_LINES`|Max definition lines in `code` output|
253
+ |`SCIP_CLI_DEBUG`|Log SQL queries to stderr|
254
254
 
255
255
  **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`.
256
256
 
@@ -263,9 +263,12 @@ scip-cli reindex --path packages/server
263
263
  scip-cli reindex --path packages/api --path packages/worker
264
264
  scip-cli reindex --tsconfig 'apps/api/tsconfig.*.json'
265
265
  scip-cli reindex --tsconfig tsconfig.app.json --tsconfig tsconfig.spec.json
266
+ scip-cli reindex --exclude '**/*.test.ts' '**/*.spec.ts'
267
+ scip-cli reindex --exclude # clear persisted excludes
268
+ scip-cli reindex --fresh # full index; clear metadata.json
266
269
  ```
267
270
 
268
- `--path` limits which discovered tsconfig **directories** are indexed (prefix match, same idea as query `--path`). `--tsconfig` skips discovery and indexes those `tsconfig*.json` **files** (repeatable; globs are expanded inside the tool). File-based runs default to one `scip-typescript` process per file so each gets its own heap (`SCIP_CLI_TS_INDEX_BATCH_SIZE` still overrides). Cannot combine `--path` and `--tsconfig`. **TypeScript only.** 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`/`--tsconfig`.
271
+ `--path` limits which discovered tsconfig **directories** are indexed (prefix match, same idea as query `--path`). `--tsconfig` skips discovery and indexes those `tsconfig*.json` **files** (repeatable; globs are expanded inside the tool). File-based runs default to one `scip-typescript` process per file so each gets its own heap (`SCIP_CLI_TS_INDEX_BATCH_SIZE` still overrides). Cannot combine `--path` and `--tsconfig`. **TypeScript only.** Scope and exclude defaults are saved in `metadata.json` next to `index.db` and reused on later `reindex` runs; use `reindex --fresh` to clear them and restore a full index. `--exclude` updates the persisted exclude list; a lone bare `--exclude` clears it.
269
272
 
270
273
  When a tsconfig (after `extends`) has `allowJs: true`, matching `.js`/`.jsx` files under that config's `include`/`files` are indexed too (same roots as `.ts`/`.tsx`). `allowJs: false` or unset leaves JavaScript out. JS-only repos with no `tsconfig.json` still use `--infer-tsconfig`.
271
274
 
@@ -286,24 +289,24 @@ scip-cli analyze --check dead_files --limit 25
286
289
 
287
290
  Sections are tagged `[high]`, `[medium]`, `[low]` and listed in that order.
288
291
 
289
- | Tier | Project sections | Action |
290
- | ---------- | ----------------------------------------------------------- | -------------------------------------------------------------------- |
291
- | **high** | Cycles, unreferenced, dead exports, dead files, stale types | Nuke or fix cycles; delete unused; `_` prefix |
292
- | **medium** | Same-file only, change surface (file target) | Module-private by usage |
293
- | **low** | Test-only consumers, coupling, bottlenecks, hotspots | Noisy on Python (index omits many same-file calls); verify with `rg` |
292
+ |Tier|Project sections|Action|
293
+ |---|---|---|
294
+ |**high**|Cycles, unreferenced, dead exports, dead files, stale types|Nuke or fix cycles; delete unused; `_` prefix|
295
+ |**medium**|Same-file only, change surface (file target)|Module-private by usage|
296
+ |**low**|Test-only consumers, coupling, bottlenecks, hotspots|Noisy on Python (index omits many same-file calls); verify with `rg`|
294
297
 
295
298
  Use `--priority high` for a quick gate; `--priority high,medium` adds context. File drill-down adds change surface and unused imports.
296
299
 
297
300
  **What to look at first**
298
301
 
299
- | Section | Easy pickings |
300
- | ----------------------- | ------------------------------------------------------------------------------------------------------ |
301
- | **Cycles** | Import/mention cycles between production files — break the edge or extract shared code |
302
- | **Unreferenced** | No usage in the index at all — delete |
303
- | **Dead exports** | No external refs — delete or `_` prefix |
304
- | **Stale types** | Classes/types with no external consumer in the index — verify in-file or type-only use before removing |
305
- | **Same-file only** | Used only inside defining file — rename to `_` |
306
- | **Test-only consumers** | Cross-file refs are all from tests — promote to e2e or accept as internal |
302
+ |Section|Easy pickings|
303
+ |---|---|
304
+ |**Cycles**|Import/mention cycles between production files — break the edge or extract shared code|
305
+ |**Unreferenced**|No usage in the index at all — delete|
306
+ |**Dead exports**|No external refs — delete or `_` prefix|
307
+ |**Stale types**|Classes/types with no external consumer in the index — verify in-file or type-only use before removing|
308
+ |**Same-file only**|Used only inside defining file — rename to `_`|
309
+ |**Test-only consumers**|Cross-file refs are all from tests — promote to e2e or accept as internal|
307
310
 
308
311
  **Per-file or package drill-down** on hubs or suspects:
309
312
 
@@ -346,7 +349,8 @@ scip_cli/
346
349
  ├── paths.py # --path scope filtering
347
350
  ├── project.py # Project root + language detection
348
351
  ├── cache.py # Index cache paths
349
- ├── scope.py # Persisted reindex scope (index-scope.json)
352
+ ├── metadata.py # Persisted reindex defaults (metadata.json)
353
+ ├── scope.py # Scoped reindex helpers
350
354
  ├── debug.py # SCIP_CLI_DEBUG stderr helpers
351
355
  ├── indexing.py # SCIP index build + get_db
352
356
  ├── symbols.py # Symbol parsing and kinds
@@ -14,19 +14,19 @@ AI agents waste tokens on grep and file scanning. scip-cli gives them precise, t
14
14
  - **Agent-first**: Install as a skill for Claude Code, Cursor, or any AI agent — precise code navigation without burning context
15
15
  - **Token-efficient**: One record per line, stderr for warnings, pipe-friendly output
16
16
  - **Fast**: Direct SQLite queries — 10x to 213x faster than alternatives
17
- - **`analyze`**: Find dead exports, import cycles, stale types, coupling hotspots — actionable health dashboards at project, file, or symbol scope
17
+ - `analyze`: Find dead exports, import cycles, stale types, coupling hotspots — actionable health dashboards at project, file, or symbol scope
18
18
  - **Auto-indexing**: Indexes on first query, caches in SQLite, zero config
19
19
 
20
20
  ## Ports
21
21
 
22
22
  CLI/output parity ports of this project (Python is the reference):
23
23
 
24
- | Language | Repository |
25
- | ------------------ | ----------------------------------------------------------------- |
26
- | Python (reference) | [flesler/scip-cli](https://github.com/flesler/scip-cli) |
27
- | Go | [flesler/scip-cli-go](https://github.com/flesler/scip-cli-go) |
28
- | Rust | [flesler/scip-cli-rust](https://github.com/flesler/scip-cli-rust) |
29
- | Zig | [flesler/scip-cli-zig](https://github.com/flesler/scip-cli-zig) |
24
+ |Language|Repository|
25
+ |---|---|
26
+ |Python (reference)|[flesler/scip-cli](https://github.com/flesler/scip-cli)|
27
+ |Go|[flesler/scip-cli-go](https://github.com/flesler/scip-cli-go)|
28
+ |Rust|[flesler/scip-cli-rust](https://github.com/flesler/scip-cli-rust)|
29
+ |Zig|[flesler/scip-cli-zig](https://github.com/flesler/scip-cli-zig)|
30
30
 
31
31
  ## For AI Agents
32
32
 
@@ -223,13 +223,13 @@ Optional `.scip-cli.json` in the project root:
223
223
 
224
224
  Other environment variables:
225
225
 
226
- | Variable | Purpose |
227
- | ------------------------------ | --------------------------------------------------------------------------------------------- |
228
- | `SCIP_CLI_MAX_HEAP_MB` | Node heap for `scip-typescript` / `scip-python` (overrides `maxHeapMb` in config) |
229
- | `SCIP_CLI_TS_INDEX_BATCH_SIZE` | Split large TS repos into multiple `scip-typescript` runs (default: all tsconfigs in one run) |
230
- | `SCIP_CLI_MERGE_BATCH_SIZE` | SQLite ATTACH batch size when merging part DBs (max 9) |
231
- | `SCIP_CLI_MAX_DEF_LINES` | Max definition lines in `code` output |
232
- | `SCIP_CLI_DEBUG` | Log SQL queries to stderr |
226
+ |Variable|Purpose|
227
+ |---|---|
228
+ |`SCIP_CLI_MAX_HEAP_MB`|Node heap for `scip-typescript` / `scip-python` (overrides `maxHeapMb` in config)|
229
+ |`SCIP_CLI_TS_INDEX_BATCH_SIZE`|Split large TS repos into multiple `scip-typescript` runs (default: all tsconfigs in one run)|
230
+ |`SCIP_CLI_MERGE_BATCH_SIZE`|SQLite ATTACH batch size when merging part DBs (max 9)|
231
+ |`SCIP_CLI_MAX_DEF_LINES`|Max definition lines in `code` output|
232
+ |`SCIP_CLI_DEBUG`|Log SQL queries to stderr|
233
233
 
234
234
  **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`.
235
235
 
@@ -242,9 +242,12 @@ scip-cli reindex --path packages/server
242
242
  scip-cli reindex --path packages/api --path packages/worker
243
243
  scip-cli reindex --tsconfig 'apps/api/tsconfig.*.json'
244
244
  scip-cli reindex --tsconfig tsconfig.app.json --tsconfig tsconfig.spec.json
245
+ scip-cli reindex --exclude '**/*.test.ts' '**/*.spec.ts'
246
+ scip-cli reindex --exclude # clear persisted excludes
247
+ scip-cli reindex --fresh # full index; clear metadata.json
245
248
  ```
246
249
 
247
- `--path` limits which discovered tsconfig **directories** are indexed (prefix match, same idea as query `--path`). `--tsconfig` skips discovery and indexes those `tsconfig*.json` **files** (repeatable; globs are expanded inside the tool). File-based runs default to one `scip-typescript` process per file so each gets its own heap (`SCIP_CLI_TS_INDEX_BATCH_SIZE` still overrides). Cannot combine `--path` and `--tsconfig`. **TypeScript only.** 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`/`--tsconfig`.
250
+ `--path` limits which discovered tsconfig **directories** are indexed (prefix match, same idea as query `--path`). `--tsconfig` skips discovery and indexes those `tsconfig*.json` **files** (repeatable; globs are expanded inside the tool). File-based runs default to one `scip-typescript` process per file so each gets its own heap (`SCIP_CLI_TS_INDEX_BATCH_SIZE` still overrides). Cannot combine `--path` and `--tsconfig`. **TypeScript only.** Scope and exclude defaults are saved in `metadata.json` next to `index.db` and reused on later `reindex` runs; use `reindex --fresh` to clear them and restore a full index. `--exclude` updates the persisted exclude list; a lone bare `--exclude` clears it.
248
251
 
249
252
  When a tsconfig (after `extends`) has `allowJs: true`, matching `.js`/`.jsx` files under that config's `include`/`files` are indexed too (same roots as `.ts`/`.tsx`). `allowJs: false` or unset leaves JavaScript out. JS-only repos with no `tsconfig.json` still use `--infer-tsconfig`.
250
253
 
@@ -265,24 +268,24 @@ scip-cli analyze --check dead_files --limit 25
265
268
 
266
269
  Sections are tagged `[high]`, `[medium]`, `[low]` and listed in that order.
267
270
 
268
- | Tier | Project sections | Action |
269
- | ---------- | ----------------------------------------------------------- | -------------------------------------------------------------------- |
270
- | **high** | Cycles, unreferenced, dead exports, dead files, stale types | Nuke or fix cycles; delete unused; `_` prefix |
271
- | **medium** | Same-file only, change surface (file target) | Module-private by usage |
272
- | **low** | Test-only consumers, coupling, bottlenecks, hotspots | Noisy on Python (index omits many same-file calls); verify with `rg` |
271
+ |Tier|Project sections|Action|
272
+ |---|---|---|
273
+ |**high**|Cycles, unreferenced, dead exports, dead files, stale types|Nuke or fix cycles; delete unused; `_` prefix|
274
+ |**medium**|Same-file only, change surface (file target)|Module-private by usage|
275
+ |**low**|Test-only consumers, coupling, bottlenecks, hotspots|Noisy on Python (index omits many same-file calls); verify with `rg`|
273
276
 
274
277
  Use `--priority high` for a quick gate; `--priority high,medium` adds context. File drill-down adds change surface and unused imports.
275
278
 
276
279
  **What to look at first**
277
280
 
278
- | Section | Easy pickings |
279
- | ----------------------- | ------------------------------------------------------------------------------------------------------ |
280
- | **Cycles** | Import/mention cycles between production files — break the edge or extract shared code |
281
- | **Unreferenced** | No usage in the index at all — delete |
282
- | **Dead exports** | No external refs — delete or `_` prefix |
283
- | **Stale types** | Classes/types with no external consumer in the index — verify in-file or type-only use before removing |
284
- | **Same-file only** | Used only inside defining file — rename to `_` |
285
- | **Test-only consumers** | Cross-file refs are all from tests — promote to e2e or accept as internal |
281
+ |Section|Easy pickings|
282
+ |---|---|
283
+ |**Cycles**|Import/mention cycles between production files — break the edge or extract shared code|
284
+ |**Unreferenced**|No usage in the index at all — delete|
285
+ |**Dead exports**|No external refs — delete or `_` prefix|
286
+ |**Stale types**|Classes/types with no external consumer in the index — verify in-file or type-only use before removing|
287
+ |**Same-file only**|Used only inside defining file — rename to `_`|
288
+ |**Test-only consumers**|Cross-file refs are all from tests — promote to e2e or accept as internal|
286
289
 
287
290
  **Per-file or package drill-down** on hubs or suspects:
288
291
 
@@ -325,7 +328,8 @@ scip_cli/
325
328
  ├── paths.py # --path scope filtering
326
329
  ├── project.py # Project root + language detection
327
330
  ├── cache.py # Index cache paths
328
- ├── scope.py # Persisted reindex scope (index-scope.json)
331
+ ├── metadata.py # Persisted reindex defaults (metadata.json)
332
+ ├── scope.py # Scoped reindex helpers
329
333
  ├── debug.py # SCIP_CLI_DEBUG stderr helpers
330
334
  ├── indexing.py # SCIP index build + get_db
331
335
  ├── symbols.py # Symbol parsing and kinds
@@ -11,16 +11,16 @@ All commands are sub-commands of `scip-cli`. Run from the project root.
11
11
 
12
12
  ## Quick Decision Guide
13
13
 
14
- | Question | Use | What you get |
15
- | ----------------------------------------- | ------------------- | ----------------------------------------------------------------------------------------- |
16
- | "Where is X defined and what does it do?" | `code X` | Definition snippet (capped at 80 lines by default). Use `members Class` for large classes |
17
- | "Where is X used/called?" | `refs X` | Up to `--limit` file:line refs (default 10). Use `--limit` to raise cap |
18
- | "What's in this file?" | `symbols file` | Up to `--limit` symbols (default 10). Bare filename works (`helper.ts`) |
19
- | "Find symbols by name" | `search name` | Functions, types, interfaces, classes |
20
- | "What files depend on this file?" | `rdeps file` | Importers — bare name works |
21
- | "What does this symbol/file depend on?" | `deps target` | Outbound dependencies — symbols referenced within a function or file |
22
- | "What methods does this class have?" | `members ClassName` | All methods/fields with line ranges |
23
- | "Health / risk / stale code?" | `analyze` | Multi-section SQL dashboard — omit target (project), pass file, or symbol |
14
+ |Question|Use|What you get|
15
+ |---|---|---|
16
+ |"Where is X defined and what does it do?"|`code X`|Definition snippet (capped at 80 lines by default). Use `members Class` for large classes|
17
+ |"Where is X used/called?"|`refs X`|Up to `--limit` file:line refs (default 10). Use `--limit` to raise cap|
18
+ |"What's in this file?"|`symbols file`|Up to `--limit` symbols (default 10). Bare filename works (`helper.ts`)|
19
+ |"Find symbols by name"|`search name`|Functions, types, interfaces, classes|
20
+ |"What files depend on this file?"|`rdeps file`|Importers — bare name works|
21
+ |"What does this symbol/file depend on?"|`deps target`|Outbound dependencies — symbols referenced within a function or file|
22
+ |"What methods does this class have?"|`members ClassName`|All methods/fields with line ranges|
23
+ |"Health / risk / stale code?"|`analyze`|Multi-section SQL dashboard — omit target (project), pass file, or symbol|
24
24
 
25
25
  ## Gotchas
26
26
 
@@ -61,18 +61,18 @@ Default `--limit` is 10 **reference lines** per symbol query (not mention chunks
61
61
 
62
62
  Commands emit one record per line on stdout; warnings and progress go to stderr. Use `--paths-only` / `--names-only` when piping into another `scip-cli` command.
63
63
 
64
- | Goal | Pipeline |
65
- | ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
66
- | Blast radius of a file | `scip-cli rdeps file.ts \|xargs -I{} scip-cli symbols {}` |
67
- | Pre-change / health briefing | `scip-cli analyze` or `scip-cli analyze path/to/file.ts` or `scip-cli analyze SymbolName` |
68
- | Files that import a symbol | `scip-cli refs Foo --paths-only` |
69
- | Symbols in referencing files | `scip-cli refs Foo --paths-only \|xargs -I{} scip-cli symbols {}` (barrel files may have no symbols; prefer `search Foo --paths-only` for definition files) |
70
- | Find classes, list members | `scip-cli search Handler --kind class --names-only \|xargs -I{} scip-cli members {}` |
71
- | Members → definitions | `scip-cli members Widget --names-only \|xargs -I{} scip-cli code Widget.{}` |
72
- | Find functions, show callers | `scip-cli search Publish --kind function --names-only \|xargs -I{} scip-cli refs {} --paths-only` |
73
- | Files touching a topic | `scip-cli search Dynamo --paths-only` |
74
- | Count importers | `scip-cli rdeps file.ts \|wc -l` |
75
- | Outbound dependency files | `scip-cli deps file.ts --paths-only` |
64
+ |Goal|Pipeline|
65
+ |---|---|
66
+ |Blast radius of a file|`scip-cli rdeps file.ts \|xargs -I{} scip-cli symbols {}`|
67
+ |Pre-change / health briefing|`scip-cli analyze` or `scip-cli analyze path/to/file.ts` or `scip-cli analyze SymbolName`|
68
+ |Files that import a symbol|`scip-cli refs Foo --paths-only`|
69
+ |Symbols in referencing files|`scip-cli refs Foo --paths-only \|xargs -I{} scip-cli symbols {}` (barrel files may have no symbols; prefer `search Foo --paths-only` for definition files)|
70
+ |Find classes, list members|`scip-cli search Handler --kind class --names-only \|xargs -I{} scip-cli members {}`|
71
+ |Members → definitions|`scip-cli members Widget --names-only \|xargs -I{} scip-cli code Widget.{}`|
72
+ |Find functions, show callers|`scip-cli search Publish --kind function --names-only \|xargs -I{} scip-cli refs {} --paths-only`|
73
+ |Files touching a topic|`scip-cli search Dynamo --paths-only`|
74
+ |Count importers|`scip-cli rdeps file.ts \|wc -l`|
75
+ |Outbound dependency files|`scip-cli deps file.ts --paths-only`|
76
76
 
77
77
  `rdeps` already prints bare paths. `deps --paths-only` deduplicates to unique files. `refs` defaults to `path:line`; add `--paths-only` to dedupe files. `search` / `members` need `--names-only` or `--paths-only` instead of `awk`.
78
78
 
@@ -136,12 +136,12 @@ Returns `startLine:endLine kind name` for each member. Members are found via SCI
136
136
  analyze [--limit N] [--path PATH] [--include-tests] [--priority LEVEL] [--check NAME] [target]
137
137
  ```
138
138
 
139
- | Target | Output |
140
- | -------------------------------------- | ----------------------------------------------------------------------------------------------- |
141
- | _(omit)_ | Project-wide dashboards only |
142
- | **directory** (`scip_cli`, `src/pkg/`) | Scoped project dashboards + per-file sections for each indexed file under the dir |
143
- | **file** | Scoped project dashboards for that file + per-file sections + top symbols by external consumers |
144
- | **symbol** | Symbol pressure, consumers, dependencies, affected |
139
+ |Target|Output|
140
+ |---|---|
141
+ |_(omit)_|Project-wide dashboards only|
142
+ |**directory** (`scip_cli`, `src/pkg/`)|Scoped project dashboards + per-file sections for each indexed file under the dir|
143
+ |**file**|Scoped project dashboards for that file + per-file sections + top symbols by external consumers|
144
+ |**symbol**|Symbol pressure, consumers, dependencies, affected|
145
145
 
146
146
  Sections are ordered **high → medium → low**. `[high]` cycles, unreferenced, dead exports, dead files, stale types; `[medium]` same-file-only, change surface (file); `[low]` test-only consumers (noisy on Python), coupling, bottlenecks, hotspots.
147
147
 
@@ -160,7 +160,11 @@ Directory detection uses the filesystem when present, otherwise an indexed path
160
160
  ### reindex
161
161
 
162
162
  ```bash
163
- reindex [--path DIR ...] [--tsconfig FILE_OR_GLOB ...] [--with-external]
163
+ reindex [--path DIR ...] [--tsconfig FILE_OR_GLOB ...] [--exclude [GLOB ...]] [--fresh] [--with-external]
164
164
  ```
165
165
 
166
- `--path` and `--tsconfig` cannot be combined (**TypeScript only**). `--tsconfig` takes `tsconfig*.json` files (repeatable; globs expanded inside the tool). File-based runs default to one `scip-typescript` process per file so each gets its own heap (`SCIP_CLI_TS_INDEX_BATCH_SIZE` still overrides). Scope is saved as `index-scope.json` and reused until a full `reindex` with neither flag.
166
+ `--path` and `--tsconfig` cannot be combined (**TypeScript only**). `--tsconfig` takes `tsconfig*.json` files (repeatable; globs expanded inside the tool). File-based runs default to one `scip-typescript` process per file so each gets its own heap (`SCIP_CLI_TS_INDEX_BATCH_SIZE` still overrides). Scope and exclude defaults are persisted in `metadata.json` next to `index.db` and reused on later `reindex` runs.
167
+
168
+ `--exclude GLOB` omits matching files from the SQLite index after conversion (repeatable; merged with `excludeGlobs` in `.scip-cli.json`). Passing `--exclude` updates the persisted exclude list; a lone bare `--exclude` (no globs on that flag) clears it — `--exclude foo --exclude` keeps `foo`. Patterns without `/` match basenames (`*.test.ts`); patterns with `/` match repo-relative paths (`tests/**`, `**/__tests__/**`). Indexers still parse excluded files when production code imports them — post-process removal is authoritative.
169
+
170
+ `--fresh` ignores persisted `metadata.json` and clears it before indexing unless `--path`, `--tsconfig`, or `--exclude` are set on the same command (use `reindex --fresh` to restore a full index).
@@ -1,3 +1,3 @@
1
1
  """scip-cli: Fast code intelligence via SCIP indexes."""
2
2
 
3
- __version__ = "2.7.0"
3
+ __version__ = "2.9.0"
@@ -154,7 +154,7 @@ def main() -> None:
154
154
  "--path",
155
155
  action="append",
156
156
  metavar="PATH",
157
- help="Index only tsconfig projects under PATH (repeatable; persisted until full reindex)",
157
+ help="Index only tsconfig projects under PATH (repeatable; persisted in metadata.json)",
158
158
  )
159
159
  reindex_parser.add_argument(
160
160
  "--tsconfig",
@@ -162,10 +162,26 @@ def main() -> None:
162
162
  metavar="GLOB",
163
163
  help=(
164
164
  "Index these tsconfig*.json files instead of auto-discovery "
165
- "(repeatable, globs ok; persisted until full reindex). "
165
+ "(repeatable, globs ok; persisted in metadata.json). "
166
166
  "Cannot be combined with --path."
167
167
  ),
168
168
  )
169
+ reindex_parser.add_argument(
170
+ "--exclude",
171
+ action="append",
172
+ nargs="*",
173
+ metavar="GLOB",
174
+ help=(
175
+ "Omit indexed files matching GLOB from the SQLite index "
176
+ "(repeatable; merged with excludeGlobs in .scip-cli.json; persisted in metadata.json). "
177
+ "Bare --exclude clears persisted excludes."
178
+ ),
179
+ )
180
+ reindex_parser.add_argument(
181
+ "--fresh",
182
+ action="store_true",
183
+ help="Ignore persisted metadata.json; clear it unless scope/exclude flags are set on this run",
184
+ )
169
185
  reindex_parser.add_argument(
170
186
  "--with-external",
171
187
  action="store_true",
@@ -10,9 +10,9 @@ from ..cache import (
10
10
  promote_next_index,
11
11
  )
12
12
  from ..indexing import index_project, log_index_complete
13
+ from ..metadata import UNSET, apply_metadata_updates
13
14
  from ..paths import normalize_path_scope
14
15
  from ..project import Language, find_project_root_and_language
15
- from ..scope import save_index_scope
16
16
  from ..tsconfig import expand_tsconfig_patterns
17
17
 
18
18
 
@@ -24,6 +24,8 @@ def main(args):
24
24
 
25
25
  path_args = getattr(args, "path", None) or []
26
26
  tsconfig_args = getattr(args, "tsconfig", None) or []
27
+ exclude_groups = getattr(args, "exclude", None)
28
+ fresh = getattr(args, "fresh", False)
27
29
  if path_args and tsconfig_args:
28
30
  print("Error: reindex --path and --tsconfig cannot be combined", file=sys.stderr)
29
31
  sys.exit(1)
@@ -32,6 +34,7 @@ def main(args):
32
34
  print(f"Error: reindex {flag} is only supported for TypeScript projects", file=sys.stderr)
33
35
  sys.exit(1)
34
36
 
37
+ scope_update: list[str] | object | None = UNSET
35
38
  if tsconfig_args:
36
39
  try:
37
40
  tsconfig_paths = expand_tsconfig_patterns(tsconfig_args, root)
@@ -39,12 +42,12 @@ def main(args):
39
42
  print(f"Error: {exc}", file=sys.stderr)
40
43
  sys.exit(1)
41
44
  scope_paths = [path.as_posix() for path in tsconfig_paths]
42
- save_index_scope(root, scope_paths)
45
+ scope_update = scope_paths
43
46
  print(f"Index scope: {', '.join(scope_paths)}", file=sys.stderr)
44
47
  print(
45
48
  (
46
49
  "Warning: scoped reindex replaces the cache with only these tsconfig files; "
47
- "run reindex with no --path/--tsconfig to restore the full index"
50
+ "run reindex --fresh to restore the full index"
48
51
  ),
49
52
  file=sys.stderr,
50
53
  )
@@ -56,17 +59,22 @@ def main(args):
56
59
  print(f"Error: invalid or empty --path: {path!r}", file=sys.stderr)
57
60
  sys.exit(1)
58
61
  scope_paths.append(normalized)
59
- save_index_scope(root, scope_paths)
62
+ scope_update = scope_paths
60
63
  print(f"Index scope: {', '.join(scope_paths)}", file=sys.stderr)
61
64
  print(
62
65
  (
63
66
  "Warning: scoped reindex replaces the cache with only these projects; "
64
- "run reindex with no --path/--tsconfig to restore the full index"
67
+ "run reindex --fresh to restore the full index"
65
68
  ),
66
69
  file=sys.stderr,
67
70
  )
68
- else:
69
- save_index_scope(root, None)
71
+
72
+ exclude_update: list[str] | object = UNSET
73
+ if exclude_groups is not None:
74
+ exclude_globs = [glob for group in exclude_groups for glob in group]
75
+ exclude_update = exclude_globs
76
+ if exclude_globs:
77
+ print(f"Index exclude: {', '.join(exclude_globs)}", file=sys.stderr)
70
78
 
71
79
  cache_dir = get_cache_dir(root)
72
80
  cache_dir.mkdir(parents=True, exist_ok=True)
@@ -76,6 +84,12 @@ def main(args):
76
84
  sys.exit(1)
77
85
 
78
86
  with index_build_lock(cache_dir):
87
+ apply_metadata_updates(
88
+ root,
89
+ fresh=fresh,
90
+ scope_paths=scope_update,
91
+ exclude_globs=exclude_update,
92
+ )
79
93
  cleanup_in_progress_index(cache_dir)
80
94
  try:
81
95
  # Pass --with-external flag to indexer via environment
@@ -16,6 +16,7 @@ class ProjectSettings:
16
16
  max_heap_mb: int | None = None
17
17
  index_roots: list[str] = field(default_factory=list)
18
18
  only_index_roots: bool = False
19
+ exclude_globs: list[str] = field(default_factory=list)
19
20
 
20
21
 
21
22
  def load_project_config(project_root: Path) -> ProjectSettings:
@@ -44,10 +45,15 @@ def load_project_config(project_root: Path) -> ProjectSettings:
44
45
  if type(only_index_roots) is not bool:
45
46
  raise RuntimeError(f"Invalid {CONFIG_FILENAME}: onlyIndexRoots must be a boolean")
46
47
 
48
+ exclude_globs = data.get("excludeGlobs", [])
49
+ if not isinstance(exclude_globs, list) or not all(isinstance(p, str) for p in exclude_globs):
50
+ raise RuntimeError(f"Invalid {CONFIG_FILENAME}: excludeGlobs must be a string array")
51
+
47
52
  return ProjectSettings(
48
53
  max_heap_mb=max_heap_mb,
49
54
  index_roots=index_roots,
50
55
  only_index_roots=only_index_roots,
56
+ exclude_globs=exclude_globs,
51
57
  )
52
58
 
53
59
 
@@ -0,0 +1,90 @@
1
+ """Persisted index exclude globs and path matching."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ from pathlib import Path, PurePosixPath
7
+
8
+ from .config import load_project_config
9
+ from .metadata import IndexMetadata, load_metadata, save_metadata
10
+
11
+
12
+ def load_persisted_exclude_globs(project_root: Path) -> tuple[str, ...]:
13
+ """Load exclude globs from persisted reindex metadata, if any."""
14
+ metadata = load_metadata(project_root)
15
+ return metadata.exclude_globs or ()
16
+
17
+
18
+ def save_persisted_exclude_globs(project_root: Path, globs: list[str] | None) -> None:
19
+ """Persist or clear reindex exclude globs for a project."""
20
+ current = load_metadata(project_root)
21
+ save_metadata(
22
+ project_root,
23
+ IndexMetadata(
24
+ scope_paths=current.scope_paths,
25
+ exclude_globs=tuple(globs) if globs else None,
26
+ ),
27
+ )
28
+
29
+
30
+ def resolve_exclude_globs(project_root: Path) -> tuple[str, ...]:
31
+ """Merge .scip-cli.json excludeGlobs with persisted reindex exclude globs."""
32
+ settings = load_project_config(project_root)
33
+ merged: list[str] = []
34
+ seen: set[str] = set()
35
+ for glob in (*settings.exclude_globs, *load_persisted_exclude_globs(project_root)):
36
+ if glob and glob not in seen:
37
+ seen.add(glob)
38
+ merged.append(glob)
39
+ return tuple(merged)
40
+
41
+
42
+ def _glob_to_regex(pattern: str) -> re.Pattern[str]:
43
+ """Translate a repo-relative glob to a regex (supports **, *, ?)."""
44
+ normalized = pattern.replace("\\", "/").lstrip("/")
45
+ parts: list[str] = []
46
+ index = 0
47
+ while index < len(normalized):
48
+ char = normalized[index]
49
+ if char == "*":
50
+ if index + 1 < len(normalized) and normalized[index + 1] == "*":
51
+ if index + 2 < len(normalized) and normalized[index + 2] == "/":
52
+ parts.append("(?:.+/)?")
53
+ index += 3
54
+ continue
55
+ parts.append(".*")
56
+ index += 2
57
+ continue
58
+ parts.append("[^/]*")
59
+ index += 1
60
+ continue
61
+ if char == "?":
62
+ parts.append("[^/]")
63
+ index += 1
64
+ continue
65
+ if char in ".^$+{}|()[]":
66
+ parts.append(re.escape(char))
67
+ else:
68
+ parts.append(char)
69
+ index += 1
70
+ return re.compile("^" + "".join(parts) + "$")
71
+
72
+
73
+ def path_matches_glob(relative_path: str, pattern: str) -> bool:
74
+ """Return True when a repo-relative path matches an exclude glob.
75
+
76
+ Patterns without '/' match the basename only (e.g. ``*.test.ts``).
77
+ Patterns with '/' match the full posix path (``tests/**``, ``src/*.spec.ts``).
78
+ """
79
+ path = relative_path.replace("\\", "/").lstrip("/")
80
+ glob = pattern.replace("\\", "/").lstrip("/")
81
+ if not glob:
82
+ return False
83
+ target = PurePosixPath(path).name if "/" not in glob else path
84
+ return bool(_glob_to_regex(glob).match(target))
85
+
86
+
87
+ def path_matches_any_glob(relative_path: str, patterns: tuple[str, ...] | list[str]) -> bool:
88
+ if not patterns:
89
+ return False
90
+ return any(path_matches_glob(relative_path, pattern) for pattern in patterns)
@@ -68,7 +68,13 @@ def prefix_document_paths(db_path: Path, prefix: str) -> None:
68
68
  conn.close()
69
69
 
70
70
 
71
- def convert_scip_to_db(scip_path, db_path, *, document_path_prefix: Path | str | None = None):
71
+ def convert_scip_to_db(
72
+ scip_path,
73
+ db_path,
74
+ *,
75
+ document_path_prefix: Path | str | None = None,
76
+ exclude_globs: tuple[str, ...] | None = None,
77
+ ):
72
78
  """Convert a SCIP protobuf file to a SQLite index at db_path."""
73
79
  db_path = Path(db_path)
74
80
  db_path.parent.mkdir(parents=True, exist_ok=True)
@@ -88,7 +94,7 @@ def convert_scip_to_db(scip_path, db_path, *, document_path_prefix: Path | str |
88
94
  if not db_path.exists():
89
95
  raise RuntimeError("Failed to convert index")
90
96
 
91
- postprocess_index(db_path)
97
+ postprocess_index(db_path, exclude_globs=exclude_globs or ())
92
98
  prefix = project_path_prefix(document_path_prefix)
93
99
  if prefix is not None:
94
100
  prefix_document_paths(db_path, prefix)