codespine 0.5.8__tar.gz → 0.5.10__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.5.8 → codespine-0.5.10}/PKG-INFO +1 -1
  2. {codespine-0.5.8 → codespine-0.5.10}/codespine/__init__.py +1 -1
  3. {codespine-0.5.8 → codespine-0.5.10}/codespine/cli.py +28 -8
  4. {codespine-0.5.8 → codespine-0.5.10}/codespine/db/store.py +16 -4
  5. {codespine-0.5.8 → codespine-0.5.10}/codespine.egg-info/PKG-INFO +1 -1
  6. {codespine-0.5.8 → codespine-0.5.10}/pyproject.toml +1 -1
  7. {codespine-0.5.8 → codespine-0.5.10}/LICENSE +0 -0
  8. {codespine-0.5.8 → codespine-0.5.10}/README.md +0 -0
  9. {codespine-0.5.8 → codespine-0.5.10}/codespine/analysis/__init__.py +0 -0
  10. {codespine-0.5.8 → codespine-0.5.10}/codespine/analysis/community.py +0 -0
  11. {codespine-0.5.8 → codespine-0.5.10}/codespine/analysis/context.py +0 -0
  12. {codespine-0.5.8 → codespine-0.5.10}/codespine/analysis/coupling.py +0 -0
  13. {codespine-0.5.8 → codespine-0.5.10}/codespine/analysis/crossmodule.py +0 -0
  14. {codespine-0.5.8 → codespine-0.5.10}/codespine/analysis/deadcode.py +0 -0
  15. {codespine-0.5.8 → codespine-0.5.10}/codespine/analysis/flow.py +0 -0
  16. {codespine-0.5.8 → codespine-0.5.10}/codespine/analysis/impact.py +0 -0
  17. {codespine-0.5.8 → codespine-0.5.10}/codespine/config.py +0 -0
  18. {codespine-0.5.8 → codespine-0.5.10}/codespine/db/__init__.py +0 -0
  19. {codespine-0.5.8 → codespine-0.5.10}/codespine/db/schema.py +0 -0
  20. {codespine-0.5.8 → codespine-0.5.10}/codespine/diff/__init__.py +0 -0
  21. {codespine-0.5.8 → codespine-0.5.10}/codespine/diff/branch_diff.py +0 -0
  22. {codespine-0.5.8 → codespine-0.5.10}/codespine/indexer/__init__.py +0 -0
  23. {codespine-0.5.8 → codespine-0.5.10}/codespine/indexer/call_resolver.py +0 -0
  24. {codespine-0.5.8 → codespine-0.5.10}/codespine/indexer/engine.py +0 -0
  25. {codespine-0.5.8 → codespine-0.5.10}/codespine/indexer/java_parser.py +0 -0
  26. {codespine-0.5.8 → codespine-0.5.10}/codespine/indexer/symbol_builder.py +0 -0
  27. {codespine-0.5.8 → codespine-0.5.10}/codespine/mcp/__init__.py +0 -0
  28. {codespine-0.5.8 → codespine-0.5.10}/codespine/mcp/server.py +0 -0
  29. {codespine-0.5.8 → codespine-0.5.10}/codespine/noise/__init__.py +0 -0
  30. {codespine-0.5.8 → codespine-0.5.10}/codespine/noise/blocklist.py +0 -0
  31. {codespine-0.5.8 → codespine-0.5.10}/codespine/overlay/__init__.py +0 -0
  32. {codespine-0.5.8 → codespine-0.5.10}/codespine/overlay/git_state.py +0 -0
  33. {codespine-0.5.8 → codespine-0.5.10}/codespine/overlay/merge.py +0 -0
  34. {codespine-0.5.8 → codespine-0.5.10}/codespine/overlay/store.py +0 -0
  35. {codespine-0.5.8 → codespine-0.5.10}/codespine/search/__init__.py +0 -0
  36. {codespine-0.5.8 → codespine-0.5.10}/codespine/search/bm25.py +0 -0
  37. {codespine-0.5.8 → codespine-0.5.10}/codespine/search/fuzzy.py +0 -0
  38. {codespine-0.5.8 → codespine-0.5.10}/codespine/search/hybrid.py +0 -0
  39. {codespine-0.5.8 → codespine-0.5.10}/codespine/search/rrf.py +0 -0
  40. {codespine-0.5.8 → codespine-0.5.10}/codespine/search/vector.py +0 -0
  41. {codespine-0.5.8 → codespine-0.5.10}/codespine/watch/__init__.py +0 -0
  42. {codespine-0.5.8 → codespine-0.5.10}/codespine/watch/watcher.py +0 -0
  43. {codespine-0.5.8 → codespine-0.5.10}/codespine.egg-info/SOURCES.txt +0 -0
  44. {codespine-0.5.8 → codespine-0.5.10}/codespine.egg-info/dependency_links.txt +0 -0
  45. {codespine-0.5.8 → codespine-0.5.10}/codespine.egg-info/entry_points.txt +0 -0
  46. {codespine-0.5.8 → codespine-0.5.10}/codespine.egg-info/requires.txt +0 -0
  47. {codespine-0.5.8 → codespine-0.5.10}/codespine.egg-info/top_level.txt +0 -0
  48. {codespine-0.5.8 → codespine-0.5.10}/gindex.py +0 -0
  49. {codespine-0.5.8 → codespine-0.5.10}/setup.cfg +0 -0
  50. {codespine-0.5.8 → codespine-0.5.10}/tests/test_branch_diff_normalize.py +0 -0
  51. {codespine-0.5.8 → codespine-0.5.10}/tests/test_call_resolver.py +0 -0
  52. {codespine-0.5.8 → codespine-0.5.10}/tests/test_community_detection.py +0 -0
  53. {codespine-0.5.8 → codespine-0.5.10}/tests/test_deadcode.py +0 -0
  54. {codespine-0.5.8 → codespine-0.5.10}/tests/test_index_and_hybrid.py +0 -0
  55. {codespine-0.5.8 → codespine-0.5.10}/tests/test_java_parser.py +0 -0
  56. {codespine-0.5.8 → codespine-0.5.10}/tests/test_multimodule_index.py +0 -0
  57. {codespine-0.5.8 → codespine-0.5.10}/tests/test_overlay.py +0 -0
  58. {codespine-0.5.8 → codespine-0.5.10}/tests/test_search_ranking.py +0 -0
  59. {codespine-0.5.8 → codespine-0.5.10}/tests/test_store_recovery.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codespine
