Skip to content

Category: The Art of Vibe Coding

8 min read The Art of Vibe Coding

Your AI Agent Can See Your Passwords. Here’s How to Fix That.

Your AI Agent Can See Your Passwords. Here's How to Fix That.

You’re working with Claude Code, building against a WordPress REST API. You need it to authenticate, so you grab the App Password from your password manager, paste it into the prompt, and hit Enter.

Claude reads the credentials, constructs a curl command, fires it off. The response comes back. Everything works.

Then you look at the transcript.

Your App Password is right there — in the user message where you typed it and in the curl command Claude built. That credential is now part of the conversation history, where it can be logged, cached, or shared.

I’ve done this more times than I’d like to admit — pasted a key, got the result, moved on, and only later thought about what I’d left behind.

Export the conversation to hand off to a teammate? The password goes with it. (I was about to export a transcript for a colleague when I spotted an API key three messages up. That was a fun thirty seconds.) Same story if the session feeds future context or you share a transcript on a bug thread.

This risk shows up any time you hand an AI agent a secret — API keys, database URLs, any credential type. The agent uses the value in a tool call, and suddenly it’s baked into the transcript like a phone number scribbled on a napkin at a crowded restaurant. Anyone who picks up that napkin gets the number.

The leak path: a pasted password lands in the transcript, then travels wherever the transcript goes — exported to a teammate, cached in context, or shared for debugging

The model never needed the actual characters in the first place. A stable reference would do — a handle it could drop into commands while the real value stayed hidden.

.

.

.

What are Function Hooks?

Stay with me here — because the fix for this is more elegant than you’d expect.

Claude Code Function Hooks are a new capability, currently behind a feature flag and in a community feedback phase. If you want to follow the discussion, it’s GitHub proposal #91870, filed September 2026. Nothing here has officially shipped yet — everything in this post was tested against a working build, but the API could change before general availability.

Function Hooks are TypeScript middleware that wraps tool calls — they can intercept, modify, or replace any command before it executes, with shared state across the session. Think of them as a checkpoint between what you type and what the model sees, and another checkpoint between what the model asks to run and what actually executes.

Middleware pipeline showing how a prompt.submit hook intercepts user input before Claude sees it, and a tool.call hook intercepts commands before they execute

.

.

.

The solution: a credential guard plugin

Two hooks working together can keep secrets out of the transcript while still letting commands execute with real credentials.

The first hook catches your input right when you hit Enter. It spots credentials, swaps each one for a safe placeholder like [WP-PASS-c481], and tucks the real value away for later. By the time the model sees your message, the secret is already gone.

On Input: the prompt.submit hook scans for credentials, replaces them with a placeholder like WP-PASS-c481, and stores the real value in an in-memory vault

The second hook watches for outgoing commands. When Claude builds a curl command using that placeholder, the hook swaps the real value back in right before execution. The command works. The transcript stays clean.

On Execution: the tool.call hook retrieves the real value from the vault and swaps it into the command before Bash executes
Side-by-side comparison: without a hook, the App Password appears in both the transcript and the curl command; with a hook, the transcript shows only a placeholder and the real value is restored at execution time

The same pattern works for any secret type — API keys, database connection strings, or any other credential.

.

.

.

Building it: the WordPress demo

Let me show you what this looks like end to end.

I wanted to test against a real API — something with actual authentication and a credential format worth detecting. WordPress App Passwords are a good candidate. They have a distinctive six-groups-of-four alphanumeric format, which makes them detectable by pattern matching. The scenario: install a WordPress plugin via the REST API using App Password auth. I used TasteWP for a throwaway test site, so there was no risk to a production environment.

Demo setup: Claude Code with the wp-credential-guard plugin connecting to the WordPress REST API on a TasteWP test site, with credentials redacted in transit

Enable Function Hooks

Since the feature is behind a flag, the first step is opting in. Add this environment variable to your Claude Code settings:

{
  "env": {
    "CLAUDE_CODE_ENABLE_FUNCTION_HOOKS": "1"
  }
}

Describe the plugin

I used Claude Code’s /plugin-authoring skill and described what I wanted in five sentences of plain English. No code. No file structure. Just the behavior:

Create a plugin called "wp-credential-guard" at .claude/plugins/wp-credential-guard/.
On prompt.submit, find WordPress credentials — usernames and App Passwords — and
replace each with a stable placeholder like [WP-USER-xxxx] and [WP-PASS-xxxx]. Store
the real values in a module-level Map (never $.store — credentials must die with the
session). On tool.call for Bash, scan the command for those placeholders and swap in
the real values before execution so curl commands work but the transcript stays clean.

Five sentences.

The /plugin-authoring prompt in Claude Code, a five-sentence plain English description of what the credential guard plugin should do

What Claude built

And here’s the kicker — what came back was more thoughtful than the spec I’d written. Claude figured out how to recognize credentials in different formats, handled edge cases I hadn’t considered, and built in safeguards so the password swap wouldn’t break the command it was modifying.

After writing the code, it tested everything automatically — and caught a bug in its own work along the way (ferpetesake). Fixed it, re-tested, all passing.

Claude's build summary showing the plugin architecture: in-memory vault design, seven ordered detection rules, shell-quoting-aware restoration, and all twenty verification tests passing

Load the plugin

After the files are created, you restart Claude Code with the plugin directory:

claude --plugin-dir .claude/plugins/wp-credential-guard

The whole plugin is four files — a manifest, a module declaration, the TypeScript source with both hooks, and a compiler config.

File tree showing the four plugin files: plugin.json manifest, hooks.json module declaration, hooks.ts with the two hooks, and tsconfig.json

A quick check of the installed plugins list confirms it loaded:

Claude Code's installed plugins list with wp-credential-guard showing as enabled under the User section

Test with real credentials

Now for the real test. I typed a prompt containing a REST API endpoint, a username, and a six-group App Password in plain text. No attempt to hide or encode the credentials — just a straightforward request to install a plugin:

The user prompt containing a real WordPress App Password in plain text, asking Claude to install a plugin via the REST API

The result

The password was fully redacted. Look at the transcript — the status line at the top reads “wp-credential-guard: masked 1 password (session-only).” Below that, the user message shows the App Password replaced with [WP-PASS-c481]. The real value, all six groups of it, is nowhere in the conversation.

When the placeholder showed up instead of my actual password, I scrolled back up to check twice. (Old habits. I wanted to believe it, but I also wanted to be sure.)

Claude then built a curl command using the placeholder and hit the WordPress REST API endpoint. From the transcript’s perspective, the password is just a bracketed token. Claude doesn’t know the difference, and it doesn’t need to. The hook swapped in the real value at execution time, and the API returned a successful response listing the site’s installed plugins.

The transcript showing the password replaced with the placeholder WP-PASS-c481, the status line confirming one password masked, the curl command using the placeholder, and the API returning a successful response

What didn’t work

The username “smashed” was not redacted. App Passwords have a distinctive format that pattern matching can catch, but a username in prose looks like any other word. This is fixable with another prompt — tell Claude to add a username detection rule — and that iterative loop is the whole point of building with an AI agent.

.

.

.

Why this matters

If you’ve been using Claude Code for a while, you’ve probably tried the CLAUDE.md approach. I wrote about it in The Single File That Makes or Breaks Your Claude Code Workflow: put your rules in CLAUDE.md and trust the model to follow them. “Never log credentials.” “Always use environment variables for secrets.”

Here’s the thing: those rules work most of the time. But they compete with every other instruction in the context window — and the context window is a crowded place. In long conversations or complex tasks, a rule can get lost in the noise. A Claude Code Function Hook is deterministic. It runs on every tool call, every time, regardless of how long the conversation has been or how much context the model is juggling.

CLAUDE.md rule versus Function Hook: a rule competes for attention among many instructions in the context window, while a hook runs deterministically on every tool call

There’s a second benefit worth calling out: each rule you move into a hook is one fewer instruction consuming context tokens. I talked about context management in Claude Code Sandbox Explained: Stop Pressing Enter 50 Times a Day, where the sandbox saves you permission fatigue. Hooks save you context space. Both free up room for the instructions that actually need the model’s attention.

If you’ve seen how secrets management tools handle credentials in CI/CD pipelines, this pattern will feel familiar. Tools like Infisical run a local proxy that injects secrets at the edge — the application references placeholders, the proxy swaps in real values at request time, and the secrets never appear in logs or config files. Claude Code Function Hooks follow the same principle: the secret gets injected at execution time, invisible to the model orchestrating the work.

👉 Credentials are the most obvious use case, but the pattern applies to anything you want to keep out of the transcript while still using in tool calls.

This is where AI agent tooling is heading. The plugin system means you don’t have to be a security engineer to build a credential guard — five sentences of plain English got me a working one. Function Hooks are still in early access, which means now is a good time to start experimenting before the community settles on conventions.

Try it yourself

  1. Enable Function Hooks in your Claude Code settings with the environment variable shown earlier in this post
  2. Use /plugin-authoring to describe a credential guard in your own words
  3. Test it against a real API call with a throwaway credential
  4. Check the transcript. The real value should never appear.
12 min read The Art of Vibe Coding

How to Install Any Skill in ChatGPT Work (Web version) With One Prompt

How to Install Any Skill in ChatGPT Work's Web Version With One Prompt

OpenAI launched ChatGPT Work in early July 2026 with two versions: a desktop app reworked from the old Codex application, and a web version that runs entirely in the browser.

Both let AI work with your files, plugins, and approved tools to complete real tasks. You give it a goal, it retrieves what it needs, builds finished deliverables, and runs multi-step workflows. This is OpenAI’s take on what Anthropic built with Claude Cowork — a workspace where AI goes beyond answering questions and actually finishes work for you.

Most of the early coverage focused on the desktop app, which makes sense — it has local execution and deeper integration with your file system.

The web version runs lighter — no local install, no background processes — and that’s exactly why I wanted my tools there too.

In How to Turn AI-Generated HTML Into WordPress Blocks (Without Breaking Them), I built a skill that validates AI-generated block markup before it reaches the WordPress editor. It catches structural problems — broken nesting, hallucinated block types, invalid attributes — before they become broken blocks the editor can’t render. Useful enough that I wanted it everywhere I work, including ChatGPT Work’s web version.

On the desktop app, the install path is clear. You can upload a zip file through the interface, or run a CLI command that pulls the skill from a public repository:

npx skills add nathanonn/agent-skills --skill validate-block-markup

Two documented options. The CLI command is especially convenient — point it at a repo, name the skill, and it pulls everything down in seconds.

The catch: those options live on the desktop. Skills you install on the desktop app stay on the desktop app. Switch to the web version, and they won’t be there — the two environments don’t share a skill directory.

Whiteboard diagram showing the Desktop App with skills installed and the Web App with an empty skill directory, separated by a red X indicating skills don't sync between them

So when I wanted to install skills in ChatGPT Work’s web version, I opened it expecting a visible install option. Clicked around, searched menus, nothing. It took several minutes of poking through Settings before I found it buried four levels deep.

The feature exists. Reaching it takes four clicks through nested settings, and once you’re there, you still need to download files, zip them, and upload the zip manually.

There’s a shortcut that skips all of it — one prompt, one GitHub URL.

.

.

.

Where ChatGPT Work hides the skill installer

The manual installation path starts in Settings.

Open the Settings panel and click the Plugins tab. At the bottom of the plugin list, there’s a “Browse plugins” link — easy to miss if you’re scrolling past your existing plugins without looking for it.

ChatGPT Work Settings panel with the Plugins tab selected, showing Browse plugins at the bottom of the plugin list

Clicking that link takes you to a separate Plugins page. Installed plugins appear on the left, featured ones on the right. Two tabs sit at the top: Plugins and Skills.

The Plugins page showing installed and featured plugins, with Plugins and Skills tabs at the top

Click the Skills tab and you’ll see a small “+” button in the corner with three choices:

  • Create with Chat: describe what you want and let ChatGPT write the skill for you
  • Create with Editor: write the skill code directly in a built-in editor
  • Upload from your computer: select a zip file or skill file from your machine
The Skills page showing the plus button dropdown with three options: Create with Chat, Create with Editor, Upload from your computer

The third option is the one you need for installing an existing skill.

Click “Upload from your computer” and you get a file-upload dialog where you can drag and drop a zip or browse your machine for one.

The Upload a skill dialog showing a drag-and-drop area for uploading zip files and skill files

So the full path runs four clicks deep: Settings, Plugins tab, Browse plugins, Skills tab. And before you can upload anything, you still need to download the skill files from wherever they live, bundle them into a zip, then upload that zip through the dialog. On the desktop, at least you can skip the UI entirely and use the CLI. The web version has no CLI equivalent — the settings menu is the only way in.

Whiteboard flow diagram showing the 4-step manual install path: Settings, Plugins, Browse Plugins, Skills Tab, then Download Zip and Upload

Think about what that means in practice.

You find a skill on GitHub that does something you need. To use it in the web version, you leave ChatGPT Work, go to GitHub, download the skill files to your machine, open a file manager, zip the folder, come back to ChatGPT Work, navigate four settings screens deep, and upload the zip. For a platform built around AI doing work for you, the process of adding a new capability has all the automation of a paper form and a stamp.

For one skill, this is tolerable. For anyone who regularly grabs skills from public repositories, the friction adds up fast.

After finding the upload path and realizing I still needed to zip files manually, I nearly closed the tab and went back to the desktop app. On a whim, I tried just asking ChatGPT to do it.

If only there were a way to do this in one step.

.

.

.

The one-prompt method

There is.

Instead of navigating settings menus and uploading zip files, you can install skills in ChatGPT Work directly from a GitHub URL.

One prompt, and ChatGPT handles the downloading, validation, and installation on its own.

Whiteboard comparison showing The Hard Way with 6 steps taking 10 minutes versus The Easy Way with 2 steps taking 1 minute

Let me walk through each step.

Step 1: Find the skill on GitHub.

Go to the repository that hosts the skill you want. I went to my agent-skills repo, where I keep the skills I build and share publicly.

GitHub repository page for nathanonn/agent-skills showing the repo root with folders for skills and plugins

Step 2: Navigate to the skill folder.

Browse into the directory where individual skills live and find the one you’re after. Each skill sits in its own folder with the files ChatGPT Work needs to install it.

GitHub file browser inside the skills directory, showing available skills including validate-block-markup

Step 3: Copy the folder URL.

Click into the skill folder and grab the URL from your browser’s address bar. You want the URL of the folder itself, not any individual file inside it.

GitHub page for the validate-block-markup skill folder, with the full URL visible in the browser address bar

Step 4: Paste the URL into a new ChatGPT Work session.

Start a fresh session in the web version. Type a prompt like this, pasting the folder URL you copied:

Help me install the following skill into my personal ChatGPT skill directory:
https://github.com/nathanonn/agent-skills/tree/main/skills/validate-block-markup

A plain request with the link. Nothing else needed.

New ChatGPT Work web session with the install prompt typed, including the GitHub URL for the validate-block-markup skill folder

Step 5: Let ChatGPT handle the rest.

Submit the prompt and step back.

ChatGPT Work starts by inspecting the repository. It reads the folder structure, identifies the skill definition file and any supporting assets, and figures out what to pull down.

ChatGPT Work processing the install request, showing an Inspecting GitHub Repository step in progress

From there, it clones the relevant files and validates them against its skill format requirements. When the install hit a packaging issue, I braced for the error message that would send me back to manual mode. Instead, ChatGPT fixed it and kept going. The whole thing finished without me doing anything.

ChatGPT Work mid-install, showing validation steps and a packaging issue it discovered and resolved automatically

When the process finishes, you get a confirmation message with the skill name and validation results.

Install complete message confirming that Validate Block Markup is installed and available in the personal skill directory, with validation results

About a minute. From pasting the URL to the confirmation message.

