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
java_codebase_rag/pipeline.py
CHANGED
|
@@ -9,10 +9,11 @@ import sys
|
|
|
9
9
|
import threading
|
|
10
10
|
import time
|
|
11
11
|
from pathlib import Path
|
|
12
|
+
from typing import Callable
|
|
12
13
|
|
|
13
|
-
from java_codebase_rag.cli_format import
|
|
14
|
-
from java_codebase_rag.cli_progress import emit_vectors_finish, emit_vectors_start
|
|
14
|
+
from java_codebase_rag.cli_format import is_noise_line
|
|
15
15
|
from java_codebase_rag.config import cocoindex_subprocess_env_defaults
|
|
16
|
+
from java_codebase_rag.progress import ProgressEvent, ProgressRelay, make_relay
|
|
16
17
|
|
|
17
18
|
COCOINDEX_TARGET = "java_index_flow_lancedb.py:JavaCodeIndexLance"
|
|
18
19
|
|
|
@@ -66,11 +67,26 @@ def _popen_capturing_stderr(
|
|
|
66
67
|
proc: subprocess.Popen[bytes],
|
|
67
68
|
*,
|
|
68
69
|
verbose: bool = True,
|
|
70
|
+
on_progress: Callable[[ProgressEvent], None] | None = None,
|
|
71
|
+
on_progress_console: object | None = None,
|
|
69
72
|
) -> tuple[str, str, int]:
|
|
70
|
-
"""Capture stdout/stderr; relay stderr through noise filter (or verbatim in verbose mode).
|
|
73
|
+
"""Capture stdout/stderr; relay stderr through noise filter (or verbatim in verbose mode).
|
|
74
|
+
|
|
75
|
+
When ``on_progress`` is set, stderr is drained through a :class:`ProgressRelay`
|
|
76
|
+
instead of the bare ``_LineFilter``: progress lines are parsed first, routed to
|
|
77
|
+
``on_progress``, and suppressed from the relay; non-progress lines follow the
|
|
78
|
+
relay's routing (``console.print`` while a Live region is up via
|
|
79
|
+
``on_progress_console``, raw ``buffer.write`` in verbose mode).
|
|
80
|
+
"""
|
|
71
81
|
out_buf = bytearray()
|
|
72
82
|
err_buf = bytearray()
|
|
73
|
-
|
|
83
|
+
if on_progress is not None:
|
|
84
|
+
relay = make_relay(
|
|
85
|
+
on_progress, console=on_progress_console, verbose=verbose
|
|
86
|
+
)
|
|
87
|
+
filt: _LineFilter | ProgressRelay | None = relay
|
|
88
|
+
else:
|
|
89
|
+
filt = _LineFilter() if not verbose else None
|
|
74
90
|
|
|
75
91
|
def drain_out() -> None:
|
|
76
92
|
assert proc.stdout is not None
|
|
@@ -112,6 +128,8 @@ def run_cocoindex_update(
|
|
|
112
128
|
quiet: bool,
|
|
113
129
|
verbose: bool = True,
|
|
114
130
|
lance_project_root: Path | None = None,
|
|
131
|
+
on_progress: Callable[[ProgressEvent], None] | None = None,
|
|
132
|
+
on_progress_console: object | None = None,
|
|
115
133
|
) -> subprocess.CompletedProcess[str]:
|
|
116
134
|
result = _run_cocoindex_update_impl(
|
|
117
135
|
env,
|
|
@@ -119,19 +137,25 @@ def run_cocoindex_update(
|
|
|
119
137
|
quiet=quiet,
|
|
120
138
|
verbose=verbose,
|
|
121
139
|
lance_project_root=lance_project_root,
|
|
140
|
+
on_progress=on_progress,
|
|
141
|
+
on_progress_console=on_progress_console,
|
|
122
142
|
)
|
|
123
143
|
# After cocoindex returns exit 0 there are no concurrent writers, so this
|
|
124
144
|
# is the safe window to compact the Lance tables. The flow disabled its
|
|
125
145
|
# in-flight background optimize (see java_index_flow_lancedb.py), making
|
|
126
146
|
# this serialized pass the sole optimizer. Optimize failure does not flip
|
|
127
147
|
# the cocoindex CompletedProcess (a successful index is still usable, just
|
|
128
|
-
# not compacted); the outcome is logged to stderr only.
|
|
148
|
+
# not compacted); the outcome is logged to stderr only. Thread the
|
|
149
|
+
# in-process on_progress so the optimize phase renders via the same
|
|
150
|
+
# renderer (the flow cannot emit it — it runs in the child).
|
|
129
151
|
if result.returncode == 0:
|
|
130
|
-
_maybe_run_serialized_optimize(env, quiet=quiet)
|
|
152
|
+
_maybe_run_serialized_optimize(env, quiet=quiet, on_progress=on_progress)
|
|
131
153
|
return result
|
|
132
154
|
|
|
133
155
|
|
|
134
|
-
def _maybe_run_serialized_optimize(
|
|
156
|
+
def _maybe_run_serialized_optimize(
|
|
157
|
+
env: dict[str, str], *, quiet: bool, on_progress: Callable | None = None
|
|
158
|
+
) -> None:
|
|
135
159
|
"""Resolve the index dir from *env* and run the serialized Lance optimize.
|
|
136
160
|
|
|
137
161
|
The flow's lifespan reads ``JAVA_CODEBASE_RAG_INDEX_DIR`` (set by the CLI /
|
|
@@ -150,7 +174,7 @@ def _maybe_run_serialized_optimize(env: dict[str, str], *, quiet: bool) -> None:
|
|
|
150
174
|
try:
|
|
151
175
|
from java_codebase_rag.lance_optimize import optimize_lance_tables
|
|
152
176
|
|
|
153
|
-
asyncio.run(optimize_lance_tables(Path(idx_raw), quiet=quiet))
|
|
177
|
+
asyncio.run(optimize_lance_tables(Path(idx_raw), quiet=quiet, on_progress=on_progress))
|
|
154
178
|
except Exception as exc:
|
|
155
179
|
# Never crash the CLI on an optimize failure — surface on stderr only.
|
|
156
180
|
print(f"java-codebase-rag: optimize failed: {exc}", file=sys.stderr)
|
|
@@ -163,9 +187,15 @@ def _run_cocoindex_update_impl(
|
|
|
163
187
|
quiet: bool,
|
|
164
188
|
verbose: bool = True,
|
|
165
189
|
lance_project_root: Path | None = None,
|
|
190
|
+
on_progress: Callable[[ProgressEvent], None] | None = None,
|
|
191
|
+
on_progress_console: object | None = None,
|
|
166
192
|
) -> subprocess.CompletedProcess[str]:
|
|
167
193
|
exe = cocoindex_bin()
|
|
168
194
|
if not exe.is_file():
|
|
195
|
+
# 127 pre-spawn stub: never mark the vectors task running — emit a
|
|
196
|
+
# terminal failed event so the renderer doesn't leave it hung.
|
|
197
|
+
if on_progress is not None:
|
|
198
|
+
on_progress(ProgressEvent(kind="vectors", phase=None, pass_=None, done=None, total=None, status="failed", elapsed_s=None))
|
|
169
199
|
return subprocess.CompletedProcess(
|
|
170
200
|
args=[str(exe)],
|
|
171
201
|
returncode=127,
|
|
@@ -175,6 +205,8 @@ def _run_cocoindex_update_impl(
|
|
|
175
205
|
bd = bundle_dir()
|
|
176
206
|
flow = bd / "java_index_flow_lancedb.py"
|
|
177
207
|
if not flow.is_file():
|
|
208
|
+
if on_progress is not None:
|
|
209
|
+
on_progress(ProgressEvent(kind="vectors", phase=None, pass_=None, done=None, total=None, status="failed", elapsed_s=None))
|
|
178
210
|
return subprocess.CompletedProcess(
|
|
179
211
|
args=[],
|
|
180
212
|
returncode=126,
|
|
@@ -201,17 +233,10 @@ def _run_cocoindex_update_impl(
|
|
|
201
233
|
text=True,
|
|
202
234
|
)
|
|
203
235
|
|
|
204
|
-
emit_progress = lance_project_root is not None
|
|
205
|
-
use_spinner = emit_progress and stderr_is_tty()
|
|
206
|
-
if emit_progress and not use_spinner:
|
|
207
|
-
emit_vectors_start()
|
|
208
|
-
spinner: Spinner | None = None
|
|
209
|
-
if use_spinner:
|
|
210
|
-
spinner = Spinner("[vectors] running · cocoindex update")
|
|
211
|
-
spinner.start()
|
|
212
236
|
t0 = time.perf_counter()
|
|
213
237
|
code = -1
|
|
214
238
|
out_s, err_s = "", ""
|
|
239
|
+
proc: subprocess.Popen[bytes] | None = None
|
|
215
240
|
try:
|
|
216
241
|
proc = subprocess.Popen(
|
|
217
242
|
cmd,
|
|
@@ -221,12 +246,27 @@ def _run_cocoindex_update_impl(
|
|
|
221
246
|
stderr=subprocess.PIPE,
|
|
222
247
|
bufsize=0,
|
|
223
248
|
)
|
|
224
|
-
|
|
249
|
+
# Vectors task is marked running only AFTER Popen succeeds — the flow's
|
|
250
|
+
# per-file ticks + approximate total stream in from the child via the
|
|
251
|
+
# relay (parsed by ProgressRelay, routed to on_progress).
|
|
252
|
+
out_s, err_s, code = _popen_capturing_stderr(
|
|
253
|
+
proc, verbose=verbose, on_progress=on_progress, on_progress_console=on_progress_console
|
|
254
|
+
)
|
|
225
255
|
finally:
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
256
|
+
# The flow cannot emit the terminal vectors event (no "all files done"
|
|
257
|
+
# hook in cocoindex flows), so the PARENT emits it here based on the
|
|
258
|
+
# cocoindex exit code. This drives clamp-on-completion + the phase
|
|
259
|
+
# transition to Optimize. Emitted even on a spawn failure (code stays
|
|
260
|
+
# -1 → failed) so the renderer's task never hangs at running.
|
|
261
|
+
if on_progress is not None:
|
|
262
|
+
elapsed = time.perf_counter() - t0
|
|
263
|
+
status = "done" if code == 0 else "failed"
|
|
264
|
+
on_progress(
|
|
265
|
+
ProgressEvent(
|
|
266
|
+
kind="vectors", phase=None, pass_=None, done=None, total=None,
|
|
267
|
+
status=status, elapsed_s=elapsed,
|
|
268
|
+
)
|
|
269
|
+
)
|
|
230
270
|
return subprocess.CompletedProcess(args=cmd, returncode=code, stdout=out_s, stderr=err_s)
|
|
231
271
|
|
|
232
272
|
|
|
@@ -259,6 +299,8 @@ def run_build_ast_graph(
|
|
|
259
299
|
verbose: bool,
|
|
260
300
|
quiet: bool = False,
|
|
261
301
|
env: dict[str, str] | None = None,
|
|
302
|
+
on_progress: Callable[[ProgressEvent], None] | None = None,
|
|
303
|
+
on_progress_console: object | None = None,
|
|
262
304
|
) -> subprocess.CompletedProcess[str]:
|
|
263
305
|
builder = bundle_dir() / "build_ast_graph.py"
|
|
264
306
|
if not builder.is_file():
|
|
@@ -297,7 +339,9 @@ def run_build_ast_graph(
|
|
|
297
339
|
stderr=subprocess.PIPE,
|
|
298
340
|
bufsize=0,
|
|
299
341
|
)
|
|
300
|
-
out_s, err_s, code = _popen_capturing_stderr(
|
|
342
|
+
out_s, err_s, code = _popen_capturing_stderr(
|
|
343
|
+
proc, verbose=verbose, on_progress=on_progress, on_progress_console=on_progress_console
|
|
344
|
+
)
|
|
301
345
|
if not verbose:
|
|
302
346
|
from java_codebase_rag.cli_format import bold_cyan, styled_check, styled_cross
|
|
303
347
|
marker = styled_check() if code == 0 else styled_cross()
|
|
@@ -312,6 +356,8 @@ def run_incremental_graph(
|
|
|
312
356
|
verbose: bool,
|
|
313
357
|
quiet: bool = False,
|
|
314
358
|
env: dict[str, str] | None = None,
|
|
359
|
+
on_progress: Callable[[ProgressEvent], None] | None = None,
|
|
360
|
+
on_progress_console: object | None = None,
|
|
315
361
|
) -> subprocess.CompletedProcess[str]:
|
|
316
362
|
"""Run incremental graph rebuild by passing --incremental flag to build_ast_graph.py."""
|
|
317
363
|
builder = bundle_dir() / "build_ast_graph.py"
|
|
@@ -352,7 +398,9 @@ def run_incremental_graph(
|
|
|
352
398
|
stderr=subprocess.PIPE,
|
|
353
399
|
bufsize=0,
|
|
354
400
|
)
|
|
355
|
-
out_s, err_s, code = _popen_capturing_stderr(
|
|
401
|
+
out_s, err_s, code = _popen_capturing_stderr(
|
|
402
|
+
proc, verbose=verbose, on_progress=on_progress, on_progress_console=on_progress_console
|
|
403
|
+
)
|
|
356
404
|
if not verbose:
|
|
357
405
|
from java_codebase_rag.cli_format import bold_cyan, styled_check, styled_cross
|
|
358
406
|
marker = styled_check() if code == 0 else styled_cross()
|