← Back to Blog
pullmdopen-sourceagplclaude-codeMay 4, 2026 · 9 min read

PullMD Part 8: From My Tool to an OSS Project – Simply Explained

PRIVATE → PUBLIC OSS · AGPL-3.0AI-generated image
Editorial diagram: PRIVATE on the left, PUBLIC OSS on the right, separated by a glowing threshold; AGPL-3.0 license boundary card and PullMD/MD nodes.Editorial diagram: PRIVATE on the left, PUBLIC OSS on the right, separated by a glowing threshold; AGPL-3.0 license boundary card and PullMD/MD nodes.

On April 25 the small tool I had been building for myself was finally ready to go public. The code was done. The README was not. And the internal working name still hadn’t been swept out of the source tree, for two reasons that had been sitting in my head since early March.

Reason 1: The Old Name No Longer Fit

The tool had grown up under a working name, a bit of wordplay that leaned on a Reddit reference. Obvious at the time, because back then the tool processed Reddit links and nothing else. Claude had suggested it in the very first concept chat, and I had taken it because I wasn’t thinking about branding at that point.

By now the tool was something quite different. Three paths to turn any web page into clean text. An interface that lets the AI assistant use the tool directly. A skill bundle you can load into a chat. A mini-app for the phone. Reddit is just one of several paths through it, and every path works for any URL.

A name that sounds Reddit-specific when the tool is actually universal sets the wrong expectation immediately. And a wrong expectation is less a branding problem than a support question nobody has asked yet. For clarity alone, something neutral was needed.

Reason 2: The Old Name Was Legally Borderline

There was also something else, and it was arguably the more important reason. A few days after the first concept chat, it became clear that the codename sounded phonetically a bit like a well-known brand from an entirely different industry. The company behind that brand is known for going after small projects too when a name sounds too similar. That is documented practice, not speculation.

The probability of something like that landing on a growing open-source project was high enough that I stopped treating the codename as the future public name. And anyone who has ever had mail from a law firm knows: even when you are in the right, it costs you weeks.

Already on March 8, weeks before the public release, I had set up the production directory on my server under the new name. That was effectively the decision: the codename stays internal, something else goes out the door. In conversations with Claude I had been using “PullMD” since mid-March. Only the source code itself still carried the old name, because internally it hadn’t mattered: one user, one server, nobody else looking at the code.

Before the step outside, “internally it doesn’t matter” turned into “this has to be cleaned up.” The choice was simple: thirty minutes of thorough cleanup, or the risk of a lawyer’s letter. The thirty minutes seemed more sensible.

The New Name: Descriptive, Boring, Good

PullMD. “Pull” as in pulling content from web pages. “MD” as in Markdown, the simple text format the tool hands back. That is the whole story. No wordplay, no reference to anything, no phonetic proximity to any brand in any category. Anyone who reads the name knows immediately what to expect.

Sometimes the boring name really is the best one.

Cleaning Up the Source Tree

Before going public, the old codename had to disappear from the entire project: the repository name, internal notes, configuration files, and even the old commit history. For the history cleanup, Claude Code used a specialised tool called git filter-repo. Think of it as a machine that walks through every single past snapshot of the project and replaces occurrences of the old name with the new one. The result: a clean history that looks as if the project had always been called PullMD.

Honestly: I let Claude Code handle the entire operation and didn’t look at the details until I was writing this article, weeks later. The operation is deterministic, and before the first public release there is no damage you can’t undo. If you trust the tool, watching and waiting is a legitimate mode. If you don’t, you should understand the commands before delegating, not afterwards while blogging.

The more delicate piece was the session archive. The transcripts of every conversation with Claude Code since March are the source this entire blog series is written from. Without them, no quotes, no reconstruction, no Part 2 or Part 5. Those got carefully backed up before the migration: a compressed archive in a private folder that never gets committed to the public repository.

The License: What Even Is That?

When you put code publicly on GitHub, you have to decide: what are other people allowed to do with it? A license is like a small notice pinned next to a community bulletin board: “You may take this, but only under these conditions.” Without that notice, legally, nobody is allowed to do anything with the code. There are many such licenses, but the two most common ends of the spectrum:

