Skip to content

feat: add experimental tree command with agent index - #3005

Draft
kanoru3101 wants to merge 184 commits into
mainfrom
feat/tree-v2
Draft

feat: add experimental tree command with agent index#3005
kanoru3101 wants to merge 184 commits into
mainfrom
feat/tree-v2

Conversation

@kanoru3101

@kanoru3101 kanoru3101 commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

What/Why/How?

Adds the experimental tree command — one command that shows an API description's structure to humans and serves it as a navigable index to LLM agents.
Humans get orientation and impact analysis in multi-file descriptions ("what breaks if I change this schema?"); agents get a way to work with descriptions far larger than a context window, borrowing the retrieval loop from PageIndex — build a small index, let the model reason over it, fetch only what it needs — fully deterministic here, because an API description already carries its structure and summaries (no AI calls, no keys).

The engine is a new api-graph module in @redocly/openapi-core: one walk of the original resolved files (the lint pattern, no bundling) builds a dependency graph plus index metadata, and the CLI renders views over it.

redocly tree openapi.yaml                                              # overview: servers, tags and webhooks with counts, component sections
redocly tree openapi.yaml --find "create subscription"                 # search operations and components by words in path, id, name, summary
redocly tree openapi.yaml --tag=Orders                                 # one tag's operations with summaries and line ranges
redocly tree openapi.yaml --path=/orders --operation=post --with-deps  # one operation plus its transitive $ref closure
redocly tree openapi.yaml --component=schemas --name=Order --used-by   # impact analysis: affected operations with via chains
redocly tree openapi.yaml --pointer='#/components/schemas/Order/properties/status'  # navigate straight from a $ref or lint pointer
redocly tree openapi.yaml --file=paths/orders.yaml                     # everything one file defines; add --used-by for file impact
redocly tree --files                                                   # the file-level $ref graph

Selection is typed — --tag, --path, --webhook, --operation, --component + --name, --file, plus the two standalone selectors --find and --pointer — so there is no id syntax to learn.
Every result carries the defining file and start_line/end_line, so any answer can be checked against its source, or read directly with plain file tools.
--with-deps returns a self-contained slice (64 KB cap with an explicit truncated marker); --used-by returns transitively affected operations and components, each with the shortest via chain.

Three output formats: stylish for a terminal, json for tooling, and ai — plain text built for agents, where a card's body is one line of minified JSON, a --with-deps closure collapses to one-line schema signatures, and every view ends with a next: line naming the flags that continue from it, so an agent runs the whole chain from the output alone without reading any documentation.

v1 scope: OpenAPI 2.0–3.2. AsyncAPI and Arazzo descriptions render their $ref dependency graph; the typed selectors are OpenAPI-only for now.

Architecture note for review: the engine lives in core on purpose — the CLI is one of several planned surfaces, and an MCP server or portal-side index generation would consume the same module.

Measured

Same multi-step task (three chained requests with a trap in each), fresh isolated agent sessions, three descriptions × three models × two conditions: the agent gets the file and nothing else, or the file plus the tree run line and one sentence about next:.
The number is the context each run added to its own session — a session opens with a fixed 26–43k of system prompt and task, identical in both conditions, which is subtracted out. Tool calls in parentheses.

Description Model no tree tree Difference
GitHub REST 10.0 MB Sonnet 5 10,523 (11) 9,459 (6) −10%
GitHub REST 10.0 MB Opus 5 9,024 (10) 9,298 (7) +3%
GitHub REST 10.0 MB Fable 5 8,762 (7) 7,226 (5) −18%
Billing API 1.3 MB Sonnet 5 20,705 (26) 20,457 (13) −1%
Billing API 1.3 MB Opus 5 19,435 (17) 18,512 (10) −5%
Billing API 1.3 MB Fable 5 18,476 (22) 15,304 (12) −17%
Cafe API 41 KB Sonnet 5 16,866 (1) 8,095 (8) −52%
Cafe API 41 KB Opus 5 16,916 (2) 9,375 (4) −45%
Cafe API 41 KB Fable 5 16,840 (1) 8,181 (7) −51%

