将生成的 CLI 以 Pull Request 形式发布到 printing-press-library 仓库
/printing-press publish
以 Pull Request 的形式,将本地库中生成的 CLI 发布到 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 规范守卫
本 Skill 仅用于提交生成类 CLI 的发布 PR;或者在传入 --blocked-api-journal 时,提交 blocked-apis.json 日志 PR。绝不能以纯文档、方案、提议或规范 PR 的形式,来替代尚未准备好发布的 CLI。若生成、校验或实测环节被卡住(blocked),请直接报告具体的阻塞原因并立即终止流程。
需要用户直接调用
发布流程可能会 fork mvanhorn/printing-press-library 仓库、推送分支并创建或更新 PR。在执行初始化设置或校验之前,请务必检查调用上下文。如果本 Skill 是作为 printing-press-polish 提示“是否发布”(Publish Offer)的链式连续调用(包括回答了 AskUserQuestion 或自动触发了润色建议),必须立即停止,并提示用户在全新的消息中手动发送 /printing-press-publish <cli-name> --from-polish。只要用户在全新的消息中明确发出了发布请求就已足够;对于直接发起的发布请求,无需再二次弹窗确认。
如果全新的用户请求中包含了 --from-polish,请为终态步骤记录 POLISH_HANDOFF=true 标记,但在解析 CLI 名称时忽略该标记。该标记并不是二次确认,也不会传递给 cli-printing-press;它的作用仅在于:在全新 Turn 里的发布完成后,保留独立润色环节原本在发布后提供的回顾建议。
如果请求中包含了 --blocked-api-journal,则进入下文的 受阻 API 日志模式(Blocked API Journal Mode),而非常规的 CLI 发布流程。该模式可以在用户明确选择“添加到受阻 API 日志”后,从 /printing-press 的 Hold 路径菜单中调用;父级菜单的这一选择已具备充分授权,允许向公共库写入日志。此单日志模式无需用户在全新 Turn 中再次发起调用。
如果全新的用户请求中包含了 --skip-live-test=<reason>,请将该非空原因精确记录为 SKIP_LIVE_TEST_REASON,并在解析 CLI 名称前移除该 flag。这是发布阶段绕过实测卡点的唯一合法出口。仅可在鉴权不可用、已知上游服务故障、局域网无法连通硬件 API 等经明确批准的具体场景下使用;绝不能因为普通的延迟波动或存在旧的 Phase 5 标记就自行推断跳过。
公共库以 library/<category>/<api-slug>/.printing-press.json 和 manifest.json 作为注册表展示字段的单一事实来源(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 分支的 Diff 只要修改了 registry.json 或 cli-skills/pp-*/SKILL.md(无论来自 Fork 还是同仓库 PR),都会直接报 Hard Fail 并拦截,因此包含这两类修改的发布 PR 在 Review 之前就会被直接拒绝。
公共库同样独立管理各 CLI 的版本发布记录。请勿在发布 PR 中手动递增 CHANGELOG.md、.printing-press-release.json 或运行时中的 var version = ...。新印制的 CLI 可以包含空白的版本日志骨架;公共库的合并后工作流会在 Merge 后自动分配最终的 YYYY.M.N 发布版本并写入运行时版本。在替换公共库中已有的 CLI 时,请保留其原有的版本日志文件,确保 Changelog 历史在重新发布 PR 中不会丢失。
blocked-apis.json 则有所不同:它是公共库中由人工维护的日志文件,而非自动生成的注册表产物。仅涉及日志更新的 PR 可以修改 blocked-apis.json,但绝不能暂存(stage)library/、registry.json、README 目录单元格或 cli-skills/。
受阻 API 日志模式
仅在调用参数包含 --blocked-api-journal 时使用此模式。该模式用于记录一次被挂起(held)的 /printing-press 尝试——其阻塞原因很可能会影响其他用户,直到机器或上游问题得到解决。
调用方必须提供的字段:
slug:规范的 API slug,而非 CLI 二进制文件名。attempted_at:格式为YYYY-MM-DD。verdict:固定为hold。reason:简明的阻塞原因,不得包含密钥、本地路径、Cookie、Token 或特定账户的敏感信息。blocking_issue:若已知 Printing Press 的 issue 编号则填写,否则填null。permanent:布尔值(true或false)。
如果调用方未提供上述某个字段,仅能从当前运行上下文中推断安全的值。若 reason 缺失或表述模糊,请暂停并要求提供一句具体的阻塞说明;切勿写入无意义的日志条目。
依次执行正常的 Setup、Configuration、作用域克隆清理和 GitHub 鉴权检查,然后像常规发布流程一样准备公共库克隆:必要时进行 Fork,确保 upstream 指向 mvanhorn/printing-press-library,拉取 upstream,并在编辑前将克隆重置为 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,PR Body 需包含以下内容:
- 被挂起的 API slug 和阻塞原因
- 该阻塞是永久性的,还是关联到了
blocking_issue - 预期的 Phase 0 行为:后续运行
/printing-press <api-slug>时会在重复尝试前发出警告
PR 创建成功后,输出其 URL 并终止流程。请勿继续执行常规的 CLI 打包、实测、注册表更新或 Skill 镜像等后续步骤。
初始化设置
在执行任何其他操作前:
<!-- PRESS_SETUP_CONTRACT_START -->
# min-binary-version: 4.0.0
# 首先推导作用域目录 — 用于检测本地构建
_scope_dir="$(git rev-parse --show-toplevel 2>/dev/null || echo "$PWD")"
_scope_dir="$(cd "$_scope_dir" && pwd -P)"
# 如果在 printing-press 仓库内部运行,优先使用本地构建。
_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
# 解析并输出 Agent 后续每次调用 `cli-printing-press` 时必须使用的绝对路径。
# 上文的 `export PATH` 仅对当前这一次 Bash 工具调用生效;
# 后续调用会打开新的 Shell 并根据用户的默认 PATH 解析 `cli-printing-press`,
# 此时旧的全局版本可能会静默覆盖本地构建。
# Agent 会捕获该标记并在后续每次调用中替换为绝对路径。
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
<!-- truncated for translation batch; full body continues in source -->




