Claude
Skills
Sign in
Back

printing-press-publish

Included with Lifetime
$97 forever

Publish a generated CLI to the printing-press-library repo

General

What this skill does


# /printing-press publish

Publish a generated CLI from your local library to the [printing-press-library](https://github.com/mvanhorn/printing-press-library) repo as a pull request.

```bash
/printing-press publish notion-pp-cli
/printing-press publish notion
/printing-press publish notion --from-polish
/printing-press publish notion --skip-live-test=auth-unavailable
/printing-press publish notion --blocked-api-journal notion
/printing-press publish
```

## Direct User Invocation Required

Publishing can fork `mvanhorn/printing-press-library`, push a branch, and open or
update a PR. Before setup or validation, check the invocation context. If this
skill was invoked as a chained continuation from `printing-press-polish`'s
Publish Offer, including an `AskUserQuestion` answer or auto-resolved polish
recommendation, stop immediately and tell the user to send
`/printing-press-publish <cli-name> --from-polish` in a fresh message. A fresh
user-authored request that explicitly asks to publish is sufficient; do not add
another confirmation prompt on top of a direct publish request.

If the fresh user-authored request includes `--from-polish`, record
`POLISH_HANDOFF=true` for the terminal-state step and ignore that marker when
resolving the CLI name. The marker is not a second confirmation and is not
passed to `cli-printing-press`; it only preserves standalone polish's old
post-publish retro offer after the fresh-turn publish completes.

If the request includes `--blocked-api-journal`, enter **Blocked API Journal
Mode** below instead of the normal printed-CLI publish flow. This mode may be
invoked from `/printing-press`'s hold-path menu after the user explicitly chose
"Add to blocked-API journal"; that parent menu choice is sufficient user
authorization for the public-library journal write. Do not require a second
fresh-turn invocation for this journal-only mode.

If the fresh user-authored request includes `--skip-live-test=<reason>`, record
the exact non-empty reason as `SKIP_LIVE_TEST_REASON` and remove the flag before
resolving the CLI name. This is the only supported escape valve for the
publish-time live test gate. Use it only for auth-unavailable, known upstream
outage, LAN-unreachable hardware APIs, or similarly concrete operator-approved
cases; never infer a skip from ordinary latency or from the presence of an
older Phase 5 marker.

The public library treats `library/<category>/<api-slug>/.printing-press.json`
and `manifest.json` as the source of truth for registry-display fields. Do not
edit `registry.json`, README catalog cells, or `cli-skills/pp-<api-slug>/SKILL.md`
in publish PRs; all three are bot-regenerated post-merge by the library's own
workflows. The library's `Fail on changes to generated artifacts` check in
`verify-library-conventions.yml` hard-fails any PR — fork or same-repo — whose
diff against base touches `registry.json` or `cli-skills/pp-*/SKILL.md`, so a
publish that includes either is pre-rejected before review.

The public library also owns per-CLI release accounting. Do not manually bump
`CHANGELOG.md`, `.printing-press-release.json`, or runtime `var version = ...`
for a publish PR. Fresh printed CLIs may include blank release-ledger skeletons;
the library's post-merge workflow assigns the final `YYYY.M.N` release and
stamps the runtime version after merge. When replacing an existing public
library CLI, preserve its existing release-ledger files so changelog history is
not lost in the reprint PR.

`blocked-apis.json` is different: it is a hand-maintained public-library journal,
not a generated registry surface. Journal-only PRs may edit `blocked-apis.json`
and must not stage `library/`, `registry.json`, README catalog cells, or
`cli-skills/`.

## Blocked API Journal Mode

Use this mode only when the invocation includes `--blocked-api-journal`. It
records a held `/printing-press` attempt whose blocker is likely to repeat for
other users until a machine or upstream issue changes.

Required fields from the caller:

- `slug`: canonical API slug, not the CLI binary name.
- `attempted_at`: `YYYY-MM-DD`.
- `verdict`: `hold`.
- `reason`: concise blocker reason, with no secrets, local paths, cookies,
  tokens, or account-specific details.
- `blocking_issue`: Printing Press issue number if known, otherwise `null`.
- `permanent`: boolean.

If the caller did not provide one of these fields, infer only safe values from
the current run context. If `reason` is missing or vague, stop and ask for one
specific blocker sentence; do not write an unhelpful journal entry.

Run the normal Setup, Configuration, scoped clone cleanup, and GitHub auth
checks, then prepare the public-library clone exactly as the normal publish
flow does: fork if needed, ensure `upstream` points to
`mvanhorn/printing-press-library`, fetch `upstream`, and reset the clone to
`upstream/main` before editing.

Then update only `$PUBLISH_REPO_DIR/blocked-apis.json`:

```bash
cd "$PUBLISH_REPO_DIR"
if [ ! -f blocked-apis.json ]; then
  printf '[]\n' > blocked-apis.json
fi
jq --arg slug "<api-slug>" \
   --arg attempted_at "<YYYY-MM-DD>" \
   --arg verdict "hold" \
   --arg reason "<reason>" \
   --argjson blocking_issue '<number-or-null>' \
   --argjson permanent '<true-or-false>' '
  (if type == "array" then . else [] end)
  | map(select(.slug != $slug))
  + [{
      slug: $slug,
      attempted_at: $attempted_at,
      verdict: $verdict,
      reason: $reason,
      blocking_issue: $blocking_issue,
      permanent: $permanent
    }]
  | sort_by(.slug)
' blocked-apis.json > blocked-apis.json.tmp || {
  rm -f blocked-apis.json.tmp
  echo "Error: jq failed to update blocked-apis.json"
  exit 1
}
if ! jq empty blocked-apis.json.tmp; then
  rm -f blocked-apis.json.tmp
  echo "Error: blocked-apis.json update produced invalid JSON"
  exit 1
fi
mv blocked-apis.json.tmp blocked-apis.json
```

Create a journal branch and PR:

```bash
git checkout -B chore/blocked-api-<api-slug>
git add blocked-apis.json
git commit -m "chore(<api-slug>): journal blocked API"
git push --force-with-lease -u origin chore/blocked-api-<api-slug>
```

Open the PR against `mvanhorn/printing-press-library` with a body that includes:

- the held API slug and reason
- whether the block is permanent or tied to `blocking_issue`
- the expected Phase 0 behavior: future `/printing-press <api-slug>` runs warn
  before repeating the attempt

After the PR is open, report the URL and stop. Do not continue into normal
printed-CLI package, live-test, registry, or skill-mirror steps.

## Setup

Before doing anything else:

<!-- PRESS_SETUP_CONTRACT_START -->
```bash
# min-binary-version: 4.0.0

# Derive scope first — needed for local build detection
_scope_dir="$(git rev-parse --show-toplevel 2>/dev/null || echo "$PWD")"
_scope_dir="$(cd "$_scope_dir" && pwd -P)"

# Prefer local build when running from inside the printing-press repo.
_press_repo=false
if [ -x "$_scope_dir/cli-printing-press" ] && [ -d "$_scope_dir/cmd/cli-printing-press" ]; then
  _press_repo=true
  export PATH="$_scope_dir:$PATH"
  echo "Using local build: $_scope_dir/cli-printing-press"
elif ! command -v cli-printing-press >/dev/null 2>&1; then
  if [ -x "$HOME/go/bin/cli-printing-press" ]; then
    echo "cli-printing-press found at ~/go/bin/cli-printing-press but not on PATH."
    echo "Add GOPATH/bin to your PATH:  export PATH=\"\$HOME/go/bin:\$PATH\""
  else
    echo "cli-printing-press binary not found."
    echo "Install with:  go install github.com/mvanhorn/cli-printing-press/v4/cmd/cli-printing-press@latest"
  fi
  return 1 2>/dev/null || exit 1
fi

# Resolve and emit the absolute path the agent must use for every later
# `cli-printing-press` invocation. `export PATH` above only affects this one
# Bash tool call; subsequent calls open a fresh shell and resolve bare
# `cli-printing-press` against the user's default PATH, where a stale global
# can silently shadow the local build. The agent captures this marker and
# substitutes the absolute path int

Related in General