And here’s the kicker: I expected ChatGPT Work to ask me to format the skill a certain way, or flag compatibility problems and kick the ball back. The skill had been built for Claude Code originally, so I was ready for format mismatches, validation failures, the usual cross-platform shenanigans. Instead, it handled the messy parts on its own — detected the packaging issue, fixed it, confirmed the fix, and completed the install.

That moment — watching an AI workspace install its own capability from a single instruction — was when the manual path stopped feeling like a reasonable default.

One prompt did what the manual path made tedious.

Confirming the install

To verify the skill is actually installed, navigate back to the Skills page through the settings path from earlier: Settings, Plugins tab, Browse plugins, Skills tab.

The skill now appears in your installed list, ready to use.

The Skills page showing Validate Block Markup listed under installed skills

To call an installed skill, start a new session and type the @ symbol followed by the skill name. An autocomplete dropdown appears as you type, showing matching skills from your directory. Select the skill from the dropdown and it becomes active for that session.

A new ChatGPT Work session with @val typed, showing an autocomplete dropdown listing the Validate Block Markup skill

Worth knowing: you need to call the skill at the start of a new session for it to activate. The skill’s instructions load at session start, so adding it mid-conversation won’t pick up its full behavior. This is a small habit to build, but it keeps the experience clean — skills only load when you explicitly ask for them.

Putting the skill to work

With the validate-block-markup skill installed, I tested it on a real task.

I started a new session, called the skill with @, and pasted a chunk of AI-generated HTML — the kind of layout you get from asking AI to build a landing page section. A hero area with a heading, body text, and a call-to-action button.

ChatGPT Work session using the Validate Block Markup skill, with HTML code pasted for conversion to WordPress block markup

The skill converted the HTML into structured WordPress block markup, wrapping each element in the correct comment tags with proper configuration attributes and nesting them inside the right container blocks. Then it validated the output against the WordPress block grammar to confirm everything would parse correctly in the editor. Valid output — ready to paste into WordPress, where every element becomes an editable block with full sidebar controls. The heading, paragraph, and button each become their own native block type, with the sidebar options your client expects when they click on any element to edit it.

The skill's output showing fully converted block markup alongside a validation result confirming validity against WordPress 7.0.2

If you’ve read How to Turn AI-Generated HTML Into WordPress Blocks (Without Breaking Them), you know why this matters.

The whole point of converting HTML to block markup is to make AI-generated designs editable inside WordPress. A block the editor can’t parse is a block the client calls you about (usually on a Friday afternoon). Having the validation step happen inside ChatGPT Work means you can check for broken markup before it ever reaches the editor, without opening a terminal.

The skill worked identically in the browser — same validation, same output format. Previously, validating block markup required a local tool running in a terminal, which tied the whole process to a specific machine and a specific setup. The web version removes that dependency.

I can now generate HTML in ChatGPT Work’s web version, convert it to block markup, validate it against the WordPress grammar, and paste the result straight into the editor. The entire workflow happens in one browser tab. The terminal and the code editor stay closed.

(And honestly, it took me longer to find the upload button than it took ChatGPT to install the skill.)

.

.

.

Any skill, any public repo

Whiteboard hub-and-spoke diagram showing Public GitHub Repos, Skills.sh directory, and any device feeding skills into ChatGPT Work Web

The validate-block-markup skill was my test case, but the method works with anything hosted publicly.

You can install any skill from a public GitHub repository the same way — find the folder, copy the URL, paste it into a new session with an install prompt. This includes everything listed on Skills.sh, the public directory for AI agent skills. Skills.sh catalogs hundreds of community-built skills across categories: code generation, data analysis, content creation, DevOps workflows, and more. Every skill in that directory lives in a public GitHub repo. Browse the catalog, find something that matches your workflow, grab the folder URL, and you’re one prompt away from having it installed.

The practical shift is about where you can work.

Previously, installing a skill meant having the desktop app or a terminal where you could run a CLI command.

The ability to install skills in ChatGPT Work’s web version from any device removes that requirement entirely. Whether you’re on a laptop during a commute or a tablet at a coffee shop, the process stays the same — copy the GitHub URL in your browser, switch to ChatGPT Work, and paste the prompt.

Once installed, skills stay in your personal directory.

They show up whenever you type @ in a new session, across every future session. Install once, use everywhere.

This also changes how you think about building skills. If you’ve published a skill to a public GitHub repo, anyone with access to ChatGPT Work’s web version can install it in about a minute. The distribution channel becomes the repo itself — you share the folder URL, and that’s the entire delivery mechanism.

Every friction point between “this skill exists” and “I’m using this skill” is a place where people give up. Requiring a CLI command loses everyone who doesn’t use a terminal. Requiring a desktop app loses everyone who works from a browser. A GitHub URL and a prompt loses almost nobody.

The larger point: The gap between discovering a useful skill and actually using it collapsed to a single prompt.

You don’t need to download files, create zips, dig through settings, or open a terminal. A browser and a GitHub link are enough. That’s the smallest possible distance between “I want this tool” and “I have this tool.”

If you’ve been building skills for Claude Code, Codex, or any agent that follows the Skills.sh format, those same skills can likely run in ChatGPT Work through this exact install method. The portability runs in every direction.

.

.

.

What to try next

The best way to see this in action is to try it with a skill you’d actually use.

Here’s the process end to end:

  1. Browse Skills.sh and find a skill that matches something you do regularly
  2. Click through to its GitHub repo and find the skill folder
  3. Copy the folder URL from your browser’s address bar
  4. Open the web version of ChatGPT Work and start a new session
  5. Paste the URL with a short install prompt, something like “Install this skill into your skill directory”
  6. Start a new session and call the skill with @

The whole process takes about two minutes, including browsing for the skill.

Once you’ve installed a skill this way, the manual upload path will feel like taking the scenic route — through four settings screens, with a detour to zip your own luggage.

And if you build skills of your own, you now know what the distribution channel looks like: a public repo and a folder URL.

Your next skill is one prompt away from anyone who could use it.

7 min read The Art of Vibe Coding

How I Remixed a Design System Into Something Original With One Command

How I Remixed a Design System Into Something Original With One Command

In I Extracted a Website’s Entire Design System Using This Skill, I showed how to pull a complete design system from any website. Colors, typography, spacing, components, a visual gallery — everything matching the original.

Then in The Skill That Makes Claude Use Your Design System Without Being Told, I turned that extraction into a self-triggering skill so Claude applies the brand automatically.

But I glossed over something important.

I’d built a landing page with the Doodler system and it looked great — until I realized it looked great because it was someone else’s work. The kind of shortcut that feels clever for about ten minutes.

Picasso reportedly said, “Good artists copy, great artists steal.”

Stealing, in the artistic sense, means absorbing influence and transforming it into something unmistakably yours. Extraction alone is copying. Useful for studying a design you admire, risky if you plan to ship it.

I had a product idea — a developer changelog tool called Devlog — and a design system I loved the feel of, extracted from a template called Doodler.

Chunky outlines, warm pastels, playful card layouts. The kind of visual personality that makes you want to build something immediately. But I couldn’t dress my product in someone else’s visual identity and pretend the look was mine.

Here’s what changed: what if I could remix that design system into something original?

One command.

That’s all it took.

.

.

.

What /design does

Claude Code shipped a built-in command called /design.

You type it followed by a brief, and Claude drafts multiple design options as side-by-side artboards on an interactive canvas — published as an Artifact you can browse, zoom, and edit. Design exploration before any code gets written. (Think of it as sketching five directions on a whiteboard before committing to paint.)

The remix workflow — Extract a design system from a site you admire, Remix it into variations with /design, Browse the canvas and pick one, Generate a complete design system from your pick

For this project, I used /design to remix the design system into five visual variations of the original, so I could find the one that felt like mine. Here’s how it played out.

The prompt

I opened Claude Code and typed a /design prompt.

Four decisions shaped what came back.

The prompt entered in Claude Code, pointing at the extracted Doodler design system, requesting 5 variations at 75-85% similarity, with the Devlog product idea and instructions not to modify the original

I pointed Claude at the full extraction. The complete token set, component catalog, and reference snippets — everything the skill had pulled from the original site. Claude could study every detail before designing alternatives.

I asked for five variations. Enough spread to notice real differences without drowning in options. (Three would have been fine, but more spread means a better chance of finding the one that clicks.)

I set a similarity constraint: 75-85%. Below 75% and you lose the qualities that attracted you in the first place. Above 85% and you’re still wearing someone else’s look.

I grounded the brief in the actual product. Every variation was designed for Devlog, with its changelog workflows and developer audience front of mind. Variations designed for “a generic landing page” tend to stay generic. Variations designed for a specific product make real decisions.

Claude Code working on the variations, studying the original design system, drafting five variations, publishing as an interactive canvas artifact

Claude studied the original, drafted five variations, and published them as a single interactive canvas. About two minutes.

.

.

.

Five variations, one canvas

Five named design variations, each rendered as a complete landing page on one scrollable canvas.

All five design variations shown side by side on the /design canvas, Ledger, Margin Notes, Board, Grid, and Night Shift, each showing nav, hero, services, and pricing sections with progressively more deviation from the Doodler original

Every variation shares the same page structure: nav, hero, services, and pricing. The visual treatment shifts progressively further from the Doodler original as you move left to right.

Similarity spectrum showing the five variations from most similar to least — Ledger at 88%, Margin Notes at 85%, Board at 82%, Grid at 78%, Night Shift at 75%

Let me show you what each one brought to the table.

Variations 1 and 2 side by side, Ledger with its ruled-paper hero and jade accent, Margin Notes with parchment canvas and amber highlighter swipes

Ledger (~88%) — the closest cousin. Oat canvas, jade accent, 3px outline, and a ruled-paper texture that gives the whole layout a stationery feel.

Margin Notes (~85%) — parchment canvas with amber highlighter swipes on headings, coral hard-offset shadows behind cards, and one deliberately squared corner per card. (That single squared corner was a small move that changed the entire personality of the card.)

Variations 3 and 4 side by side, Board with column strips and sage palette, Grid with graph-paper canvas and periwinkle crop marks

Board (~82%) — sage and lime palette with column strips on cards and a three-column hero that immediately signals “project board.”

Grid (~78%) — graph-paper canvas, periwinkle accent, crop-mark corners on feature cards, and a file-tree panel in the hero showing a real project structure.

Variation 5, Night Shift with inverted ink hero and pricing band, apricot and mint accents on dark

Night Shift (~75%) — the biggest swing. Dark ink hero and pricing band, white services section as the familiarity anchor, apricot and mint reading like chalk on a blackboard.

VariationSimilarityCanvasAccentSignature move
Ledger~88%OatJadeRuled-paper hero, 3px outline
Margin Notes~85%ParchmentAmberHighlighter swipes, one squared corner
Board~82%SageLimeColumn strips, hard ink shadow, board hero
Grid~78%Graph-paperPeriwinkleCrop-mark corners, file-tree hero panel
Night Shift~75%Ink (dark)Apricot/MintInverted hero and pricing bands

I expected five color swaps. What I got were five brands.

Same page structure across all five, but once the palette, type pairing, and border treatment shifted, each variation stood on its own. The similarity constraint gave the creativity a runway.

Stay with me — the best part is picking one.

.

.

.

Picking the winner

I kept coming back to Grid.

I didn’t score them on a spreadsheet. I scrolled through all five and Grid just stopped me — the graph-paper texture and file-tree hero felt like something I would have designed if I’d started from scratch.

(If you’ve ever flipped through a mood board and felt one option pull you forward before your brain could explain why, that’s the moment.)

Here’s the thing: at 78% similarity, Grid sat in a sweet spot.

The graph-paper canvas and crop-mark corners felt like visual language a developer would immediately connect with — blueprints, schematics, engineering paper. And the file-tree panel in the hero showed a project structure that looked like the design already understood what Devlog does.

You could trace the Doodler lineage if you knew where to look, but anyone seeing Grid for the first time would assume it had always been its own brand.

Claude Code receiving the selection, asking to turn variation 4, Grid, into a full design system, and beginning to build the grid design system folder

“I like variation 4, Grid, the most. Let’s turn this into a full design system.”

One prompt to go from browsing variations to generating a complete design system.

.

.

.

What came back

Claude took the Grid variation and built a complete design system — the folder mirrors the original Doodler extraction, so it slots directly into the same workflow.

Claude Code building the Grid design system, writing the design reference with tokens, the build protocol, and showing the folder structure mirroring the Doodler original
Whiteboard diagram showing what's inside the generated design system — tokens (colors, type, spacing), components (buttons, cards, inputs, nav), code snippets, and a visual gallery

Worth knowing — here’s what the generated system contains:

  • Design reference with tokens. Color palette, typography scale, spacing module, shape rules. Two absolutes: no shadows anywhere, crop marks on feature cards only.
  • Component catalog. Buttons, cards, inputs, navigation, chips, and section layouts with anatomy notes, variants, and hover/focus states.
  • Working code snippets. Reference implementations linking to a shared stylesheet, so the class names in the catalog match real markup.
  • Visual gallery. A single-page preview of the entire system at a glance. (I opened it next to the original Doodler site in a side tab. The grid-paper canvas and periwinkle accents looked like they’d always existed — which is exactly the point.)
The complete Grid design system rendered as a visual spec, showing the color palette, typography scale with Archivo headings and Instrument Sans body, shape and depth rules with radii, crop marks, and the 24px page module

The whole generation took about three minutes. From pointing at a variation on a canvas to holding a complete, structured design system ready to be converted into a skill.

Three minutes.

.

.

.

Your move

And here’s the kicker: the entire remix — extracting Doodler, generating five variations, picking Grid, and getting a complete design system back — took less than ten minutes of my active attention. Claude did the heavy lifting. I made one creative decision that mattered.

The whole thing is four steps:

  1. Extract a design system from a site you admire
  2. Remix it into variations using /design
  3. Browse the canvas and pick the variation that fits your product
  4. Generate a complete design system from that pick

If you want the generated system to activate automatically, The Skill That Makes Claude Use Your Design System Without Being Told walks through the skill conversion.

👉 Go find a site with a design you admire. Extract it. Remix it with /design. Pick the one that feels right.

Make it yours.

13 min read The Art of Vibe Coding

How to Turn AI-Generated HTML Into WordPress Blocks (Without Breaking Them)

How to Turn AI-Generated HTML Into WordPress Blocks (Without Breaking Them)
Watch the video walkthrough, or read the full written guide below.

You ask AI to design a landing page.

Thirty seconds later, you’re looking at a polished layout — hero section with gradient overlays, a three-column pricing grid, testimonials with circular headshots, a footer with social links. It looks like something a client would pay real money for.

(It probably took longer to type the prompt than to generate the design.)

Now get that into WordPress.

That’s where the mood changes.

Getting it into the WordPress block editor in a way where the client can edit the content themselves — change a heading by clicking on it, swap an image through the media library, adjust button colors from a sidebar panel, all without ever seeing a line of code — that’s a different challenge entirely. And it’s the challenge that matters, because a page the client can’t edit is a page you’ll be editing for them. Indefinitely.

For years, the options for WordPress design stayed in the same rotation:

  • Hire a designer who knows the platform,
  • Buy a pre-made theme or starter template, or
  • Use a page builder like Elementor, Divi, or Bricks.

Each one traded time for money or flexibility for complexity in its own way, and each one was the best answer available at the time.

AI rewrites the first half of this equation.

Generating a complete HTML design takes seconds — hero, pricing, testimonials, footer, responsive breakpoints, the whole page. A design that used to take days of back-and-forth now materializes in a single prompt.

The speed is genuine and dramatic.

But the second half — getting that design into the WordPress block editor as editable content your client can maintain — still has no clean path.

You’re left with a beautiful HTML file on one side, a WordPress site on the other, and a manual conversion process in between that quietly eats the time AI just saved you.

.

.

.

The Obvious Approach

