Create, analyze, proofread, and modify Office documents (.docx, .xlsx, .pptx) using the officecli CLI tool. Use when the user wants to create, inspect, check formatting, find issues, add charts, or modify Office documents.
AI-friendly CLI for .docx, .xlsx, .pptx. Single binary, no dependencies, no Office installation needed.
Same command for both install and upgrade:
# macOS / Linux
curl -fsSL https://raw.githubusercontent.com/iOfficeAI/OfficeCLI/main/install.sh | bash
# Windows (PowerShell)
irm https://raw.githubusercontent.com/iOfficeAI/OfficeCLI/main/install.ps1 | iex
After installation, run source ~/.zshrc (macOS) or source ~/.bashrc (Linux) to make the officecli command available.
Verify: officecli --version
officecli auto-updates daily in the background.
L1 (read) → L2 (DOM edit) → L3 (raw XML). Always prefer higher layers. Add --json for structured output.
When unsure about property names, value formats, or command syntax, ALWAYS run help instead of guessing. One help query is faster than guess-fail-retry loops.
Three-layer navigation — start from the deepest level you know:
officecli pptx set # All settable elements and their properties
officecli pptx set shape # Shape properties in detail
officecli pptx set shape.fill # Specific property format and examples
Replace pptx with docx or xlsx. Commands: view, get, query, set, add, raw.
For multi-step workflows (3+ commands on the same file), use open/close:
officecli open report.docx # keep in memory — fast subsequent commands
officecli set report.docx ... # no file I/O overhead
officecli close report.docx # save and release
PPT:
officecli create slides.pptx
officecli add slides.pptx / --type slide --prop title="Q4 Report" --prop background=1A1A2E
officecli add slides.pptx '/slide[1]' --type shape --prop text="Revenue grew 25%" --prop x=2cm --prop y=5cm --prop font=Arial --prop size=24 --prop color=FFFFFF
officecli set slides.pptx '/slide[1]' --prop transition=fade --prop advanceTime=3000
Word:
officecli create report.docx
officecli add report.docx /body --type paragraph --prop text="Executive Summary" --prop style=Heading1
officecli add report.docx /body --type paragraph --prop text="Revenue increased by 25% year-over-year."
Excel:
officecli create data.xlsx
officecli set data.xlsx /Sheet1/A1 --prop value="Name" --prop bold=true
officecli set data.xlsx /Sheet1/B1 --prop value="Score" --prop bold=true
officecli set data.xlsx /Sheet1/A2 --prop value="Alice"
officecli set data.xlsx /Sheet1/B2 --prop value=95
officecli create <file> # Create blank .docx/.xlsx/.pptx (type from extension)
officecli view <file> <mode> # outline | stats | issues | text | annotated
officecli get <file> <path> --depth N # Get a node and its children [--json]
officecli query <file> <selector> # CSS-like query
officecli validate <file> # Validate against OpenXML schema
| Mode | Description | Useful flags |
|---|---|---|
outline | Document structure | |
stats | Statistics (pages, words, shapes) | |
issues | Formatting/content/structure problems | --type format|content|structure, --limit N |
text | Plain text extraction | --start N --end N, --max-lines N |
annotated | Text with formatting annotations |
Any XML path via element localName. Use --depth N to expand children. Add --json for structured output.
officecli get report.docx '/body/p[3]' --depth 2 --json
officecli get slides.pptx '/slide[1]' --depth 1 # list all shapes on slide 1
officecli get data.xlsx '/Sheet1/B2' --json
Run officecli docx get / officecli xlsx get / officecli pptx get for all available paths.
Elements with stable IDs return @attr=value paths instead of positional indices. These paths survive insert/delete operations — use them for multi-step workflows.
Returned path format (output):
/slide[1]/shape[@id=550950021] # PPT shape (cNvPr.Id)
/slide[1]/shape[@id=550950021]/paragraph[1] # child inherits parent's @id=
/slide[1]/table[@id=1388430425]/tr[1]/tc[2] # PPT table
/body/p[@paraId=1A2B3C4D] # Word paragraph
/comments/comment[@commentId=1] # Word comment
/footnote[@footnoteId=2] # Word footnote
/endnote[@endnoteId=1] # Word endnote
/body/sdt[@sdtId=123456] # Word content control
All formats accepted as input — use returned paths directly for subsequent set/remove:
officecli set slides.pptx '/slide[1]/shape[@id=550950021]' --prop bold=true
officecli set slides.pptx '/slide[1]/shape[@name=Title 1]' --prop text="New" # @name= also works (PPT)
officecli set slides.pptx '/slide[1]/shape[2]' --prop color=red # positional still works
Elements without stable IDs (slide, paragraph, run, tr/tc, row) use positional indices as fallback.
When to use stable IDs: Prefer @id= / @paraId= paths in multi-step workflows where you add or remove elements between commands — positional indices shift, but stable IDs do not.
CSS-like selectors: [attr=value], [attr!=value], [attr~=text], [attr>=value], [attr<=value], :contains("text"), :empty, :has(formula), :no-alt.
officecli query report.docx 'paragraph[style=Normal] > run[font!=Arial]'
officecli query slides.pptx 'shape[fill=FF0000]'
officecli validate report.docx # Check for schema errors
officecli validate slides.pptx # Must pass before delivery
For large documents, ALWAYS use --max-lines or --start/--end to limit output.
officecli set <file> <path> --prop key=value [--prop ...]
Any XML attribute is settable via element path (found via get --depth N) — even attributes not currently present.
Without find=, set applies format to the entire element. To target specific text within a paragraph, use find= (see find section below).
Run officecli <format> set for all settable elements. Run officecli <format> set <element> for detail.
Value formats:
| Type | Format | Examples |
|---|---|---|
| Colors | Hex, named, RGB, theme | FF0000, red, rgb(255,0,0), accent1..accent6 |
| Spacing | Unit-qualified | 12pt, 0.5cm, 1.5x, 150% |
| Dimensions | EMU or suffixed | 914400, 2.54cm, 1in, 72pt, 96px |
Use find= with set to target specific text within a paragraph (or broader scope) for formatting or replacement. The matched text is automatically split into its own run(s). Add regex=true for regex matching. Format props are separate --prop flags — do NOT nest them (e.g. --prop bold=true, not --prop format=bold:true).
# Format matched text (auto-splits runs)
officecli set doc.docx '/body/p[1]' --prop find=weather --prop highlight=yellow
officecli set doc.docx '/body/p[1]' --prop find=weather --prop bold=true --prop color=red
# Regex matching
officecli set doc.docx '/body/p[1]' --prop 'find=\d+%' --prop regex=true --prop color=red
# Replace text
officecli set doc.docx / --prop find=draft --prop replace=final
# Replace + format
officecli set doc.docx '/body/p[1]' --prop find=TODO --prop replace=DONE --prop bold=true
# Bulk: color all dates red across all paragraphs
officecli set doc.docx / --prop 'find=\d{4}-\d{2}-\d{2}' --prop regex=true --prop color=red
# Replace in header
officecli set doc.docx '/header[1]' --prop find=Draft --prop replace=Final
PPT find works the same way:
# Format matched text
officecli set slides.pptx '/slide[1]/shape[1]' --prop find=weather --prop bold=true --prop color=red
# Regex
officecli set slides.pptx '/slide[1]/shape[1]' --prop 'find=\d+%' --prop regex=true --prop color=red
# Replace across all slides
officecli set slides.pptx / --prop find=draft --prop replace=final
# Replace + format
officecli set slides.pptx '/slide[1]/shape[1]' --prop find=TODO --prop replace=DONE --prop bold=true
# Replace in table
officecli set slides.pptx '/slide[1]/table[1]' --prop find=old --prop replace=new
Path controls search scope: / = all slides, /slide[N] = single slide, /slide[N]/shape[M] = single shape, /slide[N]/table[M] = table, /slide[N]/notes = notes pane.
Known limitation: Notes pane find+format writes correctly, but
getreturns plain text only — run-level formatting cannot be verified via CLI.
Behavior matrix:
| Props | Effect |
|---|---|
find + format props | Split runs, apply format to matched text |
find + replace | Replace matched text |
find + replace + format props | Replace text and apply format to new text |
regex=true to enable regex matching: --prop 'find=\d+%' --prop regex=true
{"props":{"find":"\\d+%","regex":"true","color":"FF0000"}}/ = body only (excludes headers/footers), /header[1] = first header, /footer[1] = first footer, /body/p[1] = specific paragraph, etc.find= matches nothing, the command succeeds with no changes (no error)--json output includes a "matched": N field indicating the number of matches found--prop 'find=(?i)error' --prop regex=truefind: / find= matches work across run boundaries — text split across multiple runs is still foundExcel limitations: Excel only supports find + replace (text replacement). find + format props (formatting matched text without replacing) is not supported in Excel — use Word or PowerPoint for that. In Excel, find without replace is treated as an unsupported property.
officecli add <file> <parent> --type <type> [--prop ...]
officecli add <file> <parent> --type <type> --after <path> [--prop ...] # insert after anchor
officecli add <file> <parent> --type <type> --before <path> [--prop ...] # insert before anchor
officecli add <file> <parent> --type <type> --index N [--prop ...] # insert at position (legacy)
officecli add <file> <parent> --from <path> # clone existing element
Insert position (--after, --before, --index are mutually exclusive):
--after "p[@paraId=1A2B3C4D]" — insert after the anchor element (short or full path)--before "/body/p[@paraId=5E6F7A8B]" — insert before the anchor element--index N — insert at 0-based position (legacy, prefer --after/--before)Element types (with aliases):
| Format | Types |
|---|---|
| pptx | slide, shape (textbox), picture (image/img), chart, table, row (tr), connector (connection/line), group, video (audio/media), equation (formula/math), notes, paragraph (para), run, zoom (slidezoom) |
| docx | paragraph (para), run, table, row (tr), cell (td), image (picture/img), header, footer, section, bookmark, comment, footnote, endnote, formfield, sdt (contentcontrol), chart, equation (formula/math), field, hyperlink, style, toc, watermark, break (pagebreak/columnbreak) |
| xlsx | sheet, row, cell, chart, image (picture), comment, table (listobject), namedrange (definedname), pivottable (pivot), sparkline, validation (datavalidation), autofilter, shape, textbox, databar/colorscale/iconset/formulacf (conditional formatting), csv (tsv) |
Text-anchored insert (--after find:X / --before find:X):
The --after and --before flags accept a find: prefix to locate an insertion point by text match within a paragraph.
# Insert run after matched text (inline, within the same paragraph)
officecli add doc.docx '/body/p[1]' --type run --after find:weather --prop text=" (sunny)"
# Insert table after matched text (block — auto-splits the paragraph)
officecli add doc.docx '/body/p[1]' --type table --after "find:First sentence." --prop rows=2 --prop cols=2
# Insert before matched text
officecli add doc.docx '/body/p[1]' --type run --before find:weather --prop text="["
PPT text-anchored insert (inline only):
officecli add slides.pptx '/slide[1]/shape[1]' --type run --after find:weather --prop text=" (sunny)"
officecli add slides.pptx '/slide[1]/shape[1]' --type run --before find:weather --prop text="["
PPT only supports inline types (run) with find: anchors — block-type insertion is not supported.
Clone: officecli add <file> / --from '/slide[1]' — copies with all cross-part relationships.
Run officecli <format> add for all addable types and their properties.
officecli move <file> <path> [--to <parent>] [--index N] [--after <path>] [--before <path>]
officecli swap <file> <path1> <path2>
officecli remove <file> '/body/p[4]'
When using --after or --before, --to can be omitted — the target container is inferred from the anchor path.
Stops on first error by default. Use --force to continue past errors.
# Via stdin
echo '[
{"command":"set","path":"/Sheet1/A1","props":{"value":"Name","bold":"true"}},
{"command":"set","path":"/Sheet1/B1","props":{"value":"Score","bold":"true"}}
]' | officecli batch data.xlsx --json
# Via --commands (inline, no stdin needed)
officecli batch data.xlsx --commands '[{"op":"set","path":"/Sheet1/A1","props":{"value":"Done"}}]' --json
# Via --input (file)
officecli batch data.xlsx --input updates.json --force --json
Batch supports: add, set, get, query, remove, move, swap, view, raw, raw-set, validate.
Batch fields: command (or op), path, parent, type, from, to, index, after, before, props (dict), selector, mode, depth, part, xpath, action, xml.
JSON output is wrapped in an envelope: {"results": [...], "summary": {"total", "executed", "succeeded", "failed", "skipped"}}. On error, each failed result includes the original batch item for debugging. Large outputs automatically spill to a temp file.
Use when L2 cannot express what you need. No xmlns declarations needed — prefixes auto-registered.
officecli raw <file> <part> # view raw XML
officecli raw-set <file> <part> --xpath "..." --action replace --xml '<w:p>...</w:p>'
officecli add-part <file> <parent> # create new document part (returns rId)
raw-set actions: append, prepend, insertbefore, insertafter, replace, remove, setattr.
Run officecli <format> raw for available parts per format.
| Pitfall | Correct Approach |
|---|---|
--name "foo" | ❌ Use --prop name="foo" — all attributes go through --prop |
x=-3cm | ❌ Negative coordinates not supported. Use x=0cm or x=36cm |
PPT shape[1] for content | ❌ shape[1] is typically the title placeholder. Use shape[2] or higher for content shapes |
/shape[myname] | ❌ Name indexing not supported. Use numeric index: /shape[3] |
| Guessing property names | ❌ Run officecli <format> set <element> to see exact names |
| Modifying an open file | ❌ Close the file in PowerPoint/WPS first |
\n in shell strings | ❌ Use \\n for newlines in --prop text="..." |
officecli set f.pptx /slide[1] | ❌ Shell glob expands brackets. Always single-quote paths: '/slide[1]' |
This skill covers the officecli CLI basics. For complex scenarios, load the dedicated skill for better results:
| Scenario | Skill | Min Version | When to Use |
|---|---|---|---|
| Word documents | officecli-docx | v1.0.23 | Create, read, edit .docx — reports, letters, memos, proposals |
| Academic papers | officecli-academic-paper | v1.0.24 | Research papers, white papers with TOC, equations, footnotes, bibliography |
| Presentations | officecli-pptx | v1.0.23 | Create, read, edit .pptx — general slide decks |
| Pitch decks | officecli-pitch-deck | v1.0.24 | Investor decks, product launches, sales decks with charts and stat callouts |
| Morph PPT | morph-ppt | v1.0.24 | Morph-animated cinematic presentations |
| Excel | officecli-xlsx | v1.0.23 | Create, read, edit .xlsx — financial models, trackers, formulas |
| Data dashboards | officecli-data-dashboard | v1.0.24 | CSV/tabular data → Excel dashboards with KPI cards, charts, sparklines |
How to load: Ask your AI tool to enable the skill by name, or load the skill file from
skills/<skill-name>/SKILL.md.
'/body/p[3]' = third paragraph--index is 0-based (array convention): --index 0 = first positionvalidate and/or view issuesofficecli <format> <command> [element[.property]] instead of guessing