scip-cli 2.6.0__tar.gz → 2.7.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.6.0 → scip_cli-2.7.0}/PKG-INFO +9 -7
- {scip_cli-2.6.0 → scip_cli-2.7.0}/README.md +8 -6
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/SKILL.md +5 -3
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/__init__.py +1 -1
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/__main__.py +6 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/analyze/file.py +8 -2
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/analyze/project.py +42 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/analyze/sections.py +72 -7
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/analyze/symbol.py +2 -1
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/commands/analyze.py +24 -5
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/test_analyze.py +61 -3
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/test_e2e.py +17 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/.agents/commands/migration-problems.md +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/.agents/rules/agent.mdc +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/.github/ISSUE_TEMPLATE/question.md +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/.github/pull_request_template.md +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/.github/workflows/ci.yml +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/.gitignore +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/.pre-commit-config.yaml +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/AGENTS.md +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/CODE_OF_CONDUCT.md +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/CONTRIBUTING.md +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/LICENSE +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/SECURITY.md +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/docs/port.md +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/docs/ports/conclusions.md +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/docs/ports/experiment-summary.md +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/docs/ports/go-experiment.md +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/docs/ports/go.md +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/docs/ports/plan.md +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/docs/ports/rust-experiment.md +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/docs/ports/rust.md +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/docs/ports/summary.md +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/docs/ports/zig-experiment.md +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/docs/ports/zig.md +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/pyproject.toml +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/analyze/__init__.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/analyze/common.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/analyze/graph.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/analyze/live.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/analyze/targets.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/cache.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/cli_args.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/commands/__init__.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/commands/code.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/commands/deps.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/commands/members.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/commands/rdeps.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/commands/refs.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/commands/reindex.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/commands/search.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/commands/skill.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/commands/symbols.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/config.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/debug.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/discover.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/indexing/__init__.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/indexing/constants.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/indexing/convert.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/indexing/core.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/indexing/languages.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/indexing/orchestrate.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/indexing/postprocess.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/indexing/runners.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/indexing/typescript.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/merge.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/output.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/paths.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/project.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/queries.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/scip_tool.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/scope.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/session.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/source.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/sql.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/symbols.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/targets.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scip_cli/tsconfig.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scripts/bench.sh +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scripts/bench_postprocess.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scripts/build.sh +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scripts/publish.sh +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/scripts/test.sh +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/__init__.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/analyze_db.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/bench_db.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/conftest.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/e2e_harness.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixture_catalog.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/package.json +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/src/app/handler.ts +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/src/config.ts +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/src/consumer.ts +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/src/dead.ts +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/src/domain/i18n/en.ts +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/src/domain/i18n/index.ts +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/src/domain/i18n/useLocale.ts +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/src/domain/labels/index.ts +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/src/events/streamTypes.ts +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/src/gateway/index.ts +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/src/helper.ts +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/src/hooks/useHookA.ts +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/src/hooks/useHookB.ts +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/src/hooks/useItems.ts +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/src/index.ts +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/src/integrations/inferenceClient.ts +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/src/lib/handler.ts +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/src/loaders/useInference.ts +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/src/models/MutationAggregate.ts +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/src/pages/panelPage.ts +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/src/rules/applyRule.ts +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/src/types/a.ts +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/src/types/b.ts +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/src/ui/Button.ts +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/src/ui/LazyPanel.ts +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/src/ui/buttonConsumer.ts +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/src/ui/menuModule.ts +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/src/user.ts +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/src/widget.ts +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/src/widgets/OrphanWidget.ts +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/tsconfig.json +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/perf_util.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/test_analyze_graph.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/test_analyze_perf.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/test_bench_queries.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/test_cache.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/test_composability.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/test_config.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/test_discover.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/test_e2e_analyze_patterns.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/test_e2e_perf.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/test_index_batching.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/test_index_prune.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/test_indexer_env.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/test_indexer_fallback.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/test_merge.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/test_multi_symbol.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/test_pure_functions.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/test_python_index_integration.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/test_qualified_symbols.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/test_reindex.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/test_scip_tool.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/test_scope.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/test_symbols_freq.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/test_targets.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/test_tsconfig.py +0 -0
- {scip_cli-2.6.0 → scip_cli-2.7.0}/tests/test_typescript_projects.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: scip-cli
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.7.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`, `--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`, `--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
|
|
|
@@ -280,15 +280,17 @@ Use `analyze` on the repo itself before broad refactors or agent review — it s
|
|
|
280
280
|
```bash
|
|
281
281
|
scip-cli analyze --limit 25
|
|
282
282
|
scip-cli analyze --priority high --limit 25 # dead exports & cycles only
|
|
283
|
+
scip-cli analyze --check cycles --limit 25 # one named section
|
|
284
|
+
scip-cli analyze --check dead_files --limit 25
|
|
283
285
|
```
|
|
284
286
|
|
|
285
287
|
Sections are tagged `[high]`, `[medium]`, `[low]` and listed in that order.
|
|
286
288
|
|
|
287
|
-
| Tier | Project sections
|
|
288
|
-
| ---------- |
|
|
289
|
-
| **high** | Cycles, unreferenced, dead exports, stale types
|
|
290
|
-
| **medium** | Same-file only, change surface (file target)
|
|
291
|
-
| **low** | Test-only consumers, coupling, bottlenecks, hotspots
|
|
289
|
+
| Tier | Project sections | Action |
|
|
290
|
+
| ---------- | ----------------------------------------------------------- | -------------------------------------------------------------------- |
|
|
291
|
+
| **high** | Cycles, unreferenced, dead exports, dead files, stale types | Nuke or fix cycles; delete unused; `_` prefix |
|
|
292
|
+
| **medium** | Same-file only, change surface (file target) | Module-private by usage |
|
|
293
|
+
| **low** | Test-only consumers, coupling, bottlenecks, hotspots | Noisy on Python (index omits many same-file calls); verify with `rg` |
|
|
292
294
|
|
|
293
295
|
Use `--priority high` for a quick gate; `--priority high,medium` adds context. File drill-down adds change surface and unused imports.
|
|
294
296
|
|
|
@@ -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`, `--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`, `--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
|
|
|
@@ -259,15 +259,17 @@ Use `analyze` on the repo itself before broad refactors or agent review — it s
|
|
|
259
259
|
```bash
|
|
260
260
|
scip-cli analyze --limit 25
|
|
261
261
|
scip-cli analyze --priority high --limit 25 # dead exports & cycles only
|
|
262
|
+
scip-cli analyze --check cycles --limit 25 # one named section
|
|
263
|
+
scip-cli analyze --check dead_files --limit 25
|
|
262
264
|
```
|
|
263
265
|
|
|
264
266
|
Sections are tagged `[high]`, `[medium]`, `[low]` and listed in that order.
|
|
265
267
|
|
|
266
|
-
| Tier | Project sections
|
|
267
|
-
| ---------- |
|
|
268
|
-
| **high** | Cycles, unreferenced, dead exports, stale types
|
|
269
|
-
| **medium** | Same-file only, change surface (file target)
|
|
270
|
-
| **low** | Test-only consumers, coupling, bottlenecks, hotspots
|
|
268
|
+
| Tier | Project sections | Action |
|
|
269
|
+
| ---------- | ----------------------------------------------------------- | -------------------------------------------------------------------- |
|
|
270
|
+
| **high** | Cycles, unreferenced, dead exports, dead files, stale types | Nuke or fix cycles; delete unused; `_` prefix |
|
|
271
|
+
| **medium** | Same-file only, change surface (file target) | Module-private by usage |
|
|
272
|
+
| **low** | Test-only consumers, coupling, bottlenecks, hotspots | Noisy on Python (index omits many same-file calls); verify with `rg` |
|
|
271
273
|
|
|
272
274
|
Use `--priority high` for a quick gate; `--priority high,medium` adds context. File drill-down adds change surface and unused imports.
|
|
273
275
|
|
|
@@ -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] [target]
|
|
136
|
+
analyze [--limit N] [--path PATH] [--include-tests] [--priority LEVEL] [--check NAME] [target]
|
|
137
137
|
```
|
|
138
138
|
|
|
139
139
|
| Target | Output |
|
|
@@ -143,17 +143,19 @@ analyze [--limit N] [--path PATH] [--include-tests] [target]
|
|
|
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, unreferenced, dead exports, stale types; `[medium]` same-file-only, change surface (file); `[low]` test-only consumers (noisy on Python), coupling, bottlenecks, hotspots.
|
|
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
|
|
|
148
148
|
`--limit` caps **result rows across the whole run** (default 20); remaining checks are skipped once the cap is reached. `(none)` sections and section headers do not count.
|
|
149
149
|
|
|
150
150
|
`--priority high` or `--priority high,medium` (also `1`/`2`/`3`) skips lower tiers.
|
|
151
151
|
|
|
152
|
+
`--check NAME` (repeatable or comma-separated) runs only those sections and ANDs with `--priority`. Unknown names error. A name that exists for another target (e.g. `unused_imports` on a project-wide run) yields no matching sections.
|
|
153
|
+
|
|
152
154
|
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`).
|
|
153
155
|
|
|
154
156
|
**Dogfood loop:** `reindex` → `analyze --limit 25` → `analyze scip_cli` or `analyze scip_cli/queries.py` on suspects. Skips test paths in project-wide and directory runs (`tests/`, `*.test.*`, `*.spec.*`); `--include-tests` to include them. File-target analyze always includes that file.
|
|
155
157
|
|
|
156
|
-
**Easy pickings:** **Cycles** and **dead
|
|
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.
|
|
157
159
|
|
|
158
160
|
### reindex
|
|
159
161
|
|
|
@@ -141,6 +141,12 @@ def main() -> None:
|
|
|
141
141
|
metavar="LEVEL",
|
|
142
142
|
help="Comma-separated check tiers: high, medium, low (or 1/2/3). Default: all. E.g. high or high,medium",
|
|
143
143
|
)
|
|
144
|
+
analyze_parser.add_argument(
|
|
145
|
+
"--check",
|
|
146
|
+
action="append",
|
|
147
|
+
metavar="NAME",
|
|
148
|
+
help="Run only these checks (repeatable or comma-separated). ANDs with --priority",
|
|
149
|
+
)
|
|
144
150
|
|
|
145
151
|
# reindex
|
|
146
152
|
reindex_parser = subparsers.add_parser("reindex", help="Force re-indexing of the current project")
|
|
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
from .common import DEFAULT_LIMIT, SYM_DEF_JOIN, analyze_noise, fetch_all, short_name
|
|
6
6
|
from .live import LiveIndex, file_has_scip_importers, has_same_file_reference_usage
|
|
7
|
-
from .sections import Check, Priority, run_checks
|
|
7
|
+
from .sections import FALSE_POSITIVE_PREFACES, Check, Priority, run_checks
|
|
8
8
|
from .symbol import symbol_pressure
|
|
9
9
|
|
|
10
10
|
|
|
@@ -302,6 +302,7 @@ def _file_checks(relative_path: str, *, include_top_symbols: bool) -> list[Check
|
|
|
302
302
|
Priority.HIGH,
|
|
303
303
|
f"Unreferenced in file {title}",
|
|
304
304
|
_bind_path(unreferenced_in_file, relative_path),
|
|
305
|
+
false_positive_preface=FALSE_POSITIVE_PREFACES["unreferenced_in_file"],
|
|
305
306
|
),
|
|
306
307
|
Check(
|
|
307
308
|
"dead_in_file",
|
|
@@ -340,8 +341,9 @@ def _run_file_checks(
|
|
|
340
341
|
priorities,
|
|
341
342
|
*,
|
|
342
343
|
budget=None,
|
|
344
|
+
check_keys=None,
|
|
343
345
|
) -> list[tuple[str, list[str], str | None]]:
|
|
344
|
-
return run_checks(checks, db, limit, priorities, budget=budget)
|
|
346
|
+
return run_checks(checks, db, limit, priorities, budget=budget, check_keys=check_keys)
|
|
345
347
|
|
|
346
348
|
|
|
347
349
|
def run_all(
|
|
@@ -350,6 +352,7 @@ def run_all(
|
|
|
350
352
|
limit: int = DEFAULT_LIMIT,
|
|
351
353
|
priorities=None,
|
|
352
354
|
budget=None,
|
|
355
|
+
check_keys=None,
|
|
353
356
|
) -> list[tuple[str, list[str], str | None]]:
|
|
354
357
|
return _run_file_checks(
|
|
355
358
|
_file_checks(relative_path, include_top_symbols=True),
|
|
@@ -357,6 +360,7 @@ def run_all(
|
|
|
357
360
|
limit,
|
|
358
361
|
priorities,
|
|
359
362
|
budget=budget,
|
|
363
|
+
check_keys=check_keys,
|
|
360
364
|
)
|
|
361
365
|
|
|
362
366
|
|
|
@@ -366,6 +370,7 @@ def run_all_sections_only(
|
|
|
366
370
|
limit: int = DEFAULT_LIMIT,
|
|
367
371
|
priorities=None,
|
|
368
372
|
budget=None,
|
|
373
|
+
check_keys=None,
|
|
369
374
|
) -> list[tuple[str, list[str], str | None]]:
|
|
370
375
|
"""Per-file sections without top-symbols (directory batch)."""
|
|
371
376
|
return _run_file_checks(
|
|
@@ -374,4 +379,5 @@ def run_all_sections_only(
|
|
|
374
379
|
limit,
|
|
375
380
|
priorities,
|
|
376
381
|
budget=budget,
|
|
382
|
+
check_keys=check_keys,
|
|
377
383
|
)
|
|
@@ -13,6 +13,7 @@ from .common import (
|
|
|
13
13
|
cycle_path_noise,
|
|
14
14
|
fetch_all,
|
|
15
15
|
file_pair_noise,
|
|
16
|
+
is_generated_analyze_path,
|
|
16
17
|
is_test_path,
|
|
17
18
|
short_name,
|
|
18
19
|
stale_type_noise,
|
|
@@ -379,6 +380,44 @@ def dead_exports(
|
|
|
379
380
|
return _format_dead_export_rows(db, rows, LiveIndex(db), include_tests=include_tests, limit=limit)
|
|
380
381
|
|
|
381
382
|
|
|
383
|
+
def dead_files(
|
|
384
|
+
db,
|
|
385
|
+
limit: int = DEFAULT_LIMIT,
|
|
386
|
+
*,
|
|
387
|
+
include_tests: bool = False,
|
|
388
|
+
scope: str | None = None,
|
|
389
|
+
) -> list[str]:
|
|
390
|
+
"""Files with no inbound mentions from other documents (empty rdeps)."""
|
|
391
|
+
scope_clause, scope_params = path_filter_sql(db, scope, doc_alias="d")
|
|
392
|
+
rows = fetch_all(
|
|
393
|
+
db,
|
|
394
|
+
f"""
|
|
395
|
+
SELECT d.relative_path
|
|
396
|
+
FROM documents d
|
|
397
|
+
WHERE NOT EXISTS (
|
|
398
|
+
SELECT 1
|
|
399
|
+
FROM defn_enclosing_ranges der
|
|
400
|
+
JOIN mentions m ON m.symbol_id = der.symbol_id AND m.role != 1
|
|
401
|
+
JOIN chunks c ON m.chunk_id = c.id
|
|
402
|
+
WHERE der.document_id = d.id AND c.document_id != d.id
|
|
403
|
+
){scope_clause}
|
|
404
|
+
ORDER BY d.relative_path
|
|
405
|
+
LIMIT ?
|
|
406
|
+
""",
|
|
407
|
+
(*scope_params, limit * 5),
|
|
408
|
+
)
|
|
409
|
+
lines: list[str] = []
|
|
410
|
+
for (path,) in rows:
|
|
411
|
+
if not include_tests and is_test_path(path):
|
|
412
|
+
continue
|
|
413
|
+
if is_generated_analyze_path(path):
|
|
414
|
+
continue
|
|
415
|
+
lines.append(path)
|
|
416
|
+
if len(lines) >= limit:
|
|
417
|
+
break
|
|
418
|
+
return lines
|
|
419
|
+
|
|
420
|
+
|
|
382
421
|
def top_coupling(
|
|
383
422
|
db,
|
|
384
423
|
limit: int = DEFAULT_LIMIT,
|
|
@@ -421,6 +460,7 @@ def run_all(
|
|
|
421
460
|
scope: str | None = None,
|
|
422
461
|
priorities=None,
|
|
423
462
|
budget=None,
|
|
463
|
+
check_keys=None,
|
|
424
464
|
) -> list[tuple[str, list[str], str | None]]:
|
|
425
465
|
suffix = _scope_suffix(scope)
|
|
426
466
|
checks = [
|
|
@@ -432,6 +472,7 @@ def run_all(
|
|
|
432
472
|
f"Dead exports (no in-file or external use){suffix}",
|
|
433
473
|
dead_exports,
|
|
434
474
|
),
|
|
475
|
+
Check("dead_files", Priority.HIGH, f"Dead files (no importers){suffix}", dead_files),
|
|
435
476
|
Check("stale_types", Priority.HIGH, f"Stale types (no external consumers){suffix}", stale_types),
|
|
436
477
|
Check("same_file_only", Priority.MEDIUM, f"Same-file only (in-file use, not exported){suffix}", same_file_only),
|
|
437
478
|
Check(
|
|
@@ -452,4 +493,5 @@ def run_all(
|
|
|
452
493
|
include_tests=include_tests,
|
|
453
494
|
scope=scope,
|
|
454
495
|
budget=budget,
|
|
496
|
+
check_keys=check_keys,
|
|
455
497
|
)
|
|
@@ -48,6 +48,54 @@ def parse_priorities(value: str | None) -> set[Priority] | None:
|
|
|
48
48
|
return out
|
|
49
49
|
|
|
50
50
|
|
|
51
|
+
# Project, file, and symbol Check.key values. --check rejects anything else.
|
|
52
|
+
CHECK_KEYS = frozenset(
|
|
53
|
+
{
|
|
54
|
+
"affected",
|
|
55
|
+
"bottlenecks",
|
|
56
|
+
"change_surface",
|
|
57
|
+
"consumer_files",
|
|
58
|
+
"coupling",
|
|
59
|
+
"cycles",
|
|
60
|
+
"dead_exports",
|
|
61
|
+
"dead_files",
|
|
62
|
+
"dead_in_file",
|
|
63
|
+
"def_context",
|
|
64
|
+
"dependencies",
|
|
65
|
+
"file_consumers",
|
|
66
|
+
"hotspots",
|
|
67
|
+
"imports_summary",
|
|
68
|
+
"same_file_only",
|
|
69
|
+
"stale_types",
|
|
70
|
+
"symbol_pressure",
|
|
71
|
+
"test_only",
|
|
72
|
+
"top_coupling",
|
|
73
|
+
"top_symbols",
|
|
74
|
+
"unreferenced",
|
|
75
|
+
"unused_imports",
|
|
76
|
+
}
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def parse_checks(values: list[str] | None) -> set[str] | None:
|
|
81
|
+
"""Parse --check (repeatable; commas allowed). None means all checks."""
|
|
82
|
+
if not values:
|
|
83
|
+
return None
|
|
84
|
+
out: set[str] = set()
|
|
85
|
+
for value in values:
|
|
86
|
+
for part in value.replace(" ", "").split(","):
|
|
87
|
+
if not part:
|
|
88
|
+
continue
|
|
89
|
+
key = part.lower()
|
|
90
|
+
if key not in CHECK_KEYS:
|
|
91
|
+
allowed = ", ".join(sorted(CHECK_KEYS))
|
|
92
|
+
raise RuntimeError(f"unknown analyze check {part!r} (use {allowed})")
|
|
93
|
+
out.add(key)
|
|
94
|
+
if not out:
|
|
95
|
+
return None
|
|
96
|
+
return out
|
|
97
|
+
|
|
98
|
+
|
|
51
99
|
@dataclass(frozen=True)
|
|
52
100
|
class Check:
|
|
53
101
|
key: str
|
|
@@ -60,24 +108,38 @@ class Check:
|
|
|
60
108
|
return f"[{self.priority.value}] {self.title}"
|
|
61
109
|
|
|
62
110
|
|
|
63
|
-
# Shown only when the section has hits (not "(none)").
|
|
111
|
+
# Shown only when the section has hits (not "(none)"). Situation hints, not exhaustive.
|
|
64
112
|
FALSE_POSITIVE_PREFACES: dict[str, str] = {
|
|
65
113
|
"dead_exports": (
|
|
66
|
-
"SCIP may miss dynamic loading (loadFiles, GraphQL)
|
|
67
|
-
"— verify with rdeps/rg before deleting."
|
|
114
|
+
"SCIP may miss dynamic loading (loadFiles, GraphQL), default-export object members, "
|
|
115
|
+
"and some export const arrows — verify with rdeps/rg before deleting."
|
|
116
|
+
),
|
|
117
|
+
"dead_files": (
|
|
118
|
+
"Empty rdeps in the index. SCIP often records export const / arrow files as module-only, "
|
|
119
|
+
"so named imports (routes, barrels) may not count; same for dynamic require. Confirm with rg."
|
|
68
120
|
),
|
|
69
121
|
"unreferenced": (
|
|
70
|
-
"No mentions in the index —
|
|
122
|
+
"No mentions in the index — may still run via dynamic import, side-effect registration, "
|
|
123
|
+
"or a call SCIP did not record."
|
|
124
|
+
),
|
|
125
|
+
"same_file_only": (
|
|
126
|
+
"Referenced only in the defining file — often handlers or private helpers, "
|
|
127
|
+
"or an external call SCIP missed; not necessarily a dead export."
|
|
128
|
+
),
|
|
129
|
+
"stale_types": (
|
|
130
|
+
"No cross-file refs in the index — may still be used in-file, as a type-only import SCIP dropped, "
|
|
131
|
+
"or as a structural shape."
|
|
71
132
|
),
|
|
72
|
-
"same_file_only": ("Referenced only in the defining file — often handlers or private helpers, not dead exports."),
|
|
73
|
-
"stale_types": ("No cross-file refs in the index — may still be used in-file or as a type-only shape."),
|
|
74
133
|
"cycles": "Remaining cycles may be barrel re-exports; confirm before refactoring.",
|
|
75
134
|
"dead_in_file": (
|
|
76
|
-
"SCIP may miss dynamic loading
|
|
135
|
+
"SCIP may miss dynamic loading, default-export indirection, and some export const arrows "
|
|
136
|
+
"— verify with rdeps/rg before deleting."
|
|
77
137
|
),
|
|
78
138
|
"unreferenced_in_file": (
|
|
79
139
|
"No mentions in the index — may still be used in-file via handlers or dynamic registration."
|
|
80
140
|
),
|
|
141
|
+
"unused_imports": ("Import may still be a type-only use or a name SCIP did not bind — confirm before removing."),
|
|
142
|
+
"test_only": "Index may miss same-file production calls, so this can look test-only when it is not.",
|
|
81
143
|
}
|
|
82
144
|
|
|
83
145
|
|
|
@@ -104,9 +166,12 @@ def run_checks(
|
|
|
104
166
|
include_tests: bool = False,
|
|
105
167
|
scope: str | None = None,
|
|
106
168
|
budget: RowBudget | None = None,
|
|
169
|
+
check_keys: set[str] | None = None,
|
|
107
170
|
) -> list[tuple[str, list[str], str | None]]:
|
|
108
171
|
"""Run checks in priority order (high → low), optionally filtered."""
|
|
109
172
|
selected = [check for check in checks if priorities is None or check.priority in priorities]
|
|
173
|
+
if check_keys is not None:
|
|
174
|
+
selected = [check for check in selected if check.key in check_keys]
|
|
110
175
|
selected.sort(key=lambda check: (_ORDER.index(check.priority), check.key))
|
|
111
176
|
budget_obj: RowBudget = budget or RowBudget(remaining=limit)
|
|
112
177
|
sections: list[tuple[str, list[str], str | None]] = []
|
|
@@ -169,6 +169,7 @@ def run_all(
|
|
|
169
169
|
limit: int = DEFAULT_LIMIT,
|
|
170
170
|
priorities=None,
|
|
171
171
|
budget=None,
|
|
172
|
+
check_keys=None,
|
|
172
173
|
) -> list[tuple[str, list[str], str | None]]:
|
|
173
174
|
checks = [
|
|
174
175
|
Check("consumer_files", Priority.HIGH, "Consumer files (direct)", _bind_symbol(consumer_files, symbol_id)),
|
|
@@ -182,4 +183,4 @@ def run_all(
|
|
|
182
183
|
),
|
|
183
184
|
Check("def_context", Priority.LOW, "Definition context", _bind_symbol0(def_context, symbol_id)),
|
|
184
185
|
]
|
|
185
|
-
return run_checks(checks, db, limit, priorities, budget=budget)
|
|
186
|
+
return run_checks(checks, db, limit, priorities, budget=budget, check_keys=check_keys)
|
|
@@ -8,7 +8,7 @@ from ..analyze import file as file_checks
|
|
|
8
8
|
from ..analyze import project as project_checks
|
|
9
9
|
from ..analyze import symbol as symbol_checks
|
|
10
10
|
from ..analyze.common import is_test_path, section
|
|
11
|
-
from ..analyze.sections import RowBudget, parse_priorities
|
|
11
|
+
from ..analyze.sections import RowBudget, parse_checks, parse_priorities
|
|
12
12
|
from ..analyze.targets import MAX_DIR_FILES, list_dir_files, resolve_analyze_target
|
|
13
13
|
from ..cli_args import path_scope_from_args
|
|
14
14
|
from ..session import resolve_one_symbol, setup
|
|
@@ -39,6 +39,7 @@ def _project_sections(
|
|
|
39
39
|
scope: str | None,
|
|
40
40
|
priorities,
|
|
41
41
|
budget: RowBudget,
|
|
42
|
+
check_keys,
|
|
42
43
|
) -> list[tuple[str, list[str], str | None]]:
|
|
43
44
|
return project_checks.run_all(
|
|
44
45
|
db,
|
|
@@ -47,6 +48,7 @@ def _project_sections(
|
|
|
47
48
|
scope=scope,
|
|
48
49
|
priorities=priorities,
|
|
49
50
|
budget=budget,
|
|
51
|
+
check_keys=check_keys,
|
|
50
52
|
)
|
|
51
53
|
|
|
52
54
|
|
|
@@ -57,8 +59,9 @@ def _file_sections(
|
|
|
57
59
|
limit: int,
|
|
58
60
|
priorities,
|
|
59
61
|
budget: RowBudget,
|
|
62
|
+
check_keys,
|
|
60
63
|
) -> list[tuple[str, list[str], str | None]]:
|
|
61
|
-
return file_checks.run_all(db, path, limit=limit, priorities=priorities, budget=budget)
|
|
64
|
+
return file_checks.run_all(db, path, limit=limit, priorities=priorities, budget=budget, check_keys=check_keys)
|
|
62
65
|
|
|
63
66
|
|
|
64
67
|
def _dir_sections(
|
|
@@ -69,6 +72,7 @@ def _dir_sections(
|
|
|
69
72
|
include_tests: bool,
|
|
70
73
|
priorities,
|
|
71
74
|
budget: RowBudget,
|
|
75
|
+
check_keys,
|
|
72
76
|
) -> list[tuple[str, list[str], str | None]]:
|
|
73
77
|
sections = _project_sections(
|
|
74
78
|
db,
|
|
@@ -77,6 +81,7 @@ def _dir_sections(
|
|
|
77
81
|
scope=scope,
|
|
78
82
|
priorities=priorities,
|
|
79
83
|
budget=budget,
|
|
84
|
+
check_keys=check_keys,
|
|
80
85
|
)
|
|
81
86
|
files = list_dir_files(db, scope, include_tests=include_tests)
|
|
82
87
|
total = len(files)
|
|
@@ -99,7 +104,11 @@ def _dir_sections(
|
|
|
99
104
|
for path in files:
|
|
100
105
|
if budget.exhausted():
|
|
101
106
|
break
|
|
102
|
-
sections.extend(
|
|
107
|
+
sections.extend(
|
|
108
|
+
file_checks.run_all_sections_only(
|
|
109
|
+
db, path, limit=limit, priorities=priorities, budget=budget, check_keys=check_keys
|
|
110
|
+
)
|
|
111
|
+
)
|
|
103
112
|
return sections
|
|
104
113
|
|
|
105
114
|
|
|
@@ -112,6 +121,7 @@ def main(args):
|
|
|
112
121
|
budget = RowBudget(remaining=limit)
|
|
113
122
|
include_tests = getattr(args, "include_tests", False)
|
|
114
123
|
priorities = parse_priorities(getattr(args, "priority", None))
|
|
124
|
+
check_keys = parse_checks(getattr(args, "check", None))
|
|
115
125
|
target = getattr(args, "target", None)
|
|
116
126
|
|
|
117
127
|
if target is None:
|
|
@@ -128,6 +138,7 @@ def main(args):
|
|
|
128
138
|
scope=None,
|
|
129
139
|
priorities=priorities,
|
|
130
140
|
budget=budget,
|
|
141
|
+
check_keys=check_keys,
|
|
131
142
|
)
|
|
132
143
|
else:
|
|
133
144
|
resolved = resolve_analyze_target(db, target, project_root, path_scope)
|
|
@@ -140,6 +151,7 @@ def main(args):
|
|
|
140
151
|
include_tests=include_tests,
|
|
141
152
|
priorities=priorities,
|
|
142
153
|
budget=budget,
|
|
154
|
+
check_keys=check_keys,
|
|
143
155
|
)
|
|
144
156
|
elif resolved.kind == "file":
|
|
145
157
|
file_include = _project_include_tests(include_tests, scope)
|
|
@@ -150,16 +162,23 @@ def main(args):
|
|
|
150
162
|
scope=scope,
|
|
151
163
|
priorities=priorities,
|
|
152
164
|
budget=budget,
|
|
165
|
+
check_keys=check_keys,
|
|
153
166
|
)
|
|
154
167
|
if not budget.exhausted():
|
|
155
|
-
sections.extend(
|
|
168
|
+
sections.extend(
|
|
169
|
+
_file_sections(
|
|
170
|
+
db, scope, limit=limit, priorities=priorities, budget=budget, check_keys=check_keys
|
|
171
|
+
)
|
|
172
|
+
)
|
|
156
173
|
else:
|
|
157
174
|
symbol_id, _symbol_str, _display = resolve_one_symbol(
|
|
158
175
|
db,
|
|
159
176
|
resolved.symbol_name,
|
|
160
177
|
path_scope=path_scope,
|
|
161
178
|
)
|
|
162
|
-
sections = symbol_checks.run_all(
|
|
179
|
+
sections = symbol_checks.run_all(
|
|
180
|
+
db, symbol_id, limit=limit, priorities=priorities, budget=budget, check_keys=check_keys
|
|
181
|
+
)
|
|
163
182
|
|
|
164
183
|
_print_sections(sections)
|
|
165
184
|
finally:
|
|
@@ -114,10 +114,10 @@ class TestProjectAnalyze:
|
|
|
114
114
|
assert total_rows <= 3
|
|
115
115
|
assert len(sections) < 9
|
|
116
116
|
|
|
117
|
-
def
|
|
117
|
+
def test_run_all_returns_ten_sections(self):
|
|
118
118
|
db = mini_codebase_db()
|
|
119
119
|
sections = project_checks.run_all(db, limit=500)
|
|
120
|
-
assert len(sections) ==
|
|
120
|
+
assert len(sections) == 10
|
|
121
121
|
titles = [title for title, _lines, _preface in sections]
|
|
122
122
|
assert sum(1 for t in titles if "[low]" in t) == 4
|
|
123
123
|
assert sum(1 for t in titles if "[medium]" in t) == 1
|
|
@@ -142,10 +142,55 @@ class TestProjectAnalyze:
|
|
|
142
142
|
|
|
143
143
|
db = mini_codebase_db()
|
|
144
144
|
sections = project_checks.run_all(db, limit=500, priorities={Priority.HIGH})
|
|
145
|
-
assert len(sections) ==
|
|
145
|
+
assert len(sections) == 5
|
|
146
146
|
titles = [title for title, _lines, _preface in sections]
|
|
147
147
|
assert all("[high]" in title for title in titles)
|
|
148
148
|
|
|
149
|
+
def test_run_all_check_keys_only(self):
|
|
150
|
+
db = mini_codebase_db()
|
|
151
|
+
sections = project_checks.run_all(db, limit=500, check_keys={"cycles"})
|
|
152
|
+
assert len(sections) == 1
|
|
153
|
+
assert "Cycles" in sections[0][0]
|
|
154
|
+
|
|
155
|
+
def test_run_all_check_and_priority_and(self):
|
|
156
|
+
from scip_cli.analyze.sections import Priority
|
|
157
|
+
|
|
158
|
+
db = mini_codebase_db()
|
|
159
|
+
sections = project_checks.run_all(
|
|
160
|
+
db,
|
|
161
|
+
limit=500,
|
|
162
|
+
priorities={Priority.HIGH},
|
|
163
|
+
check_keys={"cycles", "hotspots"},
|
|
164
|
+
)
|
|
165
|
+
assert len(sections) == 1
|
|
166
|
+
assert "Cycles" in sections[0][0]
|
|
167
|
+
|
|
168
|
+
def test_dead_files_empty_rdeps(self):
|
|
169
|
+
b = AnalyzeDbBuilder()
|
|
170
|
+
used = b.define("src/used.ts", "usedFn")
|
|
171
|
+
b.reference("src/entry.ts", used)
|
|
172
|
+
b.define("src/orphan.ts", "orphanFn")
|
|
173
|
+
b.define("tests/orphan.spec.ts", "testHelper")
|
|
174
|
+
db = b.finish()
|
|
175
|
+
lines = project_checks.dead_files(db, limit=20)
|
|
176
|
+
assert "src/orphan.ts" in lines
|
|
177
|
+
assert "src/entry.ts" in lines
|
|
178
|
+
assert "src/used.ts" not in lines
|
|
179
|
+
assert not any("orphan.spec.ts" in line for line in lines)
|
|
180
|
+
with_tests = project_checks.dead_files(db, limit=20, include_tests=True)
|
|
181
|
+
assert any("orphan.spec.ts" in line for line in with_tests)
|
|
182
|
+
|
|
183
|
+
def test_dead_files_preface_when_hits(self):
|
|
184
|
+
db = mini_codebase_db()
|
|
185
|
+
sections = project_checks.run_all(db, limit=20, check_keys={"dead_files"})
|
|
186
|
+
assert len(sections) == 1
|
|
187
|
+
_title, lines, preface = sections[0]
|
|
188
|
+
assert "Dead files" in _title
|
|
189
|
+
if lines != ["(none)"]:
|
|
190
|
+
assert preface is not None
|
|
191
|
+
assert "rdeps" in preface
|
|
192
|
+
assert "export const" in preface
|
|
193
|
+
|
|
149
194
|
|
|
150
195
|
class TestFileAnalyze:
|
|
151
196
|
def test_change_surface_lists_exports(self):
|
|
@@ -254,6 +299,19 @@ class TestAnalyzeSections:
|
|
|
254
299
|
assert parse_priorities("high") == {Priority.HIGH}
|
|
255
300
|
assert parse_priorities("1,medium") == {Priority.HIGH, Priority.MEDIUM}
|
|
256
301
|
|
|
302
|
+
def test_parse_checks(self):
|
|
303
|
+
import pytest
|
|
304
|
+
|
|
305
|
+
from scip_cli.analyze.sections import parse_checks
|
|
306
|
+
|
|
307
|
+
assert parse_checks(None) is None
|
|
308
|
+
assert parse_checks([]) is None
|
|
309
|
+
assert parse_checks(["cycles"]) == {"cycles"}
|
|
310
|
+
assert parse_checks(["cycles,hotspots"]) == {"cycles", "hotspots"}
|
|
311
|
+
assert parse_checks(["cycles", "hotspots"]) == {"cycles", "hotspots"}
|
|
312
|
+
with pytest.raises(RuntimeError, match="unknown analyze check"):
|
|
313
|
+
parse_checks(["not_a_check"])
|
|
314
|
+
|
|
257
315
|
|
|
258
316
|
class TestAnalyzeCommand:
|
|
259
317
|
def test_project_include_tests_for_test_file_path(self):
|
|
@@ -272,6 +272,23 @@ class TestAnalyze:
|
|
|
272
272
|
assert "[high]" in result.stdout
|
|
273
273
|
assert "[low]" not in result.stdout
|
|
274
274
|
|
|
275
|
+
def test_check_runs_named_section_only(self, cli):
|
|
276
|
+
result = cli.run("analyze", "--check", "cycles", "--limit", "5")
|
|
277
|
+
assert result.returncode == 0
|
|
278
|
+
assert "Cycles" in result.stdout
|
|
279
|
+
assert "Hotspots" not in result.stdout
|
|
280
|
+
assert "Dead exports" not in result.stdout
|
|
281
|
+
|
|
282
|
+
def test_check_dead_files(self, cli):
|
|
283
|
+
result = cli.run("analyze", "--check", "dead_files", "--limit", "5")
|
|
284
|
+
assert result.returncode == 0
|
|
285
|
+
assert "Dead files" in result.stdout
|
|
286
|
+
|
|
287
|
+
def test_check_unknown_exits(self, cli):
|
|
288
|
+
result = cli.run("analyze", "--check", "not_a_check")
|
|
289
|
+
assert result.returncode == 1
|
|
290
|
+
assert "unknown analyze check" in result.stderr
|
|
291
|
+
|
|
275
292
|
|
|
276
293
|
class TestIndex:
|
|
277
294
|
def test_fixture_index_queryable(self, indexed_fixture):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/src/domain/i18n/index.ts
RENAMED
|
File without changes
|
{scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/src/domain/i18n/useLocale.ts
RENAMED
|
File without changes
|
{scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/src/domain/labels/index.ts
RENAMED
|
File without changes
|
{scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/src/events/streamTypes.ts
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/src/loaders/useInference.ts
RENAMED
|
File without changes
|
{scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/src/models/MutationAggregate.ts
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/src/ui/buttonConsumer.ts
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{scip_cli-2.6.0 → scip_cli-2.7.0}/tests/fixtures/typescript-project/src/widgets/OrphanWidget.ts
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|