The Story

This CLI was written during Tessa’s Tabstack livestream — start to published repo while the stream was still going.

How it started

I was watching Tessa’s Tabstack livestream and set myself a challenge: build a working CLI for the Tabstack API before the stream ended.

Not because anyone asked. I’d built CLIs before — for parallel-web, Smriti, Zoho Invoices, Godspeed, others — and had patterns that were evolved and repeatable. I wanted to see if those patterns were solid enough to apply to something new, live, in a single sitting.

One sitting

What surprised people watching was the pace. From blank repo to bun test passing to the first version pushed to GitHub — all inside one stream.

That’s not because the implementation is trivial. The Tabstack API has multiple transports (JSON for extract/generate, Server-Sent Events for research/automate), streaming edge cases (CRLF normalization, unterminated final frames), and enough verbs that the arg parser needs careful thought to stay readable.

It came together fast for one reason: the CLI pattern was already figured out from prior work. Before the first line of code, the shape was familiar:

  • Data on stdout, progress on stderr, always
  • JSON when piped, pretty when on a TTY, no flag required
  • Exit codes that mean something (0/1/2/3)
  • --help that never touches the network
  • Every command pipeable to the next

None of that was invented during the stream. It came from iterating on previous CLI projects — the parallel-web tooling in particular — until the pattern was solid enough to replicate quickly. Reuse is faster than reinvention, especially on camera.

The product-hunt moment

The moment that made the chat react: I used the CLI to check Tabstack’s own Product Hunt launch stats. Live. During the stream.

tabstack extract json https://www.producthunt.com/products/tabstack/launches 
  --schema '{"type":"object","properties":{"launches":{"type":"array","items":{
    "type":"object","properties":{"name":{"type":"string"},
    "upvotes":{"type":"number"},"comments":{"type":"number"}}}}}}'

Returns valid JSON matching the schema. The snake ate its own tail and it worked.

How it was dogfooded

Building the CLI with the CLI created a feedback loop that caught things a test suite wouldn’t.

The Product Hunt moment — I used tabstack extract json to check the CLI’s own Product Hunt launch stats during the stream. The schema-shaped output landed correctly. The snake ate its own tail.

Star drift — Before writing the June 2026 agentic harnesses post, I ran tabstack extract json against every GitHub repo mentioned. Claude Code at 83k claimed had drifted to 131k live — +59% in 11 weeks. The post was updated from that diff.

Filing an upstream issue — I used tabstack extract markdown on the official Mozilla-Ocho/tabstack-cli source to compare implementations. That’s how I found that their agent input command was unreachable: the --interactive flag was never wired to the request body. Filed issue #15 with the exact line.

The extract vs research experiments — Two controlled experiments, real credits, adversarial judge: extract beat research on quality at 4–25× lower cost. The result is now encoded into the agent skill that ships with the CLI. I ran those experiments using the CLI I was building, verified the credit spend with tabstack usage, and wrote the decision rule directly into the documentation.

The enrich scriptscripts/enrich-post.sh uses tabstack to fact-check blog posts before they go live. It was written using tabstack to research its own implementation. Recursive, but it works.

What happened after

The CLI kept going after the stream. The usage/credit tracking system, the extract-vs-research experiments, the agent skill that installs itself — all built in the sessions that followed. All of it documented here.

The stream forced a useful constraint: nothing abstract. Every decision had to be demonstrable, on camera, before the next one.


Try it

Tabstack gives new accounts 10,000 free credits — no credit card required. That’s enough for hundreds of extract calls, a few dozen research sessions, or a mix of browser automation tasks.


Why document this?

Because the design decisions are the interesting part. Not “here is the --flag reference” — you can get that from tabstack help. What’s here is why each design choice was made, what the tradeoffs are, and what breaks if you get it wrong.

Particularly the section on CLI design for agents — most CLIs are built for humans, occasionally with an --output json flag bolted on as an afterthought. Building for agents from the start changes what matters. Exit codes become contracts. stdout purity becomes a hard requirement. The shape of streaming output determines whether a pipeline can use your tool or not.

If you’re building a CLI, that section is for you.