All 18 answers were correct on both sides, including the operation-level uploads.github.com server override and an anyOf without a discriminator: an agent that never opens the file answers as well as one that reads it.
The size of the win tracks how much of the description the baseline has to pull in — half of it on the 41 KB Cafe API, which it reads whole, and 1–18% on the large ones, which it can search instead.
Tool calls halve almost everywhere: 13 against 26, 10 against 17, 6 against 11.
Billed cost for the same runs moves the same way in eight of nine cells — $0.87 → $0.38 on the billing API with Sonnet 5, $0.70 → $0.59 on GitHub with Fable 5 — but it is the least reproducible number of the set, since a warm prompt cache can halve it for identical work, so the guide prints it for shape rather than precision.

Two findings shaped the command itself and are worth flagging for review:

  • Pointing the agent at this command's 87 KB reference page costs more than the exploration it saves. The next: lines exist so the prompt can stay one line long.
  • The update-available banner was printed to stderr on every call, spending agent context on every invocation. It is now suppressed when --format=ai.

Method, prompts, per-model command chains and the caveats are in the benchmark guide.

Docs

Reference

Testing

  • Core api-graph unit tests — graph building, selection lookups, views (overview, listings, cards), used-by reports with via chains, deps closure, pointer resolution, webhook classification, outside-cwd path normalization, callback exclusion, split-alias canonical ids.
  • CLI tree unit tests — selector routing including every invalid combination, and printers asserted against exact strings.
  • E2e — 46 cases pin every view and format, including the agent loop on a split multi-file fixture, webhook cards, pointer navigation, and the selector-error path with an exit-code assertion.
  • Docs examples are captured real CLI output.

Screenshots (optional)

Check yourself

  • This PR follows the contributing guide
  • All new/updated code is covered by tests
  • Core code changed? - Tested with other Redocly products (internal contributions only)
  • New package installed? - Tested in different environments (browser/node)
  • Documentation update has been considered

Security

  • The security impact of the change has been considered
  • Code follows company security practices and guidelines

kanoru3101 and others added 30 commits June 11, 2026 16:38
Dynamic imports inside the test body made vitest transform the whole
untransformed dependency subtree within the test, exceeding the 5000ms
per-test budget when istanbul coverage is enabled. Static top-level
imports move that cost to the file-load phase, which has no timeout.
Add the `treeview` language to the example code fences in tree.md
(matching eject.md / translate.md house style) to satisfy markdownlint
MD040.

Stop tracking the internal agentic planning/spec docs under
docs/superpowers/: they were accidentally committed into the published
documentation and caused all vale and linkcheck failures plus most
markdownlint errors. Nothing references them; they remain available
locally but are no longer published.
The absolute-ref to node-id rule, the codepoint sort comparator, and the
operation-method set each had duplicate copies across build-graph.ts and
build-structure.ts. Move them to node-id.ts as the single source so the
two graph builders cannot drift.

Also fix build-graph's edge-refs sort to use the codepoint comparator
(was the default .sort()), matching the determinism the module documents,
and drop a redundant narrating comment in build-structure.

No behavior change: 58 tree unit tests and 11 e2e snapshots pass unchanged.
@kanoru3101 kanoru3101 added the snapshot Create experimental release PR label Aug 15, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📦 A new experimental 🧪 version v0.0.0-snapshot.1786808983 of Redocly CLI has been published for testing.

Install with NPM:

npm install @redocly/cli@0.0.0-snapshot.1786808983

⚠️ Note: This is a development build and may contain unstable features.

@kanoru3101 kanoru3101 added snapshot Create experimental release PR and removed snapshot Create experimental release PR labels Aug 15, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📦 A new experimental 🧪 version v0.0.0-snapshot.1786811652 of Redocly CLI has been published for testing.

Install with NPM:

npm install @redocly/cli@0.0.0-snapshot.1786811652

⚠️ Note: This is a development build and may contain unstable features.

@kanoru3101 kanoru3101 added snapshot Create experimental release PR and removed snapshot Create experimental release PR labels Aug 16, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📦 A new experimental 🧪 version v0.0.0-snapshot.1786868116 of Redocly CLI has been published for testing.

Install with NPM:

npm install @redocly/cli@0.0.0-snapshot.1786868116

⚠️ Note: This is a development build and may contain unstable features.

@kanoru3101 kanoru3101 added snapshot Create experimental release PR and removed snapshot Create experimental release PR labels Aug 17, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📦 A new experimental 🧪 version v0.0.0-snapshot.1786981559 of Redocly CLI has been published for testing.

Install with NPM:

npm install @redocly/cli@0.0.0-snapshot.1786981559

⚠️ Note: This is a development build and may contain unstable features.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

snapshot Create experimental release PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants