
Printing Press Amend
- 4k installs
- 4.4k repo stars
- Updated August 2, 2026
- mvanhorn/cli-printing-press
printing-press-amend is a slash skill that converts dogfood friction or direct asks into scoped printing-press CLI patches and library PRs.
About
printing-press-amend turns real CLI usage friction into pull requests for published printing-press library CLIs. Two input modes feed a unified finding list: dogfood mode mines the active Claude Code session transcript for missing flags, hand-rolled API payloads, and silent-null returns, while direct-input mode accepts explicit rename, add, fix, or sniff requests with optional site endpoint discovery. Phase 0 detects MODE as dogfood, direct, both, or asks the user when ambiguous, persisting mode.txt under PRESS_RUNSTATE. Setup runs the PRESS_SETUP_CONTRACT to resolve PRINTING_PRESS_BIN absolute path, enforce min-binary-version 4.0.0, and hard-stop below the published currency floor from supported-versions.txt. Phase 1 captures typed findings with id, kind, category, classification, evidence, target_cli, rationale, and provenance, then later phases plan, execute fixes autonomously, scrub PII, and open a PR with two user checkpoints after scope capture and before PR draft. Patches track in the library .printing-press-patches directory, distinct from the binary patch subcommand AST injection. Sibling skills publish, polish, and retro cover other lifecycle stages not addressed here.
- Dogfood and direct-input modes merge into one typed finding list for Phase 2 onward.
- Setup contract resolves PRINTING_PRESS_BIN absolute path and enforces min-binary-version 4.0.0.
- Currency-floor check hard-stops binaries below published supported-versions.txt minimum.
- Two user-in-loop checkpoints: scope after capture and PR draft before opening.
- Produces git patches tracked in .printing-press-patches separate from binary patch subcommand.
Printing Press Amend by the numbers
- 3,983 all-time installs (skills.sh)
- +220 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #49 of 550 CLI & Terminal skills by installs in the Skillselion catalog
- Security screen: HIGH risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
printing-press-amend capabilities & compatibility
- Capabilities
- dogfood transcript friction mining into typed fi · direct input feature, rename, fix, and sniff cap · binary version and currency floor compatibility · autonomous fix planning and execution with pii s · pr draft with user checkpoints before open
- Works with
- github
- Use cases
- ci cd · orchestration
What printing-press-amend says it does
Turn a dogfood session into a PR for a printed CLI in the public library.
Two user-in-loop checkpoints: scope after capture, PR draft before open.
min-binary-version: 4.0.0
npx skills add https://github.com/mvanhorn/cli-printing-press --skill printing-press-amendAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 4k |
|---|---|
| repo stars | ★ 4.4k |
| Security audit | 2 / 3 scanners passed |
| Last updated | August 2, 2026 |
| Repository | mvanhorn/cli-printing-press ↗ |
How do I turn real CLI dogfood friction or feature asks into a reviewed patch PR for a published printing-press CLI?
Turn dogfood session friction or direct user asks into scoped CLI patches, scrub PII, and open PRs against the printing-press library.
Who is it for?
Developers amending published printing-press CLIs after session friction or explicit feature and bug requests.
Skip if: Skip for brand-new CLI publishing, pre-publish polish, or machine retrospection covered by sibling printing-press skills.
When should I use this skill?
User says amend the CLI, submit a patch, fix what I dogfooded, open a PR for this CLI, or run printing-press-amend.
What you get
Scoped finding list, implemented CLI fix with PII scrubbed, and opened PR against mvanhorn/printing-press-library.
- typed finding list
- CLI source patch
- pull request against printing-press-library
By the numbers
- [object Object]
- [object Object]
Files
/printing-press-amend
Turn a dogfood session into a PR for a printed CLI in the public library.
/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"This skill lives in this repo (the machine) and acts on a printed CLI in the public library. It is sibling to /printing-press-publish (adds a new CLI), /printing-press-polish (improves a CLI pre-publish), and /printing-press-retro (reflects on the machine itself). None of those cover post-publish CLI amendments driven by real-session friction.
The artifact this skill produces is semantically a "patch" (in the git/PR sense), tracked by the public library's .printing-press-patches/ directory (one file per patch). Inline // PATCH(...) source comments are optional navigation aids when they make a customized site easier to grep. The slash-skill name is amend to disambiguate from the existing cli-printing-press patch binary subcommand (which AST-injects pre-defined features — different mechanism, different intent).
Setup
Before doing anything else:
<!-- 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
echo "PRINTING_PRESS_BIN=$PRINTING_PRESS_BIN"
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"
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() {
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
}'
}
_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" | awk -F= '/^min_supported=/{print $2; exit}')
_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 | awk '/"Version":/{v=$2; gsub(/[",]/,"",v); sub(/^v/,"",v); print v; exit}')
fi
if [ -n "$_floor_min" ] && [ -n "$_floor_installed" ] && [ -n "$_floor_latest" ] &&
_semver_lt "$_floor_installed" "$_floor_min" &&
! _semver_lt "$_floor_latest" "$_floor_min"; 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<!-- PRESS_SETUP_CONTRACT_END -->
After running the setup contract, capture the PRINTING_PRESS_BIN=<abs-path> line from stdout. Every subsequent `cli-printing-press ...` invocation in this skill must use that absolute path (substitute the value, not the literal $PRINTING_PRESS_BIN token) — export PATH above only affects the single Bash tool call it runs in, so later calls open a fresh shell where bare cli-printing-press resolves against the user's default PATH and a stale global can shadow the local build.
After capturing the binary path, check binary version compatibility. Read the min-binary-version field from this skill's YAML frontmatter. Run <PRINTING_PRESS_BIN> version --json and parse the version from the output. Compare it to min-binary-version using semver rules. If the installed binary is older than the minimum, stop immediately and tell the user: "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."
If the setup contract emitted an [upgrade-required] block, the installed binary is below the published currency floor (PRESS_REQUIRED_MIN) — older releases regenerate CLIs with since-fixed bugs (PRESS_REQUIRED_REASON). This is a hard gate distinct from min-binary-version: do not amend or regenerate on that binary. Offer a one-click upgrade via AskUserQuestion — Yes — upgrade now (run go install github.com/mvanhorn/cli-printing-press/v4/cmd/cli-printing-press@latest, re-capture PRINTING_PRESS_BIN, then continue) or Cancel (stop the run). There is no skip-and-continue; below the floor the only paths are upgrade or abort. If the upgrade command fails, surface it and stop.
Phase 0 — Input Mode Detection
This skill accepts two input sources for the finding list it later patches: a Claude Code session transcript (dogfood mode, current behavior) and user-supplied asks in the slash-command prompt (direct-input mode, added in v0.2). The two modes diverge only in Phase 1; Phase 2 onward is mode-agnostic and consumes a typed finding list with identical shape regardless of source.
Decide the mode before Phase 1 runs.
Detection rubric
Read the slash-command prompt body and the immediate invocation turn from the conversation context. Classify into one of four branches:
- `MODE=direct` — the prompt contains a concrete CLI name AND at least one direct-input signal:
- Action verbs targeting the CLI:
rename,add,remove,fix,sniff,discover - Explicit URLs the user wants added (e.g.,
https://example.com/feed/x) - An enumerated list of feeds, commands, endpoints, or features
- Phrasing like "these ideas", "these features", "with the following"
- `MODE=dogfood` — the prompt is empty, OR names a CLI without any asks ("amend the superhuman CLI"), OR explicitly references the session ("what I just dogfooded", "this session's friction", "from my session today")
- `MODE=both` — the prompt clearly references both: a session AND specific asks ("I dogfooded this session and also want to add feature X", "in addition to the friction I hit, please add command Y")
- Ambiguous — only one signal is present (CLI named with no verbs, or verbs with no target CLI, or asks worded so they could be friction reports OR new asks). Ask the user via
AskUserQuestion:
"Two ways to source findings for this amend. Which fits?
1. Mine the current session transcript (dogfood mode)
2. Use the asks I just typed (direct-input mode)
3. Both — combine transcript friction with my asks"
Default when no slash-command prompt is present at all: MODE=dogfood. This preserves the canonical UX — /printing-press-amend with nothing after still works exactly as it did in v0.1.
Persist the mode
Write the resolved mode to $PRESS_RUNSTATE/current/mode.txt so later phases (and a resumed run) can read it:
echo "$MODE" > "$PRESS_RUNSTATE/current/mode.txt"Output
Phase 0 emits one line to Phase 1:
mode: <dogfood|direct|both>Phase 1 branches on this value — dogfood findings flow through ### 1a, direct-input findings flow through ### 1b, and combined runs execute both sub-sections in sequence. Phase 2 onward ignores the mode entirely — the finding list is the contract.
Phase 1 — Capture
This phase produces a typed finding list. The list shape is identical across modes: each finding carries id, kind, category, classification (bug or feature), evidence, target_cli, rationale, and provenance (transcript for dogfood, user-ask for direct, sniff for sniff-derived). Phase 2 consumes the list verbatim.
When MODE=dogfood, run only ### 1a. When MODE=direct, run only ### 1b. When MODE=both, run ### 1a first, then ### 1b, and merge the two finding lists with non-colliding IDs (1b continues numbering where 1a left off).
1a. Dogfood mode (MODE=dogfood)
Read references/transcript-parsing.md for the full procedure. Summary of what this sub-section does:
1. Resolve the active session transcript file — derive <project-dir-slug> from the current working directory, list ~/.claude/projects/<slug>/*.jsonl by mtime, pick the most-recently-modified. ALWAYS confirm the resolved path with the user via AskUserQuestion before reading — wrong-file selection ingests friction from the wrong session.
2. Walk the transcript and extract friction signals — non-zero exit codes, error messages, hand-rolled API payloads (e.g. direct curl POSTs that should be a CLI command), retry-after-failure patterns, agent commentary like "X doesn't exist" / "X returns 400", missing-flag references, silent-null returns, auth confusion. Each signal carries timestamp + category + verbatim evidence + the <slug>-pp-cli it references.
3. Classify each signal as bug or feature with a one-line rationale. Bug = CLI behavior is wrong; feature = CLI behavior is missing. The classification is the agent's best read; the user confirms or overrides at the U4 scope checkpoint.
4. Auto-detect target CLI — count occurrences of each <slug>-pp-cli in the signals, propose the most-touched CLI as the default. Confirm with AskUserQuestion (single CLI: simple yes/no; multiple close: pick from list). When the user passed an explicit <cli-name-or-path> argument, skip auto-detect.
5. Resolve target paths and publish status — accept short name, full name, or absolute path (per R4). Normalize the input to the bare CLI slug, then resolve publish status by looking up that slug in the public library (~/printing-press-library/library/*/<slug> when a local clone exists, otherwise the same path via gh api). Do not infer publish status from the local working copy's git remotes, and do not treat a missing $PRESS_LIBRARY/<slug> working copy as unpublished. If the slug is found in the public library, record target_category, published_status: published, and route the run through the managed-clone upstream PR path. Only use published_status: local-only when the slug is absent from the public library. The category is needed by U7's PR open phase and is captured here so it doesn't have to be re-derived.
Each finding emitted by 1a carries provenance: transcript. Output flows into Phase 2 as the structured finding list documented in references/transcript-parsing.md.
1b. Direct-input mode (MODE=direct)
Read references/direct-input-parsing.md for the full procedure (introduced in v0.2). Summary of what this sub-section does:
1. Read the slash-command prompt body plus the immediate agent-message turn that fired the skill — these carry the user's verbatim asks (e.g., "rename Digg 1000 to Digg, add these four feeds: ..., sniff for new endpoints"). There is no transcript to confirm; skip the U1 transcript-path modal that 1a runs.
2. Resolve the target CLI — same name-resolution rules as 1a step 4-5 (per R4), but the CLI is normally already named in the prompt itself. Extract via regex (<slug>-pp-cli or "the <slug> CLI"); if absent, ask the user.
3. Parse the asks into structured findings using the rubric in references/direct-input-parsing.md. Each ask maps to one finding with a typed kind field:
rename— "rename X to Y" / "call it X instead of Y" →classification: featureadd-command— "add command X" / "add subcommand X" →classification: featureadd-feed— "add feed <url>" / enumerated URLs the user wants added (one finding per URL) →classification: featureadd-endpoint— "add endpoint <url>" / explicit API path →classification: featurefix-bug— "fix X" / "X is broken" / "X returns null" →classification: bugsniff— "sniff for new APIs" / "find new endpoints" / "discover more" → routes to the sniff subroutine in### 1b.i
4. Each finding records the user's verbatim phrasing in evidence so the U4 scope confirmation modal shows the user what they actually wrote.
5. Edge cases — multi-CLI asks split into two separate runs (out of scope for v0.2; ask the user to pick one). Ambiguous verbs (update X without specifics) trigger an AskUserQuestion clarification rather than a guess.
Each finding emitted by 1b carries provenance: user-ask (or provenance: sniff for findings produced by the sniff subroutine). Output flows into Phase 2 as the same structured finding list shape used by 1a.
1b.i. Sniff-finding subroutine
Triggered when the parsing rubric tags any 1b ask as kind: sniff (phrases like "sniff for new APIs", "find new endpoints", "discover more endpoints in <site>"). Sniff is opt-in per run — never invoked unless the user named it. Skip this subroutine entirely when no sniff finding is present.
Step 1 — Resolve the target source URL. Read the target CLI's published manifest at ~/printing-press-library/library/<category>/<slug>/.printing-press.json and extract source_url (or spec_url as fallback). Category was resolved in 1b step 2.
If neither field is set, ask the user inline:
"Sniff needs a target URL — paste the source site you want sniffed, or skip the sniff finding for this run?"
If the user skips, drop the sniff finding from the active list and continue with the other 1b findings. If the user pastes a URL, use it for steps 2-3.
Step 2 — Run crowd-sniff first (fast, no browser). Replace <PRINTING_PRESS_BIN> with the absolute path captured at setup:
<PRINTING_PRESS_BIN> crowd-sniff --site "$SOURCE_URL" --json > /tmp/amend-sniff-crowd.json
crowd_exit=$?crowd-sniff queries npm SDKs and GitHub code search to discover candidate endpoints — no browser required. Typical runtime is under a minute.
Step 3 — Optional browser-sniff (only when the user opted in deeper). When the user's ask explicitly named browser-based discovery ("sniff with browser", "do a deep sniff") AND a captured HAR is already available, run:
<PRINTING_PRESS_BIN> browser-sniff --har "$HAR_PATH" --json > /tmp/amend-sniff-browser.json
browser_exit=$?This skill does not orchestrate HAR capture itself in v0.2 — capture is user-driven (the user opens the source site in Chrome, exports the HAR, and points the skill at it) or the deep sniff is skipped with a note. v0.3 may extend the skill to drive capture via the claude-in-chrome MCP; out of scope for v0.2.
Step 4 — Convert discoveries to findings. For each candidate endpoint in the sniff output, append one finding to the 1b finding list:
id: F<n>(next available number after the parsed asks)kind: add-endpointclassification: featureevidence: "discovered via crowd-sniff: <endpoint-path>"(orbrowser-sniffwhen applicable)target_cli: <slug>-pp-clirationale: <one-line summary from sniff output if available, otherwise "sniff candidate, user to confirm">provenance: sniff
Tier these as Tier 3 (polish/architecture) at Phase 3 by default — the user reviews and can promote individual entries to Tier 2 if they're high-priority.
Step 5 — Degraded paths.
| Condition | Behavior |
|---|---|
.printing-press.json lacks source_url AND user skips when asked | Drop sniff finding; continue with other 1b findings; log "sniff skipped — no source URL". |
crowd-sniff exits non-zero | Log the error; skip sniff findings; continue with other 1b findings. Do NOT abort the amend run. |
crowd-sniff returns zero candidate endpoints | Emit one entry to the deferred-findings list ("sniff ran, no new endpoints discovered") rather than adding nothing — gives the user a record. |
| Browser-sniff requested but no HAR available | Log; fall back to crowd-sniff results only. |
Step 6 — Surface provenance to the user. At the Phase 3 scope-confirmation modal, sniff-derived findings are visually grouped under a (sniff) provenance tag so the user can decide whether to keep them as a group, e.g.:
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)
...Phase 2 — Pre-Checkpoint Guards
Two guards run before the user sees the scope menu. Either can suppress findings or abort the run.
2a. PR cross-reference (suppress duplicate proposals)
For each finding from Phase 1, search open + recently-merged PRs in mvanhorn/printing-press-library for matches. The duplicate-detection criteria (in priority order): (1) the target CLI's directory path overlaps the PR's changed-file list, (2) keywords from the finding's category + rationale match the PR title or body.
# 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,filesFor each finding with a possible-duplicate match, present inline:
"FindingF<n>(<category>) may already be addressed by PR #<num> —<title>(<state>, <date>). Skip this finding?"
User options: skip (drops to deferred), keep, or "show me PR #<num>" (opens gh pr view <num> --repo mvanhorn/printing-press-library --web). The default for clearly-merged matches is "skip"; for open PRs, default is "keep" (the user may want to add to the in-flight PR rather than open a new one).
This guard catches the canonical failure mode from the 2026-05-15 dogfood: proposing auto-refresh for a Printing Press CLI when a similar PR had already shipped on a sibling CLI a few hours earlier. The cost of a false skip is low (the user can re-add via custom selection at U4); the cost of a false-negative duplicate is a rejected PR + reviewer time.
2b. Stale-binary check (abort if the dogfooded binary lags published)
Read the public library's .printing-press.json for the target CLI to find the published version. Compare to what the local printed CLI binary reports.
# 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 "")If local_ver is older than published (semver comparison), abort cleanly:
"The<slug>-pp-clibinary you dogfooded is v<local_ver>, but the published library version is v<published>. The friction you hit may already be fixed in the published version. Run:
>
go install github.com/mvanhorn/<slug>-pp-cli@latest
>
...then re-run /printing-press-amend after re-dogfooding. Aborting this run."Edge cases: if .printing-press.json is missing or has no version field, skip the stale check with a note. If the CLI is local-only (not yet published), skip the check.
Output
Phase 2 emits the (possibly trimmed) finding list to 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: publishedPhase 3 — Scope Confirmation Checkpoint (User-in-Loop #1)
This is the first of two user checkpoints. Everything until now has been read-only discovery; this checkpoint commits scope.
Tier the surviving findings
Group findings into three tiers:
- Tier 1 — Bugs — every finding with
classification: bug. CLI behavior is wrong; fixes restore correctness. - Tier 2 — Missing features that solve immediate session pain —
classification: featurefindings tied to a hand-rolled workaround the user actually built during the session (i.e. the user clearly needed it now, not theoretically). - Tier 3 — Polish / architecture — remaining
classification: featurefindings that are nice-to-have or architectural improvements without an immediate workaround in the session.
Display the tiered list inline before the question:
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 versionPick scope via 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 findingsThe AskUserQuestion options must be self-contained (each label must convey what it does without relying on description text — some harnesses hide the description).
For the custom selection path: present a multi-select with each finding's id + category + one-line rationale; confirm the user-checked subset before proceeding.
Persist the excluded findings
For every finding NOT in the confirmed scope, append to a deferred-list markdown file at:
$PRESS_MANUSCRIPTS/<api-slug>/<run-id>/proofs/<timestamp>-amend-<cli-name>-deferred.mdThe <run-id> is a fresh timestamped id for this amend run (e.g. amend-2026-05-15T1432). Format the deferred file as a YAML preamble + a finding-per-section markdown body so a future /printing-press-amend run on the same CLI can re-surface the items.
---
date: 2026-05-15
target_cli: superhuman-pp-cli
amend_run_id: amend-2026-05-15T1432
deferred_count: 2
---Then one section per deferred finding with: id, category, classification, rationale, evidence, reason-deferred (e.g. "user picked Tier 1 only"), and still_relevant: unknown.
On a subsequent /printing-press-amend run, Phase 3 should look in $PRESS_MANUSCRIPTS/<api-slug>/ for the most-recent *-deferred.md and offer the user the option to include any items still relevant in this run's scope. (Implementation note: this re-surfacing logic ships in v0.1; do not silently re-add — always present and confirm.)
Edge case: nothing to do
If Phase 2 suppressed every finding (everything was a duplicate), Phase 3 reports cleanly and exits without opening the menu:
"All findings from this session were addressed by existing PRs. No novel patches found."
Output
Phase 3 emits to 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 landedPhase 4 — Plan + Execute + Validate (Autonomous)
This phase runs unattended between checkpoints 1 and 2. The user does not see fix-by-fix details; they review the final diff at the Phase 6 PR-draft checkpoint.
Step 1 — Set up the managed clone
Per the Pre-Implementation Decision in the plan: this skill operates DIRECTLY on the managed clone of mvanhorn/printing-press-library rather than on $PRESS_LIBRARY/<slug>/. The managed clone is at:
$PRESS_HOME/.publish-repo-$PRESS_SCOPEThis is the same clone /printing-press-publish uses (Step 5 of that skill). Reuse it:
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
fiThe CLI's directory inside the managed clone is $PUBLISH_REPO_DIR/library/<category>/<slug>/. The category was resolved in Phase 1 (or look it up with find "$PUBLISH_REPO_DIR/library" -maxdepth 2 -name "<slug>" -type d).
CLI_DIR="$PUBLISH_REPO_DIR/library/<category>/<slug>"All edits in this phase happen INSIDE $CLI_DIR. Never touch $PRESS_LIBRARY/<slug>/ — that's a different working copy and editing it would not flow to the PR.
Step 2 — Write the per-run plan doc
Before editing code, materialize a plan markdown at:
$PRESS_MANUSCRIPTS/<slug>/<run-id>/proofs/<timestamp>-amend-<cli-name>.mdMirror to /tmp/printing-press/amend/ for quick reference. The plan doc carries:
- Frontmatter:
date,target_cli,amend_run_id,scope_tier,findings_count - One section per active finding: id, category, classification, rationale, target files (
$CLI_DIR/...paths), expected behavior change, test scenarios for this finding - Risks and dependencies between findings (if any)
The plan is decision-shape, not execution-shape — implementer-time sequencing happens during Step 3.
Step 3 — Execute the plan (with the patch contract)
For each finding in dependency order:
1. Edit the target files under $CLI_DIR/. Honor AGENTS.md anti-reimplementation rules (no hand-rolled response builders; novel commands must call the real endpoint or read from the local store via // pp:client-call / // pp:novel-static-reference opt-outs only when truly justified).
2. Optional: add a // PATCH(<short reason>) source comment at changed sites when it helps future agents find the customization quickly. Format examples:
// PATCH(amend-2026-05-15: surface refresh-token expiry to user) — was silently retrying
func (c *Client) Refresh(ctx context.Context) error {
...
}3. Create one patch file $CLI_DIR/.printing-press-patches/<id>.json (filename = the patch id). Each file is a single self-contained patch object — one file per patch, so concurrent amend PRs on the same CLI never conflict on patch metadata:
{
"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"
],
"validated_outcome": "publish validate passed; focused drafts and refresh-token checks pass",
"findings_addressed": ["F1", "F2", "F5", "F7"]
}If the CLI still ships the legacy single-array .printing-press-patches.json (older print, not yet normalized), still write your entry as a new .printing-press-patches/<id>.json file — the public library's normalize-patches workflow merges the two post-merge. Do not append to the legacy array.
If you add // PATCH(...) comments, you may also include a patch_count field for reviewer convenience. Do not add patch_count when no source comments were added.
For a temporary patch with a future supersession path, include the upstream handoff fields in that same patch entry:
{
"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>"
}The .printing-press-patches/<id>.json patch file is mandatory for code-level customizations. Inline // PATCH(...) source comments are optional navigation aids; the public library verifier no longer enforces a marker/comment pairing. See ~/printing-press-library/AGENTS.md for the authoritative spec.
Use deferred_to_upstream only when the patch intentionally leaves a future supersession path: a public API endpoint is missing today, the command relies on an unofficial host or alternate auth source, a live response shape drifted from generator assumptions, or the fix would become unnecessary once the Printing Press learns the pattern. In those cases, search mvanhorn/cli-printing-press issues first; reuse a matching issue or open one before the library PR, then set upstream_issue to that URL. Do not leave a machine-level or API-publication dependency only in the PR body.
4. Machine-vs-printed-CLI judgment (per AGENTS.md): when a finding's fix would generalize to every printed CLI (e.g. "the generator should emit --type sent for any threads list command"), surface as a borderline case:
"Finding F5 (--type sentmissing) looks like a machine-level fix — the generator templateinternal/generator/templates/threads.go.tmplshould emit it for every CLI with this endpoint shape, not just<slug>-pp-cli. Defer to a/printing-press-retrofollow-up, or proceed CLI-specific?"
When deferred, drop into the deferred-list with classification machine-level. When kept because the printed CLI needs a narrow fix now, and the patch still carries a future supersession path, create or reuse the upstream Printing Press issue before opening the library PR, add the issue URL to the patch's .printing-press-patches/<id>.json, and add a deferred_to_upstream item naming the machine-level or upstream-API condition that should supersede the local patch.
Step 4 — Validate
After all edits land, run the consolidated validator (replace <PRINTING_PRESS_BIN> with the absolute path captured at setup):
<PRINTING_PRESS_BIN> publish validate --dir "$CLI_DIR" --json > /tmp/amend-validate.json
exit_code=$?publish validate runs manifest, phase5, govulncheck (scoped to this CLI's module), go vet, go build, --help, --version. Exit 0 = clean.
Step 5 — Retry on failure (up to 3 iterations)
If publish validate reports failures, parse the error categories from the JSON, attempt targeted fixes, re-run validate. Maximum 3 iterations total. After iteration 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"Surface the final error log to the user, do NOT auto-open the PR, exit. The user can resume by re-invoking the skill (Phase 1 detects the held plan and offers to resume).
Step 6 — Check the patch manifest
This amend run must have recorded at least one patch — a <id>.json under .printing-press-patches/ (the directory layout), or, only for a CLI not yet normalized, a non-empty patches[] in the legacy .printing-press-patches.json.
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
fiMissing or empty patch manifest → fix locally before continuing.
Output
Phase 4 emits to 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>`dogfood_status` per mode. When MODE=dogfood, the value reflects the result of the dogfood validation step that consumed the transcript-derived findings (PASS if the run produced a clean fix, FAIL if it surfaced a regression). When MODE=direct, there is no transcript to dogfood against — set dogfood_status=N/A. When MODE=both, dogfood validation still runs against the transcript half of the findings; set PASS/FAIL accordingly. This default must be set at the latest by the end of Phase 4 so Phase 7's PR body and Phase 8's RESULT block never emit an empty value.
Phase 5 — PII Scrub
Read references/pii-scrubbing.md for the full procedure. Summary:
The scrub has three layers, each operating on temp staging copies (NOT on the user's session transcript or the in-progress source code):
1. Credentials — reuse the regex patterns from skills/printing-press-retro/references/secret-scrubbing.md (Stripe, GitHub PATs, bearer tokens, AWS keys, etc.) plus amend-specific additions for Authorization/Cookie/X-API-Key headers in hand-rolled API payloads quoted from the session transcript. 2. Entities — companies, people, emails matched against the user-maintained stop-list at ~/.printing-press/amend-config.yaml. Replace with shape-preserving tokens (<company-1>, <person-1>, <email-1>) that maintain identity across the artifact set so reviewers can still parse intent. 3. First-mention defense — walk each artifact for capitalized phrases that look like proper nouns and were NOT in the stop-list. Surface to the user inline before the Phase 6 PR-draft display: "Found Esper Labs (3x in plan doc, 1x in PR body) — add to stop-list and scrub, or accept?"
Targets, in priority order: PR title/body draft, per-run plan doc, deferred-findings list, any test fixtures or example outputs newly added to $CLI_DIR. For each target, copy to <path>.pre-pii-scrub BEFORE scrubbing so the user can audit what was changed.
Defense-in-depth: walk every *.go file in $CLI_DIR for stop-list matches. If any match is found, treat as BLOCKING — pause and require user resolution before Phase 6. The agent should never have introduced PII into Go source; this check exists to catch agent error.
Stop-list creation: if ~/.printing-press/amend-config.yaml doesn't exist, the skill creates a default with a starter list and a comment explaining the format. File-mode validation (warn on world-writable, abort on alien-owned).
The scrub report is written to $PRESS_MANUSCRIPTS/<slug>/<run-id>/scrub-report.json (NOT committed; for the user's audit). The user-facing summary at the end of the phase: "X tokens replaced across Y artifacts."
Phase 6 — PR Draft Review Checkpoint (User-in-Loop #2)
This is the second and final user checkpoint. Everything that follows is unattended (push + PR-open + labels + RESULT block). Show the user EVERYTHING that's about to ship before any gh command fires.
Assemble the draft
Compose the PR title, body, labels, and diff summary in memory. Title format follows the public library convention:
fix(<api-slug>): <one-line summary>when the scope is bugs-onlyfeat(<api-slug>): <one-line summary>when the scope includes featuresfeat(<api-slug>): <one-line summary>when mixed (feature wins because it's the bigger contract change)
The <one-line summary> is composed from the most important 1-3 findings (e.g. surface refresh-token expiry; add drafts new + --type sent).
PR body sections (per origin R27):
1. Summary — 1-3 sentences naming the user pain and the shape of the fix 2. Findings — table with ID, category, type (bug/feature), rationale 3. Changes — output of git diff --stat upstream/main..HEAD 4. Verification — build/test/dogfood/validate status from Phase 4 5. Evidence — full GitHub URLs to the per-run plan doc and the .printing-press-patches/ directory at the PR's HEAD SHA (captured AFTER push so links don't 404) 6. Closes #N footer when an issue match was found in Step 6 of library-pr-plumbing.md
Labels: comp:<api-slug> always; priority:P1 for bugs-only scope, priority:P2 for bugs+features, priority:P3 for all-tiers.
Display before gh fires
Show the user the title, body, label list, and git diff --stat. If Phase 5 surfaced unrecognized capitalized phrases that the user accepted as legitimate, RE-DISPLAY those inline now with the sentence each appears in:
"Reminder: PR body references <phrase> (you accepted as legitimate during Phase 5). Confirm before opening."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 keptFor edit then open: present the title and body as separate editable blocks, accept the user's revisions, re-display the full draft, confirm before proceeding.
For hold: save the plan + diff to $PRESS_MANUSCRIPTS/<slug>/<run-id>/proofs/<timestamp>-amend-<cli-name>-HELD.md and ${path%.md}.diff. Emit a RESULT block with status: held and the resume path. A future /printing-press-amend run can detect held files and offer to resume.
For abort: emit a brief confirmation. Plan doc from U5 stays (with status: aborted written into the frontmatter) so the user has a record of what was found, but nothing else is preserved. Managed clone is reset on next run.
Phase 7 — PR Open (Autonomous)
If the user picked open or edit-then-open, run references/library-pr-plumbing.md Steps 5-7:
1. Step 5 — git add "$CLI_DIR" + commit with conventional message + the findings list 2. Step 6 — search for an existing issue matching the findings; link or open new; self-assign best-effort 3. Step 7 — push the branch (push-vs-fork access mode determined in Step 1), gh pr create with --body-file, capture HEAD_SHA, apply labels
The fork/access detection, branch collision handling, and managed-clone refresh patterns are documented in detail in references/library-pr-plumbing.md. Do NOT inline those patterns here — the reference is the authoritative source.
After the PR opens, surface the URL + Greptile note in the user-facing summary:
"PR open: <url>
>
Greptile will review within ~2 minutes. Check inline comments:
>
gh api repos/mvanhorn/printing-press-library/pulls/<N>/comments
>
P0/P1 findings are worth addressing before requesting human review."
Phase 8 — Output
Emit the structured ---PATCH-RESULT--- block on completion. Format:
---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---Verification of this skill itself
The static lint pass for this SKILL.md runs via:
<PRINTING_PRESS_BIN> verify-internal-skill --dir skills/printing-press-amend(See internal/cli/verify_internal_skill.go and the matching test file. The setup-contract parity check runs as a Go test in internal/pipeline/contracts_test.go — TestSkillSetupBlocksMatchWorkspaceContract.)
Direct-Input Parsing — Ask Capture for /printing-press-amend
Scope: This reference applies when MODE=direct (Phase 0 detected user-supplied asks in the slash-command prompt) or when running the direct-input half of MODE=both. For session-friction mode (MODE=dogfood), see transcript-parsing.md instead — that mode walks a transcript and never reads the prompt body.
This reference is loaded by Phase 1's ### 1b. Direct-input mode sub-section of printing-press-amend. It defines how the agent parses the user's verbatim asks in the slash-command invocation and converts them to the typed finding list that Phase 2 consumes.
Input
The agent reads two sources:
1. The slash-command prompt body — everything the user typed after /printing-press-amend ... in the invocation that fired this skill. This is the primary signal. 2. The immediate agent-message turn — the user's prior turn that fired the skill (when applicable). Sometimes the user names asks in conversational context just before invoking the skill; that context is in-scope here.
Do NOT read the conversation transcript beyond the immediate invocation turn — that's MODE=dogfood behavior. Direct-input mode trusts the user's explicit prompt and does not infer asks from earlier conversational drift.
Parsing rubric — verbs to finding kinds
Map each ask in the prompt to one finding using the following rubric. When a single prompt contains multiple asks (which is the common case), produce one finding per ask.
| User phrasing | kind | classification | Notes |
|---|---|---|---|
| "rename X to Y", "call it X instead of Y", "should be named X not Y" | rename | feature | Renaming a command, subcommand, flag, or output label. Capture both the old and new names in evidence. |
| "add command X", "add subcommand X", "add a Y subcommand" | add-command | feature | New top-level or nested Cobra command. |
| "add feed <url>", "add these feeds: <url>, <url>", enumerated URLs | add-feed | feature | One finding per URL. evidence carries the full URL. |
| "add endpoint <path>", "add the /v1/foo endpoint", explicit API path | add-endpoint | feature | Hand-named endpoint to wrap. evidence carries the path. |
| "fix X", "X is broken", "X returns null", "X errors out", "broken: X" | fix-bug | bug | Behavior is wrong in the published CLI. |
| "sniff for new APIs", "find new endpoints", "discover more", "what else is there in <site>" | sniff | feature | Triggers the sniff subroutine (### 1b.i); produces zero-to-many add-endpoint findings with provenance: sniff. |
When a phrase fits multiple kinds (e.g., "add the X feed" — add-feed or add-command?), prefer the more specific kind based on context: a URL → add-feed; a noun like "command" or "subcommand" → add-command; an API path with a method → add-endpoint.
Finding shape
Each finding emitted by 1b carries the same fields as 1a findings, with one new field (provenance):
- id: F<n> # F1, F2, ... — continues numbering when MODE=both
kind: <rename|add-command|add-feed|add-endpoint|fix-bug>
category: <free-text categorical label, e.g. "command-rename", "feed-add">
classification: <bug|feature>
evidence: "<verbatim user phrasing>"
target_cli: <slug>-pp-cli
rationale: "<one-line agent summary of what this finding means>"
provenance: user-ask # or "sniff" for sniff-derived findingsThe evidence field carries the user's verbatim phrasing — not the agent's paraphrase — so the Phase 3 scope-confirmation modal shows the user exactly what they wrote. This makes mis-classification recoverable: the user sees their own words and can correct the agent's tier or kind at the U4 modal.
Target-CLI resolution
When the user names the CLI inside the prompt, extract it via regex (in order):
1. <slug>-pp-cli literal (e.g., digg-pp-cli) 2. the <slug> CLI or the <slug> cli (e.g., the digg CLI → digg-pp-cli) 3. for <slug> when followed by an ask verb (e.g., for digg, add feed ...) 4. <slug> alone when the prompt has only one short-name candidate
If no slug is named anywhere in the prompt, fall back to Phase 0 auto-detection: list recently-touched <slug>-pp-cli invocations in the immediate invocation turn, propose the most-touched, and confirm with AskUserQuestion. If even auto-detect can't resolve, ask the user.
Once resolved, accept any of the three forms (short name, full name, path) per origin R4 — the resolution rules are identical to 1a step 4-5.
Edge cases
Multi-CLI asks — Out of scope for v0.2. When the prompt names two or more distinct CLIs ("amend foo-pp-cli and bar-pp-cli"), ask the user to pick one and re-invoke for the other:
"This prompt names multiple CLIs (foo-pp-cli, bar-pp-cli). v0.2 amend handles one CLI per run. Which one should I scope to first?"
Ambiguous verbs — "update X", "improve X", "make X better" without further specifics trigger an AskUserQuestion clarification rather than a guess. Offer two to three concrete kind options based on the surrounding context.
Bare URLs without context — A URL in the prompt with no surrounding verb ("https://example.com/feed/x" alone) triggers a clarifying ask: is this a feed to add, an endpoint to wrap, or a sniff target?
Conflicting kinds in one ask — "rename X to Y AND fix the bug in Y" splits into two findings: one rename, one fix-bug. Findings stay atomic; don't merge them into one mixed-kind entry.
Combined-mode merging (MODE=both) — When 1b runs after 1a, 1b's finding IDs continue numbering from where 1a left off (1a emits F1..Fn; 1b emits F(n+1)..). Findings keep their own provenance regardless of which sub-section produced them. The Phase 3 modal groups by tier, not by mode — the user sees one merged list.
Output
1b emits the same structured finding list shape as 1a. Phase 2 consumes the list without branching on provenance. Sniff findings (when present) are produced by the ### 1b.i subroutine and appended to this same list before handoff.
Library PR Plumbing for /printing-press-amend
This reference is loaded by Phase 6 and Phase 7. It carries the fork → managed-clone → branch → commit → push → PR-create patterns adapted from /printing-press-publish Steps 5, 7, and 8.
Drift advisory: /printing-press-publish carries the canonical inline version of these patterns. This file is a copy adapted for amend's use case (existing-CLI patches, not new-CLI publishes). When publish's plumbing changes, this file may drift. A follow-up retro item will extract shared helpers into scripts/; until then, audit both surfaces together.
The setup contract environment variables (PRESS_HOME, PRESS_SCOPE, etc.) are already exported by the time this reference runs — see the SKILL.md's setup contract block.
---
Step 1 — Resolve managed clone access mode
The managed clone lives at $PRESS_HOME/.publish-repo-$PRESS_SCOPE. The auxiliary config at $PRESS_HOME/.publish-config-$PRESS_SCOPE.json caches the access mode so detection runs once per scope.
PUBLISH_REPO_DIR="$PRESS_HOME/.publish-repo-$PRESS_SCOPE"
PUBLISH_CONFIG="$PRESS_HOME/.publish-config-$PRESS_SCOPE.json"
# Read cached config if present
if [ -f "$PUBLISH_CONFIG" ]; then
managed_by=$(jq -r '.managed_by // empty' "$PUBLISH_CONFIG")
access=$(jq -r '.access // empty' "$PUBLISH_CONFIG") # "push" or "fork"
gh_user=$(jq -r '.gh_user // empty' "$PUBLISH_CONFIG")
protocol=$(jq -r '.protocol // empty' "$PUBLISH_CONFIG") # "ssh" or "https"
fi
# Resolve when missing
if [ -z "$access" ]; then
gh_user=$(gh api user --jq .login)
push_perm=$(gh api repos/mvanhorn/printing-press-library --jq .permissions.push 2>/dev/null || echo false)
if [ "$push_perm" = "true" ]; then
access="push"
else
access="fork"
fi
# Protocol: prefer SSH if user has it set up
if ssh -T git@github.com 2>&1 | grep -q "successfully authenticated"; then
protocol="ssh"
else
protocol="https"
fi
managed_by="amend"
jq -n --arg by "$managed_by" --arg a "$access" --arg u "$gh_user" --arg p "$protocol" \
--arg cp "$PUBLISH_REPO_DIR" --arg sd "$_scope_dir" \
'{managed_by: $by, access: $a, gh_user: $u, protocol: $p, clone_path: $cp, scope_dir: $sd}' \
> "$PUBLISH_CONFIG"
fiReference: publish SKILL.md Step 5 (lines 244-397).
---
Step 2 — Bootstrap or refresh the managed clone
if [ ! -d "$PUBLISH_REPO_DIR/.git" ]; then
# First-time setup
if [ "$access" = "push" ]; then
if [ "$protocol" = "ssh" ]; then
git clone git@github.com:mvanhorn/printing-press-library.git "$PUBLISH_REPO_DIR"
else
git clone https://github.com/mvanhorn/printing-press-library.git "$PUBLISH_REPO_DIR"
fi
cd "$PUBLISH_REPO_DIR"
git remote add upstream git@github.com:mvanhorn/printing-press-library.git 2>/dev/null || true
else
# Fork-based: ensure user fork exists, clone fork, set upstream
gh repo fork mvanhorn/printing-press-library --clone=false --remote=false 2>/dev/null || true
if [ "$protocol" = "ssh" ]; then
git clone "git@github.com:$gh_user/printing-press-library.git" "$PUBLISH_REPO_DIR"
else
git clone "https://github.com/$gh_user/printing-press-library.git" "$PUBLISH_REPO_DIR"
fi
cd "$PUBLISH_REPO_DIR"
git remote add upstream "https://github.com/mvanhorn/printing-press-library.git"
fi
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, leaving the clone permanently stuck on an amend
# branch with conflicting state.
cd "$PUBLISH_REPO_DIR"
git fetch upstream main
git checkout -f main
git reset --hard upstream/main
fiThe reset-hard + force-checkout on refresh is intentional: the managed clone is treated as a scratch surface, never as long-term local-state storage. Any local edits in it are by definition leftover state from an aborted run and must be discarded before the next run reuses the clone.
---
Step 3 — Resolve target CLI directory inside the clone
# Category was resolved in Phase 1; if missing, look it up by walking
if [ -z "$category" ]; then
category=$(find "$PUBLISH_REPO_DIR/library" -maxdepth 2 -name "$slug" -type d \
| head -1 | awk -F/ '{print $(NF-1)}')
fi
CLI_DIR="$PUBLISH_REPO_DIR/library/$category/$slug"
[ -d "$CLI_DIR" ] || { echo "ERROR: target CLI dir not found: $CLI_DIR"; exit 1; }This is what U5 edits.
---
Step 4 — Branch creation with collision detection
SHORT_SUMMARY=$(echo "$pr_title" | sed -E 's/^(feat|fix)\([^)]+\):\s*//' | tr '[:upper:] ' '[:lower:]-' | sed -E 's/[^a-z0-9-]//g; s/-+/-/g; s/^-//; s/-$//' | cut -c1-40)
BRANCH_NAME="amend/$slug-$SHORT_SUMMARY"
# Check for existing branch (open PR, own merged branch zombie, or fresh)
existing_open=$(gh pr list --repo mvanhorn/printing-press-library \
--head "$gh_user:$BRANCH_NAME" --state open --limit 1 --json number,title)
existing_local=$(git branch --list "$BRANCH_NAME" | wc -l)
if [ "$(echo "$existing_open" | jq 'length')" -gt 0 ]; then
# Open PR exists from this branch — surface to user and stop the shell flow.
# The calling skill must then resolve the conflict via AskUserQuestion
# (amend the existing PR by pushing to the same branch, or open new with
# a timestamped branch) before re-entering this snippet with the chosen path.
echo "ERROR: open PR already exists from $BRANCH_NAME:"
echo "$existing_open" | jq -r '.[0] | " PR #\(.number): \(.title)"'
echo ""
echo "Resolve before continuing:"
echo " 1. Amend the existing PR — push to $BRANCH_NAME (skip this snippet's checkout)"
echo " 2. Open a new PR — re-run with a timestamp suffix on the branch name"
exit 1
fi
if [ "$existing_local" -gt 0 ]; then
# Local zombie from prior run — timestamp to avoid clobber
TIMESTAMP=$(date -u +%Y-%m-%dT%H%M)
BRANCH_NAME="amend/$slug-$SHORT_SUMMARY-$TIMESTAMP"
fi
git checkout -b "$BRANCH_NAME"When the skill driver sees a non-zero exit from this block, it must invoke AskUserQuestion to surface the two-option choice (amend the existing PR vs. open a new timestamped one), then re-enter the snippet with the chosen path. The hard exit 1 exists so a literal shell-flow execution stops here rather than failing later with a confusing git checkout -b error when the local branch already exists.
Reference: publish SKILL.md Step 7 (lines 488-650) for the full collision matrix (open PR + own merged + zombie + branch-timestamping).
---
Step 5 — Commit
# Stage every file changed in $CLI_DIR (the validate step ensured no off-target changes)
git add "$CLI_DIR"
# Conventional commit message
git commit -m "$(cat <<EOF
$pr_title
$pr_summary
Findings addressed:
$(echo "$findings_active" | jq -r '.[] | "- \(.id): \(.category) — \(.rationale)"')
amend run: $amend_run_id
EOF
)"The PR title is composed in Phase 6's draft assembly (e.g. fix(superhuman): surface refresh-token expiry; add drafts new + --type sent).
---
Step 6 — Issue ownership
Per ~/printing-press-library/AGENTS.md, contributors search for an existing issue before opening a PR:
issue_match=$(gh issue list --repo mvanhorn/printing-press-library \
--search "$slug $primary_keyword" --state open --limit 5 \
--json number,title,labels)
if [ "$(echo "$issue_match" | jq 'length')" -gt 0 ]; then
# Surface candidates; ask user to pick one or open new
# ...
ISSUE_NUM=<chosen number>
PR_BODY_FOOTER="Closes #$ISSUE_NUM"
else
# Open a new issue first
ISSUE_NUM=$(gh issue create --repo mvanhorn/printing-press-library \
--title "$pr_title" \
--body "$(cat <<EOF
Captured during a /printing-press-amend run on $slug-pp-cli.
Findings:
$(echo "$findings_active" | jq -r '.[] | "- \(.id) (\(.classification)): \(.rationale)"')
PR with the proposed fix follows.
EOF
)" --label "comp:$slug" \
| grep -oE '[0-9]+$')
PR_BODY_FOOTER="Closes #$ISSUE_NUM"
fi
# Self-assign the issue (best-effort; permissions may block)
gh issue edit "$ISSUE_NUM" --repo mvanhorn/printing-press-library --add-assignee "$gh_user" 2>/dev/null || true---
Step 7 — Push and PR-create
# Push the branch
if [ "$access" = "push" ]; then
git push origin "$BRANCH_NAME"
PR_HEAD="$BRANCH_NAME"
else
git push -u origin "$BRANCH_NAME"
PR_HEAD="$gh_user:$BRANCH_NAME"
fi
# Capture HEAD_SHA AFTER push so evidence URLs are durable
HEAD_SHA=$(git rev-parse HEAD)
# Compose PR body file
PR_BODY_PATH=$(mktemp -t amend-pr-body)
cat > "$PR_BODY_PATH" <<EOF
## Summary
$pr_summary
## Findings
| ID | Category | Type | Rationale |
|---|---|---|---|
$(echo "$findings_active" | jq -r '.[] | "| \(.id) | \(.category) | \(.classification) | \(.rationale) |"')
## Changes
$(git diff --stat upstream/main..HEAD)
## Verification
- Build: $build_status
- Tests: $test_status
- Dogfood: ${dogfood_status:-N/A}
- \`cli-printing-press publish validate\`: PASS (after $validate_iterations iteration(s))
- Patch contract: $patch_marker_count // PATCH() comments, .printing-press-patches.json updated
## Evidence
- Patch record: https://github.com/$gh_user/printing-press-library/blob/$HEAD_SHA/library/$category/$slug/.printing-press-patches.json
- Per-finding rationale: see the per-finding evidence captured in the patch record above and the Findings table earlier in this PR body
- Local plan doc (not in the PR — PII-scrubbed local artifact): \`\$PRESS_MANUSCRIPTS/$slug/<run-id>/proofs/<timestamp>-amend-$slug.md\` (path provided here for the original printer's reference; the artifact stays local by design)
$PR_BODY_FOOTER
EOF
# Open the PR
PR_URL=$(gh pr create \
--repo mvanhorn/printing-press-library \
--head "$PR_HEAD" \
--base main \
--title "$pr_title" \
--body-file "$PR_BODY_PATH")
PR_NUMBER=$(echo "$PR_URL" | grep -oE '[0-9]+$')
# Apply labels
gh pr edit "$PR_NUMBER" --repo mvanhorn/printing-press-library \
--add-label "comp:$slug" \
--add-label "priority:P${scope_priority}" 2>/dev/null || trueReference: publish SKILL.md Step 8 (lines 671-925).
---
Step 8 — Greptile awareness (informational)
Every PR opened against mvanhorn/printing-press-library receives a Greptile auto-review. The skill does NOT auto-fix Greptile findings (deferred to v0.2). Tell the user in the final summary:
"Greptile will review your PR within ~2 minutes. Check inline comments via:
>
gh api repos/mvanhorn/printing-press-library/pulls/$PR_NUMBER/comments
>
...or in the GitHub UI. P0/P1 findings are worth addressing before requesting human review."
---
Cleanup
The managed clone stays in place for the next amend run on this scope (refresh-from-upstream on next bootstrap). Nothing to clean up.
If the user explicitly aborts mid-run, leave the clone in whatever state it's in — the next run's reset-hard will restore it.
PII Scrubbing for /printing-press-amend
This reference is loaded by Phase 5. The scrub mechanism has two layers — credentials (regex patterns, reused from /printing-press-retro) and entities (user-maintained stop-list, specific to amend).
The scrub operates on temp staging copies of the artifacts that will leave the local machine — never on the user's original session transcript or the in-progress source code in the managed clone. Source code in $CLI_DIR is presumed PII-free by the agent's own restraint (the agent should never have introduced PII into Go source); this is verified as a defense-in-depth check, not as a primary control.
What gets scrubbed
In priority order (highest leak risk first):
1. The PR title and body draft (composed in Phase 6, scrubbed before display in Phase 6's checkpoint) 2. The per-run plan doc body at $PRESS_MANUSCRIPTS/<slug>/<run-id>/proofs/<timestamp>-amend-<cli-name>.md — this is the artifact reviewers may follow links to from the PR body's Evidence section 3. The deferred-findings list at $PRESS_MANUSCRIPTS/<slug>/<run-id>/proofs/<timestamp>-amend-<cli-name>-deferred.md — same audience 4. Any test fixtures or example outputs newly added to $CLI_DIR (defense-in-depth — the agent should not have added PII here, but verify)
For each target, copy to <path>.pre-pii-scrub BEFORE scrubbing so the user can audit what changed.
Layer 1: Credentials (reuse retro patterns)
Run the credential-pattern scan from skills/printing-press-retro/references/secret-scrubbing.md — it covers Stripe keys, GitHub PATs/OAuth, bearer tokens, generic API keys, AWS access keys, etc. Point at the same regex set; do not duplicate the patterns here so both skills evolve together.
For Phase 5's purposes, the credential scan's redaction tags (<REDACTED:bearer-token>, etc.) become the shape-preserving tokens for credential entities. Same surface, same tag.
Critical addition for amend that retro's patterns don't cover by default:
Authorization: Bearer ...headers in hand-rolled API payloads quoted from the session transcript. Retro'sbearer-tokenpattern catches the value but only when the prefix is exactlyBearer— verify amend's evidence quotes also normalize to that shape, OR add a broaderAuthorization: <scheme> <opaque>regex specific to amend.Cookie: ...headers from session-replay payloads. These often contain session IDs that uniquely identify the user even if not technically secret.X-API-Key:and similar header-based auth shapes.
Layer 2: Entities (companies, persons, custom stop-list)
Read the user's stop-list at ~/.printing-press/amend-config.yaml. If the file doesn't exist, create a default with a starter list and a comment explaining the format:
# ~/.printing-press/amend-config.yaml
# User-maintained stop-list for /printing-press-amend's PII scrub.
# Add company and person names that should be replaced with shape-preserving
# tokens before any artifact leaves the local machine.
stoplist:
companies:
# - "Esper Labs"
# - "Acme Corp"
people:
# - "Matt Van Horn"
# - "Trevin Chow"
emails:
# Domain-level scrubbing — any email at this domain becomes <email-N>
# - "esperlabs.ai"
# - "company.com"
# Behavior knobs
behavior:
# When true, also flag capitalized non-stop-listed strings for user review
# before the PR draft is shown (defense against first-mention leaks).
prompt_unrecognized_capitalized: trueWhen the file exists, read it. Validate file mode (warn if world-writable; abort if owned by another user — symlink attack surface).
For each artifact, replace stop-listed values with shape-preserving tokens:
| Original shape | Token |
|---|---|
| Company name | <company-1>, <company-2>, ... |
| Person name | <person-1>, <person-2>, ... |
| Email address | <email-1>, <email-2>, ... |
Same source value gets the same token across the run; distinct sources get distinct tokens. Track the mapping in a per-run scrub report (NOT committed; written to $PRESS_MANUSCRIPTS/<slug>/<run-id>/scrub-report.json for the user's audit).
Layer 3: Defense against first-mention leaks
The stop-list is by definition incomplete on first encounter with a new entity. To catch this:
1. After Layers 1+2, walk each artifact again and find capitalized multi-word phrases that look like proper nouns (e.g. matches \b[A-Z][a-z]+ [A-Z][a-z]+\b) and were NOT replaced by Layer 2. 2. Filter out a known-safe allowlist (GitHub, Slack, Linear, Claude, Anthropic, common HTTP-shape words, the API/CLI vendor name, etc.). 3. Surface remaining candidates inline before the Phase 6 PR-draft display:
"Found unrecognized capitalized phrase: Esper Labs (appears 3 times in plan doc, 1 time in PR body draft). Add to stop-list and scrub, or accept as legitimate?"Options: scrub (add to stop-list), accept (don't scrub), show context (display surrounding lines).
This isn't perfect — uncapitalized PII (lowercase email handles, internal codenames) still slips through — but it catches the most common failure mode without forcing the user to maintain an exhaustive stop-list.
Defense-in-depth: Go source check
Walk every *.go file in $CLI_DIR and scan for stop-list matches. If any match is found, treat as a hard error:
"BLOCKING: PII pattern matched Go source at<file>:<line>. The skill should not have introduced PII into Go source — please review the diff in$CLI_DIR/<file>and resolve before continuing. The patch will not proceed to Phase 6 until this clears."
Do NOT auto-modify Go source. Pause and require user resolution.
Output
Phase 5 emits to Phase 6:
scrub_report_path: $PRESS_MANUSCRIPTS/<slug>/<run-id>/scrub-report.json
artifacts_scrubbed:
- path: <plan-doc-path>
tokens_replaced: 7
backup: <plan-doc-path>.pre-pii-scrub
- ...
unrecognized_phrases: [] # may be present if user accepted some
go_source_check: clean # or [list of blocking matches]If go_source_check is non-empty, Phase 6 cannot proceed.
Transcript Parsing — Friction Capture for /printing-press-amend
Scope: This reference applies when MODE=dogfood (Phase 0 detected a session-friction invocation) or when running the dogfood half of MODE=both. For direct-input mode (MODE=direct), see direct-input-parsing.md instead — that mode parses the user's prompt and never touches the transcript.
This reference is loaded by Phase 1's ### 1a. Dogfood mode sub-section of printing-press-amend. It defines how the agent reads a Claude Code session transcript and extracts friction signals tied to a specific printed CLI invocation.
Where the active session transcript lives
Claude Code stores per-session transcripts as JSONL files under ~/.claude/projects/<project-dir-slug>/<session-uuid>.jsonl. The slug is derived from the working directory path (slashes replaced with -).
Resolution order (use the first that resolves to a readable file):
1. Skill argument or environment — if the user passed an explicit transcript path, use it. 2. Active session via working dir — derive <project-dir-slug> from pwd -P (replace / with -, strip leading -), then list ~/.claude/projects/<slug>/*.jsonl and pick the most-recently-modified. This is the heuristic; it can be wrong when multiple Claude Code panes are running in the same dir, so confirm with the user. 3. Fallback — list ~/.claude/projects/ directories sorted by mtime, list each one's *.jsonl files, pick the most-recently-modified across all. This catches the case where the user invokes /printing-press-amend from a different working directory than the session was started in.
After picking a candidate file, ALWAYS show the user the resolved path with AskUserQuestion:
"Detected active session at <path> (modified <relative-time>). Mine this session for friction, or pick a different transcript?"Options:
- Use this transcript (recommended)
- Pick a different file (drops into a list of recent JSONL files under
~/.claude/projects/) - Cancel
The confirmation is non-optional — wrong-file selection ingests friction from the wrong session and ships PRs for bugs the user never hit.
Signal extraction taxonomy
The transcript is line-delimited JSON; each line is a turn in the conversation. Walk the file and extract these signal categories. Each signal carries: timestamp (from the turn), category (one of below), evidence (the verbatim quote that triggered it), and target_cli (when the signal references a specific <slug>-pp-cli invocation).
| Category | Signal | Bug or feature? |
|---|---|---|
| Non-zero exit code | A tool_result block whose stderr indicates a non-zero exit on a <cli>-pp-cli invocation | Bug |
| Error message | Lines like Error: ..., failed:, panic:, HTTP 4xx, HTTP 5xx returned from the CLI | Bug (usually) |
| Hand-rolled API payload | Bash commands that POST/PUT directly to a URL (e.g. curl -X POST https://api.example.com/... or scripted JSON construction) instead of calling the CLI | Feature (the CLI doesn't expose what the user needed) |
| Retry-after-failure | The same command run ≥ 2 times in a row with similar args, separated by manual edits or tool tweaks | Bug or feature (look at what changed) |
| Hand-rolled workaround comment | Agent prose saying "X doesn't exist", "X returns 400", "I had to manually...", "going around the CLI", "no built-in for ..." | Feature when "doesn't exist", bug when "returns wrong" |
| Missing-flag reference | Agent text mentioning a flag it tried that the CLI didn't accept, e.g. "tried --type sent but it's rejected" | Feature (missing flag) |
| Silent-null returns | A CLI returns data: null or empty JSON when the user clearly expected content; agent commentary acknowledges the unexpected emptiness | Bug |
| Auth confusion | Agent text mentioning expired tokens, refresh failures, "need to re-auth", confusing auth status output | Bug (poor error surfacing) |
Bug vs feature classification rubric
For each signal, choose bug or feature with a one-line rationale:
- Bug = the CLI behavior is wrong given what the CLI claims to do (broken endpoint, wrong return shape, error masked, contradicting
--help). - Feature = the CLI behavior is missing — the user wanted to do something the CLI doesn't expose.
When the same signal could be either (e.g. silent-null), prefer feature if the workaround was "construct the API call yourself" and bug if the workaround was "retry with different args".
The classification is the agent's best read; the user confirms or overrides at the U4 scope checkpoint.
Auto-detect target CLI
After extracting signals, count occurrences of each <slug>-pp-cli mentioned. The most-touched CLI is the proposed default target. When ties exist or the top two are close (within 1 mention of each other), present a small AskUserQuestion with the candidates:
"Which CLI is this patch for?"
>
1. <slug-A>-pp-cli (8 friction signals)2. <slug-B>-pp-cli (7 friction signals)3. Other (paste a CLI name)
When only one CLI was touched, default to it but still confirm:
"Detected target: <slug>-pp-cli (12 friction signals). Proceed?"When the user explicitly passed a target as the skill argument, skip auto-detect entirely and use what they passed.
Path resolution for the chosen target
Accept any of:
- short name:
superhuman-> slugsuperhuman - full name:
superhuman-pp-cli-> strip-pp-cli, slugsuperhuman - absolute path:
$PRESS_LIBRARY/superhuman-> basename slugsuperhuman
After normalizing the slug, resolve publish status by slug lookup in the public library before consulting local working-copy state:
1. If a local public-library clone exists, search ~/printing-press-library/library/*/<slug> for a matching directory. 2. If that clone is absent or stale enough to miss the slug, query GitHub for the same public-library path. First enumerate top-level categories with gh api repos/mvanhorn/printing-press-library/contents/library --jq '.[] | select(.type == "dir") | .name', then iterate those category names with gh api repos/mvanhorn/printing-press-library/contents/library/<category>/<slug> until the slug is found or every category has been checked. 3. If the slug is found, set published_status: published, set target_category from the matched parent directory, and route all edits through the managed clone opened later in the amend flow. 4. Only set published_status: local-only when the slug is absent from the public library.
Do not infer publish status from the local CLI working copy's git remotes. Printed-library CLIs can intentionally have no origin, and a remote-less local checkout may still correspond to a published CLI. Likewise, do not treat a missing $PRESS_LIBRARY/<slug> working copy as unpublished; the local-library path is only informational once the public-library slug lookup succeeds. The skill operates on the managed clone (per the Pre-Implementation Decisions in the plan), so the actual edits land in the managed clone created in U7.
Edge cases
- Empty / unreadable transcript — emit "no active session transcript found at
<path>; pass an explicit<cli-name-or-path>argument and re-run, or use--transcript <path>to point at a saved session" and exit cleanly. - Transcript with zero `<slug>-pp-cli` invocations — emit "no
<slug>-pp-cliinvocations found in this session; if you dogfooded a CLI in a different session, point me at that transcript" and exit. - Transcript references a CLI that's not in the public library by slug — emit a warning and ask the user to confirm; the CLI may be local-only (pre-publish) or under a different slug. This warning must be based on the public-library slug lookup, not on local git remotes or
$PRESS_LIBRARY/<slug>presence. - Signal extraction returns < 2 candidates — proceed but note in the user-facing summary that the signal yield was low; the user may want to resume after more dogfooding.
Output shape
Phase 1 emits a structured finding list to the next phase:
target_cli: superhuman-pp-cli
target_dir: $PRESS_LIBRARY/superhuman # may be informational; managed-clone path resolved later
target_category: productivity # resolved from the public library, used by U7
published_status: published
findings:
- id: F1
category: missing-folder-coverage
classification: feature
rationale: "User tried --type sent but only inbox/draft/etc. allowed"
evidence: "threads list --type sent → Error: invalid value for --type"
- id: F2
category: hand-rolled-payload
classification: feature
rationale: "drafts new doesn't exist; user POST'd userdata.writeMessage directly"
evidence: "curl ... -d '{\"messageId\": ..., \"writes\": [...]}'"
- ...This list flows into U3 (cross-reference + stale-binary) and then U4 (scope confirmation).
Related skills
How it compares
Pick printing-press-amend over generic prompt parsers when integrating with Printing Press Phase 1/2 pipelines and MODE-specific input handling.
FAQ
What is the difference between amend and cli-printing-press patch?
Amend produces git PR patches tracked in .printing-press-patches; the binary patch subcommand AST-injects predefined features.
When does the skill ask the user to choose input mode?
When detection is ambiguous between mining the session transcript and using typed direct asks.
What binary version is required?
min-binary-version 4.0.0 in skill frontmatter, with an additional currency floor from supported-versions.txt.
Is Printing Press Amend safe to install?
skills.sh reports 2 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.