MIT license (named after the university where it originated): very permissive. Take the code, do whatever you want, you owe nothing in return. Ideal for small helper tools meant to spread as widely as possible.

AGPL (Affero General Public License): stricter, in one specific way. If someone runs the tool as an online service and builds improvements into it, they have to publish those improvements openly as well.

For a small helper library MIT would be the natural choice. For PullMD the situation is different: it is not a passively consumed package, it is a running service that answers requests. If someone takes PullMD, improves it, and offers it as their own online service without sending the improvements back, I never learn what the real weak spots in my code are. AGPL is what prevents that.

This is not an ideological statement about free software. It is a very practical thought: who has the incentive to report a bug once they have already fixed it for themselves?

More nuance on the different licenses, without needing a lawyer: choosealicense.com.

So Other People Can Run It Too

Up to that point the tool ran only on my own infrastructure. Anyone who wanted to install it at home would have seen my personal server addresses scattered through the code. That had to change.

A single settable variable, the “own address,” now makes sure the tool works on any domain, without poking around in the code. The README got a complete rewrite: no internal shorthand, no notes that only made sense to me. A stranger should be able to read it and understand what to do.

The Push

One command, one second, and the repository was open. Well, almost. Between “locally finished” and “actually public” there were two more days of polish. A small bug had to be fixed on my own server first before the whole thing was to go live for the world.

On April 27 the actual push went through. In the days that followed came a handful of small improvements: the kind of things you only notice once you look at your own work with real distance for the first time. A footer here, a layout detail there. A tool that started in March as a Reddit toy was, eight weeks later, officially open source: github.com/AeternaLabsHQ/pullmd.

What Came After

A running service on its own domain, one I use every day: for Reddit posts, for articles, for documentation pages, for anything I want to bring into a chat context as clean text. Underneath it sits everything that has come along since March: the three extraction paths with quality score, the MCP (Model Context Protocol) server for Claude Code, the skill bundle, the phone app for the Android share workflow.

Pages That Come Back Empty: the Browser Sidecar

Since April 25 a small statistics database had been running alongside the tool, logging which extraction path works and which doesn’t. The plan was to watch for a week and then decide whether the next step was worth it: a helper service for web pages that give you nothing without JavaScript.

The trigger turned out to be a single page. Mistral’s price list came back as 452 kilobytes of HTML that didn’t contain the prices at all. The reason: the browser is supposed to assemble them on the fly when someone visits the page. My tool doesn’t run a browser, so the prices simply weren’t there. The two extraction tools in the stack, Readability and Trafilatura (think of them as different strategies for pulling the main article text out of a messy web page), did their best: Readability got 19 headings and three paragraphs, Trafilatura managed a whole 283 characters.

On top of that came the number from the log. Of 29 web fetches, static extraction had failed on four: 14 percent. The threshold at which the effort would be worth it at all was ten, according to the briefing.

What got built from that was a second sidecar, a helper container sitting next to the main service. Inside it: a browser without a window. It visits the page, waits for JavaScript to finish assembling the content, and hands back the fully rendered HTML. Then the existing extraction tools do their best again. A browser without a window sounds odd. But it is basically what your phone does when you visit a page, minus the screen. The page loads, the scripts run, everything assembles. The sidecar just does that step silently, in the background.

It only kicks in when the normal, fast path has clearly failed: too few characters left, many headings but barely any paragraphs, or the quality score has dropped below a threshold. In all other cases the tool stays with the fast static route. If the sidecar isn’t running at all, nothing breaks. You just get the weaker result with a note about what went wrong.

The plan had been to watch for a week. The sidecar was standing two days later, on April 27, a few hours after the public push, and shipped as version 1.1.0. That saved me the week of watching, because I would only have needed it if the number had been close.

What was still missing was the most obvious thing: showing it to somebody. One day after the push I wrote a post on r/ClaudeAI for exactly that. What that set off is in Part 9.


Part 8 of 15 in the PullMD series.

← PullMD Part 7: The MCP Server | PullMD Part 9: Reddit Day and the First Issues →

Comments

Commenting takes an email confirmation or a GitHub login. Comments run on my own server, not on an ad provider's — details in the privacy policy.