printing-press-publish

printing-press-publish

熱門

將產生的 CLI 發布至 printing-press-library 儲存庫

4028星標
441分支
更新於 2026/7/16
SKILL.md
唯讀
名稱
printing-press-publish
描述

將產生的 CLI 發布至 printing-press-library 儲存庫

版本
0.1.0

/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.jsonmanifest.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.jsoncli-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_atYYYY-MM-DD
  • verdicthold
  • 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