Read file contents with line numbers, a range via `offset`/`limit`, and code structure via `symbols`/`zoom`. Prefer it over shell `cat`.
{{path_policy}}
A directory lists its contents instead of erroring; `mode`, `offset` and `limit` apply only to file reads and are silently ignored for a directory.
`mode` selects `content` (default), `symbols` (AST-level symbols with line ranges — the quickest way to map a large file) or `zoom` (one symbol's full source by name, with `symbol`). `symbols` and `zoom` work only on supported code formats — Rust, JS/TS, Python, Go, C, Ruby, SQL, Markdown, JSON, TOML, CSS, HTML and shell — not on arbitrary files; the schema carries the rest. Content-mode output is truncated to a small budget (~5 KB), so read big files in slices; binary files are read with lossy UTF-8, except the document containers below and a background shell session's own output file (read as a program's output).
A content-mode raster image (PNG, JPEG or WebP) is attached to the conversation as a native image instead of lossy text; reading it twice references the already-attached image.
A content-mode document container — PDF, Word `.doc`/`.docx`/`.docm`, Excel `.xls`/`.xlsx`/`.xlsm`, PowerPoint `.ppt`/`.pptx`/`.pptm` — is converted, not returned as bytes: an encrypted file is `password-protected`, an old binary is read as that whatever its name, and its text is returned or its path if long. A chart is described where it sits (a layout's or master's chart counted): its kind under `{{chart_header}}` — `{{chart_none}}` when it names none — its `{{chart_title}}`, any `{{chart_text}}` or `{{chart_label}}`, and one `{{chart_series}}` line per series — numbered `{{chart_series_numbered}}` when the part names none — of category: value, a bubble's `{{chart_size}}` beside its value, excess counted; one without readable values says so (`{{chart_no_values}}`, `{{chart_attached}}`, `{{chart_unreadable}}` or `{{chart_extended}}`) and stays in the report. Every reading says what it did not show, by kind ({{unshown_report}}); a missing image keeps the note saying why, and what a reading never looks at is not reported. Pages and images with no text are attached as images (max 5; more by path), a page whose text was read is not drawn, its drawings out of reach, and one with neither text nor images, or that will not convert, says so.
An old binary `.doc`/`.xls`/`.ppt` is read only, in its family's shape. Hidden sheets and slides, merged ranges, cell comments, hyperlinks, defined names and formulas are never read or shown; a presentation gets no title, hidden-slide, diagram or comment mark, and a document's stories come as a labelled block each — `{{doc_headers_footers}}:`, `{{doc_footnotes}}:`, `{{doc_endnotes}}:`, `{{doc_comments}}:` — with no comment author.
A `.xlsx`/`.xlsm` workbook comes back as `Sheet "<name>":` blocks in workbook order, one line per valued cell, `A1: 42`. A formula with no stored value is shown as its formula, one with both as `42 (=SUM(B1:B2))`; an empty sheet says so, one holding a chart says `{{chart_sheet_mark}}`. Write back the stored number, not its displayed text; an unreproducible format shows the stored number, marked `{{mark_stored}}`. Hidden rows, columns and sheets are marked, a merged range where it is; comments follow the sheet's cells, a hyperlink is marked on its cell, and defined names follow the last sheet as `Defined names:`. A pivot table's values and a shape's or text box's text are never read.
A `.docx`/`.docm` is read beyond its body: a table is a `Table N:` block, one line per row under `R1:`, one `C2:` token per cell, a merged cell marked `(spans C1-C2)` and `(part of R1C1)` on each continuation. Headers and footers follow, one block per part, then footnotes, endnotes and comments, each indented under its heading, a definition nothing refers to saying so, with `[footnote 1]`, `[comment 1]` or `[comment 1 starts]`…`[comment 1 ends]` marking where it belongs. A page number or date field is `[field PAGE]`, and a text box's text is one indented `{{text_box_mark}}` block. A heading opens `[heading N]`; a link's content is followed by `(link: ADDRESS)`, `(link: internal)` inside the document, or nothing when no address is known.
A `.pptx`/`.pptm` yields each slide's text under a {{ppt_slide_label}} block, then its comments and replies under {{ppt_comments_label}} (each with the author the deck names) and its notes under {{ppt_notes_label}}: a declared title is marked `{{ppt_title_mark}}`, a hidden slide `{{ppt_hidden_slide_mark}}`, diagram text follows under `{{ppt_diagram_text_mark}}` (`{{ppt_diagram_text_lost_mark}}` when unreadable, `{{ppt_no_text_mark}}` for a slide or comment with none). A slide's layout and theme are not read.
A PDF's text comes page by page: a `Page <n>:` block per page with text, and a page with none, or one that could not be read, as an image. Text a page's crop box hides is not its text; an uncropped page reads as before. A `Description:` block leads when the file has one, naming its entries, dates in calendar form. Marks follow as an `Annotations:` section, one entry per mark as `<kind>[ (<author>)] on page <n>`, text below. A `{{pdf_form_fields}}:` section lists each field as `name (kind; options: …) [on page <n>]: value`, `{{pdf_no_value}}` when empty; kinds are {{pdf_field_kinds}}; options are named for radio/choice (long lists cut by `…`), a pushbutton or signature is not listed, an unnamed field and an option-less radio group or list box are marked {{pdf_not_fillable}}, and a form with none says so.
Unaccepted tracked changes are never passed off as final text: `[ins]…[/ins]` inserted, `[del]…[/del]` deleted, `[moved-here]…[/moved-here]` and `[moved-away]…[/moved-away]` moved, and `[ins ¶]`, `[del ¶]`, `[moved-here ¶]`, `[moved-away ¶]` revise a paragraph mark; a row or cell added or removed says `(inserted)` or `(deleted)`, and `[fmt]`, `[fmt ¶]`, `[fmt section]`, `(merge revised)`, `(formatting revised)` revise formatting alone. The document opens with an `Unaccepted tracked changes: …` line counting them and naming their authors.
Reading a credential-bearing file — `.env*`, a `.pem`/`.cer`/`.crt` certificate, `.key`/`.p12`/`.pfx`, or a known credential config (`.netrc`, `.npmrc`, `.pypirc`, `.git-credentials`, Maven `settings.xml`/`settings-security.xml`, Gradle `gradle.properties`/`init.gradle*`, cargo `credentials.toml`) — returns its content with the credential values masked. A secret stored under a name the masking does not recognise is returned whole.
Files larger than 10 MB are rejected, except document containers, which are accepted up to 50 MB.