Skip to content

Category: The Art of Vibe Coding

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   # macOS
source ~/.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: 8317
api-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}" \
    "$@"
)

After saving, reload your shell:

source ~/.zshrc   # macOS
source ~/.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 supporting
CLAUDEX_EFFORT=xhigh claudex all-sol       # Complex tasks, full Sol everywhere
claude                                     # 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@latest
playwright-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.

14 min read The Art of Vibe Coding

I Taught Claude to Steal (Ethically) a Design System I Actually Like

I Taught Claude to Steal (Ethically) a Design System I Actually Like
Watch the video walkthrough, or read the full written guide below.

You open Claude Code and ask for a landing page.

You hit enter, feeling optimistic.

A few seconds later you’re looking at a centered hero, a soft gradient, three rounded feature cards, and a “Get Started” button that could belong to any of ten thousand other apps.

Generate another one.

You get its cousin.

You know this feeling. The output is competent, clean, and completely anonymous — the visual equivalent of elevator music.

(If you’ve never stared at a freshly generated UI and thought “this is fine, I guess,” congratulations. The rest of us are not so lucky.)

.

.

.

The Reason Every AI-Generated UI Looks the Same

Here’s what nobody tells you about asking an AI to design something from a blank prompt.

The model is designing from memory.

Not your memory — the averaged-out blur of a million bootstrapped SaaS sites it absorbed during training.

Ask it for “a modern landing page” and it hands you the statistical middle of everything it has ever seen. That’s why the results feel so familiar. You’re staring at the mean of the internet.

No amount of prompt-wrangling fixes a sampling problem.

You can say “make it bold” or “make it premium,” and you’ll get a slightly bolder, slightly more premium version of the same average.

Here’s the thing: I’m a developer. A logic-and-code person. Design has always made me sweat. So my actual method for years has been embarrassingly manual — find a site whose look I admire, open DevTools, and reverse-engineer it by hand.

Eyedropper the colors, squint at the font sizes, guess at the spacing. I’d get maybe 70% of the way there before I ran out of patience and shipped something “good enough.”

(Every time I tried this, I’d get through the colors and fonts, feel good about myself, then open a fresh page on the site and discover a completely different card style I’d missed. The spec was always half-finished.)

There’s a better move.

Instead of asking an AI to invent a design, you hand it a real design system — one you already like — and make it build inside that system. The trick is capturing the system in the first place, which used to be the tedious part.

That’s the whole story of this post.

We’re going to extract a DESIGN.md from a website, get a clean, reusable spec, and then prove it works by handing that file to Claude Design and watching it build a brand-new page that still looks like the original.

.

.

.

A Site’s Design Is Already Written in Its CSS

Most “AI, look at this design” workflows start by feeding a screenshot to a vision model and asking it to guess.

That’s slow, expensive, and lossy.

Here’s the thing a lot of people miss: a website’s visual identity is already written down, in exact values, inside the page.

The primary color is a real hex code. Font sizes and weights sit in the type scale as real numbers. Corner radius and spacing rhythm live as real pixel values inside CSS custom properties and computed styles.

You don’t need a model to look at a screenshot and estimate — you can read the numbers directly, for free.

That’s the insight behind the extract-design-md skill. It reads a site’s design tokens straight out of the CSS, and only uses screenshots to inform the prose — the “this feels like a premium broadsheet” descriptions that help an agent apply the system with taste.

The output format is DESIGN.md, an open spec from Google Labs (it has picked up over 23,000 GitHub stars). One file combines two things:

  • YAML front matter — the machine-readable design tokens (colors, typography, radius, spacing, components).
  • Markdown prose — the human-readable rationale that tells an agent why those values exist and how to use them.

Tokens give an agent exact values. Prose gives it judgment. Together they form a persistent design memory you can hand to any coding agent.

If this sounds familiar, it should. Back in My App Looked Like Everyone Else’s Until I Discovered This Claude Skill Trick, I built a reusable design system by hand — generating variants, documenting them, and packaging the result as a Claude Skill. This skill automates the hardest part of that whole dance: capturing a real system so you have something to reuse in the first place.

.

.

.

Six Stages, URL to DESIGN.md

Before we run it, here’s the shape of the whole thing at a glance.

A minimal black-on-white flow diagram showing six stages left to right — 1 Discover pages, 2 Extract tokens, 3 Harvest components, 4 Capture feel, 5 Synthesize, 6 Validate — starting from a URL and ending in a solid black box labeled DESIGN.md

Six stages take a URL and hand back a validated DESIGN.md:

  1. Discover pages — map the site and pick a few representative pages (home, pricing, auth, blog) so the tokens come from real variety.
  2. Extract tokens — read the CSS variables and computed styles; resolve colors to hex; name them by role rather than dumping every shade.
  3. Harvest components — capture the buttons, inputs, and cards the pages actually expose, plus their hover and focus states.
  4. Capture feel — take light screenshots that inform the written descriptions only.
  5. Synthesize — map everything onto the DESIGN.md schema in a consistent order.
  6. Validate — run the official linter as a quality gate.

The design decisions inside those stages are what make the result trustworthy. Tokens come from real CSS values, so nothing gets invented.

Components get captured only when the page genuinely has them. And when a site has no cards or chips, the skill records that absence honestly instead of hallucinating a component to fill the gap.

(The honesty is the whole feature. You get what’s really there, with a note about what isn’t.)

.

.

.

Two Tools and One Install

Three moving parts, and only one of them is strictly required.

1. playwright-cli — the browser engine that does the reading and the screenshots. The install --skills step registers it as a skill for Claude Code so the agent can drive a real browser. Install it once:

npm install -g @playwright/cli@latest
playwright-cli install --skills
VS Code terminal showing the playwright-cli help output — a list of core browser commands like open, goto, click, fill, hover, screenshot — confirming the CLI is installed and on PATH

2. Firecrawl — used for the page-discovery step. This one is optional but recommended. Without it, the skill falls back to thinner link discovery; with it, you get better page coverage.

If you don’t already have Firecrawl running, I wrote a full free setup guide in How to Run Firecrawl for Free in the Cloud (No Credit Card, No API Keys). The one thing to add is a short CLAUDE.md block that points Claude Code at your local instance so it uses Firecrawl instead of the built-in web tools.

VS Code showing a CLAUDE.md file open with a Firecrawl instructions block — rules telling Claude Code to always use Firecrawl skills, target the localhost:3663 service, and avoid the cloud status check

3. The skill itself — one command:

npx skills add nathanonn/agent-skills --skill extract-design-md --agent claude-code
Terminal showing the npx skills add command installing extract-design-md into Claude Code — an ASCII "SKILLS" banner, the source repo github.com/nathanonn/agent-skills, and a "Repository cloned" confirmation

That’s the whole setup. One-time cost, then it’s a single command per site from here on.

.

.

.

The Live Run

I pointed the skill at my own website.

/extract-design-md https://www.nathanonn.com/
Claude Code prompt showing the /extract-design-md command run against https://www.nathanonn.com with Opus 4.8 and auto mode on

Quick honesty note on why I used my own site. Design tokens — a hex value, a font size — aren’t really the kind of thing anyone owns. But cloning a real business’s homepage pixel-for-pixel is a different and dumber move.

The sane lane: your own sites, client sites you have rights to, or a site you use as a starting point that you then make your own. I picked mine so nobody has to email me about it. Moving on.

Claude verified the three tools, created an output folder, and mapped the site.

Claude Code starting the extraction — verifying that playwright-cli, npx, and Firecrawl are all available, then creating the output folder and running firecrawl map on the site

Then it did the one thing I wish more tools would do. It stopped and asked me to confirm which pages to sample before spending any time crawling.

Claude Code presenting a page-sample confirmation — an auto-picked list of 5 pages (homepage, newsletter, contact, a blog post, archives) with options to use all 5, drop the contact page to 4, use homepage only, or type a custom set

It auto-picked five pages and offered me the choice. I dropped the contact page — it’s a trivial layout that would only add noise to the sample, and the newsletter page already covered the form inputs.

(I’ve used enough tools that just barrel ahead without asking. This one stopped, showed me its thinking, and let me trim the list. If every agent did this, I’d trust them twice as fast.)

From there it ran mostly hands-off.

I’m going to spare you the play-by-play — the output is what matters here. It worked through token extraction across the pages, harvested the components, took a light screenshot pass to inform the writing, and then ran a lint check.

Claude Code at the screenshot stage — capturing viewport screenshots for home, newsletter, blog, and archives pages, then scraping the homepage copy to inform the prose
Claude Code running the design.md linter — npx @google/design.md lint on the output file, catching a letterSpacing value that needs fixing and a few non-blocking warnings

A couple of minutes later, it was done.

Here’s the final report.

