A. Machine-detectable errors
A1 — Structured JSON error envelope + status propagation to scripts/pipe (P0, M)
Problem. There is an RzCmdStatus enum (OK/ERROR/INVALID/WRONG/NONEXISTINGCMD/EXIT) but no
JSON error output anywhere in the command layer, and a failing command is largely invisible to a
pipe consumer (empty stdout, maybe stderr noise). An agent driving Rizin over rzpipe or rizin -c
cannot reliably tell success from failure.
Action. In JSON output mode, emit a consistent envelope on non-OK status, e.g.
{"error":{"code":"WRONG_ARGS","message":"...","command":"afl"}}. Expose the last command's
RzCmdStatus to rzpipe (a way to read status alongside output), and make rizin -c/batch return a
non-zero process exit when a command errors.
Where. librz/core/cmd_api.c (RzCmd dispatch), librz/cons + librz/socket/rzpipe.c, batch
entry in binrz/rizin.
Done when. A failing command in -j/pipe context yields a parseable error object; rzpipe exposes
the status; CI test asserts non-zero exit on a known-bad command.
B. Structured output & JSON contracts
B1 — Audit and close JSON-mode gaps on data-producing commands (P0, M)
Problem. Only 315 / 1451 leaf commands (~21%) declare RZ_OUTPUT_MODE_JSON. Many commands
legitimately produce no listable output (seek, config, write, navigation), but a substantial set of
data commands still have no j mode, forcing agents to scrape human text.
Action. Audit the data-producing subset (listings, i* info, analysis dumps, disassembly,
xrefs, types, flags, search) and add RZ_OUTPUT_MODE_JSON where missing. Add a CI lint that fails
when a new data command is added without a JSON mode.
Where. librz/core/cmd_descs/*.yaml + the corresponding handlers.
Done when. Every command that emits a list/record/table offers JSON; the lint is in CI.
B2 — Publish versioned JSON Schemas for the core commands (P0, M)
Problem. JSON shapes are undocumented and unversioned; agents (and downstream tools) can't rely
on field names/types, and a refactor can break consumers silently.
Action. Author/maintain JSON Schema for the top ~30–50 high-value commands (functions list,
analysis info, sections/segments, imports/exports, strings, xrefs, disassembly, decompiled output,
binary info). Ship under doc/json-schema/, version them, and validate sample outputs against the
schemas in CI.
Where. new doc/json-schema/ + test/ validation harness.
Done when. Core commands have a published schema and CI validates real outputs against it.
B3 — Normalize JSON field conventions across subsystems (P1, M)
Problem. Key naming/casing and value encodings (e.g. addresses as decimal vs hex, signedness,
boolean vs int) are inconsistent across subsystems, defeating generic parsing.
Action. Define and document a JSON style (key casing, canonical address representation, null vs
omitted, units), then migrate outliers behind a single output-version bump.
Where. doc/ style guide + librz output code.
Done when. A documented convention exists and the core commands conform.
C. Discoverability & introspection (no AI required)
C1 — Machine-readable command-catalog export (P0, M)
Problem. There is no runtime way to dump the full command tree as data. The metadata exists
(the declarative cmd_descs), but tools/agents can't introspect it without parsing C/YAML from
source.
Action. Add a first-class export (e.g. rizin -c '?j' extended, or rizin --cmd-catalog json)
that emits the complete tree — name, summary, typed args with optional flags, examples,
and output modes — straight from RzCmd metadata. This becomes the single source of truth for
completion, docs, and any future schema/MCP generator.
Enabler. The command system is fully declarative (no OLDINPUT shims remain in the spec), so
the catalog can be complete and authoritative.
Where. librz/core/cmd_api.c / help layer + a binrz/rizin flag.
Done when. A documented command exports the whole catalog as JSON; round-trips for at least the
top-level groups.
C2 — Human-readable alias layer for high-value commands (P2, S)
Problem. Commands are terse single letters; LLMs both struggle to recall them and confuse them
with radare2's. They can, however, guess descriptive names.
Action. Add long aliases (declared in the spec so they appear in the catalog and help) for the
common verbs — e.g. list-functions→afl, disassemble→pd, decompile→pdg, info→i,
strings→iz, xrefs-to→axt. Keep canonical terse commands as primary.
Where. librz/core/cmd_descs/* + alias registration.
Done when. Aliases resolve and are listed in the catalog/help.
D. Token efficiency / context-window fit
D1 — Native limit/pagination on large listings (P1, M)
Problem. Listings have no native limit/offset; agents must pipe to head, which over a
pipe/one-shot still computes everything and wastes tokens, and is awkward to express.
Action. Add limit/offset (or an opaque cursor) args to the big listers — functions,
instructions, strings, imports/exports, xrefs, flags, search hits — and include paging metadata
(total, next) in the JSON.
Where. librz/core/cmd_descs/*.yaml + handlers.
Done when. Core listers accept limits and report paging info in JSON.
D2 — Deterministic "summary" commands (aggregation, not AI) (P1, M)
Problem. Building an agent's mental model of one entity takes many round-trips (disasm + xrefs +
strings + vars + signature), each its own command and token cost.
Action. Add aggregate commands that assemble, in one compact JSON call, what's usually needed —
e.g. a function summary: signature, basic-block count, callers/callees, referenced strings/data,
and a length-capped decompiled snippet. Pure aggregation of existing data; no LLM involved.
Where. librz/core (new aggregate command + JSON).
Done when. A single command returns a compact, documented per-entity summary.
E. Decompiler output as first-class data
E1 — Make pdgj (Ghidra JSON) first-class, schema'd, and headless-fast (P2, M)
Problem. Pseudo-C is the backbone of LLM RE, but the JSON decompiler output isn't documented as a
stable contract and is most exercised through Cutter.
Action. Ensure pdgj emits documented, versioned JSON (code, line→address mapping, recovered
types/vars/signature), keep it fully usable headless (no GUI), and track decompile latency; consider
a faster default for latency/token budgets.
Where. rizinorg/rz-ghidra (pdg* family) + Rizin core glue; schema under doc/.
Done when. pdgj has a published schema and a headless smoke test; output is stable across a
release.
F. Stability as a contract
F1 — JSON-output stability + deprecation policy (P1, S)
Problem. Agent tooling breaks silently when output shapes change without notice.
Action. Introduce an output/schema version, a documented deprecation window for JSON fields, and
a changelog section for output changes, so JSON changes are intentional and announced.
Where. doc/ policy + a version field in JSON output.
Done when. Policy is documented and a version marker is present in core JSON outputs.
F2 — Regression tests that assert JSON structure (P1, M)
Problem. Rizin's test DB is strong on values; structural drift (renamed/removed fields) can slip
through and silently break consumers.
Action. Add golden-output / schema-validation tests asserting JSON shape (keys, types,
nesting) for the core commands, so the coverage from B1–B3 stays enforced.
Where. test/db/ (+ the B2 schemas).
Done when. Core commands have structural JSON assertions in CI.
Why this set is the right foundation
Rizin already has two assets most RE tools lack: a fully declarative, typed command tree and a
unified output-mode system. The items above turn those latent assets into hard guarantees an
agent can depend on — discoverable commands (C1/C2), reliable JSON with schemas (B1–B3),
detectable errors (A1), affordable context use (D1/D2), structured decompilation (E1), and a
stability contract (F1/F2). With these in place, an MCP server or assistant built later is a thin,
mechanical layer over a solid base — and the CLI is simply nicer for humans and scripts too.
A. Machine-detectable errors
A1 — Structured JSON error envelope + status propagation to scripts/pipe (P0, M)
Problem. There is an
RzCmdStatusenum (OK/ERROR/INVALID/WRONG/NONEXISTINGCMD/EXIT) but noJSON error output anywhere in the command layer, and a failing command is largely invisible to a
pipe consumer (empty stdout, maybe stderr noise). An agent driving Rizin over
rzpipeorrizin -ccannot reliably tell success from failure.
Action. In JSON output mode, emit a consistent envelope on non-OK status, e.g.
{"error":{"code":"WRONG_ARGS","message":"...","command":"afl"}}. Expose the last command'sRzCmdStatustorzpipe(a way to read status alongside output), and makerizin -c/batch return anon-zero process exit when a command errors.
Where.
librz/core/cmd_api.c(RzCmd dispatch),librz/cons+librz/socket/rzpipe.c, batchentry in
binrz/rizin.Done when. A failing command in
-j/pipe context yields a parseable error object; rzpipe exposesthe status; CI test asserts non-zero exit on a known-bad command.
B. Structured output & JSON contracts
B1 — Audit and close JSON-mode gaps on data-producing commands (P0, M)
Problem. Only 315 / 1451 leaf commands (~21%) declare
RZ_OUTPUT_MODE_JSON. Many commandslegitimately produce no listable output (seek, config, write, navigation), but a substantial set of
data commands still have no
jmode, forcing agents to scrape human text.Action. Audit the data-producing subset (listings,
i*info, analysis dumps, disassembly,xrefs, types, flags, search) and add
RZ_OUTPUT_MODE_JSONwhere missing. Add a CI lint that failswhen a new data command is added without a JSON mode.
Where.
librz/core/cmd_descs/*.yaml+ the corresponding handlers.Done when. Every command that emits a list/record/table offers JSON; the lint is in CI.
B2 — Publish versioned JSON Schemas for the core commands (P0, M)
Problem. JSON shapes are undocumented and unversioned; agents (and downstream tools) can't rely
on field names/types, and a refactor can break consumers silently.
Action. Author/maintain JSON Schema for the top ~30–50 high-value commands (functions list,
analysis info, sections/segments, imports/exports, strings, xrefs, disassembly, decompiled output,
binary info). Ship under
doc/json-schema/, version them, and validate sample outputs against theschemas in CI.
Where. new
doc/json-schema/+test/validation harness.Done when. Core commands have a published schema and CI validates real outputs against it.
B3 — Normalize JSON field conventions across subsystems (P1, M)
Problem. Key naming/casing and value encodings (e.g. addresses as decimal vs hex, signedness,
boolean vs int) are inconsistent across subsystems, defeating generic parsing.
Action. Define and document a JSON style (key casing, canonical address representation, null vs
omitted, units), then migrate outliers behind a single output-version bump.
Where.
doc/style guide +librzoutput code.Done when. A documented convention exists and the core commands conform.
C. Discoverability & introspection (no AI required)
C1 — Machine-readable command-catalog export (P0, M)
Problem. There is no runtime way to dump the full command tree as data. The metadata exists
(the declarative
cmd_descs), but tools/agents can't introspect it without parsing C/YAML fromsource.
Action. Add a first-class export (e.g.
rizin -c '?j'extended, orrizin --cmd-catalog json)that emits the complete tree —
name,summary, typedargswithoptionalflags,examples,and output
modes— straight from RzCmd metadata. This becomes the single source of truth forcompletion, docs, and any future schema/MCP generator.
Enabler. The command system is fully declarative (no
OLDINPUTshims remain in the spec), sothe catalog can be complete and authoritative.
Where.
librz/core/cmd_api.c/ help layer + abinrz/rizinflag.Done when. A documented command exports the whole catalog as JSON; round-trips for at least the
top-level groups.
C2 — Human-readable alias layer for high-value commands (P2, S)
Problem. Commands are terse single letters; LLMs both struggle to recall them and confuse them
with radare2's. They can, however, guess descriptive names.
Action. Add long aliases (declared in the spec so they appear in the catalog and help) for the
common verbs — e.g.
list-functions→afl,disassemble→pd,decompile→pdg,info→i,strings→iz,xrefs-to→axt. Keep canonical terse commands as primary.Where.
librz/core/cmd_descs/*+ alias registration.Done when. Aliases resolve and are listed in the catalog/help.
D. Token efficiency / context-window fit
D1 — Native limit/pagination on large listings (P1, M)
Problem. Listings have no native limit/offset; agents must pipe to
head, which over apipe/one-shot still computes everything and wastes tokens, and is awkward to express.
Action. Add
limit/offset(or an opaque cursor) args to the big listers — functions,instructions, strings, imports/exports, xrefs, flags, search hits — and include paging metadata
(
total,next) in the JSON.Where.
librz/core/cmd_descs/*.yaml+ handlers.Done when. Core listers accept limits and report paging info in JSON.
D2 — Deterministic "summary" commands (aggregation, not AI) (P1, M)
Problem. Building an agent's mental model of one entity takes many round-trips (disasm + xrefs +
strings + vars + signature), each its own command and token cost.
Action. Add aggregate commands that assemble, in one compact JSON call, what's usually needed —
e.g. a function summary: signature, basic-block count, callers/callees, referenced strings/data,
and a length-capped decompiled snippet. Pure aggregation of existing data; no LLM involved.
Where.
librz/core(new aggregate command + JSON).Done when. A single command returns a compact, documented per-entity summary.
E. Decompiler output as first-class data
E1 — Make
pdgj(Ghidra JSON) first-class, schema'd, and headless-fast (P2, M)Problem. Pseudo-C is the backbone of LLM RE, but the JSON decompiler output isn't documented as a
stable contract and is most exercised through Cutter.
Action. Ensure
pdgjemits documented, versioned JSON (code, line→address mapping, recoveredtypes/vars/signature), keep it fully usable headless (no GUI), and track decompile latency; consider
a faster default for latency/token budgets.
Where.
rizinorg/rz-ghidra(pdg*family) + Rizin core glue; schema underdoc/.Done when.
pdgjhas a published schema and a headless smoke test; output is stable across arelease.
F. Stability as a contract
F1 — JSON-output stability + deprecation policy (P1, S)
Problem. Agent tooling breaks silently when output shapes change without notice.
Action. Introduce an output/schema version, a documented deprecation window for JSON fields, and
a changelog section for output changes, so JSON changes are intentional and announced.
Where.
doc/policy + a version field in JSON output.Done when. Policy is documented and a version marker is present in core JSON outputs.
F2 — Regression tests that assert JSON structure (P1, M)
Problem. Rizin's test DB is strong on values; structural drift (renamed/removed fields) can slip
through and silently break consumers.
Action. Add golden-output / schema-validation tests asserting JSON shape (keys, types,
nesting) for the core commands, so the coverage from B1–B3 stays enforced.
Where.
test/db/(+ the B2 schemas).Done when. Core commands have structural JSON assertions in CI.
Why this set is the right foundation
Rizin already has two assets most RE tools lack: a fully declarative, typed command tree and a
unified output-mode system. The items above turn those latent assets into hard guarantees an
agent can depend on — discoverable commands (C1/C2), reliable JSON with schemas (B1–B3),
detectable errors (A1), affordable context use (D1/D2), structured decompilation (E1), and a
stability contract (F1/F2). With these in place, an MCP server or assistant built later is a thin,
mechanical layer over a solid base — and the CLI is simply nicer for humans and scripts too.