mcpField
P
printing-press-amend

printing-press-amend

github.com/mvanhorn開発4.9k5.5k 回インストール今日更新
リポジトリ

printing-press-amend について

dogfood セッションで見つかった不便さや明示的な要望をもとに、公開ライブラリ内の印刷済み CLI を修正する PR を作成する Skill です。セッション記録からの抽出、直接入力、または両方の併用に対応します。

主な機能

  1. 01セッション記録から不便さを抽出
  2. 02明示的な CLI 変更要望を受付
  3. 03指摘をバグまたは機能に分類
  4. 04名前またはパスで CLI を指定
  5. 05公開ライブラリの CLI 用パッチを作成

活用シーン

  1. 01セッション中のエラーを CLI 修正につなげる
  2. 02指定されたコマンドやフィードを追加
  3. 03dogfood の指摘と直接要望を統合

インストール

コマンドをコピーしてターミナルで実行します。

$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/ ディレクトリで追跡されます(パッチごとにファイルを 1 つ作成)。カスタマイズされたサイトを grep しやすくする場合、ソースコメント // 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 ツール呼び出しの間だけです。そのため、後続の呼び出しでは新しいシェルが起動し、cli-printing-press だけを指定するとユーザーのデフォルト PATH に従って解決され、古いグローバル版がローカルビルドより優先される可能性があります。

セットアップで [go-toolchain-old] または [low-disk] が出力された場合は、その注意事項をユーザーに伝え、[setup-error] も出力されていない限り続行します。[go-toolchain-old] は、後続の Go コマンドで必要なツールチェーンがダウンロードされる可能性、またはダウンロードがブロックされると失敗する可能性があることを意味します。[low-disk] は、この実行で生成ファイル、Go のビルドキャッシュ、モジュールのダウンロード、リポジトリのクローンに数 GiB が必要になる可能性があることを意味します。

バイナリのパスを取得したら、バイナリのバージョン互換性を確認します。このスキルの YAML フロントマターから min-binary-version フィールドを読み取ります。<PRINTING_PRESS_BIN> version --json を実行し、出力からバージョンを解析します。semver の規則に従って min-binary-version と比較します。インストール済みのバイナリが最小バージョンより古い場合は、直ちに停止し、ユーザーに次のように伝えます:「cli-printing-press binary vX.Y.Z is older than the minimum required vA.B.C. Run go install github.com/mvanhorn/cli-printing-press/v4/cmd/cli-printing-press@latest to update.」