Claude Code's final extraction report — DESIGN.md written, 4 pages sampled (home, newsletter, blog, archives, with contact dropped), light theme only, 9 colors, 6 type roles, 4 radius values, components found and missing listed, and a clean lint result with 0 errors

The report is refreshingly specific about what it did and didn’t find:

  • Pages sampled: 4 — home, newsletter, blog post, archives (contact dropped from the auto-picked 5).
  • Theme: light only. No dark toggle on the site, so a single file.
  • Colors: 9, a monochrome system — black as the primary CTA, grays for text and borders, white and light-gray surfaces, a small neutral ramp. No colored accent, because the site genuinely doesn’t use one.
  • Type roles: 6, all in Inter, from a 48px/700 heading down to a mono label style.
  • Radius + spacing: a 4-step radius scale and a full spacing scale on a 1200px container.
  • Components found: primary button (with hover), secondary button, input field (with focus), link.
  • Components missing: cards and chips — the site has none, so none were invented.
  • Lint: 0 errors after a couple of automatic structural fixes, plus 8 non-blocking warnings and one contrast warning it correctly flagged as a false positive.

.

.

.

The Result: A DESIGN.md You Can Actually Read

Let me show you the artifact.

The generated DESIGN.md file open in an editor — YAML front matter with a name and description, a colors block where each color carries a hex value plus a comment showing its computed rgb() and raw oklch() source, and a typography block defining an Inter type scale for h1, h2, h3

One file. Two layers.

The YAML front matter holds the machine-readable design tokens — every color, type size, radius, spacing value, and component definition the skill extracted. The markdown prose below it holds the human-readable rationale: what the design feels like and how an agent should apply it.

Here’s the full token layer from the extraction:

---
version: alpha
name: Nathan Onn — Vibe Coding Newsletter
description: Minimal high-contrast editorial identity for a solo developer newsletter — black-on-white typography, a single black CTA, and quiet gray tonal panels.
colors:
  primary: "#000000" # black — filled CTA button
  primary-hover: "#374151" # gray-700 — CTA hover (computed rgb(55,65,81))
  on-primary: "#ffffff" # button / on-black text
  surface: "#ffffff" # page background
  surface-muted: "#f3f4f6" # gray-100 — split-screen side panel / tonal blocks (computed rgb(243,244,246))
  text: "#111827" # gray-900 — headings, body, links (computed rgb(17,24,39))
  text-muted: "#6b7280" # gray-500 — supporting copy, labels (computed rgb(107,114,128))
  border: "#6b7280" # gray-500 — input borders
  neutral-50: "#f9fafb" # gray-50  (raw: oklch(98.5% .002 247.839))
  neutral-100: "#f3f4f6" # gray-100 (raw: oklch(96.7% .003 264.542))
  neutral-300: "#d1d5db" # gray-300 (raw: oklch(87.2% .01 258.338))
  neutral-700: "#374151" # gray-700 (raw: oklch(37.3% .034 259.733))
typography:
  h1:
    fontFamily: "Inter, sans-serif"
    fontSize: 48px
    fontWeight: 700
    lineHeight: 60px
    letterSpacing: 0em
  h2:
    fontFamily: "Inter, sans-serif"
    fontSize: 30px
    fontWeight: 700
    lineHeight: 36px
    letterSpacing: 0em
  h3:
    fontFamily: "Inter, sans-serif"
    fontSize: 24px
    fontWeight: 600
    lineHeight: 32px
    letterSpacing: 0em
  body:
    fontFamily: "Inter, sans-serif"
    fontSize: 16px
    fontWeight: 400
    lineHeight: 1.6
    letterSpacing: 0em
  label-caps:
    fontFamily: "Inter, sans-serif"
    fontSize: 14px
    fontWeight: 600
    lineHeight: 1.25
    letterSpacing: 0.05em # uppercase eyebrow labels ("LATEST ISSUE")
  mono:
    fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace"
    fontSize: 14px
    fontWeight: 400
    lineHeight: 1.5
    letterSpacing: 0em
rounded:
  none: 0px
  md: 0.375rem # 6px — buttons, inputs
  lg: 0.5rem
  xl: 0.75rem
spacing:
  xs: 0.5rem # 8px
  sm: 0.75rem # 12px
  md: 1rem # 16px
  lg: 1.5rem # 24px
  xl: 2rem # 32px
  container: 1200px # max content width
components:
  button-primary:
    backgroundColor: "{colors.primary}"
    textColor: "{colors.on-primary}"
    typography: "{typography.body}"
    rounded: "{rounded.md}"
    padding: 12px 24px
  button-primary-hover:
    backgroundColor: "{colors.primary-hover}"
    textColor: "{colors.on-primary}"
    rounded: "{rounded.md}"
    padding: 12px 24px
  button-secondary:
    backgroundColor: transparent
    textColor: "{colors.text}"
    typography: "{typography.body}"
    rounded: "{rounded.md}"
    padding: 8px 16px
  input-field:
    backgroundColor: "{colors.surface}"
    textColor: "{colors.text}"
    typography: "{typography.body}"
    rounded: "{rounded.md}"
    padding: 12px 16px
  input-field-focus:
    backgroundColor: "{colors.surface}"
    rounded: "{rounded.md}"
    padding: 12px 16px
  link:
    textColor: "{colors.text}"
    typography: "{typography.body}"
---

Every value traces back to real CSS on the page. Colors are named by role — primary, surface, text-muted, border — so an agent knows what job each color does. Typography is a real scale extracted from computed styles. Components carry their exact padding, radius, and state changes.

When the site had no cards or chips, the file records that honestly instead of inventing them.

Below the tokens, the prose section describes the overall identity and guides an agent on how to apply the system with taste — things like “nothing competes with the words” and “let black do the pointing.”

And here’s the kicker: the file doesn’t stop at documentation. The official @google/design.md CLI can export it into working code:

# Turn the DESIGN.md into a Tailwind theme (v4)
npx @google/design.md export --format css-tailwind DESIGN.md > theme.css

It also exports to a Tailwind v3 config or to W3C DTCG tokens. The spec that documents your design system can drop straight into a real project.

.

.

.

The Real Test: Claude Design

A design spec is only worth something if a model can build a consistent new design from it.

So let’s test it somewhere the original site has never been seen.

Enter Claude Design, Anthropic’s design tool (currently in beta). It collaborates with you on polished visual work — prototypes, pages, slides.

The key part for us: it can build production-ready UI from your own design system. That makes it the perfect testbed, because it only knows what’s in the file.

I uploaded the DESIGN.md, chose the newly launched Claude Sonnet 5, and asked for a landing page for a fake developer tool:

Create a landing page using the attached DESIGN.md for the following SAAS: 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 Design's "What will you design today?" screen with the Devlog prompt typed in, the DESIGN.md file attached, and the model set to Claude Sonnet 5

It read the file and, like a good collaborator, came back with questions before building anything — which sections to include, what the primary CTA should do, whether the product was free or paid, and what tone the copy should take.

Claude Design asking clarifying scope questions for the Devlog landing page — which sections to include, what the CTA should do, whether it's free or paid, and what voice the copy should use

I answered a few and let it decide the rest. Then it went to work laying out the page.

Claude Design building the Devlog landing page — the hero taking shape with a black wordmark, an "OPEN SOURCE · LOCAL-FIRST" pill, the headline "A board that lives in your folder," and a terminal-style install snippet

Here’s where it landed.

A black wordmark, a black pill “Get started” button, an Inter headline, and a gray tonal panel showing a fake board.

A landing page for a fake developer tool called Devlog — black wordmark, a black pill "Get started" button, a bold Inter headline reading "A board that lives in your folder," and a gray tonal panel showing a kanban board — all generated by Claude Design from an extracted DESIGN.md

The rest of the page carried the same identity all the way down:

The Devlog "How it works" and "Features" sections — three numbered steps in Inter, then a grid of gray tonal feature cards with small caps labels, all black-on-white
The Devlog "Get started" section rendered as a full-width black band with a white Inter headline and a terminal-style install snippet with a Copy button
The Devlog FAQ section with plain expandable questions, followed by a minimal footer with the black wordmark and a "A board that lives in your folder" tagline

Look at what carried over: the single black CTA, the Inter type scale, the gray tonal cards, and the black band that mirrors the high-contrast feel of the source. The whole page reads like it belongs to the same family as my site, on a product that never existed there.

(It’s a strange feeling — recognizing your own site’s personality on a product that doesn’t exist. Like hearing someone hum a tune you wrote.)

Let me be upfront about expectations here.

My source site is a minimal black-on-white newsletter — clean, simple, intentionally restrained. So the output is also minimal and restrained. That’s exactly the point.

