java-codebase-rag 0.9.6__tar.gz → 0.10.0__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 (75) hide show
  1. {java_codebase_rag-0.9.6/src/java_codebase_rag.egg-info → java_codebase_rag-0.10.0}/PKG-INFO +30 -31
  2. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/README.md +24 -26
  3. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/pyproject.toml +6 -5
  4. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/absence/absence_vocab.py +7 -2
  5. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/analysis/pr_analysis.py +33 -3
  6. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/ast/ast_java.py +2 -1
  7. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/cli.py +23 -8
  8. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/config.py +68 -1
  9. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/graph/build_ast_graph.py +123 -4
  10. java_codebase_rag-0.10.0/src/java_codebase_rag/graph/graph_types.py +224 -0
  11. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/graph/ladybug_queries.py +45 -2
  12. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/index/java_index_flow_lancedb.py +10 -16
  13. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/install_data/agents/explorer-rag-cli.md +3 -1
  14. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/install_data/skills/explore-codebase-cli/SKILL.md +3 -1
  15. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/jrag.py +627 -661
  16. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/jrag_envelope.py +13 -0
  17. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/jrag_render.py +160 -3
  18. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/lance_optimize.py +11 -12
  19. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/mcp/mcp_v2.py +16 -1
  20. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/pipeline.py +47 -1
  21. java_codebase_rag-0.10.0/src/java_codebase_rag/read_payloads.py +781 -0
  22. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/search/search_lancedb.py +138 -6
  23. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/search/search_lexical.py +128 -30
  24. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/search/search_scoring.py +82 -0
  25. java_codebase_rag-0.10.0/src/java_codebase_rag/watch/__init__.py +0 -0
  26. java_codebase_rag-0.10.0/src/java_codebase_rag/watch/client.py +230 -0
  27. java_codebase_rag-0.10.0/src/java_codebase_rag/watch/daemon.py +368 -0
  28. java_codebase_rag-0.10.0/src/java_codebase_rag/watch/lock.py +201 -0
  29. java_codebase_rag-0.10.0/src/java_codebase_rag/watch/paths.py +76 -0
  30. java_codebase_rag-0.10.0/src/java_codebase_rag/watch/protocol.py +122 -0
  31. java_codebase_rag-0.10.0/src/java_codebase_rag/watch/server.py +273 -0
  32. java_codebase_rag-0.10.0/src/java_codebase_rag/watch/warm.py +105 -0
  33. java_codebase_rag-0.10.0/src/java_codebase_rag/watch/watcher.py +352 -0
  34. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0/src/java_codebase_rag.egg-info}/PKG-INFO +30 -31
  35. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag.egg-info/SOURCES.txt +13 -1
  36. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag.egg-info/requires.txt +5 -4
  37. java_codebase_rag-0.10.0/tests/test_config_watch.py +164 -0
  38. java_codebase_rag-0.10.0/tests/test_docs_watch.py +76 -0
  39. java_codebase_rag-0.9.6/src/java_codebase_rag/graph/graph_types.py +0 -137
  40. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/LICENSE +0 -0
  41. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/setup.cfg +0 -0
  42. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/__init__.py +0 -0
  43. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/_fdlimit.py +0 -0
  44. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/_stdio.py +0 -0
  45. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/_version.py +0 -0
  46. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/absence/__init__.py +0 -0
  47. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/absence/absence_diagnosis.py +0 -0
  48. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/absence/absence_types.py +0 -0
  49. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/analysis/__init__.py +0 -0
  50. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/analysis/resolve_service.py +0 -0
  51. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/ast/__init__.py +0 -0
  52. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/ast/brownfield_events.py +0 -0
  53. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/ast/chunk_heuristics.py +0 -0
  54. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/cli_format.py +0 -0
  55. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/cli_progress.py +0 -0
  56. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/graph/__init__.py +0 -0
  57. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/graph/graph_enrich.py +0 -0
  58. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/graph/java_ontology.py +0 -0
  59. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/graph/path_filtering.py +0 -0
  60. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/index/__init__.py +0 -0
  61. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/index/java_index_v1_common.py +0 -0
  62. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/install_data/__init__.py +0 -0
  63. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/install_data/agents/explorer-rag-enhanced.md +0 -0
  64. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/install_data/skills/explore-codebase/SKILL.md +0 -0
  65. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/installer.py +0 -0
  66. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/jrag_hints.py +0 -0
  67. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/mcp/__init__.py +0 -0
  68. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/mcp/mcp_hints.py +0 -0
  69. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/mcp/server.py +0 -0
  70. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/progress.py +0 -0
  71. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/search/__init__.py +0 -0
  72. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag/search/index_common.py +0 -0
  73. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag.egg-info/dependency_links.txt +0 -0
  74. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag.egg-info/entry_points.txt +0 -0
  75. {java_codebase_rag-0.9.6 → java_codebase_rag-0.10.0}/src/java_codebase_rag.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: java-codebase-rag
3
- Version: 0.9.6
3
+ Version: 0.10.0
4
4
  Summary: MCP server for semantic + structural search over Java codebases
5
5
  Author: HumanBean17
6
6
  License-Expression: MIT
@@ -21,21 +21,22 @@ Classifier: Operating System :: Microsoft :: Windows
21
21
  Requires-Python: >=3.11
22
22
  Description-Content-Type: text/markdown
23
23
  License-File: LICENSE
24
- Requires-Dist: cocoindex[lancedb]<2,>=1.0.7; sys_platform != "darwin" or platform_machine != "x86_64"
24
+ Requires-Dist: cocoindex[lancedb]<2,>=1.0.15; sys_platform != "darwin" or platform_machine != "x86_64"
25
25
  Requires-Dist: ladybug<0.18,>=0.17.1
