Prompt version control
Skill Notysoty/openagentskills/skills/prompt-version-control
A community-driven library of reusable AI agent skills for Claude Code, Cursor, Codex, Cline, and more.
npx -y skills add Notysoty/openagentskills --skill prompt-version-controlAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 9 stars9 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Sets up a prompt versioning system with naming conventions, diff tracking, A/B evaluation gates before promotion, and rollback triggers.
SKILL.md
7.2 KB, as published. Nobody here has run it
Prompt Version Control Workflow
What this skill does
This skill designs a prompt versioning workflow so you can track changes, test before promoting, and roll back safely. Most teams store prompts as hardcoded strings inside application code — this means prompt changes go unreviewed, regressions are invisible, and rollbacks require a full code deploy. This skill fixes that.
How to use
Claude Code / Cline
Copy this file to .agents/skills/prompt-version-control/SKILL.md in your project root.
Then ask:
- "Use the Prompt Version Control skill to set up versioning for our prompts."
- "How should we manage prompt versions across staging and production?"
Provide:
- Number of prompts in your system
- Current storage method (hardcoded, env vars, database, files)
- Whether you need A/B testing or just safe deploys
- Tech stack
Cursor / Codex
Describe your current prompt management alongside these instructions.
The Prompt / Instructions for the Agent
Step 1 — Choose a storage approach
| Approach | Best for | Tradeoffs |
|---|---|---|
| Git files (prompts/*.md) | Small teams, prompts change rarely | Simple, diffable, but requires deploy to change |
| Database table | Prompts change frequently, need hot-swap | Live updates, but needs admin UI |
| Dedicated tools (Braintrust, LangSmith, Langfuse) | Eval-heavy teams | Best tooling, external dependency |
| Environment variables | Single prompt, simple apps | Easy but no history, no diffs |
Recommended default: Git files — prompts are code, they should be reviewed like code.
Step 2 — File-based prompt versioning
prompts/
support-bot/
v1.md ← old version (keep for rollback reference)
v2.md ← current production version
v3.md ← candidate being tested
current.txt ← contains "v2" — points to active version
document-summarizer/
v1.md
current.txt
Each prompt file has a frontmatter header:
---
version: 2
created: 2026-01-15
author: tates
description: Improved tone, added JSON output format
changes_from_previous:
- Added JSON output requirement
- Shortened system role description
- Added explicit refusal instructions
---
You are a customer support agent for Acme Corp...
Load the active prompt at runtime:
import os, pathlib
def load_prompt(name: str) -> str:
prompts_dir = pathlib.Path("prompts") / name
current = (prompts_dir / "current.txt").read_text().strip()
return (prompts_dir / f"{current}.md").read_text().split("---\n", 2)[-1].strip()
Step 3 — Evaluation gate before promotion
Never promote a new prompt version without running evals first. Add this as a CI check:
# scripts/eval-prompt-candidate.py
import sys
from eval_harness import run_eval_suite, load_dataset
from prompt_loader import load_prompt_version
PASS_THRESHOLD = 0.85 # 85% pass rate required
def main(prompt_name: str, candidate_version: str):
dataset = load_dataset(f"evals/{prompt_name}/test_cases.json")
current = load_prompt_version(prompt_name, "current")
candidate = load_prompt_version(prompt_name, candidate_version)
current_results = run_eval_suite(current, dataset)
candidate_results = run_eval_suite(candidate, dataset)
print(f"Current version pass rate: {current_results['pass_rate']:.1%}")
print(f"Candidate version pass rate: {candidate_results['pass_rate']:.1%}")
# Block promotion if candidate is worse
if candidate_results["pass_rate"] < PASS_THRESHOLD:
print(f"FAIL: Candidate below {PASS_THRESHOLD:.0%} threshold")
sys.exit(1)
if candidate_results["pass_rate"] < current_results["pass_rate"] - 0.05:
print("FAIL: Candidate is more than 5% worse than current")
sys.exit(1)
print("PASS: Candidate meets quality threshold")
if __name__ == "__main__":
main(sys.argv[1], sys.argv[2])
Step 4 — GitHub Actions workflow
# .github/workflows/prompt-eval.yml
name: Prompt Evaluation
on:
pull_request:
paths:
- 'prompts/**'
jobs:
eval:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Detect changed prompts
id: changes
run: |
git diff --name-only origin/main HEAD -- prompts/ | \
grep -oP 'prompts/\K[^/]+' | sort -u > changed_prompts.txt
cat changed_prompts.txt
- name: Run evals for changed prompts
run: |
while read prompt_name; do
echo "Evaluating: $prompt_name"
# Find the candidate version (newest non-current .md)
CANDIDATE=$(ls prompts/$prompt_name/*.md | sort -V | tail -1 | xargs basename .md)
python scripts/eval-prompt-candidate.py "$prompt_name" "$CANDIDATE"
done < changed_prompts.txt
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
Step 5 — Promotion and rollback
Promote a candidate to production:
# scripts/promote-prompt.sh
PROMPT_NAME=$1
NEW_VERSION=$2
echo "$NEW_VERSION" > prompts/$PROMPT_NAME/current.txt
git add prompts/$PROMPT_NAME/current.txt
git commit -m "chore: promote $PROMPT_NAME to $NEW_VERSION"
git push
Rollback (instant, no code deploy needed):
# Roll back to previous version
echo "v1" > prompts/support-bot/current.txt
git commit -am "revert: roll back support-bot to v1 — tone regression"
git push
If prompts are loaded dynamically from the filesystem (not baked into a build), a rollback takes effect on the next request — no redeploy needed.
Step 6 — Database-backed hot-swap (advanced)
For teams that need to change prompts without any deploy:
CREATE TABLE prompt_versions (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
version INTEGER NOT NULL,
content TEXT NOT NULL,
is_active BOOLEAN DEFAULT false,
eval_pass_rate NUMERIC,
created_at TIMESTAMPTZ DEFAULT NOW(),
promoted_by TEXT,
UNIQUE(name, version)
);
-- Get active prompt
SELECT content FROM prompt_versions
WHERE name = 'support-bot' AND is_active = true;
-- Promote new version (atomic swap)
BEGIN;
UPDATE prompt_versions SET is_active = false WHERE name = 'support-bot';
UPDATE prompt_versions SET is_active = true WHERE name = 'support-bot' AND version = 3;
COMMIT;
Cache active prompts in memory with a 5-minute TTL — re-fetch on cache miss. This gives you live prompt updates with one database query overhead.
Naming conventions
| Convention | Example |
|---|---|
| Semantic versioning for major changes | v1, v2, v3 |
| Date-based for frequent iteration | 2026-01-15, 2026-02-03 |
| Feature-branch style | v2-json-output, v3-shorter-tone |
Always include in commit messages: what changed, why it changed, and eval result. Example:
chore: promote support-bot to v3
Changes: added JSON output format, removed verbose greeting
Reason: downstream parser requires structured output
Eval: 91% pass rate (up from 87% on v2)