If you feed Claude Design a bold, colorful design system, you’ll get bold, colorful output. The tool mirrors whatever you give it. I gave it monochrome restraint, and it handed back monochrome restraint — on a product that never existed on my site.

👉 Consistency was the whole goal.

A design system’s entire job is to make new things look like they belong, and the file did exactly that on a page it was never built for.

It works.

.

.

.

Steal Like an Engineer

Here’s the pattern worth taking away, bigger than any single skill.

Stop asking AI to invent a design. Give it a real system to build within, and it stops averaging and starts applying. When you extract a DESIGN.md from a website you admire, you’re handing the model a specific point of view instead of a blank canvas.

A few ways to put it to work:

  • Brand consistency. Extract your own site once, drop the file in your repo, and every new page Claude Code builds inherits the look.
  • Client work. Capture a client’s existing system so AI-built additions match what they already have.
  • Honest inspiration. Point it at a site you like — one you have the rights or the reason to reference — and use the extracted system as a starting point you transform, rather than a one-to-one copy.

That last one is where the “ethically” in the title earns its keep. Tokens aren’t copyrightable, and wholesale cloning is a bad look. Use the sharp tool responsibly.

So here’s your move:

  1. Install the skill (GitHub): npx skills add nathanonn/agent-skills --skill extract-design-md --agent claude-code
  2. Point it at a design you actually like (that you have the right to reference).
  3. Hand the resulting DESIGN.md to Claude Code or Claude Design.
  4. Build something new that finally looks like something specific.

The next UI you generate doesn’t have to be the averaged-out memory of the entire internet.

It can look like a design you chose.

Go steal one. Ethically.

13 min read The Art of Vibe Coding

Stop Losing Work When You Compact Claude Code (The Handoff-Doc Skill)

Stop Losing Work When You Compact Claude Code (The Handoff-Doc Skill)
Watch the video walkthrough, or read the full written guide below.

You know the moment.

You’ve been working for over an hour.

The status line says ctx:22% (that’s about 220k tokens in Opus). You know you should compact. You also know you should write something down first — a handoff doc, a note, anything that captures where you are so the next session can pick up cleanly.

But writing a handoff prompt from scratch, right now, mid-session? That means composing the prompt. Reviewing the output. Making sure it caught the decisions you made forty minutes ago, the file you changed twenty minutes ago, the test that’s still failing.

So you skip it.

I did.

Last month, I was two hours deep in a refactor, ctx sitting at 24%. I compacted without writing anything down. The next session spent its first fifteen minutes re-reading files I’d already inspected — and then made a decision I’d already made. In a different direction.

Fifteen minutes of rework. Because I didn’t want to spend thirty seconds.

That’s the gap.

The gap between “I should capture my state” and “I actually do it” is where session continuity goes to die.

There’s a skill that closes it.

One command. The skill reads back through the session, distills what happened, writes it to a file on disk, and reports what it captured. Thirty seconds of your time, about three minutes of processing. Then you compact with the handoff doc already durable — written to disk, safe from the reset.

The next session reads it back. Picks up cold. Knows exactly where you left off.

This post walks through the whole thing: the threshold that triggers it, five steps from “context is getting high” to “fresh session, state preserved,” and why the skill captures more than you’d write manually.

.

.

.

The System This Builds On

In Never Let Claude Code Auto-Compact Again, I built a full context management system: a status line that shows ctx% on every turn, operating zones from green to red, structured /compact instructions using a KEEP/SUMMARIZE/DROP template, and a rehydration checklist for after compaction.

At the center of that system was the HANDOFF.md habit — write a structured document to disk before compacting so the session state survives the reset.

It was also the step most people skip.

And honestly? I get it. Generating a good handoff required an eight-line prompt. You had to remember to run it. You had to review the output. When you’re already deep in a session and context is climbing, that friction adds up fast.

The handoff-doc skill replaces the entire manual step with one command.

If you haven’t read the prior post, that’s fine — this one is self-contained.

But the full context management playbook is there for anyone who wants the complete system.

.

.

.

Install the Skill

The handoff-doc skill is open source and available from the agent-skills repo.

Claude Code (plugin marketplace):

/plugin marketplace add nathanonn/agent-skills
/plugin install handoff-doc@nathanonn-agent-skills

Other agents (Codex, Cursor, GitHub Copilot):

npx skills add nathanonn/agent-skills --skill handoff-doc

For manual installation or the full list of available skills, see the repo README.

Once installed, /handoff-doc is available as a slash command in Claude Code. In other agents, describe the task and the skill triggers automatically.

.

.

.

The 20% Rule

For 1M Opus sessions, 20% is already 200,000 tokens of context.

That’s easily an hour or more of active development — file reads, tool calls, test output, back-and-forth.

The rule is simple: when ctx crosses ~20%, finish the current micro-task, invoke the handoff-doc skill, then compact.

Why 20% and not higher?

Quality degrades gradually. By 25-30%, the model is processing a growing pile of stale content on every turn — old exploration logs, resolved error output, abandoned approaches. The signal-to-noise ratio drops before you feel it in the responses.

The skill itself needs room.

It reads back through the session to distill what happened. Invoking it at 20% gives it space to work. At 30%, the distillation is competing with the content it’s trying to capture.

Early resets force a healthy rhythm. Working in 15-20% chunks means each new session starts fresh, with full model quality. The Claude Code handoff doc bridges the gap between sessions — capturing enough state that the fresh session can continue without re-reading the transcript. The work stays continuous even though each chunk gets a clean window.

Think of it as version control for your session state. Git captures code state between commits. The handoff doc captures working state between context resets. Both let you move forward with confidence that what you’ve done is preserved.

The threshold is a heuristic — a zone to start watching for a clean boundary. Some sessions run lean and don’t cross 20% for two hours. Others — heavy file reading, broad research, lots of tool calls — blow past it in thirty minutes.

The status line makes it visible. You glance at ctx:21% the way you glance at a battery indicator.

When you see it, that’s the cue.

Status line showing ctx:25% — the moment you notice the threshold has been crossed

.

.

.

Five Steps From Threshold to Reset

Here’s the full workflow. Five steps, start to finish.

Step 1: Notice the threshold

You’re mid-session.

The status line shows ctx:25%. Past the 20% zone. Time to capture state before compacting.

This is a glance — completely passive. If you set up the status line from the prior post, you see ctx% on every turn without running any command. The moment it crosses your threshold, you know.

Step 2: Invoke the skill

Type /handoff-doc followed by the destination path.

Invoking the handoff-doc skill with a destination folder

The @ reference lets you point at any folder in your project. The skill checks whether the destination already has files and follows their naming convention. If the folder uses a YYYYMMDD_NN_slug pattern, the skill continues the numbering automatically. If the folder is empty, it starts the series.

(The first time I ran this in a folder that already had three handoff docs, it picked up the naming pattern and slotted in as number four. A small thing — but it meant I never had to think about filenames. The skill just knew.)

One line.

That’s the entire invocation.

Step 3: Review the handoff doc

The skill reads back through the session, distills what happened, and writes a structured document to disk. Processing takes about three minutes, depending on session length.

When it finishes, the output reports what it captured:

Skill output showing the handoff doc summary and file location

The generated doc follows a consistent skeleton: what happened this session, where things live (files changed, key paths), verification done — and honestly, what was not verified — git state, and open follow-ups with numbered next steps.

The handoff doc opened in the editor, showing structured sections

Take thirty seconds to skim the key sections. The structure is deliberate — each section answers a question the next session will have:

  • What happened tells the next session what was accomplished and what decisions were made.
  • Where things live gives it the file paths and locations it needs to navigate the work.
  • Verification done is the honesty check — what was tested, what passed, and critically, what was not tested. This prevents the next session from assuming something works when it hasn’t been verified.
  • Git state anchors the handoff to a specific commit, branch, and push status.
  • Open follow-ups are the actual continuation point — numbered, specific, ready to pick up.

That verification section deserves a callout. One of the first times I used the skill, the handoff doc flagged — plainly, almost embarrassingly — that a feature I’d been working around hadn’t actually been tested. I would have glossed right over that in a manual handoff. The skill was more honest than I would have been.

If those sections look right, move on.

The skill has full session context to work from — it usually captures more than you’d remember to include manually.

Step 4: Compact

With the handoff doc written to disk, run /compact. You can add custom summarization instructions (the prior post has KEEP/SUMMARIZE/DROP templates for this), or run it bare.

Here’s the important shift: the handoff doc is the durable artifact now. It lives on disk, outside the context window, unaffected by whatever the compaction summarizer decides to keep or drop. The compact summary adds useful compressed context, but the handoff doc on disk is the lifeline.

This is also where the /compact instruction from the prior post pays off. If you point the compact instruction at the handoff doc — “Focus on the handoff doc at [path], current git diff, and next step” — the summary and the handoff doc reinforce each other.

