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/cli.py
CHANGED
|
@@ -131,15 +131,41 @@ def _run_with_pipeline_progress(
|
|
|
131
131
|
cfg: ResolvedOperatorConfig,
|
|
132
132
|
*,
|
|
133
133
|
quiet: bool,
|
|
134
|
-
|
|
134
|
+
verbose: bool = False,
|
|
135
|
+
work: Callable[["PipelineProgress | None"], int],
|
|
135
136
|
) -> int:
|
|
136
|
-
|
|
137
|
-
|
|
137
|
+
"""Run ``work`` under the unified progress renderer (default TTY mode only).
|
|
138
|
+
|
|
139
|
+
``work`` receives a :class:`PipelineProgress` whose ``on_progress`` callback
|
|
140
|
+
should be forwarded to the graph/vectors pipeline helpers so their
|
|
141
|
+
``JCIRAG_PROGRESS`` events feed the renderer. In ``--quiet`` or ``--verbose``
|
|
142
|
+
mode the context is ``None`` (no Live region: quiet is silent, verbose
|
|
143
|
+
raw-relays subprocess output).
|
|
144
|
+
"""
|
|
145
|
+
if quiet or verbose:
|
|
146
|
+
return int(work(None))
|
|
147
|
+
from java_codebase_rag.progress import build_index_progress_context
|
|
148
|
+
|
|
149
|
+
# PR-3 owns all three tasks in order: Vectors → Optimize → Graph. The vectors
|
|
150
|
+
# task is fed by the cocoindex child's per-file ticks + approximate total
|
|
151
|
+
# (subprocess transport, parsed by ProgressRelay); the optimize task is fed
|
|
152
|
+
# in-process by lance_optimize; the graph task is fed by the build_ast_graph
|
|
153
|
+
# child (subprocess transport). A task only becomes visible/running once its
|
|
154
|
+
# first event arrives.
|
|
155
|
+
renderer, on_progress, console = build_index_progress_context()
|
|
156
|
+
progress = PipelineProgress(renderer=renderer)
|
|
157
|
+
progress.on_progress = on_progress
|
|
158
|
+
progress.console = console
|
|
159
|
+
|
|
138
160
|
_pipeline_header(subcommand, cfg)
|
|
139
161
|
t0 = time.perf_counter()
|
|
140
162
|
code = 0
|
|
163
|
+
# start() always flips _started (the non-TTY fallback is a no-op for Live but
|
|
164
|
+
# still needs the flag so apply() routes to the concise-line printer). The
|
|
165
|
+
# TTY Live region is entered inside start() only when the console is a TTY.
|
|
166
|
+
renderer.start()
|
|
141
167
|
try:
|
|
142
|
-
code = int(work())
|
|
168
|
+
code = int(work(progress))
|
|
143
169
|
return code
|
|
144
170
|
except BaseException as exc:
|
|
145
171
|
# Keep footer aligned with process outcome (main maps unhandled Exception -> exit 2).
|
|
@@ -155,9 +181,26 @@ def _run_with_pipeline_progress(
|
|
|
155
181
|
code = 2
|
|
156
182
|
raise
|
|
157
183
|
finally:
|
|
184
|
+
renderer.stop()
|
|
158
185
|
_pipeline_footer(subcommand, t0, code)
|
|
159
186
|
|
|
160
187
|
|
|
188
|
+
class PipelineProgress:
|
|
189
|
+
"""Progress context handed to ``work``: the renderer + a ready ``on_progress``.
|
|
190
|
+
|
|
191
|
+
``on_progress``/``console`` are wired by :func:`_run_with_pipeline_progress`
|
|
192
|
+
and should be forwarded to the pipeline helpers' ``on_progress`` /
|
|
193
|
+
``on_progress_console`` parameters. ``console`` is the renderer's stderr
|
|
194
|
+
``rich.Console`` so the subprocess drain routes non-progress lines through
|
|
195
|
+
``console.print`` while the Live region is up (single-writer invariant).
|
|
196
|
+
"""
|
|
197
|
+
|
|
198
|
+
def __init__(self, *, renderer: "object | None") -> None:
|
|
199
|
+
self.renderer = renderer
|
|
200
|
+
self.on_progress: "Callable | None" = None
|
|
201
|
+
self.console: "object | None" = None
|
|
202
|
+
|
|
203
|
+
|
|
161
204
|
def _jsonable(value: Any) -> Any:
|
|
162
205
|
if hasattr(value, "model_dump"):
|
|
163
206
|
return value.model_dump()
|
|
@@ -208,7 +251,7 @@ def _startup_hints(cfg: ResolvedOperatorConfig) -> None:
|
|
|
208
251
|
|
|
209
252
|
def _add_index_embedding_flags(p: argparse.ArgumentParser) -> None:
|
|
210
253
|
p.add_argument("--source-root", type=str, default=None, help="Java repository root (default: cwd)")
|
|
211
|
-
p.add_argument("--index-dir", type=str, default=None, help="Index directory (Lance +
|
|
254
|
+
p.add_argument("--index-dir", type=str, default=None, help="Index directory (Lance + LadybugDB + cocoindex state)")
|
|
212
255
|
p.add_argument("--embedding-model", type=str, default=None, help="Override SBERT_MODEL / YAML embedding.model")
|
|
213
256
|
p.add_argument("--embedding-device", type=str, default=None, help="Override SBERT_DEVICE / YAML embedding.device")
|
|
214
257
|
|
|
@@ -266,7 +309,7 @@ def _cmd_init(args: argparse.Namespace) -> int:
|
|
|
266
309
|
return 2
|
|
267
310
|
cfg.index_dir.mkdir(parents=True, exist_ok=True)
|
|
268
311
|
|
|
269
|
-
def work() -> int:
|
|
312
|
+
def work(progress: "PipelineProgress | None") -> int:
|
|
270
313
|
env = cfg.subprocess_env()
|
|
271
314
|
verbose = bool(args.verbose)
|
|
272
315
|
coco = run_cocoindex_update(
|
|
@@ -275,6 +318,8 @@ def _cmd_init(args: argparse.Namespace) -> int:
|
|
|
275
318
|
quiet=bool(args.quiet),
|
|
276
319
|
verbose=verbose,
|
|
277
320
|
lance_project_root=None if args.quiet else cfg.source_root,
|
|
321
|
+
on_progress=progress.on_progress if progress is not None else None,
|
|
322
|
+
on_progress_console=progress.console if progress is not None else None,
|
|
278
323
|
)
|
|
279
324
|
if coco.returncode != 0:
|
|
280
325
|
_emit(
|
|
@@ -295,6 +340,8 @@ def _cmd_init(args: argparse.Namespace) -> int:
|
|
|
295
340
|
verbose=verbose,
|
|
296
341
|
quiet=bool(args.quiet),
|
|
297
342
|
env=env,
|
|
343
|
+
on_progress=progress.on_progress if progress is not None else None,
|
|
344
|
+
on_progress_console=progress.console if progress is not None else None,
|
|
298
345
|
)
|
|
299
346
|
if g.returncode != 0:
|
|
300
347
|
_emit(
|
|
@@ -310,7 +357,9 @@ def _cmd_init(args: argparse.Namespace) -> int:
|
|
|
310
357
|
_emit({"success": True, "message": "init completed"})
|
|
311
358
|
return 0
|
|
312
359
|
|
|
313
|
-
return _run_with_pipeline_progress(
|
|
360
|
+
return _run_with_pipeline_progress(
|
|
361
|
+
"init", cfg, quiet=bool(args.quiet), verbose=bool(args.verbose), work=work
|
|
362
|
+
)
|
|
314
363
|
|
|
315
364
|
|
|
316
365
|
def _cmd_increment(args: argparse.Namespace) -> int:
|
|
@@ -323,7 +372,7 @@ def _cmd_increment(args: argparse.Namespace) -> int:
|
|
|
323
372
|
if vectors_only:
|
|
324
373
|
_emit_increment_ladybug_warning()
|
|
325
374
|
|
|
326
|
-
def work() -> int:
|
|
375
|
+
def work(progress: "PipelineProgress | None") -> int:
|
|
327
376
|
env = cfg.subprocess_env()
|
|
328
377
|
coco = run_cocoindex_update(
|
|
329
378
|
env,
|
|
@@ -331,6 +380,8 @@ def _cmd_increment(args: argparse.Namespace) -> int:
|
|
|
331
380
|
quiet=bool(args.quiet),
|
|
332
381
|
verbose=bool(args.verbose),
|
|
333
382
|
lance_project_root=None if args.quiet else cfg.source_root,
|
|
383
|
+
on_progress=progress.on_progress if progress is not None else None,
|
|
384
|
+
on_progress_console=progress.console if progress is not None else None,
|
|
334
385
|
)
|
|
335
386
|
if coco.returncode != 0:
|
|
336
387
|
_emit(
|
|
@@ -356,6 +407,8 @@ def _cmd_increment(args: argparse.Namespace) -> int:
|
|
|
356
407
|
verbose=bool(args.verbose),
|
|
357
408
|
quiet=bool(args.quiet),
|
|
358
409
|
env=env,
|
|
410
|
+
on_progress=progress.on_progress if progress is not None else None,
|
|
411
|
+
on_progress_console=progress.console if progress is not None else None,
|
|
359
412
|
)
|
|
360
413
|
|
|
361
414
|
# Check if incremental fell back to full rebuild
|
|
@@ -389,7 +442,9 @@ def _cmd_increment(args: argparse.Namespace) -> int:
|
|
|
389
442
|
_emit({"success": True, "message": "increment completed (Lance + graph updated)"})
|
|
390
443
|
return 0
|
|
391
444
|
|
|
392
|
-
return _run_with_pipeline_progress(
|
|
445
|
+
return _run_with_pipeline_progress(
|
|
446
|
+
"increment", cfg, quiet=bool(args.quiet), verbose=bool(args.verbose), work=work
|
|
447
|
+
)
|
|
393
448
|
|
|
394
449
|
|
|
395
450
|
def _cmd_reprocess(args: argparse.Namespace) -> int:
|
|
@@ -397,14 +452,18 @@ def _cmd_reprocess(args: argparse.Namespace) -> int:
|
|
|
397
452
|
_startup_hints(cfg)
|
|
398
453
|
cfg.apply_to_os_environ()
|
|
399
454
|
|
|
400
|
-
def work() -> int:
|
|
455
|
+
def work(progress: "PipelineProgress | None") -> int:
|
|
401
456
|
env = cfg.subprocess_env()
|
|
402
457
|
verbose = bool(args.verbose)
|
|
403
458
|
vectors_only = bool(getattr(args, "vectors_only", False))
|
|
404
459
|
graph_only = bool(getattr(args, "graph_only", False))
|
|
405
460
|
|
|
406
461
|
if vectors_only:
|
|
407
|
-
coco = run_cocoindex_update(
|
|
462
|
+
coco = run_cocoindex_update(
|
|
463
|
+
env, full_reprocess=True, quiet=bool(args.quiet), verbose=verbose,
|
|
464
|
+
on_progress=progress.on_progress if progress is not None else None,
|
|
465
|
+
on_progress_console=progress.console if progress is not None else None,
|
|
466
|
+
)
|
|
408
467
|
if _is_cocoindex_preflight_blocker(coco):
|
|
409
468
|
payload: dict[str, Any] = {
|
|
410
469
|
"success": False,
|
|
@@ -443,6 +502,8 @@ def _cmd_reprocess(args: argparse.Namespace) -> int:
|
|
|
443
502
|
verbose=verbose,
|
|
444
503
|
quiet=bool(args.quiet),
|
|
445
504
|
env=env,
|
|
505
|
+
on_progress=progress.on_progress if progress is not None else None,
|
|
506
|
+
on_progress_console=progress.console if progress is not None else None,
|
|
446
507
|
)
|
|
447
508
|
if _is_graph_preflight_blocker(g):
|
|
448
509
|
payload = {
|
|
@@ -475,14 +536,23 @@ def _cmd_reprocess(args: argparse.Namespace) -> int:
|
|
|
475
536
|
_emit_reprocess_outcome(payload, selective_tty_mode="graph" if ok else None)
|
|
476
537
|
return _reprocess_exit_code(payload)
|
|
477
538
|
|
|
478
|
-
import server # lazy: pulls sentence_transformers/torch/lancedb/
|
|
539
|
+
import server # lazy: pulls sentence_transformers/torch/lancedb/ladybug
|
|
479
540
|
|
|
480
|
-
result = asyncio.run(
|
|
541
|
+
result = asyncio.run(
|
|
542
|
+
server.run_refresh_pipeline(
|
|
543
|
+
quiet=bool(args.quiet),
|
|
544
|
+
verbose=verbose,
|
|
545
|
+
on_progress=progress.on_progress if progress is not None else None,
|
|
546
|
+
on_progress_console=progress.console if progress is not None else None,
|
|
547
|
+
)
|
|
548
|
+
)
|
|
481
549
|
payload = result.model_dump()
|
|
482
550
|
_emit_reprocess_outcome(payload)
|
|
483
551
|
return _reprocess_exit_code(payload)
|
|
484
552
|
|
|
485
|
-
return _run_with_pipeline_progress(
|
|
553
|
+
return _run_with_pipeline_progress(
|
|
554
|
+
"reprocess", cfg, quiet=bool(args.quiet), verbose=bool(args.verbose), work=work
|
|
555
|
+
)
|
|
486
556
|
|
|
487
557
|
|
|
488
558
|
def _cmd_install(args: argparse.Namespace) -> int:
|
|
@@ -495,6 +565,7 @@ def _cmd_install(args: argparse.Namespace) -> int:
|
|
|
495
565
|
model=args.model,
|
|
496
566
|
source_root=None, # None means cwd; installer confirms interactively
|
|
497
567
|
quiet=bool(args.quiet),
|
|
568
|
+
verbose=bool(args.verbose),
|
|
498
569
|
)
|
|
499
570
|
|
|
500
571
|
|
|
@@ -504,6 +575,8 @@ def _cmd_update(args: argparse.Namespace) -> int:
|
|
|
504
575
|
return run_update(
|
|
505
576
|
force=bool(args.force),
|
|
506
577
|
dry_run=bool(args.dry_run),
|
|
578
|
+
quiet=bool(args.quiet),
|
|
579
|
+
verbose=bool(args.verbose),
|
|
507
580
|
)
|
|
508
581
|
|
|
509
582
|
|
|
@@ -537,7 +610,7 @@ def _cmd_erase(args: argparse.Namespace) -> int:
|
|
|
537
610
|
print("Aborted.", file=sys.stderr)
|
|
538
611
|
return 2
|
|
539
612
|
|
|
540
|
-
def work() -> int:
|
|
613
|
+
def work(progress: "PipelineProgress | None") -> int:
|
|
541
614
|
env = cfg.subprocess_env()
|
|
542
615
|
drop = run_cocoindex_drop(env, quiet=bool(args.quiet))
|
|
543
616
|
if drop.returncode == 127:
|
|
@@ -570,7 +643,7 @@ def _cmd_erase(args: argparse.Namespace) -> int:
|
|
|
570
643
|
_emit({"success": True, "message": "erase completed"})
|
|
571
644
|
return 0
|
|
572
645
|
|
|
573
|
-
return _run_with_pipeline_progress("erase", cfg, quiet=bool(args.quiet), work=work)
|
|
646
|
+
return _run_with_pipeline_progress("erase", cfg, quiet=bool(args.quiet), verbose=bool(getattr(args, "verbose", False)), work=work)
|
|
574
647
|
|
|
575
648
|
|
|
576
649
|
def _cmd_meta(args: argparse.Namespace) -> int:
|
|
@@ -642,7 +715,7 @@ def _cmd_unresolved_calls_list(args: argparse.Namespace) -> int:
|
|
|
642
715
|
from ladybug_queries import LadybugGraph # lazy
|
|
643
716
|
|
|
644
717
|
if not LadybugGraph.exists():
|
|
645
|
-
_emit({"success": False, "message": "
|
|
718
|
+
_emit({"success": False, "message": "LadybugDB graph not found"})
|
|
646
719
|
return 1
|
|
647
720
|
graph = LadybugGraph.get()
|
|
648
721
|
rows = graph.list_unresolved_call_sites(
|
|
@@ -663,7 +736,7 @@ def _cmd_unresolved_calls_stats(args: argparse.Namespace) -> int:
|
|
|
663
736
|
from ladybug_queries import LadybugGraph # lazy
|
|
664
737
|
|
|
665
738
|
if not LadybugGraph.exists():
|
|
666
|
-
_emit({"success": False, "message": "
|
|
739
|
+
_emit({"success": False, "message": "LadybugDB graph not found"})
|
|
667
740
|
return 1
|
|
668
741
|
graph = LadybugGraph.get()
|
|
669
742
|
buckets = graph.stats_unresolved_call_sites(by=args.by)
|
|
@@ -688,7 +761,7 @@ def _cmd_analyze_pr(args: argparse.Namespace) -> int:
|
|
|
688
761
|
from ladybug_queries import LadybugGraph # lazy
|
|
689
762
|
|
|
690
763
|
if not LadybugGraph.exists():
|
|
691
|
-
_emit({"success": False, "message": "
|
|
764
|
+
_emit({"success": False, "message": "LadybugDB graph not found"})
|
|
692
765
|
return 1
|
|
693
766
|
graph = LadybugGraph.get()
|
|
694
767
|
report = pr_analysis.analyze_pr_pipeline(graph, diff_text)
|
|
@@ -728,7 +801,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
728
801
|
help="Create a fresh index from a Java repository.",
|
|
729
802
|
description=(
|
|
730
803
|
"First-time index creation. Refuses if the resolved index directory "
|
|
731
|
-
"already contains a
|
|
804
|
+
"already contains a LadybugDB graph or Lance tables. Exit 2 on refusal."
|
|
732
805
|
),
|
|
733
806
|
)
|
|
734
807
|
_add_index_embedding_flags(init)
|
|
@@ -797,7 +870,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
797
870
|
increment = subparsers.add_parser(
|
|
798
871
|
"increment",
|
|
799
872
|
help="Pick up changes since the last index update.",
|
|
800
|
-
description="Runs cocoindex catch-up and incremental
|
|
873
|
+
description="Runs cocoindex catch-up and incremental LadybugDB graph update. Use --vectors-only to skip graph update.",
|
|
801
874
|
)
|
|
802
875
|
_add_index_embedding_flags(increment)
|
|
803
876
|
_add_verbosity_flags(increment)
|
|
@@ -810,9 +883,9 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
810
883
|
|
|
811
884
|
reprocess = subparsers.add_parser(
|
|
812
885
|
"reprocess",
|
|
813
|
-
help="Rebuild vectors and/or
|
|
886
|
+
help="Rebuild vectors and/or LadybugDB (default: both full phases).",
|
|
814
887
|
description=(
|
|
815
|
-
"Default: full Lance reprocess (cocoindex --full-reprocess) then full
|
|
888
|
+
"Default: full Lance reprocess (cocoindex --full-reprocess) then full LadybugDB graph rebuild. "
|
|
816
889
|
"Use --vectors-only or --graph-only to run a single phase (mutually exclusive)."
|
|
817
890
|
),
|
|
818
891
|
)
|
|
@@ -834,7 +907,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
834
907
|
erase = subparsers.add_parser(
|
|
835
908
|
"erase",
|
|
836
909
|
help="Delete the index from disk.",
|
|
837
|
-
description="Runs cocoindex drop, removes
|
|
910
|
+
description="Runs cocoindex drop, removes LadybugDB, and drops Lance tables. Requires --yes or TTY confirmation.",
|
|
838
911
|
)
|
|
839
912
|
_add_index_embedding_flags(erase)
|
|
840
913
|
erase.add_argument("--yes", action="store_true", help="Confirm destructive deletion (required in CI)")
|
java_codebase_rag/cli_format.py
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
"""TTY-aware ANSI formatting for CLI stderr progress."""
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
|
-
import itertools
|
|
5
4
|
import sys
|
|
6
|
-
import threading
|
|
7
|
-
import time
|
|
8
5
|
|
|
9
6
|
_RESET = "\033[0m"
|
|
10
7
|
_BOLD = "\033[1m"
|
|
@@ -16,8 +13,6 @@ _CYAN = "\033[36m"
|
|
|
16
13
|
CHECK = "✓"
|
|
17
14
|
CROSS = "✗"
|
|
18
15
|
|
|
19
|
-
_SPINNER_FRAMES = "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏"
|
|
20
|
-
|
|
21
16
|
_NOISE_CONTAINS: tuple[bytes, ...] = (
|
|
22
17
|
b"lance::",
|
|
23
18
|
b"FutureWarning",
|
|
@@ -25,6 +20,14 @@ _NOISE_CONTAINS: tuple[bytes, ...] = (
|
|
|
25
20
|
b'"event": "brownfield-',
|
|
26
21
|
b"unknown producer source strategy",
|
|
27
22
|
b"unknown client source strategy",
|
|
23
|
+
# Builder verbose heartbeats / pass banners: in default mode the renderer's
|
|
24
|
+
# bar subsumes these, so they must NOT also appear as raw lines above the
|
|
25
|
+
# Live region. --verbose raw-relay bypasses this filter and still shows them.
|
|
26
|
+
b"[graph] pass ",
|
|
27
|
+
b"[graph] scoped write ",
|
|
28
|
+
b"[graph] writing ",
|
|
29
|
+
b"[graph] done ",
|
|
30
|
+
b"[increment] ",
|
|
28
31
|
)
|
|
29
32
|
|
|
30
33
|
|
|
@@ -80,33 +83,3 @@ def styled_check() -> str:
|
|
|
80
83
|
|
|
81
84
|
def styled_cross() -> str:
|
|
82
85
|
return red(CROSS) if stderr_is_tty() else CROSS
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
class Spinner:
|
|
86
|
-
"""Braille spinner that overwrites the current stderr line until stopped."""
|
|
87
|
-
|
|
88
|
-
def __init__(self, label: str) -> None:
|
|
89
|
-
self._label = label
|
|
90
|
-
self._stop = threading.Event()
|
|
91
|
-
self._thread: threading.Thread | None = None
|
|
92
|
-
|
|
93
|
-
def start(self) -> None:
|
|
94
|
-
self._thread = threading.Thread(target=self._run, name="spinner", daemon=True)
|
|
95
|
-
self._thread.start()
|
|
96
|
-
|
|
97
|
-
def stop(self) -> None:
|
|
98
|
-
self._stop.set()
|
|
99
|
-
if self._thread is not None:
|
|
100
|
-
self._thread.join(timeout=2.0)
|
|
101
|
-
sys.stderr.buffer.write(b"\r\x1b[2K")
|
|
102
|
-
sys.stderr.buffer.flush()
|
|
103
|
-
|
|
104
|
-
def _run(self) -> None:
|
|
105
|
-
frames = itertools.cycle(_SPINNER_FRAMES)
|
|
106
|
-
t0 = time.monotonic()
|
|
107
|
-
while not self._stop.wait(0.3):
|
|
108
|
-
elapsed = time.monotonic() - t0
|
|
109
|
-
frame = next(frames)
|
|
110
|
-
line = f"\r{frame} {self._label} · {elapsed:.0f}s"
|
|
111
|
-
sys.stderr.buffer.write(line.encode())
|
|
112
|
-
sys.stderr.buffer.flush()
|
|
@@ -3,26 +3,10 @@ from __future__ import annotations
|
|
|
3
3
|
|
|
4
4
|
import asyncio
|
|
5
5
|
import sys
|
|
6
|
+
from typing import Callable
|
|
6
7
|
|
|
7
|
-
from java_codebase_rag.cli_format import
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def emit_vectors_start() -> None:
|
|
11
|
-
print(
|
|
12
|
-
bold_cyan("[vectors]") + " running · cocoindex update",
|
|
13
|
-
file=sys.stderr,
|
|
14
|
-
flush=True,
|
|
15
|
-
)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def emit_vectors_finish(*, elapsed_s: float, exit_code: int) -> None:
|
|
19
|
-
marker = styled_check() if exit_code == 0 else styled_cross()
|
|
20
|
-
print(
|
|
21
|
-
f"{marker} {bold_cyan('[vectors]')} finished · {elapsed_s:.2f}s"
|
|
22
|
-
+ (f" (exit={exit_code})" if exit_code != 0 else ""),
|
|
23
|
-
file=sys.stderr,
|
|
24
|
-
flush=True,
|
|
25
|
-
)
|
|
8
|
+
from java_codebase_rag.cli_format import is_noise_line
|
|
9
|
+
from java_codebase_rag.progress import ProgressEvent, make_relay
|
|
26
10
|
|
|
27
11
|
|
|
28
12
|
class _AsyncLineFilter:
|
|
@@ -61,8 +45,15 @@ async def accumulate_and_relay_subprocess_streams(
|
|
|
61
45
|
*,
|
|
62
46
|
relay: bool,
|
|
63
47
|
verbose: bool = True,
|
|
48
|
+
on_progress: Callable[[ProgressEvent], None] | None = None,
|
|
49
|
+
on_progress_console: object | None = None,
|
|
64
50
|
) -> tuple[bytes, bytes]:
|
|
65
|
-
"""Read stdout and stderr until EOF; optionally copy non-noise stderr chunks to stderr.
|
|
51
|
+
"""Read stdout and stderr until EOF; optionally copy non-noise stderr chunks to stderr.
|
|
52
|
+
|
|
53
|
+
When ``on_progress`` is set, stderr is drained through a :class:`ProgressRelay`
|
|
54
|
+
so ``JCIRAG_PROGRESS`` lines are parsed and routed to ``on_progress`` (and
|
|
55
|
+
suppressed from the relay), matching the sync ``pipeline._popen_capturing_stderr``.
|
|
56
|
+
"""
|
|
66
57
|
stdout = proc.stdout
|
|
67
58
|
stderr = proc.stderr
|
|
68
59
|
if stdout is None or stderr is None:
|
|
@@ -70,7 +61,12 @@ async def accumulate_and_relay_subprocess_streams(
|
|
|
70
61
|
|
|
71
62
|
out_buf = bytearray()
|
|
72
63
|
err_buf = bytearray()
|
|
73
|
-
|
|
64
|
+
if on_progress is not None:
|
|
65
|
+
filt = make_relay(on_progress, console=on_progress_console, verbose=verbose)
|
|
66
|
+
elif relay and not verbose:
|
|
67
|
+
filt = _AsyncLineFilter()
|
|
68
|
+
else:
|
|
69
|
+
filt = None
|
|
74
70
|
|
|
75
71
|
async def drain_stdout(reader: asyncio.StreamReader, target: bytearray) -> None:
|
|
76
72
|
while True:
|
|
@@ -151,15 +151,15 @@ Simple types in parentheses; generics erased. No spaces after commas. No-arg: `(
|
|
|
151
151
|
|
|
152
152
|
### Shared NodeFilter
|
|
153
153
|
|
|
154
|
-
For `find`, `filter` is required — `{}` means no predicates. **Strict frame:** unknown keys or inapplicable populated fields → `success=false
|
|
154
|
+
For `find`, `filter` is required — `{}` means no predicates. **Strict frame:** unknown keys or inapplicable populated fields → `success=false`; invalid enum values (e.g. wrong case) are rejected earlier at the schema layer with the valid set listed.
|
|
155
155
|
|
|
156
156
|
| Keys | Applies to |
|
|
157
157
|
| ---- | ---------- |
|
|
158
158
|
| `microservice`, `module` | All kinds |
|
|
159
159
|
| `role`, `exclude_roles`, `annotation`, `capability`, `fqn_prefix`, `symbol_kind`, `symbol_kinds` | **symbol** |
|
|
160
160
|
| `http_method`, `path_prefix`, `framework` | **route** |
|
|
161
|
-
| `client_kind`, `target_service`, `target_path_prefix`, `http_method` | **client** |
|
|
162
|
-
| `producer_kind`, `topic_prefix` | **producer** |
|
|
161
|
+
| `source_layer`, `client_kind`, `target_service`, `target_path_prefix`, `http_method` | **client** |
|
|
162
|
+
| `source_layer`, `producer_kind`, `topic_prefix` | **producer** |
|
|
163
163
|
|
|
164
164
|
No wildcards in prefix fields — use `search(query=…)` for fuzzy text.
|
|
165
165
|
|
|
@@ -125,15 +125,15 @@ Use these strings **verbatim** in `neighbors(..., edge_types=[...])`.
|
|
|
125
125
|
|
|
126
126
|
### NodeFilter (`find`, `search.filter`, `neighbors.filter`)
|
|
127
127
|
|
|
128
|
-
For `find`, `filter` is required — `{}` means no predicates. **Strict frame:** unknown keys or inapplicable populated fields → `success=false
|
|
128
|
+
For `find`, `filter` is required — `{}` means no predicates. **Strict frame:** unknown keys or inapplicable populated fields → `success=false`; invalid enum values (e.g. wrong case) are rejected earlier at the schema layer with the valid set listed.
|
|
129
129
|
|
|
130
130
|
| Applicable to | Keys |
|
|
131
131
|
| ------------- | ---- |
|
|
132
132
|
| All kinds | `microservice`, `module` |
|
|
133
133
|
| **symbol** only | `role`, `exclude_roles`, `annotation`, `capability`, `fqn_prefix`, `symbol_kind`, `symbol_kinds` |
|
|
134
134
|
| **route** only | `http_method`, `path_prefix`, `framework` |
|
|
135
|
-
| **client** only | `client_kind`, `target_service`, `target_path_prefix`, `http_method` |
|
|
136
|
-
| **producer** only | `producer_kind`, `topic_prefix` |
|
|
135
|
+
| **client** only | `source_layer`, `client_kind`, `target_service`, `target_path_prefix`, `http_method` |
|
|
136
|
+
| **producer** only | `source_layer`, `producer_kind`, `topic_prefix` |
|
|
137
137
|
|
|
138
138
|
No wildcards in prefix fields — use `search(query=…)` for ranked text.
|
|
139
139
|
|
|
@@ -166,8 +166,8 @@ Exclude `DTO`, `OTHER`, `MAPPER` with `exclude_roles` when tracing business logi
|
|
|
166
166
|
|
|
167
167
|
**Symbol kinds:** `class`, `interface`, `enum`, `record`, `annotation`, `method`, `constructor`.
|
|
168
168
|
|
|
169
|
-
**Route frameworks:** `spring_mvc`, `webflux
|
|
170
|
-
**Client kinds:** `feign_method`, `rest_template`, `web_client`. **Producer kinds:** `kafka_send`, `stream_bridge_send`.
|
|
169
|
+
**Route frameworks:** `spring_mvc`, `webflux`. (Route *kinds* are `http_endpoint`, `http_consumer`, `kafka_topic`, `rabbit_queue`, `jms_destination`, `stream_binding`.)
|
|
170
|
+
**Client kinds:** `feign_method`, `rest_template`, `web_client`. **Producer kinds:** `kafka_send`, `stream_bridge_send`. **Source layers (client/producer):** `builtin`, `layer_a_meta`, `layer_b_ann`, `layer_b_fqn`, `layer_c_source`.
|
|
171
171
|
**Match types:** `cross_service`, `intra_service`, `ambiguous`, `phantom`, `unresolved`.
|
|
172
172
|
|
|
173
173
|
---
|