goosetools-worker
v0.2.2
Published
The Goose Tools carousel worker — your computer drafts and renders carousel slides for goosetools.com using your own Claude account.
Downloads
347
Readme
carousel-maker
Brand-aware Instagram carousel generator. Photos, clips and words in → ready-to-post 1080×1350 slides out (PNG, or MP4 for slides built on a video).
How it works
brand.yml— your colors, fonts, handle. Set once; every slide inherits it.- A style —
styles/<name>/holds a template set +style.yml(font/color overrides) + the reference images that inspired it. Like Recraft: drop inspo screenshots instyles/<name>/refs/, have the agent distill them into templates, reuse forever.brand.yml → defaultStylepicks the default; a carousel can override withstyle: <name>in its slides.yml. - A carousel folder —
carousels/<name>/with aslides.yml(andphotos/+videos/alongside it). - Render — HTML templates filled with your brand tokens, screenshotted headless at 2x.
Usage
npm install && npx playwright install chromium # first time only
node src/render.js carousels/my-post
# → carousels/my-post/output/slide-01.png, slide-02.png, ...
# optional, last: only if you want video slides (see below)
brew install ffmpegslides.yml
slides:
- template: cover # bold hook on brand color + "swipe →"
text: |-
the hook
goes here
- template: photo # your photo, text over a dark gradient
photo: photos/desk.jpg
text: caption over the photo
- template: photo # same template, but the background MOVES
video: videos/build.mp4
text: caption over the clip
- template: text # dark slide, *asterisks* render in accent color
text: |-
body copy with
*highlighted* words
- template: cta # gradient closer with follow pill — put last
text: your call to actionTemplates
| template | look |
|---|---|
| cover | brand gradient, big bold hook |
| photo | full-bleed photo, gradient scrim, caption at bottom |
| text | dark background, bold copy, *text* → accent color |
| light | off-white background, dark copy, *text* → primary color |
| cta | brand gradient, centered text + "follow @handle" pill |
Every slide gets a progress bar + page counter baked in, and a swipe chevron on the right edge — except the last slide, where the missing chevron and full bar signal the end. Alternate dark/light templates for visual rhythm.
Add a template by dropping a new templates/<name>.html — it gets the full brand.yml context plus {{text}}, {{photoSrc}}, {{handle}}, {{ui}} (the progress bar + chevron; include it in every template). If the new template has a light background, add its name to LIGHT_TEMPLATES in src/render.js so the chrome adapts.
Video slides
Any slide that takes a photo: takes a video: instead — same template, same
focus: / zoom: framing controls, but the clip plays full-bleed behind the
slide's text. Instagram carousels accept video items, so a deck can mix stills
and motion freely.
- template: photo
video: videos/build.mp4
focus: "60% 45%" # centre of the subject, as on a photo
zoom: 1.8
text: watch it *come together*How it renders: the slide is screenshotted twice — once normally over a frame
pulled out of the clip (slide-03.png — what the framing self-check reads),
and once with every
background stripped so only the artwork remains. ffmpeg then crops the clip to
the slide shape and burns that artwork over it (slide-03.mp4, H.264 /
yuv420p, 1080 wide, audio kept, first 60s).
This means no template needs to know about video — every style's photo
template gained video support for free.
ffmpeg (optional, but required for video)
Video slides need ffmpeg and ffprobe on your PATH. They do not come
with Node, npm, or Playwright — Playwright bundles an ffmpeg, but it's built
--disable-everything for WebM screen recording: no H.264, no AAC, no
ffprobe. So it's a real install:
brew install ffmpeg # macOS
winget install Gyan.FFmpeg # Windows
sudo apt install ffmpeg # LinuxIt's genuinely optional: a deck with no video: slides never even checks for
it. But a deck with clips fails loudly rather than quietly rendering them
as stills — a carousel that looks finished and silently isn't the one you
asked for is the worse outcome. goosetools-worker status shows whether this
computer can do video.
Goose Tools worker
This repo doubles as the worker behind goosetools.com's Carousel Maker. The web app queues jobs; a worker on your own computer claims them, drafts the slide copy with your local Claude Code login (your subscription), renders with the engine above, and uploads the finished slides.
# one-time: install Claude Code + sign in, then
npx --yes goosetools-worker install --url https://goosetools.com --token gt_... # token from the websiteinstall saves the token to ~/.goosetools/env and registers a login
service (launchd on macOS, a Scheduled Task on Windows) so the worker runs
in the background whenever the computer is on — even after a restart.
npx --yes goosetools-worker uninstall removes it. To run in the foreground
instead: npx --yes goosetools-worker run --url … --token ….
Optional env: VOICE_DIR points at a writing-voice folder the drafts should
follow (see worker/index.js).
Worker commands
| Command | What it does |
| --- | --- |
| install --token gt_… | First-time setup: save the token, install the background copy, register the service. |
| update | Pull the newest code into the background copy and restart it. Reuses the saved token. |
| status | What's installed, whether it's running, and recent log lines. Start here when something's wrong. |
| uninstall | Remove the service and the saved token. |
| run | Run the loop in this window instead of in the background. |
All of them work via npx --yes goosetools-worker@latest <command>.
Worker FAQ
The website says my computer isn't connected.
Run npx --yes goosetools-worker@latest status. It tells you whether the
worker is installed, whether the process is actually running, and whether a
token is saved. Each line is one of the things that has to be true for the
website to show "Connected".
How do I get a new version onto my computer?
npx --yes goosetools-worker@latest update.
This matters more than it looks. The background service does not run from
wherever you ran npx, or from a git checkout — it runs a copy installed
under ~/.goosetools/app. New code only reaches that copy when you reinstall
it, and the running process only picks the new code up when it restarts (Node
reads every file once, at startup). update does both, and reuses your saved
token so you don't have to find it again.
I updated but nothing changed.
Check status — the "Installed" line is the version the daemon will run, and
"Running" shows the live process. If Installed is the new version but the
behavior is old, the process didn't restart; run update again.
status shows errors — is my worker broken?
Look at the "last written" time next to the error block. The service restarts
itself and keeps its old log, so errors from days ago sit there long after
they stopped mattering. If "Activity" was written recently and "Errors" wasn't,
the worker is fine — it's polling normally.
"Poll failed (will retry)" is the worker not reaching the server: laptop asleep, wifi dropped, or the site briefly down. It retries every 30s on its own; no action needed unless it's still happening now.
"This computer's worker is outdated for this job" means the website sent
a kind of job this worker version doesn't know how to do. Run update.
"Style X isn't installed on this computer yet" — styles live on the
machine that renders them (styles/<slug>/). A style generated on a different
computer won't exist on this one; regenerate it here.
Jobs sit in "queued" forever.
Queued means the website has the job but no worker has claimed it. Confirm the
worker is running with status, and confirm it's pointed at the same server
(the "Server" line) as the site you queued from.
Where are the logs?
~/.goosetools/worker.log (activity) and ~/.goosetools/worker.err.log
(errors). status tails both.
Do I need to keep a terminal open? No — that's what install sets up.
run is the foreground alternative if you'd rather watch it work.
Publishing a new worker version
Users get updates from npm, so pushing to git is not enough — update and
npx goosetools-worker@latest both resolve to the published version:
npm version patch # or minor/major
npm publish
git push --follow-tagsUntil you publish, npm view goosetools-worker version will lag the repo and
everyone else stays on the older code. status flags the mismatch on your own
machine ("you're ahead — local dev build").
Roadmap
- Secondary font (TBD in brand.yml)
- Per-platform handle overrides
- Draft the words too (hook + slide copy from a rough idea)
- Hook into build-in-public as a skill
