codespine 0.6.0__tar.gz → 0.6.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.6.0 → codespine-0.6.2}/PKG-INFO +1 -1
- {codespine-0.6.0 → codespine-0.6.2}/codespine/__init__.py +1 -1
- {codespine-0.6.0 → codespine-0.6.2}/codespine/cli.py +37 -10
- {codespine-0.6.0 → codespine-0.6.2}/codespine/db/store.py +46 -9
- {codespine-0.6.0 → codespine-0.6.2}/codespine/mcp/server.py +10 -2
- {codespine-0.6.0 → codespine-0.6.2}/codespine.egg-info/PKG-INFO +1 -1
- {codespine-0.6.0 → codespine-0.6.2}/pyproject.toml +1 -1
- {codespine-0.6.0 → codespine-0.6.2}/LICENSE +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/README.md +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/codespine/analysis/__init__.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/codespine/analysis/community.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/codespine/analysis/context.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/codespine/analysis/coupling.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/codespine/analysis/crossmodule.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/codespine/analysis/deadcode.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/codespine/analysis/flow.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/codespine/analysis/impact.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/codespine/config.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/codespine/db/__init__.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/codespine/db/schema.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/codespine/diff/__init__.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/codespine/diff/branch_diff.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/codespine/indexer/__init__.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/codespine/indexer/call_resolver.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/codespine/indexer/engine.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/codespine/indexer/java_parser.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/codespine/indexer/symbol_builder.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/codespine/mcp/__init__.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/codespine/noise/__init__.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/codespine/noise/blocklist.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/codespine/overlay/__init__.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/codespine/overlay/git_state.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/codespine/overlay/merge.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/codespine/overlay/store.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/codespine/search/__init__.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/codespine/search/bm25.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/codespine/search/fuzzy.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/codespine/search/hybrid.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/codespine/search/rrf.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/codespine/search/vector.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/codespine/watch/__init__.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/codespine/watch/watcher.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/codespine.egg-info/SOURCES.txt +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/codespine.egg-info/dependency_links.txt +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/codespine.egg-info/entry_points.txt +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/codespine.egg-info/requires.txt +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/codespine.egg-info/top_level.txt +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/gindex.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/setup.cfg +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/tests/test_branch_diff_normalize.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/tests/test_call_resolver.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/tests/test_community_detection.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/tests/test_deadcode.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/tests/test_index_and_hybrid.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/tests/test_java_parser.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/tests/test_multimodule_index.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/tests/test_overlay.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/tests/test_search_ranking.py +0 -0
- {codespine-0.6.0 → codespine-0.6.2}/tests/test_store_recovery.py +0 -0
|
@@ -751,17 +751,25 @@ def clear_project_cmd(project_id: str, allow_running: bool) -> None:
|
|
|
751
751
|
if not allow_running and _is_running():
|
|
752
752
|
click.secho("Stop MCP first ('codespine stop') to modify index.", fg="yellow")
|
|
753
753
|
return
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
754
|
+
try:
|
|
755
|
+
store = GraphStore(read_only=False)
|
|
756
|
+
recs = store.query_records(
|
|
757
|
+
"MATCH (p:Project) WHERE p.id = $pid RETURN p.id as id, p.path as path",
|
|
758
|
+
{"pid": project_id},
|
|
759
|
+
)
|
|
760
|
+
except Exception as exc:
|
|
761
|
+
click.secho(f"DB is corrupted ({exc}). Use 'codespine force-reset' to wipe all data.", fg="red")
|
|
762
|
+
return
|
|
759
763
|
if not recs:
|
|
760
764
|
click.secho(f"Project '{project_id}' not found in index.", fg="yellow")
|
|
761
765
|
return
|
|
762
766
|
project_path = recs[0].get("path", "")
|
|
763
|
-
|
|
764
|
-
|
|
767
|
+
try:
|
|
768
|
+
store.clear_analysis_artifacts()
|
|
769
|
+
store.clear_project(project_id)
|
|
770
|
+
except Exception as exc:
|
|
771
|
+
click.secho(f"DB write failed ({exc}). Use 'codespine force-reset' to recover.", fg="red")
|
|
772
|
+
return
|
|
765
773
|
store.overlay_store.clear_project(project_id)
|
|
766
774
|
meta_path = JavaIndexer._meta_cache_path(project_id)
|
|
767
775
|
if os.path.exists(meta_path):
|
|
@@ -769,6 +777,8 @@ def clear_project_cmd(project_id: str, allow_running: bool) -> None:
|
|
|
769
777
|
os.remove(meta_path)
|
|
770
778
|
except OSError:
|
|
771
779
|
pass
|
|
780
|
+
# Update the read replica so read-only callers (stats, MCP) see the change.
|
|
781
|
+
GraphStore.snapshot_to_read_replica()
|
|
772
782
|
click.secho(f"Cleared project '{project_id}' (was at {project_path}).", fg="green")
|
|
773
783
|
|
|
774
784
|
|
|
@@ -784,9 +794,23 @@ def clear_index_cmd(allow_running: bool) -> None:
|
|
|
784
794
|
if not allow_running and _is_running():
|
|
785
795
|
click.secho("Stop MCP first ('codespine stop') to modify index.", fg="yellow")
|
|
786
796
|
return
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
797
|
+
try:
|
|
798
|
+
store = GraphStore(read_only=False)
|
|
799
|
+
projects = store.query_records("MATCH (p:Project) RETURN p.id as id")
|
|
800
|
+
except Exception:
|
|
801
|
+
# DB is corrupted — can't even open it. Force-delete everything.
|
|
802
|
+
click.secho("DB is corrupted. Running force-reset instead...", fg="yellow")
|
|
803
|
+
removed = GraphStore.force_delete_all_data()
|
|
804
|
+
click.secho(f"Force-reset complete. {len(removed)} path(s) removed. Index is now empty.", fg="green")
|
|
805
|
+
return
|
|
806
|
+
try:
|
|
807
|
+
store.rebuild_empty_db()
|
|
808
|
+
except Exception as exc:
|
|
809
|
+
# rebuild_empty_db failed even with fallbacks — force-delete.
|
|
810
|
+
click.secho(f"rebuild failed ({exc}). Running force-reset...", fg="yellow")
|
|
811
|
+
GraphStore.force_delete_all_data()
|
|
812
|
+
click.secho("Force-reset complete. Index is now empty.", fg="green")
|
|
813
|
+
return
|
|
790
814
|
store.overlay_store.clear_all()
|
|
791
815
|
for p in projects:
|
|
792
816
|
meta_path = JavaIndexer._meta_cache_path(p["id"])
|
|
@@ -795,6 +819,9 @@ def clear_index_cmd(allow_running: bool) -> None:
|
|
|
795
819
|
os.remove(meta_path)
|
|
796
820
|
except OSError:
|
|
797
821
|
pass
|
|
822
|
+
# Publish an empty read replica so that read-only callers (stats, MCP)
|
|
823
|
+
# immediately see the cleared state and the MCP daemon hot-reloads.
|
|
824
|
+
GraphStore.snapshot_to_read_replica()
|
|
798
825
|
click.secho(f"Cleared {len(projects)} project(s). Index is now empty.", fg="green")
|
|
799
826
|
|
|
800
827
|
|
|
@@ -550,21 +550,58 @@ class GraphStore:
|
|
|
550
550
|
def rebuild_empty_db(self) -> None:
|
|
551
551
|
self._recycle_conn()
|
|
552
552
|
path = SETTINGS.db_path
|
|
553
|
+
# Remove the DB directory AND any stale WAL / lock files
|
|
553
554
|
self._remove_db_path(path)
|
|
555
|
+
for suffix in (".wal", ".lock"):
|
|
556
|
+
sidecar = path + suffix
|
|
557
|
+
if os.path.exists(sidecar):
|
|
558
|
+
try:
|
|
559
|
+
os.remove(sidecar)
|
|
560
|
+
except OSError:
|
|
561
|
+
pass
|
|
562
|
+
|
|
563
|
+
# Also remove the read replica so that read-only callers (stats, MCP)
|
|
564
|
+
# don't continue to see stale data from before the wipe.
|
|
565
|
+
for stale in [
|
|
566
|
+
SETTINGS.db_snapshot_path,
|
|
567
|
+
SETTINGS.db_snapshot_path + ".tmp",
|
|
568
|
+
SETTINGS.db_snapshot_path + ".updated",
|
|
569
|
+
]:
|
|
570
|
+
self._remove_db_path(stale)
|
|
571
|
+
|
|
554
572
|
# Kuzu may retain stale internal state from a previous failed open of
|
|
555
|
-
# this path (e.g. after Ctrl+C mid-write).
|
|
556
|
-
#
|
|
557
|
-
#
|
|
573
|
+
# this path (e.g. after Ctrl+C mid-write). The in-process C++ state
|
|
574
|
+
# is poisoned and will raise "unordered_map::at: key not found" on any
|
|
575
|
+
# new kuzu.Database() call — even for a freshly deleted path.
|
|
576
|
+
#
|
|
577
|
+
# Strategy: try primary → try /tmp fallback → force-delete everything
|
|
578
|
+
# and re-import kuzu to get a clean C++ state.
|
|
558
579
|
try:
|
|
559
580
|
self.db = self._open_db(path)
|
|
560
|
-
except Exception as
|
|
581
|
+
except Exception as exc1:
|
|
582
|
+
LOGGER.warning("rebuild_empty_db: primary path failed (%s)", exc1)
|
|
561
583
|
fallback = os.path.join("/tmp", ".codespine_db")
|
|
562
|
-
LOGGER.warning(
|
|
563
|
-
"Could not open fresh DB at %s after rebuild (%s); falling back to %s",
|
|
564
|
-
path, exc, fallback,
|
|
565
|
-
)
|
|
566
584
|
self._remove_db_path(fallback)
|
|
567
|
-
|
|
585
|
+
for suffix in (".wal", ".lock"):
|
|
586
|
+
sidecar = fallback + suffix
|
|
587
|
+
if os.path.exists(sidecar):
|
|
588
|
+
try:
|
|
589
|
+
os.remove(sidecar)
|
|
590
|
+
except OSError:
|
|
591
|
+
pass
|
|
592
|
+
try:
|
|
593
|
+
self.db = self._open_db(fallback)
|
|
594
|
+
except Exception as exc2:
|
|
595
|
+
# Nuclear option: force-delete all files and reimport kuzu
|
|
596
|
+
# so the C++ runtime starts from a completely clean state.
|
|
597
|
+
LOGGER.warning("rebuild_empty_db: fallback also failed (%s); force-resetting", exc2)
|
|
598
|
+
self.force_delete_all_data()
|
|
599
|
+
import importlib
|
|
600
|
+
importlib.reload(kuzu)
|
|
601
|
+
try:
|
|
602
|
+
self.db = kuzu.Database(path, buffer_pool_size=_WRITE_BUFFER_POOL_SIZE)
|
|
603
|
+
except TypeError:
|
|
604
|
+
self.db = kuzu.Database(path)
|
|
568
605
|
self._tls = threading.local()
|
|
569
606
|
ensure_schema(self._conn())
|
|
570
607
|
|
|
@@ -1227,9 +1227,13 @@ def build_mcp_server(store, repo_path_provider):
|
|
|
1227
1227
|
timeout=60,
|
|
1228
1228
|
)
|
|
1229
1229
|
if proc.returncode != 0:
|
|
1230
|
+
error_text = (proc.stderr.strip() or proc.stdout.strip())[:300]
|
|
1230
1231
|
return {
|
|
1231
1232
|
"available": False,
|
|
1232
|
-
"note":
|
|
1233
|
+
"note": (
|
|
1234
|
+
f"Reset failed: {error_text}. "
|
|
1235
|
+
"If this is a buffer pool or DB corruption error, use force_reset_index() instead."
|
|
1236
|
+
),
|
|
1233
1237
|
}
|
|
1234
1238
|
return {
|
|
1235
1239
|
"available": True,
|
|
@@ -1268,9 +1272,13 @@ def build_mcp_server(store, repo_path_provider):
|
|
|
1268
1272
|
timeout=120,
|
|
1269
1273
|
)
|
|
1270
1274
|
if proc.returncode != 0:
|
|
1275
|
+
error_text = (proc.stderr.strip() or proc.stdout.strip())[:300]
|
|
1271
1276
|
return {
|
|
1272
1277
|
"available": False,
|
|
1273
|
-
"note":
|
|
1278
|
+
"note": (
|
|
1279
|
+
f"Reset failed: {error_text}. "
|
|
1280
|
+
"If this is a buffer pool or DB corruption error, use force_reset_index() instead."
|
|
1281
|
+
),
|
|
1274
1282
|
}
|
|
1275
1283
|
|
|
1276
1284
|
cleared = [{"project_id": p["id"], "path": p["path"]} for p in projects]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|