java-codebase-rag 0.9.4__py3-none-any.whl → 0.9.5__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.
java_codebase_rag/jrag.py CHANGED
@@ -1163,6 +1163,20 @@ def build_parser() -> argparse.ArgumentParser:
1163
1163
  )
1164
1164
  search.set_defaults(handler=_cmd_search, auto_scope=True)
1165
1165
 
1166
+ # ---- vocab-index subparser (PR-ABS-1) ----
1167
+ vocab_index = subparsers.add_parser(
1168
+ "vocab-index",
1169
+ help="Rebuild the vocabulary index (absence diagnosis).",
1170
+ parents=[_core_parser()],
1171
+ description=(
1172
+ "Rebuild the vocabulary index sidecar from the current Ladybug graph. "
1173
+ "The index is a search-optimized projection of Symbol nodes used for "
1174
+ "did-you-mean suggestions and external membership checks in absence "
1175
+ "diagnosis. Printed on success: symbol count and sidecar path."
1176
+ ),
1177
+ )
1178
+ vocab_index.set_defaults(handler=_cmd_vocab_index, detail="full")
1179
+
1166
1180
  return parser
1167
1181
 
1168
1182
 
@@ -1217,6 +1231,40 @@ def _load_graph(cfg): # type: ignore[no-untyped-def]
1217
1231
  raise _IndexStale(str(exc)) from exc
1218
1232
 
1219
1233
 
1234
+ def _cmd_vocab_index(args: argparse.Namespace) -> int:
1235
+ """Rebuild the vocabulary index sidecar from the Ladybug graph."""
1236
+ from ast_java import ONTOLOGY_VERSION
1237
+ from absence_vocab import VocabularyIndex, VOCAB_INDEX_FILENAME
1238
+
1239
+ cfg = _resolve_cfg(args)
1240
+ try:
1241
+ graph = _load_graph(cfg)
1242
+ except (_IndexNotFound, _IndexStale) as exc:
1243
+ print(f"[error] {exc}", file=sys.stderr)
1244
+ return 2
1245
+
1246
+ # Build vocabulary index
1247
+ try:
1248
+ index = VocabularyIndex.build(graph, q=cfg.absence_ngram_q)
1249
+ except Exception as e:
1250
+ print(f"[error] Vocabulary index build failed: {e}", file=sys.stderr)
1251
+ return 1
1252
+
1253
+ # Save to sidecar
1254
+ sidecar_path = cfg.ladybug_path.parent / VOCAB_INDEX_FILENAME
1255
+ try:
1256
+ index.save(sidecar_path, ontology_version=ONTOLOGY_VERSION)
1257
+ except Exception as e:
1258
+ print(f"[error] Failed to save vocabulary index: {e}", file=sys.stderr)
1259
+ return 1
1260
+
1261
+ # Print success message (simple format for admin command)
1262
+ print(f"Vocabulary index rebuilt successfully:")
1263
+ print(f" Symbol count: {index.symbol_count}")
1264
+ print(f" Sidecar path: {sidecar_path}")
1265
+ return 0
1266
+
1267
+
1220
1268
  def _cmd_status(args: argparse.Namespace) -> int:
1221
1269
  from java_codebase_rag.jrag_envelope import Envelope
1222
1270
  from java_codebase_rag.jrag_render import render
@@ -4321,13 +4369,20 @@ def _cmd_search(args: argparse.Namespace) -> int:
4321
4369
  d["kind"] = "search_hit"
4322
4370
  # Add explain token when --explain is set
4323
4371
  if args.explain:
4324
- from search_lancedb import explain_score_components
4372
+ # search_lancedb is unimportable on graph-only (macOS Intel) installs —
4373
+ # lancedb/sentence-transformers are excluded by the PEP 508 markers in
4374
+ # pyproject.toml, and this module imports them at module top. Importing the
4375
+ # explain renderer from there would crash `jrag search ... --explain` on the
4376
+ # exact Intel install that runs the lexical path. search_scoring is
4377
+ # dependency-free and always installed, so the explain import works everywhere.
4378
+ from search_scoring import explain_score_components
4325
4379
  comps = d.get("score_components")
4326
4380
  d["explain"] = explain_score_components(
4327
4381
  comps,
4328
4382
  role=d.get("role"),
4329
4383
  hybrid=bool(args.hybrid),
4330
4384
  graph_expanded=False,
4385
+ lexical=bool(getattr(out, "lexical_mode", False)),
4331
4386
  )
