Scan finds the prompts you forgot you wrote
blogus scan walks your repo and lists every OpenAI and Anthropic call it can recognize, with file path, line number, and a status flag. Unversioned strings show up immediately.
MIT · published to PyPI
Extract prompts from your codebase, version them like dependencies, and keep everything in sync. Blogus is a Python CLI plus library that treats prompts as a first-class artifact: discovered, locked by content hash, and verified in CI.
$ uvx 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
$ blogus init && blogus lock
Wrote prompts/summarize.prompt
Wrote prompts.lock (3 prompts, sha256 pinned)
$ blogus verify || exit 1
ok — lockfile matches
What the CLI gives you
Every step from discovery to CI gate has a single command. They compose; you do not have to glue them together.
blogus scan walks your repo and lists every OpenAI and Anthropic call it can recognize, with file path, line number, and a status flag. Unversioned strings show up immediately.
Each prompt becomes a .prompt file with a frontmatter block: name, model id, temperature, variables, and the template. Diffs are legible; reviewers can actually approve them.
blogus lock writes a SHA256 hash and the commit it was taken from for every prompt. CI fails if the locked hash and the on-disk content disagree.
When a .prompt changes, blogus fix replaces the inline string in your code with a load_prompt() call and a sha256 marker comment. Your runtime now reads from the versioned file.
blogus verify exits non-zero if the lock file does not match the prompts on disk. Drop it into pre-commit or the build step and stop merging unreviewed prompt changes.
blogus check finds prompts that exist as inline strings but never made it into .prompt files. It is the inverse of scan: what slipped through last sprint?
blogus exec <name> renders the template with variables and calls the configured model. Works without writing a line of application code, useful for spot-checking changes.
The pipeline
01 · scan
Walks the repo and flags OpenAI and Anthropic calls in Python and JavaScript with file path and line number.
02 · version
YAML frontmatter (name, model, temperature, variables) followed by a Jinja-style template body. One file per prompt.
03 · lock
SHA256 of every template plus the commit it came from. Reviewable, diff-able, signed by the same git you already use.
04 · sync
blogus fix rewrites your code to load_prompt() with a sha256 marker comment; blogus verify fails CI if anything drifts.
A .prompt file
Reviewable in any diff tool. Defaults inline. Variables typed. Models pinned to a specific id and temperature so accidental drift in a SDK does not silently change behaviour.
---
name: code-review
description: Review code for bugs
model:
id: gpt-4o
temperature: 0.3
variables:
- name: code
required: true
- name: language
default: python
---
Review this {{language}} code for bugs:
{{code}} Honest comparisons
Most teams already use something here. The fair question is what each one is for, and where the seams are.
PromptLayer is a hosted observability + registry for prompts. blogus stays in your repo: the source of truth is files and a lock, not a SaaS dashboard.
Langfuse leans toward LLM tracing and evaluation. blogus is a build-time tool — it never sees production traffic; it makes sure what shipped is what was reviewed.
FAQ
The README documents detection of LLM API calls in Python and JavaScript source files. The scan output lists OpenAI and Anthropic invocations with file path and line number.
blogus lock produces a prompts.lock file that, for every .prompt, records a sha256 of the template content and the git commit it was generated from. blogus verify re-hashes on disk and exits non-zero if anything has changed without an updated lock entry.
A .prompt file is YAML frontmatter (name, model.id, model.temperature, variables) followed by the template body with Jinja-style {{variable}} placeholders. Variables can be marked required or given a default.
It replaces inline string concatenations with a load_prompt("name", var=value) call and inserts a comment like # @blogus:summarize sha256:a1b2c3d4 above it. The marker is what blogus verify reads to confirm sync.
No. blogus is a Python CLI and library. The lock file is text. Everything lives in your repo and your CI. The optional blogus[web] extra ships a local browser UI on http://localhost:8000.
uv add blogus, or uvx blogus scan to try it without installing. The TUI demo (blogus demo) lives behind the blogus[tui] extra. PyPI publishes it as blogus.
Yes. blogus verify only reads files and hashes — no model calls. Reserve OPENAI_API_KEY / ANTHROPIC_API_KEY for the exec, analyze, and test commands that actually invoke a model.
The scan step recognizes direct LLM SDK calls. Dynamically constructed prompts can be checked with blogus check, which flags template strings present in code but absent from the .prompt directory.