The fastest path is the most literal one.

Copy the AI-generated HTML. Open a new page in the WordPress block editor. Add an HTML block. Paste.

The page renders on the front end exactly as designed.

Looks great.

Inside the editor, though, you’re staring at raw code — and every edit becomes a code task. Finding the headline between HTML tags, locating inline styles to adjust spacing, digging through anchor elements to update a link. For you, maybe this is manageable.

Tedious, but doable.

For a client who hired you to build their site so they could manage it independently?

Different story entirely.

I learned this the hard way with a client project last year.

Beautiful landing page, AI-generated in under a minute. Three weeks after handoff, the client needed to update a phone number. One phone number. They opened the editor, saw the HTML block, and called me. That’s when I realized: a page the client can’t edit isn’t actually finished.

Here’s the thing:

The block editor was built to prevent exactly this scenario — a visual interface where site owners manage content without technical knowledge. Click a block, edit the text, hit publish. When you paste raw HTML into an HTML block, you bypass everything the editor was designed to do.

The visual editing tools sit unused, and the client loses the self-service capability they were paying for.

What started as a fast delivery turns into an ongoing maintenance dependency — the kind where every small content change routes back through you, and the time AI saved on design gets spent on indefinite support.

.

.

.

A Better Idea — WordPress Block Markup

Here’s where it gets interesting.

WordPress block editor content has a structure that looks like standard HTML — because it mostly is. The key addition: each block gets wrapped in a pair of comment tags that carry the block’s configuration. These comment markers tell the editor which block type to render, what styling options were selected, and how to display the controls in the sidebar when someone clicks on the block.

Here’s what a styled button looks like in block markup:

<!-- wp:buttons {"layout":{"type":"flex"}} --><div class="wp-block-buttons"><!-- wp:button {"width":50,"style":{"border":{"radius":"9999px"},"color":{"background":"#5140A5","text":"#FFFFFF"},"typography":{"fontSize":"36px"},"spacing":{"padding":{"top":"1.5rem","right":"3rem","bottom":"1.5rem","left":"3rem"}}}} --><div class="wp-block-button has-custom-width wp-block-button__width-50"><a class="wp-block-button__link has-text-color has-background has-custom-font-size wp-element-button" style="border-radius:9999px;color:#FFFFFF;background-color:#5140A5;padding-top:1.5rem;padding-right:3rem;padding-bottom:1.5rem;padding-left:3rem;font-size:36px">Click Me!</a></div><!-- /wp:button --></div><!-- /wp:buttons -->

The comment at the top carries the block’s settings as JSON — background color, text color, padding values. Between the comments sits the rendered HTML, what the visitor sees on the front end. A closing comment marks where the block ends. This pairing of “settings comment + rendered HTML” is what makes blocks editable: the editor reads the settings from the comment and presents them as sidebar controls.

When you shift-paste that markup into the block editor, you get a fully interactive button — background color, text color, padding, link destination — all accessible through the familiar sidebar controls.

A fully editable "Click Me!" button rendered in the WordPress block editor with the block toolbar visible

The concept follows naturally:

Instead of asking AI to generate plain HTML, ask it to generate block markup directly. If the output uses valid block structure, every element becomes a native WordPress block — text in paragraph blocks, images in image blocks, layouts in properly nested column and group blocks. The client sees the same visual editor they’re used to, with every piece of content editable through the interface WordPress built for exactly this purpose.

(Stay with me — because the concept is sound, and the execution is where things get complicated.)

.

.

.

The Catch — AI Hallucinates Block Markup Too

This is where the plan hits a wall.

Block markup for a single element — a heading, a paragraph, a standalone button — usually comes out clean. Ask AI to generate a full pricing section with nested columns, grouped elements, and multiple styled components, and the output starts to drift.

  • A comment tag references an attribute the HTML doesn’t reflect.
  • Closing markers end up in wrong positions.
  • The JSON settings inside a comment use a format the editor doesn’t recognize.

Each mismatch is individually small. Together, they trigger the error every WordPress developer knows:

“Block contains unexpected or invalid content.”

WordPress block editor showing a broken block with "Block contains unexpected or invalid content" error and "Attempt recovery" button

That error means the editor compared the markup against its internal expectations and found a discrepancy. The “Attempt recovery” button sometimes resolves the issue and sometimes strips out the formatting entirely — there’s no predicting which one you’ll get.

Even the best AI models produce this kind of output.

The difficulty is structural:

Block markup needs to satisfy two consumers simultaneously. The browser renders the HTML on the front end. The editor validates the comment structure, checks every attribute against the block type’s registered schema, and verifies that the HTML between the comments matches what it would generate from those settings. When page complexity rises — nested blocks inside groups inside columns — mismatches become almost inevitable.

And the debugging?

Brutal.

Before the skill existed, I tried this approach manually. Asked AI to generate block markup for a full landing page, pasted it in, and watched the errors cascade. Fixing one block broke two others. I spent close to two hours on what should have been a ten-minute paste — and still had three broken sections at the end.

(If you’ve ever untangled holiday lights — pull one knot free and three more tighten somewhere you weren’t even looking — you know this particular brand of shenanigans.)

For a single section, maybe that’s an hour of detective work.

For a full-page layout with dozens of nested blocks, you might spend longer debugging the markup than it would have taken to build the page by hand.

There had to be a better way.

.

.

.

The Solution — Let AI Validate Its Own Output

The WordPress core team already ships libraries that perform exactly these checks — the same validation the editor runs internally when deciding whether to show that “Attempt Block Recovery” prompt.

These libraries compare saved markup against each block type’s expected output and report precisely where each mismatch occurs.

I built a skill called validate-block-markup that makes these validation libraries available to AI during the generation process.

When AI produces block markup, the skill runs it through the same checks the editor uses. If validation fails, the AI sees the specific error — which block broke, what the editor expected, what it actually received, and which attributes caused the mismatch.

And here’s the kicker — the AI corrects its own output and revalidates.

Instead of generating markup and hoping for the best, the workflow becomes:

  1. generate
  2. validate
  3. fix
  4. revalidate

Failures get fed back with enough context for the AI to understand what went wrong and make a targeted correction.

By the time you receive the final output, it’s already passed the same structural checks the block editor will run when you paste it in.

(That two-hour debugging session I mentioned? The skill handles the same work in seconds — and catches things I would have missed.)

The skill is open source and available at github.com/nathanonn/agent-skills.

Install it with one command:

npx skills add nathanonn/agent-skills --skill validate-block-markup

It works with Claude Code, Codex, Cursor, GitHub Copilot, and other AI coding agents — any tool that supports skills.

.

.

.

The Full Workflow — From HTML to Editable Blocks

Let me walk you through the complete process.

We’ll start with a landing page that AI generated as plain HTML and convert it into fully editable WordPress block editor content.

Here’s the original design — a full page with a hero section, feature grid, pricing cards, and footer:

GIF: The original HTML landing page being previewed in a browser

The goal: get this entire design into the WordPress block editor as native, editable blocks — with clean separation between markup and styles.

Step 1: Install the Skill

One command sets it up in your project:

npx skills add nathanonn/agent-skills --skill validate-block-markup
GIF: Installing the validate-block-markup skill in Claude Code

Step 2: Ask AI to Convert the HTML to Block Markup

Point the AI at your HTML file and describe what you need. Here’s the prompt I used:

I need this html: @devlog-site/index.html in html markup that can be used in WordPress block editor. Use core blocks onlyPut it at: index-markup.htmlPut the css at a separate file: styles.cssAll the css needs to be prefix with doodler-

Three things happening in that prompt: convert to block markup using only core blocks, prefix all CSS selectors to prevent conflicts with the active theme, and output the markup and styles as separate files.

A quick note on “core blocks only” — WordPress ships with a built-in library of block types: paragraphs, headings, images, buttons, columns, groups, and dozens more. These blocks are available on every WordPress installation without plugins. By constraining the AI to core blocks, the resulting markup works on any WordPress site, regardless of what plugins are installed. No dependencies, no compatibility concerns.

As the AI works through the conversion, the validation skill activates automatically.

Each section of block markup gets checked against the WordPress validation libraries in the background. When a block fails — wrong nesting, mismatched attributes, a comment structure the editor wouldn’t accept — the AI sees the error with full context and corrects the markup before moving to the next section.

You can watch this happen in real time.

Sections that pass validation move forward. Sections that fail get corrected and revalidated on the spot. The AI handles the debugging loop on its own — the same loop that took me two hours by hand — and the final output arrives pre-validated.

GIF: Claude Code converting HTML to validated WordPress block markup

Step 3: Paste Into the WordPress Block Editor

Open your page in the WordPress block editor and switch to Code Editor view. Shift-paste the validated markup. Switch back to the visual editor.

GIF: Pasting validated block markup into WordPress block editor — all blocks render cleanly

Every element shows up as a native, editable WordPress block. Columns render with proper nesting. Groups contain their child blocks correctly. Paragraphs, buttons, and images all appear with their sidebar controls fully functional.

And critically — no “Attempt recovery” prompts anywhere on the page.

I’ll be honest — the first time I pasted an entire validated page and saw zero recovery prompts, I scrolled through twice just to make sure I wasn’t missing something. Every block, every nested column, every styled button — all clean. That was the moment this stopped being an experiment.

The client can click on any block and edit it through the visual interface — change text inline, adjust colors through the sidebar, rearrange sections by dragging.

The page works exactly like content they built directly in the editor. No special instructions needed.

.

.

.

Why This Matters — The Client Handoff

Here’s the practical payoff — and if you build WordPress sites for clients, this is the section that matters most.

A client who receives a WordPress site expects to manage their own content — updating copy when their business evolves, swapping images for seasonal campaigns, adjusting layouts as their needs change. The block editor handles all of this through a visual interface that requires zero technical knowledge. That’s the whole reason WordPress built it.

When AI-generated HTML sits inside an HTML block, you’ve delivered a page the client can see but can’t meaningfully touch.

Every future content change routes back through you. The site looks finished, but the client’s independence — the thing they were paying for — doesn’t actually exist.

(If you’ve ever handed over a site and then fielded a call every time the client needed a comma changed, you know how fast “finished” starts feeling like “ongoing.”)

Converting that same HTML into validated block markup changes the dynamic entirely.

The client receives a page where every section behaves like the WordPress content they already know how to work with — drag blocks to rearrange the layout, change colors through sidebar controls, edit text by clicking on it, add new sections from the block inserter. The visual editor becomes a functional tool for them, working the way it was designed to work.

👉 This is where AI design speed and WordPress block editor editability finally meet. The validate-block-markup skill ensures AI-generated output passes the editor’s structural validation and arrives as clean, editable blocks your client can maintain.

And the workflow scales.

One landing page, five inner pages, an entire site redesign — the process stays the same. Generate the HTML, convert to block markup with validation, paste into the editor. Each page arrives with every block editable, every section rearrangeable, every piece of content accessible through the visual tools WordPress already provides.

Your client gets a site they can actually own — and you move on to the next project instead of fielding change requests.

.

.

.

Try It With Your Own Designs

Install the validate-block-markup skill:

npx skills add nathanonn/agent-skills --skill validate-block-markup

The full repository is at github.com/nathanonn/agent-skills.

Pick an AI-generated landing page — a portfolio layout, a services page, whatever design is sitting on your desktop right now. Run it through the workflow: install the skill, ask AI to convert the HTML to block markup, and paste the validated output into the WordPress block editor.

Then hand the page to someone who’s never written a line of code and watch them edit it — clicking a heading to change it, dragging a section to a new position, managing their own content without calling you.

That’s the whole point.

11 min read The Art of Vibe Coding

The Missing Step That Makes AI-Built WordPress Plugins Look Professional

The Missing Step That Makes AI-Built WordPress Plugins Look Professional

Something nagged after the last experiment.

Can GPT-5.6-Luna Max Build a CodeCanyon-Grade WordPress Plugin? showed that Luna Max could build a functional WooCommerce bulk stock manager — per-variation editing, filters, batch operations — for 95% less than the GPT-5.5 build before it. Two models, two builds, same result: working plugins that passed manual verification.

Same weakness, too.

I opened both plugins in the browser after the previous post went live.

They worked. They passed verification. And I caught myself doing that thing where you tilt your head and think… fine, I guess.

Pull up either build and the interfaces look adequate. Functional. Plain. The kind of admin pages that work correctly and carry zero conviction about visual hierarchy or user flow.

That feeling stayed with me.

The code was solid — verification passed — but the output looked like it came from a machine that knew what controls to include and had no intuition about where to put them.

(If you’ve ever handed a project to a developer who nailed every acceptance criterion and missed the soul of the design, you know this particular flavor of disappointment.)

The reason was straightforward:

The requirements document defined what the plugin should DO, down to acceptance criteria for every user story. What it should LOOK like was left entirely to the model’s discretion. And models, given discretion, make safe choices. Like asking someone to furnish a room when you’ve only told them the square footage — they’ll pick reasonable furniture, arrange it sensibly, and the room will never feel like anyone lives there.

The missing step was learning to prototype a WordPress plugin’s interface before generating code — an explicit design phase where you build a throwaway UI, review it, annotate what needs to change, and iterate until the interface feels right. Then you hand that finalized design to the goal pipeline as a visual reference.

The prototype-first workflow: Requirements Doc → Build Prototype → Review and Annotate → Iterate → Feed to Goal Pipeline → Polished Plugin

Here’s what happened when I tried it.

.

.

.

The Problem with Skipping Design

Here’s what the previous builds looked like.

The GPT-5.5 build's admin page — functional grid with basic controls, no visual hierarchy or section grouping
The Luna Max build's admin page from the previous post — same functional layout, same plain styling

Both are competent.

The grid displays products correctly, the filters work, and inline editing saves to the database. Hand either plugin to a client and they’d use it — but they’d also notice it feels more like a developer’s debugging tool than something designed for daily use.

The gaps show up in details that matter more than they seem to:

  • No section headers to group related controls
  • No helper text explaining what the filters do
  • Pagination at the top, interrupting the scanning flow instead of sitting at the bottom
  • Stock status displayed as plain text instead of color-coded indicators
  • The controls function.
  • The layout offers no guidance on how to use them.

Here’s the thing: A requirements document is functionally complete and visually silent.

A line like “the plugin SHALL display a filterable product grid with inline stock editing” produces a grid. It says nothing about how that grid should be organized, what visual cues should separate sections, or where batch controls should live relative to the data.

When the spec has no visual opinion, the model expresses none either. It picks standard patterns — data table, top pagination, dropdown filters — and moves on to the next acceptance criterion. (Sensible defaults, every one of them. Also completely uninspired.)

Better visual input changes that equation entirely.

.

.

.

The Prototype-First Approach

The concept is straightforward: before running the expensive goal-based build, build a throwaway UI simulation first.

The prototype-first approach: Requirements document feeds into a browser-based Prototype with annotation bubbles (remove this, improve design, move here), which feeds into the Final Plugin with WordPress logo and checkmark. Tagline: Throwaway code, One command to run, No WordPress needed.

The prototype runs in the browser as a standalone page that reproduces the WordPress admin look and feel — sidebar, admin bar, page headers. Inside that frame, the plugin’s interface takes shape with real interactive controls, backed by sample data instead of a database. No WordPress installation required.

WordPress admin conventions are predictable enough that a simulation looks close to the real thing. (Decades of admin screen consistency will do that.) Design decisions made in the prototype transfer directly to the actual build.

The prototype serves as a visual spec.

When you later run the goal-based build, you tell the model: “follow the UI in the prototype folder.” The model references it during execution and uses it to make layout decisions instead of guessing.

The skill that makes this work is called prototype-wp, derived from Matt Pocock’s prototype skill (github.com/mattpocock/skills, MIT licensed).

His original provides the core framing — throwaway code, one command to run, in-memory state, built for answers rather than production. The WordPress-specific layer adds admin-faithful rendering, verification, and separation between layout and behavior.

