--- name: output-dev-upgrade-prompt-models description: Bulk-upgrade the model field across .prompt files to the latest version of each prompt's existing family. Use when prompt models have drifted (eg sonnet-4 → sonnet-4-6), after a long pause between framework updates, or as part of a periodic model-freshness pass. Within-family only — never changes provider or tier. allowed-tools: [Bash, Read, Edit, Glob] --- # Upgrade Prompt Models In-Place Walks every `.prompt` file in a project (or scoped subtree), classifies each model into its provider+family bucket, looks up the latest stable model in that bucket via the [`output-dev-model-selection`](../output-dev-model-selection/SKILL.md) snapshot, and rewrites the `model:` line. Provider and family tier are preserved — a Haiku stays a Haiku, an Anthropic stays an Anthropic. This skill explicitly does **not** swap providers or escalate tiers (eg Haiku → Sonnet). Those are deliberate human decisions handled separately. ## When to invoke - A periodic refresh: "upgrade all my prompt models to the latest" - After a long break between framework updates, where dated snapshot IDs (eg `claude-sonnet-4-20250514`) have aged out - Right after creating a new project from the CLI scaffolder, to pull every templated default forward to the current best ## Workflow ### Step 1 — Discover Find every `.prompt` file under the target scope. Default scope is the project's `src/` tree; the user may scope to a single workflow. ### Step 2 — Parse current model For each file, read the YAML frontmatter (between the first pair of `---` lines) and pull out `provider:` and `model:`. ### Step 3 — Classify family Match the existing model into a family bucket. Family is preserved across the upgrade. | Pattern | Family | |---|---| | `claude-opus-*` | `anthropic-opus` | | `claude-sonnet-*` | `anthropic-sonnet` | | `claude-haiku-*` | `anthropic-haiku` | | `gpt-*-pro` | `openai-pro` | | `gpt-*-mini` | `openai-mini` | | `gpt-*-nano` | `openai-nano` | | `gpt-N.M` (no suffix) | `openai-default` | | `gemini-*-flash-lite*` | `google-flash-lite` | | `gemini-*-flash*` | `google-flash` | | `gemini-*-pro*` | `google-pro` | If a model doesn't match any pattern, skip the file and log a warning. Do not guess. ### Step 4 — Look up latest in family For each prompt, find the latest stable model in the same family by following [`output-dev-model-selection`](../output-dev-model-selection/SKILL.md) — fetch its snapshot, apply its filter rules (skip preview/alpha/beta, prefer unversioned aliases), and translate the chosen `id` to prompt-file form. Use this family → snapshot-key + `id` regex map to pin the lookup to the existing tier: | Family | Snapshot key | `id` regex | |---|---|---| | `anthropic-opus` | `anthropic` | `claude-opus-` | | `anthropic-sonnet` | `anthropic` | `claude-sonnet-` | | `anthropic-haiku` | `anthropic` | `claude-haiku-` | | `openai-pro` | `openai` | `-pro$` | | `openai-default` | `openai` | `^openai/gpt-[0-9.]+$` | | `openai-mini` | `openai` | `-mini$` | | `openai-nano` | `openai` | `-nano$` | | `google-pro` | `google` | `-pro` (excluding `-flash`) | | `google-flash` | `google` | `-flash$\|-flash-[0-9]` | | `google-flash-lite` | `google` | `-flash-lite` | If no stable match exists for a family (only pre-release entries available), surface that to the user and skip the file rather than guessing or downgrading to a different family. **Bail loudly on a failed snapshot.** If the snapshot fetch itself returned nothing — network down, gateway shape changed, `curl` or `jq` missing — abort the run before Step 5. Do **not** continue with an empty snapshot and report "no upgrades needed", because that lies: the prompts weren't actually checked. Tell the user the snapshot fetch failed, point them at the manual-fallback steps in [`output-dev-model-selection`](../output-dev-model-selection/SKILL.md), and exit. ### Step 5 — Diff & confirm Build a per-file report comparing the current model to the resolved `latest`: ``` src/workflows/foo/prompts/bar@v1.prompt claude-sonnet-4-20250514 → claude-sonnet-4-6 src/workflows/foo/prompts/baz@v1.prompt claude-haiku-4-5 ✓ already latest ``` Print the full report. **Wait for explicit user confirmation before writing.** In CI / non-interactive contexts, default to dry-run. ### Step 6 — Edit For each confirmed file, edit only the YAML frontmatter: - Replace the `model:` line with the resolved latest ID. - If a `# current as of YYYY-MM-DD …` comment is present (the convention used in `output-dev-prompt-file` examples and CLI scaffolds), update its date to today's (`date +%Y-%m-%d`). Match the comment by the literal `current as of ` prefix and only rewrite the date — leave the trailing text intact. - Leave `provider:`, `temperature:`, `maxOutputTokens:` (or deprecated `maxTokens:`), `providerOptions:`, and the message body untouched. Refreshing the dated comment in the same edit keeps the "as of" convention coherent — without it, an upgraded prompt would have a fresh model paired with a stale date. ### Step 7 — Verify After the batch: - Spot-check a handful of files to confirm the YAML still has a frontmatter delimiter and message body. - Rebuild the worker so the new model strings take effect (`npm run output:worker:build`). The Output SDK doesn't validate prompt model IDs at build time — invalid IDs only surface at first run. If smoke-tests are available, run at least one workflow per upgraded family. ## Caveats - **Within-family only.** This skill never upgrades Sonnet → Opus, never swaps Anthropic for OpenAI. To change tier or provider, edit prompts manually or use [`output-dev-prompt-file`](../output-dev-prompt-file/SKILL.md). - **Dated snapshots get bumped.** A pin like `claude-sonnet-4-20250514` becomes the unversioned alias `claude-sonnet-4-6`. If the pin was load-bearing for reproducibility, surface that and skip the file. - **`@vertex` and `bedrock` namespace suffixes.** Models like `claude-sonnet-4-20250514@vertex` or `anthropic.claude-sonnet-4-20250514-v1:0` need manual upgrade. The AI Gateway listing covers direct provider IDs only. - **Models.dev pricing lag.** Even after the upgrade, `response.cost` may be incomplete with `total: null` for the brand-new model until [models.dev](https://models.dev) catches up. The runtime call still works. ## See also - [`output-dev-model-selection`](../output-dev-model-selection/SKILL.md) — canonical snapshot + selection rules this skill consumes - [`output-dev-prompt-file`](../output-dev-prompt-file/SKILL.md) — `.prompt` file structure