java-codebase-rag 0.11.1__tar.gz → 0.11.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.
Files changed (78) hide show
  1. {java_codebase_rag-0.11.1/src/java_codebase_rag.egg-info → java_codebase_rag-0.11.2}/PKG-INFO +1 -1
  2. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/pyproject.toml +1 -1
  3. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/install_data/agents/explorer-rag-cli.md +1 -1
  4. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/install_data/skills/explore-codebase-cli/SKILL.md +1 -1
  5. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/jrag.py +2 -0
  6. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/watch/daemon.py +40 -12
  7. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/watch/watcher.py +29 -11
  8. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2/src/java_codebase_rag.egg-info}/PKG-INFO +1 -1
  9. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/LICENSE +0 -0
  10. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/README.md +0 -0
  11. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/setup.cfg +0 -0
  12. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/__init__.py +0 -0
  13. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/_fdlimit.py +0 -0
  14. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/_stdio.py +0 -0
  15. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/_version.py +0 -0
  16. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/absence/__init__.py +0 -0
  17. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/absence/absence_diagnosis.py +0 -0
  18. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/absence/absence_types.py +0 -0
  19. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/absence/absence_vocab.py +0 -0
  20. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/analysis/__init__.py +0 -0
  21. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/analysis/pr_analysis.py +0 -0
  22. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/analysis/resolve_service.py +0 -0
  23. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/ast/__init__.py +0 -0
  24. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/ast/ast_java.py +0 -0
  25. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/ast/brownfield_events.py +0 -0
  26. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/ast/chunk_heuristics.py +0 -0
  27. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/cli.py +0 -0
  28. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/cli_format.py +0 -0
  29. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/cli_progress.py +0 -0
  30. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/config.py +0 -0
  31. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/eval/__init__.py +0 -0
  32. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/eval/ground_truth.py +0 -0
  33. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/eval/metrics.py +0 -0
  34. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/eval/runner.py +0 -0
  35. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/graph/__init__.py +0 -0
  36. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/graph/build_ast_graph.py +0 -0
  37. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/graph/graph_enrich.py +0 -0
  38. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/graph/graph_types.py +0 -0
  39. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/graph/java_ontology.py +0 -0
  40. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/graph/ladybug_queries.py +0 -0
  41. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/graph/path_filtering.py +0 -0
  42. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/index/__init__.py +0 -0
  43. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/index/java_index_flow_lancedb.py +0 -0
  44. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/index/java_index_v1_common.py +0 -0
  45. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/install_data/__init__.py +0 -0
  46. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/install_data/agents/explorer-rag-enhanced.md +0 -0
  47. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/install_data/skills/explore-codebase/SKILL.md +0 -0
  48. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/installer.py +0 -0
  49. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/jrag_envelope.py +0 -0
  50. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/jrag_hints.py +0 -0
  51. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/jrag_render.py +0 -0
  52. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/lance_optimize.py +0 -0
  53. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/mcp/__init__.py +0 -0
  54. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/mcp/mcp_hints.py +0 -0
  55. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/mcp/mcp_v2.py +0 -0
  56. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/mcp/server.py +0 -0
  57. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/pipeline.py +0 -0
  58. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/progress.py +0 -0
  59. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/read_payloads.py +0 -0
  60. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/search/__init__.py +0 -0
  61. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/search/index_common.py +0 -0
  62. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/search/search_lancedb.py +0 -0
  63. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/search/search_lexical.py +0 -0
  64. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/search/search_scoring.py +0 -0
  65. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/watch/__init__.py +0 -0
  66. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/watch/client.py +0 -0
  67. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/watch/lock.py +0 -0
  68. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/watch/paths.py +0 -0
  69. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/watch/protocol.py +0 -0
  70. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/watch/server.py +0 -0
  71. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag/watch/warm.py +0 -0
  72. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag.egg-info/SOURCES.txt +0 -0
  73. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag.egg-info/dependency_links.txt +0 -0
  74. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag.egg-info/entry_points.txt +0 -0
  75. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag.egg-info/requires.txt +0 -0
  76. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/src/java_codebase_rag.egg-info/top_level.txt +0 -0
  77. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/tests/test_config_watch.py +0 -0
  78. {java_codebase_rag-0.11.1 → java_codebase_rag-0.11.2}/tests/test_docs_watch.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: java-codebase-rag
3
- Version: 0.11.1
3
+ Version: 0.11.2
4
4
  Summary: MCP server for semantic + structural search over Java codebases
