Imported from landing-ai/ade-cli (
SKILL.md). Install upstream withnpx skills add landing-ai/ade-cli. Copyright stays with the author.
ade — agent skill
ade drives the ADE (Agentic Document Extraction) v2 document APIs
(parse, extract) and persists
everything it produces in a local store under ~/.ade. You are the
judgment; the CLI is deterministic plumbing: it parses, persists,
searches, renders, and totals — it never ranks or selects for you.
First move
ade help --json
One call returns the whole shipped surface: every command and flag, each
verb's --json result shape, the exit states, and the store layout.
Prefer it over N --help round trips. ade help workflow (also
output, credentials, errors) is the conceptual page behind it.
Check credentials with ade auth status --json; if logged out, set
ADE_API_KEY, or pipe a key in (echo $KEY | ade auth login) — no
terminal required either way. An API key acts in the organization it
was created in; browser (OAuth) sessions carry their own selection —
status reports it, ade auth org list --json shows the memberships,
and ade auth org switch <org> --env <env> changes it. If a run must
bill to a specific organization, confirm the selection before
parsing: re-running the same invocation after a switch joins the
recorded run instead of re-billing under the new organization.
Conventions
- Always pass
--json. Every command emits one stable JSON object/array on stdout — errors and pending payloads included. Human text is for terminals, not for parsing. The whole result is on stdout: the extraction, the matches, the paths. Reading the store is a convenience, never a requirement — andade help --jsonpublishes each verb's shape, so you never have to discover it by running. --id-onlywhen you only need the id.parse,extract, andfindtake it: the id(s), one per line, nothing else —JOB=$(ade parse -d report.pdf --id-only). Errors go to stderr, so a captured id is never a sentence.- Job item ids are the currency. Every parse or extract run is a
job item: one folder under
~/.ade/jobs/<job-item-id>/, keyed by the invocation (verb × environment × source × content × params; URL sources are keyed by the URL alone — no content component, so remote drift does not re-key). Commands accept an unambiguous id prefix. Changing the file, its path, or any param mints a sibling item — nothing is silently replaced. - Commands are guarantees.
parsemeans "ensure this exact run exists", not "fire a request". Re-running an already-done invocation is free — served from disk with analready parsednotice and"cached": true. Only--forcere-bills.
The loop
-
Parse (free if already done):
ade parse -d report.pdf --jsonThe payload carries
job_item_id— hold onto it. Everything else keys off it. The markdown and the elements projection stay on disk unless you ask for them:--include markdown,--include elements. -
Discover element ids (local, instant, no API call):
ade find JOB_ITEM_ID "total revenue" --json ade find JOB_ITEM_ID --type table_cell --page 3 --json(
--job JOB_ITEM_IDis the equivalent flag spelling — repeat it to search several items in one call.)Matches are citation records:
{job_item_id, element_id, type, page, box, text}. Filters compose (AND); results come in document order, never ranked — selection is your job. -
Extract structured data against a JSON Schema:
ade extract JOB_ITEM_ID --schema schema.json --jsonThe result is its own job item. The payload's
extractionkey is the schema-shaped result itself — read it there, not off disk. Per-field evidence (evidence, persisted asevidence.json) joins extraction spans to element ids, pages, and boxes — non-empty fields the model synthesised rather than quoted are flaggedungroundable; empty-valued fields (blank cells, absent optionals — nothing to ground) are labeledempty. Neither is ever silently dropped. -
Cite and show.
ade view JOB_ITEM_ID --jsonbuilds a self-contained HTML viewer; deep links are the citation contract:ade view JOB_ITEM_ID --element-id ELEMENT_ID --json # emits view.html#element=ELEMENT_ID ade crop JOB_ITEM_ID --element-id ELEMENT_ID --json # PNG of that element's region ade crop JOB_ITEM_ID --type figure --json # every figure, one commandEnd answers with one deep link per job item, citing element ids. Use
cropwhen you need to look at evidence mid-reasoning — it takesfind's own filters (--type,--page,--all), so a selection crops in one call and returnscrops[]; never loopfindintocropyourself.
Reuse posture — parse bills once
Every parse the CLI ever runs is a reusable job item. Given a document
path, extract -d FILE --schema … reuses the latest completed parse of
that path+content (logged in the summary; no parse billed). If none
exists, it runs a standalone parse first — a normal, top-level
parse item, exactly as if you had run parse -d — then the extraction
referencing it: two billable runs, both itemised. Repeat extract -d
runs of the same file then reuse that parse, so it bills exactly once.
Prefer the explicit two-step (parse -d, then extract JOB_ITEM_ID) when
you want the same parse to feed several schemas — the id makes the reuse
visible.
Pending and resume
Wait expiry is a normal outcome, not an error. If the poll budget
(--wait, default 600s) runs out, the command exits with code 3 and a
{"status": "pending", "run_id": …, "job_item_id": …} payload while the
run continues server-side (submitted work always completes and bills —
there is no cancel). The recovery gesture is always the same command,
re-run. A re-run joins the recorded run; it never resubmits and never
re-bills. Interrupts (Ctrl-C) stop the waiting, not the work — same
gesture. --wait 0 submits and returns immediately.
Submit-and-return, then collect later — the pending payload carries the id too, so this works in both steps:
JOB=$(ade parse -d report.pdf --wait 0 --id-only) # exit 3, run continuing
ade parse -d report.pdf --json # re-run: resumes, never re-bills
Exit codes
| Code | State | Meaning |
|---|---|---|
| 0 | ok | Success — the payload is on stdout. |
| 1 | failed | The run failed or the target cannot serve the request. |
| 2 | usage | The invocation itself was wrong; nothing was submitted. |
| 3 | pending | Wait budget expired; the run continues server-side. Re-run the same command to resume. |
| 4 | rate_limited | Submit was rate-limited and the wait budget ran out before a run existed; nothing billed. Re-run to retry. |
Reading the store directly
Optional, never required: every result is already on stdout. Artifacts
are plain files — history list --json gives every item's records, and
the summaries print each item's store path.
parse.json— raw ParseResponse (ground truth, verbatim)parse.md— the parse markdown extraction spans indexelements.json— flat element records with inline grounding; boxes are normalized{xmin, ymin, xmax, ymax}fractions of page size in[0, 1]extract.json— raw extraction result with per-field spansevidence.json— the field→box join (element ids, pages, boxes)
One vocabulary note when reading these files: on-disk records
(meta.json, job.json, parse/ref.json) spell the server-side run id
as job_id — the wire contract's name for the same value --json
payloads report as run_id. Neither is ever the job item id.
Prefer find over loading elements.json into context: it returns
joined records, not lines. history clear JOB_ITEM_ID deletes an item;
clearing a parse item cascades to the extractions referencing it.
Sharp edges
--optionsis a verbatim ParseOptions pass-through — the server rejects unknown keys with a 422, so consultade help parsefor the accepted keys rather than guessing.- Parse variants coexist. The same document parsed with different
params is a sibling item, not a replacement. An extraction goes stale
only when its exact parse item is re-run in place with
--force. - Moving or editing a file changes identity — the next parse of it is a new job item that bills. The old item and its evidence stay intact and true of the run they came from.
--markdownextractions have no page evidence (there is no parse to join against) — evidence degrades to spans-only, andviewrenders the markdown pane alone.- URL parses have no local bytes, so page imagery renders from an
attached copy:
parse --document-url … --keep-copyfetches it at parse time (reliable — pre-signed URLs expire); otherwise the firstview/cropdownloads it automatically (--no-downloadskips; the payload recordsdownloaded, and on a failed fetchviewdegrades to an empty preview withdownload_errorwhilecroperrors). Markdown, elements, and extractions work either way.