██████╗ ███████╗██╗ ██╗██╗ ██████╗ ██████╗
██╔══██╗██╔════╝██║ ██║██║ ██╔═══██╗██╔════╝
██║ ██║█████╗ ██║ ██║██║ ██║ ██║██║ ███╗
██║ ██║██╔══╝ ╚██╗ ██╔╝██║ ██║ ██║██║ ██║
██████╔╝███████╗ ╚████╔╝ ███████╗╚██████╔╝╚██████╔╝
╚═════╝ ╚══════╝ ╚═══╝ ╚══════╝ ╚═════╝ ╚═════╝
Most developers don't keep a dev log — or they abandon one within a week. But
AI coding agents already sit in the loop where the work happens. devlog
teaches them, once per project, to narrate that work as they go.
The result: a time-ordered, portfolio-ready record of your project that grows automatically, in the agent's own voice.
- Why devlog?
- What you get
- Quickstart
- What actually happens
- Commands
- Slash commands (Claude Code, OpenCode)
- Adaptive convention
- Supported agents
- How it works
- Configuration
- Upgrading
- Uninstalling
- License
A codebase is the what. The why — the dead ends, the constraint that ruled out the obvious design, the reason a function looks wrong but isn't — lives somewhere far more fragile. With an AI agent it lives in a chat transcript you'll close and never reopen. The commit captures the change, never the reasoning behind it. Six months later that reasoning is simply gone, and the next person — or the next agent — re-derives it from scratch or breaks it by accident.
devlog's real output isn't a blog. It's a durable, git-versioned, cross-agent record of why the code is the way it is — written down at the moment the reasoning is fresh, by the agent that did the reasoning. The portfolio-ready blog is just the human-facing rendering of that record.
The premise holds harder for agent-written code than for hand-written code:
- The reasoning never lived in your head. You didn't write the code — the agent did. The thinking is in a transcript you're about to discard, not in your memory.
- Commits carry the what, never the why. Reconstructing intent from git metadata after the fact is lossy by design — and capped by how good the commit message was.
- Native agent memory can't hold this. It's private, unversioned, and harness-bound — locked to one vendor's tool. It doesn't travel with the repo, survive a switch from one agent to another, or open up to a collaborator.
A dev log usually dies for one reason: the human is the write-trigger, and discipline lapses. devlog moves the trigger to the agent that's already in the loop where the work happens — so the record gets written because the work got done, not because someone remembered to journal.
Capture is best-effort: the agent follows the convention because it's in its
context — polite, not guaranteed. For Claude Code, an optional Stop hook
makes it far more reliable by nudging the agent before it ends a turn, and
devlog status reports when sessions ended without producing an entry — so the
blind spot is at least visible.
A blog/ directory that fills itself in as the agent works — and, underneath
it, a project memory that outlives any single session, agent, or vendor.
- A "why" record that survives. Decisions, tradeoffs, and dead ends get captured in git, in the agent's own voice, as they happen — not reconstructed post-hoc from commit messages.
- Institutional memory across sessions and agents.
.devlog/learned.mdaccumulates the project's vocabulary, recurring themes, and open threads;/devlog-catchupreloads the project arc, recent work, and glossary at the start of a session. Any agent inherits the context — not just the one that wrote it. - Onboarding for humans and agents alike. A new collaborator, or the next agent to touch the repo, reads the narrative instead of re-deriving the reasoning by hand.
- Durable and versioned. It lives in git and travels with the repo, surviving across agents and harness changes — no vendor, no private store, no lock-in.
- A portfolio-ready record as a byproduct. Time-ordered, narrative, written for whoever evaluates the work next — collaborators, employers, researchers.
- Self-maintaining. The tag vocabulary self-updates, the convention can
re-tailor its config to the project's domain, and
/devlog-manicureaudits and prunes stale entries so the record stays trustworthy.
How it compares:
| Approach | What it produces | The catch |
|---|---|---|
| devlog | Narrative why, written as the work happens | Best-effort capture (firmer with the hook) |
| Commit-driven journals | Narrative reconstructed from git after the fact | Lossy; quality capped by commit messages |
| Changelog generators | Structured release notes for upgraders | Not narrative; wrong audience |
| Manual markdown diaries | Exactly the right narrative | You have to remember to write it — and won't |
Two commands. No install.
uvx --from git+https://github.com/b3008/devlog.git devlog init
uvx --from git+https://github.com/b3008/devlog.git devlog install --ai claudeOr install it as a uv tool for repeated use:
uv tool install git+https://github.com/b3008/devlog.git
devlog init && devlog install --ai claudeInstall once and every project gets a blog — no per-project install needed:
devlog install --ai claude --global --with-hook
# or, for OpenCode (hooks are claude-only):
devlog install --ai opencode --globalThis injects the convention into the agent's global context file
(~/.claude/CLAUDE.md for Claude Code, ~/.config/opencode/AGENTS.md for
OpenCode) with self-bootstrapping instructions: the agent creates blog/,
.devlog/, and learned.md on its first entry in any project. Per-project
customization is still available via devlog init + config edits in any repo.
Running a per-project devlog install on top of a global install injects a
thin pointer block instead of duplicating the full convention (which
would cost ~1.5k duplicated context tokens per session and let the copies
drift). Pass --full to force the standalone convention — useful for repos
whose collaborators don't have the global install.
devlog install drops a single, bracketed block into your agent's context
file — leaving whatever you already had intact:
# My Project
<existing CLAUDE.md content stays untouched>
+
+ <!-- DEVLOG:START - Do not edit manually. Remove with: devlog uninstall --ai <key> -->
+ ## Development Blog (Automatic)
+ After every session where meaningful progress is made, create or
+ update a blog entry in `blog/`.
+ ...triggers, structure, voice, tags, media instructions...
+ <!-- DEVLOG:END -->From then on, the agent writes entries like this without being asked:
---
type: "Devlog Entry"
title: "Tag vocabulary now self-updates"
date: 2026-04-16
tags: [feature, cli, ux]
description: "devlog install now folds tags from existing entries into the rendered vocabulary."
---
## What changed
...
## Why it matters
...
## How it works
...
## What's next
...Entries are Open Knowledge Format (OKF) concept documents — plain markdown with YAML frontmatter whose one required field is type. The blog/ directory is an OKF bundle: blog/index.md is the reserved bundle index (stamped with okf_version), and entries cross-link through ordinary markdown links. That makes a devlog readable by any OKF-aware tool or agent, not just this one. Already have a pre-OKF blog? Run devlog migrate — it's automatic on your next devlog install.
| Command | What it does |
|---|---|
devlog init [--name NAME] |
Scaffold .devlog/, blog/, blog/media/, blog/index.md, and .devlog/learned.md. |
devlog install --ai <key> |
Inject the convention into the agent's context file. Auto-runs init if needed. |
devlog install --ai <key> --global |
Install into the agent's global config (~/.claude/CLAUDE.md, ~/.config/opencode/AGENTS.md) so the convention applies to every project. Supported: claude, opencode. |
devlog install --ai <key> --force |
Overwrite locally-edited hooks and slash commands with the shipped templates. Untouched files resync on their own; use this to discard an edit you know is obsolete. Never deletes files. |
devlog uninstall --ai <key> |
Remove the convention section and manifest. |
devlog uninstall --ai <key> --global |
Remove the global convention from the agent's global config dir. |
devlog index |
Regenerate blog/index.md from entry frontmatter (newest first). |
devlog migrate |
Bring an existing blog up to Open Knowledge Format conformance (adds type, renames summary→description, renames the index to index.md, stamps okf_version). Idempotent; --check previews. Also runs automatically during devlog install. |
devlog list |
List all supported agents. |
devlog status |
Show which agents currently have the convention active. |
devlog upgrade |
Upgrade the devlog tool, then resync this repo's convention to it. --check previews; --project-only / --tool-only scope it. |
devlog version |
Print version. |
Installing for Claude Code also drops four slash commands into
.claude/commands/ (or ~/.claude/commands/ for --global installs).
Installing for OpenCode drops the same four into .opencode/commands/
(globally: ~/.config/opencode/commands/) — the command format (markdown
body, description frontmatter, $ARGUMENTS placeholder) is shared.
They give you direct, on-demand control over the blog from inside any
session — no flag needed, they ship by default.
In a project that hasn't been initialized yet, /devlog-write will
bootstrap the scaffolding (.devlog/, blog/, learned.md) on first
use — following the global convention's First-time setup instructions
or running devlog init if it's available. /devlog-catchup and
/devlog-manicure are read-only; they report "no devlog yet" and exit
gracefully if the project hasn't been scaffolded.
| Command | What it does |
|---|---|
/devlog-catchup |
Reads blog/index.md, the 5 most recent entries, and .devlog/learned.md, then returns a structured project briefing — project arc, recent work, open threads, glossary highlights. Use at the start of a session to load context. |
/devlog-write <topic> |
Writes a new entry about the given topic. Computes the next per-day index NN and ISO timestamp, derives a kebab-case slug, follows your project's convention (sections, voice, tags from .devlog/config.yaml), and updates blog/index.md. Refuses vague input rather than fabricating. |
/devlog-manicure [topic] |
Four-phase audit of past entries: categorizes findings (followed-through, revised, discarded, drifted, etc.), writes a recap entry, then proposes wipes or dated blockquote annotations (> **Update YYYY-MM-DD**: …) for you to approve before applying. Optional topic argument scopes the manicure to a single thread. |
/devlog-upgrade [flags] |
Upgrades the devlog tool to the latest from GitHub, then resyncs this repo's convention to it. A thin driver over the two-layer devlog upgrade CLI: it previews with --check, applies when the install is self-upgradeable, and falls back to the right manual step (e.g. git pull for a source checkout) when it isn't. Scope with --tool-only / --project-only. |
The first three form a working loop: catchup loads the blog into context, write adds new entries, manicure audits and prunes what's already there. upgrade is maintenance — it pulls the latest devlog and resyncs the convention to it. Uninstall removes them automatically.
The convention isn't a frozen snapshot. Three lightweight mechanisms let it grow with the project:
.devlog/learned.md— a shared notebook the agent reads before writing and appends to when durable project knowledge surfaces (domain vocabulary, recurring themes, open threads). It lives outside the injected sentinel block, so accumulation is free and diffable. Humans can edit it too.
Self-updating tag vocabulary — on each
devlog install, entries inblog/are scanned and any tags found in their frontmatter are unioned into the rendered tag list. The agent is told it may introduce new tags when they genuinely fit; the next install folds them into the canonical vocabulary.
Self-tailoring config — if the default triggers or voice don't match the project's domain (say, creative writing rather than a code project), the injected convention instructs the agent to propose edits to
config.yamland prompt the user to re-run install. The tool adapts to the project rather than the other way around.
Together these let the convention absorb what the project has actually been
doing — without anyone curating config.yaml by hand.
The convention asks the agent to self-check at the end of each turn, but agents can interpret rules differently depending on whether the turn produced an artifact. For Claude Code, opt into a hook bundle:
devlog install --ai claude --with-hookThis installs two small scripts under .devlog/hooks/ and merges the
matching entries into .claude/settings.json (preserving any existing
config):
stop.py(Stop) — injects a one-shot reminder before the agent ends its turn, via the structured block channel (no error styling).session_end.py(SessionEnd) — appends one line per session to.devlog/sessions.jsonl, sodevlog statuscan report sessions that ended without producing an entry.
Reinstalls are idempotent and carry existing hooks forward even without
the flag (refreshing stale scripts); locally-customized hook scripts are
detected by hash and preserved — including across later template changes,
since the manifest records what devlog last wrote separately from what is
on disk. Pass --force to discard a local edit you know is obsolete; it
says which files it overwrote, and never deletes. When both global and per-project hooks
are installed, the global instance defers to the project's at runtime —
one reminder, one session record. devlog uninstall --ai claude removes
the hook entries, deletes the scripts (unless customized), and leaves
any unrelated settings untouched.
devlog status reports whether entries are actually being produced — not
just whether the sentinel block is present. If the install is more than a
day old and no entries have been written, it prints a warning with
remediation hints. With the SessionEnd hook installed it also reports
session coverage — how many sessions ended since the last entry was
written, devlog's blind spot.
Installs are version-aware: the manifest records the devlog version that
performed the install, and the sentinel block carries a version stamp
(<!-- DEVLOG:START v<version> ... -->). status flags drift — an
install from an older version, an unstamped convention block, or
artifacts whose recorded hashes differ from the currently shipped
templates — and points at devlog install --ai <key>, which doubles as
the upgrade command (customized files are preserved). If the install
was made by a newer devlog than the one running, it recommends
upgrading the tool instead, since a resync would downgrade.
Blog: blog/ — 3 entries, most recent 2026-04-15
Sessions: 12 recorded, last 2026-04-17 — 4 since the last entry
Installed Conventions
┏━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━┓
┃ Agent ┃ Context File ┃ Status ┃ Version ┃ Installed ┃
┡━━━━━━━━━━━━━╇━━━━━━━━━━━━━━╇━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━┩
│ Claude Code │ CLAUDE.md │ active │ 0.1.0 │ 2026-04-10 │
└─────────────┴──────────────┴────────┴─────────┴────────────┘
Claude Code: installed by devlog 0.1.0, current is 0.2.0. Run
devlog install --ai claude to resync (customized files are preserved).
Four agents get first-class integrations:
| Key | Agent | Context file | Extras |
|---|---|---|---|
claude |
Claude Code | CLAUDE.md |
slash commands, --global, --with-hook |
opencode |
OpenCode | AGENTS.md |
slash commands, --global |
copilot |
GitHub Copilot | .github/copilot-instructions.md |
— |
gemini |
Gemini CLI | GEMINI.md |
— |
23 more agents use the shared AGENTS.md standard — click to expand
codex · cursor-agent · kimi · qwen · agy · trae · roo ·
bob · auggie · kilocode · windsurf · codebuddy · vibe ·
amp · kiro-cli · tabnine · goose · pi ·
forge · shai · iflow · junie · qodercli
Run devlog list for the live registry.
initdrops a default.devlog/config.yaml, creates the blog skeleton, and scaffolds.devlog/learned.md.installrenders the config into a markdown block and writes it to the agent's context file between sentinel markers. Existing content is preserved; re-installing replaces only the section between the sentinels. A SHA-256 of the resulting file is saved to.devlog/manifests/<agent>.manifest.json.uninstallstrips the sentinel block and removes the manifest.
your-project/
├── .devlog/
│ ├── config.yaml # stable convention settings
│ ├── learned.md # agent-maintained project notebook
│ ├── .gitignore # ignores the runtime session log
│ ├── hooks/ # Stop + SessionEnd scripts (--with-hook)
│ ├── sessions.jsonl # session coverage log (gitignored)
│ └── manifests/
│ └── claude.manifest.json # install tracking
├── .claude/ # claude installs (.opencode/ for opencode)
│ └── commands/ # slash commands
│ ├── devlog-catchup.md
│ ├── devlog-write.md
│ ├── devlog-manicure.md
│ └── devlog-upgrade.md
├── blog/ # OKF bundle
│ ├── index.md # bundle index (okf_version stamp)
│ ├── 2026-04-16-01-first-entry.md
│ └── media/
└── CLAUDE.md # convention injected between sentinels
Edit .devlog/config.yaml to customize:
| Key | Purpose |
|---|---|
blog_dir / media_dir / index_file |
Where entries and media live. |
sections |
Headings each entry should have. |
voice |
Tone and audience guidelines baked into the convention. |
triggers |
When the agent should write an entry. |
tags |
Base tag vocabulary (auto-extended by discovery). |
frontmatter |
YAML frontmatter fields for each entry. |
media |
Supporting-artifact instructions (CLI output, diffs, Mermaid, user-provided screenshots). Set enabled: false to skip. |
Re-run devlog install --ai <key> after editing to regenerate the injected
section.
A devlog install has two layers — the tool itself and the convention it
dropped into your repo — and they version independently. devlog upgrade brings
both up to date in one step:
devlog upgradeIt detects how the tool was installed, runs the matching upgrade (e.g.
uv tool upgrade devlog), then re-invokes the freshly installed binary to
resync every agent in this repo — convention block, hooks, and slash commands —
preserving any files you've customized. Preview first with --check; scope with
--project-only (resync this repo only) or --tool-only (bump the binary only):
devlog upgrade --check # show what would happen, change nothing
devlog upgrade --project-only # resync this repo to the installed tool
devlog upgrade --tool-only # upgrade the binary, skip the resyncIf devlog is running from a source checkout or an ephemeral uvx invocation —
where there's no managed binary to replace — upgrade won't guess; it prints
the exact command to run and stops.
Prefer to do it by hand?
# 1. Upgrade the tool
uv tool upgrade devlog
# (or, for a clean reinstall from latest:)
# uv tool install --force git+https://github.com/b3008/devlog.git
# 2. Resync this repo's convention to the new tool
devlog install --ai claude # add --global if it was a global installdevlog status reports the version that performed the install versus the
running tool and flags any drift — and if the repo was last touched by a newer
devlog than your tool, it tells you to upgrade the tool first rather than
resyncing (which would downgrade).
# Remove from a specific project
devlog uninstall --ai claude
# Remove the global install
devlog uninstall --ai claude --globalThis removes the sentinel block from the context file, the Stop hook (if
installed) from settings.json, the hook script, and the manifest. Your
blog entries, .devlog/config.yaml, and .devlog/learned.md are left
untouched — they're your data, not ours.
If the context file (CLAUDE.md, AGENTS.md, etc.) is empty after
removing the devlog section, it's deleted automatically.
MIT — see LICENSE.