5
5
  Author: HumanBean17
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "java-codebase-rag"
7
- version = "0.11.1"
7
+ version = "0.11.2"
8
8
  description = "MCP server for semantic + structural search over Java codebases"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -95,7 +95,7 @@ The Decision Framework above tells you *which* command; reach for `--help` only
95
95
 
96
96
  **Prerequisite.** `jrag` needs an index — unindexed, every command exits 2 (`jrag status` checks; the file-system tools work without one).
97
97
 
98
- **Tip.** Run `jrag watch` once per session for fast, fresh queries — it keeps the index fresh on file change and serves every read command warm (no per-call model load). Optional; with no daemon running, all reads take the cold path byte-identically.
98
+ **Tip.** Run `jrag watch` once per session for fast, fresh queries — it keeps the index fresh on file change and serves every read command warm (no per-call model/graph load; warm lexical + graph on Intel Mac). Optional; with no daemon running, all reads take the cold path byte-identically.
99
99
 
100
100
  **Resolve-first contract.** Every `<query>` command resolves the identifier first, then maps `one` / `many` / `none` onto one envelope: `one` → run; `many` → return candidates and stop, **no silent guess across distinct types** (a class sharing its simple name with its own constructor still resolves to the type — narrow with `--kind` / `--role` / `--fqn-contains` / `--service`); `none` → `status: not_found` (exit 0), fall back to `search` or `Grep`. Pass names (FQN / simple name / route path / topic) or prior `sym:`/`route:`/`client:`/`producer:` ids — never raw node ids. `--kind` is a true resolve input; `--role` / `--java-kind` / `--fqn-contains` post-filter client-side.
101
101
 
@@ -94,7 +94,7 @@ The Decision Framework above tells you *which* command; reach for `--help` only
94
94
 
95
95
  **Prerequisite.** `jrag` needs an index — unindexed, every command exits 2 (`jrag status` checks; the file-system tools work without one).
96
96
 
97
- **Tip.** Run `jrag watch` once per session for fast, fresh queries — it keeps the index fresh on file change and serves every read command warm (no per-call model load). Optional; with no daemon running, all reads take the cold path byte-identically.
97
+ **Tip.** Run `jrag watch` once per session for fast, fresh queries — it keeps the index fresh on file change and serves every read command warm (no per-call model/graph load; warm lexical + graph on Intel Mac). Optional; with no daemon running, all reads take the cold path byte-identically.
98
98
 
99
99
  **Resolve-first contract.** Every `<query>` command resolves the identifier first, then maps `one` / `many` / `none` onto one envelope: `one` → run; `many` → return candidates and stop, **no silent guess across distinct types** (a class sharing its simple name with its own constructor still resolves to the type — narrow with `--kind` / `--role` / `--fqn-contains` / `--service`); `none` → `status: not_found` (exit 0), fall back to `search` or `Grep`. Pass names (FQN / simple name / route path / topic) or prior `sym:`/`route:`/`client:`/`producer:` ids — never raw node ids. `--kind` is a true resolve input; `--role` / `--java-kind` / `--fqn-contains` post-filter client-side.
100
100
 
@@ -1462,6 +1462,8 @@ def _cmd_watch_status(cfg) -> int:
1462
1462
  if alive:
1463
1463
  print(f"jrag watch: up (pid {pid}, socket {sock})")
1464
1464
  if state:
1465
+ if state.get("mode") == "lexical":
1466
+ print(" mode: lexical (graph-only)")
1465
1467
  kind = state.get("last_reindex_kind")
1466
1468
  at = state.get("last_reindex_at")
1467
1469
  count = state.get("reindex_count", 0)
@@ -15,8 +15,12 @@ Lifecycle (``run_foreground``):
15
15
 
16
16
  1. Acquire the project lock. Held elsewhere -> stderr line + ``return 2``.
17
17
  Unsupported platform -> stderr line + ``return 2``.
18
- 2. EAGERLY warm the embedding model so a load failure fails fast (stderr +
19
- ``on_event("error", …)`` + lock release + ``return 2``).
18
+ 2. EAGERLY warm the embedding model but ONLY when the vector stack is
19
+ installed — so a load failure fails fast (stderr + ``on_event("error", …)``
20
+ + lock release + ``return 2``). On graph-only installs (macOS Intel: PEP
21
+ 508 excludes sentence_transformers/lancedb) this step is skipped and the
22
+ daemon serves warm lexical/graph-only search instead; the read path
23
+ (``mcp_v2.search_v2``) degrades to lexical on its own.
20
24
  3. Install SIGINT/SIGTERM handlers that set a stop flag.
