@shortwind/cli 0.1.0-beta.0 → 0.1.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +0,0 @@
1
- {"version":3,"file":"bench-a_9WmuOE.js","names":[],"sources":["../src/fingerprint.ts","../src/detect.ts","../src/registry-source.ts","../src/lockfile.ts","../src/init.ts","../src/project.ts","../src/commands/add.ts","../src/commands/remove.ts","../src/commands/preset.ts","../src/commands/ls.ts","../src/commands/build.ts","../src/commands/dev.ts","../src/commands/upgrade.ts","../src/commands/verify.ts","../src/bench-corpus/default-recipes.ts","../src/bench-corpus/corpus.ts","../src/commands/lint.ts","../src/commands/bench.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\n\n// Accept either the canonical short form or the legacy em-dash trailer\n// `— DO NOT EDIT THIS LINE`. The two-hyphen ASCII variant (`-- DO NOT…`) is\n// rejected on purpose: writeFamily has never emitted it, so accepting it would\n// let hand-edited files silently round-trip into the canonical form.\nconst HEADER_PATTERN = /^\\/\\*\\s*shortwind:\\s+(\\S+)@(\\S+)\\s+sha:([^\\s*]+)(?:\\s+—\\s+DO NOT EDIT THIS LINE)?\\s*\\*\\/\\s*$/;\n\nexport type RecipeHeader = {\n family: string;\n version: string;\n sha: string;\n};\n\nexport function extractHeader(source: string): RecipeHeader | null {\n const eol = source.indexOf(\"\\n\");\n const firstLine = (eol === -1 ? source : source.slice(0, eol)).replace(/\\r$/, \"\");\n const m = firstLine.match(HEADER_PATTERN);\n if (!m) return null;\n // All three capture groups are guaranteed non-empty when the regex matches\n // (the pattern uses `\\S+`/`[^\\s*]+`); the `!` asserts what the regex shape\n // already requires so we don't paper over an impossible-null with \"\".\n return { family: m[1]!, version: m[2]!, sha: m[3]! };\n}\n\nexport function bodyAfterHeader(source: string): string {\n const eol = source.indexOf(\"\\n\");\n return eol === -1 ? \"\" : source.slice(eol + 1);\n}\n\nexport function normalizeBody(body: string): string {\n const lf = body.replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\");\n return lf\n .split(\"\\n\")\n .map((line) => line.replace(/[\\t ]+$/, \"\"))\n .join(\"\\n\");\n}\n\nexport function computeBodySha(source: string): string {\n const normalized = normalizeBody(bodyAfterHeader(source));\n return createHash(\"sha256\").update(normalized).digest(\"hex\").slice(0, 6);\n}\n\nexport function buildHeaderLine(family: string, version: string, sha: string): string {\n return `/* shortwind: ${family}@${version} sha:${sha} — DO NOT EDIT THIS LINE */`;\n}\n\nexport function rewriteHeaderSha(source: string, sha: string): string {\n const header = extractHeader(source);\n if (!header) return source;\n const newHeader = buildHeaderLine(header.family, header.version, sha);\n const eol = source.indexOf(\"\\n\");\n if (eol === -1) return newHeader;\n return newHeader + source.slice(eol);\n}\n\nexport function sealRecipeFile(source: string, family: string, version: string): string {\n const sha = computeBodySha(source);\n const header = buildHeaderLine(family, version, sha);\n const eol = source.indexOf(\"\\n\");\n const rest = eol === -1 ? \"\" : source.slice(eol);\n return header + rest;\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport path from \"node:path\";\n\nexport type PackageManager = \"npm\" | \"pnpm\" | \"yarn\" | \"bun\";\nexport type Bundler = \"vite\" | \"next\" | \"astro\" | \"unknown\";\nexport type Framework = \"react\" | \"vue\" | \"svelte\" | \"astro\" | \"plain\";\n\nexport type ProjectShape = {\n packageManager: PackageManager;\n tailwindVersion: string | null;\n tailwindMajor: 3 | 4 | null;\n bundler: Bundler;\n framework: Framework;\n hasPackageJson: boolean;\n};\n\nexport function detectProject(cwd: string): ProjectShape {\n const pkgPath = path.join(cwd, \"package.json\");\n const hasPackageJson = existsSync(pkgPath);\n const pkg: {\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n packageManager?: string;\n } = hasPackageJson\n ? (JSON.parse(readFileSync(pkgPath, \"utf8\")) as never)\n : { dependencies: {}, devDependencies: {} };\n\n const deps = { ...(pkg.dependencies ?? {}), ...(pkg.devDependencies ?? {}) };\n\n const packageManager = detectPackageManager(cwd, pkg.packageManager);\n const tailwindVersion = deps[\"tailwindcss\"] ?? null;\n const tailwindMajor = parseMajor(tailwindVersion);\n const bundler = detectBundler(deps);\n const framework = detectFramework(deps);\n\n return {\n packageManager,\n tailwindVersion,\n tailwindMajor,\n bundler,\n framework,\n hasPackageJson,\n };\n}\n\nfunction detectPackageManager(cwd: string, declared: string | undefined): PackageManager {\n if (declared) {\n const name = declared.split(\"@\")[0];\n if (name === \"pnpm\" || name === \"yarn\" || name === \"bun\" || name === \"npm\") return name;\n }\n if (existsSync(path.join(cwd, \"pnpm-lock.yaml\"))) return \"pnpm\";\n if (existsSync(path.join(cwd, \"yarn.lock\"))) return \"yarn\";\n if (existsSync(path.join(cwd, \"bun.lockb\"))) return \"bun\";\n if (existsSync(path.join(cwd, \"package-lock.json\"))) return \"npm\";\n return \"npm\";\n}\n\nfunction parseMajor(version: string | null): 3 | 4 | null {\n if (!version) return null;\n const m = version.match(/(\\d+)/);\n if (!m) return null;\n const n = Number(m[1]);\n if (n === 3) return 3;\n if (n === 4) return 4;\n return null;\n}\n\nfunction detectBundler(deps: Record<string, string>): Bundler {\n if (deps[\"next\"]) return \"next\";\n if (deps[\"astro\"]) return \"astro\";\n if (deps[\"vite\"] || deps[\"@vitejs/plugin-react\"] || deps[\"@vitejs/plugin-vue\"]) return \"vite\";\n return \"unknown\";\n}\n\nfunction detectFramework(deps: Record<string, string>): Framework {\n if (deps[\"astro\"]) return \"astro\";\n if (deps[\"react\"] || deps[\"next\"]) return \"react\";\n if (deps[\"vue\"]) return \"vue\";\n if (deps[\"svelte\"] || deps[\"@sveltejs/kit\"]) return \"svelte\";\n return \"plain\";\n}\n","import { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nexport type Presets = Record<string, string[] | \"*\">;\n\nexport type RegistrySource = {\n origin: string;\n loadPresets: () => Promise<Presets>;\n loadFamily: (family: string) => Promise<string>;\n listAllFamilies: () => Promise<string[]>;\n};\n\n// Family names are surfaced from network/file inputs and are interpolated\n// into filesystem and HTTP paths; restrict to a safe alphabet to refuse\n// \"../etc\", \"foo/bar\", whitespace, and other traversal/injection attempts.\nconst FAMILY_RE = /^[a-z0-9][a-z0-9-]{0,63}$/i;\n\nexport function assertValidFamilyName(family: string): void {\n if (!FAMILY_RE.test(family)) {\n throw new Error(\n `invalid family name: ${JSON.stringify(family)} (must match ${FAMILY_RE})`,\n );\n }\n}\n\nexport function createRegistrySource(origin: string): RegistrySource {\n if (origin.startsWith(\"http://\") || origin.startsWith(\"https://\")) {\n return httpSource(origin);\n }\n return fileSource(origin);\n}\n\nfunction fileSource(origin: string): RegistrySource {\n const root = origin.startsWith(\"file://\") ? fileURLToPath(origin) : origin;\n return {\n origin,\n async loadPresets() {\n const body = await readFile(path.join(root, \"presets.json\"), \"utf8\");\n return JSON.parse(body) as Presets;\n },\n async loadFamily(family) {\n assertValidFamilyName(family);\n return readFile(path.join(root, \"recipes\", `${family}.css`), \"utf8\");\n },\n async listAllFamilies() {\n const { readdir } = await import(\"node:fs/promises\");\n const files = await readdir(path.join(root, \"recipes\"));\n return files\n .filter((f) => f.endsWith(\".css\"))\n .map((f) => f.replace(/\\.css$/, \"\"))\n .filter((name) => FAMILY_RE.test(name))\n .sort();\n },\n };\n}\n\nfunction httpSource(origin: string): RegistrySource {\n const base = origin.replace(/\\/+$/, \"\");\n return {\n origin,\n async loadPresets() {\n const res = await fetch(`${base}/presets.json`);\n if (!res.ok) throw new Error(`presets.json: ${res.status} ${res.statusText}`);\n return (await res.json()) as Presets;\n },\n async loadFamily(family) {\n assertValidFamilyName(family);\n const res = await fetch(`${base}/recipes/${family}.css`);\n if (!res.ok) throw new Error(`${family}.css: ${res.status} ${res.statusText}`);\n return res.text();\n },\n async listAllFamilies() {\n const res = await fetch(`${base}/index.json`);\n if (!res.ok) throw new Error(`index.json: ${res.status} ${res.statusText}`);\n const body = (await res.json()) as { families: string[] };\n return body.families.filter((name) => FAMILY_RE.test(name));\n },\n };\n}\n\nexport function resolvePresetFamilies(\n preset: string,\n presets: Presets,\n allFamilies: string[],\n): string[] {\n if (preset === \"none\") return [];\n const entry = presets[preset];\n if (entry === undefined) {\n throw new Error(\n `Unknown preset '${preset}'. Available: ${Object.keys(presets).join(\", \")}`,\n );\n }\n if (entry === \"*\") return allFamilies;\n return entry;\n}\n","import { existsSync } from \"node:fs\";\nimport { readFile, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nexport type LockEntry = { version: string; sha: string };\nexport type Lockfile = {\n version: number;\n registry: string;\n families: Record<string, LockEntry>;\n};\n\nexport const LOCK_FILENAME = \".shortwind-lock.json\";\nexport const LOCK_VERSION = 1;\n\nexport function lockPath(recipesDir: string): string {\n return path.join(recipesDir, LOCK_FILENAME);\n}\n\nexport async function readLockfile(recipesDir: string): Promise<Lockfile> {\n const p = lockPath(recipesDir);\n if (!existsSync(p)) {\n return { version: LOCK_VERSION, registry: \"\", families: {} };\n }\n const body = await readFile(p, \"utf8\");\n const raw = JSON.parse(body) as unknown;\n if (typeof raw !== \"object\" || raw === null) {\n throw new Error(`${p}: lockfile must be a JSON object`);\n }\n const r = raw as Record<string, unknown>;\n const families: Record<string, LockEntry> = {};\n if (r[\"families\"] !== undefined) {\n if (typeof r[\"families\"] !== \"object\" || r[\"families\"] === null) {\n throw new Error(`${p}: \"families\" must be an object`);\n }\n for (const [name, entry] of Object.entries(r[\"families\"] as Record<string, unknown>)) {\n if (typeof entry !== \"object\" || entry === null) {\n throw new Error(`${p}: families[\"${name}\"] must be an object`);\n }\n const e = entry as Record<string, unknown>;\n if (typeof e[\"version\"] !== \"string\" || typeof e[\"sha\"] !== \"string\") {\n throw new Error(\n `${p}: families[\"${name}\"] must have string \"version\" and \"sha\"`,\n );\n }\n families[name] = { version: e[\"version\"], sha: e[\"sha\"] };\n }\n }\n return {\n version: typeof r[\"version\"] === \"number\" ? r[\"version\"] : LOCK_VERSION,\n registry: typeof r[\"registry\"] === \"string\" ? r[\"registry\"] : \"\",\n families,\n };\n}\n\nexport async function writeLockfile(recipesDir: string, lock: Lockfile): Promise<void> {\n const sorted: Lockfile = {\n version: lock.version || LOCK_VERSION,\n registry: lock.registry,\n families: Object.fromEntries(\n Object.entries(lock.families).sort(([a], [b]) => a.localeCompare(b)),\n ),\n };\n await writeFile(lockPath(recipesDir), JSON.stringify(sorted, null, 2) + \"\\n\");\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { applyEdits, modify, parse as parseJsonc } from \"jsonc-parser\";\nimport { buildRegistry, parseRecipeFile, renderSkillMarkdown } from \"@shortwind/core\";\nimport type { Recipe, Registry } from \"@shortwind/core\";\nimport { computeBodySha, extractHeader, rewriteHeaderSha } from \"./fingerprint.js\";\nimport { detectProject, type PackageManager } from \"./detect.js\";\nimport {\n createRegistrySource,\n resolvePresetFamilies,\n type RegistrySource,\n} from \"./registry-source.js\";\nimport { readLockfile, writeLockfile } from \"./lockfile.js\";\n\nexport const DEFAULT_REGISTRY = \"https://shortwind.dev/registry\";\n\nexport type InitOptions = {\n cwd: string;\n preset: string;\n registry?: string;\n installPackages?: InstallPackages;\n};\n\nexport type InstallPackages = (\n pm: PackageManager,\n packages: string[],\n cwd: string,\n) => Promise<void>;\n\nexport type InitResult = {\n packageManager: PackageManager;\n preset: string;\n registry: string;\n families: string[];\n installedPackages: string[];\n installedFamilies: string[];\n skippedFamilies: string[];\n configPath: string;\n vscodePath: string;\n huskyPath: string;\n skillPath: string;\n};\n\nexport async function init(options: InitOptions): Promise<InitResult> {\n const cwd = path.resolve(options.cwd);\n const registry = options.registry ?? DEFAULT_REGISTRY;\n const source = createRegistrySource(registry);\n\n const shape = detectProject(cwd);\n\n const families = await resolveFamilies(options.preset, source);\n const pkgs = pickPackages(shape.bundler);\n\n const installer = options.installPackages ?? defaultInstall;\n if (pkgs.length > 0) {\n await installer(shape.packageManager, pkgs, cwd);\n }\n\n const recipesDir = path.join(cwd, \"recipes\");\n const { installed, skipped } = await copyRecipes(source, families, recipesDir);\n await updateLockfile(recipesDir, registry, installed);\n\n const configPath = path.join(cwd, \"shortwind.config.json\");\n await writeConfig(configPath, { registry, recipesDir: \"recipes\" });\n\n const vscodePath = path.join(cwd, \".vscode\", \"settings.json\");\n await wireVscodeClassRegex(vscodePath);\n\n const huskyPath = path.join(cwd, \".husky\", \"pre-commit\");\n await installHuskyHook(huskyPath);\n\n const skillPath = path.join(cwd, \"skills\", \"shortwind\", \"SKILL.md\");\n await writeSkillMd(skillPath, recipesDir, families);\n\n return {\n packageManager: shape.packageManager,\n preset: options.preset,\n registry,\n families,\n installedPackages: pkgs,\n installedFamilies: installed,\n skippedFamilies: skipped,\n configPath,\n vscodePath,\n huskyPath,\n skillPath,\n };\n}\n\nasync function resolveFamilies(preset: string, source: RegistrySource): Promise<string[]> {\n if (preset === \"none\") return [];\n const presets = await source.loadPresets();\n const all = await source.listAllFamilies();\n return resolvePresetFamilies(preset, presets, all);\n}\n\nfunction pickPackages(bundler: ReturnType<typeof detectProject>[\"bundler\"]): string[] {\n const base = [\"@shortwind/tailwind\"];\n switch (bundler) {\n case \"vite\":\n return [...base, \"@shortwind/vite\"];\n case \"next\":\n return [...base, \"@shortwind/next\"];\n case \"astro\":\n return [...base, \"@shortwind/astro\"];\n default:\n return base;\n }\n}\n\nconst defaultInstall: InstallPackages = async (pm, packages, cwd) => {\n const { spawn } = await import(\"node:child_process\");\n const args = installArgs(pm, packages);\n await new Promise<void>((resolve, reject) => {\n const child = spawn(pm, args, { cwd, stdio: \"inherit\" });\n child.on(\"error\", reject);\n child.on(\"exit\", (code) =>\n code === 0 ? resolve() : reject(new Error(`${pm} ${args.join(\" \")} exited ${code}`)),\n );\n });\n};\n\nfunction installArgs(pm: PackageManager, packages: string[]): string[] {\n switch (pm) {\n case \"pnpm\":\n return [\"add\", \"-D\", ...packages];\n case \"yarn\":\n return [\"add\", \"-D\", ...packages];\n case \"bun\":\n return [\"add\", \"-d\", ...packages];\n case \"npm\":\n default:\n return [\"install\", \"-D\", ...packages];\n }\n}\n\nasync function updateLockfile(\n recipesDir: string,\n registry: string,\n newlyInstalled: string[],\n): Promise<void> {\n const lock = await readLockfile(recipesDir);\n if (!lock.registry) lock.registry = registry;\n for (const family of newlyInstalled) {\n const target = path.join(recipesDir, `${family}.css`);\n if (!existsSync(target)) continue;\n const source = readFileSync(target, \"utf8\");\n const header = extractHeader(source);\n if (!header) {\n // Recipes without a fingerprint header are not lockable — fail\n // loudly so the user can fix the recipe rather than discover\n // later that `shortwind upgrade` skips this family silently.\n throw new Error(\n `recipe \"${family}\" has no fingerprint header — refusing to add to lockfile`,\n );\n }\n lock.families[family] = { version: header.version, sha: header.sha };\n }\n await writeLockfile(recipesDir, lock);\n}\n\nasync function copyRecipes(\n source: RegistrySource,\n families: string[],\n recipesDir: string,\n): Promise<{ installed: string[]; skipped: string[] }> {\n await mkdir(recipesDir, { recursive: true });\n const installed: string[] = [];\n const skipped: string[] = [];\n for (const family of families) {\n const target = path.join(recipesDir, `${family}.css`);\n if (existsSync(target)) {\n skipped.push(family);\n continue;\n }\n const body = await source.loadFamily(family);\n const sha = computeBodySha(body);\n const sealed = rewriteHeaderSha(body, sha);\n await writeFile(target, sealed);\n installed.push(family);\n }\n return { installed, skipped };\n}\n\nasync function writeConfig(\n configPath: string,\n next: { registry: string; recipesDir: string },\n): Promise<void> {\n const desired = {\n registry: next.registry,\n recipesDir: next.recipesDir,\n outputPath: \"skills/shortwind/SKILL.md\",\n };\n if (!existsSync(configPath)) {\n await writeFile(configPath, JSON.stringify(desired, null, 2) + \"\\n\");\n return;\n }\n const current = JSON.parse(await readFile(configPath, \"utf8\")) as Record<string, unknown>;\n const merged = { ...current, ...desired };\n await writeFile(configPath, JSON.stringify(merged, null, 2) + \"\\n\");\n}\n\nconst CLASS_REGEX_KEY = [\"tailwindCSS.experimental.classRegex\"];\nconst CLASS_REGEX_VALUE = [\n [\"class\\\\s*[=:]\\\\s*['\\\"]([^'\\\"]*)['\\\"]\", \"([\\\\w-@/:]+)\"],\n [\"className\\\\s*=\\\\s*['\\\"]([^'\\\"]*)['\\\"]\", \"([\\\\w-@/:]+)\"],\n];\n\nasync function wireVscodeClassRegex(vscodePath: string): Promise<void> {\n await mkdir(path.dirname(vscodePath), { recursive: true });\n let body: string;\n if (existsSync(vscodePath)) {\n body = await readFile(vscodePath, \"utf8\");\n } else {\n body = \"{}\\n\";\n }\n const edits = modify(body, CLASS_REGEX_KEY, CLASS_REGEX_VALUE, {\n formattingOptions: { tabSize: 2, insertSpaces: true },\n });\n const next = applyEdits(body, edits);\n // sanity — make sure it's parseable\n parseJsonc(next);\n await writeFile(vscodePath, next.endsWith(\"\\n\") ? next : next + \"\\n\");\n}\n\nconst HUSKY_LINE = \"npx shortwind build\";\n\nasync function installHuskyHook(huskyPath: string): Promise<void> {\n await mkdir(path.dirname(huskyPath), { recursive: true });\n if (!existsSync(huskyPath)) {\n await writeFile(huskyPath, `${HUSKY_LINE}\\n`, { mode: 0o755 });\n return;\n }\n const current = await readFile(huskyPath, \"utf8\");\n if (current.includes(HUSKY_LINE)) return;\n const next = current.endsWith(\"\\n\") ? current + HUSKY_LINE + \"\\n\" : current + \"\\n\" + HUSKY_LINE + \"\\n\";\n await writeFile(huskyPath, next, { mode: 0o755 });\n}\n\nasync function writeSkillMd(\n skillPath: string,\n recipesDir: string,\n families: string[],\n): Promise<void> {\n await mkdir(path.dirname(skillPath), { recursive: true });\n const allRecipes: Recipe[] = [];\n const guidance: Record<string, string> = {};\n for (const family of families) {\n const filePath = path.join(recipesDir, `${family}.css`);\n if (!existsSync(filePath)) continue;\n const source = readFileSync(filePath, \"utf8\");\n const parsed = parseRecipeFile(source, `${family}.css`);\n if (parsed.ok) {\n allRecipes.push(...parsed.value.recipes);\n if (parsed.value.guidance) guidance[family] = parsed.value.guidance;\n }\n }\n let registry: Registry = { families: {}, flattened: {} };\n const resolved = buildRegistry(allRecipes, { guidance });\n if (resolved.ok) registry = resolved.value;\n await writeFile(skillPath, renderSkillMarkdown(registry, { order: families }));\n}\n","import { existsSync, readdirSync, readFileSync } from \"node:fs\";\nimport { readFile, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { buildRegistry, parseRecipeFile, renderSkillMarkdown } from \"@shortwind/core\";\nimport type { Recipe, Registry } from \"@shortwind/core\";\n\nexport type ShortwindConfig = {\n registry: string;\n recipesDir: string;\n outputPath: string;\n};\n\nexport const DEFAULT_CONFIG: ShortwindConfig = {\n registry: \"https://shortwind.dev/registry\",\n recipesDir: \"recipes\",\n outputPath: \"skills/shortwind/SKILL.md\",\n};\n\nexport async function readConfig(cwd: string): Promise<ShortwindConfig> {\n const configPath = path.join(cwd, \"shortwind.config.json\");\n if (!existsSync(configPath)) return DEFAULT_CONFIG;\n const body = await readFile(configPath, \"utf8\");\n let parsed: Partial<ShortwindConfig>;\n try {\n parsed = JSON.parse(body) as Partial<ShortwindConfig>;\n } catch (err) {\n throw new Error(\n `${configPath}: invalid JSON — ${(err as Error).message}`,\n );\n }\n return { ...DEFAULT_CONFIG, ...parsed };\n}\n\nexport function installedFamilies(recipesDir: string): string[] {\n if (!existsSync(recipesDir)) return [];\n return readdirSync(recipesDir)\n .filter((f) => f.endsWith(\".css\"))\n .map((f) => f.replace(/\\.css$/, \"\"))\n .sort();\n}\n\nexport function parseInstalledFamily(\n recipesDir: string,\n family: string,\n): { recipes: Recipe[]; header: { family: string; version: string; sha: string } | null } | null {\n const filePath = path.join(recipesDir, `${family}.css`);\n if (!existsSync(filePath)) return null;\n const source = readFileSync(filePath, \"utf8\");\n const result = parseRecipeFile(source, `${family}.css`);\n if (!result.ok) return null;\n return { recipes: result.value.recipes, header: result.value.header };\n}\n\nexport async function regenerateSkillMd(cwd: string, config: ShortwindConfig): Promise<string> {\n const recipesDir = path.join(cwd, config.recipesDir);\n const families = installedFamilies(recipesDir);\n const skillPath = path.join(cwd, config.outputPath);\n const { mkdir } = await import(\"node:fs/promises\");\n await mkdir(path.dirname(skillPath), { recursive: true });\n\n const allRecipes: Recipe[] = [];\n const guidance: Record<string, string> = {};\n for (const family of families) {\n const filePath = path.join(recipesDir, `${family}.css`);\n const source = readFileSync(filePath, \"utf8\");\n const parsed = parseRecipeFile(source, `${family}.css`);\n if (parsed.ok) {\n allRecipes.push(...parsed.value.recipes);\n if (parsed.value.guidance) guidance[family] = parsed.value.guidance;\n }\n }\n let registry: Registry = { families: {}, flattened: {} };\n const resolved = buildRegistry(allRecipes, { guidance });\n if (resolved.ok) registry = resolved.value;\n const order = families;\n await writeFile(skillPath, renderSkillMarkdown(registry, { order }));\n return skillPath;\n}\n\n/**\n * Rewrite a recipe file so that every occurrence of `<from>` is replaced with `<to>`:\n * - fingerprint header family slot\n * - recipe names (after `@recipe `)\n * - cross-recipe `@from` / `@from-*` references in bodies\n *\n * Descriptions and unrelated text are untouched.\n */\nexport function renameFamilyInSource(source: string, from: string, to: string): string {\n const escape = (s: string): string => s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n const f = escape(from);\n let out = source;\n out = out.replace(new RegExp(`(\\\\bshortwind:\\\\s+)${f}(\\\\b|@)`, \"g\"), `$1${to}$2`);\n out = out.replace(new RegExp(`(@recipe\\\\s+)${f}(\\\\b|-)`, \"g\"), `$1${to}$2`);\n out = out.replace(new RegExp(`@${f}(\\\\b|-)`, \"g\"), `@${to}$1`);\n return out;\n}\n","import { existsSync, readdirSync } from \"node:fs\";\nimport { mkdir, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { createRegistrySource } from \"../registry-source.js\";\nimport { computeBodySha, extractHeader, rewriteHeaderSha } from \"../fingerprint.js\";\nimport { readLockfile, writeLockfile, type Lockfile } from \"../lockfile.js\";\nimport {\n readConfig,\n regenerateSkillMd,\n renameFamilyInSource,\n parseInstalledFamily,\n} from \"../project.js\";\n\nexport type AddOptions = {\n cwd: string;\n families: string[];\n as?: string;\n all?: boolean;\n force?: boolean;\n registry?: string;\n};\n\nexport type AddResult = {\n added: string[];\n skipped: string[];\n overwritten: string[];\n missingDependencies: { family: string; references: string[] }[];\n lockfile: Lockfile;\n skillPath: string;\n};\n\nexport async function add(options: AddOptions): Promise<AddResult> {\n const cwd = path.resolve(options.cwd);\n const config = await readConfig(cwd);\n const registry = options.registry ?? config.registry;\n const source = createRegistrySource(registry);\n const recipesDir = path.join(cwd, config.recipesDir);\n await mkdir(recipesDir, { recursive: true });\n\n const lock = await readLockfile(recipesDir);\n if (!lock.registry) lock.registry = registry;\n\n const requested = options.all ? await source.listAllFamilies() : options.families;\n if (options.all && options.as) {\n throw new Error(\"--as cannot be combined with --all\");\n }\n if (options.as && requested.length !== 1) {\n throw new Error(\"--as requires exactly one family argument\");\n }\n\n const added: string[] = [];\n const skipped: string[] = [];\n const overwritten: string[] = [];\n const missingDependencies: { family: string; references: string[] }[] = [];\n\n // Scan the installed-recipe namespace once before the loop, then update\n // it as each family lands. Previously collectMissingCrossFamilyDeps()\n // re-walked the whole recipes dir for every requested family, which is\n // O(n²) on `--all` installs.\n const installedRecipeNames = readAllInstalledRecipeNames(recipesDir);\n\n for (const family of requested) {\n const targetName = options.as ?? family;\n const targetPath = path.join(recipesDir, `${targetName}.css`);\n const exists = existsSync(targetPath);\n if (exists && !options.force) {\n skipped.push(targetName);\n continue;\n }\n\n const sourceCss = await source.loadFamily(family);\n const renamed = options.as ? renameFamilyInSource(sourceCss, family, options.as) : sourceCss;\n const sha = computeBodySha(renamed);\n const finalCss = rewriteHeaderSha(renamed, sha);\n await writeFile(targetPath, finalCss);\n\n const header = extractHeader(finalCss);\n if (header) {\n lock.families[targetName] = { version: header.version, sha };\n }\n\n if (exists) overwritten.push(targetName);\n else added.push(targetName);\n\n const parsed = parseInstalledFamily(recipesDir, targetName);\n if (parsed) {\n for (const r of parsed.recipes) installedRecipeNames.add(r.name);\n const ownNames = new Set(parsed.recipes.map((r) => r.name));\n const missing = new Set<string>();\n for (const recipe of parsed.recipes) {\n for (const ref of recipe.references) {\n if (ownNames.has(ref)) continue;\n if (installedRecipeNames.has(ref)) continue;\n missing.add(ref);\n }\n }\n if (missing.size > 0) {\n missingDependencies.push({\n family: targetName,\n references: Array.from(missing).sort(),\n });\n }\n }\n }\n\n await writeLockfile(recipesDir, lock);\n const skillPath = await regenerateSkillMd(cwd, config);\n\n return { added, skipped, overwritten, missingDependencies, lockfile: lock, skillPath };\n}\n\nfunction readAllInstalledRecipeNames(recipesDir: string): Set<string> {\n const names = new Set<string>();\n for (const fam of readDirFamilies(recipesDir)) {\n const p = parseInstalledFamily(recipesDir, fam);\n if (!p) continue;\n for (const r of p.recipes) names.add(r.name);\n }\n return names;\n}\n\nfunction readDirFamilies(recipesDir: string): string[] {\n if (!existsSync(recipesDir)) return [];\n return readdirSync(recipesDir)\n .filter((f) => f.endsWith(\".css\"))\n .map((f) => f.replace(/\\.css$/, \"\"));\n}\n","import { existsSync } from \"node:fs\";\nimport { rm } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport {\n readConfig,\n regenerateSkillMd,\n installedFamilies,\n parseInstalledFamily,\n} from \"../project.js\";\nimport { readLockfile, writeLockfile, type Lockfile } from \"../lockfile.js\";\n\nexport type RemoveOptions = {\n cwd: string;\n families: string[];\n};\n\nexport type RemoveResult = {\n removed: string[];\n notFound: string[];\n brokenDependents: { dependent: string; references: string[] }[];\n lockfile: Lockfile;\n skillPath: string;\n};\n\nexport async function remove(options: RemoveOptions): Promise<RemoveResult> {\n const cwd = path.resolve(options.cwd);\n const config = await readConfig(cwd);\n const recipesDir = path.join(cwd, config.recipesDir);\n\n const lock = await readLockfile(recipesDir);\n const removed: string[] = [];\n const notFound: string[] = [];\n // Snapshot every recipe name in each family *before* deleting the file, so\n // broken-dependent detection can look for exact ref matches instead of\n // splitting the ref on the first `-` (which mis-classifies hyphenated\n // family names like `text-stack`).\n const removedRecipeNames = new Set<string>();\n\n for (const family of options.families) {\n const target = path.join(recipesDir, `${family}.css`);\n if (!existsSync(target)) {\n notFound.push(family);\n continue;\n }\n const parsed = parseInstalledFamily(recipesDir, family);\n if (parsed) {\n for (const r of parsed.recipes) removedRecipeNames.add(r.name);\n }\n await rm(target);\n delete lock.families[family];\n removed.push(family);\n }\n\n await writeLockfile(recipesDir, lock);\n\n const brokenDependents = collectBrokenDependents(recipesDir, removedRecipeNames);\n const skillPath = await regenerateSkillMd(cwd, config);\n\n return { removed, notFound, brokenDependents, lockfile: lock, skillPath };\n}\n\nfunction collectBrokenDependents(\n recipesDir: string,\n removedRecipeNames: Set<string>,\n): { dependent: string; references: string[] }[] {\n if (removedRecipeNames.size === 0) return [];\n const out: { dependent: string; references: string[] }[] = [];\n for (const family of installedFamilies(recipesDir)) {\n const parsed = parseInstalledFamily(recipesDir, family);\n if (!parsed) continue;\n const broken = new Set<string>();\n for (const recipe of parsed.recipes) {\n for (const ref of recipe.references) {\n if (removedRecipeNames.has(ref)) broken.add(ref);\n }\n }\n if (broken.size > 0) out.push({ dependent: family, references: Array.from(broken).sort() });\n }\n return out;\n}\n","import path from \"node:path\";\nimport { createRegistrySource, resolvePresetFamilies } from \"../registry-source.js\";\nimport { readConfig } from \"../project.js\";\nimport { add, type AddResult } from \"./add.js\";\n\nexport type PresetOptions = {\n cwd: string;\n name: string;\n registry?: string;\n};\n\nexport type PresetResult = AddResult & { preset: string };\n\nexport async function preset(options: PresetOptions): Promise<PresetResult> {\n const cwd = path.resolve(options.cwd);\n const config = await readConfig(cwd);\n const registry = options.registry ?? config.registry;\n const source = createRegistrySource(registry);\n\n if (options.name === \"none\") {\n throw new Error(\"Use `shortwind remove` to uninstall families; preset 'none' is for `init` only.\");\n }\n\n const presets = await source.loadPresets();\n const all = await source.listAllFamilies();\n const families = resolvePresetFamilies(options.name, presets, all);\n\n const addOptions: Parameters<typeof add>[0] = {\n cwd,\n families,\n };\n if (options.registry !== undefined) addOptions.registry = options.registry;\n\n const result = await add(addOptions);\n return { ...result, preset: options.name };\n}\n","import path from \"node:path\";\nimport { createRegistrySource } from \"../registry-source.js\";\nimport { readConfig, installedFamilies } from \"../project.js\";\nimport { readLockfile } from \"../lockfile.js\";\n\nexport type LsOptions = {\n cwd: string;\n registry?: string;\n installedOnly?: boolean;\n availableOnly?: boolean;\n};\n\nexport type LsResult = {\n installed: { family: string; version: string | null }[];\n available: string[];\n};\n\nexport async function ls(options: LsOptions): Promise<LsResult> {\n const cwd = path.resolve(options.cwd);\n const config = await readConfig(cwd);\n const recipesDir = path.join(cwd, config.recipesDir);\n const lock = await readLockfile(recipesDir);\n\n const installed = options.availableOnly\n ? []\n : installedFamilies(recipesDir).map((family) => {\n const entry = lock.families[family];\n return { family, version: entry?.version ?? null };\n });\n\n let available: string[] = [];\n if (!options.installedOnly) {\n const registry = options.registry ?? config.registry;\n const source = createRegistrySource(registry);\n try {\n available = await source.listAllFamilies();\n } catch {\n available = [];\n }\n }\n\n return { installed, available };\n}\n\nexport function formatLsText(result: LsResult): string {\n const installedSet = new Set(result.installed.map((i) => i.family));\n const lines: string[] = [];\n lines.push(\"Installed:\");\n if (result.installed.length === 0) lines.push(\" (none)\");\n for (const { family, version } of result.installed) {\n lines.push(` ${family}${version ? ` ${version}` : \"\"}`);\n }\n lines.push(\"\");\n lines.push(\"Available:\");\n if (result.available.length === 0) lines.push(\" (registry unreachable)\");\n for (const family of result.available) {\n const marker = installedSet.has(family) ? \"*\" : \" \";\n lines.push(` ${marker} ${family}`);\n }\n return lines.join(\"\\n\");\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { mkdir, writeFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { buildRegistry, parseRecipeFile, renderSkillMarkdown } from \"@shortwind/core\";\nimport type { Diagnostic, Recipe } from \"@shortwind/core\";\nimport { installedFamilies, readConfig } from \"../project.js\";\n\nexport type BuildOptions = {\n cwd: string;\n};\n\nexport type BuildResult = {\n changed: boolean;\n families: string[];\n skillPath: string;\n};\n\nexport class BuildError extends Error {\n readonly diagnostics: Diagnostic[];\n\n constructor(diagnostics: Diagnostic[]) {\n super(\n `shortwind build failed:\\n${diagnostics\n .map((d) => ` ${d.file}:${d.line}${d.column ? `:${d.column}` : \"\"} ${d.code} — ${d.message}`)\n .join(\"\\n\")}`,\n );\n this.diagnostics = diagnostics;\n this.name = \"BuildError\";\n }\n}\n\nexport async function build(options: BuildOptions): Promise<BuildResult> {\n const cwd = path.resolve(options.cwd);\n const config = await readConfig(cwd);\n const recipesDir = path.join(cwd, config.recipesDir);\n const families = installedFamilies(recipesDir);\n\n const allRecipes: Recipe[] = [];\n const guidance: Record<string, string> = {};\n const errors: Diagnostic[] = [];\n\n for (const family of families) {\n const filePath = path.join(recipesDir, `${family}.css`);\n const source = readFileSync(filePath, \"utf8\");\n const parsed = parseRecipeFile(source, `${family}.css`);\n if (!parsed.ok) {\n errors.push(...parsed.errors);\n continue;\n }\n allRecipes.push(...parsed.value.recipes);\n if (parsed.value.guidance) guidance[family] = parsed.value.guidance;\n }\n\n if (errors.length > 0) throw new BuildError(errors);\n\n const resolved = buildRegistry(allRecipes, { guidance });\n if (!resolved.ok) throw new BuildError(resolved.errors);\n\n const skillPath = path.join(cwd, config.outputPath);\n const next = renderSkillMarkdown(resolved.value, { order: families });\n const current = existsSync(skillPath) ? readFileSync(skillPath, \"utf8\") : null;\n let changed = false;\n if (current !== next) {\n await mkdir(path.dirname(skillPath), { recursive: true });\n await writeFile(skillPath, next);\n changed = true;\n }\n\n return { changed, families, skillPath };\n}\n","import path from \"node:path\";\nimport chokidar from \"chokidar\";\nimport { readConfig } from \"../project.js\";\nimport { build, BuildError } from \"./build.js\";\n\nexport type DevOptions = {\n cwd: string;\n signal?: AbortSignal;\n onStatus?: (status: DevStatus) => void;\n debounceMs?: number;\n reconcileIntervalMs?: number;\n};\n\nexport type DevStatus =\n | { kind: \"ready\"; recipesDir: string }\n | { kind: \"rebuilt\"; families: string[]; changed: boolean }\n | { kind: \"error\"; message: string };\n\nexport async function dev(options: DevOptions): Promise<{ stop: () => Promise<void> }> {\n options.signal?.throwIfAborted();\n const cwd = path.resolve(options.cwd);\n const config = await readConfig(cwd);\n const recipesDir = path.join(cwd, config.recipesDir);\n const debounceMs = options.debounceMs ?? 50;\n const reconcileIntervalMs = options.reconcileIntervalMs ?? Math.max(1000, debounceMs * 5);\n\n const status = (s: DevStatus): void => options.onStatus?.(s);\n\n const watcher = chokidar.watch(recipesDir, {\n ignoreInitial: true,\n awaitWriteFinish: { stabilityThreshold: 25, pollInterval: 10 },\n });\n\n let timer: ReturnType<typeof setTimeout> | null = null;\n let reconcileTimer: ReturnType<typeof setInterval> | null = null;\n let running = false;\n let pending = false;\n let pendingSilent = true;\n\n const runBuild = async (silentNoChange = false): Promise<void> => {\n if (running) {\n pending = true;\n pendingSilent = pendingSilent && silentNoChange;\n return;\n }\n running = true;\n try {\n let currentSilent = silentNoChange;\n do {\n pending = false;\n pendingSilent = true;\n try {\n const result = await build({ cwd });\n if (!currentSilent || result.changed) {\n status({ kind: \"rebuilt\", families: result.families, changed: result.changed });\n }\n } catch (err) {\n if (err instanceof BuildError) status({ kind: \"error\", message: err.message });\n else status({ kind: \"error\", message: err instanceof Error ? err.message : String(err) });\n }\n currentSilent = pendingSilent;\n } while (pending);\n } finally {\n running = false;\n }\n };\n\n const schedule = (): void => {\n if (timer) clearTimeout(timer);\n timer = setTimeout(() => void runBuild(false), debounceMs);\n };\n\n watcher.on(\"add\", schedule).on(\"change\", schedule).on(\"unlink\", schedule);\n\n let stopped = false;\n const stop = async (): Promise<void> => {\n if (stopped) return;\n stopped = true;\n if (timer) clearTimeout(timer);\n if (reconcileTimer) clearInterval(reconcileTimer);\n await watcher.close();\n };\n\n if (options.signal) {\n if (options.signal.aborted) {\n await stop();\n return { stop };\n }\n options.signal.addEventListener(\"abort\", () => void stop(), { once: true });\n }\n\n await new Promise<void>((resolve) => watcher.once(\"ready\", () => resolve()));\n if (stopped) return { stop };\n\n // initial build so SKILL.md is current at start\n await runBuild();\n if (stopped) return { stop };\n status({ kind: \"ready\", recipesDir });\n reconcileTimer = setInterval(() => void runBuild(true), reconcileIntervalMs);\n\n return { stop };\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { open, rename } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { createRegistrySource, type RegistrySource } from \"../registry-source.js\";\nimport { computeBodySha, extractHeader, rewriteHeaderSha } from \"../fingerprint.js\";\nimport { readLockfile, writeLockfile, type Lockfile } from \"../lockfile.js\";\nimport { installedFamilies, readConfig, regenerateSkillMd } from \"../project.js\";\n\nexport type UpgradeChoice = \"accept\" | \"keep\" | \"skip\";\n\nexport type TouchedContext = {\n family: string;\n local: string;\n baseline: { version: string; sha: string };\n incoming: { version: string; body: string };\n};\n\nexport type UpgradeResolver = (ctx: TouchedContext) => Promise<UpgradeChoice>;\n\nexport type UpgradeOptions = {\n cwd: string;\n families?: string[];\n registry?: string;\n force?: boolean;\n check?: boolean;\n resolver?: UpgradeResolver;\n source?: RegistrySource;\n};\n\nexport type FamilyState = \"pristine\" | \"touched\" | \"unchanged\" | \"missing\" | \"untracked\";\n\nexport type FamilyOutcome =\n | { family: string; action: \"updated\"; from: string; to: string; state: FamilyState }\n | { family: string; action: \"kept\"; reason: \"unchanged\" | \"user-chose-keep\"; state: FamilyState }\n | { family: string; action: \"skipped\"; reason: string; state: FamilyState }\n | { family: string; action: \"would-update\"; from: string; to: string; state: FamilyState }\n | { family: string; action: \"would-review\"; from: string; to: string; state: FamilyState };\n\nexport type UpgradeResult = {\n outcomes: FamilyOutcome[];\n hasUpdates: boolean;\n hasTouched: boolean;\n lockfile: Lockfile;\n skillPath: string | null;\n};\n\nexport class UpgradeError extends Error {\n readonly errors: { family: string; message: string }[];\n constructor(errors: { family: string; message: string }[]) {\n super(`shortwind upgrade failed:\\n${errors.map((e) => ` ${e.family}: ${e.message}`).join(\"\\n\")}`);\n this.errors = errors;\n this.name = \"UpgradeError\";\n }\n}\n\nexport async function upgrade(options: UpgradeOptions): Promise<UpgradeResult> {\n const cwd = path.resolve(options.cwd);\n const config = await readConfig(cwd);\n const registry = options.registry ?? config.registry;\n const source = options.source ?? createRegistrySource(registry);\n const recipesDir = path.join(cwd, config.recipesDir);\n\n const installed = installedFamilies(recipesDir);\n const targets = options.families && options.families.length > 0 ? options.families : installed;\n const lock = await readLockfile(recipesDir);\n let lockfileDirty = false;\n if (!lock.registry) {\n lock.registry = registry;\n lockfileDirty = true;\n }\n\n const outcomes: FamilyOutcome[] = [];\n const errors: { family: string; message: string }[] = [];\n let hasUpdates = false;\n let hasTouched = false;\n let anyWritten = false;\n\n for (const family of targets) {\n const filePath = path.join(recipesDir, `${family}.css`);\n if (!existsSync(filePath)) {\n outcomes.push({\n family,\n action: \"skipped\",\n reason: \"not installed\",\n state: \"missing\",\n });\n continue;\n }\n\n let incomingBody: string;\n try {\n incomingBody = await source.loadFamily(family);\n } catch (err) {\n errors.push({ family, message: (err as Error).message });\n continue;\n }\n const incomingHeader = extractHeader(incomingBody);\n if (!incomingHeader) {\n errors.push({ family, message: \"registry recipe has no fingerprint header\" });\n continue;\n }\n const incomingVersion = incomingHeader.version;\n\n const localBody = readFileSync(filePath, \"utf8\");\n const localHeader = extractHeader(localBody);\n const recordedSha = localHeader?.sha ?? \"\";\n const actualSha = computeBodySha(localBody);\n const lockedEntry = lock.families[family];\n const lockedVersion = lockedEntry?.version ?? localHeader?.version ?? \"\";\n\n const isTouched = recordedSha !== \"\" && recordedSha !== actualSha;\n const state: FamilyState = isTouched\n ? \"touched\"\n : lockedVersion === incomingVersion\n ? \"unchanged\"\n : \"pristine\";\n\n if (state === \"unchanged\" && !isTouched) {\n outcomes.push({\n family,\n action: \"kept\",\n reason: \"unchanged\",\n state: \"unchanged\",\n });\n continue;\n }\n\n if (options.check) {\n if (isTouched) {\n hasTouched = true;\n if (lockedVersion !== incomingVersion) hasUpdates = true;\n outcomes.push({\n family,\n action: \"would-review\",\n from: lockedVersion,\n to: incomingVersion,\n state: \"touched\",\n });\n } else {\n hasUpdates = true;\n outcomes.push({\n family,\n action: \"would-update\",\n from: lockedVersion,\n to: incomingVersion,\n state: \"pristine\",\n });\n }\n continue;\n }\n\n if (isTouched && !options.force) {\n hasTouched = true;\n const choice = options.resolver\n ? await options.resolver({\n family,\n local: localBody,\n baseline: { version: lockedVersion, sha: recordedSha },\n incoming: { version: incomingVersion, body: incomingBody },\n })\n : \"skip\";\n if (choice === \"keep\") {\n outcomes.push({ family, action: \"kept\", reason: \"user-chose-keep\", state: \"touched\" });\n continue;\n }\n if (choice === \"skip\") {\n outcomes.push({ family, action: \"skipped\", reason: \"touched\", state: \"touched\" });\n continue;\n }\n }\n\n const newSha = computeBodySha(incomingBody);\n const sealed = rewriteHeaderSha(incomingBody, newSha);\n await atomicWrite(filePath, sealed);\n lock.families[family] = { version: incomingVersion, sha: newSha };\n outcomes.push({\n family,\n action: \"updated\",\n from: lockedVersion,\n to: incomingVersion,\n state: isTouched ? \"touched\" : \"pristine\",\n });\n hasUpdates = true;\n anyWritten = true;\n lockfileDirty = true;\n }\n\n if (errors.length > 0) throw new UpgradeError(errors);\n\n let skillPath: string | null = null;\n if (!options.check) {\n if (lockfileDirty) await writeLockfile(recipesDir, lock);\n if (anyWritten) skillPath = await regenerateSkillMd(cwd, config);\n }\n\n return { outcomes, hasUpdates, hasTouched, lockfile: lock, skillPath };\n}\n\nasync function atomicWrite(filePath: string, body: string): Promise<void> {\n const tmp = filePath + \".tmp\";\n const fh = await open(tmp, \"w\");\n try {\n await fh.writeFile(body);\n await fh.sync();\n } finally {\n await fh.close();\n }\n await rename(tmp, filePath);\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { computeBodySha, extractHeader } from \"../fingerprint.js\";\nimport { readLockfile } from \"../lockfile.js\";\nimport { installedFamilies, readConfig } from \"../project.js\";\n\nexport type VerifyIssue =\n | { family: string; kind: \"missing-header\"; file: string }\n | { family: string; kind: \"header-tampered\"; file: string; recorded: string; actual: string }\n | { family: string; kind: \"lockfile-mismatch\"; file: string; locked: string; actual: string }\n | { family: string; kind: \"missing-lock-entry\"; file: string }\n | { family: string; kind: \"missing-file\"; file: string };\n\nexport type VerifyOptions = {\n cwd: string;\n};\n\nexport type VerifyResult = {\n ok: boolean;\n checked: string[];\n issues: VerifyIssue[];\n};\n\nexport async function verify(options: VerifyOptions): Promise<VerifyResult> {\n const cwd = path.resolve(options.cwd);\n const config = await readConfig(cwd);\n const recipesDir = path.join(cwd, config.recipesDir);\n\n const installed = installedFamilies(recipesDir);\n const lock = await readLockfile(recipesDir);\n const issues: VerifyIssue[] = [];\n\n const seen = new Set<string>();\n for (const family of installed) {\n seen.add(family);\n const filePath = path.join(recipesDir, `${family}.css`);\n const source = readFileSync(filePath, \"utf8\");\n const header = extractHeader(source);\n if (!header) {\n issues.push({ family, kind: \"missing-header\", file: filePath });\n continue;\n }\n const actual = computeBodySha(source);\n if (header.sha !== actual) {\n issues.push({\n family,\n kind: \"header-tampered\",\n file: filePath,\n recorded: header.sha,\n actual,\n });\n }\n const locked = lock.families[family];\n if (!locked) {\n issues.push({ family, kind: \"missing-lock-entry\", file: filePath });\n } else if (locked.sha !== actual) {\n issues.push({\n family,\n kind: \"lockfile-mismatch\",\n file: filePath,\n locked: locked.sha,\n actual,\n });\n }\n }\n\n for (const family of Object.keys(lock.families)) {\n if (seen.has(family)) continue;\n const filePath = path.join(recipesDir, `${family}.css`);\n if (!existsSync(filePath)) {\n issues.push({ family, kind: \"missing-file\", file: filePath });\n }\n }\n\n return { ok: issues.length === 0, checked: installed, issues };\n}\n","// Generated by scripts/generate-bench-recipes.ts. Do not edit directly —\n// re-run `pnpm --filter shortwind gen:bench` after editing files under\n// packages/registry/recipes/.\nexport const DEFAULT_RECIPES_CSS: Record<string, string> = {\n \"badge.css\": \"/* shortwind: badge@0.0.1 sha:000000 */\\n\\n/* @guide\\n @badge is the neutral default; tone variants @badge-success/warning/danger/\\n info carry their own color; @badge-outline is unfilled. One tone per badge.\\n @badge-base is a color-less shell for building custom tones — not for direct\\n use.\\n*/\\n\\n/* Badge shell — shape, sizing, focus ring. No bg/text/border color so\\n variants can supply their own tone without conflicts. */\\n@recipe badge-base {\\n inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2\\n}\\n\\n/* Default neutral badge. */\\n@recipe badge {\\n inline-flex items-center gap-1 rounded-full bg-muted px-2 py-0.5 text-xs font-medium text-muted-foreground\\n}\\n\\n/* Success tone badge. */\\n@recipe badge-success {\\n inline-flex items-center gap-1 rounded-full bg-green-100 px-2 py-0.5 text-xs font-medium text-green-800 dark:bg-green-900 dark:text-green-200\\n}\\n\\n/* Warning tone badge. */\\n@recipe badge-warning {\\n inline-flex items-center gap-1 rounded-full bg-amber-100 px-2 py-0.5 text-xs font-medium text-amber-800 dark:bg-amber-900 dark:text-amber-200\\n}\\n\\n/* Danger tone badge. */\\n@recipe badge-danger {\\n inline-flex items-center gap-1 rounded-full bg-destructive/15 px-2 py-0.5 text-xs font-medium text-destructive\\n}\\n\\n/* Info tone badge. */\\n@recipe badge-info {\\n inline-flex items-center gap-1 rounded-full bg-primary/15 px-2 py-0.5 text-xs font-medium text-primary\\n}\\n\\n/* Outline badge — no fill. */\\n@recipe badge-outline {\\n inline-flex items-center gap-1 rounded-full border border-border px-2 py-0.5 text-xs font-medium text-foreground\\n}\\n\",\n \"button.css\": \"/* shortwind: button@0.0.1 sha:000000 */\\n\\n/* @guide\\n Name order is @btn-<intent>[-<size>]: intent first (primary/secondary/ghost/\\n danger/outline), size second (sm/lg; omit for default). One intent per\\n button — never combine @btn-primary with @btn-danger. @btn-ghost is text-only,\\n @btn-outline is bordered with no fill, @btn-icon is a square icon button.\\n @btn-base is the shared shell; don't use it on its own.\\n*/\\n\\n/* Shared button base — sizing, focus ring, disabled state. */\\n@recipe btn-base {\\n inline-flex items-center justify-center gap-2 rounded-md px-4 py-2 text-sm font-medium transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring disabled:cursor-not-allowed disabled:opacity-50\\n}\\n\\n/* Primary call-to-action button. */\\n@recipe btn-primary {\\n @btn-base bg-primary text-primary-foreground hover:bg-primary/90\\n}\\n\\n/* Small primary button. */\\n@recipe btn-primary-sm {\\n @btn-primary px-3 py-1.5 text-xs\\n}\\n\\n/* Large primary button. */\\n@recipe btn-primary-lg {\\n @btn-primary px-6 py-3 text-base\\n}\\n\\n/* Secondary button — bordered surface tone. */\\n@recipe btn-secondary {\\n @btn-base border border-border bg-secondary text-secondary-foreground hover:bg-secondary/80\\n}\\n\\n/* Small secondary button. */\\n@recipe btn-secondary-sm {\\n @btn-secondary px-3 py-1.5 text-xs\\n}\\n\\n/* Large secondary button. */\\n@recipe btn-secondary-lg {\\n @btn-secondary px-6 py-3 text-base\\n}\\n\\n/* Ghost button — text only, no background. */\\n@recipe btn-ghost {\\n @btn-base text-foreground hover:bg-muted\\n}\\n\\n/* Small ghost button. */\\n@recipe btn-ghost-sm {\\n @btn-ghost px-3 py-1.5 text-xs\\n}\\n\\n/* Large ghost button. */\\n@recipe btn-ghost-lg {\\n @btn-ghost px-6 py-3 text-base\\n}\\n\\n/* Destructive button. */\\n@recipe btn-danger {\\n @btn-base bg-destructive text-destructive-foreground hover:bg-destructive/90\\n}\\n\\n/* Outline button — bordered without fill. */\\n@recipe btn-outline {\\n @btn-base border border-primary text-primary hover:bg-primary/10\\n}\\n\\n/* Square icon-only button. */\\n@recipe btn-icon {\\n inline-flex h-9 w-9 items-center justify-center rounded-md text-foreground transition-colors hover:bg-muted focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring disabled:cursor-not-allowed disabled:opacity-50\\n}\\n\",\n \"card.css\": \"/* shortwind: card@0.0.1 sha:000000 */\\n\\n/* @guide\\n Pick exactly one container: @card (default), @card-elevated (raised shadow),\\n @card-flat (no border), or @card-interactive (clickable hover/focus) — don't\\n stack two container variants on one element. Lay out the inside with\\n @card-header / @card-body / @card-footer.\\n*/\\n\\n/* Default content card with border, padding, and surface color. */\\n@recipe card {\\n rounded-lg border border-border bg-card text-card-foreground p-4\\n}\\n\\n/* Card with raised shadow for emphasis. */\\n@recipe card-elevated {\\n @card shadow-md\\n}\\n\\n/* Card without border, on a muted surface. */\\n@recipe card-flat {\\n rounded-lg bg-muted text-foreground p-4\\n}\\n\\n/* Clickable card with hover and focus-visible states. */\\n@recipe card-interactive {\\n @card cursor-pointer transition-shadow hover:shadow-md focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\\n}\\n\\n/* Card header region with bottom divider. */\\n@recipe card-header {\\n mb-3 border-b border-border pb-3\\n}\\n\\n/* Card body region. */\\n@recipe card-body {\\n py-1\\n}\\n\\n/* Card footer with top divider and right-aligned actions. */\\n@recipe card-footer {\\n mt-3 flex items-center justify-end gap-2 border-t border-border pt-3\\n}\\n\",\n \"code.css\": \"/* shortwind: code@0.0.1 sha:000000 */\\n\\n/* @guide\\n @code-inline for a code span inside prose, @code-block for a multi-line\\n preformatted block, @kbd for a keyboard-shortcut hint. Pick by context, not\\n by size.\\n*/\\n\\n/* Inline code span. */\\n@recipe code-inline {\\n rounded bg-muted px-1.5 py-0.5 font-mono text-[0.875em] text-foreground\\n}\\n\\n/* Block of preformatted code. */\\n@recipe code-block {\\n overflow-x-auto rounded-md border border-border bg-muted p-4 font-mono text-sm leading-6 text-foreground\\n}\\n\\n/* Keyboard shortcut hint. */\\n@recipe kbd {\\n inline-flex items-center rounded border border-border bg-muted px-1.5 py-0.5 font-mono text-xs text-foreground shadow-sm\\n}\\n\",\n \"dialog.css\": \"/* shortwind: dialog@0.0.1 sha:000000 */\\n\\n/* @guide\\n A modal is three layers: @dialog-overlay (dimmed backdrop), @dialog (the\\n centering wrapper), and @dialog-content (the panel). Structure the panel with\\n @dialog-header and @dialog-footer. Don't put content styling on @dialog\\n itself — it's only the positioner.\\n*/\\n\\n/* Modal dialog wrapper — covers the viewport, centers content. */\\n@recipe dialog {\\n fixed inset-0 z-50 flex items-center justify-center p-4\\n}\\n\\n/* Dimmed overlay behind the dialog. */\\n@recipe dialog-overlay {\\n fixed inset-0 z-40 bg-black/50\\n}\\n\\n/* Dialog content panel. */\\n@recipe dialog-content {\\n relative z-50 w-full max-w-md rounded-lg border border-border bg-popover text-popover-foreground p-6 shadow-xl\\n}\\n\\n/* Dialog header region with title. */\\n@recipe dialog-header {\\n mb-4 flex flex-col gap-1\\n}\\n\\n/* Dialog footer with right-aligned actions. */\\n@recipe dialog-footer {\\n mt-6 flex items-center justify-end gap-2\\n}\\n\",\n \"empty.css\": \"/* shortwind: empty@0.0.1 sha:000000 */\\n\\n/* @guide\\n @empty is the container for a no-data state; fill it with @empty-icon,\\n @empty-title, and @empty-description. These are slots for that pattern — use\\n @heading-* / @body from the text family for ordinary copy.\\n*/\\n\\n/* Empty-state container. */\\n@recipe empty {\\n flex flex-col items-center justify-center gap-3 rounded-md border border-dashed border-border p-8 text-center\\n}\\n\\n/* Empty-state icon slot. */\\n@recipe empty-icon {\\n flex h-12 w-12 items-center justify-center rounded-full bg-muted text-muted-foreground\\n}\\n\\n/* Empty-state title text. */\\n@recipe empty-title {\\n text-base font-semibold text-foreground\\n}\\n\\n/* Empty-state supporting description. */\\n@recipe empty-description {\\n max-w-sm text-sm text-muted-foreground\\n}\\n\",\n \"feedback.css\": \"/* shortwind: feedback@0.0.1 sha:000000 */\\n\\n/* @guide\\n Inline messages use @alert (neutral) or a tone variant @alert-success/\\n warning/danger/info — one tone each. @callout is a left-accent inline note,\\n @toast is a floating notification, @banner spans the full viewport width.\\n*/\\n\\n/* Default informational alert. */\\n@recipe alert {\\n flex items-start gap-3 rounded-md border border-border bg-card p-4 text-sm text-card-foreground\\n}\\n\\n/* Success alert. */\\n@recipe alert-success {\\n flex items-start gap-3 rounded-md border border-green-200 bg-green-50 p-4 text-sm text-green-900 dark:border-green-900 dark:bg-green-950 dark:text-green-100\\n}\\n\\n/* Warning alert. */\\n@recipe alert-warning {\\n flex items-start gap-3 rounded-md border border-amber-200 bg-amber-50 p-4 text-sm text-amber-900 dark:border-amber-900 dark:bg-amber-950 dark:text-amber-100\\n}\\n\\n/* Danger alert. */\\n@recipe alert-danger {\\n flex items-start gap-3 rounded-md border border-destructive/30 bg-destructive/10 p-4 text-sm text-destructive\\n}\\n\\n/* Informational alert. */\\n@recipe alert-info {\\n flex items-start gap-3 rounded-md border border-primary/30 bg-primary/10 p-4 text-sm text-primary\\n}\\n\\n/* Inline callout — flush left edge accent. */\\n@recipe callout {\\n border-l-4 border-primary bg-primary/10 p-4 text-sm text-foreground\\n}\\n\\n/* Floating toast notification. */\\n@recipe toast {\\n pointer-events-auto flex items-start gap-3 rounded-md border border-border bg-popover p-4 text-sm text-popover-foreground shadow-lg\\n}\\n\\n/* Full-width banner spanning the viewport. */\\n@recipe banner {\\n w-full bg-primary px-4 py-2 text-center text-sm font-medium text-primary-foreground\\n}\\n\",\n \"form.css\": \"/* shortwind: form@0.0.1 sha:000000 */\\n\\n/* @guide\\n Wrap each label+control+message in @field (use @field-error for the invalid\\n state); group related fields with @fieldset. Controls are bare: @input,\\n @textarea, @select, @checkbox, @radio, plus @input-error for invalid text\\n and @input-shell for the transparent shadcn-style shell. Helper text is\\n @help. There is no @form-group (use @field), @form-input (use @input),\\n @form-helper (use @help) or @form-checkbox (use @checkbox); the field label\\n recipe is @label, in the text family.\\n*/\\n\\n/* Text input field. */\\n@recipe input {\\n block w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-ring focus:outline-2 focus:outline-offset-2 focus:outline-ring disabled:cursor-not-allowed disabled:opacity-50\\n}\\n\\n/* shadcn/dinachi-style input shell — transparent background, h-9, file/\\n placeholder/selection/aria-invalid/focus-visible states baked in. */\\n@recipe input-shell {\\n flex h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-3 py-1 text-base shadow-xs outline-none transition-[color,box-shadow] placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:border-destructive aria-invalid:ring-destructive/20 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:bg-input/30 dark:aria-invalid:ring-destructive/40\\n}\\n\\n/* Input in error state. */\\n@recipe input-error {\\n @input border-destructive focus:border-destructive focus:outline-destructive\\n}\\n\\n/* Multi-line textarea. */\\n@recipe textarea {\\n block w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-ring focus:outline-2 focus:outline-offset-2 focus:outline-ring disabled:cursor-not-allowed disabled:opacity-50\\n}\\n\\n/* Native select control. */\\n@recipe select {\\n block w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground focus:border-ring focus:outline-2 focus:outline-offset-2 focus:outline-ring disabled:cursor-not-allowed disabled:opacity-50\\n}\\n\\n/* Checkbox input. */\\n@recipe checkbox {\\n h-4 w-4 rounded border-input text-primary focus:outline-2 focus:outline-offset-2 focus:outline-ring\\n}\\n\\n/* Radio input. */\\n@recipe radio {\\n h-4 w-4 border-input text-primary focus:outline-2 focus:outline-offset-2 focus:outline-ring\\n}\\n\\n/* Form field wrapper — label + input + help/error. */\\n@recipe field {\\n flex flex-col gap-1.5\\n}\\n\\n/* Form field in error state. */\\n@recipe field-error {\\n flex flex-col gap-1.5\\n}\\n\\n/* Grouped form section with optional legend. */\\n@recipe fieldset {\\n flex flex-col gap-4 rounded-md border border-border p-4\\n}\\n\\n/* Field-level helper text. */\\n@recipe help {\\n text-xs text-muted-foreground\\n}\\n\",\n \"icon.css\": \"/* shortwind: icon@0.0.1 sha:000000 */\\n\\n/* @guide\\n Size an icon with @icon-sm/md/lg (16/20/24px) — these set width and height\\n only; add @icon-muted for secondary color. They're for SVG/icon elements, not\\n to be confused with @btn-icon (the icon button in the button family).\\n*/\\n\\n/* Small icon — 16px. */\\n@recipe icon-sm {\\n h-4 w-4 shrink-0\\n}\\n\\n/* Default icon size — 20px. */\\n@recipe icon-md {\\n h-5 w-5 shrink-0\\n}\\n\\n/* Large icon — 24px. */\\n@recipe icon-lg {\\n h-6 w-6 shrink-0\\n}\\n\\n/* Icon with muted color. */\\n@recipe icon-muted {\\n text-muted-foreground\\n}\\n\",\n \"layout.css\": \"/* shortwind: layout@0.0.1 sha:000000 */\\n\\n/* @guide\\n Composition primitives. @stack-* stacks children vertically (flex-col);\\n @row* lays them out horizontally (flex-row). Choose the gap with the size\\n suffix (xs/sm/md/lg on stacks). Use @grid-2/3/4 only for true multi-column\\n grids, @center to center on both axes, @full to fill the parent. Common\\n slips: there is no @flex-row (use @row) or @flex-col (use a @stack-*), and\\n the grids are @grid-3, not @grid-cols-3.\\n*/\\n\\n/* Vertical stack with extra-small gap. */\\n@recipe stack-xs {\\n flex flex-col gap-1\\n}\\n\\n/* Vertical stack with small gap. */\\n@recipe stack-sm {\\n flex flex-col gap-2\\n}\\n\\n/* Vertical stack with medium gap. */\\n@recipe stack-md {\\n flex flex-col gap-4\\n}\\n\\n/* Vertical stack with large gap. */\\n@recipe stack-lg {\\n flex flex-col gap-8\\n}\\n\\n/* Horizontal row with default gap and centered items. */\\n@recipe row {\\n flex flex-row items-center gap-2\\n}\\n\\n/* Horizontal row with space between children. */\\n@recipe row-between {\\n flex flex-row items-center justify-between gap-2\\n}\\n\\n/* Horizontal row aligned to the end. */\\n@recipe row-end {\\n flex flex-row items-center justify-end gap-2\\n}\\n\\n/* Two-column responsive grid. */\\n@recipe grid-2 {\\n grid grid-cols-1 gap-4 sm:grid-cols-2\\n}\\n\\n/* Three-column responsive grid. */\\n@recipe grid-3 {\\n grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\\n}\\n\\n/* Four-column responsive grid. */\\n@recipe grid-4 {\\n grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4\\n}\\n\\n/* Center content horizontally and vertically. */\\n@recipe center {\\n flex items-center justify-center\\n}\\n\\n/* Fill the available width and height. */\\n@recipe full {\\n h-full w-full\\n}\\n\",\n \"list.css\": \"/* shortwind: list@0.0.1 sha:000000 */\\n\\n/* @guide\\n @list wraps a stack of @list-item rows; use @list-bordered for divided rows.\\n Definition lists are separate: @dl with @dt (term) and @dd (description).\\n For site navigation reach for the navigation family (@nav), not @list.\\n*/\\n\\n/* Vertical list with default gap. */\\n@recipe list {\\n flex flex-col gap-1\\n}\\n\\n/* Single list item. */\\n@recipe list-item {\\n flex items-center gap-2 rounded-md px-3 py-2 text-sm text-foreground\\n}\\n\\n/* List with dividing borders between items. */\\n@recipe list-bordered {\\n divide-y divide-border rounded-md border border-border\\n}\\n\\n/* Definition list container. */\\n@recipe dl {\\n grid grid-cols-1 gap-2 sm:grid-cols-3 sm:gap-4\\n}\\n\\n/* Definition term. */\\n@recipe dt {\\n text-sm font-medium text-muted-foreground\\n}\\n\\n/* Definition description. */\\n@recipe dd {\\n text-sm text-foreground sm:col-span-2\\n}\\n\",\n \"media.css\": \"/* shortwind: media@0.0.1 sha:000000 */\\n\\n/* @guide\\n @avatar (with @avatar-sm/lg) is a round user image; @thumb is a small square\\n thumbnail. For responsive embeds use @aspect-square or @aspect-video. Avatars\\n are circular by default — don't restyle the radius.\\n*/\\n\\n/* User/profile avatar. */\\n@recipe avatar {\\n inline-flex h-10 w-10 items-center justify-center overflow-hidden rounded-full bg-muted text-sm font-medium text-muted-foreground\\n}\\n\\n/* Small avatar. */\\n@recipe avatar-sm {\\n @avatar h-6 w-6 text-xs\\n}\\n\\n/* Large avatar. */\\n@recipe avatar-lg {\\n @avatar h-14 w-14 text-base\\n}\\n\\n/* Small image thumbnail. */\\n@recipe thumb {\\n h-16 w-16 rounded-md object-cover\\n}\\n\\n/* 1:1 aspect-ratio wrapper. */\\n@recipe aspect-square {\\n aspect-square w-full overflow-hidden rounded-md\\n}\\n\\n/* 16:9 aspect-ratio wrapper. */\\n@recipe aspect-video {\\n aspect-video w-full overflow-hidden rounded-md\\n}\\n\",\n \"navigation.css\": \"/* shortwind: navigation@0.0.1 sha:000000 */\\n\\n/* @guide\\n @nav is the container; links are @nav-link with @nav-link-active for the\\n current page. Tabs mirror that pair: @tab and @tab-active. Use @breadcrumb\\n for trail navigation. Active and inactive are separate recipes — swap the\\n whole class rather than combining them.\\n*/\\n\\n/* Top-level nav container. */\\n@recipe nav {\\n flex items-center gap-1\\n}\\n\\n/* Inactive nav link with hover/focus states. */\\n@recipe nav-link {\\n inline-flex items-center gap-2 rounded-md px-3 py-1.5 text-sm font-medium text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\\n}\\n\\n/* Active nav link. */\\n@recipe nav-link-active {\\n inline-flex items-center gap-2 rounded-md bg-muted px-3 py-1.5 text-sm font-medium text-foreground focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\\n}\\n\\n/* Breadcrumb trail container. */\\n@recipe breadcrumb {\\n flex items-center gap-1.5 text-sm text-muted-foreground\\n}\\n\\n/* Inactive tab control. */\\n@recipe tab {\\n inline-flex items-center gap-2 border-b-2 border-transparent px-3 py-2 text-sm font-medium text-muted-foreground transition-colors hover:border-border hover:text-foreground focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\\n}\\n\\n/* Active tab control. */\\n@recipe tab-active {\\n inline-flex items-center gap-2 border-b-2 border-primary px-3 py-2 text-sm font-medium text-primary focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\\n}\\n\",\n \"progress.css\": \"/* shortwind: progress@0.0.1 sha:000000 */\\n\\n/* @guide\\n A bar is two pieces: @progress-track (the background) wrapping @progress-bar\\n (the fill). For an indeterminate state use @spinner instead — it's a\\n standalone loader, not a bar.\\n*/\\n\\n/* Progress bar track (background). */\\n@recipe progress-track {\\n h-2 w-full overflow-hidden rounded-full bg-muted\\n}\\n\\n/* Progress bar fill. */\\n@recipe progress-bar {\\n h-full rounded-full bg-primary transition-all\\n}\\n\\n/* Indeterminate loading spinner. */\\n@recipe spinner {\\n inline-block h-4 w-4 animate-spin rounded-full border-2 border-border border-t-primary\\n}\\n\",\n \"skeleton.css\": \"/* shortwind: skeleton@0.0.1 sha:000000 */\\n\\n/* @guide\\n Match the skeleton to the shape it stands in for: @skeleton (block),\\n @skeleton-text (a text line), @skeleton-circle (avatar/icon). Size block and\\n text skeletons with raw width/height utilities.\\n*/\\n\\n/* Default rectangular skeleton placeholder. */\\n@recipe skeleton {\\n animate-pulse rounded-md bg-muted\\n}\\n\\n/* Single-line text skeleton. */\\n@recipe skeleton-text {\\n h-4 w-full animate-pulse rounded bg-muted\\n}\\n\\n/* Circular skeleton (avatar/icon). */\\n@recipe skeleton-circle {\\n h-10 w-10 animate-pulse rounded-full bg-muted\\n}\\n\",\n \"surface.css\": \"/* shortwind: surface@0.0.1 sha:000000 */\\n\\n/* @guide\\n @surface / @surface-muted / @surface-accent set a background+foreground pair\\n for a region — one per section. @container (or @container-tight for prose)\\n centers and width-caps content; there is no @container-lg, set a different cap\\n with max-w-* yourself. @divider-h and @divider-v are hairline rules.\\n*/\\n\\n/* Default page/section surface. */\\n@recipe surface {\\n bg-background text-foreground\\n}\\n\\n/* Muted surface — secondary background. */\\n@recipe surface-muted {\\n bg-muted text-foreground\\n}\\n\\n/* Accent surface — soft brand background. */\\n@recipe surface-accent {\\n bg-accent text-accent-foreground\\n}\\n\\n/* Standard content container with max width. */\\n@recipe container {\\n mx-auto w-full max-w-6xl px-4 sm:px-6 lg:px-8\\n}\\n\\n/* Narrow content container for prose. */\\n@recipe container-tight {\\n mx-auto w-full max-w-3xl px-4 sm:px-6\\n}\\n\\n/* Horizontal divider line. */\\n@recipe divider-h {\\n shrink-0 h-px w-full bg-border\\n}\\n\\n/* Vertical divider line. */\\n@recipe divider-v {\\n shrink-0 h-full w-px bg-border\\n}\\n\",\n \"table.css\": \"/* shortwind: table@0.0.1 sha:000000 */\\n\\n/* @guide\\n Wrap the table in @table-container for horizontal overflow, then put @table\\n (or @table-zebra for striped rows) on the <table>. Cells are @th (header) and\\n @td (body); add @tr-hover to a <tr> for row highlighting.\\n*/\\n\\n/* Scroll container for a wide table — keeps overflow horizontal. */\\n@recipe table-container {\\n w-full overflow-x-auto rounded-md border border-border\\n}\\n\\n/* Data table base. */\\n@recipe table {\\n w-full border-collapse text-left text-sm text-foreground\\n}\\n\\n/* Table header cell. */\\n@recipe th {\\n border-b border-border px-3 py-2 text-xs font-semibold uppercase tracking-wide text-muted-foreground\\n}\\n\\n/* Table body cell. */\\n@recipe td {\\n border-b border-border px-3 py-2\\n}\\n\\n/* Row hover state. */\\n@recipe tr-hover {\\n transition-colors hover:bg-muted\\n}\\n\\n/* Table with zebra striping on alternating rows. */\\n@recipe table-zebra {\\n w-full border-collapse text-left text-sm text-foreground [&_tbody_tr:nth-child(odd)]:bg-muted\\n}\\n\",\n \"text.css\": \"/* shortwind: text@0.0.1 sha:000000 */\\n\\n/* @guide\\n Headings are sized by weight, not HTML level: @heading-xl/lg/md/sm — there\\n is no @h1..@h6. Body copy: @body (default), @lead (intro paragraphs), @muted\\n (secondary), @caption (fine print). Use @label for form labels and @link for\\n inline links. Don't append a -text suffix: it's @body not @body-text, @muted\\n not @muted-text, @link not @link-text.\\n*/\\n\\n/* Top-level page heading. */\\n@recipe heading-xl {\\n text-4xl font-bold tracking-tight text-foreground\\n}\\n\\n/* Large section heading. */\\n@recipe heading-lg {\\n text-2xl font-semibold tracking-tight text-foreground\\n}\\n\\n/* Medium heading. */\\n@recipe heading-md {\\n text-xl font-semibold text-foreground\\n}\\n\\n/* Small heading. */\\n@recipe heading-sm {\\n text-base font-semibold text-foreground\\n}\\n\\n/* Default body text. */\\n@recipe body {\\n text-sm leading-6 text-foreground\\n}\\n\\n/* Lead paragraph — larger body copy for hero/intro sections. */\\n@recipe lead {\\n text-lg leading-relaxed text-muted-foreground\\n}\\n\\n/* Muted secondary text. */\\n@recipe muted {\\n text-sm text-muted-foreground\\n}\\n\\n/* Form label text. */\\n@recipe label {\\n text-sm font-medium text-foreground\\n}\\n\\n/* Caption — small supporting text. */\\n@recipe caption {\\n text-xs text-muted-foreground\\n}\\n\\n/* Inline link with hover/focus states. */\\n@recipe link {\\n text-primary underline-offset-2 hover:underline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring\\n}\\n\",\n \"tooltip.css\": \"/* shortwind: tooltip@0.0.1 sha:000000 */\\n\\n/* @guide\\n @tooltip is the floating label bubble — it styles appearance only, so pair it\\n with your own positioning.\\n*/\\n\\n/* Floating tooltip bubble. */\\n@recipe tooltip {\\n pointer-events-none z-50 rounded-md bg-foreground px-2 py-1 text-xs font-medium text-background shadow-md\\n}\\n\",\n};\n","// Representative agent-authored component files used by `shortwind bench`.\n// Every @recipe here resolves against the bundled catalog — the corpus doubles\n// as a check that the recipes an agent naturally reaches for actually exist.\nexport const CORPUS_FILES: Record<string, string> = {\n \"button.tsx\": `export function ButtonShowcase() {\n return (\n <div className=\"@row gap-4 p-6\">\n <button className=\"@btn-primary\">Primary Button</button>\n <button className=\"@btn-primary-sm\">Small Primary</button>\n <button className=\"@btn-primary-lg\">Large Primary</button>\n <button className=\"@btn-secondary\">Secondary Button</button>\n <button className=\"@btn-outline\">Outline Button</button>\n <button className=\"@btn-ghost\">Ghost Button</button>\n <button className=\"@btn-danger\">Danger Button</button>\n </div>\n );\n}`,\n\n \"card.tsx\": `export function ProductCard() {\n return (\n <div className=\"@card-interactive max-w-sm\">\n <div className=\"@card-header\">\n <h3 className=\"@heading-md\">Premium Product</h3>\n <span className=\"@badge-success\">In Stock</span>\n </div>\n <div className=\"@card-body\">\n <p className=\"@body\">\n This is a beautiful product description. It uses multiple utility classes and shortwind recipes to keep output clean and readable for agents.\n </p>\n <div className=\"@row-between mt-4\">\n <span className=\"text-2xl font-bold\">$99.99</span>\n <span className=\"@muted\">Save 20%</span>\n </div>\n </div>\n <div className=\"@card-footer\">\n <button className=\"@btn-secondary-sm\">View Details</button>\n <button className=\"@btn-primary-sm\">Buy Now</button>\n </div>\n </div>\n );\n}`,\n\n \"form.tsx\": `export function LoginForm() {\n return (\n <div className=\"@card max-w-md mx-auto p-8\">\n <h2 className=\"@heading-lg mb-6 text-center\">Welcome Back</h2>\n <form className=\"@stack-md\">\n <div className=\"@field\">\n <label className=\"@label\" htmlFor=\"email\">Email Address</label>\n <input className=\"@input\" id=\"email\" type=\"email\" placeholder=\"you@example.com\" required />\n <span className=\"@help\">We will never share your email.</span>\n </div>\n <div className=\"@field\">\n <label className=\"@label\" htmlFor=\"password\">Password</label>\n <input className=\"@input\" id=\"password\" type=\"password\" required />\n </div>\n <div className=\"@row gap-2\">\n <input className=\"@checkbox\" id=\"remember\" type=\"checkbox\" />\n <label className=\"@label\" htmlFor=\"remember\">Remember me</label>\n </div>\n <button className=\"@btn-primary w-full mt-4\" type=\"submit\">Sign In</button>\n </form>\n </div>\n );\n}`,\n\n \"table.tsx\": `export function UsersTable() {\n return (\n <div className=\"@table-container\">\n <table className=\"@table\">\n <thead>\n <tr className=\"bg-muted/50\">\n <th className=\"@th\">User</th>\n <th className=\"@th\">Status</th>\n <th className=\"@th\">Role</th>\n <th className=\"@th\">Actions</th>\n </tr>\n </thead>\n <tbody>\n <tr className=\"@tr-hover\">\n <td className=\"@td font-medium\">Alice Johnson</td>\n <td className=\"@td\"><span className=\"@badge-success\">Active</span></td>\n <td className=\"@td\">Administrator</td>\n <td className=\"@td\"><button className=\"@btn-ghost-sm\">Edit</button></td>\n </tr>\n <tr className=\"@tr-hover\">\n <td className=\"@td font-medium\">Bob Smith</td>\n <td className=\"@td\"><span className=\"@badge-warning\">Pending</span></td>\n <td className=\"@td\">Editor</td>\n <td className=\"@td\"><button className=\"@btn-ghost-sm\">Edit</button></td>\n </tr>\n <tr className=\"@tr-hover\">\n <td className=\"@td font-medium\">Charlie Brown</td>\n <td className=\"@td\"><span className=\"@badge-danger\">Suspended</span></td>\n <td className=\"@td\">Subscriber</td>\n <td className=\"@td\"><button className=\"@btn-ghost-sm\">Edit</button></td>\n </tr>\n </tbody>\n </table>\n </div>\n );\n}`,\n\n \"layout.tsx\": `export function DashboardLayout({ children }) {\n return (\n <div className=\"@row min-h-screen items-stretch bg-background\">\n <aside className=\"@stack-md w-64 border-r border-border bg-card p-4\">\n <div className=\"flex h-12 items-center px-2 font-bold text-lg\">\n Shortwind Console\n </div>\n <nav className=\"@nav flex-col items-stretch\">\n <a className=\"@nav-link-active\" href=\"/dashboard\">Dashboard</a>\n <a className=\"@nav-link\" href=\"/analytics\">Analytics</a>\n <a className=\"@nav-link\" href=\"/settings\">Settings</a>\n </nav>\n </aside>\n <div className=\"@stack-md flex-1\">\n <header className=\"@row-between h-16 border-b border-border bg-card px-6\">\n <h1 className=\"@heading-sm\">Overview</h1>\n <div className=\"@row gap-4\">\n <button className=\"@btn-icon\"><span className=\"sr-only\">Notifications</span></button>\n <div className=\"h-8 w-8 rounded-full bg-primary/20\" />\n </div>\n </header>\n <main className=\"@container @stack-lg py-8\">\n <div className=\"@grid-3\">\n <div className=\"@card-elevated\">Card 1</div>\n <div className=\"@card-elevated\">Card 2</div>\n <div className=\"@card-elevated\">Card 3</div>\n </div>\n <div className=\"flex-1\">{children}</div>\n </main>\n </div>\n </div>\n );\n}`\n};\n","import { readFile, writeFile } from \"node:fs/promises\";\nimport { readFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { glob } from \"tinyglobby\";\nimport { buildRegistry, parseRecipeFile } from \"@shortwind/core\";\nimport type { Recipe, Registry } from \"@shortwind/core\";\nimport { installedFamilies, readConfig } from \"../project.js\";\n\nexport const ALL_RULES = [\n \"recipe/unknown\",\n \"recipe/cycle\",\n \"recipe/duplicate\",\n \"recipe/unused\",\n \"recipe/no-redundant-utility\",\n \"recipe/bad-suffix-order\",\n \"recipe/conflicting-intent\",\n \"recipe/dynamic-class\",\n \"recipe/no-sibling-overlap\",\n] as const;\n\nexport type Rule = (typeof ALL_RULES)[number];\n\nexport type Severity = \"error\" | \"warning\" | \"info\";\n\nexport type Finding = {\n rule: Rule;\n severity: Severity;\n file: string;\n line: number;\n column: number;\n message: string;\n};\n\nexport type LintOptions = {\n cwd: string;\n rules?: Rule[];\n fix?: boolean;\n content?: string[];\n};\n\nexport type LintResult = {\n ok: boolean;\n findings: Finding[];\n filesFixed: string[];\n};\n\nconst DEFAULT_CONTENT = [\n \"src/**/*.{html,js,jsx,ts,tsx,vue,svelte,astro,md,mdx}\",\n];\n\nexport async function lint(options: LintOptions): Promise<LintResult> {\n const cwd = path.resolve(options.cwd);\n const config = await readConfig(cwd);\n const recipesDir = path.join(cwd, config.recipesDir);\n const enabledRules = new Set<Rule>(options.rules ?? ALL_RULES);\n const findings: Finding[] = [];\n\n const { registry, parseFindings } = loadRegistry(recipesDir, enabledRules);\n findings.push(...parseFindings);\n findings.push(...checkRecipeNames(registry, recipesDir, enabledRules));\n\n const contentGlobs = options.content ?? DEFAULT_CONTENT;\n // tinyglobby evaluates ignore patterns relative to `cwd`; an absolute\n // recipesDir path passed verbatim would either fail to match or match by\n // accident on case-folded filesystems. Use the project-relative form.\n const recipesIgnore = path.posix.join(\n path.relative(cwd, recipesDir).split(path.sep).join(\"/\") || \".\",\n \"**\",\n );\n const files = await glob(contentGlobs, {\n cwd,\n absolute: true,\n onlyFiles: true,\n ignore: [\"**/node_modules/**\", \"**/dist/**\", \"**/.next/**\", recipesIgnore],\n });\n\n const usedRecipes = new Set<string>();\n const filesFixed: string[] = [];\n\n for (const file of files) {\n const source = await readFile(file, \"utf8\");\n const usages = extractClassUsages(source);\n for (const u of usages) {\n for (const token of u.tokens) {\n if (!token.value.startsWith(\"@\")) continue;\n const name = token.value.slice(1);\n if (registry.flattened[name]) usedRecipes.add(name);\n else if (enabledRules.has(\"recipe/unknown\")) {\n findings.push({\n rule: \"recipe/unknown\",\n severity: \"error\",\n file,\n line: token.line,\n column: token.column,\n message: `unknown recipe @${name}`,\n });\n }\n }\n if (enabledRules.has(\"recipe/bad-suffix-order\")) {\n findings.push(...checkUsageSuffixOrder(file, u.tokens, registry));\n }\n if (enabledRules.has(\"recipe/conflicting-intent\")) {\n findings.push(...checkConflictingIntent(file, u.tokens, registry));\n }\n if (enabledRules.has(\"recipe/no-sibling-overlap\")) {\n findings.push(...checkSiblingOverlap(file, u.tokens, registry));\n }\n if (enabledRules.has(\"recipe/dynamic-class\")) {\n findings.push(...checkDynamicClass(file, u.dynamicTokens));\n }\n }\n\n if (enabledRules.has(\"recipe/no-redundant-utility\")) {\n const result = checkRedundantUtility(file, source, registry, options.fix === true);\n findings.push(...result.findings);\n if (options.fix && result.fixed !== null && result.fixed !== source) {\n await writeFile(file, result.fixed);\n filesFixed.push(file);\n }\n }\n }\n\n if (enabledRules.has(\"recipe/unused\")) {\n const recipesByName = new Map<string, Recipe>();\n for (const recs of Object.values(registry.families)) {\n for (const r of recs) recipesByName.set(r.name, r);\n }\n for (const name of Object.keys(registry.flattened)) {\n if (usedRecipes.has(name)) continue;\n const recipe = recipesByName.get(name);\n if (!recipe) continue;\n findings.push({\n rule: \"recipe/unused\",\n severity: \"info\",\n file: path.join(recipesDir, recipe.sourceFile),\n line: recipe.sourceLine,\n column: 1,\n message: `recipe @${name} is defined but never referenced`,\n });\n }\n }\n\n findings.sort((a, b) => {\n if (a.file !== b.file) return a.file.localeCompare(b.file);\n if (a.line !== b.line) return a.line - b.line;\n return a.column - b.column;\n });\n\n const ok = !findings.some((f) => f.severity === \"error\");\n return { ok, findings, filesFixed };\n}\n\nfunction loadRegistry(\n recipesDir: string,\n rules: Set<Rule>,\n): { registry: Registry; parseFindings: Finding[] } {\n const families = installedFamilies(recipesDir);\n const allRecipes: Recipe[] = [];\n const parseFindings: Finding[] = [];\n\n for (const family of families) {\n const filePath = path.join(recipesDir, `${family}.css`);\n const source = readFileSync(filePath, \"utf8\");\n const parsed = parseRecipeFile(source, `${family}.css`);\n if (!parsed.ok) {\n for (const err of parsed.errors) {\n parseFindings.push({\n rule: \"recipe/unknown\",\n severity: \"error\",\n file: filePath,\n line: err.line,\n column: err.column ?? 1,\n message: err.message,\n });\n }\n continue;\n }\n for (const r of parsed.value.recipes) allRecipes.push(r);\n }\n\n const built = buildRegistry(allRecipes);\n if (!built.ok) {\n for (const err of built.errors) {\n const rule = mapErrorCodeToRule(err.code);\n if (!rules.has(rule)) continue;\n parseFindings.push({\n rule,\n severity: \"error\",\n file: path.join(recipesDir, err.file),\n line: err.line,\n column: err.column ?? 1,\n message: err.message,\n });\n }\n return { registry: { flattened: {}, families: {} }, parseFindings };\n }\n return { registry: built.value, parseFindings };\n}\n\n// Explicit table from core's diagnostic codes to lint rules. Keeps the\n// mapping debuggable and prevents a typo in `err.code` from silently being\n// classified as `recipe/unknown`.\nconst ERROR_CODE_RULE: Record<string, Rule> = {\n \"resolve/cycle\": \"recipe/cycle\",\n \"resolve/duplicate-name\": \"recipe/duplicate\",\n \"resolve/unknown-reference\": \"recipe/unknown\",\n};\n\nfunction mapErrorCodeToRule(code: string): Rule {\n return ERROR_CODE_RULE[code] ?? \"recipe/unknown\";\n}\n\nconst SIZE_SUFFIXES = new Set([\"xs\", \"sm\", \"md\", \"lg\", \"xl\"]);\nconst INTENT_SUFFIXES = new Set([\n \"primary\",\n \"secondary\",\n \"ghost\",\n \"danger\",\n \"warning\",\n \"success\",\n \"info\",\n]);\n\nfunction recipeMeta(name: string, familyHint?: string): {\n family: string;\n intent: string | null;\n badOrder: string | null;\n} {\n const family =\n familyHint && (name === familyHint || name.startsWith(`${familyHint}-`))\n ? familyHint\n : name.split(\"-\")[0] ?? name;\n const suffix =\n name === family ? [] : name.slice(family.length + 1).split(\"-\").filter(Boolean);\n let intent: string | null = null;\n let firstSizeIdx = -1;\n let laterIntentIdx = -1;\n\n for (let i = 0; i < suffix.length; i++) {\n const part = suffix[i] ?? \"\";\n if (SIZE_SUFFIXES.has(part)) {\n if (firstSizeIdx === -1) firstSizeIdx = i;\n }\n if (INTENT_SUFFIXES.has(part)) {\n intent ??= part;\n if (firstSizeIdx !== -1) laterIntentIdx = i;\n }\n }\n\n let badOrder: string | null = null;\n if (firstSizeIdx !== -1 && laterIntentIdx !== -1) {\n const reordered = [\n family,\n ...suffix.filter((p) => INTENT_SUFFIXES.has(p)),\n ...suffix.filter((p) => !INTENT_SUFFIXES.has(p) && !SIZE_SUFFIXES.has(p)),\n ...suffix.filter((p) => SIZE_SUFFIXES.has(p)),\n ];\n badOrder = reordered.join(\"-\");\n }\n\n return { family, intent, badOrder };\n}\n\nfunction checkRecipeNames(\n registry: Registry,\n recipesDir: string,\n enabledRules: Set<Rule>,\n): Finding[] {\n if (!enabledRules.has(\"recipe/bad-suffix-order\")) return [];\n const findings: Finding[] = [];\n for (const [family, recipes] of Object.entries(registry.families)) {\n for (const recipe of recipes) {\n const meta = recipeMeta(recipe.name, family);\n if (!meta.badOrder) continue;\n findings.push({\n rule: \"recipe/bad-suffix-order\",\n severity: \"warning\",\n file: path.join(recipesDir, recipe.sourceFile),\n line: recipe.sourceLine,\n column: 1,\n message: `recipe @${recipe.name} uses size before intent; prefer @${meta.badOrder}`,\n });\n }\n }\n return findings;\n}\n\nfunction checkUsageSuffixOrder(\n file: string,\n tokens: ClassUsage[\"tokens\"],\n registry: Registry,\n): Finding[] {\n const findings: Finding[] = [];\n for (const token of tokens) {\n if (!token.value.startsWith(\"@\")) continue;\n const name = token.value.slice(1);\n if (!registry.flattened[name]) continue;\n const meta = recipeMeta(name, familyForRecipe(registry, name));\n if (!meta.badOrder) continue;\n findings.push({\n rule: \"recipe/bad-suffix-order\",\n severity: \"warning\",\n file,\n line: token.line,\n column: token.column,\n message: `@${name} uses size before intent; prefer @${meta.badOrder}`,\n });\n }\n return findings;\n}\n\nfunction checkConflictingIntent(\n file: string,\n tokens: ClassUsage[\"tokens\"],\n registry: Registry,\n): Finding[] {\n const byFamily = new Map<\n string,\n Map<string, { token: ClassUsage[\"tokens\"][number]; name: string }>\n >();\n for (const token of tokens) {\n if (!token.value.startsWith(\"@\")) continue;\n const name = token.value.slice(1);\n if (!registry.flattened[name]) continue;\n const meta = recipeMeta(name, familyForRecipe(registry, name));\n if (!meta.intent) continue;\n const familyIntents =\n byFamily.get(meta.family) ??\n new Map<string, { token: ClassUsage[\"tokens\"][number]; name: string }>();\n familyIntents.set(meta.intent, { token, name });\n byFamily.set(meta.family, familyIntents);\n }\n\n const findings: Finding[] = [];\n for (const [family, intents] of byFamily) {\n if (intents.size < 2) continue;\n const intentNames = Array.from(intents.values())\n .map((entry) => `@${entry.name}`)\n .sort();\n const first = Array.from(intents.values())\n .map((entry) => entry.token)\n .sort((a, b) => a.column - b.column)[0]!;\n findings.push({\n rule: \"recipe/conflicting-intent\",\n severity: \"warning\",\n file,\n line: first.line,\n column: first.column,\n message: `multiple ${family} intents on one element: ${intentNames.join(\", \")}`,\n });\n }\n return findings;\n}\n\nfunction familyForRecipe(registry: Registry, name: string): string | undefined {\n for (const [family, recipes] of Object.entries(registry.families)) {\n if (recipes.some((recipe) => recipe.name === name)) return family;\n }\n return undefined;\n}\n\nfunction checkSiblingOverlap(\n file: string,\n tokens: ClassUsage[\"tokens\"],\n registry: Registry,\n): Finding[] {\n const byFamily = new Map<string, Array<{ token: ClassUsage[\"tokens\"][number]; name: string }>>();\n for (const token of tokens) {\n if (!token.value.startsWith(\"@\")) continue;\n const name = token.value.slice(1);\n if (!registry.flattened[name]) continue;\n const family = familyForRecipe(registry, name) ?? name.split(\"-\")[0] ?? name;\n const arr = byFamily.get(family) ?? [];\n arr.push({ token, name });\n byFamily.set(family, arr);\n }\n\n const findings: Finding[] = [];\n for (const [family, entries] of byFamily) {\n const unique = new Set(entries.map((e) => e.name));\n if (unique.size < 2) continue;\n const first = entries.map((e) => e.token).sort((a, b) => a.column - b.column)[0]!;\n const names = Array.from(unique).map((n) => `@${n}`).sort();\n findings.push({\n rule: \"recipe/no-sibling-overlap\",\n severity: \"warning\",\n file,\n line: first.line,\n column: first.column,\n message: `multiple ${family} recipes on one element: ${names.join(\", \")}`,\n });\n }\n return findings;\n}\n\n// Dynamic recipe names defeat unknown-reference checking and the safelist\n// pass — Tailwind never sees the computed token, so the recipe's expanded\n// utilities won't appear in the bundle unless they're already in another file.\nfunction checkDynamicClass(\n file: string,\n dynamicTokens: ClassUsage[\"dynamicTokens\"],\n): Finding[] {\n const findings: Finding[] = [];\n for (const token of dynamicTokens) {\n if (!token.value.includes(\"@\")) continue;\n findings.push({\n rule: \"recipe/dynamic-class\",\n severity: \"warning\",\n file,\n line: token.line,\n column: token.column,\n message: `dynamic recipe name ${token.value} — Tailwind cannot statically resolve this`,\n });\n }\n return findings;\n}\n\ntype ClassUsage = {\n fileOffset: number;\n // Exact source offset of the first character inside the attribute value\n // (just past the opening quote). raw.length characters from here is the\n // closing quote. Used by the auto-fix writer; indexOf-based location\n // hunting is unsafe because two attributes can share the same raw text.\n valueStart: number;\n raw: string;\n tokens: Array<{ value: string; line: number; column: number }>;\n // Tokens that contain a `${...}` interpolation. Surfaced separately so the\n // dynamic-class rule can flag computed recipe names while the normal token\n // pipeline still treats them as opaque.\n dynamicTokens: Array<{ value: string; line: number; column: number }>;\n // Only string-literal attribute values can be auto-fixed in place;\n // JSX expression containers (className={...}) may wrap clsx() / template\n // literals where blind substring writes would be unsafe.\n fixable: boolean;\n};\n\nconst CLASS_ATTR_STR_RE = /\\b(?:class|className)\\s*=\\s*([\"'])([^\"']*)\\1/g;\nconst CLASS_ATTR_BRACE_RE = /\\b(?:class|className)\\s*=\\s*\\{/g;\nconst STRING_LITERAL_RE = /([\"'`])((?:\\\\.|(?!\\1)[^\\\\])*)\\1/g;\n\nexport function extractClassUsages(source: string): ClassUsage[] {\n const usages: ClassUsage[] = [];\n for (const m of source.matchAll(CLASS_ATTR_STR_RE)) {\n const value = m[2] ?? \"\";\n const attrStart = m.index ?? 0;\n const valueStart = attrStart + m[0]!.length - 1 - value.length;\n const { tokens, dynamicTokens } = tokenizeClassString(source, value, valueStart);\n usages.push({\n fileOffset: attrStart,\n valueStart,\n raw: value,\n tokens,\n dynamicTokens,\n fixable: true,\n });\n }\n\n for (const m of source.matchAll(CLASS_ATTR_BRACE_RE)) {\n const openBrace = (m.index ?? 0) + m[0]!.length - 1;\n const close = findMatchingBrace(source, openBrace);\n if (close === -1) continue;\n const inner = source.slice(openBrace + 1, close);\n for (const sm of inner.matchAll(STRING_LITERAL_RE)) {\n const value = sm[2] ?? \"\";\n if (value.length === 0) continue;\n const literalStart = openBrace + 1 + (sm.index ?? 0);\n const valueStart = literalStart + 1;\n const { tokens, dynamicTokens } = tokenizeClassString(source, value, valueStart);\n if (tokens.length === 0 && dynamicTokens.length === 0) continue;\n usages.push({\n fileOffset: literalStart,\n valueStart,\n raw: value,\n tokens,\n dynamicTokens,\n fixable: false,\n });\n }\n }\n\n return usages;\n}\n\nfunction tokenizeClassString(\n source: string,\n value: string,\n valueStart: number,\n): {\n tokens: Array<{ value: string; line: number; column: number }>;\n dynamicTokens: Array<{ value: string; line: number; column: number }>;\n} {\n const tokens: Array<{ value: string; line: number; column: number }> = [];\n const dynamicTokens: Array<{ value: string; line: number; column: number }> = [];\n let offset = 0;\n for (const piece of value.split(/(\\s+)/)) {\n if (/^\\s+$/.test(piece) || piece.length === 0) {\n offset += piece.length;\n continue;\n }\n const abs = valueStart + offset;\n const { line, column } = offsetToLineCol(source, abs);\n // Tokens containing `${...}` are opaque to the normal pipeline (no merge,\n // no unknown-reference check) but still surfaced separately so the\n // dynamic-class rule can warn on computed recipe names.\n if (piece.includes(\"${\")) {\n dynamicTokens.push({ value: piece, line, column });\n offset += piece.length;\n continue;\n }\n tokens.push({ value: piece, line, column });\n offset += piece.length;\n }\n return { tokens, dynamicTokens };\n}\n\nfunction findMatchingBrace(source: string, openIdx: number): number {\n let depth = 1;\n let i = openIdx + 1;\n while (i < source.length && depth > 0) {\n const ch = source[i];\n if (ch === '\"' || ch === \"'\") {\n const quote = ch;\n i++;\n while (i < source.length) {\n if (source[i] === \"\\\\\") {\n i += 2;\n continue;\n }\n if (source[i] === quote) {\n i++;\n break;\n }\n i++;\n }\n continue;\n }\n if (ch === \"`\") {\n i++;\n while (i < source.length) {\n if (source[i] === \"\\\\\") {\n i += 2;\n continue;\n }\n if (source[i] === \"`\") {\n i++;\n break;\n }\n if (source[i] === \"$\" && source[i + 1] === \"{\") {\n i += 2;\n let exprDepth = 1;\n while (i < source.length && exprDepth > 0) {\n if (source[i] === \"{\") exprDepth++;\n else if (source[i] === \"}\") exprDepth--;\n i++;\n }\n continue;\n }\n i++;\n }\n continue;\n }\n if (ch === \"{\") depth++;\n else if (ch === \"}\") depth--;\n i++;\n }\n return depth === 0 ? i - 1 : -1;\n}\n\nfunction offsetToLineCol(source: string, offset: number): { line: number; column: number } {\n const limit = Math.min(offset, source.length);\n let line = 1;\n let lastNl = -1;\n for (let i = 0; i < limit; i++) {\n if (source[i] === \"\\n\") {\n line++;\n lastNl = i;\n }\n }\n return { line, column: offset - lastNl };\n}\n\nfunction checkRedundantUtility(\n file: string,\n source: string,\n registry: Registry,\n applyFix: boolean,\n): { findings: Finding[]; fixed: string | null } {\n const findings: Finding[] = [];\n let fixed: string | null = applyFix ? \"\" : null;\n let cursor = 0;\n const usages = extractClassUsages(source).sort((a, b) => a.fileOffset - b.fileOffset);\n for (const usage of usages) {\n const expansions = new Set<string>();\n for (const tok of usage.tokens) {\n if (!tok.value.startsWith(\"@\")) continue;\n const exp = registry.flattened[tok.value.slice(1)];\n if (!exp) continue;\n for (const t of exp) expansions.add(t);\n }\n if (expansions.size === 0) continue;\n\n const kept: string[] = [];\n for (const tok of usage.tokens) {\n if (!tok.value.startsWith(\"@\") && expansions.has(tok.value)) {\n findings.push({\n rule: \"recipe/no-redundant-utility\",\n severity: \"info\",\n file,\n line: tok.line,\n column: tok.column,\n message: `${tok.value} is already included by a recipe on this element`,\n });\n continue;\n }\n kept.push(tok.value);\n }\n\n if (fixed !== null && usage.fixable) {\n // valueStart is the exact offset of the first content char (just past\n // the opening quote); raw.length is the content length.\n if (usage.valueStart < cursor) continue;\n fixed += source.slice(cursor, usage.valueStart);\n fixed += kept.join(\" \");\n cursor = usage.valueStart + usage.raw.length;\n }\n }\n if (fixed !== null) fixed += source.slice(cursor);\n return { findings, fixed };\n}\n\nexport function formatFindingsText(findings: Finding[]): string {\n if (findings.length === 0) return \"\";\n return findings\n .map(\n (f) =>\n `${f.file}:${f.line}:${f.column} ${f.severity} ${f.message} [${f.rule}]`,\n )\n .join(\"\\n\");\n}\n","import { readFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { glob } from \"tinyglobby\";\nimport { Tiktoken } from \"js-tiktoken/lite\";\nimport cl100k_base from \"js-tiktoken/ranks/cl100k_base\";\nimport { buildRegistry, parseRecipeFile, type Registry, type Recipe } from \"@shortwind/core\";\nimport { transformContent, loadRegistryFromDir } from \"@shortwind/tailwind\";\nimport { readConfig } from \"../project.js\";\nimport { DEFAULT_RECIPES_CSS } from \"../bench-corpus/default-recipes.js\";\nimport { CORPUS_FILES } from \"../bench-corpus/corpus.js\";\nimport { extractClassUsages } from \"./lint.js\";\n\nexport type BenchOptions = {\n cwd: string;\n corpus?: boolean;\n json?: boolean;\n registry?: string;\n path?: string;\n};\n\nexport type FileBenchResult = {\n filename: string;\n compactClassTokens: number;\n expandedClassTokens: number;\n compactClassBytes: number;\n expandedClassBytes: number;\n compactFileBytes: number;\n expandedFileBytes: number;\n compactLlmTokens: number;\n expandedLlmTokens: number;\n};\n\nexport type BenchTotals = Omit<FileBenchResult, \"filename\">;\n\nexport type BenchResult = {\n files: FileBenchResult[];\n totals: BenchTotals;\n};\n\nconst DEFAULT_CONTENT_GLOBS = [\n \"src/**/*.{html,js,jsx,ts,tsx,vue,svelte,astro,md,mdx}\",\n];\n\nexport async function bench(options: BenchOptions): Promise<BenchResult> {\n const cwd = path.resolve(options.cwd);\n let registry: Registry;\n\n const runOnCorpus = options.corpus || !hasShortwindConfig(cwd);\n\n if (runOnCorpus) {\n registry = loadDefaultRegistry();\n } else {\n const config = await readConfig(cwd);\n const recipesDir = path.join(cwd, config.recipesDir);\n registry = loadRegistryFromDir(recipesDir);\n }\n\n const filesToBench: Array<{ filename: string; content: string }> = [];\n\n if (runOnCorpus) {\n for (const [filename, content] of Object.entries(CORPUS_FILES)) {\n filesToBench.push({ filename, content });\n }\n } else {\n const config = await readConfig(cwd);\n const recipesDir = path.join(cwd, config.recipesDir);\n const recipesIgnore = path.posix.join(\n path.relative(cwd, recipesDir).split(path.sep).join(\"/\") || \".\",\n \"**\",\n );\n const contentGlobs = options.path ? [options.path] : DEFAULT_CONTENT_GLOBS;\n const matchedFiles = await glob(contentGlobs, {\n cwd,\n absolute: true,\n onlyFiles: true,\n ignore: [\"**/node_modules/**\", \"**/dist/**\", \"**/.next/**\", recipesIgnore],\n });\n\n for (const file of matchedFiles) {\n const content = await readFile(file, \"utf8\");\n const relative = path.relative(cwd, file);\n filesToBench.push({ filename: relative, content });\n }\n }\n\n const results: FileBenchResult[] = [];\n const totals: BenchTotals = {\n compactClassTokens: 0,\n expandedClassTokens: 0,\n compactClassBytes: 0,\n expandedClassBytes: 0,\n compactFileBytes: 0,\n expandedFileBytes: 0,\n compactLlmTokens: 0,\n expandedLlmTokens: 0,\n };\n\n for (const { filename, content } of filesToBench) {\n const expanded = transformContent(content, registry, {\n mode: filename.endsWith(\".html\") ? \"html\" : \"jsx\",\n });\n\n const compactUsages = extractClassUsages(content);\n const expandedUsages = extractClassUsages(expanded);\n\n const fileResult: FileBenchResult = {\n filename,\n compactClassTokens: sumTokens(compactUsages),\n expandedClassTokens: sumTokens(expandedUsages),\n compactClassBytes: sumBytes(compactUsages),\n expandedClassBytes: sumBytes(expandedUsages),\n compactFileBytes: Buffer.byteLength(content, \"utf8\"),\n expandedFileBytes: Buffer.byteLength(expanded, \"utf8\"),\n compactLlmTokens: countLlmTokens(content),\n expandedLlmTokens: countLlmTokens(expanded),\n };\n\n results.push(fileResult);\n\n totals.compactClassTokens += fileResult.compactClassTokens;\n totals.expandedClassTokens += fileResult.expandedClassTokens;\n totals.compactClassBytes += fileResult.compactClassBytes;\n totals.expandedClassBytes += fileResult.expandedClassBytes;\n totals.compactFileBytes += fileResult.compactFileBytes;\n totals.expandedFileBytes += fileResult.expandedFileBytes;\n totals.compactLlmTokens += fileResult.compactLlmTokens;\n totals.expandedLlmTokens += fileResult.expandedLlmTokens;\n }\n\n return { files: results, totals };\n}\n\nfunction hasShortwindConfig(cwd: string): boolean {\n return existsSync(path.join(cwd, \"shortwind.config.json\"));\n}\n\nfunction loadDefaultRegistry(): Registry {\n const allRecipes: Recipe[] = [];\n for (const [filename, source] of Object.entries(DEFAULT_RECIPES_CSS)) {\n const parsed = parseRecipeFile(source, filename);\n if (parsed.ok) {\n allRecipes.push(...parsed.value.recipes);\n }\n }\n const resolved = buildRegistry(allRecipes);\n if (!resolved.ok) {\n throw new Error(\n `Failed to build default registry: ${resolved.errors.map((e) => e.message).join(\"; \")}`,\n );\n }\n return resolved.value;\n}\n\nfunction sumTokens(usages: ReturnType<typeof extractClassUsages>): number {\n let count = 0;\n for (const u of usages) {\n count += u.tokens.length;\n }\n return count;\n}\n\nfunction sumBytes(usages: ReturnType<typeof extractClassUsages>): number {\n let count = 0;\n for (const u of usages) {\n count += Buffer.byteLength(u.raw, \"utf8\");\n }\n return count;\n}\n\n// cl100k_base is the encoding used by GPT-4 and the closest broadly-available\n// proxy for modern frontier-model tokenization. Anthropic's tokenizer isn't\n// public, but the *ratio* between compact and expanded forms is what backs the\n// README claim, and BPE schemes agree on that ratio within a few percent.\nlet _encoder: Tiktoken | null = null;\nfunction getEncoder(): Tiktoken {\n if (!_encoder) _encoder = new Tiktoken(cl100k_base);\n return _encoder;\n}\n\nexport function countLlmTokens(str: string): number {\n return getEncoder().encode(str).length;\n}\n\nexport function formatBenchTable(result: BenchResult): string {\n const lines: string[] = [];\n const colWidths = {\n file: 20,\n metric: 12,\n shortwind: 12,\n expanded: 12,\n saved: 10,\n };\n\n for (const f of result.files) {\n colWidths.file = Math.max(colWidths.file, f.filename.length);\n }\n\n const padR = (str: string, width: number): string => str.padEnd(width);\n const padL = (str: string, width: number): string => str.padStart(width);\n\n // Percent the expanded form would grow over compact, framed as \"saved\" from\n // the compact side. exp == 0 short-circuits to avoid divide-by-zero on files\n // with no class usage.\n const formatPct = (compact: number, exp: number): string => {\n if (exp === 0) return \"0.0%\";\n return `${((1 - compact / exp) * 100).toFixed(1)}%`;\n };\n\n const row = (file: string, metric: string, compact: number, exp: number): string =>\n [\n padR(file, colWidths.file),\n padR(metric, colWidths.metric),\n padL(compact.toString(), colWidths.shortwind),\n padL(exp.toString(), colWidths.expanded),\n padL(formatPct(compact, exp), colWidths.saved),\n ].join(\" \");\n\n const header = [\n padR(\"File\", colWidths.file),\n padR(\"Metric\", colWidths.metric),\n padL(\"Shortwind\", colWidths.shortwind),\n padL(\"Expanded\", colWidths.expanded),\n padL(\"Saved\", colWidths.saved),\n ].join(\" \");\n\n lines.push(header);\n lines.push(\"-\".repeat(header.length));\n\n for (const f of result.files) {\n lines.push(row(f.filename, \"Class Words\", f.compactClassTokens, f.expandedClassTokens));\n lines.push(row(\"\", \"Class Bytes\", f.compactClassBytes, f.expandedClassBytes));\n lines.push(row(\"\", \"File Bytes\", f.compactFileBytes, f.expandedFileBytes));\n lines.push(row(\"\", \"File Tokens\", f.compactLlmTokens, f.expandedLlmTokens));\n lines.push(\"-\".repeat(header.length));\n }\n\n lines.push(row(\"TOTAL\", \"Class Words\", result.totals.compactClassTokens, result.totals.expandedClassTokens));\n lines.push(row(\"\", \"Class Bytes\", result.totals.compactClassBytes, result.totals.expandedClassBytes));\n lines.push(row(\"\", \"File Bytes\", result.totals.compactFileBytes, result.totals.expandedFileBytes));\n lines.push(row(\"\", \"File Tokens\", result.totals.compactLlmTokens, result.totals.expandedLlmTokens));\n lines.push(\"-\".repeat(header.length));\n\n return lines.join(\"\\n\");\n}\n"],"mappings":"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"}