Git-style version control for LLM prompts — commit, diff, branch, merge, tag, and rollback with semantic diffs.
Project description
prompt-git (pgit)
Git for your prompts. Commit, diff, branch, merge, and rollback LLM prompts — with semantic diffs that tell you what meaning changed.
The problem
Prompts are load-bearing infrastructure in production AI systems — but they're managed like config files: no history, no diffs, no rollback.
When your agent regresses at 2 AM, you have no idea:
- Which prompt changed?
- What exactly changed in it?
- Was it broken before, or did a recent edit introduce the regression?
The solution
$ pgit diff HEAD~1 --semantic
Semantic diff: 7b1e4f1 → a3f9c2d
───────────────────────────────────────────────────────────
Summary: Tone shifted from permissive to strict; added hard
limits on off-topic queries.
Added: - Explicit instruction to refuse off-topic questions
- Hard word limit of 200 tokens per response
- Escalation path for complex queries
Removed: - "Feel free to explore" phrasing
- Open-ended example in the few-shot section
Tone: Permissive → Strict / Professional
pgit gives prompts the same rigorous version control that code has had for 30 years — plus one thing code diffs can't do: describe what the meaning changed.
Install
# Could not get prompt-git project name :-(
pip install sr-prompt-git
# For semantic diff (requires Anthropic API key):
pip install sr-prompt-git[semantic]
export PGIT_LLM_KEY=sk-ant-...
Quick start
# 1. Initialise a prompt repo in your project
pgit init
# 2. Stage and commit your first prompt
echo "You are a helpful assistant. Be concise." > system.md
pgit add system.md
pgit commit -m "initial system prompt"
# 3. Attach your eval scores to the commit
pgit eval attach pass_rate 0.87
pgit eval attach avg_cost_usd 0.003
# 4. Experiment on a branch
pgit branch create experiment/stricter
pgit branch switch experiment/stricter
echo "You are a strict assistant. Refuse all off-topic queries." > system.md
pgit add system.md
pgit commit -m "tightened safety boundaries"
pgit eval attach pass_rate 0.94
# 5. See what semantically changed
pgit diff HEAD~1 --semantic
# 6. Merge back — only if pass_rate improved by 5%+
pgit branch switch main
pgit merge experiment/stricter --if-better pass_rate
# ✓ Merged: pass_rate 0.87 → 0.94 (+8.0%)
Why prompt-git?
| Pain point | pgit solution |
|---|---|
| "Which prompt caused the regression?" | pgit log — full history with eval scores per commit |
| "What exactly changed between these versions?" | pgit diff v1.2 v1.3 --semantic |
| "Roll back to last week's prompt" | pgit tag + pgit branch switch |
| "Only ship if it tested better" | pgit merge --if-better pass_rate |
| "Audit all prompt changes this sprint" | pgit log --limit 50 --json |
Full CLI reference
pgit init Initialise a prompt repo
pgit add <file> [--model <hint>] Stage files for commit
pgit commit -m <msg> [--author <name>] Commit staged files
pgit status Show staged / unstaged state
pgit log [--branch <b>] [--limit N] [--json] Commit history with eval scores
pgit diff [<from>] [<to>] [--semantic] Line diff or LLM semantic diff
pgit branch list List branches
pgit branch create <name> [--from <ref>] Create a branch
pgit branch switch <name> Switch branches
pgit branch delete <name> Delete a branch
pgit merge <branch> [--if-better <metric>] Merge (optionally gated on eval)
pgit tag <name> [<commit>] [-m <msg>] Tag a commit
pgit eval attach <metric> <value> Attach eval score to HEAD
pgit eval show [--commit <hash>] [--json] Show eval scores
pgit push [<remote>] [<branch>] Push to remote
pgit pull [<remote>] [<branch>] Pull from remote
<from> / <to> accept: commit hashes, branch names, tag names, HEAD, HEAD~N.
Supported prompt formats
| Format | Extensions | Notes |
|---|---|---|
| Plaintext | .txt, .md |
Standard text / markdown prompts |
| Jinja2 | .j2, .jinja, .jinja2 |
Template prompts with variables |
| JSON Messages | .json |
OpenAI-style [{role, content}] arrays |
| YAML Turns | .yaml, .yml |
Multi-turn prompt files |
How it works
Everything lives in .promptgit/store.db — a plain SQLite file you can open with any viewer.
Objects are content-addressed (SHA-256, like git):
- Blob — immutable prompt text; identical content stored once regardless of commits
- Tree — maps file paths → blob hashes
- Commit — tree + parent + message + author + timestamp; immutable once written
- Tag — named pointer to a commit
- SemanticDiff — LLM-generated meaning diff, cached permanently by
(from_hash, to_hash)
Eval scores live in a separate table linked by commit hash — so attaching scores never changes a commit's hash.
Semantic diff
pgit diff HEAD~1 --semantic # all changed prompts, HEAD~1..HEAD
pgit diff v1.0 v2.0 --semantic --file system.md # one file, specific tags
pgit diff --semantic --json # machine-readable JSON output
- Powered by
claude-haiku-4-5-20251001by default (fast, cheap) - Override model:
PGIT_LLM_MODEL=claude-sonnet-4-6 - Results are cached permanently — same pair of versions never hits the LLM twice
- Always opt-in via
--semantic— never called automatically
Environment variables
| Variable | Default | Description |
|---|---|---|
PGIT_LLM_KEY |
required for --semantic |
Anthropic API key |
PGIT_LLM_MODEL |
claude-haiku-4-5-20251001 |
Model for semantic diff |
PGIT_AUTHOR |
git config user.name |
Default commit author |
PGIT_DB |
.promptgit/store.db |
Override store path |
PGIT_REMOTE_URL |
— | Default remote URL |
TypeScript SDK
A read-only TypeScript SDK is included for teams reading prompt history from Node.js:
import { PromptRepo } from 'prompt-git'
const repo = PromptRepo.open('/path/to/project')
const commits = repo.log({ limit: 10 })
console.log(commits[0].message)
Examples
See the examples/ directory:
basic_workflow.sh— init → add → commit → diffteam_sync.sh— push/pull with a shared remoteeval_driven_merge.sh— eval-gated merges in CI
Development
git clone https://github.com/your-org/prompt-git
cd prompt-git
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest tests/ -v # run tests
mypy pgit/ --strict # type check
ruff check pgit/ # lint
Tests use cassette recordings — no live LLM calls in CI.
Contributing
See CONTRIBUTING.md. New parsers, remote backends, bug fixes, and docs all welcome.
License
MIT © prompt-git contributors
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sr_prompt_git-0.1.0.tar.gz.
File metadata
- Download URL: sr_prompt_git-0.1.0.tar.gz
- Upload date:
- Size: 26.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
eabf87c13f669b3c744115bf50de9dcaf376ec483a1cf7b841a4238f5b28736d
|
|
| MD5 |
77dcfedf1d5a832ffe56f1b0d76100b1
|
|
| BLAKE2b-256 |
ff71bbbe77e20f37ef576821ac41cac0f3a838fc378cbe45f4dcdf7794bf4cdd
|
Provenance
The following attestation bundles were made for sr_prompt_git-0.1.0.tar.gz:
Publisher:
publish.yml on routsom/prompt-git
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sr_prompt_git-0.1.0.tar.gz -
Subject digest:
eabf87c13f669b3c744115bf50de9dcaf376ec483a1cf7b841a4238f5b28736d - Sigstore transparency entry: 1583813748
- Sigstore integration time:
-
Permalink:
routsom/prompt-git@b49c3df93aef21ca5585f38a6215fcb6dd2fced1 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/routsom
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b49c3df93aef21ca5585f38a6215fcb6dd2fced1 -
Trigger Event:
release
-
Statement type:
File details
Details for the file sr_prompt_git-0.1.0-py3-none-any.whl.
File metadata
- Download URL: sr_prompt_git-0.1.0-py3-none-any.whl
- Upload date:
- Size: 32.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
11473dfac626b38b9665b59118f5db197a14447ac34b29af410f3b025786177c
|
|
| MD5 |
96300143280bfe700d4b2be6e740aeda
|
|
| BLAKE2b-256 |
48bf3cc99bd19decd4d38544cb148086a3787e19bc1882ccc80ca9c1fe8b8938
|
Provenance
The following attestation bundles were made for sr_prompt_git-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on routsom/prompt-git
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sr_prompt_git-0.1.0-py3-none-any.whl -
Subject digest:
11473dfac626b38b9665b59118f5db197a14447ac34b29af410f3b025786177c - Sigstore transparency entry: 1583814108
- Sigstore integration time:
-
Permalink:
routsom/prompt-git@b49c3df93aef21ca5585f38a6215fcb6dd2fced1 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/routsom
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b49c3df93aef21ca5585f38a6215fcb6dd2fced1 -
Trigger Event:
release
-
Statement type: