Skip to content

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

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

feat: add experimental tree command with agent index#3005
kanoru3101 wants to merge 120 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 quick orientation and impact analysis in multi-file descriptions ("what breaks if I change this schema?"); agents get a way to work with descriptions too large for a context window, borrowing the retrieval loop from PageIndex: build a small hierarchical index, let the model reason over it, and fetch only what it needs — fully deterministic here, since an API description already carries its structure and summaries (no AI calls, no keys).

Everything is powered by one new api-graph module in @redocly/openapi-core: a single walk of the ORIGINAL resolved files (lint pattern, no bundling) produces a dependency graph plus index metadata, and the CLI renders views over it:

redocly tree openapi.yaml                                              # bounded overview: tags with counts, webhooks, component sections
redocly tree openapi.yaml --tag=Orders                                 # one tag's operations with summaries and line ranges
redocly tree openapi.yaml --path=/orders --operation=post              # one operation card: coordinates, typed refs, usedBy
redocly tree openapi.yaml --path=/orders --operation=post --with-deps  # + raw source lines and the transitive $ref closure
redocly tree openapi.yaml --component=schemas --name=Order --used-by   # impact analysis: affected operations with via chains
redocly tree openapi.yaml --file=components/schemas/Order.yaml         # everything one file defines; add --used-by for file impact
redocly tree openapi.yaml --paths / --operations / --webhooks          # flat listings when you want everything at once

Selection is typed — --tag, --path, --webhook, --operation (method or operationId), --component + --name, --file — so no id syntax has to be learned or explained; the up-front agent instruction measured 85 tokens.
The default view is a bounded top-level tree (tags with counts, webhook names, component sections); every listing entry is card-shaped — the same structure as a single card (coordinates + typed refs + usedBy), uniform for tooling.
Every result carries a JSON pointer, the defining file, and start_line/end_line, so an agent can follow up with plain file reads; cards list one-hop typed refs (outgoing) and usedBy (incoming).
--with-deps returns a self-contained slice (the selection plus everything it transitively references, in dependency order, 64 KB cap with an explicit truncated marker); --used-by returns the transitively affected operations and components, each with the shortest via reference chain — a machine answer for "what breaks if I change this schema".
Unresolvable $refs render as ❌ nodes with stderr warnings; stdout stays clean JSON in machine formats; --format is stylish or json for every view.
v1 scope: full OpenAPI 2.0–3.2; AsyncAPI/Arazzo render their $ref dependency tree (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; an MCP server and portal-side index generation would consume the same module directly.

Measured end to end on GitHub's own 10.0 MB REST API description (github/rest-api-description, used unmodified), with a BPE tokenizer over real command output — task: "create a repository for the authenticated user":

Input Tokens
Whole file 1,946,549
Flat --operations listing (card-shaped, the largest single view) 771,279
Three-step chain: overview → --tag=repos → card --with-deps 149,582 (+85 instruction)

This is the case the hierarchy exists for: at 1.9M tokens the file is ten 200k windows — bounded drill-down is the only way an agent can touch it; the chain lands at ~13× less than the file (~14× on the split layout over 2,842 files), a deliberate trade: listing entries carry their one-hop refs/usedBy so every view shares one uniform card structure.
Per-step numbers and real JSON output 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, retrieval slices, deps closure, webhook classification, outside-cwd path normalization, callback exclusion, split-alias canonical ids.
  • CLI tree unit tests — selector routing incl. every invalid-combination error, printers with exact-string assertions.
  • E2e — 29 cases pin every view and format incl. --file and --webhooks incl. the full agent loop on a split multi-file fixture, the webhook card, and the selector-error path with an exit-code assertion; snapshots pin real output.
  • 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 self-assigned this Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants