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.
- {scip_cli-2.10.0 → scip_cli-2.12.0}/.github/workflows/ci.yml +1 -1
- {scip_cli-2.10.0 → scip_cli-2.12.0}/PKG-INFO +3 -2
- {scip_cli-2.10.0 → scip_cli-2.12.0}/README.md +2 -1
- scip_cli-2.12.0/docs/incremental-reindex-roadmap.md +45 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/SKILL.md +3 -1
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/__init__.py +1 -1
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/__main__.py +7 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/commands/reindex.py +18 -1
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/indexing/constants.py +6 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/indexing/core.py +8 -2
- scip_cli-2.12.0/scip_cli/indexing/shards.py +201 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/indexing/typescript.py +179 -8
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/tsconfig.py +12 -0
- scip_cli-2.12.0/tests/test_incremental_shards.py +273 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_indexer_fallback.py +1 -2
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_reindex.py +67 -2
- {scip_cli-2.10.0 → scip_cli-2.12.0}/.agents/commands/migration-problems.md +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/.agents/rules/agent.mdc +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/.github/ISSUE_TEMPLATE/question.md +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/.github/pull_request_template.md +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/.gitignore +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/.pre-commit-config.yaml +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/AGENTS.md +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/CODE_OF_CONDUCT.md +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/CONTRIBUTING.md +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/LICENSE +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/SECURITY.md +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/docs/port.md +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/docs/ports/conclusions.md +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/docs/ports/experiment-summary.md +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/docs/ports/go-experiment.md +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/docs/ports/go.md +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/docs/ports/plan.md +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/docs/ports/rust-experiment.md +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/docs/ports/rust.md +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/docs/ports/summary.md +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/docs/ports/zig-experiment.md +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/docs/ports/zig.md +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/pyproject.toml +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/analyze/__init__.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/analyze/common.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/analyze/file.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/analyze/graph.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/analyze/live.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/analyze/project.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/analyze/sections.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/analyze/symbol.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/analyze/targets.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/cache.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/cli_args.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/commands/__init__.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/commands/analyze.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/commands/code.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/commands/deps.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/commands/members.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/commands/rdeps.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/commands/refs.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/commands/search.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/commands/skill.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/commands/symbols.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/config.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/debug.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/discover.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/exclude.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/indexing/__init__.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/indexing/convert.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/indexing/languages.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/indexing/orchestrate.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/indexing/postprocess.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/indexing/runners.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/merge.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/metadata.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/output.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/paths.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/project.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/queries.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/scip_tool.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/scope.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/session.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/source.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/sql.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/symbols.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scip_cli/targets.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scripts/bench.sh +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scripts/bench_postprocess.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scripts/build.sh +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scripts/publish.sh +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/scripts/test.sh +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/__init__.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/analyze_db.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/bench_db.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/conftest.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/e2e_harness.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixture_catalog.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/package.json +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/__tests__/fixtureOnly.spec.ts +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/app/handler.ts +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/config.ts +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/consumer.ts +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/dead.ts +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/domain/i18n/en.ts +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/domain/i18n/index.ts +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/domain/i18n/useLocale.ts +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/domain/labels/index.ts +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/events/streamTypes.ts +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/gateway/index.ts +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/helper.ts +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/hooks/useHookA.ts +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/hooks/useHookB.ts +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/hooks/useItems.ts +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/index.ts +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/integrations/inferenceClient.ts +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/lib/handler.ts +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/loaders/useInference.ts +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/models/MutationAggregate.ts +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/pages/panelPage.ts +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/rules/applyRule.ts +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/types/a.ts +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/types/b.ts +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/ui/Button.ts +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/ui/LazyPanel.ts +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/ui/buttonConsumer.ts +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/ui/menuModule.ts +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/user.ts +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/widget.ts +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/src/widgets/OrphanWidget.ts +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/fixtures/typescript-project/tsconfig.json +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/perf_util.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_analyze.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_analyze_graph.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_analyze_live.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_analyze_perf.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_bench_queries.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_cache.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_composability.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_config.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_discover.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_e2e.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_e2e_analyze_patterns.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_e2e_perf.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_exclude_globs.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_index_batching.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_index_prune.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_indexer_env.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_merge.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_metadata.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_multi_symbol.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_pure_functions.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_python_index_integration.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_qualified_symbols.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_scip_tool.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_scope.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_symbols_freq.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_targets.py +0 -0
- {scip_cli-2.10.0 → scip_cli-2.12.0}/tests/test_tsconfig.py +0 -0
- {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
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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`.
|
|
@@ -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(
|
|
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,
|
|
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
|