26
- Requires-Dist: lancedb<0.31,>=0.25.3; sys_platform != "darwin" or platform_machine != "x86_64"
26
+ Requires-Dist: lancedb<0.36,>=0.34; sys_platform != "darwin" or platform_machine != "x86_64"
27
27
  Requires-Dist: mcp<2,>=1.27.0
28
28
  Requires-Dist: numpy<2.5,>=1.26.4
29
29
  Requires-Dist: pathspec<2,>=1.0.4
30
- Requires-Dist: pyarrow<24,>=23.0.1
30
+ Requires-Dist: pyarrow<26,>=23.0.1
31
31
  Requires-Dist: pydantic<3,>=2.0
32
32
  Requires-Dist: PyYAML<7,>=6.0.3
33
33
  Requires-Dist: questionary<3,>=2.0
34
- Requires-Dist: rich<15,>=14
34
+ Requires-Dist: rich<16,>=14
35
35
  Requires-Dist: sentence-transformers<6,>=5.4.0; sys_platform != "darwin" or platform_machine != "x86_64"
36
36
  Requires-Dist: tree-sitter<0.26,>=0.25.2
37
37
  Requires-Dist: tree-sitter-java<0.24,>=0.23.5
38
38
  Requires-Dist: unidiff<1,>=0.7.3
39
+ Requires-Dist: watchdog<7,>=6
39
40
  Provides-Extra: dev
40
41
  Requires-Dist: pytest>=7; extra == "dev"
41
42
  Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
@@ -45,18 +46,16 @@ Dynamic: license-file
45
46
 
46
47
  # java-codebase-rag
47
48
 
48
- A graph-native code intelligence layer for Java microservice estates — usable as an **MCP server** or a **CLI** (`jrag`), two surfaces over the same graph.
49
+ A graph-native code intelligence layer for Java microservice estates, surfaced through the **`jrag` CLI** one command per engineering intent. A **legacy MCP server** (`search` / `find` / `describe` / `neighbors` / `resolve`) is also available for existing setups. Both are thin surfaces over the same **AST Graph**: a deterministic property graph extracted from Java source with tree-sitter, stored **locally** in **LadybugDB** (graph) alongside a **LanceDB** vector index (chunks). There is no server to host and no cloud round-trip — the index lives on your disk and your source never leaves the machine. Both surfaces collapse onto three primitive operations: **locate**, **inspect**, **walk**.
49
50
 
50
- The system extracts a deterministic property graph from Java source (tree-sitter), stores it in **LadybugDB** (graph) alongside a **LanceDB** vector index (chunks), and exposes two agent surfaces, picked at install time (`java-codebase-rag install --surface mcp|cli`): the **MCP** surface ships five tools — `search`, `find`, `describe`, `neighbors`, `resolve` — over stdio; the **CLI** surface ships `jrag`, one command per engineering intent. Both collapse onto three primitive operations: **locate**, **inspect**, **walk**.
51
-
52
- > **What this MCP is:** a **GPS for code navigation**, not a reasoning engine.
51
+ > **What this is: a GPS for code navigation**, not a reasoning engine.
53
52
  > Agents use a simple loop:
54
53
  >
55
- > 1. **Locate** entry nodes (`search` / `find`, or identifier-shaped **`resolve`**)
56
- > 2. **Inspect** what a node is (`describe`)
57
- > 3. **Walk** one hop at a time (`neighbors`) until enough evidence is gathered
54
+ > 1. **Locate** entry nodes (`jrag find`, `jrag search`, or identifier-shaped lookup)
55
+ > 2. **Inspect** what a node is (`jrag inspect`)
56
+ > 3. **Walk** one hop at a time (`jrag callers` / `callees` / `hierarchy` / …) until enough evidence is gathered
58
57
  >
59
- > The MCP exposes structure and adjacency; the agent owns multi-hop reasoning and stop conditions.
58
+ > The tool exposes structure and adjacency; the agent owns multi-hop reasoning and stop conditions.
60
59
 
61
60
  For the design rationale, the GPS metaphor, and the full ontology, see [`docs/paper/paper.pdf`](./docs/paper/paper.pdf) (architecture report).
62
61
 