Running /compact after the handoff doc is saved

Compaction takes one to two minutes for a session around 25%. The progress indicator shows how far along it is.

Compaction in progress — progress bar at 55%

Step 5: Fresh context, handoff preserved

After compaction completes, ctx drops to 0%.

And here’s the payoff.

Post-compaction: ctx at 0%, handoff doc re-read, skills restored

Look at the compaction output. It lists every file it re-read — including the handoff doc you just created. Skills are restored. Referenced files are loaded back into context.

The session is fresh. Your state is preserved. The model knows what was done, what’s pending, and what to do next — all from the handoff doc it just read back.

Continue from exactly where you left off.

.

.

.

Why This Beats Writing It Manually

Manual handoffs fail in three predictable ways.

You skip it entirely.

This is the most common failure mode — and the one I’m most guilty of.

When context is high and the session has been long, writing a handoff prompt feels like overhead you can defer. The compact summary might be good enough. You’ll remember what you were doing. You’ll just re-read the git diff.

Then the next session opens, and you spend fifteen minutes re-establishing context that could have been preserved in thirty seconds. Re-reading files. Re-explaining decisions. Worse — sometimes the session makes different decisions because it doesn’t know what you already decided.

The skill makes the habit frictionless enough that you actually do it.

One command instead of a prompt you compose from scratch every time.

The activation energy drops from “write an eight-line prompt and review the output” to “type one line and skim for thirty seconds.”

You write too little.

A manual prompt like “create a handoff doc” gives the model almost nothing to work with.

The result is a vague summary that misses specifics: which files were changed and why, what was tested and what wasn’t, where the git state stands, which follow-ups are still open.

With the skill, a structured skeleton does the work for you.

Each section — what happened, where things live, verification done, git state, follow-ups — exists because it answers a question the next session will have. The structure acts as the prompt.

You write too much.

A full transcript replay disguised as documentation.

Every step narrated, every error included, every tangent preserved. The next session wades through three pages when it needed three paragraphs.

The skill distills. It extracts what the next session needs to continue — decisions made, current state, open work — and drops the noise. Exploration paths that led nowhere get summarized or omitted. Error output you already resolved is gone. The handoff reads like a briefing, because that’s what it is.

Beyond those failure modes, the skill handles details you’d forget in a manual prompt. It checks the destination folder for existing naming conventions. It auto-increments the sequence number. It converts relative time references to absolute dates so the doc stays useful a week later. Every Claude Code handoff doc has the same structure, the same level of detail, the same honesty about gaps.

.

.

.

Making It a Habit

The workflow is three steps, once it’s in muscle memory:

  1. Watch ctx%. The status line makes this passive — you see the number on every turn without doing anything.
  2. At ~20%, invoke the skill. Finish the current micro-task first, then /handoff-doc with a destination path.
  3. Compact and continue. The handoff doc is on disk. Compact. The fresh session re-reads it automatically.

A few tips for adoption.

Pick a consistent destination. Use notes/handoff_docs/ or docs/handoffs/ — whatever fits your project structure. The skill handles a dedicated folder with sequential files, and it handles a single HANDOFF.md at the project root. Choose one pattern and stick with it so the skill builds a clean series over time.

Don’t review obsessively. Thirty seconds of skimming after the skill runs is enough. If the key sections are present — decisions, files changed, next step — move on. The skill has full session context. It captures more than you’d remember to write.

Pair it with Compact Instructions. If you have a Compact Instructions block in your CLAUDE.md (described in the prior post), the skill and those instructions work together. The skill writes the durable state to disk. The compact instructions shape what gets preserved in the compressed session summary. Two layers of continuity.

Use it before /clear too. The workflow pairs naturally with /compact, but it works equally well before /clear — when you want a genuinely fresh session instead of a compressed one. The handoff doc is the bridge. The reset mechanism is your choice.

Let the series accumulate. If you use a folder with sequential naming, the handoff docs become a session log. Over the course of a multi-day project, you end up with a numbered series that traces how the work evolved: what was done in each session, what decisions changed, what follow-ups carried forward. It’s useful for your own reference, and it’s useful for onboarding someone else into a project mid-stream.

One more thing worth stating: the handoff doc works for human readers too. If you need to hand a project to a colleague — or pick it up yourself after a week away — the handoff doc reads like a structured briefing. You don’t need Claude Code to benefit from what it wrote.

.

.

.

The Habit in One Sentence

When context crosses the threshold, capture your state with one command, then reset.

The handoff-doc skill makes this automatic. Session state goes to disk. Context resets clean. The next session picks up cold from one structured document that tells it everything it needs to continue.

The best sessions — the ones that actually ship — are the ones where context stays intentional from start to finish. The status line is the sensor. The threshold is the trigger. The handoff-doc skill is the mechanism that makes the whole thing effortless.

Build the habit. Watch ctx%. Invoke the skill. Compact. Continue.

For the full context management system — status line, operating zones, compact instructions, rehydration checklist — start with Never Let Claude Code Auto-Compact Again.

For readers new to Claude Code skills, Claude Skills: Your “I Know Kung Fu” Moment Has Arrived (Part 1 of 3) covers how to build and use them.

The handoff-doc skill (and several others) is open source at github.com/nathanonn/agent-skills.

11 min read The Art of Vibe Coding

Generate Images in Claude Code (using Codex plugin)

Generate Images in Claude Code — Without Even Asking for a Command
Watch the video walkthrough, or read the full written guide below.

You’re three hours into a Claude Code session.

The feature works. The tests pass. All that’s left is the picture at the top — a featured image, a hero illustration, something to make the thing look finished.

So you ask for it. Plainly, the way you’d ask a teammate sitting next to you:

“Generate an image of a minimalist line-art lighthouse on a dark navy background.”

And Claude Code apologizes.

Claude Code responding to a plain image request with "The image generation isn't something I can do — I don't have an image generation tool available in this environment," then offering SVG line-art or ASCII art instead

It offers to write you an SVG. Or some ASCII art.

Helpful — in the way a hardware store is helpful when you walked in for a sandwich.

I’ve done this more times than I want to admit — alt-tabbed to Codex for a quick image, got sidetracked tweaking the prompt, and came back to Claude Code ten minutes later having forgotten what I was about to commit. That little shrug is the whole problem in one frame. The capability you want lives one tool over, in Codex. The usual move is to alt-tab away to go get it — and the flow you spent three hours building quietly evaporates the moment you leave the window.

By the end of this post, that same sentence — no command, no tool-switch — produces a real PNG sitting neatly inside your project.

And the way it gets there opens a door bigger than any single image.

.

.

.

Why Claude Code Can’t Make Images (And Codex Can)

Let’s be fair to Claude Code first.

The limitation is a deliberate product boundary.

Claude Code is a coding harness — it edits files, runs commands, reasons about your codebase, and wires things together. Picture generation was simply never wired into that toolset. The model underneath can reason about images perfectly well; the harness around it just has no tool to make one.

So Claude does the honest thing and tells you.

You can feel how badly people want that gap filled.

Search around and you’ll find a small industry of “how to generate images in Claude Code” guides — an MCP server here, an external CLI there, a paid wrapper somewhere else. When that many workarounds exist for one missing capability, the demand is obvious. And so is the native answer, which is still no.

Codex took the other road.

In April 2026, OpenAI shipped gpt-image-2, a purpose-built image model, and made it the default for image work in Codex — a clear step up from the version it replaced, with sharper output and the ability to reason about a layout before it draws. Within weeks the older DALL-E models were retired from the API entirely, which left the new model as the whole story.

Codex exposes it through a built-in skill you trigger with $imagegen.

You describe what you want, Codex generates it, sizes it, and saves it.

Native.

So here’s the state of Claude Code image generation in mid-2026: the thing you want exists, it’s excellent, and it’s sitting in the tool next door. Keeping Codex open in a second terminal works — but it drags back the exact switching tax I wrote about all the way back in Claude Code vs Codex: Why I Use Both (And You Should Too).

Copy a path, switch windows, lose your place, switch back.

There had to be a way to borrow the capability without leaving home.

.

.

.

The Bridge Already Exists — And It Already Works

There is.

I’ve written about it before.

Back in April I covered Codex Reviews My Code Inside Claude Code — But I Don’t Trust It Blindly — OpenAI’s official Codex plugin that runs Codex inside a Claude Code session. That post used it for code review. But the plugin reaches well past review — it’s a general bridge to Codex, and one of its commands, /codex:rescue, can hand an arbitrary task to Codex and let it run.

Which left me poking at an obvious question: if the plugin gives me Codex inside Claude Code, and Codex has $imagegen… can I get a real image generated without ever leaving Claude Code?

So I tried it.