The prototype-wp skill installed in the skills folder, showing SKILL.md, BUILD.md, DRIVER.md, OUTPUTS.md, VERIFY.md, and the kit directory

.

.

.

Installing the Skill

One command:

npx skills add nathanonn/agent-skills --skill prototype-wp --agent codex
Terminal showing the skill installation command with repository cloned, 11 skills found, prototype-wp selected, and project installation scope chosen

The installer clones the repository, finds the skill, and drops it into your project’s skill directory.

Installation complete with security risk assessment showing Safe generation, 0 socket alerts, and Low Risk from Snyk

.

.

.

Building the Prototype

Here’s an important workflow detail: run this skill in the Codex app, not the CLI.

The reason comes down to one feature.

The Codex app lets you annotate the generated UI directly — draw on the screen, point at specific elements, leave notes. The CLI gives you text-only interaction. For a workflow where the whole point is visual feedback, that annotation capability changes everything.

(More on this in a moment.)

I pointed Luna Max at the requirements document and asked for a full prototype covering every aspect of the plugin spec.

Codex app showing the prototype-wp skill prompt with Luna Max selected, pointing at requirements.md with instructions to use playwright-cli for verification and ask-first for clarifications

42 minutes later, the prototype was running on a local server.

Split view — Codex app on the left showing completion in 42 minutes with 11 files edited, prototype on the right showing the full Bulk Edit Stock admin page with WordPress chrome, search/filter controls, state inspector, and product grid

One command starts the server.

None of this code ships — the prototype is disposable by design.

Its only job: does the interface make sense before we spend six hours building the real thing?

.

.

.

Reviewing the Prototype

What the prototype produces is genuinely close to the real WordPress admin experience.

The page looks like a real admin screen. Inside that familiar frame, the plugin interface fills out: search, filters, and a product grid showing 62 sample products.

Full admin page prototype showing the product grid with 62 products, filter controls at top, and state inspector panel

Each product row shows the key stock information at a glance. Variable products get expand/collapse toggles that reveal per-variation rows underneath.

Variable product expanded to show 6 variations with individual stock quantities and status

Click any cell and it becomes editable — quantities as number inputs, stock status as a dropdown.

Stock status dropdown open showing In stock, Out of stock, On backorder options

Modified cells highlight in yellow. A footer bar tracks how many products you’ve changed, with Save and Discard buttons.

Three products modified with yellow highlighting — quantity changed to 20, stock management unchecked, status changed to Out of stock — with Save Changes and Discard All buttons in the footer bar

The prototype also includes a live state inspector for debugging — a prototype-only panel that shows the application state after every action.

At this point, the prototype was ready to evaluate seriously.

But a few things bothered me about the default design.

The filter section looked plain. The pagination sat at the top where it interrupted the scanning flow. A button labeled “View product editor” occupied prime screen real estate without earning it.

Stay with me — because this is where the workflow earns its keep.

.

.

.

The Annotation Workflow — The Real Power

This section is why the post exists.

The Codex app has an annotation feature that works like a designer marking up a mockup.

You point at a specific element on the screen, type a note, and the model sees both the visual context and your instruction. For UI iteration, this is dramatically more useful than describing changes in text — because the model knows exactly which element you’re talking about.

I pointed at the button and typed two words: remove this. That was the whole instruction. No paragraph explaining which element, no CSS selector, no coordinates.

Three annotations total:

  1. Pointing at the “View product editor” button: “remove this button”
  2. Pointing at the filter/search section: “the design looks plain. Help me improve this”
  3. Pointing at the top pagination controls: “remove the pagination to the bottom”
Codex app annotation mode — a dark annotation bubble pointing at the "View product editor" button with the text "remove this button"
Second annotation — a bubble near the pagination with "remove the pagination to the bottom", and a blue badge showing 3 total annotations

Then I sent all three with a single instruction: “make changes according to the annotations.”

The Codex app chat showing all 3 annotations listed — "remove this button", "the design looks plain. Help me improve this", "remove the pagination to the bottom" — with the instruction "make changes according to the annotations. use Ask First"

Luna applied every change.

The filter section transformed.

Where there had been a bare row of dropdowns, the updated version had a section header, a descriptive subtitle with helper text, expanded category options, and a note about how filters combine. The unnecessary button vanished. Pagination moved to the bottom.

The improved prototype after annotations — section header, subtitle with helper text, expanded category list, "Filters apply together" note, and the View product editor button removed

And here’s the kicker: you’re pointing at exactly what you want changed and describing the change in natural language.

The model sees your annotation anchored to a specific location on the rendered page — no ambiguity about which element you mean or what surrounds it. (The difference between circling an item on a restaurant menu and trying to describe the dish to the waiter from memory.)

  • Want a different look for the status indicators? Point at one and describe what you’d prefer.
  • Want the batch action bar to feel more prominent? Point at it and say so.

Each annotation takes seconds, and the model applies changes with full visual context.

The iteration loop becomes: review the prototype, annotate what bothers you, let the model apply the changes, review again.

A few rounds of this and the UI converges on something you’re actually satisfied with — before a single line of production code gets written.

.

.

.

From Prototype to Production Plugin

Once the prototype felt right, I switched to the Codex CLI.

The goal-based build uses a different skill — the same one from the previous posts — that takes a requirements document and generates a full project scaffold. This time, the prompt included one additional instruction: follow the UI design in the prototype folder. The model could reference the prototype’s layout decisions during goal execution and use them as a visual spec for control placement, section grouping, and styling choices.

Codex CLI showing the wp-requirements-to-goals skill invoked with gpt-5.6-luna max, pointing at requirements.md with instructions to follow the prototype folder's UI

The scaffold phase generated 9 goals in 47 minutes — foundation, user story goals, feature goals, and an integration sweep.

Project scaffold complete — 9 goals generated in 47 minutes, passing protocol integrity checks, JSON/shell/PHP syntax checks, and dry-run of all 9 goals

I kicked off the build script before dinner and checked back after a movie. Six and a half hours, zero intervention.

All 9 goals completed in 392 minutes with 0 skipped, showing the full goal tree in the file explorer

When I opened the finished plugin in the browser, the prototype’s influence was visible immediately.

The design decisions from the annotation phase — section headers, helper text, bottom pagination, colored status badges, batch action bar — carried through to the real plugin.

(The kind of detail that makes the throwaway prototype feel less throwaway and more like the most productive hour of the entire build.)

Of the total time, only the annotation and review — roughly 15 minutes — required active attention.

Everything else ran unattended.

For a build that costs around $7, adding under an hour of design work is a marginal investment with a substantial payoff.

.

.

.

The Result — Prototype vs. No Prototype

Let me show you what the prototype step produced.

The final plugin UI with prototype — showing section headers, helper text, batch action bar, product grid with stock status badges and sortable columns

Compare that to the build from the previous post — same requirements document, same model, no prototype step:

The Luna Max build's admin page from the previous post — same functional layout, same plain styling

When I opened the final plugin, the section headers were there. The status badges were there. I scrolled through it twice because I kept expecting something to be missing.

The differences show up across the entire interface:

  • Section headers and helper text — “CATALOG CONTROLS” with a subtitle explaining how to use the filters, instead of bare filter fields on their own
  • Batch action bar — Controls for setting stock quantity, stock status, and toggling stock management in bulk
  • Bottom pagination — The grid flows naturally into page controls at the end
  • Stock status badges — Green and orange indicators that communicate status at a glance
  • Sortable columns — Click to reorder by product name, SKU, quantity, or status
  • Per-variation editing — Expanding a variable product reveals individual variation rows with editable fields and status dropdowns
Product grid showing batch actions at top, pagination at bottom, and expanded product list with type badges and stock status badges
Variable product expanded showing per-variation editing with editable quantity fields and stock status dropdowns

👉 The prototype acted as a visual specification — and every design decision from the annotation phase survived the translation into a real WordPress plugin running on WooCommerce.

That survival rate is the entire argument for this workflow.

A prototype gives the model a visual contract to honor, the same way the requirements document gives it a functional contract. When both inputs exist, the model has answers for “what should this do?” and “what should this look like?” — and the output reflects that clarity.

.

.

.

Use the Skill

Install the prototype skill:

npx skills add nathanonn/agent-skills --skill prototype-wp --agent codex

You’ll find the full repository at github.com/nathanonn/agent-skills.

.

.

.

The Bigger Picture

The bottleneck in AI-assisted plugin development keeps shifting.

Code quality was settled first — the models can write working plugins. Then cost fell by 95% with Luna Max. The remaining gap turned out to be design quality, and the answer was the same kind of answer as always: give the model better input.

A prototype is better input than a requirements document alone.

  • The requirements tell the model what the plugin does.
  • A prototype shows what it looks like doing it.

And the cost of this extra step: 42 minutes of prototype generation plus annotation time.

For a build that runs six-plus hours unattended, adding under an hour of directed design work is a marginal investment — and the result is a plugin you could actually put in front of users without apologizing for the interface.

When you prototype a WordPress plugin before building it, you’re giving the model a concrete visual target instead of asking it to invent one. The requirements define the contract. The prototype defines the experience.

Together, they produce output that looks like someone planned it — because someone did.

13 min read The Art of Vibe Coding

Can GPT-5.6-Luna Max Build a CodeCanyon-Grade WordPress Plugin?

Can GPT-5.6-Luna Max Build a CodeCanyon-Grade WordPress Plugin?

I almost didn’t run this experiment.

The last time I tried this — building a full WooCommerce plugin from a requirements document, unattended — the bill came to $131. That was I Gave Codex a Requirements Doc and Got a CodeCanyon-Grade Plugin Back — ten goals, nearly five hours of machine time, a working bulk stock manager with per-variation editing at the end. The genre of plugin that sells on CodeCanyon for $30–60.

That $131 is an API-equivalent cost — what the build would have run at published rates. On a ChatGPT Pro subscription, the usage is included, but the API math tells you how efficiently the model uses tokens. Efficiency is what this experiment is about.

At $131, the previous build felt like a considered investment. Then I looked at Luna’s pricing and thought — at this rate, the experiment costs less than the coffee I’m drinking while I decide whether to run it.

So I ran it.

Everything was identical — the requirements document, the skill, the bash script. One flag changed in the Codex terminal: I swapped GPT-5.5 for gpt-5.6-luna max, a model that’s 25x cheaper per token.

The result surprised me.

.

.

.

Why GPT-5.6-Luna Max Is Worth Testing

On July 30, 2026, OpenAI cut GPT-5.6-Luna’s API pricing by 80%.

RateBeforeAfter
Input (per 1M tokens)$1.00$0.20
Output (per 1M tokens)$6.00$1.20
Cached input (per 1M tokens)$0.10$0.02

That makes Luna 25x cheaper than both GPT-5.5 and GPT-5.6-Sol, which sit at $5/$30 per million tokens.

A price cut that steep is interesting on its own. But what makes gpt 5.6 luna max worth testing seriously is the benchmark context.

Stay with me on the numbers — they set up the rest of the post.

DeepSWE v1.1 — 113 real-world software engineering tasks across 91 repositories and 5 languages — ranks the current generation of coding models on both score and cost per task. Here’s how Luna stacks up against the models that matter for autonomous coding work:

ModelEffortScoreAvg Cost/Task
claude-opus-5max74%$11.84
gpt-5.6-solmax73%$8.39
claude-fable-5max70%$21.63
gpt-5.6-lunamax67%$0.61
gpt-5.5xhigh67%$7.23
claude-opus-4.8max59%$13.22

Luna at max reasoning scores 67% — identical to GPT-5.5, within the error bars of models costing 10x to 35x more, and 8 points ahead of Opus 4.8 at a fraction of the price.

That puts it at the efficiency frontier. The best score-per-dollar on the board by a wide margin — $0.61 per task versus $7.23 for GPT-5.5 at the same score.

The question I wanted to answer: does that benchmark efficiency translate to a real, multi-goal plugin build where each goal carries its own contract and verification?

.

.

.

Same Skill, Different Model — The Setup

The experiment design was deliberately boring.

(The boring parts are what make it trustworthy.)

I used the same skill from the previous post — the one that takes a structured requirements document and decomposes it into a full project scaffold with layered goals. Same requirements doc with tagged user stories, explicit acceptance criteria, and edge cases around out-of-stock states and variable-product handling. Same bash script to chain goals automatically.

Codex terminal showing gpt-5.6-luna max as the active model, with the wp-requirements-to-goals skill loaded and the requirements document ready to process

One variable. One comparison. The model flag in the Codex terminal went from GPT-5.5 to gpt-5.6-luna max. Everything else — the skill, the spec, the verification protocol, the run script — stayed identical.

That constraint matters.

If both models receive the same input and the same execution harness, any difference in the output tells you something about the model — how it decomposes, how long it takes, what it costs, and whether the result actually works when you open the browser and click through it.

.

.

.

The Q&A Phase — Luna Asks More Questions

Here’s where the first difference showed up.

The skill’s decomposition phase asks clarification questions before generating goals — things like project naming conventions, version targets, and how to slice user stories into goal boundaries. With GPT-5.5, that phase took two rounds of Q&A. Quick and confident. The model probed the repo, confirmed a few defaults, and started generating.

Luna asked six rounds:

  • Project vocabulary.
  • Baseline versions.
  • Foundation goal specifics.
  • Per-user-story acceptance criteria.
  • Derived coverage for feature goals.
  • Integration test case definitions.

The model wanted to confirm every layer of the decomposition before committing to a plan.

Phase 1 Q&A showing project vocabulary and WordPress baseline questions — plugin name, PHP namespace, CSS prefixes, text domain, minimum versions, WooCommerce reference, and test priority — all answered with recommended options
Foundation goal Q&A with 4 questions about the walking-skeleton artifact, CSS identifiers, hardcoded row data, and settings catalog handling — all answered with recommended options
Derived acceptance criteria Q&A showing proposed coverage for four feature goals — filtering/search, batch operations, access/dependency, and staged saving/validation — with 3 questions answered using recommended options

I answered every question with the recommended option.

The whole exchange felt like confirming a travel itinerary that someone else planned well — flight, hotel, rental car, seat preference, meal choice, extra legroom. Yes to everything. The recommendations were sensible, and the requirements doc had already made most of the hard decisions.

Here’s the thing that surprised me about this phase: the cheaper model was the more cautious one. GPT-5.5 had enough confidence to fill in gaps and move on with two rounds. Luna asked permission first, six times over — double-checking decisions the spec had already made, probing corners the more expensive model just handled quietly.

Side-by-side flow comparison: GPT-5.5 completes its Q&A in 2 rounds and 19 minutes, while GPT-5.6-Luna Max takes 6 rounds and 51 minutes — the taller Q&A box visually showing the cheaper model's extra caution

Whether that extra caution helps or slows things down probably depends on the spec you feed it. With a vague requirements document, those extra questions could be the difference between a clean decomposition and a broken one. With a thorough spec like this one, they were confirmation of decisions already made — helpful, but not load-bearing.

(I keep wondering whether that caution pattern shows up broadly across cheaper models, or whether it’s specific to Luna. Worth watching.)

.

.

.

The Scaffold

The Q&A rounds fed into the decomposition, and about 51 minutes after invoking the skill, the scaffold was done.

Nine goals. One fewer than the GPT-5.5 build.

Completed scaffold showing 9 goal folders from 00-foundation through 08-integration, with the generation summary reporting "Goals: Foundation, 3 user stories, 4 feature goals, and Integration" and all protocol checks passed

The structure followed the same layering pattern as before:

LayerGoals
FoundationWalking skeleton — plugin activates, admin page renders
User stories3 goals (quick stock update, edit variations, filter and batch)
Feature goals4 goals (filtering/search, batch operations, access/dependency, staged saving/validation)
IntegrationFull regression sweep across all prior goals

