---
name: genoffice
description: Create, convert, read and edit Office documents locally with GenOffice's command line. Build a new presentation (pptx) from a brief through a checked outline, per-page spec and render pipeline, a new spreadsheet (xlsx) from CSV or JSON data with formulas, a new Word document (docx) from Markdown or HTML, or a PDF; convert between pdf, docx, xlsx, pptx, md, html and csv; read the structure and text of an existing file (including the user's own docx / xlsx / pptx as source material) and apply structured edits to it. Use whenever the user asks for a slide deck, presentation, spreadsheet, workbook, report, Word document or any real Office file, a format conversion, a rewrite of part of an existing document, or wants the result opened in the GenOffice editor. Documents are processed locally; only search, image and media send the query or the referenced file to the provider configured in GenOffice.
metadata:
  version: 2.67.1
  cli: '>=0.9.0'
---

# genoffice — GenOffice from the terminal

`genoffice` is installed with GenOffice. Run `genoffice --version` first. If the command is not found, GenOffice writes the launcher directory to `~/.genoffice/launcher` (Windows: `%USERPROFILE%\.genoffice\launcher`) on every start: read that one line and run `"<dir>/genoffice"` from bash or `"<dir>\genoffice.cmd"` from PowerShell / cmd, quoted, in place of `genoffice` below. Default locations when that file is missing: macOS `/Applications/GenOffice.app/Contents/Resources/cli`, Windows `%LOCALAPPDATA%\Programs\GenOffice\resources\cli`, Linux `/opt/GenOffice/resources/cli`. Every command runs headless on the app's own engines; only `search`, `image` and `media` send data off the machine, to the provider configured in GenOffice.

Always add `--json` when a program reads the output: one JSON object on stdout, `{ "status": "ok" | "partial", "command", "summary", "output_path"?, "warnings"?, "detail"? }` (`partial`: an `apply` with `--best-effort` / `--stop-on-error` wrote the file but lost ops) or `{ "status": "error", "code", "error", "message", "suggestion"?, "detail"? }`. Exit codes: `0` ok, `1` usage or rejected ops, `2` file not found / output exists / file open in GenOffice, `3` conversion failed, `4` app not available. `error` is the machine-readable reason (table under "When a command fails"), `suggestion` the next step in one sentence, including `did you mean "…"?` when a command, option, op name, element id or sheet name is one typo away. `detail` carries facts as fields (`failures[]` with `index`, `op`, `error`, `reason`, `valid_range`, `available`, `supported`, `usage`; `sheets`, `options`, `commands`); never parse `message`.

## Commands