@@ -66,7 +65,7 @@ For the design rationale, the GPS metaphor, and the full ontology, see [`docs/pa
66
65
 
67
66
  Generic code-search tools (grep, ctags, vector-only RAG) hit a ceiling on real Java microservice estates: they find files but lose the structure that makes a Spring/JAX-RS system navigable. This project is built around five choices that target that gap.
68
67
 
69
- - **Hybrid RAG + GraphRAG, not either-or.** Semantic recall (LanceDB chunk vectors) and structural navigation (LadybugDB property graph) are composed in one surface. `search` finds candidate nodes by meaning; `neighbors` walks the exact edge you care about (`CALLS`, `IMPLEMENTS`, `INJECTS`, `EXPOSES`, …). The agent picks the right primitive per step instead of being forced into pure-vector or pure-symbol search.
68
+ - **Hybrid RAG + AST Graph, not either-or.** Semantic recall (LanceDB chunk vectors) and structural navigation (the LadybugDB AST Graph) are composed in one surface. `search` finds candidate nodes by meaning; `neighbors` walks the exact edge you care about (`CALLS`, `IMPLEMENTS`, `INJECTS`, `EXPOSES`, …). The agent picks the right primitive per step instead of being forced into pure-vector or pure-symbol search.
70
69
 
71
70
  - **A Java-tuned role model.** Symbols are labelled with stereotypes inferred from Spring and JAX-RS conventions — `CONTROLLER`, `SERVICE`, `REPOSITORY`, `COMPONENT`, `CONFIG`, `ENTITY`, `CLIENT`, `MAPPER`, `DTO`. Agents can ask "list controllers" or "who injects this repository" directly, instead of grep-ing for `@RestController` and hoping for the best. Roles drive both filtering (`find` with a `NodeFilter`) and ranking.
72
71
 
@@ -86,7 +85,7 @@ The rest of this README is the install, the tool/command orientation, and the re
86
85
  pip install java-codebase-rag
87
86
  ```
88
87
 
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.
88
+ Python **3.11+** required, on **Linux, macOS, and Windows**. On Linux, Windows, and **Apple Silicon** Macs every native dependency (LanceDB, LadybugDB, 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 search** over the symbol graph — BM25-ranked over a LadybugDB full-text index (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
89
  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
90
 
92
91
  ### Interactive setup (recommended)
@@ -122,19 +121,7 @@ If you prefer manual configuration, see [`docs/JAVA-CODEBASE-RAG-CLI.md`](./docs
122
121
 
123
122
  ## Tools & commands at a glance
124
123
 
125
- Pick a surface at install time `java-codebase-rag install --surface mcp|cli` (default `cli`, recommended). Both surfaces walk the same LanceDB vectors + LadybugDB graph. Switch an existing install later with `java-codebase-rag update --surface mcp|cli`.
126
-
127
- **MCP surface — five tools over stdio**
128
-
129
- | Tool | Purpose | Required args |
130
- |---|---|---|
131
- | `search` | Locate nodes by NL / code text. | `query` |
132
- | `find` | Locate nodes by structured filter. | `kind`, `filter` |
133
- | `describe` | Full record + edge counts for one node. | `id` |
134
- | `resolve` | Identifier-shaped lookup (FQN-collision-safe). Returns `one` / `many` / `none`. | `identifier` |
135
- | `neighbors` | Graph walk, one hop. | `ids`, `direction`, `edge_types` |
136
-
137
- Full schemas, `NodeFilter` / `EdgeFilter` semantics, and the hints contract live in [`docs/AGENT-GUIDE.md`](./docs/AGENT-GUIDE.md). Edge types and traversal directions are listed in [`docs/EDGE-NAVIGATION.md`](./docs/EDGE-NAVIGATION.md).
124
+ `jrag` is the default and recommended surface (`java-codebase-rag install --surface cli`). The **MCP server** (`--surface mcp`) is kept as a **legacy** option for existing setups. Both surfaces walk the same LanceDB vectors + LadybugDB **AST Graph**. Switch an existing install later with `java-codebase-rag update --surface mcp|cli`.
138
125
 
139
126
  **CLI surface — `jrag`, one command per engineering intent**
140
127
 
@@ -143,7 +130,7 @@ Full schemas, `NodeFilter` / `EdgeFilter` semantics, and the hints contract live
143
130
  jrag status # index health (ontology version, freshness, counts)
144
131
  jrag microservices # microservices with resolved type counts
145
132
  jrag map # counts per kind per service/module
146
- jrag map --module # group by module instead
133
+ jrag map --by module # group by module instead (--module filters)
147
134
  jrag conventions # dominant roles + framework tallies
148
135
  jrag overview chat-core # bundle for a microservice
149
136
  jrag overview /chat/assign # route flow (inbound callers + outbound CALLS)
@@ -191,9 +178,21 @@ jrag search "audit" --offset 5 # paginated
191
178
 
192
179
  Every `<query>` command takes human-readable identifiers (FQN / simple name / route path / topic) — never raw node IDs. Output contract, flags, and the resolve-first rule are in [`jrag` — agent CLI](#jrag--agent-cli) below.
193
180
 
181
+ **MCP surface — five tools over stdio (legacy)**
182
+
183
+ | Tool | Purpose | Required args |
184
+ |---|---|---|
185
+ | `search` | Locate nodes by NL / code text. | `query` |
186
+ | `find` | Locate nodes by structured filter. | `kind`, `filter` |
187
+ | `describe` | Full record + edge counts for one node. | `id` |
188
+ | `resolve` | Identifier-shaped lookup (FQN-collision-safe). Returns `one` / `many` / `none`. | `identifier` |
189
+ | `neighbors` | Graph walk, one hop. | `ids`, `direction`, `edge_types` |
190
+
191
+ Full schemas, `NodeFilter` / `EdgeFilter` semantics, and the hints contract live in [`docs/AGENT-GUIDE.md`](./docs/AGENT-GUIDE.md). Edge types and traversal directions are listed in [`docs/EDGE-NAVIGATION.md`](./docs/EDGE-NAVIGATION.md).
192
+
194
193
  ### Three-layer architecture
195
194
 
196
- Layer 1 (storage) → Layer 2 (5 MCP tools **or** the `jrag` CLI) → Layer 3 (skill). The MCP-surface skill **[`/explore-codebase`](./skills/explore-codebase/SKILL.md)** documents the 5-tool MCP; the CLI-surface skill **[`/explore-codebase-cli`](./skills/explore-codebase-cli/SKILL.md)** documents the `jrag` CLI (PR-JRAG-5). See the [architecture diagram in `skills/README.md`](./skills/README.md#three-layer-architecture).
195
+ Layer 1 (storage) → Layer 2 (the `jrag` CLI, **or** the legacy 5-tool MCP) → Layer 3 (skill). The CLI-surface skill **[`/explore-codebase-cli`](./skills/explore-codebase-cli/SKILL.md)** documents the `jrag` CLI; the MCP-surface skill **[`/explore-codebase`](./skills/explore-codebase/SKILL.md)** documents the legacy 5-tool MCP (PR-JRAG-5). See the [architecture diagram in `skills/README.md`](./skills/README.md#three-layer-architecture).
197
196
 
198
197
  ---
199
198
 
@@ -303,7 +302,7 @@ full design and per-PR breakdown.
303
302
  | [`docs/CONFIGURATION.md`](./docs/CONFIGURATION.md) | Environment variables, project YAML, graph ontology, brownfield overrides, ignore patterns. |
304
303
  | [`docs/JAVA-CODEBASE-RAG-CLI.md`](./docs/JAVA-CODEBASE-RAG-CLI.md) | CLI operator playbook: workflows, exit codes, env alignment. |
305
304
  | [`docs/EDGE-NAVIGATION.md`](./docs/EDGE-NAVIGATION.md) | MCP-traversable edges, directions, dot-key composition. |
306
- | [`skills/`](./skills/) | `/explore-codebase` (MCP surface) + `/explore-codebase-cli` (CLI surface) skills — operating manuals for hosts with skill discovery (alternative to copy-pasting AGENT-GUIDE). See [`skills/README.md`](./skills/README.md). |
305
+ | [`skills/`](./skills/) | `/explore-codebase-cli` (CLI surface) + `/explore-codebase` (legacy MCP surface) skills — operating manuals for hosts with skill discovery (alternative to copy-pasting AGENT-GUIDE). See [`skills/README.md`](./skills/README.md). |
307
306
  | [`docs/MANUAL-VERIFICATION-CHECKLIST.md`](./docs/MANUAL-VERIFICATION-CHECKLIST.md) | 7-phase agent-driven verification after indexing your project. |
308
307
  | [`docs/CODEBASE_REQUIREMENTS.md`](./docs/CODEBASE_REQUIREMENTS.md) | Assumptions about your Java repo + per-file edit map for non-conforming codebases. |
309
308
  | [`docs/PRODUCT-VISION.md`](./docs/PRODUCT-VISION.md) | Long-term product direction. |
@@ -1,17 +1,15 @@
1
1
  # java-codebase-rag
2
2
 
3
- A graph-native code intelligence layer for Java microservice estates — usable as an **MCP server** or a **CLI** (`jrag`), two surfaces over the same graph.
3
+ A graph-native code intelligence layer for Java microservice estates, surfaced through the **`jrag` CLI** one command per engineering intent. A **legacy MCP server** (`search` / `find` / `describe` / `neighbors` / `resolve`) is also available for existing setups. Both are thin surfaces over the same **AST Graph**: a deterministic property graph extracted from Java source with tree-sitter, stored **locally** in **LadybugDB** (graph) alongside a **LanceDB** vector index (chunks). There is no server to host and no cloud round-trip — the index lives on your disk and your source never leaves the machine. Both surfaces collapse onto three primitive operations: **locate**, **inspect**, **walk**.
4
4
 
5
- The system extracts a deterministic property graph from Java source (tree-sitter), stores it in **LadybugDB** (graph) alongside a **LanceDB** vector index (chunks), and exposes two agent surfaces, picked at install time (`java-codebase-rag install --surface mcp|cli`): the **MCP** surface ships five tools — `search`, `find`, `describe`, `neighbors`, `resolve` — over stdio; the **CLI** surface ships `jrag`, one command per engineering intent. Both collapse onto three primitive operations: **locate**, **inspect**, **walk**.
6
-
7
- > **What this MCP is:** a **GPS for code navigation**, not a reasoning engine.
5
+ > **What this is: a GPS for code navigation**, not a reasoning engine.
8
6
  > Agents use a simple loop:
9
7
  >
10
- > 1. **Locate** entry nodes (`search` / `find`, or identifier-shaped **`resolve`**)
11
- > 2. **Inspect** what a node is (`describe`)
12
- > 3. **Walk** one hop at a time (`neighbors`) until enough evidence is gathered
8
+ > 1. **Locate** entry nodes (`jrag find`, `jrag search`, or identifier-shaped lookup)
9
+ > 2. **Inspect** what a node is (`jrag inspect`)
10
+ > 3. **Walk** one hop at a time (`jrag callers` / `callees` / `hierarchy` / …) until enough evidence is gathered
13
11
  >
14
- > The MCP exposes structure and adjacency; the agent owns multi-hop reasoning and stop conditions.
12
+ > The tool exposes structure and adjacency; the agent owns multi-hop reasoning and stop conditions.
15
13
 
16
14
  For the design rationale, the GPS metaphor, and the full ontology, see [`docs/paper/paper.pdf`](./docs/paper/paper.pdf) (architecture report).
17
15
 
@@ -21,7 +19,7 @@ For the design rationale, the GPS metaphor, and the full ontology, see [`docs/pa
21
19
 
22
20
  Generic code-search tools (grep, ctags, vector-only RAG) hit a ceiling on real Java microservice estates: they find files but lose the structure that makes a Spring/JAX-RS system navigable. This project is built around five choices that target that gap.
23
21
 
24
- - **Hybrid RAG + GraphRAG, not either-or.** Semantic recall (LanceDB chunk vectors) and structural navigation (LadybugDB property graph) are composed in one surface. `search` finds candidate nodes by meaning; `neighbors` walks the exact edge you care about (`CALLS`, `IMPLEMENTS`, `INJECTS`, `EXPOSES`, …). The agent picks the right primitive per step instead of being forced into pure-vector or pure-symbol search.
22
+ - **Hybrid RAG + AST Graph, not either-or.** Semantic recall (LanceDB chunk vectors) and structural navigation (the LadybugDB AST Graph) are composed in one surface. `search` finds candidate nodes by meaning; `neighbors` walks the exact edge you care about (`CALLS`, `IMPLEMENTS`, `INJECTS`, `EXPOSES`, …). The agent picks the right primitive per step instead of being forced into pure-vector or pure-symbol search.
25
23
 
26
24
  - **A Java-tuned role model.** Symbols are labelled with stereotypes inferred from Spring and JAX-RS conventions — `CONTROLLER`, `SERVICE`, `REPOSITORY`, `COMPONENT`, `CONFIG`, `ENTITY`, `CLIENT`, `MAPPER`, `DTO`. Agents can ask "list controllers" or "who injects this repository" directly, instead of grep-ing for `@RestController` and hoping for the best. Roles drive both filtering (`find` with a `NodeFilter`) and ranking.
27
25
 
@@ -41,7 +39,7 @@ The rest of this README is the install, the tool/command orientation, and the re
41
39
  pip install java-codebase-rag
42
40
  ```
43
41
 
44
- 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.
42
+ Python **3.11+** required, on **Linux, macOS, and Windows**. On Linux, Windows, and **Apple Silicon** Macs every native dependency (LanceDB, LadybugDB, 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 search** over the symbol graph — BM25-ranked over a LadybugDB full-text index (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.
45
43
  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).
46
44
 
47
45
  ### Interactive setup (recommended)
@@ -77,19 +75,7 @@ If you prefer manual configuration, see [`docs/JAVA-CODEBASE-RAG-CLI.md`](./docs
77
75
 
78
76
  ## Tools & commands at a glance
79
77
 
80
- Pick a surface at install time `java-codebase-rag install --surface mcp|cli` (default `cli`, recommended). Both surfaces walk the same LanceDB vectors + LadybugDB graph. Switch an existing install later with `java-codebase-rag update --surface mcp|cli`.
81
-
82
- **MCP surface — five tools over stdio**
83
-
84
- | Tool | Purpose | Required args |
85
- |---|---|---|
86
- | `search` | Locate nodes by NL / code text. | `query` |
87
- | `find` | Locate nodes by structured filter. | `kind`, `filter` |
88
- | `describe` | Full record + edge counts for one node. | `id` |
89
- | `resolve` | Identifier-shaped lookup (FQN-collision-safe). Returns `one` / `many` / `none`. | `identifier` |
90
- | `neighbors` | Graph walk, one hop. | `ids`, `direction`, `edge_types` |
91
-
92
- Full schemas, `NodeFilter` / `EdgeFilter` semantics, and the hints contract live in [`docs/AGENT-GUIDE.md`](./docs/AGENT-GUIDE.md). Edge types and traversal directions are listed in [`docs/EDGE-NAVIGATION.md`](./docs/EDGE-NAVIGATION.md).
78
+ `jrag` is the default and recommended surface (`java-codebase-rag install --surface cli`). The **MCP server** (`--surface mcp`) is kept as a **legacy** option for existing setups. Both surfaces walk the same LanceDB vectors + LadybugDB **AST Graph**. Switch an existing install later with `java-codebase-rag update --surface mcp|cli`.
93
79
 
94
80
  **CLI surface — `jrag`, one command per engineering intent**
95
81
 
@@ -98,7 +84,7 @@ Full schemas, `NodeFilter` / `EdgeFilter` semantics, and the hints contract live
98
84
  jrag status # index health (ontology version, freshness, counts)
99
85
  jrag microservices # microservices with resolved type counts
100
86
  jrag map # counts per kind per service/module
101
- jrag map --module # group by module instead
87
+ jrag map --by module # group by module instead (--module filters)
102
88
  jrag conventions # dominant roles + framework tallies
103
89
  jrag overview chat-core # bundle for a microservice
104
90
  jrag overview /chat/assign # route flow (inbound callers + outbound CALLS)
@@ -146,9 +132,21 @@ jrag search "audit" --offset 5 # paginated
146
132
 
147
133
  Every `<query>` command takes human-readable identifiers (FQN / simple name / route path / topic) — never raw node IDs. Output contract, flags, and the resolve-first rule are in [`jrag` — agent CLI](#jrag--agent-cli) below.
148
134
 
135
+ **MCP surface — five tools over stdio (legacy)**
136
+
137
+ | Tool | Purpose | Required args |
138
+ |---|---|---|
139
+ | `search` | Locate nodes by NL / code text. | `query` |
140
+ | `find` | Locate nodes by structured filter. | `kind`, `filter` |
141
+ | `describe` | Full record + edge counts for one node. | `id` |
142
+ | `resolve` | Identifier-shaped lookup (FQN-collision-safe). Returns `one` / `many` / `none`. | `identifier` |
143
+ | `neighbors` | Graph walk, one hop. | `ids`, `direction`, `edge_types` |
144
+
145
+ Full schemas, `NodeFilter` / `EdgeFilter` semantics, and the hints contract live in [`docs/AGENT-GUIDE.md`](./docs/AGENT-GUIDE.md). Edge types and traversal directions are listed in [`docs/EDGE-NAVIGATION.md`](./docs/EDGE-NAVIGATION.md).
146
+
149
147
  ### Three-layer architecture
150
148
 
151
- Layer 1 (storage) → Layer 2 (5 MCP tools **or** the `jrag` CLI) → Layer 3 (skill). The MCP-surface skill **[`/explore-codebase`](./skills/explore-codebase/SKILL.md)** documents the 5-tool MCP; the CLI-surface skill **[`/explore-codebase-cli`](./skills/explore-codebase-cli/SKILL.md)** documents the `jrag` CLI (PR-JRAG-5). See the [architecture diagram in `skills/README.md`](./skills/README.md#three-layer-architecture).
149
+ Layer 1 (storage) → Layer 2 (the `jrag` CLI, **or** the legacy 5-tool MCP) → Layer 3 (skill). The CLI-surface skill **[`/explore-codebase-cli`](./skills/explore-codebase-cli/SKILL.md)** documents the `jrag` CLI; the MCP-surface skill **[`/explore-codebase`](./skills/explore-codebase/SKILL.md)** documents the legacy 5-tool MCP (PR-JRAG-5). See the [architecture diagram in `skills/README.md`](./skills/README.md#three-layer-architecture).
152
150
 
153
151
  ---
154
152
 
@@ -258,7 +256,7 @@ full design and per-PR breakdown.
258
256
  | [`docs/CONFIGURATION.md`](./docs/CONFIGURATION.md) | Environment variables, project YAML, graph ontology, brownfield overrides, ignore patterns. |
259
257
  | [`docs/JAVA-CODEBASE-RAG-CLI.md`](./docs/JAVA-CODEBASE-RAG-CLI.md) | CLI operator playbook: workflows, exit codes, env alignment. |
260
258
  | [`docs/EDGE-NAVIGATION.md`](./docs/EDGE-NAVIGATION.md) | MCP-traversable edges, directions, dot-key composition. |
261
- | [`skills/`](./skills/) | `/explore-codebase` (MCP surface) + `/explore-codebase-cli` (CLI surface) skills — operating manuals for hosts with skill discovery (alternative to copy-pasting AGENT-GUIDE). See [`skills/README.md`](./skills/README.md). |
259
+ | [`skills/`](./skills/) | `/explore-codebase-cli` (CLI surface) + `/explore-codebase` (legacy MCP surface) skills — operating manuals for hosts with skill discovery (alternative to copy-pasting AGENT-GUIDE). See [`skills/README.md`](./skills/README.md). |
262
260
  | [`docs/MANUAL-VERIFICATION-CHECKLIST.md`](./docs/MANUAL-VERIFICATION-CHECKLIST.md) | 7-phase agent-driven verification after indexing your project. |
263
261
  | [`docs/CODEBASE_REQUIREMENTS.md`](./docs/CODEBASE_REQUIREMENTS.md) | Assumptions about your Java repo + per-file edit map for non-conforming codebases. |
264
262
  | [`docs/PRODUCT-VISION.md`](./docs/PRODUCT-VISION.md) | Long-term product direction. |
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "java-codebase-rag"
7
- version = "0.9.6"
7
+ version = "0.10.0"
8
8
  description = "MCP server for semantic + structural search over Java codebases"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -32,21 +32,22 @@ classifiers = [
32
32
  # lexical (keyword) backend over the symbol graph (search_lexical.py); every other platform
33
33
  # is unchanged.
34
34
  dependencies = [
35
- "cocoindex[lancedb]>=1.0.7,<2; sys_platform != 'darwin' or platform_machine != 'x86_64'",
35
+ "cocoindex[lancedb]>=1.0.15,<2; sys_platform != 'darwin' or platform_machine != 'x86_64'",
36
36
  "ladybug>=0.17.1,<0.18",
37
- "lancedb>=0.25.3,<0.31; sys_platform != 'darwin' or platform_machine != 'x86_64'",
37
+ "lancedb>=0.34,<0.36; sys_platform != 'darwin' or platform_machine != 'x86_64'",
38
38
  "mcp>=1.27.0,<2",
39
39
  "numpy>=1.26.4,<2.5",
40
40
  "pathspec>=1.0.4,<2",
41
- "pyarrow>=23.0.1,<24",
41
+ "pyarrow>=23.0.1,<26",
42
42
  "pydantic>=2.0,<3",
43
43
  "PyYAML>=6.0.3,<7",
44
44
  "questionary>=2.0,<3",
45
- "rich>=14,<15",
45
+ "rich>=14,<16",
46
46
  "sentence-transformers>=5.4.0,<6; sys_platform != 'darwin' or platform_machine != 'x86_64'",
47
47
  "tree-sitter>=0.25.2,<0.26",
48
48
  "tree-sitter-java>=0.23.5,<0.24",
49
49
  "unidiff>=0.7.3,<1",
50
+ "watchdog>=6,<7",
50
51
  ]
51
52
 
52
53
  [project.optional-dependencies]
@@ -375,8 +375,13 @@ def get_vocabulary_index(graph: Any, cfg: Any) -> VocabularyIndex:
375
375
  # (JSONDecodeError/KeyError) — all subsumed by Exception; rebuild.
376
376
  log.debug(f"Vocab index missing/stale/corrupt ({e}), rebuilding from graph")
377
377
 
378
- # Build from graph
379
- index = VocabularyIndex.build(graph, q=cfg.absence_ngram_q)
378
+ # Build from graph. Coerce q to int (mirror absence_diagnosis.py's
379
+ # int(getattr(cfg, ...)) pattern): a non-int cfg.absence_ngram_q (e.g. a
380
+ # MagicMock cfg from a leaked test mock, or a YAML string) would otherwise
381
+ # crash _qgrams at `len(text) < q` ('int < MagicMock'). Default 3 = the
382
+ # config default for absence_ngram_q.
383
+ q = int(getattr(cfg, "absence_ngram_q", 3) or 3)
384
+ index = VocabularyIndex.build(graph, q=q)
380
385
 
381
386
  # Save to sidecar (best-effort)
382
387
  try:
@@ -377,6 +377,31 @@ def _route_ids_for_symbol(graph: Any, symbol_id: str) -> list[str]:
377
377
  return out
378
378
 
379
379
 
380
+ def _route_natural_id(graph: Any, rid: str) -> str:
381
+ """Map a raw Route node id to its agent-facing natural identifier.
382
+
383
+ Mirrors the envelope contract (``METHOD path`` for HTTP endpoints,
384
+ ``topic:<name>`` for kafka topics surfaced as :Route) so ``routes_touched``
385
+ in the PR risk report is readable instead of leaking raw graph ids like
386
+ ``r:970ffaa960a4f65d``. Falls back to ``rid`` only if the route vanished.
387
+ """
388
+ rows = graph._rows(
389
+ "MATCH (r:Route {id: $rid}) "
390
+ "RETURN r.method AS method, r.path_template AS path_template, "
391
+ "r.path AS path, r.topic AS topic LIMIT 1",
392
+ {"rid": rid},
393
+ )
394
+ if not rows:
395
+ return rid
396
+ r = rows[0]
397
+ method = str(r.get("method") or "")
398
+ path = str(r.get("path_template") or r.get("path") or "")
399
+ if method or path:
400
+ return f"{method} {path}".strip()
401
+ topic = str(r.get("topic") or "")
402
+ return f"topic:{topic}" if topic else rid
403
+
404
+
380
405
  def compute_risk(graph: Any, changed: list[ChangedSymbol]) -> PrRiskReport:
381
406
  """Aggregate blast radius, routes, cross-service callers, and v1 risk score.
382
407
 
@@ -415,7 +440,9 @@ def compute_risk(graph: Any, changed: list[ChangedSymbol]) -> PrRiskReport:
415
440
  needle = _impact_needle_for_changed(graph, fqn, cs.kind)
416
441
  ia = graph.impact_analysis(needle, depth=2, limit=400)
417
442
  n = len(ia)
418
- blast_by[cs.symbol_id] = n
443
+ # Key blast radius by the symbol's FQN (agent-facing identifier), not
444
+ # its raw graph id — the report is operator-facing JSON.
445
+ blast_by[fqn or cs.symbol_id] = n
419
446
  blast_total += n
420
447
 
421
448
  for e in graph.find_callers(cs.fqn, depth=2, limit=400):
@@ -429,8 +456,11 @@ def compute_risk(graph: Any, changed: list[ChangedSymbol]) -> PrRiskReport:
429
456
  cs_cross_service = 0
430
457
  route_ids = _route_ids_for_symbol(graph, cs.symbol_id)
431
458
  for rid in route_ids:
432
- if rid not in routes:
433
- routes.append(rid)
459
+ # Record the route by its natural identifier (METHOD path /
460
+ # topic:name), not the raw graph id.
461
+ label = _route_natural_id(graph, rid)
462
+ if label and label not in routes:
463
+ routes.append(label)
434
464
  callers = graph._rows(
435
465
  "MATCH (s:Symbol)-[:DECLARES_CLIENT]->(c:Client)-[e:HTTP_CALLS]->(r:Route {id: $rid}) "
436
466
  "WHERE e.match = 'cross_service' "
@@ -84,7 +84,8 @@ _DTO_LOMBOK_ANNOTATIONS: frozenset[str] = frozenset({
84
84
  # Phase 11: `EDGE_SCHEMA` in `java_ontology.py` (canonical edge navigation schema; v14 re-index).
85
85
  # Phase 12: CALLS `callee_declaring_role`, supertype-walk dedup, pass3 unresolved counters (v15 re-index).
86
86
  # Bumps whenever extraction / enrichment semantics change.
87
- ONTOLOGY_VERSION = 18
87
+ # Phase 13: Symbol.search_text + LadybugDB FTS (Okapi BM25) index for lexical search (v19 re-index).
88
+ ONTOLOGY_VERSION = 19
88
89
 
89
90
  ROLE_ANNOTATIONS: dict[str, str] = {
90
91
  # Spring Web
@@ -846,7 +846,15 @@ def _cmd_unresolved_calls_list(args: argparse.Namespace) -> int:
846
846
  callee_simple=args.callee_simple,
847
847
  limit=int(args.limit),
848
848
  )
849
- _emit({"success": True, "count": len(rows), "sites": rows})
849
+ # Drop the raw caller symbol id: the row already carries the agent-facing
850
+ # ``caller_fqn``, so ``caller_id`` is redundant noise in an operator-facing
851
+ # report. The call-site ``id`` (``ucs:``) is kept — it's each site's
852
+ # primary key, not a caller reference.
853
+ sites = [
854
+ {k: v for k, v in row.items() if k != "caller_id"}
855
+ for row in rows
856
+ ]
857
+ _emit({"success": True, "count": len(sites), "sites": sites})
850
858
  return 0
851
859
 
852
860
 
@@ -1060,12 +1068,7 @@ def build_parser() -> argparse.ArgumentParser:
1060
1068
  )
1061
1069
  _add_index_embedding_flags(erase)
1062
1070
  erase.add_argument("--yes", action="store_true", help="Confirm destructive deletion (required in CI)")
1063
- erase.add_argument(
1064
- "--quiet", "-q",
1065
- action="store_true",
1066
- dest="quiet",
1067
- help="Suppress stderr progress relay; stdout payload unchanged.",
1068
- )
1071
+ _add_verbosity_flags(erase)
1069
1072
  erase.set_defaults(handler=_cmd_erase)
1070
1073
 
1071
1074
  meta = subparsers.add_parser("meta", help="Print graph meta and embedding resolution.")
@@ -1162,8 +1165,20 @@ def _console_script_main() -> None:
1162
1165
  racy teardown — the command has already done its work and emitted its result.
1163
1166
  ``main()`` stays return-based so in-process test callers (``cli.main(...)``)
1164
1167
  keep working.
1168
+
1169
+ ``KeyboardInterrupt`` (Ctrl+C during a long indexing step) is caught here
1170
+ rather than left to propagate: an uncaught interrupt bypasses this function
1171
+ and runs full interpreter finalization (traceback + thread teardown),
1172
+ whereas routing it through the same flush + ``os._exit`` path gives a clean,
1173
+ immediate exit (code 130) and avoids the finalization-time SIGABRT noted
1174
+ above for commands that loaded lancedb.
1165
1175
  """
1166
- rc = main()
1176
+ try:
1177
+ rc = main()
1178
+ except KeyboardInterrupt:
1179
+ sys.stderr.write("\nInterrupted.\n")
1180
+ sys.stderr.flush()
1181
+ rc = 130
1167
1182
  sys.stdout.flush()
1168
1183
  sys.stderr.flush()
1169
1184
  os._exit(rc)
@@ -378,6 +378,14 @@ class ResolvedOperatorConfig:
378
378
  # used with no config file). Recorded into the index dir at index time so a
379
379
  # later discovery run from a sibling/cwd can relocate this config.
380
380
  yaml_config_path: Path | None = None
381
+ # ``watch:`` block knobs (jrag watch / watcher). Defaults make the block
382
+ # optional; no env vars are introduced for these (CLI flag > YAML > default).
383
+ watch_debounce_ms: int = 1500
384
+ watch_backend: str = "auto"
385
+ watch_poll_interval_ms: int = 2000
386
+ watch_debounce_ms_source: SettingSource = "default"
387
+ watch_backend_source: SettingSource = "default"
388
+ watch_poll_interval_ms_source: SettingSource = "default"
381
389
 
382
390
  def apply_to_os_environ(self) -> None:
383
391
  """Make downstream modules (server, ladybug_queries, flows) see a consistent environment.
@@ -506,16 +514,20 @@ def _pick_float(
506
514
 
507
515
  def _pick_int(
508
516
  *,
517
+ cli_val: int | None = None,
509
518
  env_key: str,
510
519
  yaml_dict: dict[str, Any],
511
520
  yaml_path: tuple[str, ...],
512
521
  default: int,
513
522
  ) -> tuple[int, SettingSource]:
514
- """Pick an int setting from env (parsed via int(...)), YAML, or default.
523
+ """Pick an int setting from CLI, env (parsed via int(...)), YAML, or default.
515
524
 
516
525
  Precedence: CLI > env > YAML > default. Env values that fail to parse as int
517
526
  fall back to the default (matching the brief's requirement for graceful degradation).
527
+ ``cli_val`` defaults to ``None`` so existing callers are unaffected.
518
528
  """
529
+ if cli_val is not None:
530
+ return int(cli_val), "cli"
519
531
  env_raw = os.environ.get(env_key, "").strip()
520
532
  if env_raw:
521
533
  try:
@@ -577,6 +589,9 @@ def resolve_operator_config(
577
589
  cli_index_dir: str | None = None,
578
590
  cli_embedding_model: str | None = None,
579
591
  cli_embedding_device: str | None = None,
592
+ cli_watch_debounce_ms: int | None = None,
593
+ cli_watch_backend: str | None = None,
594
+ cli_watch_poll_interval_ms: int | None = None,
580
595
  ) -> ResolvedOperatorConfig:
581
596
  # Phase 1: Find the config file directory
582
597
  if source_root is not None:
@@ -671,6 +686,52 @@ def resolve_operator_config(
671
686
  yaml_path=("absence", "diag_enabled"),
672
687
  default=True,
673
688
  )
689
+ # ``watch:`` block knobs. No env vars are introduced for watch (CLI > YAML >
690
+ # default), so an empty env_key is passed to the ``_pick_*`` helpers —
691
+ # ``os.environ.get("", "")`` never matches, leaving the env tier inert.
692
+ w_debounce, w_debounce_src = _pick_int(
693
+ cli_val=cli_watch_debounce_ms,
694
+ env_key="",
695
+ yaml_dict=yaml_dict,
696
+ yaml_path=("watch", "debounce_ms"),
697
+ default=1500,
698
+ )
699
+ w_backend, w_backend_src = _pick_str(
700
+ cli_val=cli_watch_backend,
701
+ env_key="",
702
+ yaml_dict=yaml_dict,
703
+ yaml_path=("watch", "backend"),
704
+ default="auto",
705
+ )
706
+ w_poll, w_poll_src = _pick_int(
707
+ cli_val=cli_watch_poll_interval_ms,
708
+ env_key="",
709
+ yaml_dict=yaml_dict,
710
+ yaml_path=("watch", "poll_interval_ms"),
711
+ default=2000,
712
+ )
713
+ # Inline floors/validation (mirror the existing graceful-degradation style).
714
+ if w_debounce < 100:
715
+ print(
716
+ f"java-codebase-rag: watch.debounce_ms={w_debounce} is below the 100 ms "
717
+ "floor; falling back to 1500.",
718
+ file=sys.stderr,
719
+ )
720
+ w_debounce, w_debounce_src = 1500, "default"
721
+ if w_backend not in ("auto", "watchdog", "polling"):
722
+ print(
723
+ f"java-codebase-rag: watch.backend={w_backend!r} is not one of "
724
+ "auto/watchdog/polling; falling back to 'auto'.",
725
+ file=sys.stderr,
726
+ )
727
+ w_backend, w_backend_src = "auto", "default"
728
+ if w_poll < 200:
729
+ print(
730
+ f"java-codebase-rag: watch.poll_interval_ms={w_poll} is below the 200 ms "
731
+ "floor; falling back to 2000.",
732
+ file=sys.stderr,
733
+ )
734
+ w_poll, w_poll_src = 2000, "default"
674
735
  ku = index_dir / "code_graph.lbug"
675
736
  coco = index_dir / "cocoindex.db"
676
737
  return ResolvedOperatorConfig(
@@ -696,6 +757,12 @@ def resolve_operator_config(
696
757
  absence_ngram_q_source=abs_q_src,
697
758
  absence_diag_enabled_source=abs_diag_src,
698
759
  yaml_config_path=find_yaml_config_file(config_dir),
760
+ watch_debounce_ms=w_debounce,
761
+ watch_backend=w_backend,
762
+ watch_poll_interval_ms=w_poll,
763
+ watch_debounce_ms_source=w_debounce_src,
764
+ watch_backend_source=w_backend_src,
765
+ watch_poll_interval_ms_source=w_poll_src,
699
766
  )
700
767
 
701
768