That 51-minute scaffold time compares to 19 minutes in the GPT-5.5 run. Most of the difference came from those six Q&A rounds. Once Luna had its answers, the actual file generation moved at a comparable pace.

One small difference in the scaffold output: Luna’s build added a step to handle back-end dependencies separately, where the GPT-5.5 version had bundled everything through a single package manager. A minor structural choice that didn’t affect the final result — both approaches worked — but a visible sign that the two models decomposed the same requirements slightly differently.

.

.

.

The Build — Run Goals and Walk Away

Pre-flight steps — installing dependencies, starting the local WordPress environment — then the trigger:

./run-goals.sh
Terminal showing ./run-goals.sh launching the WordPress development environment and starting Goal 00-foundation with the sandbox and approval settings configured for unattended execution

Then I left.

For over six hours this time.

About two hours in, I opened the terminal tab. Not because I was worried — I’d done this before. But six hours is a different trust window than five. Goal 04 was running. I closed the tab.

Terminal showing "9 goal(s) completed in 374m 14s (0 skipped)" followed by WordPress environment shutdown

374 minutes. Just over six hours. About 90 minutes longer than the GPT-5.5 build’s 283 minutes. But the same principle held from the previous post — you’re never at the keyboard for any of it. Whether the build takes five hours or six, the human cost is identical: zero hands-on time.

One honest edge worth noting.

The final integration goal ran for 51 minutes and flagged a partial result — two out of four integration test cases passed. The agent explicitly stated it hadn’t completed verification. But the automation script committed the goal as complete anyway, because the commit logic keys on the goal finishing rather than the agent’s self-assessment.

That gap is where the human verification phase earns its keep. The machine flagged something incomplete. The script moved past it. Your job, when you open the browser, is to catch what the automation missed.

.

.

.

Does It Actually Work?

Closed the terminal. Opened the browser.

When I opened the browser and saw the admin page, my first thought was “this looks right.” My second thought, after pulling up the GPT-5.5 version in another tab, was “wait — where are the stock status dropdowns?” The core worked. The extras didn’t make the cut.

The admin page rendered with the expected columns, filters, and controls:

Bulk Edit Stock admin page showing a product grid with search field, category filter, stock status filter, and columns for product name, SKU, stock quantity, stock status, and stock management — 10 products displayed with Expand toggles on variable products

For comparison, here’s the admin page from the GPT-5.5 build:

The GPT-5.5 build's admin page — same plugin, but with additional controls: editable stock status dropdowns per row, stock management checkboxes, a bulk action bar with Set Stock Quantity / Apply buttons, Set Stock Status dropdown, Toggle Stock Management control, and Expand All / Collapse All buttons

The GPT-5.5 version included inline editing controls on each row — dropdowns and checkboxes that let you change stock status and management settings directly from the grid. It also offered a bulk action bar at the top for applying changes in batch. Luna’s build covers the core functionality — the grid, the filters, the inline quantity editing — but those extra controls are absent. You could still manage those settings through WooCommerce’s standard product editor, but the gap between the two builds is visible.

Stay with me, though — because the harder test is the one that actually matters.

Variable products had Expand/Collapse toggles to show per-variation stock. That’s the feature that breaks most quick-and-dirty implementations, because WooCommerce stores variation data separately from the parent. Getting the save path right means hitting variation-specific fields — getting it wrong produces a plugin that looks like it works until someone tries to use it with variable products.

Bulk editing view with the Avenue Everyday T-Shirt expanded to show 6 variations, modified cells highlighted yellow for Black/L variation at quantity 10 and two simple products at quantity 10, with "3 products modified" status bar and Save Changes / Discard All buttons

I edited stock for a variation and a simple product, set both to 10, and hit Save Changes. Then I opened the WooCommerce product edit screens to verify the values persisted.

The variation held:

WooCommerce variation edit page for Black/L showing stock quantity 10 persisted correctly after bulk edit, with red arrow pointing to the stock quantity field

The simple product held:

WooCommerce product edit page for a simple product showing stock quantity 10 persisted correctly after bulk edit, with red arrow pointing to the quantity field

Both builds handled per-variation stock correctly — the hardest part of the plugin’s spec.

Luna shares the same UI taste limitations that GPT-5.5 showed in the previous post — functional admin interfaces with adequate layout and no visual flair. That gap looks consistent across OpenAI’s model lineup. A day of focused styling from a human — or a separate AI session aimed at the presentation layer (using Claude models) — would bring either version up to marketplace quality.

👉 The functionality survived the same manual testing that the GPT-5.5 version passed. The plugin does what the requirements said it should do.

And that’s where the cost story gets interesting.

.

.

.

What It Cost — The Seven-Dollar Plugin

And here’s the kicker.

Cost calculation showing GPT-5.6-luna pricing: 9 completed goals over 6.23 hours, 242M total input tokens with 237M cached, 0.64M output tokens, $6.82 short cost and $13.10 long cost

Here it is side by side with the GPT-5.5 run from the previous post:

MetricGPT-5.5GPT-5.6-Luna Max
Goals109
Runtime283 min (4.7 hrs)374 min (6.2 hrs)
Input tokens208M242M
Cached tokens206M237M
Output tokens0.43M0.64M
Short cost$131.40$6.82
Long cost$254.46$13.10

$131.40 down to $6.82. A 95% reduction.

Let that satisfying number land for a second.

The Luna build actually consumed more tokens — 242M input versus 208M, partly from those extra Q&A rounds and partly because Luna used more reasoning steps per goal. But when tokens cost $0.20 per million instead of $5.00, more tokens barely registers on the bill. It’s like leaving an extra light on when your electricity rate just dropped by 96% — you’d have to try very hard to notice it on the statement.

Here’s what that shift means in practice.

At GPT-5.5 pricing, every goal carries a noticeable dollar cost, and a ten-goal build adds up to a number you’d think twice about. At Luna pricing, the entire nine-goal plugin build costs less than a large coffee. The barrier to running experiments like this has effectively disappeared — and that changes behavior.

You stop asking “is this build worth the money?” and start asking “are the requirements good enough to run?”

.

.

.

The Full Comparison

Here’s the side-by-side across every dimension that matters:

MetricGPT-5.5GPT-5.6-Luna Max
ModelGPT-5.5GPT-5.6-Luna (max)
Goals generated109
Q&A rounds26
Scaffold time~19 min~51 min
Build runtime283 min (4.7 hrs)374 min (6.2 hrs)
Short cost$131.40$6.82
Long cost$254.46$13.10
IntegrationFull passPartial (2/4 TCs)
Plugin works?YesYes
UI qualityFunctional / plainFunctional / plain

The tradeoffs are clear. Luna took longer, asked more questions during decomposition, generated one fewer goal, and flagged a partial integration result. GPT-5.5 was faster, more confident, and produced a cleaner integration pass.

But the plugin works.

The core output — a functional WooCommerce bulk stock manager with per-variation editing, filtering, and batch operations — is comparable from both models. The question becomes whether those tradeoffs matter enough to justify the 19x price difference.

For a production build where you need maximum confidence in the integration sweep and don’t want to hand-verify anything the agent flagged, GPT-5.5 or Sol earns its premium. For experiments, prototypes, internal tools, or any build where you plan to open the browser and verify the result yourself — and you should — Luna at $7 changes the economics entirely.

.

.

.

Grab the Plugin

The full project is on GitHub: wc-bulk-edit-stock. The main branch has the GPT-5.5 build from the previous post. The gpt-5.6-luna-max branch has this build — every goal folder, the bash script, the complete Codex run history. You can compare both implementations side by side by switching branches.

.

.

.

Use the Skill for Your Own Plugin

Install the skill:

npx skills add nathanonn/agent-skills --skill wp-requirements-to-goals --agent codex

The repo is at github.com/nathanonn/agent-skills.

One prerequisite to know about: the verification step in each goal uses playwright-cli for browser-based tests against the running WordPress environment. If you want the full workflow — including automated verification — you’ll need it installed. The playwright-cli README covers the setup.

The real prerequisite — ferpetesake — is learning to write requirements well. Start with How to Write Better Requirements with Claude (Stop Letting AI Assume) if you haven’t already.

.

.

.

The Bigger Picture

The price barrier for this workflow just dropped by 95%.

A month ago, running a full multi-goal plugin build through Codex was a considered investment — the kind of number that makes you weigh whether the experiment is worth it before you start. At $131, deciding whether to run a build felt like deciding whether to take an Uber across town. Worth it, probably, but you’d think first. Seven dollars is bus fare. You just go.

Every dollar figure in this post is an API-equivalent cost — what you’d pay at published rates. On a ChatGPT Pro subscription, both builds would be included in the plan. But the API math reveals how efficiently each model uses tokens, and that efficiency gap matters as these workflows scale.

The tradeoff is real.

Longer runtime, one fewer goal, a partial integration flag that needed manual attention. But the core output was comparable, and the DeepSWE benchmarks suggest that pattern will hold broadly — gpt 5.6 luna max performs within error bars of far more expensive models at a fraction of the cost.

As models get cheaper and benchmark scores converge, the bottleneck keeps shifting toward the human input. The machine’s part of the work — decomposing a plan, writing code, running verification — is becoming commoditized. The human’s part — writing requirements that define exactly what “done” means and then verifying whether it’s actually done — keeps gaining leverage.

Your job is still to get good at writing the plan. The cost of executing it? Less than the coffee you’re drinking while you decide whether to try it.


More workflows like this — AI-assisted development with Claude Code, Codex, and the tools between them — land in The Art of Vibe Coding newsletter every week. If this one was useful, the next one probably will be too.

10 min read The Art of Vibe Coding

GPT-5.6 Sol Outside Codex, Part 2: The Copilot CLI Setup That Manages Context For You

GPT-5.6 Sol Outside Codex, Part 2: The Copilot CLI Setup That Manages Context For You

In GPT-5.6 Sol Is Way Better in Claude Code (Here’s How to Set It Up), I walked you through running Sol inside Claude Code using a local proxy — and asked you to try it yourself.

I took my own advice. A full week of daily use.

Two things surprised me.

Sol inside Claude Code produced remarkably strong design output — richer layouts, more component variety, deeper page structures — even with zero custom skills or design system context loaded. The same model in Codex, given identical prompts at the same reasoning effort, came back with clean but noticeably simpler pages.

(It’s as though Claude Code’s system instructions act like invisible scaffolding — quietly pushing whatever model you route through them toward more complete work.)

The second observation is less definitive.

Sol in Claude Code appears to drain less of my ChatGPT Pro allowance than the same work in Codex. I want to be upfront: I haven’t measured this. The observation comes from a week of normal sessions and some Codex dashboard squinting.

Take it with a generous grain of salt until someone benchmarks it properly.

Two-column comparison chart titled GPT-5.6 Sol In Claude Code vs In Codex showing two rows: Design output with richer layouts and more component variety on the Claude Code side versus clean but noticeably simpler on the Codex side, and ChatGPT Pro usage with battery icons showing seems to drain less on the Claude Code side versus drains faster on the Codex side, with a footnote reading one week of daily use vibes not benchmarks

Both findings deserve a closer look down the road.

But over that same week, a bigger problem surfaced — one that had nothing to do with model quality or allowance drain.

The context window.

.

.

.

The Context Window Problem

Claude Code has auto-compaction built in — but it fires as a last resort, when the window is already full and quality has already started to degrade. I wrote about this in Never Let Claude Code Auto-Compact Again, where I recommended managing context manually at clean task boundaries.

I literally wrote the post on manual compaction — and I still catch myself glancing at the context meter like it’s a fuel gauge on a long drive. The discipline works.

It’s also a tax.

Here’s the thing.

Long sessions accumulate context faster than you’d expect. File reads, tool responses, assistant turns, hook output — all of it stays in the window, in full, on every single turn. The model reprocesses that entire history each time it generates a response. Once the window crosses roughly 60%, you drift into what I’ve been calling the “dumb zone” — the region where output quality degrades because the model is wading through too much stale material.

You don’t notice right away.

That’s the insidious part.

The first few responses past 60% look fine. Then constraints start getting missed. Suggestions repeat. Decisions from earlier in the session get quietly forgotten. By the time you think to check the actual numbers, the session is already deep in the red.

Here’s what 90% looks like:

Claude Code /context output showing GPT-5.6 Sol at 334.7k out of 372k tokens used, 90 percent capacity, with Messages consuming 83 percent of the window and only 9.1 percent free space remaining

334,700 out of 372,000 tokens. Messages eating 83% of the window. 9.1% free space remaining.

Somewhere past the 90% mark, I typed a one-line follow-up and went to make coffee. The reply was still streaming when I came back — four minutes for something that took fifteen seconds at the start of the session.

That speed tax compounds.

As the window fills, replies that started at 10–15 seconds stretch into 4–6 minute waits. Over a multi-hour session, you’re losing real time on top of degraded quality.

Codex handles this transparently — it compacts proactively as you work, keeping the window fresh without intervention.

After a week of watching Sol produce excellent output inside Claude Code — only to hit the context wall in every long session — the question became obvious: can you run GPT-5.6 Sol in Copilot CLI and get that same automatic context management outside of Codex?

You can.

Side-by-side comparison showing Claude Code context window climbing into a red dumb zone above 60 percent with the label you manage it manually, versus Copilot CLI context held below 60 percent by auto-compaction with the label it manages itself

.

.

.

How Copilot CLI Manages Context

I remembered something from my Copilot CLI experiments last year, back before the pricing change: sessions just… kept going. No wall. At the time I didn’t appreciate why.

Now I do.

When the conversation reaches approximately 80% of the context window, Copilot CLI starts compacting in the background. You keep working — tool calls continue, responses keep flowing. The compaction replaces your conversation history with a structured summary: the session’s goals, what was accomplished, key technical details, important files, and planned next steps. The summary is built for continuation, so the model picks up the thread without losing direction.

Four-step flow diagram showing context hitting approximately 80 percent then compaction running in background while you keep working then history becoming a structured summary of goals decisions and next steps then checkpoint saved as context drops and the session continues

Every compaction — automatic or manual — creates a checkpoint.

Checkpoints are numbered, titled snapshots of the summary, and you can inspect them anytime with /session checkpoints. (Think of them as breadcrumbs — a record of where the session has been and what it decided along the way.)

Copilot CLI session checkpoints output listing 2 checkpoints with titles showing the progression of a multi-phase coding session

In my sessions running GPT-5.6 Sol in Copilot CLI, the context rarely exceeded 60%. On some occasions it climbed toward 70%, but compaction always brought it back down before the dumb zone became a factor.

Copilot CLI running GPT-5.6 Sol at Extra High effort with context usage at 58 percent during active work

58% context during active work with Sol at Extra High effort. In Claude Code, that same kind of session would already be deep in the dumb zone.

For the full technical breakdown — including manual compaction, live context inspection, and large tool output handling — see GitHub’s context management documentation.

.

.

.

The Setup: What’s New for Copilot CLI

Here’s what changed.

If you followed last week’s setup guide, you already have CLIProxyAPI installed, configured, authenticated with your OpenAI account, and running as a background service.

All of it carries over.

The proxy, the configuration, the OAuth session, your existing proxy key — Copilot CLI plugs into the same infrastructure. The only new pieces are Copilot CLI itself and a launcher function that routes requests through your existing proxy.

Here’s the full request chain:

Architecture flow showing GitHub Copilot CLI connecting through the OpenAI Responses API to the CLIProxyAPI local proxy on 127.0.0.1:8317 which authenticates via Codex OAuth to your ChatGPT and Codex account reaching GPT-5.6 Sol

Install Copilot CLI

On macOS via Homebrew:

brew install --cask copilot-cli

On Linux via npm (requires Node.js 22 or newer):

npm install -g @github/copilot

Confirm the installation:

copilot version

The Launcher Function