4332
4387
  hit_dicts.append(d)
4333
4388
 
@@ -18,6 +18,7 @@ import re
18
18
  from dataclasses import dataclass, field
19
19
  from typing import Any, Literal
20
20
 
21
+ from absence_types import AbsenceDiagnosis
21
22
  from graph_types import NodeRef
22
23
 
23
24
  __all__ = [
@@ -118,6 +119,9 @@ class Envelope:
118
119
  # zero in-repo callers. Distinguishes the *correct* empty result ("external
119
120
  # entrypoint — no in-repo callers") from a bug-looking bare "0 callers".
120
121
  is_external_entrypoint: bool = False
122
+ # Absence diagnosis explaining why a result is empty (PR-ABS-4). Carried
123
+ # from MCP outputs and rendered in CLI text/JSON. None on ok/ambiguous.
124
+ absence: AbsenceDiagnosis | None = None
121
125
 
122
126
  def to_dict(self) -> dict[str, Any]:
123
127
  """Serialize to a JSON-ready dict, omitting empty optionals.
@@ -151,6 +155,8 @@ class Envelope:
151
155
  out["message"] = self.message
152
156
  if self.is_external_entrypoint:
153
157
  out["is_external_entrypoint"] = True
158
+ if self.absence is not None:
159
+ out["absence"] = self.absence.model_dump()
154
160
  return out
155
161
 
156
162
  def to_json(self) -> str:
@@ -237,6 +243,8 @@ class Envelope:
237
243
  out["message"] = self.message
238
244
  if self.is_external_entrypoint:
239
245
  out["is_external_entrypoint"] = True
246
+ if self.absence is not None:
247
+ out["absence"] = self.absence.model_dump()
240
248
  return out
241
249
 
242
250
  @staticmethod
@@ -610,7 +618,7 @@ def resolve_query(
610
618
  # the agent-facing CLI).
611
619
  if "jrag search" not in raw_msg:
612
620
  raw_msg = f"{raw_msg} Use `jrag search <query>` for ranked fuzzy lookup."
613
- return None, Envelope(status="not_found", message=raw_msg)
621
+ return None, Envelope(status="not_found", message=raw_msg, absence=out.absence)
614
622
 
615
623
 
616
624
  # Listing breadcrumbs (root is None): 1–2 template hints pointing at the natural
@@ -1082,4 +1090,5 @@ def project_envelope(envelope: Envelope, detail: str) -> Envelope:
1082
1090
  file_location=envelope.file_location,
1083
1091
  message=envelope.message,
1084
1092
  is_external_entrypoint=envelope.is_external_entrypoint,
1093
+ absence=envelope.absence,
1085
1094
  )
@@ -12,6 +12,7 @@ from __future__ import annotations
12
12
 
13
13
  from typing import Any
14
14
 
15
+ from absence_types import AbsenceDiagnosis
15
16
  from java_codebase_rag.jrag_envelope import Envelope, project_envelope, simple_name
16
17
 
17
18
  __all__ = ["render", "tiered_name", "display_name"]
@@ -32,6 +33,23 @@ _CALLS_FAMILY_EDGES = frozenset({"CALLS", "HTTP_CALLS", "ASYNC_CALLS"})
32
33
  # none) and are left untagged.
33
34
  _ROUTE_KIND_TAGS: dict[str, str] = {"kafka_topic": "kafka", "http_endpoint": "http"}
34
35
 
36
+ # Absence verdict → human-readable label, shared by the not-found / listing /
37
+ # traversal empty-result renderers. ``AbsenceVerdict`` is a closed Literal of
38
+ # these four values.
39
+ _ABSENCE_VERDICT_TEXT: dict[str, str] = {
40
+ "not_in_project": "not in project",
41
+ "external_dependency": "external dependency",
42
+ "refine_query": "refine your query",
43
+ "correct_empty": "correct empty",
44
+ }
45
+
46
+
47
+ def _verdict_line(absence: AbsenceDiagnosis) -> str | None:
48
+ """A ``Verdict: <label>`` line for an absence diagnosis, or ``None`` if the
49
+ verdict is not one of the known values."""
50
+ text = _ABSENCE_VERDICT_TEXT.get(absence.verdict)
51
+ return f"Verdict: {text}" if text else None
52
+
35
53
  # Identity keys already represented in a listing line (display_name + @service +
36
54
  # kind tag). At ``--detail full`` the per-row kv-block skips these (they are in
37
55
  # the header line) and renders every OTHER key, so full listing == per-row
@@ -226,7 +244,35 @@ def _render_error(envelope: Envelope) -> str:
226
244
 
227
245
  def _render_not_found(envelope: Envelope) -> str:
228
246
  msg = envelope.message or "not found"
229
- return f"not found: {msg}"
247
+ base = f"not found: {msg}"
248
+
249
+ # If absence diagnosis is present, append verdict + message (+ did-you-mean)
250
+ if envelope.absence is not None:
251
+ lines = [base]
252
+ # Verdict line (human-readable label)
253
+ vline = _verdict_line(envelope.absence)
254
+ if vline:
255
+ lines.append(vline)
256
+
257
+ # Per-cause explanation message — surfaces the diagnosis's authored help
258
+ # (external-identity context, filter-relaxation suggestions, etc.).
259
+ if envelope.absence.message:
260
+ lines.append(envelope.absence.message)
261
+
262
+ # Add did-you-mean line if closest_symbols is non-empty
263
+ if envelope.absence.closest_symbols:
264
+ symbols = [s.fqn for s in envelope.absence.closest_symbols]
265
+ if len(symbols) == 1:
266
+ lines.append(f"Did you mean: {symbols[0]}?")
267
+ elif len(symbols) == 2:
268
+ lines.append(f"Did you mean: {symbols[0]} or {symbols[1]}?")
269
+ else:
270
+ joined = ", ".join(symbols[:-1]) + f", or {symbols[-1]}"
271
+ lines.append(f"Did you mean: {joined}?")
272
+
273
+ return "\n".join(lines)
274
+
275
+ return base
230
276
 
231
277
 
232
278
  def _render_listing(envelope: Envelope, *, noun: str, detail: str = "normal") -> str:
@@ -295,7 +341,12 @@ def _render_listing(envelope: Envelope, *, noun: str, detail: str = "normal") ->
295
341
  if rest:
296
342
  lines.extend(_render_inspect_block(rest, 1))
297
343
  if not lines:
298
- lines.append(f"0 {noun}".rstrip())
344
+ # Handle absence diagnosis (PR-ABS-4)
345
+ if envelope.absence is not None:
346
+ vline = _verdict_line(envelope.absence)
347
+ lines.append(vline if vline else f"0 {noun}".rstrip())
348
+ else:
349
+ lines.append(f"0 {noun}".rstrip())
299
350
  # Listing breadcrumbs (Phase 2): <=2 `next:` hint lines when the listing
300
351
  # command emitted agent_next_actions (routes/clients/producers/topics).
301
352
  lines.extend(_next_action_lines(envelope))
@@ -413,10 +464,23 @@ def _render_traversal(envelope: Envelope, *, noun: str, detail: str = "normal")
413
464
  root_node = envelope.nodes.get(root_id, {})
414
465
  root_fqn = str(root_node.get("fqn") or "").strip()
415
466
  root_svc = str(root_node.get("microservice") or "").strip()
416
- if envelope.is_external_entrypoint:
467
+
468
+ # Handle absence diagnosis (PR-ABS-4)
469
+ if envelope.absence is not None:
470
+ absence = envelope.absence
471
+ if absence.verdict == "correct_empty":
472
+ # Same text as the is_external_entrypoint case.
473
+ parts = ["external entrypoint — no in-repo callers"]
474
+ else:
475
+ vline = _verdict_line(absence)
476
+ if vline:
477
+ lines.append(vline)
478
+ parts = [f"0 {noun}".rstrip()]
479
+ elif envelope.is_external_entrypoint:
417
480
  parts = ["external entrypoint — no in-repo callers"]
418
481
  else:
419
482
  parts = [f"0 {noun}".rstrip()]
483
+
420
484
  if root_fqn:
421
485
  parts.append(root_fqn)
422
486
  if root_svc:
@@ -18,6 +18,16 @@ from java_codebase_rag.progress import ProgressEvent, ProgressRelay, make_relay
18
18
 
19
19
  COCOINDEX_TARGET = "java_index_flow_lancedb.py:JavaCodeIndexLance"
20
20
 
21
+ # Operator-facing line printed when an indexing command skips the vectors phase
22
+ # because the vector stack is absent (graph-only install, e.g. macOS Intel where
23
+ # cocoindex[lancedb]/lancedb/sentence-transformers are gated off by PEP 508 markers).
24
+ # Single source of truth — imported by cli.py (init/increment) and server.py
25
+ # (reprocess) so the wording can't drift between the two paths.
26
+ VECTORS_SKIPPED_GRAPH_ONLY = (
27
+ "java-codebase-rag: vectors skipped — vector stack not installed on this platform "
28
+ "(graph-only mode). The graph is built/refreshed; semantic search is unavailable."
29
+ )
30
+
21
31
 
22
32
  def bundle_dir() -> Path:
23
33
  return Path(__file__).resolve().parent.parent
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: java-codebase-rag
3
- Version: 0.9.4
3
+ Version: 0.9.5
4
4
  Summary: MCP server for semantic + structural search over Java codebases
5
5
  Author: HumanBean17
6
6
  License-Expression: MIT
@@ -86,7 +86,7 @@ The rest of this README is the install, the tool/command orientation, and the re
86
86
  pip install java-codebase-rag
87
87
  ```
88
88
 
89
- Python **3.11+** required, on **Linux, macOS, and Windows**. On Linux, Windows, and **Apple Silicon** Macs every native dependency (LanceDB, LadybugDB/kuzu, CocoIndex) ships a wheel and you get the full semantic + graph search. **Intel Macs (x86_64) install graph-only**: PyTorch ≥2.3 and LanceDB ≥0.26 dropped macOS Intel wheels, so the vector stack is auto-excluded via PEP 508 markers — `pip install java-codebase-rag` works out of the box, the graph layer (`find` / `describe` / `neighbors` / `resolve`) is fully usable, and the `search` (semantic) tool reports it is unavailable rather than failing. After install, `java-codebase-rag --help` should print the CLI groups.
89
+ Python **3.11+** required, on **Linux, macOS, and Windows**. On Linux, Windows, and **Apple Silicon** Macs every native dependency (LanceDB, LadybugDB/kuzu, CocoIndex) ships a wheel and you get the full semantic + graph search. **Intel Macs (x86_64) install graph-only**: PyTorch ≥2.3 and LanceDB ≥0.26 dropped macOS Intel wheels, so the vector stack is auto-excluded via PEP 508 markers — `pip install java-codebase-rag` works out of the box, the graph layer (`find` / `describe` / `neighbors` / `resolve`) is fully usable, and the `search` tool falls back to **lexical (keyword) search** over the symbol graph (same tool contract, keyword-ranked instead of semantic; an advisory notes the mode). Semantic/vector search needs Apple Silicon, Linux, or Windows. After install, `java-codebase-rag --help` should print the CLI groups.
90
90
  The package includes the CocoIndex lifecycle dependency used by `init`, `increment`, `reprocess`, and `erase` on platforms that have it (it is absent on Intel Mac).
91
91
 
92
92
  ### Interactive setup (recommended)
@@ -1,44 +1,49 @@
1
- ast_java.py,sha256=ZGykRdcVeLFBxTPFyUfUeAKXxlo6sf82GbyV4tuDuHg,99419
1
+ absence_diagnosis.py,sha256=EorBpkVM5LRWxaTbmbQ3eo6Ew_V7r6enTgtXfcjqzfw,27533
2
+ absence_types.py,sha256=ukDy1GKgebCYdM0OYJ7a5xoI82M2wd0i0KEJwxQFGlo,4220
3
+ absence_vocab.py,sha256=yySRdonAR8hXv0YpTgvaUV0UzihvbvUuSoIXJa5WBiU,15897
4
+ ast_java.py,sha256=1E5XU5lsc1Ltyv96kSv5VNhRl_KRTsYeHG_qDLgPMcc,99419
2
5
  brownfield_events.py,sha256=yxXkKDgMb3VPtaiakGzncHM_EGnda8xIue6w90yYp8s,2055
3
- build_ast_graph.py,sha256=wo5BwVf2JCwLEnZhMlpPPNozt59V1cVvsIhP9cuOFPs,172655
6
+ build_ast_graph.py,sha256=scaC7TnmmmrjAv3R3qy_30kLb2DH4w7lznLxjDCntOs,176403
4
7
  chunk_heuristics.py,sha256=aQk2NOKxzUdqoUAJUO3G3LE0MN_bYZWNLQ0tkmj5uts,1813
5
- graph_enrich.py,sha256=Fxp7JurH9wWTOTX1nErjH4H6Jw5bB68YCqR17rsX6gw,62829
6
- graph_types.py,sha256=P6RVdEiqZlaSgTyXepnGbtMoZq0m_MaP2q6M6i7Uxlc,4539
8
+ graph_enrich.py,sha256=CYc2K7Zw_CTefaJkL9h6nZmvw3uN8XbIkg4iYQ-ICY0,72528
9
+ graph_types.py,sha256=bf8EEkvWSyRxFQgycTnxxZQwlikLZSU3B-U1Z1cFQtg,4789
7
10
  index_common.py,sha256=HT6FKHFJ084eFvd3fR1j8z8gf4eWoPHVW8GXLpw464I,285
8
- java_index_flow_lancedb.py,sha256=kDkhnRxl-Kz0YNMY27DTk3gwa8H2nWyGDCQC8hxx-jI,28970
11
+ java_index_flow_lancedb.py,sha256=kZh8HgwtGV8Ul_pwdBS7g62hVvKkk8EXTk-qr6NNba8,29676
9
12
  java_index_v1_common.py,sha256=nF1KrSqboF_RRvWerG9knRRFmWwsrG_CvhgnsoZ8KqA,1154
10
13
  java_ontology.py,sha256=eUGFa96GV_09s2f2sWSMdqYmoxU3-1pyU2iipvcDjG0,17219
11
- ladybug_queries.py,sha256=rVnVEHwWwE4USeX7tICYEl1SSiSxJGnBNe1VX66R3Xk,100531
14
+ ladybug_queries.py,sha256=SXXNcYxdXCHcQ6SBZdZyqAa-hv5_OFaKsfNx4zVtPgY,100688
12
15
  mcp_hints.py,sha256=zp-4cnOmbYD0YovmZiLS2oGcvWcWE7n8jKVz4_xifno,42512
13
- mcp_v2.py,sha256=ABNHiZEEwQ76uPF9E5fQWncJ4JmmgtG3vmt5TDxBGVo,68901
16
+ mcp_v2.py,sha256=Q69-0IOna1XRG06RcEFEsS-YucuBh60sAVIIDKoOnBc,76688
14
17
  path_filtering.py,sha256=R--XzI51LXBu5IBKMCnJWbkNr6I5d-SDmltyQQnWco0,17674
15
18
  pr_analysis.py,sha256=zrmZZD5yotJtM02Kif6_jgI_oeformOao793akp0N6Y,18394
16
- resolve_service.py,sha256=lTHPSn5zgIFjNICiOZc6m_bBJShTn72kShhkofwZdoE,26364
17
- search_lancedb.py,sha256=1sGSZ6H8J9hGcKAwHpzB7F0_in3A_sEtOI5LvlZYIRI,43864
18
- server.py,sha256=nOK3DOr-i3PJnVmE7neye_tptJumgXfIXPlTIn01MPQ,37065
19
+ resolve_service.py,sha256=EAfWlGQ4U1qKoPCfZ7BZUUy8pm0w9tldga3Krw0_GO4,28543
20
+ search_lancedb.py,sha256=LmratGpyEKcCYohXVTapjGd-rk_kryS93oD43foGZeQ,34486
21
+ search_lexical.py,sha256=6U0SgRH36Qmh3IIPzMi553ztW7MCnCvg5cSlMpxIdu8,14250
22
+ search_scoring.py,sha256=SSs2GLvHCB_gsKPo0N-YPGeAVNWx-TsXXPJO2JPsOOY,12321
23
+ server.py,sha256=SWh7hZshzVuhYqEm6Leq1GS2VP6olml4dz9nrGwf-KM,39889
19
24
  java_codebase_rag/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
20
25
  java_codebase_rag/_fdlimit.py,sha256=vkwjsPbZfxzZ2DZTPWO5DxtuNlLzOADzIq07iYX7GCU,2465
21
26
  java_codebase_rag/_stdio.py,sha256=TDNbpt2EP0_Zd622ihdlKwlMfxkKHWOfgLVcU6TcNbo,1458
22
27
  java_codebase_rag/_version.py,sha256=Dnoh_a-c13fqPQ-HV9CsSPbFUkihUJl_z5CACW4qDyA,1365
23
- java_codebase_rag/cli.py,sha256=nI3eQ_21YDA80WbbYxKEVpIl9uA9tsf1szQhz_C-t9Q,45446
28
+ java_codebase_rag/cli.py,sha256=LcNgfOS7r8_vTckuyUbUxI7bhUyYoU_iwzUb37nT5Ts,45296
24
29
  java_codebase_rag/cli_format.py,sha256=CT7-xdwZ0bMCdP68_UOwkvm-mnLluU3LutlM-mDNk60,1839
25
30
  java_codebase_rag/cli_progress.py,sha256=q6Wh97yzLGs1B8UFk_WAKivfQu7Y5RnUUE-T2YHWkIs,3237
26
- java_codebase_rag/config.py,sha256=1EAlKtQx7LUo-gPoE6BOVLz0YEl1OpWFRABkF06zjgk,26464
31
+ java_codebase_rag/config.py,sha256=VPs69os_3co3UPSoBVhpfoVnqDaPJ6Ycbk8A4AyXZOo,30548
27
32
  java_codebase_rag/installer.py,sha256=c-_tR1Ct_O3yhmruFRnDoM1Wilz-igD9qdkDPsNqLMI,79934
28
- java_codebase_rag/jrag.py,sha256=odwSSxnWUOeyhx3O-C_q0y3D4KOVtqUqeDMyKgDVqNc,196991
29
- java_codebase_rag/jrag_envelope.py,sha256=5jD3p2O-p9acAKHqoif5FFoqgP7Q2ziCuKSiPCHoywc,47505
33
+ java_codebase_rag/jrag.py,sha256=XJN1ejEwxLpTrDJW4lyPjdhP21m7ZRv-JZh73aQDc14,199393
34
+ java_codebase_rag/jrag_envelope.py,sha256=cTe-2lk8-Fvmo9_Ps6rN0QtBSw4muSD_UXvm_rkUc48,47984
30
35
  java_codebase_rag/jrag_hints.py,sha256=k2PFE4s3lZgBYHMdZcTjx1-w28nfQcBtQEVsSxI_DvE,9262
31
- java_codebase_rag/jrag_render.py,sha256=WrBtVFvhpVlykHXg-uZtbdv2XsCw4KXNXVarE06pkLk,32350
36
+ java_codebase_rag/jrag_render.py,sha256=MYPyiB7JiQrSw_KHMjCdWg9JuaxpO3Pv_FI4W7bG8sQ,34917
32
37
  java_codebase_rag/lance_optimize.py,sha256=HI3aFebP1fenLL6Cav1jMG5kLXSrHLndO_MIJY6qQVo,11977
33
- java_codebase_rag/pipeline.py,sha256=L65mjK-IxkVWazUNVyIvzfQNi24behQu-Kdc7o_HwEk,17754
38
+ java_codebase_rag/pipeline.py,sha256=OCqBVjkGZOCvzmBElKMes6ynYn9dSN9J1bhRFyEceVE,18355
34
39
  java_codebase_rag/progress.py,sha256=2IxdMALDM0wAQCyJrrfZ975zM_85C-4BfHxf4AtYifE,23212
35
40
  java_codebase_rag/install_data/agents/explorer-rag-cli.md,sha256=bVmmzrBSSamMz7hDIKxL5alAZOXsJ_Km8TQnCcxDad0,10292
36
41
  java_codebase_rag/install_data/agents/explorer-rag-enhanced.md,sha256=gZsNFbuK0lSnOIlplbbS_muz2ozokJqvFUv65QM0NDM,10406
37
42
  java_codebase_rag/install_data/skills/explore-codebase/SKILL.md,sha256=A-v2dueVnxwBzBlxoRjZ2zOJk8DranLQ1TElwn94h0s,11529
38
43
  java_codebase_rag/install_data/skills/explore-codebase-cli/SKILL.md,sha256=K2qwZqk2xPxjPxXtUHvax1Ug2J7nBYeMTIjRznO100g,10059
39
- java_codebase_rag-0.9.4.dist-info/licenses/LICENSE,sha256=gxvtiHtuviR_q8ZAjWw-QTcF3DyPzg6ZY-lQrr8OPpw,1068
40
- java_codebase_rag-0.9.4.dist-info/METADATA,sha256=CEq0X_nWAQP89qo0VJgX5QR3Pf9NcsaRnk-AruJkL6k,20175
41
- java_codebase_rag-0.9.4.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
42
- java_codebase_rag-0.9.4.dist-info/entry_points.txt,sha256=cj3QTc11UYVQnj9T3orc4daiIGaCYrXP149vKbH2R4U,168
43
- java_codebase_rag-0.9.4.dist-info/top_level.txt,sha256=8vC-VN3cMwz5vhkSTaeJ1a1bDeqLWEfrTks1CvEvIg0,273
44
- java_codebase_rag-0.9.4.dist-info/RECORD,,
44
+ java_codebase_rag-0.9.5.dist-info/licenses/LICENSE,sha256=gxvtiHtuviR_q8ZAjWw-QTcF3DyPzg6ZY-lQrr8OPpw,1068
45
+ java_codebase_rag-0.9.5.dist-info/METADATA,sha256=cxCb0ljrq1h-E0nS7d7WI8t8LTQwUpIcYNwZXgUkMjM,20331
46
+ java_codebase_rag-0.9.5.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
47
+ java_codebase_rag-0.9.5.dist-info/entry_points.txt,sha256=cj3QTc11UYVQnj9T3orc4daiIGaCYrXP149vKbH2R4U,168
48
+ java_codebase_rag-0.9.5.dist-info/top_level.txt,sha256=gYhQnlcJKpqwIOTwAneruJrkeBzIjoC71LeHMGMZLAY,349
49
+ java_codebase_rag-0.9.5.dist-info/RECORD,,
@@ -1,3 +1,6 @@
1
+ absence_diagnosis
2
+ absence_types
3
+ absence_vocab
1
4
  ast_java
2
5
  brownfield_events
3
6
  build_ast_graph
@@ -16,4 +19,6 @@ path_filtering
16
19
  pr_analysis
17
20
  resolve_service
18
21
  search_lancedb
22
+ search_lexical
23
+ search_scoring
19
24
  server
@@ -51,7 +51,13 @@ from java_index_v1_common import (
51
51
  )
52
52
  from path_filtering import LayeredIgnore
53
53
  from ast_java import ONTOLOGY_VERSION, parse_java
54
- from graph_enrich import collect_annotation_meta_chain, enrich_chunk, load_brownfield_overrides
54
+ from graph_enrich import (
55
+ classify_java_file,
56
+ collect_annotation_meta_chain,
57
+ enrich_chunk,
58
+ load_brownfield_overrides,
59
+ load_generated_detection,
60
+ )
55
61
 
56
62
  # Older cocoindex (e.g. 1.0.0a43) uses ``tracked=False``; newer releases renamed
57
63
  # the flag to ``detect_change`` (default False) and reject ``tracked``.
@@ -280,6 +286,9 @@ class JavaLanceChunk:
280
286
  annotations_on_type: Annotated[list[str], LanceType(pa.list_(pa.string()))]
281
287
  symbols: Annotated[list[str], LanceType(pa.list_(pa.string()))]
282
288
  ontology_version: int
289
+ # Generated source detection: populated per-file, not per-chunk
290
+ generated: bool
291
+ generated_by: str | None
283
292
 
284
293
 
285
294
  @dataclass
@@ -364,12 +373,13 @@ def _parse_and_enrich_java(
364
373
  chunks: list[Any],
365
374
  rel: str,
366
375
  project_root: Path,
367
- ) -> list[Any]:
376
+ ) -> tuple[list[Any], Any]:
368
377
  """Parse one Java file and enrich every chunk, off the event loop.
369
378
 
370
- Returns a list of :class:`graph_enrich.ChunkEnrichment` aligned 1:1 with
371
- ``chunks``. Intended to run via ``asyncio.to_thread`` from
372
- ``process_java_file`` (vectors perf lever #2): while the worker thread
379
+ Returns a tuple of (enrichments, ast) where enrichments is a list of
380
+ :class:`graph_enrich.ChunkEnrichment` aligned 1:1 with ``chunks``, and ast
381
+ is the parsed :class:`JavaFileAst`. Intended to run via ``asyncio.to_thread``
382
+ from ``process_java_file`` (vectors perf lever #2): while the worker thread
373
383
  parses + enriches, the event loop is free to drive other files and keep the
374
384
  embedder's batching queue fed.
375
385
 
@@ -381,7 +391,7 @@ def _parse_and_enrich_java(
381
391
  ``lru_cache`` reads are thread-safe under the GIL.
382
392
  """
383
393
  ast = parse_java(content_bytes)
384
- return [
394
+ enrichments = [
385
395
  enrich_chunk(
386
396
  ast,
387
397
  chunk_start_byte=ch.start.byte_offset,
@@ -391,6 +401,7 @@ def _parse_and_enrich_java(
391
401
  )
392
402
  for ch in chunks
393
403
  ]
404
+ return enrichments, ast
394
405
 
395
406
 
396
407
  @coco.fn(memo=True)
@@ -430,9 +441,16 @@ async def process_java_file(
430
441
  # (vectors perf lever #2) parse + enrich off the event loop so the loop can
431
442
  # keep the embedder's batching queue fed while this file is being parsed.
432
443
  # parse_java is thread-safe (per-thread tree-sitter Parser in ast_java).
433
- enrichments = await asyncio.to_thread(
444
+ enrichments, ast = await asyncio.to_thread(
434
445
  _parse_and_enrich_java, content_bytes, chunks, rel, project_root
435
446
  )
447
+
448
+ # Compute generated source detection once per file (uses the AST and content_bytes)
449
+ generated_config = load_generated_detection(project_root)
450
+ generated, generated_by = classify_java_file(
451
+ content_bytes, ast, config=generated_config, project_root=project_root
452
+ )
453
+
436
454
  # (vectors perf lever #1) embed all chunks concurrently so the batched
437
455
  # embedder groups them into one ``model.encode(...)`` (max_batch_size=64)
438
456
  # instead of N serial batch-of-1 calls. Dominant win for ``increment``
@@ -462,6 +480,8 @@ async def process_java_file(
462
480
  annotations_on_type=enrich.annotations_on_type,
463
481
  symbols=enrich.symbols,
464
482
  ontology_version=ONTOLOGY_VERSION,
483
+ generated=generated,
484
+ generated_by=generated_by,
465
485
  )
466
486
  )
467
487
 
ladybug_queries.py CHANGED
@@ -207,7 +207,7 @@ def _symbol_return_for(alias: str) -> str:
207
207
  f"{alias}.modifiers AS modifiers, {alias}.annotations AS annotations, "
208
208
  f"{alias}.capabilities AS capabilities, "
209
209
  f"{alias}.role AS role, {alias}.signature AS signature, "
210
- f"{alias}.parent_id AS parent_id, {alias}.resolved AS resolved"
210
+ f"{alias}.parent_id AS parent_id, {alias}.resolved AS resolved, {alias}.generated AS generated, {alias}.generated_by AS generated_by"
211
211
  )
212
212
 
213
213
 
@@ -296,7 +296,7 @@ def _row_to_symbol(row: dict[str, Any]) -> SymbolHit:
296
296
  _SYM_COLS = (
297
297
  "id", "kind", "name", "fqn", "package", "module", "microservice",
298
298
  "filename", "start_line", "end_line", "start_byte", "end_byte",
299
- "modifiers", "annotations", "capabilities", "role", "signature", "parent_id", "resolved",
299
+ "modifiers", "annotations", "capabilities", "role", "signature", "parent_id", "resolved", "generated", "generated_by",
300
300
  )
301
301
 
302
302
 
@@ -1107,14 +1107,14 @@ class LadybugGraph:
1107
1107
  f"s.{c} AS s_{c}" for c in (
1108
1108
  "id", "kind", "name", "fqn", "package", "module", "microservice",
1109
1109
  "filename", "start_line", "end_line", "start_byte", "end_byte",
1110
- "modifiers", "annotations", "capabilities", "role", "signature", "parent_id", "resolved",
1110
+ "modifiers", "annotations", "capabilities", "role", "signature", "parent_id", "resolved", "generated", "generated_by",
1111
1111
  )
1112
1112
  )
1113
1113
  t_proj = ", ".join(
1114
1114
  f"t.{c} AS t_{c}" for c in (
1115
1115
  "id", "kind", "name", "fqn", "package", "module", "microservice",
1116
1116
  "filename", "start_line", "end_line", "start_byte", "end_byte",
1117
- "modifiers", "annotations", "capabilities", "role", "signature", "parent_id", "resolved",
1117
+ "modifiers", "annotations", "capabilities", "role", "signature", "parent_id", "resolved", "generated", "generated_by",
1118
1118
  )
1119
1119
  )
1120
1120
  q = (