---
title: "genoffice by genspark-ai · skilld"
canonical_url: "https://skilld.dev/gh/genspark-ai/genoffice"
meta:
  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… From genspark-ai/genoffice."
  "og: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… From genspark-ai/genoffice."
  "og:title": "genoffice by genspark-ai"
  "twitter: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… From genspark-ai/genoffice."
  "twitter:title": "genoffice by genspark-ai"
---

`

[All skills](https://skilld.dev/skills)

[![genspark-ai avatar](https://skilld.dev/_img/avatar?url=https%3A%2F%2Fgithub.com%2Fgenspark-ai.png%3Fsize%3D96)](https://skilld.dev/gh/genspark-ai)

# **/genoffice**

[@968d7d2](https://github.com/genspark-ai/genoffice/commit/968d7d23eb376b72c8a486826c36ec41965193d3 "Your agent reads SKILL.md at commit 968d7d2")

by [genspark-ai](https://skilld.dev/gh/genspark-ai)· [genspark-ai](https://skilld.dev/gh/genspark-ai)/ [genoffice](https://skilld.dev/gh/genspark-ai/genoffice)·8.3k stars

 1,075

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.

- 1 file
- 76.2 KB
- Updated 3 days ago
- [GitHub](https://github.com/genspark-ai/genoffice/blob/968d7d23eb376b72c8a486826c36ec41965193d3/skills/genoffice/SKILL.md "View SKILL.md on GitHub")
- [1 warning](#third-party-checks "Third-party checks: 1 warning · 3 checks · Risk SAFE")

## SKILL.md

76.2 KB

**≈229** tokens always: the name and description. **≈19k** when used: this file.

## 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):

```
[
  {
    "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.

Source: [SKILL.md on GitHub](https://github.com/genspark-ai/genoffice/blob/968d7d23eb376b72c8a486826c36ec41965193d3/skills/genoffice/SKILL.md)

## Third-party checks

<details>

<summary>1 warning3d3 checks · Risk SAFE</summary>



- Gen Agent Trust Hub3d

  The 'genoffice' skill allows agents to create and modify Office documents using a local CLI tool. It includes network-enabled features like search and media analysis that transmit data to external providers, and provides commands to install itself into agent environments for persistence. The primary security consideration is the indirect prompt injection risk when processing external documents.
- Socket3d

  No alerts
- Snyk3d

  Risk: MEDIUM · 1 issue

</details>

## Provenance

[Signed by skilld at 968d7d2.](https://github.com/genspark-ai/genoffice/commit/968d7d23eb376b72c8a486826c36ec41965193d3 "968d7d23eb376b72c8a486826c36ec41965193d3") This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub yesterday.

Activeupdated 3 days ago

## Capability

<dl>

<dt>metadata</dt>
<dd>

```
{
  "version": "2.67.1",
  "cli": ">=0.9.0"
}
```

</dd>

</dl>

## README badge

![README badge for genspark-ai/genoffice](https://skilld.dev/b/genspark-ai/genoffice?theme=light&label=0)

## Related skills

-
-
-
-
-
-