agentleFS
timurgaleevtimurgaleev/vibestack/skills/make-pdf/SKILL.md

make-pdf

Skill8 starsChanged 6 months agoMITView on GitHub

Generate a polished PDF from markdown, HTML or code in the repo, with cover page, table of contents and margins.

  • Reads credentials

21 other projects use a version of this, 1 of them edited.

Adopted by

Most recently taken up by

Used with

Projects with it also use ios-clean, ios-fix and 3 more.

Other files people use alongside this one.

Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
  • Saqlain-811Skill

    ios-clean

    Remove the DebugBridge SPM package and all #if DEBUG wiring from an iOS app. (gstack)

    Also in 5 projects that use this

  • Saqlain-811Skill

    ios-fix

    Autonomous iOS bug fixer. (gstack)

    Also in 5 projects that use this

  • bastosSkill

    ios-design-review

    Review iOS app UI/UX design quality using Apple's design principles and the Liquid Glass-era design language from WWDC25/26. Use when asked for a design review, UX critique, visual design assessment, or evaluation of an app's interface quality, design intent, alignment with the new Apple design system, or usability. Triggers: "design review", "UX review", "visual design critique", "review the UI", "does this design work", "evaluate this interface", "design feedback", "Liquid Glass review".

    Also in 5 projects that use this

  • +2 more from Saqlain-811

The file

We split this skill into the rules it gives an agent

8 rules. 0 are common, 2 are distinctive, and 6 are very specific to this project.

  • 0

    Common

    Most of the 16 edited versions we read keep these.

  • 2

    Distinctive

    This author's own choices. Worth a look.

  • 6

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    If this session was compacted, the copy of this skill in context may be cut off.

    line 20
  2. Very specific:

    Read ${CLAUDE_SKILL_DIR}/SKILL.md from disk and resume at the step you are on.

    line 21
  3. Distinctive:

    Use when asked to "make pdf", "generate pdf", "export to pdf", "create pdf report", or "pdf preview".

    line 25
  4. Very specific:

    If NOT_FOUND, stop and tell the user:

    line 63
  5. Very specific:

    Run the generator. The output path is the second positional argument — there is no --output flag, and any flag the CLI does not know fails …

    line 100
  6. Very specific:

    Verify the render toolchain — browse binary, Chromium launch, pdftotext (optional) — then generate and open a smoke-test PDF:

    line 148
  7. Very specific:

    If the user has a .md file open and says "make it look nice", propose "$P" generate --cover --toc and ask before running.

    line 328
  8. Distinctive:

    If you discovered a non-obvious make-pdf behavior, flag pattern, or conversion quirk during this session, log it for future sessions:

    line 363

“Common” is measured: kept by at least 6 in 10 of the 16 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

If this session was compacted, the copy of this skill in context may be cut off. Read ${CLAUDE_SKILL_DIR}/SKILL.md from disk and resume at the step you are on.

When to invoke

Use when asked to "make pdf", "generate pdf", "export to pdf", "create pdf report", or "pdf preview".

Preamble

eval "$("${VIBESTACK_HOME:-$HOME/.vibestack}/bin/vibe-slug" 2>/dev/null)" 2>/dev/null || SLUG="unknown"
_LEARN_FILE="${VIBESTACK_HOME:-$HOME/.vibestack}/projects/${SLUG:-unknown}/learnings.jsonl"
if [ -f "$_LEARN_FILE" ]; then
  _LEARN_COUNT=$(wc -l < "$_LEARN_FILE" 2>/dev/null | tr -d ' ')
  echo "LEARNINGS: $_LEARN_COUNT entries loaded"
  if [ "$_LEARN_COUNT" -gt 5 ] 2>/dev/null; then
    "${VIBESTACK_HOME:-$HOME/.vibestack}/bin/vibe-learnings-search" --limit 5 2>/dev/null || true
  fi
else
  echo "LEARNINGS: none yet"
fi

{{include lib/snippets/session-host.md}}

{{include lib/snippets/decision-brief.md}}

{{include lib/snippets/working-protocols.md}}

{{include lib/snippets/state-protocols.md}}

Step 0: Find the make-pdf renderer

# Env override first, then the launcher installed with this skill (resolves
# the repo's compiled binary or runs the CLI on bun).
P="${MAKE_PDF_BIN:-${CLAUDE_SKILL_DIR:-$HOME/.claude/skills/make-pdf}/bin/vibe-make-pdf}"
[ -x "$P" ] && "$P" version >/dev/null 2>&1 && echo "PDF_BIN: $P" || echo "NOT_FOUND"

Each Bash call starts a fresh shell, so every later block that runs "$P" opens with P='<PDF_BIN>': replace the placeholder with the path this block printed.

If NOT_FOUND, stop and tell the user:

make-pdf renderer not found. It ships with the vibestack repo. Run: cd ~/data/vibestack && bun install && bun run build:make-pdf && ./install (Bun 1.3.3 or newer — bun --version. Older Bun ignores the build's --no-compile-autoload-dotenv flag, so the binary could read a project's .env; run bun upgrade first.) Or set $MAKE_PDF_BIN to the path of an existing make-pdf binary.

After building, re-run /make-pdf.


Step 1: Detect intent

Parse the user's input to determine what to do:

  1. /make-pdf with no args — Auto-detect: look for markdown files or ask what to convert
  2. /make-pdf preview <file> — Preview mode: render the print-styled HTML and open it in a browser
  3. /make-pdf setup — Setup mode: run $P setup to configure defaults
  4. /make-pdf <file or description> — Generate mode: produce a PDF from the specified input

Step 2A: Generate mode

  1. Identify the source file(s). If the user provided a path, use it. If not, ask:
    What should I turn into a PDF?
    A) A specific file (provide path)
    B) All markdown files in this directory
    C) A generated report (I'll describe what to include)
    D) Something else
  2. Determine output filename. Default: <source-basename>.pdf in the same directory.
  3. Run the generator. The output path is the second positional argument — there is no --output flag, and any flag the CLI does not know fails the run with exit 1:
P='<PDF_BIN>'
"$P" generate "<source>" "<output.pdf>" [flags]

Common flags:

  • --cover — add a cover page (uses repo name + date)
  • --toc — add a table of contents with page numbers
  • --watermark "<text>" — overlay watermark text (e.g., "DRAFT", "CONFIDENTIAL")
  • --margins <dim> — one dimension for all four margins (default: 1in; also 72pt, 2.54cm, 25mm)
  • --page-size <size> — letter (default), a4, legal
  • --title "<title>" — override document title
  • --author "<name>" — set author metadata
  • --to pdf|html|docx — output format (default: pdf)
  • --no-confidential — suppress the CONFIDENTIAL footer stamped by default
  • --strict — missing or remote images fail the run instead of warning
  1. Report the result:
PDF GENERATED
═══════════════════════════════════════════
Output:  <output.pdf>
Size:    <file size>
Pages:   <page count>
═══════════════════════════════════════════

Step 2B: Preview mode

Render the print-styled HTML and open it:

P='<PDF_BIN>'
"$P" preview "<source>" [flags]

This writes an HTML file with the print CSS and opens it in the browser; no PDF is produced. Preview skips the diagram and image pre-pass, so diagram fences show as code. Report the path, and run generate mode when the user wants the PDF.


Step 2C: Setup mode

Verify the render toolchain — browse binary, Chromium launch, pdftotext (optional) — then generate and open a smoke-test PDF:

P='<PDF_BIN>'
"$P" setup

It writes no config file. Report which checks passed and, if Chromium or the browse binary failed, relay the fix it printed before attempting any other mode.


Core patterns

80% case — memo/letter

One command, no flags. Gets a clean PDF with running header, page numbers, and a CONFIDENTIAL right-footer. Always pass the output path: without one the CLI writes to the OS temp directory, whose location varies by platform.

P='<PDF_BIN>'
"$P" generate letter.md letter.pdf

Brand-free — no CONFIDENTIAL footer

The footer is on by default, so anything meant to leave the building needs it turned off:

P='<PDF_BIN>'
"$P" generate --no-confidential memo.md memo.pdf

Publication mode — cover + TOC + chapter breaks

P='<PDF_BIN>'
"$P" generate --cover --toc --title "On Horizons" essay.md essay.pdf

Each top-level H1 starts a new page. Disable with --no-chapter-breaks for memos that happen to have multiple H1s.

Draft-stage watermark

P='<PDF_BIN>'
"$P" generate --watermark DRAFT memo.md draft.pdf

Diagonal DRAFT across every page. Drop the flag when final.

Diagrams — mermaid and excalidraw fences render as pictures

A column-0 ```mermaid or ```excalidraw fence renders as a vector diagram, offline. Indented fences are left alone, so a fence quoted inside a list stays a code block.

Info-string options on the opening fence:

  • title="Auth flow" — accessible label for the rendered figure
  • render=false — leave this fence as a plain code block (the escape hatch when the source is what the reader needs to see)
  • page=landscape / page=portrait — force or veto a landscape page for this diagram

A fence whose source fails to parse becomes a visible red diagnostic block carrying the error and an excerpt — never a silently missing figure. When that shows up, fix the diagram source rather than dropping the fence.

Images — scaled right, never truncated

A directive suffix on a markdown image tunes its placement:

![chart](data.png){width=full}
![chart](data.png){width=50%}
![diagram](wide.svg){page=landscape}
![diagram](wide.svg){page=portrait}

width takes full, a percentage, or an absolute dimension (in, cm, mm, pt, px). By default an image renders at its intrinsic size, capped at the content box and never upscaled.