| Command                                                                                                                                                                                                                                                          | What it does                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `genoffice info <file> [--password <pw>]`                                                                                                                                                                                                                        | Metadata and structure summary (docx blocks and headings, pptx slides, xlsx sheets, pdf pages)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `genoffice convert <file> --to <fmt> [--out <path>] [--force]`                                                                                                                                                                                                   | `pdf→docx/pptx/xlsx`, `csv→xlsx`, `xls/xlsb/ods→xlsx`, `md→docx/html`, `docx→html/md`, `html→docx`, `xlsx→csv` (`--sheet`), and `docx/xlsx/pptx/md/html→pdf`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `genoffice create --type pptx --ops <file> --out <path>`                                                                                                                                                                                                         | New deck from a list of slide ops                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `genoffice create --type pptx --spec <dir\|file> [--outline <file>] --out <path> [--render [dir]] [--audit]`                                                                                                                                                     | New deck from page spec files (one per slide, name order) or one deck spec file: px-positioned text, shapes and images (the designed-deck path, see below)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `genoffice create --type xlsx --from <data.csv \| table.json> [--header] [--decimal ,] --out <path>`                                                                                                                                                             | New workbook from CSV or a 2-D array / `{sheets:[{name,rows}]}`; `"=..."` cells are formulas; `--header` freezes row 1 and filters the range                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `genoffice create --type docx --from <text.md \| fragment.html> --out <path>`                                                                                                                                                                                    | New Word document from Markdown or a restricted-HTML fragment                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `genoffice create --type pdf --from <document> --out <path>`                                                                                                                                                                                                     | PDF printed by the GenOffice renderer from any md/html/docx/xlsx/pptx file                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `genoffice slides read <pptx> [--slide n] [--full \| --max-chars n] [--layouts] [--units in\|cm\|pt\|px]` / `genoffice slides apply <pptx> --ops <file> [--dry-run]`                                                                                             | Inspect ids, geometry (`box` and `size` in EMU, or converted with `unit` named when `--units` is given) and text previews (clipped to 300 characters, ending in `…(+n chars)` with `truncated: true`; `--max-chars` moves the cap, `--full`: whole text, every table row, speaker notes), then edit with ops Text elements carry `effective` (first run's font size/family/color/bold/italic and paragraph align) with `src` naming where each value comes from: `run`, `paragraph defRPr`, `shape lstStyle`, `layout placeholder`, `master placeholder`, `master titleStyle`/`bodyStyle`, `presentation defaultTextStyle`, `theme major`/`minor`; `setFont` writes the run level and overrides all of them, `applyTheme` changes the theme fonts and colors. Elements with a hyperlink carry `link` (`url`, `slide` jump or a PowerPoint `action`: `nextslide` / `previousslide` / `firstslide` / `lastslide` / `lastslideviewed` / `endshow`); `setLink` and `setText` run `link` fields write the same three kinds.                                                                                                                                                                                                                                                                                                                                            |
| `genoffice slides audit <pptx> [--slide n]` / `genoffice slides render <pptx> --out <dir> [--scale 2]`                                                                                                                                                           | Geometry audit (`out_of_bounds`, `off_slide`, `text_overflow`, `text_overflow_width`, `overlap`, `picture_distorted` = box aspect off the cropped source by >5%): `issues[]` with a `code`, `level`, `path` (`s_n/e_*`) and, except for overlap, a `suggest` `setTransform` op you can pass to `slides apply` as is; one PNG per slide to look at                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `genoffice render <file> --out <dir> [--page n] [--scale 2] [--el e_12 [--pad px]] [--grid [--cols 4] [--tile 320]]`                                                                                                                                             | One PNG per page of a docx, xlsx, pptx, md, html or pdf file, laid out by the GenOffice renderer: look at it before reporting a document or workbook as done; `--el` (pptx) also writes the page cropped to one element (`<stem>-NN-e_12.png`, ids from `slides read`) so a small box is legible; `--grid` also writes every page onto one contact sheet (`<stem>-grid.png`, row-major, tile positions in `grid.tiles`) to see a whole deck at once                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `genoffice slides check <outline.json\|page.json>` / `genoffice slides replace <pptx> --slide n --spec <page.json>`                                                                                                                                              | Validate a deck outline (exit 1 on errors) or build-and-audit one page file and check it against its outline entry and style.md; rebuild one slide from its page file, other slides untouched                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `genoffice sheet read <xlsx> [--sheet name] [--range A1:D20] [--cols A,C:E] [--max-rows n] [--where formula\|error\|empty\|number\|text] [--stats] [--formats]` / `genoffice sheet apply <xlsx> --ops <file> \| --cells <file>` / `genoffice sheet check <xlsx>` | Read a range plus `features` (panes, filter, charts, tables, rule counts for the sheet; merges and links in the range; `--formats` adds cell formats, widths, heights); keep the output small with `--cols` (only those columns, named in `columns`), `--max-rows` (`rowsShown` / `rowsTotal` and a `note` with the `--range` to continue), `--where` (only cells of one kind as `{ref, value, formula}`, no grid) or `--stats` (counts, used range and the sheet list, no cells); edit with the workbook DSL or by cell address; `check` lists formula errors, references to missing sheets, broken defined names, chart ranges off the data, `###` columns (with a `set_col_width` suggestion), placeholder text and existing rules                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `genoffice docs read <docx> [--range a-b] [--html] [--full \| --max-chars n] [--comments] [--revisions] [--header-footer] [--sections] [--fields] [--notes]` / `genoffice docs apply <docx> --ops <file> [--dry-run]` / `genoffice docs check <docx>`            | Read blocks by index (previews are clipped to 200 characters and end in `…(+n chars)` with `truncated: true`; `--max-chars` moves the cap, `--full` lifts it; plus comment threads, tracked changes, header/footer text, page setup per section, fields with their cached results, footnotes/endnotes), then edit with ops (incl. `applyStyle`, `insertField` for SEQ / REF / DATE / PAGE… fields, `insertBookmark`, `updateFields`), restricted HTML, `insert_image` / `insert_picture` (sized, floating) / `insert_text_box`, `insert_chart` / `edit_chart`, `set_header_footer`, `set_watermark` (text, or a washed-out picture via `image`), `define_style`, `set_page_setup` / `insert_section_break`, `add_comment` / `reply_comment` / `resolve_comment` / `delete_comment`, `accept_changes` / `reject_changes` (selector: `all`, `ids` from `--revisions`, `author`, `type`, `blockIndex`, `blockRange`, `before`; `apply --track [--author name]` records the batch's own edits as tracked changes), `insert_footnote` / `insert_endnote` / `edit_note` / `delete_note`; `--styles` lists style ids; `check` lists fields without results (a fresh TOC), references to missing bookmarks, a TOC that no longer matches the headings, missing images, empty charts or headings, level skips, placeholder text, pending tracked changes and open comments |
| `genoffice merge <template.docx\|pptx\|xlsx> --data <values.json \| inline JSON> --out <path> [--force] [--strict]`                                                                                                                                              | Fill `{{key}}` placeholders in a Word, PowerPoint or Excel template from a JSON object (nested objects flatten to dotted keys `a.b`; whitespace inside the braces is tolerated) through the engines' own find/replace (Word, PowerPoint: text, tables, speaker notes) and typed `set_cell` writes (Excel: a whole-cell placeholder takes the value's type, a number stays a number; substituted text stays text even when it starts with `=`). `detail.used_keys`, `detail.unused_keys` and `detail.unresolved_placeholders[]` (`{placeholder, key, reason: no_key \| split_placeholder \| unreachable_nested, location}` — block index / slide + element id / sheet + cell) tell what happened; unresolved placeholders stay in place with a `unresolved_placeholder` warning unless `--strict` makes them an error. `split_placeholder` = Word split the `{{…}}` over runs with different formatting; retype it in the template in one run (PowerPoint fills a placeholder that spans runs). `unreachable_nested` = inside a nested pptx group (or a table in a group), which deck `findReplace` does not reach; ungroup it. A Word table nothing fills is left untouched                                                                                                                                                                                       |
| `genoffice pdf read <pdf> [--page n \| --range a-b] [--password <pw>] [--full \| --max-chars n]`                                                                                                                                                                 | Text layer of a PDF page by page: 1-based `page`, `width_pt` / `height_pt`, `text` clipped to 4000 characters ending in `…(+n chars)` with `truncated: true` (`--max-chars` moves the cap, `--full` lifts it and reads every page), plus page count, `encrypted` and title / author / producer metadata; without `--page` / `--range` the first 20 pages come back and `detail.truncated` names the rest; scanned pages have empty text (`convert --to docx` runs OCR)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `genoffice guide <slides\|docs\|sheets> [group\|op\|design\|spec] [--json] [--index] [--fingerprint]`                                                                                                                                                            | The op reference for each domain, generated from the same definitions `apply` validates against (read this before writing ops); one group or one op with its JSON schema on request; `slides design` / `slides spec` for building a deck                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `genoffice search <query> [--images] [--max n]`                                                                                                                                                                                                                  | Web / image search via the provider configured in GenOffice; `detail.results[]` `{title,url,snippet}`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `genoffice image <prompt> [--out f.png] [--aspect 16:9] [--ref a.png,b.png] [--model <name>]`                                                                                                                                                                    | Generate an image with the configured provider and save it; `detail.mime`, `output_path`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `genoffice media <file\|url> [--ask <question>]`                                                                                                                                                                                                                 | Describe / question an image, video or audio file; answer in `summary` and `detail.text`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `genoffice capabilities`                                                                                                                                                                                                                                         | Which cloud features are configured in GenOffice (search, image search, image generation, media analysis) and whether the app is installed; no network call                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `genoffice open <file> [--slide n [--el e_12] \| --block n \| --range [Sheet!]B2:D5 \| --page n]`                                                                                                                                                                | Open the file in the GenOffice app (only when the user asks to see it there); with a target, also scroll to and select that slide element / block / range / page so the user is looking at the same spot you are (`target_not_found` / `out_of_range` come back with the valid ids or range)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `genoffice selection <file> --json`                                                                                                                                                                                                                              | What the user has selected in the editor showing the file: `{slide, elements[], types[]}`, `{blocks:[a,b], text, collapsed}`, `{sheet, range, values?}` or `{page, text?}` — resolve "this one" / "here" from it before editing; `file_not_open_in_gui` when no tab shows the file                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `genoffice skill list` / `genoffice skill install <agent\|all> [--dir <path>] [--force]` / `genoffice skill path`                                                                                                                                                | Coding agents found on this machine (Claude Code, Codex, Cursor, Gemini CLI, Copilot, OpenCode, Windsurf) with the skill version each has; copy or update this skill into their skills directory, or print the bundled SKILL.md; nothing is installed without this command or a click in Settings → Integrations                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |

`--ops -` and `--cells -` read stdin. `--out` defaults to the input file's folder with the new extension (`convert`) or edits in place (`apply`). Unknown options are rejected (exit 1). The `mcp` server runs on stdio by default; `mcp [--http <port> [--host <addr>] [--token <secret>]]` serves Streamable HTTP instead; `genoffice mcp install <agent|all> [--dir <path>] [--force]` registers the stdio server in a coding agent's MCP config (`mcp uninstall <agent>` removes it, `mcp list` shows each agent).

`genoffice mcp` as an MCP client sees it:

- Tools mirror the commands one-to-one (`info`, `convert`, `create_docx` / `create_xlsx` / `create_pptx` / `create_pdf`, `docs_read` / `docs_apply` / `docs_check`, `sheet_read` / `sheet_apply` / `sheet_check`, `slides_read` / `slides_apply` / `slides_audit` / `slides_render` / `slides_check` / `slides_replace`, `render`, `guide`, `capabilities`, `search`, `image`, `media`, `open`, `selection`); ops, cells, specs and Markdown go inline, results are the `--json` envelope (a `partial` batch is not `isError`, its first text block lists the lost ops), `open` and `selection` exist on stdio only.
- `deck_start` / `deck_page` / `deck_build` / `deck_replace` are the staged deck flow above with the deck folder as state; a short deck with concrete content is one `create_pptx` call.
- The op references are resources: `genoffice://guide/docs`, `…/sheets`, `…/slides`, `…/slides/design`, `…/slides/spec`.
- `ops`, `cells` and `data` carry the per-op schema of `guide <domain> --json` (one variant per op: `op`, fields, required); `mcp --compact-schemas` (or `GENOFFICE_MCP_COMPACT_SCHEMAS=1`) advertises plain arrays for clients with a small context budget.
- `--http`: files travel with the calls (`PUT /files/<name>` uploads one and returns a URL, every path parameter also takes an http(s) URL, `out` omitted returns the file as a download URL).
- The GenOffice app runs its own MCP server on `127.0.0.1:3093` (Settings → Integrations) whose tools (`create_session`, `read_document`, `apply_ops`, `apply_slide_ops`, `apply_sheet_ops`, `read_pdf`, `open_documents`) edit the documents already open in the visible editor; `genoffice mcp` works on files on disk.

## Which command

| Task                                                       | Command path                                                                                                                                   | Not for                                                                                  |
| ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| A presentation from a brief, however short                 | `guide slides design` → `style.md` + `outline.json` → `slides check` per page → `create --type pptx --spec` → `render --grid` → `slides audit` | editing a deck that exists (`slides apply`); a fixture deck (`create --type pptx --ops`) |
| Change text, a picture or one slide of an existing deck    | `slides read` → `slides apply --ops` → `slides audit` → `render --page`                                                                        | restyling the whole deck (`applyTheme` op, or rebuild through the staged path)           |
| A workbook from data                                       | `create --type xlsx --from data.csv \| table.json` → `sheet apply --ops` for formats/charts → `sheet check`                                    | one cell fix in an existing file (`sheet apply --cells`)                                 |
| Edit cells, formats, charts, rules of an existing workbook | `sheet read` → `guide sheets <op>` → `sheet apply --ops` → `sheet check`                                                                       | `refresh_pivot`, session tables and shapes (refused: needs the editor)                   |
| A Word document from text                                  | `create --type docx --from report.md \| fragment.html` → `docs check` → `render`                                                               | a real web page (`convert page.html --to docx`)                                          |
| Edit an existing Word document                             | `docs read` → `docs apply --ops` → `docs check`                                                                                                | reading its text (`convert --to md` or `docs read --full`)                               |
| Read the user's file as source material                    | `convert --to md/csv` or `<domain> read --full --json`                                                                                         | `unzip`/`cat` on the Office file; `render` (pixels, not text)                            |
| Change the file format                                     | `convert <file> --to <fmt>`                                                                                                                    | any content change (edit first, convert after)                                           |
| See what a page looks like                                 | `render` (`--page`, `--el`, `--grid`)                                                                                                          | extracting text or ids (`read`)                                                          |
| Point the user at a spot, or act on what they selected     | `open <file> --slide/--el/--block/--range/--page` → `selection <file> --json`                                                                  | reading content (`read`); files the user has not opened (`selection` needs the tab)      |
| Look up op fields                                          | `guide <domain> [group\|op] [--json]`                                                                                                          | guessing from this file: the guide is generated from the validator and is always current |
| Give another agent (or a fresh machine) this skill         | `skill list` → `skill install <agent>` (or `skill path` and copy the file)                                                                     | writing the skill by hand; `install-cli` (that puts `genoffice` on the PATH)             |

## Behaviour to know

- **Word (`docs`)**: block indexes inside one batch are live — after `insert_content` adds two blocks, later indexes shift by two. A target that matches no block fails the whole batch (exit 1). `findReplace` does not reach table cells (use `replace_blocks` on the table). Tags outside the restricted set are stripped silently. `convert docx→md` writes GFM: `& < >` become entities, formulas become `$…$`, images are dropped and counted in `detail.skipped`. `create --from x.md` embeds local images referenced by relative path. `insert_image` takes a local path (relative to the current directory, then to the ops file), a data: URL or an http(s) URL (png/jpg/gif) and appends unless `afterBlockIndex` is given; `edit_chart` targets blocks `docs read` lists with kind `chart`; `set_header_footer` writes plain text with `{PAGE}` / `{NUMPAGES}` tokens, `view: "first"` switches the different-first-page setting on; comment ids come from `docs read --comments`; `add_comment` anchors a new thread to a block or to an exact `text` span inside it (`occurrence` picks one of several matches) without touching the document text; `delete_comment` on a thread root with replies needs `withReplies: true`; revision ids (`r1`, `r2`, …) come from `docs read --revisions` and are positional, so read again after any edit before `accept_changes` / `reject_changes`. `insertField` computes SEQ numbers, REF text, DATE and MERGEFIELD locally; PAGE / NUMPAGES / AUTHOR-style results are placeholders Word recomputes on open (`docs check` reports them as info), so say so when you hand over the file. Note ids for `edit_note` / `delete_note` come from `docs read --notes`; `edit_note` is `findReplace` inside one note (`{ id, find, replace, matchCase? }`, `kind` only when a footnote and an endnote share the id) and keeps the note's id, reference mark and formatting — `findReplace` itself never reaches note text. `define_style` patches only the fields given (an existing style keeps the rest); `applyStyle` takes a paragraph style id from `docs read --styles` and turns blocks into headings when the style is a heading style; `set_watermark` with `text: null` removes the watermark; `insert_picture` / `insert_text_box` lengths take `2.54cm` / `1in` / `72pt` / `96px` or points. `set_page_setup` changes paper / orientation / margins / columns / titlePg / page numbering of every section or of one (`section: N` or `blockIndex`), lengths as `"2.54cm"` / `"1in"` / `"72pt"`; `insert_section_break` splits a section after a block (the copy is then changed with `set_page_setup section: N`); `docs read --sections` lists sections with their block ranges. Native tables take structure ops: `insertTableRow` / `deleteTableRow` / `insertTableColumn` / `deleteTableColumn` (0-based, `position: "before"|"after"`, `count`), `mergeTableCells` / `splitTableCell` (grid `range` / `cell`; a merge that cuts an existing merged cell is refused), `setTableCellFormat` (fill, vAlign, borders, `width` in twips or `"2.5cm"`), `setTableStyle` (a style id from `docs read`'s `tableStyles`, plus look flags); target them with `nodeType: "table"` and `docs read` shows each table's `rows`, `cols` and `merged` cells.
- **Excel (`sheet`)**: ops run in order, each seeing the previous result: after `insert_rows`/`delete_cols` the following ops address the shifted grid (a formula written after the shift is stored as given, one written before it is shifted like the existing ones), a sheet added or renamed earlier in the batch is addressed by its new name; rules, links, notes, filters and visuals must come after any row/column op on their sheet, and defined-name ops cannot share a batch with sheet or row/column ops. `set_cell` with a string starting with `=` writes a formula; `type: "text"` keeps it literal text. Beyond cells, `sheet apply --ops` writes charts (`add_chart`, `edit_chart` on `xl/charts/chartN.xml`), images and shapes, Excel tables, freeze panes, hidden rows/columns, filters with checked values, conditional formats, data validation, hyperlinks, notes, page setup, sheet protection, defined names and tab order/visibility (`genoffice guide sheets` has every field). `--cells` `style` takes the same object as `format_range.format` (`{bold, fillColor, numberFormat, …}`). Colors are `#RRGGBB` or theme slots that follow the workbook theme: `"accent1"`, `"accent1+40%"` (lighter), `"dk2-25%"` (darker) or `{theme, tint}`; `fill: {pattern: "lightGray", fg, bg?}` or `fill: {gradient: {angle, stops: [{position, color}]}}` writes pattern and gradient fills, and `--formats` echoes them back with the slot, tint and resolved rgb. `sheet read` returns raw values (0.25, serial dates) and formulas separately, `features` (sheet-wide panes, filter, charts, tables and rule counts; merges and links of the range), and with `--formats` the styled cells, column widths (px) and row heights (pt); `convert --to csv` returns the displayed text (percent, dates), UTF-8 with BOM, CRLF line ends, active sheet unless `--sheet`. `create --from x.csv`: a `sep=;` first line is honoured, ISO dates (`2024-01-01`, `2024-01-02 09:30`) become real dates with a date format, `--decimal ,` reads `1.234,5` as 1234.5. Formulas using functions the local engine cannot evaluate (FILTER, SORT, UNIQUE, LET, …) are written without a cached value and Excel computes them on open; spill formulas are stored as dynamic arrays so they spill in Excel 365; the `formulas_not_cached` warning names those cells.
- **PowerPoint (`slides`)**: picture ops (`addPicture`, `replacePicture`, `addMedia`, `setImageFill`, `setBackground`) take a local path or data URL in `bytes`; the extension is inferred. `setText` runs inherit the element's first run style unless a field is set explicitly (`bold: false`). Ops in one `apply` batch run in order, so a later op may target a slide an earlier op added; atomic still writes nothing unless every op applied; `--best-effort` (formerly `--isolation per_op`) and `--stop-on-error` write what applied and answer `status: "partial"`. Created elements get new `e_*` ids: run `slides read` again before targeting them. `convert pptx→pdf` is rendered like the app's export: one bitmap per slide, no text layer. Every EMU length field (`box`, `offset`, `dx`/`dy`, `wEmu`/`hEmu`, `colWidthsEmu`, `insets`) also takes a string with a unit suffix: `"2.54cm"`, `"1in"`, `"10mm"`, `"12pt"`, `"96px"`. `slides read` lists each page's `animations[]` (`seq`, `el`, `effect`, `kind`, `trigger`, `durationMs`, `delayMs`, `direction`); `addAnimation` / `removeAnimation` / `reorderAnimation` edit one timeline item (`presetXml` passes a PowerPoint-exported effect through verbatim), `insertEquation` adds a typeset formula from LaTeX (PowerPoint renders it, previews show the linear text).
- **Conversions in the app** (`→pdf`, `docx→html`, `html→docx`, `create --type pdf`, `render`): a hidden GenOffice process per call, 1–6 s, up to 180 s timeout, safe to run concurrently. `html→docx` is html2docx for real web pages; a restricted-HTML fragment with `<formula>` goes through `create --type docx --from fragment.html` instead.
- **Cloud (`search`, `image`, `media`)**: `--images` returns `detail.images[] {title,imageUrl,sourceUrl,source,width,height}`; providers may return fewer than `--max`. `image` saves with the provider's real format (`output_path` tells you; a `.png` request may come back `.jpg`, noted in `detail.note`); `--aspect` / `--size` are validated locally.

