@openpresentation/cli 0.17.1 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,21 +1,30 @@
1
1
  # @openpresentation/cli
2
2
 
3
- The `opf` command, for agents and people working with OPF presentations. Create documents, validate them, apply precise edits, paginate content, embed the catalog records a deck uses for offline use (`opf embed`), inspect the bundled schemas and the default catalog, render, export (PPTX, PDF, PNG, SVG), convert and import (PPTX) files. Node 22 or later on macOS, Linux, or Windows is required (`engines.node` `>=22` from the release after 0.10.0; 0.10.0 and earlier 24.x-only releases make npm on Node 22 or 26 silently install the 0.7.0 CLI, which has no `export` or `render` command).
3
+ The `opf` command, for agents and people working with OPF presentations. Create decks, validate them, apply precise edits,
4
+ convert them between forms and formats (JSON, YAML, Markdown, PDF, PPTX, PNG, SVG), paginate content, embed the catalog records a
5
+ deck uses for offline use, fill templates, ingest data, and inspect the bundled schemas, the default catalog and the example
6
+ decks. Node 22 or later on macOS, Linux, or Windows is required.
4
7
 
5
- The CLI depends on `@openpresentation/opf` (core), which holds the OPF schema, catalogs and validator and reads and writes the text formats, and whose `@openpresentation/opf/node` engine reads and writes files; the CLI is the command over it. It needs no API key or network connection at runtime. `opf --version` reports the CLI and the installed core versions. Validation and editing never render; successful validation is not visual verification. `opf render`, `opf export`, `opf convert` and `opf import` use the optional peers `@openpresentation/opf-render` and `@openpresentation/opf-pptx` (see [Render, export and import](#render-export-and-import)).
8
+ The CLI depends on `@openpresentation/opf` (core), which holds the OPF schema, catalogs and validator and reads and writes the text
9
+ formats, and whose Node build reads and writes files; the CLI is the command over it. It needs no API key or network connection at
10
+ runtime. `opf --version` reports the CLI and the installed core versions. Validation and editing never render; successful validation
11
+ is not visual verification. PDF, PPTX, PNG and SVG use the optional peers `@openpresentation/opf-render` and
12
+ `@openpresentation/opf-pptx` (see [Drawing and PowerPoint](#drawing-and-powerpoint)); `opf doctor` says what is installed.
6
13
 
7
14
  ## Install
8
15
 
9
- The CLI is published on npm. Install the current release:
10
-
11
16
  ```sh
12
17
  npm install -g @openpresentation/cli
13
18
  opf --version
19
+ opf --help # every command; opf <command> --help (or -h) for one
14
20
  ```
15
21
 
16
- Or install it as a development dependency and use `npx --no-install opf`. The command covers creating, validating, editing, paginating, embedding, linting, filling, rendering, exporting, converting and importing; `opf --help` lists it, and [the compatibility matrix](../../docs/compatibility-matrix.md) names the library releases that go with each CLI. Applications use core's file API instead of the command: see [From code](#from-code).
22
+ Or install it as a development dependency and use `npx --no-install opf`. [The compatibility matrix](../../docs/compatibility-matrix.md)
23
+ names the library releases that go with each CLI. Applications use core's functions instead of the command: see [From code](#from-code).
17
24
 
18
- To verify the standalone package from source, run `pnpm install` and `pnpm test:cli:packed`. This creates `artifacts/cli/openpresentation-cli-<version>.tgz` (with the core tarball it depends on), which can be installed using its absolute path. For source development, run `pnpm --filter @openpresentation/cli build` and `node packages/cli/dist/index.js --help`.
25
+ To verify the standalone package from source, run `pnpm install` and `pnpm test:cli:packed`. This creates
26
+ `artifacts/cli/openpresentation-cli-<version>.tgz` (with the core tarball it depends on), which can be installed using its absolute
27
+ path. For source development, run `pnpm --filter @openpresentation/cli build` and `node packages/cli/dist/index.js --help`.
19
28
 
20
29
  ## Install agent skills
21
30
 
@@ -25,40 +34,85 @@ npx @openpresentation/cli@latest skills status
25
34
  npx @openpresentation/cli@latest skills update
26
35
  ```
27
36
 
28
- CLI 0.5.0 and later install all six bundled skill folders into the current project's `.agents/skills`, including references, examples and inspection scripts. It requires no paid service, provider account or symlink privileges. Once the CLI is installed, these commands work offline.
29
-
30
- Use `--agent codex --global` for personal Codex skills, `--agent claude-code` or `--agent cursor` for those project skill directories, or `--directory <path>` for another compatible agent. The default `universal` target uses `.agents/skills`; its global target is `~/.agents/skills`. See [agent skill installation](https://github.com/OpenPresentation/opf/blob/main/docs/agent-skills.md) for the complete target mapping and update behavior.
31
-
32
- The installer preserves AGENTS.md and unrelated configuration. It checks all existing skills before making changes and refuses to overwrite locally modified or unmanaged folders, including manual copies. Repeating an unchanged install does nothing. Successful updates return backup paths outside the active skill directory. `status` compares against the invoked CLI's bundled version without a network lookup. Use the same target options for install, update and status.
37
+ The CLI installs all six bundled skill folders into the current project's `.agents/skills`, including references, examples and
38
+ inspection scripts. It requires no paid service, provider account or symlink privileges. Once the CLI is installed, these commands
39
+ work offline.
40
+
41
+ Use `--agent codex --global` for personal Codex skills, `--agent claude-code` or `--agent cursor` for those project skill
42
+ directories, or `--directory <path>` for another compatible agent. The default `universal` target uses `.agents/skills`; its global
43
+ target is `~/.agents/skills`. See [agent skill installation](https://github.com/OpenPresentation/opf/blob/main/docs/agent-skills.md)
44
+ for the complete target mapping and update behavior. The installer preserves AGENTS.md and unrelated configuration and refuses to
45
+ overwrite locally modified or unmanaged folders. `status` compares against the invoked CLI's bundled version without a network lookup.
46
+
47
+ ## The conventions every command shares
48
+
49
+ - **One output convention.** A command that writes a deck takes an optional output after its inputs and writes stdout without one
50
+ (`-` says the same). `-i` (`--in-place`) rewrites the input. `format`, `embed` and `paginate` take `--check`: write nothing,
51
+ exit 1 when something would change. An existing output needs `--force`.
52
+ - **Fixed flag meanings.** `--format` is a report format only (`json`, `text`, `github` for validate, `patch` for diff). `--from` and
53
+ `--to` name deck forms (`json`, `yaml`, `md`); a name ending `.json`, `.opf.yaml`, `.yml` or `.opf.md` says the form, stdin that
54
+ starts with `{` or `[` is JSON, and anything else needs `--from`. `--data-format csv|tsv|json` names the data of `fill` and `ingest`.
55
+ - **One report.** `{ command, ok, input, outputs: [{ file, sha256, bytes, mediaType }], findings, counts, ... }`, on stdout, or on
56
+ stderr when stdout carries the deck. Errors are `{ command, ok: false, code, error, ... }` on stderr, and the message states the
57
+ fix (a missing argument, the nearest command or flag: "Did you mean --check?", a removed flag's replacement).
58
+ - **Exit codes.** `0` success, possibly with warnings. `1` an invalid document, a failed patch test, a finding at or above
59
+ `--fail-on <error|warning|info>` (default `error`), an existing output, a merge conflict, or a `--check` that found a change.
60
+ `2` usage, text that is not valid JSON, YAML or Markdown, I/O, or a missing optional peer.
61
+ - No telemetry, automatic uploads, or execution of instructions inside document text. Use `--` before file names that start with `-`.
33
62
 
34
63
  ## Create and validate
35
64
 
36
65
  ```sh
37
66
  opf create decision.opf.json --title "Launch decision"
67
+ opf create brief.opf.md --example compliance-readiness-review # opf catalog examples lists them
68
+ opf create - --title "Launch decision" | opf validate -
38
69
  opf validate decision.opf.json
39
- opf create copy.opf.json --from decision.opf.json
40
70
  ```
41
71
 
42
- Creation supplies a minimal deck with one stable slide ID, `slide-1`. `--title` sets both deck name and visible slide title. `--from` accepts a complete OPF document and preserves its fields; use it to supply arbitrary content, assets, and design options. `--title` and `--from` cannot be combined.
72
+ Creation supplies a minimal deck with one stable slide ID, `slide-1`; `--title` sets both deck name and visible slide title.
73
+ `--example <slug>` copies one of core's bundled example decks. To copy a deck into another form, use `opf convert`.
43
74
 
44
- Use `-` for stdin/stdout. Creation defaults to stdout if no destination is supplied:
75
+ `opf validate` is the one checker. It reads each file as strict JSON text (or YAML, or a Markdown deck, by its name or `--from`) and
76
+ prints the report (`valid`, `schemaValid`, `findings`, `counts`, `checks`), the input's SHA-256 digest and the core version. Each
77
+ finding has a stable `opf/<rule>` id, a severity, a category (`format`, `references`, `policy`, `accessibility`, `layout`,
78
+ `content`), a JSON Pointer path, the source line and column, a hint and sometimes suggested fixes. Only format, references and
79
+ policy findings are errors by default, so `valid` keeps meaning "correct OPF".
45
80
 
46
81
  ```sh
47
- opf create - --title "Launch decision" | opf validate -
48
- opf create imported.opf.json --from - < authored.opf.json
82
+ opf validate deck.opf.json # everything, JSON report
83
+ opf validate deck.opf.json --format text # one line per finding
84
+ opf validate decks/ --format github --fail-on warning # every *.opf.* file in a folder, as CI annotations
85
+ opf validate a.opf.json b.opf.md - # several inputs: { command, ok, files, counts }
86
+ opf validate deck.opf.json --only format,references --ignore layout --config house-rules.json
87
+ opf validate --list-rules
49
88
  ```
50
89
 
51
- `opf validate` is the one checker: it reads the file as strict JSON text (or as YAML for a name ending `.yaml`/`.yml` or `--input-format yaml`, or as a Markdown deck for a name ending `.opf.md` or `--input-format markdown`) and prints the report (`valid`, `schemaValid`, `findings`, `counts`, `checks`), the input's SHA-256 digest and the core version. Each finding has a stable `opf/<rule>` id, a severity, a category (`format`, `references`, `policy`, `accessibility`, `layout`, `content`), a JSON Pointer path, the source line and column, a hint and sometimes suggested fixes. Only format, references and policy findings are errors by default, so `valid` keeps meaning "correct OPF"; accessibility, layout and content findings are warnings and info. A content reference that resolves nowhere is the warning `opf/unresolved-reference`; a `name:id` prefix that names no catalog group is the error `opf/undeclared-catalog`.
90
+ A folder is searched for `*.opf.json`, `*.opf.yaml`, `*.opf.yml` and `*.opf.md` (sorted, skipping `node_modules` and dot folders).
91
+ The exit status is the worst over every file. `--only` and `--ignore` take comma-separated rule ids, bare names or category names,
92
+ and may be repeated. `--config` is an explicit local JSON file of `{ catalogs, contracts, severity, only, ignore, ignorePaths,
93
+ thresholds, chartPalette }`. Validation is read-only, fetches nothing and never rewrites the file. See [the validate guide](../../docs/validate.md).
94
+
95
+ ## Convert
96
+
97
+ `opf convert <input> <output>` is every change of format, the formats named by the extensions or by `--from` and `--to`:
52
98
 
53
99
  ```sh
54
- opf validate deck.opf.json # everything, JSON report
55
- opf validate deck.opf.json --format text # one line per finding
56
- opf validate deck.opf.json --only format,references --fail-on warning
57
- opf validate deck.opf.json --ignore layout --config house-rules.json
58
- opf validate --list-rules
100
+ opf convert deck.opf.md deck.pdf
101
+ opf convert deck.opf.md slides/deck.png --slides 1,3-5 --scale 2 # slides/deck-001.png, -003, -004, -005
102
+ opf convert deck.pptx deck.opf.md --signals signals.json
103
+ opf convert outline.md deck.opf.yaml # a plain .md input is OPF Markdown
104
+ opf convert deck.opf.json deck.opf.yaml --schema-comment
105
+ opf convert deck.opf.md - --to pdf > deck.pdf
59
106
  ```
60
107
 
61
- `--only` and `--ignore` take comma-separated rule ids (`opf/text-contrast`), bare names (`text-contrast`) or category names, and may be repeated. `--fail-on <error|warning|info>` picks the exit threshold (default `error`); it is the one way to say "fail on warnings" (`--fail-on warning`) on every command that checks a document. `--config` is an explicit local JSON file of `{ catalogs, contracts, severity, only, ignore, ignorePaths, thresholds, chartPalette }`: host policy and loaded catalog records, never read from the document, which can only carry data. Exit `0`: nothing at or above `--fail-on`. Exit `1`: findings at or above it, or text that is not valid JSON (an `opf/json-syntax` finding). Exit `2`: usage, configuration or I/O error. Validation is read-only, fetches nothing and never rewrites the file. See [the validate guide](../../docs/validate.md).
108
+ Inputs: `.json`, `.opf.yaml`/`.yml`, `.opf.md` and a plain `.md`, `.pptx`, or `-`. Outputs: a deck form, `.pdf`, `.pptx`, `.png` or
109
+ `.svg` (one file per slide beside the output; one selected slide is written to the output itself), `.zip` (the slides as PNG, or
110
+ SVG with `--to svg`), or `-` with `--to`. A flag applies only where its format is involved, else it is a usage error: `--split` and
111
+ `--title` for a Markdown input, `--aliases` for YAML, `--signals` for a `.pptx` input, `--schema-comment` for a YAML output,
112
+ `--drop-unsupported` for a Markdown output, and `--slides`, `--include-hidden`, `--scale`, `--raster`, `--text`, `--charts`,
113
+ `--images`, `--provenance`, `--paginate`, `--date`, `--fonts` and `--asset-dir` for drawn and PowerPoint outputs. `opf convert --help`
114
+ lists which format each belongs to. Removed in 0.18: `render`, `export`, `import`, `from-md`, `to-md`, `from-yaml`, `to-yaml` and
115
+ `create --from`, all `opf convert` now. See [docs/cli.md](https://github.com/OpenPresentation/opf/blob/main/docs/cli.md).
62
116
 
63
117
  ## Edit with JSON Patch
64
118
 
@@ -68,7 +122,6 @@ Create `changes.json`:
68
122
  [
69
123
  { "op": "test", "path": "/slides/0/id", "value": "slide-1" },
70
124
  { "op": "replace", "path": "/slides/0/title", "value": "Approve the next milestone" },
71
- { "op": "add", "path": "/slides/0/text", "value": "Describe the evidence and requested decision here." },
72
125
  { "op": "add", "path": "/slides/-", "value": { "id": "next-steps", "title": "Next steps" } }
73
126
  ]
74
127
  ```
@@ -76,73 +129,68 @@ Create `changes.json`:
76
129
  Preview the result, save a separate file, or update the source:
77
130
 
78
131
  ```sh
79
- opf edit decision.opf.json --patch changes.json --dry-run
80
- opf edit decision.opf.json --patch changes.json --output reviewed.opf.json
81
- opf edit decision.opf.json --patch changes.json --in-place
82
- ```
83
-
84
- The editor implements [JSON Patch (RFC 6902)](https://www.rfc-editor.org/rfc/rfc6902): `add`, `remove`, `replace`, `move`, `copy`, and `test`. Paths are JSON Pointers: `~1` escapes `/`, `~0` escapes `~`, and the empty path addresses the whole document. `add` can insert into arrays or append with `-`; `replace` requires an existing target. It preserves unrelated fields and validates the complete result before writing. An invalid input document can be repaired as long as the final document validates. Failed operations or validation leave the file untouched.
85
-
86
- With no destination, editing writes the resulting document to stdout. Either the source or patch can come from stdin, but not both. `--dry-run` always writes the candidate document to stdout without saving, even with `--in-place` or `--output`.
87
-
88
- Use `test` to guard specific values. Resolve a slide ID to its current array index before constructing edits. For a whole-file guard, pass the digest from an earlier `opf validate` result:
89
-
90
- ```sh
91
- opf edit decision.opf.json --patch changes.json --in-place --expect-sha256 "$EXPECTED_SHA256"
132
+ opf edit decision.opf.json --patch changes.json # stdout; nothing is written
133
+ opf edit decision.opf.json reviewed.opf.json --patch changes.json
134
+ opf edit decision.opf.json --patch changes.json -i --expect-sha256 "$EXPECTED_SHA256"
92
135
  ```
93
136
 
94
- The digest compares the exact input bytes, including whitespace. The CLI also rechecks the input before replacing that same path. Writes use a temporary sibling file and atomic publication. Existing destinations require `--force`; `--in-place` explicitly authorizes replacing the input. Symlink and non-regular destinations are rejected. Coordinate concurrent writers externally: the hash check and rename are not a filesystem compare-and-swap or a collaboration lock. There is no persistent undo history; use version control or save a separate output when needed.
137
+ The editor implements [JSON Patch (RFC 6902)](https://www.rfc-editor.org/rfc/rfc6902): `add`, `remove`, `replace`, `move`, `copy`,
138
+ and `test`, applied whole or not at all; the patch is JSON or YAML. The result must pass the format and references check before it
139
+ is written, and the report carries `inverse`, the patch that undoes the edit. `--expect-sha256` takes the digest from an earlier
140
+ `opf validate` report and refuses an input that changed. Writes use a temporary sibling file and atomic publication; symlink and
141
+ non-regular destinations are rejected. There is no persistent undo history; use version control or save a separate output.
95
142
 
96
143
  ## Diff, merge and format
97
144
 
98
- Added in CLI 0.10.0 (RR-31; not in 0.9.2 or earlier): check `opf --help`. These commands are local and deterministic. See [patch, diff, merge and format](../../docs/patch-diff-merge-format.md) for the matching rules, conflict objects and key order.
99
-
100
145
  ```sh
101
146
  opf diff before.opf.json after.opf.json # readable report
102
147
  opf diff before.opf.json after.opf.json --format patch # JSON Patch from before to after
103
148
  opf diff a.opf.json b.opf.json --exit-code # exit 1 when they differ
104
- opf merge base.opf.json ours.opf.json theirs.opf.json --output merged.opf.json
105
- opf merge base.opf.json ours.opf.json theirs.opf.json --prefer theirs --report conflicts.json --output merged.opf.json
106
- opf format deck.opf.json --in-place
107
- opf format decks/*.opf.json --check # CI: exit 1 if any file would change (the shell expands the glob)
108
- ```
109
-
110
- `diff` matches slides by `id`, then identical content, then content similarity, and reports additions, removals, moves and field, block, design and metadata changes; `--format json` adds the structured changes and `--format patch` prints only the patch, which `opf edit --patch` applies. `merge` combines two edits of a base: changes in different places merge, and conflicts are listed with the base, our and their values and **block the write** (exit 1, report on stderr) unless `--prefer ours|theirs` picks a side, in which case every conflict is still reported. The merged document is validated before it is written. `format` rewrites a file with canonical key order (the schema's property order), two-space indentation, LF endings (`--eol crlf|preserve`) and one trailing newline; it is idempotent and does not validate. `opf edit`, `diff` and `merge` share one RFC 6902 implementation with the editor.
111
-
112
- ## Facts about a deck
113
-
114
- ```sh
115
- opf stats deck.opf.json # JSON
116
- opf stats deck.opf.json --format text # a readable block per topic
117
- opf stats - --per-slide < deck.opf.json # adds a row per slide
149
+ opf merge base.opf.json ours.opf.json theirs.opf.json merged.opf.json
150
+ opf merge base.opf.json ours.opf.json theirs.opf.json -i --prefer theirs --report conflicts.json
151
+ opf format deck.opf.json -i
152
+ opf format --check decks/*.opf.json # CI: exit 1 if any file would change
118
153
  ```
119
154
 
120
- `opf stats <file|-> [--format <json|text>] [--per-slide]` reports neutral facts: slides, hidden slides and sections, layouts, payload kinds, words in content and in notes, notes coverage, images with and without alt text, charts, tables, datasets, citations, variables, assets, header and footer, fonts, colour variables and an estimated speaking time from the notes. It never validates, composes or loads fonts and never rates anything (no severities or thresholds), so it works on a deck that fails `opf validate`. Not a JSON document the other commands read: invalid JSON exits 2 and a JSON value that is not an object exits 1. See [the stats guide](../../docs/stats.md).
155
+ `diff` matches slides by `id`, then identical content, then content similarity. `merge` combines two edits of a base; conflicts block
156
+ the write (exit 1) unless `--prefer ours|theirs` picks a side, and every conflict is reported. `format` rewrites a deck in canonical
157
+ key order and layout, keeping its form (`opf convert` changes forms); it is idempotent. See
158
+ [patch, diff, merge and format](../../docs/patch-diff-merge-format.md).
121
159
 
122
- ## Import CSV and JSON data
160
+ ## Facts, pagination and embedding
123
161
 
124
162
  ```sh
125
- opf import-data revenue.csv --as table --output table.opf.json
126
- opf import-data revenue.json --as chart --chart-type line --output chart.opf.json
127
- opf import-data revenue.csv --as chart --category Quarter --series '["Revenue","Costs"]' --into decision.opf.json --in-place
163
+ opf stats deck.opf.json --format text --per-slide
164
+ opf paginate deck.opf.json paginated.opf.json
165
+ opf paginate deck.opf.json --check # CI: exit 1 when a slide overflows
166
+ opf embed deck.opf.json -i
167
+ opf embed deck.opf.json --check # CI: exit 1 when the deck is not self-contained
128
168
  ```
129
169
 
130
- Data can come from a file or stdin (`-`). JSON accepts arrays of records, row matrices, or `{columns, rows}`. `--path /slides/0/table` replaces or adds a table field inside an existing parent; use `/chart` for charts. With `--into` and no path, a new slide is appended. Other options include `--format csv|tsv|json`, `--delimiter`, `--no-header`, `--columns` (a JSON array), and `--title`. Preview on stdout by omitting an output destination. All file writes validate the complete document. CSV table strings are preserved; chart measures must be numeric (strict decimal syntax: `12%` or `$5` is rejected). `--dataset <id>` writes the data into the deck's top-level `datasets` map (replacing that dataset's columns and rows, keeping the format of each column whose name is unchanged, and recording the file as its `source`) and references it from the table or chart, so several tables and charts can share it. Data is embedded, not linked to the source file.
131
-
132
- Import is deterministic. A new slide is named `data-` plus eight hex digits of a SHA-256 over the data and the import options (`data-xxxxxxxx-2`, `-3` if the deck already has the id); `--id <slideId>` names it instead and fails if the id is taken. With `--dataset`, `source.src` is the data file relative to the deck's folder with `/` separators, and `source.retrieved` is set only by `--date YYYY-MM-DD` (the command never reads the clock).
133
-
134
- ## Fill a template
170
+ `stats` reports neutral facts (slides, sections, layouts, words, notes, images, charts, tables, datasets, citations, variables,
171
+ assets, fonts, an estimated speaking time) under `stats` in the report; it never validates, composes or rates anything. `paginate`
172
+ splits overflowing slides into continuation slides, measured with the fonts `opf convert` draws with when opf-render is installed
173
+ (`layout: "measured"`), else estimated (`layout: "estimated"`, with a `hint` that names the install command). `embed` copies every
174
+ catalog record the deck references, transitively, into it once, so the file renders the same with no catalog registered.
135
175
 
136
- A template is an OPF file with `"template": true` and variables (`{{id}}` tokens and `var:id` references, see [templates and variables](../../docs/templates-and-variables.md)). `opf fill` resolves them from data:
176
+ ## Fill a template and ingest data
137
177
 
138
178
  ```sh
139
- opf fill quarterly.opf.json --data globex.json --output globex.opf.json # one JSON object, one deck
140
- opf fill quarterly.opf.json --data clients.csv --out-dir decks --name "qbr-{client}" # one deck per row
141
- opf fill quarterly.opf.json --data clients.csv --combine --output all.opf.json # one deck, a slide group per row
142
- opf fill quarterly.opf.json --examples --output preview.opf.json # fill from each variable's example
179
+ opf fill quarterly.opf.json globex.json globex.opf.json # one JSON object, one deck
180
+ opf fill quarterly.opf.md clients.csv decks/qbr-{client}.opf.md # one deck per row, named by a column
181
+ opf fill quarterly.opf.json clients.csv all.opf.json --combine # one deck, a slide group per row
182
+ opf fill quarterly.opf.json --examples > preview.opf.json # no data: fill from each variable's example
183
+ export-rows | opf fill quarterly.opf.json - decks/deck-{n}.opf.json # data from stdin
184
+ opf ingest revenue.csv table.opf.json --as table
185
+ opf ingest revenue.csv --as chart --category Quarter --series '["Revenue","Costs"]' --into decision.opf.json -i
143
186
  ```
144
187
 
145
- CSV and TSV headers match variable ids and cells coerce per kind (numbers, ISO dates, newline-separated lists); JSON also carries rich text, lists and image objects. A blank cell keeps the declared value. An unfilled required variable or a value of the wrong kind exits 1 before any file is written; `--partial` allows unfilled variables and keeps their declarations for a later pass. `opf validate` accepts a template and reports its unfilled variables.
188
+ `fill`'s second argument is always the data, whatever its name (`-` reads it from stdin), and the third is the output: a deck name,
189
+ or a file name pattern where `{n}` is the record number and `{column}` a slug of that column's value. A blank
190
+ cell keeps the declared value; an unfilled required variable fails unless `--partial`. `ingest` turns CSV, TSV or JSON rows into a
191
+ table or chart, alone or added `--into` a deck (`--path` replaces one); `--dataset <id>` stores the rows in the deck's datasets. The
192
+ same inputs give the same slide id and bytes. See [templates and variables](../../docs/templates-and-variables.md) and
193
+ [data import](../../docs/data-import.md).
146
194
 
147
195
  ## Discover format options
148
196
 
@@ -152,81 +200,61 @@ opf schema presentation '/$defs/Composition'
152
200
  opf catalogs
153
201
  opf catalog layouts
154
202
  opf catalog fontSchemes roboto
203
+ opf catalog examples
155
204
  ```
156
205
 
157
- `schemas` and `catalogs` list available names/kinds. `schema` returns the whole schema or a branch addressed by a pointer into the schema. `catalog` returns the records in a kind, keyed by id, or one exact ID. They inspect the default catalog of the core it runs on (the full pptx.gallery catalog) and do not fetch galleries.
206
+ They inspect the default catalog of the core the CLI runs on (the full pptx.gallery catalog) and the bundled example decks, and print
207
+ the data asked for. Nothing is fetched.
158
208
 
159
- ```sh
160
- opf paginate decision.opf.json paginated.opf.json
161
- ```
209
+ ## Drawing and PowerPoint
162
210
 
163
- Pagination emits ordinary OPF slides and a page mapping. Preview the result with the renderer to assess wrapping and visual fidelity.
211
+ PDF, PNG and SVG need `@openpresentation/opf-render`; PPTX output and a `.pptx` input also need `@openpresentation/opf-pptx`. They
212
+ are **optional peer dependencies**, loaded the first time a command needs them (beside the CLI first, then in the working
213
+ directory), so the CLI stays small. `opf doctor` reports, per format, whether this install can write it and prints the one command
214
+ that installs what is missing, for the package manager in use (npm, pnpm, yarn or bun) and the way the CLI is installed (global, a
215
+ project, or npx):
164
216
 
165
217
  ```sh
166
- opf embed decision.opf.json embedded.opf.json
218
+ opf doctor --format text
219
+ opf doctor deck.opf.md # with a deck: a PDF needs sharp only when it has pictures
167
220
  ```
168
221
 
169
- Embed copies every catalog record the document references — including transitive references such as a theme's color and font schemes — into the document, once, in the group it resolves in (`catalogs.default` for the default catalog, with its `source`), so the file renders the same with no catalog registered. Records already embedded are kept, and the report lists the added records and the references that resolve nowhere. Remote media and data assets (`https:` images, chart `data.src` URLs) are not inlined. Embedding twice is a no-op. Every CLI command registers the default catalog (the pinned pptx.gallery snapshot); a validate configuration's `catalogs` registers more, first.
170
-
171
- ## Agent output contract
172
-
173
- - Reports and errors are JSON; only help text is plain text.
174
- - `validate` reports to stdout, including for invalid documents.
175
- - Commands emitting a document to stdout send their findings to stderr, so pipes remain valid JSON.
176
- - File-writing commands report the absolute output path, output SHA-256, and `findings` (format and references only) to stdout.
177
- - Exit `0`: success, possibly with warnings. Exit `1`: invalid document, failed patch/test, a finding at or above `--fail-on`, or file conflict. Exit `2`: usage, malformed JSON input to a command other than `validate`, or I/O failure.
178
- - Commands that check a document before writing it take `--fail-on <error|warning|info>` (default `error`). Use `--` before positional filenames that start with `--`.
179
- - No telemetry, automatic uploads, or execution of instructions inside document text.
180
-
181
- ## Markdown and outlines
222
+ A conversion that misses a peer exits 2 with `code: "peer-not-installed"` and that one command in `install`. Output is
223
+ deterministic: no network, no system fonts, no clock (`--date` supplies the date for `date: true` fields). Fonts are the renderer's
224
+ bundled open pack plus the `.ttf`/`.otf` files in each `--fonts` folder; relative images are read only from the input's folder
225
+ (`--asset-dir`); URLs are never fetched. Scripts beyond Latin, Greek and Cyrillic need the renderer's optional Noto script packages
226
+ (the report names them). The full reference is in [docs/cli.md](https://github.com/OpenPresentation/opf/blob/main/docs/cli.md).
182
227
 
183
- `opf from-md <deck.md|-> [output.opf.json|output.opf.yaml|-] [--split <rules|headings>] [--title <text>] [--force] [--fail-on <level>]` converts Markdown in the OPF dialect (YAML front matter, `---` between slides, `#` title, lists, quotes, tables, `chart`, `metric` and `timeline` fences, `Note:` notes, `<!-- slide: ... -->` options) to a validated deck, and `opf to-md <deck.opf.json|-> [output.md|-] [--drop-unsupported] [--force] [--fail-on <level>]` writes a deck as that Markdown, which `from-md` reads back unchanged. Both print JSON reports and follow the exit codes above; Markdown findings carry `line` and `column`. Not in releases before the one that lists it in the changelog. See the [Markdown guide](../../docs/markdown.md).
228
+ ## Markdown and YAML
184
229
 
185
- ### Markdown decks in every command
186
-
187
- JSON stays the canonical form of a deck; YAML and Markdown are authoring forms of the same data, and every command that reads or writes a deck reads and writes all three. A file ending `.opf.md` (any case) is a Markdown deck: `opf validate deck.opf.md`, `opf render deck.opf.md`, `opf export deck.opf.md --format pptx`, `opf edit deck.opf.md --patch changes.json --in-place`, `opf format deck.opf.md` (canonical Markdown; `--check` works), `opf paginate`, `opf fill`, `opf merge`, `opf diff`, `opf stats`. Only `.opf.md` counts: a plain `.md` file is never taken for a deck. Stdin and other names are JSON unless `--input-format markdown` (alias `md`) says otherwise. A Markdown syntax error exits 2 with `line:column` in the message and located findings under `markdown` in the error report, except in `opf validate`, which reports it as a `markdown/<rule>` finding (exit 1); `validate`, `render` and `export` report their findings at Markdown lines. A deck is written as Markdown when the output name ends `.opf.md` or with `--format markdown` (`md`); without either, a command writes the format it read (`create`, `import` and `from-md` have no deck input and default to JSON), and a rewrite that has to put content into an `opf-slide`/`opf-block` fence prints a warning on stderr. `from-md` and `to-md` stay explicit conversions with any file names. See [Markdown decks in every command](../../docs/markdown.md#markdown-decks-in-every-command).
188
- ## OPF as YAML
189
-
190
- A deck can be written as YAML (`deck.opf.yaml`) and every command that reads a deck or a JSON Patch reads it: a file ending `.yaml` or `.yml` is YAML (strict JSON-compatible YAML 1.2, no anchors, tags or duplicate keys); stdin and other names are JSON unless `--input-format yaml` (or `markdown`) says otherwise. A YAML syntax error exits 2 with the line and column in the message, like invalid JSON, except in `opf validate`, which reports it as a `yaml/<rule>` finding (exit 1); `validate`, `render` and `export` report their findings at YAML lines. A deck is written as YAML when the output name ends `.yaml`/`.yml` or with `--format yaml` (`create`, `edit`, `merge`, `format`, `paginate`, `embed`, `fill`, `import-data`, `from-md`, `import`); without either, a command writes the format it read, and JSON when it read none.
191
-
192
- `opf from-yaml <deck.yaml|-> [output.opf.json|-] [--aliases] [--force] [--fail-on <level>]` converts YAML to a validated JSON deck (`--aliases` expands anchors, aliases and merge keys, capped at 100 aliases), and `opf to-yaml <deck.opf.json|-> [output.opf.yaml|-] [--schema-comment] [--force]` writes canonical YAML that `from-yaml` reads back unchanged; `--schema-comment` adds the `# yaml-language-server: $schema=...` line that gives editors validation and completion. Both print JSON reports and follow the exit codes above; YAML findings carry `line` and `column`. Commands that rewrite a YAML file do not preserve its comments and print a warning on stderr. Settings files (`--config`), `fill --data` and `import-data` sources stay JSON, CSV or TSV. See the [YAML guide](../../docs/yaml.md).
230
+ JSON stays the canonical form of a deck; YAML (`deck.opf.yaml`) and Markdown (`deck.opf.md`) are authoring forms of the same data,
231
+ and every command that reads or writes a deck reads and writes all three. A syntax error exits 2 with `line:column` (`opf validate`
232
+ reports it as a finding, exit 1). A command that rewrites a YAML file does not keep its comments, and one that has to put content
233
+ into an `opf-slide`/`opf-block` fence, says so on stderr. See the [Markdown guide](../../docs/markdown.md) and the [YAML guide](../../docs/yaml.md).
193
234
 
194
235
  ## From code
195
236
 
196
- The CLI is the command. An application reads, writes, converts and exports files with core's `@openpresentation/opf/node`, the engine the `convert`, `render`, `export` and `import` commands run, so the command and the code give the same bytes:
237
+ The CLI is the command. An application calls core's `@openpresentation/opf` instead, the functions the commands run, so the command
238
+ and the code give the same result. In Node, `validate`, `stats`, `paginate`, `embed` and `edit` register the default catalog when a
239
+ call names none, as the commands do:
197
240
 
198
241
  ```ts
199
- import * as opf from "@openpresentation/opf/node";
242
+ import * as opf from "@openpresentation/opf";
200
243
 
201
244
  await opf.convert("deck.opf.md", "deck.pdf");
202
245
  const deck = await opf.open("deck.opf.md");
203
- deck.slides.push({ title: "Q4" });
204
- await opf.save(deck, "deck.opf.md");
205
- const { files } = await opf.convert(deck, { format: "pptx" }); // bytes, nothing written
206
- ```
207
-
208
- Install `@openpresentation/opf` and the engines your outputs use (the same packages as for the command, below). See [OPF files in Node](https://github.com/OpenPresentation/opf/blob/main/docs/node.md) for the options, the file naming and the errors.
209
-
210
- ## Render, export and import
211
-
212
- ```sh
213
- npm install -g @openpresentation/cli @openpresentation/opf-render @openpresentation/opf-pptx @resvg/resvg-js sharp pdf-lib @expo-google-fonts/roboto@0.4.3 @expo-google-fonts/roboto-mono@0.4.2 @expo-google-fonts/caladea@0.4.2 @expo-google-fonts/arimo@0.4.3 @expo-google-fonts/tinos@0.4.2 @expo-google-fonts/cousine@0.4.3 @expo-google-fonts/gelasio@0.4.1 @expo-google-fonts/noto-sans@0.4.2
214
- opf render deck.opf.json --slides 1,3-5 --format png --scale 2 --out slides
215
- opf export deck.opf.json --format pptx # deck.pptx
216
- opf export deck.opf.json --format pdf --pdf-mode vector
217
- opf export deck.opf.json --format svg --out slides.zip
218
- opf import deck.pptx --out deck.opf.json --signals signals.json
219
- opf convert deck.opf.md deck.pdf # the formats come from the names
220
- opf convert deck.opf.md slides/deck.png # slides/deck-001.png, -002, ...
221
- opf convert deck.pptx deck.opf.yaml
246
+ const { presentation } = opf.edit(deck, [{ op: "replace", path: "/name", value: "Q4" }]);
247
+ await opf.save(presentation, "deck.opf.md");
248
+ const { decks } = opf.fill(template, csvText);
222
249
  ```
223
250
 
224
- These commands write files; every other command only prints JSON. They check the document's format and references first and print the `opf validate` report (`findings`, `counts`, exit 1 on errors, or on findings at or above `--fail-on`, in which case nothing is written) plus an `outputs` list with each file's SHA-256. Output is deterministic: no network, no system fonts, no clock (`--date` supplies the date for `date: true` fields). Fonts are the renderer's bundled open pack plus the `.ttf`/`.otf` files in each `--font-dir`; relative images are read only from the document's folder (`--asset-dir`); URLs are never fetched. Existing outputs need `--force`. `render` and `export` take the format from `--out`'s extension (`--out deck.png` is PNG) and refuse an `--out` with another file extension.
225
-
226
- `opf convert <input> <output>` turns a `.pptx`, `.opf.md`, `.yaml`/`.yml` or `.json` file into a `.pdf`, `.pptx`, `.png`, `.svg`, `.zip`, `.opf.md`, `.yaml`/`.yml` or `.json` file, the formats named by the extensions, with the flags, report and exit codes of `opf export`. PNG and SVG give one file per slide beside the output (`slides/deck.png` writes `slides/deck-001.png`, ...; one selected slide is written to the output itself), a `.zip` holds the slides (`--format png`, the default, or `svg`), and a deck output writes the deck in that form. Images resolve next to the input file unless `--asset-dir`. It is `convert` of `@openpresentation/opf/node`.
227
-
228
- opf-render and opf-pptx are **optional peer dependencies**, loaded the first time a command needs them (beside the CLI first, then in the working directory), so the CLI stays small and dependency-free; a missing peer, or one that lacks `loadFonts`, `renderSlideSvg` or the other names of its API, exits 2 (the declared range is `^0.16.0`) with the install command. Scripts beyond Latin, Greek and Cyrillic need the renderer's optional Noto script packages (the report names them). From code, the same engine is `@openpresentation/opf/node` ([From code](#from-code)). The full reference, the report fields and the decisions are in [docs/cli.md](https://github.com/OpenPresentation/opf/blob/main/docs/cli.md).
251
+ See [OPF files in Node](https://github.com/OpenPresentation/opf/blob/main/docs/node.md) for the options and the errors.
229
252
 
230
253
  ## Development checks
231
254
 
232
- `pnpm test:cli` runs command-level regression checks, including `test/files.mjs` for render, export and import against the workspace's pinned opf-render and opf-pptx (set `UPDATE_GOLDEN=1` to refresh the pinned SVG digests after a renderer or core bump). `pnpm test:cli:packed:peers` installs the packed CLI with both peers from the npm registry and repeats those checks against the installed binary and through `npm exec`. `pnpm test:cli:packed` builds and packs the CLI, installs the tarball and the core tarball it depends on into an isolated global prefix, exercises the actual executable, asserts that the installation holds one core, and reruns the same checks against the installation. It does not change your global installation. `test/convert.test.mjs` covers `opf convert` and the `--out` rule; core's `test/node.test.mjs` covers the engine. Rebuild the CLI after a core change; the workspace links the CLI to the core in `packages/javascript`.
255
+ `pnpm test:cli` runs the command tests (`test/*.test.mjs` and `test/cli.mjs`), including `test/files.mjs` for drawing and
256
+ PowerPoint against the published opf-render and opf-pptx (set `UPDATE_GOLDEN=1` to refresh the pinned SVG digests after a renderer
257
+ or core bump). `pnpm test:cli:packed:peers` installs the packed CLI with both peers and repeats those checks against the installed
258
+ binary. `pnpm test:cli:packed` packs the CLI, installs the tarball and the core tarball it depends on into an isolated global prefix,
259
+ exercises the executable, asserts that the installation holds one core, and reruns `test/cli.mjs` against it. Rebuild the CLI after
260
+ a core change; the workspace links the CLI to the core in `packages/javascript`.