I called /codex:rescue and told it — in so many words — to use $imagegen for the lighthouse.

Claude Code prompt invoking /codex:rescue with the instruction "use the $imagegen to Generate an image of a minimalist line-art lighthouse on a dark navy background"

Codex woke up, took the task, and got to work.

The codex:codex-rescue subagent running inside Claude Code and reporting "Done" — 2 tool uses, 10.0k tokens, 2m 11s

A couple of minutes of waiting while Codex did its thing in the background, and a finished image existed — without a single window switch. The terminal I was working in never lost focus. The session I’d spent three hours building never broke.

It worked. Here’s what came out:

The generated lighthouse — minimalist white line-art on a flat dark navy background, beam lines radiating from the lantern room, gentle waves at the base

A real image, generated by gpt-image-2, without leaving Claude Code.

Proof of concept — done.

.

.

.

But You Have To Know The Magic Words

Here’s where the proof-of-concept stops being something you’d actually want to use day to day. Two frictions — and they’re the whole reason this post has a part two.

Friction one: it only fires when you summon it by name.

/codex:rescue is a command you have to remember and type, phrased just so. Ask for the image the way a human naturally asks — the plain sentence from the very top of this post — and nothing happens. Claude Code tells you it can’t make images and offers you that SVG again. The capability is reachable, but only if you already know the secret handshake. Forget the handshake and you’re back at the shrug.

Friction two: the file lands wherever Codex feels like dropping it.

Run the rescue route and the image shows up loose at the root of your project, sitting right alongside your config and your docs.

Project root file tree showing the generated lighthouse-line-art.png dropped in alongside AGENTS.md, CLAUDE.md, JOT.md, and MANIFEST.md

One stray PNG next to your project files is no crisis.

But picture a real working afternoon: a hero image for the landing page, two illustrations for a docs page, a placeholder avatar, a quick thumbnail to test a card layout. Five generations, five files scattered across your project root. By the third experiment I had four stray PNGs sitting next to my CLAUDE.md. I caught myself doing a manual cleanup and thought: this is exactly the kind of chore a script should handle.

There’s a sharper trap hiding in that route, too.

The first time I tried scripting this myself, I stared at a blank output for a solid ten minutes before I realized the shell had eaten the dollar sign. The $imagegen token starts with $, so the shell quietly expands it to nothing before Codex ever sees it — the command runs fine, no image appears, and nothing tells you why.

So here’s the scoreboard after the experiment: the capability is real and it runs in-session, but reaching it means knowing a command most people won’t, and using it means cleaning up after it. That’s exactly the kind of rough seam worth wrapping once — so you never feel it again.

.

.

.

The Fix: Say It In Plain English, Get A Tidy File

This is the part I want you to steal.

I built a small skill — codex-imagegen — that wraps the whole messy path into something that responds to how you’d actually ask.

Install it, then type the same natural sentence you’d have typed anyway.

No /codex:rescue.

No command to memorize:

Generate an image of a minimalist line-art lighthouse on a dark navy background.

I half-expected to have to type /codex-imagegen the first time. Instead I just asked for the image the way I normally would — and watched Claude load the skill on its own. That was the moment I knew the wrapper was worth building.

Claude Code automatically loading the codex-imagegen skill from the same plain-English prompt, running the bundled script with the codex plugin runtime, and reporting the image saved to .codex-image/lighthouse.png

Same words that earned a shrug two sections ago. This time: “Successfully loaded skill,” the plugin runtime picked automatically, and a finished PNG — 1200×1200, flat navy #0d1b2a — saved to .codex-image/lighthouse.png.

No handshake.

And look where the file went.

Project file tree showing the image neatly inside a dedicated .codex-image folder rather than loose at the project root

Instead of cluttering your project root, the image lands in .codex-image/ — its own corner, out of the way, easy to find later.

For readers new to skills: a skill is a small, reusable capability you teach Claude Code once and trigger by describing what you want. (I wrote a whole series on them, starting with Claude Skills: Your “I Know Kung Fu” Moment Has Arrived (Part 1 of 3).)

Here’s what the skill handles so you don’t have to:

Without the skillWith codex-imagegen
Remember and type /codex:rescue with the right phrasingAsk in plain English; the skill triggers on intent
Fish the file out of the project rootLands organized in a .codex-image/ folder
Works only when the plugin is set up just soUses the plugin if present, falls back to the Codex CLI if not

If you’d rather be deliberate, the explicit slash form /codex-imagegen "your prompt" is there too.

Most of the time you won’t reach for it — plain language is the point.

That fallback matters more than it sounds. It means the skill works on a teammate’s machine that only has the Codex CLI, or on a server with no plugin installed, with zero changes on your end. And the shell-eats-the-dollar-sign trap from earlier? Never reaches you. The script handles that token safely every single time.

One quiet bonus: Codex keeps a local cache of every image it generates and never cleans it up. The skill tidies that cache behind the scenes after each run, so the folder doesn’t quietly balloon to hundreds of megabytes while you’re not looking.

.

.

.

What This Unlocks: An Image Generator Other Skills Can Call

Here’s where it gets interesting — and why I think the natural-language trigger matters more than the convenience.

Because the skill responds to plain intent instead of a hard-coded command, it stops being something only you invoke. It becomes a building block other skills and workflows can lean on.

Picture a publishing workflow that notices a finished post has no featured image, writes a prompt from the post’s own title, calls codex-imagegen, and drops the result in the right folder — no human in the loop. Or a project scaffolder that fills in placeholder icons and hero art as it sets up a new repo, instead of leaving you a wall of empty boxes to fill later. Or a slide-deck skill that generates a custom illustration for each section as it builds the outline, so the deck arrives already looking like someone designed it.

Whiteboard-style hub-and-spoke diagram: three skills (Blog Publisher, Project Scaffolder, Slide Deck Builder) each pointing inward to a central codex-imagegen box, which outputs a cluster of images into the project

In every one of those, image generation is no longer a thing you stop and do by hand. It’s a step another skill takes on your behalf, mid-flow, because the door is now wide enough for a machine to walk through.

The principle underneath: a capability wrapped as a natural-language skill becomes composable.

One skill can hand off to another, and image generation turns into a primitive your automations reach for — rather than a manual detour you take by hand. None of that works if the only way in is a command a human has to remember to type.

I’ve been building exactly this kind of skill-calls-skill workflow, and it deserves its own post to do it justice. I’ll walk through a real one soon — if you don’t want to miss it, subscribe.

.

.

.

Install It (It’s Open Source)

The skill is open-source, in my agent-skills collection. Two ways in, depending on your setup.

For Claude Code, install the packaged plugin:

/plugin marketplace add nathanonn/agent-skills
/plugin install codex-imagegen@nathanonn-agent-skills

For any other agent — Codex, Cursor, Copilot, or Claude Code itself — use the open Agent Skills CLI:

npx skills add nathanonn/agent-skills --skill codex-imagegen

One prerequisite: the skill rides on Codex, so you need either the Codex plugin in Claude Code or the Codex CLI installed locally (npm i -g @openai/codex, then codex login). Auth runs off your OpenAI account. If neither runtime is present, the skill tells you exactly what to install rather than failing silently.

And because it ships in the cross-agent collection, the same skill works outside Claude Code too. Same plain-language trigger, in whichever agent you happen to be living in that day.

.

.

.

The Harness Gap Keeps Closing

Step back and the trend line is hard to miss.

A year ago, the honest answer to “Claude Code or Codex?” was both, in two terminals. Then the Codex plugin let one tool review the other’s code in a single session. Now a small skill lets Codex generate images inside Claude Code — and not as a command you summon, but as something Claude reaches for the moment you simply ask.

The move worth internalizing: when your main tool can’t do something, you don’t always have to switch tools or wait for a feature to ship.

Sometimes you borrow the capability from the tool next door and wrap the seam so cleanly that you — and your other skills — stop noticing it was ever a seam. The Codex plugin opened that door. A skill that triggers on plain language walks the rest of the way through it.

Claude Code image generation went from impossible here to just ask in the span of one small skill.

That’s the whole shift — a feature that used to require leaving the room now happens without you breaking stride.

So install it, open your next project, and ask for the image you need — in plain English, the way you’d ask anyone. Watch it land in your project a couple of minutes later. Then go build the rest of whatever you were making, and tell me what came out.

11 min read The Art of Vibe Coding

The Three Files That Made Codex /goal Reliable Enough to Walk Away From

Person wondering "Can I walk away?" beside three documents — GOAL (what to build), VERIFY (how to prove it), and PROGRESS (what happened) — feeding into Codex /goal, producing a completed app. Caption: "Trust comes from evidence, not vibes."

The Hard Part Was Walking Away

The first time I ran a codex goal command on something that mattered, I sat there for twenty-eight minutes pretending to check email while the terminal scrolled.

