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.
- {scip_cli-2.7.0 → scip_cli-2.9.0}/PKG-INFO +35 -31
- {scip_cli-2.7.0 → scip_cli-2.9.0}/README.md +33 -29
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/SKILL.md +34 -30
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/__init__.py +1 -1
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/__main__.py +18 -2
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/commands/reindex.py +21 -7
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/config.py +6 -0
- scip_cli-2.9.0/scip_cli/exclude.py +90 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/indexing/convert.py +8 -2
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/indexing/core.py +8 -1
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/indexing/languages.py +6 -6
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/indexing/orchestrate.py +3 -0
- scip_cli-2.9.0/scip_cli/indexing/postprocess.py +296 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/indexing/typescript.py +13 -3
- scip_cli-2.9.0/scip_cli/metadata.py +105 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/scope.py +11 -31
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/e2e_harness.py +3 -1
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixture_catalog.py +8 -0
- scip_cli-2.9.0/tests/fixtures/typescript-project/src/__tests__/fixtureOnly.spec.ts +5 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_config.py +1 -0
- scip_cli-2.9.0/tests/test_exclude_globs.py +162 -0
- scip_cli-2.9.0/tests/test_metadata.py +81 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_python_index_integration.py +1 -1
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_reindex.py +88 -7
- scip_cli-2.7.0/scip_cli/indexing/postprocess.py +0 -152
- {scip_cli-2.7.0 → scip_cli-2.9.0}/.agents/commands/migration-problems.md +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/.agents/rules/agent.mdc +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/.github/ISSUE_TEMPLATE/question.md +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/.github/pull_request_template.md +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/.github/workflows/ci.yml +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/.gitignore +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/.pre-commit-config.yaml +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/AGENTS.md +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/CODE_OF_CONDUCT.md +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/CONTRIBUTING.md +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/LICENSE +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/SECURITY.md +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/docs/port.md +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/docs/ports/conclusions.md +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/docs/ports/experiment-summary.md +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/docs/ports/go-experiment.md +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/docs/ports/go.md +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/docs/ports/plan.md +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/docs/ports/rust-experiment.md +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/docs/ports/rust.md +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/docs/ports/summary.md +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/docs/ports/zig-experiment.md +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/docs/ports/zig.md +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/pyproject.toml +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/analyze/__init__.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/analyze/common.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/analyze/file.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/analyze/graph.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/analyze/live.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/analyze/project.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/analyze/sections.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/analyze/symbol.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/analyze/targets.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/cache.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/cli_args.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/commands/__init__.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/commands/analyze.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/commands/code.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/commands/deps.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/commands/members.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/commands/rdeps.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/commands/refs.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/commands/search.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/commands/skill.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/commands/symbols.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/debug.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/discover.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/indexing/__init__.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/indexing/constants.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/indexing/runners.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/merge.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/output.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/paths.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/project.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/queries.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/scip_tool.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/session.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/source.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/sql.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/symbols.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/targets.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scip_cli/tsconfig.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scripts/bench.sh +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scripts/bench_postprocess.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scripts/build.sh +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scripts/publish.sh +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/scripts/test.sh +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/__init__.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/analyze_db.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/bench_db.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/conftest.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/package.json +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/app/handler.ts +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/config.ts +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/consumer.ts +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/dead.ts +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/domain/i18n/en.ts +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/domain/i18n/index.ts +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/domain/i18n/useLocale.ts +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/domain/labels/index.ts +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/events/streamTypes.ts +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/gateway/index.ts +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/helper.ts +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/hooks/useHookA.ts +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/hooks/useHookB.ts +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/hooks/useItems.ts +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/index.ts +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/integrations/inferenceClient.ts +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/lib/handler.ts +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/loaders/useInference.ts +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/models/MutationAggregate.ts +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/pages/panelPage.ts +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/rules/applyRule.ts +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/types/a.ts +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/types/b.ts +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/ui/Button.ts +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/ui/LazyPanel.ts +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/ui/buttonConsumer.ts +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/ui/menuModule.ts +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/user.ts +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/widget.ts +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/src/widgets/OrphanWidget.ts +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/fixtures/typescript-project/tsconfig.json +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/perf_util.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_analyze.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_analyze_graph.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_analyze_perf.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_bench_queries.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_cache.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_composability.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_discover.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_e2e.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_e2e_analyze_patterns.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_e2e_perf.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_index_batching.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_index_prune.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_indexer_env.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_indexer_fallback.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_merge.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_multi_symbol.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_pure_functions.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_qualified_symbols.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_scip_tool.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_scope.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_symbols_freq.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_targets.py +0 -0
- {scip_cli-2.7.0 → scip_cli-2.9.0}/tests/test_tsconfig.py +0 -0
- {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.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: scip-cli
|
|
3
|
-
Version: 2.
|
|
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
|
-
-
|
|
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
|
-
|
|
|
46
|
-
|
|
47
|
-
|
|
|
48
|
-
|
|
|
49
|
-
|
|
|
50
|
-
|
|
|
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
|
-
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
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.**
|
|
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
|
-
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
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
|
-
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
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
|
-
├──
|
|
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
|
-
-
|
|
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
|
-
|
|
|
25
|
-
|
|
26
|
-
|
|
|
27
|
-
|
|
|
28
|
-
|
|
|
29
|
-
|
|
|
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
|
-
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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.**
|
|
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
|
-
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
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
|
-
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
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
|
-
├──
|
|
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
|
-
|
|
|
15
|
-
|
|
16
|
-
|
|
|
17
|
-
|
|
|
18
|
-
|
|
|
19
|
-
|
|
|
20
|
-
|
|
|
21
|
-
|
|
|
22
|
-
|
|
|
23
|
-
|
|
|
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
|
-
|
|
|
65
|
-
|
|
66
|
-
|
|
|
67
|
-
|
|
|
68
|
-
|
|
|
69
|
-
|
|
|
70
|
-
|
|
|
71
|
-
|
|
|
72
|
-
|
|
|
73
|
-
|
|
|
74
|
-
|
|
|
75
|
-
|
|
|
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
|
-
|
|
|
140
|
-
|
|
141
|
-
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
|
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).
|
|
@@ -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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
67
|
+
"run reindex --fresh to restore the full index"
|
|
65
68
|
),
|
|
66
69
|
file=sys.stderr,
|
|
67
70
|
)
|
|
68
|
-
|
|
69
|
-
|
|
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(
|
|
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)
|