Prompts are dependencies. We just refuse to treat them like it. A production LLM
app has prompt strings scattered across a dozen files, concatenated inline, and
edited by whoever touched that feature last. Nobody knows which prompts exist,
which version is live, or whether the one in summarize.py matches the one the
eval ran against. We version our libraries down to the patch. Our prompts get
a content = "Summarize: " + text and a shrug.
blogus is a package.lock for AI prompts. It extracts the prompts already in
your code, versions them like dependencies, locks them with content hashes, and
syncs changes back to your source files. The tagline is exactly that:
"package.lock for AI prompts."
The core idea: discover, do not migrate
Most prompt-management tools ask you to move your prompts into their system
first. That migration is the reason they never get adopted. blogus inverts it.
It scans your existing code and finds the LLM calls where they already live, so
adoption is a scan, not a rewrite.
$ blogus scan
Found 3 LLM API calls:
src/chat.py:15 OpenAI unversioned
src/summarize.py:42 OpenAI unversioned
lib/translate.js:28 Anthropic unversioned
That output is the whole pitch in one screen. It found calls across Python and
JavaScript, identified the provider, and flagged every one as unversioned. You
did not move anything. You just found out what you have.
The workflow
blogus runs as a five-step loop that maps cleanly onto how you already think
about dependencies: extract, version, lock, sync, verify.
Versioning turns a discovered prompt into a .prompt file with front matter,
so the prompt becomes a real artifact with a name, a model config, and typed
variables:
# prompts/summarize.prompt
---
name: summarize
model:
id: gpt-4o
temperature: 0.3
variables:
- name: text
required: true
---
Summarize in 2-3 sentences: {{text}}
Locking generates prompts.lock, which is the mechanism that makes this a
dependency system rather than a folder of text files:
# prompts.lock
prompts:
summarize:
hash: sha256:a1b2c3d4...
commit: 4903f76
Each prompt gets a sha256 content hash and the commit it was locked at. Now
"which version of the summarize prompt is in production" has an answer you can
diff, not a guess.
How the sync works
The step that makes this more than a linter is fix. It rewrites your source
code to load from the versioned prompt instead of carrying an inline string:
# Before
content = "Summarize: " + text
# After
# @blogus:summarize sha256:a1b2c3d4
content = load_prompt("summarize", text=text)
The inline string becomes a load_prompt call annotated with the prompt name
and its hash. That comment is the link between code and lock file. Once it is
there, blogus verify can run in CI and fail the build if the code has drifted
from the locked prompt:
$ blogus verify || exit 1
This is the whole value. The hash in the code comment must match the hash in the
lock file. If someone edits the inline usage or the .prompt file changes
without a re-lock, verify catches it before merge. It is the same contract as a
lockfile check on your dependencies, applied to the prompts that actually drive
your model behavior.
The CLI has the surface you would expect around that loop: scan, init,
prompts to list, exec <name> to run a prompt with variables, analyze to
evaluate effectiveness, test to generate test cases, lock, verify,
check to find unversioned prompts, and fix. There is also a web UI
(uvx --with blogus[web] blogus-web on port 8000) and an interactive TUI demo.
Install is uv add blogus, or uvx blogus scan to try it with zero install.
Where it does not fit
A few honest limits.
Start with what it can even see. The scanner detects OpenAI SDK calls
(openai.chat.completions.create) and Anthropic SDK calls
(anthropic.messages.create), in Python and JavaScript/TypeScript files. That
is the detection surface. If your calls go through LiteLLM, Bedrock, a provider
router, or your own thin wrapper, or if your service is Go or Rust, scan
finds nothing and the whole pitch evaporates. Check that your stack is in that
window before you judge the idea by an empty scan.
The analyze command does LLM-powered effectiveness scoring and the test
command generates test cases. This is not a hidden detail, it is in the
signature: analyze takes a --judge-model. A model is grading your prompt.
LLM-as-judge is noisy, so treat those scores as a signal to investigate, not a
gate to block on. The trustworthy part of blogus is the deterministic core:
scan, lock, verify. The hash contract is exact. The quality scoring is fuzzy by
nature, and you should hold the two to different standards.
The fix step edits your source files. Rewriting inline strings into
load_prompt calls is a code transformation, and any codemod can get an edge
case wrong. There is a blogus fix --dry-run that previews the rewrite, and
you should use it before the real thing. Run it in a clean working tree, review
the diff, and keep it behind a PR. This is not a tool to run with uncommitted
changes.
It also introduces a load_prompt runtime dependency into your app. Your
prompts now live in .prompt files that have to ship and load at runtime. That
is a fair trade for versioning, but it is a trade. If your prompts are trivial
and never change, the lockfile ceremony is overhead you do not need. The value
scales with how many prompts you have and how often they drift.
And it manages the prompt text and model config. It does not manage what the
model actually returns. A locked, verified prompt can still produce a worse
answer after a model provider updates their model under the same name. blogus
pins your side of the contract, not the provider's.
Takeaways
- Prompts are dependencies, and the reason they rot is that nothing treats them like one. A content hash plus a lockfile fixes the "which version is live" question.
- Discover-in-place instead of migrate is why this can actually get adopted. The first command is a scan, not a rewrite.
- The
verifystep in CI is the payoff. Code drift from the locked prompt fails the build, same as a dependency lock check. - Trust the deterministic core (scan, lock, verify) more than the LLM-powered analyze and test features. Different reliability, different standards.
If your codebase has more prompts than anyone can list from memory, blogus is a five-second way to find out how many. Code and CLI reference are
scan
here: https://github.com/Skelf-Research/blogus
I would genuinely like to know the highest prompt count anyone's scan turns up.
Kick the tyres, issues welcome.
Top comments (0)