I wasn’t doing other work. I was watching.

Making Codex write code has never been the hard part.

The hard part is walking away — stepping out of the room while an autonomous agent builds something you actually care about, with no way to know if it’s going sideways until it’s done.

Over five weeks and five builds, the same question kept surfacing: How do I know it did the right thing while I was gone?

Here’s what I learned.

A good /goal skill gives Codex a clear system to follow — and gives you something concrete to audit after the run ends. That system comes down to three files: GOAL, VERIFY, and PROGRESS.

  • GOAL defines what “done” means.
  • VERIFY maps each requirement to an actual check.
  • PROGRESS records what happened during the run so you can review the evidence instead of guessing.

Together, they turn a hands-off Codex run from “hope it works” into “review the receipts.”

Person wondering "Can I walk away?" beside three documents — GOAL (what to build), VERIFY (how to prove it), and PROGRESS (what happened) — feeding into Codex /goal, producing a completed app. Caption: "Trust comes from evidence, not vibes."

.

.

.

Five Builds, One Pattern

This pattern came from building things — running the codex goal command across five projects and watching what actually made the difference between a run I could trust and one I couldn’t.

BuildScaleTrust Lesson
WordPress plugin1 goalCodex needs a clear finish line
CLI tool2 goalsConnected goals need clear verification
Browser game8 goalsSequencing matters
Expanded game7 goalsSeam checks catch hidden bugs
WooCommerce plugin10 goalsLong runs need receipts

Each build gave Codex a bigger slice of autonomous work. Different stacks, different scales, anywhere from 28 minutes to nearly five hours of runtime. The trust structure underneath stayed the same.

Here’s the thing. The first build — one goal, 28 minutes — was small enough to verify by hand. By the fifth — ten goals, nearly five hours — manual verification would have taken longer than the build itself. (Stay with me on that: a five-hour autonomous run where you come back and check the receipts instead of babysitting. That’s the payoff these three files unlock.)

The full walkthrough for each build is linked at the end of this post.

.

.

.

Does Codex Know What Done Means?

GOAL answers the first trust question: What exactly are we building?

Without a clear definition of done, Codex invents its own finish line. It wanders, second-guesses scope, and eventually gives up having shipped half the requirements. Every long run that went sideways in my five weeks traced back to one thing: the spec was too vague for Codex to grade its own work.

And Codex re-reads the goal text constantly. It uses that same document as both the to-do list and the test for “done.” A vague spec gives it nothing to grade against — so it either keeps going in circles or declares victory on a hunch.

A good /goal skill solves this by writing GOAL from evidence. It reads your project first — the language, the framework, the folder layout, the naming patterns already in place. Then it asks targeted questions, each carrying a recommended answer and a one-line reason. By the time it generates GOAL.md, the document is grounded in your actual codebase.

GOAL.md should include:

  • Objective — one sentence describing what this goal produces
  • Repo context — what the skill learned by reading the project
  • Requirements — the specific features or behaviors to build
  • Assumptions — what the skill inferred and confirmed with you
  • Boundaries — what Codex should not touch
  • Definition of done — the outcomes that constitute “finished”
  • Stop conditions — when Codex should stop and ask instead of guessing

That last one matters more than it looks.

Stop conditions are the guardrail that prevents Codex from filling in gaps with assumptions. When the spec runs out of detail, a good skill tells Codex to pause rather than improvise.

In practice, the skill handles most of this for you. It shows up with a draft of what “done” looks like and asks you to confirm or edit — which is always faster than writing it from scratch. (The whole exchange feels like confirming a restaurant reservation. “Table for one? Near the window? 7 PM?” Yes, yes, yes.) Most of the time, the recommended answers are right. When they’re wrong, editing one line is cheaper than discovering the gap mid-run.

A messy feature request passes through a funnel (read repo, clarify assumptions) and becomes GOAL.md — with objective, requirements, boundaries, definition of done, and stop conditions — pointing toward a checkered finish flag. Caption: "GOAL tells Codex what done means."

.

.

.

Proof, or Just a Green Terminal?

VERIFY answers the second trust question: How do we know the work is correct?

GOAL defines what done looks like. VERIFY maps each requirement to an actual check that proves it was built correctly. Those checks have to use real commands from your project — real test runners, real build steps, real linters. Invented checks are worse than no checks, because they hand you false confidence.

A green terminal is comforting.

But if the check doesn’t trace back to a specific requirement in GOAL, it proves nothing useful. The WooCommerce build had ten goals running across nearly five hours. Without explicit traceability from each requirement to its proof, a clean summary could easily mask three missing features — and you’d only discover them after deploying.

VERIFY.md should include:

  • Requirement-to-check mapping — each GOAL requirement paired with its verification method
  • Real commands — test, build, lint commands that exist in the project
  • Manual checks — for anything that can’t be automated (UI polish, UX flow), explicitly listed as manual
  • Expected results — what a passing check looks like
  • Environment notes — anything the checks depend on (ports, services, seed data)
  • Seam checks — for multi-goal runs, checks that test the boundaries between goals

Seam checks earn a highlight.

I almost skipped the full-loop check in the Ion Viper build — every individual goal had passed its own tests. Why bother? Four hidden bugs at the transitions between goals is why. Stale state on restart, timing conflicts between systems, projectiles accumulating silently across scene boundaries. Each goal looked fine in isolation.

The full loop revealed the assumptions that no single goal’s tests could catch.

👉 A green terminal is not proof if the check doesn’t map back to the requirement.

Three requirements linked by chains to VERIFY.md, which maps each to a proof step. An arrow leads to Codex running verification with a "No fake checks" callout, producing a PASS stamp. Caption: "VERIFY turns done into proof."

.

.

.

What Happened While You Were Gone

PROGRESS answers the third trust question: What happened while I was gone?

GOAL gives the standard. VERIFY gives the proof plan. PROGRESS is the running record of what Codex actually did — what it changed, what it checked, what passed, what broke, and how it responded.

For a quick, single-goal run, you might glance at the terminal and move on. For a ten-goal build that ran for nearly five hours, terminal output is useless as an audit tool. PROGRESS.md is the structured receipt that lets you review the entire run without scrolling through hundreds of lines of terminal history.

PROGRESS.md should include:

  • Goals started and completed — with timestamps
  • Files changed — what Codex touched in each goal
  • Checks run — which verification steps executed
  • Results — pass/fail for each check
  • Errors found and fixes made — what broke and how Codex handled it
  • Evidence paths — where to find the artifacts (test output, screenshots, logs)
  • Remaining issues — anything Codex flagged but couldn’t resolve
  • Resume point — where to pick up if the run is interrupted
  • Final summary — the overall status in one paragraph

Resume points matter for long runs.

If a build fails at goal 7 of 10, you don’t want to re-run goals 1 through 6. PROGRESS records exactly where the work stopped and what state it was in, so the next run picks up cleanly.

The WooCommerce build was the first time I actually left the desk. Nearly five hours. I came back, opened PROGRESS.md, and had the full story in under three minutes — what passed, what broke, what Codex fixed on its own, and what it flagged for me to look at. A few minutes of structured review instead of hours of babysitting. That’s the trade these files offer.

A developer walks away from a desk. A robot works on code. PROGRESS.md records: Goal 1 PASS, tests run, bug fixed, evidence saved, resume point. A human returns and reviews the document with a magnifying glass. Caption: "PROGRESS is why walking away is not blind trust."

.

.

.

The Chain That Holds It Together

The three files work because they connect.

  • GOAL defines what done means.
  • VERIFY maps those requirements to proof.
  • PROGRESS records whether the proof held up during the actual run.

Any important requirement should be traceable across all three.

Here’s the traceability test: pick any requirement from GOAL. You should be able to find its matching check in VERIFY, the expected result, the recorded outcome in PROGRESS, and the evidence a human can review. If the chain breaks at any link — and I’ve had it break — you catch it during review, before anything ships.

Let me show you what that looks like.

A GOAL requirement says “admin users can generate auto-login links.” VERIFY maps that to a specific test command plus a manual browser check. PROGRESS records that both passed, with the test output saved to a file path you can open. From that single thread, you or anyone reviewing the build can verify the claim without re-running anything.

Each file fills a role the others can’t cover. GOAL without verification is a wish list — requirements with no accountability. A proof plan with no execution record can’t confirm the tests actually ran. And an execution log with no standard to measure against is a diary that tells you what happened but can’t tell you whether it was right.

The traceability between them is what turns three documents into a system you can rely on. Hand a reviewer the three files from any goal, and they can reconstruct the full story: what was supposed to happen, how it was supposed to be tested, and what actually happened. The evidence sits on disk.

.

.

.

Where Trust Still Breaks