3
- Version: 0.5.8
3
+ Version: 0.5.10
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.5.8"
4
+ __version__ = "0.5.10"
@@ -77,6 +77,19 @@ def _dead_result_count(dead_result: list[dict] | None) -> int:
77
77
  return sum(1 for item in dead_result if isinstance(item, dict) and "_stats" not in item)
78
78
 
79
79
 
80
+ def _bar(done: int, total: int, width: int = 20) -> str:
81
+ """Return an ASCII progress bar like [████████░░░░] 40%."""
82
+ if total <= 0:
83
+ return f"[{'░' * width}] ---%"
84
+ frac = min(done / total, 1.0)
85
+ filled = int(width * frac)
86
+ return f"[{'█' * filled}{'░' * (width - filled)}] {int(frac * 100):3d}%"
87
+
88
+
89
+ def _spinner_char() -> str:
90
+ return "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏"[int(time.perf_counter() * 8) % 10]
91
+
92
+
80
93
  @click.group()
81
94
  def main() -> None:
82
95
  """CodeSpine CLI."""
@@ -141,7 +154,7 @@ def analyse(path: str, full: bool, deep: bool, embed: bool, allow_running: bool)
141
154
 
142
155
  # Shared progress state (reset per module)
143
156
  parse_state = {"shown": False, "indexed": 0, "total": 0, "last_ts": 0.0, "printed_zero": False}
144
- call_state = {"shown": False, "count": 0, "last_ts": 0.0}
157
+ call_state = {"shown": False, "count": 0, "last_ts": 0.0, "started_at": 0.0}
145
158
 
146
159
  def _reset_state() -> None:
147
160
  for k in list(parse_state):
@@ -171,22 +184,28 @@ def analyse(path: str, full: bool, deep: bool, embed: bool, allow_running: bool)
171
184
  if total == 0:
172
185
  return
173
186
  if indexed == total or (now - parse_state["last_ts"]) >= 0.2:
174
- click.echo(f"\rParsing code... {indexed}/{total}", nl=False)
187
+ click.echo(f"\rParsing code... {_bar(indexed, total)} {indexed}/{total} ", nl=False)
175
188
  parse_state["shown"] = True
176
189
  parse_state["last_ts"] = now
177
190
  return
178
191
  if event == "resolve_calls_start" and parse_state["shown"]:
179
192
  click.echo()
180
193
  parse_state["shown"] = False
