mcpField
P
printing-press-amend

printing-press-amend

github.com/mvanhorn开发4.9k5.5k 次安装今天更新
代码仓库

关于 printing-press-amend

将 dogfood 会话中的使用障碍或用户明确提出的需求转为 PR,以修改公共库中的打印版 CLI。支持从会话记录提取问题、直接输入需求,或合并两种来源。

主要功能

  1. 01从会话记录提取使用障碍
  2. 02接收明确的 CLI 修改需求
  3. 03将发现分类为缺陷或功能
  4. 04按名称或路径指定 CLI
  5. 05为公共库 CLI 创建补丁

适用场景

  1. 01将会话中的错误转为 CLI 修复
  2. 02添加指定的命令或订阅源
  3. 03合并 dogfood 发现与直接需求

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/mvanhorn/cli-printing-press --skill printing-press-amend

安装后在 Claude Code 中输入/printing-press-amend确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
.gitkeep
direct-input-parsing.md
library-pr-plumbing.md
pii-scrubbing.md
transcript-parsing.md

详细介绍

/printing-press-amend

将一次内部试用会话转化为公共库中已发布 CLI 的 PR。

/printing-press-amend                 # auto-detect target CLI from session
/printing-press-amend superhuman      # explicit short name
/printing-press-amend superhuman-pp-cli
/printing-press-amend "$PRESS_LIBRARY/superhuman"

此技能位于此仓库(机器)中,用于处理公共库中已发布的 CLI。它与 /printing-press-publish(添加新的 CLI)、/printing-press-polish(发布前改进 CLI)以及 /printing-press-retro(反思机器本身)互为同级。这些技能都不涵盖由真实会话中的摩擦问题驱动的发布后 CLI 修改。

此技能产出的成果在语义上是一个“补丁”(在 git/PR 意义上),由公共库的 .printing-press-patches/ 目录跟踪(每个补丁一个文件)。当内联 // PATCH(...) 源代码注释有助于更轻松地在定制站点中搜索时,可将其用作可选导航辅助。斜杠技能名称为 amend,以区别于现有的 cli-printing-press patch 二进制子命令(该命令通过 AST 注入预定义功能——机制不同,意图也不同)。

设置

开始任何操作之前:

# 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.10 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() {
  if [ -z "${PP_SEMVER_A:-}" ] || [ -z "${PP_SEMVER_B:-}" ]; then
    echo "[setup-error] semver comparison inputs are missing." >&2
    return 2
  fi
  awk -v a="${PP_SEMVER_A:-}" -v b="${PP_SEMVER_B:-}" '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' "${PP_GO_VERSION_INPUT:-}" | sed -nE 's/.*go([0-9]+)\.([0-9]+)(\.([0-9]+))?.*/\1.\2.\4/p' | sed -E 's/\.$/.0/'
}

_pp_check_go_currency() {
  _pp_go_installed="$(PP_GO_VERSION_INPUT="$(go env GOVERSION 2>/dev/null)" _pp_go_version_norm)"
  _pp_go_required="$(PP_GO_VERSION_INPUT="$(go version "$PRINTING_PRESS_BIN" 2>/dev/null)" _pp_go_version_norm)"
  PP_SEMVER_A="$_pp_go_installed"
  PP_SEMVER_B="$_pp_go_required"
  if [ -z "$_pp_go_installed" ] || [ -z "$_pp_go_required" ] || ! _pp_semver_lt; 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 'BEGIN {
    if ((getline header) <= 0 || (getline record) <= 0) exit
    field_count = split(record, fields)
    if (field_count >= 4) print fields[4]
  }')"
  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."
    echo "PRESS_DISK_PATH=$_pp_disk_path"
    echo "PRESS_DISK_AVAIL_KB=$_pp_disk_avail_kb"
    echo "PRESS_DISK_FAIL_KB=$_pp_disk_fail_kb"
    echo "Free disk space or set PRINTING_PRESS_HOME to a volume with more room, then re-run this skill."
    echo ""
    return 1
  fi

  if [ "$_pp_disk_avail_kb" -lt "$_pp_disk_warn_kb" ]; then
    echo ""
    echo "[low-disk] Printing Press workspace volume is low on free space."
    echo "PRESS_DISK_PATH=$_pp_disk_path"
    echo "PRESS_DISK_AVAIL_KB=$_pp_disk_avail_kb"
    echo "PRESS_DISK_WARN_KB=$_pp_disk_warn_kb"
    echo "This flow may need several GiB for generated files, Go build cache, module downloads, or repository clones."
    echo ""
  fi
}
_pp_check_disk_space || { return 1 2>/dev/null || exit 1; }

mkdir -p "$PRESS_RUNSTATE" "$PRESS_LIBRARY" "$PRESS_MANUSCRIPTS" "$PRESS_CURRENT"

