devlog — Development Blog
Bundle OKF 0.1 · 0 conceitos · b3008/devlog
Open source Repository Open in the app JSON README (API)
About
# devlog — Development Blog
<!-- Generated by `devlog index` — newest first. Edit entries, not this list. -->
- 2026-08-02 — [Six releases in, the first tag](2026-08-02-01-first-tagged-release.md)
- 2026-07-31 — [Sweeping 24 projects: 1,082 entries, and ten of them weren't git repos](2026-07-31-03-fleet-sweep.md)
- 2026-07-31 — [The upgrade path was pointing at a branch four releases behind](2026-07-31-01-fleet-upgrade-audit.md)
- 2026-07-31 — [Closing a stale review comment without touching the code](2026-07-31-02-stale-review-comment.md)
- 2026-07-29 — [Preserving a customization until the next release](2026-07-29-03-preservation-baseline.md)
- 2026-07-29 — [Landing a three-feature working tree](2026-07-29-02-landing-the-okf-wip.md)
- 2026-07-29 — [Teaching learned.md to become a wiki](2026-07-29-01-learned-md-as-wiki.md)
- 2026-07-16 — [OpenCode gets the full install surface](2026-07-16-01-opencode-install.md)
- 2026-06-17 — [Blog entries now conform to the Open Knowledge Format](
Details
- Kind
- OKF bundles
- Topic
- Productivity
- Publisher
- b3008
- Origin
- okf_github
- Category
- dados
- Version
- 0.1
- Last push
- 2026-08-02T09:23:30Z
- Repository state
- ativo
- Language
- Python
- License
- MIT
- Added
- 2026-09-09 12:02:17
- Updated
- 2026-09-09 12:02:17
- Origin id
b3008/devlog:blog/index.md
README
<div align="center">
```
██████╗ ███████╗██╗ ██╗██╗ ██████╗ ██████╗
██╔══██╗██╔════╝██║ ██║██║ ██╔═══██╗██╔════╝
██║ ██║█████╗ ██║ ██║██║ ██║ ██║██║ ███╗
██║ ██║██╔══╝ ╚██╗ ██╔╝██║ ██║ ██║██║ ██║
██████╔╝███████╗ ╚████╔╝ ███████╗╚██████╔╝╚██████╔╝
╚═════╝ ╚══════╝ ╚═══╝ ╚══════╝ ╚═════╝ ╚═════╝
```
### A development blog that writes itself — through the AI agent you already use.
[](LICENSE)
[](https://www.python.org/)
[](#supported-agents)
</div>
---
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.
<br>
## Table of contents
- [Why devlog?](#why-devlog)
- [What you get](#what-you-get)
- [Quickstart](#quickstart)
- [What actually happens](#what-actually-happens)
- [Commands](#commands)
- [Slash commands (Claude Code, OpenCode)](#slash-commands-claude-code-opencode)
- [Adaptive convention](#adaptive-convention)
- [Supported agents](#supported-agents)
- [How it works](#how-it-works)
- [Configuration](#configuration)
- [Upgrading](#upgrading)
- [Uninstalling](#uninstalling)
- [License](#license)
<br>
## Why devlog?
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.
<br>
## What you get
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.md`
accumulates the project's vocabulary, recurring themes, and open threads;
`/devlog-catchup` reloads 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-manicure` audits
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 |
<br>
## Quickstart
Two commands. No install.
```bash
uvx --from git+https://github.com/b3008/devlog.git devlog init
uvx --from git+https://github.com/b3008/devlog.git devlog install --ai claude
```
Or install it as a `uv` tool for repeated use:
```bash
uv tool install git+https://github.com/b3008/devlog.git
devlog init && devlog install --ai claude
```
### One-time global setup (Claude Code, OpenCode)
Install once and every project gets a blog — no per-project install needed:
```bash
devlog install --ai claude --global --with-hook
# or, for OpenCode (hooks are claude-only):
devlog install --ai opencode --global
```
This 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.
<br>
## What actually happens
`devlog install` drops a single, bracketed block into your agent's context
file — leaving whatever you already had intact:
```diff
# 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:
```markdown
---
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](https://github.com/GoogleCloudPlatform/knowledge-catalog/tree/main/okf) (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`](#commands) — it's automatic on your next `devlog install`.
<br>
## Commands
| 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](https://github.com/GoogleCloudPlatform/knowledge-catalog/tree/main/okf) 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. |
<br>
## Slash commands (Claude Code, OpenCode)
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`](#upgrading) 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.
<br>
## Adaptive convention
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 in
> `blog/` 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.yaml` and 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.
### Optional: runtime enforcement (Claude Code)
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:
```bash
devlog install --ai claude --with-hook
```
This 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`, so `devlog status` can 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.
### Is it working?
`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).
```
<br>
## Supported agents
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` | — |
<details>
<summary><strong>23 more agents</strong> use the shared <code>AGENTS.md</code> standard — click to expand</summary>
<br>
`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`
</details>
Run `devlog list` for the live registry.
<br>
## How it works
1. **`init`** drops a default `.devlog/config.yaml`, creates the blog
skeleton, and scaffolds `.devlog/learned.md`.
2. **`install`** renders 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`.
3. **`uninstall`** strips the sentinel block and removes the manifest.
### Layout after install
```
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
```
<br>
## Configuration
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.
<br>
## Upgrading
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:
```bash
devlog upgrade
```
It 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):
```bash
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 resync
```
If 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.
<details>
<summary>Prefer to do it by hand?</summary>
<br>
```bash
# 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 install
```
`devlog 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).
</details>
<br>
## Uninstalling
```bash
# Remove from a specific project
devlog uninstall --ai claude
# Remove the global install
devlog uninstall --ai claude --global
```
This 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.
<br>
## License
MIT — see [LICENSE](LICENSE).