scip-cli 2.9.0__tar.gz → 2.10.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.0 → scip_cli-2.10.0}/PKG-INFO +7 -4
- {scip_cli-2.9.0 → scip_cli-2.10.0}/README.md +6 -3
- {scip_cli-2.9.0 → scip_cli-2.10.0}/pyproject.toml +2 -1
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/SKILL.md +7 -7
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/__init__.py +1 -1
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/__main__.py +14 -1
- scip_cli-2.10.0/scip_cli/analyze/common.py +243 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/analyze/file.py +54 -47
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/analyze/live.py +58 -1
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/analyze/project.py +207 -172
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/analyze/sections.py +75 -42
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/analyze/symbol.py +4 -1
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/commands/analyze.py +42 -4
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/commands/reindex.py +4 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/indexing/__init__.py +2 -1
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/indexing/core.py +17 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scripts/bench.sh +1 -1
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/bench_db.py +123 -2
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/test_analyze.py +107 -10
- scip_cli-2.10.0/tests/test_analyze_live.py +71 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/test_bench_queries.py +41 -2
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/test_index_prune.py +9 -1
- scip_cli-2.9.0/scip_cli/analyze/common.py +0 -133
- {scip_cli-2.9.0 → scip_cli-2.10.0}/.agents/commands/migration-problems.md +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/.agents/rules/agent.mdc +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/.github/ISSUE_TEMPLATE/question.md +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/.github/pull_request_template.md +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/.github/workflows/ci.yml +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/.gitignore +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/.pre-commit-config.yaml +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/AGENTS.md +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/CODE_OF_CONDUCT.md +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/CONTRIBUTING.md +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/LICENSE +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/SECURITY.md +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/docs/port.md +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/docs/ports/conclusions.md +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/docs/ports/experiment-summary.md +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/docs/ports/go-experiment.md +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/docs/ports/go.md +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/docs/ports/plan.md +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/docs/ports/rust-experiment.md +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/docs/ports/rust.md +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/docs/ports/summary.md +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/docs/ports/zig-experiment.md +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/docs/ports/zig.md +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/analyze/__init__.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/analyze/graph.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/analyze/targets.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/cache.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/cli_args.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/commands/__init__.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/commands/code.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/commands/deps.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/commands/members.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/commands/rdeps.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/commands/refs.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/commands/search.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/commands/skill.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/commands/symbols.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/config.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/debug.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/discover.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/exclude.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/indexing/constants.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/indexing/convert.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/indexing/languages.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/indexing/orchestrate.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/indexing/postprocess.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/indexing/runners.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/indexing/typescript.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/merge.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/metadata.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/output.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/paths.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/project.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/queries.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/scip_tool.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/scope.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/session.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/source.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/sql.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/symbols.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/targets.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scip_cli/tsconfig.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scripts/bench_postprocess.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scripts/build.sh +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scripts/publish.sh +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/scripts/test.sh +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/__init__.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/analyze_db.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/conftest.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/e2e_harness.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/fixture_catalog.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/fixtures/typescript-project/package.json +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/fixtures/typescript-project/src/__tests__/fixtureOnly.spec.ts +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/fixtures/typescript-project/src/app/handler.ts +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/fixtures/typescript-project/src/config.ts +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/fixtures/typescript-project/src/consumer.ts +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/fixtures/typescript-project/src/dead.ts +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/fixtures/typescript-project/src/domain/i18n/en.ts +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/fixtures/typescript-project/src/domain/i18n/index.ts +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/fixtures/typescript-project/src/domain/i18n/useLocale.ts +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/fixtures/typescript-project/src/domain/labels/index.ts +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/fixtures/typescript-project/src/events/streamTypes.ts +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/fixtures/typescript-project/src/gateway/index.ts +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/fixtures/typescript-project/src/helper.ts +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/fixtures/typescript-project/src/hooks/useHookA.ts +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/fixtures/typescript-project/src/hooks/useHookB.ts +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/fixtures/typescript-project/src/hooks/useItems.ts +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/fixtures/typescript-project/src/index.ts +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/fixtures/typescript-project/src/integrations/inferenceClient.ts +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/fixtures/typescript-project/src/lib/handler.ts +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/fixtures/typescript-project/src/loaders/useInference.ts +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/fixtures/typescript-project/src/models/MutationAggregate.ts +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/fixtures/typescript-project/src/pages/panelPage.ts +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/fixtures/typescript-project/src/rules/applyRule.ts +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/fixtures/typescript-project/src/types/a.ts +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/fixtures/typescript-project/src/types/b.ts +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/fixtures/typescript-project/src/ui/Button.ts +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/fixtures/typescript-project/src/ui/LazyPanel.ts +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/fixtures/typescript-project/src/ui/buttonConsumer.ts +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/fixtures/typescript-project/src/ui/menuModule.ts +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/fixtures/typescript-project/src/user.ts +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/fixtures/typescript-project/src/widget.ts +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/fixtures/typescript-project/src/widgets/OrphanWidget.ts +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/fixtures/typescript-project/tsconfig.json +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/perf_util.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/test_analyze_graph.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/test_analyze_perf.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/test_cache.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/test_composability.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/test_config.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/test_discover.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/test_e2e.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/test_e2e_analyze_patterns.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/test_e2e_perf.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/test_exclude_globs.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/test_index_batching.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/test_indexer_env.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/test_indexer_fallback.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/test_merge.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/test_metadata.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/test_multi_symbol.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/test_pure_functions.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/test_python_index_integration.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/test_qualified_symbols.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/test_reindex.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/test_scip_tool.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/test_scope.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/test_symbols_freq.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/test_targets.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.0}/tests/test_tsconfig.py +0 -0
- {scip_cli-2.9.0 → scip_cli-2.10.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.10.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
|
|
|
@@ -282,16 +282,19 @@ Use `analyze` on the repo itself before broad refactors or agent review — it s
|
|
|
282
282
|
|
|
283
283
|
```bash
|
|
284
284
|
scip-cli analyze --limit 25
|
|
285
|
+
scip-cli analyze --limit 40 --per-check-limit 5 # spread the global budget across checks
|
|
285
286
|
scip-cli analyze --priority high --limit 25 # dead exports & cycles only
|
|
286
287
|
scip-cli analyze --check cycles --limit 25 # one named section
|
|
287
288
|
scip-cli analyze --check dead_files --limit 25
|
|
288
289
|
```
|
|
289
290
|
|
|
291
|
+
`--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.
|
|
292
|
+
|
|
290
293
|
Sections are tagged `[high]`, `[medium]`, `[low]` and listed in that order.
|
|
291
294
|
|
|
292
295
|
|Tier|Project sections|Action|
|
|
293
296
|
|---|---|---|
|
|
294
|
-
|**high**|Cycles,
|
|
297
|
+
|**high**|Cycles, dead exports, dead files, stale types (unreferenced is omitted when dead exports is on)|Nuke or fix cycles; delete unused; `_` prefix|
|
|
295
298
|
|**medium**|Same-file only, change surface (file target)|Module-private by usage|
|
|
296
299
|
|**low**|Test-only consumers, coupling, bottlenecks, hotspots|Noisy on Python (index omits many same-file calls); verify with `rg`|
|
|
297
300
|
|
|
@@ -302,7 +305,7 @@ Use `--priority high` for a quick gate; `--priority high,medium` adds context. F
|
|
|
302
305
|
|Section|Easy pickings|
|
|
303
306
|
|---|---|
|
|
304
307
|
|**Cycles**|Import/mention cycles between production files — break the edge or extract shared code|
|
|
305
|
-
|**Unreferenced**|
|
|
308
|
+
|**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
309
|
|**Dead exports**|No external refs — delete or `_` prefix|
|
|
307
310
|
|**Stale types**|Classes/types with no external consumer in the index — verify in-file or type-only use before removing|
|
|
308
311
|
|**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
|
|
|
@@ -261,16 +261,19 @@ Use `analyze` on the repo itself before broad refactors or agent review — it s
|
|
|
261
261
|
|
|
262
262
|
```bash
|
|
263
263
|
scip-cli analyze --limit 25
|
|
264
|
+
scip-cli analyze --limit 40 --per-check-limit 5 # spread the global budget across checks
|
|
264
265
|
scip-cli analyze --priority high --limit 25 # dead exports & cycles only
|
|
265
266
|
scip-cli analyze --check cycles --limit 25 # one named section
|
|
266
267
|
scip-cli analyze --check dead_files --limit 25
|
|
267
268
|
```
|
|
268
269
|
|
|
270
|
+
`--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.
|
|
271
|
+
|
|
269
272
|
Sections are tagged `[high]`, `[medium]`, `[low]` and listed in that order.
|
|
270
273
|
|
|
271
274
|
|Tier|Project sections|Action|
|
|
272
275
|
|---|---|---|
|
|
273
|
-
|**high**|Cycles,
|
|
276
|
+
|**high**|Cycles, dead exports, dead files, stale types (unreferenced is omitted when dead exports is on)|Nuke or fix cycles; delete unused; `_` prefix|
|
|
274
277
|
|**medium**|Same-file only, change surface (file target)|Module-private by usage|
|
|
275
278
|
|**low**|Test-only consumers, coupling, bottlenecks, hotspots|Noisy on Python (index omits many same-file calls); verify with `rg`|
|
|
276
279
|
|
|
@@ -281,7 +284,7 @@ Use `--priority high` for a quick gate; `--priority high,medium` adds context. F
|
|
|
281
284
|
|Section|Easy pickings|
|
|
282
285
|
|---|---|
|
|
283
286
|
|**Cycles**|Import/mention cycles between production files — break the edge or extract shared code|
|
|
284
|
-
|**Unreferenced**|
|
|
287
|
+
|**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
288
|
|**Dead exports**|No external refs — delete or `_` prefix|
|
|
286
289
|
|**Stale types**|Classes/types with no external consumer in the index — verify in-file or type-only use before removing|
|
|
287
290
|
|**Same-file only**|Used only inside defining file — rename to `_`|
|
|
@@ -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]
|
|
@@ -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
|
|
|
@@ -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",
|
|
@@ -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
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from .common import DEFAULT_LIMIT, SYM_DEF_JOIN, analyze_noise, fetch_all, short_name
|
|
6
|
-
from .live import
|
|
5
|
+
from .common import DEFAULT_LIMIT, SYM_DEF_JOIN, analyze_noise, collect_until_limit, fetch_all, short_name
|
|
6
|
+
from .live import file_has_scip_importers, has_same_file_reference_usage, live_for
|
|
7
7
|
from .sections import FALSE_POSITIVE_PREFACES, Check, Priority, run_checks
|
|
8
8
|
from .symbol import symbol_pressure
|
|
9
9
|
|
|
@@ -83,10 +83,8 @@ def file_consumers(db, relative_path: str, limit: int = DEFAULT_LIMIT) -> list[s
|
|
|
83
83
|
|
|
84
84
|
|
|
85
85
|
def unreferenced_in_file(db, relative_path: str, limit: int = DEFAULT_LIMIT) -> list[str]:
|
|
86
|
-
live =
|
|
87
|
-
|
|
88
|
-
db,
|
|
89
|
-
"""
|
|
86
|
+
live = live_for(db)
|
|
87
|
+
sql = """
|
|
90
88
|
SELECT gs.symbol, der.start_line, der.end_line, def_d.id
|
|
91
89
|
FROM global_symbols gs
|
|
92
90
|
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
@@ -102,25 +100,25 @@ def unreferenced_in_file(db, relative_path: str, limit: int = DEFAULT_LIMIT) ->
|
|
|
102
100
|
WHERE m.symbol_id = gs.id AND m.role != 1 AND c.document_id != def_d.id
|
|
103
101
|
)
|
|
104
102
|
ORDER BY der.start_line
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
def fetch_page(page_size: int, offset: int):
|
|
106
|
+
return fetch_all(db, sql + " LIMIT ? OFFSET ?", (relative_path, page_size, offset))
|
|
107
|
+
|
|
108
|
+
def accept(row):
|
|
109
|
+
symbol, start, end, def_doc_id = row
|
|
111
110
|
if analyze_noise(relative_path, symbol, include_tests=True):
|
|
112
|
-
|
|
111
|
+
return None
|
|
113
112
|
if live.dead_export_noise(symbol, def_doc_id):
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
113
|
+
return None
|
|
114
|
+
return f"{short_name(symbol)} {start + 1}:{end + 1}"
|
|
115
|
+
|
|
116
|
+
return collect_until_limit(limit, fetch_page, accept)
|
|
117
117
|
|
|
118
118
|
|
|
119
119
|
def same_file_only_in_file(db, relative_path: str, limit: int = DEFAULT_LIMIT) -> list[str]:
|
|
120
|
-
live =
|
|
121
|
-
|
|
122
|
-
db,
|
|
123
|
-
"""
|
|
120
|
+
live = live_for(db)
|
|
121
|
+
sql = """
|
|
124
122
|
SELECT gs.symbol, der.start_line, der.end_line, def_d.id
|
|
125
123
|
FROM global_symbols gs
|
|
126
124
|
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
@@ -137,27 +135,27 @@ def same_file_only_in_file(db, relative_path: str, limit: int = DEFAULT_LIMIT) -
|
|
|
137
135
|
WHERE m.symbol_id = gs.id AND m.role = 0 AND c.document_id != def_d.id
|
|
138
136
|
)
|
|
139
137
|
ORDER BY der.start_line
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
138
|
+
"""
|
|
139
|
+
|
|
140
|
+
def fetch_page(page_size: int, offset: int):
|
|
141
|
+
return fetch_all(db, sql + " LIMIT ? OFFSET ?", (relative_path, page_size, offset))
|
|
142
|
+
|
|
143
|
+
def accept(row):
|
|
144
|
+
symbol, start, end, def_doc_id = row
|
|
146
145
|
if analyze_noise(relative_path, symbol, include_tests=True):
|
|
147
|
-
|
|
146
|
+
return None
|
|
148
147
|
if live.same_file_export_noise(symbol, def_doc_id):
|
|
149
|
-
|
|
148
|
+
return None
|
|
150
149
|
if not file_has_scip_importers(db, relative_path, live=live, def_doc_id=def_doc_id):
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
150
|
+
return None
|
|
151
|
+
return f"{short_name(symbol)} {start + 1}:{end + 1}"
|
|
152
|
+
|
|
153
|
+
return collect_until_limit(limit, fetch_page, accept)
|
|
154
154
|
|
|
155
155
|
|
|
156
156
|
def dead_in_file(db, relative_path: str, limit: int = DEFAULT_LIMIT) -> list[str]:
|
|
157
|
-
live =
|
|
158
|
-
|
|
159
|
-
db,
|
|
160
|
-
"""
|
|
157
|
+
live = live_for(db)
|
|
158
|
+
sql = """
|
|
161
159
|
SELECT gs.id, gs.symbol, der.start_line, der.end_line, def_d.id
|
|
162
160
|
FROM global_symbols gs
|
|
163
161
|
JOIN defn_enclosing_ranges der ON gs.id = der.symbol_id
|
|
@@ -172,20 +170,22 @@ def dead_in_file(db, relative_path: str, limit: int = DEFAULT_LIMIT) -> list[str
|
|
|
172
170
|
AND c.document_id != def_d.id
|
|
173
171
|
)
|
|
174
172
|
ORDER BY der.start_line
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
173
|
+
"""
|
|
174
|
+
|
|
175
|
+
def fetch_page(page_size: int, offset: int):
|
|
176
|
+
return fetch_all(db, sql + " LIMIT ? OFFSET ?", (relative_path, page_size, offset))
|
|
177
|
+
|
|
178
|
+
def accept(row):
|
|
179
|
+
sym_id, symbol, start, end, def_doc_id = row
|
|
181
180
|
if analyze_noise(relative_path, symbol, include_tests=True):
|
|
182
|
-
|
|
181
|
+
return None
|
|
183
182
|
if has_same_file_reference_usage(db, sym_id, def_doc_id):
|
|
184
|
-
|
|
183
|
+
return None
|
|
185
184
|
if live.dead_export_noise(symbol, def_doc_id):
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
185
|
+
return None
|
|
186
|
+
return f"{short_name(symbol)} {start + 1}:{end + 1}"
|
|
187
|
+
|
|
188
|
+
return collect_until_limit(limit, fetch_page, accept)
|
|
189
189
|
|
|
190
190
|
|
|
191
191
|
def imports_summary(db, relative_path: str, limit: int = DEFAULT_LIMIT) -> list[str]:
|
|
@@ -342,8 +342,11 @@ def _run_file_checks(
|
|
|
342
342
|
*,
|
|
343
343
|
budget=None,
|
|
344
344
|
check_keys=None,
|
|
345
|
+
per_check_limit=None,
|
|
345
346
|
) -> list[tuple[str, list[str], str | None]]:
|
|
346
|
-
return run_checks(
|
|
347
|
+
return run_checks(
|
|
348
|
+
checks, db, limit, priorities, budget=budget, check_keys=check_keys, per_check_limit=per_check_limit
|
|
349
|
+
)
|
|
347
350
|
|
|
348
351
|
|
|
349
352
|
def run_all(
|
|
@@ -353,6 +356,7 @@ def run_all(
|
|
|
353
356
|
priorities=None,
|
|
354
357
|
budget=None,
|
|
355
358
|
check_keys=None,
|
|
359
|
+
per_check_limit=None,
|
|
356
360
|
) -> list[tuple[str, list[str], str | None]]:
|
|
357
361
|
return _run_file_checks(
|
|
358
362
|
_file_checks(relative_path, include_top_symbols=True),
|
|
@@ -361,6 +365,7 @@ def run_all(
|
|
|
361
365
|
priorities,
|
|
362
366
|
budget=budget,
|
|
363
367
|
check_keys=check_keys,
|
|
368
|
+
per_check_limit=per_check_limit,
|
|
364
369
|
)
|
|
365
370
|
|
|
366
371
|
|
|
@@ -371,6 +376,7 @@ def run_all_sections_only(
|
|
|
371
376
|
priorities=None,
|
|
372
377
|
budget=None,
|
|
373
378
|
check_keys=None,
|
|
379
|
+
per_check_limit=None,
|
|
374
380
|
) -> list[tuple[str, list[str], str | None]]:
|
|
375
381
|
"""Per-file sections without top-symbols (directory batch)."""
|
|
376
382
|
return _run_file_checks(
|
|
@@ -380,4 +386,5 @@ def run_all_sections_only(
|
|
|
380
386
|
priorities,
|
|
381
387
|
budget=budget,
|
|
382
388
|
check_keys=check_keys,
|
|
389
|
+
per_check_limit=per_check_limit,
|
|
383
390
|
)
|