Author prompts in git with promptctl
This guide sets up a git repo of prompt files and walks one prompt from first commit to a live server. You get the same habits developers have with code: every commit is validated, every edit gets a semantic verdict before it ships, and environments are git branches.
The video shows the whole flow in a terminal.
Prerequisites
Before you begin, make sure you have:
promptctl: rungo install github.com/priompt/cli/cmd/promptctl@latest(Go 1.25 or later). Git is embedded, sopromptctldoesn't need agitbinary. You still needgitforgit initandgit branchbelow.- A running Priompt server: see the Quickstart. This guide uses
localhost:8443. - An
rwtoken, if the server has authentication on. See Secure a server.
How files map to prompts
promptctl reads every *.prompt file under the repo root:
- The path is the address.
acme/support/agent.promptis published aspriompt://acme/support/agent. - The first directory is the org. A
.promptfile at the repo root has no org, so validation rejects it. - Slots come from the template. Each
{name}placeholder becomes a slot, so you never declare slots by hand.
1. Create a prompts repo
To create the repo:
-
Initialize a repo on a
mainbranch and set your identity.promptctlreadsuser.nameanduser.emailfrom git config for its commits.git init -b main prompts && cd promptsgit config user.name Ada -
Add a remote if you want
promptctl pushto sync it. HTTPS remotes read a token fromPRIOMPT_GIT_TOKEN, and SSH remotes use your ssh-agent.promptctldoesn't use git's credential helper.git remote add origin <your-remote-url>
2. Write and commit a prompt
promptctl commit validates every prompt in the tree first. One invalid file blocks the whole commit.
To commit a prompt:
-
Write the prompt and commit it:
mkdir -p acme/supportecho "You are a support agent for {org}. Be concise and kind." > acme/support/agent.promptpromptctl commit -m "first prompt"Outputcommitted 452b2079108e -
Add a second prompt. This one has a mistake:
{{name}}is an escaped literal brace to most formatters, not a slot.echo "Hello {{name}}, how can I help?" > acme/support/greeting.promptpromptctl commit -m "add greeting"Outputinvalid acme/support/greeting.prompt: template uses doubled braces near "{{name}}, how can I help?\n": slots are written {name}; {{name}} is an escaped literal brace to a formatter, not a slot2026/10/18 06:04:37 1 invalid prompt(s); nothing committedThe command exits with status 1, and nothing is committed.
-
Fix the template and commit again:
echo "Hello {name}, how can I help?" > acme/support/greeting.promptpromptctl commit -m "add greeting"Outputcommitted 6545940c0e26
3. Check what an edit means
promptctl diff runs the semantic diff on every prompt that differs from a git ref (default HEAD). The last line of each block is the verdict: minor edit, localized tweak, or structural.
To check an edit:
-
Edit the agent prompt:
echo "You are a support agent for {org}. Be concise, kind, and cite the help center." > acme/support/agent.prompt -
Diff the working tree against
HEAD:promptctl diffOutput## acme/support/agent.promptchange @ new lines 1-1 (old 1-1): replaceSignal 2 (point delta): 0.126Signal 3 up: ±2=0.126 (boundary)Signal 3 down: ±2=0.126 (boundary)=> minor edit -
Diff against an older ref to see several changes at once. A prompt that doesn't exist at the ref reads as
new:promptctl diff HEAD~1Output## acme/support/agent.promptchange @ new lines 1-1 (old 1-1): replaceSignal 2 (point delta): 0.126Signal 3 up: ±2=0.126 (boundary)Signal 3 down: ±2=0.126 (boundary)=> minor edit## acme/support/greeting.promptnew prompt (not in HEAD~1)=> new
promptctl diff exits with status 0 whatever the verdict. To fail a build on structural, add -fail-on structural, which exits with status 1 on a match. See Gate prompt changes in CI. Without an embedding model, the diff compares words, not meaning. To score meaning, see Use a real embedding model.
4. Commit and review history
To commit the edit and see a prompt's history:
-
Commit the edit:
promptctl commit -m "cite the help center"Outputcommitted 7876cc4189ce -
List every commit that touched the prompt, newest first:
promptctl log acme/support/agent.promptOutput7876cc41 cite the help center452b2079 first prompt
5. Push and publish
promptctl push validates every prompt, pushes all local branches to origin, and then publishes every prompt in the working tree to the server given by -server. Publishing is idempotent: unchanged prompts don't create commits or notify subscribers.
To publish:
-
Set your token if the server has authentication on.
promptctlsends it as a bearer token.export PRIOMPT_TOKEN=<token> -
Push and publish:
promptctl push -server localhost:8443Outputpushedpublished priompt://acme/support/agent (a4a2959e6ed2)published priompt://acme/support/greeting (033b02b3e3f7)The hash after each URI is the version the server now serves.
-
Run the same command again with no changes. Git has nothing to push, and the server keeps the same versions:
Outputalready up to datepublished priompt://acme/support/agent (a4a2959e6ed2)published priompt://acme/support/greeting (033b02b3e3f7)
To publish without touching the git remote, use promptctl publish -server localhost:8443. Pass file paths after the flags to publish only those prompts. For a TLS server, add -tls. For a certificate from a private CA, also add -ca-cert <ca.pem>, where <ca.pem> is the CA that signed it; without it, promptctl trusts the system CA roots.
6. Promote between environments
Environments are git branches. promptctl promote copies one prompt from one branch onto another, validates it, and commits it there.
To promote a prompt:
-
Create the environment branches once:
git branch staginggit branch prod -
Commit a change on
main, then promote it one step at a time:echo "You are a support agent for {org}. Be concise and kind. Cite the help center when you can." > acme/support/agent.promptpromptctl commit -m "soften citation rule"promptctl promote acme/support/agent.prompt main stagingpromptctl promote acme/support/agent.prompt staging prodOutputcommitted fd503c353514promoted acme/support/agent.prompt onto staging (26831f11d647)promoted acme/support/agent.prompt onto prod (7815a16a0a9d)promotereturns to the branch you started on, so you're still onmain. -
Check out
prodand publish it to the production server:git checkout prodpromptctl push -server prompts.example.com:8443 -tls -ca-cert ca.pemOutputpushedpublished priompt://acme/support/agent (58a297711285)published priompt://acme/support/greeting (033b02b3e3f7)
Promoting a prompt that already matches prints already up to date: acme/support/agent.prompt on prod matches staging and commits nothing.
Each environment branch publishes to its own server's main. The server's own branches are a separate feature for agents. See Branch, pin, and roll back a prompt.
Next steps
- Gate prompt changes in CI: validate and diff every pull request.
- Use a real embedding model: get verdicts that compare meaning, not words.
- Hot-reload prompts safely: decide what an agent does when you publish.
- promptctl reference: every command and environment variable.