A developer recently open-sourced a full WeChat publishing toolkit: write Markdown, hand it to a CLI, and it runs four ordered stages — prepare, review, render, publish — all the way into the official draft box. It's a nice kit. But if you read past the packaging, the genuinely reusable idea is smaller and more boring than the headline features, and that's exactly why it travels well.
The parts that got the attention — the Canvas cover renderer, the Milkdown typesetting packages, the WeChat proxy with a whitelisted IP — are all bound to one platform and one runtime. Swap WeChat for anything else and most of that evaporates. What survives is the spine: an ordered stage state machine that persists everything to disk and resumes mid-run after a timeout, plus a `content_review` gate that keeps a draft marked `unchecked` until someone explicitly moves it to `trusted`, locking cover generation and publishing until then.
Why the spine is the product
Two things in that design are worth copying regardless of where you publish.
First, resumability. The original author makes the point plainly: when a step times out on the network, re-running picks up where it left off instead of starting over. If you've ever had a long automation die on the last call and leave you guessing what already happened, you know why that matters. State on disk, read before every stage, is the whole trick.
Second, the trust gate. The reasoning behind it is honest about how AI drafts behave — first drafts hallucinate, invent details, and pad with filler. So the default state is `unchecked`, downstream actions are locked, and you only open the gate after a second model cross-checks the draft or a human reads it. That's a quality boundary expressed as state, not as a hopeful comment in the code.
There's a wider signal underneath this, too. Across the same community you see people losing Claude accounts, fighting model downgrades through resellers, and debating whether to write agent code natively in Python or reach for a framework. The common thread: the model layer is unstable and not yours to control, so the durable value lives in the orchestration you own — the state, the gates, the ledger. Build the half that doesn't depend on which vendor is up today.
The same author also admitted the painful part of the original setup: it leaned on Electron to bridge local calls, so leaving the computer meant you couldn't publish. Moving to the web fixed that. That's the other reason to rebuild the spine somewhere hosted rather than keep it trapped in a local CLI.
Rebuilding just the spine on VicroCode
You don't need to reproduce the npm CLI or the Node renderer to get the valuable behavior. You need three things: a place to execute stages, a place to keep correctable state, and a way to reach it from anywhere.
Start with the stage runner as hosted Python. Each stage is a function — `prepare`, `review`, `render`, `publish` — and a tiny dispatcher reads the current state, runs the next eligible stage, and writes the result back before returning. Because you can run Python online and expose it as an API endpoint, the runner lives on the platform instead of on your laptop, and an agent or a cron-style trigger can poke the same endpoint to advance one stage at a time. Keeping each invocation to a single stage is what makes resume-after-timeout trivial: there's no long-lived process to lose, just a row to read.
For state, use a SQLite table as a combined state-and-ledger. One row per job with a `stage` column and a `review_status` column (`unchecked` / `trusted`), plus an append-only ledger of what each stage did and when. The gate becomes one guard the render and publish stages both check: refuse to proceed unless `review_status = 'trusted'`. The quietly powerful bit is that SQLite is hand-correctable — when a job wedges in a bad state, you open the SQLite editor, flip the stage or the review flag back by hand, and let the runner continue. That's the database equivalent of the original `zzp review --status passed` command, except you're editing the source of truth directly instead of through a CLI flag.
Then put a thin control surface in front of it. A small HTML page that lists jobs, shows each draft's current stage and trust status, and offers a single "mark trusted" button is enough to replace the whole local-client problem the original author hit with Electron. With web app hosting you reach that page from a phone browser the same as a desktop, which is precisely the freedom he described gaining after dropping the client.
A review stage that actually reviews
The trust gate is only as good as what fills it. The cross-check idea from the source — have a second, differently-built model read the draft — maps cleanly onto Model Center APIs for models already available through the platform. Your `review` stage calls a model, gets back a pass/flag judgment with notes, writes that into the ledger, and leaves `review_status` at `unchecked`. It does not auto-promote to `trusted`. A machine opinion is input to the gate, not the gate itself; a human (or a second rule you trust) still makes the call. That keeps the honest spirit of the original: the gate exists because first drafts can't be trusted by default.
If you want retrieval in the loop — checking a draft's claims against your own reference notes — a LanceDB knowledge base gives the review stage something to compare against before it writes its verdict. Still advisory, still landing in the ledger, still short of flipping the flag on its own.
Where the boundary sits
Be clear about what does not come across, because pretending otherwise would just set up a bad afternoon.
The original kit is Node and npm end to end — the `zzp` CLI, the Canvas cover renderer, the Milkdown typesetting packages. VicroCode's execution path here is Python and HTML web apps, so you're rebuilding the runner in Python, not rehosting those packages. The cover generation in particular leans on native Node Canvas; reproducing pixel-exact 2.35:1-plus-1:1 cover layout is its own project and falls outside the confirmed capabilities, so treat cover rendering as out of scope rather than something to hand-wave.
The WeChat publish step is further out still. It depends on WeChat's official open-platform API and a proxy sitting on a server with a fixed, whitelisted public IP — the author shipped a separate Bun server precisely for that. VicroCode can host your own API endpoints and make in-platform tool calls, but it does not give you a WeChat-authorized fixed IP or that vendor's publishing integration. So the `publish` stage you build is a clean seam: the gate, the ledger entry, and the hand-off point. What it hands off to — and whether that destination is reachable from here at all — is a separate question you have to answer for your specific channel. None of the retention rates, star counts, or reseller claims floating around that community are verified, and they don't belong in your pipeline's assumptions either.
What you actually get
Strip it down and the rebuild is modest: a Python dispatcher that advances one stage per call, a SQLite row you can edit by hand when something jams, a review stage that asks a model but never promotes itself, and a web page you can open from a phone. That's the half of the original pipeline worth owning — the part that keeps running when a model gets flaky, an account gets banned, or a step times out halfway. The flashy renderer and the channel integration are replaceable. The state machine and the trust gate are the thing you'll still be using after you've swapped everything around them.