rgctl User Guide
End-to-end guide for installing rgctl, indexing an in-tree example, and querying a codebase from the command line. Sample outputs target rgctl-tests/ecommerce-java. Runnable examples are backed by scenarios under user-guide/scenarios/ (see change docs-agent-first-diataxis).
Concepts: Introduction. Agents: AGENTS.md. JSON fields: json-api.md.
How this guide is organized
| Zone | Sections | Role |
|---|---|---|
| Tutorial spine | §1–4, §16 | Install → fixture → discover → recommended workflow |
| How-to | §5–14 | Flags and feature commands with sample output |
| Optional UI | §15 | serve / dashboard — nice-to-have, not required for agents |
| Reference | §17–18 | Command cheat sheet + troubleshooting |
Table of contents
- Installation
- Add rgctl to your PATH
- Example project: ecommerce-java
- Index with
discover - Global CLI flags
- Query the graph with GQL
- Blast radius (change impact)
- Program slicing and taint
- Inspect CFG / PDG / dominance
- Hybrid CPG (
cpg) - Graph metrics
- Semantic search
- Export graph projections
- CI policy check
- HTTP server (
serve) — optional - Recommended workflow
- Command reference
- Troubleshooting
1. Installation
Standalone guide: Installation covers prerequisites, CLI and HTTP modes, agent skill setup, upgrading, and troubleshooting.
Option A — GitHub release (recommended)
Pre-built binaries are published on the project Releases page:
https://github.com/sshaaf/rgctl/releases
-
Open the latest release.
-
Download the archive for your platform:
Platform Typical asset name macOS (Apple Silicon) rgctl-*-aarch64-apple-darwin.tar.gzmacOS (Intel) rgctl-*-x86_64-apple-darwin.tar.gzLinux (x86_64) rgctl-*-x86_64-unknown-linux-gnu.tar.gzWindows rgctl-*-x86_64-pc-windows-msvc.zip -
Extract the archive. You should get a single
rgctlexecutable (plusrgctl.exeon Windows).
# macOS / Linux example
tar -xzf rgctl-*-aarch64-apple-darwin.tar.gz
./rgctl --version
# Windows example (PowerShell)
Expand-Archive rgctl-*-x86_64-pc-windows-msvc.zip -DestinationPath .
.\rgctl.exe --version
If no release is published yet for your platform, use Option B.
Option B — Build from source
Requires Rust 1.88+ (Edition 2024; rustup.rs).
git clone https://github.com/sshaaf/rgctl.git
cd rgctl
# Optional: code-daemon ONNX weights (~206 MB via Git LFS) if you use
# `semantic index --embedder code-daemon`. Skip for the default vocab embedder.
git lfs pull
cargo build --release --bin rgctl
./target/release/rgctl --version
All nine Tier 1 languages (Rust, Python, JavaScript, TypeScript, Go, Java, C#, C, C++) are always included in the binary.
Install the agent skill
After rgctl is on your PATH, install the bundled rgctl skill into the target repository (the same root you pass to discover via -r / --repo, or the current directory):
rgctl install --skill
# or, from another cwd:
rgctl -r /path/to/repo install --skill
That writes:
<repo>/.claude/skills/rgctl/(Claude Code)<repo>/.cursor/skills/rgctl/(Cursor)
Limit hosts with --host claude or --host cursor (default is all). Identical files are left unchanged. If a dest file differs, the command exits 1 unless you pass --force. Re-run install --skill --force after upgrading rgctl to refresh the project copy. Manual copy of skills/rgctl/ remains a fallback if you have a git checkout.
2. Add rgctl to your PATH
Pick one approach for your shell.
macOS / Linux — user-local install
mkdir -p ~/.local/bin
cp /path/to/rgctl ~/.local/bin/
chmod +x ~/.local/bin/rgctl
Add to ~/.zshrc or ~/.bashrc:
export PATH="$HOME/.local/bin:$PATH"
Reload and verify:
source ~/.zshrc # or ~/.bashrc
rgctl --version
macOS / Linux — system-wide (optional)
sudo cp /path/to/rgctl /usr/local/bin/
rgctl --version
Windows
- Copy
rgctl.exeto a folder such asC:\Tools\rgctl\. - Open Settings → System → About → Advanced system settings → Environment Variables.
- Under User variables, edit
Pathand addC:\Tools\rgctl. - Open a new terminal:
rgctl --version
Per-project usage (no PATH change)
Pass the full path or use a repo-local alias:
alias rgctl='/path/to/rgctl'
3. Example project: ecommerce-java
This guide uses the in-tree Spring Boot fixture shipped with rgctl:
It implements the same e-commerce domain as the other ecommerce-* fixtures (cart, orders, products, auth), plus a CoolStore-compatible dual API under /services/* (additive next to /api/*). No separate clone is required when you have the rgctl repo.
# From the rgctl repository root
export REPO="$PWD/rgctl-tests/ecommerce-java"
cd "$REPO"
Layout (simplified):
ecommerce-java/
├── pom.xml
└── src/main/java/com/example/ecommerce/
├── controller/ # /api/* — CartController, OrderController, ProductController, …
├── service/ # CartService, OrderService, ProductService, …
├── entity/ # Cart, Order, Product, User, …
├── repository/ # Spring Data JPA repos
├── security/ # JWT filter / token provider
└── coolstore/ # /services/* — CoolStore cart pricing + orders (in-memory)
├── rest/ # ProductEndpoint, CartEndpoint, OrderEndpoint
├── service/ # ShoppingCartService, PromoService, ShippingService, …
└── model/ # ShoppingCart, ShoppingCartItem, CatalogProduct, …
Dual REST surface (same contract on every ecommerce-* language):
| Surface | Role |
|---|---|
/api/* | JWT e-commerce API (auth, categories, cart ownership, reviews, …) |
/services/* | CoolStore-style products / cart / checkout / orders (cartId session carts) |
ShoppingCartService.priceShoppingCart mutates cart totals (promo + shipping) — the Layer F target for cpg mutations --type ShoppingCart. Full route table: rgctl-tests/README.md.
Sibling fixtures (ecommerce-python, ecommerce-rust, ecommerce-c, …) share both REST shapes.
All commands below assume REPO points at ecommerce-java, or that you run from inside that directory and use . instead of "$REPO".
Sample outputs in this guide were captured on a laptop with a release build; absolute paths are shortened to …/ecommerce-java/… for readability. Counts may differ slightly across versions.
4. Index with discover
discover scans source files, builds the knowledge graph, runs analytics (complexity, communities, centrality, blast-radius scoring), and writes artifacts under {repo}/.rgctl/.
Choosing the discover target (common pitfall):
| Command | Indexes |
|---|---|
cd repo && rgctl discover . | Recommended — current directory |
rgctl -r /path/to/repo discover | Repo at /path/to/repo |
rgctl discover /path/to/repo | Same (absolute path positional) |
rgctl -r /path/to/repo discover . | Shell cwd only — -r is ignored when . is present |
Full table: Discovering and indexing.
Built-in registry includes markdown (rgctl-lang-markdown): .md and .mdx are indexed by default (headings, links, code blocks, frontmatter). See markdown-context.md. Use -l markdown or -l markdown,java to limit languages.
Full pipeline (--full)
rgctl discover PATH --full prints an execution plan, runs a basic discover (queryable snapshot), reports that the initial discover is complete, then continues in the same process with --with-cfg --with-dashboard --with-harmonic, then semantic index. Other terminals can gql after stage 1. Does not imply taint or secret scanning.
rgctl discover . --full
Status is written to .rgctl/pipeline_status.json. A second --full on unchanged sources skips fresh stages.
Fast index (default)
cd "$REPO"
rgctl discover . -l java -e target
Example output:
==> Analyzing: …/ecommerce-java/.
[✓] Indexed 51 files -> 518 nodes, 1122 edges (0.0s)
[✓] Detected 443 communities (modularity: 0.47)
[✓] Analyzed 187 functions (avg complexity: 1.0, 0 high, 0 medium)
[*] Top hotspot: findAll (PageRank: 0.0177)
[!] Found 48 circular dependencies
[✓] Analysis complete
[✓] Saved to .rgctl/ (0.1 MB total)
[✓] Completed in 0.0s (peak memory: 21 MB)
[i] Next steps:
rgctl gql "MATCH (n:Function) RETURN n" # Query the graph
rgctl slice <file> --line <N> --variable <VAR>
rgctl serve --open # Dashboard + query API at http://127.0.0.1:8080
Typical runtime on this fixture: well under a second.
CI / automation — structured metrics on stdout:
rgctl -f json discover . -l java -e target | jq .
<!-- ug-scenario:04-discover-json -->
Example:
{
"command": "discover",
"metrics": {
"duration_ms": "…",
"edges_generated": 3293,
"files_discovered": 65,
"files_indexed": 65,
"files_skipped": 0,
"nodes_generated": 1088
},
"schema_version": 2
}
<!-- /ug-scenario:04-discover-json -->
Language and path filters
# Java only, skip Maven output
rgctl discover . -l java -e target
# Multiple languages (polyglot monorepo)
rgctl discover . -l java,typescript -e target,node_modules,dist
Default pipeline (always on)
Bare discover (no --with-*) always runs: index/extract → topology → community → complexity → PageRank/betweenness → dependency cycles → blast engine → persist analysis + snapshot.
Harmonic, dashboard, migration export, security, CFG/PDG, and discover-time taint are opt-in via the flags below.
Deeper analysis (opt-in)
| Flag | What it adds |
|---|---|
--with-security | Secret scanning |
--with-cfg | Per-function CFG, dominators, PDG (archive under .rgctl/analysis/) |
--with-taint | Discover-time taint into archive (implies CFG/PDG pass) |
--with-harmonic | Harmonic centrality (migration ranking) |
--with-dashboard | Static dashboard bundle under .rgctl/dashboard/ |
--export-migration-hints | Migration roadmap JSON (alias: --export-migration-plan) |
--with-kantra | Konveyor Kantra rule evaluation + rules graph index (see below) |
# CFG so inspect / slice have rich PDG context
rgctl discover . -l java -e target --with-cfg
# Full walkthrough set used for the samples below
rgctl discover . -l java -e target \
--with-cfg --with-dashboard --with-harmonic --export-migration-hints
Example lines from that richer run:
[!] Deep analysis enabled (--with-cfg / --with-taint).
✓ Control flow analysis:
CFG/PDG/Dominance: 178 functions analyzed
Skipped: 9 functions (unsupported language or parse error)
[✓] Migration plan (Hybrid Default): 9 steps → …/ecommerce-java/./.rgctl/migration_plan.json
[✓] Dashboard: …/ecommerce-java/./.rgctl/dashboard/index.html
Use --with-cfg when you need inspect / slice overlays; add --with-taint for discover-time taint flows. On large monorepos (100k+ functions) expect minutes to hours.
Kantra migration rules (--with-kantra)
Evaluate Konveyor Kantra rules natively against the indexed graph and source cache — no Kantra CLI, LSP, or containers. Release binaries embed the Konveyor stable/java catalog (~2.6k rules); discover --with-kantra uses it by default.
# Default: embedded catalog + eval + index rules into graph.snapshot.bin
rgctl discover . -l java -e target --with-kantra
# Migration target filter (konveyor.io/target=<NAME> labels)
rgctl discover . -l java --with-kantra --kantra-target quarkus
# CI / fixtures: override with a local ruleset directory
rgctl discover . --with-kantra --kantra-rules tests/fixtures/kantra-rules
# Dev: local konveyor/rulesets checkout instead of embedded blob
rgctl discover . --with-kantra --kantra-catalog /path/to/rulesets/stable/java
# Index rules into graph only (skip eval)
rgctl discover . --with-kantra --kantra-index-only
| Flag | Purpose |
|---|---|
--with-kantra | Run Kantra eval and index KantraRule / KantraRuleset nodes into the session graph |
--kantra-target NAME | Evaluate only rules labeled konveyor.io/target=NAME |
--kantra-rules DIR | Single ruleset directory (ruleset.yaml + *.yaml); overrides embedded catalog |
--kantra-catalog ROOT | Rulesets tree (e.g. stable/java); overrides embedded catalog |
--kantra-index-only | Skip eval; still index catalog rules into the graph |
Artifacts: .rgctl/kantra_findings.json (violations + skipped rules). Rule nodes are queryable via GQL after index:
rgctl gql "MATCH (r:KantraRule) RETURN r LIMIT 10"
rgctl gql 'MATCH (r:KantraRule) WHERE r.`konveyor.io/target` = '\''quarkus'\'' RETURN r'
Konveyor labels (konveyor.io/target, konveyor.io/source, …) are stored as node properties; use backtick-quoted property names in GQL WHERE clauses.
Source builds: full embedded catalog requires the git submodule — git submodule update --init crates/rgctl-kantra/assets/rulesets or ./scripts/init-kantra-rulesets.sh. Without it, the build falls back to tests/fixtures/kantra-rules/. See crates/rgctl-kantra/README.md.
Further reading: Kantra integration exploration · Architecture options (embedded catalog + rules graph) · JSON schema
Does not require --with-cfg. Unsupported rule providers and invalid regex patterns are skipped with reasons in skipped_rules. After full eval, VIOLATES edges link each KantraRule to matched code nodes (queryable via GQL).
Dashboard: discover --with-kantra --with-dashboard exports kantra_index.json and per-file violation shards. Open Migration Rules in the dashboard (category + Konveyor target filters, syntax-highlighted snippets). See Dashboard user guide — Migration Rules.
Verbose logging and stage profiling
rgctl discover . -v
With -v, discover emits a [profile] discover summary line (wall time, peak RSS, node count) and per-stage timings.
Extraction internals also emit populate_graph timing buckets in profile logs. These buckets are intentionally distinct (symbol_processing_secs, config_key_secs, relation_resolution_secs, config_usage_secs) and are measured independently to avoid double-counting when one bucket is empty.
RUST_LOG=info,profile=info rgctl discover . --with-cfg -v -l java -e target 2>&1 \
| tee discover-profile.log
grep '\[profile\]' discover-profile.log
Example profile lines (ecommerce-java, --with-cfg):
[profile] discover summary wall_secs=0.14 index_secs=0.01 post_index_secs=0.09 \
peak_rss_mb=27.0 functions=187 nodes=518 cfg=true security=false
[profile] stage stage="cfg_total" secs=0.030 pct_wall=21.0
[profile] stage stage="save_dashboard" secs=0.028 pct_wall=19.6
[profile] stage stage="index_extract" secs=0.012 pct_wall=8.1
Harmonic centrality is off by default — pass --with-harmonic when you need it for migration ranking. On kernel-scale graphs it adds ~30s wall and multi‑GB peak RSS.
See analysis-architecture.md and internal/profile.md for large-graph adaptive gating and cold-profile runs.
Legacy JSON graph (optional)
By default, rgctl writes a binary snapshot (graph.snapshot.bin). Legacy graph.db / graph.json are only written when requested:
rgctl discover . --write-json-graph
What discover creates
After a successful run, the layout below appears under {repo}/.rgctl/:
{repo}/.rgctl/
├── graph.snapshot.bin # Columnar mmap graph (primary cache for queries)
├── content_store.bin # Large markdown bodies / files (body_ref / blob_ref; Obsidian + doc semantic)
├── blast_engine.snapshot.bin # Pre-built blast-radius engine
├── macro_call_index.db # Blast-radius lookup cache (SQLite; not the graph)
├── macro_call_index.bin # Same index in bincode (companion to .db)
├── analysis_results.bin # Columnar analysis properties
├── kantra_findings.json # With --with-kantra (violations + skipped rules)
├── file_hashes.json # Incremental file tracker
├── migration_plan.json # With --export-migration-hints
├── analysis/ # Per-function CFG/PDG/taint (with --with-cfg / --with-taint)
│ └── cfg_pdg.archive.bin
└── dashboard/ # Only with --with-dashboard
├── index.html
├── manifest.json
├── migration_plan.json
└── graph_payload.bin
Query commands read graph.snapshot.bin when present. You do not need graph.db for normal CLI use.
Point every subsequent command at the same repo root you indexed:
export REPO="$PWD" # after cd into the repo
# or pass -r on each command:
rgctl -r "$REPO" gql 'MATCH (n:Function) RETURN n LIMIT 5'
5. Global CLI flags
These apply to every subcommand:
| Flag | Purpose |
|---|---|
-r, --repo PATH | Repository root (default: current directory) |
-d, --db PATH | Legacy graph JSON path (default: .rgctl/graph.db) |
-f, --format FORMAT | Output: text, json, graphviz, mermaid |
-o, --output FILE | Write command output to a file instead of stdout |
Examples:
# JSON for scripting
rgctl -r "$REPO" -f json gql 'MATCH (n:Class) RETURN n LIMIT 10'
# Mermaid diagram to a file
rgctl -r "$REPO" -f mermaid -o checkout-cfg.mmd inspect checkout cfg
6. Query the graph with GQL
gql runs the graph query language against the indexed graph. Run discover first.
Inventory macros
rgctl -r "$REPO" gql --macro-name all_functions unused
Text mode prints one function name per line (count varies with fixture size). JSON is better for scripts:
rgctl -r "$REPO" -f json gql --macro-name all_functions unused | jq '.count'
<!-- ug-scenario:06-gql-all-functions -->
317
<!-- /ug-scenario:06-gql-all-functions -->
Exact name match
rgctl -r "$REPO" gql \
"MATCH (n:Function) WHERE n.name = 'clearCart' RETURN n"
clearCart
clearCart
(There are two clearCart methods — service and controller.)
JSON shows file paths:
rgctl -r "$REPO" -f json gql \
"MATCH (n:Function) WHERE n.name = 'clearCart' RETURN n" | jq '.rows'
[
[
{
"binding": "n",
"file": "…/service/CartService.java",
"node": "clearCart",
"type": "Function"
}
],
[
{
"binding": "n",
"file": "…/controller/CartController.java",
"node": "clearCart",
"type": "Function"
}
]
]
Classes
rgctl -r "$REPO" -f json gql \
"MATCH (n:Class) WHERE n.name = 'CartService' RETURN n" | jq '.rows[0]'
[
{
"binding": "n",
"file": "…/service/CartService.java",
"node": "CartService",
"type": "Class"
}
]
Call relationships
Who calls clearCart?
rgctl -r "$REPO" gql \
"MATCH (a:Function)-[:CALLS]->(b:Function) WHERE b.name = 'clearCart' RETURN a,b"
checkout -> clearCart
clearCart -> clearCart
JSON (trimmed):
{
"count": 2,
"rows": [
[
{ "binding": "a", "node": "checkout", "file": "…/OrderService.java", "type": "Function" },
{ "binding": "b", "node": "clearCart", "file": "…/CartService.java", "type": "Function" }
]
],
"schema_version": 1
}
Common node / edge types
- Nodes:
Function,Class,Interface,Module,File,Import,ConfigKey, … - Edges:
CALLS,IMPORTS,CONTAINS,DEPENDS_ON,IMPLEMENTS, …
Named communities (analysis overlay)
discover runs label-propagation community detection and stores assignments in
.rgctl/analysis_results.bin — not as edges in the topology graph.
gql joins that sidecar so you can list and filter communities:
Community detection uses behavioral edges (Calls, Uses, References) by default.
On mixed code + markdown repos, doc REFERENCES participate in the same community pass as code edges; this is expected behavior.
# Macro: list communities (id, heuristic label, member_count)
rgctl -r "$REPO" -f json gql --macro-name all_communities unused | jq '.rows[:3]'
[
[
{
"binding": "c",
"node": "ecommerce.service::checkout",
"type": "Community",
"label": "ecommerce.service::checkout",
"community_id": 385,
"member_count": 19,
"file": null
}
]
]
# Members of one community (use an id from the list above)
rgctl -r "$REPO" -f json gql \
"MATCH (f:Function) WHERE f.community_id = '385' RETURN f LIMIT 10" | jq '.count'
# CLI helpers (same labels; --write refreshes analysis_results.bin)
rgctl -r "$REPO" communities list
rgctl -r "$REPO" communities label --write
Labels are heuristic (package path, top PageRank symbol, token majority, infrastructure hubs). They are for orientation — not ground-truth domain names. See community query & naming plan.
Virtual type :Community is query-only; there is no MEMBER_OF edge in the snapshot.
7. Blast radius (change impact)
blast-radius answers: “What breaks upstream if I change this symbol?”
Bare names are often ambiguous. Prefer FQN (Class::method):
rgctl -r "$REPO" blast-radius 'CartService::clearCart'
Blast radius for 'CartService::clearCart'
Score: 25.1/100
Direct callers: 1
Impact zone: 1
Callers: OrderService.checkout
Impact: OrderService.checkout
Ambiguous bare name shows remediation:
rgctl -r "$REPO" blast-radius clearCart
Error: Symbol 'clearCart' is ambiguous. Found 2 matches.
UUID | Class Context | Source File Path
… | CartService | …/CartService.java
… | CartController | …/CartController.java
Remediation: Refine your search query using a fully qualified namespace syntax:
rgctl blast-radius "ClassName::clearCart"
rgctl blast-radius "path/to/file.java::clearCart"
Symbol forms
| Form | Example |
|---|---|
| Bare name | checkout (fails if ambiguous) |
| FQN | CartService::clearCart |
| UUID | node id from GQL / blast JSON |
Disambiguate with filters:
rgctl -r "$REPO" blast-radius clearCart --class CartService
rgctl -r "$REPO" blast-radius clearCart \
--file src/main/java/com/example/ecommerce/service/CartService.java
Limit caller depth
rgctl -r "$REPO" blast-radius 'CartService::clearCart' --depth 1
rgctl -r "$REPO" blast-radius 'CartService::clearCart' --depth 5
Omit --depth for full transitive upstream closure.
JSON output
rgctl -r "$REPO" -f json blast-radius 'CartService::clearCart' \
| jq '{score: .metrics.score, callers: .topology.direct_callers}'
<!-- ug-scenario:07-blast-clearCart -->
{
"score": 25.05,
"callers": [
{
"file_path": "…/OrderService.java",
"fqn": "com.example.ecommerce.service.OrderService.checkout",
"id": "…"
}
]
}
<!-- /ug-scenario:07-blast-clearCart -->
Schema: json-api.md (blast-radius + field catalogs).
Statement-level slice hand-offs (slow)
rgctl -r "$REPO" blast-radius 'CartService::clearCart' --with-slices
Requires discover --with-cfg for rich PDG context.
8. Program slicing and taint
slice performs line-level backward or forward slicing on a source file. Paths may be absolute, cwd-relative, or relative to --repo. Run discover --with-cfg first so PDG data is available.
Backward slice
“What code influences this variable at this line?” — in OrderService.checkout, cart is assigned on line 52:
rgctl -r "$REPO" slice \
src/main/java/com/example/ecommerce/service/OrderService.java \
--line 52 \
--variable cart \
--function checkout
Backward slice for src/main/java/com/example/ecommerce/service/OrderService.java:52 (variable: cart)
Reduction: 92.3%
52
A denser example from CartService.addItem (line 53, local item):
rgctl -r "$REPO" slice \
src/main/java/com/example/ecommerce/service/CartService.java \
--line 53 \
--variable item \
--function addItem
Backward slice for src/main/java/com/example/ecommerce/service/CartService.java:53 (variable: item)
Reduction: 92.9%
53
Forward slice
rgctl -r "$REPO" slice \
src/main/java/com/example/ecommerce/service/CartService.java \
--line 38 \
--variable cart \
--function addItem \
--direction forward
Taint trace
rgctl -r "$REPO" slice \
src/main/java/com/example/ecommerce/service/OrderService.java \
--line 83 \
--variable cartService \
--function checkout \
--taint
View modes
--view | Description |
|---|---|
text | Summary (default) |
cfg | CFG overlay — use with -f mermaid or -f graphviz |
pdg | PDG overlay |
rgctl -r "$REPO" -f mermaid slice \
src/main/java/com/example/ecommerce/service/CartService.java \
--line 53 --variable item --function addItem --view cfg
--function names
--function must be the method/function name in the source file (as parsed by tree-sitter), not the enclosing class name:
rgctl -r "$REPO" gql \
"MATCH (n:Function) WHERE n.name = 'checkout' RETURN n"
9. Inspect CFG / PDG / dominance
inspect dumps semantic layers for an indexed function symbol (no --class flag — use a unique symbol or GQL to pick the right function). Run discover --with-cfg first.
rgctl -r "$REPO" inspect checkout cfg
CFG for checkout: 5 blocks, 5 edges
rgctl -r "$REPO" -f json inspect checkout cfg | jq '{layer, blocks: (.nodes|length), edges: (.edges|length)}'
{
"layer": "cfg",
"blocks": 5,
"edges": 5
}
Mermaid CFG:
rgctl -r "$REPO" -f mermaid inspect checkout cfg
flowchart TD
462c1054-… --> 14712608-…
462c1054-… --> ae5a5a76-…
14712608-… --> 897883b6-…
ae5a5a76-… --> 897883b6-…
897883b6-… --> 4165ce10-…
Other layers:
# Prune unreachable blocks
rgctl -r "$REPO" inspect checkout cfg --prune
# Program dependence graph (data edges)
rgctl -r "$REPO" inspect checkout pdg --edge-layer data
# → PDG for checkout: 13 nodes, 22 data deps, 0 control deps
rgctl -r "$REPO" inspect checkout pdg --def-use
rgctl -r "$REPO" inspect checkout dom --frontiers
10. Hybrid CPG (cpg)
The cpg façade bridges the repo call graph (L_repo) with the per-function CFG/PDG archive (L_proc) built by discover --with-cfg. Use it for typed field mutations, data flows, and Joern-style handoffs without stitching several CLI tools yourself.
Requires a prior discover … --with-cfg (the ecommerce walkthrough already uses that flag).
Status and CALL neighborhood
rgctl -r "$REPO" cpg status
# → CPG L_proc: ready (… functions) at …/cfg_pdg.archive.bin
# → CPG field writes: N indexed (cpg mutations)
rgctl -r "$REPO" cpg function priceShoppingCart
rgctl -r "$REPO" cpg calls 'ShoppingCartService::priceShoppingCart'
Field mutations (CoolStore ShoppingCart)
Find non-constructor writes to a type — useful before converting a mutable DTO/cart model to an immutable record, or to prove pricing still mutates totals:
rgctl -r "$REPO" cpg mutations --type ShoppingCart --exclude-ctors
Example (paths shortened):
Mutations of ShoppingCart [excl. ctors] (7 hits):
…/coolstore/model/ShoppingCart.java:61 this.cartTotal = cartTotal
…/coolstore/model/ShoppingCart.java:45 this.cartItemTotal = cartItemTotal
…
Pair with blast-radius on the CoolStore pricing entrypoint:
rgctl -r "$REPO" blast-radius 'ShoppingCartService::priceShoppingCart'
# → Callers include CartEndpoint.add / delete / checkout and checkOutShoppingCart
Dashboard: after discover --with-cfg --with-dashboard, the Dataflow tab includes a Field mutations (CPG) panel (same filters). Click a hit to open that function’s PDG and highlight the write line. See Dashboard user guide.
JSON for agents:
rgctl -r "$REPO" -f json cpg mutations --type ShoppingCart --exclude-ctors
Empty result means no typed non-ctor writes were recovered (receivers without a resolved type are omitted unless --include-unresolved). On C fixtures, query the struct typedef name (e.g. shopping_cart_t). See agent-recipes.md Recipe 11 and hybrid-cpg-plan.md.
Flows, AST, export
# Forward flows from a variable at a line (wraps slice; optional --with-alias)
rgctl -r "$REPO" -f json cpg flows \
src/main/java/com/example/ecommerce/coolstore/service/ShoppingCartService.java \
--line 75 --variable sc --function priceShoppingCart --direction forward
# Optional: discover --with-ast-skeleton then:
rgctl -r "$REPO" -f json cpg ast priceShoppingCart
rgctl -r "$REPO" cpg export --format graphson --output /tmp/ecommerce-cpg.json \
--path-contains coolstore/
11. Graph metrics
metrics reports network analytics on the indexed call graph. Prefer JSON for scripting (text mode prints debug-style structs).
rgctl -r "$REPO" -f json metrics --communities | jq .
{
"communities": {
"assignments": 518,
"count": 442,
"modularity": 0.49
},
"schema_version": 1
}
That summary is counts only. For named communities and membership, use GQL / communities list
(§6) or .rgctl/dashboard/communities.json after --with-dashboard.
rgctl -r "$REPO" -f json metrics --pagerank | jq '.pagerank | {iterations, converged, top: .top[:3]}'
{
"iterations": 20,
"converged": false,
"top": [
{ "node": "…uuid…", "pagerank": 0.0027 },
{ "node": "…uuid…", "pagerank": 0.0015 },
{ "node": "…uuid…", "pagerank": 0.0015 }
]
}
rgctl -r "$REPO" metrics --betweenness
rgctl -r "$REPO" -f json metrics --pagerank --iterations 50 | jq .
12. Semantic search
Semantic search is opt-in — it does not run during discover. Build a separate Hamming index, then query by natural language or keywords.
Default index scope: :Function symbols. Doc headings: semantic index --scope docs (or --scope all for functions + docs). See markdown-context.md.
Prerequisites: discover completed. Default embedder is vocab (compiled token table, no ONNX). Quality extra: --embedder code-daemon (needs git lfs pull for bundled ONNX). CI smoke: --embedder hash.
# Build semantic index (default: vocab, 256-d, declaration metadata only)
rgctl -r "$REPO" semantic index
# Incremental rebuild — reuse rows when body hash unchanged
rgctl -r "$REPO" semantic index --incremental
# Query (JSON for agents). Late fusion is ON by default.
rgctl -r "$REPO" -f json semantic query "shopping cart checkout" --limit 10
rgctl -r "$REPO" -f json semantic query "OrderService" --keyword-and
# Pure Hamming (disable fusion):
rgctl -r "$REPO" -f json semantic query "OrderService" --no-fusion --limit 10
# Community-scoped search — pool member embeddings (needs discover analysis + semantic index)
rgctl -r "$REPO" -f json semantic query "shopping cart" --scope community --limit 5
# Doc section search (markdown headings; needs discover -l markdown)
rgctl -r "$REPO" semantic index --scope docs --embedder hash
rgctl -r "$REPO" -f json semantic query "checkout flow" --scope docs --limit 10
# Hash embedder (no ONNX) — e.g. CI
rgctl -r "$REPO" semantic index --embedder hash
# Vocab is the default; optional call-graph diffusion
rgctl -r "$REPO" semantic index --diffuse \
--diffuse-alpha 0.25 --diffuse-iters 2
# Re-read function bodies into the vector (off by default; fusion still uses discover token-blooms)
rgctl -r "$REPO" semantic index --embed-bodies
# Distill our token list through a teacher (rebuild after copying to assets/vocab_matrix.bin)
rgctl -r "$REPO" semantic distill --matrix crates/rgctl-analysis/assets/vocab_matrix.bin --embedder code-daemon
# Neural code retriever (ONNX)
rgctl -r "$REPO" semantic index --embedder code-daemon
Passing --diffuse recomputes dense vectors and mixes call-graph neighbors before sign quantization (even when --incremental would otherwise reuse bits). Query does not re-diffuse — restart is not required for CLI query; for the dashboard, restart serve after rebuilding the index.
Doc semantic index: --scope docs on semantic index embeds :Module sections (kind=heading and kind=code_block). Query --scope docs does not filter hits — only semantic index --scope community changes query behavior. Build a doc-scoped index before querying doc sections. Large bodies use content_store.bin when body_ref is set. CLI success text may still say Indexed N functions (entry count, not always functions).
| Flag | Purpose |
|---|---|
semantic index --scope function|docs|all | Which symbols to embed (default: functions only) |
semantic query --scope function|community|docs|all | community = pooled community search; other scopes do not filter hits (index content determines results) |
--no-fusion | Disable late fusion (default is fusion on: blast, PageRank, name, token-bloom, community, package, callees) |
--keyword-and | Every query token must match metadata or body sketch |
--candidate-pool <N> | Hamming pool size before fusion [default: 256] |
--expand neighbors|blast|gql|all | Hybrid expansion after top hits |
--embedder hash|vocab|onnx|code-daemon | Embedding backend [default: vocab] |
--embed-bodies | Append identifier tokens from function source (off by default) |
--dimensions <N> | Float width before quantize; multiple of 8 [default: 256] |
semantic distill --matrix <PATH> | Write RBVK from our token list via a teacher (code-daemon default; not vocab) |
--diffuse / --no-diffuse | Jacobi call-graph mix on dense floats before quantize (index only; off by default) |
--diffuse-alpha / --diffuse-iters | Diffusion blend weight and iterations [defaults: 0.25, 2] |
--diffuse-bidirectional | Include callers as well as callees |
Dashboard: rgctl serve --open → Search tab uses the same index via /api/semantic/*. The UI does not choose the embedder — build the index with CLI first, then restart serve. Status shows model_id (e.g. vocab-accumulate-v1).
Perf note (linux-scale): time queries with a release binary (cargo build --release). Debug builds can be ~100× slower on Hamming scan. Index load of .rgctl/semantic_index.bin is bincode into owned strings (~tens of seconds at ~1.8M functions); query itself is ~few ms in release.
Design → Semantic search design · timing tests → cargo test --test semantic_query_timing -- --nocapture
13. Export graph projections
export writes the graph or a filter-selected subgraph to a file or directory. The --query flag uses filter syntax, not GQL MATCH (JSON/graph formats honor the filter; Obsidian/OKF use --query all):
--export-format | Output | --query |
|---|---|---|
json | Graph snapshot JSON | Filter or all |
graphml | GraphML XML | Filter or all |
graphviz | DOT | Filter or all |
mermaid | Mermaid flowchart | Filter or all |
obsidian | Obsidian vault directory | all (heading modules) |
okf | OKF JSON entity bundle | all |
| Query | Meaning |
|---|---|
all | Entire graph |
name:clearCart | Nodes with exact name |
type:Function | All functions |
functions | Shortcut for function nodes |
rgctl -r "$REPO" export \
--export-format mermaid \
--export-output cart-clear.mmd \
--query "name:clearCart"
Exported 2 nodes, 1 edges -> cart-clear.mmd
# Full graph as JSON / GraphML / DOT
rgctl -r "$REPO" export --export-format json --export-output ecommerce-graph.json --query all
rgctl -r "$REPO" export --export-format graphml --export-output ecommerce.graphml --query all
rgctl -r "$REPO" export --export-format graphviz --export-output calls.dot --query all
Markdown → Obsidian vault
For documentation repos (or -l markdown discover), export heading sections as an Obsidian vault. Requires prior discover (creates .rgctl/ and content_store.bin for large bodies).
export REPO=/path/to/docs-repo
rgctl -r "$REPO" discover -l markdown
rgctl -r "$REPO" export \
--export-format obsidian \
--export-output "$REPO/vault" \
--query all
Open $REPO/vault in Obsidian (Open folder as vault). Each note is one heading section; YAML qualified_name maps back to GQL. Re-run discover + export after doc changes.
Other doc export formats: --export-format okf (JSON entity bundle). Full walkthrough: markdown-context.md.
For GQL pattern matching, use rgctl gql — or rgctl serve + HTTP API.
14. CI policy check
check evaluates blast-radius policy rules against functions changed in the current git working tree (or all functions if git is unavailable).
Example policy files: docs/examples/policy-strict.json. Format: policy-format.md.
rgctl -r "$REPO" check --policy-file policy.json
Exit code 1 when violations are found — suitable for CI pipelines.
The fixture also ships a shared policy at rgctl-tests/rgctl-policy.json.
15. HTTP server (serve) — optional
serve binds HTTP immediately and, unless --no-pipeline, starts the same staged full pipeline as discover --full. The dashboard at / shows a preparing page until the bundle exists. Prefer CLI -f json for agents and CI.
# Starts indexing if needed; preparing page until dashboard exists
rgctl -r "$REPO" serve --port 8080
# Old fail-fast (require existing artifacts)
rgctl -r "$REPO" serve --no-pipeline --query-only
| Endpoint | Purpose |
|---|---|
/ | Dashboard UI or preparing page |
GET /api/status | Full-pipeline status JSON |
POST /api/query | GQL / macros (JSON body; 503 until graph ready) |
GET /api/semantic/status | Semantic index availability |
POST /api/semantic/query | Semantic search (JSON body) |
/api/health | Health check |
curl -sS -X POST http://127.0.0.1:8080/api/query \
-H 'Content-Type: application/json' \
-d '{"macro":"all_functions"}' | jq '.count'
Full reference: http-api.md. CoolStore walkthrough: HTTP Server and Dashboard.
16. Recommended workflow
# 1. Point at the in-tree fixture
cd /path/to/rgctl
export REPO="$PWD/rgctl-tests/ecommerce-java"
cd "$REPO"
# 2. Index (add CFG + dashboard + Kantra for the rest of this walkthrough)
rgctl discover . -l java -e target \
--with-cfg --with-dashboard --with-harmonic --with-kantra --export-migration-hints
# 3. Explore structure
rgctl -r "$REPO" -f json gql --macro-name all_functions unused | jq '.count'
rgctl -r "$REPO" -f json gql --macro-name all_communities unused | jq '.rows[:5]'
rgctl -r "$REPO" communities list | head -15
rgctl -r "$REPO" gql \
"MATCH (a:Function)-[:CALLS]->(b:Function) WHERE b.name = 'clearCart' RETURN a,b"
# 4. Change-impact before editing
rgctl -r "$REPO" blast-radius 'CartService::clearCart'
rgctl -r "$REPO" -f json blast-radius 'CartService::clearCart' | jq '.metrics'
# 5. CoolStore dual API + hybrid CPG (field mutations)
rgctl -r "$REPO" cpg status
rgctl -r "$REPO" cpg mutations --type ShoppingCart --exclude-ctors
rgctl -r "$REPO" blast-radius 'ShoppingCartService::priceShoppingCart'
# 5b. Konveyor Kantra migration rules (optional target filter)
rgctl -r "$REPO" -f json gql "MATCH (r:KantraRule) RETURN r LIMIT 5" | jq '.count'
rgctl -r "$REPO" -f json gql \
'MATCH (r:KantraRule)-[:VIOLATES]->(n) RETURN r, n LIMIT 10' | jq '.count'
jq '{violations: (.violations|length), evaluated_rules, target_filter}' \
"$REPO/.rgctl/kantra_findings.json"
# 6. Architectural hotspots
rgctl -r "$REPO" -f json metrics --communities | jq .
rgctl -r "$REPO" -f json metrics --pagerank | jq '.pagerank.top[:5]'
# 7. Deep dive on checkout
rgctl -r "$REPO" inspect checkout cfg
rgctl -r "$REPO" slice \
src/main/java/com/example/ecommerce/service/CartService.java \
--line 53 --variable item --function addItem
# 8. Export / dashboard
rgctl -r "$REPO" export --export-format mermaid \
--export-output clearCart.mmd --query 'name:clearCart'
rgctl -r "$REPO" serve --open
Migration hints (with --export-migration-hints) land under .rgctl/migration_plan.json and .rgctl/dashboard/migration_plan.json — package-level steps such as com.example.ecommerce.service, …repository, …controller, and CoolStore …coolstore.*.
17. Command reference
| Command | Purpose |
|---|---|
discover | Index repo, build .rgctl/ artifacts (--full = staged CFG/dashboard/harmonic + semantic) |
gql | Graph query language (incl. virtual :Community) |
communities | List / refresh heuristic community labels |
blast-radius | Upstream call-graph impact for a symbol |
slice | Line-level program slice or taint trace |
inspect | CFG / PDG / dominance for a function |
cpg | Hybrid CPG: status, mutations, flows, calls, export (needs --with-cfg) |
metrics | PageRank, betweenness, communities summary |
export | Serialize graph (json, graphml, dot, mermaid, obsidian vault, okf) |
check | CI policy gateway |
install | Copy the bundled agent skill into .claude/skills/ and .cursor/skills/ |
semantic | Opt-in semantic index + query (--scope community, docs, all) |
serve | HTTP dashboard + /api/query + /api/status (auto full pipeline); --no-pipeline fail-fast |
discover flags
| Flag | Description |
|---|---|
-l, --languages | Comma-separated filter (java, typescript, rust, …) |
-e, --exclude | Comma-separated path exclude patterns |
-v, --verbose | Debug logging + stage profile lines |
--with-security | Secret scanning |
--with-cfg | CFG / PDG (not taint); alias --cfg |
--with-taint | Discover-time taint (implies CFG pass) |
--with-dfg-loops | Tag loop-carried DataDependency edges in PDG (with --with-cfg) |
--with-ast-skeleton | Build AST skeleton archive for cpg ast |
--with-harmonic | Harmonic centrality (default off; needed for migration ranking) |
--with-dashboard | Static dashboard bundle (default off) |
--full | Staged pipeline: basic discover, then CFG+dashboard+harmonic, then semantic index |
--export-migration-hints | Migration roadmap JSON (alias --export-migration-plan) |
--migration-preset | Preset for migration hints (hybrid, foundational, …) |
--migration-order | scheduled (topological) or priority |
--write-json-graph | Also write legacy graph.db / graph.json |
--with-kantra | Konveyor Kantra eval + rules graph index (embedded catalog by default) |
--kantra-target NAME | Filter eval to konveyor.io/target=NAME |
--kantra-rules DIR | Override embedded catalog with one ruleset directory |
--kantra-catalog ROOT | Override embedded catalog with a rulesets tree |
--kantra-index-only | Index rules into graph; skip eval |
There is no umbrella --all flag — combine --with-cfg --with-security --with-taint explicitly when you want the former deep pass.
18. Troubleshooting
Indexed the wrong repository
If you ran rgctl -r /path/to/repo discover . from another directory, artifacts reflect shell cwd, not -r. Fix:
cd /path/to/repo && rgctl discover .
# or: rgctl -r /path/to/repo discover # no trailing .
Graph not found / run discover first
rgctl discover . -l java -e target
# or
rgctl -r "$REPO" gql 'MATCH (n:Function) RETURN n LIMIT 1'
Symbol not found / ambiguous (blast-radius, inspect)
List exact names, then use FQN:
rgctl -r "$REPO" gql "MATCH (n:Function) WHERE n.name = 'clearCart' RETURN n"
rgctl -r "$REPO" blast-radius 'CartService::clearCart'
rgctl -r "$REPO" blast-radius clearCart --class CartService
inspect takes a function name (checkout, addItem), not a class name (CartService).
Slice parse / PDG errors
Ensure you ran discover --with-cfg, then pass the method name and a variable that exists on that line:
rgctl -r "$REPO" slice \
src/main/java/com/example/ecommerce/service/CartService.java \
--line 53 --variable item --function addItem --language java
Empty cpg mutations
Confirm cpg status shows a field-write index, then match the resolved type name (Java/C#/…: ShoppingCart; C: shopping_cart_t). Setters count as mutation sites; unresolved receivers are omitted unless --include-unresolved. Re-run discover --with-cfg after adding CoolStore sources.
Slow discover
Start with the default mode. Add --with-cfg or --with-taint only when you need inspect, slice overlays, or taint. Keep --with-harmonic / --with-dashboard off unless you need migration ranking or the static UI.
On very large repos (500k+ graph nodes), discover automatically:
- Caps PageRank iterations and relaxes convergence tolerance
- Caps HyperBall harmonic rounds (when
--with-harmonic) and parallelizes propagation - Skips per-function rows in
function_metrics.json(community/metagraph view instead) - Uses on-demand blast reachability for flat call graphs (no eager multi-hundred-GB bitsets)
Profile a cold run (delete in-repo artifacts first):
rm -rf .rgctl
rgctl discover . -v # from repo root
# or:
RUST_LOG=info,profile=info rgctl discover . -v 2>&1 | grep '\[profile\]'
Further reading
- Introduction — concepts and feature goals
- cli-getting-started.md — deprecated stub (use this User Guide)
- http-api.md — dashboard HTTP API
- json-api.md — machine-readable output + field catalogs
- AGENTS.md — agent-oriented command recipes
rgctl-tests/README.md— all language fixtures + correctness suite