# brewdoc

> Fast, deterministic document-to-Markdown conversion for agent systems. Easy to install. No model weights, OCR or external services.

Local conversion after installation is offline and downloads no runtime components.
Supported formats and readable-file constraints are defined in FORMATS.md.
Version 0.4.1 added [selected ZIP/XML reader budgets](https://github.com/kochetkov-ma/brewdoc/blob/v0.7.0/FORMATS.md#zipxml-reader-limits)
for DOCX/PPTX and workbook metadata/artifacts, with refusal before output writes.
Native workbook values and total process memory are outside those bounds.

Version 0.5.0 added ODT/ODP text and tables, selected MIME email bodies and exact UTF-8
MD/TXT pass-through. It preserved workbook Unicode and exposed datetime precision and used
qualified DOCX Fallback-only selection. Selected PDF caption/table/math-spacing fixes and
textless-page facts were included.
Version 0.6.0 writes cleaner Markdown with minimal escaping, adds --body-only, accepts
documents through --url, decodes legacy HTML charsets and falls back to the whole page in
article mode. Version 0.7.0 adds --render-js auto and HTML --no-links, refuses captcha,
login and JavaScript shell pages with a plain reason, reports HTTP status, redirects and
charset in URL receipts, decodes mislabelled HTML charsets by fallback and writes code and
image links as standard Markdown links. FORMATS.md owns the limits.

- [Installation](https://github.com/kochetkov-ma/brewdoc/blob/v0.7.0/INSTALL.md): pip, uv/uvx, pipx, native downloads, PATH, update and removal.
- [CLI/API and receipts](https://github.com/kochetkov-ma/brewdoc/blob/v0.7.0/README.md): deterministic Markdown and machine-readable results, including article mode introduced in 0.4.0.
- [Format limits](https://github.com/kochetkov-ma/brewdoc/blob/v0.7.0/FORMATS.md): supported/refused inputs, omissions, resource budgets and package compatibility evidence.
- [Latest release](https://github.com/kochetkov-ma/brewdoc/releases/latest): concise features, actual assets and channel availability.
- Installation examples select 0.7.0. Use a channel listed as available in [that release](https://github.com/kochetkov-ma/brewdoc/releases/tag/v0.7.0). npm targets Linux x64/glibc and macOS ARM64; Windows uses GitHub native or Python.
- When listed as available in the release, `kochetkov-ma/brew/brewdoc` installs the complete bundle on macOS 15+ ARM64 or Linux x64/system glibc 2.35+; see installation for lifecycle commands.

## CLI and API

```sh
brewdoc file.pdf --out file.md
brewdoc --body-only paper.pdf --out paper.md
brewdoc book.xlsx --sheet Calc --sheet Inputs --out book.md
brewdoc --self-check
```

Conversion prints a JSON receipt first; without --out, Markdown follows on stdout.
Paths resolve against the caller's directory; --out creates parents. --sheet and
--artifact are repeatable and workbook-only. Parsed documents use brewdoc.markdown/2;
UTF-8 MD/TXT use brewdoc.markdown/passthrough/1 with exact bytes, no envelope and zero units.
--body-only (API body_only=True) drops the title, metadata, omissions, contents and unit
anchors; unit headings stay only for several units and the receipt is unchanged.
Local receipts are brewdoc.receipt/1, URL receipts brewdoc.receipt/2.
Exit 0 means conversion, 1 refusal and 2 invalid usage. Success does not prove fidelity.

```python
import brewdoc
code, receipt, markdown = brewdoc.run("book.epub", out="book.md")
markdown, tally = brewdoc.render_html("page.html")
```

Local run/render APIs and self-check never acquire URLs.

## Deliberate HTTP acquisition

```sh
brewdoc --url https://example.com --render-js --timeout 30 --out page.md
brewdoc --url https://arxiv.org/pdf/1706.03762 --out attention.md
brewdoc --url https://example.com --render-js auto --no-links --out page.md
```

--url deliberately fetches a public HTTP(S) page or supported document; Content-Type and
magic bytes pick the route. DNS, destinations, redirects,
TLS, requests and byte budgets are validated. The default waiting budget is 30
seconds. JS uses installed/bundled components and may preserve valid partial
content on timing-only expiry; hard failures refuse. Ordinary requests use GET;
only the narrow credential-free HN Algolia XHR POST exception is admitted.
Bare --render-js means always; --render-js auto runs JS only on a JavaScript shell page.
Prefer --render-js auto for unknown pages: always runs JS on every page and can hit
an engine quota on a page that static mode reads. Pages that need more than the
bounded engine refuse, such as crates.io.
Captcha, bot challenge, login and shell pages refuse at stage challenge or
javascript_required with a plain reason; the receipt names the challenge and HTTP status.
--no-links (HTML only) keeps link text and discloses omitted destinations in the receipt.
See the format limits for supported JS runtimes and prerequisite refusals.

## Article mode

Introduced in 0.4.0; use a channel listed as available in the release. Python JS capture
needs `brewdoc[render-js]==0.7.0`; supported standalone bundles include its prerequisites.

```sh
brewdoc page.html --article --out article.md
brewdoc --url https://example.com/article --article --out article.md
brewdoc --url https://example.com/article --render-js --article --out article.md
```

Python: `run(path, article=True)` or `render_html(path, article=True)`. Local APIs stay offline.
Roots are visible main elements or role=main, or outer article elements when no main exists;
articles outside a main are dropped when one exists. role=article and class names do not select.
Hidden and page-chrome landmark subtrees are skipped; a header inside an article or a nav inside
main stays.
Peer articles and multiple mains render in DOM order; nested articles stay inside their outer
article. With no rendered candidate, the whole clean page renders and the receipt adds
`article_fallback: true`; selection never refuses for page structure. No text scoring, title
replacement or inferred author/date metadata. When content is selected, receipt `not_carried`
and Markdown Known omissions include
`body content outside the selected article or main subtree; complete page coverage is not claimed`.
See [the article contract](https://github.com/kochetkov-ma/brewdoc/blob/v0.7.0/FORMATS.md#article-selection).

[Earlier source-package CI](https://github.com/kochetkov-ma/brewdoc/actions/runs/38431645006) passed on
Ubuntu 22.04 x64/glibc with CPython 3.13.15, macOS 15 ARM64 and Windows Server 2025 x64
with CPython 3.14.7. Each passed four independent wheel/sdist base/optional installs;
optional JS executed on Unix and refused before requests on Windows. These are selected
tested patches. CPython 3.12.14 retains local macOS full-suite and existing CI smoke evidence.
These results predate the 0.5.0 changes.
Source-package checks are separate from standalone binaries; other OS/minor combinations,
free-threaded CPython and other architectures remain untested here.
See [package compatibility](https://github.com/kochetkov-ma/brewdoc/blob/v0.7.0/FORMATS.md#python-package-compatibility).

## Related projects

- [BrewPage](https://brewpage.app): free hosting for HTML, Markdown, JSON and files; each upload returns a shareable HTTPS link.
- [BrewPage MCP server](https://github.com/kochetkov-ma/brewpage-openapi/tree/main/mcp-server): npm package brewpage-mcp for publishing to BrewPage from MCP clients.
- [claude-brewcode](https://github.com/kochetkov-ma/claude-brewcode): plugin suite for Claude Code. Its brewdoc plugin is a different tool from this brewdoc CLI.
- [claude-brewcode documentation](https://doc-claude.brewcode.app): installation and skill reference for that suite.
- [brewcode.app](https://brewcode.app): home site of the Brew projects.

This is a readable entry point, without a promise of automatic client discovery.
