Before: 422 lines. After: 386 lines. The cut is conservative because
evals/AGENT.md revealed that most of what looked like bloat is actually
load-bearing: the font_selection_procedure with its 23-font ban list
(lesson 6), the theme_selection with audience examples (lesson 9), and
the absolute_bans with literal CSS patterns (lesson 7) all drive
measurable eval improvements and must stay inline.
What moved out of SKILL.md:
- Context Gathering Protocol (52 → ~18 lines). The full protocol — cache
semantics, dispatch tree, teach/document/live exceptions, why-it-matters
— moved to reference/context.md. SKILL.md keeps only the compact hook:
load command, "never infer from codebase" warning, and pointer.
- Pin/Unpin (14 → 6 lines). Details moved to reference/pin.md.
- Spatial principles: dropped 4pt-vs-8pt rationale, gap-vs-margins CSS
technicality, and container-queries-vs-viewport explanation (not
load-bearing in the main skill). Kept all load-bearing rules
including the 80-char body-text line (detector-backed).
What did NOT move (load-bearing per evals):
- <font_selection_procedure> with the 23-font ban list
- <theme_selection> with the 8 audience examples
- <absolute_bans> with literal CSS patterns
- All XML tag structure (lesson 8: XML works better than markdown
for reasoning models, especially OpenAI)
Also added:
- reference/context.md (new) — full context protocol
- reference/pin.md (new) — full pin/unpin docs
- "Never infer brand, audience, or tone from the codebase" warning
restored to SKILL.md (was dropped in an earlier refactor)
Fixed:
- reference/colorize.md had "Accent borders: Add colored left/top
borders to cards or sections" which directly contradicted
SKILL.md's absolute_ban on border-left/right > 1px. Rewrote the
accent-border advice to use hairline borders, surface tints, or
leading glyphs instead, with an explicit reference to the ban.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two observed failure modes:
1. Smoke-test style truncation (`| head -N`) in bash commands defeats the
whole point of load-context.mjs — Claude needs the FULL file contents,
not the first few lines of JSON.
2. The old protocol didn't clearly explain session caching, leading to
repeated load-context.mjs calls across commands in the same session
(thousands of wasted tokens on 3-5KB files re-fetched 3-5 times).
Context Gathering Protocol rewrite:
- PRODUCT.md required (blocker), DESIGN.md optional (one-line nudge if
missing). Greenfield projects can't yet have a DESIGN.md to document.
- Explicit session cache: if content is in conversation history, do not
re-fetch. Exceptions listed (after teach/document/manual edit).
- Explicit "never truncate" rule: consume the full load-context.mjs
output, never pipe through head/tail/grep/jq with field filters.
- Content validity check: hasProduct=true but content <200 chars or
full of [TODO] markers = treat as missing, run teach.
- Missing-PRODUCT.md flow spells out task resumption: user asked for
/impeccable polish ButtonGroup, we must run teach, then RESUME polish
of ButtonGroup with fresh context — not silently abandon intent.
- Three explicit exceptions to the protocol:
- /impeccable teach skips it (teach creates PRODUCT.md)
- /impeccable document loads PRODUCT.md only (creates DESIGN.md)
- /impeccable live already warms context via live.mjs — don't also
run load-context.mjs
teach.md Step 6 and document.md Step 5 now re-run load-context.mjs at
the end so the freshly-written files surface in conversation history
and subsequent commands use the new version, not a stale earlier read.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The format at https://stitch.withgoogle.com/docs/design-md/format/ defines
exactly six sections in a fixed order: Overview, Colors, Typography,
Elevation, Components, Do's and Don'ts. Our previous format used
non-compliant names (Visual Theme & Atmosphere, Color Palette & Roles,
Typography Rules, Component Stylings), a non-existent "Layout Principles"
section, and had no Do's and Don'ts.
Changes to reference/document.md:
- Fixed section list to match the spec character-for-character
- Added mandatory "Creative North Star" pattern at top of Overview
- Added Named Rules pattern (e.g. "The No-Line Rule") — stickier than
bullet lists for AI consumers, mirrors Stitch's own generator output
- Added explicit Do's and Don'ts section with concrete, forceful guardrails
- Elevation is now its own section (was buried in Components)
- Layout/motion/responsive content folds into Overview + Components
rather than inventing new top-level sections
- Guidance on forceful voice ("prohibited"/"forbidden"/"never") matching
PRODUCT.md's expert-decisive tone
- Pitfalls section warns against renaming sections or adding new ones
Changes to our DESIGN.md:
- Rewrote to use spec-compliant section headers with evocative subtitles
(e.g. "## 2. Colors: The Warm-Paper Palette")
- Opened with "Creative North Star: The Editorial Sanctuary"
- Added 11 Named Rules across sections (The One Voice Rule, The Paper-
Not-White Rule, The OKLCH-Only Rule, The Italic-Is-Voice Rule,
The 1.6 Leading Rule, The Fluid-Headlines-Only Rule, The Flat-By-
Default Rule, The Low-Alpha Rule, The Tinted-Shadow-Only-For-Accent
Rule, plus the existing implicit ones)
- Full Do's and Don'ts section with 10 Dos and 15 Don'ts, many of which
translate PRODUCT.md anti-references into concrete prohibitions
- Elevation section documents shadow vocabulary separately from Components
- Layout/spacing/motion content relocated to a sub-section under Components
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This repo is the canonical example of impeccable — users who install the
skill should be able to look at how a real project uses PRODUCT.md and
(later) DESIGN.md. Ungitignored both files.
Also rewrote PRODUCT.md itself to match the new schema from teach.md
(# Product → Users / Product Purpose / Brand Personality / Anti-references
/ Design Principles / Accessibility), dropping the legacy "Design Context"
wrapper and the Aesthetic Direction section that belongs in DESIGN.md.
.impeccable.md stays gitignored as legacy (the loader auto-migrates it).
DESIGN.md for this project is pending — to be generated with
/impeccable document once we run it on our own codebase.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pioneers a two-file convention for project context:
- PRODUCT.md (strategic): users, brand, principles — answers who/what/why
- DESIGN.md (visual): follows Google's Stitch DESIGN.md spec — answers how-it-looks
Both files live at the repo root. Filename matching is case-insensitive.
DESIGN.md wins on visual conflicts, PRODUCT.md wins on strategic/voice.
Legacy .impeccable.md is auto-migrated to PRODUCT.md on first read by the
new shared loader. This is silent and one-shot — the rename is permanent.
What changed:
- New scripts/load-context.mjs: shared context loader used by every command
that needs project context. Reads both files, handles legacy migration.
- New reference/document.md: /impeccable document command that generates
DESIGN.md by auto-extracting tokens (colors, typography, spacing, radii,
shadows, components) from CSS/Tailwind/theme files, then asking the user
to confirm descriptive language for atmosphere and color character.
Follows Google's Stitch DESIGN.md format for tool compatibility.
- SKILL.md Context Gathering Protocol updated to load both files and
nudge the user to run /impeccable document when DESIGN.md is missing.
- reference/teach.md rewritten to split discovery cleanly: strategic
questions go to PRODUCT.md, visual/design-system work is delegated to
/impeccable document (skipped on empty projects).
- reference/live.md consumes {product, design, productPath, designPath,
migrated} from the loader instead of a single context blob.
- scripts/live.mjs uses the shared loader instead of inline file reading.
- Command count updated 22 → 23 (new: document). Metadata, router table,
command menu, periodic table viz, and homepage data all updated.
- .gitignore adds PRODUCT.md + DESIGN.md (repo-local, not shared).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Previously only bolder/quieter/animate/colorize/typeset/layout had
variant diversity rules. Added the same level of guidance for distill,
polish, adapt, delight, and overdrive so every live action has a
clear "each variant must differ on THIS axis" rule.
Also noted that overdrive should skip its reference's "propose and ask"
step in live mode (it's non-interactive — the user picks from variants).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two failure modes observed:
1. Claude generates N near-identical variants (small shade/size tweaks)
instead of meaningfully different design directions
2. When a sub-command like /bolder is chosen in the picker, Claude skips
loading reference/bolder.md and generates generic variants
Fixes:
- "Load reference file" is now a MANDATORY Step 2a, separate and
non-negotiable, called out as a critical failure to skip
- Added Step 2b "Plan 3+ distinctly different directions" with 7
structural axes variants must differ on (hierarchy, layout topology,
typography system, color strategy, density, tone, decomposition)
- Added action-specific diversity rules (bolder = different dimensions,
animate = different motion vocabulary, colorize = different hues, etc.)
- Freeform prompt guidance: honor the prompt direction but explore
meaningfully different interpretations, not three near-copies
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Previously, starting live mode required ~5-6 sequential bash calls:
read .impeccable.md, start server, check config, read reference, inject
tag, verify. The new live.mjs does all of this in a single command
(~340ms cold, ~90ms when reusing a running server) and returns everything
the agent needs in one JSON blob.
Workflow is now:
1. node live.mjs # start + inject + load context (1 bash call)
2. navigate browser # optional MCP call
3. node live-poll.mjs # enter poll loop
Reference doc collapsed to a single "Start Live Mode" section with the
one-command path plus a first-time config creation fallback.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
First live run: agent auto-detects framework and writes a small config.json
(file, insertBefore/insertAfter anchor, comment syntax). Every subsequent
run: live-inject.mjs handles insert/remove deterministically, no LLM needed.
The config lives at {scripts_path}/config.json and is gitignored — it's a
per-project cache that wipes on skill update and regenerates on next use.
- New live-inject.mjs: --port (insert), --remove, --check modes
- Idempotent insert: re-running with a different port replaces cleanly
- Reference doc: one-time detection step, then instant insert/remove
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Arrow keys now pass through to element picker when the freeform input
is empty, instead of being swallowed by stopPropagation
- Arrow nav works in both PICKING and CONFIGURING states, so you can
change your element selection while the config bar is open
- Clicking outside the selected element and bar returns to PICKING mode,
matching the expected deselect behavior
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The changelog and FAQ sections are inside a positioned .changelog-faq-row
wrapper, so their offsetTop was 0 (relative to parent) instead of their
actual document position. This broke current-section detection and caused
both pills to appear permanently active. Use getBoundingClientRect()
instead, which returns correct absolute positions regardless of nesting.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Accept and discard in live variant mode are now handled by a deterministic
script (live-accept.mjs) that runs inside the poller before returning to
the agent. The browser updates the DOM instantly on click (fire-and-forget)
so the user is never blocked waiting for LLM-driven file cleanup.
Key changes:
- New live-accept.mjs: deterministic accept/discard file operations
- Poller auto-runs accept script for accept/discard events (_acceptResult)
- Browser handleAccept() now commits DOM change instantly, no SAVING state
- CSS+HTML colocated in one write (style tag inside variant wrapper)
- SSE heartbeat every 30s prevents silent connection drops
- Poll timeout increased from 2min to 10min
- EventSource onopen resets retry counter for reliable reconnection
- Server --background flag for clean single-command startup
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comprehensive architecture decision record covering the live variant
mode: context, key decisions (source modification over DOM patching,
SSE over WebSocket, self-contained skill scripts, HTTP long-poll for
agent), full architecture diagram with message flows, variant wrapper
format, browser UI states, session persistence, security model,
server resilience, performance optimizations, test coverage, known
limitations, and future work.
Also picks up improvements from parallel thread: poll timeout bumped
to 10 min, SSE heartbeat every 30s, and other minor fixes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
os.tmpdir() returns /var/folders/.../T/ on macOS, not /tmp/. The skill
reference was telling the agent to cat /tmp/impeccable-live.json which
didn't exist. Moving the PID file to the project root makes it
predictable across platforms and project-scoped (multiple projects can
run independent live sessions).
Changed in: live-server.mjs, live-poll.mjs, live.md reference.
Added .impeccable-live.json to .gitignore.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The server is started with & (backgrounded), so its stdout output isn't
captured by the agent's Bash tool. The skill reference now tells the
agent to sleep 2s then cat the PID file (/tmp/impeccable-live.json) to
get the port and token. The PID file is written by the server as soon
as it starts listening.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Compact floating pill at the bottom center of the viewport, always
visible during live mode. Matches the action bar's light, translucent
aesthetic with brand-tinted active states.
Controls:
- "Impeccable" brand mark (capitalized, brand magenta)
- Detect toggle: eye icon, loads anti-pattern scanner in extension mode,
waits for impeccable-ready before first scan, shows issue count badge
inside the button. Toggle off removes overlays.
- Pick toggle: crosshair icon, enables/disables element picker. Active
by default. When pick is active, detect overlays get pointer-events:
none so the picker sees through them.
- Exit button: sends exit event and tears down all UI.
Detect + pick coexistence fixes:
- Picker highlight z-index raised above detect overlays (100001 vs 99999)
so the selection outline and element path are always visible.
- Removed layout-property transitions (top/left/width/height) from the
highlight to avoid triggering the anti-pattern detector and to give
instant cursor tracking.
- First-click-on-detect fix: script loads async, scan command is queued
until the impeccable-ready postMessage arrives.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. Skill reference: poll should run as background task with no timeout.
Changed "blocking poll loop" to "background task, no timeout" so the
agent keeps the main conversation free for other work.
2. Resume restores selectedAction from localStorage: the bar was showing
"Freeform" after page reload even when the user picked "Bolder". Also
improved selectedElement targeting to prefer the visible variant's
content over the wrapper parent.
3. Discard no longer shows "Applying variant...": accept shows the
saving→confirmed flow, but discard now dismisses immediately and
cleans up the DOM. Different intent, different UX.
4. Picker works after discard: cleanup() now removes the variant wrapper
from the live DOM and restores the original element. Previously the
stale wrapper with data-impeccable-variant attributes confused the
picker's isPickable/own checks.
5. Stop live mode: added "Stopping Live Mode" section to the skill
reference. The user can say "stop live mode" in the conversation, and
the agent proceeds to cleanup (remove script tag, stop server).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The static "[command] [target]" hint didn't help users discover available
commands. The build now reads command-metadata.json and groups commands
by category (from SKILL_CATEGORIES) with middle-dot separators for
natural line-breaking in the prompt bar.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tests:
- tests/live-wrap.test.mjs (26 tests): unit tests for buildSearchQueries,
findElement, findClosingLine, detectCommentSyntax (20 pure function
tests) + integration tests for the full wrapCli on HTML and JSX
fixtures with temp dirs (6 tests covering wrapping, ID/class lookup,
error handling, content preservation).
- tests/live-server.test.mjs (15 tests): integration tests that start a
real server on port 8499, then test /health, /live.js, /detect.js,
/poll (timeout + auth), /events POST (validation + auth), browser→agent
event flow (POST event → poll receives it), agent→browser SSE flow
(POST reply → SSE stream delivers it), /source (read, path traversal
rejection, auth, 404).
Also:
- Added auto-execute guards to live-wrap.mjs and live-poll.mjs so they
work when run directly with `node live-wrap.mjs ...` (needed for both
skill instructions and integration tests).
- Exported buildSearchQueries, findElement, findClosingLine,
detectCommentSyntax from live-wrap.mjs for unit testing.
- Updated package.json test script to include the new test files.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. CLI cleanup: removed live, poll, and wrap commands from bin/cli.js
and the liveCli export from detect-antipatterns.mjs. These now live
exclusively in the skill scripts (node scripts_path/live-server.mjs).
2. Deleted src/live/: server.mjs, poll.mjs, wrap.mjs, browser.js,
protocol.mjs. The source of truth is now source/skills/impeccable/
scripts/live-*.
3. Graceful server-lost handling: the browser tracks SSE reconnection
attempts (max 5). After exhausting retries, it cleans up the UI:
hides the bar, highlight, and cycler, shows a "Live server
disconnected" toast, resets state to IDLE. This handles agent
crashes, server kills, and network issues without leaving the
browser stuck in a "Generating..." state.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two architectural changes that make the live variant mode self-contained:
1. SSE replaces WebSocket: the server now uses Server-Sent Events for
server→browser push and regular fetch POST for browser→server
events. This eliminates the ws npm dependency entirely. The live
server is now zero-dependency pure Node.js (http, crypto, fs, net).
Browser: EventSource replaces WebSocket. sendEvent() uses fetch POST.
Server: GET /events returns SSE stream, POST /events receives browser
events. All other endpoints (poll, source, health, stop) unchanged.
2. Scripts moved to source/skills/impeccable/scripts/: live-server.mjs,
live-poll.mjs, live-wrap.mjs, live-browser.js are now part of the
skill itself. Users who install the skill via npx skills get the live
mode without needing npm install impeccable separately.
The skill reference uses {{scripts_path}}/live-server.mjs etc.
The CLI (bin/cli.js) delegates to the skill scripts as a convenience.
Removed ws from package.json dependencies.
The old src/live/ files remain as the development copy. The build system
syncs source/skills/ to all harness dirs (11 providers).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
For dev servers without HMR (Bun static imports, simple HTTP servers),
the browser can't see file changes automatically. Three changes fix this:
1. /source endpoint on live server: reads a project file from disk,
gated by session token + path-traversal guard. The browser fetches
the raw HTML directly, bypassing the dev server's cache.
2. poll --reply --file flag: agent passes the source file path when
replying done. The browser receives it via WS and knows where to
fetch. Skill reference updated to always include --file.
3. Browser injectVariantsFromSource(): on "done" with 0 DOM variants,
fetches the raw HTML from /source, parses with DOMParser, extracts
the variant wrapper, finds the matching element in the live DOM by
class/ID, and replaces it. MutationObserver picks up the injected
variants and the cycling bar appears.
Also: wrap CLI no longer hides the original element (was display:none).
The original stays visible until the first variant arrives, preventing
a flash of empty content between wrap and variant insertion.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The serveGenerated() approach for index.html served raw HTML, which
broke bare module specifiers (import "motion") that Bun's HTML import
bundler normally resolves. Restore the static import pattern.
The live mode's auto-reload fallback still works for HMR-capable dev
servers (Vite, Next.js). For Bun's serve() with static HTML imports,
the dev server needs a restart to pick up HTML changes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Accept/discard flow: clicking Accept now shows "Applying variant..."
spinner in the bar while the agent processes, then morphs into a green
"Variant applied" confirmation that auto-dismisses after 1.8s. Same
for discard. The bar stays visible during the entire operation so the
user knows something is happening.
No-HMR fallback: when the browser receives "done" but no variants
appeared in the DOM (dev server without HMR, like Bun), it auto-reloads
the page. resumeSession picks up the variants from the fresh HTML.
wrap CLI: --query replaced with structured args (--element-id, --classes,
--tag) that search in priority order: ID > class combo > single class >
tag+class > raw text. Handles elements without class names or IDs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three optimizations to cut the generate loop from ~40s to ~15-20s:
1. wrap CLI helper (src/live/wrap.mjs): finds an element in source
by ID, class names, or tag+class combo, wraps it in the variant
container with original snapshot, and returns the file path + insert
line. Replaces 3-4 agent tool calls (grep + read + edit) with one.
Supports --element-id, --classes (comma-separated), --tag, --query
(fallback). Searches in priority order: ID > class combo > single
class > raw text. Auto-detects comment syntax (HTML vs JSX).
2. Batch variant writes: skill reference updated to instruct the agent
to write ALL variants in a single file edit instead of one per
variant. Saves N-1 tool call round-trips (~3-5s each).
3. Page URL in generate event: browser now includes location.pathname
so the agent can map URL to source file directly (/ = index.html,
/about = about.tsx, etc.) without grepping.
Net effect: agent flow is now 4 tool calls (wrap + edit + read-variant
+ poll-reply) instead of 8+ (grep + read + create-wrapper + N edits
+ poll-reply).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two fixes:
1. Session persistence via localStorage: the previous DOM-attribute
approach (data-impeccable-handled) didn't survive page reloads
because the DOM is rebuilt from source. Now using localStorage:
- Session state (id, action, count, arrived, visible variant)
saved on every state change
- Handled sessions (accepted/discarded) tracked separately
- resumeSession() checks localStorage before resuming, skips
if the session was already handled
- Visible variant index preserved across reloads (user sees
the same variant they were looking at)
- cleanup() clears session, clearHandled() clears on next
load when the wrapper is gone from source
2. Dev server HTML hot-reload: replaced static Bun HTML imports
(import homepage from "../public/index.html") with dynamic
file() serving via the existing serveGenerated() helper. HTML
edits are now reflected on browser refresh without restarting
the dev server. This matches how sub-pages already work.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three bugs found and fixed during real end-to-end testing:
1. MutationObserver infinite loop: the observer watched all of
document.body, so our own bar DOM updates triggered it, which
rebuilt the bar, which triggered it again, freezing the page.
Fix: filter mutations to only react when nodes with
data-impeccable-variant attributes are added inside the variant
wrapper. Added a re-entrancy guard as a safety net.
2. Premature exit on transient WS disconnect: the server fired an
exit event the instant the last WebSocket client disconnected.
HMR page reloads cause brief disconnects that triggered false
exits. Fix: 8-second debounce before sending exit, cancelled
if a client reconnects within that window.
3. WS auth_ok clobbering resumed session state: after a page reload,
resumeSession() correctly set state to CYCLING, but then the
async WS auth_ok handler overwrote it to PICKING. Fix: only
transition to PICKING from IDLE, not from an active session state.
Also fixed: highlight tracking during variant cycling (update
selectedElement to the newly visible variant's content element so
the highlight follows the active variant, not the hidden one).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace the bulky modal action panel and separate cycler with a single
compact floating bar (~343px x 46px) that shapeshifts between three
modes: configure, generating, and cycling.
Configure mode: action pill (clickable, opens a 4-column chip grid
popover), inline text input with contextual placeholder, variant count
toggle (click to cycle 2/3/4), and Go button. One line, no labels,
no redundant "impeccable" branding.
Generating mode: action label + progressive dot indicators + status
text. Same bar, same position, content crossfades.
Cycling mode: prev/next nav buttons, clickable dot indicators,
counter, accept/discard. Same bar, same position.
Design details:
- Translucent warm paper background with 16px backdrop blur
- ease-out-quint spring entrance (translateY + opacity)
- Action picker scales from pill origin with 0.18s transition
- Dots animate in with scale(0.6)->scale(1) as variants arrive
- Bar tracks selected element on scroll via requestAnimationFrame
- All interactive elements have hover/active micro-interactions
- Input captures keyboard events (stopPropagation) to prevent
picker nav from firing while typing
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New feature: /impeccable live starts an interactive visual iteration server.
Users select elements in the browser, pick a design action (bolder, quieter,
etc.), and the agent generates HTML+CSS variants written directly to source.
The dev server's HMR hot-swaps them in, and MutationObserver progressively
reveals each variant in a cycler UI as it arrives.
Architecture:
- src/live/server.mjs: HTTP + WebSocket server with session token auth,
long-poll /poll endpoint for the agent, WebSocket for the browser
- src/live/poll.mjs: CLI client (npx impeccable poll / poll --reply)
- src/live/browser.js: element picker with keyboard nav (arrows=siblings,
shift+arrows=parent/child), action panel (12 commands, freeform input,
variant count), variant cycler with progressive reveal via MutationObserver
- src/live/protocol.mjs: shared message types and event validation
- source/skills/impeccable/reference/live.md: agent loop instructions
(inject script, poll loop, generate variants, accept/discard, cleanup)
CLI changes:
- bin/cli.js: added "poll" top-level command
- src/detect-antipatterns.mjs: liveCli() now delegates to src/live/server.mjs
- package.json: added ws dependency
Registered /impeccable live as command #22 across all standard locations.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pre-3.0, onboard was folded into /harden when we were trying to reduce
namespace pollution. In the single-skill model that tradeoff is gone,
so the weakest of the old merges is the first to undo.
Harden and onboard live in different mental modes. Harden is defensive
(edge cases, i18n, overflow, errors). Onboard is activation (first-run
flows, empty states as CTAs, progressive disclosure). A user thinking
"design the onboarding flow" was never going to type /impeccable harden.
Changes:
- New reference file at source/skills/impeccable/reference/onboard.md,
restored from the pre-merge version in git history rather than the
condensed 33-line summary that was in harden.md.
- Removed the "Onboarding & First-Run Experience" section from
source/skills/impeccable/reference/harden.md.
- Updated harden description/editorial/process-steps to drop onboarding
keywords; split commandProcessSteps so harden stays focused on
production resilience and onboard gets its own phases.
- Registered onboard in: SKILL.md description + command menu + router
table, command-metadata.json, IMPECCABLE_SUB_COMMANDS, pin.mjs
VALID_COMMANDS, SKILL_CATEGORIES, COMMAND_RELATIONSHIPS, data.js
commandCategories + commandProcessSteps + commandRelationships,
framework-viz commandSymbols + commandNumbers.
- Reused the existing content/site/skills/onboard.md editorial wrapper
(it was orphaned by the merge but never deleted), updating it to use
/impeccable onboard.
- Bumped all user-facing count references 20 -> 21: public/index.html,
CLAUDE.md, README.md, NOTICE.md, plugin.json, marketplace.json,
sitemap.xml, build-sub-pages.js.
- Harness dir audit.md and critique.md changes are the
{{available_commands}} placeholder expanding to include onboard.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove "Rewritten docs site" (internal site polish, not a shipped
feature) and "Teach runs automatically on first use" (not new; that
behavior already existed pre-3.0). What's left is the consolidation
and the pin mechanism, which are the two user-facing changes in 3.0.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
npm subprocesses can regenerate a stray package-lock.json (last time
this happened, it was reverted in 3ca60a8). Add it to .gitignore so it
stops showing up as untracked.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Bump skills plugin version 2.1.1 -> 3.0.0 (plugin.json, marketplace.json,
harness SKILL.md files). CLI and Chrome extension unchanged.
- Remove prefixed universal zip bundle and all related code:
factory.js prefix/outputSuffix options, zip.js variant pass, utils.js
prefixSkillReferences, the "universal-prefixed" entry in
download-providers.js, and the matching test suite in utils.test.js.
- Redesign Get Started step 1 "Install the skill and CLI": two terminal
rows (npx skills + npm i -g impeccable) with paired notes, drop the
Recommended badge.
- Collapse "Other install methods" back into a <details> element so the
primary install path is the first thing users see.
- Simplify step 3 to "Add the Chrome extension": remove the CLI tool
block (now in step 1), use standard .btn .btn-primary for the CTA so
it matches other primary buttons (square corners, accent slide-up
hover), and lay out the preview screenshot next to the button instead
of stacked so the screenshot no longer dominates vertical space.
- CLAUDE.md: rewrite with v3.0 architecture, the "no em dash also means
no --" rule, the harness-dirs-are-tracked gotcha, the named-export
test-spy warning, and the evals inline-skill.ts sync note.
- AGENTS.md, DEVELOP.md: drop prefixed variant references.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Biggest change in a while. Users previously had 18 standalone skill
entries cluttering their /menu; now they have one entry (/impeccable)
that routes to 20 specialized commands via argument dispatch. The pin
mechanism (/impeccable pin audit) restores standalone shortcuts on
demand for commands users hit all the time.
## Architecture
- Single /impeccable skill with command router section in SKILL.md
- 20 commands served via reference files under source/skills/impeccable/reference/
- /impeccable pin <command> creates a lightweight redirect shim so users
who prefer /audit, /polish, etc. can still have them
- Context gathering (teach) auto-runs on first use
- command-metadata.json is the single source of truth for command
descriptions, argument hints, and relationships
## Site rewrite
- Docs URL: /skills renamed to /docs (with /skills permanent redirects)
- Homepage hero frames Impeccable as "one skill with 20 commands"
- "Get Started" split into 50/50 install + how-to-use with editorial
numbered steps, /impeccable shown as the home command with three modes
- New /docs overview: home command hero card + dense category rows
matching the old cheatsheet density, with leads-to/pairs-with/
combines-with relationship metadata served from a shared source
- Cheatsheet merged into /docs, /cheatsheet redirects
- Magazine spread and mobile cards show /impeccable as a stacked
namespace label above the command name at full display size
- Periodic table updated with craft/teach/extract as first-class cells
- Skill detail pages generate from reference files, with an editorial
wrapper per command for tagline + body
- Tutorials and anti-patterns pages updated to use /impeccable <cmd>
## Build system
- Dead code removed (scripts/lib/transformers/shared.js)
- Build log wording fixed ("1 skill" not "1 skills (1 user-invocable)")
- generateApiData fallback branch removed (throws loudly if metadata
missing instead of silently degrading)
- Commands API includes editorial tagline alongside the long description;
UI surfaces prefer tagline for human display, description for auto-
trigger keyword matching
## Gitignore
- Added .claude/scheduled_tasks.lock, .claude/settings.local.json to
ignore list (local Claude Code state that should not be tracked).
- Harness skill directories (.claude/skills/, .agents/skills/, etc.)
remain tracked by design: npx skills reads them from this repo at
install time and they enable clean submodule use.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
generateYamlFrontmatter only re-quoted values starting with `[` or `{`,
but parseFrontmatter strips surrounding quotes on input. Descriptions
containing `: ` (e.g. "Also handles: critique...") round-tripped into
unquoted plain scalars that YAML parsers reject. Added a yamlNeedsQuoting
check covering colon-space, space-hash, YAML indicator chars, reserved
keywords, and number-like strings, plus regression tests.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two detector bugs that produced false positives on sites like uselinkshot.com:
1. The bg-black regex matched Tailwind opacity modifiers (bg-black/3,
hover:bg-black/5) because / is a word boundary. Added negative lookahead.
2. resolveBackground ignored url() background-images, walking past them to
the body's white bg. White text on a dark hero image was flagged as
1.0:1 white-on-white. Now bails on url() images like it does for gradients.
Also: extension build auto-generates dist/extension.zip, version bumps for
CLI (2.1.7) and extension (1.0.1).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The gallery page had broken styling and missing images. The visual
mode page already has the same specimen gallery in a better layout.
- Removed public/gallery.html and its build entry point
- Updated homepage links to point to /visual-mode#try-it-live
- Added 301 redirect from /gallery to /visual-mode#try-it-live
- Added id="try-it-live" anchor to the visual-mode gallery section
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When .claude/skills is a symlink to .agents/skills, updating both
providers wrote to the same directory twice -- the last write
(.agents) always won, making .claude content identical to .agents.
The up-to-date check also always failed because it compared
provider-specific bundle content against the wrong provider's files.
Fix: use realpathSync to detect shared directories and process each
unique real path only once with its matching bundle provider. Respects
the user's symlink setup for non-impeccable skills.
Tested: first run updates 18 skills, second run reports "up to date".
CLI bumped to v2.1.6.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Build system now injects skills version (from plugin.json) into
every SKILL.md frontmatter as a version field
- CLI reads the version from the local impeccable SKILL.md and
displays it in check/update output
- Hash comparison normalizes the version field (so a version bump
alone doesn't trigger a full re-download)
- Removed misleading CLI version display from skills commands
CLI bumped to v2.1.5.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Skills installed via npx skills add resolve {{scripts_path}} to
.agents/skills/... while our bundle resolves it per-provider
(.claude/skills/..., .cursor/skills/..., etc). Without normalizing,
identical content always shows as different.
Also compare only one provider instead of all (they have the same
content, just different path prefixes).
CLI bumped to v2.1.4.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The hash comparison was comparing the entire local skills directory
(which includes user's custom skills) against the bundle (which only
has impeccable skills). Now only compares skills that exist in the
bundle, so custom skills don't cause a false mismatch.
CLI bumped to v2.1.3.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
CLI changes (bumped to v2.1.2, skills stay at v2.1.1):
- `npx impeccable skills check` compares local skill files against
the latest bundle and reports whether updates are available
- `npx impeccable skills update` now downloads the bundle first,
compares hashes, and skips with "up to date" if nothing changed
- Removed the local-modifications warning (was confusing for users
who installed via npx skills add)
Versioning:
- CLI (package.json), skills (plugin.json/marketplace.json), and
Chrome extension (manifest.json) are now versioned independently
- CLAUDE.md updated to document when to bump each
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
npx skills update has a known upstream bug (vercel-labs/skills#775)
where it can't find the lock file. Instead of trying it first and
falling back, always use our direct download which is reliable.
Also:
- Site now recommends `npx impeccable skills update` everywhere
instead of `npx skills update`
- Direct download path now re-applies prefix after updating
- Runs cleanup after download to strip deprecated stubs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
npx skills update has a known bug where it can't find the lock file
(vercel-labs/skills#775). Our direct download fallback is the primary
working path, so the message should not sound like an error.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The post-update-cleanup section now instructs the AI to tell the user
what's happening and why before running the script, so file deletions
don't feel unexpected.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The refactored factory.js transformer dropped script file support that
existed in the old shared.js version. Scripts were read from source
but never written to dist/, so npx skills installed skills without the
cleanup-deprecated.mjs script, causing errors on first load.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>