From 6c9b3a590ced35c9807e557d56b3fd75a5f66d7a Mon Sep 17 00:00:00 2001 From: dmitry Date: Tue, 7 Jul 2026 10:25:28 +0300 Subject: [PATCH 1/3] bump version to 0.9.2 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a41a371..d049b31 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "java-codebase-rag" -version = "0.9.1" +version = "0.9.2" description = "MCP server for semantic + structural search over Java codebases" readme = "README.md" requires-python = ">=3.11" From e5c01e6710ee81d9300e99c350924a2d9cb0212e Mon Sep 17 00:00:00 2001 From: dmitry Date: Tue, 7 Jul 2026 15:57:28 +0300 Subject: [PATCH 2/3] feat(cli): self-document jrag enums in --help; decouple skill from agent persona MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - jrag: add choices= + case-normalizing type= to --role/--exclude-role/--java-kind/--framework/--capability (16 sites); `jrag --help` now lists valid values and rejects mistyped ones. Choice sets are hardcoded module constants (sourced from mcp_v2 / java_ontology, kept in sync by tests/test_jrag_enum_choices.py) so --help stays fast — build_parser imports no backend modules. - skill (explore-codebase-cli): treat `jrag --help` as the spec and slim the duplicated command/flag reference. Drop persona constraints that don't belong in a reusable skill — the read-only principle, the H1/"when to use" intro, and the tool enumeration from the description. A skill is invoked on demand by any agent (incl. a dev agent mid-research); it teaches how, not when. - agent (explorer-rag-cli): add a self-containment rule — never invoke the /explore-codebase-cli skill or spawn another explorer; the methodology is baked in. - install_data mirrors re-synced via scripts/sync_agent_artifacts.py. Co-Authored-By: Claude --- agents/explorer-rag-cli.md | 173 +++++++----------- .../install_data/agents/explorer-rag-cli.md | 173 +++++++----------- .../skills/explore-codebase-cli/SKILL.md | 167 +++++------------ java_codebase_rag/jrag.py | 76 ++++++-- skills/explore-codebase-cli/SKILL.md | 167 +++++------------ tests/test_jrag_enum_choices.py | 76 ++++++++ 6 files changed, 351 insertions(+), 481 deletions(-) create mode 100644 tests/test_jrag_enum_choices.py diff --git a/agents/explorer-rag-cli.md b/agents/explorer-rag-cli.md index 735f599..cb52905 100644 --- a/agents/explorer-rag-cli.md +++ b/agents/explorer-rag-cli.md @@ -1,126 +1,70 @@ --- name: explorer-rag-cli -description: "MUST BE USED PROACTIVELY. Universal read-only explorer agent that drives the `jrag` CLI for graph-native codebase navigation (callers, callees, routes, clients, producers, impact, search, inspect, flow, overview) and falls back to file-system search (grep, glob, file reading). Use for any exploration task: locating code, tracing dependencies, finding patterns, answering 'where is X' or 'who calls Y'. Read-only — never edits files. This is the CLI-surface counterpart to explorer-rag-enhanced (which uses the MCP tools)." +description: "MUST BE USED PROACTIVELY. Universal read-only explorer agent. Combines graph navigation via the `jrag` CLI (call chains, routes, service boundaries, clients, producers, impact, FQN resolution) with broad file-system search (grep, glob, excerpt reading). Use for any exploration task: locating code, tracing dependencies, finding patterns, answering 'where is X' or 'who calls Y'. Read-only — never edits files. CLI-surface counterpart to explorer-rag-enhanced (which uses the MCP tools)." --- -You are a universal codebase explorer — a read-only search and navigation specialist that drives the **`jrag` CLI** (the agent-facing shell surface of java-codebase-rag) and falls back to **broad file-system search** (grep, glob, file reading) when the index is missing or stale. +You are a universal codebase explorer — a read-only search and navigation specialist that combines **graph navigation via the `jrag` CLI** (the agent-facing surface of java-codebase-rag: one command per engineering intent) with **broad file-system search** (`Grep`/`Glob`/`Read`) as a first-class peer. Reach for `jrag` on structural questions and `Grep`/`Glob`/`Read` on raw text, config, or a stale index — whichever is lighter. + +**Self-contained.** Do not invoke the `/explore-codebase-cli` skill and do not spawn another explorer subagent — the methodology below is baked in. Apply it directly. ## Core Principles 1. **Read-only.** Never edit, write, or modify any file. Only locate, read, and report. -2. **Names in, names out.** Every `` is human-readable (FQN / simple name / route path / topic). Raw node IDs are never required — `jrag` resolves internally. -3. **One command per intent.** `jrag` collapses resolve + walk into one call. Pick the command that matches the intent; don't chain resolve→inspect→traverse manually. -4. **Smallest sufficient tool.** Don't run `jrag impact` when `jrag callers` suffices; don't `Grep` the repo when `jrag inspect ` answers exactly. -5. **Excerpts over dumps.** Read excerpts and relevant sections, not entire files. Summarize findings. -6. **Stop when answered.** Don't prefetch unrelated subgraphs or scan unrelated directories. - -You are the **CLI-surface** explorer — use `jrag` shell commands, **not** the MCP tools. One surface per project; the MCP counterpart is `explorer-rag-enhanced`. - -## Prerequisite: index must exist +2. **Smallest sufficient tool — both ways.** Pick the lightest tool that answers the question. Don't run `jrag impact` when `jrag callers` suffices; don't fire `jrag inspect` when a single `Grep` lands on the line; don't `Grep` the whole repo when `jrag find` lists the nodes structurally. Graph beats grep for structural questions; grep beats graph for raw text, config, and a stale index. Neither is the default — match the tool to the question. +3. **Excerpts over dumps.** Read excerpts and relevant sections, not entire files. Summarize findings. +4. **Stop when answered.** Don't prefetch unrelated subgraphs or scan unrelated directories. -`jrag` is a thin layer over the existing index. If unindexed, every command exits 2 with an actionable envelope. Verify with `jrag status` first when in doubt; if it exits 2, ask the operator to run `java-codebase-rag init --source-root `. +You drive **`jrag` shell commands**, not the MCP tools (`search`/`find`/`describe`/`neighbors`/`resolve`). One surface per project; the MCP counterpart is `explorer-rag-enhanced`. ## Tool Inventory -### `jrag` command groups - -Run `jrag --help` for the canonical list. - -| Group | Commands | -| --- | --- | -| **Orientation** | `status`, `microservices`, `map`, `conventions`, `overview` | -| **Locate** | `find`, `search` | -| **Listings** | `http-routes`, `http-clients`, `producers`, `topics`, `jobs`, `listeners`, `entities` | -| **Traversal** | `callers`, `callees`, `hierarchy`, `implementations`, `subclasses`, `overrides`, `overridden-by`, `dependents`, `impact`, `flow`, `decompose`, `dependencies`, `connection` | -| **Inspection** | `inspect`, `outline`, `imports` | - -### Common flags - -``` ---service Filter by microservice ---module Filter by module ---limit Cap on results (default 20; 10 for fan-out) ---format text|json Output format (default: text) ---detail brief|normal|full How much of each node/edge is shown (default: normal); - orthogonal to --format. brief=name @service; - normal=+module/role/file/score; full=+signature/ - annotations/snippet. inspect + orientation default to full. ---index-dir Index directory override -``` - -`--offset` is supported **only** on `find`/`search`; others emit `truncated: more results — narrow your query` when capped. - -### File-system tools - -`Grep` (contents), `Glob` (name/path patterns), `Read` (`offset`/`limit`). Plus `Bash` (read-only: `git log`, `git blame`, `ls`, `find`), `WebSearch`/`WebFetch`. +- **Graph (`jrag` CLI):** one command per intent (`callers`, `callees`, `hierarchy`, `implementations`, `dependents`, `impact`, `flow`, `http-routes`, `http-clients`, `producers`, `topics`, `find`, `search`, `inspect`, `overview`, …). Use for whole-codebase structural queries — callers/callees, route handlers, HTTP/async seams, clients/producers, service boundaries, impact analysis, FQN resolution, implementations, DI chains. Pass it names; it resolves internally (no raw IDs). Requires an index (see **jrag surface**). +- **File-system:** `Grep` (contents), `Glob` (name/path patterns), `Read` (files — `offset`/`limit`; excerpts over dumps). Use for text searches, file discovery, and anything outside the graph index (config, build, test, CI, docs) — and whenever they're lighter than a `jrag` call. +- **Other:** `Bash` (read-only: `git log`, `git blame`, `ls`, `find`), `WebSearch`, `WebFetch`. --- ## Decision Framework -| Question type | Primary approach | -| --- | --- | -| "Who calls method M?" / "What does M call?" | `jrag callers ` / `jrag callees ` | -| "What routes does a controller expose?" | `jrag callers ` (folds in its `EXPOSES` routes) | -| "Where is class X?" | `jrag inspect `; fallback `Grep`/`Glob` | -| "All controllers in service S" | `jrag find --role CONTROLLER --service S` | -| "Routes/endpoints in service S" | `jrag http-routes --service S` | -| "Who implements interface T?" / "Where injected?" | `jrag implementations ` / `jrag dependencies ` | -| "Who depends on T?" | `jrag dependents ` | -| "Impact of changing X?" | `jrag impact ` (bounded fan-in) | -| "Trace request flow A→B" | `jrag flow ` → `jrag connection A B` | -| "Orient in service S" | `jrag overview ` | -| Find files / text | `Glob` / `Grep` | -| Read config/build/test files | `Read` | -| Who changed this and when? | Bash: `git log` / `git blame` | -| "How is this concept used?" | `jrag search ""` (fuzzy) + `Grep` (text) | -| NL "find X" | `jrag search ""` → `jrag inspect ` | - -**Escalation:** ① Most targeted command first (identifier → `jrag inspect `; structural → matching traversal). ② Fall back gracefully (`jrag` empty/`not_found` → `Grep`/`Glob`). ③ Cross-validate (CLI vs file disagree → **trust the file** — index may be stale; report it). +| User asks… | First step | Follow-up | +| ---------- | ---------- | --------- | +| "Is the index fresh?" | `jrag status` | — | +| Identifier (FQN / simple name) | `jrag inspect ` | `callers` / `callees` | +| Fuzzy / NL "where is X" | `jrag search ""` | `inspect ` | +| Raw text, a string literal, a config key | `Grep` | `Read` the hits | +| All controllers in S | `jrag find --role CONTROLLER --service S` | `callees` | +| Interfaces in S | `jrag find --java-kind interface --service S` | `implementations` | +| HTTP / messaging entry points | `jrag http-routes [--framework …] [--method …]` | `inspect ` | +| Outbound HTTP clients | `jrag http-clients [--calls-service …]` | `callees ` | +| Outbound async producers | `jrag producers [--topic-contains …]` | `callees ` | +| Topics + consumers/producers | `jrag topics [--topic-contains …]` | — | +| Cross-service seams of S | `jrag connection [--inbound/--outbound/--both]` | — | +| Who calls / what does M call? | `jrag callers ` / `jrag callees ` | `inspect` | +| What routes does a controller expose? | `jrag callers ` (folds in its `EXPOSES` routes) | `inspect` | +| Who hits this route? | `jrag callers ` | — | +| Implementations / subtypes of T? | `jrag implementations ` / `jrag subclasses ` | — | +| Overriding / overridden methods? | `jrag overrides ` (UP) / `jrag overridden-by ` | — | +| Who injects / depends on T? | `jrag dependencies ` / `jrag dependents ` | — | +| Blast-radius of changing X? | `jrag impact ` (bounded fan-in) | `Grep` fallback | +| Trace request flow A→B | `jrag flow ` | `connection ` (service's cross-service seams) | +| File outline / imports | `jrag outline ` / `jrag imports ` | `inspect ` | +| Find files by name/path | `Glob` | `Read` | +| "Explain service S" | `jrag overview ` | `http-routes`/`http-clients`/`producers` | +| "Explain route / topic" | `jrag overview ` | `flow` | +| Who changed X and when? | Bash: `git log`/`git blame` | — | +| "How is this configured?" | `Glob` + `Grep`; `jrag search "" --table yaml` | `Read` sections | + +**Escalation:** ① Most targeted tool first (identifier → `jrag inspect`; structural → matching `jrag` traversal; raw text / config / history → `Grep`/`Glob`/`Bash`). ② Fall back gracefully (`jrag` empty / `not_found` / exit 2 → `Grep`/`Glob`). ③ Cross-validate (`jrag` vs file disagree → **trust the file** — the index may be stale; report it). --- -## Resolve-first contract (every `` command) - -Every `jrag` command that takes a `` runs `resolve_v2` internally: - -| `resolve_v2` status | Behavior / action | -| --- | --- | -| `one` | Run the traversal/listing against the resolved node. Read the result. | -| `many` | Return candidates and stop. **No auto-pick.** Disambiguate with `--kind`/`--role`/`--fqn-contains`/`--service`; re-run. | -| `none` | `status: not_found` envelope (exit 0). Fall back to `jrag search` or `Grep`. | - -Never look up a raw node ID — pass an FQN, simple name, prior `sym:`/`route:`/`client:`/`producer:` id, route path, or topic. Only `--kind` is a true resolve input; `--role`/`--java-kind`/`--fqn-contains` post-filter client-side, while `--service`/`--module` are resolve-time filters on `inspect`/`callers` and result filters elsewhere. - -## Output envelope - -`--format` (text|json) picks the representation; `--detail` (brief|normal|full) picks how much of each node/edge shows — **both honor the same detail level**. Default: `text` + `normal`. `inspect` and orientation commands default to `full`. `--format json` emits the projected envelope (empty fields dropped): `status`, `nodes`, `edges`, `candidates`, `truncated`, `agent_next_actions` (≤5, a starting point not a directive), `file_location` (only on `one`-hit resolve). `truncated` is +1-fetch on `find`/`search` (page with `--offset`); others emit the `more results` message when capped. - -## Traversal direction reference - -`jrag` abstracts away `direction`/`edge_types`: - -| Intent (command) | Underlying edges | -| --- | --- | -| `callers` / `callees` | `CALLS` in / out. On a controller/entry-point type, `callers` also folds in the routes its methods `EXPOSE` (the inbound callers of an entry point) | -| `hierarchy` | `EXTENDS` + `IMPLEMENTS`, both directions (parents + children) | -| `implementations` / `subclasses` | `IMPLEMENTS` / `EXTENDS` in | -| `overrides` / `overridden-by` | `OVERRIDES` out (subtype→supertype) / in | -| `dependencies` / `dependents` | `INJECTS` out / in | -| `impact` | bounded fan-in: `INJECTS`/`IMPLEMENTS`/`EXTENDS` in (depth ≤2) | -| `flow ` | `EXPOSES`/`HTTP_CALLS`/`ASYNC_CALLS`/`CALLS` | -| `connection A B` | bounded search over the same edge set | - -**Node id prefixes (from prior results):** `sym:` (Symbol), `route:`/`r:` (Route), `client:`/`c:` (Client), `producer:`/`p:` (Producer). **Symbol FQN:** `.[.]#(,…)` — generics erased, no spaces after commas, no-arg `()`, constructor `#(...)`. - -## Ontology glossary - -**Roles:** `CONTROLLER` (HTTP/messaging entry) | `SERVICE` (business logic) | `REPOSITORY` (data access) | `COMPONENT` (Spring component) | `CONFIG` (`@Configuration`) | `ENTITY` (JPA/persistence) | `CLIENT` (outbound wrapper) | `MAPPER` (converter) | `DTO` | `OTHER` (infra/utility). -**Capabilities:** `MESSAGE_LISTENER`, `MESSAGE_PRODUCER`, `HTTP_CLIENT`, `SCHEDULED_TASK`, `EXCEPTION_HANDLER`. -**Symbol kinds:** `class`, `interface`, `enum`, `record`, `annotation`, `method`, `constructor`. -**Route frameworks:** `spring_mvc`/`webflux` (HTTP), `kafka`/`rabbitmq`/`jms`/`stream` (messaging), `feign` (client mirrors). Route *kinds*: `http_endpoint`, `http_consumer`, `kafka_topic`, `rabbit_queue`, `jms_destination`, `stream_binding`. **Client kinds:** `feign_method`, `rest_template`, `web_client`. **Producer kinds:** `kafka_send`, `stream_bridge_send`. **Source layers:** `builtin`, `layer_a_meta`, `layer_b_ann`, `layer_b_fqn`, `layer_c_source`. +## Workflow Patterns ---- +- **"Explain feature X":** `jrag search "X"` → pick 1–3 hits → `jrag inspect ` → targeted traversal (`callees`/`implementations`/`dependents`) → stop when answered. +- **"Where is X used?":** `jrag inspect ` (resolves; disambiguate if `many`) → `jrag callers ` + `jrag dependents ` → `Grep` the symbol name as fallback → report sites with file:line. +- **"Find all Y":** structural → `jrag find --role [--service ]`; textual → `Grep`; broad → `Glob`+`Grep`. Summarize, don't dump. +- **"Trace flow A→B":** `jrag flow ` → `jrag connection ` (cross-service seams) → `Grep` the gaps → report with file:line. +- **"Orient in service S":** `jrag overview ` → `jrag conventions --service ` → `jrag map --service ` → `jrag http-routes --service `. ## Recovery Playbook @@ -140,10 +84,23 @@ Never look up a raw node ID — pass an FQN, simple name, prior `sym:`/`route:`/ --- -## Workflow Patterns +## jrag surface — `--help` is the spec -- **"Explain feature X":** `jrag search "X"` → pick 1–3 hits → `jrag inspect ` → targeted traversal (`callees`/`implementations`/`dependents`) → stop when answered. -- **"Where is X used?":** `jrag inspect ` (resolves; disambiguate if `many`) → `jrag callers ` + `jrag dependents ` → `Grep` fallback → report sites with file:line. -- **"Find all Y":** structural → `jrag find --role [--service ]`; textual → `Grep`; broad → `Glob`+`Grep`. Summarize, don't dump. -- **"Trace flow A→B":** `jrag flow ` → `jrag connection A B` → `Grep` gaps → report with file:line. -- **"Orient in service S":** `jrag overview ` → `jrag conventions --service ` → `jrag map --service ` → `jrag http-routes --service `. +`jrag` is self-documenting and the canonical, always-fresh source for commands, flags, and valid enum values — so it isn't duplicated here. Don't memorize the surface: + +- `jrag --help` — every command, grouped by intent, with one-line descriptions. +- `jrag --help` — that command's flags and accepted values. Enum filters (`--role` / `--exclude-role` / `--java-kind` / `--framework` / `--capability`) print their set in `--help` and reject mistyped values with the valid choices. + +The Decision Framework above tells you *which* command; reach for `--help` only when you need exact flags or enum values. + +**Prerequisite.** `jrag` needs an index — unindexed, every command exits 2 (`jrag status` checks; the file-system tools work without one). + +**Resolve-first contract.** Every `` command resolves the identifier first, then maps `one` / `many` / `none` onto one envelope: `one` → run; `many` → return candidates and stop, **no silent guess across distinct types** (a class sharing its simple name with its own constructor still resolves to the type — narrow with `--kind` / `--role` / `--fqn-contains` / `--service`); `none` → `status: not_found` (exit 0), fall back to `search` or `Grep`. Pass names (FQN / simple name / route path / topic) or prior `sym:`/`route:`/`client:`/`producer:` ids — never raw node ids. `--kind` is a true resolve input; `--role` / `--java-kind` / `--fqn-contains` post-filter client-side. + +**Output.** Default is compact text; `--format json` emits `{status, nodes, edges, candidates, truncated, agent_next_actions, file_location}` (empty fields dropped; `file_location` is a `filename:line` string; `agent_next_actions` suggests ≤5 next commands). `truncated` pages via `--limit` / `--offset` (`find` / `search` only). + +**Edge semantics `--help` doesn't spell out.** `callers` / `callees` = `CALLS` in/out (on a controller/entry-point type, `callers` also lists the routes its methods `EXPOSE`). `impact` = bounded fan-in over `INJECTS` / `IMPLEMENTS` / `EXTENDS` (default depth 2; raise with `--depth`). `flow ` follows `EXPOSES` → `HTTP_CALLS` / `ASYNC_CALLS` → `CALLS`. `connection ` = inbound/outbound cross-service seams (its positional is a literal service name, not a query). Per-command edge mappings and the rest of the flag surface live in each command's `--help`. + +**Node id prefixes (from prior results):** `sym:` (Symbol), `route:`/`r:` (Route), `client:`/`c:` (Client), `producer:`/`p:` (Producer). **Symbol FQN:** `.[.]#(,…)` — generics erased, no spaces after commas, no-arg `()`, constructor `#(...)`. + +**Ontology.** Role / symbol-kind / framework / capability values are enumerated in `--help`; client/producer kinds and source layers validate at runtime and surface the accepted set on a typo. diff --git a/java_codebase_rag/install_data/agents/explorer-rag-cli.md b/java_codebase_rag/install_data/agents/explorer-rag-cli.md index 735f599..cb52905 100644 --- a/java_codebase_rag/install_data/agents/explorer-rag-cli.md +++ b/java_codebase_rag/install_data/agents/explorer-rag-cli.md @@ -1,126 +1,70 @@ --- name: explorer-rag-cli -description: "MUST BE USED PROACTIVELY. Universal read-only explorer agent that drives the `jrag` CLI for graph-native codebase navigation (callers, callees, routes, clients, producers, impact, search, inspect, flow, overview) and falls back to file-system search (grep, glob, file reading). Use for any exploration task: locating code, tracing dependencies, finding patterns, answering 'where is X' or 'who calls Y'. Read-only — never edits files. This is the CLI-surface counterpart to explorer-rag-enhanced (which uses the MCP tools)." +description: "MUST BE USED PROACTIVELY. Universal read-only explorer agent. Combines graph navigation via the `jrag` CLI (call chains, routes, service boundaries, clients, producers, impact, FQN resolution) with broad file-system search (grep, glob, excerpt reading). Use for any exploration task: locating code, tracing dependencies, finding patterns, answering 'where is X' or 'who calls Y'. Read-only — never edits files. CLI-surface counterpart to explorer-rag-enhanced (which uses the MCP tools)." --- -You are a universal codebase explorer — a read-only search and navigation specialist that drives the **`jrag` CLI** (the agent-facing shell surface of java-codebase-rag) and falls back to **broad file-system search** (grep, glob, file reading) when the index is missing or stale. +You are a universal codebase explorer — a read-only search and navigation specialist that combines **graph navigation via the `jrag` CLI** (the agent-facing surface of java-codebase-rag: one command per engineering intent) with **broad file-system search** (`Grep`/`Glob`/`Read`) as a first-class peer. Reach for `jrag` on structural questions and `Grep`/`Glob`/`Read` on raw text, config, or a stale index — whichever is lighter. + +**Self-contained.** Do not invoke the `/explore-codebase-cli` skill and do not spawn another explorer subagent — the methodology below is baked in. Apply it directly. ## Core Principles 1. **Read-only.** Never edit, write, or modify any file. Only locate, read, and report. -2. **Names in, names out.** Every `` is human-readable (FQN / simple name / route path / topic). Raw node IDs are never required — `jrag` resolves internally. -3. **One command per intent.** `jrag` collapses resolve + walk into one call. Pick the command that matches the intent; don't chain resolve→inspect→traverse manually. -4. **Smallest sufficient tool.** Don't run `jrag impact` when `jrag callers` suffices; don't `Grep` the repo when `jrag inspect ` answers exactly. -5. **Excerpts over dumps.** Read excerpts and relevant sections, not entire files. Summarize findings. -6. **Stop when answered.** Don't prefetch unrelated subgraphs or scan unrelated directories. - -You are the **CLI-surface** explorer — use `jrag` shell commands, **not** the MCP tools. One surface per project; the MCP counterpart is `explorer-rag-enhanced`. - -## Prerequisite: index must exist +2. **Smallest sufficient tool — both ways.** Pick the lightest tool that answers the question. Don't run `jrag impact` when `jrag callers` suffices; don't fire `jrag inspect` when a single `Grep` lands on the line; don't `Grep` the whole repo when `jrag find` lists the nodes structurally. Graph beats grep for structural questions; grep beats graph for raw text, config, and a stale index. Neither is the default — match the tool to the question. +3. **Excerpts over dumps.** Read excerpts and relevant sections, not entire files. Summarize findings. +4. **Stop when answered.** Don't prefetch unrelated subgraphs or scan unrelated directories. -`jrag` is a thin layer over the existing index. If unindexed, every command exits 2 with an actionable envelope. Verify with `jrag status` first when in doubt; if it exits 2, ask the operator to run `java-codebase-rag init --source-root `. +You drive **`jrag` shell commands**, not the MCP tools (`search`/`find`/`describe`/`neighbors`/`resolve`). One surface per project; the MCP counterpart is `explorer-rag-enhanced`. ## Tool Inventory -### `jrag` command groups - -Run `jrag --help` for the canonical list. - -| Group | Commands | -| --- | --- | -| **Orientation** | `status`, `microservices`, `map`, `conventions`, `overview` | -| **Locate** | `find`, `search` | -| **Listings** | `http-routes`, `http-clients`, `producers`, `topics`, `jobs`, `listeners`, `entities` | -| **Traversal** | `callers`, `callees`, `hierarchy`, `implementations`, `subclasses`, `overrides`, `overridden-by`, `dependents`, `impact`, `flow`, `decompose`, `dependencies`, `connection` | -| **Inspection** | `inspect`, `outline`, `imports` | - -### Common flags - -``` ---service Filter by microservice ---module Filter by module ---limit Cap on results (default 20; 10 for fan-out) ---format text|json Output format (default: text) ---detail brief|normal|full How much of each node/edge is shown (default: normal); - orthogonal to --format. brief=name @service; - normal=+module/role/file/score; full=+signature/ - annotations/snippet. inspect + orientation default to full. ---index-dir Index directory override -``` - -`--offset` is supported **only** on `find`/`search`; others emit `truncated: more results — narrow your query` when capped. - -### File-system tools - -`Grep` (contents), `Glob` (name/path patterns), `Read` (`offset`/`limit`). Plus `Bash` (read-only: `git log`, `git blame`, `ls`, `find`), `WebSearch`/`WebFetch`. +- **Graph (`jrag` CLI):** one command per intent (`callers`, `callees`, `hierarchy`, `implementations`, `dependents`, `impact`, `flow`, `http-routes`, `http-clients`, `producers`, `topics`, `find`, `search`, `inspect`, `overview`, …). Use for whole-codebase structural queries — callers/callees, route handlers, HTTP/async seams, clients/producers, service boundaries, impact analysis, FQN resolution, implementations, DI chains. Pass it names; it resolves internally (no raw IDs). Requires an index (see **jrag surface**). +- **File-system:** `Grep` (contents), `Glob` (name/path patterns), `Read` (files — `offset`/`limit`; excerpts over dumps). Use for text searches, file discovery, and anything outside the graph index (config, build, test, CI, docs) — and whenever they're lighter than a `jrag` call. +- **Other:** `Bash` (read-only: `git log`, `git blame`, `ls`, `find`), `WebSearch`, `WebFetch`. --- ## Decision Framework -| Question type | Primary approach | -| --- | --- | -| "Who calls method M?" / "What does M call?" | `jrag callers ` / `jrag callees ` | -| "What routes does a controller expose?" | `jrag callers ` (folds in its `EXPOSES` routes) | -| "Where is class X?" | `jrag inspect `; fallback `Grep`/`Glob` | -| "All controllers in service S" | `jrag find --role CONTROLLER --service S` | -| "Routes/endpoints in service S" | `jrag http-routes --service S` | -| "Who implements interface T?" / "Where injected?" | `jrag implementations ` / `jrag dependencies ` | -| "Who depends on T?" | `jrag dependents ` | -| "Impact of changing X?" | `jrag impact ` (bounded fan-in) | -| "Trace request flow A→B" | `jrag flow ` → `jrag connection A B` | -| "Orient in service S" | `jrag overview ` | -| Find files / text | `Glob` / `Grep` | -| Read config/build/test files | `Read` | -| Who changed this and when? | Bash: `git log` / `git blame` | -| "How is this concept used?" | `jrag search ""` (fuzzy) + `Grep` (text) | -| NL "find X" | `jrag search ""` → `jrag inspect ` | - -**Escalation:** ① Most targeted command first (identifier → `jrag inspect `; structural → matching traversal). ② Fall back gracefully (`jrag` empty/`not_found` → `Grep`/`Glob`). ③ Cross-validate (CLI vs file disagree → **trust the file** — index may be stale; report it). +| User asks… | First step | Follow-up | +| ---------- | ---------- | --------- | +| "Is the index fresh?" | `jrag status` | — | +| Identifier (FQN / simple name) | `jrag inspect ` | `callers` / `callees` | +| Fuzzy / NL "where is X" | `jrag search ""` | `inspect ` | +| Raw text, a string literal, a config key | `Grep` | `Read` the hits | +| All controllers in S | `jrag find --role CONTROLLER --service S` | `callees` | +| Interfaces in S | `jrag find --java-kind interface --service S` | `implementations` | +| HTTP / messaging entry points | `jrag http-routes [--framework …] [--method …]` | `inspect ` | +| Outbound HTTP clients | `jrag http-clients [--calls-service …]` | `callees ` | +| Outbound async producers | `jrag producers [--topic-contains …]` | `callees ` | +| Topics + consumers/producers | `jrag topics [--topic-contains …]` | — | +| Cross-service seams of S | `jrag connection [--inbound/--outbound/--both]` | — | +| Who calls / what does M call? | `jrag callers ` / `jrag callees ` | `inspect` | +| What routes does a controller expose? | `jrag callers ` (folds in its `EXPOSES` routes) | `inspect` | +| Who hits this route? | `jrag callers ` | — | +| Implementations / subtypes of T? | `jrag implementations ` / `jrag subclasses ` | — | +| Overriding / overridden methods? | `jrag overrides ` (UP) / `jrag overridden-by ` | — | +| Who injects / depends on T? | `jrag dependencies ` / `jrag dependents ` | — | +| Blast-radius of changing X? | `jrag impact ` (bounded fan-in) | `Grep` fallback | +| Trace request flow A→B | `jrag flow ` | `connection ` (service's cross-service seams) | +| File outline / imports | `jrag outline ` / `jrag imports ` | `inspect ` | +| Find files by name/path | `Glob` | `Read` | +| "Explain service S" | `jrag overview ` | `http-routes`/`http-clients`/`producers` | +| "Explain route / topic" | `jrag overview ` | `flow` | +| Who changed X and when? | Bash: `git log`/`git blame` | — | +| "How is this configured?" | `Glob` + `Grep`; `jrag search "" --table yaml` | `Read` sections | + +**Escalation:** ① Most targeted tool first (identifier → `jrag inspect`; structural → matching `jrag` traversal; raw text / config / history → `Grep`/`Glob`/`Bash`). ② Fall back gracefully (`jrag` empty / `not_found` / exit 2 → `Grep`/`Glob`). ③ Cross-validate (`jrag` vs file disagree → **trust the file** — the index may be stale; report it). --- -## Resolve-first contract (every `` command) - -Every `jrag` command that takes a `` runs `resolve_v2` internally: - -| `resolve_v2` status | Behavior / action | -| --- | --- | -| `one` | Run the traversal/listing against the resolved node. Read the result. | -| `many` | Return candidates and stop. **No auto-pick.** Disambiguate with `--kind`/`--role`/`--fqn-contains`/`--service`; re-run. | -| `none` | `status: not_found` envelope (exit 0). Fall back to `jrag search` or `Grep`. | - -Never look up a raw node ID — pass an FQN, simple name, prior `sym:`/`route:`/`client:`/`producer:` id, route path, or topic. Only `--kind` is a true resolve input; `--role`/`--java-kind`/`--fqn-contains` post-filter client-side, while `--service`/`--module` are resolve-time filters on `inspect`/`callers` and result filters elsewhere. - -## Output envelope - -`--format` (text|json) picks the representation; `--detail` (brief|normal|full) picks how much of each node/edge shows — **both honor the same detail level**. Default: `text` + `normal`. `inspect` and orientation commands default to `full`. `--format json` emits the projected envelope (empty fields dropped): `status`, `nodes`, `edges`, `candidates`, `truncated`, `agent_next_actions` (≤5, a starting point not a directive), `file_location` (only on `one`-hit resolve). `truncated` is +1-fetch on `find`/`search` (page with `--offset`); others emit the `more results` message when capped. - -## Traversal direction reference - -`jrag` abstracts away `direction`/`edge_types`: - -| Intent (command) | Underlying edges | -| --- | --- | -| `callers` / `callees` | `CALLS` in / out. On a controller/entry-point type, `callers` also folds in the routes its methods `EXPOSE` (the inbound callers of an entry point) | -| `hierarchy` | `EXTENDS` + `IMPLEMENTS`, both directions (parents + children) | -| `implementations` / `subclasses` | `IMPLEMENTS` / `EXTENDS` in | -| `overrides` / `overridden-by` | `OVERRIDES` out (subtype→supertype) / in | -| `dependencies` / `dependents` | `INJECTS` out / in | -| `impact` | bounded fan-in: `INJECTS`/`IMPLEMENTS`/`EXTENDS` in (depth ≤2) | -| `flow ` | `EXPOSES`/`HTTP_CALLS`/`ASYNC_CALLS`/`CALLS` | -| `connection A B` | bounded search over the same edge set | - -**Node id prefixes (from prior results):** `sym:` (Symbol), `route:`/`r:` (Route), `client:`/`c:` (Client), `producer:`/`p:` (Producer). **Symbol FQN:** `.[.]#(,…)` — generics erased, no spaces after commas, no-arg `()`, constructor `#(...)`. - -## Ontology glossary - -**Roles:** `CONTROLLER` (HTTP/messaging entry) | `SERVICE` (business logic) | `REPOSITORY` (data access) | `COMPONENT` (Spring component) | `CONFIG` (`@Configuration`) | `ENTITY` (JPA/persistence) | `CLIENT` (outbound wrapper) | `MAPPER` (converter) | `DTO` | `OTHER` (infra/utility). -**Capabilities:** `MESSAGE_LISTENER`, `MESSAGE_PRODUCER`, `HTTP_CLIENT`, `SCHEDULED_TASK`, `EXCEPTION_HANDLER`. -**Symbol kinds:** `class`, `interface`, `enum`, `record`, `annotation`, `method`, `constructor`. -**Route frameworks:** `spring_mvc`/`webflux` (HTTP), `kafka`/`rabbitmq`/`jms`/`stream` (messaging), `feign` (client mirrors). Route *kinds*: `http_endpoint`, `http_consumer`, `kafka_topic`, `rabbit_queue`, `jms_destination`, `stream_binding`. **Client kinds:** `feign_method`, `rest_template`, `web_client`. **Producer kinds:** `kafka_send`, `stream_bridge_send`. **Source layers:** `builtin`, `layer_a_meta`, `layer_b_ann`, `layer_b_fqn`, `layer_c_source`. +## Workflow Patterns ---- +- **"Explain feature X":** `jrag search "X"` → pick 1–3 hits → `jrag inspect ` → targeted traversal (`callees`/`implementations`/`dependents`) → stop when answered. +- **"Where is X used?":** `jrag inspect ` (resolves; disambiguate if `many`) → `jrag callers ` + `jrag dependents ` → `Grep` the symbol name as fallback → report sites with file:line. +- **"Find all Y":** structural → `jrag find --role [--service ]`; textual → `Grep`; broad → `Glob`+`Grep`. Summarize, don't dump. +- **"Trace flow A→B":** `jrag flow ` → `jrag connection ` (cross-service seams) → `Grep` the gaps → report with file:line. +- **"Orient in service S":** `jrag overview ` → `jrag conventions --service ` → `jrag map --service ` → `jrag http-routes --service `. ## Recovery Playbook @@ -140,10 +84,23 @@ Never look up a raw node ID — pass an FQN, simple name, prior `sym:`/`route:`/ --- -## Workflow Patterns +## jrag surface — `--help` is the spec -- **"Explain feature X":** `jrag search "X"` → pick 1–3 hits → `jrag inspect ` → targeted traversal (`callees`/`implementations`/`dependents`) → stop when answered. -- **"Where is X used?":** `jrag inspect ` (resolves; disambiguate if `many`) → `jrag callers ` + `jrag dependents ` → `Grep` fallback → report sites with file:line. -- **"Find all Y":** structural → `jrag find --role [--service ]`; textual → `Grep`; broad → `Glob`+`Grep`. Summarize, don't dump. -- **"Trace flow A→B":** `jrag flow ` → `jrag connection A B` → `Grep` gaps → report with file:line. -- **"Orient in service S":** `jrag overview ` → `jrag conventions --service ` → `jrag map --service ` → `jrag http-routes --service `. +`jrag` is self-documenting and the canonical, always-fresh source for commands, flags, and valid enum values — so it isn't duplicated here. Don't memorize the surface: + +- `jrag --help` — every command, grouped by intent, with one-line descriptions. +- `jrag --help` — that command's flags and accepted values. Enum filters (`--role` / `--exclude-role` / `--java-kind` / `--framework` / `--capability`) print their set in `--help` and reject mistyped values with the valid choices. + +The Decision Framework above tells you *which* command; reach for `--help` only when you need exact flags or enum values. + +**Prerequisite.** `jrag` needs an index — unindexed, every command exits 2 (`jrag status` checks; the file-system tools work without one). + +**Resolve-first contract.** Every `` command resolves the identifier first, then maps `one` / `many` / `none` onto one envelope: `one` → run; `many` → return candidates and stop, **no silent guess across distinct types** (a class sharing its simple name with its own constructor still resolves to the type — narrow with `--kind` / `--role` / `--fqn-contains` / `--service`); `none` → `status: not_found` (exit 0), fall back to `search` or `Grep`. Pass names (FQN / simple name / route path / topic) or prior `sym:`/`route:`/`client:`/`producer:` ids — never raw node ids. `--kind` is a true resolve input; `--role` / `--java-kind` / `--fqn-contains` post-filter client-side. + +**Output.** Default is compact text; `--format json` emits `{status, nodes, edges, candidates, truncated, agent_next_actions, file_location}` (empty fields dropped; `file_location` is a `filename:line` string; `agent_next_actions` suggests ≤5 next commands). `truncated` pages via `--limit` / `--offset` (`find` / `search` only). + +**Edge semantics `--help` doesn't spell out.** `callers` / `callees` = `CALLS` in/out (on a controller/entry-point type, `callers` also lists the routes its methods `EXPOSE`). `impact` = bounded fan-in over `INJECTS` / `IMPLEMENTS` / `EXTENDS` (default depth 2; raise with `--depth`). `flow ` follows `EXPOSES` → `HTTP_CALLS` / `ASYNC_CALLS` → `CALLS`. `connection ` = inbound/outbound cross-service seams (its positional is a literal service name, not a query). Per-command edge mappings and the rest of the flag surface live in each command's `--help`. + +**Node id prefixes (from prior results):** `sym:` (Symbol), `route:`/`r:` (Route), `client:`/`c:` (Client), `producer:`/`p:` (Producer). **Symbol FQN:** `.[.]#(,…)` — generics erased, no spaces after commas, no-arg `()`, constructor `#(...)`. + +**Ontology.** Role / symbol-kind / framework / capability values are enumerated in `--help`; client/producer kinds and source layers validate at runtime and surface the accepted set on a typo. diff --git a/java_codebase_rag/install_data/skills/explore-codebase-cli/SKILL.md b/java_codebase_rag/install_data/skills/explore-codebase-cli/SKILL.md index c5b4415..4493706 100644 --- a/java_codebase_rag/install_data/skills/explore-codebase-cli/SKILL.md +++ b/java_codebase_rag/install_data/skills/explore-codebase-cli/SKILL.md @@ -1,159 +1,68 @@ --- name: explore-codebase-cli -description: "MUST BE USED PROACTIVELY. Universal read-only codebase exploration via the `jrag` CLI — one command per engineering intent (callers, callees, routes, clients, producers, impact, search, inspect, flow, overview). Use for any exploration: locating code, tracing dependencies, finding patterns, 'where is X', 'who calls Y', 'find all controllers', 'trace the flow from A to B'. Combines graph navigation with file-system search (grep, glob, file reading). Do NOT use when the answer is already in open context or for a single known file — read that file directly." +description: "MUST BE USED PROACTIVELY. Universal codebase exploration (CLI surface). Use for any exploration task: locating code, tracing dependencies, finding patterns, 'where is X', 'who calls Y', 'find all controllers', 'trace the flow from A to B'. Do NOT use when the answer is already in open context or for a single known file — read that file directly." --- -# /explore-codebase-cli — Universal codebase exploration via `jrag` - -Read-only exploration combining **graph navigation through the `jrag` CLI** with **broad file-system search**. `jrag` loads the same index as the MCP server but exposes one shell command per intent instead of five MCP tools. - -Use any time you must search, locate, navigate, or explore. **Do NOT use when** the answer is already in context or for a single known file — read it directly. - ## Core Principles -1. **Read-only.** Never edit, write, or modify any file. -2. **Names in, names out.** Every `` is human-readable (FQN / simple name / route path / topic). Raw node IDs never required. -3. **One command per intent.** `jrag` collapses resolve + walk into one call — don't chain resolve→describe→neighbors manually. -4. **Stop when answered.** Don't prefetch unrelated subgraphs or directories. - -**One surface per project.** This is the CLI surface; the MCP surface (`search`/`find`/`describe`/`neighbors`/`resolve`) is mutually exclusive — running both strands the agent in two vocabularies. - -## Prerequisite: index must exist - -`jrag` is a thin layer over the existing index. If unindexed, every command exits 2: - -``` -status: error -message: No index at . Run: java-codebase-rag init --source-root -``` - -Verify with `jrag status` when in doubt. +1. **Smallest sufficient tool — both ways.** Pick the lightest tool that answers the question. Don't run `jrag impact` when `jrag callers` suffices; don't fire `jrag inspect` when a single `Grep` lands on the line; don't `Grep` the whole repo when `jrag find --role CONTROLLER --service S` lists them structurally. Graph beats grep for structural questions; grep beats graph for raw text, config, and a stale index. Neither is the default — match the tool to the question. +2. **Excerpts over dumps.** Read excerpts and relevant sections, not entire files. Summarize findings. +3. **Stop when answered.** Don't prefetch unrelated subgraphs or scan unrelated directories. ## Tool Inventory -### `jrag` command groups - -Run `jrag --help` for the canonical list. - -| Group | Commands | -| --- | --- | -| **Orientation** | `status`, `microservices`, `map`, `conventions`, `overview` | -| **Locate** | `find`, `search` | -| **Listings** | `http-routes`, `http-clients`, `producers`, `topics`, `jobs`, `listeners`, `entities` | -| **Traversal** | `callers`, `callees`, `hierarchy`, `implementations`, `subclasses`, `overrides`, `overridden-by`, `dependents`, `impact`, `flow`, `decompose`, `dependencies`, `connection` | -| **Inspection** | `inspect`, `outline`, `imports` | - -### Common flags - -``` ---service Filter by microservice ---module Filter by module ---limit Cap on results (default 20; 10 for fan-out) ---format text|json Output format (default: text) ---detail brief|normal|full How much of each node/edge is shown (default: normal); - orthogonal to --format. brief=name @service; - normal=+module/role/file/score; full=+signature/ - annotations/snippet. inspect + orientation default to full. ---index-dir Index directory override (default: discovered from cwd) -``` +- **Graph (`jrag` CLI):** one command per intent — `callers`, `callees`, `hierarchy`, `implementations`, `dependents`, `impact`, `flow`, `http-routes`, `http-clients`, `producers`, `topics`, `find`, `search`, `inspect`, `overview`, … Drives the same index as the MCP server. Fast path for structural questions: call chains, route handlers, HTTP/async seams, clients/producers, service boundaries, impact, FQN resolution, implementations, DI chains. Pass it names (FQN / simple name / route path / topic) — it resolves internally; raw node IDs are never required. Requires an index; if unindexed every command exits 2 (see **jrag surface**). +- **File-system:** `Grep` (content/regex), `Glob` (name/path patterns), `Read` (`offset`/`limit`). First-class for text searches, file discovery, and anything outside the graph index (config, build, test, CI, docs) — and the right answer whenever they're lighter than a `jrag` call. +- **Other:** `Bash` (read-only: `git log`, `git blame`, `ls`, `find`), `WebSearch`/`WebFetch`. -`--offset` is supported **only** on `find` and `search`. Other commands emit `truncated: more results — narrow your query` when capped. - -### File-system tools - -`Grep` (content/regex), `Glob` (name/path patterns), `Read` (`offset`/`limit`). Plus `Bash` (read-only: `git log`, `git blame`, `ls`, `find`), `WebSearch`/`WebFetch`. +*CLI surface only — don't also drive the MCP tools (`search`/`find`/`describe`/`neighbors`/`resolve`) in the same session; the two vocabularies conflict.* --- ## Decision Framework -| User asks… | First `jrag` command | Follow-up | -| ---------- | -------------------- | --------- | +| User asks… | First step | Follow-up | +| ---------- | ---------- | --------- | | "Is the index fresh?" | `jrag status` | — | | Identifier (FQN / simple name) | `jrag inspect ` | `callers` / `callees` | | Fuzzy / NL "where is X" | `jrag search ""` | `inspect ` | +| Raw text, a string literal, a config key | `Grep` | `Read` the hits | | All controllers in S | `jrag find --role CONTROLLER --service S` | `callees` | | Interfaces in S | `jrag find --java-kind interface --service S` | `implementations` | | HTTP / messaging entry points | `jrag http-routes [--framework …] [--method …]` | `inspect ` | | Outbound HTTP clients | `jrag http-clients [--calls-service …]` | `callees ` | | Outbound async producers | `jrag producers [--topic-contains …]` | `callees ` | | Topics + consumers/producers | `jrag topics [--topic-contains …]` | — | +| Cross-service seams of S | `jrag connection [--inbound/--outbound/--both]` | — | | Who calls / what does M call? | `jrag callers ` / `jrag callees ` | `inspect` | -| What routes does a controller expose? | `jrag callers ` | `inspect` (`DECLARES.EXPOSES`) | +| What routes does a controller expose? | `jrag callers ` (folds in its `EXPOSES` routes) | `inspect` | | Who hits this route? | `jrag callers ` | — | | Implementations / subtypes of T? | `jrag implementations ` / `jrag subclasses ` | — | | Overriding / overridden methods? | `jrag overrides ` (UP) / `jrag overridden-by ` | — | | Who injects / depends on T? | `jrag dependencies ` / `jrag dependents ` | — | | Blast-radius of changing X? | `jrag impact ` (bounded fan-in) | `Grep` fallback | -| Trace request flow A→B | `jrag flow ` | `connection ` | +| Trace request flow A→B | `jrag flow ` | `connection ` (service's cross-service seams) | | File outline / imports | `jrag outline ` / `jrag imports ` | `inspect ` | +| Find files by name/path | `Glob` | `Read` | | "Explain service S" | `jrag overview ` | `http-routes`/`http-clients`/`producers` | -| "Explain route /topic" | `jrag overview ` | `flow` | -| Find files / text | `Glob` / `Grep` | `Read` | +| "Explain route / topic" | `jrag overview ` | `flow` | | Who changed X and when? | Bash: `git log`/`git blame` | — | | "How is this configured?" | `Glob` + `Grep`; `jrag search "" --table yaml` | `Read` sections | -**Escalation:** ① Most targeted command first → ② fall back gracefully (`callers` empty → `Grep`) → ③ cross-validate (CLI vs file disagree → **trust the file** — index may be stale). +**Escalation:** ① Most targeted tool first (identifier → `jrag inspect`; structural → matching `jrag` traversal; raw text / config / history → `Grep`/`Glob`/`Bash`). ② Fall back gracefully (`jrag` empty / `not_found` / exit 2 → `Grep`/`Glob`). ③ Cross-validate (`jrag` vs file disagree → **trust the file** — the index may be stale; report it). -**Rules of thumb:** structure beats vector for exact questions (`find`/`inspect` + traversal); vector beats structure for fuzzy discovery (`search`); file-system beats stale index. +**Rules of thumb:** structure beats vector for exact questions (`jrag find`/`inspect` + traversal); vector beats structure for fuzzy discovery (`jrag search`); raw text / config / history beats both (`Grep`/`Glob`/`Bash`); file-system beats a stale index. --- -## Resolve-first contract (every `` command) - -Every `jrag` command that takes a `` runs `resolve_v2` internally: - -| `resolve_v2` status | `jrag` behavior | -| --- | --- | -| `one` | Run the traversal/listing against the resolved node. | -| `many` | Return the candidate list and stop. **No auto-pick.** Disambiguate with `--kind`/`--role`/`--fqn-contains`/`--service`; re-run. | -| `none` | `status: not_found` envelope (exit 0). Fall back to `search` or `Grep`. | - -Never look up a raw node ID — pass an FQN, simple name, prior `sym:`/`route:`/`client:`/`producer:` id, route path, or topic. Only `--kind` is a true resolve input; `--role`/`--java-kind`/`--fqn-contains` post-filter client-side, while `--service`/`--module` are resolve-time filters on `inspect`/`callers` and result filters elsewhere. - -## Output envelope - -`--format` (text|json) picks the representation; `--detail` (brief|normal|full) picks how much of each node/edge shows — **both honor the same detail level**. Default: `text` + `normal`. `inspect` and orientation commands default to `full`. `--format json` emits the projected envelope (empty fields dropped): - -```json -{ - "status": "ok|not_found|error", - "nodes": {"": {...}}, - "edges": [{...}], - "candidates": [{...}], - "truncated": false, - "agent_next_actions": ["jrag callers ", "..."], - "file_location": {"filename": "...", "start_line": 123} -} -``` - -`truncated` is computed via +1-fetch on `find`/`search` (use `--limit`, then `--offset`); other commands emit the `more results` message when capped. `agent_next_actions` (≤5) maps result edges to next commands — a starting point, not a directive. `file_location` populates only on `one`-hit resolve. - -## Traversal direction reference - -`jrag` abstracts away `direction`/`edge_types` — you name the intent, it picks the edges: - -| Intent (command) | Underlying edges | -| --- | --- | -| `callers` / `callees` | `CALLS` in / out. On a controller/entry-point type, `callers` also folds in the routes its methods `EXPOSE` (they are the inbound callers) — use it to list a controller's endpoints. `decompose` defaults to `--follow-calls`; `--per-stage-limit` caps symbols per stage (not stage count) | -| `hierarchy` | `EXTENDS` + `IMPLEMENTS`, both directions (parents + children) | -| `implementations` / `subclasses` | `IMPLEMENTS` / `EXTENDS` in | -| `overrides` / `overridden-by` | `OVERRIDES` out (subtype→supertype) / in | -| `dependencies` / `dependents` | `INJECTS` out / in | -| `impact` | bounded fan-in: `INJECTS`/`IMPLEMENTS`/`EXTENDS` in (depth ≤2) | -| `flow ` | `EXPOSES`/`HTTP_CALLS`/`ASYNC_CALLS`/`CALLS` | -| `connection A B` | bounded search over the same edge set | - -**Node id prefixes (from prior results):** `sym:` (Symbol), `route:`/`r:` (Route), `client:`/`c:` (Client), `producer:`/`p:` (Producer). **Symbol FQN:** `.[.]#(,…)` — generics erased, no spaces after commas, no-arg `()`, constructor `#(...)`. - -## Ontology glossary - -**Roles:** `CONTROLLER` | `SERVICE` | `REPOSITORY` | `COMPONENT` | `CONFIG` | `ENTITY` | `CLIENT` | `MAPPER` | `DTO` | `OTHER`. -**Capabilities:** `MESSAGE_LISTENER`, `MESSAGE_PRODUCER`, `HTTP_CLIENT`, `SCHEDULED_TASK`, `EXCEPTION_HANDLER`. -**Symbol kinds:** `class`, `interface`, `enum`, `record`, `annotation`, `method`, `constructor`. -**Route frameworks:** `spring_mvc`/`webflux` (HTTP), `kafka`/`rabbitmq`/`jms`/`stream` (messaging), `feign` (client mirrors). Route *kinds*: `http_endpoint`, `http_consumer`, `kafka_topic`, `rabbit_queue`, `jms_destination`, `stream_binding`. **Client kinds:** `feign_method`, `rest_template`, `web_client`. **Producer kinds:** `kafka_send`, `stream_bridge_send`. **Source layers:** `builtin`, `layer_a_meta`, `layer_b_ann`, `layer_b_fqn`, `layer_c_source`. +## Workflow Patterns ---- +- **"Explain feature X":** `jrag search "X"` → pick 1–3 hits → `jrag inspect ` → targeted traversal (`callees`/`implementations`) → stop when answered. +- **"Where is X used?":** `jrag inspect ` → `jrag callers ` + `jrag dependents ` → `Grep` the symbol name as fallback → report sites with file:line. +- **"Find all Y":** structural → `jrag find --role [--service ]`; textual → `Grep`; broad → `Glob`+`Grep`. Summarize, don't dump. +- **"Trace flow A→B":** `jrag flow ` → `jrag connection ` (cross-service seams) → `Grep` the gaps → report with file:line. +- **"How is this configured?":** `Glob` `**/application*.yml` → `Grep` the key → `Read` sections → `jrag search "" --table yaml`. +- **"Orient in a new service":** `jrag overview ` → `jrag conventions --service ` → `jrag map --service ` → `jrag http-routes --service `. ## Recovery Playbook @@ -174,11 +83,23 @@ Never look up a raw node ID — pass an FQN, simple name, prior `sym:`/`route:`/ --- -## Workflow Patterns +## jrag surface — `--help` is the spec -- **"Explain feature X":** `jrag search "X"` → pick 1–3 hits → `jrag inspect ` → targeted traversal (`callees`/`implementations`) → stop when answered. -- **"Where is X used?":** `jrag inspect ` → `jrag callers ` + `jrag dependents ` → `Grep` fallback → report sites with file:line. -- **"Find all Y":** structural → `jrag find --role [--service ]`; textual → `Grep`; broad → `Glob`+`Grep`. Summarize, don't dump. -- **"Trace flow A→B":** `jrag flow ` → `jrag connection A B` → `Grep` gaps → report with file:line. -- **"How is this configured?":** `Glob` `**/application*.yml` → `Grep` the key → `Read` sections → `jrag search "" --table yaml`. -- **"Orient in a new service":** `jrag overview ` → `jrag conventions --service ` → `jrag map --service ` → `jrag http-routes --service `. +`jrag` is self-documenting and the canonical, always-fresh source for commands, flags, and valid enum values — so this skill doesn't duplicate them. Don't memorize the surface: + +- `jrag --help` — every command, grouped by intent, with one-line descriptions. +- `jrag --help` — that command's flags and accepted values. Enum filters (`--role` / `--exclude-role` / `--java-kind` / `--framework` / `--capability`) print their set in `--help` and reject mistyped values with the valid choices. + +The Decision Framework above tells you *which* command; reach for `--help` only when you need exact flags or enum values. + +**Prerequisite.** `jrag` needs an index — unindexed, every command exits 2 (`jrag status` checks; the file-system tools work without one). + +**Resolve-first contract.** Every `` command resolves the identifier first, then maps `one` / `many` / `none` onto one envelope: `one` → run; `many` → return candidates and stop, **no silent guess across distinct types** (a class sharing its simple name with its own constructor still resolves to the type — narrow with `--kind` / `--role` / `--fqn-contains` / `--service`); `none` → `status: not_found` (exit 0), fall back to `search` or `Grep`. Pass names (FQN / simple name / route path / topic) or prior `sym:`/`route:`/`client:`/`producer:` ids — never raw node ids. `--kind` is a true resolve input; `--role` / `--java-kind` / `--fqn-contains` post-filter client-side. + +**Output.** Default is compact text; `--format json` emits `{status, nodes, edges, candidates, truncated, agent_next_actions, file_location}` (empty fields dropped; `file_location` is a `filename:line` string; `agent_next_actions` suggests ≤5 next commands). `truncated` pages via `--limit` / `--offset` (`find` / `search` only). + +**Edge semantics `--help` doesn't spell out.** `callers` / `callees` = `CALLS` in/out (on a controller/entry-point type, `callers` also lists the routes its methods `EXPOSE`). `impact` = bounded fan-in over `INJECTS` / `IMPLEMENTS` / `EXTENDS` (default depth 2; raise with `--depth`). `flow ` follows `EXPOSES` → `HTTP_CALLS` / `ASYNC_CALLS` → `CALLS`. `connection ` = inbound/outbound cross-service seams (its positional is a literal service name, not a query). Per-command edge mappings and the rest of the flag surface live in each command's `--help`. + +**Node id prefixes (from prior results):** `sym:` (Symbol), `route:`/`r:` (Route), `client:`/`c:` (Client), `producer:`/`p:` (Producer). **Symbol FQN:** `.[.]#(,…)` — generics erased, no spaces after commas, no-arg `()`, constructor `#(...)`. + +**Ontology.** Role / symbol-kind / framework / capability values are enumerated in `--help`; client/producer kinds and source layers validate at runtime and surface the accepted set on a typo. diff --git a/java_codebase_rag/jrag.py b/java_codebase_rag/jrag.py index 2ad1610..0c2da3e 100644 --- a/java_codebase_rag/jrag.py +++ b/java_codebase_rag/jrag.py @@ -303,6 +303,44 @@ def _preparse_render_flags(raw: list[str]) -> tuple[str | None, str | None, list return None, None, list(raw) +# Closed enum taxonomies for the --role / --exclude-role / --java-kind / +# --framework / --capability filters. Sourced from the canonical literals +# (mcp_v2.Role, mcp_v2.DeclarationSymbolKind, mcp_v2.Framework) and +# java_ontology.VALID_CAPABILITIES, and cross-checked by test_jrag_enum_choices. +# Hardcoded here (not imported) so `jrag --help` stays fast — build_parser +# imports no backend modules, and importing mcp_v2 costs ~0.7s. +_ROLE_CHOICES = ( + "CONTROLLER", "SERVICE", "REPOSITORY", "COMPONENT", "CONFIG", + "ENTITY", "CLIENT", "MAPPER", "DTO", "OTHER", +) +_JAVA_KIND_CHOICES = ( + "class", "interface", "enum", "record", "annotation", "method", "constructor", +) +_FRAMEWORK_CHOICES = ( + "spring_mvc", "webflux", "kafka", "rabbitmq", "jms", "stream", "feign", +) +_CAPABILITY_CHOICES = ( + "MESSAGE_LISTENER", "MESSAGE_PRODUCER", "HTTP_CLIENT", + "SCHEDULED_TASK", "EXCEPTION_HANDLER", +) + + +def _upper_snake(value: str) -> str: + """Normalize a role/capability value to its stored UPPER_SNAKE form so + argparse ``choices=`` accepts flexible casing (``controller`` / + ``scheduled-task`` -> ``CONTROLLER`` / ``SCHEDULED_TASK``). Mirrors the + role/capability branch of jrag_envelope.normalize_enum.""" + return value.strip().upper().replace("-", "_").replace(" ", "_") + + +def _lower_snake(value: str) -> str: + """Normalize a java-kind/framework value to its stored lowercase form so + argparse ``choices=`` accepts flexible casing (``Spring-MVC`` -> + ``spring_mvc``). Mirrors the framework/java_kind branch of + jrag_envelope.normalize_enum.""" + return value.strip().lower().replace("-", "_").replace(" ", "_") + + def build_parser() -> argparse.ArgumentParser: """Argparse builder. Imports no backend modules. @@ -343,8 +381,8 @@ def build_parser() -> argparse.ArgumentParser: subparsers = parser.add_subparsers(dest="command", parser_class=_EnvelopeArgumentParser) # Common flags applied per command via parents=[_common_parser()]. NOT - # global so commands can override defaults (e.g. fan-out commands use - # limit=10). The helper builds a FRESH parser each call so every subparser + # global so commands can override defaults (e.g. inspect/orientation + # default --detail to full). The helper builds a FRESH parser each call so every subparser # owns its own --detail Action object — argparse `parents` shares Action # objects by reference, and `set_defaults(detail=...)` mutates the shared # action's default (CPython walks `self._actions`), so a single shared @@ -366,7 +404,7 @@ def _common_parser() -> argparse.ArgumentParser: ), ) common.add_argument( - "--limit", type=int, default=20, help="Cap on results (default 20; 10 for fan-out)." + "--limit", type=int, default=20, help="Cap on results (default 20)." ) common.add_argument( "--index-dir", @@ -461,12 +499,12 @@ def _core_parser() -> argparse.ArgumentParser: default=None, help="Node kind (omit for auto-inference from domain flags).", ) - find.add_argument("--role", type=str, default=None, help="Filter by role.") - find.add_argument("--exclude-role", type=str, default=None, help="Exclude by role.") - find.add_argument("--java-kind", type=str, default=None, help="Filter by Java symbol kind.") + find.add_argument("--role", type=_upper_snake, choices=_ROLE_CHOICES, default=None, help="Filter by role.") + find.add_argument("--exclude-role", type=_upper_snake, choices=_ROLE_CHOICES, default=None, help="Exclude by role.") + find.add_argument("--java-kind", type=_lower_snake, choices=_JAVA_KIND_CHOICES, default=None, help="Filter by Java symbol kind.") find.add_argument("--annotation", type=str, default=None, help="Filter by annotation.") - find.add_argument("--capability", type=str, default=None, help="Filter by capability.") - find.add_argument("--framework", type=str, default=None, help="Filter by framework.") + find.add_argument("--capability", type=_upper_snake, choices=_CAPABILITY_CHOICES, default=None, help="Filter by capability.") + find.add_argument("--framework", type=_lower_snake, choices=_FRAMEWORK_CHOICES, default=None, help="Filter by framework.") find.add_argument("--source-layer", type=str, default=None, help="Filter by source layer.") find.add_argument("--fqn-contains", type=str, default=None, help="Filter by FQN substring.") find.add_argument("--http-method", type=str, default=None, help="Filter by HTTP method (route).") @@ -502,8 +540,8 @@ def _core_parser() -> argparse.ArgumentParser: default=None, help="Hint for resolve (omitted for broad search).", ) - inspect.add_argument("--java-kind", type=str, default=None, help="Post-filter by Java symbol kind.") - inspect.add_argument("--role", type=str, default=None, help="Post-filter by role.") + inspect.add_argument("--java-kind", type=_lower_snake, choices=_JAVA_KIND_CHOICES, default=None, help="Post-filter by Java symbol kind.") + inspect.add_argument("--role", type=_upper_snake, choices=_ROLE_CHOICES, default=None, help="Post-filter by role.") inspect.add_argument("--fqn-contains", type=str, default=None, help="Post-filter by FQN substring.") inspect.set_defaults(handler=_cmd_inspect, detail="full") @@ -518,7 +556,7 @@ def _core_parser() -> argparse.ArgumentParser: "kafka topics live under `topics`." ), ) - http_routes.add_argument("--framework", type=str, default=None, help="Filter by framework.") + http_routes.add_argument("--framework", type=_lower_snake, choices=_FRAMEWORK_CHOICES, default=None, help="Filter by framework.") http_routes.add_argument("--path-contains", type=str, default=None, help="Filter by path substring.") http_routes.add_argument("--method", type=str, default=None, help="Filter by HTTP method.") http_routes.set_defaults(handler=_cmd_routes, detail="full", auto_scope=True) @@ -617,8 +655,8 @@ def _core_parser() -> argparse.ArgumentParser: default=None, help="Hint for resolve (omit for broad search).", ) - resolve_parent.add_argument("--java-kind", type=str, default=None, help="Post-filter by Java symbol kind.") - resolve_parent.add_argument("--role", type=str, default=None, help="Post-filter by role.") + resolve_parent.add_argument("--java-kind", type=_lower_snake, choices=_JAVA_KIND_CHOICES, default=None, help="Post-filter by Java symbol kind.") + resolve_parent.add_argument("--role", type=_upper_snake, choices=_ROLE_CHOICES, default=None, help="Post-filter by role.") resolve_parent.add_argument("--fqn-contains", type=str, default=None, help="Post-filter by FQN substring.") callers = subparsers.add_parser( @@ -700,7 +738,7 @@ def _core_parser() -> argparse.ArgumentParser: ), ) implementations.add_argument("query", help="Interface FQN or name.") - implementations.add_argument("--capability", type=str, default=None, help="Filter implementors by capability.") + implementations.add_argument("--capability", type=_upper_snake, choices=_CAPABILITY_CHOICES, default=None, help="Filter implementors by capability.") implementations.set_defaults(handler=_cmd_implementations, auto_scope=True) subclasses = subparsers.add_parser( @@ -1105,12 +1143,12 @@ def _core_parser() -> argparse.ArgumentParser: ), ) # NodeFilter flags (same set as `find` filter mode, minus the query-only ones). - search.add_argument("--role", type=str, default=None, help="Filter by role.") - search.add_argument("--exclude-role", type=str, default=None, dest="exclude_role", help="Exclude by role.") - search.add_argument("--java-kind", type=str, default=None, dest="java_kind", help="Filter by Java symbol kind.") + search.add_argument("--role", type=_upper_snake, choices=_ROLE_CHOICES, default=None, help="Filter by role.") + search.add_argument("--exclude-role", type=_upper_snake, choices=_ROLE_CHOICES, default=None, dest="exclude_role", help="Exclude by role.") + search.add_argument("--java-kind", type=_lower_snake, choices=_JAVA_KIND_CHOICES, default=None, dest="java_kind", help="Filter by Java symbol kind.") search.add_argument("--annotation", type=str, default=None, help="Filter by annotation.") - search.add_argument("--capability", type=str, default=None, help="Filter by capability.") - search.add_argument("--framework", type=str, default=None, help="Filter by framework.") + search.add_argument("--capability", type=_upper_snake, choices=_CAPABILITY_CHOICES, default=None, help="Filter by capability.") + search.add_argument("--framework", type=_lower_snake, choices=_FRAMEWORK_CHOICES, default=None, help="Filter by framework.") search.add_argument("--fqn-contains", type=str, default=None, dest="fqn_contains", help="Filter by FQN substring.") search.add_argument( "--offset", diff --git a/skills/explore-codebase-cli/SKILL.md b/skills/explore-codebase-cli/SKILL.md index c5b4415..4493706 100644 --- a/skills/explore-codebase-cli/SKILL.md +++ b/skills/explore-codebase-cli/SKILL.md @@ -1,159 +1,68 @@ --- name: explore-codebase-cli -description: "MUST BE USED PROACTIVELY. Universal read-only codebase exploration via the `jrag` CLI — one command per engineering intent (callers, callees, routes, clients, producers, impact, search, inspect, flow, overview). Use for any exploration: locating code, tracing dependencies, finding patterns, 'where is X', 'who calls Y', 'find all controllers', 'trace the flow from A to B'. Combines graph navigation with file-system search (grep, glob, file reading). Do NOT use when the answer is already in open context or for a single known file — read that file directly." +description: "MUST BE USED PROACTIVELY. Universal codebase exploration (CLI surface). Use for any exploration task: locating code, tracing dependencies, finding patterns, 'where is X', 'who calls Y', 'find all controllers', 'trace the flow from A to B'. Do NOT use when the answer is already in open context or for a single known file — read that file directly." --- -# /explore-codebase-cli — Universal codebase exploration via `jrag` - -Read-only exploration combining **graph navigation through the `jrag` CLI** with **broad file-system search**. `jrag` loads the same index as the MCP server but exposes one shell command per intent instead of five MCP tools. - -Use any time you must search, locate, navigate, or explore. **Do NOT use when** the answer is already in context or for a single known file — read it directly. - ## Core Principles -1. **Read-only.** Never edit, write, or modify any file. -2. **Names in, names out.** Every `` is human-readable (FQN / simple name / route path / topic). Raw node IDs never required. -3. **One command per intent.** `jrag` collapses resolve + walk into one call — don't chain resolve→describe→neighbors manually. -4. **Stop when answered.** Don't prefetch unrelated subgraphs or directories. - -**One surface per project.** This is the CLI surface; the MCP surface (`search`/`find`/`describe`/`neighbors`/`resolve`) is mutually exclusive — running both strands the agent in two vocabularies. - -## Prerequisite: index must exist - -`jrag` is a thin layer over the existing index. If unindexed, every command exits 2: - -``` -status: error -message: No index at . Run: java-codebase-rag init --source-root -``` - -Verify with `jrag status` when in doubt. +1. **Smallest sufficient tool — both ways.** Pick the lightest tool that answers the question. Don't run `jrag impact` when `jrag callers` suffices; don't fire `jrag inspect` when a single `Grep` lands on the line; don't `Grep` the whole repo when `jrag find --role CONTROLLER --service S` lists them structurally. Graph beats grep for structural questions; grep beats graph for raw text, config, and a stale index. Neither is the default — match the tool to the question. +2. **Excerpts over dumps.** Read excerpts and relevant sections, not entire files. Summarize findings. +3. **Stop when answered.** Don't prefetch unrelated subgraphs or scan unrelated directories. ## Tool Inventory -### `jrag` command groups - -Run `jrag --help` for the canonical list. - -| Group | Commands | -| --- | --- | -| **Orientation** | `status`, `microservices`, `map`, `conventions`, `overview` | -| **Locate** | `find`, `search` | -| **Listings** | `http-routes`, `http-clients`, `producers`, `topics`, `jobs`, `listeners`, `entities` | -| **Traversal** | `callers`, `callees`, `hierarchy`, `implementations`, `subclasses`, `overrides`, `overridden-by`, `dependents`, `impact`, `flow`, `decompose`, `dependencies`, `connection` | -| **Inspection** | `inspect`, `outline`, `imports` | - -### Common flags - -``` ---service Filter by microservice ---module Filter by module ---limit Cap on results (default 20; 10 for fan-out) ---format text|json Output format (default: text) ---detail brief|normal|full How much of each node/edge is shown (default: normal); - orthogonal to --format. brief=name @service; - normal=+module/role/file/score; full=+signature/ - annotations/snippet. inspect + orientation default to full. ---index-dir Index directory override (default: discovered from cwd) -``` +- **Graph (`jrag` CLI):** one command per intent — `callers`, `callees`, `hierarchy`, `implementations`, `dependents`, `impact`, `flow`, `http-routes`, `http-clients`, `producers`, `topics`, `find`, `search`, `inspect`, `overview`, … Drives the same index as the MCP server. Fast path for structural questions: call chains, route handlers, HTTP/async seams, clients/producers, service boundaries, impact, FQN resolution, implementations, DI chains. Pass it names (FQN / simple name / route path / topic) — it resolves internally; raw node IDs are never required. Requires an index; if unindexed every command exits 2 (see **jrag surface**). +- **File-system:** `Grep` (content/regex), `Glob` (name/path patterns), `Read` (`offset`/`limit`). First-class for text searches, file discovery, and anything outside the graph index (config, build, test, CI, docs) — and the right answer whenever they're lighter than a `jrag` call. +- **Other:** `Bash` (read-only: `git log`, `git blame`, `ls`, `find`), `WebSearch`/`WebFetch`. -`--offset` is supported **only** on `find` and `search`. Other commands emit `truncated: more results — narrow your query` when capped. - -### File-system tools - -`Grep` (content/regex), `Glob` (name/path patterns), `Read` (`offset`/`limit`). Plus `Bash` (read-only: `git log`, `git blame`, `ls`, `find`), `WebSearch`/`WebFetch`. +*CLI surface only — don't also drive the MCP tools (`search`/`find`/`describe`/`neighbors`/`resolve`) in the same session; the two vocabularies conflict.* --- ## Decision Framework -| User asks… | First `jrag` command | Follow-up | -| ---------- | -------------------- | --------- | +| User asks… | First step | Follow-up | +| ---------- | ---------- | --------- | | "Is the index fresh?" | `jrag status` | — | | Identifier (FQN / simple name) | `jrag inspect ` | `callers` / `callees` | | Fuzzy / NL "where is X" | `jrag search ""` | `inspect ` | +| Raw text, a string literal, a config key | `Grep` | `Read` the hits | | All controllers in S | `jrag find --role CONTROLLER --service S` | `callees` | | Interfaces in S | `jrag find --java-kind interface --service S` | `implementations` | | HTTP / messaging entry points | `jrag http-routes [--framework …] [--method …]` | `inspect ` | | Outbound HTTP clients | `jrag http-clients [--calls-service …]` | `callees ` | | Outbound async producers | `jrag producers [--topic-contains …]` | `callees ` | | Topics + consumers/producers | `jrag topics [--topic-contains …]` | — | +| Cross-service seams of S | `jrag connection [--inbound/--outbound/--both]` | — | | Who calls / what does M call? | `jrag callers ` / `jrag callees ` | `inspect` | -| What routes does a controller expose? | `jrag callers ` | `inspect` (`DECLARES.EXPOSES`) | +| What routes does a controller expose? | `jrag callers ` (folds in its `EXPOSES` routes) | `inspect` | | Who hits this route? | `jrag callers ` | — | | Implementations / subtypes of T? | `jrag implementations ` / `jrag subclasses ` | — | | Overriding / overridden methods? | `jrag overrides ` (UP) / `jrag overridden-by ` | — | | Who injects / depends on T? | `jrag dependencies ` / `jrag dependents ` | — | | Blast-radius of changing X? | `jrag impact ` (bounded fan-in) | `Grep` fallback | -| Trace request flow A→B | `jrag flow ` | `connection ` | +| Trace request flow A→B | `jrag flow ` | `connection ` (service's cross-service seams) | | File outline / imports | `jrag outline ` / `jrag imports ` | `inspect ` | +| Find files by name/path | `Glob` | `Read` | | "Explain service S" | `jrag overview ` | `http-routes`/`http-clients`/`producers` | -| "Explain route /topic" | `jrag overview ` | `flow` | -| Find files / text | `Glob` / `Grep` | `Read` | +| "Explain route / topic" | `jrag overview ` | `flow` | | Who changed X and when? | Bash: `git log`/`git blame` | — | | "How is this configured?" | `Glob` + `Grep`; `jrag search "" --table yaml` | `Read` sections | -**Escalation:** ① Most targeted command first → ② fall back gracefully (`callers` empty → `Grep`) → ③ cross-validate (CLI vs file disagree → **trust the file** — index may be stale). +**Escalation:** ① Most targeted tool first (identifier → `jrag inspect`; structural → matching `jrag` traversal; raw text / config / history → `Grep`/`Glob`/`Bash`). ② Fall back gracefully (`jrag` empty / `not_found` / exit 2 → `Grep`/`Glob`). ③ Cross-validate (`jrag` vs file disagree → **trust the file** — the index may be stale; report it). -**Rules of thumb:** structure beats vector for exact questions (`find`/`inspect` + traversal); vector beats structure for fuzzy discovery (`search`); file-system beats stale index. +**Rules of thumb:** structure beats vector for exact questions (`jrag find`/`inspect` + traversal); vector beats structure for fuzzy discovery (`jrag search`); raw text / config / history beats both (`Grep`/`Glob`/`Bash`); file-system beats a stale index. --- -## Resolve-first contract (every `` command) - -Every `jrag` command that takes a `` runs `resolve_v2` internally: - -| `resolve_v2` status | `jrag` behavior | -| --- | --- | -| `one` | Run the traversal/listing against the resolved node. | -| `many` | Return the candidate list and stop. **No auto-pick.** Disambiguate with `--kind`/`--role`/`--fqn-contains`/`--service`; re-run. | -| `none` | `status: not_found` envelope (exit 0). Fall back to `search` or `Grep`. | - -Never look up a raw node ID — pass an FQN, simple name, prior `sym:`/`route:`/`client:`/`producer:` id, route path, or topic. Only `--kind` is a true resolve input; `--role`/`--java-kind`/`--fqn-contains` post-filter client-side, while `--service`/`--module` are resolve-time filters on `inspect`/`callers` and result filters elsewhere. - -## Output envelope - -`--format` (text|json) picks the representation; `--detail` (brief|normal|full) picks how much of each node/edge shows — **both honor the same detail level**. Default: `text` + `normal`. `inspect` and orientation commands default to `full`. `--format json` emits the projected envelope (empty fields dropped): - -```json -{ - "status": "ok|not_found|error", - "nodes": {"": {...}}, - "edges": [{...}], - "candidates": [{...}], - "truncated": false, - "agent_next_actions": ["jrag callers ", "..."], - "file_location": {"filename": "...", "start_line": 123} -} -``` - -`truncated` is computed via +1-fetch on `find`/`search` (use `--limit`, then `--offset`); other commands emit the `more results` message when capped. `agent_next_actions` (≤5) maps result edges to next commands — a starting point, not a directive. `file_location` populates only on `one`-hit resolve. - -## Traversal direction reference - -`jrag` abstracts away `direction`/`edge_types` — you name the intent, it picks the edges: - -| Intent (command) | Underlying edges | -| --- | --- | -| `callers` / `callees` | `CALLS` in / out. On a controller/entry-point type, `callers` also folds in the routes its methods `EXPOSE` (they are the inbound callers) — use it to list a controller's endpoints. `decompose` defaults to `--follow-calls`; `--per-stage-limit` caps symbols per stage (not stage count) | -| `hierarchy` | `EXTENDS` + `IMPLEMENTS`, both directions (parents + children) | -| `implementations` / `subclasses` | `IMPLEMENTS` / `EXTENDS` in | -| `overrides` / `overridden-by` | `OVERRIDES` out (subtype→supertype) / in | -| `dependencies` / `dependents` | `INJECTS` out / in | -| `impact` | bounded fan-in: `INJECTS`/`IMPLEMENTS`/`EXTENDS` in (depth ≤2) | -| `flow ` | `EXPOSES`/`HTTP_CALLS`/`ASYNC_CALLS`/`CALLS` | -| `connection A B` | bounded search over the same edge set | - -**Node id prefixes (from prior results):** `sym:` (Symbol), `route:`/`r:` (Route), `client:`/`c:` (Client), `producer:`/`p:` (Producer). **Symbol FQN:** `.[.]#(,…)` — generics erased, no spaces after commas, no-arg `()`, constructor `#(...)`. - -## Ontology glossary - -**Roles:** `CONTROLLER` | `SERVICE` | `REPOSITORY` | `COMPONENT` | `CONFIG` | `ENTITY` | `CLIENT` | `MAPPER` | `DTO` | `OTHER`. -**Capabilities:** `MESSAGE_LISTENER`, `MESSAGE_PRODUCER`, `HTTP_CLIENT`, `SCHEDULED_TASK`, `EXCEPTION_HANDLER`. -**Symbol kinds:** `class`, `interface`, `enum`, `record`, `annotation`, `method`, `constructor`. -**Route frameworks:** `spring_mvc`/`webflux` (HTTP), `kafka`/`rabbitmq`/`jms`/`stream` (messaging), `feign` (client mirrors). Route *kinds*: `http_endpoint`, `http_consumer`, `kafka_topic`, `rabbit_queue`, `jms_destination`, `stream_binding`. **Client kinds:** `feign_method`, `rest_template`, `web_client`. **Producer kinds:** `kafka_send`, `stream_bridge_send`. **Source layers:** `builtin`, `layer_a_meta`, `layer_b_ann`, `layer_b_fqn`, `layer_c_source`. +## Workflow Patterns ---- +- **"Explain feature X":** `jrag search "X"` → pick 1–3 hits → `jrag inspect ` → targeted traversal (`callees`/`implementations`) → stop when answered. +- **"Where is X used?":** `jrag inspect ` → `jrag callers ` + `jrag dependents ` → `Grep` the symbol name as fallback → report sites with file:line. +- **"Find all Y":** structural → `jrag find --role [--service ]`; textual → `Grep`; broad → `Glob`+`Grep`. Summarize, don't dump. +- **"Trace flow A→B":** `jrag flow ` → `jrag connection ` (cross-service seams) → `Grep` the gaps → report with file:line. +- **"How is this configured?":** `Glob` `**/application*.yml` → `Grep` the key → `Read` sections → `jrag search "" --table yaml`. +- **"Orient in a new service":** `jrag overview ` → `jrag conventions --service ` → `jrag map --service ` → `jrag http-routes --service `. ## Recovery Playbook @@ -174,11 +83,23 @@ Never look up a raw node ID — pass an FQN, simple name, prior `sym:`/`route:`/ --- -## Workflow Patterns +## jrag surface — `--help` is the spec -- **"Explain feature X":** `jrag search "X"` → pick 1–3 hits → `jrag inspect ` → targeted traversal (`callees`/`implementations`) → stop when answered. -- **"Where is X used?":** `jrag inspect ` → `jrag callers ` + `jrag dependents ` → `Grep` fallback → report sites with file:line. -- **"Find all Y":** structural → `jrag find --role [--service ]`; textual → `Grep`; broad → `Glob`+`Grep`. Summarize, don't dump. -- **"Trace flow A→B":** `jrag flow ` → `jrag connection A B` → `Grep` gaps → report with file:line. -- **"How is this configured?":** `Glob` `**/application*.yml` → `Grep` the key → `Read` sections → `jrag search "" --table yaml`. -- **"Orient in a new service":** `jrag overview ` → `jrag conventions --service ` → `jrag map --service ` → `jrag http-routes --service `. +`jrag` is self-documenting and the canonical, always-fresh source for commands, flags, and valid enum values — so this skill doesn't duplicate them. Don't memorize the surface: + +- `jrag --help` — every command, grouped by intent, with one-line descriptions. +- `jrag --help` — that command's flags and accepted values. Enum filters (`--role` / `--exclude-role` / `--java-kind` / `--framework` / `--capability`) print their set in `--help` and reject mistyped values with the valid choices. + +The Decision Framework above tells you *which* command; reach for `--help` only when you need exact flags or enum values. + +**Prerequisite.** `jrag` needs an index — unindexed, every command exits 2 (`jrag status` checks; the file-system tools work without one). + +**Resolve-first contract.** Every `` command resolves the identifier first, then maps `one` / `many` / `none` onto one envelope: `one` → run; `many` → return candidates and stop, **no silent guess across distinct types** (a class sharing its simple name with its own constructor still resolves to the type — narrow with `--kind` / `--role` / `--fqn-contains` / `--service`); `none` → `status: not_found` (exit 0), fall back to `search` or `Grep`. Pass names (FQN / simple name / route path / topic) or prior `sym:`/`route:`/`client:`/`producer:` ids — never raw node ids. `--kind` is a true resolve input; `--role` / `--java-kind` / `--fqn-contains` post-filter client-side. + +**Output.** Default is compact text; `--format json` emits `{status, nodes, edges, candidates, truncated, agent_next_actions, file_location}` (empty fields dropped; `file_location` is a `filename:line` string; `agent_next_actions` suggests ≤5 next commands). `truncated` pages via `--limit` / `--offset` (`find` / `search` only). + +**Edge semantics `--help` doesn't spell out.** `callers` / `callees` = `CALLS` in/out (on a controller/entry-point type, `callers` also lists the routes its methods `EXPOSE`). `impact` = bounded fan-in over `INJECTS` / `IMPLEMENTS` / `EXTENDS` (default depth 2; raise with `--depth`). `flow ` follows `EXPOSES` → `HTTP_CALLS` / `ASYNC_CALLS` → `CALLS`. `connection ` = inbound/outbound cross-service seams (its positional is a literal service name, not a query). Per-command edge mappings and the rest of the flag surface live in each command's `--help`. + +**Node id prefixes (from prior results):** `sym:` (Symbol), `route:`/`r:` (Route), `client:`/`c:` (Client), `producer:`/`p:` (Producer). **Symbol FQN:** `.[.]#(,…)` — generics erased, no spaces after commas, no-arg `()`, constructor `#(...)`. + +**Ontology.** Role / symbol-kind / framework / capability values are enumerated in `--help`; client/producer kinds and source layers validate at runtime and surface the accepted set on a typo. diff --git a/tests/test_jrag_enum_choices.py b/tests/test_jrag_enum_choices.py new file mode 100644 index 0000000..f6a2f40 --- /dev/null +++ b/tests/test_jrag_enum_choices.py @@ -0,0 +1,76 @@ +"""Cross-check jrag's hardcoded enum ``choices=`` constants against the +canonical ontology literals (``mcp_v2`` / ``java_ontology``), so the constants +in ``java_codebase_rag/jrag.py`` can't silently drift (they're hardcoded to +keep ``jrag --help`` fast — see the comment above ``_ROLE_CHOICES`` there). + +Also covers the ``choices=`` wiring and the case-normalizing ``type=`` on +``--role`` / ``--exclude-role`` / ``--java-kind`` / ``--framework`` / +``--capability``: flexible casing must normalize to the stored literal, and +typos must be rejected with the valid set. +""" +from __future__ import annotations + +import argparse +from typing import get_args + +import pytest + +import java_ontology +import mcp_v2 + +from java_codebase_rag.jrag import ( + _CAPABILITY_CHOICES, + _FRAMEWORK_CHOICES, + _JAVA_KIND_CHOICES, + _ROLE_CHOICES, + build_parser, +) + + +def test_role_choices_match_canonical_literal() -> None: + assert set(_ROLE_CHOICES) == set(get_args(mcp_v2.Role)) + + +def test_java_kind_choices_match_canonical_literal() -> None: + assert set(_JAVA_KIND_CHOICES) == set(get_args(mcp_v2.DeclarationSymbolKind)) + + +def test_framework_choices_match_canonical_literal() -> None: + assert set(_FRAMEWORK_CHOICES) == {f for f in get_args(mcp_v2.Framework) if f} + + +def test_capability_choices_match_canonical_set() -> None: + assert set(_CAPABILITY_CHOICES) == set(java_ontology.VALID_CAPABILITIES) + + +@pytest.mark.parametrize( + "argv,attr,expected", + [ + (["find", "--role", "controller"], "role", "CONTROLLER"), + (["find", "--role", "CONTROLLER"], "role", "CONTROLLER"), + (["find", "--exclude-role", "dto"], "exclude_role", "DTO"), + (["find", "--capability", "scheduled-task"], "capability", "SCHEDULED_TASK"), + (["find", "--java-kind", "Interface"], "java_kind", "interface"), + (["http-routes", "--framework", "Spring-MVC"], "framework", "spring_mvc"), + (["search", "payment", "--role", "service"], "role", "SERVICE"), + ], +) +def test_enum_flags_normalize_flexible_casing(argv, attr, expected) -> None: + ns = build_parser().parse_args(argv) + assert getattr(ns, attr) == expected + + +@pytest.mark.parametrize( + "argv", + [ + ["find", "--role", "CONROLER"], + ["find", "--capability", "nope"], + ["find", "--java-kind", "klass"], + ["http-routes", "--framework", "django"], + ["search", "--exclude-role", "WIDGET"], + ], +) +def test_enum_flags_reject_invalid_values(argv) -> None: + # build_parser's error() raises argparse.ArgumentError (not SystemExit). + with pytest.raises(argparse.ArgumentError): + build_parser().parse_args(argv) From aea911449227c561469913baea9653dea9d79499 Mon Sep 17 00:00:00 2001 From: dmitry Date: Tue, 7 Jul 2026 16:22:51 +0300 Subject: [PATCH 3/3] bump version to 0.9.3 Co-Authored-By: Claude --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d049b31..1145387 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "java-codebase-rag" -version = "0.9.2" +version = "0.9.3" description = "MCP server for semantic + structural search over Java codebases" readme = "README.md" requires-python = ">=3.11"