office-transform
Derive new Office files from structural selections without touching the original
Install / Use
npx skills add CherryHQ/cherry-studio --skill office-transformInstalls into whichever agent you are using.
SKILL.md
Installable skill definition
Quality Score
Category
Data & AnalyticsSupported Platforms
Our assessment of office-transform
office-transform scores 87/100 on our quality scale, 55th of 158 Data & Analytics skills we index (top 35%).
Its SKILL.md is 18 KB long, well organised into 12 sections with 5 code examples: a thorough specification that gives an agent plenty to work with.
With 52,134 GitHub stars, it is one of the more widely adopted skills in the catalogue.
Maintenance, license and trust
- The repository was last updated today, so office-transform is actively maintained.
- It is released under AGPL-3.0, a copyleft license: you can use it, but modified versions you distribute must carry the same license.
- Its trust signals score 100/100, with no cautions. These come from repository metadata, not a code audit — read the skill file before letting an agent act on it.
office-transform compared with similar skills
All 4 of these similar skills score higher than office-transform; compare them before choosing.
| Skill | Score | Stars | Updated | Format |
|---|---|---|---|---|
| office-transform (this skill)by CherryHQ | 87 | 52.1k | today | SKILL.md |
| algorithmic-artby anthropics | 100 | 177.9k | 2d ago | SKILL.md |
| pptxby anthropics | 100 | 177.9k | 2d ago | SKILL.md |
| designby nextlevelbuilder | 100 | 130.2k | 3d ago | SKILL.md |
| ui-ux-pro-maxby nextlevelbuilder | 100 | 130.2k | 3d ago | SKILL.md |
Frequently asked questions
- How do I install office-transform?
- Run
npx skills add CherryHQ/cherry-studio --skill office-transform. The install tabs above show the steps for each supported agent. - Which AI agents does office-transform work with?
- It is written for Universal, as a SKILL.md file. Other agents that read the same format can often use it too.
- Is office-transform safe to use?
- It is AGPL-3.0-licensed and scores 100/100 on trust signals. Skills are instructions an agent will follow, so read the file before installing it and do not approve commands you do not understand.
- Is office-transform still maintained?
- The repository was last updated today, so office-transform is actively maintained.
Skill content
View source on GitHubname: office-transform description: Derive new Office files from structural selections without touching the original. Use when the user points at part of a spreadsheet, Word document, PDF, or PowerPoint deck (a worksheet range, paragraph, page, slide, shape, or a pasted selection-ref block) and wants it extracted, converted, or edited — the result is always a NEW file; the source file is never modified. Covers xlsx range extraction to csv/markdown/xlsx, docx paragraph extraction and text replacement, pdf page extraction, pptx slide/shape/table-cell extraction and editing, and targeted xlsx cell edits. version: 1.0.0
Office Transform
Turn a structural selection of an Office/PDF document into a new derived file.
Two invariants hold for every operation:
- The source file is read-only. Every result is a new file; both scripts refuse to write to the source path or overwrite an existing file. Never work around this with ad-hoc shell edits to the original.
- Anchors are structural. Selections address the document's own coordinates — worksheet + A1 range, body-level paragraph ordinal, one-based page number — never screen or DOM positions.
When to use which tool
- Only need to read a document to summarize or answer questions → use
mcp__cherry-tools__to_markdowninstead (see the cherry-tool-guide skill); it is lossy but built for reading. - The user wants a file out — an extracted range, a converted fragment, an edited copy → this skill.
Input: selection references
Chat surfaces may hand you a fenced selection-ref block:
{"path": "/abs/report.xlsx", "anchor": {"format": "xlsx", "sheet": "Sheet1", "range": "A1:C10"}, "excerpt": "…", "fileStamp": {"size": 1024, "mtimeMs": 1700000000000}}
The anchor object is exactly what the scripts take via --anchor.
Freshness check. fileStamp.mtimeMs is milliseconds since the Unix epoch, floored to
whole milliseconds. Do not compare it to stat's default output: stat -f %m (macOS) and
stat -c %Y (GNU) give whole seconds, so multiplying by 1000 loses the sub-second part and
never matches. Read whole milliseconds the way the app wrote them:
uv run python -c "import os,sys;st=os.stat(sys.argv[1]);print(st.st_size, st.st_mtime_ns//1_000_000)" '/abs/report.xlsx'
Treat the file as changed when the size differs, or when the mtimes differ by more than 2 ms.
The tolerance is small on purpose: both sides floor the same nanosecond timestamp to
milliseconds, so they agree exactly, and the couple of milliseconds only covers the rounding
of a double that can no longer represent current epoch milliseconds exactly. A wider window
would hide real edits — a same-size change made within it would read as unchanged, which is
why the anchor check below is not optional. On a change, tell the user the file changed since
they selected and ask them to re-select — never silently re-anchor.
Anchor check. Before a patch-copy edit, also verify the anchor still points where the
user thinks: extract the anchored region and compare its text with the reference's excerpt,
normalizing both sides the same way (NFC, collapse each whitespace run to one space, trim).
The two are not equal and are not meant to be — the excerpt is what the user selected, while
the extract is the whole region an edit would replace. A selection sitting inside one region
makes the excerpt the shorter side; a selection running past that region anchors to where it
starts, which makes it the longer one. So the test is containment, whichever way round fits:
the excerpt appears inside the extract, or the extract's tail is where the excerpt begins.
Only when neither holds has the anchor stopped pointing at the selection — then stop and tell
the user, never edit at a mismatched anchor and never go searching for a "close enough"
location. Two cases need care because the two sides are not directly comparable as-is:
- xlsx: the
excerptis tab-separated cells and newline-separated rows, while extraction writes csv or md. Compare cell values, not the raw file — read the csv with a csv reader and join the fields with single spaces before normalizing, so,and|never count as a difference. That join drops the cell boundaries on both sides, so a match proves the text is unchanged, not the grid — the same words re-split across cells still reads as a match. The freshness check above is what catches that; never skip it on a passing anchor check. Number formats are the second asymmetry: theexcerptholds what Excel displays, because that is what the user pointed at (1,234.50,45.67%), while extraction writes the stored value (1234.5,0.4567). Neither side is wrong, so a formatted cell never matches literally. Compare those cells by value, and leave them out of the joined strings so the containment test runs on the text cells alone. Dates, times and durations are the same asymmetry seen from the other side: extraction writes one fixed shape (2024-01-03,2024-01-03 15:04:05,26:30:00) rather than the cell's format, so treat a cell that differs only in date or time presentation as a format difference too. A difference that is only the number format is not a moved anchor, and stopping on one sends the user back to re-select a selection that never moved. - docx with
charRange: the slice is only part of what patch-copy compares and replaces — it rewrites the whole paragraph. Extract the paragraph withoutcharRangeas well, and read "Edit docx" below before writing.
Users may also describe the region in words ("sheet 2, columns A through C"); build the anchor JSON yourself, confirming the worksheet name or paragraph if ambiguous.
Anchor shapes:
| Format | Anchor |
| --- | --- |
| xlsx | {"format":"xlsx","sheet":"Sheet1","range":"A1:C10"} (range may be one cell) |
| docx | {"format":"docx","paragraph":3,"paraId":"502E8D33","charRange":[0,12]} (paraId optional = the paragraph's w14:paraId, resolved first when present; charRange optional; ordinal counts body-level paragraphs only, tables excluded) |
| pdf | {"format":"pdf","page":3,"charRange":[0,120]} (charRange optional, applies to extracted text) |
| pptx | {"format":"pptx","slide":2,"nodeId":"4","paragraph":0} or {"format":"pptx","slide":2,"nodeId":"7","tableCell":{"row":1,"col":0}} (slide is one-based; nodeId is the OOXML shape id — omit for the whole slide; paragraph and tableCell are optional, mutually exclusive, and only valid together with nodeId) |
Operations
Scripts live in this skill's scripts/ directory; resolve paths relative to this
skill folder. The library-edit recipes routed to below live in references/ beside them.
Python dependencies are per-format and provided at invocation time via
uv run --with <pkg> (the bundled-shell idiom — do not pip install globally).
Which route an edit takes follows from the library that can write the format: openpyxl
drops charts and drawings on a round-trip, which is why xlsx edits go through patch-copy,
while python-pptx keeps XML it does not understand, which is why pptx edits go through
the library.
Extract — pull the anchored region into a new file
Always single-quote paths — real documents have spaces in their names (Q1 report.xlsx).
If a path itself contains a single quote, close and reopen the quoting around it:
'/abs/Bob'\''s deck.pptx'.
uv run --with openpyxl python scripts/office_extract.py \
--file '/abs/report.xlsx' \
--anchor '{"format":"xlsx","sheet":"Sheet1","range":"A1:C10"}' \
--out '/abs/report-q1-range.csv'
The output format is inferred from --out's extension:
| Source | Dependency (--with) | Output formats |
| --- | --- | --- |
| xlsx | openpyxl | xlsx, csv, md |
| docx | 'python-docx>=1.1,<2' | docx, txt, md |
| pdf | pypdf | pdf (page copy), txt, md |
| pptx | python-pptx | txt, md (slide, shape, paragraph, or table-cell text) |
The docx pin is not optional. Patch-copy's expectText gate compares a paragraph read
with python-docx against the same paragraph read by the script's own paragraph_text,
which reproduces python-docx's Paragraph.text element for element. That equivalence was
checked against 1.x; a release that changes what .text spells would make the gate refuse
paragraphs nobody edited. Quote the specifier — > and < are redirects to a shell.
xlsx extraction reads computed values (data_only), so formula cells yield their last
saved result. docx extraction to docx carries text only, not run styling.
Patch-copy — derive an edited copy, standard library only
uv run python scripts/office_patch_copy.py \
--file '/abs/report.xlsx' \
--edits '{"format":"xlsx","sheet":"Sheet1","cells":{"B2":42,"C3":"hello"}}' \
--out '/abs/report-updated.xlsx'
OOXML packages are ZIPs of XML parts. Patch-copy copies every part byte-for-byte and
re-serializes only what an edit reaches — the target worksheet or word/document.xml, plus
the workbook bookkeeping noted below for xlsx — so styles, charts, images, and macros in
untouched parts survive exactly. Edit shapes:
{"format":"xlsx","sheet":"S","cells":{"B2":42,"C3":"text","D4":true}}— numbers, strings, and booleans; an existing formula in an edited cell is replaced by the value. Patch-copy does not recalculate, so a formula reading an edited cell keeps the value it last cached; every write setsfullCalcOnLoadinxl/workbook.xmlso Excel recomputes those on open. Replacing a formula also dropsxl/calcChain.xml(a recalculation cache Excel rebuilds), along with the content-type and relationship entries that would otherwise point at a part no longer there; keeping a chain entry for a cell that no longer has a formula makes Excel report the derived file as damaged. Cells in a shared, array, or data-table formula group are refused — the expression lives in one member and the others only reference it, so overwriting a member would strip the formula from cells you never named. Rewrite such a range withopenpyxl. Coordinates outside the worksheet grid (past XFD or row 1048576) are refused too.{"format":"docx","replacements":[{"paragraph":3,"text":"new text","paraId":"502E8D33","expectText":"old text"}]}— the paragraph keeps its paragraph style and the first run's character style; extra run-level styling within that one paragraph is flattened into the new text.textmust be the complete new paragraph. The whole body paragraph is replaced, andcharRangedoes not narrow that — feeding back acharRangeslice astextsilently discards the rest of the sentence. The output is exactlyw:p > [w:pPr] + w:r > [w:rPr] + w:t, so a paragraph holding anything that shape cannot carry is refused, not silently stripped. That covers bookmarks, comment anchors and fields (their start/end can pair across paragraphs, and rewriting one half unbalances the document), images, embedded objects, footnote/endnote references, hyperlinks, tracked changes and moves, content controls, equations — and anything else not on the short allow-list, including elements from namespaces that did not exist when this was written. A droppedw:delwould even accept a pending deletion on the user's behalf. A page or column break (<w:br w:type="page"/>) is refused for a quieter reason: it carries no characters, so the extract reads the text on either side of it as one string and the anchor check cannot see that the rewrite would delete it. A bare<w:br/>line break still passes. To edit such a paragraph, see "Edit docx" below — do not reach forParagraph.text, which destroys exactly the same content, only silently.paraId(optional) is resolved before the ordinal; a disagreement between the two is an error, never a silent pick.expectText(optional but strongl
Truncated for display — read the full file on GitHub.
Related Skills
algorithmic-art
177.9kCreating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems.
pptx
177.9kUse this skill any time a .pptx or .potx file is involved in any way — as input, output, or both. This includes: creating slide decks, pitch decks, or presentations; reading, parsing, or extracting text from any .pptx or .potx file (even if the extracted content will be used elsewhere, like in an em…
design
130.2kComprehensive design skill: brand identity, design tokens, UI styling, logo generation (55 styles, Gemini, Atlas Cloud, or MuAPI AI), corporate identity program (50 deliverables, CIP mockups), HTML presentations (Chart.js), banner design (22 styles, social/ads/web/print), icon design (15 styles, SVG…
ui-ux-pro-max
130.2kUI/UX design intelligence for web, mobile, and desktop. This skill should be used when designing, building, reviewing, or fixing interfaces, including pages, components, design systems, accessibility, interaction, responsive layout, typography, color, charts, and stack-specific UI implementation.
Languages
Trust signals
From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.
