
Printing Press Import
- 4.8k installs
- 4.3k repo stars
- Updated July 23, 2026
- mvanhorn/cli-printing-press
printing-press-import is an agent skill: Bring a published CLI from the public library into the internal libraryso it's identical to a freshly-generated copy — module path reverted,
About
The printing-press-import skill Bring a published CLI from the public library into the internal libraryso it's identical to a freshly-generated copy — module path reverted,manuscripts placed alongside, ready for /printing-press-polish or/printing-press-emboss. Use when the public library has a CLI youdon't have locally, or to recover from a broken/lost internal copy.Trigger phrases: "import the CLI", "bring it into my library","fetch from public library", "I don't have it locally yet".. /printing-press-import Bring a published CLI from the public library ([`mvanhorn/printing-press-library`](https://github.com/mvanhorn/printing-press-library)) into the internal library at `$PRESS_LIBRARY/` so it matches the form the generator would produce. Manuscripts ride along. The internal library is the working copy; the public library is the durable artifact. After import, the CLI is ready for polish, emboss, or re-publish — the publish step will re-apply the module path rewrites. When to run - The public library has a CLI you don't have locally - The internal copy is broken, lost, or out of sync - You
- Covers printing-press-import quick start, workflow steps, and reference pointers from SKILL.md.
- Tagged for stage build and subphase agent-tooling in the closed Skillselion taxonomy.
- Documents prerequisites, permissions shell, filesystem, git, and compatible agents.
- Includes AEO tagMeta with task queries, keywords, and evidence quotes for discovery.
- Cross-links related skills and generated REFERENCE.md tables where the repo provides them.
Printing Press Import by the numbers
- 4,778 all-time installs (skills.sh)
- +261 installs in the week ending Jul 28, 2026 (Skillselion tracking)
- Ranked #38 of 560 CLI & Terminal skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
printing-press-import capabilities & compatibility
- Capabilities
- printing press import documented workflow · quick start examples · reference parameter lookup · taxonomy aligned metadata · aeo discovery fields
- Works with
- github
- Use cases
- orchestration · documentation
npx skills add https://github.com/mvanhorn/cli-printing-press --skill printing-press-importAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 4.8k |
|---|---|
| repo stars | ★ 4.3k |
| Security audit | 1 / 3 scanners passed |
| Last updated | July 23, 2026 |
| Repository | mvanhorn/cli-printing-press ↗ |
How do I run printing-press-import correctly without guessing steps, tools, or parameters?
Bring a published CLI from the public library into the internal libraryso it's identical to a freshly-generated copy — module path reverted,manuscripts placed alongside, ready for /printing-press
Who is it for?
Teams using printing-press-import when SKILL.md triggers match the user request.
Skip if: Skip when the task is outside printing-press-import documented triggers or sibling skill scope.
When should I use this skill?
User mentions printing-press-import, related trigger phrases, or asks to follow this SKILL.md workflow.
What you get
Completed printing-press-import workflow with outputs and checks defined in SKILL.md.
- printing-press-import output per SKILL.md
By the numbers
- Stage build/agent-tooling
- Category CLI & Terminal
- Complexity intermediate
Files
/printing-press-import
Bring a published CLI from the public library (`mvanhorn/printing-press-library`) into the internal library at $PRESS_LIBRARY/ so it matches the form the generator would produce. Manuscripts ride along.
/printing-press-import notion
/printing-press-import cal.com
/printing-press-import allrecipes --from-clone ~/Code/printing-press-libraryThe internal library is the working copy; the public library is the durable artifact. After import, the CLI is ready for polish, emboss, or re-publish — the publish step will re-apply the module path rewrites.
When to run
- The public library has a CLI you don't have locally
- The internal copy is broken, lost, or out of sync
- You want a clean baseline before running polish on a published CLI
If the user is asking to polish a CLI and mentions "in/from the public library" or "from the repo", suggest running this skill first.
Setup
PRESS_HOME="${PRINTING_PRESS_HOME:-$HOME/printing-press}"
PRESS_LIBRARY="$PRESS_HOME/library"
PRESS_MANUSCRIPTS="$PRESS_HOME/manuscripts"
SCRIPTS_DIR="$(dirname "${BASH_SOURCE[0]:-$0}")/references"The four reference scripts live alongside this SKILL.md under references/:
import-fetch.sh <library-path> <staging> [--clone <path>]import-backup.sh <api-slug>(prints zip path on stdout)import-rewrite.sh <staging> <api-slug>import-place.sh <staging> <api-slug>
Phase 1 — Resolve the CLI
The argument can be anything natural: an API slug (notion), a brand name (cal.com), an old CLI name (notion-pp-cli), or close enough (Allrecipes). Resolve via the public library's registry.json — which carries name, category, api, description, and path for every entry, in one fetch.
REGISTRY=$(mktemp)
gh api -H "Accept: application/vnd.github.v3.raw" \
repos/mvanhorn/printing-press-library/contents/registry.json \
> "$REGISTRY"Match in this order:
1. Exact `name` match — jq --arg q "$ARG" '.entries[] | select(.name == $q)' "$REGISTRY" 2. Normalized exact — strip -pp-cli suffix, lowercase, dot→hyphen, then exact match 3. Substring on `name` or `description` — case-insensitive contains
# Exact:
jq --arg q "$ARG" '.entries[] | select(.name == $q)' "$REGISTRY"
# Normalized exact (after $ARG2 = lowercase, dot→hyphen, suffix-stripped):
jq --arg q "$ARG2" '.entries[] | select(.name == $q)' "$REGISTRY"
# Fuzzy (substring on name or description):
jq --arg q "$ARG2" '.entries[]
| select((.name | ascii_downcase | contains($q | ascii_downcase))
or (.description | ascii_downcase | contains($q | ascii_downcase)))
' "$REGISTRY"If you get one match: use it. If multiple: present at most 4 to the user via AskUserQuestion showing name + description per candidate. If zero: tell the user the public library doesn't have that CLI.
The matched entry gives you everything you need:
LIB_PATHfrom.path(e.g.,library/productivity/cal-com)API_SLUGfrom.nameCATEGORYfrom.category
Don't slurp whole files when reasoning over candidates. The fields above are enough; if you genuinely need more, the per-CLI manifest is just <LIB_PATH>/manifest.json and the description there can be pulled the same way (gh api -H "Accept: ... raw" .../manifest.json | jq -r '.description').
Phase 2 — Decide on overwrite
Check whether the internal library already has this CLI:
LIB_TARGET="$PRESS_LIBRARY/$API_SLUG"
MAN_TARGET="$PRESS_MANUSCRIPTS/$API_SLUG"If neither exists: straightforward import — proceed to Phase 3.
If either exists: read provenance from both sides to decide whether to overwrite. Don't read whole .printing-press.json files — pull just the fields that matter:
# Internal provenance (if present):
jq '{run_id, generated_at, printing_press_version, spec_checksum}' \
"$LIB_TARGET/.printing-press.json" 2>/dev/null
# Public provenance (one-shot via raw):
gh api -H "Accept: application/vnd.github.v3.raw" \
repos/mvanhorn/printing-press-library/contents/$LIB_PATH/.printing-press.json \
| jq '{run_id, generated_at, printing_press_version, spec_checksum}'Reason over the diff:
- Same `run_id` — public is the same generation as internal. Likely
no-op; ask before clobbering. If the user wants to import anyway (e.g., to recover from a broken internal copy), proceed.
- Public newer `generated_at` — public has changes the internal
doesn't. Importing is the safe move; ask the user to confirm.
- Internal newer `generated_at` — internal has work the public
doesn't (in-progress polish, manual fixes). Importing would clobber that. Stop and surface this to the user — they likely want to publish the internal changes first.
- Either side missing `.printing-press.json` — older or hand-imported.
Ask the user.
When the user confirms overwrite, the backup step in Phase 3 captures the current internal state.
Phase 3 — Import
STAGING=$(mktemp -d)
# Fetch (remote unless --from-clone was passed)
if [[ -n "${CLONE_PATH:-}" ]]; then
bash "$SCRIPTS_DIR/import-fetch.sh" "$LIB_PATH" "$STAGING" --clone "$CLONE_PATH"
else
bash "$SCRIPTS_DIR/import-fetch.sh" "$LIB_PATH" "$STAGING"
fi
# Backup if anything is being clobbered. Prints zip path on stdout.
if [[ -d "$LIB_TARGET" || -d "$MAN_TARGET" ]]; then
BACKUP_ZIP=$(bash "$SCRIPTS_DIR/import-backup.sh" "$API_SLUG")
echo "Backed up to: $BACKUP_ZIP"
fi
# Reverse the publish-step module path rewrites.
bash "$SCRIPTS_DIR/import-rewrite.sh" "$STAGING" "$API_SLUG"
# Atomically move staging into place.
bash "$SCRIPTS_DIR/import-place.sh" "$STAGING" "$API_SLUG"Phase 4 — Verify internal consistency
After the move, confirm the imported CLI builds and is structurally intact. Treat any failure as a real problem — don't paper over it.
cd "$LIB_TARGET"
# Module path is local form
grep -q "^module ${API_SLUG}-pp-cli\$" go.mod \
|| { echo "FAIL: go.mod still on public module path"; exit 1; }
# No public module path leaked into source
if grep -rq "github.com/mvanhorn/printing-press-library/library" \
--include='*.go' --include='*.yaml' --include='*.yml' .; then
echo "FAIL: source still references public module path"
exit 1
fi
# Build
go build ./... \
|| { echo "FAIL: go build"; exit 1; }
# Doctor (self-check)
make doctor 2>/dev/null \
|| ./bin/${API_SLUG}-pp-cli doctor 2>/dev/null \
|| true # best-effort; not all CLIs have doctor wired the same wayReport the import outcome:
- Source path (from registry:
<category>/<api-slug>) - Run ID (from
.printing-press.json) - Manuscripts run-ids placed (count + names)
- Backup zip path (if any)
- Build status
Polish-side hint
If the user's request to import was triggered by a polish ask (e.g., they said "polish notion in the public library"), suggest:
Imported $API_SLUG. To polish: /printing-press-polish $API_SLUGThe polish skill operates on the internal library, so import-then-polish is the right flow when starting from a published CLI.
#!/usr/bin/env bash
# import-backup.sh — zip an existing internal library CLI + its manuscripts
# to /tmp/printing-press/ before overwriting.
#
# Usage:
# import-backup.sh <api-slug>
#
# Backs up:
# ${PRINTING_PRESS_HOME:-$HOME/printing-press}/library/<api-slug>/
# ${PRINTING_PRESS_HOME:-$HOME/printing-press}/manuscripts/<api-slug>/ (if present)
#
# Output: prints the absolute path of the resulting zip on stdout.
set -euo pipefail
[[ $# -eq 1 ]] || { echo "usage: $0 <api-slug>" >&2; exit 2; }
API_SLUG="$1"
BACKUP_DIR="/tmp/printing-press"
TS=$(date -u +%Y%m%dT%H%M%SZ)
ZIP_PATH="$BACKUP_DIR/${API_SLUG}-${TS}.zip"
PRESS_HOME="${PRINTING_PRESS_HOME:-$HOME/printing-press}"
LIBRARY_DIR="$PRESS_HOME/library/$API_SLUG"
MANUSCRIPTS_DIR="$PRESS_HOME/manuscripts/$API_SLUG"
if [[ ! -d "$LIBRARY_DIR" && ! -d "$MANUSCRIPTS_DIR" ]]; then
echo "nothing to backup for $API_SLUG" >&2
exit 0
fi
mkdir -p "$BACKUP_DIR"
# Stage in a temp dir so the zip preserves the relative layout users
# would need to restore by hand: library/<api>/ and manuscripts/<api>/.
STAGE=$(mktemp -d)
trap 'rm -rf "$STAGE"' EXIT
if [[ -d "$LIBRARY_DIR" ]]; then
mkdir -p "$STAGE/library"
cp -R "$LIBRARY_DIR" "$STAGE/library/"
fi
if [[ -d "$MANUSCRIPTS_DIR" ]]; then
mkdir -p "$STAGE/manuscripts"
cp -R "$MANUSCRIPTS_DIR" "$STAGE/manuscripts/"
fi
(cd "$STAGE" && zip -qr "$ZIP_PATH" .)
echo "$ZIP_PATH"
#!/usr/bin/env bash
# import-fetch.sh — fetch one CLI subtree + its .manuscripts from the
# public library into a staging dir.
#
# Usage:
# import-fetch.sh <library-path> <staging-dir> [--clone <local-clone-path>]
#
# Where <library-path> is the `path` field from registry.json (e.g.
# `library/productivity/cal-com`). When --clone is supplied, the script
# copies from the local clone instead of hitting GitHub.
#
# Exits 0 on success; non-zero on any failure.
set -euo pipefail
usage() {
echo "usage: $0 <library-path> <staging-dir> [--clone <path>]" >&2
exit 2
}
[[ $# -lt 2 ]] && usage
LIB_PATH="$1"
STAGING="$2"
shift 2
CLONE_PATH=""
while [[ $# -gt 0 ]]; do
case "$1" in
--clone)
CLONE_PATH="${2:-}"
[[ -z "$CLONE_PATH" ]] && usage
shift 2
;;
*)
usage
;;
esac
done
mkdir -p "$STAGING"
if [[ -n "$CLONE_PATH" ]]; then
SRC="$CLONE_PATH/$LIB_PATH"
[[ -d "$SRC" ]] || { echo "source not found in clone: $SRC" >&2; exit 1; }
cp -R "$SRC/." "$STAGING/"
exit 0
fi
# Remote fetch: shallow clone the whole repo to a temp dir, then copy the
# subtree out. GitHub's contents API can't return whole subtrees in one
# call and rate-limits per-file fetching for large CLIs. A shallow clone
# is ~2-5 MB and one round-trip.
TMP_CLONE=$(mktemp -d)
trap 'rm -rf "$TMP_CLONE"' EXIT
git clone --depth 1 --quiet \
https://github.com/mvanhorn/printing-press-library.git \
"$TMP_CLONE"
SRC="$TMP_CLONE/$LIB_PATH"
[[ -d "$SRC" ]] || { echo "source not found in clone: $SRC" >&2; exit 1; }
cp -R "$SRC/." "$STAGING/"
#!/usr/bin/env bash
# import-place.sh — atomically move a staged CLI + its manuscripts into
# the internal library at ${PRINTING_PRESS_HOME:-$HOME/printing-press}/.
#
# Layout:
# <staging>/ (CLI files at root)
# <staging>/.manuscripts/<run>/ (one or more run-id dirs)
#
# Lands as:
# ${PRINTING_PRESS_HOME:-$HOME/printing-press}/library/<api-slug>/ (CLI files)
# ${PRINTING_PRESS_HOME:-$HOME/printing-press}/manuscripts/<api-slug>/<run>/ (each run dir)
#
# Any pre-existing target dirs are removed first; back them up with
# import-backup.sh before invoking this.
#
# Usage:
# import-place.sh <staging-dir> <api-slug>
set -euo pipefail
[[ $# -eq 2 ]] || { echo "usage: $0 <staging-dir> <api-slug>" >&2; exit 2; }
STAGING="$1"
API_SLUG="$2"
[[ -d "$STAGING" ]] || { echo "staging dir not found: $STAGING" >&2; exit 1; }
PRESS_HOME="${PRINTING_PRESS_HOME:-$HOME/printing-press}"
LIB_TARGET="$PRESS_HOME/library/$API_SLUG"
MAN_TARGET_ROOT="$PRESS_HOME/manuscripts/$API_SLUG"
# Move manuscripts out of the staging dir before placing the CLI. This
# keeps the CLI subtree clean (no .manuscripts/ inside the library dir).
MAN_STAGE="$STAGING/.manuscripts"
mkdir -p "$(dirname "$LIB_TARGET")"
rm -rf "$LIB_TARGET"
if [[ -d "$MAN_STAGE" ]]; then
mkdir -p "$MAN_TARGET_ROOT"
for run_dir in "$MAN_STAGE"/*/; do
[[ -d "$run_dir" ]] || continue
run_name=$(basename "$run_dir")
run_target="$MAN_TARGET_ROOT/$run_name"
rm -rf "$run_target"
mv "$run_dir" "$run_target"
done
rmdir "$MAN_STAGE" 2>/dev/null || true
fi
# Now the staging dir contains only CLI files; move it into place.
mv "$STAGING" "$LIB_TARGET"
echo "placed: $LIB_TARGET"
[[ -d "$MAN_TARGET_ROOT" ]] && echo "manuscripts: $MAN_TARGET_ROOT"
#!/usr/bin/env bash
# import-rewrite.sh — reverse the publish-step module-path rewrites in a
# staged CLI directory so it matches the freshly-generated form.
#
# Mirrors internal/pipeline/modulepath.go's RewriteModulePath in reverse:
# module github.com/mvanhorn/printing-press-library/library/<cat>/<api>
# -> module <api>-pp-cli
#
# github.com/mvanhorn/printing-press-library/library/<cat>/<api>/internal/
# -> <api>-pp-cli/internal/
#
# github.com/mvanhorn/printing-press-library/library/<cat>/<api>/cmd/
# -> <api>-pp-cli/cmd/
#
# README links and goreleaser ldflags pointing at the public module path
# get reverted alongside imports. Other GitHub URLs in docs (e.g., links
# to the public release page itself) are left alone — re-publishing will
# overwrite them.
#
# Usage:
# import-rewrite.sh <staging-dir> <api-slug>
set -euo pipefail
[[ $# -eq 2 ]] || { echo "usage: $0 <staging-dir> <api-slug>" >&2; exit 2; }
STAGING="$1"
API_SLUG="$2"
[[ -d "$STAGING" ]] || { echo "staging dir not found: $STAGING" >&2; exit 1; }
[[ -f "$STAGING/go.mod" ]] || { echo "go.mod not found in $STAGING" >&2; exit 1; }
# Read the current module path from go.mod to derive the public prefix.
PUBLIC_MODULE=$(awk '$1=="module"{print $2; exit}' "$STAGING/go.mod")
if [[ -z "$PUBLIC_MODULE" ]]; then
echo "could not parse module path from $STAGING/go.mod" >&2
exit 1
fi
LOCAL_MODULE="${API_SLUG}-pp-cli"
if [[ "$PUBLIC_MODULE" == "$LOCAL_MODULE" ]]; then
echo "go.mod already on local module path; nothing to rewrite" >&2
exit 0
fi
# Rewrite go.mod first (single-line replace, anchored).
perl -pi -e "s|^module \Q${PUBLIC_MODULE}\E\$|module ${LOCAL_MODULE}|" \
"$STAGING/go.mod"
# Rewrite import-style references in source files. Limit to the
# extensions RewriteModulePath touches: .go, .yaml, .yml, .md.
find "$STAGING" \
\( -name '*.go' -o -name '*.yaml' -o -name '*.yml' -o -name '*.md' \) \
-type f \
-print0 \
| xargs -0 perl -pi \
-e "s|\Q${PUBLIC_MODULE}\E/internal/|${LOCAL_MODULE}/internal/|g;" \
-e "s|\Q${PUBLIC_MODULE}\E/cmd/|${LOCAL_MODULE}/cmd/|g;"
Related skills
How it compares
printing-press-import implements its own SKILL.md workflow rather than a generic substitute skill.
FAQ
Who is printing-press-import for?
Agents and developers following the printing-press-import SKILL.md guidance.
When should I use printing-press-import?
When user intent matches description triggers and quick start scenarios.
Is printing-press-import safe to install?
Review the Security Audits panel before production shell or network use.