From 1d79255a85676ec21d333a93880cf7e8eb7e605c Mon Sep 17 00:00:00 2001 From: Bianca Ragsdale <128001048+lai-bianca@users.noreply.github.com> Date: Tue, 4 Aug 2026 11:06:03 -0700 Subject: [PATCH 1/3] Add writing-style standards; backtick inline code in help prose New docs/agents/writing-style.md: inline code (flags, commands, paths, env vars) is always backticked in help text and docstrings. The hosted docs generate their CLI reference from help --json, where a bare --flag renders as an em dash plus the flag name; backticks also make literals unambiguous for agents. tests/test_help_style.py enforces the flag rule across the help surface's prose fields (topic bodies exempt: they are pre-formatted terminal layouts). Wraps the ~50 existing bare mentions and regenerates the help.json snapshot. Co-Authored-By: Claude Fable 5 --- CLAUDE.md | 4 +++ CONTRIBUTING.md | 4 +++ docs/agents/writing-style.md | 36 +++++++++++++++++++ docs/reference/help.json | 64 +++++++++++++++++----------------- src/ade_cli/auth.py | 6 ++-- src/ade_cli/extract.py | 2 +- src/ade_cli/find.py | 4 +-- src/ade_cli/help.py | 30 ++++++++-------- src/ade_cli/main.py | 4 +-- src/ade_cli/output.py | 4 +-- src/ade_cli/parse.py | 6 ++-- src/ade_cli/view.py | 10 +++--- tests/test_help_style.py | 67 ++++++++++++++++++++++++++++++++++++ 13 files changed, 176 insertions(+), 65 deletions(-) create mode 100644 docs/agents/writing-style.md create mode 100644 tests/test_help_style.py diff --git a/CLAUDE.md b/CLAUDE.md index 6bddf3d..850b193 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -14,6 +14,10 @@ Default canonical labels: `needs-triage`, `needs-info`, `ready-for-agent`, `read Single-context: one `CONTEXT.md` + `docs/adr/` at the repo root. See `docs/agents/domain.md`. +### Writing style + +Standards for help text, the README, and SKILL.md; inline code in help prose is always backticked (test-enforced). See `docs/agents/writing-style.md`. + ## Development Setup, from-source install, the design-doc map, and the release process live in `CONTRIBUTING.md`. The README is user-facing only. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b725b15..e9c8289 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -45,6 +45,10 @@ no Python required. ``` - `docs/telemetry.md`, `docs/user-agent.md` — wire-visible contracts. +- `docs/agents/writing-style.md` — standards for help text, the README, + and SKILL.md. Backticked inline code in help prose is enforced by + `tests/test_help_style.py`; the hosted docs generate their CLI + reference from `help --json`, so these strings are customer-facing. ## Releasing diff --git a/docs/agents/writing-style.md b/docs/agents/writing-style.md new file mode 100644 index 0000000..82a066e --- /dev/null +++ b/docs/agents/writing-style.md @@ -0,0 +1,36 @@ +# Writing style + +Standards for every string a user or an agent reads: help text and +docstrings (which become `ade help` and the hosted CLI reference), the +README, and SKILL.md. + +## Prose standards + +- Second person ("you"), active voice, direct language. +- No promotional language. Describe what a thing does. +- No idioms. Plain wording wins. +- Test every code example before it lands. Fence README and SKILL.md + code blocks with a language tag. +- Error and status messages state what happened and what to do next. + +## Inline code is always backticked (enforced) + +Wrap flags (`--api-key`), commands (`ade update`), paths +(`~/.ade/credentials.json`), environment variables (`ADE_ENV`), and +literal values (`priority`) in backticks wherever they appear in prose. + +Why: the hosted docs generate the CLI reference from `help --json` and +render a bare `--flag` as an em dash plus the flag name; agents need +unambiguous literal boundaries; and terminal output prints backticks +literally, which is already this CLI's voice. + +`tests/test_help_style.py` enforces the flag rule across the help +surface's prose fields. Pre-formatted blocks are exempt: topic bodies +and usage lines are terminal layouts and copy-pasteable commands, so +leave them bare. + +## Relationship to the docs repo + +The docs repo (landing-ai/docs) has stricter rules for hosted pages. +This file governs text that lives in this repo; the docs repo's +generator is the compatibility layer between the two. diff --git a/docs/reference/help.json b/docs/reference/help.json index e7904b5..f68a2b1 100644 --- a/docs/reference/help.json +++ b/docs/reference/help.json @@ -4,11 +4,11 @@ "conventions": [ { "name": "--json", - "rule": "Every command supports --json: one stable JSON object/array on stdout (errors and pending payloads follow the same rule). Agents should always pass it. Each command's published shape is its 'result' block below \u2014 the full result is always on stdout, never only in a file." + "rule": "Every command supports `--json`: one stable JSON object/array on stdout (errors and pending payloads follow the same rule). Agents should always pass it. Each command's published shape is its 'result' block below \u2014 the full result is always on stdout, never only in a file." }, { "name": "--id-only", - "rule": "parse, extract, and find also take --id-only: just the id(s), one per line, for piping (JOB=$(ade parse -d f.pdf --id-only)). Errors and hints go to stderr so a captured id is never a sentence." + "rule": "parse, extract, and find also take `--id-only`: just the id(s), one per line, for piping (`JOB=$(ade parse -d f.pdf --id-only)`). Errors and hints go to stderr so a captured id is never a sentence." }, { "name": "job item ids", @@ -16,7 +16,7 @@ }, { "name": "guarantees", - "rule": "parse and extract ensure a run exists rather than fire a request: an already-done run is served from disk free with an explicit notice (--force consents to a re-bill); a pending run is resumed, never resubmitted; Ctrl-C stops the waiting, not the work." + "rule": "parse and extract ensure a run exists rather than fire a request: an already-done run is served from disk free with an explicit notice (`--force` consents to a re-bill); a pending run is resumed, never resubmitted; Ctrl-C stops the waiting, not the work." }, { "name": "env overrides", @@ -27,7 +27,7 @@ { "name": "auth login", "usage": "ade auth login [options] [--json]", - "summary": "Ensure the target environment is logged in; --api-key\nauthenticates with a key directly ('-' prompts with hidden input).\nTargets --env, else $ADE_ENV, else production \u2014 nothing is stored\nabout the choice.", + "summary": "Ensure the target environment is logged in; `--api-key`\nauthenticates with a key directly ('-' prompts with hidden input).\nTargets `--env`, else $ADE_ENV, else production \u2014 nothing is stored\nabout the choice.", "arguments": [], "flags": [ { @@ -138,7 +138,7 @@ { "name": "auth logout", "usage": "ade auth logout [options] [--json]", - "summary": "Log out of one environment (the resolved target by default); --all\nclears every environment. Idempotent; OAuth refresh tokens are revoked\nbest-effort first.", + "summary": "Log out of one environment (the resolved target by default); `--all`\nclears every environment. Idempotent; OAuth refresh tokens are revoked\nbest-effort first.", "arguments": [], "flags": [ { @@ -178,7 +178,7 @@ }, { "key": "environment", - "what": "the environment cleared (null with --all)" + "what": "the environment cleared (null with `--all`)" } ] }, @@ -187,7 +187,7 @@ { "name": "login", "usage": "ade login [options] [--json]", - "summary": "Alias of `ade auth login`: ensure the target environment is logged in; --api-key authenticates with a key directly ('-' prompts with hidden input).", + "summary": "Alias of `ade auth login`: ensure the target environment is logged in; `--api-key` authenticates with a key directly ('-' prompts with hidden input).", "arguments": [], "flags": [ { @@ -248,7 +248,7 @@ { "name": "logout", "usage": "ade logout [options] [--json]", - "summary": "Alias of `ade auth logout`: log out of one environment (the resolved target by default); --all clears every environment.", + "summary": "Alias of `ade auth logout`: log out of one environment (the resolved target by default); `--all` clears every environment.", "arguments": [], "flags": [ { @@ -288,7 +288,7 @@ }, { "key": "environment", - "what": "the environment cleared (null with --all)" + "what": "the environment cleared (null with `--all`)" } ] }, @@ -357,7 +357,7 @@ { "name": "help", "usage": "ade help [COMMAND|TOPIC]... [--json]", - "summary": "Print the whole-surface command reference in one call: every\ncommand and flag, the output convention, each verb's result shape,\nexit states, and the store layout. The agent bootstrap \u2014 run this\n(with --json) before anything else. `help TOPIC` prints one\nconceptual page instead (workflow, output, credentials, errors).", + "summary": "Print the whole-surface command reference in one call: every\ncommand and flag, the output convention, each verb's result shape,\nexit states, and the store layout. The agent bootstrap \u2014 run this\n(with `--json`) before anything else. `help TOPIC` prints one\nconceptual page instead (workflow, output, credentials, errors).", "arguments": [ { "name": "[COMMAND|TOPIC]...", @@ -409,14 +409,14 @@ "metavar": "FILE", "required": false, "default": null, - "help": "Local document file to parse; pass exactly one of -d/--document or --document-url." + "help": "Local document file to parse; pass exactly one of -d/`--document` or `--document-url`." }, { "flags": "--document-url", "metavar": "TEXT", "required": false, "default": null, - "help": "Document URL the server fetches (identity is the URL x params; a re-run dedups even if the remote content changed \u2014 --force refreshes)." + "help": "Document URL the server fetches (identity is the URL x params; a re-run dedups even if the remote content changed \u2014 `--force` refreshes)." }, { "flags": "--model", @@ -444,7 +444,7 @@ "metavar": "TEXT", "required": false, "default": null, - "help": "Full ParseOptions pass-through as a JSON object, sent verbatim (the server rejects unknown keys with a 422). Keys as of this release \u2014 pages: 1-indexed integer array (default: all pages); atomic_grounding: bool (default true; false omits the per-line atomic_grounding field from every node); inline_markdown: bool (default false; true adds each node's own markdown slice inline); blocks..markdown: bool (default true; false suppresses that type's markdown; types: text, table, figure, marginalia, attestation, logo, scan_code, card); blocks.table.format: 'html' (default) or 'markdown'; password: always rejected (422) \u2014 decrypt PDFs before upload. Example: '{\"inline_markdown\": true, \"blocks\": {\"table\": {\"format\": \"markdown\"}}}'. Merges with --pages; giving pages in both is an error." + "help": "Full ParseOptions pass-through as a JSON object, sent verbatim (the server rejects unknown keys with a 422). Keys as of this release \u2014 pages: 1-indexed integer array (default: all pages); atomic_grounding: bool (default true; false omits the per-line atomic_grounding field from every node); inline_markdown: bool (default false; true adds each node's own markdown slice inline); blocks..markdown: bool (default true; false suppresses that type's markdown; types: text, table, figure, marginalia, attestation, logo, scan_code, card); blocks.table.format: 'html' (default) or 'markdown'; password: always rejected (422) \u2014 decrypt PDFs before upload. Example: '{\"inline_markdown\": true, \"blocks\": {\"table\": {\"format\": \"markdown\"}}}'. Merges with `--pages`; giving pages in both is an error." }, { "flags": "--env", @@ -479,7 +479,7 @@ "metavar": null, "required": false, "default": null, - "help": "Print only the id(s) this run produced, one per line \u2014 the piping mode (JOB=$(ade parse -d f.pdf --id-only)). Takes precedence over --json; errors and hints go to stderr." + "help": "Print only the id(s) this run produced, one per line \u2014 the piping mode (JOB=$(ade parse -d f.pdf `--id-only`)). Takes precedence over `--json`; errors and hints go to stderr." } ], "supports_json": true, @@ -540,11 +540,11 @@ }, { "key": "markdown", - "what": "the parse markdown \u2014 only with --include markdown" + "what": "the parse markdown \u2014 only with `--include markdown`" }, { "key": "elements", - "what": "the flat projection \u2014 only with --include elements" + "what": "the flat projection \u2014 only with `--include elements`" } ] }, @@ -616,7 +616,7 @@ "metavar": "TEXT", "required": false, "default": null, - "help": "Environment to run against: dev, staging, production, eu (default: $ADE_ENV, then production). The JOB_ITEM_ID form inherits the parse item's environment instead \u2014 its server-side parse job only exists there \u2014 and a conflicting --env is refused." + "help": "Environment to run against: dev, staging, production, eu (default: $ADE_ENV, then production). The JOB_ITEM_ID form inherits the parse item's environment instead \u2014 its server-side parse job only exists there \u2014 and a conflicting `--env` is refused." }, { "flags": "--wait", @@ -637,7 +637,7 @@ "metavar": null, "required": false, "default": null, - "help": "Print only the id(s) this run produced, one per line \u2014 the piping mode (JOB=$(ade parse -d f.pdf --id-only)). Takes precedence over --json; errors and hints go to stderr." + "help": "Print only the id(s) this run produced, one per line \u2014 the piping mode (JOB=$(ade parse -d f.pdf `--id-only`)). Takes precedence over `--json`; errors and hints go to stderr." } ], "supports_json": true, @@ -772,7 +772,7 @@ }, { "key": "stale", - "what": "extract items: true when the referenced parse was --force re-run after this extraction" + "what": "extract items: true when the referenced parse was `--force` re-run after this extraction" }, { "key": "created_at / completed_at", @@ -821,12 +821,12 @@ { "name": "find", "usage": "ade find [JOB_ITEM_ID] [QUERY] [options] [--json]", - "summary": "Search parsed elements locally: `find JOB_ITEM_ID [QUERY]`, or --job\n(repeatable) for several items; no query lists every element.\n\nIds discovered here are what `view --element-id` deep-links and\n`crop --element-id` renders \u2014 though `crop` takes these same filters\ndirectly (`crop JOB_ITEM_ID --type figure`) when you want the images\nrather than the records.", + "summary": "Search parsed elements locally: `find JOB_ITEM_ID [QUERY]`, or `--job`\n(repeatable) for several items; no query lists every element.\n\nIds discovered here are what `view --element-id` deep-links and\n`crop --element-id` renders \u2014 though `crop` takes these same filters\ndirectly (`crop JOB_ITEM_ID --type figure`) when you want the images\nrather than the records.", "arguments": [ { "name": "[JOB_ITEM_ID] [QUERY]", "required": false, - "help": "Job item id (or unambiguous prefix) to search, then an optional case-insensitive substring QUERY. With --job, the one allowed positional is the QUERY." + "help": "Job item id (or unambiguous prefix) to search, then an optional case-insensitive substring QUERY. With `--job`, the one allowed positional is the QUERY." } ], "flags": [ @@ -877,7 +877,7 @@ "metavar": null, "required": false, "default": null, - "help": "Print only the id(s) this run produced, one per line \u2014 the piping mode (JOB=$(ade parse -d f.pdf --id-only)). Takes precedence over --json; errors and hints go to stderr." + "help": "Print only the id(s) this run produced, one per line \u2014 the piping mode (JOB=$(ade parse -d f.pdf `--id-only`)). Takes precedence over `--json`; errors and hints go to stderr." } ], "supports_json": true, @@ -909,7 +909,7 @@ "what": "the element's markdown slice" } ], - "note": "One record per match, in document order; [] when nothing matched. --id-only prints the element ids alone." + "note": "One record per match, in document order; [] when nothing matched. `--id-only` prints the element ids alone." }, "band": "local read models" }, @@ -937,21 +937,21 @@ "metavar": null, "required": false, "default": null, - "help": "With --element-id: render that element's PNG crop instead of the HTML artifact." + "help": "With `--element-id`: render that element's PNG crop instead of the HTML artifact." }, { "flags": "--open, --no-open", "metavar": null, "required": false, "default": null, - "help": "Open the result in the browser. Default: open when stdout is a terminal; --json runs and piped output never auto-open." + "help": "Open the result in the browser. Default: open when stdout is a terminal; `--json` runs and piped output never auto-open." }, { "flags": "--dpi", "metavar": "INTEGER", "required": false, "default": null, - "help": "Page render dpi (default 120); with --crop, the crop dpi (default 300)." + "help": "Page render dpi (default 120); with `--crop`, the crop dpi (default 300)." }, { "flags": "--pages", @@ -972,14 +972,14 @@ "metavar": null, "required": false, "default": null, - "help": "Open via a local server (http://127.0.0.1) instead of file:// \u2014 browser zoom then covers every viewer natively. Starts the server if needed; reuses a running one. It retires itself after 30 idle minutes, or immediately with --stop-server." + "help": "Open via a local server (http://127.0.0.1) instead of file:// \u2014 browser zoom then covers every viewer natively. Starts the server if needed; reuses a running one. It retires itself after 30 idle minutes, or immediately with `--stop-server`." }, { "flags": "--stop-server", "metavar": null, "required": false, "default": null, - "help": "Stop the local viewer server (started by --serve) and exit." + "help": "Stop the local viewer server (started by `--serve`) and exit." } ], "supports_json": true, @@ -988,7 +988,7 @@ "keys": [ { "key": "status", - "what": "'viewed' ('cropped' with --crop, 'synced' with --sync-viewers)" + "what": "'viewed' ('cropped' with `--crop`, 'synced' with `--sync-viewers`)" }, { "key": "job_item_id", @@ -1000,7 +1000,7 @@ }, { "key": "path", - "what": "the self-contained view.html (or the PNG, with --crop)" + "what": "the self-contained view.html (or the PNG, with `--crop`)" }, { "key": "built", @@ -1016,7 +1016,7 @@ }, { "key": "deep_link", - "what": "view.html#element=... when --element-id was given" + "what": "view.html#element=... when `--element-id` was given" }, { "key": "history_items", @@ -1117,7 +1117,7 @@ "what": "one record per PNG (element_id, type, page, box, dpi, path, width, height)" } ], - "note": "One shape whatever matched: a single --element-id is count 1 with one crops[] record; a filter (--type/--page/--all) matching nothing is count 0 with crops []." + "note": "One shape whatever matched: a single `--element-id` is count 1 with one crops[] record; a filter (`--type`/`--page`/`--all`) matching nothing is count 0 with crops []." }, "band": "local read models" } diff --git a/src/ade_cli/auth.py b/src/ade_cli/auth.py index bd4b2c2..1ec53d6 100644 --- a/src/ade_cli/auth.py +++ b/src/ade_cli/auth.py @@ -74,9 +74,9 @@ def login( environment: str | None = typer.Option(None, "--env", help=_ENV_HELP), as_json: bool = JSON_FLAG, ) -> None: - """Ensure the target environment is logged in; --api-key + """Ensure the target environment is logged in; `--api-key` authenticates with a key directly ('-' prompts with hidden input). - Targets --env, else $ADE_ENV, else production — nothing is stored + Targets `--env`, else $ADE_ENV, else production — nothing is stored about the choice.""" home = ade_home() resolved = resolve_target(home, environment, as_json=as_json) @@ -586,7 +586,7 @@ def logout( ), as_json: bool = JSON_FLAG, ) -> None: - """Log out of one environment (the resolved target by default); --all + """Log out of one environment (the resolved target by default); `--all` clears every environment. Idempotent; OAuth refresh tokens are revoked best-effort first.""" home = ade_home() diff --git a/src/ade_cli/extract.py b/src/ade_cli/extract.py index 94701e7..9782c16 100644 --- a/src/ade_cli/extract.py +++ b/src/ade_cli/extract.py @@ -273,7 +273,7 @@ def extract( help=f"Environment to run against: {', '.join(ENVIRONMENTS)} " "(default: $ADE_ENV, then production). The JOB_ITEM_ID form inherits the " "parse item's environment instead — its server-side parse job only " - "exists there — and a conflicting --env is refused.", + "exists there — and a conflicting `--env` is refused.", ), wait: float = typer.Option(600.0, "--wait", help="Poll budget in seconds."), force: bool = typer.Option( diff --git a/src/ade_cli/find.py b/src/ade_cli/find.py index f0dbacd..6c2f61b 100644 --- a/src/ade_cli/find.py +++ b/src/ade_cli/find.py @@ -33,7 +33,7 @@ def find( None, metavar="[JOB_ITEM_ID] [QUERY]", help="Job item id (or unambiguous prefix) to search, then an " - "optional case-insensitive substring QUERY. With --job, the one " + "optional case-insensitive substring QUERY. With `--job`, the one " "allowed positional is the QUERY.", ), job_tokens: list[str] = typer.Option( @@ -61,7 +61,7 @@ def find( as_json: bool = JSON_FLAG, id_only: bool = ID_ONLY_FLAG, ) -> None: - """Search parsed elements locally: `find JOB_ITEM_ID [QUERY]`, or --job + """Search parsed elements locally: `find JOB_ITEM_ID [QUERY]`, or `--job` (repeatable) for several items; no query lists every element. Ids discovered here are what `view --element-id` deep-links and diff --git a/src/ade_cli/help.py b/src/ade_cli/help.py index 7b9779e..b8ae87e 100644 --- a/src/ade_cli/help.py +++ b/src/ade_cli/help.py @@ -180,8 +180,8 @@ ("stored", "false when the result could not be persisted"), ("store_dir", "absolute path of the job item folder"), ("artifacts", "artifact filenames written there"), - ("markdown", "the parse markdown — only with --include markdown"), - ("elements", "the flat projection — only with --include elements"), + ("markdown", "the parse markdown — only with `--include markdown`"), + ("elements", "the flat projection — only with `--include elements`"), ], }, "extract": { @@ -222,7 +222,7 @@ ("text", "the element's markdown slice"), ], "note": "One record per match, in document order; [] when nothing " - "matched. --id-only prints the element ids alone.", + "matched. `--id-only` prints the element ids alone.", }, "crop": { "shape": "object", @@ -234,21 +234,21 @@ ("crops", "one record per PNG (element_id, type, " "page, box, dpi, path, width, height)"), ], - "note": "One shape whatever matched: a single --element-id is " - "count 1 with one crops[] record; a filter (--type/--page/--all) " + "note": "One shape whatever matched: a single `--element-id` is " + "count 1 with one crops[] record; a filter (`--type`/`--page`/`--all`) " "matching nothing is count 0 with crops [].", }, "view": { "shape": "object", "keys": [ - ("status", "'viewed' ('cropped' with --crop, 'synced' with --sync-viewers)"), + ("status", "'viewed' ('cropped' with `--crop`, 'synced' with `--sync-viewers`)"), ("job_item_id", "the item rendered"), ("kind", "parse | extract"), - ("path", "the self-contained view.html (or the PNG, with --crop)"), + ("path", "the self-contained view.html (or the PNG, with `--crop`)"), ("built", "true when this run rebuilt the artifact"), ("pages_embedded", "pages inlined; the rest load from sidecars"), ("note", "why the render weakened, when it did (else null)"), - ("deep_link", "view.html#element=... when --element-id was given"), + ("deep_link", "view.html#element=... when `--element-id` was given"), ("history_items", "items in the rebuilt sidebar read model"), ("sidebar_sync", "true when sibling viewers build in the background"), ], @@ -265,7 +265,7 @@ ("parse", "extract items: the referenced parse — {job_item_id, " "run_id (the parse generation extracted against), missing}"), ("stale", "extract items: true when the referenced parse was " - "--force re-run after this extraction"), + "`--force` re-run after this extraction"), ("created_at / completed_at", "epoch seconds (null when unknown)"), ], }, @@ -308,7 +308,7 @@ ("cleared", "false when there was nothing stored to clear"), ("revoked", "refresh tokens revoked best-effort"), ("scope", "environment | all"), - ("environment", "the environment cleared (null with --all)"), + ("environment", "the environment cleared (null with `--all`)"), ], }, "version": { @@ -387,7 +387,7 @@ CONVENTIONS = [ ( "--json", - "Every command supports --json: one stable JSON object/array on " + "Every command supports `--json`: one stable JSON object/array on " "stdout (errors and pending payloads follow the same rule). Agents " "should always pass it. Each command's published shape is its " "'result' block below — the full result is always on stdout, never " @@ -395,8 +395,8 @@ ), ( "--id-only", - "parse, extract, and find also take --id-only: just the id(s), one " - "per line, for piping (JOB=$(ade parse -d f.pdf --id-only)). Errors " + "parse, extract, and find also take `--id-only`: just the id(s), one " + "per line, for piping (`JOB=$(ade parse -d f.pdf --id-only)`). Errors " "and hints go to stderr so a captured id is never a sentence.", ), ( @@ -409,7 +409,7 @@ "guarantees", "parse and extract ensure a run exists rather than fire a request: " "an already-done run is served from disk free with an explicit " - "notice (--force consents to a re-bill); a pending run is resumed, " + "notice (`--force` consents to a re-bill); a pending run is resumed, " "never resubmitted; Ctrl-C stops the waiting, not the work.", ), ( @@ -677,7 +677,7 @@ def help_command( """Print the whole-surface command reference in one call: every command and flag, the output convention, each verb's result shape, exit states, and the store layout. The agent bootstrap — run this - (with --json) before anything else. `help TOPIC` prints one + (with `--json`) before anything else. `help TOPIC` prints one conceptual page instead (workflow, output, credentials, errors).""" root = ctx.find_root().command assert isinstance(root, TyperGroup) diff --git a/src/ade_cli/main.py b/src/ade_cli/main.py index b6a4bfa..5d3c5c1 100644 --- a/src/ade_cli/main.py +++ b/src/ade_cli/main.py @@ -133,13 +133,13 @@ def _force_utf8_stdio() -> None: app.command( "login", help="Alias of `ade auth login`: ensure the target environment is " - "logged in; --api-key authenticates with a key directly ('-' prompts " + "logged in; `--api-key` authenticates with a key directly ('-' prompts " "with hidden input).", )(login) app.command( "logout", help="Alias of `ade auth logout`: log out of one environment (the " - "resolved target by default); --all clears every environment.", + "resolved target by default); `--all` clears every environment.", )(logout) app.command()(parse) app.command()(extract) diff --git a/src/ade_cli/output.py b/src/ade_cli/output.py index 72f2bb4..2eda30e 100644 --- a/src/ade_cli/output.py +++ b/src/ade_cli/output.py @@ -33,8 +33,8 @@ False, "--id-only", help="Print only the id(s) this run produced, one per line — the " - "piping mode (JOB=$(ade parse -d f.pdf --id-only)). Takes precedence " - "over --json; errors and hints go to stderr.", + "piping mode (JOB=$(ade parse -d f.pdf `--id-only`)). Takes precedence " + "over `--json`; errors and hints go to stderr.", ) # Machine-readable exit states, shared by every command: pending is a normal diff --git a/src/ade_cli/parse.py b/src/ade_cli/parse.py index 8676f56..b8d49e6 100644 --- a/src/ade_cli/parse.py +++ b/src/ade_cli/parse.py @@ -102,12 +102,12 @@ def parse( ctx: typer.Context, document: Path | None = typer.Option( None, "-d", "--document", exists=True, dir_okay=False, readable=True, - help="Local document file to parse; pass exactly one of -d/--document or --document-url.", + help="Local document file to parse; pass exactly one of -d/`--document` or `--document-url`.", ), document_url: str | None = typer.Option( None, "--document-url", help="Document URL the server fetches (identity is the URL x params; " - "a re-run dedups even if the remote content changed — --force refreshes).", + "a re-run dedups even if the remote content changed — `--force` refreshes).", ), model: str = typer.Option( DEFAULT_MODEL, "--model", help="Parse model registry version." @@ -138,7 +138,7 @@ def parse( "password: always rejected (422) — decrypt PDFs before upload. " "Example: '{\"inline_markdown\": true, \"blocks\": {\"table\": " "{\"format\": \"markdown\"}}}'. " - "Merges with --pages; giving pages in both is an error.", + "Merges with `--pages`; giving pages in both is an error.", ), environment: str | None = typer.Option( None, "--env", diff --git a/src/ade_cli/view.py b/src/ade_cli/view.py index 88a666a..72ed663 100644 --- a/src/ade_cli/view.py +++ b/src/ade_cli/view.py @@ -898,18 +898,18 @@ def view( crop: bool = typer.Option( False, "--crop", - help="With --element-id: render that element's PNG crop instead of " + help="With `--element-id`: render that element's PNG crop instead of " "the HTML artifact.", ), open_browser: bool | None = typer.Option( None, "--open/--no-open", help="Open the result in the browser. Default: open when stdout is " - "a terminal; --json runs and piped output never auto-open.", + "a terminal; `--json` runs and piped output never auto-open.", ), dpi: int | None = typer.Option( None, "--dpi", min=1, - help=f"Page render dpi (default {DEFAULT_DPI}); with --crop, the crop " + help=f"Page render dpi (default {DEFAULT_DPI}); with `--crop`, the crop " f"dpi (default {DEFAULT_CROP_DPI}).", ), pages: str | None = typer.Option( @@ -924,11 +924,11 @@ def view( help="Open via a local server (http://127.0.0.1) instead of file:// " "— browser zoom then covers every viewer natively. Starts the " "server if needed; reuses a running one. It retires itself after " - "30 idle minutes, or immediately with --stop-server.", + "30 idle minutes, or immediately with `--stop-server`.", ), stop_server: bool = typer.Option( False, "--stop-server", - help="Stop the local viewer server (started by --serve) and exit.", + help="Stop the local viewer server (started by `--serve`) and exit.", ), sync_viewers: bool = typer.Option( False, "--sync-viewers", hidden=True, diff --git a/tests/test_help_style.py b/tests/test_help_style.py new file mode 100644 index 0000000..48a0c5d --- /dev/null +++ b/tests/test_help_style.py @@ -0,0 +1,67 @@ +"""Style guard for the help surface: inline code is always backticked. + +A bare ``--flag`` in help prose is ambiguous for agents and renders as +an em dash plus the flag name on the hosted docs, which generate the +CLI reference from ``help --json``. See docs/agents/writing-style.md. + +Scope: every prose field in the help surface. Topic bodies are exempt: +they are pre-formatted terminal layouts (aligned flag columns, literal +example commands) where backticks would corrupt the rendering. Other +inline-code kinds (paths, commands, env vars) stay convention; a test +cannot reliably tell a path from prose, but a long flag is unmistakable. +""" + +from __future__ import annotations + +import json +import re + +import pytest + +# A long flag in prose that is not already inside a backtick span. +CODE_SPAN = re.compile(r"`[^`]*`") +BARE_FLAG = re.compile(r"(? dict: + result = cli.invoke("help", "--json") + assert result.exit_code == 0 + return json.loads(result.stdout) + + +def _prose_strings(reference: dict): + """Yield (locator, text) for every prose string in the help surface.""" + yield "description", reference["description"] + for convention in reference["conventions"]: + yield f"conventions[{convention['name']}]", convention["rule"] + for command in reference["commands"]: + where = f"commands[{command['name']}]" + yield f"{where}.summary", command["summary"] + for argument in command["arguments"]: + yield f"{where}.arguments[{argument['name']}]", argument["help"] + for flag in command["flags"]: + yield f"{where}.flags[{flag['flags']}]", flag["help"] + for key in command.get("result", {}).get("keys", []): + yield f"{where}.result[{key['key']}]", key["what"] + note = command.get("result", {}).get("note") + if note: + yield f"{where}.result.note", note + # topics[].body is deliberately absent: pre-formatted terminal text. + for state in reference["exit_states"]: + yield f"exit_states[{state['name']}]", state["meaning"] + for entry in reference["store"]["layout"]: + yield f"store[{entry['path']}]", entry["what"] + + +def test_flags_in_help_prose_are_backticked(reference): + """Every --flag mentioned in help prose is wrapped in backticks.""" + offenders = [] + for locator, text in _prose_strings(reference): + stripped = CODE_SPAN.sub("", text) + for match in BARE_FLAG.findall(stripped): + offenders.append(f"{locator}: bare {match}") + assert not offenders, ( + "Bare flags in help prose; wrap them in backticks " + "(docs/agents/writing-style.md):\n" + "\n".join(offenders) + ) From 5377bffb4a5870b7b744ff701fa92ec4fdda757f Mon Sep 17 00:00:00 2001 From: Bianca Ragsdale <128001048+lai-bianca@users.noreply.github.com> Date: Fri, 7 Aug 2026 09:49:50 -0700 Subject: [PATCH 2/3] docs: keep help prose free of references a reader cannot follow Adds two prose standards: cite what a reader outside this repo can open, and leave out issue numbers, internal URLs, and anything that resolves only inside LandingAI. An issue number is dead weight to someone reading the shipped binary, so the prose states the behavior and the number stays in the commit history. Extends tests/test_help_style.py with an issue-reference guard over the same prose fields as the flag rule. The lookbehind spares URL fragments, which are legitimate prose (view.html#element). Co-Authored-By: Claude Opus 5 (1M context) --- docs/agents/writing-style.md | 4 ++++ tests/test_help_style.py | 22 +++++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/docs/agents/writing-style.md b/docs/agents/writing-style.md index 82a066e..f5a74fe 100644 --- a/docs/agents/writing-style.md +++ b/docs/agents/writing-style.md @@ -9,6 +9,10 @@ README, and SKILL.md. - Second person ("you"), active voice, direct language. - No promotional language. Describe what a thing does. - No idioms. Plain wording wins. +- Cite what a reader outside this repo can open: public docs, `ade help` + topics, and shipped flags. +- Leave out issue numbers (enforced), internal URLs, and anything that + resolves only inside LandingAI. - Test every code example before it lands. Fence README and SKILL.md code blocks with a language tag. - Error and status messages state what happened and what to do next. diff --git a/tests/test_help_style.py b/tests/test_help_style.py index 48a0c5d..c3a9571 100644 --- a/tests/test_help_style.py +++ b/tests/test_help_style.py @@ -1,9 +1,14 @@ -"""Style guard for the help surface: inline code is always backticked. +"""Style guards for the help surface: backticked inline code, and no +references a reader outside this repo cannot follow. A bare ``--flag`` in help prose is ambiguous for agents and renders as an em dash plus the flag name on the hosted docs, which generate the CLI reference from ``help --json``. See docs/agents/writing-style.md. +An issue number is dead weight to everyone reading the shipped binary: +it resolves only against this repo's tracker, so ``help`` prose states +the behavior and leaves the number to the commit history. + Scope: every prose field in the help surface. Topic bodies are exempt: they are pre-formatted terminal layouts (aligned flag columns, literal example commands) where backticks would corrupt the rendering. Other @@ -21,6 +26,9 @@ # A long flag in prose that is not already inside a backtick span. CODE_SPAN = re.compile(r"`[^`]*`") BARE_FLAG = re.compile(r"(? Date: Fri, 7 Aug 2026 09:57:40 -0700 Subject: [PATCH 3/3] style: apply the help-prose rules to strings main added since v1.0.2 The branch predated v1.0.3 through v1.0.5, so the strings those releases added never met either rule. Backticks the 15 bare flags the guard names (history list's summary, note, and `--limit` help; parse's `--keep-copy`; view and crop's download flags; the job-item-ids convention; meta.json's store entry) and drops the two issue references from view and crop, whose sentences already state the behavior. Regenerates the help.json snapshot. Both style guards pass; full suite 742 passed, 8 skipped. Co-Authored-By: Claude Opus 5 (1M context) --- docs/reference/help.json | 86 ++++++++++++++++++++-------------------- src/ade_cli/crop.py | 4 +- src/ade_cli/help.py | 12 +++--- src/ade_cli/history.py | 4 +- src/ade_cli/parse.py | 2 +- src/ade_cli/view.py | 6 +-- 6 files changed, 57 insertions(+), 57 deletions(-) diff --git a/docs/reference/help.json b/docs/reference/help.json index 2dff6a2..7072274 100644 --- a/docs/reference/help.json +++ b/docs/reference/help.json @@ -4,19 +4,19 @@ "conventions": [ { "name": "--json", - "rule": "Every command supports --json: one stable JSON object/array on stdout (errors and pending payloads follow the same rule). Agents should always pass it. Each command's published shape is its 'result' block below \u2014 the full result is always on stdout, never only in a file." + "rule": "Every command supports `--json`: one stable JSON object/array on stdout (errors and pending payloads follow the same rule). Agents should always pass it. Each command's published shape is its 'result' block below \u2014 the full result is always on stdout, never only in a file." }, { "name": "--id-only", - "rule": "parse, extract, and find also take --id-only: just the id(s), one per line, for piping (JOB=$(ade parse -d f.pdf --id-only)). Errors and hints go to stderr so a captured id is never a sentence." + "rule": "parse, extract, and find also take `--id-only`: just the id(s), one per line, for piping (`JOB=$(ade parse -d f.pdf --id-only)`). Errors and hints go to stderr so a captured id is never a sentence." }, { "name": "job item ids", - "rule": "Store commands take a job item id or an unambiguous prefix. Discover ids with `history list`; ambiguous or unknown ids error with candidates listed. Distinct from the server-side run id: --json payloads report that as run_id, and on-disk records spell the same value job_id (the wire's name) \u2014 neither is ever a job item id." + "rule": "Store commands take a job item id or an unambiguous prefix. Discover ids with `history list`; ambiguous or unknown ids error with candidates listed. Distinct from the server-side run id: `--json` payloads report that as run_id, and on-disk records spell the same value job_id (the wire's name) \u2014 neither is ever a job item id." }, { "name": "guarantees", - "rule": "parse and extract ensure a run exists rather than fire a request: an already-done run is served from disk free with an explicit notice (--force consents to a re-bill); a pending run is resumed, never resubmitted; Ctrl-C stops the waiting, not the work." + "rule": "parse and extract ensure a run exists rather than fire a request: an already-done run is served from disk free with an explicit notice (`--force` consents to a re-bill); a pending run is resumed, never resubmitted; Ctrl-C stops the waiting, not the work." }, { "name": "env overrides", @@ -27,7 +27,7 @@ { "name": "auth login", "usage": "ade auth login [options] [--json]", - "summary": "Ensure the target environment is logged in; --api-key\nauthenticates with a key directly ('-' prompts with hidden input).\nTargets --env, else $ADE_ENV, else production \u2014 nothing is stored\nabout the choice.", + "summary": "Ensure the target environment is logged in; `--api-key`\nauthenticates with a key directly ('-' prompts with hidden input).\nTargets `--env`, else $ADE_ENV, else production \u2014 nothing is stored\nabout the choice.", "arguments": [], "flags": [ { @@ -149,7 +149,7 @@ { "name": "auth logout", "usage": "ade auth logout [options] [--json]", - "summary": "Log out of one environment (the resolved target by default); --all\nclears every environment. Idempotent; OAuth refresh tokens are revoked\nbest-effort first.", + "summary": "Log out of one environment (the resolved target by default); `--all`\nclears every environment. Idempotent; OAuth refresh tokens are revoked\nbest-effort first.", "arguments": [], "flags": [ { @@ -189,7 +189,7 @@ }, { "key": "environment", - "what": "the environment cleared (null with --all)" + "what": "the environment cleared (null with `--all`)" } ] }, @@ -318,7 +318,7 @@ { "name": "login", "usage": "ade login [options] [--json]", - "summary": "Alias of `ade auth login`: ensure the target environment is logged in; --api-key authenticates with a key directly ('-' prompts with hidden input).", + "summary": "Alias of `ade auth login`: ensure the target environment is logged in; `--api-key` authenticates with a key directly ('-' prompts with hidden input).", "arguments": [], "flags": [ { @@ -390,7 +390,7 @@ { "name": "logout", "usage": "ade logout [options] [--json]", - "summary": "Alias of `ade auth logout`: log out of one environment (the resolved target by default); --all clears every environment.", + "summary": "Alias of `ade auth logout`: log out of one environment (the resolved target by default); `--all` clears every environment.", "arguments": [], "flags": [ { @@ -430,7 +430,7 @@ }, { "key": "environment", - "what": "the environment cleared (null with --all)" + "what": "the environment cleared (null with `--all`)" } ] }, @@ -499,7 +499,7 @@ { "name": "help", "usage": "ade help [COMMAND|TOPIC]... [--json]", - "summary": "Print the whole-surface command reference in one call: every\ncommand and flag, the output convention, each verb's result shape,\nexit states, and the store layout. The agent bootstrap \u2014 run this\n(with --json) before anything else. `help TOPIC` prints one\nconceptual page instead (workflow, output, credentials, errors).", + "summary": "Print the whole-surface command reference in one call: every\ncommand and flag, the output convention, each verb's result shape,\nexit states, and the store layout. The agent bootstrap \u2014 run this\n(with `--json`) before anything else. `help TOPIC` prints one\nconceptual page instead (workflow, output, credentials, errors).", "arguments": [ { "name": "[COMMAND|TOPIC]...", @@ -551,14 +551,14 @@ "metavar": "FILE", "required": false, "default": null, - "help": "Local document file to parse; pass exactly one of -d/--document or --document-url." + "help": "Local document file to parse; pass exactly one of -d/`--document` or `--document-url`." }, { "flags": "--document-url", "metavar": "TEXT", "required": false, "default": null, - "help": "Document URL the server fetches (identity is the URL x params; a re-run dedups even if the remote content changed \u2014 --force refreshes)." + "help": "Document URL the server fetches (identity is the URL x params; a re-run dedups even if the remote content changed \u2014 `--force` refreshes)." }, { "flags": "--model", @@ -586,7 +586,7 @@ "metavar": "TEXT", "required": false, "default": null, - "help": "Full ParseOptions pass-through as a JSON object, sent verbatim (the server rejects unknown keys with a 422). Keys as of this release \u2014 pages: 1-indexed integer array (default: all pages); atomic_grounding: bool (default true; false omits the per-line atomic_grounding field from every node); inline_markdown: bool (default false; true adds each node's own markdown slice inline); blocks..markdown: bool (default true; false suppresses that type's markdown; types: text, table, figure, marginalia, attestation, logo, scan_code, card); blocks.table.format: 'html' (default) or 'markdown'; password: always rejected (422) \u2014 decrypt PDFs before upload. Example: '{\"inline_markdown\": true, \"blocks\": {\"table\": {\"format\": \"markdown\"}}}'. Merges with --pages; giving pages in both is an error." + "help": "Full ParseOptions pass-through as a JSON object, sent verbatim (the server rejects unknown keys with a 422). Keys as of this release \u2014 pages: 1-indexed integer array (default: all pages); atomic_grounding: bool (default true; false omits the per-line atomic_grounding field from every node); inline_markdown: bool (default false; true adds each node's own markdown slice inline); blocks..markdown: bool (default true; false suppresses that type's markdown; types: text, table, figure, marginalia, attestation, logo, scan_code, card); blocks.table.format: 'html' (default) or 'markdown'; password: always rejected (422) \u2014 decrypt PDFs before upload. Example: '{\"inline_markdown\": true, \"blocks\": {\"table\": {\"format\": \"markdown\"}}}'. Merges with `--pages`; giving pages in both is an error." }, { "flags": "--env", @@ -614,7 +614,7 @@ "metavar": null, "required": false, "default": null, - "help": "--document-url only: also download the document into the job item (plain HTTP, no API credits) so page previews and crops render locally \u2014 fetched now, while the URL (often pre-signed) still works. Without it, the first `view`/`crop` fetches the copy instead, by which time a pre-signed URL may have expired." + "help": "`--document-url` only: also download the document into the job item (plain HTTP, no API credits) so page previews and crops render locally \u2014 fetched now, while the URL (often pre-signed) still works. Without it, the first `view`/`crop` fetches the copy instead, by which time a pre-signed URL may have expired." }, { "flags": "--include", @@ -628,7 +628,7 @@ "metavar": null, "required": false, "default": null, - "help": "Print only the id(s) this run produced, one per line \u2014 the piping mode (JOB=$(ade parse -d f.pdf --id-only)). Takes precedence over --json; errors and hints go to stderr." + "help": "Print only the id(s) this run produced, one per line \u2014 the piping mode (JOB=$(ade parse -d f.pdf `--id-only`)). Takes precedence over `--json`; errors and hints go to stderr." } ], "supports_json": true, @@ -689,19 +689,19 @@ }, { "key": "markdown", - "what": "the parse markdown \u2014 only with --include markdown" + "what": "the parse markdown \u2014 only with `--include markdown`" }, { "key": "elements", - "what": "the flat projection \u2014 only with --include elements" + "what": "the flat projection \u2014 only with `--include elements`" }, { "key": "kept_copy", - "what": "with --keep-copy: whether the URL document's copy was stored in the job item" + "what": "with `--keep-copy`: whether the URL document's copy was stored in the job item" }, { "key": "keep_copy_error", - "what": "with --keep-copy: why the copy could not be stored (the parse itself still succeeded)" + "what": "with `--keep-copy`: why the copy could not be stored (the parse itself still succeeded)" } ] }, @@ -773,7 +773,7 @@ "metavar": "TEXT", "required": false, "default": null, - "help": "Environment to run against: dev, staging, production, eu (default: $ADE_ENV, then production). The JOB_ITEM_ID form inherits the parse item's environment instead \u2014 its server-side parse job only exists there \u2014 and a conflicting --env is refused." + "help": "Environment to run against: dev, staging, production, eu (default: $ADE_ENV, then production). The JOB_ITEM_ID form inherits the parse item's environment instead \u2014 its server-side parse job only exists there \u2014 and a conflicting `--env` is refused." }, { "flags": "--wait", @@ -794,7 +794,7 @@ "metavar": null, "required": false, "default": null, - "help": "Print only the id(s) this run produced, one per line \u2014 the piping mode (JOB=$(ade parse -d f.pdf --id-only)). Takes precedence over --json; errors and hints go to stderr." + "help": "Print only the id(s) this run produced, one per line \u2014 the piping mode (JOB=$(ade parse -d f.pdf `--id-only`)). Takes precedence over `--json`; errors and hints go to stderr." } ], "supports_json": true, @@ -892,7 +892,7 @@ { "name": "history list", "usage": "ade history list [options] [--json]", - "summary": "List stored job items: id, kind, state, env, params, source \u2014\nthe newest 100 submissions first (--limit/--all adjust, --asc for\noldest first). Extract items referencing a parse item indent\nbeneath it. Bare `ade history` defaults to this command.", + "summary": "List stored job items: id, kind, state, env, params, source \u2014\nthe newest 100 submissions first (`--limit`/`--all` adjust, `--asc` for\noldest first). Extract items referencing a parse item indent\nbeneath it. Bare `ade history` defaults to this command.", "arguments": [], "flags": [ { @@ -907,7 +907,7 @@ "metavar": "INTEGER", "required": false, "default": 100, - "help": "Keep only the newest N items (default 100), whatever the order; --all lifts the cap." + "help": "Keep only the newest N items (default 100), whatever the order; `--all` lifts the cap." }, { "flags": "--all", @@ -951,14 +951,14 @@ }, { "key": "stale", - "what": "extract items: true when the referenced parse was --force re-run after this extraction" + "what": "extract items: true when the referenced parse was `--force` re-run after this extraction" }, { "key": "created_at / completed_at", "what": "epoch seconds (null when unknown)" } ], - "note": "Ordered newest submission first (timestamp-less items last), matching the viewer sidebar; --asc restores oldest-first. Capped at the newest 100 items by default \u2014 --limit N adjusts, --all lifts the cap, and a capped run says so up front (the first line of the listing; on stderr for --json). The --json array follows the same order and cap." + "note": "Ordered newest submission first (timestamp-less items last), matching the viewer sidebar; `--asc` restores oldest-first. Capped at the newest 100 items by default \u2014 `--limit` N adjusts, `--all` lifts the cap, and a capped run says so up front (the first line of the listing; on stderr for `--json`). The `--json` array follows the same order and cap." }, "band": "local read models" }, @@ -1001,12 +1001,12 @@ { "name": "find", "usage": "ade find [JOB_ITEM_ID] [QUERY] [options] [--json]", - "summary": "Search parsed elements locally: `find JOB_ITEM_ID [QUERY]`, or --job\n(repeatable) for several items; no query lists every element.\n\nIds discovered here are what `view --element-id` deep-links and\n`crop --element-id` renders \u2014 though `crop` takes these same filters\ndirectly (`crop JOB_ITEM_ID --type figure`) when you want the images\nrather than the records.", + "summary": "Search parsed elements locally: `find JOB_ITEM_ID [QUERY]`, or `--job`\n(repeatable) for several items; no query lists every element.\n\nIds discovered here are what `view --element-id` deep-links and\n`crop --element-id` renders \u2014 though `crop` takes these same filters\ndirectly (`crop JOB_ITEM_ID --type figure`) when you want the images\nrather than the records.", "arguments": [ { "name": "[JOB_ITEM_ID] [QUERY]", "required": false, - "help": "Job item id (or unambiguous prefix) to search, then an optional case-insensitive substring QUERY. With --job, the one allowed positional is the QUERY." + "help": "Job item id (or unambiguous prefix) to search, then an optional case-insensitive substring QUERY. With `--job`, the one allowed positional is the QUERY." } ], "flags": [ @@ -1057,7 +1057,7 @@ "metavar": null, "required": false, "default": null, - "help": "Print only the id(s) this run produced, one per line \u2014 the piping mode (JOB=$(ade parse -d f.pdf --id-only)). Takes precedence over --json; errors and hints go to stderr." + "help": "Print only the id(s) this run produced, one per line \u2014 the piping mode (JOB=$(ade parse -d f.pdf `--id-only`)). Takes precedence over `--json`; errors and hints go to stderr." } ], "supports_json": true, @@ -1089,7 +1089,7 @@ "what": "the element's markdown slice" } ], - "note": "One record per match, in document order; [] when nothing matched. --id-only prints the element ids alone." + "note": "One record per match, in document order; [] when nothing matched. `--id-only` prints the element ids alone." }, "band": "local read models" }, @@ -1117,21 +1117,21 @@ "metavar": null, "required": false, "default": null, - "help": "With --element-id: render that element's PNG crop instead of the HTML artifact." + "help": "With `--element-id`: render that element's PNG crop instead of the HTML artifact." }, { "flags": "--open, --no-open", "metavar": null, "required": false, "default": null, - "help": "Open the result in the browser. Default: open when stdout is a terminal; --json runs and piped output never auto-open." + "help": "Open the result in the browser. Default: open when stdout is a terminal; `--json` runs and piped output never auto-open." }, { "flags": "--dpi", "metavar": "INTEGER", "required": false, "default": null, - "help": "Page render dpi (default 120); with --crop, the crop dpi (default 300)." + "help": "Page render dpi (default 120); with `--crop`, the crop dpi (default 300)." }, { "flags": "--pages", @@ -1145,7 +1145,7 @@ "metavar": null, "required": false, "default": null, - "help": "URL-parsed items: fetch the document from its recorded URL into the job item and render page previews from that copy \u2014 the parse itself never gives the CLI the bytes (#169). This happens automatically when no copy is attached yet (a notice and progress line land on stderr); --no-download skips the fetch and previews stay empty. Explicit --download makes a failed fetch an error instead of a warning. Plain HTTP, no API credits; the copy is unverified against the parsed run. Also works on an extract item id (fetches into its referenced parse item)." + "help": "URL-parsed items: fetch the document from its recorded URL into the job item and render page previews from that copy \u2014 the parse itself never gives the CLI the bytes. This happens automatically when no copy is attached yet (a notice and progress line land on stderr); `--no-download` skips the fetch and previews stay empty. Explicit `--download` makes a failed fetch an error instead of a warning. Plain HTTP, no API credits; the copy is unverified against the parsed run. Also works on an extract item id (fetches into its referenced parse item)." }, { "flags": "--no-sidebar-sync", @@ -1159,14 +1159,14 @@ "metavar": null, "required": false, "default": null, - "help": "Open via a local server (http://127.0.0.1) instead of file:// \u2014 browser zoom then covers every viewer natively. Starts the server if needed; reuses a running one. It retires itself after 30 idle minutes, or immediately with --stop-server." + "help": "Open via a local server (http://127.0.0.1) instead of file:// \u2014 browser zoom then covers every viewer natively. Starts the server if needed; reuses a running one. It retires itself after 30 idle minutes, or immediately with `--stop-server`." }, { "flags": "--stop-server", "metavar": null, "required": false, "default": null, - "help": "Stop the local viewer server (started by --serve) and exit." + "help": "Stop the local viewer server (started by `--serve`) and exit." } ], "supports_json": true, @@ -1175,7 +1175,7 @@ "keys": [ { "key": "status", - "what": "'viewed' ('cropped' with --crop, 'synced' with --sync-viewers)" + "what": "'viewed' ('cropped' with `--crop`, 'synced' with `--sync-viewers`)" }, { "key": "job_item_id", @@ -1187,7 +1187,7 @@ }, { "key": "path", - "what": "the self-contained view.html (or the PNG, with --crop)" + "what": "the self-contained view.html (or the PNG, with `--crop`)" }, { "key": "built", @@ -1203,7 +1203,7 @@ }, { "key": "downloaded", - "what": "URL items: true when this run fetched the document into the job item (automatic on first view). false when the automatic fetch failed (see download_error) or explicit --download found the copy already attached; absent when nothing needed fetching" + "what": "URL items: true when this run fetched the document into the job item (automatic on first view). false when the automatic fetch failed (see download_error) or explicit `--download` found the copy already attached; absent when nothing needed fetching" }, { "key": "download_error", @@ -1211,7 +1211,7 @@ }, { "key": "deep_link", - "what": "view.html#element=... when --element-id was given" + "what": "view.html#element=... when `--element-id` was given" }, { "key": "history_items", @@ -1291,7 +1291,7 @@ "metavar": null, "required": false, "default": null, - "help": "URL-parsed items: fetch the document from its recorded URL into the job item and crop from that copy \u2014 the parse itself never gives the CLI the bytes (#169). This happens automatically when no copy is attached yet (a notice and progress line land on stderr); --no-download skips the fetch, and the crop then fails honestly (a crop has no empty-imagery fallback)." + "help": "URL-parsed items: fetch the document from its recorded URL into the job item and crop from that copy \u2014 the parse itself never gives the CLI the bytes. This happens automatically when no copy is attached yet (a notice and progress line land on stderr); `--no-download` skips the fetch, and the crop then fails honestly (a crop has no empty-imagery fallback)." } ], "supports_json": true, @@ -1323,7 +1323,7 @@ "what": "URL items: true when this run fetched the document into the parse item before cropping (absent when nothing needed fetching)" } ], - "note": "One shape whatever matched: a single --element-id is count 1 with one crops[] record; a filter (--type/--page/--all) matching nothing is count 0 with crops []." + "note": "One shape whatever matched: a single `--element-id` is count 1 with one crops[] record; a filter (`--type`/`--page`/`--all`) matching nothing is count 0 with crops []." }, "band": "local read models" } @@ -1475,7 +1475,7 @@ }, { "path": " meta.json", - "what": "commit record: kind, source, identity, params, state, timestamps, artifact index. Its job_id field is the server-side run id (= run_id in --json payloads), never the job item id" + "what": "commit record: kind, source, identity, params, state, timestamps, artifact index. Its job_id field is the server-side run id (= run_id in `--json` payloads), never the job item id" }, { "path": " job.json", diff --git a/src/ade_cli/crop.py b/src/ade_cli/crop.py index 192982d..2408f1b 100644 --- a/src/ade_cli/crop.py +++ b/src/ade_cli/crop.py @@ -199,9 +199,9 @@ def crop( None, "--download/--no-download", help="URL-parsed items: fetch the document from its recorded URL " "into the job item and crop from that copy — the parse itself " - "never gives the CLI the bytes (#169). This happens " + "never gives the CLI the bytes. This happens " "automatically when no copy is attached yet (a notice and " - "progress line land on stderr); --no-download skips the fetch, " + "progress line land on stderr); `--no-download` skips the fetch, " "and the crop then fails honestly (a crop has no empty-imagery " "fallback).", ), diff --git a/src/ade_cli/help.py b/src/ade_cli/help.py index cb51b1d..1965a6a 100644 --- a/src/ade_cli/help.py +++ b/src/ade_cli/help.py @@ -297,10 +297,10 @@ ("created_at / completed_at", "epoch seconds (null when unknown)"), ], "note": "Ordered newest submission first (timestamp-less items " - "last), matching the viewer sidebar; --asc restores oldest-first. " - "Capped at the newest 100 items by default — --limit N adjusts, " - "--all lifts the cap, and a capped run says so up front (the " - "first line of the listing; on stderr for --json). The --json " + "last), matching the viewer sidebar; `--asc` restores oldest-first. " + "Capped at the newest 100 items by default — `--limit` N adjusts, " + "`--all` lifts the cap, and a capped run says so up front (the " + "first line of the listing; on stderr for `--json`). The `--json` " "array follows the same order and cap.", }, "history clear": { @@ -472,7 +472,7 @@ "Store commands take a job item id or an unambiguous prefix. " "Discover ids with `history list`; ambiguous or unknown ids error " "with candidates listed. Distinct from the server-side run id: " - "--json payloads report that as run_id, and on-disk records spell " + "`--json` payloads report that as run_id, and on-disk records spell " "the same value job_id (the wire's name) — neither is ever a " "job item id.", ), @@ -516,7 +516,7 @@ "path": " meta.json", "what": "commit record: kind, source, identity, params, state, " "timestamps, artifact index. Its job_id field is the server-side " - "run id (= run_id in --json payloads), never the job item id", + "run id (= run_id in `--json` payloads), never the job item id", }, { "path": " job.json", diff --git a/src/ade_cli/history.py b/src/ade_cli/history.py index 958d66c..c420be4 100644 --- a/src/ade_cli/history.py +++ b/src/ade_cli/history.py @@ -46,7 +46,7 @@ "--limit", min=1, help=f"Keep only the newest N items (default {DEFAULT_LIST_LIMIT}), " - "whatever the order; --all lifts the cap.", + "whatever the order; `--all` lifts the cap.", ) LIST_ALL_FLAG = typer.Option( False, "--all", help="List every stored job item — no limit." @@ -140,7 +140,7 @@ def list_items( as_json: bool = JSON_FLAG, ) -> None: """List stored job items: id, kind, state, env, params, source — - the newest 100 submissions first (--limit/--all adjust, --asc for + the newest 100 submissions first (`--limit`/`--all` adjust, `--asc` for oldest first). Extract items referencing a parse item indent beneath it. Bare `ade history` defaults to this command.""" jobs = store.JobStore(ade_home()) diff --git a/src/ade_cli/parse.py b/src/ade_cli/parse.py index 9bdeb4c..50be9fd 100644 --- a/src/ade_cli/parse.py +++ b/src/ade_cli/parse.py @@ -154,7 +154,7 @@ def parse( ), keep_copy: bool = typer.Option( False, "--keep-copy", - help="--document-url only: also download the document into the " + help="`--document-url` only: also download the document into the " "job item (plain HTTP, no API credits) so page previews and " "crops render locally — fetched now, while the URL (often " "pre-signed) still works. Without it, the first `view`/`crop` " diff --git a/src/ade_cli/view.py b/src/ade_cli/view.py index f22aef6..3868447 100644 --- a/src/ade_cli/view.py +++ b/src/ade_cli/view.py @@ -991,10 +991,10 @@ def view( None, "--download/--no-download", help="URL-parsed items: fetch the document from its recorded URL " "into the job item and render page previews from that copy — " - "the parse itself never gives the CLI the bytes (#169). This " + "the parse itself never gives the CLI the bytes. This " "happens automatically when no copy is attached yet (a notice " - "and progress line land on stderr); --no-download skips the " - "fetch and previews stay empty. Explicit --download makes a " + "and progress line land on stderr); `--no-download` skips the " + "fetch and previews stay empty. Explicit `--download` makes a " "failed fetch an error instead of a warning. Plain HTTP, no API " "credits; the copy is unverified against the parsed run. Also " "works on an extract item id (fetches into its referenced parse "