# --- Currency-floor check (standalone, fail-open) ---
# Hard-stop on binaries below the published supported floor so amend does not
# regenerate CLIs with since-fixed bugs. Repo checkouts build from source and
# are exempt. The floor is clamped to <= latest so a bad value cannot brick
# every install. Fetched fresh each run rather than reusing the printing-press
# preflight's TTL cache: amend is low-frequency, so the bounded curl + go-list
# cost is not worth its own cache here.
if [ "$_press_repo" != "true" ] && command -v curl >/dev/null 2>&1; then
  _semver_lt() {
    if [ -z "${PP_SEMVER_A:-}" ] || [ -z "${PP_SEMVER_B:-}" ]; then
      echo "[setup-error] semver comparison inputs are missing." >&2
      return 2
    fi
    awk -v a="${PP_SEMVER_A:-}" -v b="${PP_SEMVER_B:-}" '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
    }'
  }
  _floor_installed=$("$PRINTING_PRESS_BIN" version --json 2>/dev/null | sed -nE 's/.*"version"[[:space:]]*:[[:space:]]*"([^"]+)".*/\1/p')
  _floor_doc=$(curl -fsSL --max-time 5 \
    https://raw.githubusercontent.com/mvanhorn/cli-printing-press/main/supported-versions.txt 2>/dev/null || true)
  _floor_min=$(printf '%s\n' "$_floor_doc" | sed -nE 's/^min_supported=//p' | head -n 1)
  _floor_reason=$(printf '%s\n' "$_floor_doc" | sed -nE 's/^reason=//p' | head -n 1)
  _floor_latest=""
  if command -v go >/dev/null 2>&1; then
    _floor_latest=$(go list -m -json github.com/mvanhorn/cli-printing-press/v4@latest 2>/dev/null | sed -nE 's/^[[:space:]]*"Version":[[:space:]]*"v?([^"]+)".*/\1/p' | head -n 1)
  fi
  if [ -n "$_floor_min" ] && [ -n "$_floor_installed" ] && [ -n "$_floor_latest" ] &&
     PP_SEMVER_A="$_floor_installed" PP_SEMVER_B="$_floor_min" _semver_lt &&
     ! PP_SEMVER_A="$_floor_latest" PP_SEMVER_B="$_floor_min" _semver_lt; then
    echo ""
    echo "[upgrade-required] printing-press v$_floor_min is the minimum supported version (you have v$_floor_installed)"
    echo "PRESS_REQUIRED_MIN=$_floor_min"
    echo "PRESS_REQUIRED_INSTALLED=$_floor_installed"
    echo "PRESS_REQUIRED_REASON=$_floor_reason"
    echo ""
  fi
fi

运行设置约定后,从标准输出中捕获 PRINTING_PRESS_BIN=<abs-path> 行。此技能中后续每次调用 cli-printing-press ... 时,都必须使用该绝对路径(替换为对应值,而不是字面上的 $PRINTING_PRESS_BIN 标记)——上面的 export PATH 只影响它所运行的单次 Bash 工具调用,因此后续调用会启动新的 shell,此时裸命令 cli-printing-press 会根据用户的默认 PATH 解析,过时的全局版本可能会遮蔽本地构建版本。

如果设置输出了 [go-toolchain-old] 或 [low-disk],请向用户提示该警告并继续,除非设置同时输出了 [setup-error]。[go-toolchain-old] 表示后续 Go 命令可能会下载所需工具链,或在下载受阻时失败;[low-disk] 表示本次运行可能需要数 GiB 空间,用于生成文件、Go 构建缓存、模块下载或仓库克隆。

捕获二进制文件路径后,检查二进制版本兼容性。读取此技能 YAML 前置元数据中的 min-binary-version 字段。运行 <PRINTING_PRESS_BIN> version --json 并从输出中解析版本。按照 semver 规则将其与 min-binary-version 比较。如果已安装的二进制版本低于最低要求,请立即停止并告知用户:“cli-printing-press 二进制版本 vX.Y.Z 低于最低要求 vA.B.C。请运行 go install github.com/mvanhorn/cli-printing-press/v4/cmd/cli-printing-press@latest 进行更新。”

如果设置约定输出了 [upgrade-required] 块,则表示已安装的二进制版本低于已发布的版本下限(PRESS_REQUIRED_MIN)——较旧版本重新生成 CLI 时会带有已修复的问题(PRESS_REQUIRED_REASON)。这是一个不同于 min-binary-version 的硬性门槛:不得使用该二进制版本进行修改或重新生成。通过 AskUserQuestion 提供一键升级选项——是 — 立即升级(运行 go install github.com/mvanhorn/cli-printing-press/v4/cmd/cli-printing-press@latest,重新捕获 PRINTING_PRESS_BIN,然后继续)或 取消(停止本次运行)。不得跳过并继续;低于版本下限时,只能升级或中止。如果升级命令失败,请说明情况并停止。

阶段 0 — 输入模式检测

此技能接受两种用于后续补丁所用发现事项列表的输入来源:Claude Code 会话记录(内部试用模式,当前行为)和斜杠命令提示词中的用户自定义需求(直接输入模式,v0.2 中新增)。两种模式仅在阶段 1 存在差异;从阶段 2 开始与模式无关,使用形状完全相同的类型化发现事项列表。

在阶段 1 运行前确定模式。

检测标准

阅读对话上下文中的斜杠命令提示词正文和紧接着的调用轮次。分为以下四种情况:

  • MODE=direct — 提示词包含具体的 CLI 名称,且至少有一种直接输入信号:

    • 针对 CLI 的动作动词:rename、add、remove、fix、sniff、discover
    • 用户希望添加的明确 URL(例如:https://example.com/feed/x)
    • 列举的订阅源、命令、端点或功能
    • 类似“这些想法”“这些功能”“包含以下内容”的措辞
  • MODE=dogfood — 提示词为空,或提到某个 CLI 但没有任何要求(“修改 superhuman CLI”),或明确提及当前会话(“我刚才试用的内容”“本次会话中的阻碍”“根据我今天的会话”)

  • MODE=both — 提示词明确同时提及会话和具体要求(“我试用了本次会话,也想添加功能 X”“除了我遇到的阻碍,请再添加命令 Y”)

  • 有歧义 — 只出现一种信号(提到 CLI 但没有动词,或有动词但没有指定目标 CLI,或提出的要求可能是阻碍反馈,也可能是新增需求)。通过 AskUserQuestion 询问用户:

    "有两种方式可以为此次修改获取发现结果。哪种方式适合?

    1. 挖掘当前会话记录(dogfood 模式)
    2. 使用我刚才输入的要求(direct-input 模式)
    3. 两者都用——结合会话中的阻碍和我的要求"

如果完全没有斜杠命令提示词,则默认使用 MODE=dogfood。这样可保留标准体验——仅输入 /printing-press-amend 时,仍与 v0.1 中的行为完全一致。

持久化模式

将解析出的模式写入 $PRESS_RUNSTATE/current/mode.txt,供后续阶段(以及恢复运行时)读取:

echo "$MODE" > "$PRESS_RUNSTATE/current/mode.txt"
输出

阶段 0 向阶段 1 输出一行:

mode: <dogfood|direct|both>

阶段 1 根据此值进行分支:dogfood 发现结果通过 ### 1a 处理,direct-input 发现结果通过 ### 1b 处理,组合运行则依次执行这两个小节。从阶段 2 开始完全忽略该模式——发现结果列表就是约定。

阶段 1 — 捕获

此阶段会生成类型化的发现结果列表。所有模式下的列表结构相同:每条发现结果都包含 id、kind、category、classification(bug 或 feature)、evidence、target_cli、rationale 和 provenance(dogfood 对应 transcript,direct 对应 user-ask,sniff 衍生结果对应 sniff)。阶段 2 将原样使用该列表。

当 MODE=dogfood 时,仅运行 ### 1a。当 MODE=direct 时,仅运行 ### 1b。当 MODE=both 时,先运行 ### 1a,再运行 ### 1b,并合并两个发现结果列表,确保 ID 不重复(1b 从 1a 结束的编号继续)。

1a. Dogfood 模式(MODE=dogfood)

完整流程请阅读 references/transcript-parsing.md。本小节的工作概述:

  1. 解析当前活动会话的 transcript 文件 — 根据当前工作目录推导 <project-dir-slug>,按 mtime 列出 ~/.claude/projects/<slug>/*.jsonl,选择最近修改的文件。读取前,务必通过 AskUserQuestion 向用户确认解析出的路径——选错文件会导致从错误的会话中摄取摩擦信号。

  2. 遍历 transcript 并提取摩擦信号 — 非零退出码、错误消息、手写 API 请求负载(例如本应使用 CLI 命令,却直接通过 curl POST)、失败后重试模式、Agent 评论(如“X 不存在”/“X 返回 400”)、对缺少标志的提及、静默返回 null、身份验证方面的困惑。每个信号都包含时间戳、类别、原文证据以及它引用的 <slug>-pp-cli。

  3. 将每个信号分类为 bug 或 feature,并提供一句话理由。Bug = CLI 行为有误;feature = CLI 缺少相应行为。分类是 Agent 的最佳判断;用户会在 U4 范围检查点确认或修改。

  4. 自动检测目标 CLI — 统计信号中每个 <slug>-pp-cli 的出现次数,建议出现次数最多的 CLI 作为默认选项。通过 AskUserQuestion 确认(只有一个 CLI:简单的是/否确认;多个 CLI 次数接近:从列表中选择)。如果用户传入了明确的 <cli-name-or-path> 参数,则跳过自动检测。

  5. 解析目标路径并确定发布状态 — 接受短名称、全名或绝对路径(参见 R4)。将输入规范化为纯 CLI slug,然后通过在公共库中查找该 slug 来确定发布状态(本地有克隆时查找 ~/printing-press-library/library/*/<slug>,否则通过 gh api 查询相同路径)。不要根据本地工作副本的 git 远程仓库推断发布状态,也不要将缺少 $PRESS_LIBRARY/<slug> 工作副本视为未发布。如果在公共库中找到该 slug,则记录 target_category、published_status: published,并通过托管克隆的上游 PR 流程处理本次运行。只有当公共库中不存在该 slug 时,才使用 published_status: local-only。U7 的 PR 创建阶段需要此类别;在此处记录可避免重新推导。

1a 输出的每个发现都带有 provenance: transcript。输出将作为结构化发现列表流入 Phase 2,格式详见 references/transcript-parsing.md。

1b. 直接输入模式(MODE=direct)

完整流程请参阅 references/direct-input-parsing.md(v0.2 引入)。本小节的功能概要:

  1. 读取斜杠命令提示词正文,以及触发该技能的紧接着的 Agent 消息轮次——其中包含用户的原话请求(例如:“将 Digg 1000 重命名为 Digg,添加以下四个 Feed:……,探查新端点”)。没有对话记录可供确认;跳过 1a 中使用的 U1 对话记录路径模态框。

  2. 确定目标 CLI——遵循与 1a 步骤 4-5 相同的名称解析规则(依据 R4),但 CLI 通常已在提示词中命名。通过正则表达式提取(<slug>-pp-cli 或“the CLI”);如果没有,则询问用户。

  3. 使用 references/direct-input-parsing.md 中的规则,将请求解析为结构化发现项。每个请求对应一个带有类型化 kind 字段的发现项:

    • rename — “rename X to Y” / “call it X instead of Y” → classification: feature
    • add-command — “add command X” / “add subcommand X” → classification: feature
    • add-feed — “add feed ” / 用户希望添加的 URL 列表(每个 URL 对应一个发现项)→ classification: feature
    • add-endpoint — “add endpoint ” / 明确的 API 路径 → classification: feature
    • fix-bug — “fix X” / “X is broken” / “X returns null” → classification: bug
    • sniff — “sniff for new APIs” / “find new endpoints” / “discover more” → 转至 ### 1b.i 中的探查子程序
  4. 每个发现项都会在 evidence 中记录用户的原话,以便 U4 范围确认模态框向用户展示他们实际写下的内容。

  5. 边缘情况——涉及多个 CLI 的请求拆分为两次独立运行(超出 v0.2 的范围;请用户选择一个)。含义模糊的动词(例如未说明具体内容的 update X)会触发 AskUserQuestion 澄清,而不是进行猜测。

1b 生成的每个发现项都带有 provenance: user-ask(由探查子程序生成的发现项则为 provenance: sniff)。输出将传入第 2 阶段,采用与 1a 相同的结构化发现项列表格式。

1b.i. 嗅探子程序

当解析规则将任何 1b 请求标记为 kind: sniff 时触发(例如“嗅探新 API”“查找新端点”“在 中发现更多端点”等表述)。嗅探需要在每次运行中选择启用——除非用户明确提出,否则绝不调用。如果没有嗅探发现请求,则完全跳过此子程序。

步骤 1 — 确定目标来源 URL。 读取目标 CLI 已发布的清单 ~/printing-press-library/library/<category>/<slug>/.printing-press.json,并提取 source_url(若无,则使用 spec_url)。类别已在 1b 步骤 2 中确定。

如果这两个字段都未设置,请直接询问用户:

“嗅探需要目标 URL——请粘贴要嗅探的来源网站,或者跳过本次运行的嗅探请求?”

如果用户跳过,则从活动列表中移除该嗅探请求,并继续处理其他 1b 发现项。如果用户粘贴了 URL,则在步骤 2–3 中使用该 URL。

步骤 2 — 先运行 crowd-sniff(快速,无需浏览器)。 将 <PRINTING_PRESS_BIN> 替换为设置时记录的绝对路径:

<PRINTING_PRESS_BIN> crowd-sniff --site "$SOURCE_URL" --json > /tmp/amend-sniff-crowd.json
crowd_exit=$?

crowd-sniff 会查询 npm SDK 和 GitHub 代码搜索,以发现候选端点——无需浏览器。通常运行时间不到一分钟。

步骤 3 — 可选的 browser-sniff(仅当用户选择深入嗅探时)。 当用户的请求明确提到基于浏览器的发现(“使用浏览器嗅探”“进行深度嗅探”),且已有捕获的 HAR 时,运行:

<PRINTING_PRESS_BIN> browser-sniff --har "$HAR_PATH" --json > /tmp/amend-sniff-browser.json
browser_exit=$?

此技能在 v0.2 中不会协调 HAR 捕获——捕获由用户驱动(用户在 Chrome 中打开来源网站、导出 HAR,并将其提供给技能),否则会附注说明并跳过深度嗅探。v0.3 可能会扩展此技能,通过 claude-in-chrome MCP 驱动捕获;这不在 v0.2 的范围内。

步骤 4 — 将发现结果转换为发现项。 对于嗅探输出中的每个候选端点,将一个发现项追加到 1b 发现项列表中:

  • id: F<n>(解析出的请求之后的下一个可用编号)
  • kind: add-endpoint
  • classification: feature
  • evidence: "通过 crowd-sniff 发现:<endpoint-path>"(适用时使用 browser-sniff)
  • target_cli: <slug>-pp-cli
  • rationale: <嗅探输出中的单行摘要(若有),否则为 "嗅探候选项,待用户确认">
  • provenance: sniff

默认在第 3 阶段将这些项目划为第 3 层级(优化/架构)——用户审核后,可将个别条目提升至第 2 层级(如果优先级较高)。

步骤 5 — 降级处理路径。

条件行为
.printing-press.json 缺少 source_url,且用户在被询问时选择跳过移除嗅探请求;继续处理其他 1b 发现项;记录“已跳过嗅探——没有来源 URL”。
crowd-sniff 以非零状态退出记录错误;跳过嗅探发现项;继续处理其他 1b 发现项。不要中止修改运行。
crowd-sniff 返回零个候选端点在延后发现项列表中添加一条记录(“已运行嗅探,未发现新端点”),而不是不添加任何内容——为用户保留记录。
用户请求 Browser-sniff,但没有可用的 HAR记录日志;仅使用 crowd-sniff 结果。

步骤 6 — 向用户显示来源信息。 在第 3 阶段的范围确认弹窗中,来源于嗅探的发现项会按 (sniff) 来源标签进行视觉分组,方便用户决定是否整组保留,例如:

Tier 3 — Polish / architecture (5)
  F8  add-endpoint /v1/feeds/stars (sniff)
  F9  add-endpoint /v1/feeds/new (sniff)
  F10 add-endpoint /v1/feeds/activity (sniff)
  ...

第 2 阶段 — 检查点前防护

在用户看到范围菜单之前,会运行两项防护。任一项都可能抑制发现项或中止运行。

2a. PR 交叉引用(抑制重复提案)

对于 Phase 1 中的每项发现,请在 mvanhorn/printing-press-library 中搜索未关闭和近期已合并的 PR,查找匹配项。重复检测标准(按优先级排序):(1)目标 CLI 的目录路径与 PR 的已更改文件列表有重叠;(2)发现类别和理由中的关键词与 PR 标题或正文匹配。

# Replace <PRINTING_PRESS_BIN> use with the absolute path captured at setup.
# This phase uses gh, not the press binary.

# Open PRs touching this CLI
gh pr list --repo mvanhorn/printing-press-library \
  --search "in:title,body <slug>" --state open --limit 20 \
  --json number,title,state,headRefName,files

# Recently merged PRs (last 90 days) touching this CLI.
# Compute "90 days ago" portably — `date -v-90d` is BSD/macOS only, `date -d`
# is GNU/Linux only. Try GNU first, fall back to BSD, then to python3. If
# every form fails, abort with an explicit error rather than letting the
# dedup guard silently drop out with an empty `merged:>` qualifier.
ninety_days_ago=$(date -u -d '90 days ago' +%Y-%m-%d 2>/dev/null \
  || date -u -v-90d +%Y-%m-%d 2>/dev/null \
  || python3 -c 'import datetime; print((datetime.datetime.now(datetime.UTC).date() - datetime.timedelta(days=90)).isoformat())' 2>/dev/null)
if [ -z "$ninety_days_ago" ]; then
  echo "ERROR: cannot compute 90-days-ago date — no GNU date, BSD date, or python3 available."
  exit 1
fi

gh pr list --repo mvanhorn/printing-press-library \
  --search "in:title,body <slug> merged:>$ninety_days_ago" \
  --state merged --limit 20 \
  --json number,title,state,mergedAt,headRefName,files

对于存在可能重复匹配项的每项发现,内联显示:

“发现 F<n>(<category>)可能已由 PR # — <title>(,)解决。要跳过这项发现吗?”

用户选项:跳过(移至 deferred)、保留,或“查看 PR #”(打开 gh pr view <num> --repo mvanhorn/printing-press-library --web)。对于明确已合并的匹配项,默认选择“跳过”;对于未关闭的 PR,默认选择“保留”(用户可能想将内容添加到正在处理的 PR,而不是新开一个 PR)。

此检查旨在避免 2026-05-15 dogfood 中出现的典型失败情况:某个 Printing Press CLI 已有类似 PR 在几小时前于另一个 CLI 上合并,却仍提议添加自动刷新功能。误跳过的代价较低(用户可在 U4 通过自定义选择重新添加);漏掉重复项的代价则是 PR 被拒绝并耗费审核者时间。

2b. 过期二进制检查(如果 dogfood 使用的二进制版本落后于已发布版本,则中止)

读取公共库中目标 CLI 的 .printing-press.json,找到已发布版本。将其与本地打印的 CLI 二进制报告的版本进行比较。

# Read published version (managed clone if available, else gh api)
if [ -f "$HOME/printing-press-library/library/<category>/<slug>/.printing-press.json" ]; then
  published=$(jq -r '.version // empty' "$HOME/printing-press-library/library/<category>/<slug>/.printing-press.json")
else
  published=$(gh api repos/mvanhorn/printing-press-library/contents/library/<category>/<slug>/.printing-press.json \
    --jq '.content' | base64 -d | jq -r '.version // empty')
fi

# Read local binary version (if installed; the user dogfooded with this binary)
local_ver=$(<slug>-pp-cli version --json 2>/dev/null | jq -r '.version // empty' || echo "")

如果 local_ver 早于 published(按 semver 比较),则正常中止:

“你用于 dogfood 的 <slug>-pp-cli 二进制版本为 v<local_ver>,但库中已发布的版本为 v<published>。你遇到的问题可能已在已发布版本中修复。请运行:

go install github.com/mvanhorn/<slug>-pp-cli@latest

……然后重新进行 dogfood,再运行 /printing-press-amend。本次运行已中止。”

边缘情况:如果 .printing-press.json 缺失或没有 version 字段,则跳过过期检查并附上说明。如果 CLI 仅在本地使用(尚未发布),则跳过检查。

输出

Phase 2 将(可能经过精简的)发现列表传递给 Phase 3:

findings_kept:
  - <finding from Phase 1>
findings_suppressed:
  - id: F3
    reason: "Duplicate of PR #571 (merged 2026-05-13)"
target_binary_check: { local: "1.0.0", published: "1.0.0", status: "current" }
published_status: published

Phase 3 — 范围确认检查点(用户参与环节 #1)

这是两个用户检查点中的第一个。到目前为止,所有操作都是只读探索;此检查点将确定范围。

为剩余发现划分层级

将发现分为三个层级:

  • Tier 1 — Bug — 所有 classification: bug 的发现。CLI 行为有误;修复可恢复正确性。
  • Tier 2 — 解决当前会话痛点的缺失功能 — 与用户在会话期间实际编写的临时变通方案相关的 classification: feature 发现(即用户现在明确需要该功能,而非仅仅认为它在理论上有用)。
  • Tier 3 — 润色 / 架构 — 其余 classification: feature 发现;这些属于可选改进或架构优化,且会话期间没有对应的临时变通方案。

在提问前内联显示分层后的列表:

Friction found for <slug>-pp-cli (12 signals, 2 suppressed as duplicates):

Tier 1 — Bugs (4)
  F1  drafts list returns 400 silently
  F4  messages query returns data: null
  F7  refresh-token expiry not surfaced in errors
  F11 ai --query returns code 500

Tier 2 — Missing features that solve session pain (4)
  F2  no `drafts new` command (user hand-rolled writeMessage payload)
  F5  no `--type sent` for threads list (user worked around with messages query)
  F8  no `--remind-in <duration>` flag for send (user manually re-flagged drafts)
  F10 no `bootstrap` to local SQLite (user did 50+ thread API calls)

Tier 3 — Polish / architecture (2)
  F12 `auth status` doesn't link to `auth login` when refresh expired
  F13 doctor doesn't surface stale-binary warning vs. published version
通过 AskUserQuestion 选择范围
Which scope should this patch cover?
  1. Bugs only (Tier 1) — 4 findings
  2. Bugs + immediate features (Tier 1 + Tier 2) — 8 findings
  3. All tiers (Tier 1 + Tier 2 + Tier 3) — 10 findings
  4. Custom selection — pick individual findings

AskUserQuestion 选项必须自包含(每个标签都必须能独立说明其作用,不依赖描述文本——有些 harness 会隐藏描述)。

对于自定义选择路径:显示一个多选列表,每项包含发现的 id、类别和一行理由;继续之前,先确认用户勾选的子集。

持久化未纳入范围的发现

对于每项未纳入已确认范围的发现,将其追加到以下路径的延后列表 Markdown 文件中:

$PRESS_MANUSCRIPTS/<api-slug>/<run-id>/proofs/<timestamp>-amend-<cli-name>-deferred.md

<run-id> 是本次修订运行的全新时间戳 ID(例如 amend-2026-05-15T1432)。将延后文件格式设为 YAML 前置元数据 + 按发现分节的 Markdown 正文,以便未来在同一 CLI 上运行 /printing-press-amend 时重新展示这些条目。

---
date: 2026-05-15
target_cli: superhuman-pp-cli
amend_run_id: amend-2026-05-15T1432
deferred_count: 2
---

然后,为每项延后的发现建立一个分节,包含:id、category、classification、rationale、evidence、reason-deferred(例如「用户仅选择了 Tier 1」)以及 still_relevant: unknown。

后续运行 /printing-press-amend 时,Phase 3 应在 $PRESS_MANUSCRIPTS/<api-slug>/ 中查找最新的 *-deferred.md,并询问用户是否要将仍与本次运行范围相关的条目纳入本次运行。(实现说明:此重新展示逻辑将在 v0.1 中发布;不得静默地重新加入——务必先展示并确认。)

边界情况:无事可做

如果 Phase 2 抑制了所有发现(全部为重复项),Phase 3 应清楚地报告结果并退出,不打开菜单:

“本次会话中的所有发现均已由现有 PR 处理。未发现新的补丁。”

输出

Phase 3 向 Phase 4 输出:

published_status: published
scope_tier: bugs+features            # or bugs|all|custom
findings_active: [...]               # the user-confirmed subset
findings_deferred_path: <path>       # where the deferred file landed

Phase 4 — 规划 + 执行 + 验证(自主运行)

此阶段在检查点 1 和 2 之间无人值守运行。用户不会看到逐项修复的详情;他们将在 Phase 6 的 PR 草稿检查点审阅最终差异。

步骤 1 — 设置受管克隆

根据计划中的实施前决策:此技能直接操作 mvanhorn/printing-press-library 的受管克隆,而不是 $PRESS_LIBRARY/<slug>/。受管克隆位于:

$PRESS_HOME/.publish-repo-$PRESS_SCOPE

这是 /printing-press-publish 使用的同一克隆(该技能的步骤 5)。复用它:

PUBLISH_REPO_DIR="$PRESS_HOME/.publish-repo-$PRESS_SCOPE"
PUBLISH_CONFIG="$PRESS_HOME/.publish-config-$PRESS_SCOPE.json"

if [ ! -d "$PUBLISH_REPO_DIR/.git" ]; then
  # First-time setup: see references/library-pr-plumbing.md for the full
  # detection (push-vs-fork access via gh api .../permissions.push,
  # SSH-vs-HTTPS protocol detection, scoped-clone cleanup loop).
  echo "Managed clone not present — bootstrapping..."
  # ... (see library-pr-plumbing.md)
else
  # Refresh from upstream. -f on checkout discards any local edits left behind
  # by a prior run that aborted between Phase 4's edits and Phase 7's commit —
  # without -f, those uncommitted changes block the checkout and the subsequent
  # reset --hard never runs.
  cd "$PUBLISH_REPO_DIR"
  git fetch upstream main
  git checkout -f main
  git reset --hard upstream/main
fi

CLI 在受管克隆中的目录为 $PUBLISH_REPO_DIR/library/<category>/<slug>/。类别已在 Phase 1 中确定(也可通过 find "$PUBLISH_REPO_DIR/library" -maxdepth 2 -name "<slug>" -type d 查找)。

CLI_DIR="$PUBLISH_REPO_DIR/library/<category>/<slug>"

此阶段的所有编辑都在 $CLI_DIR 内进行。切勿触碰 $PRESS_LIBRARY/<slug>/——那是另一个工作副本,编辑它不会反映到 PR 中。

步骤 2 — 编写本次运行的计划文档

在编辑代码之前,将计划写入以下路径的 Markdown 文件:

$PRESS_MANUSCRIPTS/<slug>/<run-id>/proofs/<timestamp>-amend-<cli-name>.md

同时复制到 /tmp/printing-press/amend/,以便快速查阅。计划文档包含:

  • Frontmatter:date、target_cli、amend_run_id、scope_tier、findings_count
  • 每项活跃发现一个分节:id、category、classification、rationale、目标文件($CLI_DIR/... 路径)、预期行为变更,以及该发现的测试场景
  • 风险和发现之间的依赖关系(如有)

计划文档应体现决策结构,而非执行结构——实施时的排序将在步骤 3 中确定。

步骤 3 — 执行计划(遵循补丁约定)

按依赖顺序处理每项发现:

  1. 编辑 $CLI_DIR/ 下的目标文件。遵守 AGENTS.md 中禁止重新实现的规则(不得手写响应构造器;新命令必须调用真实端点,或通过 // pp:client-call / // pp:novel-static-reference 例外标记从本地存储读取,但仅限确有正当理由的情况)。

  2. 可选:在修改位置添加 // PATCH(<short reason>) 源代码注释,便于后续 Agent 快速找到自定义内容。格式示例:

    // PATCH(amend-2026-05-15: surface refresh-token expiry to user) — was silently retrying
    func (c *Client) Refresh(ctx context.Context) error {
        ...
    }
    
  3. 创建一个补丁文件 $CLI_DIR/.printing-press-patches/<id>.json(文件名 = 补丁 id)。每个文件都是一个完整独立的补丁对象,每个补丁单独一个文件,这样并发的 amend PR 不会在补丁元数据上发生冲突:

    {
      "schema_version": 2,
      "id": "<api-slug>-refresh-token-expiry",
      "applied_at": "<YYYY-MM-DD>",
      "base_run_id": "<copy from .printing-press.json>",
      "base_printing_press_version": "<copy from .printing-press.json>",
      "summary": "fix(superhuman): surface refresh-token expiry; add drafts new + --type sent",
      "reason": "The generated CLI hid an expired refresh token and omitted a workflow flag needed by the live API.",
      "files": [
        "internal/auth/refresh.go",
        "internal/cli/drafts.go",
        "internal/cli/threads.go"
      ],
      "call_sites": ["printRefreshTokenExpiry("],
      "validated_outcome": "publish validate passed; focused drafts and refresh-token checks pass",
      "findings_addressed": ["F1", "F2", "F5", "F7"]
    }
    

    如果 CLI 仍包含旧版的单数组 .printing-press-patches.json(来自尚未规范化的旧版印刷构建),仍要将条目写入新的 .printing-press-patches/<id>.json 文件;公共库的 normalize-patches 工作流会在合并后合并这两者。不要追加到旧数组中。

    如果添加了 // PATCH(...) 注释,也可以包含 patch_count 字段,方便审阅者查看。未添加源代码注释时,不要添加 patch_count。

    对于具有未来替代路径的临时补丁,请在同一补丁条目中包含上游交接字段:

    {
      "deferred_to_upstream": [
        {
          "feature": "Generator or upstream API capability this printed-CLI patch should eventually supersede",
          "reason": "Why the local patch is intentionally temporary or API-specific."
        }
      ],
      "upstream_issue": "https://github.com/mvanhorn/cli-printing-press/issues/<n>"
    }
    

    代码级自定义必须创建 .printing-press-patches/<id>.json 补丁文件。行内 // PATCH(...) 源代码注释是可选的导航辅助;公共库验证器不再强制要求标记与注释成对出现。如果自定义内容中起关键作用的调用可能消失,但文件仍然存在,请在 call_sites 或 markers(或 marker)中声明该调用,并确保这些检索标记位于 files[] 中,以便其消失时 regen 和 publish validate 能够 fail closed。每个检索标记都必须有对应的 files[];这些文件之外残留的匹配项不计入。

    仅当补丁有意保留未来替代路径时,才使用 deferred_to_upstream:目前缺少公共 API 端点;命令依赖非官方主机或备用身份验证来源;实时响应结构与生成器假设不符;或者 Printing Press 学会该模式后便不再需要此修复。在这些情况下,先搜索 mvanhorn/cli-printing-press 的 issues;复用匹配的 issue,或在提交库 PR 前新建一个 issue,然后将该 URL 设为 upstream_issue。不要只在 PR 正文中说明对机器层面或 API 发布流程的依赖。

  4. 机器层面与印刷版 CLI 的判断(依据 AGENTS.md):如果某项发现的修复适用于所有印刷版 CLI(例如“生成器应为任何 threads list 命令生成 --type sent”),请将其作为边界情况提出:

    “发现 F5(缺少 --type sent)看起来属于机器层面修复——生成器模板 internal/generator/templates/threads.go.tmpl 应为每个具有此端点结构的 CLI 生成该参数,而不只是 <slug>-pp-cli。是延后到 /printing-press-retro 跟进处理,还是仅针对 CLI 修复?”

    若决定延后,将其归入延后列表并标记为 machine-level。若因印刷版 CLI 现在需要局部修复而决定保留,并且补丁仍保留未来替代路径,则须在开启库 PR 前创建或复用上游 Printing Press issue,将 issue URL 添加到补丁的 .printing-press-patches/<id>.json 中,并添加一项 deferred_to_upstream,说明应取代本地补丁的机器层面或上游 API 条件。

步骤 4 — 验证

所有编辑完成后,运行整合验证器(将 <PRINTING_PRESS_BIN> 替换为设置时记录的绝对路径):

<PRINTING_PRESS_BIN> publish validate --dir "$CLI_DIR" --json > /tmp/amend-validate.json
exit_code=$?

publish validate 会运行 manifest、phase5、govulncheck(限定为此 CLI 的模块)、go vet、go build、--help 和 --version。退出码为 0 表示验证通过。

步骤 5 — 失败时重试(最多 3 轮)

如果 publish validate 报告失败,请从 JSON 中解析错误类别,尝试有针对性的修复,然后重新运行 validate。总共最多重试 3 轮。第 3 轮后:

# Save the in-progress plan + diff to a holding location
HELD_PATH="$PRESS_MANUSCRIPTS/<slug>/<run-id>/proofs/<timestamp>-amend-<cli-name>-INCOMPLETE.md"
git -C "$PUBLISH_REPO_DIR" diff > "${HELD_PATH%.md}.diff"
cp "$PLAN_PATH" "$HELD_PATH"

将最终错误日志展示给用户,不要自动打开 PR,然后退出。用户可以通过再次调用此技能来继续(Phase 1 会检测到暂存的计划,并提供继续选项)。

步骤 6 — 检查补丁清单

此 amend 运行必须至少记录一个补丁——.printing-press-patches/ 下的一个 <id>.json(目录布局);或者,仅当 CLI 尚未规范化时,旧版 .printing-press-patches.json 中的 patches[] 非空。

dir_count=0
if [ -d "$CLI_DIR/.printing-press-patches" ]; then
  dir_count=$(find "$CLI_DIR/.printing-press-patches" -maxdepth 1 -name '*.json' ! -name '_meta.json' | wc -l | tr -d ' ')
fi
legacy_count=0
if [ -f "$CLI_DIR/.printing-press-patches.json" ]; then
  legacy_count=$(jq '(.patches // []) | length' "$CLI_DIR/.printing-press-patches.json")
fi
if [ "$dir_count" -eq 0 ] && [ "$legacy_count" -eq 0 ]; then
  echo "ERROR: this amend run must record at least one patch under .printing-press-patches/ (or the legacy .printing-press-patches.json)."
  exit 1
fi

补丁清单缺失或为空 → 请先在本地修复,再继续。

步骤 6 — 文档更新检查点

在 Phase 4 输出通过结果之前,检查已确认的发现和最终 diff,查看所有新增、重命名或更改的面向用户的命令、标志、工作流、运行模式或 MCP 可见操作。每项此类更改都必须在同一个公共库 PR 中附带文档:

  • 在 SKILL.md 和 README.md 中新增或更新 cookbook 配方。
  • 新增或更新对应的 Unique Features / Unique Capabilities 条目,使生成的文档和面向 Agent 的摘要都能说明更改后的工作流。
  • 确保示例准确:命令、标志、位置参数和输出片段必须与 $CLI_DIR 中现有的代码一致。
  • 如果存在该路径,请从公共库检出目录运行 python3 .github/scripts/verify-skill/verify_skill.py --dir "$CLI_DIR";否则运行检出目录中可用的打包版验证器等效命令,并记录该命令。

如果缺少任何必需的文档修改或验证器结果,请停止并提供阻塞检查清单。该清单需列出相关命令以及缺失的 README/SKILL/Unique Features 内容。清单清空前,不要进入 PR 草稿检查点。如果 amend 仅修复内部问题,没有面向用户的行为变更,请在 Phase 4 输出中记录 documentation_checkpoint: no-user-facing-doc-change。

输出

Phase 4 向 Phase 5 输出:

plan_doc_path: <path>
managed_clone_dir: <path>
cli_dir_in_clone: <path>
findings_addressed: [...]
build_status: PASS|FAIL
test_status: PASS|FAIL
dogfood_status: PASS|FAIL|N/A    # PASS|FAIL when MODE=dogfood (or "both"); always N/A when MODE=direct
validate_iterations: <n>
patch_entry_count: <n>
documentation_checkpoint: PASS|no-user-facing-doc-change

每种模式的 dogfood_status。 当 MODE=dogfood 时,该值反映使用基于转录内容的发现进行的 dogfood 验证步骤的结果(如果运行产生了干净的修复,则为 PASS;如果发现了回归,则为 FAIL)。当 MODE=direct 时,没有可用于 dogfood 验证的转录内容——设置 dogfood_status=N/A。当 MODE=both 时,仍需针对发现中基于转录内容的部分运行 dogfood 验证;相应地设置 PASS/FAIL。最迟必须在 Phase 4 结束时设置此默认值,以确保 Phase 7 的 PR 正文和 Phase 8 的 RESULT 区块不会输出空值。

阶段 5 — PII 清理

阅读 references/pii-scrubbing.md,了解完整流程。概要如下:

清理分为三个层次,每层都在临时暂存副本上操作(不会处理用户的会话记录或正在编写的源代码):

  1. 凭证 — 复用 skills/printing-press-retro/references/secret-scrubbing.md 中的正则表达式模式(Stripe、GitHub PAT、Bearer 令牌、AWS 密钥等),并针对从会话记录中引用的手工编写 API 负载中的 Authorization/Cookie/X-API-Key 标头,添加 amend 专用规则。
  2. 实体 — 将公司、人员和电子邮件与用户维护的停用列表 ~/.printing-press/amend-config.yaml 匹配。替换为保留形态的标记(<company-1>、<person-1>、<email-1>),并在所有产物中保持标识一致,以便审阅者仍能理解意图。
  3. 首次提及防护 — 检查每个产物,找出看起来像专有名词、首字母大写且未列入停用列表的词组。在阶段 6 显示 PR 草稿前,将其直接展示给用户:“发现 Esper Labs(计划文档中出现 3 次,PR 正文中出现 1 次)——要添加到停用列表并清理,还是接受?”

目标按优先级排序:PR 标题/正文草稿、单次运行计划文档、延后处理的问题列表,以及新添加到 $CLI_DIR 的任何测试固件或示例输出。清理每个目标前,先复制为 <path>.pre-pii-scrub,以便用户审查更改内容。

纵深防御:检查 $CLI_DIR 中的每个 *.go 文件是否包含停用列表中的内容。若发现任何匹配项,则视为阻塞问题——暂停并要求用户在进入阶段 6 前解决。Agent 不应在 Go 源代码中引入 PII;此检查用于捕获 Agent 的错误。

创建停用列表:如果 ~/.printing-press/amend-config.yaml 不存在,此技能会创建默认文件,其中包含初始列表和格式说明注释。验证文件模式(对所有者以外用户可写时发出警告;文件由非当前用户拥有时中止)。

清理报告写入 $PRESS_MANUSCRIPTS/<slug>/<run-id>/scrub-report.json(不提交;供用户审查)。阶段结束时向用户显示的摘要:“已在 Y 个产物中替换 X 个标记。”

阶段 6 — PR 草稿审查检查点(用户参与点 #2)

这是第二个也是最后一个用户检查点。之后的所有操作均在无人值守的情况下进行(推送 + 打开 PR + 添加标签 + RESULT 块)。在执行任何 gh 命令前,向用户展示即将发布的所有内容。

在此次审查中加入阶段 4 的文档检查结果,确保未记录的面向用户的更改不会混入 PR。

组装草稿

在内存中撰写 PR 标题、正文、标签和差异摘要。标题格式遵循公共库约定:

  • 当范围仅包含错误修复时,使用 fix(<api-slug>): <one-line summary>
  • 当范围包含功能时,使用 feat(<api-slug>): <one-line summary>
  • 当范围混合时(功能的契约变更更大),使用 feat(<api-slug>): <one-line summary>

<one-line summary> 由最重要的 1–3 项发现组成(例如:surface refresh-token expiry; add drafts new + --type sent)。

PR 正文部分(依据源 R27):

  1. 摘要 — 用 1–3 句话说明用户痛点和修复方案
  2. 发现 — 包含 ID、类别、类型(错误/功能)、理由的表格
  3. 变更 — git diff --stat upstream/main..HEAD 的输出
  4. 验证 — 第 4 阶段的构建/测试/dogfood/验证状态,以及文档检查点结果
  5. 证据 — 指向本次运行计划文档和 PR 的 HEAD SHA 对应的 .printing-press-patches/ 目录的完整 GitHub URL(在推送后获取,以免链接返回 404)
  6. 如果在 library-pr-plumbing.md 的第 6 步找到匹配的问题,添加 Closes #N 页脚

标签:始终建议使用 comp:<api-slug>;仅错误修复时使用 priority:P1,错误修复加功能时使用 priority:P2,覆盖所有层级时使用 priority:P3。将标签应用到上游库 PR 之前,先用 gh label list --repo mvanhorn/printing-press-library --json name --jq '.[].name' 查询目标仓库,并仅应用仓库中存在的标签。如果缺少建议的标签,则跳过,并在 PR 正文或最终运行日志中注明;不要让缺少单 CLI 或优先级标签导致 amend 流程失败。

gh 执行前先展示

向用户展示标题、正文、标签列表和 git diff --stat。如果第 5 阶段发现了用户确认属于合法内容、但未识别的首字母大写短语,现在要在对应句子中重新展示这些短语:

“提醒:PR 正文中提到了 <phrase>(你在第 5 阶段已确认其合法)。请在创建 PR 前确认。”

AskUserQuestion:打开 / 编辑 / 暂缓 / 中止
PR draft ready. What now?
  1. Open PR as drafted (recommended)
  2. Edit then open — drop into an interactive review of title/body
  3. Hold — save plan + diff for later resume; nothing pushed
  4. Abort — discard everything, no record kept

对于编辑后打开:将标题和正文分别作为可编辑区块呈现,接受用户修改,重新展示完整草稿,并在继续前确认。

对于暂缓:将计划和差异保存到 $PRESS_MANUSCRIPTS/<slug>/<run-id>/proofs/<timestamp>-amend-<cli-name>-HELD.md 和 ${path%.md}.diff。输出一个 RESULT 区块,包含 status: held 和恢复路径。之后运行 /printing-press-amend 时,可以检测到暂缓文件并询问是否恢复。

对于中止:输出简短确认。U5 中的计划文档仍会保留(在 frontmatter 中写入 status: aborted),以便用户留存发现记录,但不会保留其他内容。托管克隆会在下次运行时重置。

第 7 阶段 — 创建 PR(自主执行)

如果用户选择打开或编辑后打开,则运行 references/library-pr-plumbing.md 的第 5–7 步:

  1. 第 5 步 — git add "$CLI_DIR" + 使用约定式提交信息和发现列表进行提交
  2. 第 6 步 — 搜索与这些发现匹配的现有问题;关联该问题或新建问题;尽力自行分配
  3. 第 7 步 — 推送分支(推送还是 fork 访问模式由第 1 步决定),使用 gh pr create 和 --body-file 创建 PR,捕获 HEAD_SHA,应用标签

fork/访问权限检测、分支冲突处理和托管克隆刷新模式均已在 references/library-pr-plumbing.md 中详细说明。不要在此处重复这些模式——该参考文档是权威来源。

PR 创建后,在面向用户的摘要中展示 URL 和 Greptile 提示:

“PR 已创建:

Greptile 将在约 2 分钟内进行审查。查看行内评论:

gh api repos/mvanhorn/printing-press-library/pulls/<N>/comments

在请求人工审查前,建议先处理 P0/P1 级发现。”

第 8 阶段 — 输出

完成后输出结构化的 ---PATCH-RESULT--- 区块。格式:

---PATCH-RESULT---
pr_url: <url>
pr_number: <n>
branch_name: <name>
api_slug: <slug>
scope_tier: <bugs|bugs+features|all|custom>
files_changed:
- <file>
build_status: <PASS|FAIL>
test_status: <PASS|FAIL>
dogfood_status: <PASS|FAIL|N/A>
pii_scrub_summary: <N tokens replaced across M artifacts>
findings_addressed:
- <one-line-summary>
findings_deferred:
- <one-line-summary>
deferred_list_path: <path>
plan_doc_path: <path>
---END-PATCH-RESULT---

对此技能本身的验证

此 SKILL.md 的静态 lint 检查通过以下命令运行:

<PRINTING_PRESS_BIN> verify-internal-skill --dir skills/printing-press-amend

(请参阅 internal/cli/verify_internal_skill.go 和对应的测试文件。设置契约一致性检查作为 Go 测试在 internal/pipeline/contracts_test.go 中运行,即 TestSkillSetupBlocksMatchWorkspaceContract。)

评论

支持 Markdown