codespine 0.6.0__tar.gz → 0.6.1__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 (59) hide show
  1. {codespine-0.6.0 → codespine-0.6.1}/PKG-INFO +1 -1
  2. {codespine-0.6.0 → codespine-0.6.1}/codespine/__init__.py +1 -1
  3. {codespine-0.6.0 → codespine-0.6.1}/codespine/cli.py +32 -10
  4. {codespine-0.6.0 → codespine-0.6.1}/codespine/db/store.py +37 -9
  5. {codespine-0.6.0 → codespine-0.6.1}/codespine/mcp/server.py +10 -2
  6. {codespine-0.6.0 → codespine-0.6.1}/codespine.egg-info/PKG-INFO +1 -1
  7. {codespine-0.6.0 → codespine-0.6.1}/pyproject.toml +1 -1
  8. {codespine-0.6.0 → codespine-0.6.1}/LICENSE +0 -0
  9. {codespine-0.6.0 → codespine-0.6.1}/README.md +0 -0
  10. {codespine-0.6.0 → codespine-0.6.1}/codespine/analysis/__init__.py +0 -0
  11. {codespine-0.6.0 → codespine-0.6.1}/codespine/analysis/community.py +0 -0
  12. {codespine-0.6.0 → codespine-0.6.1}/codespine/analysis/context.py +0 -0
  13. {codespine-0.6.0 → codespine-0.6.1}/codespine/analysis/coupling.py +0 -0
  14. {codespine-0.6.0 → codespine-0.6.1}/codespine/analysis/crossmodule.py +0 -0
  15. {codespine-0.6.0 → codespine-0.6.1}/codespine/analysis/deadcode.py +0 -0
  16. {codespine-0.6.0 → codespine-0.6.1}/codespine/analysis/flow.py +0 -0
  17. {codespine-0.6.0 → codespine-0.6.1}/codespine/analysis/impact.py +0 -0
  18. {codespine-0.6.0 → codespine-0.6.1}/codespine/config.py +0 -0
  19. {codespine-0.6.0 → codespine-0.6.1}/codespine/db/__init__.py +0 -0
  20. {codespine-0.6.0 → codespine-0.6.1}/codespine/db/schema.py +0 -0
  21. {codespine-0.6.0 → codespine-0.6.1}/codespine/diff/__init__.py +0 -0
  22. {codespine-0.6.0 → codespine-0.6.1}/codespine/diff/branch_diff.py +0 -0
  23. {codespine-0.6.0 → codespine-0.6.1}/codespine/indexer/__init__.py +0 -0
  24. {codespine-0.6.0 → codespine-0.6.1}/codespine/indexer/call_resolver.py +0 -0
  25. {codespine-0.6.0 → codespine-0.6.1}/codespine/indexer/engine.py +0 -0
  26. {codespine-0.6.0 → codespine-0.6.1}/codespine/indexer/java_parser.py +0 -0
  27. {codespine-0.6.0 → codespine-0.6.1}/codespine/indexer/symbol_builder.py +0 -0
  28. {codespine-0.6.0 → codespine-0.6.1}/codespine/mcp/__init__.py +0 -0
  29. {codespine-0.6.0 → codespine-0.6.1}/codespine/noise/__init__.py +0 -0
  30. {codespine-0.6.0 → codespine-0.6.1}/codespine/noise/blocklist.py +0 -0
  31. {codespine-0.6.0 → codespine-0.6.1}/codespine/overlay/__init__.py +0 -0
  32. {codespine-0.6.0 → codespine-0.6.1}/codespine/overlay/git_state.py +0 -0
  33. {codespine-0.6.0 → codespine-0.6.1}/codespine/overlay/merge.py +0 -0
  34. {codespine-0.6.0 → codespine-0.6.1}/codespine/overlay/store.py +0 -0
  35. {codespine-0.6.0 → codespine-0.6.1}/codespine/search/__init__.py +0 -0
  36. {codespine-0.6.0 → codespine-0.6.1}/codespine/search/bm25.py +0 -0
  37. {codespine-0.6.0 → codespine-0.6.1}/codespine/search/fuzzy.py +0 -0
  38. {codespine-0.6.0 → codespine-0.6.1}/codespine/search/hybrid.py +0 -0
  39. {codespine-0.6.0 → codespine-0.6.1}/codespine/search/rrf.py +0 -0
  40. {codespine-0.6.0 → codespine-0.6.1}/codespine/search/vector.py +0 -0
  41. {codespine-0.6.0 → codespine-0.6.1}/codespine/watch/__init__.py +0 -0
  42. {codespine-0.6.0 → codespine-0.6.1}/codespine/watch/watcher.py +0 -0
  43. {codespine-0.6.0 → codespine-0.6.1}/codespine.egg-info/SOURCES.txt +0 -0
  44. {codespine-0.6.0 → codespine-0.6.1}/codespine.egg-info/dependency_links.txt +0 -0
  45. {codespine-0.6.0 → codespine-0.6.1}/codespine.egg-info/entry_points.txt +0 -0
  46. {codespine-0.6.0 → codespine-0.6.1}/codespine.egg-info/requires.txt +0 -0
  47. {codespine-0.6.0 → codespine-0.6.1}/codespine.egg-info/top_level.txt +0 -0
  48. {codespine-0.6.0 → codespine-0.6.1}/gindex.py +0 -0
  49. {codespine-0.6.0 → codespine-0.6.1}/setup.cfg +0 -0
  50. {codespine-0.6.0 → codespine-0.6.1}/tests/test_branch_diff_normalize.py +0 -0
  51. {codespine-0.6.0 → codespine-0.6.1}/tests/test_call_resolver.py +0 -0
  52. {codespine-0.6.0 → codespine-0.6.1}/tests/test_community_detection.py +0 -0
  53. {codespine-0.6.0 → codespine-0.6.1}/tests/test_deadcode.py +0 -0
  54. {codespine-0.6.0 → codespine-0.6.1}/tests/test_index_and_hybrid.py +0 -0
  55. {codespine-0.6.0 → codespine-0.6.1}/tests/test_java_parser.py +0 -0
  56. {codespine-0.6.0 → codespine-0.6.1}/tests/test_multimodule_index.py +0 -0
  57. {codespine-0.6.0 → codespine-0.6.1}/tests/test_overlay.py +0 -0
  58. {codespine-0.6.0 → codespine-0.6.1}/tests/test_search_ranking.py +0 -0
  59. {codespine-0.6.0 → codespine-0.6.1}/tests/test_store_recovery.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codespine