The launcher creates an isolated Copilot profile that routes model requests through your CLIProxyAPI proxy. Your normal copilot command stays completely untouched — nothing about your existing Copilot setup changes.

Add this function to your shell configuration file (.zshrc on macOS, .bashrc on Linux):

copilotx() (  set -eu  key_file="${COPILOTX_KEY_FILE:-${XDG_CONFIG_HOME:-$HOME/.config}/copilotx/proxy-key}"  if [ ! -r "$key_file" ]; then    printf 'Missing proxy key: %s\n' "$key_file" >&2    exit 1  fi  proxy_key="$(tr -d '\r\n' < "$key_file")"  if [ -z "$proxy_key" ]; then    printf 'Proxy key is empty: %s\n' "$key_file" >&2    exit 1  fi  # Remove stale custom-provider settings that could override this route.  unset COPILOT_PROVIDER_BEARER_TOKEN  unset COPILOT_PROVIDER_MODEL_ID  unset COPILOT_PROVIDER_WIRE_MODEL  unset COPILOT_PROVIDER_TRANSPORT  unset COPILOT_PROVIDER_MAX_PROMPT_TOKENS  unset COPILOT_PROVIDER_MAX_OUTPUT_TOKENS  # Route Copilot CLI through the local OpenAI-compatible proxy.  export COPILOT_PROVIDER_TYPE="openai"  export COPILOT_PROVIDER_BASE_URL="http://127.0.0.1:8317/v1"  export COPILOT_PROVIDER_API_KEY="$proxy_key"  # GPT-5.6 Sol uses the OpenAI Responses API.  export COPILOT_PROVIDER_WIRE_API="responses"  # Model exposed by CLIProxyAPI through Codex OAuth.  export COPILOT_MODEL="gpt-5.6-sol"  command copilot \    --model "$COPILOT_MODEL" \    --effort "${COPILOTX_EFFORT:-high}" \    "$@")

After saving, reload your shell:

source ~/.zshrc   # macOSsource ~/.bashrc  # Linux

The function runs in a subshell, so all the proxy variables vanish when the session ends. Your normal Copilot configuration remains separate — switching between copilotx (Sol through the proxy) and copilot (GitHub-hosted models) is just a matter of which command you type.


Reusing Your Existing Proxy Key

If you already have a proxy key from the Claude Code setup, you don’t need to generate a new one. (One less secret to manage.) Point the launcher at your existing key file before launching:

export COPILOTX_KEY_FILE="$HOME/.config/claudex/proxy-key"

Or change the default path inside the function itself:

key_file="${COPILOTX_KEY_FILE:-${XDG_CONFIG_HOME:-$HOME/.config}/claudex/proxy-key}"

The key must match the value in your CLIProxyAPI configuration — the same key you’re already using for the Claude Code proxy.


The Two Flags You Need

Launch Copilot with full autonomous access:

copilotx --allow-all --autopilot

Both flags work together:

FlagWhat it grants
--allow-allAll tools, workspace and external paths, URL access
--autopilotAutonomous continuation through successive implementation steps

And here’s the kicker — running autopilot without full permissions creates a specific failure mode: the agent reaches an operation that needs approval, can’t pause for your input, and the operation gets automatically denied. The session stalls with permission errors instead of making progress. Both flags together give the agent the autonomy and the permissions to work through multi-step tasks end to end.

For a normal interactive session where Copilot asks before each sensitive action:

copilotx

Worth knowing: the launcher deliberately keeps these flags out of its defaults. You add them explicitly each time, so you’re always making a conscious choice about how much autonomy to grant. (GitHub’s own documentation recommends using --allow-all only inside repositories you trust.)


Choosing Reasoning Effort

The launcher defaults to high. Override it depending on the task:

EffortWhen to useLaunch command
lowFast, routine tasksCOPILOTX_EFFORT=low copilotx --allow-all --autopilot
mediumStandard implementationCOPILOTX_EFFORT=medium copilotx --allow-all --autopilot
highGeneral work (default)copilotx --allow-all --autopilot
xhighComplex debugging, architecture, migrationsCOPILOTX_EFFORT=xhigh copilotx --allow-all --autopilot

Verify the Setup

Two quick checks while CLIProxyAPI is running. That’s all.

Proxy health:

KEY="$(tr -d '\r\n' \  < "${XDG_CONFIG_HOME:-$HOME/.config}/copilotx/proxy-key")"curl -sS \  -o /dev/null \  -w 'Proxy HTTP status: %{http_code}\n' \  http://127.0.0.1:8317/v1/models \  -H "Authorization: Bearer $KEY"unset KEY

Expected:

Proxy HTTP status: 200

When reusing the key from the Claude Code setup, change the path in the command to point at your existing key file location.

End-to-end through Copilot:

COPILOT_OFFLINE=true \  copilotx -p 'Reply exactly: copilot-sol-ok'

Expected:

copilot-sol-ok

The offline flag prevents Copilot CLI from contacting GitHub during this test while still allowing requests through your configured model provider. It’s a clean way to confirm the response is coming through the local proxy rather than a GitHub-hosted model.

If both checks pass, the full chain is working: Copilot CLI to your local proxy to Codex OAuth to Sol and back.

.

.

.

What This Costs

Model inference goes through your ChatGPT/Codex allowance via the Codex OAuth session you set up last week — the same one your Claude Code proxy already uses. There’s no separate OpenAI API key involved, and Copilot’s own credit system doesn’t apply to BYOK model requests routed through a custom provider. (Your Codex allowance does the heavy lifting here — the proxy just translates the request format.)

Your GitHub sign-in remains separate. When you’re signed into GitHub, Copilot CLI can still use GitHub-specific capabilities — repository tools, issue lookups, pull request context, code search — while model inference follows your configured BYOK provider.

One caveat worth stating clearly: this exact end-to-end combination — Copilot CLI routing through CLIProxyAPI to Codex OAuth — works reliably in my testing, but the complete chain is a community integration. GitHub and OpenAI haven’t officially documented it as a supported configuration. Verify your usage on the Codex dashboard after the first few sessions to confirm billing lands where you expect.


Try It and Report Back

The setup adds roughly ten minutes on top of what you built last week.

The payoff is Sol running through Copilot CLI with automatic context management — structured compaction summaries, inspectable checkpoints, and a context window that stays in the productive zone without you having to babysit it.

Run a long session. Watch how the context behaves when you check /context after an hour of real work. If you’ve been hitting the dumb zone in Claude Code, the difference should be visible fast.

And if you notice anything about the token-usage observation — whether Sol through Copilot CLI drains more or less of your Codex allowance compared to Sol in Codex directly — I’d like to hear about it. My own dashboard squinting suggests a difference, but one person’s observation shouldn’t shape yours.

Let me know what you find.

12 min read The Art of Vibe Coding

GPT-5.6 Sol Is Way Better in Claude Code (Here’s How to Set It Up)

GPT-5.6 Sol Is Way Better in Claude Code (Here's How to Set It Up)

I was halfway through Theo’s video when I opened a new terminal.

Couldn’t help it — I needed to see this for myself.

He was showing a setup where GPT 5.6 Sol runs through Claude Code’s interface using a local proxy. Same model you’d get in Codex, but wrapped in Claude Code’s system instructions, tools, and workflow scaffolding. Tibo posted about the same setup on X around the same time, and the results he shared looked impressive.

So I set it up.

Ran the same prompts through both environments. Compared the output side by side.

Here’s the thing.

The same model, at the same effort level, produces dramatically different results depending on where you run it. And the gap was wide enough that I wanted to document exactly what I saw — and then walk you through the full setup so you can try it yourself.

.

.

.

The Comparison: Same Model, Different Results

I gave the same three SaaS landing page prompts to GPT-5.6 Sol in Claude Code (via a local proxy called CLIProxyAPI) and directly in Codex. Same model. Same effort level (xhigh). Different surroundings.

The three prompts were for fictional products:

ProductDescription
DevlogA project board that lives in your codebase
FlowPilotA team coordination workspace
ReviewFlowA client feedback and review tool

Each prompt was a single sentence describing the product. Here’s what the prompt looked like in Claude Code and in Codex:

The same SaaS landing page prompt running in Claude Code (left) and Codex (right), both using GPT-5.6 Sol at xhigh effort

Identical prompt, identical model, identical reasoning effort. Let’s look at what came out.


Devlog

Devlog landing page comparison — Claude Code version (left) with rich multi-section layout versus Codex version (right) with fewer sections

The Claude Code version feels like a complete marketing site — multiple distinct sections, strong visual variety, and the kind of detail you’d expect from a finished product page. The Codex version is clean and professional, but reads more like a polished template with roughly half the depth.


FlowPilot

FlowPilot landing page comparison — Claude Code version (left) with richer component variety versus Codex version (right) with simpler patterns

The Claude Code version has richer component variety and more interactive elements throughout. The Codex version is visually cohesive and well-structured, but leans on simpler, more repetitive patterns.


ReviewFlow

ReviewFlow landing page comparison — Claude Code version (left) with pricing tiers, product demos, and FAQ versus Codex version (right) with fewer content sections

The Claude Code version goes deeper — more content-rich sections, more complex components like pricing tables and product demos, and the kind of page structure you’d see on a real SaaS site. The Codex version is polished and distinctive, but covers less ground overall.


What the Comparison Reveals

I opened the first Claude Code output next to the Codex version and actually said “wait, really?” out loud.

I expected a difference.

I didn’t expect it to be this obvious.

Across all three tests, the pattern held. Sol in Claude Code produced pages with more sections, more component variety, and more of the elements you’d expect on a real SaaS marketing site — pricing tables, FAQ accordions, testimonials with specific metrics, product demo sections.

Sol in Codex produced clean, professional pages every time.

The design quality was solid.

But the output was consistently simpler: fewer sections, fewer interactive components, less of the detail work that separates a landing page from a finished marketing site.

Theo called this out in his video.

Claude Code’s system instructions — its built-in knowledge of how to structure projects, use design patterns, and scaffold complete outputs — act as an invisible co-pilot that amplifies whatever model is behind it.

.

.

.

Why Sol Performs Better Inside Claude Code

Claude Code provides rich system instructions that shape how the model approaches every task.

When you ask for a landing page, those instructions guide the model toward common page structures, component patterns, and file organization conventions. The model receives a substantial context before your prompt even arrives.

Codex is a more minimal environment. It gives Sol direct access to tools and a sandbox, but less guidance on how to use them. The model has to infer structure, conventions, and completeness standards from the prompt alone.

Stay with me — because this is the part that reframes the whole comparison.

Think of it like a skilled carpenter and two different workshops.

The carpenter’s talent is the same in both rooms. But in the workshop with the jigs, the templates, and the well-organized workbench, every cut lands cleaner and every joint sits tighter. (If you’ve ever tried assembling IKEA furniture with the right Allen wrench versus a butter knife, you already know this feeling at a smaller scale.)

That’s the dynamic at play here.

Sol’s raw capability is impressive in both environments. Inside Claude Code, that capability gets channeled through a set of conventions and structural expectations that push the output toward completeness and consistency.

The takeaway: environment matters as much as raw model capability. And if you have access to Sol through your Codex allowance, you can put it inside the better environment right now.

.

.

.

The Setup Guide: Running GPT-5.6 Sol in Claude Code

Here’s the full walkthrough.

By the end of this section, you’ll have Sol running inside Claude Code with a dedicated launcher that keeps the proxy configuration separate from your normal Claude setup.

Let me show you what you need before we start.

What You Need

Before you start, make sure you have:

  • macOS or Linux — Windows users can follow the CLIProxyAPI Windows installation guide and the Claude Code Windows/WSL setup docs
  • Claude Code installed — if you haven’t yet, run curl -fsSL https://claude.ai/install.sh | bash
  • An OpenAI account with Codex access — Plus, Pro, Business, or Enterprise plans get Sol; Free and Go accounts receive Terra
  • CLIProxyAPI — a local proxy that translates between Claude Code’s API format and OpenAI’s Codex OAuth

The flow looks like this:

Architecture flow: Claude Code sends requests to CLIProxyAPI on localhost, which authenticates via OpenAI Codex OAuth and routes to GPT-5.6 Sol

Your prompts go through Claude Code’s interface, hit the local proxy, get translated into the Codex format, and reach Sol. Responses come back through the same chain. From your perspective, you’re using Claude Code exactly as you normally would — the model behind it is just different.


Step 1: Install CLIProxyAPI

macOS:

brew install cliproxyapi

Linux:

Download the installer, inspect it, and run it:

curl -fsSLo /tmp/cliproxyapi-installer \  https://raw.githubusercontent.com/router-for-me/cliproxyapi-installer/refs/heads/master/cliproxyapi-installer
Terminal showing CLIProxyAPI v7.2.88 installing on Linux — downloading the binary, extracting it, setting up configuration, generating API keys, and creating a systemd service

The installer places everything under your home directory, generates a default configuration, and creates a systemd service definition you can enable later. Don’t start the service yet — running it manually first makes configuration errors easier to spot.


Step 2: Configure for Security

The default configuration works, but a few changes make it safer for a proxy that handles OAuth credentials.

Here’s what the secure configuration does:

  • Binds to 127.0.0.1 only — prevents other devices on your network from reaching the proxy
  • Generates a random local API key — protects the proxy endpoints
  • Disables remote management and the web control panel — reduces the attack surface
  • Stores OAuth credentials in a dedicated directory under your home folder

The configuration is a YAML file. On macOS it lives at the Homebrew prefix; on Linux it’s in the CLIProxyAPI install directory.

macOS:

PROXY_KEY="sk-local-$(openssl rand -hex 32)"printf '%s\n' "$PROXY_KEY" > "$HOME/.config/claudex/proxy-key"

Linux:

mkdir -p "$HOME/.config/claudex"PROXY_KEY="sk-local-$(openssl rand -hex 32)"printf '%s\n' "$PROXY_KEY" > "$HOME/.config/claudex/proxy-key"

The key settings in your config file:

host: "127.0.0.1"port: 8317api-keys:  - "sk-local-your-generated-key-here"remote-management:  allow-remote: false  disable-control-panel: true

The full configuration script (with backup, permissions, and cleanup) is in the linked reference guide at the bottom of this post.

The API Key Gotcha: I spent a good twenty minutes staring at connection errors before I realized the default placeholder keys were still sitting in the config. The proxy was running, accepting connections, and rejecting every request. Classic config issue. The installer seeds the configuration with placeholder entries, and CLIProxyAPI deliberately blocks its proxy endpoints until all of them are removed. Removing the sample entries and leaving only the real generated key fixed it immediately.


Step 3: Connect Your OpenAI Account

CLIProxyAPI authenticates with OpenAI through a Codex OAuth flow. Run the login command, and your browser will open so you can sign in with the OpenAI account whose Codex allowance you want to use.

macOS:

cliproxyapi \  --config "$(brew --prefix)/etc/cliproxyapi.conf" \  --codex-login

Linux:

"$HOME/cliproxyapi/cli-proxy-api" \  --config "$HOME/cliproxyapi/config.yaml" \  --codex-login

If you’re on a headless or remote machine (like a Raspberry Pi), add --no-browser and use an SSH tunnel to forward the callback port. Running OAuth on a headless Raspberry Pi meant setting up an SSH tunnel just to complete the login. One of those detours that makes you question your choices for about ten minutes — and then it works and you forget you were ever annoyed.

"$HOME/cliproxyapi/cli-proxy-api" \  --config "$HOME/cliproxyapi/config.yaml" \  --codex-login \  --no-browser

When the OAuth flow completes, you’ll see this screen:

OpenAI authentication successful screen showing a green checkmark and the message 'You have successfully authenticated with Codex'

Step 4: Start CLIProxyAPI

Run the server manually first to verify everything is wired up correctly.

macOS:

cliproxyapi \  --config "$(brew --prefix)/etc/cliproxyapi.conf"

Linux:

"$HOME/cliproxyapi/cli-proxy-api" \  --config "$HOME/cliproxyapi/config.yaml"

You should see the server start up, refresh its model list from OpenAI, and begin listening on your configured address:

CLIProxyAPI server running — version 7.2.88, listening on 127.0.0.1:8317, with Codex client model refresh completed and 1 auth entry loaded

Leave that terminal open. The remaining steps happen in a second terminal.

Once you’ve confirmed it works, enable it as a background service so it starts automatically:

macOS:

brew services start cliproxyapi

Linux:

systemctl --user enable --now cliproxyapi.service

Step 5: The Launcher

Here’s where Claude Code and Sol actually meet.

The launcher is a shell function called claudex. It launches Claude Code with the proxy configuration pre-loaded, keeping all the proxy environment variables isolated in a subshell so your normal claude command stays completely untouched.

It provides two profiles:

ProfileMain SessionSubagentsBackground
BalancedSolTerraLuna
All SolSolSolSol

Add this function to your shell configuration file (.zshrc on macOS, .bashrc on Linux):

claudex() (  set -eu  profile="${CLAUDEX_PROFILE:-balanced}"  case "${1:-}" in    balanced|all-sol)      profile="$1"      shift      ;;  esac  key_file="${XDG_CONFIG_HOME:-$HOME/.config}/claudex/proxy-key"  if [ ! -r "$key_file" ]; then    printf 'Missing proxy key: %s\n' "$key_file" >&2    exit 1  fi  proxy_key="$(tr -d '\r\n' < "$key_file")"  if [ -z "$proxy_key" ]; then    printf 'Proxy key is empty: %s\n' "$key_file" >&2    exit 1  fi  unset ANTHROPIC_API_KEY  unset ANTHROPIC_MODEL  unset CLAUDE_CODE_USE_BEDROCK  unset CLAUDE_CODE_USE_VERTEX  unset CLAUDE_CODE_USE_FOUNDRY  export ANTHROPIC_BASE_URL="http://127.0.0.1:8317"  export ANTHROPIC_AUTH_TOKEN="$proxy_key"  export ANTHROPIC_CUSTOM_MODEL_OPTION="gpt-5.6-sol"  export ANTHROPIC_CUSTOM_MODEL_OPTION_NAME="GPT-5.6 Sol via CLIProxyAPI"  export ANTHROPIC_CUSTOM_MODEL_OPTION_DESCRIPTION="OpenAI Codex OAuth through a local proxy"  export ANTHROPIC_CUSTOM_MODEL_OPTION_SUPPORTED_CAPABILITIES="effort,xhigh_effort,max_effort"  export CLAUDE_CODE_ALWAYS_ENABLE_EFFORT=1  export CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY="${CLAUDEX_CONCURRENCY:-3}"  export ENABLE_TOOL_SEARCH=false  case "$profile" in    balanced)      export CLAUDE_CODE_SUBAGENT_MODEL="gpt-5.6-terra"      export ANTHROPIC_DEFAULT_HAIKU_MODEL="gpt-5.6-luna"      ;;    all-sol)      export CLAUDE_CODE_SUBAGENT_MODEL="gpt-5.6-sol"      export ANTHROPIC_DEFAULT_HAIKU_MODEL="gpt-5.6-sol"      ;;  esac  command claude \    --model gpt-5.6-sol \    --effort "${CLAUDEX_EFFORT:-high}" \    "$@")Shell

