Given an arxiv identifier or arxiv abs/pdf URL, download the LaTeX source (or, if PDF-only, fall back to MinerU's PDF -> Markdown API), ask an OpenRouter mod...
---
name: arxiv-to-beamer
description: Given an arxiv identifier or arxiv abs/pdf URL, download the LaTeX source (or, if PDF-only, fall back to MinerU's PDF -> Markdown API), ask an OpenRouter model to generate a Beamer presentation introducing the work, and package the result as an Overleaf-uploadable .zip. Use when the user provides an arxiv link/id and asks for a beamer / slides / 演示文稿 / 介绍 derived from the paper.
---
# arxiv-to-beamer
Turn an arxiv paper into an Overleaf-ready Beamer project, using an OpenRouter
model to draft the slides from the paper's own LaTeX source — or, when arxiv
has no TeX source, from a Markdown rendering produced by MinerU.
## Prerequisites
- Env var `OPENROUTER_API_KEY` must be exported in the shell.
- Env var `MINERU_API_TOKEN` should be exported when the paper might be
PDF-only (used for the MinerU fallback). Get one from
<https://mineru.net> → console → API.
- Python 3.9+ on PATH (only stdlib is used — no `pip install` needed).
If `OPENROUTER_API_KEY` is not set, stop and ask the user to export it before
running anything. If a paper turns out to be PDF-only and `MINERU_API_TOKEN`
is missing, ask the user to export that too (or pass `--no-mineru-fallback`
to skip the fallback entirely).
## Usage
Invoke the bundled script with the arxiv id (e.g. `2603.19835`) or any
arxiv URL (`abs/`, `pdf/`, `e-print/`). Always prefer the
`${CLAUDE_PROJECT_DIR}` variable so the path resolves regardless of cwd:
```bash
python "${CLAUDE_PROJECT_DIR:-.}/.claude/skills/arxiv-to-beamer/scripts/arxiv_to_beamer.py" <arxiv_id_or_url>
```
Optional flags:
- `--output <path>` — output zip path (default `<id>-beamer.zip` in cwd).
- `--model <name>` — OpenRouter model id (default `anthropic/claude-sonnet-4.5`).
- `--language <lang>` — slide language hint passed to the model (default `中文`).
- `--keep-source` — also copy the extracted arxiv source (or MinerU
markdown output) next to the zip.
- `--max-chars <n>` — cap source bytes sent to the model (default 200000).
- `--mineru-timeout <sec>` — how long to wait for MinerU PDF parsing
(default 900s).
- `--no-mineru-fallback` — skip the MinerU fallback when arxiv is PDF-only
(script then exits with code 2 instead).
## What the script does
1. Parses the arxiv id from the input (supports `2603.19835`, `2603.19835v2`,
the legacy `hep-th/9901001` form, and full URLs).
2. Downloads `https://arxiv.org/e-print/<id>`. Detects PDF-only submissions
via magic bytes.
3. **If TeX source is present:** extracts the archive (gzipped tar, plain
tar, or a single gzipped `.tex`) into a temp dir and gathers every
`.tex` / `.bib` file.
4. **If PDF-only:** falls back to the MinerU API:
- `POST https://mineru.net/api/v4/extract/task` with
`{"url": "https://arxiv.org/pdf/<id>", ...}`.
- Polls `GET .../extract/task/<task_id>` until `state == "done"`.
- Downloads the result zip (`full_zip_url`), extracts it, and
concatenates every `.md` file as the source text.
- Aborts (exit `2`) if MinerU is unreachable, the task fails or times
out, or `MINERU_API_TOKEN` is not set.
5. Builds a prompt containing the collected source (truncated to
`--max-chars`) and asks the model: "帮我做一个 beamer 来介绍一下这个
研究工作", with formatting instructions so the response is parseable.
6. Parses files from the response. Recognised forms, in order:
- `===== FILE: <relpath> =====` blocks (preferred, multi-file).
- Fenced code blocks with a filename hint (`` ```latex main.tex ``).
- A single fenced LaTeX block → `main.tex`.
- Anything else → dumped verbatim into `main.tex`.
7. Writes everything into `<id>-beamer.zip`. Upload that zip via
Overleaf → New Project → Upload Project.
## When invoked
- Run the script in the foreground; do not background it.
- Surface the script's stdout/stderr to the user.
- Report the absolute path of the produced `.zip`.
- If the script exits with code `2`:
- Check the stderr message: it will say either "TeX source missing and
MINERU_API_TOKEN not set" (ask the user to export the token, or rerun
with `--no-mineru-fallback`), "MinerU PDF parsing failed" (network /
quota issue — surface the detail), or "no downloadable TeX source"
(paper is withdrawn). Do not blindly retry.
- If OpenRouter returns an HTTP error, surface the message; do not retry
silently more than once.
don't have the plugin yet? install it then click "run inline in claude" again.
separated intent/inputs/procedure/decision-points/output-contract/outcome-signal per implexa standard, documented all external apis and env vars with setup guidance, made arxiv pdf-only detection explicit, added edge cases for rate limits/auth/timeouts/truncation/zip validation, preserved original author attribution and all cli flags, clarified file parsing logic from model output, added exit codes and error taxonomy.
turn an arxiv paper into an overleaf-ready beamer project. the skill extracts the paper's latex source, feeds it to an openrouter model to draft presentation slides, then packages everything as a .zip ready to upload.
use this when a user provides an arxiv link or id and asks for slides, a beamer presentation, 演示文稿, 介绍, or equivalent. the skill handles both tex-source papers (preferred) and pdf-only submissions (fallback to mineru's pdf-to-markdown extraction). output is an overleaf-uploadable .zip containing a ready-to-compile beamer project.
required environment variables:
OPENROUTER_API_KEY: your openrouter api key. if missing, the skill fails immediately and tells the user to export it.optional environment variables:
MINERU_API_TOKEN: mineru api token (get from https://mineru.net console). required only if the paper is pdf-only and you want the fallback. if missing and pdf-only paper is encountered, the skill asks the user to export it or pass --no-mineru-fallback.required tools:
external apis:
https://arxiv.org/e-print/<id>.https://mineru.net/api/v4/extract/task (post to create task, get to poll status).input parameters (via cli):
2603.19835, 2603.19835v2, hep-th/9901001, or full url like https://arxiv.org/abs/2603.19835.--output <path>: where to write the .zip (default: <arxiv_id>-beamer.zip in cwd).--model <name>: openrouter model id (default: anthropic/claude-sonnet-4.5).--language <lang>: language hint for slide generation (default: 中文).--keep-source: also save extracted source (tex or markdown) alongside the zip.--max-chars <n>: truncate source to this many bytes before sending to model (default: 200000). edge case: if source is shorter than this, send it all.--mineru-timeout <sec>: max seconds to wait for mineru pdf parsing (default: 900). edge case: if timeout reached, fail cleanly with exit code 2.--no-mineru-fallback: skip mineru entirely; exit with code 2 if paper is pdf-only.parse arxiv id from input. accept formats: bare id (2603.19835, 2603.19835v2), legacy id (hep-th/9901001), or full urls (https://arxiv.org/abs/2603.19835, https://arxiv.org/pdf/2603.19835, https://arxiv.org/e-print/2603.19835). extract the canonical id and version (if present). output: normalized arxiv id.
download paper from arxiv. fetch https://arxiv.org/e-print/<id> (without version suffix). check response status: if 404, the id is invalid or paper is withdrawn (exit with code 2, message "no downloadable tex source"). if other http error, surface it and do not retry. output: raw bytes of downloaded file.
detect if pdf-only or tex-source. read first 4-8 bytes of downloaded file. if magic bytes match gzip (1f 8b), tar (ustar), or pdf (%PDF), classify accordingly. if pdf magic detected, set flag is_pdf_only = true. output: file type classification and raw file bytes.
if tex source present (not pdf-only): extract archive into temp directory. handle three formats: gzipped tar, plain tar, or single gzipped .tex file. if archive is malformed, fail with message "corrupt tar archive" (exit 1). recursively walk temp dir and collect all .tex and .bib files in original tree order. concatenate their contents into single source text. output: source text string and list of extracted files.
if pdf-only, invoke mineru fallback: check MINERU_API_TOKEN env var. if missing and --no-mineru-fallback not set, ask user to export token or pass flag (exit 1). if --no-mineru-fallback set, exit with code 2 and message "pdf-only paper and mineru fallback disabled". else, post request to https://mineru.net/api/v4/extract/task with json body containing arxiv pdf url. capture task_id from response. edge case: if http error from mineru, surface error message and exit 1. output: task_id string.
poll mineru task until done. poll GET https://mineru.net/api/v4/extract/task/<task_id> every 2-5 seconds. check response field state. if state == "done", extract full_zip_url and proceed. if state == "failed", exit 2 with message "mineru pdf parsing failed: --mineru-timeout), exit 2 with message "mineru pdf parsing timeout". edge case: malformed json response triggers retry (up to 3 times) before failing. output: full_zip_url string.
download and extract mineru result. fetch full_zip_url, write to temp file, extract as zip. walk extracted dir, find all .md files, concatenate their contents in file order. output: source text string.
truncate source to max-chars limit. measure byte length of source text. if longer than --max-chars, truncate at char boundary (do not split multi-byte utf-8 sequences). append note "[source truncated]" to end. output: source text, at most --max-chars bytes.
build prompt for model. construct system prompt with formatting instructions (see decision points). user message: source text. request model to generate a beamer presentation slide set introducing the paper, respecting the --language hint. output: prompt (system + user).
call openrouter api. post to https://api.openrouter.ai/api/v1/chat/completions with model name, messages, max tokens (e.g. 8000), temperature (0.7). check response status. if 401/403, exit 1 with "openrouter auth failed". if 429, surface rate limit and exit 1 (do not retry silently). if 5xx, surface error but retry once after 5 seconds. output: response text (model's generated beamer code).
parse files from model response. scan response for recognized file markers, in order:
===== FILE: <relpath> ===== blocks followed by fenced code: extract all such blocks, create file tree.```latex main.tex, ```python main.py): extract code, infer filename.main.tex.main.tex.
output: dict of filename -> file content.build beamer project structure. ensure main.tex exists. if model did not produce it, create minimal beamer stub. gather any .bib files from model output. create standard beamer project layout: main.tex at root, supporting files (.bib, images, etc.) in subdirs if needed. output: file tree dict.
package as zip. create .zip at --output path. add all files from project tree, preserving relative paths. ensure zip is valid (can be unzipped without errors). output: path to .zip file.
optionally keep source. if --keep-source flag set, write extracted source (tex or markdown) to <arxiv_id>-source.tex or <arxiv_id>-source.md in same dir as zip. output: source file path or none.
report success. print absolute path of output .zip to stdout. exit with code 0.
if openrouter api key is missing: exit immediately (code 1), tell user to run export OPENROUTER_API_KEY=<key> before retrying.
if paper is pdf-only and mineru token is missing: either ask user to export MINERU_API_TOKEN, or advise them to pass --no-mineru-fallback to skip. do not guess or use a fallback token.
if arxiv id is invalid or paper withdrawn: exit 2, surface exact arxiv error. do not retry.
if mineru polling times out: exit 2 with "mineru parsing timeout". do not continue with stale data.
if model response is unparseable (no recognized file markers): treat entire response as main.tex and proceed. log a warning.
if model response contains multiple main.tex definitions: keep the first one, warn about duplicates.
if openrouter returns 429 (rate limit): surface the rate limit message and exit 1. do not retry automatically; user must wait and retry manually.
if zip creation fails (disk full, permission denied): surface the exact io error and exit 1.
--output (or default <arxiv_id>-beamer.zip). zip must be valid (extractable), contain main.tex at root, and include any supporting files (.bib, custom styles, etc.) generated by the model. zip size should be < 50 mb (edge case: warn if > 10 mb). optional: if --keep-source set, also write <arxiv_id>-source.tex or .md in same dir.--verbose set)./home/user/2603.19835-beamer.zip.--keep-source was set, user also sees the extracted source file alongside the zip, useful for debugging or editing.