scip-cli 2.10.0__tar.gz → 2.12.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 (158) hide show
  1. {scip_cli-2.10.0 → scip_cli-2.12.0}/.github/workflows/ci.yml +1 -1
  2. {scip_cli-2.10.0 → scip_cli-2.12.0}/PKG-INFO +3 -2
  3. {scip_cli-2.10.0 → scip_cli-2.12.0}/README.md +2 -1
  4. scip_cli-2.12.0/docs/incremental-reindex-roadmap.md +45 -0
  5. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/SKILL.md +3 -1
  6. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/__init__.py +1 -1
  7. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/__main__.py +7 -0
  8. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/commands/reindex.py +18 -1
  9. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/indexing/constants.py +6 -0
  10. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/indexing/core.py +8 -2
  11. scip_cli-2.12.0/scip_cli/indexing/shards.py +201 -0
  12. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/indexing/typescript.py +179 -8
  13. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/tsconfig.py +12 -0
  14. scip_cli-2.12.0/tests/test_incremental_shards.py +273 -0
  15. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_indexer_fallback.py +1 -2
  16. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_reindex.py +67 -2
  17. {scip_cli-2.10.0 → scip_cli-2.12.0}/.agents/commands/migration-problems.md +0 -0
  18. {scip_cli-2.10.0 → scip_cli-2.12.0}/.agents/rules/agent.mdc +0 -0
  19. {scip_cli-2.10.0 → scip_cli-2.12.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  20. {scip_cli-2.10.0 → scip_cli-2.12.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  21. {scip_cli-2.10.0 → scip_cli-2.12.0}/.github/ISSUE_TEMPLATE/question.md +0 -0
  22. {scip_cli-2.10.0 → scip_cli-2.12.0}/.github/pull_request_template.md +0 -0
  23. {scip_cli-2.10.0 → scip_cli-2.12.0}/.gitignore +0 -0
  24. {scip_cli-2.10.0 → scip_cli-2.12.0}/.pre-commit-config.yaml +0 -0
  25. {scip_cli-2.10.0 → scip_cli-2.12.0}/AGENTS.md +0 -0
  26. {scip_cli-2.10.0 → scip_cli-2.12.0}/CODE_OF_CONDUCT.md +0 -0
  27. {scip_cli-2.10.0 → scip_cli-2.12.0}/CONTRIBUTING.md +0 -0
  28. {scip_cli-2.10.0 → scip_cli-2.12.0}/LICENSE +0 -0
  29. {scip_cli-2.10.0 → scip_cli-2.12.0}/SECURITY.md +0 -0
  30. {scip_cli-2.10.0 → scip_cli-2.12.0}/docs/port.md +0 -0
  31. {scip_cli-2.10.0 → scip_cli-2.12.0}/docs/ports/conclusions.md +0 -0
  32. {scip_cli-2.10.0 → scip_cli-2.12.0}/docs/ports/experiment-summary.md +0 -0
  33. {scip_cli-2.10.0 → scip_cli-2.12.0}/docs/ports/go-experiment.md +0 -0
  34. {scip_cli-2.10.0 → scip_cli-2.12.0}/docs/ports/go.md +0 -0
  35. {scip_cli-2.10.0 → scip_cli-2.12.0}/docs/ports/plan.md +0 -0
  36. {scip_cli-2.10.0 → scip_cli-2.12.0}/docs/ports/rust-experiment.md +0 -0
  37. {scip_cli-2.10.0 → scip_cli-2.12.0}/docs/ports/rust.md +0 -0
  38. {scip_cli-2.10.0 → scip_cli-2.12.0}/docs/ports/summary.md +0 -0
  39. {scip_cli-2.10.0 → scip_cli-2.12.0}/docs/ports/zig-experiment.md +0 -0
  40. {scip_cli-2.10.0 → scip_cli-2.12.0}/docs/ports/zig.md +0 -0
  41. {scip_cli-2.10.0 → scip_cli-2.12.0}/pyproject.toml +0 -0
  42. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/analyze/__init__.py +0 -0
  43. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/analyze/common.py +0 -0
  44. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/analyze/file.py +0 -0
  45. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/analyze/graph.py +0 -0
  46. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/analyze/live.py +0 -0
  47. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/analyze/project.py +0 -0
  48. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/analyze/sections.py +0 -0
  49. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/analyze/symbol.py +0 -0
  50. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/analyze/targets.py +0 -0
  51. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/cache.py +0 -0
  52. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/cli_args.py +0 -0
  53. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/commands/__init__.py +0 -0
  54. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/commands/analyze.py +0 -0
  55. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/commands/code.py +0 -0
  56. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/commands/deps.py +0 -0
  57. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/commands/members.py +0 -0
  58. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/commands/rdeps.py +0 -0
  59. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/commands/refs.py +0 -0
  60. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/commands/search.py +0 -0
  61. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/commands/skill.py +0 -0
  62. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/commands/symbols.py +0 -0
  63. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/config.py +0 -0
  64. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/debug.py +0 -0
  65. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/discover.py +0 -0
  66. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/exclude.py +0 -0
  67. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/indexing/__init__.py +0 -0
  68. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/indexing/convert.py +0 -0
  69. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/indexing/languages.py +0 -0
  70. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/indexing/orchestrate.py +0 -0
  71. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/indexing/postprocess.py +0 -0
  72. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/indexing/runners.py +0 -0
  73. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/merge.py +0 -0
  74. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/metadata.py +0 -0
  75. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/output.py +0 -0
  76. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/paths.py +0 -0
  77. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/project.py +0 -0
  78. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/queries.py +0 -0
  79. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/scip_tool.py +0 -0
  80. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/scope.py +0 -0
  81. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/session.py +0 -0
  82. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/source.py +0 -0
  83. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/sql.py +0 -0
  84. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/symbols.py +0 -0
  85. {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/targets.py +0 -0
  86. {scip_cli-2.10.0 → scip_cli-2.12.0}/scripts/bench.sh +0 -0
  87. {scip_cli-2.10.0 → scip_cli-2.12.0}/scripts/bench_postprocess.py +0 -0
  88. {scip_cli-2.10.0 → scip_cli-2.12.0}/scripts/build.sh +0 -0
  89. {scip_cli-2.10.0 → scip_cli-2.12.0}/scripts/publish.sh +0 -0
  90. {scip_cli-2.10.0 → scip_cli-2.12.0}/scripts/test.sh +0 -0
  91. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/__init__.py +0 -0
  92. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/analyze_db.py +0 -0
  93. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/bench_db.py +0 -0
  94. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/conftest.py +0 -0
  95. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/e2e_harness.py +0 -0
  96. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixture_catalog.py +0 -0
  97. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/package.json +0 -0
  98. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/__tests__/fixtureOnly.spec.ts +0 -0
  99. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/app/handler.ts +0 -0
  100. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/config.ts +0 -0
  101. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/consumer.ts +0 -0
  102. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/dead.ts +0 -0
  103. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/domain/i18n/en.ts +0 -0
  104. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/domain/i18n/index.ts +0 -0
  105. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/domain/i18n/useLocale.ts +0 -0
  106. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/domain/labels/index.ts +0 -0
  107. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/events/streamTypes.ts +0 -0
  108. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/gateway/index.ts +0 -0
  109. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/helper.ts +0 -0
  110. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/hooks/useHookA.ts +0 -0
  111. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/hooks/useHookB.ts +0 -0
  112. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/hooks/useItems.ts +0 -0
  113. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/index.ts +0 -0
  114. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/integrations/inferenceClient.ts +0 -0
  115. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/lib/handler.ts +0 -0
  116. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/loaders/useInference.ts +0 -0
  117. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/models/MutationAggregate.ts +0 -0
  118. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/pages/panelPage.ts +0 -0
  119. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/rules/applyRule.ts +0 -0
  120. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/types/a.ts +0 -0
  121. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/types/b.ts +0 -0
  122. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/ui/Button.ts +0 -0
  123. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/ui/LazyPanel.ts +0 -0
  124. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/ui/buttonConsumer.ts +0 -0
  125. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/ui/menuModule.ts +0 -0
  126. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/user.ts +0 -0
  127. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/widget.ts +0 -0
  128. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/widgets/OrphanWidget.ts +0 -0
  129. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/tsconfig.json +0 -0
  130. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/perf_util.py +0 -0
  131. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_analyze.py +0 -0
  132. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_analyze_graph.py +0 -0
  133. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_analyze_live.py +0 -0
  134. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_analyze_perf.py +0 -0
  135. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_bench_queries.py +0 -0
  136. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_cache.py +0 -0
  137. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_composability.py +0 -0
  138. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_config.py +0 -0
  139. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_discover.py +0 -0
  140. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_e2e.py +0 -0
  141. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_e2e_analyze_patterns.py +0 -0
  142. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_e2e_perf.py +0 -0
  143. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_exclude_globs.py +0 -0
  144. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_index_batching.py +0 -0
  145. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_index_prune.py +0 -0
  146. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_indexer_env.py +0 -0
  147. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_merge.py +0 -0
  148. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_metadata.py +0 -0
  149. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_multi_symbol.py +0 -0
  150. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_pure_functions.py +0 -0
  151. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_python_index_integration.py +0 -0
  152. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_qualified_symbols.py +0 -0
  153. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_scip_tool.py +0 -0
  154. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_scope.py +0 -0
  155. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_symbols_freq.py +0 -0
  156. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_targets.py +0 -0
  157. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_tsconfig.py +0 -0
  158. {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_typescript_projects.py +0 -0
@@ -29,7 +29,7 @@ jobs:
29
29
  python -m venv .venv
30
30
  source .venv/bin/activate
31
31
  pip install -e ".[dev]"
32
- npm install -g @sourcegraph/scip-typescript
32
+ npm install -g github:flesler/scip-typescript#feat/incremental-tsc
33
33
  mkdir -p ~/.local/bin
34
34
  curl -sL https://github.com/scip-code/scip/releases/download/v0.9.0/scip-linux-amd64.tar.gz | tar xz -C ~/.local/bin
35
35
  echo "$VIRTUAL_ENV/bin" >> $GITHUB_PATH
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: scip-cli
3
- Version: 2.10.0
3
+ Version: 2.12.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
@@ -252,7 +252,7 @@ Other environment variables:
252
252
  |`SCIP_CLI_MAX_DEF_LINES`|Max definition lines in `code` output|
253
253
  |`SCIP_CLI_DEBUG`|Log SQL queries to stderr|
254
254
 
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`.
255
+ **Version policy:** only the `scip` converter (`expt-convert`) is pinned to the 0.8.x release line because it defines the SQLite schema. `scip-typescript` installs via npx from the GitHub fork in `scip_cli/indexing/constants.py` when not on PATH (until upstream ships incremental flags). `scip-python` via `npx` at latest; `scip-go` via `go install @latest`. `rust-analyzer` installs via `rustup component add`.
256
256
 
257
257
  Large monorepos (>10 tsconfig projects) log per-project progress to stderr during indexing; smaller repos stay quiet aside from the final `Indexed … (size)` line.
258
258
 
@@ -266,6 +266,7 @@ scip-cli reindex --tsconfig tsconfig.app.json --tsconfig tsconfig.spec.json
266
266
  scip-cli reindex --exclude '**/*.test.ts' '**/*.spec.ts'
267
267
  scip-cli reindex --exclude # clear persisted excludes
268
268
  scip-cli reindex --fresh # full index; clear metadata.json
269
+ scip-cli reindex --incremental # reuse unchanged project shards (see SKILL for language support)
269
270
  ```
270
271
 
271
272
  `--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.
@@ -231,7 +231,7 @@ Other environment variables:
231
231
  |`SCIP_CLI_MAX_DEF_LINES`|Max definition lines in `code` output|
232
232
  |`SCIP_CLI_DEBUG`|Log SQL queries to stderr|
233
233
 
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`.
234
+ **Version policy:** only the `scip` converter (`expt-convert`) is pinned to the 0.8.x release line because it defines the SQLite schema. `scip-typescript` installs via npx from the GitHub fork in `scip_cli/indexing/constants.py` when not on PATH (until upstream ships incremental flags). `scip-python` via `npx` at latest; `scip-go` via `go install @latest`. `rust-analyzer` installs via `rustup component add`.
235
235
 
236
236
  Large monorepos (>10 tsconfig projects) log per-project progress to stderr during indexing; smaller repos stay quiet aside from the final `Indexed … (size)` line.
237
237
 
@@ -245,6 +245,7 @@ scip-cli reindex --tsconfig tsconfig.app.json --tsconfig tsconfig.spec.json
245
245
  scip-cli reindex --exclude '**/*.test.ts' '**/*.spec.ts'
246
246
  scip-cli reindex --exclude # clear persisted excludes
247
247
  scip-cli reindex --fresh # full index; clear metadata.json
248
+ scip-cli reindex --incremental # reuse unchanged project shards (see SKILL for language support)
248
249
  ```
249
250
 
250
251
  `--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.
@@ -0,0 +1,45 @@
1
+ # Incremental reindex roadmap
2
+
3
+ Phased plan to avoid full `scip-typescript` runs on every `reindex`. Shard reuse first; file-level and upstream TSC incremental follow.
4
+
5
+ ## Phase 1 — scip-cli shard reuse (shipped)
6
+
7
+ Per-tsconfig shard cache with content fingerprints. **Shippable:** unit + integration tests in-repo; large monorepos can be validated manually outside the repo (not checked in).
8
+
9
+ - [x] Roadmap doc
10
+ - [x] `shards/manifest.json` in cache dir (fingerprint + part DB path per tsconfig project)
11
+ - [x] `compute_shard_fingerprint()` — tsconfig chain + included source file content hashes + exclude globs
12
+ - [x] `reindex --incremental` — per-project shards (batch size 1), reuse unchanged part DBs
13
+ - [x] Persist shard part DBs under `cache/shards/*.db`
14
+ - [x] Full `reindex` clears shard cache (avoid stale reuse after non-incremental rebuild)
15
+ - [x] `reindex --fresh` clears shard cache
16
+ - [x] Unit tests for fingerprint, manifest merge, and warm `index_typescript` reuse
17
+ - [x] Reindex CLI flag tests (`--incremental`, rejects with `--fresh` / Python)
18
+ - [x] Stderr stats: `Incremental: N shard(s) reused, M reindexed`
19
+ - [x] Update `scip_cli/SKILL.md` and README flag list
20
+
21
+ ## Phase 2 — scip-typescript fork: TSC incremental program (in progress)
22
+
23
+ Fork work lives outside this repo (local clone + PR to `sourcegraph/scip-typescript`).
24
+
25
+ - [x] Fork `sourcegraph/scip-typescript` (local: implement + tests passing)
26
+ - [x] `--incremental` / `--ts-build-info-dir` CLI flags
27
+ - [x] `createIncrementalProgram` + `createIncrementalCompilerHost` in `ProjectIndexer`
28
+ - [x] scip-cli passes `--incremental --ts-build-info-dir <cache>/tsbuildinfo` on `reindex --incremental` dirty shards
29
+ - [x] npx fallback via `github:flesler/scip-typescript#feat/incremental-tsc` (`SCIP_TYPESCRIPT_NPX_PACKAGE`; switch back when upstream merges)
30
+ - [ ] Upstream PR merged or fork published to npm long-term
31
+ - [ ] Benchmark dirty shard: program build time before/after
32
+
33
+ ## Phase 3 — File-level incremental inside a shard
34
+
35
+ - [ ] Document-level upsert in SQLite (replace rows for changed paths; merge is insert-only today)
36
+ - [ ] `reindex --incremental` accepts optional changed-file list (content-hash manifest in cache)
37
+ - [ ] Invalidation: changed files ∪ direct importers (from existing index `mentions`)
38
+ - [ ] scip-typescript fork: skip unchanged files in `ProjectIndexer` loop (`--files` or internal dirty set)
39
+ - [ ] Optional git input via env / scip-atlas (`last_index_commit`, `git diff --name-only`) — not a hard scip-cli dependency
40
+
41
+ ## Phase 4 — Polish
42
+
43
+ - [ ] Dogfood on this repo + optional large-monorepo validation (manual, outside CI)
44
+ - [ ] Submit scip-typescript fork as upstream PR (phase 2, then 3 indexer pieces)
45
+ - [ ] `scripts/bench.sh` scenario for incremental reindex
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: scip-cli
3
- description: Read when needing symbols, definitions, refs, members, or SQL health dashboards in TS/JS, Python, Go, or Rust
3
+ description: Read when needing symbol lookup or SQL health dashboards in code.
4
4
  ---
5
5
 
6
6
  TypeScript/JavaScript (.ts, .tsx, .js, .jsx), Python (.py), Go (.go), and Rust (.rs) — not GraphQL, CSS, or other files.
@@ -168,3 +168,5 @@ reindex [--path DIR ...] [--tsconfig FILE_OR_GLOB ...] [--exclude [GLOB ...]] [-
168
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
169
 
170
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).
171
+
172
+ `--incremental` reuses cached per-project part DBs under `shards/` in the project cache dir (`~/.cache/scip-cli/projects/<slug>/`, alongside `index.db` and `metadata.json`) when inputs are unchanged. **Currently TypeScript only** (Python reindex rejects the flag). Forces one indexer run per discovered project. On dirty shards passes TSC incremental flags; `.tsbuildinfo` under `tsbuildinfo/` in the same cache dir. When `scip-typescript` is not on PATH, npx installs from the GitHub fork (`SCIP_TYPESCRIPT_NPX_PACKAGE` in `indexing/constants.py`) until upstream ships those flags. A plain `reindex` clears the incremental cache; run `reindex --incremental` after an initial full index to benefit. Cannot combine with `--fresh`.
@@ -1,3 +1,3 @@
1
1
  """scip-cli: Fast code intelligence via SCIP indexes."""
2
2
 
3
- __version__ = "2.10.0"
3
+ __version__ = "2.12.0"
@@ -195,6 +195,13 @@ def main() -> None:
195
195
  action="store_true",
196
196
  help="Ignore persisted metadata.json; clear it unless scope/exclude flags are set on this run",
197
197
  )
198
+ reindex_parser.add_argument(
199
+ "--incremental",
200
+ action="store_true",
201
+ help=(
202
+ "Reuse cached per-project index parts when inputs are unchanged (full reindex clears the incremental cache)"
203
+ ),
204
+ )
198
205
  reindex_parser.add_argument(
199
206
  "--with-external",
200
207
  action="store_true",
@@ -11,6 +11,7 @@ from ..cache import (
11
11
  promote_next_index,
12
12
  )
13
13
  from ..indexing import index_project, log_index_complete
14
+ from ..indexing.shards import clear_shard_cache
14
15
  from ..metadata import UNSET, apply_metadata_updates
15
16
  from ..paths import normalize_path_scope
16
17
  from ..project import Language, find_project_root_and_language
@@ -27,6 +28,13 @@ def main(args):
27
28
  tsconfig_args = getattr(args, "tsconfig", None) or []
28
29
  exclude_groups = getattr(args, "exclude", None)
29
30
  fresh = getattr(args, "fresh", False)
31
+ incremental = getattr(args, "incremental", False)
32
+ if incremental and fresh:
33
+ print("Error: reindex --incremental and --fresh cannot be combined", file=sys.stderr)
34
+ sys.exit(1)
35
+ if incremental and lang is not None and lang != Language.TYPESCRIPT:
36
+ print("Error: reindex --incremental is only supported for TypeScript projects", file=sys.stderr)
37
+ sys.exit(1)
30
38
  if path_args and tsconfig_args:
31
39
  print("Error: reindex --path and --tsconfig cannot be combined", file=sys.stderr)
32
40
  sys.exit(1)
@@ -92,6 +100,8 @@ def main(args):
92
100
  exclude_globs=exclude_update,
93
101
  )
94
102
  cleanup_in_progress_index(cache_dir)
103
+ if fresh or not incremental:
104
+ clear_shard_cache(cache_dir)
95
105
  try:
96
106
  # Pass --with-external flag to indexer via environment
97
107
  if getattr(args, "with_external", False):
@@ -100,7 +110,14 @@ def main(args):
100
110
  os.environ["SCIP_CLI_KEEP_EXTERNAL"] = "1"
101
111
 
102
112
  started = time.perf_counter()
103
- _output_db, skipped, total = index_project(root, lang, cache_dir, replace=True, log=False)
113
+ _output_db, skipped, total = index_project(
114
+ root,
115
+ lang,
116
+ cache_dir,
117
+ replace=True,
118
+ log=False,
119
+ incremental=incremental,
120
+ )
104
121
  elapsed_seconds = time.perf_counter() - started
105
122
  except RuntimeError as e:
106
123
  cleanup_in_progress_index(cache_dir)
@@ -7,6 +7,12 @@ SCIP_INSTALL_URL = "https://github.com/scip-code/scip/releases"
7
7
 
8
8
  # Language indexers (npx / go install) use latest on first use.
9
9
  # The scip expt-convert binary is pinned separately in scip_tool.py (DB schema).
10
+ #
11
+ # scip-typescript npx fallback (when the binary is not on PATH). Fork adds
12
+ # --incremental / --ts-build-info-dir for reindex --incremental. Switch back to
13
+ # @sourcegraph/scip-typescript once upstream merges and releases; otherwise
14
+ # publish the fork to npm long-term.
15
+ SCIP_TYPESCRIPT_NPX_PACKAGE = "github:flesler/scip-typescript#feat/incremental-tsc"
10
16
  # scip-typescript accepts many tsconfig paths per invocation (one .scip, one convert, no merge).
11
17
  # Split only when SCIP_CLI_TS_INDEX_BATCH_SIZE is set (OOM/timeout tuning).
12
18
  DEFAULT_TS_INDEX_BATCH_SIZE = None
@@ -97,7 +97,7 @@ def indexer_env(project_root=None):
97
97
  return env
98
98
 
99
99
 
100
- def index_project(root, lang, cache_dir, *, replace=False, log=True):
100
+ def index_project(root, lang, cache_dir, *, replace=False, log=True, incremental=False):
101
101
  """Run the language-specific indexer and convert to DB."""
102
102
  from ..project import Language
103
103
 
@@ -110,7 +110,13 @@ def index_project(root, lang, cache_dir, *, replace=False, log=True):
110
110
  if lang == Language.TYPESCRIPT:
111
111
  projects = typescript_projects(root)
112
112
  output_db, _indexed, skipped, total = index_typescript(
113
- root, cache_dir, projects, env, replace=replace, exclude_globs=exclude_globs
113
+ root,
114
+ cache_dir,
115
+ projects,
116
+ env,
117
+ replace=replace,
118
+ exclude_globs=exclude_globs,
119
+ incremental=incremental,
114
120
  )
115
121
  if log:
116
122
  log_index_complete(
@@ -0,0 +1,201 @@
1
+ """Per-tsconfig shard fingerprints and cached part DBs for incremental reindex."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ import json
7
+ import shutil
8
+ from pathlib import Path
9
+
10
+ from ..exclude import path_matches_glob
11
+ from ..tsconfig import resolved_exclude, resolved_include_or_files, tsconfig_for_project, walk_tsconfig_chain
12
+
13
+ MANIFEST_VERSION = 1
14
+ MANIFEST_FILENAME = "manifest.json"
15
+ SHARDS_DIRNAME = "shards"
16
+ TSBUILDINFO_DIRNAME = "tsbuildinfo"
17
+
18
+ _SOURCE_SUFFIXES = frozenset({".ts", ".tsx", ".js", ".jsx", ".mts", ".cts", ".mjs", ".cjs"})
19
+
20
+
21
+ def shards_dir(cache_dir: Path) -> Path:
22
+ return Path(cache_dir) / SHARDS_DIRNAME
23
+
24
+
25
+ def ts_build_info_dir(cache_dir: Path) -> Path:
26
+ """Directory for per-tsconfig .tsbuildinfo files passed to scip-typescript."""
27
+ return Path(cache_dir) / TSBUILDINFO_DIRNAME
28
+
29
+
30
+ def manifest_path(cache_dir: Path) -> Path:
31
+ return shards_dir(cache_dir) / MANIFEST_FILENAME
32
+
33
+
34
+ def shard_key(project: Path) -> str:
35
+ """Stable manifest key for a TypeScript project path."""
36
+ return project.as_posix()
37
+
38
+
39
+ def shard_db_filename(project: Path) -> str:
40
+ stem = shard_key(project).replace("/", "__").replace("\\", "__")
41
+ return f"{stem}.db"
42
+
43
+
44
+ def shard_db_path(cache_dir: Path, project: Path) -> Path:
45
+ return shards_dir(cache_dir) / shard_db_filename(project)
46
+
47
+
48
+ def clear_shard_cache(cache_dir: Path) -> None:
49
+ """Remove persisted shard part DBs and manifest."""
50
+ root = shards_dir(cache_dir)
51
+ if root.is_dir():
52
+ shutil.rmtree(root)
53
+
54
+
55
+ def load_shard_manifest(cache_dir: Path) -> dict[str, dict[str, str]]:
56
+ """Return shard key → {fingerprint, part_db} from manifest.json."""
57
+ path = manifest_path(cache_dir)
58
+ if not path.is_file():
59
+ return {}
60
+ try:
61
+ data = json.loads(path.read_text(encoding="utf-8"))
62
+ except (OSError, json.JSONDecodeError):
63
+ return {}
64
+ if not isinstance(data, dict):
65
+ return {}
66
+ raw_shards = data.get("shards")
67
+ if not isinstance(raw_shards, dict):
68
+ return {}
69
+ out: dict[str, dict[str, str]] = {}
70
+ for key, entry in raw_shards.items():
71
+ if not isinstance(key, str) or not isinstance(entry, dict):
72
+ continue
73
+ fingerprint = entry.get("fingerprint")
74
+ part_db = entry.get("part_db")
75
+ if isinstance(fingerprint, str) and isinstance(part_db, str):
76
+ out[key] = {"fingerprint": fingerprint, "part_db": part_db}
77
+ return out
78
+
79
+
80
+ def save_shard_manifest(cache_dir: Path, shards: dict[str, dict[str, str]]) -> None:
81
+ """Merge shard entries into manifest.json (full reindex clears stale entries)."""
82
+ root = shards_dir(cache_dir)
83
+ root.mkdir(parents=True, exist_ok=True)
84
+ merged = {**load_shard_manifest(cache_dir), **shards}
85
+ payload = {
86
+ "version": MANIFEST_VERSION,
87
+ "shards": {
88
+ key: {"fingerprint": entry["fingerprint"], "part_db": entry["part_db"]}
89
+ for key, entry in sorted(merged.items())
90
+ },
91
+ }
92
+ manifest_path(cache_dir).write_text(json.dumps(payload, indent=2) + "\n", encoding="utf-8")
93
+
94
+
95
+ def _hash_file(path: Path) -> str:
96
+ digest = hashlib.sha256()
97
+ with path.open("rb") as handle:
98
+ for chunk in iter(lambda: handle.read(1024 * 1024), b""):
99
+ digest.update(chunk)
100
+ return digest.hexdigest()
101
+
102
+
103
+ def _glob_source_files(base_dir: Path, patterns: list[str]) -> list[Path]:
104
+ found: set[Path] = set()
105
+ for pattern in patterns:
106
+ if Path(pattern).is_absolute():
107
+ candidate = Path(pattern)
108
+ if candidate.is_file() and candidate.suffix.lower() in _SOURCE_SUFFIXES:
109
+ found.add(candidate.resolve())
110
+ continue
111
+ for match in base_dir.glob(pattern):
112
+ if match.is_file() and match.suffix.lower() in _SOURCE_SUFFIXES:
113
+ found.add(match.resolve())
114
+ return sorted(found)
115
+
116
+
117
+ def _tsconfig_excludes_path(tsconfig: Path, file_path: Path) -> bool:
118
+ """Return True when a file is excluded by the leaf tsconfig exclude list."""
119
+ exclude_patterns = resolved_exclude(tsconfig)
120
+ if not exclude_patterns:
121
+ return False
122
+ try:
123
+ relative = file_path.relative_to(tsconfig.parent).as_posix()
124
+ except ValueError:
125
+ return False
126
+ return any(path_matches_glob(relative, pattern) for pattern in exclude_patterns)
127
+
128
+
129
+ def list_project_source_files(root: Path, project: Path) -> list[Path]:
130
+ """Source files that contribute to a shard fingerprint (tsconfig include/files)."""
131
+ root = Path(root).resolve()
132
+ tsconfig = tsconfig_for_project(root, project)
133
+ if tsconfig is None:
134
+ return []
135
+
136
+ include, files = resolved_include_or_files(tsconfig)
137
+ base_dir = tsconfig.parent
138
+ if files is not None:
139
+ candidates = _glob_source_files(base_dir, files)
140
+ elif include is not None:
141
+ candidates = _glob_source_files(base_dir, include)
142
+ else:
143
+ candidates = _glob_source_files(base_dir, ["**/*"])
144
+ return [path for path in candidates if not _tsconfig_excludes_path(tsconfig, path)]
145
+
146
+
147
+ def compute_shard_fingerprint(
148
+ root: Path,
149
+ project: Path,
150
+ *,
151
+ exclude_globs: tuple[str, ...] = (),
152
+ ) -> str:
153
+ """Hash tsconfig chain + indexed sources + exclude globs for one project shard."""
154
+ root = Path(root).resolve()
155
+ tsconfig = tsconfig_for_project(root, project)
156
+ digest = hashlib.sha256()
157
+ digest.update(shard_key(project).encode())
158
+ digest.update(b"\0")
159
+
160
+ if tsconfig is not None:
161
+ for path, _data in walk_tsconfig_chain(tsconfig):
162
+ digest.update(path.as_posix().encode())
163
+ digest.update(b"\0")
164
+ digest.update(path.read_bytes())
165
+ digest.update(b"\0")
166
+
167
+ for path in list_project_source_files(root, project):
168
+ try:
169
+ relative = path.relative_to(root).as_posix()
170
+ except ValueError:
171
+ relative = path.as_posix()
172
+ digest.update(relative.encode())
173
+ digest.update(b"\0")
174
+ digest.update(_hash_file(path).encode())
175
+ digest.update(b"\0")
176
+
177
+ for glob in exclude_globs:
178
+ digest.update(glob.encode())
179
+ digest.update(b"\0")
180
+
181
+ return digest.hexdigest()
182
+
183
+
184
+ def resolve_cached_shard_db(
185
+ cache_dir: Path,
186
+ project: Path,
187
+ fingerprint: str,
188
+ manifest: dict[str, dict[str, str]],
189
+ ) -> Path | None:
190
+ """Return a reusable shard part DB when fingerprint and file both match."""
191
+ key = shard_key(project)
192
+ entry = manifest.get(key)
193
+ if entry is None or entry.get("fingerprint") != fingerprint:
194
+ return None
195
+ candidate = shards_dir(cache_dir) / Path(entry["part_db"]).name
196
+ if candidate.is_file():
197
+ return candidate
198
+ fallback = shard_db_path(cache_dir, project)
199
+ if fallback.is_file():
200
+ return fallback
201
+ return None