[object Object]

← back to Cli Printing Press

docs(skills): document separate-file pattern for extending emitted files (#1624)

af9ba8c7ab6ccb9688d9109c00eb415e0d357e1e · 2026-05-18 13:20:26 -0700 · Trevin Chow

* docs(skills): document separate-file pattern for extending emitted files

Add a Phase 3 callout to skills/printing-press/SKILL.md warning that
hand-edits to any generator-emitted file (`config.go`, `client.go`,
`auth.go`, `store.go`, `root.go`, plus the `cliutil_*` / MCP / sync
families) are wiped on `printing-press generate --force` and reconciled
by `printing-press regen-merge`. Only whole hand-authored files survive
across regen; inline additions (struct field, header in do(),
AddCommand call, migration row) are not preserved.

Names the separate-file pattern per common extension case:

- Custom config fields -> internal/config/<api>_config.go
- Custom request headers -> internal/client/<api>_headers.go
- Custom auth flow -> internal/auth/<api>_auth.go
- Extended store schema -> internal/store/<api>_migrations.go
- New novel command -> internal/cli/<feature>.go

When no separate-file route exists (an AddCommand call with no registry
hook, a case branch in a method switch), the callout directs agents to
file a generator issue rather than inline-editing.

Closes #1604

* docs(skills): reconcile novel-command bullet with Phase 3 skeleton; scope header injection

Two Greptile findings on PR #1624:

P1 - "New novel command" bullet contradicted the existing Phase 3
skeleton (lines 2491-2495), which instructs agents to wire hand-authored
commands via AddCommand in root.go. The earlier text implied the body
file alone was sufficient, and the closing paragraph routed AddCommand
edits to "file a generator issue." An agent following that guidance
would ship a command file that is never registered in the Cobra tree.

Reworded to:
- The command body lives in internal/cli/<feature>.go (survives regen).
- The AddCommand call still goes in root.go per the existing skeleton.
- `generate --force` wipes that call, but `regen-merge` re-injects it via
  the lost-registration mechanism in internal/pipeline/regenmerge/apply.go.
- Prefer regen-merge over --force for refreshes.
- Spec-declared commands need no hand-wired AddCommand at all.

P2 - "Custom request headers" bullet said the exported func is called
"before each request," implying global injection. The generated client.go
has no per-request mutator chain; the only injection surface is
GetWithHeaders / PostWithHeaders called explicitly by novel code.

Reworded to make the scope explicit: the helper builds the header map,
novel code passes it to GetWithHeaders/PostWithHeaders, and generated
endpoint commands are unaffected.

Also tightened the closing fallback to exclude AddCommand (which is
already covered by regen-merge) and call out which inline diffs actually
warrant a generator-issue ask.

* fix(skills): correct auth extension path and remove fictional method switch

The custom-auth-flow extension pattern pointed agents at
`internal/auth/<api>_auth.go`, but no `internal/auth/` package exists —
all four auth templates (`auth.go.tmpl`, `auth_simple.go.tmpl`,
`auth_browser.go.tmpl`, `auth_client_credentials.go.tmpl`) emit to
`internal/cli/` under `package cli`. Following the guidance produced
an orphan package that nothing in the generated tree imports.

Also dropped the reference to a "templated method switch in `auth.go`":
the generated `auth.go` uses constructor functions
(`newAuthLoginCmd`, `newAuthSetupCmd`, etc.), not a switch.

Aligns the auth extension with the side-by-side same-package invariant
that the other four extension patterns already follow
(`internal/config/`, `internal/client/`, `internal/store/`,
`internal/cli/`).

* Update skills/printing-press/SKILL.md

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

---------

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

Files touched

Diff

commit af9ba8c7ab6ccb9688d9109c00eb415e0d357e1e
Author: Trevin Chow <trevin@trevinchow.com>
Date:   Mon May 18 13:20:26 2026 -0700

    docs(skills): document separate-file pattern for extending emitted files (#1624)
    
    * docs(skills): document separate-file pattern for extending emitted files
    
    Add a Phase 3 callout to skills/printing-press/SKILL.md warning that
    hand-edits to any generator-emitted file (`config.go`, `client.go`,
    `auth.go`, `store.go`, `root.go`, plus the `cliutil_*` / MCP / sync
    families) are wiped on `printing-press generate --force` and reconciled
    by `printing-press regen-merge`. Only whole hand-authored files survive
    across regen; inline additions (struct field, header in do(),
    AddCommand call, migration row) are not preserved.
    
    Names the separate-file pattern per common extension case:
    
    - Custom config fields -> internal/config/<api>_config.go
    - Custom request headers -> internal/client/<api>_headers.go
    - Custom auth flow -> internal/auth/<api>_auth.go
    - Extended store schema -> internal/store/<api>_migrations.go
    - New novel command -> internal/cli/<feature>.go
    
    When no separate-file route exists (an AddCommand call with no registry
    hook, a case branch in a method switch), the callout directs agents to
    file a generator issue rather than inline-editing.
    
    Closes #1604
    
    * docs(skills): reconcile novel-command bullet with Phase 3 skeleton; scope header injection
    
    Two Greptile findings on PR #1624:
    
    P1 - "New novel command" bullet contradicted the existing Phase 3
    skeleton (lines 2491-2495), which instructs agents to wire hand-authored
    commands via AddCommand in root.go. The earlier text implied the body
    file alone was sufficient, and the closing paragraph routed AddCommand
    edits to "file a generator issue." An agent following that guidance
    would ship a command file that is never registered in the Cobra tree.
    
    Reworded to:
    - The command body lives in internal/cli/<feature>.go (survives regen).
    - The AddCommand call still goes in root.go per the existing skeleton.
    - `generate --force` wipes that call, but `regen-merge` re-injects it via
      the lost-registration mechanism in internal/pipeline/regenmerge/apply.go.
    - Prefer regen-merge over --force for refreshes.
    - Spec-declared commands need no hand-wired AddCommand at all.
    
    P2 - "Custom request headers" bullet said the exported func is called
    "before each request," implying global injection. The generated client.go
    has no per-request mutator chain; the only injection surface is
    GetWithHeaders / PostWithHeaders called explicitly by novel code.
    
    Reworded to make the scope explicit: the helper builds the header map,
    novel code passes it to GetWithHeaders/PostWithHeaders, and generated
    endpoint commands are unaffected.
    
    Also tightened the closing fallback to exclude AddCommand (which is
    already covered by regen-merge) and call out which inline diffs actually
    warrant a generator-issue ask.
    
    * fix(skills): correct auth extension path and remove fictional method switch
    
    The custom-auth-flow extension pattern pointed agents at
    `internal/auth/<api>_auth.go`, but no `internal/auth/` package exists —
    all four auth templates (`auth.go.tmpl`, `auth_simple.go.tmpl`,
    `auth_browser.go.tmpl`, `auth_client_credentials.go.tmpl`) emit to
    `internal/cli/` under `package cli`. Following the guidance produced
    an orphan package that nothing in the generated tree imports.
    
    Also dropped the reference to a "templated method switch in `auth.go`":
    the generated `auth.go` uses constructor functions
    (`newAuthLoginCmd`, `newAuthSetupCmd`, etc.), not a switch.
    
    Aligns the auth extension with the side-by-side same-package invariant
    that the other four extension patterns already follow
    (`internal/config/`, `internal/client/`, `internal/store/`,
    `internal/cli/`).
    
    * Update skills/printing-press/SKILL.md
    
    Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
    
    ---------
    
    Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
    Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
---
 skills/printing-press/SKILL.md | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/skills/printing-press/SKILL.md b/skills/printing-press/SKILL.md
index 9f41eb8c..98a1af2d 100644
--- a/skills/printing-press/SKILL.md
+++ b/skills/printing-press/SKILL.md
@@ -2604,6 +2604,18 @@ SELECT id, COALESCE(json_extract(data, '$.name'), '') FROM resources WHERE ...
 
 **Typed exit-code verification:** If a novel command intentionally returns a non-zero code for a non-error control-flow result, add `cmd.Annotations["pp:typed-exit-codes"] = "0,<code>"` (or the equivalent `Annotations: map[string]string{...}` literal) and document the same command-specific codes in its help. Do not list the global failure palette in command help unless those exits should count as a verify pass for that command; keep general exit-code troubleshooting in README/SKILL prose.
 
+**Hand-edits to generator-emitted files are not durable.** Every file carrying `// Generated by CLI Printing Press ... DO NOT EDIT.` — `config.go`, `client.go`, `auth.go`, `store.go`, `root.go`, every `cliutil_*.go`, the typed MCP wrappers, and `sync.go` / `analytics.go` / `jobs.go` — is overwritten on `printing-press generate --force` and reconciled by `printing-press regen-merge`. Inline additions (a field on `Config`, a header in `client.go`'s `do()`, a row in `store.go`'s migrations slice) are not preserved; only whole hand-authored files survive across regen. (`AddCommand` calls in `root.go` are the exception: `regen-merge` re-injects them automatically — see the novel-command bullet below.)
+
+For an extension to be durable, put it in its own file beside the emitted one:
+
+- **Custom config fields:** create `internal/config/<api>_config.go` exporting accessors your novel code reads directly. Do not add fields to the emitted `Config` struct.
+- **Custom request headers** (vendor fingerprint, `X-CSRF`, app-version, signed timestamps): create `internal/client/<api>_headers.go` exporting a func that builds the header map; novel code passes that map to `client.GetWithHeaders` / `PostWithHeaders` when it calls the API. The generated `client.go` has no global request mutator, so this pattern only covers requests made directly from novel code — it does not intercept calls from generated endpoint commands. Do not edit the templated header block in `client.go`.
+- **Custom auth flow** (browser-sniffed sessions, vendor SSO, refresh hooks beyond OAuth2): create `internal/cli/<api>_auth.go` (package `cli`, same as the generated `auth.go`) with the API-specific token capture or refresh, and wire it from a novel command rather than editing the templated `auth.go` constructor functions (`newAuthLoginCmd`, `newAuthSetupCmd`, etc.).
+- **Extended store schema** (typed tables beyond `resources`, vendor JSON columns, full-text indexes): create `internal/store/<api>_migrations.go` running its own `CREATE TABLE ... IF NOT EXISTS` from a lazy init invoked by the novel commands that need it. Do not edit the migration slice in `store.go`.
+- **New novel command:** put the command body in its own `internal/cli/<feature>.go` file — it survives regen as a whole hand-authored unit. The `AddCommand` call wiring it into the Cobra tree still goes in `root.go` per the Phase 3 novel-command skeleton above; `printing-press generate --force` wipes that call, but `printing-press regen-merge` re-injects it via its lost-registration mechanism (see `internal/pipeline/regenmerge/apply.go`). Prefer `regen-merge` over `--force` for routine refreshes so the AddCommand call doesn't need a manual re-apply. Spec-declared commands are picked up by the generator's typed-tool path and need no hand-wired `AddCommand` at all.
+
+If an extension genuinely cannot live in a separate file (a `case` branch in a templated method switch, an inline modification to a generated handler with no registry hook), file a generator issue requesting the hook rather than carrying the edit across regens. The `AddCommand` case above is covered by `regen-merge`; most other inline diffs are not.
+
 **MCP exposure:** The generator emits `internal/mcp/cobratree/`, and the MCP binary mirrors the Cobra tree at startup. When you add, rename, or remove a user-facing Cobra command, the MCP surface follows automatically. Two annotations control how each command appears as an MCP tool:
 
 - `cmd.Annotations["mcp:hidden"] = "true"` — exclude the command from the MCP surface entirely. Use only for debug/internal commands that should not become agent tools.

← 766354e6 feat(cli): add sync --path-context KEY=VAL runtime override  ·  back to Cli Printing Press  ·  fix(cli): correct live-check and helper emission (#1637) 8c955701 →