* feat(skill): add anydoc core content and references
Add the anydoc skill content tree: SKILL.md (progressive-disclosure index
with frontmatter per ALLOWED_FIELDS), human-facing README, the five reference
files (formats, cli-reference, errors, workflows, sources), 24 committed
fixtures (valid + error cases), and a fixture-grounded eval manifest with 8
cases. Every documented behavior, exit code, and error message was verified
against the real pinned CLI (npx -y @firecrawl/anydoc@0.1.6); verbatim --help
and error transcripts are reproduced character-for-character.
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
* feat(skill): add anydoc wrapper script and unit tests
Implements scripts/anydoc, a stdlib-only Python wrapper around the pinned
@firecrawl/anydoc@0.1.6 CLI: convert/batch/info subcommands, global
--json/--dry-run, input and output pre-validation, friendly hints for the
no-OCR/encrypted/malformed/unsupported error classes, Node >= 20 and npx
availability checks, deterministic batch output naming with documented
duplicate/collision behavior, and exit codes 0/1/2. Adds offline unittest
suite (46 tests, real-CLI tests skip when npx is unavailable) and keeps the
wrapper contract documented in cli-reference.md and errors.md.
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
* feat(skill): ratchet anydoc evals to 14 grounded cases
Verify the pre-authored 8-case manifest and extend it with six
high-signal cases (PDF lower-fidelity pipeline, legacy .ppt table
flattening, ODP same-serializer, RTF, EPUB, CSV header promotion),
each grounded in real pinned-CLI runs against the committed fixtures.
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
* feat(skill): integrate anydoc into repo catalog and artifacts
Add the sorted anydoc catalog entry to README.md (between agent-skills
and api-design-and-evolution), regenerate the tracked catalog artifacts
(.claude-plugin/marketplace.json, .codex-plugin/plugin.json,
.agents/plugins/marketplace.json, llms.txt) with the ruby generators,
and add a routing note to documents/SKILL.md pointing office-document
to-markdown conversion at the anydoc skill.
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
* fix(skill): polish anydoc wrapper timeout, JSON shape, and docs
- run_cli raises CliTimeoutError on the 120s timeout; convert/batch with
--json now emit one parseable JSON error envelope (error_class "timeout")
on stdout before exiting, so --json always yields exactly one JSON doc
- batch JSON failure entries (pre-validation and CLI) now carry error_class
("io" for missing/dir inputs, mapped classes for CLI failures), so all
batch failure entries share the same shape
- build_cli_command places -o/-f before the -- separator for dash-leading
filenames, so `convert -f csv -- -weird` converts instead of misparsing
("unexpected second input"); absolute-path inputs unchanged
- workflows.md vault-ingestion recipe globs notes/* instead of docs/* and
warns to run from a temp/vault dir, never touching repo-root docs/
- unit tests: +6 (timeout envelope x4, batch error_class shape,
dash-leading filename); suite grows 46 -> 52
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
---------
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
10 KiB
CLI reference: the Any Doc CLI (pinned @firecrawl/anydoc@0.1.6)
Everything here was captured by running the pinned CLI on this machine
(npx -y @firecrawl/anydoc@0.1.6, version 0.1.6, Node v22). The CLI is a 4.7 KB
Node wrapper (bin.anydoc = cli.js) around a native NAPI binding that ships as
an npm optionalDependency per platform.
Verbatim --help output
anydoc: convert documents to GitHub-Flavored Markdown
Usage:
anydoc <file> [options]
anydoc - [options] < file
Converts one document per invocation and writes the Markdown to stdout.
Pass - as the input to read the document from stdin. Never prompts; all
diagnostics go to stderr.
Options:
-o, --output <path> Write the Markdown to <path> instead of stdout
-f, --format <format> Name the input format instead of detecting it:
doc, docx, odt, pdf, ppt, pptx, rtf, epub, xlsx, ods, odp, csv
(extension aliases like xls, docm, ppsx resolve
to these)
-h, --help Print this help and exit
-V, --version Print the version and exit
The format is detected from the file content; the file extension is the
fallback for signature-less formats (CSV). stdin has no extension, so CSV
input from stdin needs --format csv. Scanned or image-only PDFs need OCR,
which anydoc does not do, and error as unsupported.
Exit codes:
0 success
1 the document could not be read or converted
2 usage error: unknown option, missing input, or invalid --format
Examples:
anydoc report.docx
anydoc slides.pptx -o slides.md
anydoc - --format csv < data.csv
curl -s https://example.com/paper.pdf | anydoc -
anydoc --version prints exactly 0.1.6 (verified; both --help and
--version exit 0 and write to stdout).
Invocation forms
anydoc <file> [options] # convert a path on disk
anydoc - [options] < file # read the document from stdin
-as the input reads the document from stdin.- The CLI accepts exactly one document per invocation — there is no batch
mode. Passing a second input exits 2:
anydoc: one document per invocation: unexpected second input '<path>'. For multiple documents use a shell loop orscripts/anydoc batch(see workflows.md).
Flag reference
| Token | Behavior (verified) |
|---|---|
<file> |
Input path. Format detected from content; extension is the fallback for signature-less formats (CSV). |
- |
Read the document from stdin. If stdin is a TTY, exits 2 with anydoc: stdin is a terminal; pipe or redirect a document into anydoc -. |
-o <path>, --output <path> |
Write the Markdown to <path> instead of stdout. Silently overwrites an existing file (verified). Writing to a directory fails with exit 1: anydoc: EISDIR: illegal operation on a directory, open '<path>'. With -o, stdout stays silent. |
-f <fmt>, --format <fmt> |
Force the input format instead of detecting it. Values: doc, docx, odt, pdf, ppt, pptx, rtf, epub, xlsx, ods, odp, csv. Extension aliases resolve through the parser mapping (verified: --format xls, --format docm accepted). Invalid value → exit 2: anydoc: invalid format 'bogus'; expected one of: doc, docx, odt, pdf, ppt, pptx, rtf, epub, xlsx, ods, odp, csv. |
-h, --help |
Print help to stdout, exit 0. Works even when the native binding is unavailable. |
-V, --version |
Print the version (0.1.6) to stdout, exit 0. Binding-independent like --help. |
--format=x |
Inline = value syntax is supported for long options (verified: --format=rtf works). |
-- |
End of options: everything after -- is treated as a positional input (a filename starting with -). |
| Missing option value | anydoc: <option> requires a value → exit 2 (e.g. anydoc: -o requires a value). |
| Unknown option | anydoc: unknown option '--bogus' (see anydoc --help) → exit 2. |
| No input | anydoc: missing input: pass a document path, or - for stdin (see anydoc --help) → exit 2. |
stdin / stdout / stderr conventions
-
stdin input via
-. Because stdin has no file extension, CSV from stdin requires--format csv(CSV has no content signature). Without it, CSV bytes fail with exit 1:anydoc: unsupported input: unrecognized file content: name the format explicitly. Verified success pattern:printf 'name,role\nAlice,Engineer\n' | npx -y @firecrawl/anydoc@0.1.6 - --format csv -
Markdown goes to stdout only. With
-o, stdout stays silent. -
All diagnostics go to stderr as exactly one
anydoc: <message>line per failure. Nothing is ever printed to stdout on failure. -
The CLI never prompts — no confirmation, no interaction. (
-yon thenpxinvocation exists only to answer npx's package-install prompt.) -
EPIPE is handled: if the downstream pipe closes early (
anydoc big.xlsx | head -n 1), the CLI exits 0 with no stderr noise (verified). Piping intoheadis not treated as a conversion failure. -
No environment variables — the CLI uses only argv, stdin, and the filesystem (verified by reading
cli.js).
Running it: npx invocation
npx -y @firecrawl/anydoc@0.1.6 report.docx # markdown to stdout
npx -y @firecrawl/anydoc@0.1.6 slides.pptx -o slides.md # to a file
npx -y @firecrawl/anydoc@0.1.6 - --format csv < data.csv # stdin (CSV needs --format)
curl -s https://example.com/paper.pdf | npx -y @firecrawl/anydoc@0.1.6 - # URL → stdin
Version pinning
Always pin the version: npx -y @firecrawl/anydoc@0.1.6. An unpinned
npx -y @firecrawl/anydoc floats to the latest published tag, so conversions
are not reproducible across time. All behavior in this skill is documented
against 0.1.6. The -y flag answers npx's "Ok to proceed?" install prompt
non-interactively; without it, bare npx @firecrawl/anydoc will prompt on a
cold cache.
First run and offline behavior
- The first
npxinvocation downloads the npm package plus the native platform binary (network required once). Verified with a fresh empty npm cache:env npm_config_cache=$(mktemp -d) npx -y @firecrawl/anydoc@0.1.6 --versionprints0.1.6and exits 0. - Later runs reuse the npm cache; measured warm startup is ~0.33–0.55 s per invocation (see workflows.md).
- Cold-cache offline: if the package is not cached and there is no network, npx itself fails with a clear fetch error before anydoc runs. The conversion itself is fully local — only package retrieval needs network.
- Permanent / offline-capable alternative:
npm install -g @firecrawl/anydoconce, then invokeanydocdirectly (still pinning is up to you). This satisfies the skill's "no service dependency" claim: there is no server, no API key, and no upload — the only network use is downloading the tool.
Distribution and system requirements
- Node.js >= 20 (package
engines). Verified under Node v22. - The native binary ships via npm
optionalDependencies— one small package per platform (darwin-x64,darwin-arm64,linux-x64-gnu,linux-arm64-gnu,linux-x64-musl,linux-arm64-musl,win32-x64-msvc), with no postinstall script and no compilation. - The npm package
@firecrawl/anydoc0.1.6 is ~48 KB unpacked (the binding package is a few MB per platform); published 2026-08-05T18:29:40Z. - The Rust crate
anydoc(crates.io) and Python wheelsfirecrawl-anydoc(PyPI, imports asanydoc, Python >= 3.10) ship in the same release train. There is no standalone Rust CLI binary (cargo install anydocis an open feature request) — the CLI exists only through the npm package.
The wrapper: scripts/anydoc
The skill ships a Python 3 standard-library wrapper at scripts/anydoc that
delegates to the pinned CLI. It adds value beyond a thin npx alias:
convert <file|-> [-o out.md] [-f <format>] [--json] [--dry-run]— pre-validates the input path (missing file, directory input) and the-opath (existing directory) before invoking the CLI, validates-fagainst the 21 accepted format names (the 12 canonical parsers plus the 9 aliases, exit 2 on an invalid name), maps known failure classes to friendly hints (no-OCR, encrypted, malformed, unsupported), and forwards the CLI's exit code. Stdin input via-is passed straight through. A dash-leading filename is supported through the CLI's--marker with the options first:anydoc convert -f csv -- -weird(the wrapper emits-o/-fbefore--, since npx forwards--to the CLI and anything after it reads as an extra input). Absolute paths never need this.batch <inputs...> [--out-dir DIR] [--json] [--dry-run]— converts many documents one at a time, prints per-file status, continues past failures, and exits 1 when any input failed. Output naming is deterministic: each input becomes<stem>.mdunder--out-dir, which is created when missing and defaults to the current working directory. Duplicate inputs convert per occurrence (a later conversion overwrites the earlier output); same-basename inputs from different directories collide on the same<stem>.mdand the last one wins.info [--version]— reports the tool name and the pinned CLI version (anydoc 0.1.6 (wraps @firecrawl/anydoc@0.1.6)) without invoking the converter;info --versionprints exactly0.1.6.- Global
--json(exactly one JSON document on stdout; diagnostics stay on stderr) and--dry-run(print what would run — the exactnpxcommand line and output paths — and execute nothing: no CLI spawn, no output files, no directory creation). With--json,convertembeds the converted markdown in the JSON document when-ois not given. - Checks for Node >= 20 (missing
node, or a version below 20, exits 1 with a clear message naming Node.js and the required version) and fornpx(missingnpxexits 1 namingnpxand the pinned package@firecrawl/anydoc@0.1.6); always invokes npx with-y; never prompts; exit codes 0/1/2 mirror the CLI.
Run it as anydoc/scripts/anydoc <subcommand> ... from the repository root,
scripts/anydoc <subcommand> ... from the skill directory, or
python3 anydoc/scripts/anydoc <subcommand> ... anywhere (the executable bit
and #!/usr/bin/env python3 shebang let it run directly). See
workflows.md for recipes and errors.md for the
error vocabulary.