## Workflow

1. `genoffice info` or `genoffice <domain> read --json` to learn the structure: block indexes (docx), `s_<n>` / `e_*` ids and EMU geometry (pptx), sheet names and cell values (xlsx).
2. `genoffice guide <domain>` for the op fields (`guide <domain> <op> --json` for one op's schema), then write the ops JSON yourself.
3. `genoffice <domain> apply --dry-run` when the batch is large; a rejected op comes back as `op_rejected` with the executor's usage line — fix that op and resend the whole batch. Atomic by default: on rejection the file is untouched and `detail.batch` is `{total, applied: 0, failed, skipped}`. `--best-effort` applies every op that can apply, `--stop-on-error` applies in order up to the first rejection; both write the file and answer `status: "partial"` (exit 0) with the lost ops in `detail.failures` and the counts in `detail.batch` — a program must check `status`, not only the exit code.
4. The QA gate below, then report the output path.

## Pitfalls

| Pitfall                                                                                                                                                                             | Rule                                                                                                                                                           |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `slides read/audit/render/replace --slide n` and op targets `{ "slide": n }` are **0-based**; `render --page n` is **1-based**; docx block indexes are 0-based                      | `s_0` is page 1 of the PNGs.                                                                                                                                   |
| `--ops -`, `--cells -` and `--spec -` read stdin (`--from` does not); any other value must be an existing file                                                                      | A path that does not exist is `file_not_found`, not an empty batch.                                                                                            |
| Structural edits renumber: new `e_*` ids after `addElement`, `s_*` after adding slides, docx block indexes after `insert_content`/`delete_blocks`                                   | Inside one batch indexes are live (later ops see earlier results); before the next batch run `read` again.                                                     |
| EMU fields (`offset`, `box`, `dx`/`dy`, `wEmu`/`hEmu`, `colWidthsEmu`, `insets`) take integers or unit strings `"2.54cm"`, `"1in"`, `"10mm"`, `"12pt"`, `"96px"`                    | Do not multiply by 914400 yourself; `slides read` returns EMU integers.                                                                                        |
| CSV import: a `sep=;` first line is honoured and removed, ISO dates become real dates, `--decimal ,` for `1.234,5`                                                                  | `--header` freezes row 1 and adds a filter; without it the first row is data.                                                                                  |
| Formulas the local engine cannot evaluate (FILTER, SORT, UNIQUE, LET, LAMBDA …) are written without a cached value                                                                  | `formulas_not_cached` warning lists them; `sheet read` shows the formula and no value until Excel opens the file. Not an error.                                |
| `sheet apply` addresses sheets by the **current tab name in the file**, spelled as in the tab                                                                                       | `sheet_not_found` carries `detail.sheets` and a `did you mean`.                                                                                                |
| Structural sheet ops (insert/delete rows, columns, sheets) shift the addresses of the ops after them in the same batch; defined-name ops do not mix with sheet or row/column ops    | Write later ops in post-shift coordinates (or put the structural op last); rules, links, notes, filters and visuals go after the row/column op of their sheet. |
| GenOffice has the file open in a tab → `apply` refuses (exit 2, `file_open_in_gui`)                                                                                                 | Ask the user to close the tab, or `--out other.ext`; `--force` only if the user accepts the editor may overwrite it.                                           |
| `create`/`convert`/`image` never overwrite (exit 2, `output_exists`); `apply --out` onto an existing file also refuses                                                              | Pick another name, or `--force` when the user asked for replacement.                                                                                           |
| `apply --best-effort` / `--stop-on-error` (formerly `slides apply --isolation per_op`) answer `status: "partial"` with exit 0; `check`/`audit` exit 0 with problems inside `detail` | Read `status`, `detail.batch`, `detail.failures` / `detail.issues`; exit 0 is not "clean".                                                                     |
| Deck specs are px on a 1280×720 canvas; ops are EMU                                                                                                                                 | Never mix the two in one file.                                                                                                                                 |
| `convert pptx→pdf` has no text layer; `convert docx→md` drops images (`detail.skipped`)                                                                                             | Use `docs read --full` when you need every word.                                                                                                               |
| `docs findReplace` skips table cells; unknown HTML tags in `insert_content` are stripped silently                                                                                   | `replace_blocks` on the table block; stay inside the restricted HTML set (`guide docs`).                                                                       |

## When a command fails

Branch on `error`, do what `suggestion` says, then resend the **whole** batch (atomic apply wrote nothing). `detail` has the facts as fields, never parse `message`.

| `error`                                | Next command                                                                                                                                                                                 |
| -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `unknown_command`, `unknown_option`    | Use `suggestion`'s `did you mean`; `detail.commands` / `detail.options` list the valid ones; `genoffice <command> --help` when this skill is older than the app                              |
| `missing_argument`, `invalid_argument` | Fix the flag named in `message`; `genoffice <command> --help` shows the flags                                                                                                                |
| `invalid_json`                         | Re-serialize the ops file; check quoting when piping through `--ops -`                                                                                                                       |
| `unknown_op`                           | `detail.failures[].supported` and `did_you_mean`; `genoffice guide <domain>` for the list                                                                                                    |
| `op_rejected`                          | `detail.failures[].usage` is the executor's usage line; `genoffice guide <domain> <op> --json` for the schema; fix that one op                                                               |
| `unresolved_placeholder`               | `merge --strict`: `detail.unresolved_placeholders[]` names each placeholder and where it is; add the keys to `--data` (or fix a `split_placeholder` in the template) and rerun               |
| `target_not_found`                     | `detail.failures[].available` lists the ids on that slide / the matching blocks; otherwise `genoffice <domain> read <file> --json` and retarget                                              |
| `out_of_range`                         | `detail.failures[].valid_range` (or `detail.valid_range`); `read` shows the current count                                                                                                    |
| `sheet_not_found`                      | `detail.sheets` has the tab names as in the file; use one verbatim                                                                                                                           |
| `file_not_found`                       | Check the path (relative to the current directory); for `--ops`, `-` means stdin                                                                                                             |
| `output_exists`                        | Another `--out`, or `--force` if the user asked to replace                                                                                                                                   |
| `file_open_in_gui`                     | Ask the user to close the tab, or write with `--out`; `--force` only with consent                                                                                                            |
| `file_not_open_in_gui`                 | `selection`/`open --el` need the file in a tab: `genoffice open <file>` first, then ask the user to select                                                                                   |
| `outside_allowed_roots`                | Stay inside `GENOFFICE_ALLOWED_ROOTS`; do not try another location                                                                                                                           |
| `resource_limit`                       | The package has too many or too large parts, an implausible compression ratio or an entry path that escapes the archive; `detail` names the entry and the limit. Do not retry; tell the user |
| `unsupported`                          | `detail.supported` lists what this command accepts; pick another route from the table above                                                                                                  |
| `conversion_failed`, `app_unavailable` | The file or the app: retry once; if `capabilities --json` says the app is missing, tell the user (PDF and `→html`/`html→` routes need GenOffice)                                             |
| `app_crashed`                          | GenOffice crashed during a headless export (`detail.signal`): retry once; if it repeats, tell the user and name the document and the crash log path from `suggestion`                        |

A successful result may still carry `warnings[]` (`{code, message, suggestion?}`): `formulas_not_cached`, `op_warning` (a sheets op was adjusted), `images_dropped` (`convert docx→md`), `output_renamed` (`image` saved with the provider's real extension), `unresolved_placeholder` (`merge` left placeholders without a value in place). Read them, report what matters, do not retry.

## Before you say "done"

Three lines per artifact, every time:

| Artifact | 1. Check                                                                                                                         | 2. Look                                                              | 3. Report                                       |
| -------- | -------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | ----------------------------------------------- |
| Deck     | `slides audit deck.pptx --json` → `detail.issues` empty (apply `suggest` ops otherwise)                                          | `render deck.pptx --out shots --grid --json`, open the contact sheet | The `.pptx` path and anything you left as is    |
| Workbook | `sheet check book.xlsx --json` → no `formula_error`, `missing_sheet_ref`, `number_overflow` (apply the `set_col_width` suggests) | `render book.xlsx --out shots --json`, look at the sheet PNG         | The `.xlsx` path; name uncached formulas if any |
| Document | `docs check report.docx --json` → no `broken_ref`, `placeholder_left`, `field_not_evaluated` you can fix                         | `render report.docx --out shots --json`, look at page 1 and the last | The `.docx` path                                |

`check`/`audit` exit 0 whether or not they found something; the findings are in `detail.issues[]` with a stable `code`, a `path` and, when possible, a `suggest` op to pass to `apply` as is. Do not run `genoffice open` unless the user asks to see the file in GenOffice.

## Reading the user's files

Source material the user hands you (a report to turn into slides, a workbook to summarize) is read by you, not by genoffice: md, txt, csv, pdf and images you open directly. Office files are zipped XML, so go through the CLI first:

- docx: `genoffice convert file.docx --to md --out file.md` for the whole text (images are dropped), or `genoffice docs read file.docx --full --json` / `--html` for blocks with indexes.
- xlsx: `genoffice convert file.xlsx --to csv --sheet <name> --out file.csv` for a whole sheet as displayed text, or `genoffice sheet read file.xlsx --range A1:H200 --json` for raw values (without `--range` it stops at 500 rows and says `truncated: true`; `--stats` first to size a workbook, `--cols`/`--max-rows` to page through a wide or long sheet, `--where formula` for the formulas alone).
- pptx: `genoffice slides read file.pptx --full --json` for every slide's text, tables and speaker notes; `genoffice slides render file.pptx --out shots` when you need to see the pages.
- pdf: `genoffice pdf read file.pdf --range 1-20 --json` when you cannot open the PDF yourself (a password-protected file, or page sizes and metadata are needed).

Take figures and wording from that material; never `cat` or `unzip` the Office file itself.

## Designing a deck

Whenever the user wants a presentation, however short, do not hand-place ops: follow the staged pipeline the GenOffice app itself uses. You are the model, genoffice is the engine, and each stage is a file the CLI checks before the next one starts. Ops (`create --ops`, `slides apply`) are for editing a deck that already exists.

1. `genoffice guide slides design` once per session, then `genoffice guide slides spec`. Run `genoffice capabilities --json` once: it says whether search, image search and image generation are configured in GenOffice; use them for facts and photos only when they are, and plan photo-free pages otherwise.
2. Make a folder for the deck. Write the style sheet to `deck/style.md` (colors, fonts, layout variants, one sentence of style). Then the outline to `deck/outline.json` (core hook + one entry per page: title, type, layout, brief with real figures, image_queries) and run `genoffice slides check deck/outline.json --json`; fix every error before going on. Show the outline to the user only if they asked to review it.
3. One page per step: re-read `deck/style.md` and the page's outline entry, resolve its photos, write `deck/pages/NN.json` (one page object echoing the entry's `title`, `type`, `layout`), run `genoffice slides check deck/pages/NN.json --json`; it finds `outline.json` and `style.md` one folder up and checks the page against entry N and the palette. Fix until `detail.audit`, `detail.outline.findings` and `detail.style.offPalette` are empty (a disagreement with the outline exits 1), then the next page. Never write several pages in one file or one step.
4. `genoffice create --type pptx --spec deck/pages --outline deck/outline.json --out deck/deck.pptx --json` (refuses to run while an outline page has no file or a page disagrees with its entry). Then `genoffice slides render deck/deck.pptx --out deck/shots --json` and look at the PNGs; `genoffice slides audit deck/deck.pptx --json` for the geometry findings. Fix a page in its file and `genoffice slides replace deck/deck.pptx --slide n --spec deck/pages/NN.json --json`. At most two fix rounds.
5. Report the path. Photos come from `genoffice search --images` results, `genoffice image`, or local files the user gave you; never draw a grey box in place of a photo. Figures come from the user's material or `genoffice search`; say so when a number is illustrative.

## Editing an existing deck

A rewrite of one slide, a new title, a swapped picture: use ops, not the staged pipeline, and keep the deck's own design.

1. `genoffice slides read deck.pptx --slide n --full --json` for the element ids and the current text; `genoffice slides render deck.pptx --slide n --out shots --json` when the words alone do not tell you what the page looks like.
2. `setText` on the existing element (runs you leave unstyled inherit the original font, size and color), `addElement` / `addPicture` for new content, `deleteElement` for what goes. Write with `--out` when the user wants the original kept.
3. `genoffice slides audit deck.pptx --slide n --json` after the edit: a longer sentence in the same box overflows; apply the `suggest` ops it returns (they grow or move the box), then render once more and look.
4. Ids of edited elements change: run `slides read` again before a second round of ops. `slides replace --slide n --spec` rebuilds the page from a spec and is for decks the staged pipeline built, not for restyling a deck the user brought.

## Examples

Deck from ops (an existing deck's edits, or a fixture; a presentation for a person goes through the staged path above):

```json
[
  {
    "op": "addElement",
    "target": { "slide": 0 },
    "kind": "textbox",
    "offset": { "x": 914400, "y": 685800, "cx": 7315200, "cy": 914400 },
    "paragraphs": [{ "runs": [{ "text": "Q3 Review", "bold": true, "fontSize": 36 }] }]
  },
  { "op": "addBlankSlide", "target": { "slide": 0 } },
  {
    "op": "addTable",
    "target": { "slide": 1 },
    "rows": 3,
    "cols": 3,
    "offset": { "x": 914400, "y": 1828800, "cx": 7315200, "cy": 2286000 }
  }
]
```

```
genoffice create --type pptx --ops deck.json --out q3.pptx --json
```

Workbook with formulas, then a formatted header:

```
echo '[["item","qty","price"],["Apple",2,1.5],["Total","=SUM(B2:B2)",""]]' > table.json
genoffice create --type xlsx --from table.json --out sales.xlsx
echo '[{"op":"format_range","range":"A1:C1","format":{"bold":true,"fillColor":"#FFFF00"}}]' | genoffice sheet apply sales.xlsx --ops - --json
```

Word from Markdown, then insert a paragraph after block 0:

```
genoffice create --type docx --from report.md --out report.docx
echo '[{"op":"insert_content","afterBlockIndex":0,"html":"<p>Executive summary.</p>"}]' | genoffice docs apply report.docx --ops -
```

## Path rules

- Pass absolute paths or paths relative to the current directory; genoffice never writes outside the `--out` path or the file being edited.
- `create` and `convert` refuse to overwrite an existing output without `--force`. `apply` edits in place unless `--out` is given; `--out` onto another existing file also needs `--force`.
- genoffice refuses to rewrite a file the GenOffice window currently has open (exit 2, message "GenOffice has this file open"). Ask the user to close that tab, write to another path with `--out`, or pass `--force` only if they accept that the editor may overwrite the change.
- If `GENOFFICE_ALLOWED_ROOTS` is set in the environment, genoffice only reads and writes inside those directories (exit 2 otherwise); stay within them. Every executed command is logged to `~/.genoffice/cli-audit.jsonl` (`GENOFFICE_AUDIT_LOG` moves or disables it). `GENOFFICE_USER_DATA` points genoffice at a non-default GenOffice profile (open-documents registry, AI settings).
- Image ops (`addPicture`, `setImageFill`, `addMedia`) accept a local file path in their `bytes` field; genoffice reads it.

## Limits

- Conversions to PDF run inside the GenOffice app (hidden, a few seconds each); they need GenOffice installed, or `GENOFFICE_APP_BIN` pointing at it; the same holds for `docx→html` and `html→docx`.
- `add_pivot` writes the pivot's cells at `targetCell` (no values or formulas there, on a sheet that already exists in the file) plus a native pivot Excel refreshes, source formulas are evaluated from the file so set them in an earlier batch; `add_sparkline` writes one in-cell sparkline per data row (at most 200 rows per op); `convert_to_values` replaces formulas with the values the workbook engine computes from the file (error results keep their formula; cell edits go in an earlier batch); `set_page_setup` also takes `printTitles`, `header`/`footer` and `rowBreaks`/`colBreaks`. Excel ops that still need the live editor (`refresh_pivot`, edits to session-created tables and shapes) are refused with the reason. `add_conditional_format` and `set_data_validation` add to the rules a sheet already has (`set_data_validation` on a range that has one replaces it, `null` removes it); `clear_conditional_formats` starts over.
- Formulas are evaluated with the workbook engine after writing, so `sheet read` and cached-value readers see numbers.
