java-codebase-rag 0.6.2__py3-none-any.whl → 0.6.4__py3-none-any.whl
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.
- ast_java.py +28 -57
- build_ast_graph.py +118 -26
- graph_enrich.py +13 -35
- java_codebase_rag/cli.py +97 -24
- java_codebase_rag/cli_format.py +8 -35
- java_codebase_rag/cli_progress.py +17 -21
- java_codebase_rag/install_data/agents/explorer-rag-enhanced.md +3 -3
- java_codebase_rag/install_data/skills/explore-codebase/SKILL.md +5 -5
- java_codebase_rag/installer.py +180 -43
- java_codebase_rag/lance_optimize.py +125 -51
- java_codebase_rag/pipeline.py +71 -23
- java_codebase_rag/progress.py +570 -0
- {java_codebase_rag-0.6.2.dist-info → java_codebase_rag-0.6.4.dist-info}/METADATA +13 -10
- java_codebase_rag-0.6.4.dist-info/RECORD +34 -0
- java_index_flow_lancedb.py +238 -18
- ladybug_queries.py +45 -51
- mcp_v2.py +9 -17
- path_filtering.py +14 -17
- pr_analysis.py +3 -4
- search_lancedb.py +2 -2
- server.py +75 -9
- java_codebase_rag-0.6.2.dist-info/RECORD +0 -33
- {java_codebase_rag-0.6.2.dist-info → java_codebase_rag-0.6.4.dist-info}/WHEEL +0 -0
- {java_codebase_rag-0.6.2.dist-info → java_codebase_rag-0.6.4.dist-info}/entry_points.txt +0 -0
- {java_codebase_rag-0.6.2.dist-info → java_codebase_rag-0.6.4.dist-info}/licenses/LICENSE +0 -0
- {java_codebase_rag-0.6.2.dist-info → java_codebase_rag-0.6.4.dist-info}/top_level.txt +0 -0
path_filtering.py
CHANGED
|
@@ -342,24 +342,22 @@ class LayeredIgnore:
|
|
|
342
342
|
)
|
|
343
343
|
return mega, GitIgnoreSpec.from_lines(mega), meta
|
|
344
344
|
|
|
345
|
-
def is_ignored(self, path: Path) ->
|
|
346
|
-
"""Return whether ``path`` is ignored
|
|
345
|
+
def is_ignored(self, path: Path) -> bool:
|
|
346
|
+
"""Return whether ``path`` is ignored by any configured layer.
|
|
347
|
+
|
|
348
|
+
Boolean-only fast path for the per-file index walk. It deliberately does
|
|
349
|
+
not compute *which* layer/source last matched: that attribution is
|
|
350
|
+
O(rules²) via :func:`_winning_row` (one ``GitIgnoreSpec`` rebuild per
|
|
351
|
+
rule prefix) and is only needed for ``diagnose-ignore``, so it lives in
|
|
352
|
+
:meth:`diagnose_dict` and is never paid on the hot path.
|
|
353
|
+
"""
|
|
347
354
|
rel = self._rel_project(path)
|
|
348
355
|
if rel is None:
|
|
349
|
-
return False
|
|
350
|
-
mega, spec,
|
|
356
|
+
return False
|
|
357
|
+
mega, spec, _ = self._mega(rel)
|
|
351
358
|
if not mega:
|
|
352
|
-
return False
|
|
353
|
-
|
|
354
|
-
if not ignored:
|
|
355
|
-
return False, None
|
|
356
|
-
src, fp, ln, _pat = _winning_row(rel, mega, meta)
|
|
357
|
-
return True, IgnoreLayer(
|
|
358
|
-
root=self.project_root,
|
|
359
|
-
spec=spec,
|
|
360
|
-
source=src,
|
|
361
|
-
ignore_file=fp,
|
|
362
|
-
)
|
|
359
|
+
return False
|
|
360
|
+
return spec.match_file(rel)
|
|
363
361
|
|
|
364
362
|
def diagnose(self, path: Path) -> str:
|
|
365
363
|
"""Human-readable, multi-line explanation of the ignore decision."""
|
|
@@ -466,7 +464,6 @@ def iter_java_source_files(
|
|
|
466
464
|
if not fn.endswith(".java"):
|
|
467
465
|
continue
|
|
468
466
|
p = Path(dirpath) / fn
|
|
469
|
-
|
|
470
|
-
if ign:
|
|
467
|
+
if ignore_ctx.is_ignored(p):
|
|
471
468
|
continue
|
|
472
469
|
yield p
|
pr_analysis.py
CHANGED
|
@@ -152,7 +152,7 @@ def _resolve_graph_filename(
|
|
|
152
152
|
*,
|
|
153
153
|
ambiguity_notes: list[str] | None = None,
|
|
154
154
|
) -> str | None:
|
|
155
|
-
"""Map a diff path to `Symbol.filename` values stored in
|
|
155
|
+
"""Map a diff path to `Symbol.filename` values stored in LadybugDB."""
|
|
156
156
|
variants = {_strip_ab_prefix(path)}
|
|
157
157
|
for v in list(variants):
|
|
158
158
|
if v.startswith("./"):
|
|
@@ -362,7 +362,7 @@ def _is_public_interface_method(graph: Any, sym: SymbolHit) -> bool:
|
|
|
362
362
|
|
|
363
363
|
|
|
364
364
|
def _route_ids_for_symbol(graph: Any, symbol_id: str) -> list[str]:
|
|
365
|
-
# Note:
|
|
365
|
+
# Note: LadybugDB rejects `ORDER BY r.id` together with `RETURN DISTINCT r.id` (binder loses `r`).
|
|
366
366
|
q = (
|
|
367
367
|
"MATCH (s:Symbol)-[e:EXPOSES]->(r:Route) WHERE s.id = $sid "
|
|
368
368
|
"RETURN r.id AS id ORDER BY id"
|
|
@@ -384,7 +384,6 @@ def compute_risk(graph: Any, changed: list[ChangedSymbol]) -> PrRiskReport:
|
|
|
384
384
|
bump (up to +1.0) after normalization so they influence rank while
|
|
385
385
|
preserving the public scalar contract.
|
|
386
386
|
"""
|
|
387
|
-
notes: list[str] = []
|
|
388
387
|
blast_by: dict[str, int] = {}
|
|
389
388
|
blast_total = 0
|
|
390
389
|
routes: list[str] = []
|
|
@@ -495,7 +494,7 @@ def compute_risk(graph: Any, changed: list[ChangedSymbol]) -> PrRiskReport:
|
|
|
495
494
|
routes_touched=routes,
|
|
496
495
|
risk_score=score,
|
|
497
496
|
risk_band=band,
|
|
498
|
-
notes=
|
|
497
|
+
notes=[],
|
|
499
498
|
)
|
|
500
499
|
|
|
501
500
|
|
search_lancedb.py
CHANGED
|
@@ -677,8 +677,8 @@ def _graph_expand_merge(
|
|
|
677
677
|
expand_depth: int,
|
|
678
678
|
ladybug_path: str | None,
|
|
679
679
|
) -> list[dict]:
|
|
680
|
-
"""Expand vector top-k through the
|
|
681
|
-
# Lazy import so the module works without
|
|
680
|
+
"""Expand vector top-k through the LadybugDB graph and fuse (RRF) with the original list."""
|
|
681
|
+
# Lazy import so the module works without ladybug installed when graph_expand=False.
|
|
682
682
|
try:
|
|
683
683
|
from ladybug_queries import LadybugGraph
|
|
684
684
|
except Exception:
|
server.py
CHANGED
|
@@ -13,9 +13,8 @@ import mcp_v2
|
|
|
13
13
|
from index_common import SBERT_MODEL
|
|
14
14
|
from java_codebase_rag.cli_progress import (
|
|
15
15
|
accumulate_and_relay_subprocess_streams,
|
|
16
|
-
emit_vectors_finish,
|
|
17
|
-
emit_vectors_start,
|
|
18
16
|
)
|
|
17
|
+
from java_codebase_rag.progress import ProgressEvent
|
|
19
18
|
from java_codebase_rag._fdlimit import raise_fd_limit
|
|
20
19
|
from java_codebase_rag.config import (
|
|
21
20
|
cocoindex_subprocess_env_defaults,
|
|
@@ -113,7 +112,44 @@ class ScopeManager:
|
|
|
113
112
|
|
|
114
113
|
def _detect_scope(self) -> str | None:
|
|
115
114
|
from graph_enrich import detect_microservice_from_path
|
|
116
|
-
|
|
115
|
+
|
|
116
|
+
candidate = detect_microservice_from_path(Path.cwd(), self.source_root)
|
|
117
|
+
if candidate is None:
|
|
118
|
+
return None
|
|
119
|
+
# Only auto-scope to a microservice that actually has indexed code.
|
|
120
|
+
# detect_microservice_from_path can mislabel a non-microservice
|
|
121
|
+
# top-level child of source_root — most importantly the config/context
|
|
122
|
+
# directory the MCP server is launched from (no build marker, no
|
|
123
|
+
# source) — via its "first path segment under root" fallback. Scoping
|
|
124
|
+
# every query to such a name yields zero matches, so all tools return
|
|
125
|
+
# empty. A real microservice the operator is working in is, by
|
|
126
|
+
# definition, present in the index, so validating against the indexed
|
|
127
|
+
# set cannot suppress a legitimate scope. When the index is unreadable
|
|
128
|
+
# (empty known set) we keep the detected candidate rather than silently
|
|
129
|
+
# disabling auto-scope on a transient graph error.
|
|
130
|
+
known = self._indexed_microservices()
|
|
131
|
+
if known and candidate not in known:
|
|
132
|
+
return None
|
|
133
|
+
return candidate
|
|
134
|
+
|
|
135
|
+
def _indexed_microservices(self) -> set[str]:
|
|
136
|
+
"""Microservice names that have indexed type symbols.
|
|
137
|
+
|
|
138
|
+
Graph-only source of truth: the graph is always built alongside Lance,
|
|
139
|
+
and a Lance-only index (no graph) is not a supported state. Any failure
|
|
140
|
+
(graph missing, open error, empty index) returns an empty set, which
|
|
141
|
+
``_detect_scope`` treats as "cannot validate — keep detection".
|
|
142
|
+
"""
|
|
143
|
+
try:
|
|
144
|
+
if not LadybugGraph.exists():
|
|
145
|
+
return set()
|
|
146
|
+
# LadybugGraph.get() opens the DB and runs meta(); it can raise
|
|
147
|
+
# (e.g. RuntimeError on ontology-version mismatch). Caught here ->
|
|
148
|
+
# empty set -> _detect_scope keeps the detected scope.
|
|
149
|
+
counts = LadybugGraph.get().microservice_counts()
|
|
150
|
+
return {name for name in counts if name}
|
|
151
|
+
except Exception:
|
|
152
|
+
return set()
|
|
117
153
|
|
|
118
154
|
def _log_detection(self) -> None:
|
|
119
155
|
if self.default_scope:
|
|
@@ -270,10 +306,20 @@ def list_code_index_tables_payload() -> IndexInfoOutput:
|
|
|
270
306
|
)
|
|
271
307
|
|
|
272
308
|
|
|
273
|
-
async def run_refresh_pipeline(
|
|
309
|
+
async def run_refresh_pipeline(
|
|
310
|
+
*,
|
|
311
|
+
quiet: bool = False,
|
|
312
|
+
verbose: bool = True,
|
|
313
|
+
on_progress=None,
|
|
314
|
+
on_progress_console: object | None = None,
|
|
315
|
+
) -> RefreshIndexOutput:
|
|
274
316
|
root = _project_root()
|
|
275
317
|
cocoindex_bin = Path(sys.executable).parent / "cocoindex"
|
|
276
318
|
if not cocoindex_bin.is_file():
|
|
319
|
+
# 127 pre-spawn: emit a terminal failed vectors event so the renderer's
|
|
320
|
+
# task doesn't hang at running (matches the sync pipeline path).
|
|
321
|
+
if on_progress is not None:
|
|
322
|
+
on_progress(ProgressEvent(kind="vectors", phase=None, pass_=None, done=None, total=None, status="failed", elapsed_s=None))
|
|
277
323
|
return RefreshIndexOutput(
|
|
278
324
|
success=False,
|
|
279
325
|
message=f"cocoindex not found next to Python: {cocoindex_bin}",
|
|
@@ -286,6 +332,8 @@ async def run_refresh_pipeline(*, quiet: bool = False, verbose: bool = True) ->
|
|
|
286
332
|
if fallback.is_file():
|
|
287
333
|
flow_path = fallback
|
|
288
334
|
else:
|
|
335
|
+
if on_progress is not None:
|
|
336
|
+
on_progress(ProgressEvent(kind="vectors", phase=None, pass_=None, done=None, total=None, status="failed", elapsed_s=None))
|
|
289
337
|
return RefreshIndexOutput(
|
|
290
338
|
success=False,
|
|
291
339
|
message=f"java_index_flow_lancedb.py not found under {root} nor {bundle_dir}",
|
|
@@ -308,13 +356,14 @@ async def run_refresh_pipeline(*, quiet: bool = False, verbose: bool = True) ->
|
|
|
308
356
|
)
|
|
309
357
|
out_b, err_b = await proc.communicate()
|
|
310
358
|
except Exception as exc:
|
|
359
|
+
if on_progress is not None:
|
|
360
|
+
on_progress(ProgressEvent(kind="vectors", phase=None, pass_=None, done=None, total=None, status="failed", elapsed_s=None))
|
|
311
361
|
return RefreshIndexOutput(
|
|
312
362
|
success=False,
|
|
313
363
|
message=f"spawn failed: {exc!s}",
|
|
314
364
|
phases_run=[],
|
|
315
365
|
)
|
|
316
366
|
else:
|
|
317
|
-
emit_vectors_start()
|
|
318
367
|
t0 = time.perf_counter()
|
|
319
368
|
code_c = -1
|
|
320
369
|
try:
|
|
@@ -329,16 +378,30 @@ async def run_refresh_pipeline(*, quiet: bool = False, verbose: bool = True) ->
|
|
|
329
378
|
stdout=asyncio.subprocess.PIPE,
|
|
330
379
|
stderr=asyncio.subprocess.PIPE,
|
|
331
380
|
)
|
|
332
|
-
|
|
381
|
+
# The vectors task is fed by the child's per-file ticks + the
|
|
382
|
+
# approximate total line, parsed by the ProgressRelay inside the
|
|
383
|
+
# async drain and routed to on_progress.
|
|
384
|
+
out_b, err_b = await accumulate_and_relay_subprocess_streams(
|
|
385
|
+
proc, relay=True, verbose=verbose,
|
|
386
|
+
on_progress=on_progress, on_progress_console=on_progress_console,
|
|
387
|
+
)
|
|
333
388
|
code_c = proc.returncode if proc.returncode is not None else -1
|
|
334
389
|
except Exception as exc:
|
|
390
|
+
if on_progress is not None:
|
|
391
|
+
on_progress(ProgressEvent(kind="vectors", phase=None, pass_=None, done=None, total=None, status="failed", elapsed_s=None))
|
|
335
392
|
return RefreshIndexOutput(
|
|
336
393
|
success=False,
|
|
337
394
|
message=f"spawn failed: {exc!s}",
|
|
338
395
|
phases_run=[],
|
|
339
396
|
)
|
|
340
397
|
finally:
|
|
341
|
-
|
|
398
|
+
# The parent emits the terminal vectors event (the flow can't — no
|
|
399
|
+
# "all files done" hook). Drives clamp-on-completion + phase
|
|
400
|
+
# transition to Optimize.
|
|
401
|
+
if on_progress is not None:
|
|
402
|
+
elapsed = time.perf_counter() - t0
|
|
403
|
+
status = "done" if code_c == 0 else "failed"
|
|
404
|
+
on_progress(ProgressEvent(kind="vectors", phase=None, pass_=None, done=None, total=None, status=status, elapsed_s=elapsed))
|
|
342
405
|
assert proc is not None
|
|
343
406
|
out = out_b.decode(errors="replace")
|
|
344
407
|
err = err_b.decode(errors="replace")
|
|
@@ -366,7 +429,7 @@ async def run_refresh_pipeline(*, quiet: bool = False, verbose: bool = True) ->
|
|
|
366
429
|
idx_dir = Path(idx_raw)
|
|
367
430
|
else:
|
|
368
431
|
idx_dir = (root / ".java-codebase-rag").resolve()
|
|
369
|
-
await optimize_lance_tables(idx_dir, quiet=quiet)
|
|
432
|
+
await optimize_lance_tables(idx_dir, quiet=quiet, on_progress=on_progress)
|
|
370
433
|
except Exception as exc:
|
|
371
434
|
optimize_error = f"lance optimize failed: {exc}"
|
|
372
435
|
print(f"java-codebase-rag: {optimize_error}", file=sys.stderr)
|
|
@@ -394,7 +457,10 @@ async def run_refresh_pipeline(*, quiet: bool = False, verbose: bool = True) ->
|
|
|
394
457
|
if quiet:
|
|
395
458
|
gout_b, gerr_b = await gproc.communicate()
|
|
396
459
|
else:
|
|
397
|
-
gout_b, gerr_b = await accumulate_and_relay_subprocess_streams(
|
|
460
|
+
gout_b, gerr_b = await accumulate_and_relay_subprocess_streams(
|
|
461
|
+
gproc, relay=True, verbose=verbose,
|
|
462
|
+
on_progress=on_progress, on_progress_console=on_progress_console,
|
|
463
|
+
)
|
|
398
464
|
graph_code = gproc.returncode
|
|
399
465
|
graph_out = gout_b.decode(errors="replace")
|
|
400
466
|
graph_err = gerr_b.decode(errors="replace")
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
ast_java.py,sha256=NQgZzstbsMq-PdowoD6r_ixJKxEEFzTP9xUzqDpiXeU,99661
|
|
2
|
-
brownfield_events.py,sha256=yxXkKDgMb3VPtaiakGzncHM_EGnda8xIue6w90yYp8s,2055
|
|
3
|
-
build_ast_graph.py,sha256=OKigswkUmWwUAKXXRNH4zplw2VonIdWUWzVjC-t5roo,152893
|
|
4
|
-
chunk_heuristics.py,sha256=aQk2NOKxzUdqoUAJUO3G3LE0MN_bYZWNLQ0tkmj5uts,1813
|
|
5
|
-
graph_enrich.py,sha256=POT4LwSkTsrjUmP67bsm2UezUam70cunuPDYDh-v1Bs,63332
|
|
6
|
-
index_common.py,sha256=HT6FKHFJ084eFvd3fR1j8z8gf4eWoPHVW8GXLpw464I,285
|
|
7
|
-
java_index_flow_lancedb.py,sha256=MH9iTNF6HDHDTt5Jn7TOVE5hQ4WUPNt7PlQoh1tuh9o,13212
|
|
8
|
-
java_index_v1_common.py,sha256=nF1KrSqboF_RRvWerG9knRRFmWwsrG_CvhgnsoZ8KqA,1154
|
|
9
|
-
java_ontology.py,sha256=71bCLDNvMy0SpZPzSR5apJ0qJXNd6y5ggkLdBEw_PFo,16682
|
|
10
|
-
ladybug_queries.py,sha256=912j9VAYDjcU4ReVorWQ6R4DZl0tteKic-Pqu0jyBS0,90837
|
|
11
|
-
mcp_hints.py,sha256=3swh05LSiWur3tm3-yssndBsLxIxFhy501kBtJI8jJ0,42509
|
|
12
|
-
mcp_v2.py,sha256=o94GJI7j6dLJDIA3R_1ZiQhjzQfMAEW3etdeZYnHOUc,80637
|
|
13
|
-
path_filtering.py,sha256=-oX16SYLWYwX9pcV1fu3vbVTIhY1GzFflT7J1E2tqPY,17122
|
|
14
|
-
pr_analysis.py,sha256=3-5L8_G5XupdJsl9RN73Lq-ejPoK11B3m_VzAx2fGG8,18413
|
|
15
|
-
search_lancedb.py,sha256=scG6HBUrsgIeSWFrGcLcGdhWv1qODOx4JOBMAlLDY_E,36793
|
|
16
|
-
server.py,sha256=Js3XDpV7ThAtj352StH6QdhHutf1D5qUkbR-8k3jO8g,31303
|
|
17
|
-
java_codebase_rag/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
18
|
-
java_codebase_rag/_fdlimit.py,sha256=WroFdfSNbcriKok6q8znTf74dqlznxea_1Fd5bHl_3o,1930
|
|
19
|
-
java_codebase_rag/cli.py,sha256=HkzCP8-G3WlCzoXcVCI2K3forDOMpTmUSoxgx3jbKk4,34774
|
|
20
|
-
java_codebase_rag/cli_format.py,sha256=arU7P9W6Fvm7X_wzR1wJ8EfyxK1rDP_ESEhdA0ub4Mo,2579
|
|
21
|
-
java_codebase_rag/cli_progress.py,sha256=9jCqEagYOXs32SYVA31_sOCrONvYy7cl1CrdBD2Pg44,3168
|
|
22
|
-
java_codebase_rag/config.py,sha256=bfwYI4R8PU9YV_M4r8-03iaUZ_0TW-qN_NuhIsDXy2M,18769
|
|
23
|
-
java_codebase_rag/installer.py,sha256=sE0l85K_o291PdpF1vpesefR9VgdvvVeARXrpTxa30A,46689
|
|
24
|
-
java_codebase_rag/lance_optimize.py,sha256=MzACYlgwxmkJCK64qQLyIAdizSq5BARqaMYSZONlc1I,6069
|
|
25
|
-
java_codebase_rag/pipeline.py,sha256=UcgluFAW9Ghnas8u40x45bVic0mQv6rjzcliDKsnYJI,11936
|
|
26
|
-
java_codebase_rag/install_data/agents/explorer-rag-enhanced.md,sha256=APl9d-No12qZNZLjU7mwNRwxHIgnT3ZtQZiD4clWlyU,14413
|
|
27
|
-
java_codebase_rag/install_data/skills/explore-codebase/SKILL.md,sha256=pIM-Xdwq_fXkhhBJCdb-fA2nes5c_mMPcdUXb7Adyxo,12040
|
|
28
|
-
java_codebase_rag-0.6.2.dist-info/licenses/LICENSE,sha256=gxvtiHtuviR_q8ZAjWw-QTcF3DyPzg6ZY-lQrr8OPpw,1068
|
|
29
|
-
java_codebase_rag-0.6.2.dist-info/METADATA,sha256=X92kaZ5TbEacz0sznWtUtpYEJvBdWIXX0s8MlqOeRyg,16934
|
|
30
|
-
java_codebase_rag-0.6.2.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
31
|
-
java_codebase_rag-0.6.2.dist-info/entry_points.txt,sha256=wsPZwot0Ui4JI3TIgW8LcbN8bNtKFbwQAlHAAJXfYgQ,117
|
|
32
|
-
java_codebase_rag-0.6.2.dist-info/top_level.txt,sha256=syQgi8XPBwY2ws_NZ1uRCxTf_s41NpshwEHNdcdnk3A,245
|
|
33
|
-
java_codebase_rag-0.6.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|