Wide, small-text images auto-promote to their own landscape page. The heuristic is deliberately conservative — aspect ratio at least 1.8, intrinsic width over roughly 2.5x the content box, and either diagram provenance or a diagram-ish word in the alt text — so it misses more often than it fires. {page=landscape} forces promotion; {page=portrait} vetoes it.

Local images are inlined as data URIs and rasters wider than print resolution are downscaled. An image that is missing, not a regular file, or over 64MB degrades to a visible placeholder with a warning; one resolving outside the markdown's own directory is inlined but warned about, because an agent rendering untrusted markdown should not quietly embed a file from elsewhere on the machine into a shareable document.

Other formats — single-file HTML and Word

P='<PDF_BIN>'
"$P" generate readme.md out.html --to html     # one self-contained file, no network refs
"$P" generate readme.md out.docx --to docx     # content fidelity; diagrams become PNG

--to is the output format. --format is something else entirely — an alias for --page-size — so --format html asks for a page size named "html", not HTML output.

CI mode — fail loud on missing assets

P='<PDF_BIN>'
"$P" generate docs.md --strict

Missing, unreadable, oversized, out-of-tree, and remote images become hard failures instead of warnings, so a broken asset path fails the build rather than shipping a placeholder.

Fast iteration via preview

P='<PDF_BIN>'
"$P" preview essay.md

Renders with print CSS and opens in browser. Skip the PDF round trip until you're ready.


Common flags

Output format:
  --to pdf|html|docx         What to produce (default: pdf)

Page layout:
  --margins <dim>            1in (default) | 72pt | 2.54cm | 25mm
  --page-size letter|a4|legal   (alias --format — page SIZE, not --to)

Structure:
  --cover                    Cover page (title, author, date)
  --toc                      Clickable TOC with page numbers
  --no-chapter-breaks        Don't start a new page at every H1

Branding:
  --watermark <text>         Diagonal watermark ("DRAFT", "CONFIDENTIAL")
  --header-template <html>   Custom running header
  --footer-template <html>   Custom footer (mutex with --page-numbers)
  --no-confidential          Suppress the CONFIDENTIAL right-footer (on by default)

Output:
  --page-numbers             "N of M" footer (default on)
  --tagged                   Accessible PDF (default on)
  --outline                  PDF bookmarks from headings (default on)
  --quiet                    Suppress progress on stderr
  --verbose                  Per-stage timings

Images:
  --strict                   Missing/remote images fail the run (CI mode)

Network:
  --allow-network            Fetch external images. Off by default
                             (blocks tracking pixels).

Metadata:
  --title "..."              Document title (defaults to first H1)
  --author "..."             Author for cover + PDF metadata
  --date "..."               Date for cover (defaults to today)

When to run it

Watch for markdown-to-PDF intent. Any of these → run "$P" generate:

  • "Can you make this markdown a PDF"
  • "Export it as a PDF"
  • "Turn this into a PDF"
  • "I need a PDF of this"
  • "Print this as a PDF for me"

If the user has a .md file open and says "make it look nice", propose "$P" generate --cover --toc and ask before running.


Output contract

stdout: /path/to/letter.pdf      ← just the path, one line
stderr: Rendering HTML...        ← progress (unless --quiet)
        Generating PDF...
        Done in 1.5s. 43 words · 22KB · /path/to/letter.pdf

exit code: 0 success / 1 bad args or unknown flag / 2 render error / 3 TOC page numbers failed / 4 binary unavailable

Capture the path: PDF=$("$P" generate letter.md letter.pdf) — then use $PDF.


Debugging

  • Blank output → check binary is executable: ls -la "$P"
  • Fragmented text on copy-paste → remove fenced code blocks and regenerate
  • Exit 3 with --toc → the TOC page numbers could not be filled (they kept moving after three prints, or the PDF had no destination for a heading); shorten the longest TOC entries or drop --toc
  • External image missing → the binary fetches external images only when --allow-network is set
  • Wrong text metrics or ▯ boxes where emoji should be (Linux, containers) → the print CSS falls back to Liberation Sans and a system color-emoji font; install fonts-liberation and a Noto color-emoji package, neither of which the vibestack installer provides

Capture Learnings

If you discovered a non-obvious make-pdf behavior, flag pattern, or conversion quirk during this session, log it for future sessions:

"${VIBESTACK_HOME:-$HOME/.vibestack}/bin/vibe-learnings-log" '{"skill":"make-pdf","type":"TYPE","key":"SHORT_KEY","insight":"DESCRIPTION","confidence":N,"source":"SOURCE","files":["path/to/relevant/file"]}'

Types: pattern (reusable approach), pitfall (what NOT to do), tool (binary behavior), operational (env/path/dependency quirk).

Only log genuine discoveries. A good test: would this save time in a future session?

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.