Reconcile questionnaire branch with main's site split and redesign

Post-rebase repairs after main moved 226 commits (site/ split to the
private impeccable-site repo, DESIGN.md spec now eight sections, register
extraction replaced by extractSectionValue, seed mode rewritten):

- document.md: transplant this branch's seed pipeline (assets, interview,
  visual cues, picker) onto main's rewritten file; adopt the canonical
  section vocabulary. The interplay with new-work's workshop is left for
  PR review.
- picker: vendor kinpaku tokens/kit CSS, hero art, and favicon under
  picker/ (site/ no longer exists here); drop the sharp resize step and
  ship the compressed JPEG directly; declare astro/sharp-free deps.
- picker-server: read the register via extractSectionValue('Register')
  since extractRegister is gone from context.mjs.
- palette.mjs / utils.js / test-suites.mjs / package.json / release.mjs:
  rebuild as main's version plus this branch's additions, undoing hunks
  the -X theirs replay had clobbered.
- craft.md / shape.md: reset to main (this branch's net change was zero).
- progress bar: animate transform instead of width (design hook finding).

Validation: bun run build green (prose validators clean), bun run test
198/198, picker-server suite 7/7.

AI-assisted (agent-implemented, maintainer-directed).

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Abdul Wahab
2026-09-01 10:02:39 +05:00
co-authored by Cursor
parent c19c349fc6
commit d64909decf
14 changed files with 2002 additions and 364 deletions
+3 -3
View File
@@ -43,9 +43,8 @@
"build:picker": "node scripts/build-picker.mjs",
"build:skills": "bun run scripts/build.js --skip-root-sync",
"build:skills:release": "bun run scripts/build.js",
"build:site": "npx astro build",
"build": "bun run build:picker && bun run build:skills && bun run build:site && cp -R dist build/_data/dist",
"build:release": "bun run build:picker && bun run build:skills:release && bun run build:site && cp -R dist build/_data/dist",
"build": "bun run build:picker && bun run build:skills && mkdir -p build/_data && rm -rf build/_data/dist && cp -R dist build/_data/dist",
"build:release": "bun run build:picker && bun run build:skills:release && mkdir -p build/_data && rm -rf build/_data/dist && cp -R dist build/_data/dist",
"build:browser": "node scripts/build-browser-detector.js",
"build:extension": "node scripts/build-extension.js",
"clean": "rm -rf dist build",
@@ -95,6 +94,7 @@
"@babel/parser": "^8.0.4",
"ai": "^7.0.14",
"archiver": "^8.0.0",
"astro": "^7.0.0",
"playwright": "^1.59.1",
"svelte": "^5",
"zod": "^4.3.6"
+13
View File
@@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<style>
/* Light OS theme: black icon. Dark OS theme: pristine kinpaku gold,
matching the header logo (--ks-kinpaku, #ffb900) rather than the old
muddier #d8a83a. */
path { fill: #141207; }
@media (prefers-color-scheme: dark) {
path { fill: #ffb900; }
}
</style>
<path d="M7 1 L18 1 L8 31 L7 31 Q1 31 1 25 L1 7 Q1 1 7 1 Z"/>
<path d="M22 1 L25 1 Q31 1 31 7 L31 25 Q31 31 25 31 L12 31 Z"/>
</svg>

After

Width:  |  Height:  |  Size: 501 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

+8 -4
View File
@@ -1,5 +1,7 @@
@import "../../site/styles/kinpaku-tokens.css";
@import "../../site/styles/kinpaku-kit.css";
/* Vendored from the site's design system (now in the private impeccable-site
repo) so the picker stays self-contained. Update by re-copying from there. */
@import "./vendor/kinpaku-tokens.css";
@import "./vendor/kinpaku-kit.css";
/* Scoped subset of the reset imported by site/styles/main.css through
site/styles/tokens.css (lines 12-27). */
@@ -170,10 +172,12 @@ html.light .ks-button.ks-button-primary:active {
.picker-progress span {
display: block;
width: calc(var(--progress, 0) * 100%);
width: 100%;
height: 100%;
background: var(--ks-champagne);
transition: width 180ms var(--ks-ease);
transform: scaleX(var(--progress, 0));
transform-origin: left;
transition: transform 180ms var(--ks-ease);
}
#picker-form[data-current="01"] .picker-progress {
File diff suppressed because it is too large Load Diff
+205
View File
@@ -0,0 +1,205 @@
/*
* impeccable.style — Neo kinpaku tokens (page-global, single source of truth)
*
* Every brand-bearing color, surface, text color, hairline rule, type-scale
* value, and motion easing used on the site lives here. New pages should read
* these tokens via var(--ks-*) rather than hand-typing oklch values.
*
* Pages may override individual tokens locally when there's a documented
* reason (e.g. .home-kinpaku bumps --ks-rule alpha for sharper UI borders
* against bento tiles). When in doubt, do not override — match.
*
* Imported by design-system.css, home-kinpaku.css, and designing-kinpaku.css.
* If a new page-level stylesheet needs these tokens, @import this file at the
* top so the values are available at :root.
*/
:root {
/* color-scheme follows html.light / html.dark class on <html> */
/* ============================================================
Brand anchors. Two anchors carry the brand — kinpaku gold for
commitment and brand voice, verdigris patina for state and
contrast. Everything else is supporting role.
============================================================ */
/* Kinpaku gold. The rich, warm, vibrant primary. */
--ks-kinpaku: oklch(84% 0.19 80.46); /* primary accent, wordmark, icon */
--ks-kinpaku-pale: oklch(86% 0.07 84); /* hover lift, pale fills */
--ks-kinpaku-rich: oklch(77% 0.13 82); /* active CTA, severity-medium */
--ks-kinpaku-deep: oklch(61% 0.085 78); /* borders against the brand */
/* Verdigris patina. The secondary accent — state, signal, selection. */
--ks-patina: oklch(70% 0.12 188); /* secondary accent */
--ks-patina-pale: oklch(82% 0.07 188); /* hover lift on patina */
--ks-patina-deep: oklch(49% 0.08 188); /* deep oxide, dark variants */
/* Warning — vermilion. Used sparingly, only for failures and warnings. */
--ks-vermilion: oklch(58% 0.15 35);
/* ============================================================
Surfaces. Warm-black lacquer carries everything.
============================================================ */
--ks-lacquer: oklch(7% 0.006 95); /* page ground */
--ks-lacquer-deep: oklch(4% 0.004 95); /* deepest inset */
--ks-lacquer-raised: oklch(11% 0.006 95); /* raised panels, inputs */
--ks-graphite: oklch(15% 0.008 95); /* inactive surfaces */
--ks-graphite-2: oklch(19% 0.008 95); /* one step up from graphite */
/* ============================================================
Text. The whole text ramp runs near-neutral and bright so copy,
headings, and labels read crisp on lacquer instead of mushing
into the warm floor. Warmth now lives only in the gold accents
and the surfaces, not in the type. (--ks-champagne keeps its name
for compatibility but is no longer warm.)
============================================================ */
--ks-champagne: oklch(91% 0 0); /* headlines, <strong> — neutral white */
--ks-text: oklch(88% 0 0); /* body — neutral near-white */
--ks-text-muted: oklch(72% 0 0); /* captions, meta */
--ks-text-faint: oklch(62% 0 0); /* subdued */
--ks-text-mute-deep: oklch(52% 0 0); /* disabled */
/* ============================================================
Rules. The hairline is neutral so borders/dividers/labels don't
carry warmth (warmth lives in the gold accents and surfaces). For a
gold accent border, use --ks-kinpaku directly, not a readability variant.
============================================================ */
--ks-rule: oklch(78% 0 0 / 0.16);
/* ============================================================
Code. One treatment site-wide. Inline code is a neutral graphite
chip; blocks / CLI commands are a deep lacquer terminal with a
neutral hairline. Gold is only for code that is itself a command
link (--ks-code-cmd). All code rules read these — don't hand-type.
============================================================ */
--ks-code-fg: oklch(91% 0 0); /* inline code text */
--ks-code-bg: var(--ks-graphite-2); /* inline code chip */
--ks-code-radius: 3px;
--ks-code-pad: 0.3em 0.5em; /* inline code chip padding */
--ks-code-block-fg: oklch(86% 0 0); /* block / CLI text */
--ks-code-block-bg: var(--ks-lacquer-deep);
--ks-code-block-border: var(--ks-rule);
--ks-code-block-radius: 2px;
--ks-code-cmd: var(--ks-kinpaku); /* code that's a command link */
/* ============================================================
Typography — two faces.
- --ks-font-display: Alumni Sans. A weightable humanist grotesque;
the hero h1 runs at a thin hairline (100) for elegance, section
h2s at a heavier cut (300) to anchor each block.
- --ks-font-wordmark: Alumni Sans, same face as display, held at
weight 400 for the brand lockup.
- --ks-font: Albert Sans. Quiet geometric humanist, used for body
and UI text. Pairs cleanly with the Alumni Sans display without
competing.
All loaded via Base.astro's Google Fonts link.
============================================================ */
--ks-font-display: "Alumni Sans", "Albert Sans", Arial, sans-serif;
--ks-font-wordmark: "Alumni Sans", "Albert Sans", Arial, sans-serif;
--ks-font: "Albert Sans", "Avenir Next", "Helvetica Neue", Arial, system-ui, sans-serif;
--ks-mono: "SFMono-Regular", "Roboto Mono", "JetBrains Mono", Consolas, monospace;
/* ============================================================
Type scale. These are the actual sizes the homepage ships with —
new pages should read them via var(...) rather than hand-typing.
The display vs headline weight split is deliberate: the hero h1
reads thin (100) for elegance; section h2s read heavier (300) to
anchor each block. Section h2s look heavier than the h1 on purpose.
============================================================ */
/* Display — hero h1. */
--ks-type-display-size: clamp(3.4rem, 6.5vw, 5.6rem);
--ks-type-display-weight: 100;
--ks-type-display-line: 1.02;
--ks-type-display-track: -0.01em;
/* Headline — section h2. Heavier than display. */
--ks-type-headline-size: clamp(2.6rem, 4vw, 3.4rem);
--ks-type-headline-weight: 300;
--ks-type-headline-line: 1.04;
/* Title — card and panel headings (h3). */
--ks-type-title-size: 1.18rem;
--ks-type-title-weight: 500;
--ks-type-title-line: 1.35;
/* Body. */
--ks-type-body-size: 1.02rem;
--ks-type-body-line: 1.8;
/* Eyebrow — small mono labels above titles. */
--ks-type-eyebrow-size: 0.7rem;
--ks-type-eyebrow-track: 0.18em;
/* Wordmark — IMPECCABLE in the header. */
--ks-type-wordmark-size: 1.15rem;
--ks-type-wordmark-track: 0.42em;
/* Mono — code, terminal, audit lines. */
--ks-type-mono-size: 0.72rem;
--ks-type-mono-track: 0.22em;
/* ============================================================
Motion.
============================================================ */
--ks-ease: cubic-bezier(0.2, 0.8, 0.2, 1);
}
html.dark {
color-scheme: dark;
}
/* ============================================================
Light mode — pale surfaces, dark ink type, and kinpaku kept as the
real brand gold. Activated via html.light on
<html> (set by theme.js + inline FOUC guard in Base.astro).
============================================================ */
html.light {
color-scheme: light;
/* Brand gold — same fills as dark mode (logo, buttons, marks).
For decorative small text on paper, use patina. Use --ks-link-on-paper
for body-sized links that need stronger contrast. */
--ks-kinpaku: oklch(84% 0.19 80.46);
--ks-kinpaku-pale: oklch(86% 0.07 84);
--ks-kinpaku-rich: oklch(77% 0.13 82);
--ks-kinpaku-deep: oklch(61% 0.085 78);
--ks-kinpaku-ink: var(--ks-patina);
/* Text links + active nav on paper — patina, not fill gold. */
--ks-link-on-paper: var(--ks-patina-deep);
--ks-link-on-paper-hover: var(--ks-patina);
--ks-link-on-paper-line: oklch(49% 0.08 188 / 0.42);
--ks-nav-active: var(--ks-patina-deep);
/* Patina — brand values for fills; deep for text on paper. */
--ks-patina: oklch(70% 0.12 188);
--ks-patina-pale: oklch(82% 0.07 188);
--ks-patina-deep: oklch(49% 0.08 188);
--ks-vermilion: oklch(52% 0.16 35);
/* Surfaces — warm paper and cream panels. */
--ks-lacquer: oklch(97% 0.012 95);
--ks-lacquer-deep: oklch(94% 0.014 95);
--ks-lacquer-raised: oklch(99% 0.008 95);
--ks-graphite: oklch(91% 0.012 95);
--ks-graphite-2: oklch(88% 0.014 95);
/* Text — dark ink on paper. */
--ks-champagne: oklch(18% 0.02 95);
--ks-text: oklch(25% 0.018 95);
--ks-text-muted: oklch(45% 0.015 95);
--ks-text-faint: oklch(55% 0.012 95);
--ks-text-mute-deep: oklch(65% 0.01 95);
/* Rules — dark hairlines. */
--ks-rule: oklch(25% 0.02 95 / 0.12);
/* Code — light chips, dark text. */
--ks-code-fg: oklch(28% 0.02 95);
--ks-code-bg: var(--ks-graphite);
--ks-code-block-fg: oklch(30% 0.02 95);
--ks-code-block-bg: var(--ks-lacquer-raised);
--ks-code-block-border: var(--ks-rule);
--ks-code-cmd: var(--ks-link-on-paper);
}
+5 -10
View File
@@ -3,18 +3,16 @@
import { execFileSync } from 'node:child_process';
import { cp, copyFile, mkdir, rm } from 'node:fs/promises';
import path from 'node:path';
import sharp from 'sharp';
import { fileURLToPath } from 'node:url';
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const buildDir = path.join(root, 'build-picker');
const outputDir = path.join(root, 'skill/scripts/picker');
const faviconSource = path.join(root, 'site/public/favicon.svg');
// Static assets vendored under picker/assets/ (the site now lives in the
// private impeccable-site repo, so the picker carries its own copies).
const faviconSource = path.join(root, 'picker/assets/favicon.svg');
const faviconOutput = path.join(outputDir, 'favicon.svg');
const heroSource = path.join(
root,
'site/public/assets/neo-kinpaku/candidates/finalists/m-01-v2-01-light.png',
);
const heroSource = path.join(root, 'picker/assets/hero-light.jpg');
const heroOutput = path.join(outputDir, 'assets/hero-light.jpg');
await rm(buildDir, { recursive: true, force: true });
@@ -28,10 +26,7 @@ await rm(outputDir, { recursive: true, force: true });
await cp(buildDir, outputDir, { recursive: true });
await mkdir(path.dirname(heroOutput), { recursive: true });
await copyFile(faviconSource, faviconOutput);
await sharp(heroSource)
.resize({ width: 1536, withoutEnlargement: true })
.jpeg({ quality: 80, mozjpeg: true })
.toFile(heroOutput);
await copyFile(heroSource, heroOutput);
await rm(buildDir, { recursive: true, force: true });
console.log(`Built ${path.relative(root, outputDir)}/`);
+1 -1
View File
@@ -701,7 +701,7 @@ export function replacePlaceholders(content, provider, commandNames = [], allSki
* their command prefix from lib/provider.mjs, whose declaration is replaced
* here by an exact string match.
*/
export function replaceScriptProviderMarker(content, provider) {
export function replaceScriptProviderMarker(content, provider, buildProvider = provider) {
if (Buffer.isBuffer(content)) return content;
const placeholders = PROVIDER_PLACEHOLDERS[provider] || PROVIDER_PLACEHOLDERS.cursor;
const commandPrefix = placeholders.command_prefix || '/';
-1
View File
@@ -28,7 +28,6 @@ export const SUITES = {
/^picker\//,
/^scripts\/(?!benchmark-detector|build-browser-detector|build-extension)/,
/^skill\/(SKILL\.src\.md|agents\/|reference\/|scripts\/(cleanup-deprecated|comp-diff|comp-spec|build-phase|font-match|data\/font-index|concept-seed|generate-image|context|context-signals|critique-storage|design-parser|doctor|hook|impeccable-paths|is-generated|lib\/(artifact-schema|png|raster|image-metrics|font-fingerprint|font-index|hero-checks|composition-catalog|concept-catalog|provider|staleness|staleness-deep|staleness-notice|surface-briefs|target-slug|template-extensions)|picker|pin|surface-brief))/,
/^site\/(pages|content|components|layouts)\//,
/^README(\.npm)?\.md$/,
/^cli\/bin\//,
],
+1 -119
View File
@@ -2,122 +2,4 @@
`craft` is a deprecated alias for an ordinary request to make new visual work. It adds no setup, interview, checkpoint, tool, or quality behavior. Apply SKILL.md's normal routing: create missing PRODUCT.md through [init.md](init.md), then follow [new-work.md](new-work.md) for visual authority, world and surface decisions, implementation, and finish.
Before writing code, you need: PRODUCT.md loaded, register identified and the matching reference loaded, and a confirmed design direction for this task (either from `shape` or supplied by the user). PRODUCT.md is project context, not a task-specific brief.
Treat any approved visual direction (generated mock or stated reference) as a concrete contract for composition, hierarchy, density, atmosphere, signature motifs, and distinctive visual moves. Don't let mocks replace structure, copy, accessibility, or state design. But if the live result lacks the approved direction's major ingredients, the implementation is wrong.
### Gates: do not compress
Craft has **multiple user gates**, not one. When the harness has native image generation (Codex via `image_gen`), the gate sequence before code is:
1. **Shape brief confirmed** (Step 1)
2. **Direction questions answered** (codex.md Step A)
3. **Palette confirmed** (codex.md Step B)
4. **One mock direction approved or delegated** (codex.md Step D)
You must stop at every gate. **Shape confirmation alone is NOT a green light to start coding.** It is the green light to begin codex.md Step A. Compressing gates 2 through 4 because the shape brief felt complete is the dominant failure mode of this flow.
When the harness lacks native image generation, gates 2-4 collapse into the brief itself, and shape confirmation does advance straight to code.
## Step 0: Project Foundation
Before shape, before code: figure out what kind of project you're working in.
Look at the working directory. Run `ls`. Check for:
- An existing framework: `astro.config.mjs/ts`, `next.config.js/ts`, `nuxt.config.ts`, `svelte.config.js`, `vite.config.js/ts`, `package.json` with framework deps, `Cargo.toml` + Leptos/Yew, `Gemfile` + Rails. **If found, use it.** Do not start a parallel build, do not introduce a second framework, do not write to `dist/` or `build/` directly. Whatever pipeline the project has, respect it.
- An existing component library or design system: `src/components/`, `app/components/`, a `tokens.css` / `theme.ts`, an `astro.config` `integrations`. Read what's there before adding to it.
- An existing icon set: `lucide-react`, `@phosphor-icons/react`, `@iconify/*`, hand-rolled SVG sprites in `assets/icons/`. **Use what's already in the project**; don't introduce a second set.
If the directory is empty (greenfield), don't pick a framework silently. Ask the user via the AskUserQuestion tool, with sensible defaults framed by the brief:
```text
What should this be built on?
- Astro (default for content-led brand sites, landing pages, marketing surfaces)
- SvelteKit / Next.js / Nuxt (when the brief implies an app surface or significant interactivity)
- Single index.html (one-shot demo, prototype, or a deliberately framework-free experiment)
```
Default: Astro for brand briefs, the project's existing framework for product briefs. Ask once; don't re-ask mid-task.
## Step 1: Shape the Design
Run {{command_prefix}}impeccable shape, passing along whatever feature description the user provided. Shape is **required** for craft; it is what produces a confirmed direction.
Present the shape output and stop. Wait for the user to confirm, override, or course-correct before writing code.
If the user already supplied a confirmed brief or ran shape separately, use it and skip this step.
When the original prompt + PRODUCT.md already answer scope, content, and visual direction with no real ambiguity, the shape output can be **compact** (3-5 bullets stating what you're building and the visual lane, ending with one or two specific questions or "confirm or override"). The full 10-section structured brief is reserved for genuinely ambiguous, multi-screen, or stakeholder-heavy tasks. Don't pad a clear brief into a long one to look thorough; equally, don't skip the pause to look efficient.
If the harness has native image generation (Codex), a compact shape's "confirm or override" advances to **Step 3 and the codex.md flow**, not to Step 4. Phrase the closing line accordingly: "Confirm or override; once we lock direction, I'll run a couple of palette and reference questions before generating any mocks." This stops the model from reading shape confirmation as code-green.
## Step 2: Load References
Based on the design brief's "Recommended References" section, consult the relevant impeccable reference files. At minimum, always consult:
- [layout.md](layout.md) for layout, spacing, grid, container queries, optical adjustments
- [typeset.md](typeset.md) for type hierarchy, font selection, web font loading, OpenType features (Reference Material section)
Then add references based on the brief's needs:
- Complex interactions or forms? Consult [interaction-design.md](interaction-design.md)
- Animation or transitions? Consult [animate.md](animate.md) (Reference Material covers motion materials, durations, easing, perceived performance)
- Color-heavy or themed? Consult [colorize.md](colorize.md) (Reference Material covers OKLCH, palette structure, dark mode, contrast)
- Responsive requirements? Consult [adapt.md](adapt.md) (Reference Material covers breakpoints, input methods, safe areas, responsive images)
- Heavy on copy, labels, or errors? Consult [clarify.md](clarify.md) (Reference Material covers button labels, error formula, voice/tone, translation)
## Step 3: Visual Direction & Assets (Harness-Gated)
If the harness has **native image generation** (currently Codex via `image_gen`), this step is mandatory. **Stop and load [codex.md](codex.md)**. It covers palette generation, mock exploration, the approval loop, mock-fidelity inventory, and asset slicing via the `impeccable_asset_producer` subagent. Follow Steps A-F in that file, then return here for Step 4.
If the harness lacks native image generation, **state in one line that the visual-direction-by-generation step is being skipped because the harness lacks native image generation, then proceed**. The one-line announcement is required; it forces a conscious decision instead of letting the step quietly evaporate. The brief is your only visual reference. Implement directly from it, treating any named anchor references and the brief's "Design Direction" as the contract.
Whether you generated mocks or not: don't replace required imagery with generic cards, bullets, emoji, fake metrics, decorative CSS panels, or filler copy. Image-led briefs (restaurants, hotels, magazines, photography, hobbyist communities, food, travel, fashion, product) need real or sourced imagery in the build, not CSS scenery.
## Step 4: Build to Production Quality
**Precondition.** If Step 3 routed you to codex.md (native image generation available), Steps A through D in that file must be complete before any code: questions answered, palette confirmed, mocks generated, one direction approved or delegated. **Do not mention implementation, file paths, or patch plans until that's done.** A confirmed shape brief is not enough; the model that compressed those gates is the model that already failed this flow.
Implement the feature following the design brief. Build in passes so structure, visual system, states, motion/media, and responsive behavior each get deliberate attention. The list below is the definition of done, not inspiration.
### Production bar
- **Real content.** No placeholder copy, placeholder images, dead links, fake controls, or unused scaffold at presentation time.
- **Preserve the approved mock's major ingredients.** Missing hero objects, world/product imagery, section structure, CTA/nav treatment, or distinctive motifs are blocking defects unless the user accepted the change.
- **Semantic first.** Real headings, landmarks, labels, form associations, button/link semantics, accessible names, state announcements where needed.
- **Deliberate spacing and alignment.** No default gaps, arbitrary margins, unbalanced whitespace, or accidental optical misalignment.
- **Intentional typography.** Chosen loading strategy, clear hierarchy, readable measure, stable line breaks, no overflow at any width.
- **Realistic state coverage.** Default, hover, focus-visible, active, disabled, loading, error, success, empty, overflow, long/short text, first-run.
- **Finished interaction quality.** Keyboard paths, touch targets, feedback timing, scroll behavior, state transitions, no hover-only functionality.
- **Coherent icon set.** Use the project's established set; otherwise pick one library or use accessible text. Don't mix.
- **Respect the build pipeline.** Edit source files and run the project's build (`npm run build` or equivalent). Don't write to `build/` / `dist/` / `.next/` with `cat`, heredoc, or Bash redirects; that skips asset hashing, image optimization, code splitting, and CSS extraction, and produces output the dev server won't serve.
- **Verify image URLs before referencing them.** Use image-search MCP or web-fetch when available; guessed photo IDs ship as broken-image placeholders. Without verification, prefer fewer images you're confident about.
- **Optimized imagery and media.** Correct dimensions, useful alt text, lazy loading below the fold, modern formats when practical, responsive `srcset`/`picture` for raster, no project-referenced asset left outside the workspace.
- **Premium motion.** Use atmospheric blur, filter, mask, shadow, reveal when they improve the experience. Avoid casual layout-property animation, bound expensive effects, verify smoothness in-browser, respect reduced motion, and avoid choreography that blocks task completion.
- **Maintainable.** Reusable local patterns, clear component boundaries, project conventions. No rasterized UI text or one-off hacks when a local pattern exists.
- **Technically clean.** Production build passes, no console errors, no avoidable layout shift, no needless dependencies, no broken asset paths.
- **Ask when uncertain.** If a discovery materially changes the brief or approved direction, stop and ask. Don't guess.
## Step 5: Iterate Visually
Look at what you built like a designer would. Your eyes are whatever the harness gives you: a connected browser, a screenshotting tool, Playwright, or asking the user. Use them for responsive testing (mobile, tablet, desktop minimum) and general visual validation.
If your tool returns a file path, read the PNG back into the conversation. A screenshot you didn't read doesn't count.
For long-form brand surfaces, inspect major sections individually. Thumbnails hide spacing, clipping, and cascade defects.
After the first pass, write an honest critique against the brief, the approved mock's major ingredients (hero silhouette, motifs, imagery, nav/CTA, density), and impeccable's DON'Ts. Patch material defects and re-inspect. **Don't invent defects to demonstrate iteration.** A confident "first pass clean, shipping" beats a fake fix.
Actively check: responsive behavior (composes, not shrinks), every state (empty / error / loading / edge), craft details (spacing, alignment, hierarchy, contrast, motion timing, focus), performance basics. The exit bar: defensible in a high-end studio review.
Detector or QA output is defect evidence only; never proof the work is finished.
## Step 6: Present
Present the result to the user:
- Show the feature in its primary state
- Summarize the browser/viewports checked and the most important fixes made after inspection
- Walk through the key states (empty, error, responsive)
- Explain design decisions that connect back to the design brief and, when used, the chosen north-star mock. Include any accepted deviations from the mock; do not hide unimplemented mock ingredients.
- Note any remaining limitations or follow-up risks honestly
- Ask: "What's working? What isn't?"
Do not tell users they need to invoke `craft`. Natural requests such as “build this feature,” “make a landing page,” or “redesign this screen” use the same flow.
+8 -5
View File
@@ -75,7 +75,7 @@ If a `DESIGN.md` already exists, **do not silently overwrite it**. Show the user
- **Scan mode** (default): the project has design tokens, components, or rendered output. Extract, then confirm descriptive language. Use when there's code to analyze.
- **Seed mode**: the project is pre-implementation (fresh init, nothing built yet). Gather any existing brand assets, interview for five high-level answers, optionally generate visual cues for the palette pick, write a minimal DESIGN.md marked `<!-- SEED -->`. Re-run in scan mode once there's code.
Decide by scanning first (Scan mode Step 1). If the scan finds no tokens, no component files, and no rendered site, offer seed mode; don't silently switch. `/impeccable document --seed` requests new-work's world workshop, but it does not authorize replacing coherent code: when an incumbent system exists, offer scan mode or route an explicit identity-replacement request through new-work.
Decide by scanning first (Scan mode Step 1). If the scan finds no tokens, no component files, and no rendered site, offer seed mode; don't silently switch. `/impeccable document --seed` forces seed mode on a pre-implementation project, but it does not authorize replacing coherent code: when an incumbent system exists, offer scan mode or route an explicit identity-replacement request through new-work.
## Scan mode (approach C: auto-extract, then confirm descriptive language)
@@ -349,7 +349,9 @@ Your own write is the freshest source; subsequent commands in this session don't
## Seed mode
For projects with no visual system to extract yet. Produces a user-chosen visual-world scaffold, not a fabricated token spec.
For projects with no visual system to extract yet. Produces a minimal, user-chosen scaffold, not a fabricated token spec.
PRODUCT.md is the prerequisite. If it is missing, load [init.md](init.md) and complete its product interview first. Do not create a visual identity without durable product context.
### Step 1: Confirm seed mode and ask for assets
@@ -412,7 +414,7 @@ When generation is available, **stop and load [visual-cues.md](visual-cues.md)**
### Step 5: Write seed DESIGN.md
Use the six-section spec from Scan mode. Populate what the interview, the assets, and any cue pick answer; leave the rest as honest placeholders. The seed is a scaffold, not a fabricated spec.
Use the canonical section order from Scan mode. Populate what the interview, the assets, and any cue pick answer; leave the rest as honest placeholders. The seed is a scaffold, not a fabricated spec.
Lead the file with:
@@ -425,9 +427,10 @@ Per-section guidance in seed mode:
- **Overview**: Creative North Star and philosophy phrased from the answers (color strategy + motion energy + references). Reference the user's anti-reference directly.
- **Colors**: Color strategy as a Named Rule (e.g. *"The Drenched Rule. The surface IS the color."*). Hue family or anchor reference. Colors sampled from a provided logo, or from a cue image the user picked in Step 4, are real; include them with exact values and note the source. Everything else stays `[to be resolved during implementation]`; those sampled anchors are the only hex a seed may carry.
- **Typography**: the direction the user picked (e.g. "Serif display + sans body"). No font names yet: `[font pairing to be chosen at implementation]`.
- **Elevation**: inferred from motion energy. Restrained/Responsive → flat by default; Choreographed → layered. One sentence.
- **Layout** and **Shapes**: omit unless an asset or answer established a spatial or form preference; do not invent grids or corner language pre-implementation.
- **Elevation & Depth**: inferred from motion energy. Restrained/Responsive → flat by default; Choreographed → layered. One sentence.
- **Components**: omit entirely; no components exist yet.
- **Do's and Don'ts**: record the durable guardrails confirmed during the world choice, not task-local refusals.
- **Do's and Don'ts**: carry PRODUCT.md's anti-references directly plus the anti-reference named in Q5.
Seed mode writes a minimal frontmatter with `name` and `description` only; no colors, typography, rounded, spacing, or components yet. Real tokens land on the next Scan-mode run. Skip the `.impeccable/design.json` sidecar in seed mode for the same reason: nothing to render.
+1 -104
View File
@@ -56,107 +56,4 @@ Use three to five bullets when the task is settled; use the full structure only
Present the brief for explicit confirmation or one correction round, then stop: shape never writes code or a direction contract.
### Constraints
- Are there technical constraints? (Framework, performance budget, browser support)
- Are there content constraints? (Localization, dynamic text length, user-generated content)
- Mobile/responsive requirements?
- Accessibility requirements beyond WCAG AA?
### Anti-Goals
- What should this NOT be? What would be a wrong direction?
- What's the biggest risk of getting this wrong?
## Phase 1.5: Visual Direction Probe (Capability-Gated)
After the discovery interview, generate a small set of visual direction probes **before** writing the final brief when all of these are true:
- The work is **net-new** or directionally ambiguous enough that visual exploration will clarify the brief.
- The requested fidelity is **mid-fi, high-fi, or production-ready**. Skip for sketch-only planning.
- The current harness gives you native image generation (Codex's `image_gen`, an equivalent MCP tool, or similar). Don't ask the user to install APIs or tooling.
When those conditions are met, this step is mandatory. If image generation isn't natively available, do not ask the user to install APIs or tooling. State in one line that the image step is skipped because the harness lacks native image generation, then proceed. The one-line announcement is required, not optional; it forces a conscious decision instead of letting the step quietly evaporate.
Use probes to explore visual lanes, not to replace the brief.
Do not skip probes because the final UI will be semantic, editable, code-native, responsive, or accessible. Those are implementation requirements, not reasons to avoid visual exploration.
### What to generate
Generate **2 to 4** distinct direction probes based on the discovery answers, especially:
- Color strategy
- Theme scene sentence
- Named anchor references
- Scope and fidelity
The probes should differ in primary visual direction (hierarchy, topology, density, typographic voice, or color strategy), not just palette tweaks.
### How to use the probes
- Treat them as **direction tests**, not final designs.
- Use them to pressure-test whether the brief is pointing at the right lane.
- Ask the user which direction feels closest, what feels off, and what should carry forward.
- If the probes reveal a mismatch, revise the brief inputs before finalizing the brief.
### Important limits
- Do **not** skip discovery because image generation is available.
- Do **not** treat generated imagery as final UX specification, final copy, or final accessibility behavior.
- Do **not** use this step for minor refinements of existing work. It's for shaping a new surface or clarifying a big directional choice.
If image generation isn't natively available, announce the skip in one line and proceed to the design brief.
## Phase 2: Design Brief
After the interview and any required probes, present a brief and **end your response**. The user must confirm before any implementation runs. Do not present a brief and then continue to code in the same response, even if the brief feels obvious to you. The user's confirmation is the gate.
**Choose the brief shape based on how clear the answers are:**
- **Compact form (3-5 bullets)** when discovery was crisp and the original prompt + PRODUCT.md already pinned scope, content, and direction. State what you're building, the visual lane, and end with one or two specific questions or a clear "confirm or override?" prompt. This is the default for typical craft requests with a clear prompt.
- **Full structured form (sections below)** when the task is genuinely ambiguous, multi-screen, or when the user asked for shape as a standalone step. Use this when the discipline of structure earns its weight.
Don't pad a clear brief into a long one to look thorough. A 70-line brief restating answers the user just gave is noise, not rigor. Equally, don't skip the confirmation pause to look efficient: the pause is the point.
Present the brief, then **stop and wait for explicit confirmation**. You are not the judge of whether the user already approved. Even when the brief feels obviously right, ask once and wait. The pause is what separates shape from premature implementation.
### Brief Structure
**1. Feature Summary** (2-3 sentences)
What this is, who it's for, what it needs to accomplish.
**2. Primary User Action**
The single most important thing a user should do or understand here.
**3. Design Direction**
Color strategy (Restrained / Committed / Full palette / Drenched) + the theme scene sentence + 23 named anchor references. Reference PRODUCT.md and DESIGN.md where they already answer, and note any per-surface overrides.
If you ran the Visual Direction Probe step, name which probe direction won and what changed in the brief because of it.
**4. Scope**
Fidelity, breadth, interactivity, and time intent from the Scope section of the interview. Task-scoped; these don't persist beyond the brief.
**5. Layout Strategy**
High-level spatial approach: what gets emphasis, what's secondary, how information flows. Describe the visual hierarchy and rhythm, not specific CSS.
**6. Key States**
List every state the feature needs: default, empty, loading, error, success, edge cases. For each, note what the user needs to see and feel.
**7. Interaction Model**
How users interact with this feature. What happens on click, hover, scroll? What feedback do they get? What's the flow from entry to completion?
**8. Content Requirements**
What copy, labels, empty state messages, error messages, and microcopy are needed. Note any dynamic content and its realistic ranges. For image-led surfaces, also list the required image/media roles and their likely source (project asset, generated raster, semantic SVG/CSS, canvas/WebGL, icon library, or accepted omission).
**9. Recommended References**
Based on the brief, list which impeccable reference files would be most valuable during implementation (e.g., layout.md for complex layouts, animate.md for animated features, interaction-design.md for form-heavy features, typeset.md for typography-driven pages, colorize.md for color-led brands).
**10. Open Questions**
Anything genuinely unresolved. Don't list "open questions" you've already recommended a default for; assert the default and move on. If you'd write `Recommend: X` next to a question, just decide X.
---
{{ask_instruction}} Ask for explicit confirmation of the brief before finishing.
If the user disagrees with any part, revisit the relevant discovery questions. A shape run is incomplete until the user confirms direction.
Once confirmed, the brief is complete. The user can now hand it to {{command_prefix}}impeccable, or use it to guide any other implementation approach. (If the user wants the full discovery-then-build flow in one step, they should use {{command_prefix}}impeccable craft instead, which runs this command internally.)
When no human or structured answer mechanism exists, mark assumptions plainly, return the brief, and stop.
+116 -115
View File
@@ -420,8 +420,6 @@ const SEEDS = [
strategy: "Pure white surface so the rose-pink primary carries all the brand warmth, paired with a near-black ink and a desaturated mauve accent for editorial restraint." },
];
export { SEEDS };
function parseArgs(argv) {
const args = { id: null, from: null };
for (let i = 0; i < argv.length; i++) {
@@ -497,139 +495,142 @@ function hueWord(H) {
// ---------------------------------------------------------------
// The picker server imports SEEDS to serve /palettes.json; the CLI tail
// below only runs when this file is the entry point, so importing it has
// no side effects.
export { SEEDS };
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
const args = parseArgs(process.argv.slice(2));
const seed = pickSeed(SEEDS, args);
const [L, C, H] = seed.oklch;
const args = parseArgs(process.argv.slice(2));
const seed = pickSeed(SEEDS, args);
const [L, C, H] = seed.oklch;
// The mood + strategy on each seed were derived by the model that
// originally judged it. We surface them as *hints*, not commands —
// the brief should still drive what the seed becomes.
const moodHint = seed.mood ? ` (one read: "${seed.mood}")` : '';
const strategyHint = seed.strategy ? `\n - one example strategy: ${seed.strategy}` : '';
// The mood + strategy on each seed were derived by the model that
// originally judged it. We surface them as *hints*, not commands —
// the brief should still drive what the seed becomes.
const moodHint = seed.mood ? ` (one read: "${seed.mood}")` : '';
const strategyHint = seed.strategy ? `\n - one example strategy: ${seed.strategy}` : '';
// ---------------------------------------------------------------
// Fat tool-exit response — what the model sees on stdout.
// ---------------------------------------------------------------
// ---------------------------------------------------------------
// Fat tool-exit response — what the model sees on stdout.
// ---------------------------------------------------------------
process.stdout.write(`BRAND SEED · ${seed.id}
process.stdout.write(`BRAND SEED · ${seed.id}
Seed color (anchor for your primary brand color):
${fmtOklch(seed.oklch)} ${hueWord(H)}${moodHint}
Seed color (anchor for your primary brand color):
${fmtOklch(seed.oklch)} ${hueWord(H)}${moodHint}
This is the brand's anchor a single beautiful color. Compose the rest of
the palette around it using YOUR judgment, the brief (PRODUCT.md /
DESIGN.md / the user's prompt), and the color-strategy guidance already in
SKILL.md.
This is the brand's anchor a single beautiful color. Compose the rest of
the palette around it using YOUR judgment, the brief (PRODUCT.md /
DESIGN.md / the user's prompt), and the color-strategy guidance already in
SKILL.md.
How to use:
How to use:
1. Read the brief. Write one specific phrase describing the mood this
product calls for. Be granular. Good: "1970s travel poster sun-baked
warmth, considered", "midnight jazz club smoky brass, saxophone
light", "Scandinavian winter morning quiet light through frost". Bad:
"modern and clean", "warm and inviting". The first lets you compose; the
second is generic and will produce generic palettes.
1. Read the brief. Write one specific phrase describing the mood this
product calls for. Be granular. Good: "1970s travel poster sun-baked
warmth, considered", "midnight jazz club smoky brass, saxophone
light", "Scandinavian winter morning quiet light through frost". Bad:
"modern and clean", "warm and inviting". The first lets you compose; the
second is generic and will produce generic palettes.
2. The seed's hue (${H.toFixed(0)}°) anchors your primary brand color. You
choose L and C to match the mood. The same hue can be deep-and-velvet,
bright-and-confident, or pale-and-faded pick the one the mood demands.
Primary's hue should stay within ±10° of the seed.${strategyHint}
2. The seed's hue (${H.toFixed(0)}°) anchors your primary brand color. You
choose L and C to match the mood. The same hue can be deep-and-velvet,
bright-and-confident, or pale-and-faded pick the one the mood demands.
Primary's hue should stay within ±10° of the seed.${strategyHint}
3. Now compose the full palette in OKLCH (5 more roles):
bg the most important architectural choice.
CORE PRINCIPLE: the mood lives in the BRAND COLORS
(primary + accent) and typography, NOT in the surface.
Stripe is warm its purple does that, bg is pure
white. Linear is cool its blue does that, bg is
pure. Notion is warm its accents do that, bg is
near-pure-white. Putting warmth in BOTH primary AND
bg is the AI cliché.
3. Now compose the full palette in OKLCH (5 more roles):
bg the most important architectural choice.
CORE PRINCIPLE: the mood lives in the BRAND COLORS
(primary + accent) and typography, NOT in the surface.
A warm brand puts the warmth in its primary against a
pure surface. Putting warmth in BOTH primary AND bg is
the AI cliché.
DEFAULT A PURE white: exactly oklch(1.000 0.000 0).
Not 0.99, not chroma 0.002. Stripe / Notion / Apple
use literal #ffffff. Don't add hidden warmth.
Refs: Stripe, Notion, Linear (light), Apple.com,
Vercel docs, Figma marketing, Loom, Substack.
DEFAULT A PURE white: exactly oklch(1.000 0.000 0).
Not 0.99, not chroma 0.002. The most confident
brands in every field fashion houses, galleries,
publishers, tool makers use literal #ffffff.
Don't add hidden warmth.
DEFAULT B PURE black/near-black: L 0.04-0.12,
chroma exactly 0.000. No hue tint. Vercel is
roughly oklch(0.08 0 0). Pick L for mood; C is 0.
Refs: Vercel, A24, Acne, Apple dark, MUBI.
DEFAULT B PURE black/near-black: L 0.04-0.12,
chroma exactly 0.000. No hue tint. Pick L for the
mood (cinema dark, gallery dark, instrument-panel
dark); C stays 0.
ALT 2 TINTED: chroma 0.015-0.05.
Use ONLY when:
(a) the mood is EXPLICITLY environmental the surface
IS part of the brand (1920s lacquered interior,
leather library, ceramic studio, hotel lobby), or
(b) the seed itself is desaturated (chroma < 0.10) and
needs a tinted surface to read as a brand.
NOT for "feels warm" / "modern + warm" / "moody". If
your mood says "warm" but doesn't name a specific
environment, use PURE white and let primary carry
the warmth.
ALT 2 TINTED: chroma 0.015-0.05.
Use ONLY when:
(a) the mood is EXPLICITLY environmental the surface
IS part of the brand (1920s lacquered interior,
leather library, ceramic studio, hotel lobby), or
(b) the seed itself is desaturated (chroma < 0.10) and
needs a tinted surface to read as a brand.
NOT for "feels warm" / "modern + warm" / "moody". If
your mood says "warm" but doesn't name a specific
environment, use PURE white and let primary carry
the warmth.
HEURISTIC: if seed chroma > 0.10 AND mood is product-
focused (not environment-focused), it's almost always
PURE white. Target distribution across many palettes:
~50% pure white, ~25% pure black, ~25% tinted.
surface bg pulled slightly toward ink (10-15% mix). Same hue
family as bg. Used for cards, panels, sections.
ink body text color. Must reach 7:1 contrast vs bg.
Can carry the brand hue at low chroma in light mode
(slight warmth or coolness toward the brand).
accent a SECOND brand color, distinct from primary in BOTH
hue AND lightness. Picked to complement the mood (not
default-complementary across the wheel). Used for
badges, status pills, links, accent rules.
muted secondary text. Ink pulled 40% toward bg, keeping ink's
hue. Must reach 3.5:1 contrast vs bg.
HEURISTIC: if the seed's chroma > 0.10 and the mood
doesn't name a specific environment, it's almost
always PURE white. Target distribution across many
palettes: ~50% pure white, ~25% pure black, ~25%
tinted.
surface bg pulled slightly toward ink (10-15% mix). Same hue
family as bg. Used for cards, panels, sections.
ink body text color. Must reach 7:1 contrast vs bg.
Can carry the brand hue at low chroma in light mode
(slight warmth or coolness toward the brand).
accent a SECOND brand color, distinct from primary in BOTH
hue AND lightness. Picked to complement the mood (not
default-complementary across the wheel). Used for
badges, status pills, links, accent rules.
muted secondary text. Ink pulled 40% toward bg, keeping ink's
hue. Must reach 3.5:1 contrast vs bg.
4. Pick a color STRATEGY (the four steps from SKILL.md):
Restrained: tinted neutrals + accent 10% product default
Committed: one saturated color carries 30-60% identity-driven
Full palette: 3-4 named roles each used deliberately brand work
Drenched: the surface IS the color campaign, hero, statement
The brief picks the strategy. A startup dashboard a perfume brand.
4. Pick a color STRATEGY (the four steps from SKILL.md):
Restrained: tinted neutrals + accent 10% product default
Committed: one saturated color carries 30-60% identity-driven
Full palette: 3-4 named roles each used deliberately brand work
Drenched: the surface IS the color campaign, hero, statement
The brief picks the strategy. A startup dashboard a perfume brand.
Hard rules (already in SKILL.md, recapped because the seed step is where
they actually bite):
Hard rules (already in SKILL.md, recapped because the seed step is where
they actually bite):
- OKLCH only never hex. Never #RRGGBB.
- ink-vs-bg WCAG contrast 7 (body text must be readable)
- primary chroma 0.23 (above this, primary glows perceptually and
no text on it is readable acid-bright is a UI failure)
- if primary L > 0.78, primary chroma 0.18 (the fluorescent zone)
- primary-vs-accent contrast 1.7 (they must be visually distinct,
not two variants of the same hue at similar lightness)
- accent must carry readable text on a filled badge/pill: EITHER
saturated (chroma 0.10) OR clearly light (L 0.85) OR clearly
dark (L 0.30). Never a muddy mid-tone (L 0.45-0.72 + chroma < 0.10)
taupe/mushroom/dusty-grey accents read as weak and can't hold text
either way. Saturate it or push its lightness to a clear light/dark.
- avoid the saturated AI attractor zones: claude-beige (warm-cream bg
+ dusty brown primary), forest-green-on-cream, AI-purple-on-white,
navy-cream-with-orange-accent
- OKLCH only never hex. Never #RRGGBB.
- ink-vs-bg WCAG contrast 7 (body text must be readable)
- primary chroma 0.23 (above this, primary glows perceptually and
no text on it is readable acid-bright is a UI failure)
- if primary L > 0.78, primary chroma 0.18 (the fluorescent zone)
- primary-vs-accent contrast 1.7 (they must be visually distinct,
not two variants of the same hue at similar lightness)
- accent must carry readable text on a filled badge/pill: EITHER
saturated (chroma 0.10) OR clearly light (L 0.85) OR clearly
dark (L 0.30). Never a muddy mid-tone (L 0.45-0.72 + chroma < 0.10)
taupe/mushroom/dusty-grey accents read as weak and can't hold text
either way. Saturate it or push its lightness to a clear light/dark.
- avoid the saturated AI attractor zones: claude-beige (warm-cream bg
+ dusty brown primary), forest-green-on-cream, AI-purple-on-white,
navy-cream-with-orange-accent
TEXT-ON-COLOR FILLS pick by perceptual contrast, not just WCAG. The
rule applies to ANY element where text sits on a saturated color fill:
primary buttons, accent buttons, badges, status pills, tag highlights,
filled callouts. Don't only think "primary button" apply consistently.
TEXT-ON-COLOR FILLS pick by perceptual contrast, not just WCAG. The
rule applies to ANY element where text sits on a saturated color fill:
primary buttons, accent buttons, badges, status pills, tag highlights,
filled callouts. Don't only think "primary button" apply consistently.
For any saturated mid-luminance color (L between 0.42 and 0.78, chroma
0.08), use WHITE text (or near-white from your bg), not dark text even
if WCAG says dark technically passes. The Helmholtz-Kohlrausch effect
makes saturated colors appear brighter than their luminance suggests,
and dark text on a warm-or-cool-saturated fill reads as muddy.
For any saturated mid-luminance color (L between 0.42 and 0.78, chroma
0.08), use WHITE text (or near-white from your bg), not dark text even
if WCAG says dark technically passes. The Helmholtz-Kohlrausch effect
makes saturated colors appear brighter than their luminance suggests,
and dark text on a warm-or-cool-saturated fill reads as muddy.
Convention: Stripe orange CTAs, McDonald's red, every fintech orange
button, Vercel's filled badges, Linear's status pills all use white
text on saturated bg fills.
Convention: saturated action fills in the wild, from fast-food reds to
status pills to filled badges, near-universally carry white text.
Dark text is correct only on PALE fills (L > 0.85) or PURE-NEUTRAL fills
(chroma near 0). Everything else: white text.
Dark text is correct only on PALE fills (L > 0.85) or PURE-NEUTRAL fills
(chroma near 0). Everything else: white text.
Return your composed palette in CSS custom properties using OKLCH, then
build with it. The seed is the start, not the recipe.
`);
Return your composed palette in CSS custom properties using OKLCH, then
build with it. The seed is the start, not the recipe.
`);
}
+3 -2
View File
@@ -10,7 +10,7 @@ import { readFile, mkdir, stat, writeFile } from 'node:fs/promises';
import net from 'node:net';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { extractRegister } from './context.mjs';
import { extractSectionValue } from './context.mjs';
import { SEEDS } from './palette.mjs';
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
const pickerDir = path.join(scriptDir, 'picker');
@@ -173,7 +173,8 @@ if (options.help) {
let register = 'brand';
try {
const product = await readFile(path.resolve(process.cwd(), 'PRODUCT.md'), 'utf8');
register = extractRegister(product) || register;
const value = (extractSectionValue(product, 'Register') || '').toLowerCase();
if (value === 'brand' || value === 'product') register = value;
} catch {
// A missing or unreadable PRODUCT.md keeps the brand preview default.
}