codespine 0.7.0__tar.gz → 0.7.2__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.
- {codespine-0.7.0 → codespine-0.7.2}/PKG-INFO +125 -36
- {codespine-0.7.0 → codespine-0.7.2}/README.md +125 -36
- {codespine-0.7.0 → codespine-0.7.2}/codespine/__init__.py +1 -1
- {codespine-0.7.0 → codespine-0.7.2}/codespine/analysis/coupling.py +8 -8
- {codespine-0.7.0 → codespine-0.7.2}/codespine/cli.py +5 -5
- {codespine-0.7.0 → codespine-0.7.2}/codespine/config.py +1 -1
- {codespine-0.7.0 → codespine-0.7.2}/codespine/db/store.py +3 -3
- {codespine-0.7.0 → codespine-0.7.2}/codespine/guide.py +1 -1
- {codespine-0.7.0 → codespine-0.7.2}/codespine/mcp/server.py +3 -3
- {codespine-0.7.0 → codespine-0.7.2}/codespine.egg-info/PKG-INFO +125 -36
- {codespine-0.7.0 → codespine-0.7.2}/pyproject.toml +1 -1
- {codespine-0.7.0 → codespine-0.7.2}/LICENSE +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/codespine/analysis/__init__.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/codespine/analysis/community.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/codespine/analysis/context.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/codespine/analysis/crossmodule.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/codespine/analysis/deadcode.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/codespine/analysis/flow.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/codespine/analysis/impact.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/codespine/db/__init__.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/codespine/db/schema.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/codespine/diff/__init__.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/codespine/diff/branch_diff.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/codespine/indexer/__init__.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/codespine/indexer/call_resolver.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/codespine/indexer/engine.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/codespine/indexer/java_parser.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/codespine/indexer/symbol_builder.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/codespine/mcp/__init__.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/codespine/noise/__init__.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/codespine/noise/blocklist.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/codespine/overlay/__init__.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/codespine/overlay/git_state.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/codespine/overlay/merge.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/codespine/overlay/store.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/codespine/search/__init__.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/codespine/search/bm25.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/codespine/search/fuzzy.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/codespine/search/hybrid.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/codespine/search/rrf.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/codespine/search/vector.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/codespine/watch/__init__.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/codespine/watch/watcher.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/codespine.egg-info/SOURCES.txt +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/codespine.egg-info/dependency_links.txt +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/codespine.egg-info/entry_points.txt +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/codespine.egg-info/requires.txt +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/codespine.egg-info/top_level.txt +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/gindex.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/setup.cfg +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/tests/test_branch_diff_normalize.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/tests/test_call_resolver.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/tests/test_community_detection.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/tests/test_deadcode.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/tests/test_index_and_hybrid.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/tests/test_java_parser.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/tests/test_multimodule_index.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/tests/test_overlay.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/tests/test_search_ranking.py +0 -0
- {codespine-0.7.0 → codespine-0.7.2}/tests/test_store_recovery.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codespine
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.2
|
|
4
4
|
Summary: Local Java code intelligence indexer backed by a graph database
|
|
5
5
|
Author: CodeSpine contributors
|
|
6
6
|
License: MIT License
|
|
@@ -223,45 +223,134 @@ If the client launches the wrong Python environment, use the absolute binary pat
|
|
|
223
223
|
}
|
|
224
224
|
```
|
|
225
225
|
|
|
226
|
-
|
|
226
|
+
### Agent Onboarding
|
|
227
227
|
|
|
228
|
-
|
|
229
|
-
- `find_symbol(name, kind, project, limit)`
|
|
230
|
-
- `get_symbol_context(query, max_depth, project)`
|
|
231
|
-
- `get_impact(symbol, max_depth, project)`
|
|
232
|
-
- `detect_dead_code(limit, project, strict)`
|
|
233
|
-
- `trace_execution_flows(entry_symbol, max_depth, project)`
|
|
234
|
-
- `get_symbol_community(symbol)`
|
|
235
|
-
- `get_change_coupling(months, min_strength, min_cochanges, project)`
|
|
236
|
-
- `compare_branches(base_ref, head_ref)`
|
|
237
|
-
- `get_codebase_stats()`
|
|
228
|
+
When an agent connects to CodeSpine for the first time, it should call:
|
|
238
229
|
|
|
239
|
-
|
|
230
|
+
1. **`guide()`** — returns a structured catalog of every tool, organized by category, with recommended workflows and tips.
|
|
231
|
+
2. **`get_capabilities()`** — returns what is indexed right now, which features are ready, and what's missing.
|
|
240
232
|
|
|
241
|
-
|
|
233
|
+
The same information is available from the CLI:
|
|
242
234
|
|
|
243
235
|
```bash
|
|
244
|
-
codespine
|
|
245
|
-
codespine
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
236
|
+
codespine guide # tool catalog, workflows, tips
|
|
237
|
+
codespine guide --json # structured JSON for tooling
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### MCP Tools
|
|
241
|
+
|
|
242
|
+
**Discovery & Status**
|
|
243
|
+
|
|
244
|
+
| Tool | Description |
|
|
245
|
+
|------|-------------|
|
|
246
|
+
| `guide()` | Tool catalog, workflows, and tips. Call first if new to CodeSpine. |
|
|
247
|
+
| `get_capabilities()` | What is indexed and which features are available right now. |
|
|
248
|
+
| `list_projects()` | All indexed projects with symbol/file counts. |
|
|
249
|
+
| `get_codebase_stats()` | Per-project stats: files, classes, methods, call edges, embeddings. |
|
|
250
|
+
| `list_packages(project)` | Java packages in the index. |
|
|
251
|
+
| `ping()` | Verify the MCP server is alive. |
|
|
252
|
+
|
|
253
|
+
**Search & Lookup**
|
|
254
|
+
|
|
255
|
+
| Tool | Description |
|
|
256
|
+
|------|-------------|
|
|
257
|
+
| `search_hybrid(query, k, project)` | Ranked symbol search (BM25 + vector + fuzzy via RRF). |
|
|
258
|
+
| `find_symbol(name, kind, project, limit)` | Exact/prefix name lookup across all projects. |
|
|
259
|
+
| `get_symbol_context(query, max_depth, project)` | One-shot deep context: search + impact + community + flows. |
|
|
260
|
+
| `get_neighborhood(symbol, project)` | Callers, callees, siblings, and override/implements. |
|
|
261
|
+
|
|
262
|
+
**Analysis**
|
|
263
|
+
|
|
264
|
+
| Tool | Description |
|
|
265
|
+
|------|-------------|
|
|
266
|
+
| `get_impact(symbol, max_depth, project)` | Caller-tree impact analysis with confidence scores. |
|
|
267
|
+
| `detect_dead_code(limit, project, strict)` | Methods with no callers (Java-aware exemptions). |
|
|
268
|
+
| `trace_execution_flows(entry_symbol, max_depth, project)` | Execution paths from entry points. |
|
|
269
|
+
| `get_symbol_community(symbol)` | Architectural community cluster for a symbol. |
|
|
270
|
+
| `get_change_coupling(months, min_strength, min_cochanges)` | Files that historically change together. |
|
|
271
|
+
|
|
272
|
+
**Git**
|
|
273
|
+
|
|
274
|
+
| Tool | Description |
|
|
275
|
+
|------|-------------|
|
|
276
|
+
| `git_log(file_path, limit, project)` | Recent git commits. |
|
|
277
|
+
| `git_diff(ref, file_path, project)` | Git diff (working tree vs ref, or between refs). |
|
|
278
|
+
| `compare_branches(base_ref, head_ref, project)` | Symbol-level diff between two git refs. |
|
|
279
|
+
|
|
280
|
+
**Indexing & Watch**
|
|
281
|
+
|
|
282
|
+
| Tool | Description |
|
|
283
|
+
|------|-------------|
|
|
284
|
+
| `analyse_project(path, full, deep, embed)` | Index a Java project (background job). |
|
|
285
|
+
| `get_analyse_status()` | Poll analysis progress. |
|
|
286
|
+
| `reindex_file(file_path, project)` | Re-index a single `.java` file (<1 s). |
|
|
287
|
+
| `start_watch(path)` | Watch for `.java` changes and update overlay in real time. |
|
|
288
|
+
| `stop_watch()` | Stop the background watch process. |
|
|
289
|
+
| `get_watch_status()` | Watch mode status: running, path, uptime. |
|
|
290
|
+
|
|
291
|
+
**Overlay**
|
|
292
|
+
|
|
293
|
+
| Tool | Description |
|
|
294
|
+
|------|-------------|
|
|
295
|
+
| `get_overlay_status(project)` | Uncommitted overlay state by project/module. |
|
|
296
|
+
| `promote_overlay(project)` | Commit dirty overlay into the base index. |
|
|
297
|
+
| `clear_overlay(project)` | Discard dirty overlay without changing the base. |
|
|
298
|
+
|
|
299
|
+
**Reset**
|
|
300
|
+
|
|
301
|
+
| Tool | Description |
|
|
302
|
+
|------|-------------|
|
|
303
|
+
| `reset_project(project_id)` | Remove all data for one project. |
|
|
304
|
+
| `reset_index()` | Remove ALL data across every project. |
|
|
305
|
+
| `force_reset_index()` | Emergency: delete data files when normal reset fails. |
|
|
306
|
+
|
|
307
|
+
**Advanced**
|
|
308
|
+
|
|
309
|
+
| Tool | Description |
|
|
310
|
+
|------|-------------|
|
|
311
|
+
| `run_cypher(query)` | Run a raw Cypher query against the graph DB. |
|
|
312
|
+
|
|
313
|
+
## CLI
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
# Indexing
|
|
317
|
+
codespine analyse <path> # incremental index
|
|
318
|
+
codespine analyse <path> --full # full re-index
|
|
319
|
+
codespine analyse <path> --deep # + communities, flows, dead code, coupling
|
|
320
|
+
codespine analyse <path> --embed # + vector embeddings
|
|
321
|
+
codespine watch --path . # live re-index on file changes
|
|
322
|
+
|
|
323
|
+
# Search & Analysis
|
|
324
|
+
codespine search "query" # hybrid search
|
|
325
|
+
codespine context "symbol" # one-shot deep context
|
|
326
|
+
codespine impact "symbol" # caller-tree impact
|
|
327
|
+
codespine deadcode # dead code candidates
|
|
328
|
+
codespine flow # execution flows
|
|
329
|
+
codespine community # architectural clusters
|
|
330
|
+
codespine coupling # git change coupling
|
|
331
|
+
codespine diff main..feature # symbol-level branch diff
|
|
332
|
+
|
|
333
|
+
# Status & Info
|
|
334
|
+
codespine stats # per-project statistics
|
|
335
|
+
codespine list # indexed projects
|
|
336
|
+
codespine status # service and database status
|
|
337
|
+
codespine guide # tool catalog and workflows
|
|
338
|
+
|
|
339
|
+
# Overlay
|
|
340
|
+
codespine overlay-status # dirty overlay state
|
|
341
|
+
codespine overlay-promote # commit overlay to base
|
|
342
|
+
codespine overlay-clear # discard overlay
|
|
343
|
+
|
|
344
|
+
# Server Management
|
|
345
|
+
codespine start # launch background MCP server
|
|
346
|
+
codespine stop # stop background MCP server
|
|
347
|
+
codespine mcp # foreground MCP (stdio, for IDE)
|
|
348
|
+
|
|
349
|
+
# Cleanup & Reset
|
|
350
|
+
codespine clear-project <project_id> # remove one project
|
|
351
|
+
codespine clear-index # remove all indexed data
|
|
352
|
+
codespine force-reset # emergency: delete all data files
|
|
353
|
+
codespine setup # check dependencies
|
|
265
354
|
```
|
|
266
355
|
|
|
267
356
|
`analyse` defaults to incremental mode. Repeat runs are designed to be fast when files have not changed.
|
|
@@ -323,8 +412,8 @@ Running `codespine analyse --deep --embed` on one project while querying a diffe
|
|
|
323
412
|
- `codespine start` launches a background MCP server. Most IDE MCP clients should use `codespine mcp` instead and manage the process themselves.
|
|
324
413
|
- `codespine watch` updates the dirty overlay first; it does not rewrite the committed base index on every save.
|
|
325
414
|
- `codespine clear-index` rebuilds the local index database from scratch. This also removes the read replica; run `analyse` again to republish it.
|
|
415
|
+
- `codespine force-reset` is the nuclear option — it deletes all data files without going through the DB engine. Use it when `clear-index` fails due to DB corruption.
|
|
326
416
|
- For large Spring or JPA-heavy repos, dead-code results should still be reviewed before deletion. The tool is conservative, not authoritative.
|
|
327
|
-
- The first run after upgrading to v0.5.7 will not have a read replica yet. Run `codespine analyse` once to create it.
|
|
328
417
|
|
|
329
418
|
## Project Docs
|
|
330
419
|
|
|
@@ -159,45 +159,134 @@ If the client launches the wrong Python environment, use the absolute binary pat
|
|
|
159
159
|
}
|
|
160
160
|
```
|
|
161
161
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
- `search_hybrid(query, k, project)`
|
|
165
|
-
- `find_symbol(name, kind, project, limit)`
|
|
166
|
-
- `get_symbol_context(query, max_depth, project)`
|
|
167
|
-
- `get_impact(symbol, max_depth, project)`
|
|
168
|
-
- `detect_dead_code(limit, project, strict)`
|
|
169
|
-
- `trace_execution_flows(entry_symbol, max_depth, project)`
|
|
170
|
-
- `get_symbol_community(symbol)`
|
|
171
|
-
- `get_change_coupling(months, min_strength, min_cochanges, project)`
|
|
172
|
-
- `compare_branches(base_ref, head_ref)`
|
|
173
|
-
- `get_codebase_stats()`
|
|
162
|
+
### Agent Onboarding
|
|
174
163
|
|
|
175
|
-
|
|
164
|
+
When an agent connects to CodeSpine for the first time, it should call:
|
|
165
|
+
|
|
166
|
+
1. **`guide()`** — returns a structured catalog of every tool, organized by category, with recommended workflows and tips.
|
|
167
|
+
2. **`get_capabilities()`** — returns what is indexed right now, which features are ready, and what's missing.
|
|
176
168
|
|
|
177
|
-
|
|
169
|
+
The same information is available from the CLI:
|
|
178
170
|
|
|
179
171
|
```bash
|
|
180
|
-
codespine
|
|
181
|
-
codespine
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
172
|
+
codespine guide # tool catalog, workflows, tips
|
|
173
|
+
codespine guide --json # structured JSON for tooling
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### MCP Tools
|
|
177
|
+
|
|
178
|
+
**Discovery & Status**
|
|
179
|
+
|
|
180
|
+
| Tool | Description |
|
|
181
|
+
|------|-------------|
|
|
182
|
+
| `guide()` | Tool catalog, workflows, and tips. Call first if new to CodeSpine. |
|
|
183
|
+
| `get_capabilities()` | What is indexed and which features are available right now. |
|
|
184
|
+
| `list_projects()` | All indexed projects with symbol/file counts. |
|
|
185
|
+
| `get_codebase_stats()` | Per-project stats: files, classes, methods, call edges, embeddings. |
|
|
186
|
+
| `list_packages(project)` | Java packages in the index. |
|
|
187
|
+
| `ping()` | Verify the MCP server is alive. |
|
|
188
|
+
|
|
189
|
+
**Search & Lookup**
|
|
190
|
+
|
|
191
|
+
| Tool | Description |
|
|
192
|
+
|------|-------------|
|
|
193
|
+
| `search_hybrid(query, k, project)` | Ranked symbol search (BM25 + vector + fuzzy via RRF). |
|
|
194
|
+
| `find_symbol(name, kind, project, limit)` | Exact/prefix name lookup across all projects. |
|
|
195
|
+
| `get_symbol_context(query, max_depth, project)` | One-shot deep context: search + impact + community + flows. |
|
|
196
|
+
| `get_neighborhood(symbol, project)` | Callers, callees, siblings, and override/implements. |
|
|
197
|
+
|
|
198
|
+
**Analysis**
|
|
199
|
+
|
|
200
|
+
| Tool | Description |
|
|
201
|
+
|------|-------------|
|
|
202
|
+
| `get_impact(symbol, max_depth, project)` | Caller-tree impact analysis with confidence scores. |
|
|
203
|
+
| `detect_dead_code(limit, project, strict)` | Methods with no callers (Java-aware exemptions). |
|
|
204
|
+
| `trace_execution_flows(entry_symbol, max_depth, project)` | Execution paths from entry points. |
|
|
205
|
+
| `get_symbol_community(symbol)` | Architectural community cluster for a symbol. |
|
|
206
|
+
| `get_change_coupling(months, min_strength, min_cochanges)` | Files that historically change together. |
|
|
207
|
+
|
|
208
|
+
**Git**
|
|
209
|
+
|
|
210
|
+
| Tool | Description |
|
|
211
|
+
|------|-------------|
|
|
212
|
+
| `git_log(file_path, limit, project)` | Recent git commits. |
|
|
213
|
+
| `git_diff(ref, file_path, project)` | Git diff (working tree vs ref, or between refs). |
|
|
214
|
+
| `compare_branches(base_ref, head_ref, project)` | Symbol-level diff between two git refs. |
|
|
215
|
+
|
|
216
|
+
**Indexing & Watch**
|
|
217
|
+
|
|
218
|
+
| Tool | Description |
|
|
219
|
+
|------|-------------|
|
|
220
|
+
| `analyse_project(path, full, deep, embed)` | Index a Java project (background job). |
|
|
221
|
+
| `get_analyse_status()` | Poll analysis progress. |
|
|
222
|
+
| `reindex_file(file_path, project)` | Re-index a single `.java` file (<1 s). |
|
|
223
|
+
| `start_watch(path)` | Watch for `.java` changes and update overlay in real time. |
|
|
224
|
+
| `stop_watch()` | Stop the background watch process. |
|
|
225
|
+
| `get_watch_status()` | Watch mode status: running, path, uptime. |
|
|
226
|
+
|
|
227
|
+
**Overlay**
|
|
228
|
+
|
|
229
|
+
| Tool | Description |
|
|
230
|
+
|------|-------------|
|
|
231
|
+
| `get_overlay_status(project)` | Uncommitted overlay state by project/module. |
|
|
232
|
+
| `promote_overlay(project)` | Commit dirty overlay into the base index. |
|
|
233
|
+
| `clear_overlay(project)` | Discard dirty overlay without changing the base. |
|
|
234
|
+
|
|
235
|
+
**Reset**
|
|
236
|
+
|
|
237
|
+
| Tool | Description |
|
|
238
|
+
|------|-------------|
|
|
239
|
+
| `reset_project(project_id)` | Remove all data for one project. |
|
|
240
|
+
| `reset_index()` | Remove ALL data across every project. |
|
|
241
|
+
| `force_reset_index()` | Emergency: delete data files when normal reset fails. |
|
|
242
|
+
|
|
243
|
+
**Advanced**
|
|
244
|
+
|
|
245
|
+
| Tool | Description |
|
|
246
|
+
|------|-------------|
|
|
247
|
+
| `run_cypher(query)` | Run a raw Cypher query against the graph DB. |
|
|
248
|
+
|
|
249
|
+
## CLI
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
# Indexing
|
|
253
|
+
codespine analyse <path> # incremental index
|
|
254
|
+
codespine analyse <path> --full # full re-index
|
|
255
|
+
codespine analyse <path> --deep # + communities, flows, dead code, coupling
|
|
256
|
+
codespine analyse <path> --embed # + vector embeddings
|
|
257
|
+
codespine watch --path . # live re-index on file changes
|
|
258
|
+
|
|
259
|
+
# Search & Analysis
|
|
260
|
+
codespine search "query" # hybrid search
|
|
261
|
+
codespine context "symbol" # one-shot deep context
|
|
262
|
+
codespine impact "symbol" # caller-tree impact
|
|
263
|
+
codespine deadcode # dead code candidates
|
|
264
|
+
codespine flow # execution flows
|
|
265
|
+
codespine community # architectural clusters
|
|
266
|
+
codespine coupling # git change coupling
|
|
267
|
+
codespine diff main..feature # symbol-level branch diff
|
|
268
|
+
|
|
269
|
+
# Status & Info
|
|
270
|
+
codespine stats # per-project statistics
|
|
271
|
+
codespine list # indexed projects
|
|
272
|
+
codespine status # service and database status
|
|
273
|
+
codespine guide # tool catalog and workflows
|
|
274
|
+
|
|
275
|
+
# Overlay
|
|
276
|
+
codespine overlay-status # dirty overlay state
|
|
277
|
+
codespine overlay-promote # commit overlay to base
|
|
278
|
+
codespine overlay-clear # discard overlay
|
|
279
|
+
|
|
280
|
+
# Server Management
|
|
281
|
+
codespine start # launch background MCP server
|
|
282
|
+
codespine stop # stop background MCP server
|
|
283
|
+
codespine mcp # foreground MCP (stdio, for IDE)
|
|
284
|
+
|
|
285
|
+
# Cleanup & Reset
|
|
286
|
+
codespine clear-project <project_id> # remove one project
|
|
287
|
+
codespine clear-index # remove all indexed data
|
|
288
|
+
codespine force-reset # emergency: delete all data files
|
|
289
|
+
codespine setup # check dependencies
|
|
201
290
|
```
|
|
202
291
|
|
|
203
292
|
`analyse` defaults to incremental mode. Repeat runs are designed to be fast when files have not changed.
|
|
@@ -259,8 +348,8 @@ Running `codespine analyse --deep --embed` on one project while querying a diffe
|
|
|
259
348
|
- `codespine start` launches a background MCP server. Most IDE MCP clients should use `codespine mcp` instead and manage the process themselves.
|
|
260
349
|
- `codespine watch` updates the dirty overlay first; it does not rewrite the committed base index on every save.
|
|
261
350
|
- `codespine clear-index` rebuilds the local index database from scratch. This also removes the read replica; run `analyse` again to republish it.
|
|
351
|
+
- `codespine force-reset` is the nuclear option — it deletes all data files without going through the DB engine. Use it when `clear-index` fails due to DB corruption.
|
|
262
352
|
- For large Spring or JPA-heavy repos, dead-code results should still be reviewed before deletion. The tool is conservative, not authoritative.
|
|
263
|
-
- The first run after upgrading to v0.5.7 will not have a read replica yet. Run `codespine analyse` once to create it.
|
|
264
353
|
|
|
265
354
|
## Project Docs
|
|
266
355
|
|
|
@@ -9,7 +9,7 @@ from codespine.config import SETTINGS
|
|
|
9
9
|
from codespine.indexer.symbol_builder import file_id
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
def _git_changed_file_sets(repo_path: str,
|
|
12
|
+
def _git_changed_file_sets(repo_path: str, days: int) -> list[set[str]]:
|
|
13
13
|
cmd = [
|
|
14
14
|
"git",
|
|
15
15
|
"-C",
|
|
@@ -17,7 +17,7 @@ def _git_changed_file_sets(repo_path: str, months: int) -> list[set[str]]:
|
|
|
17
17
|
"log",
|
|
18
18
|
"--name-only",
|
|
19
19
|
"--pretty=format:__COMMIT__",
|
|
20
|
-
f"--since={
|
|
20
|
+
f"--since={days}.days",
|
|
21
21
|
]
|
|
22
22
|
proc = subprocess.run(cmd, capture_output=True, text=True, check=False)
|
|
23
23
|
if proc.returncode != 0:
|
|
@@ -43,7 +43,7 @@ def compute_coupling(
|
|
|
43
43
|
store,
|
|
44
44
|
repo_path: str,
|
|
45
45
|
project_id: str,
|
|
46
|
-
|
|
46
|
+
days: int = SETTINGS.default_coupling_days,
|
|
47
47
|
min_strength: float = SETTINGS.default_min_coupling_strength,
|
|
48
48
|
min_cochanges: int = SETTINGS.default_min_cochanges,
|
|
49
49
|
progress=None,
|
|
@@ -53,7 +53,7 @@ def compute_coupling(
|
|
|
53
53
|
progress(msg)
|
|
54
54
|
|
|
55
55
|
_ping("reading git history")
|
|
56
|
-
changesets = _git_changed_file_sets(repo_path,
|
|
56
|
+
changesets = _git_changed_file_sets(repo_path, days)
|
|
57
57
|
if not changesets:
|
|
58
58
|
return []
|
|
59
59
|
|
|
@@ -77,7 +77,7 @@ def compute_coupling(
|
|
|
77
77
|
|
|
78
78
|
aid = file_id(project_id, a)
|
|
79
79
|
bid = file_id(project_id, b)
|
|
80
|
-
store.upsert_coupling(aid, bid, strength, pair_count,
|
|
80
|
+
store.upsert_coupling(aid, bid, strength, pair_count, days)
|
|
81
81
|
results.append(
|
|
82
82
|
{
|
|
83
83
|
"file_a": a,
|
|
@@ -91,7 +91,7 @@ def compute_coupling(
|
|
|
91
91
|
return results
|
|
92
92
|
|
|
93
93
|
|
|
94
|
-
def get_coupling(store, symbol: str | None = None,
|
|
94
|
+
def get_coupling(store, symbol: str | None = None, days: int = 5, min_strength: float = 0.3, min_cochanges: int = 3) -> dict:
|
|
95
95
|
if symbol:
|
|
96
96
|
recs = store.query_records(
|
|
97
97
|
"""
|
|
@@ -113,13 +113,13 @@ def get_coupling(store, symbol: str | None = None, months: int = 6, min_strength
|
|
|
113
113
|
recs = store.query_records(
|
|
114
114
|
"""
|
|
115
115
|
MATCH (f:File)-[r:CO_CHANGED_WITH]-(f2:File)
|
|
116
|
-
WHERE r.
|
|
116
|
+
WHERE r.days = $days AND r.strength >= $min_strength AND r.cochanges >= $min_cochanges
|
|
117
117
|
RETURN f.path as file, f2.path as coupled_file, r.strength as strength, r.cochanges as cochanges
|
|
118
118
|
ORDER BY strength DESC, cochanges DESC
|
|
119
119
|
LIMIT 500
|
|
120
120
|
""",
|
|
121
121
|
{
|
|
122
|
-
"
|
|
122
|
+
"days": days,
|
|
123
123
|
"min_strength": min_strength,
|
|
124
124
|
"min_cochanges": min_cochanges,
|
|
125
125
|
},
|
|
@@ -314,7 +314,7 @@ def analyse(path: str, full: bool, deep: bool, embed: bool, allow_running: bool)
|
|
|
314
314
|
store,
|
|
315
315
|
coupling_root,
|
|
316
316
|
coupling_project,
|
|
317
|
-
|
|
317
|
+
days=SETTINGS.default_coupling_days,
|
|
318
318
|
min_strength=SETTINGS.default_min_coupling_strength,
|
|
319
319
|
min_cochanges=SETTINGS.default_min_cochanges,
|
|
320
320
|
progress=lambda s: _live_phase(coup_label, s),
|
|
@@ -467,20 +467,20 @@ def community(symbol: str | None, as_json: bool) -> None:
|
|
|
467
467
|
|
|
468
468
|
|
|
469
469
|
@main.command()
|
|
470
|
-
@click.option("--
|
|
470
|
+
@click.option("--days", default=5, show_default=True, type=int)
|
|
471
471
|
@click.option("--min-strength", default=0.3, show_default=True, type=float)
|
|
472
472
|
@click.option("--min-cochanges", default=3, show_default=True, type=int)
|
|
473
473
|
@click.option("--json", "as_json", is_flag=True)
|
|
474
|
-
def coupling(
|
|
474
|
+
def coupling(days: int, min_strength: float, min_cochanges: int, as_json: bool) -> None:
|
|
475
475
|
"""Compute and query git change coupling."""
|
|
476
476
|
store = GraphStore(read_only=False)
|
|
477
477
|
project = store.query_records("MATCH (p:Project) RETURN p.id as id LIMIT 1")
|
|
478
478
|
project_id = project[0]["id"] if project else os.path.basename(os.getcwd())
|
|
479
|
-
compute_coupling(store, os.getcwd(), project_id,
|
|
479
|
+
compute_coupling(store, os.getcwd(), project_id, days=days, min_strength=min_strength, min_cochanges=min_cochanges)
|
|
480
480
|
result = get_coupling(
|
|
481
481
|
store,
|
|
482
482
|
symbol=None,
|
|
483
|
-
|
|
483
|
+
days=days,
|
|
484
484
|
min_strength=min_strength,
|
|
485
485
|
min_cochanges=min_cochanges,
|
|
486
486
|
)
|
|
@@ -18,7 +18,7 @@ class Settings:
|
|
|
18
18
|
write_batch_size: int = 500
|
|
19
19
|
index_file_batch_size: int = 20
|
|
20
20
|
edge_write_batch_size: int = 500
|
|
21
|
-
|
|
21
|
+
default_coupling_days: int = 5
|
|
22
22
|
default_min_coupling_strength: float = 0.3
|
|
23
23
|
default_min_cochanges: int = 3
|
|
24
24
|
default_global_interval_s: int = 30
|
|
@@ -648,18 +648,18 @@ class GraphStore:
|
|
|
648
648
|
)
|
|
649
649
|
self._recycle_conn()
|
|
650
650
|
|
|
651
|
-
def upsert_coupling(self, file_a: str, file_b: str, strength: float, cochanges: int,
|
|
651
|
+
def upsert_coupling(self, file_a: str, file_b: str, strength: float, cochanges: int, days: int) -> None:
|
|
652
652
|
self.execute(
|
|
653
653
|
"""
|
|
654
654
|
MATCH (a:File {id: $a}), (b:File {id: $b})
|
|
655
|
-
MERGE (a)-[:CO_CHANGED_WITH {strength: $strength, cochanges: $cochanges,
|
|
655
|
+
MERGE (a)-[:CO_CHANGED_WITH {strength: $strength, cochanges: $cochanges, days: $days}]->(b)
|
|
656
656
|
""",
|
|
657
657
|
{
|
|
658
658
|
"a": file_a,
|
|
659
659
|
"b": file_b,
|
|
660
660
|
"strength": strength,
|
|
661
661
|
"cochanges": int(cochanges),
|
|
662
|
-
"
|
|
662
|
+
"days": int(days),
|
|
663
663
|
},
|
|
664
664
|
)
|
|
665
665
|
|
|
@@ -60,7 +60,7 @@ GUIDE_SECTIONS: list[dict] = [
|
|
|
60
60
|
{"name": "detect_dead_code", "one_liner": "Methods with no callers (Java-aware exemptions). strict=True for thorough audit."},
|
|
61
61
|
{"name": "trace_execution_flows", "one_liner": "Execution paths from entry points (main methods, tests, controllers)."},
|
|
62
62
|
{"name": "get_symbol_community", "one_liner": "Architectural community cluster a symbol belongs to."},
|
|
63
|
-
{"name": "get_change_coupling", "one_liner": "Files that
|
|
63
|
+
{"name": "get_change_coupling", "one_liner": "Files that changed together in the last N days (default 5). git co-change analysis."},
|
|
64
64
|
],
|
|
65
65
|
},
|
|
66
66
|
{
|
|
@@ -522,15 +522,15 @@ def build_mcp_server(store, repo_path_provider):
|
|
|
522
522
|
@mcp.tool()
|
|
523
523
|
def get_change_coupling(
|
|
524
524
|
symbol: str | None = None,
|
|
525
|
-
|
|
525
|
+
days: int = 5,
|
|
526
526
|
min_strength: float = 0.3,
|
|
527
527
|
min_cochanges: int = 3,
|
|
528
528
|
):
|
|
529
529
|
"""
|
|
530
|
-
Files that
|
|
530
|
+
Files that changed together in the last N days (git co-change coupling).
|
|
531
531
|
Requires 'codespine analyse --deep' to have been run.
|
|
532
532
|
"""
|
|
533
|
-
result = get_coupling(store, symbol=symbol,
|
|
533
|
+
result = get_coupling(store, symbol=symbol, days=days, min_strength=min_strength, min_cochanges=min_cochanges)
|
|
534
534
|
if not result:
|
|
535
535
|
return {
|
|
536
536
|
"available": False,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codespine
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.2
|
|
4
4
|
Summary: Local Java code intelligence indexer backed by a graph database
|
|
5
5
|
Author: CodeSpine contributors
|
|
6
6
|
License: MIT License
|
|
@@ -223,45 +223,134 @@ If the client launches the wrong Python environment, use the absolute binary pat
|
|
|
223
223
|
}
|
|
224
224
|
```
|
|
225
225
|
|
|
226
|
-
|
|
226
|
+
### Agent Onboarding
|
|
227
227
|
|
|
228
|
-
|
|
229
|
-
- `find_symbol(name, kind, project, limit)`
|
|
230
|
-
- `get_symbol_context(query, max_depth, project)`
|
|
231
|
-
- `get_impact(symbol, max_depth, project)`
|
|
232
|
-
- `detect_dead_code(limit, project, strict)`
|
|
233
|
-
- `trace_execution_flows(entry_symbol, max_depth, project)`
|
|
234
|
-
- `get_symbol_community(symbol)`
|
|
235
|
-
- `get_change_coupling(months, min_strength, min_cochanges, project)`
|
|
236
|
-
- `compare_branches(base_ref, head_ref)`
|
|
237
|
-
- `get_codebase_stats()`
|
|
228
|
+
When an agent connects to CodeSpine for the first time, it should call:
|
|
238
229
|
|
|
239
|
-
|
|
230
|
+
1. **`guide()`** — returns a structured catalog of every tool, organized by category, with recommended workflows and tips.
|
|
231
|
+
2. **`get_capabilities()`** — returns what is indexed right now, which features are ready, and what's missing.
|
|
240
232
|
|
|
241
|
-
|
|
233
|
+
The same information is available from the CLI:
|
|
242
234
|
|
|
243
235
|
```bash
|
|
244
|
-
codespine
|
|
245
|
-
codespine
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
236
|
+
codespine guide # tool catalog, workflows, tips
|
|
237
|
+
codespine guide --json # structured JSON for tooling
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### MCP Tools
|
|
241
|
+
|
|
242
|
+
**Discovery & Status**
|
|
243
|
+
|
|
244
|
+
| Tool | Description |
|
|
245
|
+
|------|-------------|
|
|
246
|
+
| `guide()` | Tool catalog, workflows, and tips. Call first if new to CodeSpine. |
|
|
247
|
+
| `get_capabilities()` | What is indexed and which features are available right now. |
|
|
248
|
+
| `list_projects()` | All indexed projects with symbol/file counts. |
|
|
249
|
+
| `get_codebase_stats()` | Per-project stats: files, classes, methods, call edges, embeddings. |
|
|
250
|
+
| `list_packages(project)` | Java packages in the index. |
|
|
251
|
+
| `ping()` | Verify the MCP server is alive. |
|
|
252
|
+
|
|
253
|
+
**Search & Lookup**
|
|
254
|
+
|
|
255
|
+
| Tool | Description |
|
|
256
|
+
|------|-------------|
|
|
257
|
+
| `search_hybrid(query, k, project)` | Ranked symbol search (BM25 + vector + fuzzy via RRF). |
|
|
258
|
+
| `find_symbol(name, kind, project, limit)` | Exact/prefix name lookup across all projects. |
|
|
259
|
+
| `get_symbol_context(query, max_depth, project)` | One-shot deep context: search + impact + community + flows. |
|
|
260
|
+
| `get_neighborhood(symbol, project)` | Callers, callees, siblings, and override/implements. |
|
|
261
|
+
|
|
262
|
+
**Analysis**
|
|
263
|
+
|
|
264
|
+
| Tool | Description |
|
|
265
|
+
|------|-------------|
|
|
266
|
+
| `get_impact(symbol, max_depth, project)` | Caller-tree impact analysis with confidence scores. |
|
|
267
|
+
| `detect_dead_code(limit, project, strict)` | Methods with no callers (Java-aware exemptions). |
|
|
268
|
+
| `trace_execution_flows(entry_symbol, max_depth, project)` | Execution paths from entry points. |
|
|
269
|
+
| `get_symbol_community(symbol)` | Architectural community cluster for a symbol. |
|
|
270
|
+
| `get_change_coupling(months, min_strength, min_cochanges)` | Files that historically change together. |
|
|
271
|
+
|
|
272
|
+
**Git**
|
|
273
|
+
|
|
274
|
+
| Tool | Description |
|
|
275
|
+
|------|-------------|
|
|
276
|
+
| `git_log(file_path, limit, project)` | Recent git commits. |
|
|
277
|
+
| `git_diff(ref, file_path, project)` | Git diff (working tree vs ref, or between refs). |
|
|
278
|
+
| `compare_branches(base_ref, head_ref, project)` | Symbol-level diff between two git refs. |
|
|
279
|
+
|
|
280
|
+
**Indexing & Watch**
|
|
281
|
+
|
|
282
|
+
| Tool | Description |
|
|
283
|
+
|------|-------------|
|
|
284
|
+
| `analyse_project(path, full, deep, embed)` | Index a Java project (background job). |
|
|
285
|
+
| `get_analyse_status()` | Poll analysis progress. |
|
|
286
|
+
| `reindex_file(file_path, project)` | Re-index a single `.java` file (<1 s). |
|
|
287
|
+
| `start_watch(path)` | Watch for `.java` changes and update overlay in real time. |
|
|
288
|
+
| `stop_watch()` | Stop the background watch process. |
|
|
289
|
+
| `get_watch_status()` | Watch mode status: running, path, uptime. |
|
|
290
|
+
|
|
291
|
+
**Overlay**
|
|
292
|
+
|
|
293
|
+
| Tool | Description |
|
|
294
|
+
|------|-------------|
|
|
295
|
+
| `get_overlay_status(project)` | Uncommitted overlay state by project/module. |
|
|
296
|
+
| `promote_overlay(project)` | Commit dirty overlay into the base index. |
|
|
297
|
+
| `clear_overlay(project)` | Discard dirty overlay without changing the base. |
|
|
298
|
+
|
|
299
|
+
**Reset**
|
|
300
|
+
|
|
301
|
+
| Tool | Description |
|
|
302
|
+
|------|-------------|
|
|
303
|
+
| `reset_project(project_id)` | Remove all data for one project. |
|
|
304
|
+
| `reset_index()` | Remove ALL data across every project. |
|
|
305
|
+
| `force_reset_index()` | Emergency: delete data files when normal reset fails. |
|
|
306
|
+
|
|
307
|
+
**Advanced**
|
|
308
|
+
|
|
309
|
+
| Tool | Description |
|
|
310
|
+
|------|-------------|
|
|
311
|
+
| `run_cypher(query)` | Run a raw Cypher query against the graph DB. |
|
|
312
|
+
|
|
313
|
+
## CLI
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
# Indexing
|
|
317
|
+
codespine analyse <path> # incremental index
|
|
318
|
+
codespine analyse <path> --full # full re-index
|
|
319
|
+
codespine analyse <path> --deep # + communities, flows, dead code, coupling
|
|
320
|
+
codespine analyse <path> --embed # + vector embeddings
|
|
321
|
+
codespine watch --path . # live re-index on file changes
|
|
322
|
+
|
|
323
|
+
# Search & Analysis
|
|
324
|
+
codespine search "query" # hybrid search
|
|
325
|
+
codespine context "symbol" # one-shot deep context
|
|
326
|
+
codespine impact "symbol" # caller-tree impact
|
|
327
|
+
codespine deadcode # dead code candidates
|
|
328
|
+
codespine flow # execution flows
|
|
329
|
+
codespine community # architectural clusters
|
|
330
|
+
codespine coupling # git change coupling
|
|
331
|
+
codespine diff main..feature # symbol-level branch diff
|
|
332
|
+
|
|
333
|
+
# Status & Info
|
|
334
|
+
codespine stats # per-project statistics
|
|
335
|
+
codespine list # indexed projects
|
|
336
|
+
codespine status # service and database status
|
|
337
|
+
codespine guide # tool catalog and workflows
|
|
338
|
+
|
|
339
|
+
# Overlay
|
|
340
|
+
codespine overlay-status # dirty overlay state
|
|
341
|
+
codespine overlay-promote # commit overlay to base
|
|
342
|
+
codespine overlay-clear # discard overlay
|
|
343
|
+
|
|
344
|
+
# Server Management
|
|
345
|
+
codespine start # launch background MCP server
|
|
346
|
+
codespine stop # stop background MCP server
|
|
347
|
+
codespine mcp # foreground MCP (stdio, for IDE)
|
|
348
|
+
|
|
349
|
+
# Cleanup & Reset
|
|
350
|
+
codespine clear-project <project_id> # remove one project
|
|
351
|
+
codespine clear-index # remove all indexed data
|
|
352
|
+
codespine force-reset # emergency: delete all data files
|
|
353
|
+
codespine setup # check dependencies
|
|
265
354
|
```
|
|
266
355
|
|
|
267
356
|
`analyse` defaults to incremental mode. Repeat runs are designed to be fast when files have not changed.
|
|
@@ -323,8 +412,8 @@ Running `codespine analyse --deep --embed` on one project while querying a diffe
|
|
|
323
412
|
- `codespine start` launches a background MCP server. Most IDE MCP clients should use `codespine mcp` instead and manage the process themselves.
|
|
324
413
|
- `codespine watch` updates the dirty overlay first; it does not rewrite the committed base index on every save.
|
|
325
414
|
- `codespine clear-index` rebuilds the local index database from scratch. This also removes the read replica; run `analyse` again to republish it.
|
|
415
|
+
- `codespine force-reset` is the nuclear option — it deletes all data files without going through the DB engine. Use it when `clear-index` fails due to DB corruption.
|
|
326
416
|
- For large Spring or JPA-heavy repos, dead-code results should still be reviewed before deletion. The tool is conservative, not authoritative.
|
|
327
|
-
- The first run after upgrading to v0.5.7 will not have a read replica yet. Run `codespine analyse` once to create it.
|
|
328
417
|
|
|
329
418
|
## Project Docs
|
|
330
419
|
|
|
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
|