← back to Cli Printing Press
feat(cli): credit original printer in per-CLI README (closes #745) (#748)
ee8f6ad99ce0dd4b6400785a69b014a508534862 · 2026-05-08 14:59:06 -0700 · Matt Van Horn
* feat(cli): capture printer @handle and display name through manifest chain
Adds Printer (GitHub @handle) and PrinterName (display) fields that flow
generator -> .printing-press.json -> publish-side rewrite. First print
wins: the resolver tier reads existing manifest before git config so
regens by other contributors do not overwrite the original printer.
Distinct from Owner/OwnerName (which carry the API-spec / wrapper-author
identity); Printer is the human who ran the press.
When git config github.user is unset, Generate() warns and falls back to
the slug-shaped Owner so emission stays non-empty (mirrors the existing
OwnerName empty-fallback). PrinterName has no slug fallback because the
README byline parenthetical is rendered conditionally.
Wires APISpec, CLIManifest, GenerateManifestParams, WriteManifestForGenerate,
writeCLIManifestForPublish carry-forward, and the standalone generate
command's two call sites in cmd/cli/root.go.
* feat(cli): render Printed by byline in per-CLI README between title and Install
Adds a conditional template block in readme.md.tmpl that emits
`Printed by [@handle](https://github.com/handle) (Display Name).`
when spec.Printer is set. Wrapped in {{if .Printer}} so older prints
without the field render unchanged.
Updates goldens for 6 fixtures whose harness env (scripts/golden.sh)
sets GIT_CONFIG_KEY_0=github.user and GIT_CONFIG_KEY_1=user.name to the
deterministic golden_owner slug, exercising the new byline + manifest
fields. Manifest gains `printer` and `printer_name`; README gains the
byline. No other changes.
* docs(cli): document Printer/PrinterName parallel pair and add publish-time strict check
AGENTS.md gains a parallel paragraph to the existing Owner/OwnerName
disambiguation, naming Printer/PrinterName as the second slug-vs-display
pair and explaining its distinct semantics (printer is the human, owner
is the API vendor/wrapper-author). Reinforces the no-"USER"-sentinel
rule and points at the publish-side gate.
skills/printing-press-publish/SKILL.md Step 6 packaging gains a strict
check on the freshly-copied .printing-press.json: refuses to publish
when .printer is empty, equals the literal "USER"/"user" sentinel, or
equals .owner (slug-fallback case). The generator's stderr warning at
print time is advisory; the publish step is the enforcing gate so
broken attribution rows cannot reach the library catalog.
* fix(cli): avoid printer owner fallback false positives
---------
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Trevin Chow <trevin@trevinchow.com>
Files touched
M AGENTS.mdM internal/cli/root.goM internal/generator/generator.goM internal/generator/plan_generate.goA internal/generator/printer_test.goM internal/generator/templates/readme.md.tmplM internal/pipeline/climanifest.goM internal/pipeline/publish.goM internal/spec/spec.goM skills/printing-press-publish/SKILL.mdM testdata/golden/expected/generate-golden-api-rich-auth/printing-press-rich-auth/README.mdM testdata/golden/expected/generate-golden-api-unicode/cafe-bistro/.printing-press.jsonM testdata/golden/expected/generate-golden-api/printing-press-golden/.printing-press.jsonM testdata/golden/expected/generate-golden-api/printing-press-golden/README.mdM testdata/golden/expected/generate-mcp-api/mcp-cloudflare/.printing-press.jsonM testdata/golden/expected/generate-public-param-names/public-param-golden/README.mdM testdata/golden/expected/generate-tier-routing-api/tier-routing-golden/.printing-press.json
Diff
commit ee8f6ad99ce0dd4b6400785a69b014a508534862
Author: Matt Van Horn <mvanhorn@users.noreply.github.com>
Date: Fri May 8 14:59:06 2026 -0700
feat(cli): credit original printer in per-CLI README (closes #745) (#748)
* feat(cli): capture printer @handle and display name through manifest chain
Adds Printer (GitHub @handle) and PrinterName (display) fields that flow
generator -> .printing-press.json -> publish-side rewrite. First print
wins: the resolver tier reads existing manifest before git config so
regens by other contributors do not overwrite the original printer.
Distinct from Owner/OwnerName (which carry the API-spec / wrapper-author
identity); Printer is the human who ran the press.
When git config github.user is unset, Generate() warns and falls back to
the slug-shaped Owner so emission stays non-empty (mirrors the existing
OwnerName empty-fallback). PrinterName has no slug fallback because the
README byline parenthetical is rendered conditionally.
Wires APISpec, CLIManifest, GenerateManifestParams, WriteManifestForGenerate,
writeCLIManifestForPublish carry-forward, and the standalone generate
command's two call sites in cmd/cli/root.go.
* feat(cli): render Printed by byline in per-CLI README between title and Install
Adds a conditional template block in readme.md.tmpl that emits
`Printed by [@handle](https://github.com/handle) (Display Name).`
when spec.Printer is set. Wrapped in {{if .Printer}} so older prints
without the field render unchanged.
Updates goldens for 6 fixtures whose harness env (scripts/golden.sh)
sets GIT_CONFIG_KEY_0=github.user and GIT_CONFIG_KEY_1=user.name to the
deterministic golden_owner slug, exercising the new byline + manifest
fields. Manifest gains `printer` and `printer_name`; README gains the
byline. No other changes.
* docs(cli): document Printer/PrinterName parallel pair and add publish-time strict check
AGENTS.md gains a parallel paragraph to the existing Owner/OwnerName
disambiguation, naming Printer/PrinterName as the second slug-vs-display
pair and explaining its distinct semantics (printer is the human, owner
is the API vendor/wrapper-author). Reinforces the no-"USER"-sentinel
rule and points at the publish-side gate.
skills/printing-press-publish/SKILL.md Step 6 packaging gains a strict
check on the freshly-copied .printing-press.json: refuses to publish
when .printer is empty, equals the literal "USER"/"user" sentinel, or
equals .owner (slug-fallback case). The generator's stderr warning at
print time is advisory; the publish step is the enforcing gate so
broken attribution rows cannot reach the library catalog.
* fix(cli): avoid printer owner fallback false positives
---------
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Trevin Chow <trevin@trevinchow.com>
---
AGENTS.md | 2 +
internal/cli/root.go | 4 +
internal/generator/generator.go | 16 ++++
internal/generator/plan_generate.go | 43 ++++++++++
internal/generator/printer_test.go | 97 ++++++++++++++++++++++
internal/generator/templates/readme.md.tmpl | 4 +
internal/pipeline/climanifest.go | 10 ++-
internal/pipeline/publish.go | 6 ++
internal/spec/spec.go | 2 +
skills/printing-press-publish/SKILL.md | 11 +++
.../printing-press-rich-auth/README.md | 2 +
.../cafe-bistro/.printing-press.json | 2 +
.../printing-press-golden/.printing-press.json | 2 +
.../printing-press-golden/README.md | 2 +
.../mcp-cloudflare/.printing-press.json | 2 +
.../public-param-golden/README.md | 2 +
.../tier-routing-golden/.printing-press.json | 2 +
17 files changed, 208 insertions(+), 1 deletion(-)
diff --git a/AGENTS.md b/AGENTS.md
index cf40f8ad..fddd527b 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -94,6 +94,8 @@ See [`docs/GLOSSARY.md`](docs/GLOSSARY.md) for the full term table and disambigu
**`Owner` (slug) vs `OwnerName` (display) — keep them straight.** `APISpec` carries two related but semantically distinct fields. `Owner` is path-safe (e.g. `trevin-chow`) and drives Go module paths and `// Copyright YYYY <slug>.` headers — sanitized in `New()`. `OwnerName` is prose-shaped (e.g. `Trevin Chow`) and flows into Hermes `author:`, README byline, and other human-facing surfaces — preserved verbatim, YAML-escaped at template emission. Resolution paths are deliberately different: `OwnerName` reads raw `git config user.name` only (no `github.user` fallback, no slug sanitization, no `"USER"` default). When `OwnerName` is unset at `Generate()` time, the generator emits a stderr warning and falls back to the slug — non-fatal so the package stays reusable by tests and `mcp-sync`/`regen-merge`. The library-wide sweep tool overrides this code path with its own per-CLI authorship mapping. Don't conflate the two fields when authoring helpers, templates, or test fixtures.
+**`Printer` (slug @handle) vs `PrinterName` (display) — parallel pair, distinct from Owner/OwnerName.** `APISpec` and `CLIManifest` carry a second slug-vs-display pair for printer attribution. `Printer` is the GitHub @handle of the human who originally ran the press (e.g. `mvanhorn`), drives the per-CLI README byline link and the library-side registry attribution, and reads `git config github.user`. `PrinterName` is the prose-shaped display name (e.g. `Matt Van Horn`), rendered as the README byline parenthetical, and reads raw `git config user.name`. Resolution is tiered for both: the existing manifest wins over git config, so regens by other contributors do not overwrite the original printer (mirrors `resolveOwnerForExisting`'s preserve-on-regen guarantee). When `Printer` is unset at `Generate()` time, the generator emits a stderr warning and leaves it empty; the publish skill (Step 6) refuses to publish empty or literal `"USER"`/`"user"` sentinel values. Distinct from `Owner`/`OwnerName` (which carry the API-spec / wrapper-author identity); the printer is the human, the owner is the API vendor or wrapper author.
+
## Commit Style
Format: `type(scope): description`. Both type and scope are required.
diff --git a/internal/cli/root.go b/internal/cli/root.go
index e7c029e7..f65ed773 100644
--- a/internal/cli/root.go
+++ b/internal/cli/root.go
@@ -170,6 +170,8 @@ func newGenerateCmd() *cobra.Command {
DocsURL: docsURL,
OutputDir: absOut,
Owner: parsed.Owner,
+ Printer: parsed.Printer,
+ PrinterName: parsed.PrinterName,
RunID: runID,
Spec: parsed,
NovelFeatures: novelFeatures,
@@ -332,6 +334,8 @@ func newGenerateCmd() *cobra.Command {
SpecURL: specURL,
OutputDir: absOut,
Owner: apiSpec.Owner,
+ Printer: apiSpec.Printer,
+ PrinterName: apiSpec.PrinterName,
RunID: runID,
Spec: apiSpec,
NovelFeatures: novelFeatures,
diff --git a/internal/generator/generator.go b/internal/generator/generator.go
index 65be8a1f..a6ae6f0b 100644
--- a/internal/generator/generator.go
+++ b/internal/generator/generator.go
@@ -177,6 +177,15 @@ func New(s *spec.APISpec, outputDir string) *Generator {
if s.OwnerName == "" {
s.OwnerName = resolveOwnerNameForExisting(outputDir)
}
+
+ // Preserve printer attribution from the manifest before consulting git config.
+ if s.Printer == "" {
+ s.Printer = resolvePrinterForExisting(outputDir)
+ }
+ // Preserve the prose-shaped printer display name when regenerating a CLI.
+ if s.PrinterName == "" {
+ s.PrinterName = resolvePrinterNameForExisting(outputDir)
+ }
g := &Generator{
Spec: s,
OutputDir: outputDir,
@@ -1544,6 +1553,13 @@ func (g *Generator) Generate() error {
)
g.Spec.OwnerName = g.Spec.Owner
}
+ if g.Spec.Printer == "" {
+ // Publish enforces this so self-owned CLIs can still use matching owner/printer slugs.
+ fmt.Fprintf(os.Stderr,
+ "WARNING: spec.Printer is empty; README printer attribution will be omitted. "+
+ "Set `git config github.user` (your GitHub @handle) to populate this correctly before publishing.\n",
+ )
+ }
if err := g.prepareOutput(); err != nil {
return err
}
diff --git a/internal/generator/plan_generate.go b/internal/generator/plan_generate.go
index 3c2a47df..bb1dbb90 100644
--- a/internal/generator/plan_generate.go
+++ b/internal/generator/plan_generate.go
@@ -411,3 +411,46 @@ func sanitizeOwner(s string) string {
return -1
}, s)
}
+
+// resolvePrinterForExisting preserves the original printer before consulting git config.
+func resolvePrinterForExisting(outputDir string) string {
+ if p := readManifestPrinter(outputDir); p != "" {
+ return p
+ }
+ return resolvePrinterForNew()
+}
+
+// readManifestPrinter returns the `printer` @handle from the manifest.
+func readManifestPrinter(outputDir string) string {
+ return readManifestField(outputDir, "printer")
+}
+
+// resolvePrinterForNew returns "" instead of a sentinel when github.user is unset.
+func resolvePrinterForNew() string {
+ if out, err := exec.Command("git", "config", "github.user").Output(); err == nil && len(out) > 0 {
+ return strings.TrimSpace(string(out))
+ }
+ return ""
+}
+
+// resolvePrinterNameForExisting preserves the printer display name on regen.
+func resolvePrinterNameForExisting(outputDir string) string {
+ if name := readManifestPrinterName(outputDir); name != "" {
+ return name
+ }
+ return resolvePrinterNameForNew()
+}
+
+// readManifestPrinterName returns the manifest printer display-name field.
+func readManifestPrinterName(outputDir string) string {
+ return readManifestField(outputDir, "printer_name")
+}
+
+// resolvePrinterNameForNew returns raw git user.name for a fresh print.
+func resolvePrinterNameForNew() string {
+ out, err := exec.Command("git", "config", "user.name").Output()
+ if err != nil {
+ return ""
+ }
+ return strings.TrimSpace(string(out))
+}
diff --git a/internal/generator/printer_test.go b/internal/generator/printer_test.go
new file mode 100644
index 00000000..c268d83f
--- /dev/null
+++ b/internal/generator/printer_test.go
@@ -0,0 +1,97 @@
+package generator
+
+import (
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+// TestResolvePrinterForExistingManifestWins pins the regen-preservation tier.
+func TestResolvePrinterForExistingManifestWins(t *testing.T) {
+ dir := t.TempDir()
+ manifest := `{"schema_version":1,"printer":"mvanhorn","cli_name":"foo-pp-cli","api_name":"foo"}`
+ require.NoError(t, os.WriteFile(filepath.Join(dir, ".printing-press.json"), []byte(manifest), 0o644))
+
+ assert.Equal(t, "mvanhorn", resolvePrinterForExisting(dir))
+}
+
+// TestResolvePrinterForExistingFallsThroughOnAbsentPrinter exercises the git-config tier.
+func TestResolvePrinterForExistingFallsThroughOnAbsentPrinter(t *testing.T) {
+ dir := t.TempDir()
+ manifest := `{"schema_version":1,"cli_name":"foo-pp-cli","api_name":"foo"}`
+ require.NoError(t, os.WriteFile(filepath.Join(dir, ".printing-press.json"), []byte(manifest), 0o644))
+
+ // We do not assert the value (depends on runner's git config), only
+ // that the fall-through path is exercised without panicking.
+ _ = resolvePrinterForExisting(dir)
+}
+
+// TestResolvePrinterForExistingFallsThroughOnEmptyPrinter treats empty as absent.
+func TestResolvePrinterForExistingFallsThroughOnEmptyPrinter(t *testing.T) {
+ dir := t.TempDir()
+ manifest := `{"schema_version":1,"printer":"","cli_name":"foo-pp-cli","api_name":"foo"}`
+ require.NoError(t, os.WriteFile(filepath.Join(dir, ".printing-press.json"), []byte(manifest), 0o644))
+
+ _ = resolvePrinterForExisting(dir)
+}
+
+// TestReadManifestPrinterHandlesMissingFile keeps missing manifests non-fatal.
+func TestReadManifestPrinterHandlesMissingFile(t *testing.T) {
+ assert.Equal(t, "", readManifestPrinter(t.TempDir()))
+}
+
+// TestReadManifestPrinterHandlesMalformed keeps invalid JSON non-fatal.
+func TestReadManifestPrinterHandlesMalformed(t *testing.T) {
+ dir := t.TempDir()
+ require.NoError(t, os.WriteFile(filepath.Join(dir, ".printing-press.json"), []byte("not json"), 0o644))
+ assert.Equal(t, "", readManifestPrinter(dir))
+}
+
+// TestReadManifestPrinterHandlesNonStringValue treats non-string values as missing.
+func TestReadManifestPrinterHandlesNonStringValue(t *testing.T) {
+ dir := t.TempDir()
+ manifest := `{"schema_version":1,"printer":42,"cli_name":"foo-pp-cli","api_name":"foo"}`
+ require.NoError(t, os.WriteFile(filepath.Join(dir, ".printing-press.json"), []byte(manifest), 0o644))
+ assert.Equal(t, "", readManifestPrinter(dir))
+}
+
+// TestResolvePrinterNameForExistingManifestWins pins display-name preservation.
+func TestResolvePrinterNameForExistingManifestWins(t *testing.T) {
+ dir := t.TempDir()
+ manifest := `{"schema_version":1,"printer_name":"Matt Van Horn","cli_name":"foo-pp-cli","api_name":"foo"}`
+ require.NoError(t, os.WriteFile(filepath.Join(dir, ".printing-press.json"), []byte(manifest), 0o644))
+
+ assert.Equal(t, "Matt Van Horn", resolvePrinterNameForExisting(dir))
+}
+
+// TestResolvePrinterNameForExistingFallsThroughOnAbsentName exercises git config.
+func TestResolvePrinterNameForExistingFallsThroughOnAbsentName(t *testing.T) {
+ dir := t.TempDir()
+ manifest := `{"schema_version":1,"cli_name":"foo-pp-cli","api_name":"foo"}`
+ require.NoError(t, os.WriteFile(filepath.Join(dir, ".printing-press.json"), []byte(manifest), 0o644))
+
+ // Returned value depends on runner's git config; just exercise the path.
+ _ = resolvePrinterNameForExisting(dir)
+}
+
+// TestReadManifestPrinterNameHandlesMissingFile keeps missing manifests non-fatal.
+func TestReadManifestPrinterNameHandlesMissingFile(t *testing.T) {
+ assert.Equal(t, "", readManifestPrinterName(t.TempDir()))
+}
+
+// TestGenerateLeavesPrinterEmptyWhenGitHandleUnset prevents owner-slug false positives.
+func TestGenerateLeavesPrinterEmptyWhenGitHandleUnset(t *testing.T) {
+ t.Setenv("GIT_CONFIG_COUNT", "1")
+ t.Setenv("GIT_CONFIG_KEY_0", "github.user")
+ t.Setenv("GIT_CONFIG_VALUE_0", "")
+
+ apiSpec := minimalSpec("self-owned")
+ outputDir := filepath.Join(t.TempDir(), "self-owned-pp-cli")
+ gen := New(apiSpec, outputDir)
+ require.NoError(t, gen.Generate())
+
+ assert.Empty(t, gen.Spec.Printer)
+}
diff --git a/internal/generator/templates/readme.md.tmpl b/internal/generator/templates/readme.md.tmpl
index ed0376ef..f8f1f62e 100644
--- a/internal/generator/templates/readme.md.tmpl
+++ b/internal/generator/templates/readme.md.tmpl
@@ -15,6 +15,10 @@
Learn more at [{{.ProseName}}]({{.WebsiteURL}}).
{{- end}}
+{{- if .Printer}}
+
+Printed by [@{{.Printer}}](https://github.com/{{.Printer}}){{if .PrinterName}} ({{.PrinterName}}){{end}}.
+{{- end}}
## Install
diff --git a/internal/pipeline/climanifest.go b/internal/pipeline/climanifest.go
index f69b6ea7..c0364bf2 100644
--- a/internal/pipeline/climanifest.go
+++ b/internal/pipeline/climanifest.go
@@ -49,7 +49,11 @@ type CLIManifest struct {
// Owner is the attribution recorded in generated copyright headers
// (for example "hiten-shah"). Persisted here so subsequent regens
// preserve attribution regardless of who's running the generator.
- Owner string `json:"owner,omitempty"`
+ Owner string `json:"owner,omitempty"`
+ // Printer is the original printer's GitHub handle, preserved across regens.
+ Printer string `json:"printer,omitempty"`
+ // PrinterName is the optional display name rendered beside the printer handle.
+ PrinterName string `json:"printer_name,omitempty"`
SpecURL string `json:"spec_url,omitempty"`
SpecPath string `json:"spec_path,omitempty"`
SpecFormat string `json:"spec_format,omitempty"`
@@ -379,6 +383,8 @@ type GenerateManifestParams struct {
DocsURL string // --docs URL, if used
OutputDir string
Owner string // resolved owner attribution (manifest preserve > copyright parse > git config)
+ Printer string // resolved printer @handle (manifest preserve > git config github.user > empty)
+ PrinterName string // resolved printer display name (manifest preserve > git config user.name > empty)
RunID string // YYYYMMDD-HHMMSS, derived from --research-dir basename when empty
Spec *spec.APISpec // parsed spec for MCP metadata (nil if unavailable)
NovelFeatures []NovelFeatureManifest // transcendence features from research (nil if unavailable)
@@ -417,6 +423,8 @@ func WriteManifestForGenerate(p GenerateManifestParams) error {
CLIName: naming.CLI(p.APIName),
RunID: p.RunID,
Owner: p.Owner,
+ Printer: p.Printer,
+ PrinterName: p.PrinterName,
}
// Populate spec_url / spec_path from the first spec source.
diff --git a/internal/pipeline/publish.go b/internal/pipeline/publish.go
index 98bf4a1a..346339b2 100644
--- a/internal/pipeline/publish.go
+++ b/internal/pipeline/publish.go
@@ -221,6 +221,12 @@ func writeCLIManifestForPublish(state *PipelineState, dir string) error {
if existing.Owner != "" {
m.Owner = existing.Owner
}
+ if existing.Printer != "" {
+ m.Printer = existing.Printer
+ }
+ if existing.PrinterName != "" {
+ m.PrinterName = existing.PrinterName
+ }
if existing.CatalogEntry != "" {
m.CatalogEntry = existing.CatalogEntry
}
diff --git a/internal/spec/spec.go b/internal/spec/spec.go
index 2b4776d1..f7160c0e 100644
--- a/internal/spec/spec.go
+++ b/internal/spec/spec.go
@@ -107,6 +107,8 @@ type APISpec struct {
EndpointTemplateVars []string `yaml:"endpoint_template_vars,omitempty" json:"endpoint_template_vars,omitempty"`
Owner string `yaml:"owner,omitempty" json:"owner,omitempty"` // GitHub owner for import paths and Homebrew tap
OwnerName string `yaml:"owner_name,omitempty" json:"owner_name,omitempty"` // Display name (e.g. "Trevin Chow") for prose surfaces — Hermes author:, README byline. Distinct from Owner (slug) which drives module paths and copyright headers.
+ Printer string `yaml:"printer,omitempty" json:"printer,omitempty"` // GitHub @handle of the human who ran the press for this CLI. Drives the per-CLI README byline link and the registry-side attribution. Distinct from Owner (the API-spec owner / wrapper-author identity).
+ PrinterName string `yaml:"printer_name,omitempty" json:"printer_name,omitempty"` // Display name of the printer (e.g. "Matt Van Horn") for prose surfaces — README byline parenthetical. Resolution path mirrors OwnerName: raw git config user.name, no slug fallback, no "USER" sentinel.
Kind string `yaml:"kind,omitempty" json:"kind,omitempty"` // "rest" (default) or "synthetic" — synthetic CLIs aggregate multiple sources beyond the spec; dogfood's path-validity check is relaxed accordingly
SpecSource string `yaml:"spec_source,omitempty" json:"spec_source,omitempty"` // official, community, sniffed, docs — affects generated client defaults
ClientPattern string `yaml:"client_pattern,omitempty" json:"client_pattern,omitempty"` // rest (default), proxy-envelope — affects generated HTTP client
diff --git a/skills/printing-press-publish/SKILL.md b/skills/printing-press-publish/SKILL.md
index 6589ec0d..8d365f56 100644
--- a/skills/printing-press-publish/SKILL.md
+++ b/skills/printing-press-publish/SKILL.md
@@ -400,6 +400,17 @@ cp -r "$STAGING_DIR/library/<category>/<cli-name>" "$PUBLISH_REPO_DIR/library/<c
# Remove binaries (should not be committed)
rm -f "$PUBLISH_REPO_DIR/library/<category>/<api-slug>/<api-slug>" "$PUBLISH_REPO_DIR/library/<category>/<api-slug>/<cli-name>"
+# Strict-validate printer attribution before it reaches README and registry surfaces.
+PRINTER=$(jq -r '.printer // ""' "$PUBLISH_REPO_DIR/library/<category>/<api-slug>/.printing-press.json")
+if [ -z "$PRINTER" ]; then
+ echo "ERROR: manifest .printer is empty. Set 'git config --global github.user <your-handle>' and re-print before publishing."
+ exit 1
+fi
+if [ "$PRINTER" = "USER" ] || [ "$PRINTER" = "user" ]; then
+ echo "ERROR: manifest .printer is the literal sentinel \"$PRINTER\" (git config github.user was unset at print time). Set it and re-print before publishing."
+ exit 1
+fi
+
# Regenerate the flat cli-skills mirror from the library tree so library PR CI passes mirror parity.
if [ -f "$PUBLISH_REPO_DIR/tools/generate-skills/main.go" ]; then
(cd "$PUBLISH_REPO_DIR" && go run ./tools/generate-skills/main.go)
diff --git a/testdata/golden/expected/generate-golden-api-rich-auth/printing-press-rich-auth/README.md b/testdata/golden/expected/generate-golden-api-rich-auth/printing-press-rich-auth/README.md
index c40c6b35..6ef17e40 100644
--- a/testdata/golden/expected/generate-golden-api-rich-auth/printing-press-rich-auth/README.md
+++ b/testdata/golden/expected/generate-golden-api-rich-auth/printing-press-rich-auth/README.md
@@ -2,6 +2,8 @@
Purpose-built fixture for rich auth env-var model coverage.
+Printed by [@printing-press-golden](https://github.com/printing-press-golden) (printing-press-golden).
+
## Install
The recommended path installs both the `printing-press-rich-pp-cli` binary and the `pp-printing-press-rich` agent skill in one shot:
diff --git a/testdata/golden/expected/generate-golden-api-unicode/cafe-bistro/.printing-press.json b/testdata/golden/expected/generate-golden-api-unicode/cafe-bistro/.printing-press.json
index 2d2edcf9..72b0e9e6 100644
--- a/testdata/golden/expected/generate-golden-api-unicode/cafe-bistro/.printing-press.json
+++ b/testdata/golden/expected/generate-golden-api-unicode/cafe-bistro/.printing-press.json
@@ -12,6 +12,8 @@
"mcp_ready": "full",
"mcp_tool_count": 1,
"owner": "printing-press-golden",
+ "printer": "printing-press-golden",
+ "printer_name": "printing-press-golden",
"printing_press_version": "<PRINTING_PRESS_VERSION>",
"schema_version": 1,
"spec_checksum": "sha256:8cfba131c384836e8dde461511c9fd63cf2cbab7793553067765d515f2007aa1",
diff --git a/testdata/golden/expected/generate-golden-api/printing-press-golden/.printing-press.json b/testdata/golden/expected/generate-golden-api/printing-press-golden/.printing-press.json
index a892f8cd..7059887f 100644
--- a/testdata/golden/expected/generate-golden-api/printing-press-golden/.printing-press.json
+++ b/testdata/golden/expected/generate-golden-api/printing-press-golden/.printing-press.json
@@ -13,6 +13,8 @@
"mcp_ready": "full",
"mcp_tool_count": 8,
"owner": "printing-press-golden",
+ "printer": "printing-press-golden",
+ "printer_name": "printing-press-golden",
"printing_press_version": "<PRINTING_PRESS_VERSION>",
"schema_version": 1,
"spec_checksum": "sha256:333b2c06c645b8f394fbbaaedf8126e06919529d7d9305c94c5ebcf1f5a897b5",
diff --git a/testdata/golden/expected/generate-golden-api/printing-press-golden/README.md b/testdata/golden/expected/generate-golden-api/printing-press-golden/README.md
index 9d5bdaf8..5c5d2154 100644
--- a/testdata/golden/expected/generate-golden-api/printing-press-golden/README.md
+++ b/testdata/golden/expected/generate-golden-api/printing-press-golden/README.md
@@ -2,6 +2,8 @@
Purpose-built fixture for golden generation coverage.
+Printed by [@printing-press-golden](https://github.com/printing-press-golden) (printing-press-golden).
+
## Install
The recommended path installs both the `printing-press-golden-pp-cli` binary and the `pp-printing-press-golden` agent skill in one shot:
diff --git a/testdata/golden/expected/generate-mcp-api/mcp-cloudflare/.printing-press.json b/testdata/golden/expected/generate-mcp-api/mcp-cloudflare/.printing-press.json
index b278c199..ca3ffc18 100644
--- a/testdata/golden/expected/generate-mcp-api/mcp-cloudflare/.printing-press.json
+++ b/testdata/golden/expected/generate-mcp-api/mcp-cloudflare/.printing-press.json
@@ -12,6 +12,8 @@
"mcp_ready": "full",
"mcp_tool_count": 1,
"owner": "printing-press-golden",
+ "printer": "printing-press-golden",
+ "printer_name": "printing-press-golden",
"printing_press_version": "<PRINTING_PRESS_VERSION>",
"schema_version": 1,
"spec_checksum": "sha256:46c60ba489f2e138ffe6ab01531f2ed550369299820342a7f0093b94d0fc17b4",
diff --git a/testdata/golden/expected/generate-public-param-names/public-param-golden/README.md b/testdata/golden/expected/generate-public-param-names/public-param-golden/README.md
index ec2b4076..2a6280ec 100644
--- a/testdata/golden/expected/generate-public-param-names/public-param-golden/README.md
+++ b/testdata/golden/expected/generate-public-param-names/public-param-golden/README.md
@@ -2,6 +2,8 @@
Public parameter name golden fixture
+Printed by [@printing-press-golden](https://github.com/printing-press-golden) (printing-press-golden).
+
## Install
The recommended path installs both the `public-param-golden-pp-cli` binary and the `pp-public-param-golden` agent skill in one shot:
diff --git a/testdata/golden/expected/generate-tier-routing-api/tier-routing-golden/.printing-press.json b/testdata/golden/expected/generate-tier-routing-api/tier-routing-golden/.printing-press.json
index 8d1a7a22..23f1bde6 100644
--- a/testdata/golden/expected/generate-tier-routing-api/tier-routing-golden/.printing-press.json
+++ b/testdata/golden/expected/generate-tier-routing-api/tier-routing-golden/.printing-press.json
@@ -15,6 +15,8 @@
"mcp_ready": "full",
"mcp_tool_count": 3,
"owner": "printing-press-golden",
+ "printer": "printing-press-golden",
+ "printer_name": "printing-press-golden",
"printing_press_version": "<PRINTING_PRESS_VERSION>",
"schema_version": 1,
"spec_checksum": "sha256:a059b4da997e0e835f0cdcd911ef5894fc048b50506bdcd90260a7478efb2958",
← ca580efc fix(cli): align generated install and rename metadata (#749)
·
back to Cli Printing Press
·
fix(cli): harden force-generate preservation (#750) c397021f →