scip-cli 2.3.0__tar.gz → 2.4.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.3.0 → scip_cli-2.4.0}/.cursor/rules/agent.mdc +2 -1
- {scip_cli-2.3.0 → scip_cli-2.4.0}/.pre-commit-config.yaml +4 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/PKG-INFO +3 -2
- {scip_cli-2.3.0 → scip_cli-2.4.0}/README.md +1 -1
- {scip_cli-2.3.0 → scip_cli-2.4.0}/pyproject.toml +18 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/__init__.py +1 -1
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/__main__.py +6 -3
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/analyze/common.py +1 -1
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/analyze/file.py +1 -1
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/analyze/live.py +1 -1
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/analyze/project.py +9 -2
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/analyze/sections.py +9 -7
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/analyze/symbol.py +2 -2
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/cache.py +4 -2
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/commands/analyze.py +12 -10
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/commands/deps.py +1 -1
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/commands/members.py +1 -1
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/commands/reindex.py +13 -5
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/commands/search.py +5 -3
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/discover.py +4 -2
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/indexing.py +10 -8
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/output.py +4 -2
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/paths.py +5 -4
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/queries.py +25 -9
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/scip_tool.py +8 -7
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/source.py +20 -5
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/sql.py +7 -3
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/symbols.py +11 -9
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scripts/test.sh +4 -1
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/test_analyze.py +2 -1
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/test_analyze_perf.py +5 -3
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/test_discover.py +5 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/test_pure_functions.py +2 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/test_reindex.py +15 -1
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/test_scope.py +3 -2
- {scip_cli-2.3.0 → scip_cli-2.4.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/.github/ISSUE_TEMPLATE/question.md +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/.github/pull_request_template.md +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/.gitignore +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/CODE_OF_CONDUCT.md +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/CONTRIBUTING.md +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/LICENSE +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/SECURITY.md +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/SKILL.md +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/analyze/__init__.py +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/analyze/graph.py +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/analyze/targets.py +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/cli_args.py +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/commands/__init__.py +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/commands/code.py +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/commands/rdeps.py +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/commands/refs.py +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/commands/skill.py +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/commands/symbols.py +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/config.py +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/debug.py +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/merge.py +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/project.py +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/scope.py +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/session.py +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scip_cli/targets.py +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scripts/build.sh +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/scripts/publish.sh +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/__init__.py +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/analyze_db.py +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/conftest.py +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/e2e_harness.py +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/fixture_catalog.py +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/fixtures/sample-project/package.json +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/fixtures/sample-project/src/app/handler.ts +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/fixtures/sample-project/src/config.ts +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/fixtures/sample-project/src/consumer.ts +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/fixtures/sample-project/src/dead.ts +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/fixtures/sample-project/src/domain/i18n/en.ts +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/fixtures/sample-project/src/domain/i18n/index.ts +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/fixtures/sample-project/src/domain/i18n/useLocale.ts +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/fixtures/sample-project/src/domain/labels/index.ts +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/fixtures/sample-project/src/events/streamTypes.ts +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/fixtures/sample-project/src/gateway/index.ts +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/fixtures/sample-project/src/helper.ts +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/fixtures/sample-project/src/hooks/useHookA.ts +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/fixtures/sample-project/src/hooks/useHookB.ts +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/fixtures/sample-project/src/hooks/useItems.ts +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/fixtures/sample-project/src/index.ts +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/fixtures/sample-project/src/integrations/inferenceClient.ts +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/fixtures/sample-project/src/loaders/useInference.ts +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/fixtures/sample-project/src/pages/panelPage.ts +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/fixtures/sample-project/src/rules/applyRule.ts +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/fixtures/sample-project/src/types/a.ts +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/fixtures/sample-project/src/types/b.ts +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/fixtures/sample-project/src/ui/Button.ts +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/fixtures/sample-project/src/ui/LazyPanel.ts +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/fixtures/sample-project/src/ui/buttonConsumer.ts +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/fixtures/sample-project/src/ui/menuModule.ts +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/fixtures/sample-project/src/user.ts +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/fixtures/sample-project/src/widget.ts +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/fixtures/sample-project/src/widgets/OrphanWidget.ts +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/fixtures/sample-project/tsconfig.json +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/perf_util.py +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/test_analyze_graph.py +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/test_cache.py +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/test_composability.py +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/test_config.py +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/test_e2e.py +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/test_e2e_analyze_patterns.py +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/test_e2e_perf.py +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/test_index_prune.py +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/test_indexer_env.py +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/test_merge.py +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/test_multi_symbol.py +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/test_qualified_symbols.py +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/test_scip_tool.py +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/test_targets.py +0 -0
- {scip_cli-2.3.0 → scip_cli-2.4.0}/tests/test_typescript_projects.py +0 -0
|
@@ -33,8 +33,9 @@ Node.js + `npx` required for integration tests (`scip-typescript`). Optional: `S
|
|
|
33
33
|
| Full suite | `pytest` |
|
|
34
34
|
| E2e loop | `pytest tests/test_e2e.py tests/test_e2e_analyze_patterns.py` |
|
|
35
35
|
| Lint / format | `ruff check .` / `ruff format .` |
|
|
36
|
+
| Typecheck | `basedpyright scip_cli/` |
|
|
36
37
|
|
|
37
|
-
Pre-commit: ruff + ruff-format + full pytest. E2e before commit on command changes.
|
|
38
|
+
Pre-commit: ruff + ruff-format + basedpyright + full pytest. E2e before commit on command changes.
|
|
38
39
|
|
|
39
40
|
## Improving this repo (do this before review subagents)
|
|
40
41
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: scip-cli
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.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
|
|
@@ -12,6 +12,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
12
12
|
Classifier: Topic :: Software Development :: Code Generators
|
|
13
13
|
Requires-Python: >=3.9
|
|
14
14
|
Provides-Extra: dev
|
|
15
|
+
Requires-Dist: basedpyright>=1.39.9; extra == 'dev'
|
|
15
16
|
Requires-Dist: build>=1; extra == 'dev'
|
|
16
17
|
Requires-Dist: pre-commit>=4.0; extra == 'dev'
|
|
17
18
|
Requires-Dist: pytest>=7; extra == 'dev'
|
|
@@ -79,7 +80,7 @@ python -m venv .venv && source .venv/bin/activate
|
|
|
79
80
|
pip install -e ".[dev]"
|
|
80
81
|
```
|
|
81
82
|
|
|
82
|
-
Tests and lint: `pytest`, `ruff check
|
|
83
|
+
Tests and lint: `pytest`, `ruff check .`, `basedpyright scip_cli/` from the same environment (or `scripts/test.sh` for the publish gate).
|
|
83
84
|
|
|
84
85
|
### 2. Install prerequisites (optional)
|
|
85
86
|
|
|
@@ -59,7 +59,7 @@ python -m venv .venv && source .venv/bin/activate
|
|
|
59
59
|
pip install -e ".[dev]"
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
-
Tests and lint: `pytest`, `ruff check
|
|
62
|
+
Tests and lint: `pytest`, `ruff check .`, `basedpyright scip_cli/` from the same environment (or `scripts/test.sh` for the publish gate).
|
|
63
63
|
|
|
64
64
|
### 2. Install prerequisites (optional)
|
|
65
65
|
|
|
@@ -32,6 +32,7 @@ packages = ["scip_cli"]
|
|
|
32
32
|
[project.optional-dependencies]
|
|
33
33
|
dev = [
|
|
34
34
|
"ruff>=0.11",
|
|
35
|
+
"basedpyright>=1.39.9",
|
|
35
36
|
"pytest>=7",
|
|
36
37
|
"pre-commit>=4.0",
|
|
37
38
|
"build>=1",
|
|
@@ -57,3 +58,20 @@ addopts = "-q --tb=line --no-header"
|
|
|
57
58
|
markers = [
|
|
58
59
|
"integration: uses the shared indexed TS fixture (requires npx / scip-typescript)",
|
|
59
60
|
]
|
|
61
|
+
|
|
62
|
+
[tool.basedpyright]
|
|
63
|
+
pythonVersion = "3.10"
|
|
64
|
+
venvPath = "."
|
|
65
|
+
venv = ".venv"
|
|
66
|
+
include = ["scip_cli"]
|
|
67
|
+
exclude = ["tests"]
|
|
68
|
+
|
|
69
|
+
# Gradual typing: basedpyright's unknown* rules fire hundreds of times on CLI/SQL glue
|
|
70
|
+
# pythonVersion=3.10 matches union syntax in annotations; package still supports 3.9 via __future__ annotations
|
|
71
|
+
reportAny = "none"
|
|
72
|
+
reportUnknownParameterType = "none"
|
|
73
|
+
reportUnknownArgumentType = "none"
|
|
74
|
+
reportUnknownVariableType = "none"
|
|
75
|
+
reportUnknownMemberType = "none"
|
|
76
|
+
reportMissingParameterType = "none"
|
|
77
|
+
reportUnusedCallResult = "none"
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
"""CLI entry point for scip-cli."""
|
|
2
2
|
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
3
5
|
import argparse
|
|
4
6
|
import logging
|
|
5
7
|
import os
|
|
6
8
|
import shutil
|
|
7
9
|
import sqlite3
|
|
8
10
|
import sys
|
|
11
|
+
from collections.abc import Callable
|
|
9
12
|
|
|
10
13
|
from . import __version__
|
|
11
14
|
from .cli_args import add_limit_argument, add_names_only_argument, add_path_argument, add_paths_only_argument
|
|
@@ -19,7 +22,7 @@ else:
|
|
|
19
22
|
logging.disable(logging.DEBUG)
|
|
20
23
|
|
|
21
24
|
|
|
22
|
-
def main():
|
|
25
|
+
def main() -> None:
|
|
23
26
|
parser = argparse.ArgumentParser(
|
|
24
27
|
prog="scip-cli",
|
|
25
28
|
description="Fast code intelligence via SCIP indexes",
|
|
@@ -45,7 +48,7 @@ def main():
|
|
|
45
48
|
type=int,
|
|
46
49
|
default=None,
|
|
47
50
|
metavar="N",
|
|
48
|
-
help=
|
|
51
|
+
help="Max source lines per definition body (default: 80, env SCIP_CLI_MAX_DEF_LINES). Use 0 for unlimited.",
|
|
49
52
|
)
|
|
50
53
|
code_parser.add_argument(
|
|
51
54
|
"--full",
|
|
@@ -151,7 +154,7 @@ def main():
|
|
|
151
154
|
return
|
|
152
155
|
|
|
153
156
|
# Dispatch to command handlers
|
|
154
|
-
dispatch = {
|
|
157
|
+
dispatch: dict[str, Callable[..., None]] = {
|
|
155
158
|
"refs": refs.main,
|
|
156
159
|
"code": code.main,
|
|
157
160
|
"search": search.main,
|
|
@@ -298,7 +298,7 @@ def top_symbol_pressure(db, relative_path: str, limit: int = DEFAULT_LIMIT) -> l
|
|
|
298
298
|
|
|
299
299
|
|
|
300
300
|
def _bind_path(fn, relative_path: str):
|
|
301
|
-
def run(db, limit: int, **
|
|
301
|
+
def run(db, limit: int, **_kwargs):
|
|
302
302
|
return fn(db, relative_path, limit)
|
|
303
303
|
|
|
304
304
|
return run
|
|
@@ -416,7 +416,6 @@ def run_all(
|
|
|
416
416
|
budget=None,
|
|
417
417
|
) -> list[tuple[str, list[str], str | None]]:
|
|
418
418
|
suffix = _scope_suffix(scope)
|
|
419
|
-
opts = {"include_tests": include_tests, "scope": scope, "budget": budget}
|
|
420
419
|
checks = [
|
|
421
420
|
Check("cycles", Priority.HIGH, f"Cycles (file dependencies){suffix}", cycles),
|
|
422
421
|
Check("unreferenced", Priority.HIGH, f"Unreferenced symbols (no refs){suffix}", unreferenced_symbols),
|
|
@@ -438,4 +437,12 @@ def run_all(
|
|
|
438
437
|
Check("bottlenecks", Priority.LOW, f"Bottlenecks (fan-in x fan-out){suffix}", bottlenecks),
|
|
439
438
|
Check("hotspots", Priority.LOW, f"Hotspots (most referenced){suffix}", hotspots),
|
|
440
439
|
]
|
|
441
|
-
return run_checks(
|
|
440
|
+
return run_checks(
|
|
441
|
+
checks,
|
|
442
|
+
db,
|
|
443
|
+
limit,
|
|
444
|
+
priorities,
|
|
445
|
+
include_tests=include_tests,
|
|
446
|
+
scope=scope,
|
|
447
|
+
budget=budget,
|
|
448
|
+
)
|
|
@@ -5,7 +5,6 @@ from __future__ import annotations
|
|
|
5
5
|
from collections.abc import Callable
|
|
6
6
|
from dataclasses import dataclass
|
|
7
7
|
from enum import Enum
|
|
8
|
-
from typing import Any
|
|
9
8
|
|
|
10
9
|
from .common import section
|
|
11
10
|
|
|
@@ -101,20 +100,23 @@ def run_checks(
|
|
|
101
100
|
db,
|
|
102
101
|
limit: int,
|
|
103
102
|
priorities: set[Priority] | None,
|
|
104
|
-
|
|
103
|
+
*,
|
|
104
|
+
include_tests: bool = False,
|
|
105
|
+
scope: str | None = None,
|
|
106
|
+
budget: RowBudget | None = None,
|
|
105
107
|
) -> list[tuple[str, list[str], str | None]]:
|
|
106
108
|
"""Run checks in priority order (high → low), optionally filtered."""
|
|
107
109
|
selected = [check for check in checks if priorities is None or check.priority in priorities]
|
|
108
110
|
selected.sort(key=lambda check: (_ORDER.index(check.priority), check.key))
|
|
109
|
-
|
|
111
|
+
budget_obj: RowBudget = budget or RowBudget(remaining=limit)
|
|
110
112
|
sections: list[tuple[str, list[str], str | None]] = []
|
|
111
113
|
for check in selected:
|
|
112
|
-
if
|
|
114
|
+
if budget_obj.exhausted():
|
|
113
115
|
break
|
|
114
|
-
lines = check.run(db,
|
|
116
|
+
lines = check.run(db, budget_obj.remaining, include_tests=include_tests, scope=scope)
|
|
115
117
|
if lines != ["(none)"]:
|
|
116
|
-
lines = lines[:
|
|
117
|
-
|
|
118
|
+
lines = lines[: budget_obj.remaining]
|
|
119
|
+
budget_obj.remaining -= len(lines)
|
|
118
120
|
preface = check.false_positive_preface if check.false_positive_preface else _preface_for(check.key)
|
|
119
121
|
sections.append(section(check.labeled_title(), lines, preface=preface))
|
|
120
122
|
return sections
|
|
@@ -8,14 +8,14 @@ from .sections import Check, Priority, run_checks
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
def _bind_symbol(fn, symbol_id: int):
|
|
11
|
-
def run(db, limit: int, **
|
|
11
|
+
def run(db, limit: int, **_kwargs):
|
|
12
12
|
return fn(db, symbol_id, limit)
|
|
13
13
|
|
|
14
14
|
return run
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
def _bind_symbol0(fn, symbol_id: int):
|
|
18
|
-
def run(db,
|
|
18
|
+
def run(db, _limit: int, **_kwargs):
|
|
19
19
|
return fn(db, symbol_id)
|
|
20
20
|
|
|
21
21
|
return run
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""Index cache path resolution."""
|
|
2
2
|
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
3
5
|
import contextlib
|
|
4
6
|
import fcntl
|
|
5
7
|
import hashlib
|
|
@@ -78,13 +80,13 @@ def project_cache_slug(project_root: Path) -> str:
|
|
|
78
80
|
return f"{slug}-{digest}"
|
|
79
81
|
|
|
80
82
|
|
|
81
|
-
def get_cache_dir(project_root):
|
|
83
|
+
def get_cache_dir(project_root: Path | str) -> Path:
|
|
82
84
|
"""Get the cache directory for a project (one dir per repo root)."""
|
|
83
85
|
root = Path(project_root).resolve()
|
|
84
86
|
return Path.home() / ".cache" / "scip-cli" / "projects" / project_cache_slug(root)
|
|
85
87
|
|
|
86
88
|
|
|
87
|
-
def find_db(project_root=None):
|
|
89
|
+
def find_db(project_root: Path | str | None = None) -> Path | None:
|
|
88
90
|
"""Find the index.db for the given project (or cwd)."""
|
|
89
91
|
root = project_root or find_project_root()
|
|
90
92
|
if not root:
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""analyze command — run SQL-based analysis dashboards."""
|
|
2
2
|
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
3
5
|
import sys
|
|
4
6
|
|
|
5
7
|
from ..analyze import file as file_checks
|
|
@@ -85,8 +87,10 @@ def _dir_sections(
|
|
|
85
87
|
)
|
|
86
88
|
elif total > MAX_DIR_FILES:
|
|
87
89
|
print(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
+
(
|
|
91
|
+
f"Note: {total} indexed files under {scope}; showing first {MAX_DIR_FILES} "
|
|
92
|
+
f"(analyze one file for full detail)"
|
|
93
|
+
),
|
|
90
94
|
file=sys.stderr,
|
|
91
95
|
)
|
|
92
96
|
files = files[:MAX_DIR_FILES]
|
|
@@ -127,29 +131,28 @@ def main(args):
|
|
|
127
131
|
)
|
|
128
132
|
else:
|
|
129
133
|
resolved = resolve_analyze_target(db, target, project_root, path_scope)
|
|
134
|
+
scope = resolved.scope or ""
|
|
130
135
|
if resolved.kind == "dir":
|
|
131
136
|
sections = _dir_sections(
|
|
132
137
|
db,
|
|
133
|
-
|
|
138
|
+
scope,
|
|
134
139
|
limit=limit,
|
|
135
140
|
include_tests=include_tests,
|
|
136
141
|
priorities=priorities,
|
|
137
142
|
budget=budget,
|
|
138
143
|
)
|
|
139
144
|
elif resolved.kind == "file":
|
|
140
|
-
file_include = _project_include_tests(include_tests,
|
|
145
|
+
file_include = _project_include_tests(include_tests, scope)
|
|
141
146
|
sections = _project_sections(
|
|
142
147
|
db,
|
|
143
148
|
limit=limit,
|
|
144
149
|
include_tests=file_include,
|
|
145
|
-
scope=
|
|
150
|
+
scope=scope,
|
|
146
151
|
priorities=priorities,
|
|
147
152
|
budget=budget,
|
|
148
153
|
)
|
|
149
154
|
if not budget.exhausted():
|
|
150
|
-
sections.extend(
|
|
151
|
-
_file_sections(db, resolved.scope, limit=limit, priorities=priorities, budget=budget)
|
|
152
|
-
)
|
|
155
|
+
sections.extend(_file_sections(db, scope, limit=limit, priorities=priorities, budget=budget))
|
|
153
156
|
else:
|
|
154
157
|
symbol_id, _symbol_str, _display = resolve_one_symbol(
|
|
155
158
|
db,
|
|
@@ -160,5 +163,4 @@ def main(args):
|
|
|
160
163
|
|
|
161
164
|
_print_sections(sections)
|
|
162
165
|
finally:
|
|
163
|
-
|
|
164
|
-
db.close()
|
|
166
|
+
db.close()
|
|
@@ -5,8 +5,8 @@ import sys
|
|
|
5
5
|
from ..cli_args import path_scope_from_args
|
|
6
6
|
from ..output import limit_and_warn
|
|
7
7
|
from ..paths import path_in_scope
|
|
8
|
-
from ..queries import debug_execute
|
|
9
8
|
from ..session import resolve_one_file, resolve_one_symbol, setup
|
|
9
|
+
from ..sql import debug_execute
|
|
10
10
|
from ..symbols import extract_leaf_name
|
|
11
11
|
|
|
12
12
|
|
|
@@ -9,7 +9,7 @@ from ..cache import (
|
|
|
9
9
|
index_db_path,
|
|
10
10
|
promote_next_index,
|
|
11
11
|
)
|
|
12
|
-
from ..indexing import
|
|
12
|
+
from ..indexing import index_project, log_index_complete
|
|
13
13
|
from ..paths import normalize_path_scope
|
|
14
14
|
from ..project import Language, find_project_root_and_language
|
|
15
15
|
from ..scope import save_index_scope
|
|
@@ -27,12 +27,20 @@ def main(args):
|
|
|
27
27
|
sys.exit(1)
|
|
28
28
|
|
|
29
29
|
if path_args:
|
|
30
|
-
scope_paths = [
|
|
30
|
+
scope_paths: list[str] = []
|
|
31
|
+
for path in path_args:
|
|
32
|
+
normalized = normalize_path_scope(path, root)
|
|
33
|
+
if normalized is None:
|
|
34
|
+
print(f"Error: invalid or empty --path: {path!r}", file=sys.stderr)
|
|
35
|
+
sys.exit(1)
|
|
36
|
+
scope_paths.append(normalized)
|
|
31
37
|
save_index_scope(root, scope_paths)
|
|
32
38
|
print(f"Index scope: {', '.join(scope_paths)}", file=sys.stderr)
|
|
33
39
|
print(
|
|
34
|
-
|
|
35
|
-
|
|
40
|
+
(
|
|
41
|
+
"Warning: scoped reindex replaces the cache with only these projects; "
|
|
42
|
+
"run reindex with no --path to restore the full index"
|
|
43
|
+
),
|
|
36
44
|
file=sys.stderr,
|
|
37
45
|
)
|
|
38
46
|
else:
|
|
@@ -48,7 +56,7 @@ def main(args):
|
|
|
48
56
|
with index_build_lock(cache_dir):
|
|
49
57
|
cleanup_in_progress_index(cache_dir)
|
|
50
58
|
try:
|
|
51
|
-
_output_db, skipped, total =
|
|
59
|
+
_output_db, skipped, total = index_project(root, lang, cache_dir, replace=True, log=False)
|
|
52
60
|
except RuntimeError as e:
|
|
53
61
|
cleanup_in_progress_index(cache_dir)
|
|
54
62
|
print(f"Error: {e}", file=sys.stderr)
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""search command - search symbols by pattern."""
|
|
2
2
|
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
3
5
|
import re
|
|
4
6
|
import sys
|
|
5
7
|
|
|
@@ -117,12 +119,12 @@ def _qualified_pattern(pattern: str) -> bool:
|
|
|
117
119
|
def _search_results_from_symbols(db, project_root, symbols, limit):
|
|
118
120
|
"""Turn resolve_symbol rows into search result tuples."""
|
|
119
121
|
symbols = limit_and_warn(symbols, limit, "results")
|
|
120
|
-
results = []
|
|
122
|
+
results: list[tuple[str, int | str, str, str]] = []
|
|
121
123
|
for symbol_id, symbol_str, _display_name in symbols:
|
|
122
124
|
loc = resolve_def_location(db, project_root, symbol_id, symbol_str)
|
|
123
125
|
if loc:
|
|
124
126
|
file_path, start_line, _end_line = loc
|
|
125
|
-
line = start_line + 1
|
|
127
|
+
line: int | str = start_line + 1
|
|
126
128
|
else:
|
|
127
129
|
file_path = _resolve_file_path(db, symbol_str)
|
|
128
130
|
line = "?"
|
|
@@ -229,7 +231,7 @@ def main(args):
|
|
|
229
231
|
loc = resolve_def_location(db, project_root, symbol_id, symbol_str)
|
|
230
232
|
if loc:
|
|
231
233
|
file_path, resolved_start, _end = loc
|
|
232
|
-
line = resolved_start + 1
|
|
234
|
+
line: int | str = resolved_start + 1
|
|
233
235
|
else:
|
|
234
236
|
file_path = _resolve_file_path(db, symbol_str, doc_path)
|
|
235
237
|
line = start_line + 1 if start_line is not None else "?"
|
|
@@ -23,7 +23,7 @@ _SKIP_DIR_NAMES = frozenset(
|
|
|
23
23
|
)
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
def _read_json(path: Path) -> dict | None:
|
|
26
|
+
def _read_json(path: Path) -> dict[str, object] | None:
|
|
27
27
|
try:
|
|
28
28
|
return json.loads(path.read_text(encoding="utf-8"))
|
|
29
29
|
except (OSError, json.JSONDecodeError):
|
|
@@ -52,7 +52,9 @@ def _tsconfig_covers_subdirectories(tsconfig_path: Path) -> bool:
|
|
|
52
52
|
if not data:
|
|
53
53
|
return False
|
|
54
54
|
include = data.get("include")
|
|
55
|
-
if include
|
|
55
|
+
if isinstance(include, str):
|
|
56
|
+
include = [include]
|
|
57
|
+
if not isinstance(include, list) or not include:
|
|
56
58
|
return False
|
|
57
59
|
return any(isinstance(pattern, str) and ("**" in pattern or "/" in pattern) for pattern in include)
|
|
58
60
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""SCIP index building and SQLite database access."""
|
|
2
2
|
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
3
5
|
import os
|
|
4
6
|
import re
|
|
5
7
|
import shutil
|
|
@@ -9,7 +11,6 @@ import sys
|
|
|
9
11
|
import tempfile
|
|
10
12
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
11
13
|
from pathlib import Path
|
|
12
|
-
from typing import Optional
|
|
13
14
|
|
|
14
15
|
from .cache import (
|
|
15
16
|
cleanup_in_progress_index,
|
|
@@ -59,7 +60,7 @@ def log_index_complete(
|
|
|
59
60
|
db_path: Path,
|
|
60
61
|
lang: str,
|
|
61
62
|
*,
|
|
62
|
-
projects:
|
|
63
|
+
projects: int | None = None,
|
|
63
64
|
skipped: int = 0,
|
|
64
65
|
) -> None:
|
|
65
66
|
"""One-line stderr summary after a successful index write."""
|
|
@@ -188,8 +189,8 @@ def _warn_old_scip(binary):
|
|
|
188
189
|
_scip_version_warned = True
|
|
189
190
|
print(
|
|
190
191
|
f"Warning: {binary} {'.'.join(map(str, version))} is older than 0.8.0; "
|
|
191
|
-
"upgrade from "
|
|
192
|
-
f"{SCIP_INSTALL_URL} if indexing fails.",
|
|
192
|
+
+ "upgrade from "
|
|
193
|
+
+ f"{SCIP_INSTALL_URL} if indexing fails.",
|
|
193
194
|
file=sys.stderr,
|
|
194
195
|
)
|
|
195
196
|
|
|
@@ -388,6 +389,7 @@ def _index_typescript(root, cache_dir, projects, env, *, replace=False):
|
|
|
388
389
|
): index
|
|
389
390
|
for index, project in enumerate(projects, start=1)
|
|
390
391
|
}
|
|
392
|
+
indexed_parts: list[tuple[int, Path]] = []
|
|
391
393
|
for future in as_completed(futures):
|
|
392
394
|
label, db_path, error = future.result()
|
|
393
395
|
completed += 1
|
|
@@ -395,10 +397,10 @@ def _index_typescript(root, cache_dir, projects, env, *, replace=False):
|
|
|
395
397
|
skipped += 1
|
|
396
398
|
print(f"Warning: skipped {label}: {error}", file=sys.stderr)
|
|
397
399
|
else:
|
|
398
|
-
|
|
400
|
+
indexed_parts.append((futures[future], db_path))
|
|
399
401
|
if show_progress:
|
|
400
402
|
print(f"Indexed {completed}/{total}: {label}", file=sys.stderr)
|
|
401
|
-
part_dbs = [db for _, db in sorted(
|
|
403
|
+
part_dbs = [db for _, db in sorted(indexed_parts, key=lambda item: item[0])]
|
|
402
404
|
else:
|
|
403
405
|
for index, project in enumerate(projects, start=1):
|
|
404
406
|
label = "." if project == Path(".") else str(project)
|
|
@@ -427,7 +429,7 @@ def _index_typescript(root, cache_dir, projects, env, *, replace=False):
|
|
|
427
429
|
return output_db, len(part_dbs), skipped, total
|
|
428
430
|
|
|
429
431
|
|
|
430
|
-
def
|
|
432
|
+
def index_project(root, lang, cache_dir, *, replace=False, log=True):
|
|
431
433
|
"""Run the language-specific indexer and convert to DB."""
|
|
432
434
|
from .project import Language
|
|
433
435
|
|
|
@@ -497,7 +499,7 @@ def get_db(project_root=None):
|
|
|
497
499
|
else:
|
|
498
500
|
cleanup_in_progress_index(cache_dir)
|
|
499
501
|
try:
|
|
500
|
-
_output_db, skipped, total =
|
|
502
|
+
_output_db, skipped, total = index_project(root, lang, cache_dir, replace=True, log=False)
|
|
501
503
|
promote_next_index(cache_dir)
|
|
502
504
|
log_index_complete(
|
|
503
505
|
index_db_path(cache_dir),
|
|
@@ -153,7 +153,9 @@ def print_def_truncation_notice(query_name, body_offset, lines_shown, def_body_l
|
|
|
153
153
|
return
|
|
154
154
|
at_line = body_offset + lines_shown
|
|
155
155
|
print(
|
|
156
|
-
|
|
157
|
-
|
|
156
|
+
(
|
|
157
|
+
f"Warning: truncated at line {at_line}/{def_body_lines} of definition. "
|
|
158
|
+
f"Continue: code --offset {next_offset} {query_name}"
|
|
159
|
+
),
|
|
158
160
|
file=sys.stderr,
|
|
159
161
|
)
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import sqlite3
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
|
|
7
8
|
from .sql import debug_execute, escape_like
|
|
@@ -33,7 +34,7 @@ def path_in_scope(relative_path: str, scope: str | None) -> bool:
|
|
|
33
34
|
return relative_path.startswith(scope.rstrip("/") + "/")
|
|
34
35
|
|
|
35
36
|
|
|
36
|
-
def path_filter_sql(db, scope: str | None, doc_alias: str = "d") -> tuple[str, list]:
|
|
37
|
+
def path_filter_sql(db: sqlite3.Connection, scope: str | None, doc_alias: str = "d") -> tuple[str, list[str]]:
|
|
37
38
|
"""Build a SQL AND-clause that restricts rows to a file or directory scope."""
|
|
38
39
|
if not scope:
|
|
39
40
|
return "", []
|
|
@@ -49,13 +50,13 @@ def path_filter_sql(db, scope: str | None, doc_alias: str = "d") -> tuple[str, l
|
|
|
49
50
|
return f" AND ({col} = ? OR {col} LIKE ? ESCAPE '\\')", [scope, f"{escaped}/%"]
|
|
50
51
|
|
|
51
52
|
|
|
52
|
-
def path_filter_sql_any(db, scope: str | None, *doc_aliases: str) -> tuple[str, list]:
|
|
53
|
+
def path_filter_sql_any(db: sqlite3.Connection, scope: str | None, *doc_aliases: str) -> tuple[str, list[str]]:
|
|
53
54
|
"""AND-clause: row matches when any document alias column falls in scope."""
|
|
54
55
|
if not scope:
|
|
55
56
|
return "", []
|
|
56
57
|
|
|
57
58
|
parts: list[str] = []
|
|
58
|
-
params: list = []
|
|
59
|
+
params: list[str] = []
|
|
59
60
|
for alias in doc_aliases:
|
|
60
61
|
clause, clause_params = path_filter_sql(db, scope, doc_alias=alias)
|
|
61
62
|
if not clause:
|
|
@@ -69,7 +70,7 @@ def path_filter_sql_any(db, scope: str | None, *doc_aliases: str) -> tuple[str,
|
|
|
69
70
|
return " AND (" + " OR ".join(parts) + ")", params
|
|
70
71
|
|
|
71
72
|
|
|
72
|
-
def list_indexed_paths_in_scope(db, scope: str) -> list[str]:
|
|
73
|
+
def list_indexed_paths_in_scope(db: sqlite3.Connection, scope: str) -> list[str]:
|
|
73
74
|
"""Repo-relative document paths under scope (file or directory), sorted."""
|
|
74
75
|
clause, params = path_filter_sql(db, scope, doc_alias="d")
|
|
75
76
|
if not clause:
|