21
25
  4. ``server.start()`` then ``watcher.start()``.
22
26
  5. Write the state file (``paths.state_path``) so ``--status``/``--stop`` from
@@ -42,6 +46,7 @@ import threading
42
46
  import time
43
47
  from typing import TYPE_CHECKING, Any
44
48
 
49
+ from java_codebase_rag.pipeline import vector_stack_installed
45
50
  from java_codebase_rag.watch import paths
46
51
  from java_codebase_rag.watch.lock import (
47
52
  LockHeldError,
@@ -75,6 +80,10 @@ class WatchDaemon:
75
80
 
76
81
  def __init__(self, cfg: "ResolvedOperatorConfig") -> None:
77
82
  self.cfg = cfg
83
+ # Probed once (cheap: 3x importlib.util.find_spec). When False (graph-only
84
+ # install — macOS Intel), the daemon skips the embedding-model warm-up and
85
+ # the cocoindex vectors reindex; the read path degrades to lexical on its own.
86
+ self._vector_enabled = vector_stack_installed()
78
87
  self.lock = ProjectLock(cfg.index_dir)
79
88
  self.warm = WarmResources(cfg)
80
89
  self.server = WatchServer(self.warm, cfg)
@@ -94,6 +103,13 @@ class WatchDaemon:
94
103
  "started_at": None,
95
104
  "pid": None,
96
105
  "socket": str(paths.socket_path(cfg.index_dir)),
106
+ # Display label derived from the install-time probe above, NOT a live
107
+ # search-capability check — the read path's actual lexical/vector choice
108
+ # is mcp_v2's (``_ensure_vector_backend``). The two agree under the PEP
109
+ # 508 markers (the vector trio is present or absent together). Surfaced
110
+ # in the status panel and ``jrag watch --status``; omitted from display
111
+ # on the normal (vector) path to avoid noise.
112
+ "mode": "lexical" if not self._vector_enabled else "vector",
97
113
  "last_reindex_at": None,
98
114
  "last_reindex_kind": None,
99
115
  "reindex_count": 0,
@@ -128,16 +144,26 @@ class WatchDaemon:
128
144
  print("jrag watch: watch mode requires macOS/Linux", file=sys.stderr)
129
145
  return 2
130
146
 
131
- # 2. Eagerly warm the model so a load failure fails fast (before the
132
- # server accepts a single query). The model is the only heavy,
133
- # failure-prone resource that is not lazy on the read path.
134
- try:
135
- self.warm.model()
136
- except Exception as exc: # noqa: BLE001 report any load failure, then bail
137
- print(f"jrag watch: failed to load embedding model: {exc}", file=sys.stderr)
138
- self._record("error", {"phase": "model_load", "error": repr(exc)})
139
- self.lock.release()
140
- return 2
147
+ # 2. Eagerly warm the embedding model so a load failure fails fast (before
148
+ # the server accepts a single query) but ONLY when the vector stack is
149
+ # installed. The model is the only heavy, failure-prone resource that is
150
+ # not lazy on the read path. On a graph-only install (macOS Intel) there
151
+ # is no vector stack to warm; the daemon serves lexical/graph-only search
152
+ # and ``mcp_v2.search_v2`` degrades on its own, so we skip straight to
153
+ # serving rather than failing on a missing ``sentence_transformers``.
154
+ if self._vector_enabled:
155
+ try:
156
+ self.warm.model()
157
+ except Exception as exc: # noqa: BLE001 — report any load failure, then bail
158
+ print(f"jrag watch: failed to load embedding model: {exc}", file=sys.stderr)
159
+ self._record("error", {"phase": "model_load", "error": repr(exc)})
160
+ self.lock.release()
161
+ return 2
162
+ else:
163
+ print(
164
+ "jrag watch: vector stack unavailable — serving lexical (graph-only) search",
165
+ file=sys.stderr,
166
+ )
141
167
 
142
168
  # 3. Install stop-signal handlers (main thread only).
143
169
  signal.signal(signal.SIGINT, self._on_signal)
@@ -279,6 +305,8 @@ class WatchDaemon:
279
305
  state = dict(self._state)
280
306
  table = Table(title=f"jrag watch (pid {os.getpid()})", show_header=False, box=None)
281
307
  table.add_row("socket", str(state.get("socket")))
308
+ if state.get("mode") == "lexical":
309
+ table.add_row("mode", "lexical (graph-only)")
282
310
  table.add_row("reindex count", str(state.get("reindex_count", 0)))
283
311
  last_kind = state.get("last_reindex_kind")
284
312
  last_at = state.get("last_reindex_at")
@@ -40,7 +40,11 @@ from watchdog.observers import Observer
40
40
  from watchdog.observers.polling import PollingObserver
41
41
 
42
42
  from java_codebase_rag.graph.path_filtering import LayeredIgnore
43
- from java_codebase_rag.pipeline import run_cocoindex_update, run_incremental_graph
43
+ from java_codebase_rag.pipeline import (
44
+ run_cocoindex_update,
45
+ run_incremental_graph,
46
+ vector_stack_installed,
47
+ )
44
48
 
45
49
  if TYPE_CHECKING:
46
50
  from java_codebase_rag.config import ResolvedOperatorConfig
@@ -126,6 +130,10 @@ class SourceWatcher:
126
130
  ) -> None:
