將產生的 CLI 發布至 printing-press-library 儲存庫
/printing-press publish
將本地庫中產生的 CLI 以 Pull Request 形式發布至 printing-press-library 儲存庫。
/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
PR shape guard
此 Skill 僅會建立發布已產生 CLI 的 PR,或者在帶有 --blocked-api-journal 參數時建立 blocked-apis.json 日誌 PR。切勿建立純文件(docs-only)、計畫(plan)、提案(proposal)或規格(spec)PR 來替代尚未準備好發布的 CLI。若產生、驗證或實機測試(live testing)受阻,請回報確切的阻擋原因並停止執行。
Direct User Invocation Required
發布流程可能會 fork mvanhorn/printing-press-library、推送分支並建立或更新 PR。在進行設定或驗證之前,請先檢查呼叫上下文(invocation context)。若此 Skill 是作為 printing-press-polish「Publish Offer」的鏈式後續呼叫(包括回應 AskUserQuestion 或自動套用的 polish 建議),請立即停止,並提示使用者在新的訊息中傳送 /printing-press-publish <cli-name> --from-polish。使用者發起的全新發布請求即視為授權,無須在直接請求之上額外提示確認。
若使用者的全新請求包含 --from-polish,請記錄 POLISH_HANDOFF=true 供終端狀態步驟使用,並在解析 CLI 名稱時忽略該標記。此標記並非二次確認,也不會傳遞給 cli-printing-press;它僅用於在新的 turn 完成發布後,保留原本獨立 polish 的發布後復盤(retro offer)選項。
若請求包含 --blocked-api-journal,請進入下方的 Blocked API Journal 模式,而非一般的 printed-CLI 發布流程。此模式可以在使用者明確選擇「Add to blocked-API journal」後從 /printing-press 的暫緩選單(hold-path menu)呼叫;上層選單的選擇已足夠代表使用者對公開庫日誌寫入的授權。此僅限日誌(journal-only)的模式無須要求第二次全新 turn 的呼叫。
若使用者的全新請求包含 --skip-live-test=<reason>,請將非空的確切原因記錄為 SKIP_LIVE_TEST_REASON,並在解析 CLI 名稱前移除該 flag。這是發布時實機測試門檻(live test gate)唯一支援的豁免機制。僅能在無法取得驗證權限(auth-unavailable)、已知上游故障(upstream outage)、區域網路無法連線的硬體 API 等明確經操作者批准的情況下使用;絕不能僅因普通的延遲或存在較舊的 Phase 5 標記就自動推斷跳過。
公開庫將 library/<category>/<api-slug>/.printing-press.json 與 manifest.json 視為 Registry 顯示欄位的權威來源(source of truth)。請勿在發布 PR 中編輯 registry.json、README 目錄表格儲存格或 cli-skills/pp-<api-slug>/SKILL.md;這三者在 PR 合併後皆由公開庫自身的自動化工作流重新產生。公開庫在 verify-library-conventions.yml 中的 Fail on changes to generated artifacts 檢查會嚴格打回任何與基底分支(base)比對變更涉及 registry.json 或 cli-skills/pp-*/SKILL.md 的 PR(無論是 fork 還是同儲存庫 PR),因此包含上述檔案變更的發布 PR 在審查前就會被系統退件。
公開庫同時負責管理每個 CLI 的版本發布記錄。請勿在發布 PR 中手動更新 CHANGELOG.md、.printing-press-release.json 或執行階段的 var version = ...。新產生的 CLI 可包含空白的版本記錄骨架;公開庫的合併後工作流會在合併後自動分配最終的 YYYY.M.N 版本號並寫入執行階段版本。替換既有的公開庫 CLI 時,請保留其現有的版本記錄檔案,以免在重新印刷(reprint)PR 中遺失變更日誌歷史。
blocked-apis.json 則不同:它是手動維護的公開庫日誌,而非自動產生的 Registry 介面。純日誌 PR 可編輯 blocked-apis.json,但絕不能暫存(stage)library/、registry.json、README 目錄欄位或 cli-skills/。
Blocked API Journal Mode
僅在呼叫包含 --blocked-api-journal 時使用此模式。它用於記錄處於暫緩(hold)狀態的 /printing-press 嘗試,其阻擋原因很可能在機器或上游問題改變前持續影響其他使用者。
呼叫端必須提供的欄位:
slug:標準 API slug,而非 CLI 二進位檔名稱。attempted_at:YYYY-MM-DD。verdict:hold。reason:簡明的阻擋原因,不得包含任何機密、本地路徑、cookie、token 或特定帳號資訊。blocking_issue:若已知 Printing Press Issue 編號請填入,否則填入null。permanent:布林值。
若呼叫端未提供上述某個欄位,僅能從目前的執行上下文中推斷安全的預設值。若 reason 缺失或過於模糊,請停止並要求提供一句具體的阻擋說明;切勿寫入無意義的日誌條目。
執行標準的 Setup、Configuration、區域作用域複製清除(scoped clone cleanup)與 GitHub 驗證檢查,然後與正常發布流程一樣準備公開庫的 clone:必要時進行 fork、確保 upstream 指向 mvanhorn/printing-press-library、擷取(fetch)upstream,並在編輯前將 clone 重置為 upstream/main。
接著僅更新 $PUBLISH_REPO_DIR/blocked-apis.json:
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
建立日誌分支與 PR:
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>
向 mvanhorn/printing-press-library 建立 PR,內容應包含:
- 暫緩的 API slug 與阻擋原因
- 該阻擋是永久性的還是與
blocking_issue關聯 - 預期的 Phase 0 行為:未來執行
/printing-press <api-slug>時會在重複嘗試前發出警告
PR 建立完成後,回報 URL 並停止。請勿繼續進行一般的 printed-CLI 打包、實機測試、Registry 或 Skill 鏡像步驟。
Setup
在執行其他任何操作之前:
<!-- PRESS_SETUP_CONTRACT_START -->
# 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 into every later invocation.
if [ "$_press_repo" = "true" ]; then
PRINTING_PRESS_BIN="$_scope_dir/cli-printing-press"
else
PRINTING_PRESS_BIN="$(command -v cli-printing-press 2>/dev/null || true)"
fi
if ! command -v go >/dev/null 2>&1; then
echo ""
echo "[setup-error] Go toolchain not found."
echo ""
echo "This Printing Press flow runs Go-based build or validation commands."
echo "Install Go 1.26.5 or newer from https://go.dev/dl/, then verify with:"
echo " go version"
echo "Then re-run this skill."
echo ""
return 1 2>/dev/null || exit 1
fi
echo "PRINTING_PRESS_BIN=$PRINTING_PRESS_BIN"
_pp_semver_lt() {
awk -v a="$1" -v b="$2" 'BEGIN {
split(a, x, "."); split(b, y, ".")
for (i = 1; i <= 3; i++) {
if ((x[i] + 0) < (y[i] + 0)) exit 0
if ((x[i] + 0) > (y[i] + 0)) exit 1
}
exit 1
}'
}
_pp_go_version_norm() {
printf '%s\n' "$1" | sed -nE 's/.*go([0-9]+)\.([0-9]+)(\.([0-9]+))?.*/\1.\2.\4/p' | awk -F. 'NF >= 2 { printf "%d.%d.%d\n", $1, $2, ($3 == "" ? 0 : $3) }'
}
_pp_check_go_currency() {
_pp_go_installed="$(_pp_go_version_norm "$(go env GOVERSION 2>/dev/null)")"
_pp_go_required="$(_pp_go_version_norm "$(go version "$PRINTING_PRESS_BIN" 2>/dev/null)")"
if [ -z "$_pp_go_installed" ] || [ -z "$_pp_go_required" ] || ! _pp_semver_lt "$_pp_go_installed" "$_pp_go_required"; then
return 0
fi
echo ""
if [ "${GOTOOLCHAIN:-auto}" = "local" ]; then
echo "[setup-error] Go $_pp_go_required or newer is required by this cli-printing-press binary (installed: $_pp_go_installed)."
echo "GOTOOLCHAIN=local disables automatic toolchain downloads, so later Go quality gates would fail."
echo "Install Go $_pp_go_required or newer from https://go.dev/dl/, or unset GOTOOLCHAIN."
echo ""
return 1
fi
echo "[go-toolchain-old] Go $_pp_go_required or newer is required by this cli-printing-press binary (installed: $_pp_go_installed)."
echo "PRESS_GO_INSTALLED=$_pp_go_installed"
echo "PRESS_GO_REQUIRED=$_pp_go_required"
echo "Default GOTOOLCHAIN behavior may download the required toolchain during Go commands."
echo ""
return 0
}
_pp_check_go_currency || { return 1 2>/dev/null || exit 1; }
PRESS_BASE="$(basename "$_scope_dir" | tr '[:upper:]' '[:lower:]' | sed -E 's/[^a-z0-9_-]/-/g; s/^-+//; s/-+$//')"
if [ -z "$PRESS_BASE" ]; then
PRESS_BASE="workspace"
fi
PRESS_SCOPE="$PRESS_BASE-$(printf '%s' "$_scope_dir" | shasum -a 256 | cut -c1-8)"
PRESS_HOME="${PRINTING_PRESS_HOME:-$HOME/printing-press}"
PRESS_RUNSTATE="$PRESS_HOME/.runstate/$PRESS_SCOPE"
PRESS_LIBRARY="$PRESS_HOME/library"
PRESS_MANUSCRIPTS="$PRESS_HOME/manuscripts"
PRESS_CURRENT="$PRESS_RUNSTATE/current"
_pp_check_disk_space() {
_pp_disk_warn_kb="${PRINTING_PRESS_DISK_WARN_KB:-3145728}"
_pp_disk_fail_kb="${PRINTING_PRESS_DISK_FAIL_KB:-524288}"
case "$_pp_disk_warn_kb$_pp_disk_fail_kb" in
""|*[!0-9]*) return 0 ;;
esac
_pp_disk_path="$PRESS_HOME"
while [ ! -e "$_pp_disk_path" ] && [ "$_pp_disk_path" != "/" ]; do
_pp_disk_path="$(dirname "$_pp_disk_path")"
done
_pp_disk_avail_kb="$(df -Pk "$_pp_disk_path" 2>/dev/null | awk 'NR == 2 { print $4; exit }')"
case "$_pp_disk_avail_kb" in
""|*[!0-9]*) return 0 ;;
esac
if [ "$_pp_disk_avail_kb" -lt "$_pp_disk_fail_kb" ]; then
echo ""
echo "[setup-error] Critically low disk space on the Printing Press workspace volume."
e




