I Gave My Skills Repo a Publish Button That Can't Leak Its Own Key
Packaging 13 skills for Claude Code and Codex, then splitting the build job from the push job so no branch can touch the token.
Learn · Oct 23, 2026
I had 13 skills sitting on my site. People could read them. Installing one meant copying text by hand and hoping it landed in the right folder.
That's a bad deal for anyone who isn't already comfortable in a terminal. So this session I made every skill installable, in Claude, in Claude Code, and in Codex.
The packaging was the fun part. The part that made me slow down was the automation that publishes it. Because a workflow that pushes to a public repo holds a token. And a token is only as safe as the code that can reach it.
Generate the skills folder from source
I wrote a build script that turns every skill into a distributable skills/ folder. It has no external dependencies, and it's deterministic. Same input, same output, every time. That last part matters more than it sounds. If the output is predictable, a pull request check can compare the committed folder to a fresh build and catch drift. I added that check. The folder now covers all 13 skills. Each one has plugin metadata, its instruction file, and its reference material. I ran real install round trips for both Claude Code and Codex before I called it done. Not a quick glance at the folder. An actual install, then an actual check that the skill showed up and ran. That's the habit I'm trying to build. Don't trust that a package works because it built. Install it the way a stranger would, and see what happens.
Lay out each plugin so it doubles as a Codex skill
I didn't want two output formats to maintain. So each Claude Code plugin directory is structured to also satisfy the Codex skills folder convention. One layout, two tools. The build script stays simple, and so does the published repo. I also left the version out of the marketplace manifest on purpose. With no pinned version, people who install track the latest commit. Every time I update a skill, there's no version bump to remember.
Split the build job from the push job
Here's the thing about a mirror workflow. It runs code, and it holds a secret. If both happen in the same job, any branch that can trigger the run can try to read the secret. So I made two jobs. The first one runs the generator with no secrets at all. It hands its output to the second job as an artifact. Only the second job holds the push token. That means code from any branch, including a manually triggered run, never gets near the credential.
Put the token behind a named environment
The push job also sits behind a named environment called skills-mirror. That lets me restrict the token to pushes from main, and optionally require a reviewer before it runs. A second lock on the same door. I'd rather have one lock too many than find out the hard way. One honest note. The token and the environment still need to be set up in the repo settings. Until I do that, the workflow is inert on purpose. It sits there and does nothing, which is exactly what I want from it.
Add a three-tab install card to every skill page
Then the part visitors actually see. Every skill page now has an install card with three tabs: Claude, Claude Code, and Codex. Claude gets a downloadable .skill file. Claude Code gets the plugin marketplace command, or a download. Codex gets an installer command, or a download. The download tabs include a copyable unzip command, so nobody has to guess. The install commands point at the public skills repo by default. I had that behind an environment variable at first. But once the repo existed, requiring the variable would have left every install instruction broken for visitors. So I changed the default to match reality and kept the variable as an override.
What broke: The "Try it" example prompts looked wrong. Long prompts in the skill source files are hard-wrapped, with a line break every 80 or so characters. Inside the card, those breaks showed up as ragged lines, even though the copy button produced clean text. So what you saw and what you copied didn't match. That's a small thing, but it's the kind of small thing that makes a page feel unfinished. It also would've bitten me again. Every new skill with a long example would have shown the same ragged lines. Fixing it one skill at a time wasn't going to scale past the first few.
The fix: I reflowed the hard-wrapped text into single paragraphs when the card renders. Now the display matches the clipboard. And because the fix lives in the card, not in each skill, any future skill with a long example prompt works without a per-skill workaround. Next up is finishing the repo settings so the mirror actually runs. After that, every skill update syncs to the public repo on its own, and I can stop thinking about it.
Review this GitHub Actions workflow. It pushes to a public repository using a token.
1. List every job and tell me which ones can read the token.
2. Find any step where code from a pull request, a fork, or a manual run could execute in a job that holds the token.
3. If build and push happen in the same job, propose a split: a first job with no secrets that produces an artifact, and a second job that downloads the artifact and holds the token.
4. Recommend a named environment for the push job, restricted to the main branch.
5. Show me the exact YAML changes. Don't change anything else.
If you're unsure whether a step is safe, say so and explain why.Go deeper
I Made the Skills Behind My Own Build Process Public →
Six Claude skills, free on GitHub, and why lowering the floor for other builders is the whole point.
A Text Editor Is a Security Decision →
Autosave, a Mark Done button that never publishes, and an allowlist that decides what gets stored. Building Switchpath's project workspace, step 7.
Get new guides and builds in your inbox
No noise. New artifacts, skills, and lessons when they drop — and early access when agents launch.
Want a full structured path instead of one-off guides?
That's Learn AI with AI — a sequenced course that takes you from zero to building real things, with community and accountability built in.
Check out Learn AI with AI →