Milepost Markdown Agent Memory vs md2rich 2026
Milepost landed on Hacker News on July 15, 2026 with four points and a single comment, but the timing is more important than the score: the AI coding world just got its first credible plain-markdown long-term memory plugin, and the Markdown-first writing community has been waiting for it without knowing it. This guide walks through what Milepost is, why it matters for writers who publish Markdown to X Articles, Bluesky, and LinkedIn, and how a tool like md2rich slots into the workflow to keep the agent's memory clean enough that grep, git, and the next session can actually use it.
What Milepost actually is
Milepost is an MIT-licensed Claude Code plugin by Saša Mitrović that gives the agent a milestone-triggered, in-session work diary stored entirely as plain markdown files. You install it inside Claude Code with two commands:
/plugin marketplace add sashamitrovich/milepost
/plugin install milepost@milepost
Once installed, four pieces cooperate. A CLAUDE.md policy block tells the agent when a milestone is worth recording and how to write it. A throttled Stop hook (default: once per 15 minutes) gently nudges the agent to check for unrecorded milestones on long sessions. A read-only SessionStart hook surfaces the current project's STATUS.md back into context at the start of every session, matched via a git-root-aware project slug. A PreToolUse hook mechanically blocks diary writes containing secret-shaped content — API keys, AWS/GitHub/Slack tokens, JWTs, URLs with embedded credentials — so the policy alone does not have to be trusted.
Per project, on disk:
~/.claude/memory/milepost/<project>/
├── log.md # append-only, timestamped milestone entries
├── STATUS.md # living snapshot: current goal, where things stand, next step
└── reflections/ # optional /reflect syntheses
A real entry, copied from the README, looks like this:
## 2026-06-27 14:32 — Converted architect PDF to per-page JPEGs
**Type:** deliverable
**What:** Rendered all 4 A3 pages at 200 DPI with ImageMagick.
**Why:** Client needed individual page images for review.
**Next:** —
Readable by humans. Parseable by machines. Owned by you. No SQLite, no vector DB, no cloud, no lock-in. That last line is the part the Markdown-first writing community has been waiting for, because most AI memory systems trade portability for convenience in a way that breaks grep and breaks git, and the people who write everything in Markdown tend to be the people who care about that.
Why Markdown-first writers care
Three reasons. First, the writers who already keep their drafts in plain markdown (Obsidian vaults, Logseq journals, static-site drafts, GitHub repos) are the same writers most likely to use Claude Code for research-heavy long-form posts. They need an agent memory that lives where their drafts already live — in git, on disk, in grep. Second, the platform-specific wrappers around long-form social posts (Bluesky facets, X Articles rich blocks, LinkedIn URNs) are hostile to agent ingestion. If you paste raw HTML into a Markdown memory file, the diary fills up with platform metadata, tracking pixels, and broken inline styles, and the agent spends its context window re-reading garbage. Third, Milepost's milestone-triggered logging is closer to how writers actually work than continuous capture: a draft was published, a decision was reversed, a research thread hit a wall, an editor asked for a structural change. Those are milestones. Continuous token-by-token capture is not.
The other plugins in the space split into two camps. claude-mem captures everything automatically into SQLite plus a vector store — useful for retrieval, opaque to inspection, and creates the exact lock-in that Markdown-first users reject by default. claude-diary writes markdown but only when you run a command or after a session ends, which means the diary misses the in-session reasoning that often matters most. Milepost takes the third path: plain markdown, written as you go, triggered by milestones — not by a clock, a command, or a database.
The dirty secret about feeding social posts into agent memory
Writers who publish to X Articles, Bluesky long-form, or LinkedIn newsletter often want the published post to live in the agent's memory too — not just the draft. "What did I write about Bluesky federation last month?" is a normal question to ask Claude Code on a Tuesday morning, and the answer should not require re-Googling your own handle.
The problem is that copying a long-form post from X Articles or Bluesky and pasting it into a markdown file embeds platform metadata. Bluesky long-form exports include facet markup (mentions, links, tags) wrapped in angle-bracket delimiters that survive a copy but break in most Markdown parsers. X Articles rich-text blocks carry inline styles that look like <b> tags inside your plain text. LinkedIn posts include URNs for every mention. If you paste any of these into log.md, the file works in Notion but breaks in grep, breaks in git diff, and the agent spends the first 500 tokens of its next session trying to figure out which <span> is content and which is wrapper.
The fix is a normalization step: take the rich text from the platform, run it through a converter that emits clean Markdown, then drop the Markdown into the diary. md2rich is built on the browser's ClipboardItem API and runs the conversion locally with no upload, no account, no API key. Paste the published post into the editor, click "Copy as Markdown," and the clipboard holds a normalized plain-markdown version that survives grep, git, and any agent session that comes next.
A 5-step workflow: normalize → diary → recall
Step 1: Draft and publish as usual
Write the post in Markdown. Run the Markdown draft through md2rich to convert to rich text. Paste into X Articles, Bluesky long-form, or LinkedIn. Publish. This is the workflow every social publishing guide in 2026 recommends, and the conversion step has nothing to do with agent memory — it is just the standard paste-without-losing-formatting step.
Step 2: Copy the published rich text back out
After publishing, select the post in your platform's UI, copy it as rich text (Cmd-C on Mac in the X Articles composer, the standard copy in Bluesky, Cmd-Shift-C in some LinkedIn clients). The clipboard now holds platform-rich HTML with all the wrappers your agent memory does not want.
Step 3: Convert rich text → clean Markdown
Paste the rich text into the md2rich editor at md2rich.com. Click "Copy as Markdown." The clipboard now holds a normalized Markdown version with the platform wrappers stripped, the inline styles converted to asterisks and backticks, and the mentions and links rendered as plain [text](url). The conversion runs locally in the browser; nothing is uploaded, nothing is logged.
Step 4: Drop into log.md or STATUS.md
Open ~/.claude/memory/milepost/<your-publishing-project>/log.md and append a new milestone entry. The Milepost CLAUDE.md policy will eventually do this for you automatically when it judges a publication to be a milestone, but for the first few weeks of using the plugin, manual entries are useful while you learn the diary's rhythm:
## 2026-07-16 09:14 — Published "X Articles vs LinkedIn Newsletter" long-form comparison
**Type:** deliverable
**What:** Shipped the cross-platform comparison post, normalized from
rich text to clean Markdown via md2rich before logging here.
**Why:** Wanted the diary to keep a greppable record of every published
long-form post so future sessions can recall what I covered.
**Next:** Promote on Bluesky; rewrite the lede for a LinkedIn newsletter cut.
For a project-wide snapshot, the same normalized Markdown goes into STATUS.md. The living STATUS.md is what the SessionStart hook reads back into context the next time you sit down at Claude Code, so the agent knows you just published a comparison post and the next step is the LinkedIn newsletter cut without you having to re-explain it.
Step 5: Let the agent recall, verify, and continue
The next time you open Claude Code on this project, the Milepost SessionStart hook reads STATUS.md back into context. The agent already knows the project state. You can ask "what did I publish this week?" or "did I cover Bluesky federation yet?" without re-explaining the project history. The Milepost /status command refreshes the snapshot on demand; /reflect synthesizes recurring themes from the log. Both are read-only and never modify your entries without your say-so.
How Milepost compares to the alternatives
The honest comparison matrix from the README puts three plugins side by side. The differences matter more for Markdown-first writers than for AI-agent power users, because the writer's existing toolchain (Obsidian, Logseq, plain text editor, grep, git) is the one that has to keep working alongside whatever memory plugin is running.
| Dimension | Milepost | claude-mem | claude-diary |
|---|---|---|---|
| Storage | Plain markdown | SQLite + vector DB | Plain markdown |
| Greppable / exportable | Yes | Only via export | Yes |
| Writes during a session | Yes | Yes | No (session-end only) |
| Milestone-aware | Yes (high-signal) | No (captures everything) | No (session-level) |
| Trigger | Milestones | Continuous | Manual / pre-compact |
| Living status file | Yes (STATUS.md) | No | No |
| Database required | No | Yes | No |
| Secret-content guard | PreToolUse hook (blocks) | Policy only | Policy only |
The row that matters most for writers is the secret-content guard. Milepost does not just ask the agent nicely to leave secrets out of the diary; it blocks any diary write that matches a secret pattern, including shell redirection via Bash. The other two plugins rely on the CLAUDE.md policy alone, which is necessary but not sufficient for client work or anything where an accidental commit would be embarrassing. The audit command bash ~/.claude/hooks/milepost-secret-scan.sh < ~/.claude/memory/milepost/<project>/log.md returns exit 1 plus the matched pattern names on any hit, so existing diaries can be scanned retroactively.
Three real use cases for Markdown writers
Use case 1: long-running research thread
You have been writing a 5-post series on Bluesky federation for six weeks. Each post builds on the last, and you keep losing track of which ATProto protocol terms you have already defined for the reader. Milepost's STATUS.md holds the running list, refreshed at every milestone. At session start, the SessionStart hook surfaces it, and the agent knows what was covered and what is still pending. The diary entries are plain markdown, so when you are ready to assemble the series into an ebook (a separate project), the entries can be grep'd, sorted by date, and pulled into the manuscript in chronological order with no parser.
Use case 2: client work with strict NDAs
Some clients do not want their name in any long-term memory, ever. Drop a .no-milepost file at the project root and Milepost excludes the project entirely: no diary, no nudges, no recall. For client work where summaries are fine but the agent might accidentally leak a customer name, MILEPOST_STRICT=1 blocks IP addresses and e-mail addresses in addition to the standard secret patterns. The diary dir itself is chmod 700 — owner-only access — so even local users on the same machine cannot read it without sudo.
Use case 3: the publish-and-archive loop
You publish a long-form post to X Articles, then a shorter version to Bluesky, then a newsletter cut to LinkedIn. You want all three platforms and the original draft in one searchable place. The workflow: draft in Markdown, publish via md2rich-converted rich text to all three platforms, copy the published rich text back out of the platform, run it through md2rich again to extract clean Markdown, and drop the normalized output into Milepost's log.md as a milestone entry. The diary entry contains the canonical Markdown, the platforms each contain their own rich-text rendering, and a single grep across the diary dir returns every post you have ever shipped with no parser involved.
Configuration knobs worth knowing
Three settings control the diary's rhythm. MILEPOST_NUDGE_INTERVAL defaults to 900 seconds (15 minutes) and is the minimum gap between Stop-hook nudges; raise it to journal less often, lower it to journal more. The .no-milepost file is a per-project opt-out and is the cleanest way to keep client work out of the diary entirely. MILEPOST_STRICT=1 activates the IP and e-mail address blocklist in addition to the default secret patterns; set it in settings.json under "env": {"MILEPOST_STRICT": "1"} or in your shell profile so Claude Code's hooks can see it. For zero-hook operation, the CLAUDE.md policy works on its own; the diary just leans on the model to remember during very long sessions.
A neat detail from the README: the PreToolUse guard hook's high-entropy catch-all is tuned so that git SHAs, UUIDs, paths, and ordinary identifiers do not trip it. The threshold is 32-character base64url-ish runs mixing upper/lower/digits. A diary entry containing commit a3f8d92c1b4e is fine; a diary entry containing AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY is blocked with a message naming the matched pattern. The honest caveat applies: milestone detection is a judgment, so the diary may occasionally over- or under-log, and entries use a little of the working session's tokens. /milepost is the manual backstop for cases where the agent missed a milestone you care about.
Where md2rich fits
md2rich is not a memory plugin and does not pretend to be one. It is a client-side Markdown-to-rich-text converter that runs in the browser and writes to the clipboard via the standard ClipboardItem API. The fit with Milepost is in the normalization step: anything coming out of a social platform's composer is rich text with platform wrappers, and anything going into a Markdown diary should be clean plain Markdown. md2rich does the round trip in both directions. Draft to rich text for publishing. Published rich text back to Markdown for logging. The conversion is local, no upload, no account, no API key, no rate limit.
For long-running projects that span weeks of social posts and code work, this two-tool loop keeps the diary clean enough that grep, git, and the next Claude Code session can actually use it. The agent memory stays small (milestone entries, not every token), the storage stays portable (plain markdown, not SQLite), and the writing workflow stays the same (Markdown draft, md2rich conversion, paste into X Articles or Bluesky or LinkedIn). The only addition is the normalize-on-publish step that converts the published rich text back into clean Markdown for the diary.
If you write in Markdown, publish to X Articles, Bluesky, or LinkedIn, and use Claude Code for research-heavy long-form work, install Milepost first and bookmark try md2rich for the round trip. The two tools are smaller than the agents they serve, and that is the point. The agent's memory is yours. The conversion runs in your browser. The diary lives on your disk. None of it is locked inside a database, and a year from now you can still grep it.
Try md2rich — client-side Markdown to rich text
Open the editor, paste your Markdown, click Copy rich text, paste into LinkedIn, X Articles, Bluesky, or Notion. Or paste published rich text back in and click Copy as Markdown for a normalized version suitable for an agent memory file. The conversion runs in your browser tab using the standard ClipboardItem API; no upload, no account, no API key. Bold, italics, headers, links, fenced code blocks, and inline code all survive the round trip, and your draft never reaches a server.