181
- _phase("Tracing calls...", "running")
194
+ call_state["started_at"] = now
195
+ _phase("Tracing calls...", "starting...")
182
196
  return
183
197
  if event == "resolve_calls_start":
184
- _phase("Tracing calls...", "running")
198
+ call_state["started_at"] = now
199
+ _phase("Tracing calls...", "starting...")
185
200
  return
186
201
  if event == "resolve_calls_progress":
187
202
  call_state["count"] = int(payload.get("calls_resolved", 0))
188
203
  if (now - call_state["last_ts"]) >= 0.25:
189
- click.echo(f"\rTracing calls... {call_state['count']} resolved", nl=False)
204
+ elapsed_s = now - call_state["started_at"]
205
+ click.echo(
206
+ f"\r{_spinner_char()} Tracing calls... {call_state['count']:>6} resolved {elapsed_s:.1f}s ",
207
+ nl=False,
208
+ )
190
209
  call_state["shown"] = True
191
210
  call_state["last_ts"] = now
192
211
  return
@@ -194,7 +213,8 @@ def analyse(path: str, full: bool, deep: bool, embed: bool, allow_running: bool)
194
213
  if call_state["shown"]:
195
214
  click.echo()
196
215
  call_state["shown"] = False
197
- _phase("Tracing calls...", f"{int(payload.get('calls_resolved', 0))} calls resolved")
216
+ elapsed_s = (now - call_state["started_at"]) if call_state["started_at"] else 0.0
217
+ _phase("Tracing calls...", f"{int(payload.get('calls_resolved', 0))} calls resolved ({elapsed_s:.1f}s)")
198
218
  return
199
219
  if event == "resolve_types_start":
200
220
  _phase("Analyzing types...", "running")
@@ -226,11 +246,11 @@ def analyse(path: str, full: bool, deep: bool, embed: bool, allow_running: bool)
226
246
  # ── Helper for in-place progress updates ────────────────────────────
227
247
  def _live_phase(label: str, status: str) -> None:
228
248
  """Overwrite the current line with a status update."""
229
- click.echo(f"\r{label:<30} {status:<50}", nl=False)
249
+ click.echo(f"\r{_spinner_char()} {label:<28} {status:<48}", nl=False)
230
250
 
231
251
  def _finish_phase(label: str, result: str) -> None:
232
252
  """Finalise an in-place phase line and move to the next line."""
233
- click.echo(f"\r{label:<30} {result:<50}")
253
+ click.echo(f"\r{label:<28} {result:<48}")
234
254
 
235
255
  # ── Cross-module call linking ──────────────────────────────────────
236
256
  if is_multi and len(modules_with_ids) > 1:
@@ -28,6 +28,11 @@ _RECOVERABLE_DB_ERROR_MARKERS = (
28
28
  "corrupt",
29
29
  "corrupted",
30
30
  "invalid database",
31
+ # Kuzu internal error: abrupt process termination (Ctrl+C) during a write
32
+ # leaves the WAL in an inconsistent state; Kuzu raises this as an
33
+ # IndexError from its internal unordered_map when re-opening the path.
34
+ "unordered_map",
35
+ "key not found",
31
36
  )
32
37
 
33
38
 
@@ -500,14 +505,21 @@ class GraphStore:
500
505
  def rebuild_empty_db(self) -> None:
501
506
  self._recycle_conn()
502
507
  path = SETTINGS.db_path
508
+ self._remove_db_path(path)
509
+ # Kuzu may retain stale internal state from a previous failed open of
510
+ # this path (e.g. after Ctrl+C mid-write). If re-opening the just-
511
+ # deleted path raises, fall back to a clean /tmp location so the
512
+ # command succeeds rather than leaving the user stuck.
503
513
  try:
504
- self._remove_db_path(path)
505
- except OSError:
514
+ self.db = self._open_db(path)
515
+ except Exception as exc:
506
516
  fallback = os.path.join("/tmp", ".codespine_db")
517
+ LOGGER.warning(
518
+ "Could not open fresh DB at %s after rebuild (%s); falling back to %s",
519
+ path, exc, fallback,
520
+ )
507
521
  self._remove_db_path(fallback)
508
522
  self.db = self._open_db(fallback)
509
- else:
510
- self.db = self._open_db(path)
511
523
  self._tls = threading.local()
512
524
  ensure_schema(self._conn())
513
525
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codespine
3
- Version: 0.5.8
3
+ Version: 0.5.10
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.5.8"
7
+ version = "0.5.10"
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