The biggest risk is an unclear spec.

I wrote “build a wishlist plugin” once — three words, no detail. The result had wishlist functionality, technically. Just not the ones I needed. (If you’ve ever gotten back exactly what you asked for and realized the problem was what you asked for, you know the feeling.)

That costs real time and money.

The WooCommerce plugin build used roughly $131 worth of subscription usage across ten goals and nearly five hours. A vague spec that forces a second attempt doubles that cost. The three files pay for themselves by making the first run more likely to be the only run.

Codex also doesn’t do design polish. It builds things that work, but the look and feel comes out plain. Admin interfaces are functional and ugly. That’s fine — the hours the three files freed up from implementation are hours you can spend on refinement instead.

And I still review everything.

The three files don’t replace human judgment. They move it to better places: before the run, you define the right goal. After the run, you audit the receipts and polish what needs a human eye.

The codex goal command handles the middle part.

Your job is the beginning and the end.

.

.

.

Trust Is Engineered

The goal was always the same: give Codex a system that leaves evidence you can audit.

GOAL sets the finish line, VERIFY maps the proof plan, and PROGRESS keeps the receipts.

Together, they turn a hands-off run from “hope it works” into “review the evidence.” That’s what made walking away possible — across five builds, five different stacks, and run times stretching from 28 minutes to nearly five hours.

The skills keep getting better, too. Every gotcha you teach one is a gotcha it handles next time. The second project on a given stack goes smoother than the first, and the fifth smoother still. The investment compounds.

The specific stack doesn’t matter, either. Whether you’re building WordPress plugins, CLI tools, browser games, or something else entirely, the three-file structure translates. Build the skill once for your domain, teach it your stack’s hard-won rules, and future projects start ahead.

Build the system once. Document the evidence. Review the receipts.

All four skills from this series are open source:

npx skills add nathanonn/agent-skills --agent codex

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

The series, if you’re catching up:


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.

14 min read The Art of Vibe Coding

I Gave Codex a Requirements Doc and Got a CodeCanyon-Grade Plugin Back

Assembly line illustration for turning a requirements doc into a CodeCanyon-grade WordPress plugin with Codex /goal

The first time I used Codex /goal, I sat at my desk for twenty-eight minutes pretending to do other work while an autologin plugin built itself from a one-paragraph spec.

That was How to Use Codex /goal to Build WordPress Plugins (My Spec-to-Ship Workflow). One feature. One goal. The kind of experiment where you peek at the terminal every 90 seconds and try to look casual about it.

This time, my input was a full requirements document and this single line:

./run-goals.sh

Then I walked away. For nearly five hours.

When I came back, a complete WooCommerce plugin was sitting in the repo — an admin grid for bulk-editing stock quantities across products, including per-variation stock for variable products. That’s the exact kind of WooCommerce complexity that breaks naive implementations. The genre of plugin that sells on CodeCanyon for $30–60.

All built while I made dinner, watched half a movie, and checked the terminal exactly once. (More on that later.)

VS Code file explorer showing the starting state with only wp-requirements-to-goals skill, playwright-cli skill, and requirements.md — the entire human input is one requirements file plus two skills

Everything I’ve built with the codex goal command up to this point has fit inside a demo. The autologin plugin took twenty-eight minutes. How I Chained Two Codex /goal Runs to Build a Complete CLI Tool scaled the pattern to two linked goals. How I Used 8 Codex /goal Runs to Build a Browser Game From Scratch pushed it to eight.

The question I’ve been carrying — and maybe you have too — is whether /goal survives contact with real software. Multi-feature. Edge cases. Settings pages. The kind of product someone would actually pay for.

This post is where I find out.

The honest caveat lands early, same as always: /goal produced the code, but the requirements produced the outcome. And this time the spec was a full requirements document, decomposed by a skill into a layered tree of goals — each with its own contract, its own verification, its own proof.

(If you’re new to the series, the autologin post covers what /goal is and how the goal trio works. Everything here builds on that foundation.)

.

.

.

The Requirements Are the Real Work

A paragraph was enough for an autologin plugin.

A full product needs a full brief.

I learned this the hard way on a previous build. The requirements were loose enough that the agent met every acceptance criterion — and still missed what I actually wanted. (If you’ve ever written a Jira ticket and gotten back something that was technically correct and completely wrong, you know the feeling.)

That gap is where I started treating the requirements doc as the real product.