3
- Version: 0.6.0
3
+ Version: 0.6.1
4
4
  Summary: Local Java code intelligence indexer backed by a graph database
5
5
  Author: CodeSpine contributors
6
6
  License: MIT License
@@ -1,4 +1,4 @@
1
1
  """CodeSpine package."""
2
2
 
3
3
  __all__ = ["__version__"]
4
- __version__ = "0.6.0"
4
+ __version__ = "0.6.1"
@@ -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
- store = GraphStore(read_only=False)
755
- recs = store.query_records(
756
- "MATCH (p:Project) WHERE p.id = $pid RETURN p.id as id, p.path as path",
757
- {"pid": project_id},
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
- store.clear_analysis_artifacts()
764
- store.clear_project(project_id)
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):
@@ -784,9 +792,23 @@ def clear_index_cmd(allow_running: bool) -> None:
784
792
  if not allow_running and _is_running():
785
793
  click.secho("Stop MCP first ('codespine stop') to modify index.", fg="yellow")
786
794
  return
787
- store = GraphStore(read_only=False)
788
- projects = store.query_records("MATCH (p:Project) RETURN p.id as id")
789
- store.rebuild_empty_db()
795
+ try:
796
+ store = GraphStore(read_only=False)
797
+ projects = store.query_records("MATCH (p:Project) RETURN p.id as id")
798
+ except Exception:
799
+ # DB is corrupted — can't even open it. Force-delete everything.
800
+ click.secho("DB is corrupted. Running force-reset instead...", fg="yellow")
801
+ removed = GraphStore.force_delete_all_data()
802
+ click.secho(f"Force-reset complete. {len(removed)} path(s) removed. Index is now empty.", fg="green")
803
+ return
804
+ try:
805
+ store.rebuild_empty_db()
806
+ except Exception as exc:
807
+ # rebuild_empty_db failed even with fallbacks — force-delete.
808
+ click.secho(f"rebuild failed ({exc}). Running force-reset...", fg="yellow")
809
+ GraphStore.force_delete_all_data()
810
+ click.secho("Force-reset complete. Index is now empty.", fg="green")
811
+ return
790
812
  store.overlay_store.clear_all()
791
813
  for p in projects:
792
814
  meta_path = JavaIndexer._meta_cache_path(p["id"])
@@ -550,21 +550,49 @@ 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
+
554
563
  # Kuzu may retain stale internal state from a previous failed open of
555
- # this path (e.g. after Ctrl+C mid-write). If re-opening the just-
556
- # deleted path raises, fall back to a clean /tmp location so the
557
- # command succeeds rather than leaving the user stuck.
564
+ # this path (e.g. after Ctrl+C mid-write). The in-process C++ state
565
+ # is poisoned and will raise "unordered_map::at: key not found" on any
566
+ # new kuzu.Database() call even for a freshly deleted path.
567
+ #
568
+ # Strategy: try primary → try /tmp fallback → force-delete everything
569
+ # and re-import kuzu to get a clean C++ state.
558
570
  try:
559
571
  self.db = self._open_db(path)
560
- except Exception as exc:
572
+ except Exception as exc1:
573
+ LOGGER.warning("rebuild_empty_db: primary path failed (%s)", exc1)
561
574
  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
575
  self._remove_db_path(fallback)
567
- self.db = self._open_db(fallback)
576
+ for suffix in (".wal", ".lock"):
577
+ sidecar = fallback + suffix
578
+ if os.path.exists(sidecar):
579
+ try:
580
+ os.remove(sidecar)
581
+ except OSError:
582
+ pass
583
+ try:
584
+ self.db = self._open_db(fallback)
585
+ except Exception as exc2:
586
+ # Nuclear option: force-delete all files and reimport kuzu
587
+ # so the C++ runtime starts from a completely clean state.
588
+ LOGGER.warning("rebuild_empty_db: fallback also failed (%s); force-resetting", exc2)
589
+ self.force_delete_all_data()
590
+ import importlib
591
+ importlib.reload(kuzu)
592
+ try:
593
+ self.db = kuzu.Database(path, buffer_pool_size=_WRITE_BUFFER_POOL_SIZE)
594
+ except TypeError:
595
+ self.db = kuzu.Database(path)
568
596
  self._tls = threading.local()
569
597
  ensure_schema(self._conn())
570
598
 
@@ -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": f"Reset failed: {proc.stderr.strip() or proc.stdout.strip()}",
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": f"Reset failed: {proc.stderr.strip() or proc.stdout.strip()}",
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]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codespine
3
- Version: 0.6.0
3
+ Version: 0.6.1
4
4
  Summary: Local Java code intelligence indexer backed by a graph database
5
5
  Author: CodeSpine contributors
6
6
  License: MIT License
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "codespine"
7
- version = "0.6.0"
7
+ version = "0.6.1"
8
8
  description = "Local Java code intelligence indexer backed by a graph database"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
File without changes
File without changes
File without changes
File without changes
File without changes