scip-cli 2.9.1__tar.gz → 2.11.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.9.1 → scip_cli-2.11.0}/PKG-INFO +8 -4
- {scip_cli-2.9.1 → scip_cli-2.11.0}/README.md +7 -3
- scip_cli-2.11.0/docs/incremental-reindex-roadmap.md +42 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/pyproject.toml +2 -1
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/SKILL.md +10 -8
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/__init__.py +1 -1
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/__main__.py +21 -1
- scip_cli-2.11.0/scip_cli/analyze/common.py +243 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/analyze/file.py +54 -47
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/analyze/live.py +58 -1
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/analyze/project.py +207 -172
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/analyze/sections.py +75 -42
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/analyze/symbol.py +4 -1
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/commands/analyze.py +42 -4
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/commands/reindex.py +18 -1
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/indexing/core.py +8 -2
- scip_cli-2.11.0/scip_cli/indexing/shards.py +195 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/indexing/typescript.py +153 -4
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/tsconfig.py +12 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scripts/bench.sh +1 -1
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/bench_db.py +123 -2
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/test_analyze.py +107 -10
- scip_cli-2.11.0/tests/test_analyze_live.py +71 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/test_bench_queries.py +41 -2
- scip_cli-2.11.0/tests/test_incremental_shards.py +227 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/test_reindex.py +67 -2
- scip_cli-2.9.1/scip_cli/analyze/common.py +0 -133
- {scip_cli-2.9.1 → scip_cli-2.11.0}/.agents/commands/migration-problems.md +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/.agents/rules/agent.mdc +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/.github/ISSUE_TEMPLATE/question.md +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/.github/pull_request_template.md +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/.github/workflows/ci.yml +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/.gitignore +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/.pre-commit-config.yaml +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/AGENTS.md +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/CODE_OF_CONDUCT.md +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/CONTRIBUTING.md +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/LICENSE +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/SECURITY.md +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/docs/port.md +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/docs/ports/conclusions.md +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/docs/ports/experiment-summary.md +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/docs/ports/go-experiment.md +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/docs/ports/go.md +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/docs/ports/plan.md +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/docs/ports/rust-experiment.md +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/docs/ports/rust.md +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/docs/ports/summary.md +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/docs/ports/zig-experiment.md +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/docs/ports/zig.md +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/analyze/__init__.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/analyze/graph.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/analyze/targets.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/cache.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/cli_args.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/commands/__init__.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/commands/code.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/commands/deps.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/commands/members.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/commands/rdeps.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/commands/refs.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/commands/search.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/commands/skill.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/commands/symbols.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/config.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/debug.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/discover.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/exclude.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/indexing/__init__.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/indexing/constants.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/indexing/convert.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/indexing/languages.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/indexing/orchestrate.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/indexing/postprocess.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/indexing/runners.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/merge.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/metadata.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/output.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/paths.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/project.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/queries.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/scip_tool.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/scope.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/session.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/source.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/sql.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/symbols.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scip_cli/targets.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scripts/bench_postprocess.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scripts/build.sh +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scripts/publish.sh +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/scripts/test.sh +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/__init__.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/analyze_db.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/conftest.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/e2e_harness.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/fixture_catalog.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/fixtures/typescript-project/package.json +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/fixtures/typescript-project/src/__tests__/fixtureOnly.spec.ts +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/fixtures/typescript-project/src/app/handler.ts +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/fixtures/typescript-project/src/config.ts +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/fixtures/typescript-project/src/consumer.ts +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/fixtures/typescript-project/src/dead.ts +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/fixtures/typescript-project/src/domain/i18n/en.ts +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/fixtures/typescript-project/src/domain/i18n/index.ts +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/fixtures/typescript-project/src/domain/i18n/useLocale.ts +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/fixtures/typescript-project/src/domain/labels/index.ts +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/fixtures/typescript-project/src/events/streamTypes.ts +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/fixtures/typescript-project/src/gateway/index.ts +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/fixtures/typescript-project/src/helper.ts +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/fixtures/typescript-project/src/hooks/useHookA.ts +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/fixtures/typescript-project/src/hooks/useHookB.ts +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/fixtures/typescript-project/src/hooks/useItems.ts +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/fixtures/typescript-project/src/index.ts +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/fixtures/typescript-project/src/integrations/inferenceClient.ts +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/fixtures/typescript-project/src/lib/handler.ts +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/fixtures/typescript-project/src/loaders/useInference.ts +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/fixtures/typescript-project/src/models/MutationAggregate.ts +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/fixtures/typescript-project/src/pages/panelPage.ts +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/fixtures/typescript-project/src/rules/applyRule.ts +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/fixtures/typescript-project/src/types/a.ts +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/fixtures/typescript-project/src/types/b.ts +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/fixtures/typescript-project/src/ui/Button.ts +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/fixtures/typescript-project/src/ui/LazyPanel.ts +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/fixtures/typescript-project/src/ui/buttonConsumer.ts +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/fixtures/typescript-project/src/ui/menuModule.ts +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/fixtures/typescript-project/src/user.ts +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/fixtures/typescript-project/src/widget.ts +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/fixtures/typescript-project/src/widgets/OrphanWidget.ts +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/fixtures/typescript-project/tsconfig.json +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/perf_util.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/test_analyze_graph.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/test_analyze_perf.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/test_cache.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/test_composability.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/test_config.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/test_discover.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/test_e2e.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/test_e2e_analyze_patterns.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/test_e2e_perf.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/test_exclude_globs.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/test_index_batching.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/test_index_prune.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/test_indexer_env.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/test_indexer_fallback.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/test_merge.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/test_metadata.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/test_multi_symbol.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/test_pure_functions.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/test_python_index_integration.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/test_qualified_symbols.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/test_scip_tool.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/test_scope.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/test_symbols_freq.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/test_targets.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/test_tsconfig.py +0 -0
- {scip_cli-2.9.1 → scip_cli-2.11.0}/tests/test_typescript_projects.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: scip-cli
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.11.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
|
|
@@ -153,7 +153,7 @@ scip-cli <command> [arguments]
|
|
|
153
153
|
- `rdeps <file>` - Find files that depend on a file (`--path`)
|
|
154
154
|
- `deps <symbol|file>` - Find outbound dependencies (what a symbol or file calls) (`--path`, `--paths-only`)
|
|
155
155
|
- `members <symbol>` - List members of a class/interface (`--path`)
|
|
156
|
-
- `analyze [target]` - SQL health dashboards (`--limit`, `--priority`, `--check`, `--include-tests`). No target: project-wide; directory or file path; symbol name. See [Finding easy wins with `analyze`](#finding-easy-wins-with-analyze).
|
|
156
|
+
- `analyze [target]` - SQL health dashboards (`--limit`, `--per-check-limit`, `--priority`, `--check`, `--include-tests`). No target: project-wide; directory or file path; symbol name. See [Finding easy wins with `analyze`](#finding-easy-wins-with-analyze).
|
|
157
157
|
- `reindex` - Force re-indexing of the current project (`--path` to limit scope; repeatable)
|
|
158
158
|
- `skill [path]` - Install or dump the SKILL.md
|
|
159
159
|
|
|
@@ -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.
|
|
@@ -282,16 +283,19 @@ Use `analyze` on the repo itself before broad refactors or agent review — it s
|
|
|
282
283
|
|
|
283
284
|
```bash
|
|
284
285
|
scip-cli analyze --limit 25
|
|
286
|
+
scip-cli analyze --limit 40 --per-check-limit 5 # spread the global budget across checks
|
|
285
287
|
scip-cli analyze --priority high --limit 25 # dead exports & cycles only
|
|
286
288
|
scip-cli analyze --check cycles --limit 25 # one named section
|
|
287
289
|
scip-cli analyze --check dead_files --limit 25
|
|
288
290
|
```
|
|
289
291
|
|
|
292
|
+
`--limit` is a **global** finding-row budget (truncation markers do not count). `--per-check-limit` (default unlimited) caps each section. Directory analyze spends that same budget on per-file dumps after dashboards.
|
|
293
|
+
|
|
290
294
|
Sections are tagged `[high]`, `[medium]`, `[low]` and listed in that order.
|
|
291
295
|
|
|
292
296
|
|Tier|Project sections|Action|
|
|
293
297
|
|---|---|---|
|
|
294
|
-
|**high**|Cycles,
|
|
298
|
+
|**high**|Cycles, dead exports, dead files, stale types (unreferenced is omitted when dead exports is on)|Nuke or fix cycles; delete unused; `_` prefix|
|
|
295
299
|
|**medium**|Same-file only, change surface (file target)|Module-private by usage|
|
|
296
300
|
|**low**|Test-only consumers, coupling, bottlenecks, hotspots|Noisy on Python (index omits many same-file calls); verify with `rg`|
|
|
297
301
|
|
|
@@ -302,7 +306,7 @@ Use `--priority high` for a quick gate; `--priority high,medium` adds context. F
|
|
|
302
306
|
|Section|Easy pickings|
|
|
303
307
|
|---|---|
|
|
304
308
|
|**Cycles**|Import/mention cycles between production files — break the edge or extract shared code|
|
|
305
|
-
|**Unreferenced**|
|
|
309
|
+
|**Unreferenced**|Same survivors as dead exports; omitted unless `--check unreferenced`. Empty `rdeps` is usually a SCIP miss — verify with `rg -F STEM` (file basename in `(...)`)|
|
|
306
310
|
|**Dead exports**|No external refs — delete or `_` prefix|
|
|
307
311
|
|**Stale types**|Classes/types with no external consumer in the index — verify in-file or type-only use before removing|
|
|
308
312
|
|**Same-file only**|Used only inside defining file — rename to `_`|
|
|
@@ -132,7 +132,7 @@ scip-cli <command> [arguments]
|
|
|
132
132
|
- `rdeps <file>` - Find files that depend on a file (`--path`)
|
|
133
133
|
- `deps <symbol|file>` - Find outbound dependencies (what a symbol or file calls) (`--path`, `--paths-only`)
|
|
134
134
|
- `members <symbol>` - List members of a class/interface (`--path`)
|
|
135
|
-
- `analyze [target]` - SQL health dashboards (`--limit`, `--priority`, `--check`, `--include-tests`). No target: project-wide; directory or file path; symbol name. See [Finding easy wins with `analyze`](#finding-easy-wins-with-analyze).
|
|
135
|
+
- `analyze [target]` - SQL health dashboards (`--limit`, `--per-check-limit`, `--priority`, `--check`, `--include-tests`). No target: project-wide; directory or file path; symbol name. See [Finding easy wins with `analyze`](#finding-easy-wins-with-analyze).
|
|
136
136
|
- `reindex` - Force re-indexing of the current project (`--path` to limit scope; repeatable)
|
|
137
137
|
- `skill [path]` - Install or dump the SKILL.md
|
|
138
138
|
|
|
@@ -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.
|
|
@@ -261,16 +262,19 @@ Use `analyze` on the repo itself before broad refactors or agent review — it s
|
|
|
261
262
|
|
|
262
263
|
```bash
|
|
263
264
|
scip-cli analyze --limit 25
|
|
265
|
+
scip-cli analyze --limit 40 --per-check-limit 5 # spread the global budget across checks
|
|
264
266
|
scip-cli analyze --priority high --limit 25 # dead exports & cycles only
|
|
265
267
|
scip-cli analyze --check cycles --limit 25 # one named section
|
|
266
268
|
scip-cli analyze --check dead_files --limit 25
|
|
267
269
|
```
|
|
268
270
|
|
|
271
|
+
`--limit` is a **global** finding-row budget (truncation markers do not count). `--per-check-limit` (default unlimited) caps each section. Directory analyze spends that same budget on per-file dumps after dashboards.
|
|
272
|
+
|
|
269
273
|
Sections are tagged `[high]`, `[medium]`, `[low]` and listed in that order.
|
|
270
274
|
|
|
271
275
|
|Tier|Project sections|Action|
|
|
272
276
|
|---|---|---|
|
|
273
|
-
|**high**|Cycles,
|
|
277
|
+
|**high**|Cycles, dead exports, dead files, stale types (unreferenced is omitted when dead exports is on)|Nuke or fix cycles; delete unused; `_` prefix|
|
|
274
278
|
|**medium**|Same-file only, change surface (file target)|Module-private by usage|
|
|
275
279
|
|**low**|Test-only consumers, coupling, bottlenecks, hotspots|Noisy on Python (index omits many same-file calls); verify with `rg`|
|
|
276
280
|
|
|
@@ -281,7 +285,7 @@ Use `--priority high` for a quick gate; `--priority high,medium` adds context. F
|
|
|
281
285
|
|Section|Easy pickings|
|
|
282
286
|
|---|---|
|
|
283
287
|
|**Cycles**|Import/mention cycles between production files — break the edge or extract shared code|
|
|
284
|
-
|**Unreferenced**|
|
|
288
|
+
|**Unreferenced**|Same survivors as dead exports; omitted unless `--check unreferenced`. Empty `rdeps` is usually a SCIP miss — verify with `rg -F STEM` (file basename in `(...)`)|
|
|
285
289
|
|**Dead exports**|No external refs — delete or `_` prefix|
|
|
286
290
|
|**Stale types**|Classes/types with no external consumer in the index — verify in-file or type-only use before removing|
|
|
287
291
|
|**Same-file only**|Used only inside defining file — rename to `_`|
|
|
@@ -0,0 +1,42 @@
|
|
|
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
|
|
22
|
+
|
|
23
|
+
- [ ] Fork `sourcegraph/scip-typescript`
|
|
24
|
+
- [ ] `--incremental` / `--ts-build-info-file` CLI flags
|
|
25
|
+
- [ ] `createIncrementalProgram` + `createIncrementalCompilerHost` in `ProjectIndexer`
|
|
26
|
+
- [ ] Default `tsBuildInfoFile` under scip-cli cache dir (not repo root)
|
|
27
|
+
- [ ] scip-cli passes `--ts-build-info-file` when shard is dirty
|
|
28
|
+
- [ ] Benchmark dirty shard: program build time before/after
|
|
29
|
+
|
|
30
|
+
## Phase 3 — File-level incremental inside a shard
|
|
31
|
+
|
|
32
|
+
- [ ] Document-level upsert in SQLite (replace rows for changed paths; merge is insert-only today)
|
|
33
|
+
- [ ] `reindex --incremental` accepts optional changed-file list (content-hash manifest in cache)
|
|
34
|
+
- [ ] Invalidation: changed files ∪ direct importers (from existing index `mentions`)
|
|
35
|
+
- [ ] scip-typescript fork: skip unchanged files in `ProjectIndexer` loop (`--files` or internal dirty set)
|
|
36
|
+
- [ ] Optional git input via env / scip-atlas (`last_index_commit`, `git diff --name-only`) — not a hard scip-cli dependency
|
|
37
|
+
|
|
38
|
+
## Phase 4 — Polish
|
|
39
|
+
|
|
40
|
+
- [ ] Dogfood on this repo + optional large-monorepo validation (manual, outside CI)
|
|
41
|
+
- [ ] Submit scip-typescript fork as upstream PR (phase 2, then 3 indexer pieces)
|
|
42
|
+
- [ ] `scripts/bench.sh` scenario for incremental reindex
|
|
@@ -54,9 +54,10 @@ testpaths = ["tests"]
|
|
|
54
54
|
python_files = ["test_*.py"]
|
|
55
55
|
python_classes = ["Test*"]
|
|
56
56
|
python_functions = ["test_*"]
|
|
57
|
-
addopts = "-q --tb=line --no-header"
|
|
57
|
+
addopts = "-q --tb=line --no-header -m 'not benchmark'"
|
|
58
58
|
markers = [
|
|
59
59
|
"integration: uses the shared indexed TS fixture (requires npx / scip-typescript)",
|
|
60
|
+
"benchmark: scaled :memory: query timings (run via scripts/bench.sh only)",
|
|
60
61
|
]
|
|
61
62
|
|
|
62
63
|
[tool.basedpyright]
|
|
@@ -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.
|
|
@@ -64,7 +64,7 @@ Commands emit one record per line on stdout; warnings and progress go to stderr.
|
|
|
64
64
|
|Goal|Pipeline|
|
|
65
65
|
|---|---|
|
|
66
66
|
|Blast radius of a file|`scip-cli rdeps file.ts \|xargs -I{} scip-cli symbols {}`|
|
|
67
|
-
|Pre-change / health briefing|`scip-cli analyze
|
|
67
|
+
|Pre-change / health briefing|`scip-cli analyze --limit 40 --per-check-limit 5` (or a file / symbol)|
|
|
68
68
|
|Files that import a symbol|`scip-cli refs Foo --paths-only`|
|
|
69
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
70
|
|Find classes, list members|`scip-cli search Handler --kind class --names-only \|xargs -I{} scip-cli members {}`|
|
|
@@ -133,7 +133,7 @@ Returns `startLine:endLine kind name` for each member. Members are found via SCI
|
|
|
133
133
|
### analyze
|
|
134
134
|
|
|
135
135
|
```bash
|
|
136
|
-
analyze [--limit N] [--path PATH] [--include-tests] [--priority LEVEL] [--check NAME] [target]
|
|
136
|
+
analyze [--limit N] [--per-check-limit N] [--path PATH] [--include-tests] [--priority LEVEL] [--check NAME] [target]
|
|
137
137
|
```
|
|
138
138
|
|
|
139
139
|
|Target|Output|
|
|
@@ -143,9 +143,11 @@ analyze [--limit N] [--path PATH] [--include-tests] [--priority LEVEL] [--check
|
|
|
143
143
|
|**file**|Scoped project dashboards for that file + per-file sections + top symbols by external consumers|
|
|
144
144
|
|**symbol**|Symbol pressure, consumers, dependencies, affected|
|
|
145
145
|
|
|
146
|
-
Sections are ordered **high → medium → low**. `[high]` cycles,
|
|
146
|
+
Sections are ordered **high → medium → low**. `[high]` cycles, dead exports, dead files, stale types (unreferenced is skipped when dead exports is on — same survivors; file-target unreferenced-in-file is skipped when dead-in-file is on); `[medium]` same-file-only, change surface (file); `[low]` test-only consumers (noisy on Python), coupling, bottlenecks, hotspots.
|
|
147
147
|
|
|
148
|
-
`--limit` caps **result rows across the whole run** (default 20); remaining checks are skipped once the cap is reached. `(none)` sections and
|
|
148
|
+
`--limit` caps **result rows across the whole run** (default 20); remaining checks are skipped once the cap is reached. `(none)` sections, headers, and `… truncated` / `[note]` lines do not count as findings. `--per-check-limit N` additionally caps each section (default: unlimited) so one noisy check cannot spend the whole budget. Agents: `analyze --limit 40 --per-check-limit 5`.
|
|
149
|
+
|
|
150
|
+
Directory `analyze some/dir` spends the **same global `--limit`** on per-file sections after dashboards (max 30 files). Prefer project/dir dashboards, then `analyze` one file.
|
|
149
151
|
|
|
150
152
|
`--priority high` or `--priority high,medium` (also `1`/`2`/`3`) skips lower tiers.
|
|
151
153
|
|
|
@@ -153,9 +155,7 @@ Sections are ordered **high → medium → low**. `[high]` cycles, unreferenced,
|
|
|
153
155
|
|
|
154
156
|
Directory detection uses the filesystem when present, otherwise an indexed path prefix. `--path` narrows ambiguous file/symbol resolution only (not directory scope — pass the dir as `target`).
|
|
155
157
|
|
|
156
|
-
**
|
|
157
|
-
|
|
158
|
-
**Easy pickings:** **Cycles**, **dead exports**, and **dead files** (production paths) — cross-file cleanup. **Stale types** — types with no external refs in the index. Ignore `analyze/*` section helpers in dead exports. “Dead” = no refs from _other_ files in the index (empty `rdeps` for files), not `vulture`. Sections with hits print a short false-positive caveat; `export const` arrows often look like dead files because SCIP indexes the module only.
|
|
158
|
+
**Easy pickings:** **Cycles**, **dead exports**, and **dead files** (production paths) — cross-file cleanup. **Stale types** — types with no external refs in the index. Ignore `analyze/*` section helpers in dead exports. “Dead” = no refs from _other_ files in the index, not `vulture`. Empty `scip-cli rdeps` on a hit is usually a SCIP miss (`require()`, named imports, barrels), not proof unused. Unreferenced is omitted when dead exports is on (same survivors). Sections with hits print one `Warn:`; for each row run `rg -F STEM` where STEM is the **file basename inside `(...)`**, no extension — ignore hits in that same file. Do not `rg` the symbol name.
|
|
159
159
|
|
|
160
160
|
### reindex
|
|
161
161
|
|
|
@@ -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. A plain `reindex` clears the incremental cache; run `reindex --incremental` after an initial full index to benefit. Cannot combine with `--fresh`.
|
|
@@ -11,7 +11,13 @@ import sys
|
|
|
11
11
|
from collections.abc import Callable
|
|
12
12
|
|
|
13
13
|
from . import __version__
|
|
14
|
-
from .cli_args import
|
|
14
|
+
from .cli_args import (
|
|
15
|
+
add_limit_argument,
|
|
16
|
+
add_names_only_argument,
|
|
17
|
+
add_path_argument,
|
|
18
|
+
add_paths_only_argument,
|
|
19
|
+
positive_int,
|
|
20
|
+
)
|
|
15
21
|
from .commands import analyze, code, deps, members, rdeps, refs, reindex, search, skill, symbols
|
|
16
22
|
from .symbols import SymbolKind
|
|
17
23
|
|
|
@@ -131,6 +137,13 @@ def main() -> None:
|
|
|
131
137
|
)
|
|
132
138
|
add_path_argument(analyze_parser)
|
|
133
139
|
add_limit_argument(analyze_parser, default=20, help_suffix="result rows total")
|
|
140
|
+
analyze_parser.add_argument(
|
|
141
|
+
"--per-check-limit",
|
|
142
|
+
type=positive_int(),
|
|
143
|
+
default=None,
|
|
144
|
+
metavar="N",
|
|
145
|
+
help="Max rows per check (default: unlimited). --limit remains the global row budget",
|
|
146
|
+
)
|
|
134
147
|
analyze_parser.add_argument(
|
|
135
148
|
"--include-tests",
|
|
136
149
|
action="store_true",
|
|
@@ -182,6 +195,13 @@ def main() -> None:
|
|
|
182
195
|
action="store_true",
|
|
183
196
|
help="Ignore persisted metadata.json; clear it unless scope/exclude flags are set on this run",
|
|
184
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
|
+
)
|
|
185
205
|
reindex_parser.add_argument(
|
|
186
206
|
"--with-external",
|
|
187
207
|
action="store_true",
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"""Shared SQL helpers and formatting for analyze checks."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
from collections.abc import Callable, Iterable
|
|
7
|
+
|
|
8
|
+
from ..sql import debug_execute
|
|
9
|
+
from ..symbols import extract_leaf_name, is_module_symbol
|
|
10
|
+
|
|
11
|
+
DEFAULT_LIMIT = 20
|
|
12
|
+
# Cap rows scanned when post-filtering SQL (filters drop many SCIP false hits).
|
|
13
|
+
ANALYZE_MAX_SCAN_ROWS = 10_000
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def collect_until_limit(
|
|
17
|
+
limit: int,
|
|
18
|
+
fetch_page: Callable[[int, int], list[object]],
|
|
19
|
+
accept: Callable[[object], str | None],
|
|
20
|
+
*,
|
|
21
|
+
page_size: int | None = None,
|
|
22
|
+
max_scan: int = ANALYZE_MAX_SCAN_ROWS,
|
|
23
|
+
) -> list[str]:
|
|
24
|
+
"""Fetch SQL pages until `limit` rows pass `accept`, or data is exhausted.
|
|
25
|
+
|
|
26
|
+
Use only when a check post-filters SQL rows (analyze_noise, live heuristics, …).
|
|
27
|
+
Checks that format every SQL row as-is should use a single ``LIMIT ?`` instead.
|
|
28
|
+
"""
|
|
29
|
+
batch = page_size or max(limit, 50)
|
|
30
|
+
lines: list[str] = []
|
|
31
|
+
offset = 0
|
|
32
|
+
scanned = 0
|
|
33
|
+
while len(lines) < limit:
|
|
34
|
+
rows = fetch_page(batch, offset)
|
|
35
|
+
if not rows:
|
|
36
|
+
break
|
|
37
|
+
for row in rows:
|
|
38
|
+
scanned += 1
|
|
39
|
+
if scanned > max_scan:
|
|
40
|
+
return lines
|
|
41
|
+
line = accept(row)
|
|
42
|
+
if line is None:
|
|
43
|
+
continue
|
|
44
|
+
lines.append(line)
|
|
45
|
+
if len(lines) >= limit:
|
|
46
|
+
return lines
|
|
47
|
+
if len(rows) < batch:
|
|
48
|
+
break
|
|
49
|
+
offset += len(rows)
|
|
50
|
+
return lines
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def collect_from_producer(
|
|
54
|
+
limit: int,
|
|
55
|
+
produce: Callable[[int], list[str]],
|
|
56
|
+
accept: Callable[[str], str | None],
|
|
57
|
+
*,
|
|
58
|
+
max_scan: int = ANALYZE_MAX_SCAN_ROWS,
|
|
59
|
+
) -> list[str]:
|
|
60
|
+
"""Fill ``limit`` rows from a capped producer that can return more candidates on retry.
|
|
61
|
+
|
|
62
|
+
Used when results are not SQL-paginated (e.g. graph cycle search). ``produce(cap)``
|
|
63
|
+
must return a deterministic prefix as ``cap`` grows; already-seen strings are skipped.
|
|
64
|
+
"""
|
|
65
|
+
lines: list[str] = []
|
|
66
|
+
seen: set[str] = set()
|
|
67
|
+
scanned = 0
|
|
68
|
+
fetch_cap = limit
|
|
69
|
+
while len(lines) < limit and fetch_cap <= max_scan:
|
|
70
|
+
candidates = produce(fetch_cap)
|
|
71
|
+
if not candidates:
|
|
72
|
+
break
|
|
73
|
+
for candidate in candidates:
|
|
74
|
+
if candidate in seen:
|
|
75
|
+
continue
|
|
76
|
+
seen.add(candidate)
|
|
77
|
+
scanned += 1
|
|
78
|
+
if scanned > max_scan:
|
|
79
|
+
return lines
|
|
80
|
+
line = accept(candidate)
|
|
81
|
+
if line is None:
|
|
82
|
+
continue
|
|
83
|
+
lines.append(line)
|
|
84
|
+
if len(lines) >= limit:
|
|
85
|
+
return lines
|
|
86
|
+
if len(candidates) < fetch_cap:
|
|
87
|
+
break
|
|
88
|
+
fetch_cap += limit
|
|
89
|
+
return lines
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def collect_from_iterable(
|
|
93
|
+
limit: int,
|
|
94
|
+
candidates: Iterable[str],
|
|
95
|
+
accept: Callable[[str], str | None],
|
|
96
|
+
*,
|
|
97
|
+
max_scan: int = ANALYZE_MAX_SCAN_ROWS,
|
|
98
|
+
) -> list[str]:
|
|
99
|
+
"""Filter an in-memory candidate list until ``limit`` rows pass ``accept``."""
|
|
100
|
+
lines: list[str] = []
|
|
101
|
+
scanned = 0
|
|
102
|
+
for candidate in candidates:
|
|
103
|
+
scanned += 1
|
|
104
|
+
if scanned > max_scan:
|
|
105
|
+
break
|
|
106
|
+
line = accept(candidate)
|
|
107
|
+
if line is None:
|
|
108
|
+
continue
|
|
109
|
+
lines.append(line)
|
|
110
|
+
if len(lines) >= limit:
|
|
111
|
+
break
|
|
112
|
+
return lines
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
# Definition document for a symbol (our trimmed schema uses defn_enclosing_ranges).
|
|
116
|
+
SYM_DEF_JOIN = """
|
|
117
|
+
JOIN defn_enclosing_ranges sym_def ON sym_def.symbol_id = gs.id
|
|
118
|
+
JOIN documents def_d ON sym_def.document_id = def_d.id
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def fetch_all(db, sql: str, params=()):
|
|
123
|
+
return debug_execute(db, sql, params).fetchall()
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def fetch_one(db, sql: str, params=()):
|
|
127
|
+
return debug_execute(db, sql, params).fetchone()
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def short_name(symbol: str) -> str:
|
|
131
|
+
if is_module_symbol(symbol):
|
|
132
|
+
return "(module)"
|
|
133
|
+
leaf = extract_leaf_name(symbol)
|
|
134
|
+
if leaf:
|
|
135
|
+
return leaf
|
|
136
|
+
if symbol.endswith("/"):
|
|
137
|
+
return "(module)"
|
|
138
|
+
return symbol.split("/")[-1][:60]
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def is_test_path(relative_path: str) -> bool:
|
|
142
|
+
"""True for common test layout paths and test-like filenames."""
|
|
143
|
+
p = relative_path.replace("\\", "/")
|
|
144
|
+
lower = p.lower()
|
|
145
|
+
if lower.startswith(("tests/", "test/")):
|
|
146
|
+
return True
|
|
147
|
+
if "/tests/" in lower or "/test/" in lower or "/__tests__/" in lower:
|
|
148
|
+
return True
|
|
149
|
+
if "/__fixtures__/" in lower or "/__mocks__/" in lower:
|
|
150
|
+
return True
|
|
151
|
+
name = lower.rsplit("/", 1)[-1]
|
|
152
|
+
if ".test." in name or ".spec." in name or ".mocha." in name:
|
|
153
|
+
return True
|
|
154
|
+
stem = name.rsplit(".", 1)[0]
|
|
155
|
+
if stem.endswith("_test") or stem.endswith("_spec"):
|
|
156
|
+
return True
|
|
157
|
+
if "test-fixture" in name or "test_fixture" in name or "test-mocks" in name:
|
|
158
|
+
return True
|
|
159
|
+
if name.startswith("test_") and name.endswith(".py"):
|
|
160
|
+
return True
|
|
161
|
+
return name == "conftest.py"
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def is_dynamic_loader_path(relative_path: str) -> bool:
|
|
165
|
+
"""True for migration files typically loaded by filename; SCIP has no import edge."""
|
|
166
|
+
p = relative_path.replace("\\", "/").lower()
|
|
167
|
+
return "/migrations/" in p
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def is_python_main_entrypoint(relative_path: str, symbol: str) -> bool:
|
|
171
|
+
"""True for main() in __main__.py — argparse entrypoints SCIP does not link to callers."""
|
|
172
|
+
if short_name(symbol) != "main":
|
|
173
|
+
return False
|
|
174
|
+
return relative_path.replace("\\", "/").endswith("__main__.py")
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def analyze_noise(relative_path: str, symbol: str, *, include_tests: bool = False) -> bool:
|
|
178
|
+
"""True for rows that clutter analyze dashboards (test paths, module-private helpers)."""
|
|
179
|
+
if not include_tests and is_test_path(relative_path):
|
|
180
|
+
return True
|
|
181
|
+
if short_name(symbol).startswith("_"):
|
|
182
|
+
return True
|
|
183
|
+
if is_python_main_entrypoint(relative_path, symbol):
|
|
184
|
+
return True
|
|
185
|
+
if is_dynamic_loader_path(relative_path):
|
|
186
|
+
return True
|
|
187
|
+
if short_name(symbol) in {"<constructor>", "constructor"}:
|
|
188
|
+
return True
|
|
189
|
+
return is_analyze_dashboard_export(relative_path, symbol)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
_ANALYZE_DASHBOARD_SUFFIXES = (
|
|
193
|
+
"analyze/project.py",
|
|
194
|
+
"analyze/file.py",
|
|
195
|
+
"analyze/symbol.py",
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def is_analyze_dashboard_export(relative_path: str, symbol: str) -> bool:
|
|
200
|
+
"""Section runner functions in analyze/* (same-file only, look like dead exports)."""
|
|
201
|
+
path = relative_path.replace("\\", "/")
|
|
202
|
+
if not any(path.endswith(suffix) for suffix in _ANALYZE_DASHBOARD_SUFFIXES):
|
|
203
|
+
return False
|
|
204
|
+
name = short_name(symbol)
|
|
205
|
+
return "()." in symbol or name.endswith(")")
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def is_component_props_type(symbol: str) -> bool:
|
|
209
|
+
"""React-style Props interfaces — rarely referenced outside the component file."""
|
|
210
|
+
name = short_name(symbol)
|
|
211
|
+
return bool(name and name.endswith("Props") and "#" in symbol.split("/")[-1])
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def stale_type_noise(relative_path: str, symbol: str, consumers: int) -> bool:
|
|
215
|
+
"""Dataclass-style types with no SCIP consumers (typing-only)."""
|
|
216
|
+
if is_component_props_type(symbol):
|
|
217
|
+
return True
|
|
218
|
+
if consumers > 0:
|
|
219
|
+
return False
|
|
220
|
+
name = short_name(symbol)
|
|
221
|
+
if not name or name == "(module)" or not name[0].isupper():
|
|
222
|
+
return False
|
|
223
|
+
path = relative_path.replace("\\", "/")
|
|
224
|
+
return path.endswith(("config.py", "scope.py", "analyze/targets.py"))
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def file_pair_noise(file1: str, file2: str, *, include_tests: bool = False) -> bool:
|
|
228
|
+
if include_tests:
|
|
229
|
+
return False
|
|
230
|
+
return is_test_path(file1) or is_test_path(file2)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def cycle_path_noise(cycle_line: str, *, include_tests: bool = False) -> bool:
|
|
234
|
+
if include_tests:
|
|
235
|
+
return False
|
|
236
|
+
parts = re.split(r"\s<->\s|\s->\s", cycle_line)
|
|
237
|
+
return any(is_test_path(part.strip()) for part in parts if part.strip())
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def section(title: str, lines: list[str], *, preface: str | None = None) -> tuple[str, list[str], str | None]:
|
|
241
|
+
if not lines:
|
|
242
|
+
return title, ["(none)"], None
|
|
243
|
+
return title, lines, preface
|