Office documents
Agent tools and skills to read docx/xlsx/pdfs
npx -y skills add mickzijdel/readoc --skill office-documentsAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Use when you need to read OR edit the contents of Office documents (.docx, .xlsx, .xlsm, .pdf) or explore/search a folder of mixed documents. Provides the readoc, readir, and editdoc CLIs. Reach for this whenever a task involves reading or modifying a Word doc, Excel sheet, PDF, or a directory of such files.
SKILL.md
12.2 KB, ~3.1k tokens by cl100k_base, as published. Nobody here has run it
Reading & Editing Documents
Three CLIs ship with this plugin under ${CLAUDE_PLUGIN_ROOT}/bin/. They are also
symlinked onto PATH as readoc, readir, and editdoc, so you can usually
call them by bare name.
The standard Read/Edit/Write tools cannot parse or modify
.docx/.xlsx/.xlsm/.pdf. Use these instead. The readers never truncate
content — spreadsheet cells, pages, and tables are emitted in full.
readoc — read specific files
readoc file.docx # read a Word document
readoc file.xlsx # read an Excel spreadsheet
readoc file.pdf # read a PDF
readoc file1.docx file2.xlsx # read several; each gets a header box
readoc --no-comments file.docx # omit comments (included by default)
# Search inside one file (structure-aware context — see "Search modes" below)
readoc search file.docx "deadline" # default: line context
readoc search file.docx "deadline" --context-paragraphs 1
readoc search report.pdf "risk" --context-chars 120
readoc search budget.xlsx "Q3" # cell-coordinate aware
Each file is preceded by an unambiguous header box (path (Type, size)), so
concatenated output is never confusing. Headings, tables, and sheets are
preserved; long cells overflow their column rather than being cut.
Comments
Document comments are included by default, appended per file in a trailing
--- Comments --- block with the author and an anchor — Word comments
(author: text), Excel cell comments ([Sheet!A1] author: text), and PDF
annotations / sticky notes ([Page N] author: text). Pass --no-comments to
suppress them (body text is unaffected). The block is only emitted when a file
actually has comments.
readir — explore / read / search a folder
# Tree — explore structure
readir tree path/to/folder # file tree with sizes
readir tree path/to/folder --summary # + extension/size summary
readir tree path/to/folder --max-depth 1 # depth: 1 = top level only, 2 = one level deep
readir tree path/to/folder --filter docx,pdf # only certain extensions
# Read — concatenate all readable files (same header-box convention as readoc)
readir read path/to/folder
readir read path/to/folder --filter md,docx # only specific types
readir read path/to/folder --exclude pdf # skip certain types
readir read path/to/folder --max-size 2000 # KB ceiling; files over it are
# SKIPPED (and listed), not truncated
readir read path/to/folder --max-depth 1
readir read path/to/folder --no-skip-report
readir read path/to/folder --no-comments # omit comments (included by default)
# Search — grep across all docs (incl. docx/xlsx/pdf)
readir search path/to/folder "query"
readir search path/to/folder "query" --context 5 # lines of context (default 2)
readir search path/to/folder "query" --context-paragraphs 1 # whole paragraphs of context
readir search path/to/folder "query" --context-chars 120 # character window of context
readir search path/to/folder "query" --context-rows 3 # rows of context (spreadsheets)
readir search path/to/folder "query" --filter docx
readir search path/to/folder "query" --no-comments # don't search comment text
Search modes
Both readir search and readoc search are structure-aware, doing what a
plain grep cannot:
- Prose (
.docx,.pdf,.md,.txt, …) — choose one context unit (mutually exclusive, default--contextlines):--context N— N lines around each match (the original behaviour).--context-paragraphs N— the matching paragraph ± N whole paragraphs (units marked¶); respects real paragraph boundaries.--context-chars N— a ±N character window around each match, merging overlapping windows. The matched term is wrapped in»…«.
- Spreadsheets (
.xlsx,.xlsm) — always searched cell by cell. Each hit reportsSheet!Cellplus its column header (row 1) and row header (column A), e.g.Budget!C7 [col "Amount" / row "Q3"] <value>, followed by a window of the matching rows ±--context-rows(default 2). Cell comments are searched too.
You can still pipe to ordinary tools for line-oriented work
(readoc file.docx | grep -A3 deadline, … | head, find ./docs -name '*.docx')
— the built-in modes add the structure-aware context that piping can't.
Comments are extracted by default for readir read (and are searchable by both
search commands), in the same --- Comments --- form as readoc. Use
--no-comments to opt out.
Supported formats: .md .txt .csv .docx .xlsx .xlsm .pdf .json .yaml .yml.
The one deliberate limit
readir read defaults to skipping files larger than 500 KB to avoid dumping
huge files unintentionally. Skipped files are listed in the skip report
(never silently dropped, never truncated). Raise the ceiling with
--max-size <KB> when you genuinely need a large file.
editdoc — edit .docx / .xlsx / .xlsm files
editdoc is to binary Office documents what the built-in Edit tool is to text
files. It takes the target file as an argv and reads a JSON edit spec from
stdin — a single object, or an array of objects applied as one atomic batch.
The edit type is chosen by which keys the object carries:
# 1) Word — exact in-paragraph replace (mirrors the Edit tool)
echo '{"old_string": "due Monday", "new_string": "due Friday"}' | editdoc report.docx
echo '{"old_string": "TODO", "new_string": "Done", "replace_all": true}' | editdoc report.docx
# 2) Word — paragraph / range replace (structural)
echo '{"start_contains": "Quarterly results", "new_text": "Replaced paragraph."}' | editdoc report.docx
echo '{"start_contains": "First old para", "end_contains": "last old para", "new_text": "New A\nNew B"}' | editdoc report.docx
echo '{"start_contains": "Stale section", "new_text": ""}' | editdoc report.docx # "" deletes
# 3) Excel — set a cell
echo '{"sheet": "Budget", "cell": "B2", "value": "1250"}' | editdoc book.xlsx
# Batch: an array is applied all-or-nothing
echo '[{"old_string":"a","new_string":"b"},{"sheet":"S","cell":"A1","value":1}]' | editdoc f.docx
# Workbooks with charts, images, comments or macros need no special handling
echo '{"sheet":"Budget","cell":"B2","value":42}' | editdoc book.xlsx
docx edit types
- In-paragraph replace —
{"old_string", "new_string", "replace_all"?}.old_stringis matched exactly (whitespace included) against each paragraph's visible text, across body paragraphs and table cells. It must be unique in the document; if it appears more than once you get a loud error — add surrounding context to disambiguate, or set"replace_all": true. A match cannot span a paragraph break (no newline inold_string). - Paragraph / range replace —
{"start_contains", "end_contains"?, "new_text"}.start_contains(and optionalend_contains) each uniquely identify a paragraph; the inclusive block between them is replaced bynew_text, split on\ninto one-or-more paragraphs that inherit the original's style. Emptynew_textdeletes the block. A range must stay within one container (the body, or a single table cell).
xlsx edit type and value coercion
- Cell set —
{"sheet", "cell", "value"}. The sheet must exist and the cell must be a valid reference (B2). A stringvaluebecomes a number only when it round-trips exactly:"1250"→1250,"-42"→-42,"3.5"→3.5, but codes that would be corrupted stay text — leading zeros ("00501"), underscores ("1_000"), scientific notation ("1e3"), and non-finite values ("nan","inf"). Pass a JSON number (42,3.5) to force numeric storage; a JSON boolean stores a boolean; JSONnullclears the cell (keeping its formatting). Text is written as an inline string rather than through the workbook's shared-string table, so an edit changes exactly one zip part and cannot disturb another cell that happened to share the same string.
Why a naive find/replace fails on Word (and how editdoc handles it)
Word stores a visible sentence as a chain of formatting runs — "the report is due" might be ["the ", "report", " is due"] with the middle run bold. A
literal search on the raw XML usually misses it. editdoc reconstructs each
paragraph's text, matches there, and rewrites the runs so the replacement
inherits the first matched run's formatting while untouched text keeps its
own. After each edit it prints a confirmation line plus the affected paragraph
rendered as Markdown (**bold**, *italic*, heading #) so you can verify the
formatting landed as intended.
Multi-paragraph rewrites
In-paragraph replace can't cross a paragraph break. To rewrite several
consecutive paragraphs at once, use a single range replace: anchor the first
paragraph with start_contains, the last with end_contains, and pass the full
new text in new_text (newline-separated for multiple paragraphs). This turns,
say, a five-paragraph block into three paragraphs in one atomic edit — no
per-paragraph delete chain.
Safety
Every edit is validated before the file is written; if any edit in a batch fails,
nothing is written (the file stays byte-identical) and editdoc exits
non-zero with a specific error. Saves go through a temp file + atomic replace, so
a crash never corrupts the original. Read the result back with readoc to
confirm.
Limitations
- Hyperlinks / fields / footnote refs: a paragraph whose text isn't carried
entirely by ordinary runs (it contains a hyperlink, field, or footnote
reference) is refused rather than rewritten — editing it would scramble the
non-run content. Delete the whole paragraph (range op with empty
new_text) or adjust the document instead. Such a paragraph can still be matched/deleted, just not rewritten in place. - Nested tables: only top-level table cells are reached; text inside a table nested within a cell is not found.
- xlsx surgical editing: an
.xlsx(or macro-enabled.xlsm) is patched as the zip it is — only the worksheet part the edit touches is rewritten, and every other entry is copied through byte-for-byte in its original order and compression. Charts, pivot tables, images, cell comments and their VML anchors, threaded comments, macros, in-cell rich text, and SharePoint'scustomXml/[trash]parts therefore all survive an edit intact — there is no lossy mode and no flag to pass.--force/--allow-lossyare still accepted but ignored. Whateditdocrefuses instead of guessing, each with a specific error: a cell holding a formula (overwriting it would strand the workbook's calculation chain — edit the formula's inputs instead); a cell inside a merged range that isn't the range's anchor; text containing control characters or an_xHHHH_sequence (Excel would decode it back to a character); and a worksheet whose rows or cells carry nor=position or sit out of order. When a workbook has formulas, editdoc setsfullCalcOnLoadso Excel recomputes cached results the edit invalidated. - docx orphan parts: a
.docxis edited through python-docx, which walks the package's relationship graph — parts reachable from it (includingcustomXml) survive, but a part in the zip that nothing references is dropped. - No
.pdfediting (no clean text reflow).
Requirements
uv must be on PATH, it's the only prerequisite.
Each CLI declares its Python dependencies inline via PEP 723 and runs through
uv run --script, so uv installs them into a cached environment automatically on
first use: readoc/readir use python-docx, openpyxl, and pymupdf (for
.docx, .xlsx, and .pdf); editdoc uses only python-docx (spreadsheets
are patched with the standard library's zipfile).
Gives 0 of the 12 instructions most pdf office docs skills give in ~3.1k tokens
Counted across 636 of the 690 authors here whose files we hold, read 2026-08-07
- extract text using pdfplumberin 89 of 636, across 23 files
- create PDFs using reportlabin 83 of 636, across 16 files
- read forms.md to fill out pdf formsin 80 of 636, across 13 files
- OCR scanned PDFs using pytesseractin 77 of 636, across 10 files
- merge or split PDFs using qpdfin 70 of 636, across 3 files
- use excel formulas instead of hardcoded calculated valuesin 68 of 636, across 13 files
- unpack edit xml and repack existing documentsin 63 of 636, across 8 files
- document sources for hardcoded valuesin 61 of 636, across 9 files
- write minimal python code without unnecessary commentsin 59 of 636, across 7 files
- run the recalculation script after adding or modifying formulasin 59 of 636, across 7 files
- fix all identified formula errors and recalculatein 58 of 636, across 6 files
- format years as text stringsin 57 of 636, across 5 files
Said here and by no other author read
- use readoc for office documents
- use readir to explore folders
- pipe json edit spec to editdoc
- verify edits with readoc
- pass a json number to force numeric storage
- use range replace to rewrite multiple paragraphs
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.