After saving, reload your shell:

source ~/.zshrc   # macOSsource ~/.bashrc  # Linux

Worth knowing — a few things about how this works:

  • Balanced mode uses Sol for your main conversation, Terra (faster, lighter) for subagent tasks, and Luna (fastest, cheapest) for background operations. Good for everyday work where you want Sol’s intelligence on the main task without burning through your Codex allowance on routine operations.
  • All Sol mode puts Sol everywhere. Consistent quality across the board, but concurrent subagents consume your allowance faster.
  • Effort levels control how much reasoning Sol applies. The launcher defaults to high. Override it with the CLAUDEX_EFFORT environment variable: medium, high, xhigh, or max.

Usage examples:

claudex balanced                           # Daily work, Sol main + Terra/Luna supportingCLAUDEX_EFFORT=xhigh claudex all-sol       # Complex tasks, full Sol everywhereclaude                                     # Normal Anthropic-backed Claude Code, unchanged

Because claudex runs in a subshell, exiting the session removes all the proxy variables. Your normal claude command is always there, pointing at Anthropic’s models, completely separate.


Step 6: Verify It Works

Two quick checks from a second terminal while CLIProxyAPI is running.

Check 1 — Proxy health:

KEY="$(tr -d '\r\n' \  < "${XDG_CONFIG_HOME:-$HOME/.config}/claudex/proxy-key")"curl -sS \  -o /dev/null \  -w 'Proxy HTTP status: %{http_code}\n' \  http://127.0.0.1:8317/v1/models \  -H "Authorization: Bearer $KEY"unset KEY

You should see:

Proxy HTTP status: 200

Check 2 — End-to-end through Claude Code:

claudex balanced -p 'Reply exactly: sol-ok'

If Sol responds with sol-ok, the full chain is working: Claude Code to CLIProxyAPI to Codex OAuth to Sol and back.

.

.

.

The Bigger Picture

Here’s what I found interesting about this experiment — and it goes beyond Sol specifically.

The pattern emerging is that the best results come from combining the right model with the right environment. Claude Code functions as a kind of universal cockpit — you can fly different engines through the same interface, and the cockpit’s instruments (system instructions, tool scaffolding, context management, skills) make every engine perform better than it would alone. A well-equipped cockpit improves the flight regardless of what’s generating the thrust.

If you have access to Sol through your Codex allowance, you can now use GPT-5.6 Sol in Claude Code with all the workflow benefits you’ve built up — your custom skills, your sub-agent patterns, your project-level rules. Everything carries over because the interface stays the same.

Try the setup.

Run your own comparisons.

I’m genuinely curious whether the gap I saw holds across different types of tasks — or whether there are cases where Codex’s minimal environment actually produces better output.

Let me know what you find.


References

10 min read The Art of Vibe Coding

The Skill That Makes Claude Use Your Design System Without Being Told

The Skill That Makes Claude Use Your Design System Without Being Told

You extracted a design system from a site you admire.

Every color, every font weight, every spacing value — captured. You went further and pulled the full bundle: component blueprints, section patterns, working code examples, an instruction manual that tells a coding agent exactly how to build with the system.

Then you pointed Claude Code at the bundle, told it which file to read first, and watched it produce a page that genuinely looked native to the brand.

It worked.

(And if you’ve done this even once, you know how good that moment feels.)

I closed the session that night feeling like I’d cracked something. Opened a fresh one the next morning, typed the same kind of prompt — and watched the AI produce something I wouldn’t have shipped. Soft shadows. Rounded cards. The same elevator music it always defaults to. The bundle was sitting right there in the folder, but the fresh session had absolutely no idea it existed.

So I did what you’ve probably done too.

Re-attached the bundle. Walked the agent through the protocol again. Pointed it back at the instruction manual. The page came out on-brand — because I’d stood over the machine and guided every step.

Sound familiar?

Two-panel black-and-white comic — MONDAY: a developer high-fives an AI robot in front of a branded website with confetti, saying We did it The design system works — TUESDAY: same developer exhaustedly holding up cue cards reading USE THE DESIGN SYSTEM while the AI has a question mark and the monitor shows a generic template, captioned Memory of a goldfish

(If you’ve spent more time reminding the agent about your design system than actually building with it, you know exactly what I’m describing.)

Here’s the thing.

A design system you have to keep hand-delivering isn’t really reusable yet. Your extraction was right, and the bundle was right. What was missing — the last mile — was making the agent reach for it automatically.

This week we close that gap.

One skill converts the Part 2 bundle into something Claude Code auto-triggers on any UI work, so you describe the product and the brand shows up on its own.

.

.

.

Where the First Two Posts Left Us

Two weeks ago, in I Taught Claude to Steal (Ethically) a Design System I Actually Like, I extracted a site’s design tokens — colors, fonts, spacing — and packaged them as a single file a coding agent can follow. The paint.

Last week, in I Extracted a Website’s Entire Design System Using This Skill, I went further: capturing component blueprints, section layouts, hover behaviors, and an instruction manual that tells the agent how to build with the system. The furniture.

Both outputs are excellent references.

Both are thorough, well-organized — and they sit in a folder waiting for someone (you) to carry them to the agent and explain what they mean.

For a while I had a sticky note on my monitor — ferpetesake, a sticky note — that said “ATTACH THE BUNDLE.” That’s when I realized the workflow had a hole in it.

You’ve furnished the room beautifully.

But you still have to walk the AI into it every single time.

The third skill in this series takes that finished bundle and turns it into something the agent picks up on its own — before you say a word about which brand to use.

.

.

.

The Idea: Package the System as a Skill

Here’s the shift in one line:

Instead of a folder you point at, you get a design system skill Claude Code already knows when to trigger.

Think about the difference between a reference binder on a shelf and a reflex. The binder might be thorough, beautifully indexed — but someone has to walk over, pull it down, and open it to the right page every time. A reflex fires the moment the situation calls for it. No conscious effort.

That’s what “auto-trigger” means in practice.

The skill carries a short description of when it should fire — any page, section, hero, button, card, or styling task — and Claude reads that description and applies the brand without being told which file to open. The wiring is built into the skill itself.

Whiteboard-style illustration showing three horizontal lanes — Manual: a stick figure running back and forth between a folder and a confused AI robot every session — Convert: folder goes through a funnel and becomes a star badge labeled Skill — Automatic: dev sits at a desk typing while the AI robot with the skill badge outputs a branded website, captioned describe the product brand follows

There’s also a stronger mode — more on this in a moment — an opt-in switch that makes this brand the sole design system for the entire project, so the AI can’t quietly wander back to generic defaults even if it wanted to.

.

.

.

Setup: One Install, One Command

Install the skill

One line, same shape as the prior two installs:

npx skills add nathanonn/agent-skills --skill design-system-to-skill --agent claude-code
VS Code terminal showing the npx skills add command installing design-system-to-skill — an ASCII SKILLS banner, the source repo, Found 10 skills, and Installation complete confirmation with the skill sitting next to the Part 1 and Part 2 skills in the file tree

Same repo as the first two parts. One-time cost. (Note the “runs with full agent permissions” caveat at the bottom — review the skill before use, as with any agent tool.)

Point it at the Part 2 bundle

The invocation takes one argument — the design system folder that the previous extraction produced:

/design-system-to-skill Turn this design system into a skill: @.design_systems/doodler
Claude Code terminal showing the slash command invoked on the doodler bundle — Claude narrates its plan, validates the bundle, and the worker returns structured JSON with slug doodler and name doodler-design-system

That folder is all the skill needs. A deterministic worker handles the mechanical staging: validating the bundle, copying assets, wiring the trigger. The AI does the authoring; a script does the plumbing.

The conversion at a glance

Here’s the shape of the whole thing, start to finish:

Minimal black-on-white flowchart showing four stages left to right — Design system bundle, Validate, Write auto-trigger plus MUST-USE wiring, and a solid black box labeled Per-brand Skill

Four stages.

Feed it a bundle, it verifies the bundle is real, it writes the trigger wiring that makes the brand auto-apply, and out comes a finished per-brand skill.

You point, it converts.

.

.

.

The Heart: The Skill Writes Its Own Trigger

Stay with me — this is the conceptual payoff.

The reason the conversion produces something genuinely reusable.

A per-brand skill is only useful if the agent knows when to reach for it. So the conversion writes that “when to reach for me” note as the very first thing it does: a trigger description that names the brand, the source site, the visual feel, what the skill reads, and concrete phrases that should fire it.

Claude Code diff view of the generated SKILL.md — a red line showing the DESCRIPTION placeholder being replaced by a green block with the authored trigger description naming the Doodler brand, its source site, its design feel, and trigger phrases, followed by the MUST-USE managed block being written into the project CLAUDE.md

The placeholder gets replaced with a detailed description: this skill captures the Doodler brand, it comes from a specific source site, it reads the component catalog and design tokens, and the agent should trigger it on phrases like “build a landing page,” “make a pricing page,” “design a hero section,” “style this component,” or “use the Doodler design system.”

Then the MUST-USE block gets written into the project’s guide file. That’s the wiring that makes the brand auto-apply in every future session — and the reason the demo prompt in a few paragraphs never names the skill.

.

.

.

What You Get

The finished skill on disk

Let me make “a skill” concrete.

VS Code showing the doodler-design-system skill folder expanded — assets/snippets with reference HTML files for buttons, cards, and sections, plus references folder with COMPONENTS.md open in the editor showing a button contract with Confidence high, Evidence 13 instances per 1 page, anatomy details, and a Variants by states table binding to design tokens

The finished folder contains everything a coding agent needs to build on-brand:

  • Reference HTML snippets — working code for each component (buttons, pricing cards, hero sections, testimonials) that the agent reads as a construction reference
  • Component catalog — anatomy, variants, states, and usage rules for every piece in the system
  • Design reference — the full token layer from Part 1 (colors, type scale, spacing, radii)
  • Token export — machine-readable values in a standard format

The series has layered up: a single file (Part 1) became a full bundle (Part 2) and now becomes a reusable design system skill Claude Code picks up automatically.

An honest note about MUST-USE

MUST-USE is opt-in — it’s off by default.

Turning it on makes this brand the sole, authoritative design system for the entire project. Every other design system skill you’ve installed goes off-limits for UI work there. The skill warns you which ones will be affected before you commit.

That exclusivity is the feature. When one project serves one brand (the common case), MUST-USE is what stops the AI from drifting back to generic defaults between sessions. The agent can’t “forget” the brand or quietly substitute its own guesses, because the brand is the only option.

If you juggle multiple brands in a single repo, leave it off and trigger the skill by name instead. But for most projects — one product, one look — turning it on is exactly what you want.

.

.

.

The Real Test: One Plain Prompt, No Skill Named

Here’s where the whole series pays off.

Fresh session. Empty context. The entire ask is a product brief — no mention of Doodler, the design system, or any skill name:

“Create a Multipage SAAS website (in HTML) for the following idea: Devlog turns a folder in your project into a real board — no server to run, no account to make, no extra tab to keep open. Claude Code reads and writes it directly while you work.”

Claude Code fresh session at ctx 0 percent showing a plain product prompt — Create a Multipage SAAS website for Devlog — with no mention of Doodler, the design system, or any skill name, and an In CLAUDE.md indicator in the bottom right

And here’s the moment that makes the conversion worth building.

Without any mention of Doodler in the prompt, the agent recognized this as UI work, loaded the design system skill on its own, and started reading the component contracts and snippets — all before writing a single line of code.

Claude Code auto-loading the Doodler design system skill — the agent says Since this involves UI work I must use the Doodler design system as required by the project instructions, then Skill doodler-design-system Successfully loaded skill, followed by reading the design system data and component contracts

The first time I typed a prompt and watched Claude load the design system on its own — without me saying a word about Doodler — I sat there for a second. It felt like the difference between giving someone directions every time and them just knowing the way.

👉 I specified the product. The brand showed up by itself.

How it stayed on brand

Here’s what the build summary reported.

Claude Code build summary showing Built in devlog-site using the Doodler design system as the sole authority, a Pages table listing 5 HTML files with purposes, and a How it stays faithful to Doodler section citing exact color hex values, typography choices, 4px ink outline rule, and token-resolved radii

Five pages — a landing page, features, pricing, docs, and about — each built from the same design system. The summary listed every brand rule the agent followed: the color palette, the type choices, the signature card borders, the component patterns. All pulled from the extracted tokens, applied consistently across every page.

The agent read the system and reported what it honored.

.

.

.

The Renders

Let me show you the output.

(This is the part I kept refreshing the browser for.)