127
131
  self.cfg = cfg
128
132
  self.warm = warm
133
+ # Probed once: when False (graph-only install — macOS Intel) the vectors
134
+ # (cocoindex) reindex step is skipped entirely, so the graph reindex still
135
+ # completes and fires ``indexing_done`` instead of bailing on a 127 stub.
136
+ self._vector_enabled = vector_stack_installed()
129
137
  self._debounce_s = max(int(debounce_ms), 1) / 1000.0
130
138
  self._backend = backend
131
139
  # watchdog's PollingObserver takes a float timeout in SECONDS (it flows
@@ -281,7 +289,7 @@ class SourceWatcher:
281
289
  # -- reindex (runs on the debounce worker thread) ------------------------
282
290
 
283
291
  def reindex(self, kinds: set[str]) -> None:
284
- """Run one debounced reindex: vectors always; graph only when java changed.
292
+ """Run one debounced reindex: vectors always (when installed); graph only when java changed.
285
293
 
286
294
  COW lifecycle (design §4.7): the graph subprocess writes the ORIGINAL
287
295
  graph while reads are served from a sidecar copy. ``begin_graph_snapshot``
@@ -290,20 +298,30 @@ class SourceWatcher:
290
298
  graph failure the existing ``.graph_increment_in_progress`` crash marker
291
299
  drives the next full rebuild. Lance needs no snapshot (commits are atomic
292
300
  per version; fresh per-query reads are fine).
301
+
302
+ Graph-only installs (macOS Intel) have no vector stack: the cocoindex
303
+ step is skipped (``vres=None``) so the graph reindex completes and fires
304
+ ``indexing_done`` instead of bailing on a 127 cocoindex-not-found stub.
293
305
  """
294
306
  if not kinds:
295
307
  return
296
308
  kind_list = sorted(kinds)
297
309
  self._emit("indexing_started", {"kinds": kind_list})
298
310
  try:
299
- # Vectors run for every indexed type (java/sql/yaml all flow through cocoindex).
300
- self._emit("vectors", {"kinds": kind_list})
301
- vres = run_cocoindex_update(
302
- self.cfg.subprocess_env(),
303
- full_reprocess=False,
304
- quiet=True,
305
- verbose=False,
306
- )
311
+ # Vectors run for every indexed type (java/sql/yaml all flow through
312
+ # cocoindex) — but only when the vector stack is installed. On a
313
+ # graph-only install cocoindex is absent and the call would return a
314
+ # 127 stub; skipping it keeps ``indexing_done`` reachable.
315
+ if self._vector_enabled:
316
+ self._emit("vectors", {"kinds": kind_list})
317
+ vres = run_cocoindex_update(
318
+ self.cfg.subprocess_env(),
319
+ full_reprocess=False,
320
+ quiet=True,
321
+ verbose=False,
322
+ )
323
+ else:
324
+ vres = None
307
325
 
308
326
  graph_rc = 0
309
327
  if "java" in kinds:
@@ -328,7 +346,7 @@ class SourceWatcher:
328
346
  # is never left dangling. No-ops when no snapshot is active.
329
347
  self.warm.commit_graph_snapshot()
330
348
 
331
- if vres.returncode != 0:
349
+ if vres is not None and vres.returncode != 0:
332
350
  self._emit("error", {"phase": "vectors", "returncode": vres.returncode})
333
351
  return
334
352
  if graph_rc != 0:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: java-codebase-rag
3
- Version: 0.11.1
3
+ Version: 0.11.2
4
4
  Summary: MCP server for semantic + structural search over Java codebases
5
5
  Author: HumanBean17
6
6
  License-Expression: MIT