scip-cli 1.0.1__tar.gz → 1.0.3__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-1.0.1/scip_cli.egg-info → scip_cli-1.0.3}/PKG-INFO +29 -22
- {scip_cli-1.0.1 → scip_cli-1.0.3}/README.md +27 -20
- {scip_cli-1.0.1 → scip_cli-1.0.3}/scip_cli/SKILL.md +7 -8
- {scip_cli-1.0.1 → scip_cli-1.0.3}/scip_cli/__init__.py +1 -1
- {scip_cli-1.0.1 → scip_cli-1.0.3}/scip_cli/__main__.py +28 -20
- scip_cli-1.0.3/scip_cli/commands/def_cmd.py +39 -0
- scip_cli-1.0.3/scip_cli/commands/members.py +85 -0
- scip_cli-1.0.3/scip_cli/commands/rdeps.py +39 -0
- {scip_cli-1.0.1 → scip_cli-1.0.3}/scip_cli/commands/refs.py +34 -58
- scip_cli-1.0.3/scip_cli/commands/reindex.py +26 -0
- scip_cli-1.0.3/scip_cli/commands/search.py +131 -0
- {scip_cli-1.0.1 → scip_cli-1.0.3}/scip_cli/commands/skill.py +1 -1
- scip_cli-1.0.3/scip_cli/commands/symbols.py +35 -0
- scip_cli-1.0.3/scip_cli/lib.py +462 -0
- {scip_cli-1.0.1 → scip_cli-1.0.3/scip_cli.egg-info}/PKG-INFO +29 -22
- {scip_cli-1.0.1 → scip_cli-1.0.3}/scip_cli.egg-info/SOURCES.txt +1 -0
- {scip_cli-1.0.1 → scip_cli-1.0.3}/setup.py +8 -2
- scip_cli-1.0.3/tests/test_pure_functions.py +393 -0
- scip_cli-1.0.1/scip_cli/commands/def_cmd.py +0 -50
- scip_cli-1.0.1/scip_cli/commands/members.py +0 -84
- scip_cli-1.0.1/scip_cli/commands/rdeps.py +0 -52
- scip_cli-1.0.1/scip_cli/commands/search.py +0 -120
- scip_cli-1.0.1/scip_cli/commands/symbols.py +0 -42
- scip_cli-1.0.1/scip_cli/lib.py +0 -369
- scip_cli-1.0.1/tests/test_pure_functions.py +0 -121
- {scip_cli-1.0.1 → scip_cli-1.0.3}/LICENSE +0 -0
- {scip_cli-1.0.1 → scip_cli-1.0.3}/MANIFEST.in +0 -0
- {scip_cli-1.0.1 → scip_cli-1.0.3}/pyproject.toml +0 -0
- {scip_cli-1.0.1 → scip_cli-1.0.3}/scip_cli/commands/__init__.py +0 -0
- {scip_cli-1.0.1 → scip_cli-1.0.3}/scip_cli.egg-info/dependency_links.txt +0 -0
- {scip_cli-1.0.1 → scip_cli-1.0.3}/scip_cli.egg-info/entry_points.txt +0 -0
- {scip_cli-1.0.1 → scip_cli-1.0.3}/scip_cli.egg-info/top_level.txt +0 -0
- {scip_cli-1.0.1 → scip_cli-1.0.3}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: scip-cli
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.3
|
|
4
4
|
Summary: Fast code intelligence via SCIP indexes
|
|
5
5
|
Home-page: https://github.com/flesler/scip-cli
|
|
6
6
|
Author: Ariel Flesler
|
|
@@ -9,7 +9,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
9
9
|
Classifier: License :: OSI Approved :: MIT License
|
|
10
10
|
Classifier: Operating System :: OS Independent
|
|
11
11
|
Classifier: Topic :: Software Development :: Code Generators
|
|
12
|
-
Requires-Python: >=3.
|
|
12
|
+
Requires-Python: >=3.9
|
|
13
13
|
Description-Content-Type: text/markdown
|
|
14
14
|
License-File: LICENSE
|
|
15
15
|
Dynamic: author
|
|
@@ -27,11 +27,11 @@ Dynamic: summary
|
|
|
27
27
|
[](https://badge.fury.io/py/scip-cli)
|
|
28
28
|
[](https://opensource.org/licenses/MIT)
|
|
29
29
|
|
|
30
|
-
Fast code intelligence CLI for TypeScript/JavaScript projects. Query SCIP indexes directly via SQLite for instant results.
|
|
30
|
+
Fast code intelligence CLI for TypeScript/JavaScript and Python projects. Query SCIP indexes directly via SQLite for instant results.
|
|
31
31
|
|
|
32
32
|
## Features
|
|
33
33
|
|
|
34
|
-
- **Fast**: Direct SQLite queries,
|
|
34
|
+
- **Fast**: Direct SQLite queries, eliminating skippable overhead
|
|
35
35
|
- **Simple**: Single binary with subcommands
|
|
36
36
|
- **Auto-indexing**: Automatically indexes projects on first query
|
|
37
37
|
- **Token-efficient**: Clean, minimal output optimized for AI consumption
|
|
@@ -44,17 +44,24 @@ Fast code intelligence CLI for TypeScript/JavaScript projects. Query SCIP indexe
|
|
|
44
44
|
pip install scip-cli
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
### 2. Install prerequisites
|
|
47
|
+
### 2. Install prerequisites (optional)
|
|
48
48
|
|
|
49
|
-
scip-cli
|
|
49
|
+
scip-cli can automatically download the required indexing tools when needed, or you can install them globally for faster performance:
|
|
50
50
|
|
|
51
|
-
**
|
|
51
|
+
**Option A: Let scip-cli handle it (recommended)**
|
|
52
|
+
|
|
53
|
+
Just use scip-cli - it will automatically download the required tools via `npx` on first use. No global installation needed.
|
|
54
|
+
|
|
55
|
+
**Option B: Install globally for better performance**
|
|
52
56
|
|
|
53
57
|
```bash
|
|
54
|
-
#
|
|
58
|
+
# TypeScript/JavaScript indexer (also handles plain JS via --infer-tsconfig)
|
|
55
59
|
npm install -g @sourcegraph/scip-typescript
|
|
56
60
|
|
|
57
|
-
#
|
|
61
|
+
# Python indexer
|
|
62
|
+
npm install -g @sourcegraph/scip-python
|
|
63
|
+
|
|
64
|
+
# SCIP CLI for index conversion
|
|
58
65
|
npm install -g @sourcegraph/scip
|
|
59
66
|
```
|
|
60
67
|
|
|
@@ -62,16 +69,10 @@ npm install -g @sourcegraph/scip
|
|
|
62
69
|
|
|
63
70
|
```bash
|
|
64
71
|
scip-cli --help
|
|
65
|
-
scip-typescript --version
|
|
66
|
-
scip --version
|
|
72
|
+
scip-typescript --version # Only if you chose Option B
|
|
73
|
+
scip --version # Only if you chose Option B
|
|
67
74
|
```
|
|
68
75
|
|
|
69
|
-
## Prerequisites
|
|
70
|
-
|
|
71
|
-
- Python 3.7+
|
|
72
|
-
- `scip-typescript` (for indexing TypeScript/JavaScript)
|
|
73
|
-
- `scip` CLI (for converting indexes)
|
|
74
|
-
|
|
75
76
|
## Usage
|
|
76
77
|
|
|
77
78
|
All commands are subcommands of `scip-cli`:
|
|
@@ -88,6 +89,7 @@ scip-cli <command> [arguments]
|
|
|
88
89
|
- `symbols <file>` - List all symbols in a file
|
|
89
90
|
- `rdeps <file>` - Find files that depend on a file
|
|
90
91
|
- `members <symbol>` - List members of a class/interface
|
|
92
|
+
- `reindex` - Force re-indexing of the current project
|
|
91
93
|
- `skill [path]` - Install or dump the SKILL.md
|
|
92
94
|
|
|
93
95
|
### Examples
|
|
@@ -117,14 +119,16 @@ scip-cli skill ~/.claude/skills/scip/SKILL.md
|
|
|
117
119
|
|
|
118
120
|
## How It Works
|
|
119
121
|
|
|
120
|
-
1. On first query, automatically
|
|
121
|
-
2.
|
|
122
|
-
3.
|
|
123
|
-
4.
|
|
122
|
+
1. On first query, automatically detects project language from `package.json` (TS/JS) or `pyproject.toml`/`setup.py` (Python)
|
|
123
|
+
2. Indexes using `scip-typescript` (adds `--infer-tsconfig` for JS-only projects) or `scip-python`
|
|
124
|
+
3. Converts the SCIP index to SQLite using `scip expt-convert`
|
|
125
|
+
4. Caches the database in `~/.cache/scip-cli/projects/<hash>/index.db`
|
|
126
|
+
5. Subsequent queries are instant SQLite lookups
|
|
124
127
|
|
|
125
128
|
## Performance
|
|
126
129
|
|
|
127
|
-
Compared to bash
|
|
130
|
+
Inspired by [scip-query](https://github.com/PlunderStruck/scip-query), scip-cli is a lightweight Python reimplementation optimized for speed. Compared to the original bash wrapper scripts:
|
|
131
|
+
|
|
128
132
|
- `refs`: 6.4s → 0.03s (213x faster)
|
|
129
133
|
- `def`: 2.8s → 0.05s (56x faster)
|
|
130
134
|
- `search`: 2.6s → 0.03s (87x faster)
|
|
@@ -132,6 +136,8 @@ Compared to bash wrappers:
|
|
|
132
136
|
- `rdeps`: 0.2s → 0.02s (10x faster)
|
|
133
137
|
- `members`: 3.1s → 0.03s (103x faster)
|
|
134
138
|
|
|
139
|
+
The speedup comes from direct SQLite queries instead of shell command chains, eliminating subprocess overhead.
|
|
140
|
+
|
|
135
141
|
## Architecture
|
|
136
142
|
|
|
137
143
|
```
|
|
@@ -146,6 +152,7 @@ scip_cli/
|
|
|
146
152
|
├── symbols.py
|
|
147
153
|
├── rdeps.py
|
|
148
154
|
├── members.py
|
|
155
|
+
├── reindex.py
|
|
149
156
|
└── skill.py
|
|
150
157
|
```
|
|
151
158
|
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
[](https://badge.fury.io/py/scip-cli)
|
|
4
4
|
[](https://opensource.org/licenses/MIT)
|
|
5
5
|
|
|
6
|
-
Fast code intelligence CLI for TypeScript/JavaScript projects. Query SCIP indexes directly via SQLite for instant results.
|
|
6
|
+
Fast code intelligence CLI for TypeScript/JavaScript and Python projects. Query SCIP indexes directly via SQLite for instant results.
|
|
7
7
|
|
|
8
8
|
## Features
|
|
9
9
|
|
|
10
|
-
- **Fast**: Direct SQLite queries,
|
|
10
|
+
- **Fast**: Direct SQLite queries, eliminating skippable overhead
|
|
11
11
|
- **Simple**: Single binary with subcommands
|
|
12
12
|
- **Auto-indexing**: Automatically indexes projects on first query
|
|
13
13
|
- **Token-efficient**: Clean, minimal output optimized for AI consumption
|
|
@@ -20,17 +20,24 @@ Fast code intelligence CLI for TypeScript/JavaScript projects. Query SCIP indexe
|
|
|
20
20
|
pip install scip-cli
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
### 2. Install prerequisites
|
|
23
|
+
### 2. Install prerequisites (optional)
|
|
24
24
|
|
|
25
|
-
scip-cli
|
|
25
|
+
scip-cli can automatically download the required indexing tools when needed, or you can install them globally for faster performance:
|
|
26
26
|
|
|
27
|
-
**
|
|
27
|
+
**Option A: Let scip-cli handle it (recommended)**
|
|
28
|
+
|
|
29
|
+
Just use scip-cli - it will automatically download the required tools via `npx` on first use. No global installation needed.
|
|
30
|
+
|
|
31
|
+
**Option B: Install globally for better performance**
|
|
28
32
|
|
|
29
33
|
```bash
|
|
30
|
-
#
|
|
34
|
+
# TypeScript/JavaScript indexer (also handles plain JS via --infer-tsconfig)
|
|
31
35
|
npm install -g @sourcegraph/scip-typescript
|
|
32
36
|
|
|
33
|
-
#
|
|
37
|
+
# Python indexer
|
|
38
|
+
npm install -g @sourcegraph/scip-python
|
|
39
|
+
|
|
40
|
+
# SCIP CLI for index conversion
|
|
34
41
|
npm install -g @sourcegraph/scip
|
|
35
42
|
```
|
|
36
43
|
|
|
@@ -38,16 +45,10 @@ npm install -g @sourcegraph/scip
|
|
|
38
45
|
|
|
39
46
|
```bash
|
|
40
47
|
scip-cli --help
|
|
41
|
-
scip-typescript --version
|
|
42
|
-
scip --version
|
|
48
|
+
scip-typescript --version # Only if you chose Option B
|
|
49
|
+
scip --version # Only if you chose Option B
|
|
43
50
|
```
|
|
44
51
|
|
|
45
|
-
## Prerequisites
|
|
46
|
-
|
|
47
|
-
- Python 3.7+
|
|
48
|
-
- `scip-typescript` (for indexing TypeScript/JavaScript)
|
|
49
|
-
- `scip` CLI (for converting indexes)
|
|
50
|
-
|
|
51
52
|
## Usage
|
|
52
53
|
|
|
53
54
|
All commands are subcommands of `scip-cli`:
|
|
@@ -64,6 +65,7 @@ scip-cli <command> [arguments]
|
|
|
64
65
|
- `symbols <file>` - List all symbols in a file
|
|
65
66
|
- `rdeps <file>` - Find files that depend on a file
|
|
66
67
|
- `members <symbol>` - List members of a class/interface
|
|
68
|
+
- `reindex` - Force re-indexing of the current project
|
|
67
69
|
- `skill [path]` - Install or dump the SKILL.md
|
|
68
70
|
|
|
69
71
|
### Examples
|
|
@@ -93,14 +95,16 @@ scip-cli skill ~/.claude/skills/scip/SKILL.md
|
|
|
93
95
|
|
|
94
96
|
## How It Works
|
|
95
97
|
|
|
96
|
-
1. On first query, automatically
|
|
97
|
-
2.
|
|
98
|
-
3.
|
|
99
|
-
4.
|
|
98
|
+
1. On first query, automatically detects project language from `package.json` (TS/JS) or `pyproject.toml`/`setup.py` (Python)
|
|
99
|
+
2. Indexes using `scip-typescript` (adds `--infer-tsconfig` for JS-only projects) or `scip-python`
|
|
100
|
+
3. Converts the SCIP index to SQLite using `scip expt-convert`
|
|
101
|
+
4. Caches the database in `~/.cache/scip-cli/projects/<hash>/index.db`
|
|
102
|
+
5. Subsequent queries are instant SQLite lookups
|
|
100
103
|
|
|
101
104
|
## Performance
|
|
102
105
|
|
|
103
|
-
Compared to bash
|
|
106
|
+
Inspired by [scip-query](https://github.com/PlunderStruck/scip-query), scip-cli is a lightweight Python reimplementation optimized for speed. Compared to the original bash wrapper scripts:
|
|
107
|
+
|
|
104
108
|
- `refs`: 6.4s → 0.03s (213x faster)
|
|
105
109
|
- `def`: 2.8s → 0.05s (56x faster)
|
|
106
110
|
- `search`: 2.6s → 0.03s (87x faster)
|
|
@@ -108,6 +112,8 @@ Compared to bash wrappers:
|
|
|
108
112
|
- `rdeps`: 0.2s → 0.02s (10x faster)
|
|
109
113
|
- `members`: 3.1s → 0.03s (103x faster)
|
|
110
114
|
|
|
115
|
+
The speedup comes from direct SQLite queries instead of shell command chains, eliminating subprocess overhead.
|
|
116
|
+
|
|
111
117
|
## Architecture
|
|
112
118
|
|
|
113
119
|
```
|
|
@@ -122,6 +128,7 @@ scip_cli/
|
|
|
122
128
|
├── symbols.py
|
|
123
129
|
├── rdeps.py
|
|
124
130
|
├── members.py
|
|
131
|
+
├── reindex.py
|
|
125
132
|
└── skill.py
|
|
126
133
|
```
|
|
127
134
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: scip-cli
|
|
3
|
-
description: Read when needing to find symbols, definitions, references, or members in TypeScript/JavaScript code
|
|
3
|
+
description: Read when needing to find symbols, definitions, references, or members in TypeScript/JavaScript or Python code
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
TypeScript/JavaScript
|
|
6
|
+
TypeScript/JavaScript (.ts, .tsx, .js, .jsx) and Python (.py) — not GraphQL, CSS, or other files.
|
|
7
7
|
|
|
8
8
|
All commands are sub-commands of `scip-cli`. Run from the project root.
|
|
9
9
|
|
|
@@ -20,20 +20,19 @@ All commands are sub-commands of `scip-cli`. Run from the project root.
|
|
|
20
20
|
|
|
21
21
|
## Gotchas
|
|
22
22
|
|
|
23
|
-
- **Bare names** resolve functions, types (aliases + interfaces), and classes. Consts/variables need `def --
|
|
24
|
-
- **Ambiguous types** (e.g. `Opts` in multiple hooks) — `def` returns all; `refs` picks the first and warns. Use `search`
|
|
25
|
-
- **First run** in a project may auto-index (one-time wait, ~10-30s for large codebases).
|
|
26
|
-
- **Precision escape hatch**: qualified names like `src:hooks:usePatientEntries:usePatientEntries()` always work.
|
|
23
|
+
- **Bare names** resolve functions, types (aliases + interfaces), and classes. Consts/variables need `def --kind variable X` or `search --kind variable X`. Class methods need `members ClassName`, not bare `def methodName`.
|
|
24
|
+
- **Ambiguous types** (e.g. `Opts` in multiple hooks) — `def` returns all; `refs` picks the first and warns. Use `search` with a more specific pattern to disambiguate.
|
|
25
|
+
- **First run** in a project may auto-index (one-time wait, ~10-30s for large codebases). JS-only projects (no `tsconfig.json`) are supported automatically.
|
|
27
26
|
|
|
28
27
|
## Details
|
|
29
28
|
|
|
30
29
|
### def
|
|
31
30
|
|
|
32
31
|
```bash
|
|
33
|
-
def [--
|
|
32
|
+
def [--kind <kind>] <symbol>
|
|
34
33
|
```
|
|
35
34
|
|
|
36
|
-
Kinds: `function`, `
|
|
35
|
+
Kinds: `function`, `method`, `class`, `property`, `variable` — use `--kind` when the bare name isn't in the default set above.
|
|
37
36
|
|
|
38
37
|
### refs
|
|
39
38
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""scip-cli: Fast code intelligence via SCIP indexes."""
|
|
2
|
-
__version__ = "1.0.
|
|
2
|
+
__version__ = "1.0.3"
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
1
|
"""CLI entry point for scip-cli."""
|
|
3
2
|
import argparse
|
|
4
3
|
import sys
|
|
5
4
|
|
|
6
5
|
from . import __version__
|
|
7
|
-
from .
|
|
6
|
+
from .lib import SymbolKind
|
|
7
|
+
from .commands import refs, def_cmd, search, symbols, rdeps, members, skill, reindex
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
def main():
|
|
@@ -21,12 +21,12 @@ def main():
|
|
|
21
21
|
|
|
22
22
|
# def
|
|
23
23
|
def_parser = subparsers.add_parser("def", help="Find symbol definition")
|
|
24
|
-
def_parser.add_argument("--
|
|
24
|
+
def_parser.add_argument("--kind", choices=SymbolKind.filterable_values(), help="Filter by kind")
|
|
25
25
|
def_parser.add_argument("symbol", help="Symbol name")
|
|
26
26
|
|
|
27
27
|
# search
|
|
28
28
|
search_parser = subparsers.add_parser("search", help="Search symbols by pattern")
|
|
29
|
-
search_parser.add_argument("--kind", help="Filter by kind")
|
|
29
|
+
search_parser.add_argument("--kind", choices=SymbolKind.filterable_values(), help="Filter by kind")
|
|
30
30
|
search_parser.add_argument("pattern", help="Search pattern")
|
|
31
31
|
|
|
32
32
|
# symbols
|
|
@@ -45,27 +45,35 @@ def main():
|
|
|
45
45
|
skill_parser = subparsers.add_parser("skill", help="Install or dump the scip-cli SKILL.md")
|
|
46
46
|
skill_parser.add_argument("path", nargs="?", help="Optional file path to write to (creates dirs)")
|
|
47
47
|
|
|
48
|
+
# reindex
|
|
49
|
+
subparsers.add_parser("reindex", help="Force re-indexing of the current project")
|
|
50
|
+
|
|
48
51
|
args = parser.parse_args()
|
|
49
52
|
|
|
50
|
-
|
|
53
|
+
# Dispatch to command handlers
|
|
54
|
+
dispatch = {
|
|
55
|
+
"refs": refs.main,
|
|
56
|
+
"def": def_cmd.main,
|
|
57
|
+
"search": search.main,
|
|
58
|
+
"symbols": symbols.main,
|
|
59
|
+
"rdeps": rdeps.main,
|
|
60
|
+
"members": members.main,
|
|
61
|
+
"skill": skill.main,
|
|
62
|
+
"reindex": reindex.main,
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
handler = dispatch.get(args.command)
|
|
66
|
+
if not handler:
|
|
51
67
|
parser.print_help()
|
|
52
68
|
sys.exit(1)
|
|
53
69
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
elif args.command == "symbols":
|
|
62
|
-
symbols.main(args)
|
|
63
|
-
elif args.command == "rdeps":
|
|
64
|
-
rdeps.main(args)
|
|
65
|
-
elif args.command == "members":
|
|
66
|
-
members.main(args)
|
|
67
|
-
elif args.command == "skill":
|
|
68
|
-
skill.main(args)
|
|
70
|
+
try:
|
|
71
|
+
handler(args)
|
|
72
|
+
except RuntimeError as e:
|
|
73
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
74
|
+
sys.exit(1)
|
|
75
|
+
except KeyboardInterrupt:
|
|
76
|
+
sys.exit(130)
|
|
69
77
|
|
|
70
78
|
|
|
71
79
|
if __name__ == "__main__":
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""def command - find symbol definitions."""
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
from ..lib import (
|
|
5
|
+
setup,
|
|
6
|
+
resolve_symbol,
|
|
7
|
+
read_source_lines,
|
|
8
|
+
infer_kind,
|
|
9
|
+
get_def_location,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def main(args):
|
|
14
|
+
"""Find the definition of a symbol."""
|
|
15
|
+
db, project_root = setup()
|
|
16
|
+
try:
|
|
17
|
+
symbols = resolve_symbol(db, args.symbol, args.kind)
|
|
18
|
+
if not symbols:
|
|
19
|
+
print(f"Symbol '{args.symbol}' not found", file=sys.stderr)
|
|
20
|
+
sys.exit(1)
|
|
21
|
+
|
|
22
|
+
for symbol_id, symbol_str, display_name in symbols:
|
|
23
|
+
row = get_def_location(db, symbol_id)
|
|
24
|
+
if not row:
|
|
25
|
+
continue
|
|
26
|
+
|
|
27
|
+
rel_path, start_line, end_line = row
|
|
28
|
+
kind = infer_kind(symbol_str)
|
|
29
|
+
|
|
30
|
+
lines = read_source_lines(project_root, rel_path, start_line, end_line)
|
|
31
|
+
if lines is None:
|
|
32
|
+
source_snippet = "(could not read source)"
|
|
33
|
+
else:
|
|
34
|
+
source_snippet = ''.join(lines).rstrip('\n')
|
|
35
|
+
|
|
36
|
+
print(f"{rel_path}:{start_line + 1}:{end_line + 1}")
|
|
37
|
+
print(source_snippet)
|
|
38
|
+
finally:
|
|
39
|
+
db.close()
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""members command - list members of a class/interface."""
|
|
2
|
+
import sys
|
|
3
|
+
import re
|
|
4
|
+
|
|
5
|
+
from ..lib import (
|
|
6
|
+
setup,
|
|
7
|
+
resolve_one_symbol,
|
|
8
|
+
get_members,
|
|
9
|
+
get_def_location,
|
|
10
|
+
infer_kind,
|
|
11
|
+
extract_leaf_name,
|
|
12
|
+
read_source_lines,
|
|
13
|
+
SymbolKind,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _member_source_patterns(member_symbol, short, kind):
|
|
18
|
+
"""Build TS/JS and Python regex patterns for finding a member's source line."""
|
|
19
|
+
if "<constructor>" in member_symbol:
|
|
20
|
+
ts_pattern = r'^\s*constructor\s*\('
|
|
21
|
+
elif "<get>" in member_symbol:
|
|
22
|
+
ts_pattern = rf'^\s*(?:public\s+|private\s+|protected\s+|static\s+|readonly\s+)*get\s+{re.escape(short)}\s*\('
|
|
23
|
+
elif "<set>" in member_symbol:
|
|
24
|
+
ts_pattern = rf'^\s*(?:public\s+|private\s+|protected\s+|static\s+|readonly\s+)*set\s+{re.escape(short)}\s*\('
|
|
25
|
+
else:
|
|
26
|
+
ts_pattern = rf'^\s*(?:public\s+|private\s+|protected\s+|static\s+|readonly\s+)*{re.escape(short)}\s*\??\s*[:=(]'
|
|
27
|
+
|
|
28
|
+
py_pattern = None
|
|
29
|
+
if kind == SymbolKind.METHOD:
|
|
30
|
+
py_pattern = rf'^\s*(?:async\s+)?def\s+{re.escape(short)}\s*\('
|
|
31
|
+
elif kind == SymbolKind.PROPERTY:
|
|
32
|
+
py_pattern = rf'^\s*{re.escape(short)}\s*[=:]'
|
|
33
|
+
elif kind == SymbolKind.CLASS:
|
|
34
|
+
py_pattern = rf'^\s*class\s+{re.escape(short)}\s*[:\(]'
|
|
35
|
+
|
|
36
|
+
return ts_pattern, py_pattern
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def main(args):
|
|
40
|
+
"""List members of a class or interface."""
|
|
41
|
+
db, project_root = setup()
|
|
42
|
+
try:
|
|
43
|
+
symbol_id, _, _ = resolve_one_symbol(db, args.symbol)
|
|
44
|
+
members = get_members(db, symbol_id)
|
|
45
|
+
|
|
46
|
+
if not members:
|
|
47
|
+
print(f"No members found for '{args.symbol}'", file=sys.stderr)
|
|
48
|
+
sys.exit(1)
|
|
49
|
+
|
|
50
|
+
parent_def = get_def_location(db, symbol_id)
|
|
51
|
+
parent_file = parent_def[0] if parent_def else None
|
|
52
|
+
parent_start = parent_def[1] if parent_def else None
|
|
53
|
+
parent_end = parent_def[2] if parent_def else None
|
|
54
|
+
|
|
55
|
+
needs_lookup = any(m[3] is None for m in members)
|
|
56
|
+
source_lines = None
|
|
57
|
+
if needs_lookup and project_root and parent_file and parent_start is not None:
|
|
58
|
+
source_lines = read_source_lines(project_root, parent_file, parent_start, parent_end)
|
|
59
|
+
|
|
60
|
+
for member_id, member_symbol, member_name, start_line, end_line in members:
|
|
61
|
+
kind = infer_kind(member_symbol)
|
|
62
|
+
short = extract_leaf_name(member_symbol)
|
|
63
|
+
|
|
64
|
+
if start_line is None and source_lines:
|
|
65
|
+
ts_pattern, py_pattern = _member_source_patterns(member_symbol, short, kind)
|
|
66
|
+
patterns = []
|
|
67
|
+
if parent_file and parent_file.endswith('.py'):
|
|
68
|
+
if py_pattern:
|
|
69
|
+
patterns.append(py_pattern)
|
|
70
|
+
patterns.append(ts_pattern)
|
|
71
|
+
else:
|
|
72
|
+
patterns.append(ts_pattern)
|
|
73
|
+
if py_pattern:
|
|
74
|
+
patterns.append(py_pattern)
|
|
75
|
+
|
|
76
|
+
for i, line in enumerate(source_lines):
|
|
77
|
+
if any(re.match(p, line) for p in patterns):
|
|
78
|
+
start_line = parent_start + i
|
|
79
|
+
end_line = start_line
|
|
80
|
+
break
|
|
81
|
+
|
|
82
|
+
line_info = f"{start_line + 1}:{end_line + 1}" if start_line is not None else "??"
|
|
83
|
+
print(f"{line_info} {kind} {short}")
|
|
84
|
+
finally:
|
|
85
|
+
db.close()
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""rdeps command - find reverse dependencies of a file."""
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
from ..lib import (
|
|
5
|
+
setup,
|
|
6
|
+
resolve_one_file,
|
|
7
|
+
get_file_symbols,
|
|
8
|
+
get_refs_for_symbols,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def main(args):
|
|
13
|
+
"""Find all files that import from this file."""
|
|
14
|
+
db, _ = setup()
|
|
15
|
+
try:
|
|
16
|
+
file_path = resolve_one_file(db, args.file)
|
|
17
|
+
|
|
18
|
+
symbols = get_file_symbols(db, file_path)
|
|
19
|
+
if not symbols:
|
|
20
|
+
print(f"No symbols found in '{file_path}'", file=sys.stderr)
|
|
21
|
+
sys.exit(1)
|
|
22
|
+
|
|
23
|
+
symbol_ids = [s[0] for s in symbols]
|
|
24
|
+
refs = get_refs_for_symbols(db, symbol_ids)
|
|
25
|
+
|
|
26
|
+
rdeps = set()
|
|
27
|
+
for symbol_id, ref_list in refs.items():
|
|
28
|
+
for ref_path, ref_line in ref_list:
|
|
29
|
+
if ref_path != file_path:
|
|
30
|
+
rdeps.add(ref_path)
|
|
31
|
+
|
|
32
|
+
if not rdeps:
|
|
33
|
+
print(f"No reverse dependencies found for '{file_path}'", file=sys.stderr)
|
|
34
|
+
sys.exit(1)
|
|
35
|
+
|
|
36
|
+
for dep_path in sorted(rdeps):
|
|
37
|
+
print(dep_path)
|
|
38
|
+
finally:
|
|
39
|
+
db.close()
|
|
@@ -2,10 +2,8 @@
|
|
|
2
2
|
import sys
|
|
3
3
|
|
|
4
4
|
from ..lib import (
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
warn_ambiguous,
|
|
8
|
-
find_project_root,
|
|
5
|
+
setup,
|
|
6
|
+
resolve_one_symbol,
|
|
9
7
|
read_source_lines,
|
|
10
8
|
extract_leaf_name,
|
|
11
9
|
)
|
|
@@ -13,14 +11,12 @@ from ..lib import (
|
|
|
13
11
|
|
|
14
12
|
def get_exact_refs(db, symbol_id, project_root):
|
|
15
13
|
"""Get references with exact line numbers by reading source files."""
|
|
16
|
-
# Get symbol name once
|
|
17
14
|
sym_row = db.execute("SELECT symbol FROM global_symbols WHERE id = ?", (symbol_id,)).fetchone()
|
|
18
15
|
if not sym_row:
|
|
19
16
|
return []
|
|
20
|
-
|
|
17
|
+
|
|
21
18
|
leaf = extract_leaf_name(sym_row[0])
|
|
22
|
-
|
|
23
|
-
# Get all chunks that reference this symbol
|
|
19
|
+
|
|
24
20
|
chunks = db.execute("""
|
|
25
21
|
SELECT c.id, c.document_id, c.start_line, c.end_line, d.relative_path
|
|
26
22
|
FROM mentions m
|
|
@@ -28,80 +24,60 @@ def get_exact_refs(db, symbol_id, project_root):
|
|
|
28
24
|
JOIN documents d ON c.document_id = d.id
|
|
29
25
|
WHERE m.symbol_id = ? AND m.role != 1
|
|
30
26
|
""", (symbol_id,)).fetchall()
|
|
31
|
-
|
|
27
|
+
|
|
32
28
|
if not chunks:
|
|
33
29
|
return []
|
|
34
|
-
|
|
35
|
-
# Group by document
|
|
30
|
+
|
|
36
31
|
by_doc = {}
|
|
37
32
|
for chunk_id, doc_id, start_line, end_line, rel_path in chunks:
|
|
38
33
|
if doc_id not in by_doc:
|
|
39
34
|
by_doc[doc_id] = {'path': rel_path, 'chunks': []}
|
|
40
35
|
by_doc[doc_id]['chunks'].append((chunk_id, start_line, end_line))
|
|
41
|
-
|
|
36
|
+
|
|
42
37
|
results = []
|
|
43
|
-
|
|
44
|
-
# For each document, read source and find exact lines
|
|
38
|
+
|
|
45
39
|
for doc_id, info in by_doc.items():
|
|
46
40
|
rel_path = info['path']
|
|
47
|
-
|
|
48
|
-
# Get min/max line range for this document
|
|
49
41
|
min_line = min(c[1] for c in info['chunks'])
|
|
50
42
|
max_line = max(c[2] for c in info['chunks'])
|
|
51
|
-
|
|
52
|
-
# Read only the needed range
|
|
43
|
+
|
|
53
44
|
lines = read_source_lines(project_root, rel_path, min_line, max_line)
|
|
54
45
|
if lines is None:
|
|
55
|
-
# If can't read file, fall back to chunk start lines
|
|
56
46
|
for chunk_id, start_line, end_line in info['chunks']:
|
|
57
47
|
results.append((rel_path, start_line + 1))
|
|
58
48
|
continue
|
|
59
|
-
|
|
60
|
-
# Search for the symbol in each chunk's line range
|
|
49
|
+
|
|
61
50
|
for chunk_id, start_line, end_line in info['chunks']:
|
|
62
|
-
# Search within the chunk range (adjusted for offset)
|
|
63
51
|
offset = min_line
|
|
52
|
+
found = False
|
|
64
53
|
for line_idx in range(start_line - offset, min(end_line - offset + 1, len(lines))):
|
|
65
|
-
|
|
66
|
-
# Simple check: does the line contain the symbol name?
|
|
67
|
-
if leaf in line:
|
|
54
|
+
if leaf in lines[line_idx]:
|
|
68
55
|
results.append((rel_path, line_idx + offset + 1))
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
56
|
+
found = True
|
|
57
|
+
break
|
|
58
|
+
if not found:
|
|
72
59
|
results.append((rel_path, start_line + 1))
|
|
73
|
-
|
|
60
|
+
|
|
74
61
|
return results
|
|
75
62
|
|
|
76
63
|
|
|
77
64
|
def main(args):
|
|
78
65
|
"""Find all references to a symbol."""
|
|
79
|
-
db =
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if not refs:
|
|
98
|
-
print(f"No references found for '{args.symbol}'", file=sys.stderr)
|
|
99
|
-
sys.exit(1)
|
|
100
|
-
|
|
101
|
-
# Deduplicate and sort
|
|
102
|
-
seen = set()
|
|
103
|
-
for path, line in refs:
|
|
104
|
-
key = (path, line)
|
|
105
|
-
if key not in seen:
|
|
106
|
-
seen.add(key)
|
|
107
|
-
print(f"{path}:{line}")
|
|
66
|
+
db, project_root = setup()
|
|
67
|
+
try:
|
|
68
|
+
symbol_id, _, _ = resolve_one_symbol(db, args.symbol)
|
|
69
|
+
|
|
70
|
+
refs = get_exact_refs(db, symbol_id, project_root)
|
|
71
|
+
|
|
72
|
+
if not refs:
|
|
73
|
+
print(f"No references found for '{args.symbol}'", file=sys.stderr)
|
|
74
|
+
sys.exit(1)
|
|
75
|
+
|
|
76
|
+
seen = set()
|
|
77
|
+
for path, line in refs:
|
|
78
|
+
key = (path, line)
|
|
79
|
+
if key not in seen:
|
|
80
|
+
seen.add(key)
|
|
81
|
+
print(f"{path}:{line}")
|
|
82
|
+
finally:
|
|
83
|
+
db.close()
|