
Terminal Print
- 122 installs
- 62 repo stars
- Updated August 3, 2026
- terrylica/cc-skills
Use terminal-print for development tasks
About
terminal-print: A skill for development. This provides functionality for development workflows.
- terminal-print
Terminal Print by the numbers
- 122 all-time installs (skills.sh)
- Ranked #2,825 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/terrylica/cc-skills --skill terminal-printAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 122 |
|---|---|
| repo stars | ★ 62 |
| Last updated | August 3, 2026 |
| Repository | terrylica/cc-skills ↗ |
What it does
Use terminal-print for development tasks
Files
Terminal Print
Print terminal output from iTerm2 to your HP network printer with a single command.
Self-Evolving Skill: This skill improves through use. If instructions are wrong, parameters drifted, or a workaround was needed — fix this file immediately, don't defer. Only update for real, reproducible issues.
When to Use This Skill
Use this skill when:
- Printing terminal output to a network printer
- Creating PDF copies of command-line session output
- Archiving terminal logs in print-friendly format
- Sharing terminal output in meetings or documentation
Quick Start
1. Copy terminal output in iTerm2 (Cmd+C) 2. Invoke this skill 3. Review PDF preview, press Enter to print
How It Works
Clipboard → Strip ANSI → Markdown code block → pandoc/xelatex → PDF → Preview → Print- ANSI codes stripped: Colors and escape sequences removed for clean B&W output
- Monospace font: DejaVu Sans Mono for proper character alignment
- Landscape orientation: Fits ~120 characters per line
- US Letter paper: Auto-detected from printer settings
Execution
/usr/bin/env bash << 'PRINT_EOF'
SKILL_DIR="${CLAUDE_PLUGIN_ROOT:-$HOME/.claude/plugins/marketplaces/cc-skills/plugins/doc-tools}/skills/terminal-print"
bash "$SKILL_DIR/assets/print-terminal.sh"
PRINT_EOFOptions
Run with arguments by modifying the execution block:
/usr/bin/env bash << 'PRINT_EOF'
SKILL_DIR="${CLAUDE_PLUGIN_ROOT:-$HOME/.claude/plugins/marketplaces/cc-skills/plugins/doc-tools}/skills/terminal-print"
bash "$SKILL_DIR/assets/print-terminal.sh" --no-preview
PRINT_EOF| Flag | Description |
|---|---|
--file FILE | Read from file instead of clipboard |
--no-preview | Skip PDF preview, print directly |
--no-print | Generate PDF only, don't send to printer |
--printer NAME | Override target printer queue (default: system default) |
--bypass-airprint | Use socket-9100 + PostScript queue. Workaround for HP LaserJet Pro MFP 3101/3108/3201/3208/3301/3308 AirPrint blank pages. |
-h, --help | Show help message |
Examples
Print from clipboard (default)
# Copy terminal output in iTerm2, then:
/usr/bin/env bash << 'EOF'
bash "${CLAUDE_PLUGIN_ROOT}/skills/terminal-print/assets/print-terminal.sh"
EOFPrint from file
/usr/bin/env bash << 'EOF'
bash "${CLAUDE_PLUGIN_ROOT}/skills/terminal-print/assets/print-terminal.sh" --file ~/session.log
EOFGenerate PDF only (no print)
/usr/bin/env bash << 'EOF'
bash "${CLAUDE_PLUGIN_ROOT}/skills/terminal-print/assets/print-terminal.sh" --no-print
EOFPrerequisites
All dependencies are already available on macOS with MacTeX:
| Tool | Purpose | Status |
|---|---|---|
pandoc | Markdown to PDF | Required |
xelatex | PDF engine | Required (MacTeX) |
pbpaste | Clipboard access | Built-in |
lpr | CUPS printing | Built-in |
Output
- PDF location:
/tmp/terminal-output-YYYYMMDD_HHMMSS.pdf - Markdown source:
/tmp/terminal-YYYYMMDD_HHMMSS.md - Cleanup: macOS automatically cleans
/tmpperiodically
Troubleshooting
"No text in clipboard"
Copy terminal output first using Cmd+C in iTerm2.
"Missing pandoc" or "Missing xelatex"
Install MacTeX: brew install --cask mactex
Printer not found
Check printer status: lpstat -p -d
The script auto-detects the system default printer. Override with --printer NAME or set TERMINAL_PRINT_PRINTER in the environment.
Blank page came out (or lpr reported success but nothing printed)
Strong fingerprint of the HP LaserJet Pro MFP 3101/3108/3201/3208/3301/3308 AirPrint blank-page bug — the printer's IPP-Everywhere PDF interpreter silently drops jobs while CUPS reports completed. Confirmed with this exact symptom on 2026-05-09.
Quick fix:
# 1. One-time setup of bypass queue (socket-9100 + Generic PostScript PPD)
"${CLAUDE_PLUGIN_ROOT}/skills/terminal-print/assets/setup-socket-9100-queue.sh"
# 2. Reprint with --bypass-airprint
bash "${CLAUDE_PLUGIN_ROOT}/skills/terminal-print/assets/print-terminal.sh" --bypass-airprintFull diagnostic playbook with command sequences and decision tree: see airprint-blank-page-troubleshooting.md.
Key takeaway: do not trust lpstat -o or job-state=completed — query the printer's own job ledger via ipptool -tv ipp://<printer>.local.:631/ipp/print for job-impressions-completed and job-media-sheets-completed. CUPS lies; the printer (mostly) tells the truth.
Related Skills
- pandoc-pdf-generation - General Markdown to PDF conversion
- asciinema-converter - Convert terminal recordings
Post-Execution Reflection
After this skill completes, check before closing:
1. Did the command succeed? — If not, fix the instruction or error table that caused the failure. 2. Did parameters or output change? — If the underlying tool's interface drifted, update Usage examples and Parameters table to match. 3. Was a workaround needed? — If you had to improvise (different flags, extra steps), update this SKILL.md so the next invocation doesn't need the same workaround.
Only update if the issue is real and reproducible — not speculative.
#!/bin/bash
# Terminal Print - Print iTerm2 output to HP printer
# Based on cc-skills pandoc-pdf-generation pattern
#
# Usage: ./print-terminal.sh [OPTIONS]
#
# Options:
# --file FILE Read from file instead of clipboard
# --no-preview Skip preview, print directly
# --no-print Generate PDF only, don't print
# -h, --help Show help
#
# ADR: /docs/adr/2025-12-28-terminal-print-skill.md (if created)
set -e
TMPDIR="${TMPDIR:-/tmp}"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
# Default printer is the system default; users with multiple printers can override via env or --printer
PRINTER="${TERMINAL_PRINT_PRINTER:-$(lpstat -d 2>/dev/null | awk -F': ' '/system default/{print $2}')}"
PRINTER="${PRINTER:-HP_LaserJet_Pro_MFP_3101_3108__A02E22__20250803224332}"
# AirPrint bypass queue (created by setup-socket-9100-queue.sh)
# When --bypass-airprint is passed, this queue is used instead.
# See: ../references/airprint-blank-page-troubleshooting.md
BYPASS_QUEUE="${TERMINAL_PRINT_BYPASS_QUEUE:-HP_3101_PS9100}"
# Defaults
INPUT_FILE=""
NO_PREVIEW=""
NO_PRINT=""
BYPASS_AIRPRINT=""
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
--file)
INPUT_FILE="$2"
shift 2
;;
--no-preview)
NO_PREVIEW="yes"
shift
;;
--no-print)
NO_PRINT="yes"
shift
;;
--bypass-airprint)
# Use the socket-9100 + PostScript queue instead of the AirPrint queue.
# Diagnoses blank-page output from the IPP-Everywhere PDF interpreter on
# HP LaserJet Pro MFP 3101/3108/3201/3208/3301/3308 firmware.
BYPASS_AIRPRINT="yes"
shift
;;
--printer)
PRINTER="$2"
shift 2
;;
-h|--help)
cat <<HELP
Terminal Print - Print iTerm2 output to HP printer
Usage: $0 [OPTIONS]
Options:
--file FILE Read from file instead of clipboard
--no-preview Skip preview, print directly
--no-print Generate PDF only, don't print
--printer NAME Override target printer queue (default: system default)
--bypass-airprint Use socket-9100 + PostScript queue ('$BYPASS_QUEUE')
instead of the default AirPrint queue. Workaround for
HP LaserJet Pro MFP 3101/3108/3201/3208/3301/3308
firmware that silently drops PDF jobs from AirPrint.
Run setup-socket-9100-queue.sh first to create the queue.
See references/airprint-blank-page-troubleshooting.md.
-h, --help Show this help
Environment overrides:
TERMINAL_PRINT_PRINTER Default printer queue
TERMINAL_PRINT_BYPASS_QUEUE Bypass queue name (default: HP_3101_PS9100)
HELP
exit 0
;;
*)
echo "Unknown option: $1" >&2
echo "Use -h for help" >&2
exit 1
;;
esac
done
# Pre-flight checks
preflight_check() {
local missing=()
command -v pandoc &>/dev/null || missing+=("pandoc (brew install pandoc)")
command -v xelatex &>/dev/null || missing+=("xelatex (brew install --cask mactex)")
if ! lpstat -p &>/dev/null; then
missing+=("printer (none configured)")
fi
if [[ ${#missing[@]} -gt 0 ]]; then
echo "❌ Missing prerequisites:"
for item in "${missing[@]}"; do
echo " - $item"
done
exit 1
fi
}
preflight_check
# 1. Get input
if [[ -n "$INPUT_FILE" ]]; then
if [[ ! -f "$INPUT_FILE" ]]; then
echo "❌ File not found: $INPUT_FILE"
exit 1
fi
content=$(cat "$INPUT_FILE")
echo "📋 Reading from file: $INPUT_FILE"
else
content=$(pbpaste)
echo "📋 Reading from clipboard"
fi
# 2. Check for empty input
if [[ -z "$content" ]]; then
echo "❌ No text in clipboard."
echo " Copy terminal output first (Cmd+C in iTerm2)."
exit 1
fi
# 3. Strip ANSI escape codes
# Handles: colors, cursor movement, and other escape sequences
clean_content=$(echo "$content" | sed 's/\x1b\[[0-9;]*[a-zA-Z]//g')
echo "📝 Stripped ANSI codes, preparing markdown..."
# 4. Create markdown with code block
MD_FILE="$TMPDIR/terminal-$TIMESTAMP.md"
cat > "$MD_FILE" << 'MARKDOWN_HEADER'
# Terminal Output
```text
MARKDOWN_HEADER
echo "$clean_content" >> "$MD_FILE"
echo '```' >> "$MD_FILE"
# 5. Generate PDF with pandoc (landscape, monospace)
OUTPUT_PDF="$TMPDIR/terminal-output-$TIMESTAMP.pdf"
echo "🔧 Generating PDF with pandoc + xelatex..."
pandoc "$MD_FILE" \
-o "$OUTPUT_PDF" \
--pdf-engine=xelatex \
-V geometry:letterpaper,landscape \
-V geometry:margin=0.5in \
-V mainfont="DejaVu Sans Mono" \
-V monofont="DejaVu Sans Mono" \
-V fontsize=9pt
echo "📄 PDF generated: $OUTPUT_PDF"
# 6. Preview (unless --no-preview)
if [[ -z "$NO_PREVIEW" ]]; then
open "$OUTPUT_PDF"
echo ""
echo "👀 Preview opened. Press Enter to print, or Ctrl+C to cancel."
read -r
fi
# 7. Print (unless --no-print)
if [[ -z "$NO_PRINT" ]]; then
if [[ -n "$BYPASS_AIRPRINT" ]]; then
# AirPrint blank-page workaround: route through the socket-9100/PostScript queue.
# See references/airprint-blank-page-troubleshooting.md for the full diagnostic.
if ! lpstat -p "$BYPASS_QUEUE" &>/dev/null; then
echo "❌ Bypass queue '$BYPASS_QUEUE' does not exist."
echo " Run: $(dirname "$0")/setup-socket-9100-queue.sh"
exit 1
fi
TARGET_QUEUE="$BYPASS_QUEUE"
echo "🔀 --bypass-airprint: routing through $BYPASS_QUEUE (socket-9100 + PostScript)"
else
TARGET_QUEUE="$PRINTER"
fi
lpr -P "$TARGET_QUEUE" -o sides=one-sided "$OUTPUT_PDF"
echo "✅ Sent to printer: $TARGET_QUEUE"
echo "ℹ️ Verify the page actually came out — CUPS reports 'completed' even when"
echo " the printer silently drops jobs. If blank or missing, retry with --bypass-airprint."
else
echo "ℹ️ --no-print specified, skipping print"
fi
echo "📁 PDF saved: $OUTPUT_PDF"
#!/bin/bash
# setup-socket-9100-queue.sh — create a CUPS queue that bypasses AirPrint
#
# Why this exists:
# HP LaserJet Pro MFP 3101/3108/3201/3208/3301/3308 family firmware has
# a buggy IPP-Everywhere PDF interpreter that silently drops jobs while
# reporting "completed" to CUPS. The printer's PostScript Level 3
# interpreter is reliable. This script creates a parallel queue that
# sends raw PostScript over JetDirect socket-9100, taking the broken
# PDF/PWG path out of the loop.
#
# Reference: ../references/airprint-blank-page-troubleshooting.md
#
# Usage:
# setup-socket-9100-queue.sh [--name QUEUE_NAME] [--ip IP] [--probe-only]
#
# Defaults:
# QUEUE_NAME = HP_3101_PS9100
# IP = auto-resolved from Bonjour (first reachable HP printer)
set -e
QUEUE_NAME="HP_3101_PS9100"
PRINTER_IP=""
PROBE_ONLY=""
PPD="drv:///sample.drv/generic.ppd"
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
--name) QUEUE_NAME="$2"; shift 2 ;;
--ip) PRINTER_IP="$2"; shift 2 ;;
--probe-only) PROBE_ONLY="yes"; shift ;;
--ppd) PPD="$2"; shift 2 ;;
-h|--help)
sed -n '2,18p' "$0" | sed 's|^# \?||'
exit 0 ;;
*)
echo "Unknown option: $1" >&2
exit 1 ;;
esac
done
# 1. If queue already exists, report and exit successfully
if lpstat -p "$QUEUE_NAME" &>/dev/null; then
EXISTING_URI=$(lpstat -v "$QUEUE_NAME" 2>/dev/null | awk -F': ' '{print $2}')
echo "✅ Queue '$QUEUE_NAME' already exists"
echo " URI: $EXISTING_URI"
[[ -n "$PROBE_ONLY" ]] && exit 0
echo " To recreate: lpadmin -x $QUEUE_NAME && rerun this script"
exit 0
fi
# 2. Resolve printer IP via Bonjour if not provided
if [[ -z "$PRINTER_IP" ]]; then
echo "🔍 Discovering HP printers via Bonjour..."
# Browse for IPP/S printers, pick the first HP LaserJet, resolve its hostname
INSTANCE=$(timeout 4 dns-sd -B _ipps._tcp local. 2>/dev/null \
| awk '/HP LaserJet|HP OfficeJet/ {sub(/.*_ipps\._tcp\.\s+/,""); print; exit}')
if [[ -z "$INSTANCE" ]]; then
echo "❌ No HP printers found via Bonjour. Pass --ip <address> manually."
exit 1
fi
HOSTNAME=$(timeout 3 dns-sd -L "$INSTANCE" _ipps._tcp local. 2>&1 \
| awk '/reached at/ {gsub(":.*","",$3); print $3; exit}')
if [[ -z "$HOSTNAME" ]]; then
echo "❌ Failed to resolve hostname for '$INSTANCE'"
exit 1
fi
# Resolve .local hostname to IP via mDNS
PRINTER_IP=$(dscacheutil -q host -a name "${HOSTNAME%.}" 2>/dev/null \
| awk '/^ip_address:/ {print $2; exit}')
if [[ -z "$PRINTER_IP" ]]; then
# Fallback: ping briefly to populate the mDNS cache, then re-query
ping -c 1 -W 2000 "$HOSTNAME" &>/dev/null || true
PRINTER_IP=$(dscacheutil -q host -a name "${HOSTNAME%.}" 2>/dev/null \
| awk '/^ip_address:/ {print $2; exit}')
fi
if [[ -z "$PRINTER_IP" ]]; then
echo "❌ Failed to resolve IP for $HOSTNAME"
exit 1
fi
echo "✅ Found HP printer: $INSTANCE → $HOSTNAME → $PRINTER_IP"
fi
# 3. Probe TCP/9100 (JetDirect) reachability
echo "🔌 Probing TCP/9100 on $PRINTER_IP..."
if ! nc -z -v -w 3 "$PRINTER_IP" 9100 2>&1 | grep -q "succeeded"; then
echo "❌ Printer not listening on TCP/9100. Check that JetDirect / Raw IP printing"
echo " is enabled in the printer's web UI (usually Networking → TCP/IP → Raw)."
exit 1
fi
echo "✅ TCP/9100 reachable"
[[ -n "$PROBE_ONLY" ]] && { echo "ℹ️ --probe-only: skipping queue creation"; exit 0; }
# 4. Create the queue
echo "🛠 Creating queue '$QUEUE_NAME' with PPD '$PPD'..."
lpadmin -p "$QUEUE_NAME" \
-E \
-v "socket://$PRINTER_IP:9100" \
-m "$PPD" \
-D "HP LaserJet (PostScript over socket-9100, AirPrint bypass)" \
-L "JetDirect bypass for AirPrint PDF interpreter bug"
# 5. Confirm
if lpstat -p "$QUEUE_NAME" &>/dev/null; then
echo "✅ Queue created: $QUEUE_NAME"
echo " Print with: lp -d $QUEUE_NAME -o media=Letter <file>"
echo " Remove with: lpadmin -x $QUEUE_NAME"
else
echo "❌ Queue creation reported success but lpstat can't see it"
exit 1
fi
AirPrint / IPP-Everywhere Blank-Page Troubleshooting
When lpr reports success but no page comes out — or a blank page comes out — on an HP AirPrint / IPP-Everywhere printer (HP LaserJet Pro MFP 3101/3108 family and similar), use this playbook.
This is not theoretical: it captures the full diagnostic odyssey and the working fix from a real session (2026-05-09) where Chrome-headless PDFs, cgpdftops-converted PostScript, and even direct IPP submissions all silently dropped pages on the AirPrint queue, while a parallel socket://IP:9100 + PostScript-PPD queue worked first try.
---
Symptom Signature
You're hitting this exact bug if any of the following are true:
| Observation | Meaning |
|---|---|
lpstat -o shows queue empty within ~6 seconds | Job left the local Mac spool — it's on the printer's plate now |
ipptool -tv ipp://localhost/jobs/<N> returns job-state=completed and job-state-reasons=processing-to-stop-point with job-impressions-completed=0 | Local CUPS thinks success; printer reported back that it rasterized zero pages |
Printer's own ledger (ipptool -tv ipp://<printer>.local.:631/ipp/print querying completed jobs) shows impressions=0, sheets=0 for the job | Printer received the job and dropped it before rasterization (PDF interpreter rejected) |
Printer's own ledger shows impressions=1, sheets=0 for a plain-text job | Printer rasterized text but the IPP sheets counter on this firmware under-reports by one — page actually did come out |
| Channel test (plain text) prints fine, but PDFs come out blank | Document-format-specific rejection by the firmware, not a transport issue |
| Same PDF prints fine on another machine / via another protocol | Confirms it's the local CUPS → AirPrint path, not the document |
The local CUPS spool will report job-state=completed whether the printer printed a real page, a blank page, or nothing at all. Do not trust local CUPS as ground truth. The only ground truths are (a) the printer's own job ledger over IPP and (b) what's physically in the output tray.
---
Diagnostic Ladder (in order)
Run these top-to-bottom. Stop when you find the first one that explains the failure.
1. Is the printer reachable?
# Bonjour discovery — list nearby IPP printers
timeout 4 dns-sd -B _ipps._tcp local.
# Resolve hostname for this printer (substitute the instance name from -B)
timeout 3 dns-sd -L "HP LaserJet Pro MFP 3101-3108 [A02E22]" _ipps._tcp local.
# Look for "reached at <hostname>:<port>" in output
# Ping the resolved hostname (e.g. HP28C5C8A02E22.local.)
ping -c 2 HP28C5C8A02E22.local.If unreachable: printer is asleep / off-network / DNS-SD record stale. Wake it via the front panel and retry. Skip the rest of the ladder until ping works.
2. Read the printer's own job ledger (NOT the local CUPS ledger)
PRINTER_HOST="HP28C5C8A02E22.local.:631" # <-- from step 1
cat > /tmp/get-completed-jobs.test <<'EOF'
{
OPERATION Get-Jobs
GROUP operation-attributes-tag
ATTR charset attributes-charset utf-8
ATTR naturalLanguage attributes-natural-language en
ATTR uri printer-uri $uri
ATTR keyword which-jobs completed
ATTR keyword requested-attributes job-id,job-state,job-state-reasons,job-impressions-completed,job-media-sheets-completed,job-name
STATUS successful-ok
EXPECT job-id
}
EOF
ipptool -tv "ipp://$PRINTER_HOST/ipp/print" /tmp/get-completed-jobs.test \
| grep -E "job-(id|name|impressions-completed|media-sheets-completed)" | head -30Compare your job to known-good jobs in the printer's history:
impressions | sheets | Interpretation |
|---|---|---|
| 1+ | 1+ | ✅ Real success |
| 1 | 0 | Probably success — this firmware under-reports sheets by one for some job types |
| 0 | 0 | ❌ Document-format rejection — printer didn't even rasterize. Skip to step 4 |
| Job not in list at all | — | Job never reached printer. Re-check transport (Wi-Fi flapping, dnssd record stale). |
3. Is it a mechanical-state issue?
Send a plain-text channel test:
echo "PRINT CHANNEL TEST $(date)" | lp -d <printer-queue> -t channel-test
sleep 6
# Check the printer's ledger again — and walk to the printer- Page comes out → mechanical fine, problem is document-specific (go to step 4).
- Nothing comes out, and the printer's ledger reports
impressions=1, sheets=0for the channel test → genuine mechanical issue (paper out, jam, cover open). The IPPstate-reasons=nonewill lie about this on this firmware family — front panel tells the truth. Look for blinking lights or error codes on the printer.
4. Bypass the IPP-Everywhere PDF/PWG interpreter (the real fix)
If the channel test prints but PDFs don't, the printer's IPP-Everywhere PDF interpreter is dropping the document. Do not waste time re-rendering, untagging, flattening, or version-downgrading the PDF — those won't help. Instead, add a parallel queue that uses raw socket-9100 with a PostScript PPD:
# Run the helper script (or do it manually below)
"${CLAUDE_PLUGIN_ROOT:-$HOME/.claude/plugins/marketplaces/cc-skills/plugins/doc-tools}/skills/terminal-print/assets/setup-socket-9100-queue.sh"What that script does, manually:
# 1. Get printer IP (resolve from Bonjour)
PRINTER_IP=$(dscacheutil -q host -a name HP28C5C8A02E22.local | awk '/ip_address/{print $2; exit}')
# 2. Confirm TCP/9100 (JetDirect) listening
nc -z -v -w 3 "$PRINTER_IP" 9100 # expect "Connection ... succeeded"
# 3. Create a parallel queue with Generic PostScript PPD
lpadmin -p HP_3101_PS9100 \
-E \
-v "socket://$PRINTER_IP:9100" \
-m drv:///sample.drv/generic.ppd \
-D "HP LaserJet (PostScript over socket-9100, AirPrint bypass)"
# 4. Print to the new queue — local CUPS converts PDF→PS via cgpdftops,
# ships raw PostScript over JetDirect, printer's PS interpreter renders it.
lp -d HP_3101_PS9100 -o media=Letter /path/to/document.pdfTo remove the queue later: lpadmin -x HP_3101_PS9100.
5. If even socket-9100 + PostScript fails
The cgpdftops PostScript output itself may have something the printer's PS interpreter can't render (rare). Fallbacks in order:
# (a) Convert via cupsfilter to the printer's preferred format and submit raw
cupsfilter -p HP_3101_PS9100 -m image/pwg-raster /path/to/doc.pdf > /tmp/doc.pwg
lp -d HP_3101_PS9100 -o raw /tmp/doc.pwg
# (b) Use the LaserJet PCL 4/5 PPD instead of Generic PS
lpadmin -p HP_3101_PCL9100 -E -v "socket://$PRINTER_IP:9100" -m drv:///sample.drv/laserjet.ppd
# (c) Re-render the source HTML to a simpler PDF — drop tagged PDF, drop landscape,
# drop -webkit-print-color-adjust. Some firmware PDF interpreters reject these.---
What CUPS Will Tell You (and Why You Can't Trust It)
lpstat -o # active queue — empties in seconds even on failure
ipptool -tv ipp://localhost/jobs/<N> # local-CUPS view — says "completed" regardless
tail /var/log/cups/error_log # default verbosity is "warn" — silent on these failuresThe local Mac CUPS treats the IPP-Everywhere transaction as binary success/failure. If the printer's IPP server returned successful-ok (it does), CUPS marks completed and discards the job. The fact that the printer rasterized 0 pages is invisible at this layer.
The only reliable signal from the local Mac side is job-state-reasons=processing-to-stop-point paired with job-impressions-completed=0. That combo is the fingerprint of "AirPrint accepted the bytes and the printer ate them silently."
---
Why This Happens
HP LaserJet Pro MFP 3101 (and the broader 3101/3108/3201/3208/3301/3308 family) ships an IPP-Everywhere implementation whose PDF interpreter has known incompatibilities. Documented externally:
- Manjaro forum (forum.manjaro.org/t/.../92072) — same printer family, same blank-page symptom. Resolution: socket://IP:9100 + PostScript driver.
- Apple CUPS issue #5002 (github.com/apple/cups/issues/5002) — unresolved upstream; workaround discussions point to direct socket submission.
- HP support has stopped shipping full-feature drivers for macOS (Sequoia/Tahoe), forcing reliance on AirPrint and exposing this firmware bug to all Mac users.
The firmware's PostScript Level 3 interpreter, by contrast, is reliable on this same printer — confirmed end-to-end on 2026-05-09. Use it.
---
Quick Recipe Card (when you're sure this is what you're hitting)
# One-time setup
PRINTER_IP=192.168.0.196 # or resolve via dscacheutil
lpadmin -p HP_3101_PS9100 -E -v "socket://$PRINTER_IP:9100" -m drv:///sample.drv/generic.ppd
# Every print
lp -d HP_3101_PS9100 -o media=Letter document.pdfEvolution Log
Convention: Reverse chronological order (newest on top, oldest at bottom). Prepend new entries.
---
2026-05-09: AirPrint blank-page workaround
Status: New flag --bypass-airprint, new helper script, new troubleshooting reference.
What changed
- `SKILL.md`: Description now triggers on AirPrint/blank-page symptoms. Added
--bypass-airprintand--printer NAMEflags to the options table. New "Blank page came out" troubleshooting section pointing to the new reference. Default-printer description corrected — script now auto-detects vialpstat -drather than hardcoding. - `assets/print-terminal.sh`: Auto-detects system default printer (was hardcoded). New
--bypass-airprintflag routes through a parallelsocket://IP:9100queue with a Generic PostScript PPD. New--printer NAMEoverride. Help text expanded. After-print message reminds the user to verify physical output, since CUPS markscompletedregardless. - `assets/setup-socket-9100-queue.sh` (new): One-shot helper that auto-discovers the HP printer via Bonjour, probes TCP/9100, and creates the bypass CUPS queue (
HP_3101_PS9100by default). Idempotent — exits cleanly if the queue already exists. - `references/airprint-blank-page-troubleshooting.md` (new): Full diagnostic playbook with the printer-side IPP ledger queries, the failure-signature decision table, the diagnostic ladder, and the manual setup recipe. Captures the lesson that
job-state=completedfrom local CUPS is meaningless on AirPrint queues — the only ground truth isjob-impressions-completedqueried directly off the printer. - `references/workflow.md`: New "Dual-Queue Architecture" section explaining the AirPrint vs socket-9100 split with an ASCII diagram. New "Diagnostic ledger — when CUPS lies" section with the
ipptoolquery template. Files table expanded.
Why it changed
Empirical session 2026-05-09 (Claude Code, plus user testing): printing a Chrome-headless landscape Letter PDF to the HP LaserJet Pro MFP 3101 via the default AirPrint queue silently produced no output across multiple attempts. CUPS reported job-state=completed. The printer's own IPP job ledger reported job-impressions-completed=0 — diagnostic of the firmware's IPP-Everywhere PDF interpreter dropping the document. A cupsfilter PDF→PostScript conversion submitted via the same AirPrint queue produced a _blank_ page (printer rasterized something, but it was empty). Direct IPP submission also failed.
A parallel queue using socket://192.168.0.196:9100 with the Generic PostScript Printer PPD worked first try. This matches the documented Manjaro forum fix for the same printer family (forum.manjaro.org/.../92072) and the unresolved Apple CUPS issue #5002.
The skill previously had no awareness of this failure mode, so a Claude session debugging it had to rediscover the diagnostic protocol from scratch (~30 minutes). The update encodes the symptom signature, the printer-side ledger query, and the working fix so the next session can resolve it in seconds.
Files affected
SKILL.md (modified)
assets/print-terminal.sh (modified)
assets/setup-socket-9100-queue.sh (new)
references/workflow.md (modified)
references/airprint-blank-page-troubleshooting.md (new)
references/evolution-log.md (this entry)Provenance
- Real session, 2026-05-09, while printing a one-page integration-spectrum field guide for a CS-1 student.
- Failure modes observed in order: PDF rejected (
impressions=0); cgpdftops PostScript via AirPrint produced blank page (impressions=0but sheet ejected blank); plain text via AirPrint printed correctly (impressions=1, sheets=0per IPP — counter under-reports); socket-9100 + PostScript PPD printed correctly first try. - Affected printer firmware: HP LaserJet Pro MFP 3101fdw (and 3108/3201/3208/3301/3308 share the IPP stack).
---
2026-02-26: Initial Evolution Log
Status: Skill is in use and maintained. Track improvements here.
Purpose
This evolution log tracks updates to the skill. Each entry should note:
- What changed (content, structure, tooling)
- Why it changed (bug fix, feature request, best practice)
- Files affected
How to Use
1. When updating SKILL.md or references, add an entry here with the date 2. Keep entries reverse-chronological (newest first) 3. Link to ADRs or GitHub issues when relevant 4. Reference specific line changes when helpful
---
Terminal Print Workflow
Detailed documentation for the terminal-print skill.
Pipeline Architecture
``` ┌─────────────────────────────────────────────────────────────────────┐ │ INPUT (Clipboard or File) │ │ pbpaste OR cat file.txt │ └─────────────────────────────────┬───────────────────────────────────┘ │ ▼ ┌─────────────────────────────┐ │ Strip ANSI Codes │ │ sed 's/\x1b\[[0-9;]*m//g' │ └─────────────┬───────────────┘ │ ▼ ┌─────────────────────────────┐ │ Wrap in Markdown Code │ │ Block with `text │ └─────────────┬───────────────┘ │ ▼ ┌─────────────────────────────┐ │ pandoc + xelatex │ │ Markdown → PDF │ │ (Letter, landscape, │ │ DejaVu Sans Mono) │ └─────────────┬───────────────┘ │ ▼ ┌─────────────────────────────┐ │ Preview in Preview.app │ │ (unless --no-preview) │ └─────────────┬───────────────┘ │ ▼ ┌─────────────────────────────┐ │ Confirm & Print │ │ lpr → HP printer │ │ (single-sided) │ └─────────────────────────────┘ ```
Design Decisions
Why Strip ANSI Instead of Converting to Colors?
1. Target printer is B&W: HP LaserJet Pro MFP 3101 is a monochrome printer 2. Simpler pipeline: No HTML intermediate, no color-to-grayscale mapping 3. cc-skills pattern: Follows existing Markdown → LaTeX → PDF workflow 4. Reliability: Avoids pandoc HTML table issues with <br> tags
Why Markdown Code Blocks?
1. Monospace rendering: Proper character alignment for terminal output 2. Native pandoc support: Direct LaTeX conversion without HTML 3. Syntax highlighting: Optional via --highlight-style (not enabled by default)
Why Landscape Orientation?
Terminal output typically has 80-120 character lines. Landscape orientation:
- Fits ~120 characters per line at 9pt font
- Reduces line wrapping
- Better matches terminal aspect ratio
Customization
Change Default Printer
Edit assets/print-terminal.sh line 19:
PRINTER="Your_Printer_Name_Here"Find your printer name with: lpstat -p -d
Change Font Size
Edit the pandoc command in assets/print-terminal.sh:
-V fontsize=9pt # Default
-V fontsize=8pt # Smaller (more content per page)
-V fontsize=10pt # Larger (easier to read)Enable Syntax Highlighting
Add to the pandoc command:
--highlight-style=tangoAvailable styles: pygments, tango, espresso, zenburn, kate, monochrome
Change Paper Size
Edit the geometry variable:
-V geometry:letterpaper,landscape # US Letter (default)
-V geometry:a4paper,landscape # A4 (international)Integration with cc-skills
This skill follows the cc-skills pattern established by:
- pandoc-pdf-generation: Universal build script pattern
- asciinema-converter: ANSI stripping pattern
- Shell command portability: Heredoc invocation for zsh compatibility
Dual-Queue Architecture (AirPrint blank-page workaround)
This skill supports two CUPS queues to the same physical printer, picked at runtime via --bypass-airprint:
┌─────────────────────────────────────┐
│ HP LaserJet Pro MFP 3101 (192.168) │
├─────────────────────────────────────┤
PDF ──▶ AirPrint queue (default) ──▶ dnssd:// ─▶│ IPP-Everywhere PDF interpreter │ ✗ buggy: drops PDFs silently
(HP_LaserJet_…AirPrint…) │ ────────────────────────────── │
│ PostScript Level 3 interpreter │ ✓ reliable
PDF ─cgpdftops─▶ Bypass queue ──▶ socket://9100 ┘ ┘
(HP_3101_PS9100)
Generic PostScript PPDWhy two queues?
The HP LaserJet Pro MFP 3101/3108 family ships an IPP-Everywhere PDF interpreter that silently drops some PDFs (notably Chrome-headless landscape output with tagged structure). CUPS receives successful-ok from the printer's IPP server and marks the local job completed, while the printer rasterized zero pages. The printer's own job ledger reveals the truth — job-impressions-completed=0. Documented externally on Manjaro forums and Apple CUPS issue #5002.
The PostScript Level 3 interpreter on the same printer is reliable. socket://IP:9100 (JetDirect raw print) sends pre-rasterized PostScript directly to that interpreter, bypassing the broken IPP-Everywhere path entirely.
Setup the bypass queue (one-time)
"${CLAUDE_PLUGIN_ROOT}/skills/terminal-print/assets/setup-socket-9100-queue.sh"The script auto-discovers the printer via Bonjour, probes TCP/9100, and creates a CUPS queue named HP_3101_PS9100 with the Generic PostScript Printer PPD. See airprint-blank-page-troubleshooting.md for the full diagnostic protocol.
Diagnostic ledger — when CUPS lies
Local CUPS reports job-state=completed whether or not the printer actually rendered the page. The reliable signal is the printer's own ledger queried directly:
PRINTER_HOST="HP28C5C8A02E22.local.:631"
ipptool -tv "ipp://$PRINTER_HOST/ipp/print" - <<'EOF' | grep -E "job-(id|name|impressions-completed|media-sheets-completed)"
{
OPERATION Get-Jobs
GROUP operation-attributes-tag
ATTR charset attributes-charset utf-8
ATTR naturalLanguage attributes-natural-language en
ATTR uri printer-uri $uri
ATTR keyword which-jobs completed
ATTR keyword requested-attributes job-id,job-state,job-impressions-completed,job-media-sheets-completed,job-name
STATUS successful-ok
}
EOFimpressions=0, sheets=0 is the unambiguous fingerprint of "AirPrint accepted the bytes and the printer dropped them."
Files
| File | Purpose |
|---|---|
SKILL.md | Skill definition and quick start |
assets/print-terminal.sh | Main execution script |
assets/setup-socket-9100-queue.sh | One-time setup of the AirPrint bypass queue |
references/workflow.md | This detailed documentation |
references/airprint-blank-page-troubleshooting.md | Full diagnostic playbook for blank-page failures |
references/evolution-log.md | Update history |