セットアップの契約で [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 — 入力モードの判定

このスキルが後でパッチを適用する指摘リストの入力元は 2 つです。Claude Code のセッショントランスクリプト(ドッグフードモード、現行の動作)と、スラッシュコマンドのプロンプト内でユーザーが指定する要望(直接入力モード、v0.2 で追加)です。2 つのモードで異なるのはフェーズ 1 のみです。フェーズ 2 以降はモードに依存せず、入力元にかかわらず同一形式の型付き指摘リストを使用します。

フェーズ 1 の実行前にモードを決定してください。

検出ルーブリック

会話コンテキストから、スラッシュコマンドのプロンプト本文と直近の呼び出しターンを読み取ります。次の4つの分岐のいずれかに分類します。

  • MODE=direct — プロンプトに具体的な CLI 名と、少なくとも1つの直接入力シグナルが含まれている場合:

    • CLI を対象とする動作動詞:rename、add、remove、fix、sniff、discover
    • ユーザーが追加を希望する明示的な URL(例:https://example.com/feed/x)
    • フィード、コマンド、エンドポイント、機能の列挙
    • 「these ideas」「these features」「with the following」のような表現
  • MODE=dogfood — プロンプトが空の場合、要求を伴わずに CLI 名を挙げている場合(「amend the superhuman CLI」)、またはセッションを明示的に参照している場合(「what I just dogfooded」「this session's friction」「from my session today」)

  • MODE=both — セッションと具体的な要求の両方を明確に参照している場合(「I dogfooded this session and also want to add feature X」「in addition to the friction I hit, please add command Y」)

  • 曖昧 — シグナルが1つしかない場合(動詞を伴わず CLI 名だけがある、対象 CLI を伴わず動詞だけがある、または不満の報告にも新しい要求にも解釈できる表現)。AskUserQuestion を使ってユーザーに確認します。

    「この修正で調査結果を収集する方法は2つあります。どちらが適切ですか?

    1. 現在のセッションのトランスクリプトを調べる(dogfood モード)
    2. 先ほど入力した要求を使う(直接入力モード)
    3. 両方 — トランスクリプトの不満点と要求を組み合わせる」

スラッシュコマンドのプロンプトがまったくない場合は、既定で MODE=dogfood にします。これにより、末尾に何も付けない /printing-press-amend が v0.1 とまったく同じように動作する、標準的な UX が維持されます。

モードの保存

後続フェーズ(および再開した実行)で参照できるよう、解決したモードを $PRESS_RUNSTATE/current/mode.txt に書き込みます。

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

フェーズ 0 は、フェーズ 1 に次の1行を出力します。

mode: <dogfood|direct|both>

フェーズ 1 はこの値に応じて分岐します。dogfood の調査結果は ### 1a を通り、直接入力の調査結果は ### 1b を通ります。組み合わせて実行する場合は、両方のサブセクションを順番に実行します。フェーズ 2 以降はモードを完全に無視し、調査結果のリストを契約として扱います。

フェーズ 1 — 収集

このフェーズでは、型付きの調査結果リストを作成します。リストの形式はすべてのモードで同一です。各調査結果には、id、kind、category、classification(バグまたは機能)、evidence、target_cli、rationale、および provenance(dogfood の場合は transcript、直接入力の場合は user-ask、sniff 由来の場合は sniff)が含まれます。フェーズ 2 はリストをそのまま使用します。

MODE=dogfood の場合は、### 1a のみを実行します。MODE=direct の場合は、### 1b のみを実行します。MODE=both の場合は、まず ### 1a を実行し、次に ### 1b を実行して、ID が重複しないよう2つの調査結果リストを統合します(1b は 1a が終わった位置から番号を続けます)。

1a. ドッグフードモード(MODE=dogfood)

手順の全文は references/transcript-parsing.md をお読みください。このサブセクションで行う処理の概要は次のとおりです。

  1. アクティブなセッションのトランスクリプトファイルを特定する — 現在の作業ディレクトリから <project-dir-slug> を導出し、~/.claude/projects/<slug>/*.jsonl を更新日時順に一覧表示して、最も最近更新されたファイルを選びます。読み取る前に、必ず AskUserQuestion で解決したパスをユーザーに確認してください。誤ったファイルを選ぶと、別のセッションの摩擦が取り込まれてしまいます。

  2. トランスクリプトをたどり、摩擦のシグナルを抽出する — 0 以外の終了コード、エラーメッセージ、手作業で組み立てた API ペイロード(例:CLI コマンドにすべき直接の curl POST)、失敗後の再試行パターン、「X が存在しない」/「X は 400 を返す」といったエージェントのコメント、フラグ不足への言及、値を返さずに null を返す動作、認証に関する混乱などを抽出します。各シグナルには、タイムスタンプ、カテゴリ、証拠の原文、および参照している <slug>-pp-cli を含めます。

  3. 各シグナルをバグまたは機能として分類する — 1 行の根拠を添えます。バグ=CLI の動作が誤っている、機能=CLI に必要な動作がない。分類はエージェントによる最善の判断です。ユーザーは U4 のスコープ確認時に承認するか、変更できます。

  4. 対象 CLI を自動検出する — シグナル内で各 <slug>-pp-cli が出現する回数を数え、最も多く言及されている CLI を既定の候補として提示します。AskUserQuestion で確認します(CLI が 1 つの場合は単純な yes/no、候補が複数あり差が小さい場合は一覧から選択)。ユーザーが明示的な <cli-name-or-path> 引数を指定した場合は、自動検出を省略します。

  5. 対象パスを特定し、公開状況を確認する — 短縮名、正式名、または絶対パスを受け付けます(R4 に従います)。入力を CLI のスラッグのみに正規化した後、パブリックライブラリ内でそのスラッグを検索し、公開状況を確認します(ローカルクローンがある場合は ~/printing-press-library/library/*/<slug>、ない場合は gh api で同じパスを検索します)。ローカルの作業コピーの git リモートから公開状況を推測してはいけません。また、$PRESS_LIBRARY/<slug> の作業コピーが見つからないことを、未公開の根拠として扱ってはいけません。スラッグがパブリックライブラリで見つかった場合は、target_category と published_status: published を記録し、管理対象クローン経由のアップストリーム PR パスで実行します。published_status: local-only を使用するのは、スラッグがパブリックライブラリに存在しない場合に限ります。カテゴリは U7 の PR 作成フェーズで必要になるため、ここで取得し、再導出の手間を省きます。

1a が出力する各検出結果には provenance: transcript が含まれます。出力は references/transcript-parsing.md に記載された構造化された検出結果リストとして Phase 2 に渡されます。

1b. 直接入力モード(MODE=direct)

手順の全容については、references/direct-input-parsing.md を参照してください(v0.2 で導入)。このサブセクションで行う処理の概要は次のとおりです。

  1. スラッシュコマンドのプロンプト本文と、このスキルを起動した直近のエージェントメッセージターンを読み取ります。これらには、ユーザーの依頼がそのまま記載されています(例:「Digg 1000 を Digg に名前変更し、次の 4 つのフィードを追加してください:……。新しいエンドポイントがないか調べてください」)。確認できるトランスクリプトはないため、1a で実行する U1 のトランスクリプトパスモーダルはスキップします。

  2. 対象の CLI を特定します — R4 に従い、1a の手順 4〜5 と同じ名前解決ルールを使いますが、通常はプロンプト内に CLI の名前がすでに記載されています。正規表現(<slug>-pp-cli または「the CLI」)で抽出します。見つからない場合は、ユーザーに確認します。

  3. references/direct-input-parsing.md の基準に従って、依頼を構造化された指摘事項に解析します。各依頼は、型付きの kind フィールドを持つ 1 つの指摘事項に対応します。

    • rename — 「X を Y に名前変更する」/「Y ではなく X と呼ぶ」→ classification: feature
    • add-command — 「コマンド X を追加する」/「サブコマンド X を追加する」→ classification: feature
    • add-feed — 「フィード を追加する」/ユーザーが追加を希望する列挙済み URL(URL ごとに 1 つの指摘事項)→ classification: feature
    • add-endpoint — 「エンドポイント を追加する」/明示された API パス → classification: feature
    • fix-bug — 「X を修正する」/「X が壊れている」/「X が null を返す」→ classification: bug
    • sniff — 「新しい API を探す」/「新しいエンドポイントを見つける」/「さらに発見する」→ ### 1b.i の sniff サブルーチンに進みます。
  4. 各指摘事項の evidence には、ユーザーが実際に書いた内容をそのまま記録します。これにより、U4 のスコープ確認モーダルにユーザーが実際に書いた内容が表示されます。

  5. エッジケース — 複数の CLI に関する依頼は、2 つの個別の実行に分割します(v0.2 では対象外のため、ユーザーに 1 つ選ぶよう確認します)。動詞が曖昧な場合(具体的な内容を伴わない「X を更新する」など)は、推測せずに AskUserQuestion で確認します。

1b が出力する各指摘事項には、provenance: user-ask(sniff サブルーチンが生成した指摘事項の場合は provenance: sniff)が付与されます。出力は Phase 2 に渡され、1a と同じ構造の指摘事項リストとして扱われます。

1b.i. スニッフィングによる検出サブルーチン

解析ルーブリックによって、1b の依頼が kind: sniff と分類された場合に実行されます(「新しい API をスニッフィングする」「新しいエンドポイントを見つける」「 でさらにエンドポイントを検出する」などの表現)。スニッフィングは実行ごとにオプトイン方式です。ユーザーが明示的に指定しない限り、実行されることはありません。スニッフィングによる検出がない場合は、このサブルーチン全体をスキップします。

Step 1 — 対象のソース URL を解決します。 対象 CLI の公開済みマニフェスト ~/printing-press-library/library/<category>/<slug>/.printing-press.json を読み込み、source_url(フォールバックとして spec_url)を抽出します。category は 1b のステップ 2 で解決済みです。

どちらのフィールドも設定されていない場合は、ユーザーにその場で確認します。

"スニッフィングには対象 URL が必要です。スニッフィングするソースサイトを貼り付けるか、今回の実行ではスニッフィングによる検出をスキップしますか?"

ユーザーがスキップした場合は、アクティブリストからスニッフィングによる検出を削除し、他の 1b の検出項目を続行します。ユーザーが URL を貼り付けた場合は、ステップ 2~3 で使用します。

Step 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 のコード検索に問い合わせて候補エンドポイントを検出します。ブラウザーは不要です。通常、実行時間は 1 分未満です。

Step 3 — オプションの browser-sniff(ユーザーがより深い検出をオプトインした場合のみ)。 ユーザーの依頼でブラウザーを使った検出が明示的に指定されている(「ブラウザーでスニッフィングする」「詳細にスニッフィングする」など)かつ、取得済みの HAR がすでに利用できる場合は、次を実行します。

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

このスキルは v0.2 では HAR の取得自体を調整しません。HAR の取得はユーザーが行います(ユーザーが Chrome でソースサイトを開き、HAR をエクスポートして、その場所をスキルに指定します)。または、注記を添えて詳細なスニッフィングをスキップします。v0.3 では、claude-in-chrome MCP 経由で取得を実行できるようにスキルを拡張する可能性がありますが、v0.2 の範囲外です。

Step 4 — 検出結果を検出項目に変換します。 スニッフィングの出力に含まれる候補エンドポイントごとに、1b の検出項目リストへ検出項目を 1 つ追加します。

  • id: F<n>(解析済みの依頼の後に続く、次に利用可能な番号)
  • kind: add-endpoint
  • classification: feature
  • evidence: "crowd-sniff で検出: <endpoint-path>"(該当する場合は browser-sniff)
  • target_cli: <slug>-pp-cli
  • rationale: <スニッフィングの出力に要約があればその1行、なければ "スニッフィング候補、ユーザー確認待ち">
  • provenance: sniff

デフォルトでは、フェーズ 3 でこれらを Tier 3(仕上げ/アーキテクチャ)に分類します。ユーザーが確認し、優先度の高い項目を個別に Tier 2 へ昇格できます。

Step 5 — 縮退時の動作。

条件動作
.printing-press.json に source_url がなく、確認時にユーザーがスキップした場合スニッフィングによる検出を削除し、他の 1b の検出項目を続行します。「スニッフィングをスキップ — ソース URL なし」と記録します。
crowd-sniff が 0 以外の終了コードを返した場合エラーを記録し、スニッフィングによる検出をスキップして、他の 1b の検出項目を続行します。修正実行を中止してはいけません。
crowd-sniff が候補エンドポイントを 1 つも返さなかった場合何も追加しないのではなく、保留中の検出項目リストに 1 件追加します(「スニッフィングを実行したが、新しいエンドポイントは検出されなかった」)。これにより、ユーザーが記録を確認できます。
browser-sniff がリクエストされたものの、HAR が利用できない場合記録し、crowd-sniff の結果のみを使用します。

Step 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 — チェックポイント前のガード

スコープメニューをユーザーに表示する前に、2 つのガードが実行されます。どちらも検出項目を抑制したり、実行を中止したりする場合があります。

2a. PR の相互参照(重複する提案を抑制)

フェーズ 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

重複の可能性がある PR が見つかった指摘ごとに、次の内容をインラインで表示します。

「指摘 F<n>(<category>)は、PR # — <title>(、)ですでに対応済みの可能性があります。この指摘をスキップしますか?」

ユーザーは、スキップ(保留に移動)、保持、または「PR # を表示」(gh pr view <num> --repo mvanhorn/printing-press-library --web を開く)を選べます。明らかにマージ済みの一致では、デフォルトを「スキップ」とします。オープン中の PR では、デフォルトを「保持」とします(新しい PR を開くのではなく、進行中の PR に追加したい場合があるためです)。

このチェックにより、2026-05-15 のドッグフーディングで発生した典型的な失敗を防げます。Printing Press CLI の自動更新を提案したところ、数時間前に兄弟 CLI で同様の PR がすでにリリースされていました。誤ってスキップするコストは低く(ユーザーは U4 のカスタム選択から再度追加できます)、重複を見逃すコストは PR の却下とレビュー担当者の時間です。

2b. 古いバイナリのチェック(ドッグフーディングしたバイナリが公開済みのものより古い場合は中止)

公開ライブラリの対象 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 比較)、正常に中止します。

「ドッグフーディングした <slug>-pp-cli バイナリは v<local_ver> ですが、公開ライブラリのバージョンは v<published> です。遭遇した不便は、公開済みのバージョンではすでに修正されている可能性があります。次を実行してください。

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

…その後、再度ドッグフーディングしてから /printing-press-amend を実行してください。この実行を中止します。」

エッジケース:.printing-press.json がない、または version フィールドがない場合は、メモを表示して古いバージョンのチェックをスキップします。CLI がローカル専用(まだ公開されていない)の場合は、チェックをスキップします。

出力

フェーズ 2 からフェーズ 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

フェーズ 3 — スコープ確認チェックポイント(ユーザー・イン・ザ・ループ #1)

これは、2 回あるユーザーチェックポイントのうち最初のものです。ここまではすべて読み取り専用の調査でした。このチェックポイントでスコープを確定します。

残った指摘をティア分けする

指摘を 3 つのティアに分類します。

  • ティア 1 — バグ — classification: bug の指摘すべて。CLI の動作が誤っており、修正によって正しさが回復します。
  • ティア 2 — 直近のセッションでの不便を解消する機能不足 — ユーザーがセッション中に実際に手作業で回避策を実装した classification: feature の指摘(つまり、理論上ではなく、今すぐ明らかに必要としていた機能)です。
  • ティア 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 の選択肢は、それぞれのラベルだけで内容がわかるようにしてください(説明文に頼らない形式にします。ハーネスによっては説明文が表示されないためです)。

カスタム選択の経路では、各指摘の ID、カテゴリ、1 行の根拠を含む複数選択肢を提示し、続行する前にユーザーが選んだ項目を確認します。

除外された指摘の永続化

確定スコープに含まれないすべての指摘について、次の場所にある deferred-list の Markdown ファイルに追記します:

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

<run-id> は、この amend 実行のための新しくタイムスタンプを付けた id です(例: amend-2026-05-15T1432)。deferred ファイルは、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
---

次に、deferred された指摘ごとに 1 セクションを設け、以下を含めます: 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 ドラフトチェックポイントで最終的な diff をレビューします。

Step 1 — 管理対象クローンのセットアップ

計画内の Pre-Implementation Decision に従い、このスキルは $PRESS_LIBRARY/<slug>/ ではなく、mvanhorn/printing-press-library の管理対象クローンに対して直接操作します。管理対象クローンは次の場所にあります:

$PRESS_HOME/.publish-repo-$PRESS_SCOPE

これは /printing-press-publish が使用するのと同じクローンです(そのスキルの Step 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>/ です。category は 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 には反映されません。

Step 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
  • アクティブな指摘ごとに 1 セクション: id、category、classification、rationale、対象ファイル($CLI_DIR/... パス)、期待される動作変更、この指摘のテストシナリオ
  • 指摘間のリスクと依存関係(ある場合)

計画は決定の形であり、実行の形ではありません — 実装時の順序付けは Step 3 の間に行われます。

ステップ 3 — 計画を実行する(パッチ契約に従う)

依存関係の順序に従い、各検出事項について次の手順を実行します。

  1. $CLI_DIR/ 配下の対象ファイルを編集します。AGENTS.md の再実装禁止ルールに従ってください(独自のレスポンスビルダーを作成しないでください。新しいコマンドは実際のエンドポイントを呼び出すか、ローカルストアから読み取る必要があります。// pp:client-call/// pp:novel-static-reference のオプトアウトは、本当に正当な場合に限ります)。

  2. 任意:カスタマイズ箇所を後続のエージェントがすばやく見つけられるように、変更箇所に // PATCH(<short reason>) 形式のソースコメントを追加できます。書式の例:

    // 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 にパッチファイルを 1 つ作成します(ファイル名はパッチの id と同じにします)。各ファイルは自己完結したパッチオブジェクト 1 つのみとします。CLI が同じ複数の 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(古い print で、まだ正規化されていないもの)が残っている場合も、エントリは新しい .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(...) ソースコメントは任意のナビゲーション補助であり、公開ライブラリの検証ツールは、コメントとマーカーのペアを必須としなくなりました。正式な仕様については ~/printing-press-library/AGENTS.md を参照してください。ファイルが残っていてもカスタマイズの根幹となる呼び出しが消える可能性がある場合は、call_sites、markers、または marker で宣言し、それらの検出文字列を files[] に含めてください。これにより、見つからなくなった場合に regen と publish validate が確実に失敗します。すべての検出文字列には files[] が必要です。指定したファイル以外に残っている一致はカウントされません。

    パッチが将来の置き換え経路を意図的に残す場合に限り、deferred_to_upstream を使用してください。該当するのは、公開 API エンドポイントが現時点で存在しない場合、非公式ホストまたは代替の認証ソースにコマンドが依存している場合、ライブレスポンスの形式がジェネレーターの想定からずれている場合、または Printing Press がそのパターンに対応すれば修正が不要になる場合です。その場合は、まず mvanhorn/cli-printing-press の issue を検索してください。一致する issue があれば再利用し、なければライブラリ PR の前に issue を作成してから、その URL を upstream_issue に設定してください。マシンレベルの依存関係や API 公開への依存関係を PR 本文だけに記載しないでください。

  4. マシン側の修正か、生成済み CLI 側の修正かを判断する(AGENTS.md に従う):ある検出事項への修正が、すべての生成済み CLI に適用できるもの(例:「スレッド一覧コマンドでは、どの CLI でもジェネレーターが --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 を再利用してください。issue の URL をパッチの .printing-press-patches/<id>.json に追加し、ローカルパッチに取って代わるべきマシン側または上流 API 側の条件を記した deferred_to_upstream 項目を追加します。

ステップ 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 を自動で開かずに終了します。ユーザーはスキルを再度呼び出すことで再開できます(フェーズ 1 が保留中のプランを検出し、再開を提案します)。

ステップ 6 — パッチマニフェストを確認する

この amend 実行では、少なくとも 1 つのパッチが記録されている必要があります — .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 — ドキュメント更新チェックポイント

フェーズ 4 が pass を出力する前に、新規、名称変更、または変更されたすべてのユーザー向けコマンド、フラグ、ワークフロー、ランタイムモード、または MCP から見えるアクションについて、確定した検出事項と最終 diff を検査します。そのような各変更は、同じパブリックライブラリの PR 内でドキュメントを同梱する必要があります:

  • SKILL.md と README.md の両方に cookbook レシピを追加または更新します。
  • 対応する Unique Features / Unique Capabilities のエントリを追加または更新し、生成されるドキュメントとエージェント向けサマリーが変更されたワークフローを明示するようにします。
  • 例は誠実に保ちます:コマンド、フラグ、位置引数、出力スニペットは、$CLI_DIR に現在存在するコードと一致している必要があります。
  • そのパスが存在する場合は、パブリックライブラリのチェックアウトから python3 .github/scripts/verify-skill/verify_skill.py --dir "$CLI_DIR" を実行します。存在しない場合は、チェックアウトで利用可能なパッケージ版の同等の verifier を実行し、そのコマンドを記録します。

必須のドキュメント編集または verifier の結果が欠けている場合は、ブロッキングチェックリストとともに停止します。ブロッキングチェックリストには、コマンド名と、欠けている README/SKILL/Unique Features の項目を記載します。チェックリストが空になるまで、PR ドラフトチェックポイントに進まないでください。amend がユーザー向けの動作変更を伴わない内部のみの修正である場合は、フェーズ 4 の出力に documentation_checkpoint: no-user-facing-doc-change を記録します。

出力

フェーズ 4 はフェーズ 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 を設定してください。このデフォルトは、フェーズ 7 の PR 本文とフェーズ 8 の RESULT ブロックが空の値を出力しないよう、遅くともフェーズ 4 の終了までに設定する必要があります。

フェーズ 5 — PII のスクラブ

手順の詳細は references/pii-scrubbing.md をお読みください。概要は次のとおりです。

スクラブは3つのレイヤーで構成され、それぞれ一時的なステージングコピーに対して実行します(ユーザーのセッションのトランスクリプトや、作業中のソースコードには実行しません)。

  1. 認証情報 — skills/printing-press-retro/references/secret-scrubbing.md の正規表現パターン(Stripe、GitHub PAT、ベアラートークン、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 に進む前にユーザーによる解決を必須とします。エージェントが Go のソースコードに PII を追加してはなりません。この確認は、エージェントのミスを検出するためのものです。

ストップリストの作成:~/.printing-press/amend-config.yaml が存在しない場合、スキルは初期リストと書式を説明するコメントを含むデフォルトファイルを作成します。ファイルモードも検証し、全ユーザーに書き込み可能な場合は警告し、所有者が不明な場合は処理を中止します。

スクラブレポートは $PRESS_MANUSCRIPTS/<slug>/<run-id>/scrub-report.json に書き込まれます(コミットしません。ユーザーによる監査用です)。フェーズの最後にユーザーへ示す概要は、「Y 個の成果物で X 個のトークンを置き換えました。」です。

フェーズ 6 — PR 下書きレビューのチェックポイント(ユーザー確認 #2)

これが2回目で最後のユーザーチェックポイントです。この後のすべての処理(プッシュ、PR の作成、ラベル付け、RESULT ブロックの出力)は無人で実行されます。gh コマンドを実行する前に、これから公開される内容をすべてユーザーに表示してください。 PR に記載のないユーザー向けの変更が紛れ込まないよう、フェーズ 4 のドキュメント確認結果もこのレビューに含めてください。

ドラフトを組み立てる

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. Summary — ユーザーの困りごとと修正の概要を示す 1~3 文
  2. Findings — ID、カテゴリ、種類(バグ/機能)、根拠を含む表
  3. Changes — git diff --stat upstream/main..HEAD の出力
  4. Verification — Phase 4 のビルド/テスト/dogfood/検証のステータス、およびドキュメントチェックポイントの結果
  5. Evidence — 実行ごとの計画ドキュメントと、PR の HEAD SHA における .printing-press-patches/ ディレクトリへの完全な GitHub URL(リンク切れを防ぐため、プッシュ後に取得)
  6. Step 6 の library-pr-plumbing.md で該当する issue が見つかった場合は、末尾に 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 をユーザーに表示します。Phase 5 で認識できない大文字始まりの語句が見つかり、ユーザーが正当な語句として受け入れた場合は、該当する文とともにその語句をここで再表示します。

「リマインダー:PR 本文に <phrase> が含まれています(Phase 5 で正当な語句として受け入れられました)。作成前に確認してください。」

AskUserQuestion:open / edit / hold / abort
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

edit then open の場合:タイトルと本文を別々の編集可能なブロックとして提示し、ユーザーの修正を受け付けます。ドラフト全体を再表示し、続行前に確認します。

hold の場合:計画と差分を $PRESS_MANUSCRIPTS/<slug>/<run-id>/proofs/<timestamp>-amend-<cli-name>-HELD.md および ${path%.md}.diff に保存します。status: held と再開パスを含む RESULT ブロックを出力します。今後の /printing-press-amend の実行時に保留ファイルを検出し、再開を提案できます。

abort の場合:簡潔な確認メッセージを出力します。U5 の計画ドキュメントはそのまま残し(frontmatter に status: aborted を記録)、調査結果を記録できるようにします。それ以外は何も保存しません。管理対象のクローンは次回の実行時にリセットされます。

Phase 7 — PR の作成(自律実行)

ユーザーが open または edit-then-open を選択した場合は、references/library-pr-plumbing.md の Step 5~7 を実行します。

  1. Step 5 — git add "$CLI_DIR" を実行し、conventional commit 形式のメッセージと調査結果の一覧を含めてコミットする
  2. Step 6 — 調査結果に該当する既存 issue を検索し、リンクするか新規作成する。可能であれば自分を担当者に設定する
  3. Step 7 — ブランチをプッシュし(プッシュとフォークのアクセスモードは Step 1 で判定)、--body-file を指定して gh pr create を実行し、HEAD_SHA を取得してラベルを適用する

フォーク/アクセスの検出、ブランチの競合処理、管理対象クローンの更新方法については、references/library-pr-plumbing.md に詳しく記載されています。ここに手順を展開しないでください。正式な参照先はこのドキュメントです。

PR の作成後、ユーザー向けの概要に URL と Greptile に関する注記を表示します。

「PR を作成しました:

Greptile は約 2 分以内にレビューします。インラインコメントを確認してください:

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

人間によるレビューを依頼する前に、P0/P1 の指摘への対応を検討してください。」

Phase 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 と、対応するテストファイルを参照してください。セットアップ契約の整合性チェックは、internal/pipeline/contracts_test.go の Go テスト TestSkillSetupBlocksMatchWorkspaceContract として実行します。)

コメント