The hero. The whole brand identity — visible in one shot. Navigation style, canvas color, hand-drawn elements, headline typography, accent colors. All matching the source site, on a product that never existed there.

The rendered Devlog landing page hero in the Doodler brand — a floating capsule navigation with Devlog star mark, a peach pastel canvas with hand-drawn wavy doodles in the corners, a mint Built for Claude Code pill, a large Clash Display headline reading Your project board lives in your repo, dual CTAs, and a 5-star social proof line

The feature grid. Six cards, all built from the same component patterns as the source site. Consistent borders, consistent icons, consistent typography — on every card.

The Devlog features section showing a 6-up grid of white cards each with a thick ink outline border, mint-accented line icons, Clash Display card titles like Plain-text tickets and Versioned by git, Inter body text, and the floating nav pill above

The pricing section. Three tiers with an inverted emphasis card for the featured plan. Pricing tables are the classic component AI tends to botch — this one rendered correctly, with the right highlight treatment and accent placement.

The Devlog pricing page showing the full section — a PRICING eyebrow, a bold headline The board is free Always, supporting copy, and three complete pricing tier cards with feature lists and CTAs: Solo at zero dollars forever, Pro in an inverted dark card with a mint MOST POPULAR pill at 8 dollars per month, and Team at 5 dollars per repo per month

The full-page scroll. This is the one that seals it. A still image can show that one section looks right; continuous motion shows that every section holds the same brand from top to bottom.

Animated scroll through the full Devlog landing page from hero to features to a split section with a Kanban board mock — every section maintaining the Doodler brand with peach canvases, ink-outline cards, mint accents, and hand-drawn doodles

Hero to features to pricing to a split section with a Kanban board mock — peach canvases, ink-outline cards, mint accents, hand-drawn doodles between sections. Unbroken. Coherent.

The page feels native to a brand it was never built for, on a product that never existed on that site. And the prompt never named the brand.

.

.

.

Where We Are Now

Three weeks, three layers:

  • A website you admire became a DESIGN.md — the paint (Part 1)
  • That file grew into a full design system bundle — the furniture (Part 2)
  • The bundle became an auto-triggering skill — the reflex (Part 3)

The previous extraction gave you the system. This conversion makes agents use it without you standing over them. You describe the product; the brand follows.

And there’s one more thing I want to show you — but that’s next week.

.

.

.

Your Move

Here’s the complete path, start to finish:

  1. Install the skill:
    npx skills add nathanonn/agent-skills --skill design-system-to-skill --agent claude-code
    
  2. Point it at a Part 2 bundle — the design system folder sitting in your project’s design systems directory.
  3. Decide on MUST-USE. On for single-brand projects (the common case). Off if you juggle multiple brands in one repo.
  4. In a fresh session, describe what to build. The brand shows up on its own. The agent reports which rules it honored.

The skill is open source at github.com/nathanonn/agent-skills — same repo as Parts 1 and 2.

Design has always made me sweat. Seriously — my method for years was embarrassingly manual: find a site I liked, open DevTools, and squint at values until my eyes crossed. This series turned that squinting habit into a real pipeline, from a URL to a design system skill that Claude Code reaches for automatically — about 25 minutes of total extraction and conversion time.

The last mile of a design system is getting the AI to use it without a reminder. Now it does.

Go build something on-brand.

11 min read The Art of Vibe Coding

I Extracted a Website’s Entire Design System Using This Skill

I Extracted a Website's Entire Design System Using This Skill
Watch the video walkthrough, or read the full written guide below.

I ran the token extraction on a site I’d been eyeing using the “extract-design-md” skill.

The colors landed perfectly — exact hex codes, correct font weights, spacing on point. I felt good about it.

Then I put my page next to the original, and every single component was a stranger wearing the right outfit.

Cards came out with soft shadows instead of thick borders. The navigation stretched edge to edge where the original floated as a rounded capsule over the hero. The accent color showed up everywhere instead of the one or two spots where it actually belonged.

Same palette. Different furniture.

Last week, in I Taught Claude to Steal (Ethically) a Design System I Actually Like, I showed how to capture a site’s design tokens — colors, fonts, spacing values — and package them in a file a coding agent can follow. The approach works: exact values mean the AI stops guessing at shades of blue and rounding corners to the wrong radius. But tokens are the paint. They tell Claude what color the walls should be. They say nothing about how the furniture is built.

Imagine walking into a room that’s been repainted to match a showroom you love. Wall color, trim, floor tone — every surface is right. But the furniture is from a completely different store: chairs the wrong shape, shelves too tall, light fixtures from a different catalog entirely.

That’s token-only extraction in a nutshell.

Two cards side by side — left labeled "Tokens only" shows a card with the right colors but a generic soft shadow, right labeled "Full design system" shows the same card with a thick distinctive border matching the source brand

This post fixes that.

One skill, one command, and you can extract a design system from a website — the paint and the furniture — in a package a coding agent can build from.

.

.

.

What Tokens Can’t Tell You

Think of a token file as a box of labeled paint cans and a ruler.

It tells you “use this shade of dark ink” and “round the corners this much.”

Useful.

But it can’t tell you how anything is built.

Here’s the thing: I ran a test — same prompt, same source site, tokens only — and watched the AI get four things wrong when all it had was the palette.

Cards. The source site uses a thick, bold border on every card — the kind of deliberate outline that makes each piece pop off the page. The AI defaulted to a subtle shadow instead. Same card shape, completely different feel.

Side-by-side comparison — left card labeled "What the AI built" with a soft drop shadow, right card labeled "What the source looks like" with a thick dark border, with an annotation highlighting the structural difference

Navigation. On the source, the nav floats as a rounded capsule over the hero image. The AI stretched it edge to edge like a standard website header — a completely different structural decision that changes the whole feel of the page.

Side-by-side comparison — left shows a full-width header bar stretching edge to edge, right shows a floating capsule-shaped navigation over a peach-colored hero, with an annotation pointing out the difference

Hover behavior. Hovering over a button on the source triggers a gentle fade. The AI made buttons darken or grow on hover, which feels like visiting a different site entirely.

Side-by-side comparison — left shows a button with a darkening hover effect, right shows a button with a gentle opacity fade on hover, demonstrating different interaction feels

Pricing emphasis. One pricing plan on the source flips to a dark background to stand out from the rest. The AI highlighted it with a bright accent color instead — a different visual strategy for the same goal.

Side-by-side comparison — left shows a pricing card highlighted with a bright accent color, right shows a pricing card with an inverted dark background, showing different emphasis approaches

These are design choices — about how each piece is built, how it behaves, and where specific elements belong.

A palette can’t capture structure, behavior, or placement rules.

(If you’ve ever been pleased with a rebuild and then held it up next to the original — and felt that quiet sinking “oh, that’s off” in your stomach — you know exactly what I mean.)

Last week’s skill was designed for a different job: giving Claude exact values so it stops guessing at colors and spacing. And it does that well. But when you want the AI to build pages that genuinely look like they belong to the source — matching shapes and behaviors along with colors — you need more than a palette.

That’s what the new skill captures.

.

.

.

One Install, Same Setup

Stay with me — the setup is fast.

Two tools and one install, same as last week if you already set those up.

1. playwright-cli — the browser engine that reads the pages and captures screenshots. Install it once:

npm install -g @playwright/cli@latestplaywright-cli install --skills

2. Firecrawl (optional but recommended) — gives the skill better page discovery, so it samples more than just the homepage. Without it, the skill falls back to thinner link detection. If you need the free setup, I covered it in How to Run Firecrawl for Free in the Cloud (No Credit Card, No API Keys).

3. The skill itself:

npx skills add nathanonn/agent-skills --skill extract-design-system --agent claude-code
Terminal zooming in as the install command appears — npx skills add nathanonn/agent-skills with the extract-design-system skill and claude-code agent flags, in a fresh project with an empty file tree

If you set up last week’s skill, the only change is the skill name. Same repo, same install shape.

This skill includes everything the previous one did — it still extracts colors, fonts, and spacing — and then goes further by capturing how every component is actually built. Use it when you want pages that match at every level; use last week’s version when you only need the color and typography reference.

(Three commands. Less than a minute.)

.

.

.

The Extraction: URL In, Design System Out

Let me show you what happens when you point it at a real site.

/extract-design-system https://doodler-landing.webflow.io
Claude Code with the slash command entered and zoomed in — /extract-design-system https://doodler-landing.webflow.io — pointing the skill at a live Webflow site

Worth knowing: Doodler is a Webflow cloneable template under a Creative Commons Public Domain License (CC0). We’re working with a freely licensed design here — no gray area about copying someone’s live business. You can clone the same template on Webflow and follow along if you want.

Here’s what happens after you hit enter. Five phases, each building on the last.

Five-phase flow diagram showing the extraction pipeline — URL goes through Discover pages, Read tokens, Capture components, Assemble bundle, and Validate, producing a complete Design System Bundle

1. Discover — The skill maps the site and picks a handful of representative pages to study. The homepage alone won’t show everything — forms, pricing tables, and blog layouts live on other pages, and you need that variety to capture the full system.

2. Read tokens — Colors, fonts, and spacing values pulled straight from the page’s styles. This is the same foundation last week’s skill built. Exact values read from real CSS, nothing estimated from screenshots.

3. Capture components — Here’s what’s new. The skill goes through every sampled page and records how each button, card, input field, and navigation bar is actually built. What elements sit inside each one. What variations exist — a bold version, a subtle version, an accented version. How each piece behaves when you hover over it or click it. And any design rules worth preserving, like “that thick border is the brand’s signature.”

(This is everything that token-only extraction misses — the structural DNA of the design.)

4. Assemble — Packages the whole extraction into a single folder you can hand to a coding agent. Inside: a design reference, component blueprints, working code for each piece, and a set of instructions that tells the agent exactly how to use everything. The instructions are the critical addition — they turn a reference folder into something an agent can follow step by step.

5. Validate — Checks its own work before handing anything over. The design reference gets quality-checked. Example components get rendered and compared against the live site. And 71 test assertions ran against the actual source to verify color accuracy, component structure, and layout fidelity. Nothing ships until every gate passes.

The whole extraction — from URL to a fully validated design system — finished in under 20 minutes.

Nineteen minutes.

Terminal showing the extraction complete — checkmark and "Design system extracted" confirmation, all four validation gates passed, self-test results showing 71 passed with 0 failed on the live source
Plain-English summary of the extraction results — 24 tokens, 7 atoms including buttons and cards, 8 section patterns, all 4 validation gates passed, total time of 19 minutes 6 seconds

24 tokens. 7 component types. 8 section patterns. 71 self-test assertions, all passing. Ready to use.

.

.

.

What You Get: The Bundle

Here’s what lands in the output folder.

Folder tree showing the design system bundle structure — a root folder containing the design reference, component blueprints, example code with a visual gallery, the instruction manual, token export, and quality checks

The design reference — everything from last week’s extraction (colors, fonts, spacing) expanded with a catalog of every component the site uses. The complete style guide, structured for a machine to follow. An agent reading this gets the same understanding of the brand that a designer would get from a printed brand book.

Component blueprints — for every button, card, input field, and page section: what’s inside it, what variations exist, how it behaves on hover and click, and which design rules are sacred. The blueprints capture things like “that thick border is the brand’s signature — never replace it with a shadow.”

Example code — working samples of each component, plus a visual gallery that renders them all side by side. This is the proof layer — you or the AI can compare against the source at a glance and verify that the extraction got the details right.

(I spent an embarrassing amount of time clicking between the gallery and the live site. They matched.)

And here’s the kicker: the instruction manual. A step-by-step protocol that tells a coding agent what to read first, what rules to follow, and which elements are non-negotiable. The agent follows a playbook — and in my testing, this single piece was the biggest factor separating “close enough” from “looks native.”

Token export + quality checks — the raw design values in a standard format for other tools, plus the tests the skill used to verify its own output — reusable for validating pages you build from the system later.

.

.

.

The Real Test: Build a Page From It

Time to prove it works.

I opened a fresh project — empty folder, no prior context — and handed Claude Code the extracted bundle with a simple request: build a SaaS landing page for a fictional product called Devlog, a project board that lives inside your repo.

The build prompt being typed into Claude Code — "Create a SaaS landing page using this design system" with the extracted bundle's instruction manual attached as a reference, followed by the Devlog product idea

What happened next showed exactly why the bundle matters.

The playbook came first. The first time I pointed Claude Code at the bundle with the instruction manual, it did something I hadn’t seen before: it read the entire protocol — design reference, component blueprints, example gallery — before writing a single line.

Claude Code reading the design system bundle — consuming the design reference, component blueprints, and gallery examples per the instruction manual's protocol before writing any markup

When the page came out with the exact same thick-bordered cards and floating capsule nav, I realized what had been missing all along. A plan — an actual set of instructions telling the agent how each piece was supposed to be built.

Blueprints shaped every component. Feature cards came out with the source’s bold border. The navigation floated as a capsule. The mint accent appeared in exactly the right spots. These matched because the agent had blueprints, with no guessing.

A self-report sealed it. The agent listed which brand rules it followed — thick outlines, floating nav, doodle accents, mint reserved for punctuation. You can verify at a glance that it matched the source.

Claude Code's build report showing "Brand non-negotiables honored" — 4px ink outline on feature cards, white rounded nav pill, hand-drawn doodles, mint reserved for punctuation only

Under the hood, the extracted tokens landed in the generated page as real values — color names, radius values, spacing units, all pulled from the original site and wired directly into the source code.

Generated HTML showing the extracted design tokens wired in as CSS custom properties under the root element — ink, surface, accent, peach, and coral colors plus the radius scale and spacing values

And here’s the result.

A full landing page — hero, features, how-it-works, pricing, call to action, and footer — that looks like it was built by the same designer who built the original site.

Full-page scroll of the generated Devlog landing page — hero with floating nav pill over peach canvas, feature cards with bold borders, how-it-works section on a green panel, pricing cards, mint CTA band, and footer — all native to the Doodler brand

Every card has the right border, and the nav floats as a capsule. The mint accent shows up in exactly the right place and nowhere else. The page feels native to the Doodler brand, on a product that never existed on that site.

👉 That’s the distance between having the paint and having the furniture. Last week’s tokens got the colors right. This week’s bundle got everything right.

.

.

.

Your Move

Design has always made me sweat.

I’m a developer — logic and code, that’s my lane. My actual method for borrowing a look (ferpetesake) was embarrassingly manual: find a site I liked, open DevTools, and squint at hex codes until I got maybe 70% of the way there. This skill does in 19 minutes what I used to fail at in an afternoon.

Any time you want to extract a design system from a website you like, the process is four steps.

  1. Install the skill.
npx skills add nathanonn/agent-skills --skill extract-design-system --agent claude-code

  1. Point it at a site you admire — one you have the right to reference. Same principle as last week: your own site, a client’s site, or a freely licensed template you plan to make your own.



  2. Hand the bundle to Claude Code. Point the agent at the instruction manual and describe what you want to build. The agent reads the playbook, follows the blueprints, and reports what it honored.



  3. Build something new that matches at every level. The buttons, the cards, the sections, the brand rules — the paint and the furniture.


The skill is open source at github.com/nathanonn/agent-skills — same repo as last week’s token extraction.

Here’s how the two skills fit together.

Last week’s post gave you the paint — exact colors, fonts, and spacing values that make a site look like itself. This post gave you the furniture — components, layouts, behaviors, and a set of instructions that tells a coding agent how to assemble everything.

Together, that’s the complete design system — captured from a real site in under 20 minutes, packaged for a coding agent, and validated before you ever use it.

If you tried token-only extraction and felt like the output was close but off, the palette was always right. The missing piece was how each component was built. Now you have both.

The Doodler extraction took 19 minutes. The landing page took 5 more. Under half an hour from a URL to a brand-native page — and zero squinting at DevTools.

Go extract one.