(Full requirements: https://github.com/nathanonn/wc-bulk-edit-stock/blob/main/requirements.md)

The requirements for this build carried tagged user stories with explicit acceptance criteria, edge cases around out-of-stock states and variable-product handling, and cross-cutting concerns like validation and save resilience:

  • US-01: Quickly update a single product’s stock from a filterable admin grid
  • US-02: Set a group of products to out-of-stock at once (bulk action)
  • US-03: Edit per-variation stock for variable products inline
  • Edge cases: WooCommerce inactive, concurrent edits, deleted staged products, 100+ variations
  • Cross-cutting: Save/validation resilience, filtering/search, batch selection

That doc is the product brief, the architecture, and the test plan — all in one file. The better it is, the less you touch the build.

I wrote about the upstream discipline in How to Write Better Requirements with Claude (Stop Letting AI Assume). That post produces the input this post consumes. If you’re going to try this workflow, start there.

Here’s the thing: the codex goal command runs on evidence, and the requirements doc is where that evidence gets defined. Every acceptance criterion becomes a checkbox the machine has to satisfy before declaring a goal complete. Write the criteria well, and you’ve written the test plan. Write them vaguely, and the build reflects that vagueness right back at you.

The leverage point from the autologin post still holds — the autonomy /goal provides downstream is paid for upfront, in the spec. Here the spec is bigger, so the downstream autonomy stretches wider too.

.

.

.

Meet wp-requirements-to-goals — The Skill That Decomposes

The autologin post introduced a skill that turns a vague paragraph into one goal trio. One input, one output.

This post’s counterpart is wp-requirements-to-goals.

Same family, different scale. It takes a structured requirements doc and produces an entire project — a goals plan, a root scaffold, and a layered tree of goals ready to execute. When I first ran it against the bulk stock manager requirements, the decomposition it produced was almost exactly what I would have designed myself — except it took minutes instead of an afternoon of whiteboarding.

The layering follows a consistent pattern:

LayerWhat it builds
00-foundationWalking skeleton — plugin activates, settings register, one artifact renders
Per-US goalsOne goal per user story, acceptance criteria copied verbatim from requirements
Non-US feature goalsCross-cutting concerns that don’t map to a single story
Integration goalRe-verifies every prior goal + cross-cutting edge cases

Each goal carries its own GOAL.md, VERIFY.md, and PROGRESS.md — the same trio from the autologin post, repeated across the full tree. Acceptance criteria are copied verbatim from the requirements document. Never paraphrased. That’s what keeps the machine’s definition of “done” identical to yours.

The integration goal at the end re-runs every previous verification — the same QC checkpoint idea readers of Your Codex Skills Should Evolve With Your Project (Ion Viper Part 2) will recognize, now baked into the WordPress skill instead of manually authored.

And before asking any questions, the skill probes the repo. It checks for existing config files, reads the slug, namespace, WordPress version, and PHP target from whatever’s already on disk. The clarification rounds stay short because the filesystem already answered most of the questions.

(Smart enough to look before it asks — which, let’s be honest, puts it ahead of a lot of people I’ve worked with.)

Codex terminal showing the wp-requirements-to-goals skill invoked against the requirements file

.

.

.

One-Shot or Phased — and the Q&A That Sets the Plan

The skill’s first question is a mode decision: generate goals phased or one-shot?

Phased writes the plan first, pauses so you can review and edit, then generates the goal files and scaffold. Safer for a first run — because the plan decomposition is the highest-risk decision. If the skill slices the requirements poorly, every downstream goal inherits the mistake.

One-shot generates the plan, scaffold, and all goal folders in a single pass. Faster, and what I chose here. The requirements doc was clean enough that I trusted the decomposition, and I wanted to see how far the unattended pipeline could stretch.

Codex asking whether to generate goals phased or one-shot, with three options: Phased recommended, One-shot, and None of the above
Selecting One-shot option to generate all goals and scaffold in one pass

After the mode decision, the skill ran through a handful of clarification rounds. I went with the recommended option on every one — the repo probe had already answered the identity questions, so these were mostly confirming sensible defaults.

(The whole exchange felt like confirming a restaurant reservation. “Table for one? Near the window? 7 PM?” Yes, yes, yes.)

First Q&A round with scaffold questions answered using recommended defaults — project identity, WordPress baseline, goal slicing, edge-case ownership
Second Q&A round covering test seeding method, derived acceptance criteria, and integration verification policy — all answered with recommended options

Then Codex laid out its five-step generation plan and started working.

Codex updated plan showing five generation steps: Phase 1 config, scaffold, foundation goal, per-US and non-US goals, integration goal

About 19 minutes later, the scaffold was done. Ten goal folders sitting in the goals directory. A root config, a plugin bootstrap folder, a verification protocol, and the bash script to run them all. Every contract written. Nothing implemented yet.

The project was runnable.

VS Code showing the finished scaffold — 10 goal folders from 00-foundation through 09-integration in the goals directory, plus root config files, ready to run

.

.

.

The Part That’s New: One Bash Command Runs Every Goal

Here’s what changed between this post and every previous one in the series.

In every prior build, I pasted each /goal command by hand. Copy the command, swap the folder name, press enter, wait, repeat. The build was autonomous within each goal, but the handoff between goals was manual. ME, copying and pasting. Every. Single. Time.

run-goals.sh removes that last handoff.

It chains every goal in order — starts the WordPress environment, runs the first goal, and when that one completes it auto-proceeds to the next, all the way through the integration goal at the end. One trigger, then leave.

Two pre-flight steps first. Install the local WordPress tooling:

Terminal showing npm install output — 404 packages installed for wp-env

Start the local WordPress environment:

wp-env start output with WordPress dev site at localhost:8888 and test site at localhost:8889

Then the trigger:

./run-goals.sh
Running ./run-goals.sh — the script starts wp-env, then launches Goal 00-foundation with danger-full-access sandbox and never approval

A practical note on plan tiers: on a ChatGPT Pro (x5) plan, the full unattended run fits inside usage limits. On a lower plan like Plus, you’d run goals in chunks to stay within limits — and the script supports exactly that:

./run-goals.sh --from 00 --to 02   # run goals 00, 01, 02
./run-goals.sh --only 03           # run a single goal

The foundation goal finished in about 14 minutes. The script committed the result and moved straight to the next goal without pausing.

Goal 00-foundation completed in 14 minutes 17 seconds, auto-proceeding to Goal 01-access-control with no human input

That auto-proceed is the whole point. The autologin post removed the per-step approvals. This one removes the per-goal handoffs. You are now outside the loop for the entire multi-goal build.

.

.

.

The Nearly-Five-Hour Black Box

The first time I left a single /goal run alone, the gap was 28 minutes. That felt long.

Nearly five hours is a different animal entirely. Ten goals. The entire implementation of a multi-feature WooCommerce plugin, start to finish, with nobody at the keyboard.

I won’t pretend the first time you let a run that long go feels comfortable. The trust window is ten times wider than the autologin post, and the stakes are proportionally bigger — more goals means more surface area for things to go wrong.

About two hours in, I opened the terminal tab. Just a glance — the kind where you tell yourself you’re checking “out of curiosity,” not because you’re nervous. Goal 05 was running. I closed the tab and made dinner.

Here’s what made the absence workable:

Each goal’s VERIFY.md defines what counts as proof. The continuation prompt refuses to declare a goal complete without mapping every acceptance criterion to evidence. Scope boundaries in each GOAL.md keep Codex from wandering into unrelated files. And the integration goal at the end — which alone took 91 minutes, about a third of the total runtime — ran a full regression sweep three times, re-verifying every prior goal’s work against the live WordPress environment.

Let me say that again. A third of the total build time was pure verification.

That regression discipline carries through the whole chain. Each goal re-checks the ones that came before it. A late goal breaking an early one would surface in that goal’s own verification pass, long before the integration sweep catches it again. The tests compound across the chain, and what you’re left with is a result you can audit from the artifacts alone.

283 minutes, 9 seconds. Ten goals completed, zero skipped.

Terminal showing 10 goals completed in 283 minutes 9 seconds with 0 skipped, followed by wp-env shutdown

.

.

.

What It Cost

I’ve been writing this series for months without ever putting a dollar figure on the autonomy.

This one does.

Before this experiment, I’d browsed CodeCanyon for bulk stock managers. The $40–60 listings had mixed reviews and half of them hadn’t been updated in a year. I wanted to know whether a clean spec and under five hours of machine time could land in the same category — so I built a bash script that totals input and output tokens across the full run and applies current GPT-5.5 API pricing.

Here’s what the 10-goal build cost:

Cost calculation output showing GPT-5.5 pricing: 10 completed goals, 4.71 hours, 208M input tokens with 206M cached, 0.43M output, Short Cost $131.40, Long Cost $254.46

How to think about that number:

Hiring a freelance WordPress developer to build a multi-feature WooCommerce admin plugin from a requirements doc would cost anywhere from $500 to several thousand dollars, depending on the complexity and the developer’s rate. Buying an existing CodeCanyon plugin and customizing it runs $30–60 for the license, plus hours of adaptation time to make it fit your exact spec.

$131 for a working, tested, multi-feature plugin built from your exact requirements — with zero hands-on coding time — lands in a genuinely interesting spot.

.

.

.

Does It Actually Work? (And the UI Taste Caveat)

Closed the terminal. Opened the browser. Tested the plugin like a regular human would.

The honest caveat first: the generated admin UI is functional but plain. GPT-5.5 builds things that work, but its visual design sense is weaker than Claude models. The admin page has the right columns, the right filters, the right controls — everything the requirements specified. The layout and styling are just… adequate. Functional without any flair.

The generated Bulk Edit Stock admin page showing a product table with search, category filter, stock status filter, and columns for product name, type, stock managed, stock quantity, and stock status — functional but visually plain

A day of CSS polish from a human — or a Claude session focused on UI — would bring it up to marketplace standard. The functionality, though, is the part the requirements controlled. And the functionality held up.

Here’s the test that matters most.

I edited stock for a simple product (set quantity to 20) and for a variable product’s “Small” variation (set quantity to 19), then hit Save Changes.

Bulk editing stock quantities — WC BES G09 Seasonal Two changed to 20, Small variation changed to 19, with Save Changes button and 2 products modified indicator

Then I opened the actual WooCommerce product edit screens to check whether the values persisted. The simple product showed 20.

WooCommerce product edit page for WC BES G09 Seasonal Two showing stock quantity of 20 persisted correctly after bulk edit, with red arrow pointing to the quantity field

The variation showed 19.

WooCommerce variation edit page for Small variation showing stock quantity of 19 persisted correctly after bulk edit, with red arrow pointing to the stock quantity field

Per-variation stock on variable products is exactly where a lazy plugin implementation falls apart — WooCommerce stores variation stock separately from the parent product, and the save path requires hitting variation-specific meta fields.

That complexity is the reason I chose this plugin as the test case. And it held up.

👉 What this series keeps landing on: /goal offloads the implementation so you can spend your time being a good tester. Hours of machine work freed me to focus entirely on verification. Opening the browser, clicking through the plugin, checking that values persisted — that’s where my time belongs now.

.

.

.

Grab the Plugin

The full project is on GitHub: wc-bulk-edit-stock. Every goal folder, the bash script, the complete Codex run history — all of it. You can walk through the entire build, goal by goal, in the commit log. (It’s one of those repos where the journey is the documentation.)

If you just want the finished plugin, the releases page has a downloadable zip. Drop it into any WooCommerce site and you’ve got yourself a working bulk stock manager.

.

.

.

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.

Decomposition, scaffolding, and goal generation — that’s what the skill handles. Execution is on the bash script. But both are only as good as the requirements doc you feed in. Vague requirements produce vague goals, and the build reflects that.

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

.

.

.

The Bigger Picture

Five entries in this series. One pattern. An input that keeps shrinking.

The autologin post started with a paragraph and a pasted command — one feature. This one started with a requirements doc and one bash command — a complete, multi-feature product.

The skill carries the domain knowledge. /goal runs the execution loop. PROGRESS.md proves the work. What changed is the ceiling — the scope of what you can build without writing code or babysitting the build.

The human’s job has compressed to two things: writing the requirements well and verifying the result. Everything between those two — decomposition, scaffolding, implementation, testing, regression — is now machine work you can trigger and walk away from. Like leaving a slow cooker on and coming back to a finished meal. (Except the meal is a WooCommerce plugin, and the slow cooker cost $131.)

The codex goal command reaches marketplace-grade complexity here, and that’s the claim this post earns. A bulk stock manager with per-variation editing, cross-cutting validation, and a full integration sweep is the kind of plugin people actually sell. The build handled it.

The honest forward edge: the UI taste gap is real, the $131 cost is real, and “marketplace-grade functionality” still needs a human’s polish and judgment before it’s ready for paying customers. Functional code and a shippable product are different things — the gap between them is taste, branding, documentation, and support. All human work.

But the part AI is getting genuinely good at — executing a well-specified plan, unattended, across an entire multi-feature build — just took another visible step.

Your job is to get good at writing the plan.


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.