scip-cli 1.0.2__tar.gz → 1.1.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.
Files changed (34) hide show
  1. {scip_cli-1.0.2/scip_cli.egg-info → scip_cli-1.1.0}/PKG-INFO +50 -17
  2. {scip_cli-1.0.2 → scip_cli-1.1.0}/README.md +48 -15
  3. {scip_cli-1.0.2 → scip_cli-1.1.0}/scip_cli/SKILL.md +23 -14
  4. {scip_cli-1.0.2 → scip_cli-1.1.0}/scip_cli/__init__.py +1 -1
  5. {scip_cli-1.0.2 → scip_cli-1.1.0}/scip_cli/__main__.py +45 -20
  6. scip_cli-1.1.0/scip_cli/commands/def_cmd.py +47 -0
  7. scip_cli-1.1.0/scip_cli/commands/members.py +86 -0
  8. scip_cli-1.1.0/scip_cli/commands/rdeps.py +47 -0
  9. scip_cli-1.1.0/scip_cli/commands/refs.py +116 -0
  10. scip_cli-1.1.0/scip_cli/commands/reindex.py +26 -0
  11. scip_cli-1.1.0/scip_cli/commands/search.py +138 -0
  12. {scip_cli-1.0.2 → scip_cli-1.1.0}/scip_cli/commands/skill.py +1 -1
  13. scip_cli-1.1.0/scip_cli/commands/symbols.py +40 -0
  14. scip_cli-1.1.0/scip_cli/lib.py +505 -0
  15. {scip_cli-1.0.2 → scip_cli-1.1.0/scip_cli.egg-info}/PKG-INFO +50 -17
  16. {scip_cli-1.0.2 → scip_cli-1.1.0}/scip_cli.egg-info/SOURCES.txt +1 -0
  17. {scip_cli-1.0.2 → scip_cli-1.1.0}/setup.py +8 -2
  18. scip_cli-1.1.0/tests/test_pure_functions.py +532 -0
  19. scip_cli-1.0.2/scip_cli/commands/def_cmd.py +0 -50
  20. scip_cli-1.0.2/scip_cli/commands/members.py +0 -84
  21. scip_cli-1.0.2/scip_cli/commands/rdeps.py +0 -52
  22. scip_cli-1.0.2/scip_cli/commands/refs.py +0 -107
  23. scip_cli-1.0.2/scip_cli/commands/search.py +0 -120
  24. scip_cli-1.0.2/scip_cli/commands/symbols.py +0 -42
  25. scip_cli-1.0.2/scip_cli/lib.py +0 -390
  26. scip_cli-1.0.2/tests/test_pure_functions.py +0 -121
  27. {scip_cli-1.0.2 → scip_cli-1.1.0}/LICENSE +0 -0
  28. {scip_cli-1.0.2 → scip_cli-1.1.0}/MANIFEST.in +0 -0
  29. {scip_cli-1.0.2 → scip_cli-1.1.0}/pyproject.toml +0 -0
  30. {scip_cli-1.0.2 → scip_cli-1.1.0}/scip_cli/commands/__init__.py +0 -0
  31. {scip_cli-1.0.2 → scip_cli-1.1.0}/scip_cli.egg-info/dependency_links.txt +0 -0
  32. {scip_cli-1.0.2 → scip_cli-1.1.0}/scip_cli.egg-info/entry_points.txt +0 -0
  33. {scip_cli-1.0.2 → scip_cli-1.1.0}/scip_cli.egg-info/top_level.txt +0 -0
  34. {scip_cli-1.0.2 → scip_cli-1.1.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scip-cli
3
- Version: 1.0.2
3
+ Version: 1.1.0
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.7
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
  [![PyPI version](https://badge.fury.io/py/scip-cli.svg)](https://badge.fury.io/py/scip-cli)
28
28
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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, 100-500x faster than bash wrappers
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
@@ -40,10 +40,27 @@ Fast code intelligence CLI for TypeScript/JavaScript projects. Query SCIP indexe
40
40
 
41
41
  ### 1. Install scip-cli
42
42
 
43
+ **From PyPI:**
44
+
43
45
  ```bash
44
46
  pip install scip-cli
45
47
  ```
46
48
 
49
+ **From source (local development):**
50
+
51
+ ```bash
52
+ git clone https://github.com/flesler/scip-cli.git
53
+ cd scip-cli
54
+ pip install .
55
+ ```
56
+
57
+ For editable development (where `pip install -e .` fails due to permissions):
58
+
59
+ ```bash
60
+ export PYTHONPATH=/path/to/scip-cli:$PYTHONPATH
61
+ python -m scip_cli --help
62
+ ```
63
+
47
64
  ### 2. Install prerequisites (optional)
48
65
 
49
66
  scip-cli can automatically download the required indexing tools when needed, or you can install them globally for faster performance:
@@ -55,10 +72,13 @@ Just use scip-cli - it will automatically download the required tools via `npx`
55
72
  **Option B: Install globally for better performance**
56
73
 
57
74
  ```bash
58
- # Install scip-typescript (SCIP indexer)
75
+ # TypeScript/JavaScript indexer (also handles plain JS via --infer-tsconfig)
59
76
  npm install -g @sourcegraph/scip-typescript
60
77
 
61
- # Install scip (SCIP CLI for index conversion)
78
+ # Python indexer
79
+ npm install -g @sourcegraph/scip-python
80
+
81
+ # SCIP CLI for index conversion
62
82
  npm install -g @sourcegraph/scip
63
83
  ```
64
84
 
@@ -70,12 +90,6 @@ scip-typescript --version # Only if you chose Option B
70
90
  scip --version # Only if you chose Option B
71
91
  ```
72
92
 
73
- ## Prerequisites
74
-
75
- - Python 3.7+
76
- - `scip-typescript` (for indexing TypeScript/JavaScript)
77
- - `scip` CLI (for converting indexes)
78
-
79
93
  ## Usage
80
94
 
81
95
  All commands are subcommands of `scip-cli`:
@@ -92,6 +106,7 @@ scip-cli <command> [arguments]
92
106
  - `symbols <file>` - List all symbols in a file
93
107
  - `rdeps <file>` - Find files that depend on a file
94
108
  - `members <symbol>` - List members of a class/interface
109
+ - `reindex` - Force re-indexing of the current project
95
110
  - `skill [path]` - Install or dump the SKILL.md
96
111
 
97
112
  ### Examples
@@ -121,14 +136,16 @@ scip-cli skill ~/.claude/skills/scip/SKILL.md
121
136
 
122
137
  ## How It Works
123
138
 
124
- 1. On first query, automatically indexes the project using `scip-typescript`
125
- 2. Converts the SCIP index to SQLite using `scip expt-convert`
126
- 3. Caches the database in `~/.cache/scip-query/projects/<hash>/index.db`
127
- 4. Subsequent queries are instant SQLite lookups
139
+ 1. On first query, automatically detects project language from `package.json` (TS/JS) or `pyproject.toml`/`setup.py` (Python)
140
+ 2. Indexes using `scip-typescript` (adds `--infer-tsconfig` for JS-only projects) or `scip-python`
141
+ 3. Converts the SCIP index to SQLite using `scip expt-convert`
142
+ 4. Caches the database in `~/.cache/scip-cli/projects/<hash>/index.db`
143
+ 5. Subsequent queries are instant SQLite lookups
128
144
 
129
145
  ## Performance
130
146
 
131
- Compared to bash wrappers:
147
+ 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:
148
+
132
149
  - `refs`: 6.4s → 0.03s (213x faster)
133
150
  - `def`: 2.8s → 0.05s (56x faster)
134
151
  - `search`: 2.6s → 0.03s (87x faster)
@@ -136,6 +153,8 @@ Compared to bash wrappers:
136
153
  - `rdeps`: 0.2s → 0.02s (10x faster)
137
154
  - `members`: 3.1s → 0.03s (103x faster)
138
155
 
156
+ The speedup comes from direct SQLite queries instead of shell command chains, eliminating subprocess overhead.
157
+
139
158
  ## Architecture
140
159
 
141
160
  ```
@@ -150,9 +169,23 @@ scip_cli/
150
169
  ├── symbols.py
151
170
  ├── rdeps.py
152
171
  ├── members.py
172
+ ├── reindex.py
153
173
  └── skill.py
154
174
  ```
155
175
 
176
+ ## Development
177
+
178
+ ### Debug Logging
179
+
180
+ Set `SCIP_CLI_DEBUG=1` to enable SQL query logging to stderr:
181
+
182
+ ```bash
183
+ SCIP_CLI_DEBUG=1 scip-cli refs MyFunction
184
+ # Shows: SQL: SELECT ... | params: (...)
185
+ ```
186
+
187
+ This is useful for testing and debugging SQL queries without exposing a `--debug` flag to users.
188
+
156
189
  ## License
157
190
 
158
191
  MIT
@@ -3,11 +3,11 @@
3
3
  [![PyPI version](https://badge.fury.io/py/scip-cli.svg)](https://badge.fury.io/py/scip-cli)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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, 100-500x faster than bash wrappers
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
@@ -16,10 +16,27 @@ Fast code intelligence CLI for TypeScript/JavaScript projects. Query SCIP indexe
16
16
 
17
17
  ### 1. Install scip-cli
18
18
 
19
+ **From PyPI:**
20
+
19
21
  ```bash
20
22
  pip install scip-cli
21
23
  ```
22
24
 
25
+ **From source (local development):**
26
+
27
+ ```bash
28
+ git clone https://github.com/flesler/scip-cli.git
29
+ cd scip-cli
30
+ pip install .
31
+ ```
32
+
33
+ For editable development (where `pip install -e .` fails due to permissions):
34
+
35
+ ```bash
36
+ export PYTHONPATH=/path/to/scip-cli:$PYTHONPATH
37
+ python -m scip_cli --help
38
+ ```
39
+
23
40
  ### 2. Install prerequisites (optional)
24
41
 
25
42
  scip-cli can automatically download the required indexing tools when needed, or you can install them globally for faster performance:
@@ -31,10 +48,13 @@ Just use scip-cli - it will automatically download the required tools via `npx`
31
48
  **Option B: Install globally for better performance**
32
49
 
33
50
  ```bash
34
- # Install scip-typescript (SCIP indexer)
51
+ # TypeScript/JavaScript indexer (also handles plain JS via --infer-tsconfig)
35
52
  npm install -g @sourcegraph/scip-typescript
36
53
 
37
- # Install scip (SCIP CLI for index conversion)
54
+ # Python indexer
55
+ npm install -g @sourcegraph/scip-python
56
+
57
+ # SCIP CLI for index conversion
38
58
  npm install -g @sourcegraph/scip
39
59
  ```
40
60
 
@@ -46,12 +66,6 @@ scip-typescript --version # Only if you chose Option B
46
66
  scip --version # Only if you chose Option B
47
67
  ```
48
68
 
49
- ## Prerequisites
50
-
51
- - Python 3.7+
52
- - `scip-typescript` (for indexing TypeScript/JavaScript)
53
- - `scip` CLI (for converting indexes)
54
-
55
69
  ## Usage
56
70
 
57
71
  All commands are subcommands of `scip-cli`:
@@ -68,6 +82,7 @@ scip-cli <command> [arguments]
68
82
  - `symbols <file>` - List all symbols in a file
69
83
  - `rdeps <file>` - Find files that depend on a file
70
84
  - `members <symbol>` - List members of a class/interface
85
+ - `reindex` - Force re-indexing of the current project
71
86
  - `skill [path]` - Install or dump the SKILL.md
72
87
 
73
88
  ### Examples
@@ -97,14 +112,16 @@ scip-cli skill ~/.claude/skills/scip/SKILL.md
97
112
 
98
113
  ## How It Works
99
114
 
100
- 1. On first query, automatically indexes the project using `scip-typescript`
101
- 2. Converts the SCIP index to SQLite using `scip expt-convert`
102
- 3. Caches the database in `~/.cache/scip-query/projects/<hash>/index.db`
103
- 4. Subsequent queries are instant SQLite lookups
115
+ 1. On first query, automatically detects project language from `package.json` (TS/JS) or `pyproject.toml`/`setup.py` (Python)
116
+ 2. Indexes using `scip-typescript` (adds `--infer-tsconfig` for JS-only projects) or `scip-python`
117
+ 3. Converts the SCIP index to SQLite using `scip expt-convert`
118
+ 4. Caches the database in `~/.cache/scip-cli/projects/<hash>/index.db`
119
+ 5. Subsequent queries are instant SQLite lookups
104
120
 
105
121
  ## Performance
106
122
 
107
- Compared to bash wrappers:
123
+ 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:
124
+
108
125
  - `refs`: 6.4s → 0.03s (213x faster)
109
126
  - `def`: 2.8s → 0.05s (56x faster)
110
127
  - `search`: 2.6s → 0.03s (87x faster)
@@ -112,6 +129,8 @@ Compared to bash wrappers:
112
129
  - `rdeps`: 0.2s → 0.02s (10x faster)
113
130
  - `members`: 3.1s → 0.03s (103x faster)
114
131
 
132
+ The speedup comes from direct SQLite queries instead of shell command chains, eliminating subprocess overhead.
133
+
115
134
  ## Architecture
116
135
 
117
136
  ```
@@ -126,9 +145,23 @@ scip_cli/
126
145
  ├── symbols.py
127
146
  ├── rdeps.py
128
147
  ├── members.py
148
+ ├── reindex.py
129
149
  └── skill.py
130
150
  ```
131
151
 
152
+ ## Development
153
+
154
+ ### Debug Logging
155
+
156
+ Set `SCIP_CLI_DEBUG=1` to enable SQL query logging to stderr:
157
+
158
+ ```bash
159
+ SCIP_CLI_DEBUG=1 scip-cli refs MyFunction
160
+ # Shows: SQL: SELECT ... | params: (...)
161
+ ```
162
+
163
+ This is useful for testing and debugging SQL queries without exposing a `--debug` flag to users.
164
+
132
165
  ## License
133
166
 
134
167
  MIT
@@ -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 only (.ts, .tsx, .js, .jsx) — not GraphQL, CSS, or other files.
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
 
@@ -11,8 +11,8 @@ All commands are sub-commands of `scip-cli`. Run from the project root.
11
11
 
12
12
  | Question | Use | What you get |
13
13
  |----------|-----|--------------|
14
- | "Where is X defined and what does it do?" | `def X` | Functions: full body. Classes: full definition. Multiple exact matches returned together |
15
- | "Where is X used/called?" | `refs X` | All file:line locations (~0.03s). Ambiguous bare names first match only |
14
+ | "Where is X defined and what does it do?" | `def X` | Functions: full body. Classes: full definition. Use `--limit` to cap results |
15
+ | "Where is X used/called?" | `refs X` | All file:line locations. Shows refs for all matching symbols. Use `--limit` to cap |
16
16
  | "What's in this file?" | `symbols file` | All symbols — bare filename works (`HistoryTab`, `usePatientEntries`) |
17
17
  | "Find symbols by name" | `search name` | Functions, types, interfaces, classes. Use `--kind variable` for consts |
18
18
  | "What files depend on this file?" | `rdeps file` | Importers — bare name works |
@@ -20,53 +20,62 @@ 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 --type 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` or a qualified `src:…` name to disambiguate.
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 matches; `refs` returns refs for all matching symbols. Use `--limit N` to cap results, or 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 [--type <kind>] <symbol>
32
+ def [--kind <kind>] [--limit N] <symbol>
34
33
  ```
35
34
 
36
- Kinds: `function`, `class`, `interface`, `type`, `method`, `variable` — use `--type` when the bare name isn't in the default set above.
35
+ Kinds: `function`, `method`, `class`, `property`, `variable` — use `--kind` when the bare name isn't in the default set above.
36
+
37
+ Default `--limit` is 10. Use `--limit 0` for unlimited (not recommended for large codebases).
37
38
 
38
39
  ### refs
39
40
 
40
41
  ```bash
41
- refs <symbol>
42
+ refs [--limit N] <symbol>
42
43
  ```
43
44
 
44
45
  Returns `file:line` for each reference. Reads source files to find exact line numbers.
45
46
 
47
+ Default `--limit` is 10. When multiple symbols match, refs are grouped by symbol with `# <symbol>` headers.
48
+
46
49
  ### search
47
50
 
48
51
  ```bash
49
- search [--kind <kind>] <pattern>
52
+ search [--kind <kind>] [--limit N] <pattern>
50
53
  ```
51
54
 
52
55
  Returns `file:line Kind symbolName`. Filters noisy symbols (file-level, parameters, type literals).
53
56
 
57
+ Default `--limit` is 10.
58
+
54
59
  ### symbols
55
60
 
56
61
  ```bash
57
- symbols <file>
62
+ symbols [--limit N] <file>
58
63
  ```
59
64
 
60
65
  Returns `startLine-endLine kind name` for each symbol in the file.
61
66
 
67
+ Default `--limit` is 10.
68
+
62
69
  ### rdeps
63
70
 
64
71
  ```bash
65
- rdeps <file>
72
+ rdeps [--limit N] <file>
66
73
  ```
67
74
 
68
75
  Returns list of files that import from this file.
69
76
 
77
+ Default `--limit` is 10.
78
+
70
79
  ### members
71
80
 
72
81
  ```bash
@@ -1,2 +1,2 @@
1
1
  """scip-cli: Fast code intelligence via SCIP indexes."""
2
- __version__ = "1.0.2"
2
+ __version__ = "1.1.0"
@@ -1,10 +1,22 @@
1
- #!/usr/bin/env python3
2
1
  """CLI entry point for scip-cli."""
3
2
  import argparse
3
+ import logging
4
+ import os
4
5
  import sys
5
6
 
6
7
  from . import __version__
7
- from .commands import refs, def_cmd, search, symbols, rdeps, members, skill
8
+ from .lib import SymbolKind
9
+ from .commands import refs, def_cmd, search, symbols, rdeps, members, skill, reindex
10
+
11
+ # Set up debug logging based on SCIP_CLI_DEBUG env var
12
+ if os.environ.get("SCIP_CLI_DEBUG"):
13
+ logging.basicConfig(
14
+ level=logging.DEBUG,
15
+ format="%(name)s: %(message)s",
16
+ stream=sys.stderr
17
+ )
18
+ else:
19
+ logging.disable(logging.DEBUG)
8
20
 
9
21
 
10
22
  def main():
@@ -18,23 +30,28 @@ def main():
18
30
  # refs
19
31
  refs_parser = subparsers.add_parser("refs", help="Find references to a symbol")
20
32
  refs_parser.add_argument("symbol", help="Symbol name")
33
+ refs_parser.add_argument("--limit", type=int, default=10, help="Max results (default: 10)")
21
34
 
22
35
  # def
23
36
  def_parser = subparsers.add_parser("def", help="Find symbol definition")
24
- def_parser.add_argument("--type", dest="kind", help="Filter by kind (function, class, etc)")
37
+ def_parser.add_argument("--kind", choices=SymbolKind.filterable_values(), help="Filter by kind")
38
+ def_parser.add_argument("--limit", type=int, default=10, help="Max results (default: 10)")
25
39
  def_parser.add_argument("symbol", help="Symbol name")
26
40
 
27
41
  # search
28
42
  search_parser = subparsers.add_parser("search", help="Search symbols by pattern")
29
- search_parser.add_argument("--kind", help="Filter by kind")
43
+ search_parser.add_argument("--kind", choices=SymbolKind.filterable_values(), help="Filter by kind")
44
+ search_parser.add_argument("--limit", type=int, default=10, help="Max results (default: 10)")
30
45
  search_parser.add_argument("pattern", help="Search pattern")
31
46
 
32
47
  # symbols
33
48
  symbols_parser = subparsers.add_parser("symbols", help="List symbols in a file")
49
+ symbols_parser.add_argument("--limit", type=int, default=10, help="Max results (default: 10)")
34
50
  symbols_parser.add_argument("file", help="File path or pattern")
35
51
 
36
52
  # rdeps
37
53
  rdeps_parser = subparsers.add_parser("rdeps", help="Find reverse dependencies of a file")
54
+ rdeps_parser.add_argument("--limit", type=int, default=10, help="Max results (default: 10)")
38
55
  rdeps_parser.add_argument("file", help="File path or pattern")
39
56
 
40
57
  # members
@@ -45,27 +62,35 @@ def main():
45
62
  skill_parser = subparsers.add_parser("skill", help="Install or dump the scip-cli SKILL.md")
46
63
  skill_parser.add_argument("path", nargs="?", help="Optional file path to write to (creates dirs)")
47
64
 
65
+ # reindex
66
+ subparsers.add_parser("reindex", help="Force re-indexing of the current project")
67
+
48
68
  args = parser.parse_args()
49
69
 
50
- if not args.command:
70
+ # Dispatch to command handlers
71
+ dispatch = {
72
+ "refs": refs.main,
73
+ "def": def_cmd.main,
74
+ "search": search.main,
75
+ "symbols": symbols.main,
76
+ "rdeps": rdeps.main,
77
+ "members": members.main,
78
+ "skill": skill.main,
79
+ "reindex": reindex.main,
80
+ }
81
+
82
+ handler = dispatch.get(args.command)
83
+ if not handler:
51
84
  parser.print_help()
52
85
  sys.exit(1)
53
86
 
54
- # Dispatch to command handlers
55
- if args.command == "refs":
56
- refs.main(args)
57
- elif args.command == "def":
58
- def_cmd.main(args)
59
- elif args.command == "search":
60
- search.main(args)
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)
87
+ try:
88
+ handler(args)
89
+ except RuntimeError as e:
90
+ print(f"Error: {e}", file=sys.stderr)
91
+ sys.exit(1)
92
+ except KeyboardInterrupt:
93
+ sys.exit(130)
69
94
 
70
95
 
71
96
  if __name__ == "__main__":
@@ -0,0 +1,47 @@
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
+ format_line_range,
11
+ )
12
+
13
+
14
+ def main(args):
15
+ """Find the definition of a symbol."""
16
+ db, project_root = setup()
17
+ try:
18
+ limit = args.limit
19
+ symbols = resolve_symbol(db, args.symbol, args.kind, limit=limit + 1)
20
+ if not symbols:
21
+ print(f"Symbol '{args.symbol}' not found", file=sys.stderr)
22
+ sys.exit(1)
23
+
24
+ hit_limit = len(symbols) > limit
25
+ symbols = symbols[:limit]
26
+
27
+ for symbol_id, symbol_str, display_name in symbols:
28
+ row = get_def_location(db, symbol_id)
29
+ if not row:
30
+ continue
31
+
32
+ rel_path, start_line, end_line = row
33
+ kind = infer_kind(symbol_str)
34
+
35
+ lines = read_source_lines(project_root, rel_path, start_line, end_line)
36
+ if lines is None:
37
+ source_snippet = "(could not read source)"
38
+ else:
39
+ source_snippet = ''.join(lines).rstrip('\n')
40
+
41
+ print(f"{rel_path}:{format_line_range(start_line, end_line)}")
42
+ print(source_snippet)
43
+
44
+ if hit_limit:
45
+ print(f"# Warning: more than {limit} symbols match, showing first {limit}", file=sys.stderr)
46
+ finally:
47
+ db.close()
@@ -0,0 +1,86 @@
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
+ format_line_range,
14
+ SymbolKind,
15
+ )
16
+
17
+
18
+ def _member_source_patterns(member_symbol, short, kind):
19
+ """Build TS/JS and Python regex patterns for finding a member's source line."""
20
+ if "<constructor>" in member_symbol:
21
+ ts_pattern = r'^\s*constructor\s*\('
22
+ elif "<get>" in member_symbol:
23
+ ts_pattern = rf'^\s*(?:public\s+|private\s+|protected\s+|static\s+|readonly\s+)*get\s+{re.escape(short)}\s*\('
24
+ elif "<set>" in member_symbol:
25
+ ts_pattern = rf'^\s*(?:public\s+|private\s+|protected\s+|static\s+|readonly\s+)*set\s+{re.escape(short)}\s*\('
26
+ else:
27
+ ts_pattern = rf'^\s*(?:public\s+|private\s+|protected\s+|static\s+|readonly\s+)*{re.escape(short)}\s*\??\s*[:=(]'
28
+
29
+ py_pattern = None
30
+ if kind == SymbolKind.METHOD:
31
+ py_pattern = rf'^\s*(?:async\s+)?def\s+{re.escape(short)}\s*\('
32
+ elif kind == SymbolKind.PROPERTY:
33
+ py_pattern = rf'^\s*{re.escape(short)}\s*[=:]'
34
+ elif kind == SymbolKind.CLASS:
35
+ py_pattern = rf'^\s*class\s+{re.escape(short)}\s*[:\(]'
36
+
37
+ return ts_pattern, py_pattern
38
+
39
+
40
+ def main(args):
41
+ """List members of a class or interface."""
42
+ db, project_root = setup()
43
+ try:
44
+ symbol_id, _, _ = resolve_one_symbol(db, args.symbol)
45
+ members = get_members(db, symbol_id)
46
+
47
+ if not members:
48
+ print(f"No members found for '{args.symbol}'", file=sys.stderr)
49
+ sys.exit(1)
50
+
51
+ parent_def = get_def_location(db, symbol_id)
52
+ parent_file = parent_def[0] if parent_def else None
53
+ parent_start = parent_def[1] if parent_def else None
54
+ parent_end = parent_def[2] if parent_def else None
55
+
56
+ needs_lookup = any(m[3] is None for m in members)
57
+ source_lines = None
58
+ if needs_lookup and project_root and parent_file and parent_start is not None:
59
+ source_lines = read_source_lines(project_root, parent_file, parent_start, parent_end)
60
+
61
+ for member_id, member_symbol, member_name, start_line, end_line in members:
62
+ kind = infer_kind(member_symbol)
63
+ short = extract_leaf_name(member_symbol)
64
+
65
+ if start_line is None and source_lines:
66
+ ts_pattern, py_pattern = _member_source_patterns(member_symbol, short, kind)
67
+ patterns = []
68
+ if parent_file and parent_file.endswith('.py'):
69
+ if py_pattern:
70
+ patterns.append(py_pattern)
71
+ patterns.append(ts_pattern)
72
+ else:
73
+ patterns.append(ts_pattern)
74
+ if py_pattern:
75
+ patterns.append(py_pattern)
76
+
77
+ for i, line in enumerate(source_lines):
78
+ if any(re.match(p, line) for p in patterns):
79
+ start_line = parent_start + i
80
+ end_line = start_line
81
+ break
82
+
83
+ line_info = format_line_range(start_line, end_line)
84
+ print(f"{line_info} {kind} {short}")
85
+ finally:
86
+ db.close()
@@ -0,0 +1,47 @@
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
+ limit = args.limit
17
+ file_path = resolve_one_file(db, args.file)
18
+
19
+ symbols = get_file_symbols(db, file_path)
20
+ if not symbols:
21
+ print(f"No symbols found in '{file_path}'", file=sys.stderr)
22
+ sys.exit(1)
23
+
24
+ symbol_ids = [s[0] for s in symbols]
25
+ refs = get_refs_for_symbols(db, symbol_ids)
26
+
27
+ rdeps = set()
28
+ for symbol_id, ref_list in refs.items():
29
+ for ref_path, ref_line in ref_list:
30
+ if ref_path != file_path:
31
+ rdeps.add(ref_path)
32
+
33
+ if not rdeps:
34
+ print(f"No reverse dependencies found for '{file_path}'", file=sys.stderr)
35
+ sys.exit(1)
36
+
37
+ sorted_rdeps = sorted(rdeps)
38
+ hit_limit = len(sorted_rdeps) > limit
39
+ sorted_rdeps = sorted_rdeps[:limit]
40
+
41
+ for dep_path in sorted_rdeps:
42
+ print(dep_path)
43
+
44
+ if hit_limit:
45
+ print(f"# Warning: more than {limit} reverse dependencies, showing first {limit}", file=sys.stderr)
46
+ finally:
47
+ db.close()