@getvitops/cli 8.3.0-alpha.3 → 8.3.0-alpha.5
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.
- package/CHANGELOG.md +51 -0
- package/dist/cli.mjs +218 -29
- package/dist/cli.mjs.map +1 -1
- package/package.json +3 -3
- package/skill/SKILL.md +5 -54
package/dist/cli.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.mjs","names":["parse","SETUP_HELP","sleep","parse","formatSetupPlan","planSetup","parse","planAds","formatPlanAds","hasAdsDrift","formatAdsSummary","parse","formatSummary","parse","parse","formatEmailPlan","hasEmailDrift","backoffSchedule","formatEmailSummary","ghRepoNwo","ghBranches","ghDefaultBranch","ghMainProtected","ghEnvironments","parse","planHosting","formatPlan","formatSummary","planTurnstile","formatTurnstilePlan","hasTurnstileDrift","formatTurnstileSummary","e","lineAt","CLASS_ATTR","formatMediaPlan","parse","planReviews","formatReviewsPlan","readReviewsCache"],"sources":["../src/args.ts","../src/commands/shared.ts","../src/credentials.ts","../src/help.ts","../src/commands/search.ts","../src/prompt.ts","../src/commands/ads.ts","../src/commands/audit.ts","../src/commands/crm.ts","../src/commands/email.ts","../src/prerender-check.ts","../src/commands/emdash.ts","../src/commands/setup.ts","../src/agents.ts","../src/css-scan.ts","../src/lint-css.ts","../src/lint-markup.ts","../src/lint-tokens.ts","../src/lint.ts","../src/cli.ts"],"sourcesContent":["/**\n * A typed wrapper around `node:util`'s `parseArgs`, used by every `vitops` subcommand.\n *\n * `parseArgs` types every value as `string | boolean | string[] | undefined` regardless\n * of the `type`/`default` declared for it, so every call site otherwise repeats its own\n * `values.x as string` / `as boolean` cast. The accessors below carry that cast once,\n * at the point where the option's actual shape (has a default vs. optional vs. a flag)\n * is known.\n */\nimport { parseArgs, type ParseArgsOptionsConfig } from 'node:util';\nimport { resolve } from 'node:path';\n\nexport function parse<T extends ParseArgsOptionsConfig>(\n argv: string[],\n options: T,\n allowPositionals = false,\n) {\n const { values, positionals } = parseArgs({\n args: argv,\n options,\n allowPositionals,\n strict: true,\n });\n // `values`'s type is a mapped type keyed by literal option names, which TS\n // cannot index with a generic `keyof T` — the runtime shape is exactly\n // `Record<string, string | boolean | string[] | undefined>` regardless, so the\n // accessors below read through that instead of fighting the mapped type.\n const raw = values as Record<string, string | boolean | string[] | undefined>;\n return {\n /** An option with a `default` — always a string. */\n str: (k: keyof T & string): string => raw[k] as string,\n /** An option with no `default` — may be absent. */\n opt: (k: keyof T & string): string | undefined => raw[k] as string | undefined,\n /** A `type: 'boolean'` option. */\n flag: (k: keyof T & string): boolean => raw[k] === true,\n /** A `multiple: true` string option. */\n list: (k: keyof T & string): string[] => (raw[k] as string[] | undefined) ?? [],\n /** A path option, resolved against the cwd — folds in `resolve(values.x as string)`. */\n path: (k: keyof T & string): string => resolve(raw[k] as string),\n /** Raw `parseArgs` output, for the rare case none of the above fit. */\n values,\n positionals,\n };\n}\n\n/** `(err as Error).message`, named — every `catch` block wants this. */\nexport function msg(err: unknown): string {\n return (err as Error).message;\n}\n","/**\n * Helpers shared by the `vitops` subcommands that read a `Config` (site.json) —\n * `legal`, `robots`, `reviews`, `icons`, and the `search`/`ads` subcommand groups\n * (in `commands/search.ts` / `commands/ads.ts`).\n */\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport { resolveConfig, type Config } from '@getvitops/generator';\nimport { msg } from '../args.ts';\n\nexport function fail(message: string): never {\n console.error(`✖ ${message}`);\n process.exit(1);\n}\n\n/** The `--input`/`--site-env` pair every `Config`-anchored command accepts. */\nexport const SITE_OPTS = {\n input: { type: 'string', short: 'i', default: 'site.json' },\n 'site-env': { type: 'string', default: 'production' },\n} as const;\n\n/** Read a JSON file, or exit — the shape shared by every config loader below. */\nexport function readJsonOrFail(path: string, notFoundLabel: string): unknown {\n if (!existsSync(path)) fail(`${notFoundLabel} not found: ${path}`);\n try {\n return JSON.parse(readFileSync(path, 'utf8'));\n } catch (err) {\n fail(`could not parse JSON: ${msg(err)}`);\n }\n}\n\n/**\n * Read + resolve a site config, or exit with a message.\n *\n * Accepts JSON, or a JS/TS module with a default export — deliberately not YAML,\n * which would mean a parser dependency for one command. `resolveConfig` is\n * documented as taking an already-parsed object precisely so the loader is the\n * consumer's choice; anyone on YAML can convert it or export from a module.\n */\nexport async function loadConfig(path: string, siteEnv: string): Promise<Config> {\n if (!existsSync(path)) fail(`site config not found: ${path}`);\n let raw: unknown;\n if (/\\.(json)$/.test(path)) {\n raw = readJsonOrFail(path, 'site config');\n } else {\n try {\n raw = (await import(pathToFileURL(path).href)).default;\n } catch (err) {\n fail(`could not import ${path}: ${msg(err)}`);\n }\n if (raw == null) fail(`${path} has no default export`);\n }\n try {\n return resolveConfig(raw, siteEnv);\n } catch (err) {\n // resolveConfig already formats one issue per line.\n fail(msg(err));\n }\n}\n\n/** `loadConfig`, from an args object built by `SITE_OPTS` — the repeated preamble. */\nexport async function siteConfig(a: {\n path: (k: 'input') => string;\n str: (k: 'site-env') => string;\n}): Promise<Config> {\n return loadConfig(a.path('input'), a.str('site-env'));\n}\n\n/**\n * Print a plan, then decide whether to keep going.\n *\n * Covers the shape shared by `reviews` and `search notify`: print the plan,\n * stop silently if it's `blocked`, stop with a message under `--dry`. `search\n * setup` and `ads setup` each combine `--check`/`--dry` differently enough\n * (a distinct check-success message, or one branch covering both flags with no\n * per-branch message) that forcing them through this same shape would either\n * lose behavior or need a configuration object bigger than the duplication it\n * removes — they stay bespoke.\n */\nexport function printPlanAndDryGate(opts: {\n planText: string;\n /** Truthy blocks — `indexing`'s plan carries the reason as a string here. */\n blocked: unknown;\n dry: boolean;\n dryMessage: string;\n}): boolean {\n console.log(opts.planText);\n if (opts.blocked) return false;\n if (opts.dry) {\n console.log(`\\n(--dry: ${opts.dryMessage})`);\n return false;\n }\n return true;\n}\n\n/**\n * Filter a list to the one item matching `filterValue` (a `--domain`/`--provider`\n * flag), or fail naming what's actually configured. The `--domain`/`--provider`\n * \"found none matching\" shape shared by `toSearchConsoleSetup` and `toAdProperties`.\n */\nexport function pickOne<T>(\n items: T[],\n filterValue: string | undefined,\n keyOf: (item: T) => string,\n label: { flag: string; source: string },\n): T[] {\n if (!filterValue) return items;\n const one = items.find((i) => keyOf(i) === filterValue);\n if (!one)\n fail(\n `--${label.flag} \"${filterValue}\" is not in ${label.source} (have: ${items.map(keyOf).join(', ') || 'none'})`,\n );\n return [one];\n}\n\n/** `mkdirSync(dirname(path), { recursive: true }); writeFileSync(path, contents)`. */\nexport function writeOut(path: string, contents: string): void {\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, contents);\n}\n","/**\n * Credential loaders shared by `search setup`, `search notify`, `ads setup` and\n * `reviews` — every command that talks to Google or Cloudflare. Never reads a\n * config file: these are secrets, and a `site.json` is committed.\n */\nimport { existsSync, readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport {\n SCOPES,\n parseServiceAccount,\n adcCredentialsPath,\n adcQuotaProject,\n parseAdcUser,\n type GoogleCredential,\n type ServiceAccount,\n} from '@getvitops/utils/indexing';\nimport type { GoogleOAuth } from '@getvitops/utils/onboarding';\nimport { fail } from './commands/shared.ts';\n\n/**\n * The Search Console credential, from either supported source.\n *\n * Never from the config file: it is the one genuine secret in this command, and a\n * `site.json` is committed. Returns `undefined` rather than failing, so a site\n * running IndexNow alone doesn't need a GCP project at all.\n */\nexport function loadServiceAccount(): ServiceAccount | undefined {\n const inline = process.env.VITOPS_GSC_SERVICE_ACCOUNT;\n const path = process.env.GOOGLE_APPLICATION_CREDENTIALS;\n const raw = inline ?? (path && existsSync(path) ? readFileSync(path, 'utf8') : undefined);\n if (!raw) return undefined;\n // An ADC file is not a malformed service account, it is a different KIND of\n // credential — and one this command already supports. `GOOGLE_APPLICATION_CREDENTIALS`\n // is exactly where `gcloud auth application-default login` writes an\n // `authorized_user` file, so parsing it as a service account died on \"missing\n // client_email or private_key\" while holding a perfectly usable user credential.\n // Hand it to the OAuth path rather than failing.\n if (parseAdcUser(raw)) return undefined;\n try {\n return parseServiceAccount(raw);\n } catch (err) {\n fail(\n `${inline ? 'VITOPS_GSC_SERVICE_ACCOUNT' : `GOOGLE_APPLICATION_CREDENTIALS (${path})`}: ${(err as Error).message}`,\n );\n }\n}\n\n/**\n * The Cloudflare DNS credential. From the environment only — a `site.json` is\n * committed, and this is a real secret. A `Zone:DNS:Edit` token (the standard\n * \"Edit zone DNS\" template, which also carries `Zone:Read`).\n */\nexport function loadCloudflareToken(): string | undefined {\n return process.env.CLOUDFLARE_API_TOKEN || undefined;\n}\n\n/** The message printed everywhere a missing Cloudflare token blocks a command. */\nexport const CLOUDFLARE_TOKEN_HINT =\n 'set CLOUDFLARE_API_TOKEN (a Zone:DNS:Edit token — the standard \"Edit zone DNS\" template, ' +\n 'which also carries the Zone:Read that the zone-by-name lookup needs)';\n\n/**\n * Set when the Google credential came from an ADC file rather than the env vars.\n *\n * Provenance matters for exactly one check: an ADC credential authenticates through\n * a shared OAuth client that owns no project, so it CANNOT work without\n * `site.google.project`, while a credential from your own OAuth client works fine\n * without one. Knowing which we hold is what turns Google's \"requires a quota\n * project\" 403 into an error that names the field and the likely value.\n */\nlet adcOrigin: { path: string; project?: string } | undefined;\n\n/**\n * Refuse an ADC credential with no project, before spending a request on a 403.\n *\n * This is the mistake waiting for the *second* site: the command is copied from\n * whatever onboarded the first one, `site.google.project` is left out, and Google\n * answers with a message about quota projects that names neither the config field\n * nor the file the credential came from.\n */\nexport function requireAdcProject(project: string | undefined, input: string): void {\n if (!adcOrigin || project) return;\n fail(\n `this Google credential comes from ${adcOrigin.path}, which authenticates through a shared ` +\n `OAuth client that owns no project — so it needs \"site.google.project\" in ${input} to say ` +\n `which project the API usage belongs to.` +\n (adcOrigin.project\n ? `\\n gcloud recorded \"${adcOrigin.project}\" as the quota project for that login, so that is probably the value.`\n : '') +\n `\\n One project per site keeps each site's usage and billing separate. That project also ` +\n `needs searchconsole.googleapis.com and siteverification.googleapis.com enabled, and the ` +\n `identity needs serviceusage.services.use on it.` +\n `\\n Or set VITOPS_GOOGLE_CLIENT_ID / _CLIENT_SECRET / _REFRESH_TOKEN to use your own OAuth client instead.`,\n );\n}\n\n/**\n * The Google user-OAuth credential (refresh-token flow), from the environment.\n *\n * A user token, not a service account: onboarding acts *as a person* who can own\n * the sites. All-or-nothing — a partial set is a misconfiguration worth naming\n * rather than a reason to run degraded.\n */\nexport function loadGoogleOAuth(): GoogleOAuth | undefined {\n const clientId = process.env.VITOPS_GOOGLE_CLIENT_ID;\n const clientSecret = process.env.VITOPS_GOOGLE_CLIENT_SECRET;\n const refreshToken = process.env.VITOPS_GOOGLE_REFRESH_TOKEN;\n // Explicit vars win over ambient credentials: an operator who set them meant them,\n // and a stale gcloud login silently overriding a deliberate CI secret is the worst\n // available precedence. Fall back to ADC only when nothing was set at all — that is\n // what makes \"I'm logged in to gcloud\" enough for a one-time human `search setup`.\n if (!clientId && !clientSecret && !refreshToken) {\n const path =\n process.env.GOOGLE_APPLICATION_CREDENTIALS ??\n adcCredentialsPath({ env: process.env, platform: process.platform, home: homedir(), join });\n if (path && existsSync(path)) {\n const raw = readFileSync(path, 'utf8');\n const adc = parseAdcUser(raw);\n if (adc) {\n // Remember that this came from ADC, and what project gcloud recorded with it.\n // A shared-client credential is refused outright without a project, so the\n // preflight below can name the likely value instead of leaving a bare 403.\n const recorded = adcQuotaProject(raw);\n adcOrigin = recorded ? { path, project: recorded } : { path };\n return adc;\n }\n }\n return undefined;\n }\n const missing = [\n ['VITOPS_GOOGLE_CLIENT_ID', clientId],\n ['VITOPS_GOOGLE_CLIENT_SECRET', clientSecret],\n ['VITOPS_GOOGLE_REFRESH_TOKEN', refreshToken],\n ]\n .filter(([, v]) => !v)\n .map(([k]) => k);\n if (missing.length) fail(`incomplete Google OAuth credential — missing ${missing.join(', ')}`);\n return { clientId: clientId!, clientSecret: clientSecret!, refreshToken: refreshToken! };\n}\n\n/**\n * Whether `oauth` can actually be used to read live state.\n *\n * An ADC credential with no project is refused outright by `requireAdcProject` —\n * so for the purposes of \"can this dry run read live state\", it is not usable\n * credentials, the same as no credential at all.\n */\nexport function isOauthUsable(\n oauth: GoogleOAuth | undefined,\n project: string | undefined,\n): boolean {\n return !!oauth && !(adcOrigin && !project);\n}\n\n/**\n * The Google identity for `search notify`, preferring a service account.\n *\n * Search Console accepts either, and the preference is about *where this command\n * runs*: it fires on every deploy, in CI, and a service account has no expiry,\n * while a refresh token can be revoked — and for an OAuth client still in\n * *Testing* publishing status Google expires it after 7 days, which is a bad\n * property for a deploy step. So the robot wins when it is available.\n *\n * The fallback is the point, though. `search setup` needs user OAuth (verifying\n * a site makes the caller an owner, which should be a person), so a consumer who\n * ran setup already has a Google credential. Demanding a second, unrelated one\n * for the other half of the same command bought nothing.\n */\nexport function loadSearchCredential(): GoogleCredential | undefined {\n const account = loadServiceAccount();\n if (account) return { kind: 'service-account', account, scope: SCOPES.webmasters };\n const oauth = loadGoogleOAuth();\n return oauth ? { kind: 'oauth', oauth } : undefined;\n}\n\n/** Both spellings, for an error that has to tell you what to actually set. */\nexport const CREDENTIAL_HINT =\n 'a service account in VITOPS_GSC_SERVICE_ACCOUNT / GOOGLE_APPLICATION_CREDENTIALS, or a user OAuth credential in VITOPS_GOOGLE_CLIENT_ID / VITOPS_GOOGLE_CLIENT_SECRET / VITOPS_GOOGLE_REFRESH_TOKEN';\n\n/**\n * The HubSpot private-app access token, from the environment. Never the\n * config file — same rule as every other credential here.\n */\nexport function loadHubSpotToken(): string | undefined {\n return process.env.VITOPS_HUBSPOT_ACCESS_TOKEN || undefined;\n}\n\n/**\n * The Zoho self-client OAuth credential, for Bigin. All-or-nothing, matching\n * `loadGoogleOAuth`'s own rule: a partial set is a misconfiguration worth\n * naming, not a reason to run degraded.\n */\nexport function loadZohoOAuth():\n | { clientId: string; clientSecret: string; refreshToken: string }\n | undefined {\n const clientId = process.env.VITOPS_ZOHO_CLIENT_ID;\n const clientSecret = process.env.VITOPS_ZOHO_CLIENT_SECRET;\n const refreshToken = process.env.VITOPS_ZOHO_REFRESH_TOKEN;\n if (!clientId && !clientSecret && !refreshToken) return undefined;\n const missing = [\n ['VITOPS_ZOHO_CLIENT_ID', clientId],\n ['VITOPS_ZOHO_CLIENT_SECRET', clientSecret],\n ['VITOPS_ZOHO_REFRESH_TOKEN', refreshToken],\n ]\n .filter(([, v]) => !v)\n .map(([k]) => k);\n if (missing.length) fail(`incomplete Zoho OAuth credential — missing ${missing.join(', ')}`);\n return { clientId: clientId!, clientSecret: clientSecret!, refreshToken: refreshToken! };\n}\n\n/**\n * The Jobber self-client OAuth credential. All-or-nothing, matching\n * `loadZohoOAuth`'s own rule. Jobber's refresh token must be static — Refresh\n * Token Rotation off in the app's Developer Center — since a Worker reading a\n * static env var cannot persist a rotated one; `jobber-token.ts` detects a\n * rotated token at exchange time and names the fix.\n */\nexport function loadJobberOAuth():\n | { clientId: string; clientSecret: string; refreshToken: string }\n | undefined {\n const clientId = process.env.VITOPS_JOBBER_CLIENT_ID;\n const clientSecret = process.env.VITOPS_JOBBER_CLIENT_SECRET;\n const refreshToken = process.env.VITOPS_JOBBER_REFRESH_TOKEN;\n if (!clientId && !clientSecret && !refreshToken) return undefined;\n const missing = [\n ['VITOPS_JOBBER_CLIENT_ID', clientId],\n ['VITOPS_JOBBER_CLIENT_SECRET', clientSecret],\n ['VITOPS_JOBBER_REFRESH_TOKEN', refreshToken],\n ]\n .filter(([, v]) => !v)\n .map(([k]) => k);\n if (missing.length) fail(`incomplete Jobber OAuth credential — missing ${missing.join(', ')}`);\n return { clientId: clientId!, clientSecret: clientSecret!, refreshToken: refreshToken! };\n}\n","/**\n * The CLI's help text, and the addressing that lets every subcommand answer\n * `--help`.\n *\n * It lives beside `cli.ts` rather than inside it for one reason: `cli.ts` calls\n * `main()` at import time, so nothing can import it to check anything. The\n * drift guard in `help.test.ts` needs `COMMANDS` and `helpSection` as values.\n */\n\nexport const HELP = `vitops — generate design-system outputs from a design-system.json\n\nAnywhere a command takes --input, that file may be a design-system.json OR the\nlarger site config that embeds one (company.json / site.json). The two are told\napart by shape, and a site config also supplies the site-level facts generation\nreads — its default colour scheme, its legal documents, its icon sprite.\n\nUsage:\n vitops generate [options] Generate platform output from a config\n vitops init [options] Scaffold a starter design-system.json\n vitops validate <file> Validate a config against the schema it says it is\n vitops favicon [options] Generate a favicon set from a source image\n vitops agents [sync] [options] Link the agent skills + keep a short managed block at the top of AGENTS.md\n vitops docs [topic] Print live design-system reference docs to stdout\n vitops lint [opts] [files] Report classes that resolve to nothing + primitives you re-implemented\n vitops legal [options] Render legal documents from a site config\n vitops robots [options] Render robots.txt from a site config's environment\n vitops reviews [options] Fetch + cache Google Business Profile reviews\n vitops icons [options] Report which icons your source uses, and build the sprite\n vitops search <sub> [opts] Search Console: onboard domains (setup) + notify deploys (notify)\n vitops ads <sub> [opts] Ad properties: verify domains (setup) + emit pixels (tags) + lint\n vitops setup <sub> [opts] Scripted onboarding: repo, hosting, domain, search, ads, all\n vitops email <sub> [opts] Sender-domain onboarding: check (setup) + deploy guard (check)\n vitops media [options] Encode raw video into web-ready WebM + MP4 + poster\n vitops audit [options] Performance + accessibility budgets over a built site (Lighthouse + axe)\n\nGenerate options:\n -i, --input <path> design-system.json or site config (default: ./design-system.json)\n -f, --format <list> bricks | css | tailwind | design (comma-separated; default: bricks)\n design emits DESIGN.md only (the agent-facing brief) — pair it\n with --out . to write it beside AGENTS.md, or compose:\n --format css,design\n -o, --out <dir> Output directory (default: ./dist)\n --theme <name> Which designSystem.themes entry to build, when --input is\n a site config (default: its defaultTheme, else \"default\")\n --site <path> Site config, when it is a different file from --input.\n Emits legal/*.html into the output directory (see:\n vitops legal). Omitted with a plain design-system.json:\n no legal files.\n --site-env <env> Environment whose A/B variant applies (default: production)\n\nInit options:\n -o, --out <path> Where to write (default: ./design-system.json, or\n ./site.json with --site)\n --force Overwrite an existing file\n --site Write a site config (Config) instead of a bare\n design-system.json — embeds a starter design system at\n designSystem.themes.default, since a Config can't\n merely reference a sibling file\n --name <name> organization.name, when --site is set\n --url <url> site.environments.{production,dev}.url, when --site is set\n\nValidate options:\n <file> Config to check (default: ./design-system.json). Either\n kind — a design-system.json or the config that embeds one.\n -i, --input <path> Same, as a flag\n --site-env <env> Environment whose A/B variant applies (default: production)\n\n Checks the schema, then the things a schema cannot: that every role points at a\n palette hue that exists, that every var() an authored declaration references\n resolves to a token this config emits, and that the colour grammar is current.\n\nFavicon options:\n -i, --input <path> Source SVG or PNG (required)\n -o, --out <dir> Output directory (default: ./public)\n --low-res <path> Optional simplified source for the 16px icon\n --background <hex> Background under the maskable icons, which must be\n opaque (default: #ffffff)\n\nAgents options:\n -i, --input <path> design-system.json or site config (default: ./design-system.json;\n validated if present)\n --theme <name> Theme to read, when --input is a site config\n -o, --out <path> Doc file to update, idempotently (default: ./AGENTS.md). Only the fenced\n \\`<!-- vitops:start -->\\` block is written: added at the top when absent,\n replaced in place when present, everything else left alone. It names the\n config and links the vitops skills linked under .agents/skills (the CLI's own\n and those of an installed @getvitops/skills, which this command also links).\n --docs-dir <dir> Legacy layout: write the docs bundle as files to this dir\n instead of linking the packaged skill\n\nDocs options:\n <topic> classes | authoring | formats | color | scales | patterns | elements\n (no topic: list topics with summaries)\n -i, --input <path> site config or design-system.json (default: ./site.json, else\n ./design-system.json)\n --theme <name> Theme to render docs from, when --input is a site config\n --all Print every topic, concatenated\n\nIcons options:\n -i, --input <path> Site config carrying the \"icons\" block (default: ./site.json)\n --site-env <env> Environment whose A/B variant applies (default: production)\n --src <dir> Source to scan for icon usage (default: ./src)\n --sprite Also build the SVG sprite\n -o, --out <dir> Where to write icons.svg with --sprite (default: ./dist)\n --json Machine-readable report on stdout\n\nLint options:\n -i, --input <path> site config or design-system.json (default: ./site.json, else\n ./design-system.json)\n --theme <name> Theme to judge classes against, when --input is a site config\n -f, --format <fmt> Format you build (bricks | css | tailwind; default: bricks).\n Responsive md-* classes are real in css/bricks, inert in tailwind.\n -s, --src <dir> Directory to scan (default: ./src)\n --strict Also fail on suggestions (reuse hints), not just on\n classes that resolve to nothing\n [files...] Lint these paths instead of scanning --src. Unreadable\n and non-source paths are skipped, so this can be wired\n into a pre-commit hook that appends the staged files.\n --fix Rewrite pre-1.0 colour-grammar token references\n (var(--<role>-<suffix>) → var(--color-<target>-<role>…))\n in CSS and <style> blocks. Applied in ONE pass, so the\n renames that rotate cannot compound. Nothing else is\n rewritten — every other finding is a judgement call.\n\n Run this after a major bump: it is the migration tool for anything the\n changelog says was renamed.\n\nLegal options:\n -i, --input <path> Site config: .json, or a .js/.ts module with a default\n export (default: ./site.json)\n -d, --doc <name> privacy | terms | cookies (repeatable; default: those\n enabled in the config)\n -f, --format <fmt> md | html | portable-text (default: md)\n html suits WordPress/Bricks and plain sites;\n portable-text suits EmDash\n -o, --out <dir> Write files here (default: print to stdout)\n --site-env <env> Environment whose A/B variant applies (default: production)\n\n Generated from your config — a starting point, not legal advice. Review before\n publishing, and make sure the config describes what your site actually does.\n\nRobots options:\n -i, --input <path> Site config: .json, or a .js/.ts module with a default\n export (default: ./site.json)\n -o, --out <dir> Write robots.txt here (default: print to stdout)\n --site-env <env> Which site.environments entry to render for (default: production)\n\n Reads the resolved environment's robots directive (falling back to\n site.seo.robots) — the same fact vitops search notify's noindex gate already\n reads. A directive containing noindex disallows every user agent and emits\n no Sitemap line; otherwise it allows everything and links\n site.seo.indexing.sitemapUrl, or the derived <canonical>/sitemap-index.xml.\n\nReviews options:\n -i, --input <path> Site config carrying site.reviews (default: ./site.json)\n --site-env <env> Environment whose A/B variant applies (default: production)\n --location <key> Fetch only this site.reviews.locations key\n --cache <path> Where to read/write the cache (default: .vitops/reviews.json)\n --out <dir> Also write the selected reviews per location as JSON here\n --dry Print the plan and exit. Fetches nothing.\n --check Fetch and diff against the cache; exit non-zero on drift.\n Mutates nothing.\n\n Reviews live only on the legacy Business Profile v4 API — there is no newer\n replacement — and the calling GCP project must be allowlisted by Google before\n any call succeeds (a one-time approval; a 403 says so rather than failing\n opaquely). Auth is user OAuth, the same VITOPS_GOOGLE_CLIENT_ID /\n VITOPS_GOOGLE_CLIENT_SECRET / VITOPS_GOOGLE_REFRESH_TOKEN (or ADC login) that\n vitops search setup uses, scoped to business.manage. The cache is meant to be\n committed: reviews change slowly, and a build should not need a live Google\n credential. Review JSON-LD is off by default (site.reviews.schema) — a business\n marking up reviews of itself is not eligible for Google's rich results and\n risks a manual action.\n\nSearch subcommands:\n vitops search setup [opts] Onboard site.searchConsole domains into GSC\n vitops search notify [opts] Tell search engines about a deploy (sitemap + IndexNow)\n\nSearch setup options:\n -i, --input <path> Site config carrying site.searchConsole (default: ./site.json)\n --site-env <env> Environment whose A/B variant applies (default: production)\n --domain <name> Scope to a single site.searchConsole entry\n --dry Print the plan and exit. Changes nothing, and runs without\n credentials (planning from scratch, which it says).\n --check Report drift and exit non-zero if any domain is not fully\n onboarded. Mutates nothing, but does need credentials —\n drift is a comparison against live state.\n\n For each domain it ensures the apex verification TXT in Cloudflare, verifies\n ownership (DNS_TXT, retried with backoff while DNS propagates — a still-\n unverified domain is reported PENDING, not failed), adds the sc-domain: property,\n and adds any delegatedOwners to the web resource. DNS is only ever created,\n never edited or deleted. Credentials come from the environment:\n CLOUDFLARE_API_TOKEN (Zone:DNS:Edit + Zone:Read — the standard \"Edit zone DNS\"\n template covers both; Zone:Read is what the zone-by-name lookup uses), and\n VITOPS_GOOGLE_CLIENT_ID /\n VITOPS_GOOGLE_CLIENT_SECRET / VITOPS_GOOGLE_REFRESH_TOKEN (a user OAuth token\n scoped to siteverification + webmasters). Granting a Google Group Full-User\n access has no API and is surfaced as a reminder.\n\n Or just be logged in: with no VITOPS_GOOGLE_* set, an Application Default\n Credentials login is used —\n gcloud auth application-default login \\\\\n --scopes=openid,https://www.googleapis.com/auth/siteverification,\\\\\n https://www.googleapis.com/auth/webmasters,https://www.googleapis.com/auth/cloud-platform\n ADC authenticates through a shared OAuth client that owns no project, so it also\n needs site.google.project in the config (sent as x-goog-user-project), both APIs\n enabled on that project, and serviceusage.services.use on it. One project per site\n is what keeps each site's API usage and billing separate.\n\nSearch notify options:\n -i, --input <path> Site config carrying seo.indexing (default: ./site.json)\n --site-env <env> Environment to notify for (default: production). An\n environment whose robots policy says noindex is refused.\n --sitemap <src> Sitemap URL or local path (default: from the config)\n --urls <list> Comma-separated URLs to submit, skipping the diff\n --all Submit every URL in the sitemap, not just what changed\n --dry Print the plan and exit. Makes no requests.\n --check Read-only: ask Google whether seo.indexing.priorityUrls\n are indexed. Exits non-zero if one is not.\n --new-key Print a fresh IndexNow key and exit\n --write-key <dir> Write the IndexNow key file into <dir> (for stacks with\n no Astro integration to do it — Bricks, WordPress)\n --snapshot <path> Changed-URL state file (default: .vitops/sitemap-snapshot.json).\n Persist it between runs (a CI cache) or every run submits\n everything.\n\n What notify can and cannot do: Google exposes no \"request indexing\" API, and its\n sitemap ping endpoint was removed in 2023 — so it resubmits your sitemap\n through the Search Console API and verifies the result with --check. IndexNow\n reaches Bing, Yandex, Naver, Seznam and Yep; Google does not participate.\n Search Console takes either credential, whichever you already have, added as an\n owner of the property: a service account in VITOPS_GSC_SERVICE_ACCOUNT (inline\n JSON) or GOOGLE_APPLICATION_CREDENTIALS (a path), or the same user OAuth\n credential \"search setup\" uses (VITOPS_GOOGLE_CLIENT_ID / _CLIENT_SECRET /\n _REFRESH_TOKEN). The service account is preferred when both are set — this runs\n on every deploy, and it does not expire.\n\nAds subcommands:\n vitops ads setup [opts] Ensure each platform's domain-verification DNS record\n vitops ads tags [opts] Print the consent-gated pixel snippets\n vitops ads lint [opts] Report ad properties the rest of the config can't see\n\nAds setup options:\n -i, --input <path> Site config carrying site.ads (default: ./site.json)\n --site-env <env> Environment whose A/B variant applies (default: production)\n --provider <name> Scope to one site.ads entry (google | meta | linkedin |\n reddit | tiktok | microsoft | pinterest | snapchat)\n --dry Print the plan and exit. Creates nothing. With\n CLOUDFLARE_API_TOKEN set it reads live DNS first, so the\n plan says what is already done; without one it plans from\n scratch and says so, rather than refusing to run.\n --check Report drift and exit non-zero if any property is not\n linked. Mutates nothing, prompts for nothing.\n --no-prompt Never ask for a missing token; fail with the field name\n instead (the default when stdin is not a TTY, e.g. in CI)\n --no-write Don't persist an answered token back into the config\n\n Four platforms verify a domain by DNS TXT — Meta, TikTok, Pinterest, Snapchat —\n and that record is the one thing this creates for you (in Cloudflare, via\n CLOUDFLARE_API_TOKEN; created only, never edited or deleted). Google Ads,\n LinkedIn, Reddit and Microsoft Ads have no domain verification at all: linking\n there is the tag and the account ID, and the run says so rather than skipping in\n silence. No platform Marketing API is called — Meta's needs a system-user token\n and Google's needs an approved developer token, so the final \"Verify\" click stays\n a reminder. The verification token is not a secret (it is published in DNS, which\n is the ownership proof), so it lives in the config and is prompted for on the\n first run.\n\nAds tags options:\n -i, --input <path> Site config carrying site.ads (default: ./site.json)\n --provider <name> Print one platform's tag\n --strategy <s> idle | async | interaction (default: idle)\n\n Prints each pixel as an INERT, consent-gated <script>: type=\"text/plain\" with the\n library URL on data-src, so an undecided visitor's page issues no third-party\n request. For stacks with no Astro integration — Bricks, WordPress, Eleventy.\n Paste into your template; @getvitops/core's consent runtime activates them.\n\nAds lint options:\n -i, --input <path> Site config carrying site.ads (default: ./site.json)\n\n Reports the gaps that are invisible at runtime: a click-ID parameter this\n platform stamps that attribution doesn't capture, a pixel while site.tracking is\n off (every conversion arrives unattributed), and a configured property with no\n tag ID. Exits non-zero on a finding.\n\nCrm check options:\n -i, --input <path> Site config carrying site.crm (default: ./site.json)\n --site-env <env> Environment to resolve (default: production)\n\n Fetches the live field/property list (HubSpot: crm/v3/properties/contacts;\n Bigin: bigin/v2/settings/fields) and reports every fieldMap/trackingFields\n target that doesn't exist there. HubSpot rejects a submission naming an\n unrecognised property; Bigin silently ignores it — this is the only way to\n see a broken mapping before a real submission finds it. Needs\n VITOPS_HUBSPOT_ACCESS_TOKEN, VITOPS_ZOHO_CLIENT_ID/_CLIENT_SECRET/\n _REFRESH_TOKEN, or (unused for now) VITOPS_JOBBER_CLIENT_ID/_CLIENT_SECRET/\n _REFRESH_TOKEN, matching whichever provider site.crm names. Jobber currently\n renders fieldMap/trackingFields as note-text lines rather than app-configured\n custom fields, so there is nothing live to check there yet. Exits non-zero\n on any missing target.\n\nCrm setup options:\n -i, --input <path> Site config carrying site.crm (default: ./site.json)\n --site-env <env> Environment to resolve (default: production)\n --dry Print what would be created and exit. Creates nothing.\n\n HubSpot: creates missing contact properties named by fieldMap/trackingFields.\n Bigin: no such API exists, so this always prints a manual reminder listing\n the fields to add in Zoho Bigin's field editor, then re-run \"crm check\".\n Jobber: app-configured custom fields aren't wired up yet, so this prints a\n reminder instead of creating anything.\n\nSetup repo options:\n --dry Print the plan and exit\n --check Report drift/blocked steps and exit non-zero, mutating nothing\n --public Create the repo public (default: private)\n --platform <p> cloudflare (default) | node — shown for parity with the hosting stage's plan\n --no-dev-resources Same — this stage never provisions D1/R2 either way\n\n Creates the GitHub repo (via \\`gh\\`), pushes main + dev, makes dev the default\n branch, and requests main branch protection + the production/dev environments.\n Idempotent — a re-run of a fully set-up repo is all skips. Branch protection and\n environment reviewers 403 on a private repo without GitHub Pro/Team; that is\n reported as blocked, with the manual Settings path, never as a failure.\n\nSetup hosting options:\n --dry Print the plan and exit\n --check Report drift/blocked steps and exit non-zero, mutating nothing\n --platform <p> cloudflare (default) | node\n --no-dev-resources Skip D1/R2 — a pure static Workers Assets project has\n nothing to provision, just the account id + token\n\n Provisions the isolated dev D1 database + R2 bucket (cloudflare) and sets the\n DEV_D1_ID / CLOUDFLARE_ACCOUNT_ID repo secrets, once \\`vitops setup repo\\` has run.\n CLOUDFLARE_API_TOKEN is always reported as blocked — no API can create one; see\n the printed dashboard link. Requires \\`npx wrangler login\\` first.\n\nSetup domain options:\n -i, --input <path> Site config (default: ./site.json)\n --site-env <env> Environment to check (default: production)\n --dry Print the plan and exit\n --check Exit non-zero if the domain state is inconsistent\n --domain <apex> Print the manual cutover steps for this apex\n\n Plans and checks the consistency between wrangler.jsonc's workers.dev/custom-domain\n state and site.json's environments/domains/robots — it NEVER edits wrangler.jsonc\n or deploys. Cutover overwrites the zone's DNS record and can take an existing live\n site down; that step is manual, on purpose. See SETUP.html's \"Domain strategy\" stage.\n\nSetup turnstile options:\n -i, --input <path> Site config (default: ./site.json)\n --site-env <env> Environment to read (default: production)\n --dry Print the plan and exit (needs no token)\n --check Exit non-zero if no widget/site key is set up; mutates nothing\n --no-write Do not write site.security.turnstile.siteKey back to the config\n --no-secret Do not run \\`wrangler secret put\\` for the new widget's secret\n\n Creates a managed Turnstile widget for the site's hostnames (canonical + each\n environment URL) unless one already covers them, and writes the PUBLIC site key to\n site.security.turnstile.siteKey. The SECRET is returned once at creation: it is piped\n to \\`wrangler secret put VITOPS_TURNSTILE_SECRET\\` and never written to a config file.\n Never edits, rotates or deletes an existing widget.\n Needs CLOUDFLARE_API_TOKEN with \"Account > Turnstile > Edit\", plus CLOUDFLARE_ACCOUNT_ID\n (or a token that can list accounts), and \\`wrangler login\\` for the secret.\n\nSetup search options:\n Forwards every option to \\`vitops search setup\\` unchanged — see \"Search setup\n options:\" above.\n\nSetup ads options:\n Forwards every option to \\`vitops ads setup\\` unchanged — see \"Ads setup options:\"\n above.\n\nSetup all options:\n --dry Print every stage's plan and exit\n --check Report drift across every stage and exit non-zero\n --public Create the repo public, for the repo stage\n --platform <p> cloudflare (default) | node, for the repo + hosting stages\n --no-dev-resources Skip D1/R2 in the hosting stage (repo stage never provisions them)\n\n Runs repo, hosting, search, ads in order. Stops at the first stage that fails;\n a blocked step (the API token, GitHub Pro/Team gating) is reported, not fatal.\n\nEmdash subcommands:\n vitops emdash prepare-db Migrate and seed the local D1 the build prerenders from\n vitops emdash check-prerender [d] Fail if a prerendered page is not a real page\n vitops emdash build [d] astro build, then check-prerender; fails on blocks with no renderer\n\nEmdash prepare-db options:\n (none) Starts \\`astro dev\\` once, calls EmDash's dev-bypass setup (migrates, applies the\n seed's sample content), checks the home page renders, stops. Only writes the\n disposable .wrangler/state. SKIP_PREPARE_LOCAL_DB=1 skips it.\n\nEmdash check-prerender options:\n [dir] Build output to check (default: dist/client). Fails on a page that carries the\n pending-migrations message, is not an HTML document, or has no <title>.\n --strict Also fail (not just warn) when a page uses a type-role class (font-display, …)\n the built CSS has no rule for, links to pages the build did not produce, a page without\n exactly one <h1>, or a form with nowhere to send it (reads ./site.json, or --site=<path>).\n\nEmdash build options:\n [dir] Runs \\`astro build\\`, then check-prerender on [dir] (default: dist/client). Also fails\n when the build reports a block type with no component (\"PortableText\n [components.type] is missing …\"): the page was published without that content.\n --strict Passed through to check-prerender.\n\nEmail subcommands:\n vitops email setup [domain] Onboard a from-address domain, waiting for its DNS to resolve\n vitops email check [opts] Non-blocking deploy guard: warn if a from domain isn't onboarded\n\nEmail setup options:\n -i, --input <path> Site config carrying site.notifications.email (default: ./site.json)\n --site-env <env> Environment whose A/B variant applies (default: production)\n [domain] Onboard this exact name instead of deriving candidates from\n config + a source scan\n --src <dir> Source to scan for from: literals as a supplement to config\n (default: ./src)\n --dry Print the plan and exit. Needs no credential — plans from\n scratch and says so.\n --check Report drift and exit non-zero. Mutates nothing.\n --no-prompt Never ask before enabling a domain (the default when\n stdin is not a TTY, e.g. in CI — it just enables)\n --timeout <s> Cap on each DNS-propagation poll wait, in seconds (default: 5)\n\n The onboarded entity is an EXACT name, and it is usually a subdomain\n (send.example.com), never the apex a from address names. When the exact\n domain is absent but a sibling in the same zone is already onboarded, this\n is reported blocked rather than silently enabling the apex too — enabling\n it rewrites _dmarc.<domain>, which can tighten policy on every OTHER mail\n sent from that domain. Sending from the onboarded sibling is the\n recommended fix. Needs CLOUDFLARE_API_TOKEN (Zone:Read + an Email Sending\n scope — broader than the \"Edit zone DNS\" template other commands use).\n\nEmail check options:\n -i, --input <path> Site config carrying site.notifications.email (default: ./site.json)\n --site-env <env> Environment whose A/B variant applies (default: production)\n --src <dir> Source to scan for from: literals (default: ./src)\n --strict Exit non-zero on drift (the default just warns and exits 0)\n --skip Skip the check entirely (same as VITOPS_SKIP_EMAIL_CHECK)\n\n Meant to run before a deploy. With no CLOUDFLARE_API_TOKEN it reports \"not\n checked\" and exits 0, so a CI job without the secret never fails on the\n check itself. Skip per-site with site.notifications.email.verifySender: false\n when the scan can't see the real from address and the warning would be a\n false positive.\n\nMedia options:\n --raw <dir> Directory of unprocessed video, walked recursively\n (default: ./raw)\n -o, --out <dir> Where the encoded outputs go (default: ./src/assets/processed).\n Subdirectories under --raw are preserved.\n --max-width <px> Cap the output width, keeping aspect ratio (default: 1920;\n 0 disables scaling)\n --crf <n> Quality on VP9's scale, 0-63, lower is better (default: 32).\n The MP4 fallback uses the equivalent on H.264's scale.\n --max-bitrate <r> Optional ceiling, e.g. 2M or 800k (default: none, which is\n constant quality rather than constrained)\n --audio Keep the audio track (default: dropped — the common case is\n a muted autoplay loop)\n --poster-time <s> Timestamp the poster frame is taken from (default: 0, which\n is often black on a clip that fades in)\n --outputs <list> Comma-separated: webm | mp4 | poster (default: all three)\n --manifest <path> Cache file (default: .vitops/media-manifest.json)\n --force Re-encode everything, ignoring the cache\n --dry Print the plan and exit. Encodes nothing.\n\n Needs ffmpeg on PATH — it is an external tool, not an npm dependency, and this\n command fails rather than quietly skipping. Commit the outputs and the manifest:\n ffmpeg output is not reproducible across versions, so re-encoding in CI churns\n the diff on every toolchain bump. Use --force when you mean to re-encode.\n\nAudit options:\n --build Run \\`astro build\\` first (default: audit the existing build)\n --url <base> Audit an already-running server instead of \\`astro preview\\`\n --port <n> Port for \\`astro preview\\` (default: 4398; aborts if taken)\n --dir <path> Project directory (default: .)\n --pages <list> Extra comma-separated paths. The page list is the built sitemap\n plus links in seed/seed.json's primary* and footer* menus; only\n when neither exists are / /privacy /terms /cookies /contact\n guessed. A 404 skips a page (with its reason in the report); a\n 404 on / fails the run.\n --no-defaults Never guess the default pages\n --seed <path> EmDash seed to read menus from (default: seed/seed.json if present)\n --lcp <ms> LCP budget (default: 2500)\n --cls <n> CLS budget (default: 0.1)\n --tbt <ms> Total Blocking Time budget, the INP proxy (default: 200)\n --impact <list> axe impacts that fail a page (default: serious,critical)\n --form-factor <f> mobile | desktop Lighthouse profile (default: mobile)\n -o, --out <dir> Report directory (default: .vitops/audit): report.json, report.md,\n and pages/<page>.{json,md}\n --warn Report without failing (exit 0), for first adoption\n\n Needs the optional packages: vp add -D playwright @axe-core/playwright lighthouse\n && vp exec playwright install chromium (checked before any server starts). A pass means no automated axe violations and the\n budgets held — it is NOT AODA certification; report.md lists the manual checks.\n\nCommon:\n -h, --help Show this help\n`;\n\nexport const SEARCH_HELP = `vitops search — Google Search Console\n\n vitops search setup [opts] Onboard site.searchConsole domains as GSC domain properties\n vitops search notify [opts] Tell search engines a deploy happened (sitemap + IndexNow)\n\nRun \\`vitops --help\\` for the full option list for each subcommand.\n`;\n\nexport const ADS_HELP = `vitops ads — link this site to its ad properties\n\n vitops ads setup [opts] Ensure each platform's domain-verification DNS record\n vitops ads tags [opts] Print the consent-gated pixel snippets\n vitops ads lint [opts] Report ad properties the rest of the config can't see\n\nRun \\`vitops --help\\` for the full option list for each subcommand.\n`;\n\nexport const CRM_HELP = `vitops crm — push form submissions into a client CRM\n\n vitops crm check [opts] Compare site.crm's fieldMap/trackingFields against the live CRM\n vitops crm setup [opts] Create missing HubSpot properties (Bigin/Jobber have no such API)\n\nRun \\`vitops --help\\` for the full option list for each subcommand.\n`;\n\nexport const EMAIL_HELP = `vitops email — sender-domain onboarding checks\n\n vitops email setup [domain] Onboard a from-address domain, waiting for its DNS to resolve\n vitops email check [opts] Non-blocking deploy guard: warn if a from domain isn't onboarded\n\nRun \\`vitops --help\\` for the full option list for each subcommand.\n`;\n\nexport const EMDASH_HELP = `vitops emdash — build-time steps for an EmDash site\n\n vitops emdash prepare-db Migrate and seed the local D1 the build prerenders from\n (SKIP_PREPARE_LOCAL_DB=1 skips)\n vitops emdash check-prerender [d] Fail if a prerendered page under [d] (default dist/client)\n is not a real page\n vitops emdash build [d] astro build, then check-prerender; also fails when the build\n reports a block type with no component (content dropped)\n\nRun \\`vitops --help\\` for the full option list for each subcommand.\n`;\n\nexport const SETUP_HELP = `vitops setup — the scripted path from an empty scaffold to a live site\n\n vitops setup repo [opts] GitHub repo + branch policy (main protected, dev open)\n vitops setup hosting [opts] Dev D1 + R2 + repo secrets (cloudflare) or a Node target\n vitops setup domain [opts] Plan/check the workers.dev-vs-custom-domain consistency\n (never applies the cutover — see SETUP.html)\n vitops setup search [opts] Forwards to \\`vitops search setup\\`\n vitops setup ads [opts] Forwards to \\`vitops ads setup\\`\n vitops setup all [opts] Run repo, hosting, search, ads in order\n\nRun \\`vitops --help\\` for the full option list for each subcommand.\n`;\n\n/**\n * Every command `main()` dispatches. Exported so the unknown-command message\n * and the help drift guard read the same list — the message used to carry its\n * own hand-maintained copy.\n */\nexport const COMMANDS = [\n 'generate',\n 'init',\n 'validate',\n 'favicon',\n 'agents',\n 'docs',\n 'lint',\n 'legal',\n 'robots',\n 'reviews',\n 'icons',\n 'search',\n 'ads',\n 'crm',\n 'setup',\n 'email',\n 'emdash',\n 'media',\n 'audit',\n] as const;\n\n/** Commands that group subcommands instead of taking options directly. */\nexport const SUBCOMMANDS: Record<string, readonly string[]> = {\n search: ['setup', 'notify'],\n ads: ['setup', 'tags', 'lint'],\n crm: ['check', 'setup'],\n setup: ['repo', 'hosting', 'domain', 'turnstile', 'search', 'ads', 'all'],\n email: ['setup', 'check'],\n emdash: ['prepare-db', 'check-prerender', 'build'],\n};\n\n/**\n * The slice of `HELP` documenting one command, addressed by its heading.\n *\n * `HELP` is already organised as `<Command> options:` blocks, so this reads the\n * section rather than duplicating it — a second copy of every option list is a\n * second thing to keep true.\n */\nexport function helpSection(...parts: string[]): string | undefined {\n // Headings capitalise the command only, not the subcommand (\"Search setup\n // options:\"), so capitalising every part silently misses the two-word ones.\n const name = parts.join(' ');\n const title = `${name.charAt(0).toUpperCase()}${name.slice(1)} options:`;\n const start = HELP.indexOf(`\\n${title}\\n`);\n if (start === -1) return undefined;\n const body = HELP.slice(start + 1);\n // Stop at the next top-level heading (`Legal options:`, `Common:`, …).\n const next = body.slice(title.length).search(/\\n[A-Z][A-Za-z ]*:\\n/);\n return next === -1 ? body : body.slice(0, title.length + next);\n}\n\n/**\n * `--help` on a leaf subcommand used to reach `parseArgs`, which is strict and\n * declares no `help` option — so `vitops lint --help` exited non-zero with a\n * bare `Unknown option '--help'`. Every subcommand answers it now, and it is\n * resolved in one place rather than in fifteen `options` objects, so a new\n * command cannot forget to.\n */\nexport const wantsHelp = (argv: string[]): boolean =>\n argv.includes('-h') || argv.includes('--help');\n\n/** The help text to print for `vitops <command> [sub] --help`. */\nexport function helpFor(command: string, argv: string[]): string {\n const sub = argv[0] && !argv[0].startsWith('-') ? argv[0] : undefined;\n return (\n (sub ? helpSection(command, sub) : undefined) ??\n helpSection(command) ??\n (command === 'search'\n ? SEARCH_HELP\n : command === 'ads'\n ? ADS_HELP\n : command === 'crm'\n ? CRM_HELP\n : command === 'setup'\n ? SETUP_HELP\n : command === 'email'\n ? EMAIL_HELP\n : command === 'emdash'\n ? EMDASH_HELP\n : undefined) ??\n HELP\n );\n}\n","/**\n * `vitops search` — everything that talks to search engines: `setup` onboards a\n * domain into Search Console (DNS + verification + property), `notify` tells the\n * engines a deploy happened (sitemap + IndexNow). `notify` is the former\n * top-level `vitops indexing`.\n */\nimport { existsSync, readFileSync } from 'node:fs';\nimport { join, relative, resolve } from 'node:path';\nimport {\n SNAPSHOT_PATH,\n collectEntries,\n formatPlan,\n googleAccessToken,\n inspectUrl,\n keyFileContents,\n newKey,\n plan,\n readSnapshot,\n resolveKeyLocation,\n resolveSitemapUrl,\n submitBatch,\n submitSitemap,\n toSnapshot,\n verifyKeyFile,\n writeSnapshot,\n type GoogleAuthLike,\n type GoogleCredential,\n type IndexingConfig,\n} from '@getvitops/utils/indexing';\nimport {\n addSite,\n backoffSchedule,\n createApexTxt,\n findZoneId,\n formatPlan as formatSetupPlan,\n formatSummary,\n refreshTokenGrant,\n getSite,\n getVerificationToken,\n getWebResource,\n hasDrift,\n listApexTxt,\n ownerUnion,\n plan as planSetup,\n siteUrlFor,\n updateOwners,\n verifyWebResource,\n type DomainResult,\n type DomainSetup,\n type DomainState,\n} from '@getvitops/utils/onboarding';\nimport { parse } from '../args.ts';\nimport {\n CLOUDFLARE_TOKEN_HINT,\n CREDENTIAL_HINT,\n isOauthUsable,\n loadCloudflareToken,\n loadGoogleOAuth,\n loadSearchCredential,\n requireAdcProject,\n} from '../credentials.ts';\nimport { fail, pickOne, printPlanAndDryGate, siteConfig, SITE_OPTS, writeOut } from './shared.ts';\nimport { SEARCH_HELP } from '../help.ts';\nimport type { Config } from '@getvitops/generator';\n\n/**\n * Adapt a `Config` to the indexing module's own option shape.\n *\n * `IndexNowConfig` and `SearchConsoleConfig` are the schema's own types now, so those blocks pass\n * straight through. What this adapter still does is genuine resolution — the origin and the\n * robots policy come from the resolved environment, not from the `seo.indexing` block — see below.\n */\nfunction toIndexingConfig(cfg: Config, siteEnv: string): IndexingConfig {\n const site = cfg.site;\n const indexing = site.seo?.indexing ?? {};\n const env = site.environments?.[siteEnv];\n /*\n * The environment's own URL wins over `domains.canonical`.\n *\n * `domains.canonical` is the production origin — it is what absolute URLs and\n * SEO tags resolve against. Notifying a non-production environment while\n * deriving URLs from the canonical origin would submit *production* URLs under\n * the belief they were staging's, which the noindex gate below would then not\n * catch, because the gate reads the environment and the URLs would not.\n */\n const canonical = env?.url ?? site.domains?.canonical;\n return {\n ...(canonical ? { canonical } : {}),\n ...(indexing.sitemapUrl ? { sitemapUrl: indexing.sitemapUrl } : {}),\n ...(indexing.indexNow ? { indexNow: indexing.indexNow } : {}),\n ...(indexing.searchConsole ? { searchConsole: indexing.searchConsole } : {}),\n ...(indexing.priorityUrls ? { priorityUrls: indexing.priorityUrls } : {}),\n // Fall back to the site-wide policy so a site that states `noindex` once,\n // globally, is still protected.\n ...((env?.robots ?? site.seo?.robots) ? { robots: env?.robots ?? site.seo?.robots } : {}),\n };\n}\n\n/**\n * Adapt a `Config` to the onboarding module's own option shape.\n *\n * `DomainSetup` is the `site.searchConsole.<domain>` block plus its key, so this is a spread and a\n * filter. `--domain` scopes to a single entry, and a name that isn't there is an error, not an\n * empty run.\n */\nfunction toSearchConsoleSetup(cfg: Config, domainFilter: string | undefined): DomainSetup[] {\n const entries = cfg.site.searchConsole ?? {};\n const setups: DomainSetup[] = Object.entries(entries).map(([domain, e]) => ({ ...e, domain }));\n return pickOne(setups, domainFilter, (s) => s.domain, {\n flag: 'domain',\n source: 'site.searchConsole',\n });\n}\n\n/** Read a sitemap from a URL or a local path. */\nconst readSitemap = async (source: string): Promise<string> => {\n if (/^https?:\\/\\//i.test(source)) {\n const res = await fetch(source);\n if (!res.ok) fail(`could not fetch ${source}: ${res.status}`);\n return res.text();\n }\n if (!existsSync(source)) fail(`sitemap not found: ${source}`);\n return readFileSync(source, 'utf8');\n};\n\n/**\n * Attach the site's Google project to a token — for a **user** credential only.\n *\n * A user credential (an ADC login, or `VITOPS_GOOGLE_*`) authenticates through a\n * shared OAuth client that owns no project, so Google refuses the call outright\n * without `x-goog-user-project`. A service account already belongs to a project, and\n * pointing it at a *different* one would newly require `serviceusage.services.use`\n * there — turning a working CI credential into a 403 for no benefit. So the header\n * is sent exactly when it is needed, never as a default.\n */\nfunction searchAuth(\n token: string,\n credential: GoogleCredential,\n cfg: Config,\n input: string,\n): GoogleAuthLike {\n const project = cfg.site?.google?.project;\n // Only a user credential can be an ADC one, so only it can be missing a project.\n if (credential.kind === 'oauth') requireAdcProject(project, input);\n return project && credential.kind === 'oauth' ? { token, quotaProject: project } : token;\n}\n\nconst sleep = (ms: number) => new Promise<void>((r) => setTimeout(r, ms));\n\n/**\n * Tell search engines a deploy happened.\n *\n * Worth being precise about what this is, because the obvious expectation is\n * wrong: **Google has no API that requests indexing.** The Search Console button\n * is not exposed anywhere, URL Inspection is read-only, and the sitemap ping\n * endpoint was removed in 2023. So the Google half of this command resubmits the\n * sitemap and then *verifies* the outcome with `--check`; the immediate-push half\n * is IndexNow, which Google does not participate in.\n *\n * The Indexing API is deliberately absent — see `@getvitops/utils/indexing`'s\n * `gsc.ts`.\n */\nasync function cmdSearchNotify(argv: string[]) {\n const a = parse(argv, {\n ...SITE_OPTS,\n sitemap: { type: 'string' },\n urls: { type: 'string' },\n all: { type: 'boolean', default: false },\n dry: { type: 'boolean', default: false },\n check: { type: 'boolean', default: false },\n 'new-key': { type: 'boolean', default: false },\n 'write-key': { type: 'string' },\n snapshot: { type: 'string', default: SNAPSHOT_PATH },\n });\n\n // Stands alone: generating a key is what you do *before* there is a config to\n // put it in.\n if (a.flag('new-key')) {\n const key = newKey();\n console.log(key);\n console.log(\n `\\n Add to your site config:\\n \"seo\": { \"indexing\": { \"indexNow\": { \"key\": \"${key}\" } } }`,\n );\n console.log(` Then serve it at /${key}.txt — \\`vitops search notify --write-key public\\`.`);\n return;\n }\n\n const site = await siteConfig(a);\n const config = toIndexingConfig(site, a.str('site-env'));\n\n if (a.values['write-key'] !== undefined) {\n const key = config.indexNow?.key;\n if (!key) fail('no site.seo.indexing.indexNow.key in the config — generate one with --new-key');\n const file = join(resolve(a.str('write-key')), `${key}.txt`);\n writeOut(file, keyFileContents(key));\n console.log(`✓ IndexNow key file → ${file}`);\n console.log(` It must be served at ${resolveKeyLocation(config) ?? `/${key}.txt`}`);\n return;\n }\n\n const sitemapUrl = a.opt('sitemap') ?? resolveSitemapUrl(config);\n if (!sitemapUrl)\n fail(\n 'no sitemap to read — set seo.indexing.sitemapUrl or domains.canonical, or pass --sitemap',\n );\n\n const explicitUrls = a\n .opt('urls')\n ?.split(',')\n .map((u) => u.trim())\n .filter(Boolean);\n\n // The sitemap is only needed when the URL set comes from it.\n const current =\n explicitUrls?.length && !a.flag('check')\n ? []\n : (await collectEntries(sitemapUrl, readSitemap)).entries;\n\n const snapshotPath = a.path('snapshot');\n const p = plan({\n config: { ...config, sitemapUrl },\n current,\n previous: readSnapshot(snapshotPath),\n explicitUrls,\n all: a.flag('all'),\n });\n\n const keepGoing = printPlanAndDryGate({\n planText: formatPlan(p),\n // Not a failure. Running this against a `noindex` environment is a coherent\n // thing for a deploy script to do — the config said not to index it, and the\n // command honoured that. Failing the build would punish the correct setup.\n blocked: p.blocked,\n dry: a.flag('dry'),\n dryMessage: 'nothing was submitted',\n });\n if (!keepGoing) return;\n\n const credential = loadSearchCredential();\n let failed = false;\n\n // ── --check: read-only, and nothing else runs ───────────────────────────────\n if (a.flag('check')) {\n if (!p.searchConsole.siteUrl)\n fail('--check needs seo.indexing.searchConsole.siteUrl (the Search Console property)');\n if (!credential) fail(`--check needs a Search Console credential — ${CREDENTIAL_HINT}`);\n if (p.check.length === 0)\n fail('--check needs seo.indexing.priorityUrls — the pages whose indexing matters');\n\n const auth = searchAuth(await googleAccessToken(credential), credential, site, a.str('input'));\n console.log(`\\nInspecting ${p.check.length} priority URL(s):`);\n for (const url of p.check) {\n const r = await inspectUrl(auth, p.searchConsole.siteUrl, url);\n if (r.error) {\n console.log(` ? ${url} — ${r.error}`);\n failed = true;\n } else {\n console.log(\n ` ${r.indexed ? '✓' : '✗'} ${url} — ${r.coverageState ?? r.verdict ?? 'unknown'}`,\n );\n if (!r.indexed) failed = true;\n }\n }\n if (failed) {\n console.error('\\n✖ one or more priority URLs are not indexed');\n process.exit(1);\n }\n console.log('\\n✓ every priority URL is indexed');\n return;\n }\n\n // ── IndexNow ────────────────────────────────────────────────────────────────\n if (p.indexNow.enabled) {\n const { key, keyLocation } = p.indexNow;\n const check = await verifyKeyFile(keyLocation!, key!);\n if (!check.ok) {\n // Hard stop rather than \"submit and hope\": IndexNow answers 202 and then\n // discards the batch when the key doesn't verify, so submitting anyway\n // would print a success it did not earn.\n console.error(`✖ IndexNow key file check failed — ${check.reason}`);\n console.error(` Write it with: vitops search notify --write-key <public dir>`);\n failed = true;\n } else {\n let sent = 0;\n for (const batch of p.indexNow.batches) {\n const r = await submitBatch(p.indexNow, batch);\n if (!r.ok) {\n console.error(`✖ IndexNow: ${r.message ?? r.status}`);\n failed = true;\n break;\n }\n sent += r.urls;\n }\n if (!failed) console.log(`✓ IndexNow: ${sent} URL(s) submitted`);\n }\n }\n\n // ── Search Console ──────────────────────────────────────────────────────────\n if (p.searchConsole.enabled) {\n if (!credential) {\n console.log(`· Search Console: skipped — no credential (${CREDENTIAL_HINT})`);\n } else {\n const auth = searchAuth(\n await googleAccessToken(credential),\n credential,\n site,\n a.str('input'),\n );\n const r = await submitSitemap(auth, p.searchConsole.siteUrl!, sitemapUrl);\n if (r.ok) console.log(`✓ Search Console: sitemap resubmitted`);\n else {\n console.error(`✖ Search Console: ${r.message}`);\n failed = true;\n }\n }\n }\n\n if (failed) process.exit(1);\n\n /*\n * The snapshot is written last, and only on success.\n *\n * Writing it eagerly would record URLs as notified that never were — and since\n * the next run diffs against it, a single transient 503 would drop those pages\n * from every future run, silently and permanently.\n */\n if (current.length > 0) {\n writeSnapshot(snapshotPath, toSnapshot(sitemapUrl, current, new Date().toISOString()));\n console.log(` state → ${relative(process.cwd(), snapshotPath)}`);\n }\n if (p.check.length > 0)\n console.log(\n `\\n Indexing is not instant. Run \\`vitops search notify --check\\` in a day or two to see what Google actually did.`,\n );\n}\n\n/**\n * Onboard domains into Google Search Console as domain properties.\n *\n * Idempotent by construction: the planner decides each step from the domain's live\n * state, so a re-run of a fully-onboarded domain is all skips. `--check` reports\n * drift and exits non-zero without mutating; `--dry` prints the plan and stops.\n * Neither is offline: the state gather runs before the planner either way, so both\n * read from Cloudflare and Google and both need credentials.\n * DNS is only ever *created* — the verification TXT — never edited or removed.\n *\n * Search Console has no user/permission API, so granting a Google Group Full-User\n * access stays manual and is surfaced as a reminder, not attempted.\n */\nasync function cmdSearchSetup(argv: string[]) {\n const a = parse(argv, {\n ...SITE_OPTS,\n domain: { type: 'string' },\n check: { type: 'boolean', default: false },\n dry: { type: 'boolean', default: false },\n });\n\n const cfg = await siteConfig(a);\n const domains = toSearchConsoleSetup(cfg, a.opt('domain'));\n if (domains.length === 0)\n fail('no domains in site.searchConsole — add domains to onboard (keyed by bare hostname)');\n\n const cfToken = loadCloudflareToken();\n const oauth = loadGoogleOAuth();\n const project = cfg.site?.google?.project;\n const usableOauth = isOauthUsable(oauth, project);\n\n // A dry run that mutates nothing should not demand the credentials to mutate.\n // It cannot READ live state without them either, so it plans from \"nothing is\n // done yet\" and says so — which is still the complete account of what a first\n // run would do, and is what makes the plan showable before anyone has\n // provisioned anything. `--check` is different: reporting drift means\n // comparing against reality, so it keeps needing to see it.\n if (a.flag('dry') && (!cfToken || !usableOauth)) {\n const blank: DomainState = {\n zoneId: '',\n txtPresent: false,\n verified: false,\n currentOwners: [],\n propertyExists: false,\n };\n console.log(\n formatSetupPlan(planSetup({ domains }, new Map(domains.map((d) => [d.domain, blank])))),\n );\n const missing = [\n ...(cfToken ? [] : ['CLOUDFLARE_API_TOKEN (Zone:DNS:Edit + Zone:Read)']),\n ...(usableOauth\n ? []\n : oauth\n ? [`site.google.project in ${a.str('input')} (this ADC login has no project)`]\n : [\n 'VITOPS_GOOGLE_CLIENT_ID / VITOPS_GOOGLE_CLIENT_SECRET / VITOPS_GOOGLE_REFRESH_TOKEN',\n ]),\n ];\n console.log(\n `\\n(--dry, and no credentials set: planned from scratch rather than from live state, ` +\n `so nothing here is reported as already done.\\n A real run needs ${missing.join(' and ')}.)`,\n );\n return;\n }\n\n if (!cfToken) fail(CLOUDFLARE_TOKEN_HINT);\n if (!oauth)\n fail(\n 'set VITOPS_GOOGLE_CLIENT_ID / VITOPS_GOOGLE_CLIENT_SECRET / VITOPS_GOOGLE_REFRESH_TOKEN — a user OAuth token scoped to siteverification + webmasters — or run ' +\n '`gcloud auth application-default login` with the siteverification + webmasters + cloud-platform scopes (see --help)',\n );\n\n // Always a user credential here — `setup` requires one — so the site's project is\n // what makes the calls attributable. Without it a shared-client credential (a gcloud\n // ADC login) is refused outright; a credential from your own OAuth client carries its\n // project in the client id and works either way, which is why this isn't required.\n // Checked BEFORE the grant: a run that cannot succeed should spend no requests.\n requireAdcProject(project, a.str('input'));\n\n const token = await refreshTokenGrant(oauth);\n const auth: GoogleAuthLike = project ? { token, quotaProject: project } : token;\n\n // ── Observe live state per domain (the executors gather; the planner decides) ──\n const states = new Map<string, DomainState>();\n const verifyTokens = new Map<string, string>();\n const webIds = new Map<string, string>();\n for (const d of domains) {\n const zone = await findZoneId(cfToken, d.domain);\n if (!zone.ok || !zone.zoneId) fail(`${d.domain}: ${zone.message}`);\n\n const vt = await getVerificationToken(auth, d.domain);\n if (!vt.ok || !vt.token)\n fail(`${d.domain}: could not obtain a verification token — ${vt.message}`);\n verifyTokens.set(d.domain, vt.token);\n\n const txt = await listApexTxt(cfToken, zone.zoneId, d.domain);\n if (!txt.ok) fail(`${d.domain}: ${txt.message}`);\n\n const wr = await getWebResource(auth, d.domain);\n if (!wr.ok) fail(`${d.domain}: ${wr.message}`);\n if (wr.id) webIds.set(d.domain, wr.id);\n\n const site = await getSite(auth, siteUrlFor(d.domain));\n if (!site.ok) fail(`${d.domain}: ${site.message}`);\n\n states.set(d.domain, {\n zoneId: zone.zoneId,\n txtPresent: txt.contents.includes(vt.token),\n verified: wr.exists,\n currentOwners: wr.owners,\n propertyExists: site.exists,\n });\n }\n\n const p = planSetup({ domains }, states);\n console.log(formatSetupPlan(p));\n\n if (a.flag('check')) {\n if (hasDrift(p)) {\n console.error('\\n✖ drift — one or more domains are not fully onboarded');\n process.exit(1);\n }\n console.log('\\n✓ every domain is onboarded');\n return;\n }\n if (a.flag('dry')) {\n console.log('\\n(--dry: nothing was changed)');\n return;\n }\n\n // ── Execute ─────────────────────────────────────────────────────────────────\n const results: DomainResult[] = [];\n let failed = false;\n const delays = backoffSchedule();\n\n for (const d of domains) {\n const dp = p.domains.find((x) => x.domain === d.domain)!;\n const state = states.get(d.domain)!;\n const result: DomainResult = {\n domain: d.domain,\n txt: '—',\n verified: '—',\n property: '—',\n reminders: dp.reminders,\n };\n\n // 1. Ensure the apex TXT record (create only).\n if (dp.txt.action === 'skip') result.txt = 'present';\n else {\n const r = await createApexTxt(cfToken, state.zoneId!, d.domain, verifyTokens.get(d.domain)!);\n if (!r.ok) {\n console.error(`✖ ${d.domain} TXT: ${r.message}`);\n result.txt = 'failed';\n failed = true;\n results.push(result);\n continue;\n }\n result.txt = 'created';\n }\n\n // 2. Verify ownership, retrying with backoff while DNS propagates.\n let verified = state.verified;\n if (verified) result.verified = 'yes';\n else {\n for (let attempt = 0; attempt < delays.length; attempt++) {\n const v = await verifyWebResource(auth, d.domain);\n if (v.ok) {\n verified = true;\n if (v.id) webIds.set(d.domain, v.id);\n // Verification is what CREATES the web resource, so this response is the\n // first sight of its owner list — and it contains the verifying account.\n // The pre-verification observation could only report `[]`. Dropping this\n // makes the union below a bare `ownersToAdd`, and the PUT then removes\n // the very account that just proved ownership.\n state.currentOwners = v.owners;\n break;\n }\n if (attempt < delays.length - 1) {\n const wait = delays[attempt]!;\n console.log(\n ` … ${d.domain}: not verified yet (attempt ${attempt + 1}/${delays.length}); retrying in ${wait / 1000}s`,\n );\n await sleep(wait);\n }\n }\n // PENDING, not failed: propagation is slow, not broken. Re-run later.\n result.verified = verified ? 'yes' : 'pending';\n }\n if (!verified) {\n results.push(result);\n continue;\n }\n\n // 3. Add the Search Console property.\n if (dp.property.action === 'skip') result.property = 'present';\n else {\n const r = await addSite(auth, dp.siteUrl);\n if (!r.ok) {\n console.error(`✖ ${d.domain} property: ${r.message}`);\n result.property = 'failed';\n failed = true;\n } else result.property = 'added';\n }\n\n // 4. Add delegated owners to the verified web resource (additive union).\n if (dp.owners.action === 'update' && dp.ownersToAdd.length) {\n const id = webIds.get(d.domain);\n if (!id) {\n console.error(`✖ ${d.domain} owners: no web-resource id (verify may be too fresh)`);\n failed = true;\n } else {\n const r = await updateOwners(\n token,\n id,\n d.domain,\n ownerUnion(state.currentOwners, dp.ownersToAdd),\n );\n if (!r.ok) {\n console.error(`✖ ${d.domain} owners: ${r.message}`);\n failed = true;\n } else console.log(`✓ ${d.domain}: added ${dp.ownersToAdd.length} owner(s)`);\n }\n }\n\n results.push(result);\n }\n\n console.log(`\\n${formatSummary(results)}`);\n if (failed) process.exit(1);\n}\n\nexport async function cmdSearch(argv: string[]) {\n const [sub, ...rest] = argv;\n switch (sub) {\n case 'setup':\n return cmdSearchSetup(rest);\n case 'notify':\n return cmdSearchNotify(rest);\n case undefined:\n case '-h':\n case '--help':\n console.log(SEARCH_HELP);\n return;\n default:\n fail(`unknown \"search\" subcommand \"${sub}\" (expected: setup | notify). Try: vitops --help`);\n }\n}\n","/**\n * Asking for the config values `vitops ads setup` cannot derive, and writing the\n * answers back.\n *\n * The realistic first run has no verification token in the config, because the\n * token does not exist until someone opens the platform UI and asks for one. That\n * makes a missing token a **question**, not an error — but only when there is\n * somebody there to answer it, which is why every decision about *whether* to ask\n * is made here and the planner stays pure.\n *\n * The split mirrors the rest of the toolchain: `questionFor` and `patchRaw` are\n * pure and tested; `ask` and `writeConfigPatch` are the I/O, and they decide\n * nothing.\n *\n * Two rules hold the whole file together:\n *\n * - **A prompt must never hang a CI run.** Asking requires a TTY and an explicit\n * absence of `--dry` / `--check` / `--no-prompt`; otherwise the caller gets the\n * same named error it would have got before this file existed.\n * - **A fact about the site goes in the config; a credential stays in the\n * environment.** A verification token is published in DNS — the platform\n * fetching it back is the ownership proof — so persisting it is right, and it is\n * the only thing here that gets written. `CLOUDFLARE_API_TOKEN` may be typed at\n * a prompt and lives only in the process.\n */\nimport { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport { createInterface } from 'node:readline/promises';\nimport { AD_PLATFORMS, type AdProvider, type MissingField } from '@getvitops/utils/ads';\n\nexport interface Question {\n provider: AdProvider;\n needs: MissingField;\n /** One line naming what is being asked for. */\n title: string;\n /** Where to get it — UI path first, then the URL. */\n hints: string[];\n /** The inline label the answer is typed after. */\n label: string;\n}\n\n/**\n * What to ask for one gap, and where the operator finds it.\n *\n * The hints are the point. \"meta.domainVerification is missing\" is true and\n * useless; the UI path plus the console URL is the difference between a prompt\n * someone can answer now and one they abandon to go searching.\n */\nexport function questionFor(provider: AdProvider, needs: MissingField): Question {\n const platform = AD_PLATFORMS[provider];\n if (needs === 'domainVerification') {\n return {\n provider,\n needs,\n title: `${platform.name} — domain verification token`,\n hints: [\n ...(platform.verification.where ? [platform.verification.where] : []),\n platform.consoleUrl,\n ],\n // The prefix is shown as part of the prompt so it is obvious the platform's\n // bare token is what goes here, not the whole record. `txtRecord` accepts\n // either, but showing the prefix stops the ambiguity arising at all.\n label: platform.verification.txtPrefix ?? 'token',\n };\n }\n const what =\n needs === 'pixelId'\n ? provider === 'google'\n ? 'conversion ID (AW-…, not the customer ID)'\n : 'pixel / tag ID'\n : 'advertising account ID';\n return {\n provider,\n needs,\n title: `${platform.name} — ${what}`,\n hints: [platform.consoleUrl],\n label: needs,\n };\n}\n\n/** The error a non-interactive run gets instead of a prompt. */\nexport function missingFieldMessage(provider: AdProvider, needs: MissingField): string {\n const q = questionFor(provider, needs);\n return (\n `site.ads.${provider}.${needs} is not set — ${q.title}.\\n` +\n q.hints.map((h) => ` ${h}`).join('\\n') +\n `\\n Run this interactively to be prompted for it, or add it to the config yourself.`\n );\n}\n\n/** Whether this run is allowed to ask. Everything that would hang CI is checked here. */\nexport function canPrompt(opts: {\n dry: boolean;\n check: boolean;\n noPrompt: boolean;\n isTty?: boolean | undefined;\n}): boolean {\n if (opts.dry || opts.check || opts.noPrompt) return false;\n return opts.isTty ?? Boolean(process.stdin.isTTY && process.stdout.isTTY);\n}\n\n/**\n * Ask one question. An empty answer means \"skip this one\" — reported as still\n * blocked rather than treated as a value, so nothing is written and nothing is\n * created on the strength of a stray Enter.\n */\nexport async function ask(q: Question): Promise<string | undefined> {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n try {\n console.log(`\\n${q.title}`);\n for (const hint of q.hints) console.log(` ${hint}`);\n const answer = (await rl.question(` ${q.label} `)).trim();\n return answer || undefined;\n } finally {\n rl.close();\n }\n}\n\n/**\n * Merge one ad-property field into a **raw, on-disk** config object.\n *\n * Raw is load-bearing, and it is the rule `@getvitops/vite`'s `designSystemPath()`\n * already follows for the theme editor's save: `resolveConfig` normalises shorthand\n * shapes and resolves `extends` in memory, so writing from the resolved object\n * would grow keys beside the author's and silently edit a copy nothing builds from.\n * Everything outside `site.ads.<provider>.<field>` is left exactly as it was.\n */\nexport function patchRaw(\n raw: unknown,\n provider: AdProvider,\n field: MissingField,\n value: string,\n): unknown {\n if (typeof raw !== 'object' || raw == null || Array.isArray(raw))\n throw new Error('config is not an object');\n const root = raw as Record<string, unknown>;\n const site = (root['site'] ?? {}) as Record<string, unknown>;\n const ads = (site['ads'] ?? {}) as Record<string, unknown>;\n const entry = (ads[provider] ?? {}) as Record<string, unknown>;\n return {\n ...root,\n site: { ...site, ads: { ...ads, [provider]: { ...entry, [field]: value } } },\n };\n}\n\n/** Indentation the file already uses, so a patch doesn't reformat the whole document. */\nfunction detectIndent(source: string): string | number {\n const match = /\\n([ \\t]+)\"/.exec(source);\n if (!match?.[1]) return 2;\n return match[1].includes('\\t') ? '\\t' : match[1].length;\n}\n\n/**\n * Persist one answered field into the config file.\n *\n * JSON only: a `.js`/`.ts` config is code, and rewriting code to insert a value is\n * a different (and much worse) problem than merging an object — so the caller is\n * told to paste it instead. Returns whether it wrote.\n */\nexport function writeConfigPatch(\n path: string,\n provider: AdProvider,\n field: MissingField,\n value: string,\n): { written: boolean; reason?: string } {\n if (!path.endsWith('.json'))\n return {\n written: false,\n reason: `${path} is a module, not JSON — add \"${field}\": \"${value}\" to site.ads.${provider} yourself`,\n };\n if (!existsSync(path)) return { written: false, reason: `${path} no longer exists` };\n const source = readFileSync(path, 'utf8');\n const raw: unknown = JSON.parse(source);\n const next = patchRaw(raw, provider, field, value);\n const trailingNewline = source.endsWith('\\n') ? '\\n' : '';\n writeFileSync(path, JSON.stringify(next, null, detectIndent(source)) + trailingNewline);\n return { written: true };\n}\n","/**\n * `vitops ads` — everything that links this site to an ad platform. Three\n * subcommands, split by what they touch: `setup` writes DNS, `tags` writes\n * nothing and prints markup, `lint` reads and reports.\n */\nimport {\n AD_PLATFORMS,\n AD_PROVIDER_KEYS,\n formatPlan as formatPlanAds,\n formatSummary as formatAdsSummary,\n hasDrift as hasAdsDrift,\n isAdProvider,\n missingFields,\n normalizeConversionActionIds,\n normalizeConversionLabels,\n plan as planAds,\n renderTag,\n tagId,\n type AdDomainState,\n type AdPropertyResult,\n type AdPropertySetup,\n type AdProvider,\n type MissingField,\n} from '@getvitops/utils/ads';\nimport { createApexTxt, findZoneId, listApexTxt } from '@getvitops/utils/onboarding';\nimport { PLATFORM_PARAMS } from '@getvitops/utils/tracking';\nimport type { Config } from '@getvitops/generator';\nimport { parse } from '../args.ts';\nimport { CLOUDFLARE_TOKEN_HINT, loadCloudflareToken } from '../credentials.ts';\nimport { ask, canPrompt, missingFieldMessage, questionFor, writeConfigPatch } from '../prompt.ts';\nimport { fail, loadConfig, pickOne, siteConfig, SITE_OPTS } from './shared.ts';\nimport { ADS_HELP } from '../help.ts';\n\n/**\n * Adapt a `Config` to the ads module's own option shape.\n *\n * `AdPropertySetup` is the `site.ads.<provider>` block plus its key, so this spreads the block.\n * (It used to copy a hand-picked subset of fields, which silently dropped `conversionActionId` and\n * `reporting` on the way to the planner.) The domain default is the one derived value: a platform\n * verifies the host the site is published at, so\n * `domains.canonical` answers it, and only an ad account verified against some\n * other host needs `site.ads.<provider>.domain` stated.\n */\nfunction toAdProperties(cfg: Config, providerFilter: string | undefined): AdPropertySetup[] {\n const canonicalHost = (() => {\n const url = cfg.site.domains?.canonical;\n if (!url) return undefined;\n try {\n return new URL(url).hostname;\n } catch {\n return undefined;\n }\n })();\n\n const entries = cfg.site.ads ?? {};\n const properties: AdPropertySetup[] = AD_PROVIDER_KEYS.filter((p) => entries[p] != null).map(\n (provider) => {\n const e = entries[provider]!;\n const domain = e.domain ?? canonicalHost;\n return { ...e, provider, ...(domain ? { domain } : {}) };\n },\n );\n\n if (providerFilter && !isAdProvider(providerFilter))\n fail(\n `--provider \"${providerFilter}\" is not a known ad platform (expected: ${AD_PROVIDER_KEYS.join(' | ')})`,\n );\n return pickOne(properties, providerFilter, (p) => p.provider, {\n flag: 'provider',\n source: 'site.ads',\n });\n}\n\n/**\n * Link each configured ad property: ensure the platform's domain-verification TXT,\n * report what only a human can finish.\n *\n * Idempotent by construction — the planner decides each step from live DNS, so a\n * re-run of a linked property is all skips. `--check` reports drift and exits\n * non-zero without mutating; `--dry` prints the plan and stops. Neither is offline:\n * the DNS observation runs before the planner either way.\n *\n * The one thing this does that `search setup` does not is **ask**. A first run has\n * no verification token, because the token doesn't exist until someone fetches it\n * from the platform UI — so a blocked step becomes a prompt, the answer is folded\n * into the setup, and the planner runs again on it. That re-plan matters: what gets\n * executed is always a plan the pure planner produced, never a special case built\n * out of an answer.\n */\nasync function cmdAdsSetup(argv: string[]) {\n const a = parse(argv, {\n ...SITE_OPTS,\n provider: { type: 'string' },\n check: { type: 'boolean', default: false },\n dry: { type: 'boolean', default: false },\n 'no-prompt': { type: 'boolean', default: false },\n 'no-write': { type: 'boolean', default: false },\n });\n\n const input = a.path('input');\n const cfg = await loadConfig(input, a.str('site-env'));\n let properties = toAdProperties(cfg, a.opt('provider'));\n if (properties.length === 0)\n fail('no properties in site.ads — add the ad platforms this site is linked to');\n\n const dry = a.flag('dry');\n const check = a.flag('check');\n\n // Only the DNS-verifying platforms need a Cloudflare token; a config of\n // tag-only platforms (Google Ads, LinkedIn, Reddit, Microsoft) has no DNS work\n // at all, and demanding a credential for a run that writes nothing would be an\n // obstacle standing in front of an empty room.\n const needsDns = properties.some(\n (p) => AD_PLATFORMS[p.provider].verification.method === 'dns-txt',\n );\n const cfToken = loadCloudflareToken();\n // As in `search setup`: a dry run mutates nothing, so it must not demand the\n // credential to mutate. Without one it cannot read live DNS either, so\n // `observe()` returns an empty map and the plan is \"from scratch\" — still the\n // complete account of a first run, and showable before anything is provisioned.\n if (needsDns && !cfToken && dry !== true) fail(CLOUDFLARE_TOKEN_HINT);\n\n /** Observe live DNS per domain. Cached by domain: several platforms share one. */\n const observe = async (): Promise<Map<string, AdDomainState>> => {\n const states = new Map<string, AdDomainState>();\n if (!cfToken) return states;\n for (const p of properties) {\n const domain = p.domain;\n if (!domain || states.has(domain)) continue;\n if (AD_PLATFORMS[p.provider].verification.method !== 'dns-txt') continue;\n const zone = await findZoneId(cfToken, domain);\n if (!zone.ok || !zone.zoneId) fail(`${domain}: ${zone.message}`);\n const txt = await listApexTxt(cfToken, zone.zoneId, domain);\n if (!txt.ok) fail(`${domain}: ${txt.message}`);\n states.set(domain, { zoneId: zone.zoneId, txtContents: txt.contents });\n }\n return states;\n };\n\n let states = await observe();\n let adsPlan = planAds({ properties }, states);\n\n // ── Ask for what the config is missing, then re-plan on the answers ──────────\n const interactive = canPrompt({\n dry,\n check,\n noPrompt: a.flag('no-prompt'),\n });\n const gaps = missingFields(adsPlan);\n if (gaps.length && interactive) {\n const answers: { provider: AdProvider; needs: MissingField; value: string }[] = [];\n for (const gap of gaps) {\n const value = await ask(questionFor(gap.provider as AdProvider, gap.needs));\n if (value) answers.push({ provider: gap.provider as AdProvider, needs: gap.needs, value });\n }\n if (answers.length) {\n properties = properties.map((p) => {\n const mine = answers.filter((a) => a.provider === p.provider);\n return mine.reduce((acc, a) => ({ ...acc, [a.needs]: a.value }), p);\n });\n adsPlan = planAds({ properties }, states);\n if (!a.flag('no-write')) {\n for (const ans of answers) {\n const res = writeConfigPatch(input, ans.provider, ans.needs, ans.value);\n if (res.written) console.log(`✓ wrote site.ads.${ans.provider}.${ans.needs} to ${input}`);\n else console.error(`! ${res.reason}`);\n }\n }\n }\n // A token answered now may name a record we haven't looked for yet.\n states = await observe();\n adsPlan = planAds({ properties }, states);\n } else if (gaps.length && !dry && !check) {\n for (const gap of gaps)\n console.error(`! ${missingFieldMessage(gap.provider as AdProvider, gap.needs)}`);\n }\n\n if (dry || check) {\n console.log(formatPlanAds(adsPlan));\n if (check && hasAdsDrift(adsPlan)) {\n console.error('\\n✖ ad properties are not fully linked');\n process.exit(1);\n }\n return;\n }\n\n // ── Apply ────────────────────────────────────────────────────────────────────\n const results: AdPropertyResult[] = [];\n let failed = false;\n for (const p of adsPlan.properties) {\n const result: AdPropertyResult = {\n provider: p.provider,\n domain: p.domain ?? '—',\n txt: 'n/a',\n tag: p.tag.action === 'skip' ? 'ready' : 'blocked',\n reminders: p.reminders,\n };\n // A property still missing a field after the prompt round is not linked, and\n // the exit code has to say so — a run that silently succeeds while a pixel has\n // no id is the failure this command exists to make visible.\n if (p.txt.action === 'blocked' || p.tag.action === 'blocked') failed = true;\n if (p.txt.action === 'blocked') result.txt = 'blocked';\n else if (p.txt.action === 'skip')\n result.txt = AD_PLATFORMS[p.provider].verification.method === 'dns-txt' ? 'present' : 'n/a';\n else {\n const zoneId = p.domain ? states.get(p.domain)?.zoneId : undefined;\n if (!cfToken || !zoneId || !p.domain || !p.txtContent) {\n console.error(`✖ ${p.provider}: no Cloudflare zone for ${p.domain ?? '(no domain)'}`);\n result.txt = 'failed';\n failed = true;\n } else {\n const r = await createApexTxt(cfToken, zoneId, p.domain, p.txtContent);\n if (!r.ok) {\n console.error(`✖ ${p.provider} TXT: ${r.message}`);\n result.txt = 'failed';\n failed = true;\n } else {\n console.log(`✓ ${p.provider}: created TXT on ${p.domain}`);\n result.txt = 'created';\n }\n }\n }\n results.push(result);\n }\n\n console.log(`\\n${formatAdsSummary(results)}`);\n if (results.some((r) => r.txt === 'created'))\n console.log('\\n DNS takes minutes to propagate before a platform can see the record.');\n if (results.some((r) => r.reminders.length))\n console.log(' Finish the reminders above in the platform UI — no API covers those.');\n if (failed) process.exit(1);\n}\n\n/**\n * Print each configured pixel as an inert, consent-gated `<script>`.\n *\n * The universal delivery path, the same reasoning as `vitops legal`: every consumer\n * has this CLI whatever their stack, so Bricks, WordPress and a hand-built site all\n * get the correct gated markup without integration code.\n */\nasync function cmdAdsTags(argv: string[]) {\n const a = parse(argv, {\n ...SITE_OPTS,\n provider: { type: 'string' },\n strategy: { type: 'string', default: 'idle' },\n });\n\n const strategy = a.str('strategy');\n if (!['idle', 'async', 'interaction'].includes(strategy))\n fail(`--strategy \"${strategy}\" is not one of: idle | async | interaction`);\n\n const cfg = await siteConfig(a);\n const properties = toAdProperties(cfg, a.opt('provider'));\n if (properties.length === 0) fail('no properties in site.ads — nothing to emit');\n\n const out: string[] = [];\n for (const p of properties) {\n if (p.enabled === false) continue;\n const tag = renderTag(p, strategy as 'idle' | 'async' | 'interaction');\n if (!tag) {\n console.error(\n `! ${p.provider}: no ${AD_PLATFORMS[p.provider].tag.needs} — skipped (run \\`vitops ads setup\\` to be prompted for it)`,\n );\n continue;\n }\n out.push(tag);\n }\n if (out.length === 0) fail('no ad property has a tag id yet');\n console.log(out.join('\\n\\n'));\n}\n\n/**\n * Report the ad-property gaps that are invisible at runtime.\n *\n * Each finding is a silent failure, which is the bar for being here at all: an\n * uncaptured click ID looks exactly like organic traffic, and a pixel on a site\n * with tracking off produces conversions no campaign gets credit for.\n */\nasync function cmdAdsLint(argv: string[]) {\n const a = parse(argv, SITE_OPTS);\n\n const cfg = await siteConfig(a);\n const properties = toAdProperties(cfg, undefined);\n if (properties.length === 0) {\n console.log('· no site.ads properties — nothing to check');\n return;\n }\n\n const findings: string[] = [];\n const trackingOn = cfg.site.tracking?.enabled === true;\n\n for (const p of properties) {\n const platform = AD_PLATFORMS[p.provider];\n if (p.enabled === false) continue;\n\n if (!tagId(p))\n findings.push(\n `${p.provider}: no ${platform.tag.needs} — the property is on record but no tag can be emitted`,\n );\n\n const uncaptured = platform.clickIdParams.filter((param) => !(param in PLATFORM_PARAMS));\n if (uncaptured.length)\n findings.push(\n `${p.provider}: click ID ${uncaptured.join(', ')} is not in the capture vocabulary — every conversion from this platform will arrive unattributed`,\n );\n\n if (!trackingOn)\n findings.push(\n `${p.provider}: site.tracking.enabled is not true, so ${platform.clickIdParams.join('/')} is never captured — conversions will be unattributed`,\n );\n\n // `reporting: 'server'` with no matching `conversionActionId` isn't a bug —\n // `gtagBootstrap` keeps that action firing client-side rather than\n // dropping it — but it does mean the setting is a no-op today, worth\n // surfacing rather than leaving someone to wonder why nothing changed.\n if (p.provider === 'google' && p.reporting === 'server') {\n const labels = normalizeConversionLabels(p.conversionLabel);\n const actionIds = normalizeConversionActionIds(p.conversionActionId);\n const configuredNoId = (Object.keys(labels) as (keyof typeof labels)[]).filter(\n (action) => labels[action] && !actionIds[action],\n );\n if (configuredNoId.length)\n findings.push(\n `google: reporting is \"server\" but conversionActionId.${configuredNoId.join(', .')} ` +\n `is not set — ${configuredNoId.length === 1 ? 'that action' : 'those actions'} still ` +\n `fire client-side, not a failure but not what \"server\" asked for either`,\n );\n }\n }\n\n if (findings.length === 0) {\n console.log(\n `✓ ${properties.length} ad propert${properties.length === 1 ? 'y' : 'ies'} check out`,\n );\n return;\n }\n for (const f of findings) console.error(`✖ ${f}`);\n process.exit(1);\n}\n\nexport async function cmdAds(argv: string[]) {\n const [sub, ...rest] = argv;\n switch (sub) {\n case 'setup':\n return cmdAdsSetup(rest);\n case 'tags':\n return cmdAdsTags(rest);\n case 'lint':\n return cmdAdsLint(rest);\n case undefined:\n case '-h':\n case '--help':\n console.log(ADS_HELP);\n return;\n default:\n fail(`unknown \"ads\" subcommand \"${sub}\" (expected: setup | tags | lint). Try: vitops --help`);\n }\n}\n","/**\n * `vitops audit` — performance + accessibility budgets over a built site.\n *\n * The planner (page discovery, status handling, budgets, report rendering) is pure and\n * lives in `@getvitops/utils/audit` beside `ads/`, `indexing/` and `onboarding/`, as\n * `plan.ts`; this file is wiring only — flags in, files + exit code out. The browser\n * tooling (Playwright, axe, Lighthouse) is lazy and optional, see `audit/run.ts`.\n */\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\nimport {\n DEFAULT_BUDGETS,\n buildReport,\n fetchSitemapPaths,\n preflight,\n formatSummary,\n parseImpacts,\n pathsFromSeed,\n planPages,\n reportFiles,\n runAudit,\n runCommand,\n startPreview,\n type AuditBudgets,\n type FormFactor,\n} from '@getvitops/utils/audit';\nimport { msg, parse } from '../args.ts';\nimport { fail } from './shared.ts';\n\n/** Dedicated port: not Astro's 4321, which a developer's own `astro dev` is likely on. */\nconst DEFAULT_PORT = 4398;\n\nfunction num(flag: string, raw: string | undefined, fallback: number): number {\n if (raw === undefined) return fallback;\n const n = Number(raw);\n if (!Number.isFinite(n) || n <= 0) fail(`audit: --${flag} must be a positive number`);\n return n;\n}\n\nexport async function cmdAudit(argv: string[]): Promise<void> {\n const args = parse(argv, {\n dir: { type: 'string', default: '.' },\n url: { type: 'string' },\n build: { type: 'boolean', default: false },\n port: { type: 'string' },\n pages: { type: 'string' },\n 'no-defaults': { type: 'boolean', default: false },\n seed: { type: 'string' },\n out: { type: 'string', short: 'o', default: '.vitops/audit' },\n lcp: { type: 'string' },\n cls: { type: 'string' },\n tbt: { type: 'string' },\n impact: { type: 'string' },\n 'form-factor': { type: 'string', default: 'mobile' },\n warn: { type: 'boolean', default: false },\n });\n\n const dir = resolve(args.str('dir'));\n const formFactor = args.str('form-factor') as FormFactor;\n if (formFactor !== 'mobile' && formFactor !== 'desktop')\n fail('audit: --form-factor must be mobile or desktop');\n\n let impacts = DEFAULT_BUDGETS.blockingImpacts;\n const impactFlag = args.opt('impact');\n if (impactFlag) {\n try {\n impacts = parseImpacts(impactFlag);\n } catch (err) {\n fail(`audit: ${msg(err)}`);\n }\n }\n const budgets: AuditBudgets = {\n lcpMs: num('lcp', args.opt('lcp'), DEFAULT_BUDGETS.lcpMs),\n cls: num('cls', args.opt('cls'), DEFAULT_BUDGETS.cls),\n tbtMs: num('tbt', args.opt('tbt'), DEFAULT_BUDGETS.tbtMs),\n blockingImpacts: impacts,\n };\n\n // Menu-derived candidates: the EmDash seed, when there is one. Explicit `--seed` must\n // exist; the default location is only looked at, never required.\n const seedFlag = args.opt('seed');\n const seedPath = seedFlag ? resolve(seedFlag) : join(dir, 'seed', 'seed.json');\n let menuPaths: string[] = [];\n if (existsSync(seedPath)) {\n try {\n menuPaths = pathsFromSeed(JSON.parse(readFileSync(seedPath, 'utf8')));\n } catch (err) {\n fail(`audit: could not read menus from ${seedPath}: ${msg(err)}`);\n }\n } else if (seedFlag) fail(`audit: seed not found: ${seedPath}`);\n\n if (args.flag('build') && !args.opt('url')) {\n console.log('▸ building (astro build)');\n try {\n await runCommand('npx', ['--no-install', 'astro', 'build'], dir);\n } catch (err) {\n fail(`audit: build failed: ${msg(err)}`);\n }\n }\n\n // Missing peers or Chromium fail here, before any server exists to be left running.\n try {\n await preflight();\n } catch (err) {\n fail(`audit: ${msg(err)}`);\n }\n\n let server: Awaited<ReturnType<typeof startPreview>> | undefined;\n let baseUrl = args.opt('url');\n // `fail` and an uncaught throw skip `finally`, and Ctrl+C skips it too: stop the detached\n // preview on every way out, or the next run aborts with \"port already in use\".\n const stopServer = () => server?.stop();\n const onSignal = (signal: NodeJS.Signals) => {\n stopServer();\n process.exit(signal === 'SIGINT' ? 130 : 143);\n };\n process.once('SIGINT', onSignal);\n process.once('SIGTERM', onSignal);\n process.once('exit', stopServer);\n try {\n if (!baseUrl) {\n const port = num('port', args.opt('port'), DEFAULT_PORT);\n console.log(`▸ astro preview on :${port}`);\n server = await startPreview({ cwd: dir, port });\n baseUrl = server.baseUrl;\n }\n // Planned once the server is up: the built sitemap is the page list when there is one.\n const sitemapPaths = await fetchSitemapPaths(baseUrl);\n const paths = planPages({\n sitemapPaths,\n menuPaths,\n extra: (args.opt('pages') ?? '')\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean),\n includeDefaults: !args.flag('no-defaults'),\n });\n console.log(\n sitemapPaths.length\n ? `▸ ${sitemapPaths.length} page(s) from the sitemap`\n : '▸ no sitemap found; using menus and defaults',\n );\n console.log(`▸ auditing ${paths.length} candidate page(s) at ${baseUrl} (${formFactor})`);\n const pages = await runAudit({\n baseUrl,\n paths,\n budgets,\n formFactor,\n onProgress: (m) => console.log(` ${m}`),\n });\n const report = buildReport({\n generatedAt: new Date().toISOString(),\n baseUrl,\n formFactor,\n budgets,\n pages,\n });\n\n const out = resolve(dir, args.str('out'));\n for (const [name, contents] of Object.entries(reportFiles(report))) {\n const file = join(out, name);\n mkdirSync(dirname(file), { recursive: true });\n writeFileSync(file, contents);\n }\n console.log(`\\n${formatSummary(report)}\\n\\nReport: ${join(out, 'report.md')}`);\n\n if (!report.passed) {\n if (args.flag('warn')) console.log('(--warn: reporting only, exit 0)');\n else process.exitCode = 1;\n }\n } catch (err) {\n // `fail` exits the process, which skips `finally` — stop the server first or the\n // detached preview outlives the run.\n server?.stop();\n fail(`audit: ${msg(err)}`);\n } finally {\n server?.stop();\n }\n}\n","/**\n * `vitops crm` — the two things `site.crm` needs a human-in-the-loop for.\n *\n * `check` is the drift guard, and the reason this command exists at all:\n * HubSpot *rejects* a submission naming an unrecognised property, and Bigin\n * silently *ignores* one — so a typo'd `fieldMap`/`trackingFields` entry is\n * invisible in one CRM and fatal in the other, and nothing short of fetching\n * the live field list can tell you which. `setup` creates the missing\n * properties HubSpot's API allows creating; Bigin has no such endpoint, so it\n * always resolves to a manual reminder, matching how `ads setup` handles a\n * platform with no verification API. Jobber currently renders `fieldMap`/\n * `trackingFields` values as note-text lines rather than app-configured\n * custom fields (see `@getvitops/utils/crm`'s `jobber.ts` docblock for why\n * that surface is deferred), so there is nothing live to check yet — both\n * subcommands say so rather than guessing at an unverified API.\n *\n * Modelled on `commands/ads.ts`: read-and-create only, never destructive.\n */\nimport {\n zohoAccountsHost,\n zohoApiHost,\n type CrmConfig,\n type CrmProvider,\n} from '@getvitops/utils/crm';\nimport { parse } from '../args.ts';\nimport { loadHubSpotToken, loadZohoOAuth } from '../credentials.ts';\nimport { CRM_HELP } from '../help.ts';\nimport { fail, siteConfig, SITE_OPTS } from './shared.ts';\n\n/** Every CRM property name this site's config asserts exists, from both maps —\n * `fieldMap`/`trackingFields` values are property names, not the submitted\n * field names on the left. */\nfunction configuredProperties(crm: CrmConfig): string[] {\n return [...Object.values(crm.fieldMap ?? {}), ...Object.values(crm.trackingFields ?? {})];\n}\n\nasync function hubspotContactProperties(accessToken: string): Promise<Set<string>> {\n const res = await fetch('https://api.hubapi.com/crm/v3/properties/contacts', {\n headers: { authorization: `Bearer ${accessToken}` },\n });\n if (!res.ok) fail(`HubSpot properties lookup failed (${res.status}): ${await res.text()}`);\n const json = (await res.json()) as { results?: { name: string }[] };\n return new Set((json.results ?? []).map((p) => p.name));\n}\n\nasync function biginContactFields(\n oauth: { clientId: string; clientSecret: string; refreshToken: string },\n dataCenter: string,\n): Promise<Set<string>> {\n const body = new URLSearchParams({\n grant_type: 'refresh_token',\n client_id: oauth.clientId,\n client_secret: oauth.clientSecret,\n refresh_token: oauth.refreshToken,\n });\n const tokenRes = await fetch(`https://${zohoAccountsHost(dataCenter)}/oauth/v2/token`, {\n method: 'POST',\n headers: { 'content-type': 'application/x-www-form-urlencoded' },\n body: body.toString(),\n });\n if (!tokenRes.ok)\n fail(`Zoho token exchange failed (${tokenRes.status}): ${await tokenRes.text()}`);\n const { access_token } = (await tokenRes.json()) as { access_token?: string };\n if (!access_token) fail('Zoho token exchange returned no access_token');\n\n const res = await fetch(\n `https://${zohoApiHost(dataCenter)}/bigin/v2/settings/fields?module=Contacts`,\n { headers: { authorization: `Zoho-oauthtoken ${access_token}` } },\n );\n if (!res.ok) fail(`Bigin fields lookup failed (${res.status}): ${await res.text()}`);\n const json = (await res.json()) as { fields?: { api_name: string }[] };\n return new Set((json.fields ?? []).map((f) => f.api_name));\n}\n\n/** What each provider's `check` needs to say when it doesn't fail outright.\n * Kept as a table rather than an `if/else if` chain so a fourth provider is\n * a missing entry (a type error), not a silently-wrong branch. */\nconst CHECK_NOUNS: Record<CrmProvider, string> = {\n hubspot: 'properties',\n bigin: 'fields',\n jobber: 'fields',\n};\nconst CHECK_REJECTION: Record<CrmProvider, string> = {\n hubspot: 'HubSpot will reject the whole submission',\n bigin: 'Bigin will silently drop this value',\n jobber: 'Jobber will silently drop this value',\n};\n\n/**\n * Fetch the live field/property vocabulary and report every `fieldMap`/\n * `trackingFields` target that doesn't exist there.\n */\nasync function cmdCrmCheck(argv: string[]) {\n const a = parse(argv, SITE_OPTS);\n const cfg = await siteConfig(a);\n const crm = cfg.site.crm;\n if (!crm) {\n console.log('· no site.crm — nothing to check');\n return;\n }\n if (crm.enabled === false) {\n console.log('· site.crm.enabled is false — nothing to check');\n return;\n }\n\n const wanted = configuredProperties(crm);\n if (wanted.length === 0) {\n console.log('· site.crm has no fieldMap/trackingFields entries — nothing to check');\n return;\n }\n\n if (crm.provider === 'jobber') {\n console.log(\n '· no live check for Jobber yet — fieldMap/trackingFields values currently render as ' +\n 'note-text lines, not app-configured custom fields, so nothing there can silently ' +\n 'mismatch (see @getvitops/utils/crm jobber.ts docblock).',\n );\n return;\n }\n\n let known: Set<string>;\n if (crm.provider === 'hubspot') {\n const token = loadHubSpotToken();\n if (!token) fail('set VITOPS_HUBSPOT_ACCESS_TOKEN to check the live property list');\n known = await hubspotContactProperties(token);\n } else {\n const oauth = loadZohoOAuth();\n if (!oauth)\n fail(\n 'set VITOPS_ZOHO_CLIENT_ID / VITOPS_ZOHO_CLIENT_SECRET / VITOPS_ZOHO_REFRESH_TOKEN to check the live field list',\n );\n known = await biginContactFields(oauth, crm.bigin!.dataCenter);\n }\n\n const missing = wanted.filter((p) => !known.has(p));\n if (missing.length === 0) {\n console.log(`✓ ${wanted.length} mapped ${CHECK_NOUNS[crm.provider]} exist`);\n return;\n }\n for (const p of missing)\n console.error(\n `✖ \"${p}\" is not a ${crm.provider === 'hubspot' ? 'HubSpot contact property' : 'Bigin Contacts field'} — ${CHECK_REJECTION[crm.provider]}`,\n );\n process.exit(1);\n}\n\n/**\n * Create the HubSpot properties `fieldMap`/`trackingFields` name but don't\n * exist yet. Bigin has no property-creation API and Jobber's app-configured\n * custom fields aren't wired up yet (see the module docblock) — both resolve\n * to `blocked`/a manual reminder, matching `ads setup`'s handling of a\n * platform with no verification API.\n */\nasync function cmdCrmSetup(argv: string[]) {\n const a = parse(argv, { ...SITE_OPTS, dry: { type: 'boolean', default: false } });\n const cfg = await siteConfig(a);\n const crm = cfg.site.crm;\n if (!crm) fail('no site.crm configured — nothing to set up');\n\n const wanted = configuredProperties(crm);\n\n if (crm.provider === 'jobber') {\n console.log(\n '· Jobber app-configured custom fields are not wired up yet — fieldMap/trackingFields ' +\n 'values render as note-text lines instead, which need no setup. If you do want them as ' +\n \"real Jobber custom fields, create them manually in Jobber's Developer Center first.\",\n );\n return;\n }\n\n if (crm.provider === 'bigin') {\n if (wanted.length === 0) {\n console.log('· no fieldMap/trackingFields entries to check against Bigin');\n return;\n }\n console.log(\n `? Bigin has no API to create Contacts fields. Create these manually in Zoho Bigin's ` +\n `field editor, then re-run \\`vitops crm check\\`:\\n` +\n wanted.map((p) => ` - ${p}`).join('\\n'),\n );\n return;\n }\n\n const token = loadHubSpotToken();\n if (!token) fail('set VITOPS_HUBSPOT_ACCESS_TOKEN to create HubSpot properties');\n const known = await hubspotContactProperties(token);\n const missing = wanted.filter((p) => !known.has(p));\n\n if (missing.length === 0) {\n console.log('✓ every mapped property already exists');\n return;\n }\n\n if (a.flag('dry')) {\n console.log(\n `would create ${missing.length} HubSpot contact propert${missing.length === 1 ? 'y' : 'ies'}:`,\n );\n for (const p of missing) console.log(` + ${p}`);\n return;\n }\n\n for (const p of missing) {\n const res = await fetch('https://api.hubapi.com/crm/v3/properties/contacts', {\n method: 'POST',\n headers: { authorization: `Bearer ${token}`, 'content-type': 'application/json' },\n body: JSON.stringify({\n name: p,\n label: p,\n type: 'string',\n fieldType: 'text',\n groupName: 'contactinformation',\n }),\n });\n if (!res.ok) {\n console.error(`✖ ${p}: ${res.status} ${await res.text()}`);\n process.exit(1);\n }\n console.log(`✓ created ${p}`);\n }\n}\n\nexport async function cmdCrm(argv: string[]) {\n const [sub, ...rest] = argv;\n switch (sub) {\n case 'check':\n return cmdCrmCheck(rest);\n case 'setup':\n return cmdCrmSetup(rest);\n case undefined:\n case '-h':\n case '--help':\n console.log(CRM_HELP);\n return;\n default:\n fail(`unknown \"crm\" subcommand \"${sub}\" (expected: check | setup). Try: vitops --help`);\n }\n}\n","/**\n * `vitops email` — connect `site.notifications.email.from` to whether its domain\n * is actually onboarded onto an outbound email provider.\n *\n * The silent failure this exists to catch: `EMAIL.send()` throws\n * `E_SENDER_NOT_VERIFIED` at runtime with nothing at build or deploy time saying\n * a word, because nothing previously checked the `from` domain against Cloudflare\n * Email Sending's actual onboarding state. Two subcommands, split by what they\n * do: `setup` onboards a domain and waits for its DNS to resolve; `check` is the\n * non-blocking deploy guard.\n */\nimport { existsSync } from 'node:fs';\nimport { createInterface } from 'node:readline/promises';\nimport {\n blockedDomains,\n dnsStatus,\n enableSending,\n findZoneForHost,\n formatPlan as formatEmailPlan,\n formatSummary as formatEmailSummary,\n hasDrift as hasEmailDrift,\n listSendingSubdomains,\n planSendingDomains,\n resolveTxt,\n sendingDns,\n backoffSchedule,\n collectFromAddresses,\n type DomainResult,\n type FromCandidate,\n type ZoneSendingState,\n} from '@getvitops/utils/email';\nimport { normalizeEmailChannel, resolveSender } from '@getvitops/utils/notify';\nimport { scanFiles } from '@getvitops/utils';\nimport type { Config } from '@getvitops/generator';\nimport { parse } from '../args.ts';\nimport { CLOUDFLARE_TOKEN_HINT, loadCloudflareToken } from '../credentials.ts';\nimport { fail, loadConfig, siteConfig, SITE_OPTS } from './shared.ts';\nimport { EMAIL_HELP } from '../help.ts';\n\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));\n\n/**\n * Every `from` domain worth checking: the explicit config value first, its\n * derived `noreply@<canonical>` default second, and a best-effort scan of\n * `--src` as a supplement — never the primary source, since a computed value in\n * source is reported as dynamic rather than guessed at (see `email/scan.ts`).\n */\nfunction gatherCandidates(\n cfg: Config,\n srcDir: string,\n): { candidates: FromCandidate[]; dynamicNotes: string[] } {\n const candidates: FromCandidate[] = [];\n const dynamicNotes: string[] = [];\n\n const channel = normalizeEmailChannel(cfg.site.notifications?.email);\n const canonical = cfg.site.domains?.canonical;\n const address = resolveSender(channel ?? { provider: 'cloudflare' }, { canonical });\n if (address) {\n const domain = address.split('@')[1];\n if (domain) candidates.push({ address, domain, source: channel?.from ? 'config' : 'derived' });\n }\n\n if (existsSync(srcDir)) {\n const { addresses, dynamic } = collectFromAddresses(scanFiles(srcDir));\n for (const a of addresses) {\n const domain = a.split('@')[1];\n if (domain && !candidates.some((c) => c.address === a))\n candidates.push({ address: a, domain, source: 'scan' });\n }\n for (const d of dynamic)\n dynamicNotes.push(`! ${d.file}:${d.line}: from is computed (\\`${d.expr}\\`) — not checked`);\n }\n\n return { candidates, dynamicNotes };\n}\n\n/** Observe each candidate domain's zone, keyed by the domain itself (not the zone name). */\nasync function observeZones(\n token: string | undefined,\n domains: string[],\n): Promise<{ states: Map<string, ZoneSendingState>; failures: string[] }> {\n const states = new Map<string, ZoneSendingState>();\n const failures: string[] = [];\n if (!token) return { states, failures };\n for (const domain of domains) {\n const zone = await findZoneForHost(token, domain);\n if (!zone.ok || !zone.zoneId) {\n failures.push(`${domain}: ${zone.message}`);\n continue;\n }\n const res = await listSendingSubdomains(token, zone.zoneId);\n if (!res.ok) {\n failures.push(`${domain}: ${res.message}`);\n continue;\n }\n states.set(domain, { zoneId: zone.zoneId, subdomains: res.subdomains });\n }\n return { states, failures };\n}\n\nasync function confirm(question: string): Promise<boolean> {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n try {\n const answer = (await rl.question(`${question} [y/N] `)).trim().toLowerCase();\n return answer === 'y' || answer === 'yes';\n } finally {\n rl.close();\n }\n}\n\n/**\n * Onboard a `from` domain and wait for its DNS to resolve.\n *\n * Idempotent by construction — a re-run of a fully-onboarded domain is all\n * skips. `--dry` prints the plan and mutates nothing (and needs no credential,\n * planning from scratch); `--check` reports drift and exits non-zero.\n */\nasync function cmdEmailSetup(argv: string[]) {\n const a = parse(\n argv,\n {\n ...SITE_OPTS,\n src: { type: 'string', default: 'src' },\n dry: { type: 'boolean', default: false },\n check: { type: 'boolean', default: false },\n 'no-prompt': { type: 'boolean', default: false },\n timeout: { type: 'string', default: '5' },\n },\n true,\n );\n\n const cfg = await loadConfig(a.path('input'), a.str('site-env'));\n const explicitDomain = a.positionals[0];\n const dry = a.flag('dry');\n const check = a.flag('check');\n\n const { candidates, dynamicNotes } = explicitDomain\n ? {\n candidates: [\n {\n address: `noreply@${explicitDomain}`,\n domain: explicitDomain,\n source: 'config' as const,\n },\n ],\n dynamicNotes: [],\n }\n : gatherCandidates(cfg, a.path('src'));\n\n if (candidates.length === 0)\n fail(\n 'no from domain to check — set site.notifications.email.from or site.domains.canonical, or pass a domain: vitops email setup <domain>',\n );\n\n const cfToken = loadCloudflareToken();\n if (!cfToken && !dry) fail(CLOUDFLARE_TOKEN_HINT);\n\n const domains = [...new Set(candidates.map((c) => c.domain))];\n const { states, failures } = await observeZones(cfToken, domains);\n const emailPlan = planSendingDomains({ candidates, provider: 'cloudflare' }, states);\n\n if (dry || check) {\n console.log(formatEmailPlan(emailPlan));\n for (const n of dynamicNotes) console.log(n);\n for (const f of failures) console.error(`! ${f}`);\n if (check && hasEmailDrift(emailPlan)) {\n console.error('\\n✖ a from domain is not fully onboarded');\n process.exit(1);\n }\n return;\n }\n\n const interactive = !a.flag('no-prompt') && Boolean(process.stdin.isTTY && process.stdout.isTTY);\n const timeoutMs = Number(a.str('timeout')) * 1000;\n\n const results: DomainResult[] = [];\n let failed = false;\n for (const d of emailPlan.domains) {\n if (d.onboard.action === 'skip') {\n results.push({ domain: d.domain, onboard: 'present', reminders: [] });\n continue;\n }\n if (d.onboard.action === 'blocked') {\n console.error(`? ${d.domain}: ${d.onboard.detail}`);\n results.push({ domain: d.domain, onboard: 'blocked', reminders: [] });\n failed = true;\n continue;\n }\n\n console.log(`\\n${d.domain}: ${d.onboard.detail}`);\n if (interactive && !(await confirm(`Enable Email Sending for \"${d.domain}\"?`))) {\n results.push({ domain: d.domain, onboard: 'blocked', reminders: ['skipped by operator'] });\n continue;\n }\n\n const zone = await findZoneForHost(cfToken!, d.domain);\n if (!zone.ok || !zone.zoneId) {\n console.error(`✖ ${d.domain}: ${zone.message}`);\n results.push({ domain: d.domain, onboard: 'failed', reminders: [] });\n failed = true;\n continue;\n }\n const enabled = await enableSending(cfToken!, zone.zoneId, d.domain);\n if (!enabled.ok) {\n console.error(`✖ ${d.domain}: ${enabled.message}`);\n results.push({ domain: d.domain, onboard: 'failed', reminders: [] });\n failed = true;\n continue;\n }\n console.log(`✓ ${d.domain}: enabled`);\n\n const reminders: string[] = [];\n if (enabled.tag) {\n const dns = await sendingDns(cfToken!, zone.zoneId, enabled.tag);\n const expected = dns.records.filter((r) => r.type === 'TXT');\n if (dns.ok && expected.length) {\n const schedule = backoffSchedule();\n let pending = expected;\n for (let attempt = 0; attempt < schedule.length && pending.length; attempt++) {\n await sleep(Math.min(schedule[attempt]!, timeoutMs));\n const resolved = new Map<string, string[]>();\n for (const name of new Set(pending.map((r) => r.name))) {\n const r = await resolveTxt(name);\n if (r.ok) resolved.set(name, r.contents);\n }\n const status = dnsStatus(pending, resolved);\n pending = status.filter((s) => !s.present).map((s) => s.record);\n console.log(\n ` DNS: ${expected.length - pending.length}/${expected.length} records resolving (attempt ${attempt + 1}/${schedule.length})`,\n );\n }\n if (pending.length)\n reminders.push(\n `${pending.length} DNS record(s) not yet resolving — check again shortly.`,\n );\n }\n }\n results.push({ domain: d.domain, onboard: 'created', reminders });\n }\n\n console.log(`\\n${formatEmailSummary(results)}`);\n for (const n of dynamicNotes) console.log(n);\n for (const f of failures) console.error(`! ${f}`);\n if (failed) process.exit(1);\n}\n\n/**\n * The non-blocking deploy guard: warn when a `from` domain isn't onboarded,\n * exit 0 by default. `--strict` makes it a real gate.\n *\n * Runs with no credential too — it then reports \"not checked\", exits 0, and a\n * CI job with no CLOUDFLARE_API_TOKEN never fails on the check itself.\n */\nasync function cmdEmailCheck(argv: string[]) {\n const a = parse(argv, {\n ...SITE_OPTS,\n src: { type: 'string', default: 'src' },\n strict: { type: 'boolean', default: false },\n skip: { type: 'boolean', default: false },\n });\n\n if (a.flag('skip') || process.env['VITOPS_SKIP_EMAIL_CHECK']) {\n console.log('· vitops email check: skipped (--skip / VITOPS_SKIP_EMAIL_CHECK)');\n return;\n }\n\n const cfg = await siteConfig(a);\n const emailCfg = cfg.site.notifications?.email;\n if (typeof emailCfg === 'object' && emailCfg.verifySender === false) {\n console.log('· vitops email check: skipped (site.notifications.email.verifySender is false)');\n return;\n }\n\n const { candidates, dynamicNotes } = gatherCandidates(cfg, a.path('src'));\n if (candidates.length === 0) {\n console.log('· no from domain configured — nothing to check');\n return;\n }\n\n const cfToken = loadCloudflareToken();\n if (!cfToken) {\n console.log('· vitops email check: not checked — no CLOUDFLARE_API_TOKEN');\n return;\n }\n\n const domains = [...new Set(candidates.map((c) => c.domain))];\n const { states, failures } = await observeZones(cfToken, domains);\n const emailPlan = planSendingDomains({ candidates, provider: 'cloudflare' }, states);\n\n for (const n of dynamicNotes) console.log(n);\n for (const f of failures) console.error(`! ${f}`);\n\n const blocked = blockedDomains(emailPlan);\n if (blocked.length === 0) {\n console.log(`✓ every from domain is onboarded`);\n return;\n }\n for (const b of blocked) console.error(`! ${b.domain}: ${b.onboard.detail}`);\n if (a.flag('strict')) {\n console.error('\\n✖ a from domain is not onboarded (--strict)');\n process.exit(1);\n }\n}\n\nexport async function cmdEmail(argv: string[]) {\n const [sub, ...rest] = argv;\n switch (sub) {\n case 'setup':\n return cmdEmailSetup(rest);\n case 'check':\n return cmdEmailCheck(rest);\n case undefined:\n case '-h':\n case '--help':\n console.log(EMAIL_HELP);\n return;\n default:\n fail(`unknown \"email\" subcommand \"${sub}\" (expected: setup | check). Try: vitops --help`);\n }\n}\n","/**\n * The pure half of `vitops emdash check-prerender`: is a prerendered `.html` body a real page?\n *\n * `astro build` succeeds even when a prerendered route's response was an error. Against a local\n * database with pending migrations EmDash answers with a one-line \"Database migrations are\n * required\" body, which is written to `index.html` like any other page, and the deploy then\n * publishes it.\n */\nimport { hasFormDelivery } from '@getvitops/utils/notify';\n\nexport const PENDING_MIGRATIONS = /Database migrations are required/i;\n\n/** Why `html` is not a real page, or `undefined` when it looks like one. */\nexport function prerenderProblem(html: string): string | undefined {\n if (PENDING_MIGRATIONS.test(html)) return 'carries the pending-migrations message';\n if (!/<html[\\s>]/i.test(html)) return 'is not an HTML document';\n if (!/<title>[^<]+<\\/title>/i.test(html)) return 'has no <title>';\n const unrendered = unrenderedBlocks(html);\n if (unrendered.length)\n return `dropped block(s) with no renderer: ${unrendered.join(', ')} (see \\`PortableText [components.type] is missing\\` in the build log)`;\n return undefined;\n}\n\n/**\n * `vitops_*` block types a page's nested body dropped. `vitops_section` and widget areas emit a\n * `data-vitops-unrendered=\"<type>\"` marker for each, so a page whose sections rendered as\n * headings over empty bodies fails here instead of passing for a real page. (The build-log check in\n * `vitops emdash build` also catches top-level bodies, which carry no marker.)\n */\nexport function unrenderedBlocks(html: string): string[] {\n const types = new Set<string>();\n for (const m of html.matchAll(/data-vitops-unrendered\\s*=\\s*\"([^\"]+)\"/gi)) types.add(m[1]!);\n return [...types].sort();\n}\n\n/** The type-role utilities (`font-<role>`) the framework defines; a closed list from its docs. */\nconst TYPE_ROLES = [\n 'display',\n 'title',\n 'heading',\n 'body',\n 'quote',\n 'caption',\n 'eyebrow',\n 'code',\n 'lead',\n 'footnote',\n 'tag',\n];\nconst TYPE_ROLE_CLASS = new RegExp(`^font-(?:${TYPE_ROLES.join('|')})$`);\n\n/** Every `class=\"…\"` token in a page. */\nfunction classesIn(html: string): Set<string> {\n const out = new Set<string>();\n for (const m of html.matchAll(/\\sclass\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)')/gi))\n for (const token of (m[1] ?? m[2] ?? '').split(/\\s+/)) if (token) out.add(token);\n return out;\n}\n\n/**\n * Type-role classes a page uses that the built stylesheet has no rule for.\n *\n * Tailwind v4 does not scan `node_modules`, so a utility used only by a package's own renderer\n * (`font-display` in `vitops_stats`) was never generated, and the page rendered at body size with\n * no error. The type roles are the one family checkable without the site's config: the list is\n * closed. A class is \"defined\" when the CSS names it as a selector.\n */\nexport function missingTypeRoleRules(html: string, css: string): string[] {\n const defined = (cls: string) => new RegExp(`\\\\.${cls}(?![\\\\w-])`).test(css);\n return [...classesIn(html)].filter((c) => TYPE_ROLE_CLASS.test(c) && !defined(c)).sort();\n}\n\n/**\n * Block types EmDash's `<PortableText>` reported as having no component, from a build's output.\n *\n * `astro build` prints `PortableText [components.type] is missing \"vitops_cards\"` once per\n * occurrence and carries on, rendering nothing for that block. A single missing renderer is a\n * page published with its content gone, and the prerender guard cannot see it in the HTML. The\n * known cause is an import cycle that empties the plugin's block map for a whole build, so the\n * warning arrives dozens of times; it is reported once per type.\n */\nexport function missingBlockComponents(log: string): string[] {\n // eslint-disable-next-line no-control-regex\n const plain = log.replace(/\\u001b\\[[0-9;]*m/g, '');\n const seen = new Set<string>();\n for (const m of plain.matchAll(/PortableText \\[components\\.type\\] is missing \"([^\"]+)\"/g))\n seen.add(m[1]!);\n return [...seen].sort();\n}\n\n/**\n * How many `<h1>` a page has. A page with none has no title in its outline; a page with two (a\n * hero left on its `h1` default beside a layout title) has a muddled one. Counted outside\n * `<template>`, `<script>` and comments so inert markup does not skew it.\n */\nexport function h1Count(html: string): number {\n const live = html\n .replace(/<!--[\\s\\S]*?-->/g, '')\n .replace(/<(script|template|style)[\\s\\S]*?<\\/\\1>/gi, '');\n return (live.match(/<h1[\\s>]/gi) ?? []).length;\n}\n\n/** The URL path a built `.html` file is served at: `a/index.html` and `a.html` are both `/a`. */\nexport function routeOf(relativeFile: string): string {\n const path =\n '/' +\n relativeFile\n .replace(/\\\\/g, '/')\n .replace(/(^|\\/)index\\.html$/, '$1')\n .replace(/\\.html$/, '');\n return path.length > 1 ? path.replace(/\\/+$/, '') : '/';\n}\n\n/**\n * Site-relative links in a page that point at a route the build did not produce: a planned link\n * (`/pricing` in a menu before the page exists) that would 404. Anchors, query-only links, assets\n * (anything with a file extension), `/_emdash/*` and `/api/*` (Worker routes) are not judged.\n */\nexport function brokenInternalLinks(html: string, routes: ReadonlySet<string>): string[] {\n const live = html\n .replace(/<!--[\\s\\S]*?-->/g, '')\n .replace(/<(script|template|style)[\\s\\S]*?<\\/\\1>/gi, '');\n const broken = new Set<string>();\n for (const m of live.matchAll(/<a\\s[^>]*?href\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)')/gi)) {\n const href = (m[1] ?? m[2] ?? '').trim();\n if (!href.startsWith('/') || href.startsWith('//')) continue;\n const path = href.split(/[?#]/)[0] || '/';\n if (/\\.[a-z0-9]{1,8}$/i.test(path) || path.startsWith('/_') || path.startsWith('/api/'))\n continue;\n const norm = path.length > 1 ? path.replace(/\\/+$/, '') : path;\n if (!routes.has(norm)) broken.add(norm);\n }\n return [...broken].sort();\n}\n\n/** Whether a built page carries the `vitops_form` block's post (to the conversion route). */\nexport function hasConversionForm(html: string): boolean {\n return /<form[^>]*\\saction\\s*=\\s*[\"']\\/api\\/track[\"']/i.test(html);\n}\n\n/**\n * Why a site with a form has nowhere to send it, or `undefined` when something is configured.\n * `createConversionRoute` refuses a post with no recipient and no CRM rather than accept a message\n * nobody receives, so the first visitor to submit would be told it failed. A recipient is\n * `site.notifications.email` (a bare address, or `{ to }`) or a location `email`; a CRM also counts.\n */\nexport function deliveryProblem(config: unknown): string | undefined {\n const c = (config ?? {}) as {\n site?: { notifications?: { email?: unknown }; crm?: unknown };\n organization?: { locations?: Record<string, { email?: unknown }> | Array<{ email?: unknown }> };\n };\n const locations = c.organization?.locations;\n const locationEmails = (Array.isArray(locations) ? locations : Object.values(locations ?? {}))\n .map((l) => l?.email)\n .filter((e): e is string => typeof e === 'string' && e !== '');\n // The same test the route makes (`hasFormDelivery`), so the build and the route cannot disagree.\n return hasFormDelivery(\n { notifications: c.site?.notifications as never, locationEmails },\n c.site?.crm,\n )\n ? undefined\n : 'no recipient: set `site.notifications.email`, or give an organization location an `email`, or configure `site.crm`';\n}\n\nconst NOT_COLLECTED = new Set([\n '_next',\n 'website',\n 'honeypot',\n '_honeypot',\n 'cf-turnstile-response',\n]);\n\n/**\n * The visitor-supplied field names of the form that posts to `/api/track`, from the built HTML:\n * every named control except the hidden ones, the honeypot and the Turnstile token, which are not\n * personal information the visitor gave. Repeated names (a tick-box group) count once.\n */\nexport function formFieldNames(html: string): string[] {\n const names = new Set<string>();\n for (const form of html.matchAll(\n /<form[^>]*\\saction\\s*=\\s*[\"']\\/api\\/track[\"'][^>]*>([\\s\\S]*?)<\\/form>/gi,\n )) {\n for (const m of form[1]!.matchAll(/<(input|textarea|select)\\b([^>]*)>/gi)) {\n const attrs = m[2]!;\n if (/\\stype\\s*=\\s*[\"']hidden[\"']/i.test(attrs)) continue;\n const name = /\\sname\\s*=\\s*[\"']([^\"']+)[\"']/i.exec(attrs)?.[1];\n if (name && !NOT_COLLECTED.has(name)) names.add(name);\n }\n }\n return [...names].sort();\n}\n\n/**\n * Form fields that are on a built page but not in any `site.templates` form entry. `vitops legal`\n * derives \"what personal information we collect\" from those entries, not from the editor's block,\n * so a field added in the admin is invisible to the generated privacy policy until mirrored.\n */\nexport function uninventoriedFields(config: unknown, built: string[]): string[] {\n const templates = (\n config as {\n site?: { templates?: Record<string, { type?: string; fields?: { name?: string }[] }> };\n }\n )?.site?.templates;\n const known = new Set<string>();\n for (const tpl of Object.values(templates ?? {}))\n if (tpl?.type === 'form') for (const f of tpl.fields ?? []) if (f?.name) known.add(f.name);\n return built.filter((name) => !known.has(name));\n}\n","/**\n * `vitops emdash` — build-time steps for an EmDash site on the Cloudflare adapter.\n *\n * These used to be copied into every scaffold as `scripts/*.mjs`, which froze each fix at\n * scaffold time. As commands they arrive with `@getvitops/cli`, so a site's `package.json`\n * keeps one-line entries and `vp update` delivers the logic (the same move `vitops setup`\n * made for the template's old `init-github`/`init-hosting` scripts).\n *\n * prepare-db migrate and seed the LOCAL D1 the build prerenders from\n * check-prerender fail if a prerendered page is not a real page\n * build `astro build`, then the same checks, plus a failure for any block with no renderer\n *\n * Why both exist: `astro build` prerenders every page inside a local workerd against a local D1\n * (`.wrangler/state`), never the deployed database. The template sets\n * `migrations.runtime: 'check'`, so a build against a local database with pending migrations\n * prerenders EmDash's one-line \"Database migrations are required\" response into every page,\n * and the build still succeeds. A fresh CI runner, a first deploy, a `SKIP_PULL_CONTENT=1`\n * build and an upgrade deploy all hand the build exactly that database.\n */\nimport { spawn, spawnSync } from 'node:child_process';\nimport { existsSync, readFileSync, readdirSync } from 'node:fs';\nimport { createServer } from 'node:net';\nimport { join, relative } from 'node:path';\nimport {\n brokenInternalLinks,\n deliveryProblem,\n formFieldNames,\n uninventoriedFields,\n hasConversionForm,\n h1Count,\n missingBlockComponents,\n missingTypeRoleRules,\n prerenderProblem,\n PENDING_MIGRATIONS,\n routeOf,\n} from '../prerender-check.ts';\nimport { fail } from './shared.ts';\nimport { EMDASH_HELP } from '../help.ts';\n\nfunction freePort(): Promise<number> {\n return new Promise((resolve, reject) => {\n const server = createServer();\n server.once('error', reject);\n server.listen(0, '127.0.0.1', () => {\n const { port } = (server.address() as { port: number }) ?? { port: 0 };\n server.close(() => resolve(port));\n });\n });\n}\n\nasync function until(test: () => Promise<boolean>, what: string, ms = 120_000) {\n const deadline = Date.now() + ms;\n while (Date.now() < deadline) {\n try {\n if (await test()) return;\n } catch {\n // not up yet\n }\n await new Promise((r) => setTimeout(r, 1000));\n }\n throw new Error(`timed out waiting for ${what}`);\n}\n\nconst log = (msg: string) => console.log(`prepare-db: ${msg}`);\n\n/**\n * `astro dev` daemonizes: the child outlives the command that started it, so capturing its\n * output through pipes would leave this process waiting on the daemon's inherited handles,\n * which looks exactly like a hang. Output is ignored and every call has a timeout.\n */\nconst astro = (args: string[]) =>\n spawnSync(existsSync('node_modules/.bin/astro') ? 'node_modules/.bin/astro' : 'astro', args, {\n stdio: 'ignore',\n timeout: 300_000,\n env: { ...process.env, CI: '1' },\n });\n\n/**\n * Starts `astro dev` once, calls EmDash's `dev-bypass` setup endpoint (the non-interactive\n * setup wizard: it runs migrations, `dev: 'auto'`, and applies the seed), checks the home page\n * renders, stops. Only ever writes the local `.wrangler/state`, which is disposable; the dev\n * administrator it creates lives in that copy and is never deployed. Content already there\n * (from `pull-content`) is kept: the seed applies with `onConflict: skip`.\n */\nasync function cmdPrepareDb() {\n if (process.env.SKIP_PREPARE_LOCAL_DB) {\n log('SKIP_PREPARE_LOCAL_DB set — leaving the local database as it is.');\n return;\n }\n const port = await freePort();\n const base = `http://127.0.0.1:${port}`;\n\n log(`starting astro dev on ${base} ...`);\n const started = astro(['dev', '--port', String(port), '--host', '127.0.0.1']);\n if ((started.error as NodeJS.ErrnoException | undefined)?.code === 'ENOENT')\n fail('`astro` is not on PATH. Run it through the package scripts (`vpr prepare-local-db`).');\n if (started.status !== 0)\n fail(\n `could not start astro dev (${started.signal ?? `exit ${started.status}`}). ` +\n 'It is killed after 5 minutes (exit 143 = SIGTERM). Run `astro dev` by hand to see why.',\n );\n\n let failure: Error | null = null;\n try {\n await until(async () => (await fetch(`${base}/_emdash/api/setup/status`)).ok, 'astro dev');\n log('dev server is up; migrating and seeding (first run can take a minute) ...');\n // A database that is already set up may decline; the home page check decides if it matters.\n const setup = await fetch(`${base}/_emdash/api/setup/dev-bypass?redirect=/`, {\n redirect: 'manual',\n signal: AbortSignal.timeout(120_000),\n });\n if (setup.status >= 400)\n console.warn(\n `prepare-db: dev-bypass answered ${setup.status}; checking the home page anyway.`,\n );\n\n const home = await fetch(`${base}/`);\n const html = await home.text();\n if (!home.ok)\n throw new Error(`astro dev answered ${home.status} on / — the dev runtime is broken`);\n if (PENDING_MIGRATIONS.test(html))\n throw new Error('the local database still has pending migrations');\n log('the local database is migrated and the home page renders.');\n } catch (e) {\n failure = e as Error;\n } finally {\n astro(['dev', 'stop']);\n }\n if (failure)\n fail(\n `${failure.message}\\nRefusing to continue: prerendering now would publish error pages as the site.`,\n );\n}\n\nfunction* filesWith(dir: string, ext: string): Generator<string> {\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n const path = join(dir, entry.name);\n if (entry.isDirectory()) yield* filesWith(path, ext);\n else if (entry.name.endsWith(ext)) yield path;\n }\n}\nconst htmlFiles = (dir: string) => filesWith(dir, '.html');\nconst cssFiles = (dir: string) => [...filesWith(dir, '.css')];\n\nfunction cmdCheckPrerender(argv: string[]) {\n const root = argv.find((a) => !a.startsWith('-')) ?? 'dist/client';\n const strict = argv.includes('--strict');\n if (!existsSync(root)) fail(`${root} does not exist (run \\`astro build\\` first).`);\n const bad: string[] = [];\n const unstyled: string[] = [];\n const outline: string[] = [];\n const dead: string[] = [];\n let formPage: string | undefined;\n const formFields = new Set<string>();\n const routes = new Set([...htmlFiles(root)].map((f) => routeOf(relative(root, f))));\n const css = cssFiles(root)\n .map((f) => readFileSync(f, 'utf8'))\n .join('\\n');\n let count = 0;\n for (const file of htmlFiles(root)) {\n count += 1;\n const html = readFileSync(file, 'utf8');\n const why = prerenderProblem(html);\n if (why) bad.push(` ${file} ${why}`);\n else {\n const h1s = h1Count(html);\n if (h1s !== 1 && !file.endsWith('/404.html')) outline.push(` ${file}: ${h1s} <h1>`);\n }\n if (!why && hasConversionForm(html)) {\n formPage ??= file;\n for (const n of formFieldNames(html)) formFields.add(n);\n }\n if (!why) {\n const links = brokenInternalLinks(html, routes);\n if (links.length) dead.push(` ${file}: ${links.join(', ')}`);\n }\n if (!why && css) {\n const missing = missingTypeRoleRules(html, css);\n if (missing.length) unstyled.push(` ${file}: ${missing.join(', ')}`);\n }\n }\n if (count === 0) fail(`no .html files under ${root}.`);\n if (bad.length)\n fail(\n `the build prerendered pages that are not real pages:\\n${bad.join('\\n')}\\n\\n` +\n 'The local database the build reads is probably unmigrated. Run `vpr prepare-local-db` ' +\n '(it migrates and seeds the local D1 through `astro dev`), then build again.',\n );\n if (unstyled.length) {\n const note =\n `type-role classes with no rule in the built CSS (they render at body size):\\n${unstyled.join('\\n')}\\n\\n` +\n 'Tailwind does not scan node_modules. Use @getvitops/astro >= the release that adds the ' +\n \"`@source` lines, or add `@source` for the package that renders them to the site's stylesheet.\";\n if (strict) fail(note);\n console.warn(`check-prerender: warning: ${note}`);\n }\n if (formPage) {\n const siteFile = argv.find((a) => a.startsWith('--site='))?.slice(7) ?? 'site.json';\n let siteConfig: unknown;\n try {\n if (existsSync(siteFile)) siteConfig = JSON.parse(readFileSync(siteFile, 'utf8'));\n } catch {\n siteConfig = undefined; // an unreadable config is `vitops validate`'s to report\n }\n if (siteConfig) {\n const problem = deliveryProblem(siteConfig);\n if (problem) {\n const note =\n `${formPage} has a form, but ${problem}. The route refuses submissions until one is set, ` +\n 'so the first visitor to send a message would be told it failed. Run `vitops email setup` ' +\n 'for the sender domain once it is.';\n if (strict) fail(note);\n console.warn(`check-prerender: warning: ${note}`);\n }\n const missing = uninventoriedFields(siteConfig, [...formFields].sort());\n if (missing.length) {\n const note =\n `the form collects ${missing.join(', ')}, which no site.templates form entry in ${siteFile} ` +\n 'lists. `vitops legal` builds the privacy policy\\'s \"what we collect\" from those entries, ' +\n 'so the generated policy would under-report. Mirror the fields in a `type: \"form\"` template.';\n if (strict) fail(note);\n console.warn(`check-prerender: warning: ${note}`);\n }\n }\n }\n if (dead.length) {\n const note =\n `links to pages the build did not produce (they would 404):\\n${dead.join('\\n')}\\n\\n` +\n 'Publish the page, or mark the menu item as a planned link by adding `draft` to its CSS ' +\n 'classes so it is not rendered until the page exists.';\n if (strict) fail(note);\n console.warn(`check-prerender: warning: ${note}`);\n }\n if (outline.length) {\n const note =\n `pages whose outline has not exactly one <h1>:\\n${outline.join('\\n')}\\n\\n` +\n 'Give each page one: set a `vitops_hero` (or section) heading level to h1, or let the ' +\n 'layout render the title, and make other display headings h2 or p.';\n if (strict) fail(note);\n console.warn(`check-prerender: warning: ${note}`);\n }\n console.log(`check-prerender: ${count} prerendered page(s) look like real pages.`);\n}\n\n/**\n * `astro build` and then `check-prerender`, failing on any block type EmDash reported as having\n * no component. That report is a build-log warning, not an exit code, so the build's output is\n * streamed through unchanged and scanned afterwards.\n */\nasync function cmdBuild(argv: string[]) {\n const bin = existsSync('node_modules/.bin/astro') ? 'node_modules/.bin/astro' : 'astro';\n let log = '';\n const code = await new Promise<number>((resolve) => {\n const child = spawn(bin, ['build'], { stdio: ['inherit', 'pipe', 'pipe'] });\n child.stdout.on('data', (d) => {\n log += d;\n process.stdout.write(d);\n });\n child.stderr.on('data', (d) => {\n log += d;\n process.stderr.write(d);\n });\n child.on('error', () => resolve(1));\n child.on('close', (c) => resolve(c ?? 1));\n });\n if (code !== 0) process.exit(code);\n const missing = missingBlockComponents(log);\n if (missing.length)\n fail(\n `the build rendered nothing for these block types (no component registered):\\n` +\n missing.map((t) => ` ${t}`).join('\\n') +\n '\\n\\nEvery page using them was published without that content. If they are `vitops_*` ' +\n 'blocks, the plugin block map was empty for this build: check that no module imported by ' +\n '`@getvitops/emdash/astro` imports `emdash/ui` statically (an import cycle), and that the ' +\n 'plugin is registered in astro.config.mjs.',\n );\n cmdCheckPrerender(argv);\n}\n\nexport async function cmdEmdash(argv: string[]) {\n const [sub, ...rest] = argv;\n switch (sub) {\n case 'prepare-db':\n return cmdPrepareDb();\n case 'check-prerender':\n return cmdCheckPrerender(rest);\n case 'build':\n return cmdBuild(rest);\n case undefined:\n case '-h':\n case '--help':\n console.log(EMDASH_HELP);\n return;\n default:\n fail(\n `unknown \"emdash\" subcommand \"${sub}\" (expected: prepare-db | check-prerender | build). Try: vitops --help`,\n );\n }\n}\n","/**\n * `vitops setup` — the scripted path from an empty scaffold to a live site.\n * Subcommands: `repo | hosting | domain | search | ads | all`.\n *\n * Absorbs what each template's `scripts/init-{github,hosting}.mjs` used to do\n * template-locally, so the capability reaches every consumer, not just a fresh\n * `vp create` scaffold. `repo`/`hosting` follow the pure-planner/executor split in\n * `@getvitops/utils/hosting` — see that module's docblock. `search`/`ads` are not\n * reimplemented: this shells out to the CLI's own `search setup`/`ads setup`\n * subcommands as child processes, so their behaviour (prompting, `--dry`,\n * `--check`, writing the config patch back) stays exactly what it already is,\n * with no risk of the two copies drifting.\n *\n * `domain` only ever plans and checks the workers.dev-vs-custom-domain\n * consistency — it never touches `wrangler.jsonc` or deploys anything. Cutover\n * overwrites the zone's DNS record and can take an existing live site down; that\n * is a human's call, made in `SETUP.html`, not a step this command applies.\n */\nimport { execFileSync } from 'node:child_process';\nimport { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport {\n adminCollaboratorIds,\n branches as ghBranches,\n createD1,\n createDevEnvironment,\n createProdEnvironment,\n createR2Bucket,\n createRepo,\n defaultBranch as ghDefaultBranch,\n environments as ghEnvironments,\n findD1,\n findR2Bucket,\n formatPlan,\n formatSummary,\n ghAuthed,\n mainProtected as ghMainProtected,\n originRemote,\n plan as planHosting,\n planDomainConsistency,\n protectMain,\n pushBranches,\n repoNwo as ghRepoNwo,\n secretsPresent,\n setDefaultBranch,\n setSecret,\n wranglerWhoami,\n type DomainState,\n type HostingResult,\n type HostingSetup,\n type HostingState,\n} from '@getvitops/utils/hosting';\nimport {\n createWidget,\n deriveDomains,\n formatPlan as formatTurnstilePlan,\n formatSummary as formatTurnstileSummary,\n hasDrift as hasTurnstileDrift,\n listWidgets,\n patchSiteKey,\n plan as planTurnstile,\n resolveAccountId,\n type TurnstileResult,\n} from '@getvitops/utils/turnstile';\nimport { parse, msg } from '../args.ts';\nimport { CLOUDFLARE_TOKEN_HINT, loadCloudflareToken } from '../credentials.ts';\nimport { fail, loadConfig, SITE_OPTS } from './shared.ts';\n\nconst SETUP_HELP = `vitops setup <sub> [options]\n\n vitops setup repo [--dry] [--check] [--public]\n vitops setup hosting [--dry] [--check] [--platform cloudflare|node]\n vitops setup domain [--dry] [--check] [--domain <apex>]\n vitops setup turnstile [--dry] [--check] [--no-write] [--no-secret]\n vitops setup search [options] (forwards to \\`vitops search setup\\`)\n vitops setup ads [options] (forwards to \\`vitops ads setup\\`)\n vitops setup all [--dry] [--check] [--public] [--platform <p>]\n\n--dry (or --dry-run) prints the plan and stops. --check reports drift/blocked steps and exits\nnon-zero, mutating nothing. Neither creates the Cloudflare API token or performs\na custom-domain cutover — both are named as manual steps; see SETUP.html.`;\n\nfunction projectName(): string {\n const path = resolve('package.json');\n if (!existsSync(path)) fail('package.json not found — run this from the project root');\n const pkg = JSON.parse(readFileSync(path, 'utf8')) as { name?: string };\n if (!pkg.name) fail('package.json has no \"name\" field');\n return pkg.name!.replace(/^@[^/]+\\//, '');\n}\n\nasync function gatherHostingState(\n name: string,\n platform: 'cloudflare' | 'node',\n): Promise<HostingState> {\n const [wrangler, gh, origin] = await Promise.all([\n platform === 'cloudflare' ? wranglerWhoami() : Promise.resolve({ authed: false } as const),\n ghAuthed(),\n originRemote(),\n ]);\n const nwo = origin ? await ghRepoNwo() : undefined;\n const [branchesList, def, protectedMain, envs, secrets, d1Id, r2] = await Promise.all([\n ghBranches(),\n nwo ? ghDefaultBranch(nwo) : Promise.resolve(undefined),\n nwo ? ghMainProtected(nwo) : Promise.resolve(false),\n nwo ? ghEnvironments(nwo) : Promise.resolve([]),\n nwo ? secretsPresent(nwo) : Promise.resolve([]),\n platform === 'cloudflare' ? findD1(`${name}-dev`) : Promise.resolve(undefined),\n platform === 'cloudflare' ? findR2Bucket(`${name}-media-dev`) : Promise.resolve(false),\n ]);\n return {\n wranglerAuthed: wrangler.authed,\n ...('accountId' in wrangler && wrangler.accountId ? { accountId: wrangler.accountId } : {}),\n d1Present: Boolean(d1Id),\n ...(d1Id ? { d1Id } : {}),\n r2Present: r2,\n ghAuthed: gh,\n ...(origin ? { originRemote: origin } : {}),\n ...(nwo ? { repoNwo: nwo } : {}),\n branches: branchesList,\n ...(def ? { defaultBranch: def } : {}),\n mainProtected: protectedMain,\n environments: envs,\n secretsPresent: secrets,\n };\n}\n\nasync function cmdSetupRepo(argv: string[]) {\n const a = parse(argv, {\n dry: { type: 'boolean', default: false },\n check: { type: 'boolean', default: false },\n public: { type: 'boolean', default: false },\n platform: { type: 'string', default: 'cloudflare' },\n 'no-dev-resources': { type: 'boolean', default: false },\n });\n const platform = a.str('platform');\n if (platform !== 'cloudflare' && platform !== 'node')\n fail(`--platform \"${platform}\" must be cloudflare or node`);\n const name = projectName();\n const setup: HostingSetup = {\n platform,\n projectName: name,\n repoVisibility: a.flag('public') ? 'public' : 'private',\n devResources: !a.flag('no-dev-resources'),\n };\n const state = await gatherHostingState(name, setup.platform);\n const p = planHosting(setup, state);\n const dry = a.flag('dry');\n const check = a.flag('check');\n\n if (dry || check) {\n console.log(formatPlan(p));\n if (\n check &&\n (p.repo.action !== 'skip' ||\n p.branches.action !== 'skip' ||\n p.defaultBranch.action !== 'skip')\n ) {\n console.error('\\n✖ repo/branch setup is not complete');\n process.exit(1);\n }\n return;\n }\n\n const results: HostingResult[] = [];\n if (p.repo.action === 'create') {\n const r = await createRepo(name, setup.repoVisibility);\n results.push({\n step: 'repo',\n status: r.ok ? 'created' : 'failed',\n detail: r.ok ? name : (r.message ?? 'failed'),\n });\n if (r.ok) {\n const push = await pushBranches();\n results.push({\n step: 'branches',\n status: push.ok ? 'created' : 'failed',\n detail: push.ok ? 'main + dev pushed' : (push.message ?? 'failed'),\n });\n }\n } else {\n results.push({ step: 'repo', status: 'present', detail: p.repo.detail });\n }\n\n const nwo = state.repoNwo ?? (await ghRepoNwo());\n if (nwo) {\n if (p.defaultBranch.action !== 'skip') {\n const r = await setDefaultBranch(nwo, 'dev');\n results.push({\n step: 'default branch',\n status: r.ok ? 'updated' : 'failed',\n detail: r.ok ? 'dev' : (r.message ?? 'failed'),\n });\n } else results.push({ step: 'default branch', status: 'present', detail: 'dev' });\n\n if (p.mainProtection.action === 'blocked') {\n const r = await protectMain(nwo);\n results.push({\n step: 'main protection',\n status: r.ok ? 'created' : 'blocked',\n detail: r.ok ? 'protected' : (r.message ?? p.mainProtection.detail),\n });\n } else\n results.push({ step: 'main protection', status: 'present', detail: p.mainProtection.detail });\n\n if (p.prodEnvironment.action === 'blocked') {\n const ids = await adminCollaboratorIds(nwo);\n const r = await createProdEnvironment(nwo, ids);\n results.push({\n step: 'production env',\n status: r.ok ? 'created' : 'blocked',\n detail: r.ok ? `${ids.length} reviewer(s)` : (r.message ?? p.prodEnvironment.detail),\n });\n } else\n results.push({ step: 'production env', status: 'present', detail: p.prodEnvironment.detail });\n\n if (p.devEnvironment.action === 'create') {\n const r = await createDevEnvironment(nwo);\n results.push({\n step: 'dev env',\n status: r.ok ? 'created' : 'failed',\n detail: r.ok ? 'no required reviewers' : (r.message ?? 'failed'),\n });\n } else results.push({ step: 'dev env', status: 'present', detail: p.devEnvironment.detail });\n } else {\n results.push({\n step: 'branch policy',\n status: 'blocked',\n detail: 'no GitHub repo yet — run this again once \"repo\" succeeds',\n });\n }\n\n console.log(formatSummary(results));\n if (results.some((r) => r.status === 'failed')) process.exit(1);\n}\n\nasync function cmdSetupHosting(argv: string[]) {\n const a = parse(argv, {\n dry: { type: 'boolean', default: false },\n check: { type: 'boolean', default: false },\n platform: { type: 'string', default: 'cloudflare' },\n 'no-dev-resources': { type: 'boolean', default: false },\n });\n const platform = a.str('platform');\n if (platform !== 'cloudflare' && platform !== 'node')\n fail(`--platform \"${platform}\" must be cloudflare or node`);\n const name = projectName();\n // --no-dev-resources: a pure-static Workers Assets project (no D1/R2 at all)\n // still needs the account id + eventual token, but has nothing to provision.\n const setup: HostingSetup = {\n platform,\n projectName: name,\n repoVisibility: 'private',\n devResources: !a.flag('no-dev-resources'),\n };\n const state = await gatherHostingState(name, platform);\n const p = planHosting(setup, state);\n const dry = a.flag('dry');\n const check = a.flag('check');\n\n if (platform === 'node') {\n console.log(\n 'Hosting target \"node\" has no cloud resources to provision — see the template README\\'s \"Hosting\" section.',\n );\n return;\n }\n if (!state.wranglerAuthed && !dry)\n fail('wrangler is not logged in — run `npx wrangler login` first');\n\n if (dry || check) {\n console.log(formatPlan(p));\n if (check && (p.d1.action !== 'skip' || p.r2.action !== 'skip')) {\n console.error('\\n✖ hosting resources are not fully provisioned');\n process.exit(1);\n }\n return;\n }\n\n const results: HostingResult[] = [];\n let d1Id = state.d1Id;\n if (p.d1.action === 'create') {\n const r = await createD1(`${name}-dev`);\n results.push({\n step: 'dev D1',\n status: r.ok ? 'created' : 'failed',\n detail: r.ok ? (r.id ?? '') : (r.message ?? 'failed'),\n });\n if (r.ok) d1Id = r.id;\n } else results.push({ step: 'dev D1', status: 'present', detail: p.d1.detail });\n\n if (p.r2.action === 'create') {\n const r = await createR2Bucket(`${name}-media-dev`);\n results.push({\n step: 'dev R2',\n status: r.ok ? 'created' : 'failed',\n detail: r.ok ? `${name}-media-dev` : (r.message ?? 'failed'),\n });\n } else results.push({ step: 'dev R2', status: 'present', detail: p.r2.detail });\n\n const nwo = state.repoNwo ?? (await ghRepoNwo());\n if (nwo && state.ghAuthed) {\n if (d1Id && !state.secretsPresent.includes('DEV_D1_ID')) {\n const r = await setSecret('DEV_D1_ID', d1Id);\n results.push({\n step: 'secret DEV_D1_ID',\n status: r.ok ? 'created' : 'failed',\n detail: r.ok ? 'set' : (r.message ?? 'failed'),\n });\n } else\n results.push({\n step: 'secret DEV_D1_ID',\n status: state.secretsPresent.includes('DEV_D1_ID') ? 'present' : 'blocked',\n detail: d1Id ?? 'D1 id unknown',\n });\n\n if (state.accountId && !state.secretsPresent.includes('CLOUDFLARE_ACCOUNT_ID')) {\n const r = await setSecret('CLOUDFLARE_ACCOUNT_ID', state.accountId);\n results.push({\n step: 'secret CLOUDFLARE_ACCOUNT_ID',\n status: r.ok ? 'created' : 'failed',\n detail: r.ok ? 'set' : (r.message ?? 'failed'),\n });\n } else\n results.push({\n step: 'secret CLOUDFLARE_ACCOUNT_ID',\n status: state.secretsPresent.includes('CLOUDFLARE_ACCOUNT_ID') ? 'present' : 'blocked',\n detail: state.accountId ?? 'unknown',\n });\n } else {\n results.push({\n step: 'secrets',\n status: 'blocked',\n detail: 'no GitHub repo wired up yet — run `vitops setup repo` first',\n });\n }\n\n results.push({\n step: 'secret CLOUDFLARE_API_TOKEN',\n status: 'blocked',\n detail: p.secrets['CLOUDFLARE_API_TOKEN']?.detail ?? 'create it by hand',\n });\n\n console.log(formatSummary(results));\n for (const n of p.notes) console.log(`! ${n}`);\n if (results.some((r) => r.status === 'failed')) process.exit(1);\n}\n\nasync function cmdSetupDomain(argv: string[]) {\n const a = parse(argv, {\n ...SITE_OPTS,\n dry: { type: 'boolean', default: false },\n check: { type: 'boolean', default: false },\n domain: { type: 'string' },\n });\n const wranglerPath = resolve('wrangler.jsonc');\n if (!existsSync(wranglerPath)) fail('wrangler.jsonc not found — run this from the project root');\n const raw = readFileSync(wranglerPath, 'utf8');\n const customDomainMatch = /^\\s*\"routes\":\\s*\\[\\s*\\{\\s*\"pattern\":\\s*\"([^\"]+)\"/m.exec(\n raw\n .split('\\n')\n .filter((l) => !l.trim().startsWith('//'))\n .join('\\n'),\n );\n const customDomain = customDomainMatch?.[1] ?? (a.opt('domain') ? undefined : undefined);\n\n const cfg = await loadConfig(a.path('input'), a.str('site-env'));\n const env = cfg.site.environments[a.str('site-env')] ?? cfg.site.environments['production'];\n\n const state: DomainState = {\n wranglerDev: !raw.includes('\"routes\"') || Boolean(customDomainMatch) === false,\n ...(customDomain ? { customDomain } : {}),\n ...(env?.url ? { configuredUrl: env.url } : {}),\n ...(cfg.site.domains?.canonical ? { canonical: cfg.site.domains.canonical } : {}),\n ...(env?.robots ? { robots: env.robots } : {}),\n };\n\n const step = planDomainConsistency(state);\n console.log(`${step.action === 'skip' ? '·' : '?'} domain consistency ${step.detail}`);\n if (a.opt('domain'))\n console.log(\n `\\nCutover is never applied by this command — see SETUP.html stage 4b. To cut over: uncomment ` +\n `routes in wrangler.jsonc with \"pattern\": \"${a.opt('domain')}\", deploy, then update ` +\n `site.domains.canonical and site.environments.${a.str('site-env')}.url to match.`,\n );\n if (a.flag('check') && step.action !== 'skip') process.exit(1);\n}\n\n/**\n * Create the Cloudflare Turnstile widget for the site and record its PUBLIC site key.\n *\n * The secret is returned by Cloudflare only at creation. It is never written to a config\n * file: it is piped to `wrangler secret put VITOPS_TURNSTILE_SECRET` on stdin (never argv,\n * never logged). If that fails, it is shown once on an interactive terminal and otherwise\n * not at all, with the rotate instruction. Never edits, rotates or deletes a widget.\n */\nasync function cmdSetupTurnstile(argv: string[]) {\n const a = parse(argv, {\n ...SITE_OPTS,\n dry: { type: 'boolean', default: false },\n check: { type: 'boolean', default: false },\n 'no-write': { type: 'boolean', default: false },\n 'no-secret': { type: 'boolean', default: false },\n });\n const input = a.path('input');\n const cfg = await loadConfig(input, a.str('site-env'));\n const dry = a.flag('dry');\n const check = a.flag('check');\n const token = loadCloudflareToken();\n // Like `ads setup`: a dry run mutates nothing, so it must not demand the credential.\n if (!token && !dry)\n fail(\n `${CLOUDFLARE_TOKEN_HINT}\\n For this command the token needs \"Account > Turnstile > Edit\" ` +\n `(also set CLOUDFLARE_ACCOUNT_ID, or the token must be able to list accounts).`,\n );\n\n const setup = {\n name: (() => {\n // `organization.name` may be a per-locale map; any one value names the widget.\n const n = cfg.organization?.name;\n return (typeof n === 'string' ? n : n ? Object.values(n)[0] : undefined) ?? projectName();\n })(),\n domains: deriveDomains(cfg.site),\n siteKey: cfg.site.security?.turnstile?.siteKey,\n };\n let accountId: string | undefined;\n let widgets: Awaited<ReturnType<typeof listWidgets>>['widgets'] = [];\n if (token) {\n const acct = await resolveAccountId(token, process.env.CLOUDFLARE_ACCOUNT_ID || undefined);\n if (!acct.ok) {\n if (!dry) fail(`Cloudflare account: ${acct.message}`);\n } else {\n accountId = acct.accountId;\n const listed = await listWidgets(token, accountId!);\n if (!listed.ok) fail(`listing Turnstile widgets: ${listed.message}`);\n widgets = listed.widgets;\n }\n }\n const p = planTurnstile(setup, { accountId, widgets });\n\n if (dry || check) {\n console.log(formatTurnstilePlan(p));\n if (check && hasTurnstileDrift(p)) {\n console.error('\\n✖ Turnstile is not set up');\n process.exit(1);\n }\n return;\n }\n\n const results: TurnstileResult[] = [];\n let siteKey = p.adopt?.sitekey;\n if (p.widget.action === 'blocked') {\n results.push({ step: 'widget', status: 'blocked', detail: p.widget.detail });\n } else if (p.widget.action === 'create') {\n const r = await createWidget(token!, accountId!, { name: setup.name, domains: p.domains });\n if (!r.ok) results.push({ step: 'widget', status: 'failed', detail: r.message ?? 'failed' });\n else {\n siteKey = r.sitekey;\n results.push({ step: 'widget', status: 'created', detail: r.sitekey! });\n if (a.flag('no-secret')) {\n console.error(\n '! --no-secret: the secret was NOT saved and cannot be read back. Rotate it in the dashboard, then `wrangler secret put VITOPS_TURNSTILE_SECRET`.',\n );\n } else {\n try {\n execFileSync('npx', ['wrangler', 'secret', 'put', 'VITOPS_TURNSTILE_SECRET'], {\n input: r.secret!,\n stdio: ['pipe', 'inherit', 'inherit'],\n });\n results.push({\n step: 'secret',\n status: 'created',\n detail: 'VITOPS_TURNSTILE_SECRET (Worker secret)',\n });\n } catch {\n results.push({ step: 'secret', status: 'failed', detail: 'wrangler secret put failed' });\n if (process.stdout.isTTY) {\n console.error(\n `! Save this now, it is shown once: VITOPS_TURNSTILE_SECRET=${r.secret}\\n Then: npx wrangler secret put VITOPS_TURNSTILE_SECRET`,\n );\n } else {\n console.error(\n '! The secret was not saved and is not printed (non-interactive). Rotate it in the dashboard, then `wrangler secret put VITOPS_TURNSTILE_SECRET`.',\n );\n }\n }\n }\n }\n } else results.push({ step: 'widget', status: 'present', detail: p.widget.detail });\n\n if (siteKey && p.config.action === 'create' && !a.flag('no-write')) {\n if (!input.endsWith('.json'))\n results.push({\n step: 'site.json',\n status: 'blocked',\n detail: `${input} is a module — add site.security.turnstile.siteKey: \"${siteKey}\" yourself`,\n });\n else {\n const source = readFileSync(input, 'utf8');\n const indent = /\\n([ \\t]+)\"/.exec(source)?.[1];\n writeFileSync(\n input,\n JSON.stringify(\n patchSiteKey(JSON.parse(source), siteKey),\n null,\n indent?.includes('\\t') ? '\\t' : (indent?.length ?? 2),\n ) + (source.endsWith('\\n') ? '\\n' : ''),\n );\n results.push({\n step: 'site.json',\n status: 'created',\n detail: 'site.security.turnstile.siteKey',\n });\n }\n } else if (p.config.action === 'skip')\n results.push({ step: 'site.json', status: 'present', detail: p.config.detail });\n\n console.log(`\\n${formatTurnstileSummary(results)}`);\n for (const r of p.reminders) console.log(` ${r}`);\n if (results.some((r) => r.status === 'failed' || r.status === 'blocked')) process.exit(1);\n}\n\n/** Forward to the CLI's own `search setup` / `ads setup` as a child process — see\n * this module's docblock for why (their behaviour must not be re-derived). */\nfunction forward(sub: 'search' | 'ads', argv: string[]): boolean {\n try {\n execFileSync(process.execPath, [process.argv[1]!, sub, 'setup', ...argv], { stdio: 'inherit' });\n return true;\n } catch (err) {\n const e = err as { status?: number };\n if (e.status != null) return false;\n fail(msg(err));\n }\n}\n\nexport async function cmdSetup(argv: string[]): Promise<void> {\n // `--dry-run` is the spelling most people reach for; the flag is `--dry` everywhere else.\n const [sub, ...rest] = argv.map((a) => (a === '--dry-run' ? '--dry' : a));\n switch (sub) {\n case 'repo':\n return cmdSetupRepo(rest);\n case 'hosting':\n return cmdSetupHosting(rest);\n case 'domain':\n return cmdSetupDomain(rest);\n case 'turnstile':\n return cmdSetupTurnstile(rest);\n case 'search':\n if (!forward('search', rest)) process.exit(1);\n return;\n case 'ads':\n if (!forward('ads', rest)) process.exit(1);\n return;\n case 'all': {\n const a = parse(rest, {\n dry: { type: 'boolean', default: false },\n check: { type: 'boolean', default: false },\n public: { type: 'boolean', default: false },\n platform: { type: 'string', default: 'cloudflare' },\n 'no-dev-resources': { type: 'boolean', default: false },\n });\n const dryCheckFlags = [\n ...(a.flag('dry') ? ['--dry'] : []),\n ...(a.flag('check') ? ['--check'] : []),\n ];\n const hostingFlags = [\n ...dryCheckFlags,\n '--platform',\n a.str('platform'),\n ...(a.flag('no-dev-resources') ? ['--no-dev-resources'] : []),\n ];\n console.log('── repo ──');\n await cmdSetupRepo([...hostingFlags, ...(a.flag('public') ? ['--public'] : [])]);\n console.log('\\n── hosting ──');\n await cmdSetupHosting(hostingFlags);\n console.log('\\n── search ──');\n const searchOk = forward('search', dryCheckFlags);\n console.log('\\n── ads ──');\n const adsOk = forward('ads', dryCheckFlags);\n if (!searchOk || !adsOk) process.exit(1);\n return;\n }\n case undefined:\n case '-h':\n case '--help':\n console.log(SETUP_HELP);\n return;\n default:\n fail(\n `unknown \"setup\" subcommand \"${sub}\" (expected: repo | hosting | domain | turnstile | search | ads | all). Try: vitops --help`,\n );\n }\n}\n","/**\n * Side-effect-free helpers for the `vitops agents` / `vitops docs` commands.\n * Kept out of cli.ts (which runs main() on import) so they are unit-testable.\n */\nimport {\n existsSync,\n lstatSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n readlinkSync,\n rmSync,\n symlinkSync,\n} from 'node:fs';\nimport { dirname, join, parse, relative, resolve } from 'node:path';\n\nexport const SKILL_NAME = 'vitops-design-system';\n\n/** Marker the 0.5.0-era generated skill carried; lets us migrate that layout. */\nexport const GENERATED_MARKER = '<!-- GENERATED by @getvitops/cli';\n\n/**\n * `vitops docs` topics → docs-bundle paths (as returned by `generateDocs`).\n * Order is the `--all` concatenation order.\n */\nexport const TOPICS: Record<string, { path: string; summary: string; external?: 'emdash' }> = {\n classes: {\n path: 'css/classes.md',\n summary: 'CSS framework class vocabulary (colour, type, space, layout, animation, patterns)',\n },\n authoring: {\n path: 'authoring.md',\n summary: 'every design-system.json field, rendered from the JSON Schema',\n },\n config: {\n path: 'config.md',\n summary:\n 'every field of the three-section config (designSystem / organization / site), rendered from the JSON Schema',\n },\n formats: {\n path: 'formats.md',\n summary: \"tailwind vs css vs bricks: what's emitted vs what the platform provides\",\n },\n color: {\n path: 'concepts/color.md',\n summary: 'seeded OKLCH scales, functional tokens, automatic dark mode',\n },\n scales: {\n path: 'concepts/scales.md',\n summary: 'fluid modular type & space scales',\n },\n icons: {\n path: 'concepts/icons.md',\n summary: 'semantic icon names across sets, bundle derivation, sprite delivery',\n },\n patterns: {\n path: 'concepts/patterns.md',\n summary: 'pattern CSS: token cascade, override hooks, states, role variants',\n },\n components: {\n path: 'concepts/components.md',\n summary: 'which of the four tiers provides each pattern, and the call to make',\n },\n consent: {\n path: 'concepts/consent.md',\n summary: 'the consent gate: inert gated tags, demand-driven prompting, three-valued choices',\n },\n tracking: {\n path: 'concepts/tracking.md',\n summary: 'ad-click attribution, the `_ac` cookie, conversion notifications',\n },\n search: {\n path: 'concepts/search.md',\n summary: 'what search engines accept, Search Console onboarding, deploy notification',\n },\n legal: {\n path: 'concepts/legal.md',\n summary: 'privacy policy / terms / cookie notice derived from config facts',\n },\n blocks: {\n path: '',\n external: 'emdash',\n summary: 'EmDash Portable Text blocks and every field (needs @getvitops/emdash installed)',\n },\n elements: {\n path: 'bricks/elements.md',\n summary: 'Bricks Builder custom elements, controls, defaults',\n },\n};\n\n/**\n * Find the installed package's skill directory by walking up from `cwd` looking\n * for `node_modules/@getvitops/cli/skill/SKILL.md`. Returns the LOGICAL path\n * (through the `node_modules/@getvitops/cli` link, unresolved) or null.\n *\n * Deliberately not derived from import.meta.url: under pnpm the running module's\n * URL is the realpath in `node_modules/.pnpm/@getvitops+cli@<version>/…`, which\n * changes on every version bump — a symlink to it would dangle after upgrades.\n * The logical path is recreated by the package manager on every install, so a\n * link through it always resolves to the currently installed version.\n */\nexport function findSkillTarget(cwd: string): string | null {\n let dir = resolve(cwd);\n const { root } = parse(dir);\n for (;;) {\n const skillDir = join(dir, 'node_modules', '@getvitops', 'cli', 'skill');\n if (existsSync(join(skillDir, 'SKILL.md'))) return skillDir;\n if (dir === root) return null;\n dir = dirname(dir);\n }\n}\n\n/**\n * Idempotently symlink `linkPath` → `targetDir` (relative target). Replaces a\n * stale symlink; migrates a real directory left by the old emitting `vitops\n * agents` (identified by GENERATED_MARKER in its SKILL.md); refuses to clobber\n * anything else. Returns a warning string, or null on success / no-op.\n */\nexport function linkSkill(linkPath: string, targetDir: string): string | null {\n const target = relative(dirname(linkPath), resolve(targetDir));\n try {\n const st = lstatSync(linkPath, { throwIfNoEntry: false });\n if (st && !st.isSymbolicLink()) {\n const oldSkill = join(linkPath, 'SKILL.md');\n const isGenerated =\n st.isDirectory() &&\n existsSync(oldSkill) &&\n readFileSync(oldSkill, 'utf8').includes(GENERATED_MARKER);\n if (!isGenerated)\n return `left ${linkPath} alone (exists and is not a symlink) — link it to ${targetDir} yourself if wanted`;\n rmSync(linkPath, { recursive: true }); // old generated skill layout → replace\n } else if (st) {\n if (readlinkSync(linkPath) === target) return null;\n rmSync(linkPath);\n } else {\n mkdirSync(dirname(linkPath), { recursive: true });\n }\n symlinkSync(target, linkPath);\n return null;\n } catch (err) {\n return `could not symlink ${linkPath}: ${(err as Error).message}`;\n }\n}\n\nexport const AGENTS_START = '<!-- vitops:start -->';\nexport const AGENTS_END = '<!-- vitops:end -->';\n\n/** A skill the block links to. */\nexport interface BlockSkill {\n name: string;\n /** Path from the project root, as the link target. */\n path: string;\n /** One short line: when to load it. */\n summary: string;\n}\n\n/** A frontmatter `description`, whether a plain line or a folded (`>-`) scalar. */\nfunction descriptionOf(skillMd: string): string {\n const fm = /^---\\n([\\s\\S]*?)\\n---/.exec(skillMd)?.[1] ?? '';\n const m = /^description:[ \\t]*(.*)((?:\\n[ \\t]+.*)*)/m.exec(fm);\n if (!m) return '';\n const first = m[1]!.trim();\n const folded = /^[>|][-+]?$/.test(first);\n return [folded ? '' : first, ...m[2]!.split('\\n').map((l) => l.trim())].filter(Boolean).join(' ');\n}\n\n/**\n * One short line saying when to load a skill: its \"Use when/for/before …\" sentence if it has one\n * (that is the part that answers \"is this the one?\"), else its first sentence.\n */\nexport function skillSummary(skillMd: string, max = 90): string {\n const description = descriptionOf(skillMd);\n const use = /\\bUse (?:when|for|before|ONLY)\\b[^.]*/.exec(description)?.[0];\n const line = use ?? description.split(/(?<=[.!?])\\s/)[0] ?? description;\n return line.length > max ? `${line.slice(0, max - 1).trimEnd()}…` : line;\n}\n\n/**\n * The skills that belong in the block: those under `.agents/skills` that are links into an installed\n * `@getvitops/*` package. A site's own skills and the EmDash ones are not ours to list, and the block\n * stays short.\n */\nexport function discoverBlockSkills(cwd: string): BlockSkill[] {\n const dir = resolve(cwd, '.agents', 'skills');\n if (!existsSync(dir)) return [];\n const out: BlockSkill[] = [];\n for (const name of readdirSync(dir).sort()) {\n const link = join(dir, name);\n const skillFile = join(link, 'SKILL.md');\n if (!lstatSync(link).isSymbolicLink() || !existsSync(skillFile)) continue;\n if (!readlinkSync(link).replace(/\\\\/g, '/').includes('node_modules/@getvitops/')) continue;\n out.push({\n name,\n path: `.agents/skills/${name}/SKILL.md`,\n summary: skillSummary(readFileSync(skillFile, 'utf8')),\n });\n }\n return out;\n}\n\n/**\n * Link every skill an installed `@getvitops/skills` ships into `.agents/skills` (and `.claude/skills`),\n * the same logical-path links `skills experimental_sync` makes, so `vitops agents sync` leaves a project\n * with the skills the block names even where that CLI has not run. Returns warnings.\n */\nexport function linkPackagedSkills(cwd: string): string[] {\n const warnings: string[] = [];\n let dir = resolve(cwd);\n const { root } = parse(dir);\n for (;;) {\n const skills = join(dir, 'node_modules', '@getvitops', 'skills', 'skills');\n if (existsSync(skills)) {\n for (const name of readdirSync(skills)) {\n const target = join(skills, name);\n if (!existsSync(join(target, 'SKILL.md'))) continue;\n for (const base of ['.agents', '.claude']) {\n const warn = linkSkill(resolve(cwd, base, 'skills', name), target);\n if (warn) warnings.push(warn);\n }\n }\n return warnings;\n }\n if (dir === root) return warnings;\n dir = dirname(dir);\n }\n}\n\n/**\n * The managed block for the top of an `AGENTS.md`: what the project is built with, the one rule that\n * matters, and links to the skills that carry the detail. Deliberately short: depth lives in the\n * skills and in `vitops docs`, which agents load on demand rather than on every session.\n */\nexport function renderAgentsBlock(opts: {\n config: string;\n skills: BlockSkill[];\n version: string;\n}): string {\n const lines = [\n AGENTS_START,\n `Built with the Vitops design system (\\`@getvitops/*\\`); tokens and site facts live in \\`${opts.config}\\`.`,\n 'Prefer its classes and patterns over hand-written CSS. Load the skill that matches the task:',\n '',\n ...opts.skills.map((s) => `- [${s.name}](${s.path})${s.summary ? ` — ${s.summary}` : ''}`),\n ...(opts.skills.length ? [''] : []),\n 'Reference, rendered from this project: `vitops docs <topic>` (no topic lists them).',\n `<!-- managed by \\`vitops agents sync\\` · @getvitops/cli@${opts.version} — edit outside this block -->`,\n AGENTS_END,\n ];\n return lines.join('\\n');\n}\n\n/**\n * Put `block` into an AGENTS.md body. An existing fenced block is replaced in place and nothing else\n * is touched; with none, the block goes at the TOP, so it is the first thing an agent reads.\n */\nexport function upsertAgentsBlock(\n current: string | undefined,\n block: string,\n): { text: string; action: 'created' | 'updated' | 'added' } {\n if (current === undefined) return { text: `${block}\\n`, action: 'created' };\n const s = current.indexOf(AGENTS_START);\n const e = current.indexOf(AGENTS_END);\n if (s !== -1 && e !== -1 && e > s)\n return {\n text: current.slice(0, s) + block + current.slice(e + AGENTS_END.length),\n action: 'updated',\n };\n return { text: `${block}\\n\\n${current.replace(/^\\s+/, '')}`, action: 'added' };\n}\n","/**\n * A minimal reader for hand-written CSS: rule selector, declarations as authored,\n * and the at-rule conditions the rule sits inside.\n *\n * **Why not lightningcss.** The generator owns it, and exposing an `analyzeCss`\n * from there was the obvious move — but lightningcss parses declarations into a\n * typed value model, and the typed model is exactly what these rules need to see\n * through. `display: flex` comes back as `pair`, `grid-template-columns: 1fr 1fr`\n * as `track-list`, and `margin-inline: auto` as an empty structured node. Every\n * rule here matches on *authored text*, so recovering it property-by-property\n * would mean re-implementing a serialiser for each one.\n *\n * A brace-depth scanner keeps the text intact, needs no native binary in the\n * CLI's dependency tree, and follows what `generate.ts` already does twice\n * (`stripForTailwind`, `isVariantBlock`).\n *\n * It is deliberately shallow: no specificity, no cascade, no `@supports`\n * semantics. It answers \"what did someone type in this rule\", which is all a\n * reuse suggestion needs.\n */\n\nexport interface CssDeclaration {\n property: string;\n value: string;\n}\n\nexport interface CssRule {\n selector: string;\n declarations: CssDeclaration[];\n /** Enclosing at-rule preludes, outermost first (`@media (min-width: 48rem)`). */\n conditions: string[];\n /** 1-indexed line of the selector. */\n line: number;\n}\n\n/** Strip comments, preserving newlines so line numbers survive. */\nfunction stripComments(css: string): string {\n return css.replace(/\\/\\*[\\s\\S]*?\\*\\//g, (m) => m.replace(/[^\\n]/g, ''));\n}\n\nconst lineAt = (src: string, index: number) => src.slice(0, index).split('\\n').length;\n\n/**\n * Parse declarations from a rule body. Splits on top-level `;` only, so a\n * `var(--x, a; b)` or a data URI cannot end a declaration early.\n */\nfunction parseDeclarations(body: string): CssDeclaration[] {\n const out: CssDeclaration[] = [];\n let depth = 0;\n let start = 0;\n const push = (chunk: string) => {\n const colon = chunk.indexOf(':');\n if (colon < 0) return;\n const property = chunk.slice(0, colon).trim();\n const value = chunk.slice(colon + 1).trim();\n // A nested block leaks in as a property containing `{`; skip it rather than\n // reporting a declaration that was never written.\n if (!property || property.includes('{') || !value) return;\n out.push({ property, value });\n };\n for (let i = 0; i < body.length; i++) {\n const ch = body[i];\n if (ch === '(' || ch === '[') depth++;\n else if (ch === ')' || ch === ']') depth--;\n else if (ch === ';' && depth === 0) {\n push(body.slice(start, i));\n start = i + 1;\n }\n }\n push(body.slice(start));\n return out;\n}\n\n/**\n * Scan a stylesheet into its style rules.\n *\n * Never throws: this runs over consumer source that may be a fragment, and a\n * linter that dies on one unparseable file is one nobody keeps in CI.\n */\nexport function scanCss(css: string): CssRule[] {\n const src = stripComments(css);\n const rules: CssRule[] = [];\n // Each open brace pushes its prelude; at-rule preludes become `conditions` for\n // everything nested inside, style-rule preludes become a selector.\n const stack: { prelude: string; index: number }[] = [];\n let depth = 0;\n let preludeStart = 0;\n\n for (let i = 0; i < src.length; i++) {\n const ch = src[i];\n if (ch === '\"' || ch === \"'\") {\n // Skip strings whole — a brace inside content: \"{\" would desync the depth.\n const quote = ch;\n i++;\n while (i < src.length && src[i] !== quote) {\n if (src[i] === '\\\\') i++;\n i++;\n }\n continue;\n }\n if (ch === '{') {\n const raw = src.slice(preludeStart, i);\n const prelude = raw.trim();\n // Offset of the first non-whitespace character, not of `preludeStart` —\n // that sits where the previous rule ended, so a rule's reported line would\n // be the blank line above it.\n stack.push({ prelude, index: preludeStart + raw.indexOf(prelude.slice(0, 1)) });\n depth++;\n preludeStart = i + 1;\n continue;\n }\n if (ch === '}') {\n const frame = stack.pop();\n depth--;\n if (frame && !frame.prelude.startsWith('@')) {\n // A style rule: its body is everything since it opened, minus any nested\n // blocks, which `parseDeclarations` discards.\n const bodyStart = src.indexOf('{', frame.index) + 1;\n rules.push({\n selector: frame.prelude,\n declarations: parseDeclarations(src.slice(bodyStart, i)),\n conditions: stack.filter((f) => f.prelude.startsWith('@')).map((f) => f.prelude),\n line: lineAt(src, frame.index),\n });\n }\n preludeStart = i + 1;\n continue;\n }\n }\n // Unbalanced braces (a fragment, a truncated file) leave frames on the stack;\n // returning what was parsed beats reporting nothing.\n return rules.filter((r) => r.selector && depth >= 0);\n}\n\n/** `<style>` block contents from an .astro/.vue/.svelte/.html file. */\nexport function extractStyleBlocks(src: string): { css: string; line: number }[] {\n const out: { css: string; line: number }[] = [];\n for (const m of src.matchAll(/<style\\b[^>]*>([\\s\\S]*?)<\\/style>/gi)) {\n // Offset so a finding's line number points into the original file, not into\n // the extracted block.\n const line = lineAt(src, (m.index ?? 0) + m[0].indexOf('>') + 1);\n out.push({ css: m[1] ?? '', line });\n }\n return out;\n}\n","/**\n * The inverse of `lint.ts`: hand-written CSS that re-implements a framework\n * primitive.\n *\n * `lint.ts` catches a class that resolves to nothing — you named something and it\n * isn't there. This catches the costlier direction, where the code *works*: a\n * centred container written by hand, a two-column split behind a media query, a\n * flex row. Nothing is broken, so nothing ever surfaces it, and the design system\n * quietly stops being the place those decisions live.\n *\n * Every finding here is a `suggestion`, never an `error`. These are judgement\n * calls — a rule can be right about the pattern and wrong about the intent — and\n * a reuse hint that failed CI on the day it shipped would be a worse defect than\n * the drift it reports. `--strict` is there for consumers who want the ratchet.\n *\n * Rules stay conservative on purpose: each requires the *combination* that makes\n * the intent unambiguous. `margin-inline: auto` alone is not a centred track;\n * `display: flex` alone is not a cluster.\n */\nimport type { Format } from '@getvitops/generator';\nimport { type CssRule, extractStyleBlocks, scanCss } from './css-scan.ts';\nimport type { LintFinding } from './lint.ts';\n\n/**\n * The framework's own track widths. Declared in two places today\n * (`core/css/layout.css` and the tailwind emitter), so a rule keyed to the token\n * NAME rather than to a value survives either one moving.\n */\nconst TRACK_WIDTHS = ['--width-measure', '--width-breakout', '--width-spotlight'];\n\n/**\n * Class names that mean \"the thing that constrains page width\".\n *\n * The token-anchored rule below only fires once someone already knows about\n * `--width-measure`, which is precisely the author who was never going to\n * hand-roll a container. The reported failure was the other author: agents\n * inventing a `.wrap` from scratch, with a plain pixel `max-width`, on site after\n * site. `wrap` is also the more *available* name for the concept, so it wins the\n * naming race against `.centered` every time unless something says otherwise.\n *\n * Matched on the name only in combination with a width-constraining declaration —\n * a `.wrapper` that merely sets `display: flex` is not this pattern.\n */\nconst CONTAINER_NAMES = [\n 'wrap',\n 'wrapper',\n 'container',\n 'inner',\n 'outer',\n 'content-wrap',\n 'content-wrapper',\n 'page-wrap',\n 'page-wrapper',\n 'site-wrap',\n 'site-wrapper',\n 'shell',\n 'constrain',\n 'measure',\n];\n\n/**\n * Page scale, as a rem-equivalent, above which a width cap is a *container*\n * rather than an element.\n *\n * Keyed to the framework's own `md` breakpoint (48rem) rather than to a number\n * picked for this rule, and it is what keeps a capped figure quiet — someone\n * writing `max-width: 40rem` on an image is not reinventing `.centered`.\n */\nconst PAGE_SCALE_REM = 48;\n/** The same line in `ch`, where a width cap is a reading measure by construction. */\nconst PAGE_SCALE_CH = 40;\n\n/**\n * The largest length in a width value, as a rem-equivalent — so `min(100%,\n * 1200px)` and `clamp(20rem, 90vw, 75rem)` are read at their cap rather than\n * skipped for not being a bare length.\n *\n * Relative-to-viewport units (`%`, `vw`, `vmin`) are deliberately ignored: they\n * express \"as wide as there is\", which is not a container cap.\n */\nfunction widthScale(value: string): { rem: number; ch: number } {\n let rem = 0;\n let ch = 0;\n for (const m of value.matchAll(/([\\d.]+)(rem|px|em|ch)\\b/g)) {\n const n = Number(m[1]);\n if (!Number.isFinite(n)) continue;\n if (m[2] === 'ch') ch = Math.max(ch, n);\n else rem = Math.max(rem, m[2] === 'px' ? n / 16 : n);\n }\n return { rem, ch };\n}\n\n/**\n * Column ratios that correspond to a `split-<a>-<b>` class.\n *\n * `.split` is FLEX, not grid — `display: flex` with `flex: 1` children, and a\n * ratio class setting `--_split-a`/`--_split-b`. Equal columns are the base\n * `.split` with no ratio class, which is why `1fr 1fr` is deliberately absent:\n * suggesting `split-1-1` would name a class that does not exist, and this file\n * would then be committing the very defect `lint.ts` reports.\n */\nconst SPLITS: Record<string, string> = {\n '1fr 2fr': '1-2',\n '2fr 1fr': '2-1',\n '1fr 3fr': '1-3',\n '3fr 1fr': '3-1',\n '1fr 4fr': '1-4',\n '4fr 1fr': '4-1',\n '2fr 3fr': '2-3',\n '3fr 2fr': '3-2',\n};\n\nconst decl = (rule: CssRule, property: string) =>\n rule.declarations.find((d) => d.property === property)?.value;\n\n/** Is this rule inside a `min-width` media or container query? */\nconst inMinWidthQuery = (rule: CssRule) => rule.conditions.some((c) => /min-width\\s*:/.test(c));\n\n/** The framework breakpoint a `min-width` condition corresponds to, if any. */\nfunction breakpointOf(rule: CssRule): string | null {\n const rem = rule.conditions.map((c) => /min-width\\s*:\\s*([\\d.]+)rem/.exec(c)?.[1]).find(Boolean);\n if (!rem) return null;\n const n = Number(rem);\n // The framework's container breakpoints: sm/md/lg/xl = 30/48/64/80rem.\n return n >= 80 ? 'xl' : n >= 64 ? 'lg' : n >= 48 ? 'md' : n >= 30 ? 'sm' : null;\n}\n\n/** Spell a responsive utility the way the target format actually emits it. */\nconst responsive = (bp: string, base: string, format: Format) =>\n format === 'tailwind' ? `@${bp}:${base}` : `${bp}-${base}`;\n\ntype Verdict = Omit<LintFinding, 'file' | 'line'>;\n\n/**\n * One rule = one check. Kept as data rather than a chain of ifs so adding a\n * primitive is adding an entry, and so each carries its own explanation.\n */\nconst RULES: ((rule: CssRule, format: Format) => Verdict | null)[] = [\n // ── a centred track, written by hand ──────────────────────────────────────\n //\n // Three ways in, one verdict. They are one rule rather than three because they\n // describe the same mistake and a rule reports once — a `.wrap` with a pixel cap\n // and auto margins trips all three, and three findings on one line would read as\n // three problems.\n (rule) => {\n const cap = decl(rule, 'max-inline-size') ?? decl(rule, 'max-width') ?? decl(rule, 'width');\n const centred = /\\bauto\\b/.test(decl(rule, 'margin-inline') ?? decl(rule, 'margin') ?? '');\n if (!cap) return null;\n\n const token = TRACK_WIDTHS.find((t) => cap.includes(t));\n const { rem, ch } = widthScale(cap);\n const pageScale = rem >= PAGE_SCALE_REM || ch >= PAGE_SCALE_CH;\n // A container-shaped NAME lowers the bar to any cap at all: the name is the\n // stated intent, so the value no longer has to prove it.\n const named = CONTAINER_NAMES.some((n) =>\n // `[-_]+` rather than `[-_]`, so BEM's `__` separator is one boundary:\n // `.site-header__inner` is the container here, and requiring a single\n // separator missed every element-notation selector.\n new RegExp(`[.#]([a-z0-9]+[-_]+)*${n}([-_]+[a-z0-9]+)*\\\\b`, 'i').test(rule.selector),\n );\n\n // What makes each trigger unambiguous. A framework track token or a\n // container-shaped name is enough on its own; a bare length has to be both\n // page-scale AND centred, or it is just a capped element.\n const why = token\n ? `it caps width at \\`var(${token})\\`, a framework track`\n : named\n ? `\\`${rule.selector.trim()}\\` names a page container`\n : pageScale && centred\n ? `it caps width at page scale and centres with auto margins`\n : null;\n if (!why) return null;\n if (token && !centred) return null;\n\n return {\n cls: `${rule.selector} { ${cap.length > 40 ? 'width cap' : `max-width: ${cap}`} }`,\n severity: 'suggestion',\n reason: `\\`.centered\\` is this pattern — ${why}`,\n suggestion:\n 'use `.centered`. It is a grid of named tracks, not a max-width box, so a child ' +\n 'opts into a wider track with `breakout`, `spotlight` or `fullbleed` on itself — ' +\n 'a hand-rolled container has no answer for the full-bleed image except more CSS',\n };\n },\n\n // ── a two-column split behind a breakpoint ────────────────────────────────\n (rule, format) => {\n const cols = decl(rule, 'grid-template-columns');\n if (!cols || !inMinWidthQuery(rule)) return null;\n const ratio = SPLITS[cols.replace(/\\s+/g, ' ').trim()];\n const bp = ratio && breakpointOf(rule);\n if (!ratio || !bp) return null;\n return {\n cls: `${rule.selector} { grid-template-columns: ${cols} }`,\n severity: 'suggestion',\n reason: `a ${cols} split behind a min-width query is the \\`split-${ratio}\\` utility`,\n suggestion:\n `${responsive(bp, `split-${ratio}`, format)} on the container — the responsive form is ` +\n 'equal below the breakpoint and takes the ratio above it, which is what the query is doing by hand',\n };\n },\n\n // ── a repeated-item grid, which is what `.subgrid` is for ─────────────────\n //\n // Deliberately NOT gated on a media query, which is what keeps it off the split\n // rule's territory: a `repeat()` is a set of like items, an explicit `1fr 2fr`\n // is two different panels. The two cannot both fire on one rule anyway — this\n // requires `repeat()` and the split requires a `min-width` condition.\n (rule) => {\n const cols = decl(rule, 'grid-template-columns');\n if (!cols || !/^\\s*repeat\\(/i.test(cols)) return null;\n if (!/\\bgrid\\b/.test(decl(rule, 'display') ?? '')) return null;\n // `subgrid` in the same rule means this IS the framework pattern, being\n // configured or extended rather than replaced.\n if (rule.declarations.some((d) => /subgrid/.test(d.value))) return null;\n\n const count = /^\\s*repeat\\(\\s*(\\d+)/i.exec(cols)?.[1];\n const auto = /^\\s*repeat\\(\\s*auto-(fit|fill)/i.test(cols);\n if (!count && !auto) return null;\n if (count && Number(count) < 2) return null;\n\n return {\n cls: `${rule.selector} { grid-template-columns: ${cols} }`,\n severity: 'suggestion',\n reason:\n 'a grid of repeated items is `.subgrid` — a plain grid aligns the outer boxes but ' +\n 'not the tranches inside them, so headings, bodies and footers land at different ' +\n 'heights across the set',\n suggestion: auto\n ? '`.subgrid subgrid-responsive` (2 columns under 60rem, 1 under 40rem) with ' +\n '`--subgrid-row-span` set to the number of row bands each item contains — or ' +\n '`.grid-auto` with `--grid-min`, which is the framework class for this exact ' +\n 'auto-fit track and the right answer when the items have no tranches to align'\n : `\\`.subgrid subgrid-cols-${count}\\`, with \\`--subgrid-row-span\\` (or ` +\n '`subgrid-rows-<n>`) set to the number of row bands each item contains — and ' +\n '`.subgrid-card` on the items to get an edge-bled `__media` and a pinned `__footer`',\n };\n },\n\n // ── a flex row with centred items ─────────────────────────────────────────\n (rule, format) => {\n if (decl(rule, 'display') !== 'flex') return null;\n if (decl(rule, 'align-items') !== 'center') return null;\n if (/column/.test(decl(rule, 'flex-direction') ?? '')) return null;\n return {\n cls: `${rule.selector} { display: flex; align-items: center }`,\n severity: 'suggestion',\n reason: 'the framework has this as utilities',\n suggestion:\n format === 'tailwind'\n ? \"Tailwind's own `flex items-center` (the framework drops these names in this format)\"\n : '`flex items-center`, or `.cluster-start` if this is a row of related controls',\n };\n },\n];\n\n/**\n * Lint stylesheets and the `<style>` blocks of component files.\n *\n * `files` carries raw text, same as `lintSource` — this module stays free of the\n * filesystem so the caller owns discovery.\n */\n/**\n * The banner every generated stylesheet opens with.\n *\n * Linting the generator's own output is the worst false positive this tool can\n * produce: the emitted bundle contains the framework's real implementations of\n * these very patterns, so it reports `.split` as reinventing `.split`. The\n * `css` format writes `styles.css` into the consumer's source tree (`src/styles`\n * by default), which is squarely inside what `--src` scans.\n *\n * Matched on the banner rather than on a path, because the output directory is\n * configurable and a path rule would silently stop working when someone changes\n * it.\n */\n// Case-sensitive on purpose: the banner shouts `GENERATED`, and matching it\n// case-insensitively also swallowed an authored `/* generated content here */`.\nconst GENERATED = /^\\s*\\/\\*!?\\s*GENERATED\\b|do not edit by hand\\./;\n\nexport function lintCss(files: { path: string; text: string }[], format: Format): LintFinding[] {\n const findings: LintFinding[] = [];\n const seen = new Set<string>();\n for (const file of files) {\n if (GENERATED.test(file.text.slice(0, 400))) continue;\n // A .css file is one block starting at line 1; a component file may hold\n // several, each offset to where its <style> opens so a reported line points\n // into the original file rather than into the extracted block.\n const blocks = file.path.endsWith('.css')\n ? [{ css: file.text, line: 1 }]\n : extractStyleBlocks(file.text);\n for (const block of blocks)\n for (const rule of scanCss(block.css))\n for (const check of RULES) {\n const verdict = check(rule, format);\n if (!verdict) continue;\n const line = block.line + rule.line - 1;\n const key = `${file.path}:${line}:${verdict.reason}`;\n if (seen.has(key)) continue;\n seen.add(key);\n findings.push({ file: file.path, line, ...verdict });\n break; // one suggestion per rule — the first match wins\n }\n }\n return findings;\n}\n","/**\n * The third linting direction: markup that lays out a repeated set of things\n * without the framework's pattern for it.\n *\n * `lint.ts` catches a class that resolves to nothing. `lint-css.ts` catches\n * hand-written CSS that re-implements a primitive. Neither reaches the most common\n * reported drift, because it involves no bad class and no hand-written CSS at all:\n * a page renders a list of cards with `class=\"grid\"`, or with nothing, and every\n * class in it is real. The design system has `.subgrid` for exactly this and it\n * goes unused, so across three cards the headings sit at three heights and the\n * CTAs float wherever the copy ended.\n *\n * That cannot be judged from a class token in isolation, so this module is the one\n * that looks at *shape*: is there a repeated card construct here, and is it inside\n * a subgrid?\n *\n * **It is heuristic, and the heuristics are chosen to under-report.** Every finding\n * is a `suggestion`, at most one per file, and each requires a repetition signal\n * strong enough to state in the message: a loop that renders a card, or three or\n * more sibling cards written out. Two cards in a file is not enough — they are as\n * likely to be in unrelated sections, and a rule that fires there would teach\n * people to stop reading the output, which costs more than the drift.\n */\nimport { extname } from 'node:path';\nimport type { LintFinding } from './lint.ts';\n\n/**\n * Only real markup is examined.\n *\n * Markdown is excluded on purpose, and it is the exclusion that matters most: a\n * `.md` file is as likely to *discuss* `.card` as to use it. Run against this\n * repo's own docs site, the first version reported `changelog.md` and two\n * generated reference pages — prose about the framework, counted as drift by it.\n * A consumer's markdown content can hold real markup, so this does give up a\n * little coverage; that is the right side to err on for a heuristic.\n */\nconst MARKUP_EXT = new Set([\n '.astro',\n '.html',\n '.htm',\n '.jsx',\n '.tsx',\n '.vue',\n '.svelte',\n '.ts',\n '.js',\n]);\n\n/**\n * A class attribute's value, in the spellings the supported frameworks use.\n * Mirrors `lint.ts`'s extractor — the token must be in a real `class`, not merely\n * somewhere in the file.\n */\nconst CLASS_ATTR =\n /(?:class|className|class:list)\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)'|\\{`([^`]*)`\\}|\\{([^}]*)\\})/g;\n\n/**\n * The card-ish classes worth caring about.\n *\n * Anchored to `card` as a whole word or suffix, which covers the framework's own\n * `card` / `subgrid-card` / `pricing-card` and a consumer's `service-card` alike.\n * Not `*-item` or `*-tile`: too many of those are not laid out as a set.\n */\nconst CARD_CLASS = /(?:^|[\\s\"'`])((?:[a-z0-9]+-)*card)(?:__[a-z0-9-]+)?(?=[\\s\"'`]|$)/i;\n\n/** Every card class named in a real class attribute, with its offset in `src`. */\nfunction cardsInClassAttrs(src: string): { name: string; index: number }[] {\n const out: { name: string; index: number }[] = [];\n for (const m of src.matchAll(CLASS_ATTR)) {\n const value = m[1] ?? m[2] ?? m[3] ?? m[4] ?? '';\n for (const tok of value.split(/[\\s'\"`]+/)) {\n const card = CARD_CLASS.exec(` ${tok} `);\n // Element classes (`card__body`) belong to a card already counted by its\n // block class — counting them would report every single-card file as three.\n if (card && !tok.includes('__')) out.push({ name: card[1] as string, index: m.index ?? 0 });\n }\n }\n return out;\n}\n\n/**\n * Already using the pattern — in any tier's spelling.\n *\n * `<Cards>` and `<wc-cards>` count: `<Cards>` renders a `Subgrid`, and `<wc-cards>`\n * only ever wraps one. Omitting them reported a correct card grid as a missed one.\n */\nconst HAS_SUBGRID = /\\bsubgrid\\b|<Subgrid\\b|<Cards\\b|<wc-cards\\b/;\n\n/**\n * Constructs that render one item many times.\n *\n * The JS/Astro/JSX `.map(`, and the block forms Svelte/Vue/Handlebars use. A loop\n * whose body contains a card is a repeated card set by construction — no counting\n * and no sibling analysis needed, which is why it is the primary signal.\n */\nconst LOOPS = [/\\.\\s*map\\s*\\(/g, /\\{#each\\b/g, /v-for\\s*=/g, /\\{%\\s*for\\b/g];\n\n/** Line number of a character offset. */\nconst lineAt = (src: string, index: number) => src.slice(0, index).split('\\n').length;\n\n/**\n * The body of a loop, approximated by taking the text that follows it.\n *\n * A real brace-matcher would be more precise and is not worth it: the question is\n * only \"does a card appear inside this repetition\", and a card 600 characters past\n * a `.map(` is inside it in every realistic template. Over-reaching here can only\n * produce a suggestion on a file that does render cards somewhere.\n */\nconst LOOP_WINDOW = 600;\n\ninterface CardSignal {\n line: number;\n /** What made this a repeated set, phrased for the message. */\n evidence: string;\n cls: string;\n}\n\n/** The strongest repetition signal in one file, or none. */\nfunction cardSignal(text: string): CardSignal | null {\n const cards = cardsInClassAttrs(text);\n if (!cards.length) return null;\n\n // ── a loop that renders a card ────────────────────────────────────────────\n // The stronger signal, and it needs no counting: one card inside a repetition\n // is a set by construction.\n for (const loop of LOOPS)\n for (const m of text.matchAll(loop)) {\n const start = (m.index ?? 0) + m[0].length;\n const card = cards.find((c) => c.index >= start && c.index < start + LOOP_WINDOW);\n if (!card) continue;\n return {\n line: lineAt(text, m.index ?? 0),\n evidence: `a \\`${m[0].trim()}\\` loop renders \\`.${card.name}\\``,\n cls: `${m[0].trim()} … .${card.name}`,\n };\n }\n\n // ── three or more cards written out ──────────────────────────────────────\n if (cards.length >= 3) {\n const first = cards[0] as { name: string; index: number };\n return {\n line: lineAt(text, first.index),\n evidence: `\\`.${first.name}\\` appears on ${cards.length} elements in this file`,\n cls: `.${first.name} ×${cards.length}`,\n };\n }\n return null;\n}\n\n/**\n * Lint markup for repeated card sets laid out without `.subgrid`.\n *\n * Takes raw text like the other linters — discovery stays with the caller. One\n * finding per file: the drift is a decision about a layout, not about a line, and\n * repeating it per card would bury every other finding the command prints.\n */\n/**\n * An anchor carrying a card class inside a subgrid.\n *\n * The shape everyone reaches for when the whole card is a link — and it renders\n * fine, which is why it survives. The `<li>` is the grid item, so the anchor is an\n * ordinary block inside it and the tranches within the anchor never reach the\n * parent's shared row lines: the alignment `.subgrid` exists for silently does not\n * happen, and the anchor doesn't fill the cell either. This is the one markup\n * finding that reports a *broken* result rather than a missed primitive.\n */\nconst LINK_CARD = /<a\\b[^>]*\\bclass(?:Name)?\\s*=\\s*[\"'{][^\"'}]*\\b(?:[a-z0-9]+-)*card\\b/gi;\n\n/**\n * Is this offset inside an `<li>`?\n *\n * The rule's claim is \"the anchor is nested inside the grid item rather than being\n * it\", and only an anchor within an `<li>` is that. Checking the file merely\n * *mentions* subgrid is far too coarse: a page documenting several patterns tripped\n * on a standalone `<a class=\"tile card\">`, which is a perfectly good anchor-as-card —\n * outside a grid there is no grid item for it to compete with.\n */\nfunction insideListItem(text: string, index: number): boolean {\n const before = text.slice(0, index);\n return before.lastIndexOf('<li') > before.lastIndexOf('</li');\n}\n\nfunction linkCardFinding(file: { path: string; text: string }): LintFinding | null {\n const m = [...file.text.matchAll(LINK_CARD)].find((hit) =>\n insideListItem(file.text, hit.index ?? 0),\n );\n if (!m || m.index === undefined) return null;\n return {\n file: file.path,\n line: lineAt(file.text, m.index),\n cls: '<a class=\"…card\">',\n severity: 'suggestion',\n reason:\n 'a card class on an `<a>` inside a `subgrid` does not align — the `<li>` is the grid ' +\n \"item, so the anchor's tranches never reach the parent's shared row lines, and the \" +\n 'anchor does not fill the cell either. It renders, it just silently is not a subgrid',\n suggestion:\n 'keep the card as the item (`<li class=\"card subgrid-card\">`) and make the whole card ' +\n 'clickable from inside it — either `class=\"stretched-link\"` on a link (no JS, but the ' +\n \"card's text stops being selectable) or `<Cards>` / `<wc-cards>` around the list \" +\n '(keeps selection, needs JS, falls back to the link). Both keep the accessible name on ' +\n 'real text. Lift anything that must stay above a stretched overlay with `raised`',\n };\n}\n\n/**\n * `.stretched-link` inside `<wc-cards>` — the two whole-card-click mechanisms layered.\n *\n * They are alternatives, not layers, and combining them is silently the worse of the\n * two: the overlay takes the pointer-drag, so the card's text stops being selectable\n * — which is the only reason to reach for `<wc-cards>` — and the element's\n * click-vs-drag logic never gets a chance to run, because the browser follows the\n * overlaid link natively first.\n */\nconst WC_CARDS = /<wc-cards\\b|<Cards\\b/;\nconst STRETCHED = /\\bclass(?:Name)?\\s*=\\s*[\"'{][^\"'}]*\\bstretched-link\\b/;\n\nfunction layeredCardLinkFinding(file: { path: string; text: string }): LintFinding | null {\n if (!WC_CARDS.test(file.text)) return null;\n const m = STRETCHED.exec(file.text);\n if (!m) return null;\n return {\n file: file.path,\n line: lineAt(file.text, m.index),\n cls: 'stretched-link inside wc-cards',\n severity: 'suggestion',\n reason:\n '`.stretched-link` and `<wc-cards>` are two ways to do the same thing, and layering ' +\n \"them silently picks the worse one — the overlay takes the pointer-drag, so the card's \" +\n 'text is no longer selectable, which is the only reason to use `<wc-cards>` at all',\n suggestion:\n 'drop one. Keep `stretched-link` and use `<Subgrid>` if you want zero JS and can accept ' +\n 'unselectable card text; keep `<Cards>` / `<wc-cards>` and remove `stretched-link` if ' +\n 'selectable text matters — the plain `link` class on the heading is then the fallback',\n };\n}\n\nexport function lintMarkup(files: { path: string; text: string }[]): LintFinding[] {\n const findings: LintFinding[] = [];\n for (const file of files) {\n if (!MARKUP_EXT.has(extname(file.path).toLowerCase())) continue;\n // Layering the two whole-card-click mechanisms is judged wherever it appears —\n // `<wc-cards>` governs any container, not only a subgrid.\n const layered = layeredCardLinkFinding(file);\n if (layered) findings.push(layered);\n if (HAS_SUBGRID.test(file.text)) {\n // Already using the pattern, so the missed-primitive rule does not apply —\n // but the link-card mistake only exists *inside* a subgrid, so it is checked\n // here rather than skipped with everything else.\n const broken = linkCardFinding(file);\n if (broken) findings.push(broken);\n continue;\n }\n const signal = cardSignal(file.text);\n if (!signal) continue;\n // `.grid-auto` and `.grid` ARE framework classes, so a message implying no\n // framework layout was used would be wrong on exactly the sites that are\n // trying — and a linter that misreads what you wrote stops being read. The\n // point stands either way; only the framing changes.\n const usingGrid = /\\bgrid-auto\\b|class(?:Name)?=\"[^\"]*\\bgrid\\b/.test(file.text);\n findings.push({\n file: file.path,\n line: signal.line,\n cls: signal.cls,\n severity: 'suggestion',\n reason:\n `${signal.evidence}, ${\n usingGrid\n ? 'laid out with a plain grid rather than a `subgrid`'\n : 'and nothing here is a `subgrid`'\n } — more than one card is what \\`.subgrid\\` is for. A plain grid aligns the outer boxes ` +\n \"but not the tranches inside them, so the set's headings, bodies and footers land at \" +\n 'different heights',\n suggestion:\n '`<ul class=\"subgrid subgrid-cols-3\" role=\"list\">` with ' +\n '`<li class=\"card subgrid-card\">` items (a set of cards is a list; `role=\"list\"` is ' +\n 'needed because `.subgrid` is markerless and Safari stops announcing a marker-less ' +\n '`ul` as a list); set `--subgrid-row-span` to the number of row bands each card ' +\n 'contains. In Astro, `<Subgrid />` adds the role for you. If the items genuinely have ' +\n 'no internal tranches to align (a bare image gallery), `.grid-auto` is the right call ' +\n 'and this is a false positive',\n });\n }\n return findings;\n}\n","/**\n * `vitops lint` — find pre-1.0 colour-grammar token references in consumer\n * source, and rewrite them.\n *\n * The class linter (`lint.ts`) reads `class` attributes; this reads `var(--…)`\n * out of CSS and `<style>` blocks, which is the other half of where a design\n * system is referenced and the half nothing checked. `validate()` covers the\n * same grammar inside a `design-system.json`; a consumer's own stylesheets are\n * outside that file and were entirely unguarded.\n *\n * **Why a codemod and not just a report.** The 1.0 rename is mechanical, and a\n * downstream consumer did it by hand — correctly, but only because they noticed\n * that applying the renames *sequentially* compounds them. A surface role's\n * backgrounds rotate (`--surface-bg` → `--color-bg-surface-muted`,\n * `--surface-bg-bold` → `--color-bg-surface`), so a naive\n * find-and-replace-each-in-turn walks a value through two renames and lands\n * somewhere neither table entry names. `applyRenames` builds one alternation and\n * rewrites in a single pass, which makes simultaneity structural rather than\n * something the operator has to know.\n */\nimport type { LintFinding } from './lint.ts';\n\n/** A rewrite this tool can perform without a judgement call. */\nexport interface TokenFix {\n from: string;\n to: string;\n}\n\n/**\n * Rewrite every old name in one pass.\n *\n * Longest-first alternation so a name that is a prefix of another (`--x-bg` vs\n * `--x-bg-bold`) cannot be matched short, and a single `replace` so no output of\n * one rename is ever input to the next.\n */\nexport function applyRenames(text: string, renames: Record<string, string>): string {\n const names = Object.keys(renames).sort((a, b) => b.length - a.length);\n if (!names.length) return text;\n const escaped = names.map((n) => n.replaceAll(/[.*+?^${}()|[\\]\\\\]/g, String.raw`\\$&`));\n // `(?![\\w-])` so `--brand-bg` does not match inside `--brand-bg-bold` — the\n // longest-first ordering handles the listed names, this handles unlisted ones.\n const re = new RegExp(`(${escaped.join('|')})(?![\\\\w-])`, 'g');\n return text.replace(re, (m) => renames[m] ?? m);\n}\n\n/** Every `var(--name)` in a text, with its line number. */\nfunction varRefs(src: string): { name: string; line: number }[] {\n const out: { name: string; line: number }[] = [];\n for (const m of src.matchAll(/var\\(\\s*(--[\\w-]+)/g))\n out.push({ name: m[1] as string, line: src.slice(0, m.index).split('\\n').length });\n return out;\n}\n\n/**\n * Report old-grammar token references in source files.\n *\n * `renames` comes from the generator's `movedTokens()`, built from the\n * consumer's own role names — there is no fixed list, which is why nothing\n * detected these before.\n */\nexport function lintTokens(\n files: { path: string; text: string }[],\n renames: Record<string, string>,\n): (LintFinding & { fix: TokenFix })[] {\n const findings: (LintFinding & { fix: TokenFix })[] = [];\n const seen = new Set<string>();\n for (const f of files)\n for (const { name, line } of varRefs(f.text)) {\n const to = renames[name];\n if (to == null) continue;\n const key = `${f.path}:${line}:${name}`;\n if (seen.has(key)) continue;\n seen.add(key);\n findings.push({\n file: f.path,\n line,\n cls: `var(${name})`,\n severity: 'error',\n reason: `\\`${name}\\` is the pre-1.0 colour grammar and resolves to nothing`,\n suggestion: `var(${to})`,\n fix: { from: name, to },\n });\n }\n return findings;\n}\n","/**\n * `vitops lint` — find framework classes in consumer source that resolve to\n * nothing in the format being built.\n *\n * The problem this exists for: an unknown utility class is indistinguishable\n * from a working one. Nothing errors, nothing warns, the element just doesn't\n * get the style. Two shapes of that were found in a real consumer site:\n *\n * • `bg-navy-d` — a leftover from the named-step scale (`d`/`xd`/`xxd`)\n * deleted in favour of numeric steps. The hue is real, the step is not.\n * It appeared ~10× and looked fine only because the `.card` it sat on\n * supplied its own background.\n * • `md-flex-row` — the css/bricks spelling of a responsive variant, used in\n * a `tailwind`-format project where that class is never emitted (Tailwind\n * regenerates those on demand as `@md:`). Silently inert.\n *\n * DESIGN: this deliberately only inspects classes ANCHORED TO THE CONSUMER'S\n * OWN CONFIG — a hue name, a role name, a type role, a shadow, a pattern. It\n * never tries to enumerate \"all valid classes\", because in the tailwind format\n * that would mean knowing Tailwind's entire built-in vocabulary, and every\n * unknown would be a false positive. Anchoring means a finding is always\n * actionable: you named something from your design system, and it doesn't\n * resolve.\n */\nimport { NUMERIC_STEPS, type DesignSystem, type Format } from '@getvitops/generator';\n\n/**\n * `error` — you named something from your design system and it resolves to\n * nothing. Always actionable, and fails the command.\n *\n * `suggestion` — the code works, but the framework already has a primitive for\n * it. Advisory by design: these are judgement calls, and a new rule that failed\n * CI on first run would be a worse defect than the drift it reports. `--strict`\n * promotes them for consumers who want the ratchet.\n */\nexport type LintSeverity = 'error' | 'suggestion';\n\nexport interface LintFinding {\n file: string;\n line: number;\n /** The class token, or for a CSS rule the declaration that triggered it. */\n cls: string;\n reason: string;\n suggestion?: string;\n severity: LintSeverity;\n}\n\nconst UTIL_FAMILIES = ['bg', 'text', 'icon', 'border', 'outline', 'fill', 'stroke'];\n/** Class families the framework owns in every format, for variant-spelling checks. */\nconst FRAMEWORK_FAMILY = new RegExp(\n `^(${[...UTIL_FAMILIES, 'font', 'drop-shadow', 'split', 'flex', 'items', 'justify', 'gap', 'space'].join('|')})(-|$)`,\n);\n\n/**\n * The modifiers actually emitted for one family + role, read back out of the\n * generator's own class list.\n *\n * This used to be a hand-maintained array, which is the wrong shape for a\n * suggestion: the two role kinds emit different modifiers (a chromatic role has\n * `solid` and no bare form; a surface role the reverse), so a single list was\n * wrong for one of them no matter what it said.\n */\nconst emittedModifiers = (roleClasses: Set<string>, fam: string, role: string): string[] => {\n const prefix = `${fam}-${role}`;\n return [...roleClasses]\n .filter((c) => c === prefix || c.startsWith(`${prefix}-`))\n .map((c) => c.slice(prefix.length + 1))\n .filter(Boolean)\n .sort();\n};\n\n/** Everything the config makes namable, so we only judge classes we can judge. */\ninterface Vocabulary {\n roles: string[];\n hues: string[];\n steps: string[];\n typeRoles: string[];\n shadows: string[];\n patterns: string[];\n /** Every role utility the generator actually emits, e.g. `bg-surface-x-muted`. */\n roleClasses: Set<string>;\n}\n\nexport function vocabulary(ds: DesignSystem, roleClasses: Iterable<string>): Vocabulary {\n return {\n roles: Object.keys(ds.colors?.roles ?? {}),\n hues: Object.keys(ds.colors?.palette ?? {}),\n steps: [...NUMERIC_STEPS],\n typeRoles: Object.keys(ds.typography?.roles ?? {}),\n shadows: Object.keys(ds.shadows ?? {}),\n patterns: Object.keys(ds.patterns?.items ?? {}),\n roleClasses: new Set(roleClasses),\n };\n}\n\n/**\n * Strip variant prefixes (`@md:`, `hover:`, `dark:`) from a candidate.\n *\n * Format-aware, because the container-query spelling only exists in tailwind.\n * Stripping `@md:` unconditionally made the check asymmetric: `md-split-1-2` in a\n * tailwind project was caught, but `@md:split-1-2` in a css/bricks project became\n * a bare `split-1-2` that matched nothing and returned null — the same silent\n * no-op, in the other direction, unreported.\n */\nconst stripVariants = (cls: string, format: Format): string => {\n let out = cls;\n while (/^@?[a-zA-Z0-9_-]+:/.test(out)) {\n if (format !== 'tailwind' && out.startsWith('@')) break;\n out = out.slice(out.indexOf(':') + 1);\n }\n return out;\n};\n\nconst nearest = (want: string, pool: string[]): string | undefined =>\n pool.find((p) => p.startsWith(want) || want.startsWith(p));\n\n/**\n * Judge one class token. Returns null when the class is none of our business —\n * which is the common case and the whole reason this is low-noise.\n */\nexport function judge(\n raw: string,\n v: Vocabulary,\n format: Format,\n): Omit<LintFinding, 'file' | 'line'> | null {\n const cls = stripVariants(raw, format);\n\n // ── the `@<bp>:` container-query spelling, in a format that has none ───────\n // Tailwind's engine expands these; css/bricks ship pre-expanded `<bp>-` classes\n // instead, so this spelling resolves to nothing there.\n const cq = /^@(sm|md|lg|xl):(.+)$/.exec(cls);\n if (cq && format !== 'tailwind')\n return {\n cls: raw,\n severity: 'error' as const,\n reason: `\\`@${cq[1]}:\\` container-query variants are a tailwind-format spelling; the ${format} format emits pre-expanded classes`,\n suggestion: `${cq[1]}-${cq[2]}`,\n };\n\n // ── Tailwind state variants (`hover:`, `focus:`, `dark:` …) in a format that\n // emits none. css/bricks pre-expand behaviour into hyphenated classes\n // (`md-flex-row`, `hover-fade-in`); a colon-spelled state variant on a framework\n // class resolves to nothing there. Only framework families are judged, so a\n // consumer's own `hover:` rule in their own CSS is not ours.\n const sv =\n /^(hover|focus|focus-visible|focus-within|active|disabled|dark|group-hover):(.+)$/.exec(raw);\n if (sv && format !== 'tailwind' && FRAMEWORK_FAMILY.test(sv[2] as string))\n return {\n cls: raw,\n severity: 'error' as const,\n reason: `\\`${sv[1]}:\\` state variants are a tailwind-format spelling; the ${format} format emits no such class`,\n suggestion: `${sv[1]}-${sv[2]} where an effect class exists, otherwise a pattern state`,\n };\n\n // ── the `<bp>-` responsive spelling ────────────────────────────────────────\n const bp = /^(sm|md|lg|xl)-(.+)$/.exec(cls);\n if (bp && format === 'tailwind')\n return {\n cls: raw,\n severity: 'error' as const,\n reason: `\\`${bp[1]}-\\` responsive classes are not emitted in the tailwind format`,\n suggestion: `@${bp[1]}:${bp[2]} (framework breakpoints) or ${bp[1]}:${bp[2]} (Tailwind's)`,\n };\n // In css/bricks the bare-prefix form is real; fall through and judge the base\n // against the rest of the vocabulary.\n const base = bp && format !== 'tailwind' ? (bp[2] as string) : cls;\n\n // ── <util>-<hue>-<step> ────────────────────────────────────────────────────\n const util = new RegExp(`^(${UTIL_FAMILIES.join('|')})-(.+)$`).exec(base);\n if (util) {\n const [, fam, rest] = util as unknown as [string, string, string];\n // Anything the generator actually emits is valid, full stop. Checking this\n // first also settles the ambiguity below for every well-formed class.\n if (v.roleClasses.has(base)) return null;\n\n // Role and hue names can share a prefix — `defaultConfig()` has the hue\n // `brand` and the role `brand-primary`, so matching hues first would read\n // `bg-brand-primary-x-muted` as hue `brand` + step `primary-x-muted` and\n // report a bogus bad step. Take the LONGEST match across both vocabularies.\n const prefix = (names: string[]) =>\n names\n .filter((n) => rest === n || rest.startsWith(`${n}-`))\n .sort((a, b) => b.length - a.length)[0];\n const hue = prefix(v.hues);\n const role = prefix(v.roles);\n const useRole = role && (!hue || role.length > hue.length);\n\n if (useRole) {\n const mod = rest.slice((role as string).length + 1);\n const valid = emittedModifiers(v.roleClasses, fam as string, role as string);\n return {\n cls: raw,\n severity: 'error' as const,\n reason: `\\`${fam}-${role}${mod ? `-${mod}` : ''}\\` is not emitted for the \\`${role}\\` role`,\n suggestion: valid.length\n ? `valid modifiers: ${valid.join(', ')}`\n : `the \\`${role}\\` role emits no \\`${fam}-*\\` utilities`,\n };\n }\n if (hue) {\n const step = rest.slice(hue.length + 1);\n if (step === '')\n return {\n cls: raw,\n severity: 'error' as const,\n reason: `\\`${fam}-${hue}\\` names a palette hue with no step`,\n suggestion: `${fam}-${hue}-500, or a role such as ${fam}-${v.roles[0] ?? 'neutral'}`,\n };\n if (!v.steps.includes(step))\n return {\n cls: raw,\n severity: 'error' as const,\n reason: `\\`${step}\\` is not a step on the \\`${hue}\\` scale (steps are 50…950)`,\n suggestion: `${fam}-${hue}-500, or a role such as ${fam}-${v.roles[0] ?? 'neutral'} which also flips in dark mode`,\n };\n return null; // valid raw step\n }\n return null; // not anchored to this config — not ours to judge\n }\n\n // ── font-<type-role> ───────────────────────────────────────────────────────\n const font = /^font-(.+)$/.exec(base);\n if (font && v.typeRoles.length && !v.typeRoles.includes(font[1] as string)) {\n const near = nearest(font[1] as string, v.typeRoles);\n // Only complain when it looks like a type role attempt, not e.g. font-bold.\n if (near)\n return {\n cls: raw,\n severity: 'error' as const,\n reason: `\\`${font[1]}\\` is not a typography role`,\n suggestion: `font-${near}`,\n };\n }\n\n // ── drop-shadow-<name> ─────────────────────────────────────────────────────\n const ds = /^drop-shadow-(.+)$/.exec(base);\n if (ds && v.shadows.length && !v.shadows.includes(ds[1] as string))\n return {\n cls: raw,\n severity: 'error' as const,\n reason: `\\`${ds[1]}\\` is not a defined shadow`,\n suggestion: `drop-shadow-${v.shadows[0]}`,\n };\n\n return null;\n}\n\nconst CLASS_ATTR = /(?:class|className|:class)\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)'|\\{`([^`]*)`\\})/g;\n\n/** Extract class tokens from a source file, with line numbers. */\nexport function extractClasses(src: string): { cls: string; line: number }[] {\n const out: { cls: string; line: number }[] = [];\n for (const m of src.matchAll(CLASS_ATTR)) {\n const value = m[1] ?? m[2] ?? m[3] ?? '';\n const line = src.slice(0, m.index).split('\\n').length;\n for (const tok of value.split(/\\s+/))\n // Skip template holes and dynamic fragments — a partial token would be\n // judged as a typo and this must not cry wolf.\n if (tok && !tok.includes('${') && !tok.includes('{') && /^[@\\w:[\\]().%/-]+$/.test(tok))\n out.push({ cls: tok, line });\n }\n return out;\n}\n\nexport function lintSource(\n files: { path: string; text: string }[],\n v: Vocabulary,\n format: Format,\n): LintFinding[] {\n const findings: LintFinding[] = [];\n const seen = new Set<string>();\n for (const f of files)\n for (const { cls, line } of extractClasses(f.text)) {\n const verdict = judge(cls, v, format);\n if (!verdict) continue;\n const key = `${f.path}:${line}:${cls}`;\n if (seen.has(key)) continue;\n seen.add(key);\n findings.push({ file: f.path, line, ...verdict });\n }\n return findings;\n}\n","#!/usr/bin/env node\n/**\n * `vitops` — CLI for the Vitops design-system generator.\n *\n * vitops generate --input <json> --format <bricks|css|tailwind|design[,…]> --out <dir>\n * vitops init [--out design-system.json] [--force] [--site [--name <n>] [--url <u>]]\n * vitops validate <design-system.json|site.json>\n * vitops favicon --input <svg|png> --out <dir> [--low-res <svg|png>] [--background <hex>]\n * vitops agents [--input <json>] [--out AGENTS.md] [--docs-dir <dir>]\n * vitops docs [topic] [--input <json>] [--all]\n * vitops lint [--input <json>] [--format <fmt>] [--src <dir>]\n * vitops legal [--input <site.json>] [--doc <name>] [--format <md|html|portable-text>] [--out <dir>]\n * vitops robots [--input <site.json>] [--site-env <name>] [--out <dir>]\n * vitops reviews [--input <site.json>] [--location <key>] [--dry] [--check] [--out <dir>]\n * vitops search setup [--domain <name>] [--dry] [--check] | notify [--dry] [--all] [--check]\n * vitops ads setup [--provider <name>] [--dry] [--check] | tags | lint\n * vitops setup repo | hosting | domain | search | ads | all [--dry] [--check]\n * vitops email setup [domain] [--dry] [--check] | check [--strict] [--skip]\n * vitops emdash prepare-db | check-prerender [dir]\n * vitops media [--raw <dir>] [--out <dir>] [--force] [--dry]\n * vitops audit [--url <base>] [--build] [--pages <list>] [--warn] [--out <dir>]\n *\n * Thin wrapper over @getvitops/generator (generation) and @getvitops/utils (favicons).\n * Every client brings their own consumer-editable design-system.json.\n *\n * `search`/`ads` live in commands/search.ts + commands/ads.ts — the only two\n * command groups big enough, and self-contained enough, to earn their own file.\n * Everything else stays here.\n */\nimport { parseArgs } from 'node:util';\nimport { pathToFileURL } from 'node:url';\nimport { writeFileSync, existsSync, readFileSync, mkdirSync } from 'node:fs';\nimport { resolve, join, dirname, relative, extname } from 'node:path';\nimport { createRequire } from 'node:module';\nimport {\n buildIconSprite,\n generate,\n validate,\n defaultConfig,\n defaultSiteConfig,\n generateDocs,\n generateLegal,\n enabledDocs,\n processorsMissingLocation,\n JURISDICTION_COUNTRIES,\n isConfig,\n resolveInput,\n resolveConfig,\n resolveTheme,\n roleColorUtilities,\n functionalRole,\n expandPalette,\n roleHue,\n roleKind,\n movedTokens,\n renderRobotsTxt,\n SCHEMA_LOCAL_PATH,\n CONFIG_SCHEMA_LOCAL_PATH,\n type Format,\n type StylesheetFormat,\n type DesignSystem,\n type LegalDoc,\n type LegalOutput,\n type ResolvedInput,\n type Config,\n} from '@getvitops/generator';\nimport {\n collectIconRefs,\n generateFavicons,\n generateIconInclude,\n resolveIcon,\n scanFiles,\n} from '@getvitops/utils';\n// Its own subpath: `indexing` is the only network-touching module in utils, and the\n// other commands should not pull it in. Only `googleAccessToken`/`GoogleAuthLike`\n// are needed here — everything else that used to be imported for `search notify`\n// now lives in commands/search.ts.\nimport { googleAccessToken, type GoogleAuthLike } from '@getvitops/utils/indexing';\n// Its own subpath for the same reason: `reviews` is network-touching (Google\n// Business Profile). `formatPlan` is aliased against `@getvitops/utils/media`'s\n// own `formatPlan`, imported below.\nimport {\n REVIEWS_CACHE_PATH,\n fetchReviews,\n formatPlan as formatReviewsPlan,\n mergeCache,\n plan as planReviews,\n readCache as readReviewsCache,\n selectReviews,\n toCache,\n writeCache as writeReviewsCache,\n type LocationReviews,\n type ReviewsConfig,\n} from '@getvitops/utils/reviews';\n// Its own subpath for the same reason: `media` shells out to ffmpeg, and no other\n// command should carry it.\nimport {\n MEDIA_MANIFEST_PATH,\n formatPlan as formatMediaPlan,\n processMedia,\n type MediaConfig,\n type OutputKind,\n} from '@getvitops/utils/media';\nimport { msg, parse } from './args.ts';\nimport { loadGoogleOAuth, requireAdcProject } from './credentials.ts';\nimport {\n fail,\n loadConfig,\n readJsonOrFail,\n siteConfig,\n SITE_OPTS,\n writeOut,\n} from './commands/shared.ts';\nimport { cmdSearch } from './commands/search.ts';\nimport { cmdAds } from './commands/ads.ts';\nimport { cmdAudit } from './commands/audit.ts';\nimport { cmdCrm } from './commands/crm.ts';\nimport { cmdEmail } from './commands/email.ts';\nimport { cmdEmdash } from './commands/emdash.ts';\nimport { cmdSetup } from './commands/setup.ts';\nimport {\n AGENTS_END,\n AGENTS_START,\n discoverBlockSkills,\n findSkillTarget,\n linkPackagedSkills,\n linkSkill,\n renderAgentsBlock,\n SKILL_NAME,\n TOPICS,\n upsertAgentsBlock,\n} from './agents.ts';\nimport { COMMANDS, HELP, helpFor, wantsHelp } from './help.ts';\nimport { lintCss } from './lint-css.ts';\nimport { lintMarkup } from './lint-markup.ts';\nimport { applyRenames, lintTokens } from './lint-tokens.ts';\nimport { lintSource, vocabulary } from './lint.ts';\n\nconst FORMATS = new Set<Format>(['bricks', 'css', 'tailwind', 'design']);\n// `lint` judges class names against what a build emits, and the `design` format\n// emits no CSS at all — so it is a valid target for `generate` and a category\n// error for `lint`.\nconst LINT_FORMATS = new Set<StylesheetFormat>(['bricks', 'css', 'tailwind']);\n\n// `legal` reads a *site* config, not a design-system.json, and has its own\n// output set — it renders documents, not stylesheets.\nconst LEGAL_DOCS = new Set<LegalDoc>(['privacy', 'terms', 'cookies']);\nconst LEGAL_OUTPUTS = new Set<LegalOutput>(['md', 'html', 'portable-text']);\n\nasync function cmdGenerate(argv: string[]) {\n const { values } = parseArgs({\n args: argv,\n options: {\n input: { type: 'string', short: 'i', default: 'design-system.json' },\n format: { type: 'string', short: 'f', default: 'bricks' },\n out: { type: 'string', short: 'o', default: 'dist' },\n site: { type: 'string' },\n theme: { type: 'string' },\n 'site-env': { type: 'string' },\n },\n allowPositionals: false,\n });\n const input = resolve(values.input as string);\n if (!existsSync(input)) fail(`config not found: ${input}`);\n const site = values.site ? resolve(values.site as string) : undefined;\n if (site && !existsSync(site)) fail(`site config not found: ${site}`);\n const formats = (values.format as string)\n .split(',')\n .map((f) => f.trim())\n .filter(Boolean);\n for (const f of formats)\n if (!FORMATS.has(f as Format))\n fail(`unknown format \"${f}\" (expected: bricks | css | tailwind | design)`);\n\n for (const format of formats as Format[]) {\n try {\n const res = await generate({\n input,\n format,\n outDir: values.out as string,\n ...(site ? { site } : {}),\n ...(values.theme ? { theme: values.theme as string } : {}),\n ...(values['site-env'] ? { siteEnv: values['site-env'] as string } : {}),\n });\n console.log(`✓ ${format} → ${res.outDir} (${res.written.length} paths)`);\n } catch (err) {\n fail(msg(err));\n }\n }\n}\n\n/**\n * `vitops init` writes a starter `design-system.json`; `--site` writes a starter\n * `site.json` instead — the umbrella `Config` that embeds the design system under\n * `designSystem.themes.default`, since there is no file-level `extends` and a\n * scaffolded config can't merely reference a sibling file (see `defaultSiteConfig`'s\n * docblock in `@getvitops/generator`). `--name`/`--url` fill the two facts a\n * consumer almost certainly has on hand; everything else stays the documented\n * placeholder, edited by hand or via `vitops setup` (see AGENTS.md's \"Client\n * onboarding\" section).\n */\nfunction cmdInit(argv: string[]) {\n const { values } = parseArgs({\n args: argv,\n options: {\n out: { type: 'string', short: 'o' },\n force: { type: 'boolean', default: false },\n site: { type: 'boolean', default: false },\n name: { type: 'string' },\n url: { type: 'string' },\n },\n allowPositionals: false,\n });\n const out = resolve((values.out as string) ?? (values.site ? 'site.json' : 'design-system.json'));\n if (existsSync(out) && !values.force) fail(`${out} already exists (use --force to overwrite)`);\n if (values.site) {\n const config = defaultSiteConfig({\n name: values.name as string | undefined,\n url: values.url as string | undefined,\n });\n writeFileSync(out, JSON.stringify(config, null, 2) + '\\n');\n console.log(`✓ wrote ${out}`);\n console.log(` Edit it, then: vitops validate ${relative('.', out)}`);\n console.log(\n ` Editors read the \"$schema\" (${CONFIG_SCHEMA_LOCAL_PATH}) for autocomplete + validation — ` +\n `the installed copy, so it matches the toolchain you build with.`,\n );\n return;\n }\n writeFileSync(out, JSON.stringify(defaultConfig(), null, 2) + '\\n');\n console.log(`✓ wrote ${out}`);\n console.log(\n ` Edit it, then: vitops generate --input ${relative('.', out)} --format tailwind --out src/styles`,\n );\n console.log(\n ` Editors read the \"$schema\" (${SCHEMA_LOCAL_PATH}) for autocomplete + validation — ` +\n `the installed copy, so it matches the toolchain you build with.`,\n );\n}\n\n/**\n * Validate a config against the schema its shape says it is.\n *\n * Routing on kind rather than asking the caller matters here: pointed at a site\n * config, the design-system schema used to report a single `unrecognized_keys`\n * for `designSystem` and nothing about the file's actual contents — a wrong\n * answer that reads like a right one. A site config is validated whole, which\n * also covers the cross-field integrity JSON Schema can't express.\n */\nfunction cmdValidate(argv: string[]) {\n const { values, positionals } = parseArgs({\n args: argv,\n options: { input: { type: 'string', short: 'i' }, 'site-env': { type: 'string' } },\n allowPositionals: true,\n });\n const path = resolve((positionals[0] ?? values.input ?? 'design-system.json') as string);\n const raw = readJsonOrFail(path, 'config');\n\n if (isConfig(raw)) {\n let site: Config;\n try {\n site = resolveConfig(raw, values['site-env'] as string | undefined);\n } catch (err) {\n // Already formatted as one issue per line, with field paths.\n console.error(`✖ ${path} is invalid as a site config:`);\n console.error(msg(err).replace(/^Invalid site config:\\n/, ''));\n process.exit(1);\n }\n // Each theme is validated in turn: `validateConfig` checks the `extends` chain\n // resolves, not that what it resolves to is a complete design system, so a\n // theme could pass there and still fail to build.\n const themes = (site.designSystem?.themes ?? {}) as Parameters<typeof resolveTheme>[0];\n let bad = false;\n for (const name of Object.keys(themes)) {\n const result = validate(resolveTheme(themes, name));\n for (const w of result.warnings) console.warn(` ! designSystem.themes.${name}: ${w}`);\n if (result.ok) continue;\n bad = true;\n console.error(`✖ ${path} — designSystem.themes.${name} is not a complete design system:`);\n for (const e of result.errors)\n console.error(` • ${e.path.join('.') || '(root)'}: ${e.message}`);\n }\n if (bad) process.exit(1);\n const n = Object.keys(themes).length;\n console.log(`✓ ${path} is a valid site config (${n} theme${n === 1 ? '' : 's'})`);\n return;\n }\n\n const result = validate(raw);\n for (const w of result.warnings) console.warn(` ! ${w}`);\n if (result.ok) {\n console.log(`✓ ${path} is valid`);\n return;\n }\n console.error(`✖ ${path} is invalid:`);\n for (const e of result.errors) console.error(` • ${e.path.join('.') || '(root)'}: ${e.message}`);\n process.exit(1);\n}\n\nasync function cmdFavicon(argv: string[]) {\n const { values } = parseArgs({\n args: argv,\n options: {\n input: { type: 'string', short: 'i' },\n out: { type: 'string', short: 'o', default: 'public' },\n 'low-res': { type: 'string' },\n // The maskable outputs must be opaque — the OS crops them to its own shape,\n // so transparency there becomes a black frame, not \"no background\".\n background: { type: 'string' },\n },\n allowPositionals: false,\n });\n if (!values.input) fail('favicon: --input <svg|png> is required');\n const source = resolve(values.input as string);\n if (!existsSync(source)) fail(`source not found: ${source}`);\n try {\n const written = await generateFavicons({\n source,\n outputDir: values.out as string,\n ...(values['low-res'] ? { lowResSource: resolve(values['low-res'] as string) } : {}),\n ...(values.background ? { backgroundColor: values.background as string } : {}),\n });\n console.log(`✓ favicons → ${values.out} (${written.length} files)`);\n } catch (err) {\n fail(msg(err));\n }\n}\n\nconst OUTPUT_KINDS = new Set<OutputKind>(['webm', 'mp4', 'poster']);\n\n/**\n * `vitops media` — encode a directory of raw video into web-ready outputs.\n *\n * Flags only, with no config file, following `favicon`: `site.favicon` exists in\n * the schema and `cmdFavicon` still doesn't read it. The same reasoning applies\n * more strongly here — `legal`, `icons` and `indexing` are anchored to a\n * `Config` because what they emit *describes the site*, and an encoder setting\n * describes a build step.\n */\nasync function cmdMedia(argv: string[]) {\n const { values } = parseArgs({\n args: argv,\n options: {\n raw: { type: 'string', default: 'raw' },\n out: { type: 'string', short: 'o', default: 'src/assets/processed' },\n 'max-width': { type: 'string' },\n crf: { type: 'string' },\n 'max-bitrate': { type: 'string' },\n audio: { type: 'boolean', default: false },\n 'poster-time': { type: 'string' },\n outputs: { type: 'string' },\n manifest: { type: 'string', default: MEDIA_MANIFEST_PATH },\n force: { type: 'boolean', default: false },\n dry: { type: 'boolean', default: false },\n },\n allowPositionals: false,\n });\n\n // Parsed here rather than passed through as strings, so a typo is caught before\n // ffmpeg spends minutes encoding at a quality nobody asked for.\n const num = (flag: string, raw: string | undefined): number | undefined => {\n if (raw === undefined) return undefined;\n const n = Number(raw);\n if (!Number.isFinite(n) || n < 0) fail(`media: --${flag} must be a non-negative number`);\n return n;\n };\n\n let outputs: OutputKind[] | undefined;\n if (values.outputs) {\n outputs = (values.outputs as string).split(',').map((s) => s.trim()) as OutputKind[];\n for (const kind of outputs)\n if (!OUTPUT_KINDS.has(kind))\n fail(`media: unknown output \"${kind}\" (expected: webm | mp4 | poster)`);\n }\n\n const maxWidth = num('max-width', values['max-width'] as string | undefined);\n const crf = num('crf', values.crf as string | undefined);\n const posterTime = num('poster-time', values['poster-time'] as string | undefined);\n\n const config: MediaConfig = {\n ...(maxWidth !== undefined ? { maxWidth } : {}),\n ...(crf !== undefined ? { crf } : {}),\n ...(values['max-bitrate'] ? { maxBitrate: values['max-bitrate'] as string } : {}),\n ...(values.audio ? { audio: true } : {}),\n ...(posterTime !== undefined ? { posterTime } : {}),\n ...(outputs ? { outputs } : {}),\n };\n\n try {\n const result = await processMedia({\n raw: values.raw as string,\n out: values.out as string,\n config,\n manifest: values.manifest as string,\n force: values.force as boolean,\n dry: values.dry as boolean,\n // An encode is minutes of silence otherwise, which reads as a hang.\n onProgress: (message) => console.error(` … ${message}`),\n });\n\n if (values.dry) {\n console.log(formatMediaPlan(result.plan));\n return;\n }\n console.log(\n `✓ media → ${values.out} (${result.written.length} written, ${result.skipped} unchanged)`,\n );\n for (const note of result.plan.notes) console.error(` ! ${note}`);\n } catch (err) {\n fail(msg(err));\n }\n}\n\n/**\n * Read + parse + validate the design system at `path`, or exit with a message.\n *\n * `path` may be a `design-system.json` **or** the larger site config that embeds\n * one — every command that judges source against the design system (`docs`,\n * `lint`, `agents`) accepts either, so a consumer who keeps their tokens in\n * `company.json` doesn't have to maintain a second file for the tooling's sake.\n * `resolveInput` tells them apart by shape and resolves the theme.\n */\nfunction loadDesignSystem(path: string, theme?: string): DesignSystem {\n const raw = readJsonOrFail(path, 'config');\n let resolved: ResolvedInput;\n try {\n resolved = resolveInput(raw, theme != null ? { theme } : {});\n } catch (err) {\n // resolveConfig formats one issue per line; a bad --theme is one line.\n fail(`${path}: ${msg(err)}`);\n }\n const result = validate(resolved.designSystem);\n if (!result.ok) {\n const where = resolved.theme != null ? ` (designSystem.themes.${resolved.theme})` : '';\n console.error(`✖ ${path}${where} is invalid:`);\n for (const e of result.errors)\n console.error(` • ${e.path.join('.') || '(root)'}: ${e.message}`);\n process.exit(1);\n }\n // Warnings go to stderr, not stdout: `vitops docs <topic>` is designed to be\n // piped, so anything here must not land in the piped document. They used to\n // be dropped entirely on this path, which meant `docs` and `agents` stayed\n // silent about collisions that `generate` and `validate` both reported.\n for (const w of result.warnings) console.warn(` ! ${w}`);\n return result.data;\n}\n\n/**\n * Report the icon vocabulary a project actually uses, and optionally build the\n * sprite from it.\n *\n * A sibling of `legal` rather than a widening of `lint`: `lint` judges classes\n * against a design-system.json, whereas icons are anchored to a Config.\n *\n * Exit codes carry the same distinction the Astro integration makes. A name the\n * config DECLARES but that doesn't resolve is a config error and fails the\n * command; a name only the SCAN found is reported and tolerated, because a bare\n * unmapped name is more often a local src/icons/*.svg than a mistake. Runtime-\n * computed names are listed with file and line so they can be declared, never\n * guessed at.\n */\nasync function cmdIcons(argv: string[]) {\n const a = parse(argv, {\n ...SITE_OPTS,\n src: { type: 'string', default: 'src' },\n sprite: { type: 'boolean', default: false },\n out: { type: 'string', short: 'o', default: 'dist' },\n json: { type: 'boolean', default: false },\n });\n\n const sitePath = a.path('input');\n const icons = existsSync(sitePath)\n ? (((await loadConfig(sitePath, a.str('site-env'))).site.icons ?? {}) as Record<\n string,\n unknown\n >)\n : {};\n const ui = (icons.ui as string) ?? 'fa7-solid';\n const brand = (icons.brand as string) ?? 'simple-icons';\n const weight = icons.weight as string | undefined;\n const weightOpt = weight ? { weight } : {};\n\n // Declared first — this throws on an unresolvable name, by design.\n let include: Record<string, string[]> = {};\n try {\n include = generateIconInclude({ ...(icons as object) } as Parameters<\n typeof generateIconInclude\n >[0]);\n } catch (e) {\n fail(msg(e));\n }\n const add = (prefix: string, name: string) => {\n const list = (include[prefix] ??= []);\n if (!list.includes(name)) list.push(name);\n };\n\n const srcDir = a.path('src');\n const scanned = existsSync(srcDir) ? collectIconRefs(scanFiles(srcDir)) : null;\n const unmapped: string[] = [];\n for (const name of scanned?.names ?? []) {\n const colon = name.indexOf(':');\n if (colon > 0) {\n add(name.slice(0, colon), name.slice(colon + 1));\n continue;\n }\n try {\n const q = resolveIcon(name, ui, weightOpt);\n add(q.slice(0, q.indexOf(':')), q.slice(q.indexOf(':') + 1));\n } catch {\n unmapped.push(name);\n }\n }\n\n const dynamic = (scanned?.dynamic ?? []).map((d) => ({\n file: relative(process.cwd(), d.file),\n line: d.line,\n expr: d.expr,\n }));\n const total = Object.values(include).flat().length;\n\n let sprite: { ids: string[]; missing: string[] } | null = null;\n if (a.flag('sprite')) {\n const aliases: Record<string, string> = {};\n for (const name of (icons.semantic as string[]) ?? []) {\n try {\n aliases[`icon-${name}`] = resolveIcon(name, ui, weightOpt);\n } catch {\n /* already reported by generateIconInclude above */\n }\n }\n const built = await buildIconSprite({ include, aliases });\n writeOut(join(a.path('out'), 'icons.svg'), built.svg);\n sprite = { ids: built.ids, missing: built.missing };\n }\n\n if (a.flag('json')) {\n process.stdout.write(\n `${JSON.stringify({ ui, brand, weight: weight ?? null, include, unmapped, dynamic, sprite }, null, 2)}\\n`,\n );\n } else {\n console.log(`icon sets: ui=${ui} brand=${brand}${weight ? ` weight=${weight}` : ''}`);\n for (const [prefix, names] of Object.entries(include))\n console.log(` ${prefix}: ${names.length} — ${names.join(', ')}`);\n console.log(`✓ ${total} icon(s) across ${Object.keys(include).length} set(s)`);\n if (sprite) console.log(`✓ sprite → ${a.path('out')}/icons.svg (${sprite.ids.length} symbols)`);\n for (const d of dynamic)\n console.log(` ! ${d.file}:${d.line} ${d.expr} — computed at runtime, declare it in icons`);\n if (unmapped.length)\n console.log(\n ` ! not in the '${ui}' map: ${unmapped.join(', ')} (fine for a local svg or sprite id)`,\n );\n if (sprite?.missing.length)\n console.log(` ! absent from the sprite: ${sprite.missing.join(', ')}`);\n }\n\n // Dynamic holes and unmapped bare names are warnings, not failures — same\n // tolerance `lint` shows template holes, but visible rather than silent.\n if (sprite?.missing.length) process.exitCode = 1;\n}\n\nasync function cmdLegal(argv: string[]) {\n const a = parse(argv, {\n ...SITE_OPTS,\n doc: { type: 'string', short: 'd', multiple: true },\n format: { type: 'string', short: 'f', default: 'md' },\n out: { type: 'string', short: 'o' },\n });\n\n const output = a.str('format') as LegalOutput;\n if (!LEGAL_OUTPUTS.has(output))\n fail(`unknown format \"${output}\" (expected: ${[...LEGAL_OUTPUTS].join(' | ')})`);\n\n const requested = a.list('doc');\n for (const d of requested)\n if (!LEGAL_DOCS.has(d as LegalDoc))\n fail(`unknown doc \"${d}\" (expected: ${[...LEGAL_DOCS].join(' | ')})`);\n\n const site = await siteConfig(a);\n const docs = requested.length ? (requested as LegalDoc[]) : enabledDocs(site);\n if (docs.length === 0)\n fail(\n 'no legal documents are enabled — set site.legal.privacyPolicy.enabled (or termsOfService / cookieConsent) in your config, or name one with --doc',\n );\n\n const files = generateLegal(site, { docs, output });\n\n /**\n * A processor placed nowhere cannot appear in the cross-border disclosure — there\n * is nothing true to say about it — so it drops out of that section silently,\n * which is the failure that looks tidy. Reported here rather than in the document:\n * a policy must not editorialise about its own gaps.\n *\n * On **stderr** because stdout is the document and gets piped or redirected.\n */\n if (docs.includes('privacy'))\n for (const name of processorsMissingLocation(site))\n console.error(\n ` ! ${name}: no country, storage or operatorCountry declared — it will not appear in the cross-border transfer disclosure. Add one, or state storage: [{ country: \"${JURISDICTION_COUNTRIES[site.site.legal?.jurisdiction ?? 'ca']}\" }] to say it does not leave the country.`,\n );\n\n const REVIEW =\n ' These are generated from your config and are not legal advice — review them before publishing.';\n\n if (!a.opt('out')) {\n process.stdout.write(Object.values(files).join('\\n'));\n // On stderr, so it survives a redirect of the document itself. It used to be\n // omitted entirely on this path — the one path where the output is most\n // likely being piped straight into a page.\n console.error(REVIEW);\n return;\n }\n const outDir = a.path('out');\n mkdirSync(outDir, { recursive: true });\n // Enabling a document is a config edit with consequences outside the config —\n // a new file needs a page, a route, a menu link, a policyUrl. Flipping\n // `cookieConsent.enabled` silently produced `cookie-notice.*` and said only\n // \"3 files\", so nothing pointed at the work that had just appeared.\n const isNew = (name: string) => !existsSync(join(outDir, name));\n const added = Object.keys(files).filter(isNew);\n for (const [name, content] of Object.entries(files)) writeFileSync(join(outDir, name), content);\n console.log(`✓ legal → ${outDir}`);\n for (const name of Object.keys(files))\n console.log(` ${name}${added.includes(name) ? ' (new)' : ''}`);\n if (added.length)\n console.log(\n added.length === 1\n ? `\\n That document is new. It needs somewhere to live: a page or route that renders it, ` +\n `and a link to it${added[0]?.startsWith('cookie') ? ' — including the URL your consent banner points at' : ''}.`\n : `\\n Those ${added.length} documents are new. Each needs somewhere to live: a page or ` +\n `route that renders it, and a link to it — for a cookie notice, including the URL your ` +\n `consent banner points at.`,\n );\n console.log(REVIEW);\n}\n\n/**\n * `robots.txt` — anchored to a `Config` like `legal`/`icons`/`search`, because\n * what it emits describes a site (which environment, which sitemap) rather\n * than a token set.\n */\nasync function cmdRobots(argv: string[]) {\n const a = parse(argv, {\n ...SITE_OPTS,\n out: { type: 'string', short: 'o' },\n });\n\n const siteEnv = a.str('site-env');\n const site = await loadConfig(a.path('input'), siteEnv);\n const content = renderRobotsTxt(site, { env: siteEnv });\n\n if (!a.opt('out')) {\n process.stdout.write(content);\n return;\n }\n const file = join(a.path('out'), 'robots.txt');\n writeOut(file, content);\n console.log(`✓ robots.txt → ${file}`);\n}\n\n/**\n * Fetch and cache a client's Google Business Profile reviews.\n *\n * Reviews live only on the legacy v4 Business Profile API, and the calling GCP\n * project must be allowlisted by Google before any call succeeds — see\n * `@getvitops/utils/reviews`'s `gbp.ts`. Auth is always user OAuth (the same\n * VITOPS_GOOGLE_* / ADC credential `search setup` uses): reading a client's own\n * reviews is a person acting with owner/manager rights, not a deploy robot.\n *\n * The cache (`.vitops/reviews.json` by default) is meant to be committed — a\n * build should not need a live Google credential to render reviews that change\n * slowly. `--check` fetches and diffs against it without writing, for CI to flag\n * a stale commit; a real run merges only the locations it fetched, so one\n * location's failure leaves every other location's cached reviews untouched.\n */\nasync function cmdReviews(argv: string[]) {\n const a = parse(argv, {\n ...SITE_OPTS,\n location: { type: 'string' },\n cache: { type: 'string', default: REVIEWS_CACHE_PATH },\n out: { type: 'string', short: 'o' },\n dry: { type: 'boolean', default: false },\n check: { type: 'boolean', default: false },\n });\n\n const site = await siteConfig(a);\n const reviewsConfig = site.site.reviews as ReviewsConfig | undefined;\n const knownLocations = new Set(\n Object.entries(site.organization?.locations ?? {})\n .filter(([, loc]) => loc.listings?.google)\n .map(([key]) => key),\n );\n\n // Env-only, no network — safe to resolve before the --dry check, the same way\n // `search setup`'s credential presence shapes its own plan.\n const oauth = loadGoogleOAuth();\n\n const p = planReviews({\n config: reviewsConfig,\n knownLocations,\n hasCredential: !!oauth,\n ...(a.opt('location') ? { onlyLocation: a.str('location') } : {}),\n });\n\n console.log(formatReviewsPlan(p));\n if (p.blocked) return;\n\n const active = p.locations.filter((l) => !l.skip);\n if (a.flag('dry')) {\n console.log('\\n(--dry: nothing was fetched)');\n return;\n }\n\n const cachePath = a.path('cache');\n const existing = readReviewsCache(cachePath);\n\n const project = site.site?.google?.project;\n requireAdcProject(project, a.str('input'));\n const auth: GoogleAuthLike = {\n token: await googleAccessToken({ kind: 'oauth', oauth: oauth! }),\n ...(project ? { quotaProject: project } : {}),\n };\n\n const fetched: Record<string, LocationReviews> = {};\n let failed = false;\n for (const loc of active) {\n const r = await fetchReviews(auth, loc.accountId!, loc.locationId!);\n if (!r.ok) {\n console.error(`✖ ${loc.key}: ${r.message}`);\n failed = true;\n continue;\n }\n console.log(`✓ ${loc.key}: ${r.reviews.length} review(s)`);\n fetched[loc.key] = {\n locationId: loc.locationId!,\n accountId: loc.accountId!,\n fetchedAt: new Date().toISOString(),\n ...(r.averageRating !== undefined ? { averageRating: r.averageRating } : {}),\n ...(r.totalReviewCount !== undefined ? { totalReviewCount: r.totalReviewCount } : {}),\n reviews: r.reviews,\n };\n }\n\n // ── --check: read-only, and nothing is written ──────────────────────────────\n if (a.flag('check')) {\n let drift = false;\n for (const [key, loc] of Object.entries(fetched)) {\n const before = existing?.locations[key];\n const beforeIds = new Set((before?.reviews ?? []).map((rv) => rv.id));\n const afterIds = loc.reviews.map((rv) => rv.id);\n const changed =\n beforeIds.size !== afterIds.length || afterIds.some((id) => !beforeIds.has(id));\n if (changed) {\n drift = true;\n console.log(\n ` ~ ${key}: cache is stale (${before?.reviews.length ?? 0} → ${loc.reviews.length})`,\n );\n }\n }\n if (drift || failed) process.exit(1);\n console.log('\\n✓ cache matches live reviews');\n return;\n }\n\n if (Object.keys(fetched).length) {\n const merged = toCache(mergeCache(existing, fetched).locations);\n writeReviewsCache(cachePath, merged);\n console.log(` cache → ${relative(process.cwd(), cachePath)}`);\n\n if (a.opt('out')) {\n const outDir = a.path('out');\n mkdirSync(outDir, { recursive: true });\n for (const [key, loc] of Object.entries(merged.locations)) {\n const locConfig = reviewsConfig?.locations[key];\n const selected = locConfig ? selectReviews(loc.reviews, locConfig) : loc.reviews;\n writeFileSync(join(outDir, `${key}.json`), `${JSON.stringify(selected, null, 2)}\\n`);\n }\n console.log(` selected reviews → ${outDir}`);\n }\n }\n\n if (failed) process.exit(1);\n}\n\n// This CLI's own version (dist/cli.mjs → package-root package.json).\nfunction cliVersion(): string {\n try {\n return (\n JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version ??\n '0.0.0'\n );\n } catch {\n return '0.0.0';\n }\n}\n\n// The framework asset root shipped inside @getvitops/generator (holds the Bricks\n// PHP that generateDocs reads for the element reference).\nfunction generatorAssetsDir(): string {\n const pkg = createRequire(import.meta.url).resolve('@getvitops/generator/package.json');\n return join(dirname(pkg), 'assets');\n}\n\n/**\n * Input path for the read-only commands (`docs`, `lint`) when `-i` is absent.\n *\n * `site.json` is the file `vitops init --site` and every scaffold write, so it\n * is the default; a bare `design-system.json` is the fallback for projects that\n * keep tokens on their own. An explicit `-i` is never second-guessed. If\n * neither exists the legacy name is returned so the \"config not found\" message\n * names a path a reader recognises.\n */\nfunction defaultDesignInput(argv: string[], value: string): string {\n const explicit = argv.some((a) => a === '-i' || a === '--input' || a.startsWith('--input='));\n if (explicit) return value;\n for (const name of ['site.json', 'design-system.json'])\n if (existsSync(resolve(name))) return name;\n return value;\n}\n\n/** Print a live reference doc (rendered from the project's config) to stdout. */\nasync function cmdDocs(argv: string[]) {\n const { values, positionals } = parseArgs({\n args: argv,\n options: {\n input: { type: 'string', short: 'i', default: 'design-system.json' },\n theme: { type: 'string' },\n all: { type: 'boolean', default: false },\n },\n allowPositionals: true,\n });\n const topic = positionals[0];\n const topicList = () =>\n Object.entries(TOPICS)\n .map(([name, t]) => ` ${name.padEnd(10)} ${t.summary}`)\n .join('\\n');\n if (!topic && !values.all) {\n console.log(\n `vitops docs <topic> — print a live design-system reference to stdout\\n\\nTopics:\\n${topicList()}\\n\\nAlso: vitops docs --all (every topic, concatenated)`,\n );\n return;\n }\n if (topic && !TOPICS[topic]) fail(`unknown topic \"${topic}\". Valid topics:\\n${topicList()}`);\n const names = values.all ? Object.keys(TOPICS) : [topic as string];\n // An external topic (blocks) needs no design system, so don't demand a config for it.\n const docs = names.every((n) => TOPICS[n]!.external)\n ? {}\n : generateDocs(\n loadDesignSystem(\n resolve(defaultDesignInput(argv, values.input as string)),\n values.theme as string | undefined,\n ),\n generatorAssetsDir(),\n );\n const parts: string[] = [];\n for (const name of names) {\n const t = TOPICS[name]!;\n if (!t.external) {\n parts.push((docs as Record<string, string>)[t.path]!);\n continue;\n }\n // Rendered by the plugin that owns the data; the generator cannot import it.\n const doc = await externalDoc(t.external);\n if (doc) parts.push(doc);\n else if (!values.all)\n fail(`topic \"${name}\" needs @getvitops/${t.external} installed in this project`);\n }\n process.stdout.write(parts.join('\\n'));\n}\n\n/** Render a topic owned by an optional package, resolved from the project, not from the CLI. */\nasync function externalDoc(pkg: 'emdash'): Promise<string | null> {\n try {\n // `require.resolve` rejects an ESM-only `exports` map, but `./package.json` is exported\n // for exactly this: find the package, then read its `import` entry from the manifest.\n const manifest = createRequire(join(process.cwd(), 'package.json')).resolve(\n `@getvitops/${pkg}/package.json`,\n );\n const exp = (\n JSON.parse(readFileSync(manifest, 'utf8')) as {\n exports: Record<string, { import: string }>;\n }\n ).exports['.']!.import;\n const entry = join(dirname(manifest), exp);\n const mod = (await import(pathToFileURL(entry).href)) as { renderBlocksDoc?: () => string };\n return mod.renderBlocksDoc?.() ?? null;\n } catch {\n return null;\n }\n}\n\nfunction cmdLint(argv: string[]) {\n const { values, positionals } = parseArgs({\n args: argv,\n options: {\n input: { type: 'string', short: 'i', default: 'design-system.json' },\n theme: { type: 'string' },\n format: { type: 'string', short: 'f', default: 'bricks' },\n src: { type: 'string', short: 's', default: 'src' },\n // Suggestions are advisory: a reuse rule that failed CI the day it shipped\n // would be a worse defect than the drift it reports.\n strict: { type: 'boolean', default: false },\n fix: { type: 'boolean', default: false },\n },\n // Positionals are explicit files to lint instead of scanning `--src`. This is\n // what makes the command usable as a pre-commit hook: `vp`'s `staged` key\n // appends the staged paths to whatever it runs, so a command that refused\n // positionals could not be wired into the one place the feedback actually\n // lands — at the moment the code is written, rather than whenever someone\n // remembers to run a linter.\n allowPositionals: true,\n });\n const format = values.format as StylesheetFormat;\n if (!LINT_FORMATS.has(format))\n fail(`unknown format \"${format}\" (expected: bricks | css | tailwind)`);\n const ds = loadDesignSystem(\n resolve(defaultDesignInput(argv, values.input as string)),\n values.theme as string | undefined,\n );\n const srcDir = resolve(values.src as string);\n if (!positionals.length && !existsSync(srcDir)) fail(`source directory not found: ${srcDir}`);\n\n // Ask the generator what it actually emits rather than re-deriving the rules\n // here — a linter that models the vocabulary separately just drifts into\n // reporting classes that do exist.\n const palette = expandPalette(ds.colors.palette);\n const roleClasses = roleColorUtilities(\n Object.entries(ds.colors.roles).map(([role, spec]) =>\n functionalRole(role, roleHue(spec), palette[roleHue(spec)]!, roleKind(spec)),\n ),\n ds.colors.utilities ?? ['bg', 'text', 'icon', 'border'],\n ).map((u) => u.cls);\n\n // Explicit paths win over the scan. A pre-commit hook is handed whatever was\n // staged, which includes deletions and files of every kind, so unreadable and\n // uninteresting paths are skipped rather than fatal — a hook that fails because\n // someone staged a PNG is a hook that gets removed.\n const LINTABLE = new Set(['.astro', '.html', '.js', '.jsx', '.ts', '.tsx', '.vue', '.svelte']);\n const named = positionals\n .filter((p) => LINTABLE.has(extname(p)) || extname(p) === '.css')\n .flatMap((p) => {\n try {\n return [{ path: p, text: readFileSync(resolve(p), 'utf8') }];\n } catch {\n return [];\n }\n });\n // `.css` is not in the default extension set — the class linter reads markup,\n // not stylesheets — so the reuse rules get their own pass. Component files are\n // scanned twice on purpose: once for their class attributes, once for any\n // `<style>` block, which is where hand-rolled layout tends to accumulate.\n const files = positionals.length\n ? named.filter((f) => extname(f.path) !== '.css')\n : scanFiles(srcDir);\n const cssFiles = positionals.length\n ? named\n : [...files, ...scanFiles(srcDir, { exts: ['.css'] })];\n // Nothing staged that this command has an opinion about — say so and succeed,\n // rather than reporting a clean run over zero files as if it had checked them.\n if (positionals.length && !cssFiles.length) {\n console.log('✓ no lintable files among the paths given');\n return;\n }\n // The pre-1.0 colour grammar, built from this config's own role names. Read\n // from CSS and <style> blocks — the half of a codebase that references the\n // design system by token rather than by class, and the half nothing checked.\n const renames = movedTokens(ds.colors.roles);\n const tokenFindings = lintTokens(cssFiles, renames);\n const findings = [\n ...lintSource(files, vocabulary(ds, roleClasses), format),\n ...lintCss(cssFiles, format),\n // Markup shape, which is the only one of the three that can see a repeated\n // card set laid out with no bad class and no hand-written CSS — every class\n // real, the pattern simply unused. Reads `files`, not `cssFiles`: a stylesheet\n // has no markup, and passing the doubled list would report each component\n // twice.\n ...lintMarkup(files),\n ...tokenFindings,\n ];\n\n if (values.fix === true) {\n // Only the token renames are rewritten. Everything else this command\n // reports is a judgement call — `bg-navy-d`'s \"suggestion\" is the linter's\n // best guess at what you meant, and silently writing a guess into source is\n // worse than printing it.\n const byFile = new Map<string, Set<string>>();\n for (const f of tokenFindings)\n (byFile.get(f.file) ?? byFile.set(f.file, new Set()).get(f.file)!).add(f.fix.from);\n for (const [file, names] of byFile) {\n const subset = Object.fromEntries([...names].map((n) => [n, renames[n] as string]));\n writeFileSync(file, applyRenames(readFileSync(file, 'utf8'), subset));\n }\n const remaining = findings.length - tokenFindings.length;\n console.log(\n `✓ rewrote ${tokenFindings.length} token reference${tokenFindings.length === 1 ? '' : 's'} ` +\n `in ${byFile.size} file${byFile.size === 1 ? '' : 's'}` +\n (remaining\n ? `\\n! ${remaining} other finding${remaining === 1 ? '' : 's'} need a decision — ` +\n `re-run without --fix to see ${remaining === 1 ? 'it' : 'them'}`\n : ''),\n );\n if (remaining) process.exitCode = 1;\n return;\n }\n\n // Name what was actually checked. With explicit paths `--src` was never read,\n // and reporting it would claim a scan that did not happen.\n const scope = positionals.length ? 'the files given' : (values.src as string);\n const scanned = positionals.length ? cssFiles.length : files.length;\n\n if (!findings.length) {\n console.log(\n `✓ no unresolvable framework classes or missed primitives in ${scope} ` +\n `(${scanned} file${scanned === 1 ? '' : 's'} checked)`,\n );\n return;\n }\n\n const errors = findings.filter((f) => f.severity === 'error');\n const suggestions = findings.filter((f) => f.severity === 'suggestion');\n // Errors first: a class that resolves to nothing is a defect, a reuse\n // suggestion is a judgement call, and mixing them buries the former.\n for (const f of [...errors, ...suggestions]) {\n console.error(`${f.file}:${f.line} ${f.severity === 'error' ? f.cls : `(${f.cls})`}`);\n console.error(` ${f.reason}`);\n if (f.suggestion) console.error(` try: ${f.suggestion}`);\n }\n\n const count = (n: number, one: string, many: string) => `${n} ${n === 1 ? one : many}`;\n const parts: string[] = [];\n if (errors.length) parts.push(count(errors.length, 'unresolvable class', 'unresolvable classes'));\n if (suggestions.length) parts.push(count(suggestions.length, 'suggestion', 'suggestions'));\n const failing = errors.length > 0 || (values.strict === true && suggestions.length > 0);\n console.error(\n `\\n${failing ? '✖' : '!'} ${parts.join(', ')} in ` +\n `${scanned} file${scanned === 1 ? '' : 's'}` +\n (suggestions.length && !values.strict ? ' (suggestions do not fail; use --strict)' : ''),\n );\n if (failing) process.exit(1);\n}\n\n/**\n * Find the config when `design-system.json` isn't there.\n *\n * Keyed to SHAPE, not just to the filename — `resolveInput` already tells a\n * design system from the config that embeds one, and consumers name that file\n * whatever they like (`company.json`, `site.json`). The candidate list is\n * ordered, and a tie is resolved by taking the first: this only ever supplies a\n * default that the run then prints, so a wrong guess is visible rather than\n * silent, and `-i` overrides it.\n */\nfunction discoverConfig(cwd: string): string | undefined {\n const candidates = [\n 'design-system.json',\n 'vitops.json',\n 'site.json',\n 'company.json',\n 'organization.json',\n ];\n for (const name of candidates) {\n const p = resolve(cwd, name);\n if (!existsSync(p)) continue;\n try {\n const raw = JSON.parse(readFileSync(p, 'utf8'));\n // Either kind counts: a bare design system, or a config that embeds one.\n if (isConfig(raw) || (raw && typeof raw === 'object' && 'colors' in raw)) return name;\n } catch {\n // Unparseable: not a candidate, and the real error surfaces if -i names it.\n }\n }\n return undefined;\n}\n\nfunction cmdAgents(rawArgv: string[]) {\n // `vitops agents sync` is the explicit verb; bare `vitops agents` does the same.\n const argv = rawArgv[0] === 'sync' ? rawArgv.slice(1) : rawArgv;\n const { values } = parseArgs({\n args: argv,\n options: {\n input: { type: 'string', short: 'i', default: 'design-system.json' },\n theme: { type: 'string' },\n out: { type: 'string', short: 'o', default: 'AGENTS.md' },\n 'docs-dir': { type: 'string' },\n },\n allowPositionals: false,\n });\n // This command WRITES documentation, so getting the input path wrong is not a\n // missing feature, it is a false statement committed to the repo: it used to\n // warn and then write \"tokens live in design-system.json\" into a project whose\n // tokens are in company.json, with the emitted commands broken to match. So\n // when the default path is absent, look for the config rather than assuming\n // it — and fail if there is nothing to name.\n const explicitInput = argv.includes('-i') || argv.includes('--input');\n let inputRel = values.input as string;\n if (!explicitInput && !existsSync(resolve(inputRel))) {\n const found = discoverConfig(process.cwd());\n if (found) {\n inputRel = found;\n console.warn(` ⚠ no design-system.json here — using ${found} (pass -i to choose another)`);\n } else {\n fail(\n `config not found: ${resolve(inputRel)}\\n` +\n ` This command writes the path into ${values.out}, so it will not guess one. ` +\n `Pass -i <path> if your tokens live elsewhere (a site config works), or run \\`vitops init\\`.`,\n );\n }\n }\n // Validate the config (fail on an invalid one). Docs themselves are rendered\n // live by `vitops docs`, so nothing is generated into the repo from it here.\n let ds: DesignSystem | null = null;\n if (existsSync(resolve(inputRel)))\n ds = loadDesignSystem(resolve(inputRel), values.theme as string | undefined);\n else console.warn(` ⚠ ${inputRel} not found — run \\`vitops init\\` to scaffold one`);\n\n // Explicit --docs-dir = legacy layout: write the docs bundle as files, no skill.\n const legacyDocsDir = values['docs-dir'] as string | undefined;\n let summary: string;\n let legacyBlock: string | undefined;\n\n if (legacyDocsDir) {\n if (!ds) fail(`--docs-dir needs a config to render docs from (${inputRel} not found)`);\n let docs: Record<string, string> = {};\n try {\n docs = generateDocs(ds, generatorAssetsDir());\n } catch (err) {\n fail(`could not generate docs: ${msg(err)}`);\n }\n for (const [rel, content] of Object.entries(docs)) {\n const p = resolve(legacyDocsDir, rel);\n mkdirSync(dirname(p), { recursive: true });\n writeFileSync(p, content);\n }\n summary = `wrote ${Object.keys(docs).length} docs to ${legacyDocsDir}/`;\n legacyBlock = [\n AGENTS_START,\n `Built with the Vitops design system (\\`@getvitops/*\\`); tokens and site facts live in \\`${inputRel}\\`.`,\n `Prefer its classes and patterns over hand-written CSS. Reference docs are in \\`${legacyDocsDir}/\\``,\n `(\\`css/classes.md\\`, \\`authoring.md\\`, \\`formats.md\\`, \\`bricks/elements.md\\`); regenerate with \\`vitops agents --docs-dir ${legacyDocsDir}\\`.`,\n AGENTS_END,\n ].join('\\n');\n } else {\n // Link the packaged skill (ships inside @getvitops/cli) into the agent\n // discovery locations; nothing is generated into the repo.\n for (const warn of linkPackagedSkills(process.cwd())) console.warn(` ⚠ ${warn}`);\n const target = findSkillTarget(process.cwd());\n if (target) {\n for (const dir of ['.agents', '.claude']) {\n const warn = linkSkill(resolve(dir, 'skills', SKILL_NAME), target);\n if (warn) console.warn(` ⚠ ${warn}`);\n }\n summary = `linked {.agents,.claude}/skills/${SKILL_NAME} → ${target}`;\n } else {\n console.warn(\n ` ⚠ could not find node_modules/@getvitops/cli/skill — install @getvitops/cli as a devDependency, then re-run \\`vitops agents\\``,\n );\n summary = 'no skill linked';\n }\n }\n\n const block =\n legacyBlock ??\n renderAgentsBlock({\n config: inputRel,\n skills: discoverBlockSkills(process.cwd()),\n version: cliVersion(),\n });\n\n const outPath = resolve(values.out as string);\n const existing = existsSync(outPath) ? readFileSync(outPath, 'utf8') : undefined;\n const { text, action } = upsertAgentsBlock(existing, block);\n if (existing === undefined) mkdirSync(dirname(outPath), { recursive: true });\n writeFileSync(outPath, text);\n const verb = { created: 'created', updated: 'updated block in', added: 'added block to top of' }[\n action\n ];\n console.log(`✓ ${verb} ${values.out} + ${summary}`);\n}\n\nasync function main() {\n const [command, ...rest] = process.argv.slice(2);\n if (!command || command === '-h' || command === '--help' || command === 'help') {\n console.log(HELP);\n return;\n }\n // Answered before dispatch, because `parseArgs` is strict and every\n // subcommand would otherwise reject `--help` as an unknown option.\n if (wantsHelp(rest)) {\n console.log(helpFor(command, rest));\n return;\n }\n switch (command) {\n case 'generate':\n return cmdGenerate(rest);\n case 'init':\n return cmdInit(rest);\n case 'validate':\n return cmdValidate(rest);\n case 'favicon':\n return cmdFavicon(rest);\n case 'agents':\n return cmdAgents(rest);\n case 'docs':\n return cmdDocs(rest);\n case 'lint':\n return cmdLint(rest);\n case 'legal':\n return cmdLegal(rest);\n case 'robots':\n return cmdRobots(rest);\n case 'reviews':\n return cmdReviews(rest);\n case 'icons':\n return cmdIcons(rest);\n case 'search':\n return cmdSearch(rest);\n case 'ads':\n return cmdAds(rest);\n case 'crm':\n return cmdCrm(rest);\n case 'email':\n return cmdEmail(rest);\n case 'emdash':\n return cmdEmdash(rest);\n case 'setup':\n return cmdSetup(rest);\n case 'media':\n return cmdMedia(rest);\n case 'audit':\n return cmdAudit(rest);\n default:\n fail(`unknown command \"${command}\" (expected: ${COMMANDS.join(' | ')}). Try: vitops --help`);\n }\n}\n\nmain().catch((err) => fail(msg(err)));\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.mjs","names":["parse","SETUP_HELP","sleep","parse","formatSetupPlan","planSetup","parse","planAds","formatPlanAds","hasAdsDrift","formatAdsSummary","parse","formatSummary","parse","parse","formatEmailPlan","hasEmailDrift","backoffSchedule","formatEmailSummary","ghRepoNwo","ghBranches","ghDefaultBranch","ghMainProtected","ghEnvironments","parse","planHosting","formatPlan","formatSummary","planTurnstile","formatTurnstilePlan","hasTurnstileDrift","formatTurnstileSummary","e","lineAt","CLASS_ATTR","formatMediaPlan","parse","planReviews","formatReviewsPlan","readReviewsCache"],"sources":["../src/args.ts","../src/commands/shared.ts","../src/credentials.ts","../src/help.ts","../src/commands/search.ts","../src/prompt.ts","../src/commands/ads.ts","../src/commands/audit.ts","../src/commands/crm.ts","../src/commands/email.ts","../src/prerender-check.ts","../src/form-template.ts","../src/commands/emdash.ts","../src/commands/setup.ts","../src/agents.ts","../src/css-scan.ts","../src/lint-css.ts","../src/lint-markup.ts","../src/lint-tokens.ts","../src/lint.ts","../src/cli.ts"],"sourcesContent":["/**\n * A typed wrapper around `node:util`'s `parseArgs`, used by every `vitops` subcommand.\n *\n * `parseArgs` types every value as `string | boolean | string[] | undefined` regardless\n * of the `type`/`default` declared for it, so every call site otherwise repeats its own\n * `values.x as string` / `as boolean` cast. The accessors below carry that cast once,\n * at the point where the option's actual shape (has a default vs. optional vs. a flag)\n * is known.\n */\nimport { parseArgs, type ParseArgsOptionsConfig } from 'node:util';\nimport { resolve } from 'node:path';\n\nexport function parse<T extends ParseArgsOptionsConfig>(\n argv: string[],\n options: T,\n allowPositionals = false,\n) {\n const { values, positionals } = parseArgs({\n args: argv,\n options,\n allowPositionals,\n strict: true,\n });\n // `values`'s type is a mapped type keyed by literal option names, which TS\n // cannot index with a generic `keyof T` — the runtime shape is exactly\n // `Record<string, string | boolean | string[] | undefined>` regardless, so the\n // accessors below read through that instead of fighting the mapped type.\n const raw = values as Record<string, string | boolean | string[] | undefined>;\n return {\n /** An option with a `default` — always a string. */\n str: (k: keyof T & string): string => raw[k] as string,\n /** An option with no `default` — may be absent. */\n opt: (k: keyof T & string): string | undefined => raw[k] as string | undefined,\n /** A `type: 'boolean'` option. */\n flag: (k: keyof T & string): boolean => raw[k] === true,\n /** A `multiple: true` string option. */\n list: (k: keyof T & string): string[] => (raw[k] as string[] | undefined) ?? [],\n /** A path option, resolved against the cwd — folds in `resolve(values.x as string)`. */\n path: (k: keyof T & string): string => resolve(raw[k] as string),\n /** Raw `parseArgs` output, for the rare case none of the above fit. */\n values,\n positionals,\n };\n}\n\n/** `(err as Error).message`, named — every `catch` block wants this. */\nexport function msg(err: unknown): string {\n return (err as Error).message;\n}\n","/**\n * Helpers shared by the `vitops` subcommands that read a `Config` (site.json) —\n * `legal`, `robots`, `reviews`, `icons`, and the `search`/`ads` subcommand groups\n * (in `commands/search.ts` / `commands/ads.ts`).\n */\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport { resolveConfig, type Config } from '@getvitops/generator';\nimport { msg } from '../args.ts';\n\nexport function fail(message: string): never {\n console.error(`✖ ${message}`);\n process.exit(1);\n}\n\n/** The `--input`/`--site-env` pair every `Config`-anchored command accepts. */\nexport const SITE_OPTS = {\n input: { type: 'string', short: 'i', default: 'site.json' },\n 'site-env': { type: 'string', default: 'production' },\n} as const;\n\n/** Read a JSON file, or exit — the shape shared by every config loader below. */\nexport function readJsonOrFail(path: string, notFoundLabel: string): unknown {\n if (!existsSync(path)) fail(`${notFoundLabel} not found: ${path}`);\n try {\n return JSON.parse(readFileSync(path, 'utf8'));\n } catch (err) {\n fail(`could not parse JSON: ${msg(err)}`);\n }\n}\n\n/**\n * Read + resolve a site config, or exit with a message.\n *\n * Accepts JSON, or a JS/TS module with a default export — deliberately not YAML,\n * which would mean a parser dependency for one command. `resolveConfig` is\n * documented as taking an already-parsed object precisely so the loader is the\n * consumer's choice; anyone on YAML can convert it or export from a module.\n */\nexport async function loadConfig(path: string, siteEnv: string): Promise<Config> {\n if (!existsSync(path)) fail(`site config not found: ${path}`);\n let raw: unknown;\n if (/\\.(json)$/.test(path)) {\n raw = readJsonOrFail(path, 'site config');\n } else {\n try {\n raw = (await import(pathToFileURL(path).href)).default;\n } catch (err) {\n fail(`could not import ${path}: ${msg(err)}`);\n }\n if (raw == null) fail(`${path} has no default export`);\n }\n try {\n return resolveConfig(raw, siteEnv);\n } catch (err) {\n // resolveConfig already formats one issue per line.\n fail(msg(err));\n }\n}\n\n/** `loadConfig`, from an args object built by `SITE_OPTS` — the repeated preamble. */\nexport async function siteConfig(a: {\n path: (k: 'input') => string;\n str: (k: 'site-env') => string;\n}): Promise<Config> {\n return loadConfig(a.path('input'), a.str('site-env'));\n}\n\n/**\n * Print a plan, then decide whether to keep going.\n *\n * Covers the shape shared by `reviews` and `search notify`: print the plan,\n * stop silently if it's `blocked`, stop with a message under `--dry`. `search\n * setup` and `ads setup` each combine `--check`/`--dry` differently enough\n * (a distinct check-success message, or one branch covering both flags with no\n * per-branch message) that forcing them through this same shape would either\n * lose behavior or need a configuration object bigger than the duplication it\n * removes — they stay bespoke.\n */\nexport function printPlanAndDryGate(opts: {\n planText: string;\n /** Truthy blocks — `indexing`'s plan carries the reason as a string here. */\n blocked: unknown;\n dry: boolean;\n dryMessage: string;\n}): boolean {\n console.log(opts.planText);\n if (opts.blocked) return false;\n if (opts.dry) {\n console.log(`\\n(--dry: ${opts.dryMessage})`);\n return false;\n }\n return true;\n}\n\n/**\n * Filter a list to the one item matching `filterValue` (a `--domain`/`--provider`\n * flag), or fail naming what's actually configured. The `--domain`/`--provider`\n * \"found none matching\" shape shared by `toSearchConsoleSetup` and `toAdProperties`.\n */\nexport function pickOne<T>(\n items: T[],\n filterValue: string | undefined,\n keyOf: (item: T) => string,\n label: { flag: string; source: string },\n): T[] {\n if (!filterValue) return items;\n const one = items.find((i) => keyOf(i) === filterValue);\n if (!one)\n fail(\n `--${label.flag} \"${filterValue}\" is not in ${label.source} (have: ${items.map(keyOf).join(', ') || 'none'})`,\n );\n return [one];\n}\n\n/** `mkdirSync(dirname(path), { recursive: true }); writeFileSync(path, contents)`. */\nexport function writeOut(path: string, contents: string): void {\n mkdirSync(dirname(path), { recursive: true });\n writeFileSync(path, contents);\n}\n","/**\n * Credential loaders shared by `search setup`, `search notify`, `ads setup` and\n * `reviews` — every command that talks to Google or Cloudflare. Never reads a\n * config file: these are secrets, and a `site.json` is committed.\n */\nimport { existsSync, readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport {\n SCOPES,\n parseServiceAccount,\n adcCredentialsPath,\n adcQuotaProject,\n parseAdcUser,\n type GoogleCredential,\n type ServiceAccount,\n} from '@getvitops/utils/indexing';\nimport type { GoogleOAuth } from '@getvitops/utils/onboarding';\nimport { fail } from './commands/shared.ts';\n\n/**\n * The Search Console credential, from either supported source.\n *\n * Never from the config file: it is the one genuine secret in this command, and a\n * `site.json` is committed. Returns `undefined` rather than failing, so a site\n * running IndexNow alone doesn't need a GCP project at all.\n */\nexport function loadServiceAccount(): ServiceAccount | undefined {\n const inline = process.env.VITOPS_GSC_SERVICE_ACCOUNT;\n const path = process.env.GOOGLE_APPLICATION_CREDENTIALS;\n const raw = inline ?? (path && existsSync(path) ? readFileSync(path, 'utf8') : undefined);\n if (!raw) return undefined;\n // An ADC file is not a malformed service account, it is a different KIND of\n // credential — and one this command already supports. `GOOGLE_APPLICATION_CREDENTIALS`\n // is exactly where `gcloud auth application-default login` writes an\n // `authorized_user` file, so parsing it as a service account died on \"missing\n // client_email or private_key\" while holding a perfectly usable user credential.\n // Hand it to the OAuth path rather than failing.\n if (parseAdcUser(raw)) return undefined;\n try {\n return parseServiceAccount(raw);\n } catch (err) {\n fail(\n `${inline ? 'VITOPS_GSC_SERVICE_ACCOUNT' : `GOOGLE_APPLICATION_CREDENTIALS (${path})`}: ${(err as Error).message}`,\n );\n }\n}\n\n/**\n * The Cloudflare DNS credential. From the environment only — a `site.json` is\n * committed, and this is a real secret. A `Zone:DNS:Edit` token (the standard\n * \"Edit zone DNS\" template, which also carries `Zone:Read`).\n */\nexport function loadCloudflareToken(): string | undefined {\n return process.env.CLOUDFLARE_API_TOKEN || undefined;\n}\n\n/** The message printed everywhere a missing Cloudflare token blocks a command. */\nexport const CLOUDFLARE_TOKEN_HINT =\n 'set CLOUDFLARE_API_TOKEN (a Zone:DNS:Edit token — the standard \"Edit zone DNS\" template, ' +\n 'which also carries the Zone:Read that the zone-by-name lookup needs)';\n\n/**\n * Set when the Google credential came from an ADC file rather than the env vars.\n *\n * Provenance matters for exactly one check: an ADC credential authenticates through\n * a shared OAuth client that owns no project, so it CANNOT work without\n * `site.google.project`, while a credential from your own OAuth client works fine\n * without one. Knowing which we hold is what turns Google's \"requires a quota\n * project\" 403 into an error that names the field and the likely value.\n */\nlet adcOrigin: { path: string; project?: string } | undefined;\n\n/**\n * Refuse an ADC credential with no project, before spending a request on a 403.\n *\n * This is the mistake waiting for the *second* site: the command is copied from\n * whatever onboarded the first one, `site.google.project` is left out, and Google\n * answers with a message about quota projects that names neither the config field\n * nor the file the credential came from.\n */\nexport function requireAdcProject(project: string | undefined, input: string): void {\n if (!adcOrigin || project) return;\n fail(\n `this Google credential comes from ${adcOrigin.path}, which authenticates through a shared ` +\n `OAuth client that owns no project — so it needs \"site.google.project\" in ${input} to say ` +\n `which project the API usage belongs to.` +\n (adcOrigin.project\n ? `\\n gcloud recorded \"${adcOrigin.project}\" as the quota project for that login, so that is probably the value.`\n : '') +\n `\\n One project per site keeps each site's usage and billing separate. That project also ` +\n `needs searchconsole.googleapis.com and siteverification.googleapis.com enabled, and the ` +\n `identity needs serviceusage.services.use on it.` +\n `\\n Or set VITOPS_GOOGLE_CLIENT_ID / _CLIENT_SECRET / _REFRESH_TOKEN to use your own OAuth client instead.`,\n );\n}\n\n/**\n * The Google user-OAuth credential (refresh-token flow), from the environment.\n *\n * A user token, not a service account: onboarding acts *as a person* who can own\n * the sites. All-or-nothing — a partial set is a misconfiguration worth naming\n * rather than a reason to run degraded.\n */\nexport function loadGoogleOAuth(): GoogleOAuth | undefined {\n const clientId = process.env.VITOPS_GOOGLE_CLIENT_ID;\n const clientSecret = process.env.VITOPS_GOOGLE_CLIENT_SECRET;\n const refreshToken = process.env.VITOPS_GOOGLE_REFRESH_TOKEN;\n // Explicit vars win over ambient credentials: an operator who set them meant them,\n // and a stale gcloud login silently overriding a deliberate CI secret is the worst\n // available precedence. Fall back to ADC only when nothing was set at all — that is\n // what makes \"I'm logged in to gcloud\" enough for a one-time human `search setup`.\n if (!clientId && !clientSecret && !refreshToken) {\n const path =\n process.env.GOOGLE_APPLICATION_CREDENTIALS ??\n adcCredentialsPath({ env: process.env, platform: process.platform, home: homedir(), join });\n if (path && existsSync(path)) {\n const raw = readFileSync(path, 'utf8');\n const adc = parseAdcUser(raw);\n if (adc) {\n // Remember that this came from ADC, and what project gcloud recorded with it.\n // A shared-client credential is refused outright without a project, so the\n // preflight below can name the likely value instead of leaving a bare 403.\n const recorded = adcQuotaProject(raw);\n adcOrigin = recorded ? { path, project: recorded } : { path };\n return adc;\n }\n }\n return undefined;\n }\n const missing = [\n ['VITOPS_GOOGLE_CLIENT_ID', clientId],\n ['VITOPS_GOOGLE_CLIENT_SECRET', clientSecret],\n ['VITOPS_GOOGLE_REFRESH_TOKEN', refreshToken],\n ]\n .filter(([, v]) => !v)\n .map(([k]) => k);\n if (missing.length) fail(`incomplete Google OAuth credential — missing ${missing.join(', ')}`);\n return { clientId: clientId!, clientSecret: clientSecret!, refreshToken: refreshToken! };\n}\n\n/**\n * Whether `oauth` can actually be used to read live state.\n *\n * An ADC credential with no project is refused outright by `requireAdcProject` —\n * so for the purposes of \"can this dry run read live state\", it is not usable\n * credentials, the same as no credential at all.\n */\nexport function isOauthUsable(\n oauth: GoogleOAuth | undefined,\n project: string | undefined,\n): boolean {\n return !!oauth && !(adcOrigin && !project);\n}\n\n/**\n * The Google identity for `search notify`, preferring a service account.\n *\n * Search Console accepts either, and the preference is about *where this command\n * runs*: it fires on every deploy, in CI, and a service account has no expiry,\n * while a refresh token can be revoked — and for an OAuth client still in\n * *Testing* publishing status Google expires it after 7 days, which is a bad\n * property for a deploy step. So the robot wins when it is available.\n *\n * The fallback is the point, though. `search setup` needs user OAuth (verifying\n * a site makes the caller an owner, which should be a person), so a consumer who\n * ran setup already has a Google credential. Demanding a second, unrelated one\n * for the other half of the same command bought nothing.\n */\nexport function loadSearchCredential(): GoogleCredential | undefined {\n const account = loadServiceAccount();\n if (account) return { kind: 'service-account', account, scope: SCOPES.webmasters };\n const oauth = loadGoogleOAuth();\n return oauth ? { kind: 'oauth', oauth } : undefined;\n}\n\n/** Both spellings, for an error that has to tell you what to actually set. */\nexport const CREDENTIAL_HINT =\n 'a service account in VITOPS_GSC_SERVICE_ACCOUNT / GOOGLE_APPLICATION_CREDENTIALS, or a user OAuth credential in VITOPS_GOOGLE_CLIENT_ID / VITOPS_GOOGLE_CLIENT_SECRET / VITOPS_GOOGLE_REFRESH_TOKEN';\n\n/**\n * The HubSpot private-app access token, from the environment. Never the\n * config file — same rule as every other credential here.\n */\nexport function loadHubSpotToken(): string | undefined {\n return process.env.VITOPS_HUBSPOT_ACCESS_TOKEN || undefined;\n}\n\n/**\n * The Zoho self-client OAuth credential, for Bigin. All-or-nothing, matching\n * `loadGoogleOAuth`'s own rule: a partial set is a misconfiguration worth\n * naming, not a reason to run degraded.\n */\nexport function loadZohoOAuth():\n | { clientId: string; clientSecret: string; refreshToken: string }\n | undefined {\n const clientId = process.env.VITOPS_ZOHO_CLIENT_ID;\n const clientSecret = process.env.VITOPS_ZOHO_CLIENT_SECRET;\n const refreshToken = process.env.VITOPS_ZOHO_REFRESH_TOKEN;\n if (!clientId && !clientSecret && !refreshToken) return undefined;\n const missing = [\n ['VITOPS_ZOHO_CLIENT_ID', clientId],\n ['VITOPS_ZOHO_CLIENT_SECRET', clientSecret],\n ['VITOPS_ZOHO_REFRESH_TOKEN', refreshToken],\n ]\n .filter(([, v]) => !v)\n .map(([k]) => k);\n if (missing.length) fail(`incomplete Zoho OAuth credential — missing ${missing.join(', ')}`);\n return { clientId: clientId!, clientSecret: clientSecret!, refreshToken: refreshToken! };\n}\n\n/**\n * The Jobber self-client OAuth credential. All-or-nothing, matching\n * `loadZohoOAuth`'s own rule. Jobber's refresh token must be static — Refresh\n * Token Rotation off in the app's Developer Center — since a Worker reading a\n * static env var cannot persist a rotated one; `jobber-token.ts` detects a\n * rotated token at exchange time and names the fix.\n */\nexport function loadJobberOAuth():\n | { clientId: string; clientSecret: string; refreshToken: string }\n | undefined {\n const clientId = process.env.VITOPS_JOBBER_CLIENT_ID;\n const clientSecret = process.env.VITOPS_JOBBER_CLIENT_SECRET;\n const refreshToken = process.env.VITOPS_JOBBER_REFRESH_TOKEN;\n if (!clientId && !clientSecret && !refreshToken) return undefined;\n const missing = [\n ['VITOPS_JOBBER_CLIENT_ID', clientId],\n ['VITOPS_JOBBER_CLIENT_SECRET', clientSecret],\n ['VITOPS_JOBBER_REFRESH_TOKEN', refreshToken],\n ]\n .filter(([, v]) => !v)\n .map(([k]) => k);\n if (missing.length) fail(`incomplete Jobber OAuth credential — missing ${missing.join(', ')}`);\n return { clientId: clientId!, clientSecret: clientSecret!, refreshToken: refreshToken! };\n}\n","/**\n * The CLI's help text, and the addressing that lets every subcommand answer\n * `--help`.\n *\n * It lives beside `cli.ts` rather than inside it for one reason: `cli.ts` calls\n * `main()` at import time, so nothing can import it to check anything. The\n * drift guard in `help.test.ts` needs `COMMANDS` and `helpSection` as values.\n */\n\nexport const HELP = `vitops — generate design-system outputs from a design-system.json\n\nAnywhere a command takes --input, that file may be a design-system.json OR the\nlarger site config that embeds one (company.json / site.json). The two are told\napart by shape, and a site config also supplies the site-level facts generation\nreads — its default colour scheme, its legal documents, its icon sprite.\n\nUsage:\n vitops generate [options] Generate platform output from a config\n vitops init [options] Scaffold a starter design-system.json\n vitops validate <file> Validate a config against the schema it says it is\n vitops favicon [options] Generate a favicon set from a source image\n vitops agents [sync] [options] Link the agent skills + keep a short managed block at the top of AGENTS.md\n vitops docs [topic] Print live design-system reference docs to stdout\n vitops lint [opts] [files] Report classes that resolve to nothing + primitives you re-implemented\n vitops legal [options] Render legal documents from a site config\n vitops robots [options] Render robots.txt from a site config's environment\n vitops reviews [options] Fetch + cache Google Business Profile reviews\n vitops icons [options] Report which icons your source uses, and build the sprite\n vitops search <sub> [opts] Search Console: onboard domains (setup) + notify deploys (notify)\n vitops ads <sub> [opts] Ad properties: verify domains (setup) + emit pixels (tags) + lint\n vitops setup <sub> [opts] Scripted onboarding: repo, hosting, domain, search, ads, all\n vitops email <sub> [opts] Sender-domain onboarding: check (setup) + deploy guard (check)\n vitops media [options] Encode raw video into web-ready WebM + MP4 + poster\n vitops audit [options] Performance + accessibility budgets over a built site (Lighthouse + axe)\n\nGenerate options:\n -i, --input <path> design-system.json or site config (default: ./design-system.json)\n -f, --format <list> bricks | css | tailwind | design (comma-separated; default: bricks)\n design emits DESIGN.md only (the agent-facing brief) — pair it\n with --out . to write it beside AGENTS.md, or compose:\n --format css,design\n -o, --out <dir> Output directory (default: ./dist)\n --theme <name> Which designSystem.themes entry to build, when --input is\n a site config (default: its defaultTheme, else \"default\")\n --site <path> Site config, when it is a different file from --input.\n Emits legal/*.html into the output directory (see:\n vitops legal). Omitted with a plain design-system.json:\n no legal files.\n --site-env <env> Environment whose A/B variant applies (default: production)\n\nInit options:\n -o, --out <path> Where to write (default: ./design-system.json, or\n ./site.json with --site)\n --force Overwrite an existing file\n --site Write a site config (Config) instead of a bare\n design-system.json — embeds a starter design system at\n designSystem.themes.default, since a Config can't\n merely reference a sibling file\n --name <name> organization.name, when --site is set\n --url <url> site.environments.{production,dev}.url, when --site is set\n\nValidate options:\n <file> Config to check (default: ./design-system.json). Either\n kind — a design-system.json or the config that embeds one.\n -i, --input <path> Same, as a flag\n --site-env <env> Environment whose A/B variant applies (default: production)\n\n Checks the schema, then the things a schema cannot: that every role points at a\n palette hue that exists, that every var() an authored declaration references\n resolves to a token this config emits, and that the colour grammar is current.\n\nFavicon options:\n -i, --input <path> Source SVG or PNG (required)\n -o, --out <dir> Output directory (default: ./public)\n --low-res <path> Optional simplified source for the 16px icon\n --background <hex> Background under the maskable icons, which must be\n opaque (default: #ffffff)\n\nAgents options:\n -i, --input <path> design-system.json or site config (default: ./design-system.json;\n validated if present)\n --theme <name> Theme to read, when --input is a site config\n -o, --out <path> Doc file to update, idempotently (default: ./AGENTS.md). Only the fenced\n \\`<!-- vitops:start -->\\` block is written: added at the top when absent,\n replaced in place when present, everything else left alone. It names the\n config and links the vitops skills linked under .agents/skills (the CLI's own\n and those of an installed @getvitops/skills, which this command also links).\n --docs-dir <dir> Legacy layout: write the docs bundle as files to this dir\n instead of linking the packaged skill\n\nDocs options:\n <topic> classes | authoring | formats | color | scales | patterns | elements\n (no topic: list topics with summaries)\n -i, --input <path> site config or design-system.json (default: ./site.json, else\n ./design-system.json)\n --theme <name> Theme to render docs from, when --input is a site config\n --all Print every topic, concatenated\n\nIcons options:\n -i, --input <path> Site config carrying the \"icons\" block (default: ./site.json)\n --site-env <env> Environment whose A/B variant applies (default: production)\n --src <dir> Source to scan for icon usage (default: ./src)\n --sprite Also build the SVG sprite\n -o, --out <dir> Where to write icons.svg with --sprite (default: ./dist)\n --json Machine-readable report on stdout\n\nLint options:\n -i, --input <path> site config or design-system.json (default: ./site.json, else\n ./design-system.json)\n --theme <name> Theme to judge classes against, when --input is a site config\n -f, --format <fmt> Format you build (bricks | css | tailwind; default: bricks).\n Responsive md-* classes are real in css/bricks, inert in tailwind.\n -s, --src <dir> Directory to scan (default: ./src)\n --strict Also fail on suggestions (reuse hints), not just on\n classes that resolve to nothing\n [files...] Lint these paths instead of scanning --src. Unreadable\n and non-source paths are skipped, so this can be wired\n into a pre-commit hook that appends the staged files.\n --fix Rewrite pre-1.0 colour-grammar token references\n (var(--<role>-<suffix>) → var(--color-<target>-<role>…))\n in CSS and <style> blocks. Applied in ONE pass, so the\n renames that rotate cannot compound. Nothing else is\n rewritten — every other finding is a judgement call.\n\n Run this after a major bump: it is the migration tool for anything the\n changelog says was renamed.\n\nLegal options:\n -i, --input <path> Site config: .json, or a .js/.ts module with a default\n export (default: ./site.json)\n -d, --doc <name> privacy | terms | cookies (repeatable; default: those\n enabled in the config)\n -f, --format <fmt> md | html | portable-text (default: md)\n html suits WordPress/Bricks and plain sites;\n portable-text suits EmDash\n -o, --out <dir> Write files here (default: print to stdout)\n --site-env <env> Environment whose A/B variant applies (default: production)\n\n Generated from your config — a starting point, not legal advice. Review before\n publishing, and make sure the config describes what your site actually does.\n\nRobots options:\n -i, --input <path> Site config: .json, or a .js/.ts module with a default\n export (default: ./site.json)\n -o, --out <dir> Write robots.txt here (default: print to stdout)\n --site-env <env> Which site.environments entry to render for (default: production)\n\n Reads the resolved environment's robots directive (falling back to\n site.seo.robots) — the same fact vitops search notify's noindex gate already\n reads. A directive containing noindex disallows every user agent and emits\n no Sitemap line; otherwise it allows everything and links\n site.seo.indexing.sitemapUrl, or the derived <canonical>/sitemap-index.xml.\n\nReviews options:\n -i, --input <path> Site config carrying site.reviews (default: ./site.json)\n --site-env <env> Environment whose A/B variant applies (default: production)\n --location <key> Fetch only this site.reviews.locations key\n --cache <path> Where to read/write the cache (default: .vitops/reviews.json)\n --out <dir> Also write the selected reviews per location as JSON here\n --dry Print the plan and exit. Fetches nothing.\n --check Fetch and diff against the cache; exit non-zero on drift.\n Mutates nothing.\n\n Reviews live only on the legacy Business Profile v4 API — there is no newer\n replacement — and the calling GCP project must be allowlisted by Google before\n any call succeeds (a one-time approval; a 403 says so rather than failing\n opaquely). Auth is user OAuth, the same VITOPS_GOOGLE_CLIENT_ID /\n VITOPS_GOOGLE_CLIENT_SECRET / VITOPS_GOOGLE_REFRESH_TOKEN (or ADC login) that\n vitops search setup uses, scoped to business.manage. The cache is meant to be\n committed: reviews change slowly, and a build should not need a live Google\n credential. Review JSON-LD is off by default (site.reviews.schema) — a business\n marking up reviews of itself is not eligible for Google's rich results and\n risks a manual action.\n\nSearch subcommands:\n vitops search setup [opts] Onboard site.searchConsole domains into GSC\n vitops search notify [opts] Tell search engines about a deploy (sitemap + IndexNow)\n\nSearch setup options:\n -i, --input <path> Site config carrying site.searchConsole (default: ./site.json)\n --site-env <env> Environment whose A/B variant applies (default: production)\n --domain <name> Scope to a single site.searchConsole entry\n --dry Print the plan and exit. Changes nothing, and runs without\n credentials (planning from scratch, which it says).\n --check Report drift and exit non-zero if any domain is not fully\n onboarded. Mutates nothing, but does need credentials —\n drift is a comparison against live state.\n\n For each domain it ensures the apex verification TXT in Cloudflare, verifies\n ownership (DNS_TXT, retried with backoff while DNS propagates — a still-\n unverified domain is reported PENDING, not failed), adds the sc-domain: property,\n and adds any delegatedOwners to the web resource. DNS is only ever created,\n never edited or deleted. Credentials come from the environment:\n CLOUDFLARE_API_TOKEN (Zone:DNS:Edit + Zone:Read — the standard \"Edit zone DNS\"\n template covers both; Zone:Read is what the zone-by-name lookup uses), and\n VITOPS_GOOGLE_CLIENT_ID /\n VITOPS_GOOGLE_CLIENT_SECRET / VITOPS_GOOGLE_REFRESH_TOKEN (a user OAuth token\n scoped to siteverification + webmasters). Granting a Google Group Full-User\n access has no API and is surfaced as a reminder.\n\n Or just be logged in: with no VITOPS_GOOGLE_* set, an Application Default\n Credentials login is used —\n gcloud auth application-default login \\\\\n --scopes=openid,https://www.googleapis.com/auth/siteverification,\\\\\n https://www.googleapis.com/auth/webmasters,https://www.googleapis.com/auth/cloud-platform\n ADC authenticates through a shared OAuth client that owns no project, so it also\n needs site.google.project in the config (sent as x-goog-user-project), both APIs\n enabled on that project, and serviceusage.services.use on it. One project per site\n is what keeps each site's API usage and billing separate.\n\nSearch notify options:\n -i, --input <path> Site config carrying seo.indexing (default: ./site.json)\n --site-env <env> Environment to notify for (default: production). An\n environment whose robots policy says noindex is refused.\n --sitemap <src> Sitemap URL or local path (default: from the config)\n --urls <list> Comma-separated URLs to submit, skipping the diff\n --all Submit every URL in the sitemap, not just what changed\n --dry Print the plan and exit. Makes no requests.\n --check Read-only: ask Google whether seo.indexing.priorityUrls\n are indexed. Exits non-zero if one is not.\n --new-key Print a fresh IndexNow key and exit\n --write-key <dir> Write the IndexNow key file into <dir> (for stacks with\n no Astro integration to do it — Bricks, WordPress)\n --snapshot <path> Changed-URL state file (default: .vitops/sitemap-snapshot.json).\n Persist it between runs (a CI cache) or every run submits\n everything.\n\n What notify can and cannot do: Google exposes no \"request indexing\" API, and its\n sitemap ping endpoint was removed in 2023 — so it resubmits your sitemap\n through the Search Console API and verifies the result with --check. IndexNow\n reaches Bing, Yandex, Naver, Seznam and Yep; Google does not participate.\n Search Console takes either credential, whichever you already have, added as an\n owner of the property: a service account in VITOPS_GSC_SERVICE_ACCOUNT (inline\n JSON) or GOOGLE_APPLICATION_CREDENTIALS (a path), or the same user OAuth\n credential \"search setup\" uses (VITOPS_GOOGLE_CLIENT_ID / _CLIENT_SECRET /\n _REFRESH_TOKEN). The service account is preferred when both are set — this runs\n on every deploy, and it does not expire.\n\nAds subcommands:\n vitops ads setup [opts] Ensure each platform's domain-verification DNS record\n vitops ads tags [opts] Print the consent-gated pixel snippets\n vitops ads lint [opts] Report ad properties the rest of the config can't see\n\nAds setup options:\n -i, --input <path> Site config carrying site.ads (default: ./site.json)\n --site-env <env> Environment whose A/B variant applies (default: production)\n --provider <name> Scope to one site.ads entry (google | meta | linkedin |\n reddit | tiktok | microsoft | pinterest | snapchat)\n --dry Print the plan and exit. Creates nothing. With\n CLOUDFLARE_API_TOKEN set it reads live DNS first, so the\n plan says what is already done; without one it plans from\n scratch and says so, rather than refusing to run.\n --check Report drift and exit non-zero if any property is not\n linked. Mutates nothing, prompts for nothing.\n --no-prompt Never ask for a missing token; fail with the field name\n instead (the default when stdin is not a TTY, e.g. in CI)\n --no-write Don't persist an answered token back into the config\n\n Four platforms verify a domain by DNS TXT — Meta, TikTok, Pinterest, Snapchat —\n and that record is the one thing this creates for you (in Cloudflare, via\n CLOUDFLARE_API_TOKEN; created only, never edited or deleted). Google Ads,\n LinkedIn, Reddit and Microsoft Ads have no domain verification at all: linking\n there is the tag and the account ID, and the run says so rather than skipping in\n silence. No platform Marketing API is called — Meta's needs a system-user token\n and Google's needs an approved developer token, so the final \"Verify\" click stays\n a reminder. The verification token is not a secret (it is published in DNS, which\n is the ownership proof), so it lives in the config and is prompted for on the\n first run.\n\nAds tags options:\n -i, --input <path> Site config carrying site.ads (default: ./site.json)\n --provider <name> Print one platform's tag\n --strategy <s> idle | async | interaction (default: idle)\n\n Prints each pixel as an INERT, consent-gated <script>: type=\"text/plain\" with the\n library URL on data-src, so an undecided visitor's page issues no third-party\n request. For stacks with no Astro integration — Bricks, WordPress, Eleventy.\n Paste into your template; @getvitops/core's consent runtime activates them.\n\nAds lint options:\n -i, --input <path> Site config carrying site.ads (default: ./site.json)\n\n Reports the gaps that are invisible at runtime: a click-ID parameter this\n platform stamps that attribution doesn't capture, a pixel while site.tracking is\n off (every conversion arrives unattributed), and a configured property with no\n tag ID. Exits non-zero on a finding.\n\nCrm check options:\n -i, --input <path> Site config carrying site.crm (default: ./site.json)\n --site-env <env> Environment to resolve (default: production)\n\n Fetches the live field/property list (HubSpot: crm/v3/properties/contacts;\n Bigin: bigin/v2/settings/fields) and reports every fieldMap/trackingFields\n target that doesn't exist there. HubSpot rejects a submission naming an\n unrecognised property; Bigin silently ignores it — this is the only way to\n see a broken mapping before a real submission finds it. Needs\n VITOPS_HUBSPOT_ACCESS_TOKEN, VITOPS_ZOHO_CLIENT_ID/_CLIENT_SECRET/\n _REFRESH_TOKEN, or (unused for now) VITOPS_JOBBER_CLIENT_ID/_CLIENT_SECRET/\n _REFRESH_TOKEN, matching whichever provider site.crm names. Jobber currently\n renders fieldMap/trackingFields as note-text lines rather than app-configured\n custom fields, so there is nothing live to check there yet. Exits non-zero\n on any missing target.\n\nCrm setup options:\n -i, --input <path> Site config carrying site.crm (default: ./site.json)\n --site-env <env> Environment to resolve (default: production)\n --dry Print what would be created and exit. Creates nothing.\n\n HubSpot: creates missing contact properties named by fieldMap/trackingFields.\n Bigin: no such API exists, so this always prints a manual reminder listing\n the fields to add in Zoho Bigin's field editor, then re-run \"crm check\".\n Jobber: app-configured custom fields aren't wired up yet, so this prints a\n reminder instead of creating anything.\n\nSetup repo options:\n --dry Print the plan and exit\n --check Report drift/blocked steps and exit non-zero, mutating nothing\n --public Create the repo public (default: private)\n --platform <p> cloudflare (default) | node — shown for parity with the hosting stage's plan\n --no-dev-resources Same — this stage never provisions D1/R2 either way\n\n Creates the GitHub repo (via \\`gh\\`), pushes main + dev, makes dev the default\n branch, and requests main branch protection + the production/dev environments.\n Idempotent — a re-run of a fully set-up repo is all skips. Branch protection and\n environment reviewers 403 on a private repo without GitHub Pro/Team; that is\n reported as blocked, with the manual Settings path, never as a failure.\n\nSetup hosting options:\n --dry Print the plan and exit\n --check Report drift/blocked steps and exit non-zero, mutating nothing\n --platform <p> cloudflare (default) | node\n --no-dev-resources Skip D1/R2 — a pure static Workers Assets project has\n nothing to provision, just the account id + token\n\n Provisions the isolated dev D1 database + R2 bucket (cloudflare) and sets the\n DEV_D1_ID / CLOUDFLARE_ACCOUNT_ID repo secrets, once \\`vitops setup repo\\` has run.\n CLOUDFLARE_API_TOKEN is always reported as blocked — no API can create one; see\n the printed dashboard link. Requires \\`npx wrangler login\\` first.\n\nSetup domain options:\n -i, --input <path> Site config (default: ./site.json)\n --site-env <env> Environment to check (default: production)\n --dry Print the plan and exit\n --check Exit non-zero if the domain state is inconsistent\n --domain <apex> Print the manual cutover steps for this apex\n\n Plans and checks the consistency between wrangler.jsonc's workers.dev/custom-domain\n state and site.json's environments/domains/robots — it NEVER edits wrangler.jsonc\n or deploys. Cutover overwrites the zone's DNS record and can take an existing live\n site down; that step is manual, on purpose. See SETUP.html's \"Domain strategy\" stage.\n\nSetup turnstile options:\n -i, --input <path> Site config (default: ./site.json)\n --site-env <env> Environment to read (default: production)\n --dry Print the plan and exit (needs no token)\n --check Exit non-zero if no widget/site key is set up; mutates nothing\n --no-write Do not write site.security.turnstile.siteKey back to the config\n --no-secret Do not run \\`wrangler secret put\\` for the new widget's secret\n\n Creates a managed Turnstile widget for the site's hostnames (canonical + each\n environment URL) unless one already covers them, and writes the PUBLIC site key to\n site.security.turnstile.siteKey. The SECRET is returned once at creation: it is piped\n to \\`wrangler secret put VITOPS_TURNSTILE_SECRET\\` and never written to a config file.\n Never edits, rotates or deletes an existing widget.\n Needs CLOUDFLARE_API_TOKEN with \"Account > Turnstile > Edit\", plus CLOUDFLARE_ACCOUNT_ID\n (or a token that can list accounts), and \\`wrangler login\\` for the secret.\n\nSetup search options:\n Forwards every option to \\`vitops search setup\\` unchanged — see \"Search setup\n options:\" above.\n\nSetup ads options:\n Forwards every option to \\`vitops ads setup\\` unchanged — see \"Ads setup options:\"\n above.\n\nSetup all options:\n --dry Print every stage's plan and exit\n --check Report drift across every stage and exit non-zero\n --public Create the repo public, for the repo stage\n --platform <p> cloudflare (default) | node, for the repo + hosting stages\n --no-dev-resources Skip D1/R2 in the hosting stage (repo stage never provisions them)\n\n Runs repo, hosting, search, ads in order. Stops at the first stage that fails;\n a blocked step (the API token, GitHub Pro/Team gating) is reported, not fatal.\n\nEmdash subcommands:\n vitops emdash prepare-db Migrate and seed the local D1 the build prerenders from\n vitops emdash check-prerender [d] Fail if a prerendered page is not a real page\n vitops emdash build [d] astro build, then check-prerender; fails on blocks with no renderer\n\nEmdash prepare-db options:\n (none) Starts \\`astro dev\\` once, calls EmDash's dev-bypass setup (migrates, applies the\n seed's sample content), checks the home page renders, stops. Only writes the\n disposable .wrangler/state. SKIP_PREPARE_LOCAL_DB=1 skips it.\n\nEmdash check-prerender options:\n [dir] Build output to check (default: dist/client). Fails on a page that carries the\n pending-migrations message, is not an HTML document, or has no <title>.\n --strict Also fail (not just warn) when a page uses a type-role class (font-display, …)\n the built CSS has no rule for, links to pages the build did not produce, a page without\n exactly one <h1>, or a form with nowhere to send it (reads ./site.json, or --site=<path>).\n\nEmdash build options:\n [dir] Runs \\`astro build\\`, then check-prerender on [dir] (default: dist/client). Also fails\n when the build reports a block type with no component (\"PortableText\n [components.type] is missing …\"): the page was published without that content.\n --strict Passed through to check-prerender.\n\nEmail subcommands:\n vitops email setup [domain] Onboard a from-address domain, waiting for its DNS to resolve\n vitops email check [opts] Non-blocking deploy guard: warn if a from domain isn't onboarded\n\nEmail setup options:\n -i, --input <path> Site config carrying site.notifications.email (default: ./site.json)\n --site-env <env> Environment whose A/B variant applies (default: production)\n [domain] Onboard this exact name instead of deriving candidates from\n config + a source scan\n --src <dir> Source to scan for from: literals as a supplement to config\n (default: ./src)\n --dry Print the plan and exit. Needs no credential — plans from\n scratch and says so.\n --check Report drift and exit non-zero. Mutates nothing.\n --no-prompt Never ask before enabling a domain (the default when\n stdin is not a TTY, e.g. in CI — it just enables)\n --timeout <s> Cap on each DNS-propagation poll wait, in seconds (default: 5)\n\n The onboarded entity is an EXACT name, and it is usually a subdomain\n (send.example.com), never the apex a from address names. When the exact\n domain is absent but a sibling in the same zone is already onboarded, this\n is reported blocked rather than silently enabling the apex too — enabling\n it rewrites _dmarc.<domain>, which can tighten policy on every OTHER mail\n sent from that domain. Sending from the onboarded sibling is the\n recommended fix. Needs CLOUDFLARE_API_TOKEN (Zone:Read + an Email Sending\n scope — broader than the \"Edit zone DNS\" template other commands use).\n\nEmail check options:\n -i, --input <path> Site config carrying site.notifications.email (default: ./site.json)\n --site-env <env> Environment whose A/B variant applies (default: production)\n --src <dir> Source to scan for from: literals (default: ./src)\n --strict Exit non-zero on drift (the default just warns and exits 0)\n --skip Skip the check entirely (same as VITOPS_SKIP_EMAIL_CHECK)\n\n Meant to run before a deploy. With no CLOUDFLARE_API_TOKEN it reports \"not\n checked\" and exits 0, so a CI job without the secret never fails on the\n check itself. Skip per-site with site.notifications.email.verifySender: false\n when the scan can't see the real from address and the warning would be a\n false positive.\n\nMedia options:\n --raw <dir> Directory of unprocessed video, walked recursively\n (default: ./raw)\n -o, --out <dir> Where the encoded outputs go (default: ./src/assets/processed).\n Subdirectories under --raw are preserved.\n --max-width <px> Cap the output width, keeping aspect ratio (default: 1920;\n 0 disables scaling)\n --crf <n> Quality on VP9's scale, 0-63, lower is better (default: 32).\n The MP4 fallback uses the equivalent on H.264's scale.\n --max-bitrate <r> Optional ceiling, e.g. 2M or 800k (default: none, which is\n constant quality rather than constrained)\n --audio Keep the audio track (default: dropped — the common case is\n a muted autoplay loop)\n --poster-time <s> Timestamp the poster frame is taken from (default: 0, which\n is often black on a clip that fades in)\n --outputs <list> Comma-separated: webm | mp4 | poster (default: all three)\n --manifest <path> Cache file (default: .vitops/media-manifest.json)\n --force Re-encode everything, ignoring the cache\n --dry Print the plan and exit. Encodes nothing.\n\n Needs ffmpeg on PATH — it is an external tool, not an npm dependency, and this\n command fails rather than quietly skipping. Commit the outputs and the manifest:\n ffmpeg output is not reproducible across versions, so re-encoding in CI churns\n the diff on every toolchain bump. Use --force when you mean to re-encode.\n\nAudit options:\n --build Run \\`astro build\\` first (default: audit the existing build)\n --url <base> Audit an already-running server instead of \\`astro preview\\`\n --port <n> Port for \\`astro preview\\` (default: 4398; aborts if taken)\n --dir <path> Project directory (default: .)\n --pages <list> Extra comma-separated paths. The page list is the built sitemap\n plus links in seed/seed.json's primary* and footer* menus; only\n when neither exists are / /privacy /terms /cookies /contact\n guessed. A 404 skips a page (with its reason in the report); a\n 404 on / fails the run.\n --no-defaults Never guess the default pages\n --seed <path> EmDash seed to read menus from (default: seed/seed.json if present)\n --lcp <ms> LCP budget (default: 2500)\n --cls <n> CLS budget (default: 0.1)\n --tbt <ms> Total Blocking Time budget, the INP proxy (default: 200)\n --impact <list> axe impacts that fail a page (default: serious,critical)\n --form-factor <f> mobile | desktop Lighthouse profile (default: mobile)\n -o, --out <dir> Report directory (default: .vitops/audit): report.json, report.md,\n and pages/<page>.{json,md}\n --warn Report without failing (exit 0), for first adoption\n\n Needs the optional packages: vp add -D playwright @axe-core/playwright lighthouse\n && vp exec playwright install chromium (checked before any server starts). A pass means no automated axe violations and the\n budgets held — it is NOT AODA certification; report.md lists the manual checks.\n\nCommon:\n -h, --help Show this help\n`;\n\nexport const SEARCH_HELP = `vitops search — Google Search Console\n\n vitops search setup [opts] Onboard site.searchConsole domains as GSC domain properties\n vitops search notify [opts] Tell search engines a deploy happened (sitemap + IndexNow)\n\nRun \\`vitops --help\\` for the full option list for each subcommand.\n`;\n\nexport const ADS_HELP = `vitops ads — link this site to its ad properties\n\n vitops ads setup [opts] Ensure each platform's domain-verification DNS record\n vitops ads tags [opts] Print the consent-gated pixel snippets\n vitops ads lint [opts] Report ad properties the rest of the config can't see\n\nRun \\`vitops --help\\` for the full option list for each subcommand.\n`;\n\nexport const CRM_HELP = `vitops crm — push form submissions into a client CRM\n\n vitops crm check [opts] Compare site.crm's fieldMap/trackingFields against the live CRM\n vitops crm setup [opts] Create missing HubSpot properties (Bigin/Jobber have no such API)\n\nRun \\`vitops --help\\` for the full option list for each subcommand.\n`;\n\nexport const EMAIL_HELP = `vitops email — sender-domain onboarding checks\n\n vitops email setup [domain] Onboard a from-address domain, waiting for its DNS to resolve\n vitops email check [opts] Non-blocking deploy guard: warn if a from domain isn't onboarded\n\nRun \\`vitops --help\\` for the full option list for each subcommand.\n`;\n\nexport const EMDASH_HELP = `vitops emdash — build-time steps for an EmDash site\n\n vitops emdash prepare-db Migrate and seed the local D1 the build prerenders from\n (SKIP_PREPARE_LOCAL_DB=1 skips)\n vitops emdash check-prerender [d] Fail if a prerendered page under [d] (default dist/client)\n is not a real page\n vitops emdash build [d] astro build, then check-prerender; also fails when the build\n reports a block type with no component (content dropped)\n vitops emdash form-template --from <seed.json> [--name contact] [--site site.json] [--dry]\n Copy a vitops_form block into site.templates (the privacy\n policy's \"what we collect\" and a check-prerender input)\n\nRun \\`vitops --help\\` for the full option list for each subcommand.\n`;\n\nexport const SETUP_HELP = `vitops setup — the scripted path from an empty scaffold to a live site\n\n vitops setup repo [opts] GitHub repo + branch policy (main protected, dev open)\n vitops setup hosting [opts] Dev D1 + R2 + repo secrets (cloudflare) or a Node target\n vitops setup domain [opts] Plan/check the workers.dev-vs-custom-domain consistency\n (never applies the cutover — see SETUP.html)\n vitops setup search [opts] Forwards to \\`vitops search setup\\`\n vitops setup ads [opts] Forwards to \\`vitops ads setup\\`\n vitops setup all [opts] Run repo, hosting, search, ads in order\n\nRun \\`vitops --help\\` for the full option list for each subcommand.\n`;\n\n/**\n * Every command `main()` dispatches. Exported so the unknown-command message\n * and the help drift guard read the same list — the message used to carry its\n * own hand-maintained copy.\n */\nexport const COMMANDS = [\n 'generate',\n 'init',\n 'validate',\n 'favicon',\n 'agents',\n 'docs',\n 'lint',\n 'legal',\n 'robots',\n 'reviews',\n 'icons',\n 'search',\n 'ads',\n 'crm',\n 'setup',\n 'email',\n 'emdash',\n 'media',\n 'audit',\n] as const;\n\n/** Commands that group subcommands instead of taking options directly. */\nexport const SUBCOMMANDS: Record<string, readonly string[]> = {\n search: ['setup', 'notify'],\n ads: ['setup', 'tags', 'lint'],\n crm: ['check', 'setup'],\n setup: ['repo', 'hosting', 'domain', 'turnstile', 'search', 'ads', 'all'],\n email: ['setup', 'check'],\n emdash: ['prepare-db', 'check-prerender', 'build'],\n};\n\n/**\n * The slice of `HELP` documenting one command, addressed by its heading.\n *\n * `HELP` is already organised as `<Command> options:` blocks, so this reads the\n * section rather than duplicating it — a second copy of every option list is a\n * second thing to keep true.\n */\nexport function helpSection(...parts: string[]): string | undefined {\n // Headings capitalise the command only, not the subcommand (\"Search setup\n // options:\"), so capitalising every part silently misses the two-word ones.\n const name = parts.join(' ');\n const title = `${name.charAt(0).toUpperCase()}${name.slice(1)} options:`;\n const start = HELP.indexOf(`\\n${title}\\n`);\n if (start === -1) return undefined;\n const body = HELP.slice(start + 1);\n // Stop at the next top-level heading (`Legal options:`, `Common:`, …).\n const next = body.slice(title.length).search(/\\n[A-Z][A-Za-z ]*:\\n/);\n return next === -1 ? body : body.slice(0, title.length + next);\n}\n\n/**\n * `--help` on a leaf subcommand used to reach `parseArgs`, which is strict and\n * declares no `help` option — so `vitops lint --help` exited non-zero with a\n * bare `Unknown option '--help'`. Every subcommand answers it now, and it is\n * resolved in one place rather than in fifteen `options` objects, so a new\n * command cannot forget to.\n */\nexport const wantsHelp = (argv: string[]): boolean =>\n argv.includes('-h') || argv.includes('--help');\n\n/** The help text to print for `vitops <command> [sub] --help`. */\nexport function helpFor(command: string, argv: string[]): string {\n const sub = argv[0] && !argv[0].startsWith('-') ? argv[0] : undefined;\n return (\n (sub ? helpSection(command, sub) : undefined) ??\n helpSection(command) ??\n (command === 'search'\n ? SEARCH_HELP\n : command === 'ads'\n ? ADS_HELP\n : command === 'crm'\n ? CRM_HELP\n : command === 'setup'\n ? SETUP_HELP\n : command === 'email'\n ? EMAIL_HELP\n : command === 'emdash'\n ? EMDASH_HELP\n : undefined) ??\n HELP\n );\n}\n","/**\n * `vitops search` — everything that talks to search engines: `setup` onboards a\n * domain into Search Console (DNS + verification + property), `notify` tells the\n * engines a deploy happened (sitemap + IndexNow). `notify` is the former\n * top-level `vitops indexing`.\n */\nimport { existsSync, readFileSync } from 'node:fs';\nimport { join, relative, resolve } from 'node:path';\nimport {\n SNAPSHOT_PATH,\n collectEntries,\n formatPlan,\n googleAccessToken,\n inspectUrl,\n keyFileContents,\n newKey,\n plan,\n readSnapshot,\n resolveKeyLocation,\n resolveSitemapUrl,\n submitBatch,\n submitSitemap,\n toSnapshot,\n verifyKeyFile,\n writeSnapshot,\n type GoogleAuthLike,\n type GoogleCredential,\n type IndexingConfig,\n} from '@getvitops/utils/indexing';\nimport {\n addSite,\n backoffSchedule,\n createApexTxt,\n findZoneId,\n formatPlan as formatSetupPlan,\n formatSummary,\n refreshTokenGrant,\n getSite,\n getVerificationToken,\n getWebResource,\n hasDrift,\n listApexTxt,\n ownerUnion,\n plan as planSetup,\n siteUrlFor,\n updateOwners,\n verifyWebResource,\n type DomainResult,\n type DomainSetup,\n type DomainState,\n} from '@getvitops/utils/onboarding';\nimport { parse } from '../args.ts';\nimport {\n CLOUDFLARE_TOKEN_HINT,\n CREDENTIAL_HINT,\n isOauthUsable,\n loadCloudflareToken,\n loadGoogleOAuth,\n loadSearchCredential,\n requireAdcProject,\n} from '../credentials.ts';\nimport { fail, pickOne, printPlanAndDryGate, siteConfig, SITE_OPTS, writeOut } from './shared.ts';\nimport { SEARCH_HELP } from '../help.ts';\nimport type { Config } from '@getvitops/generator';\n\n/**\n * Adapt a `Config` to the indexing module's own option shape.\n *\n * `IndexNowConfig` and `SearchConsoleConfig` are the schema's own types now, so those blocks pass\n * straight through. What this adapter still does is genuine resolution — the origin and the\n * robots policy come from the resolved environment, not from the `seo.indexing` block — see below.\n */\nfunction toIndexingConfig(cfg: Config, siteEnv: string): IndexingConfig {\n const site = cfg.site;\n const indexing = site.seo?.indexing ?? {};\n const env = site.environments?.[siteEnv];\n /*\n * The environment's own URL wins over `domains.canonical`.\n *\n * `domains.canonical` is the production origin — it is what absolute URLs and\n * SEO tags resolve against. Notifying a non-production environment while\n * deriving URLs from the canonical origin would submit *production* URLs under\n * the belief they were staging's, which the noindex gate below would then not\n * catch, because the gate reads the environment and the URLs would not.\n */\n const canonical = env?.url ?? site.domains?.canonical;\n return {\n ...(canonical ? { canonical } : {}),\n ...(indexing.sitemapUrl ? { sitemapUrl: indexing.sitemapUrl } : {}),\n ...(indexing.indexNow ? { indexNow: indexing.indexNow } : {}),\n ...(indexing.searchConsole ? { searchConsole: indexing.searchConsole } : {}),\n ...(indexing.priorityUrls ? { priorityUrls: indexing.priorityUrls } : {}),\n // Fall back to the site-wide policy so a site that states `noindex` once,\n // globally, is still protected.\n ...((env?.robots ?? site.seo?.robots) ? { robots: env?.robots ?? site.seo?.robots } : {}),\n };\n}\n\n/**\n * Adapt a `Config` to the onboarding module's own option shape.\n *\n * `DomainSetup` is the `site.searchConsole.<domain>` block plus its key, so this is a spread and a\n * filter. `--domain` scopes to a single entry, and a name that isn't there is an error, not an\n * empty run.\n */\nfunction toSearchConsoleSetup(cfg: Config, domainFilter: string | undefined): DomainSetup[] {\n const entries = cfg.site.searchConsole ?? {};\n const setups: DomainSetup[] = Object.entries(entries).map(([domain, e]) => ({ ...e, domain }));\n return pickOne(setups, domainFilter, (s) => s.domain, {\n flag: 'domain',\n source: 'site.searchConsole',\n });\n}\n\n/** Read a sitemap from a URL or a local path. */\nconst readSitemap = async (source: string): Promise<string> => {\n if (/^https?:\\/\\//i.test(source)) {\n const res = await fetch(source);\n if (!res.ok) fail(`could not fetch ${source}: ${res.status}`);\n return res.text();\n }\n if (!existsSync(source)) fail(`sitemap not found: ${source}`);\n return readFileSync(source, 'utf8');\n};\n\n/**\n * Attach the site's Google project to a token — for a **user** credential only.\n *\n * A user credential (an ADC login, or `VITOPS_GOOGLE_*`) authenticates through a\n * shared OAuth client that owns no project, so Google refuses the call outright\n * without `x-goog-user-project`. A service account already belongs to a project, and\n * pointing it at a *different* one would newly require `serviceusage.services.use`\n * there — turning a working CI credential into a 403 for no benefit. So the header\n * is sent exactly when it is needed, never as a default.\n */\nfunction searchAuth(\n token: string,\n credential: GoogleCredential,\n cfg: Config,\n input: string,\n): GoogleAuthLike {\n const project = cfg.site?.google?.project;\n // Only a user credential can be an ADC one, so only it can be missing a project.\n if (credential.kind === 'oauth') requireAdcProject(project, input);\n return project && credential.kind === 'oauth' ? { token, quotaProject: project } : token;\n}\n\nconst sleep = (ms: number) => new Promise<void>((r) => setTimeout(r, ms));\n\n/**\n * Tell search engines a deploy happened.\n *\n * Worth being precise about what this is, because the obvious expectation is\n * wrong: **Google has no API that requests indexing.** The Search Console button\n * is not exposed anywhere, URL Inspection is read-only, and the sitemap ping\n * endpoint was removed in 2023. So the Google half of this command resubmits the\n * sitemap and then *verifies* the outcome with `--check`; the immediate-push half\n * is IndexNow, which Google does not participate in.\n *\n * The Indexing API is deliberately absent — see `@getvitops/utils/indexing`'s\n * `gsc.ts`.\n */\nasync function cmdSearchNotify(argv: string[]) {\n const a = parse(argv, {\n ...SITE_OPTS,\n sitemap: { type: 'string' },\n urls: { type: 'string' },\n all: { type: 'boolean', default: false },\n dry: { type: 'boolean', default: false },\n check: { type: 'boolean', default: false },\n 'new-key': { type: 'boolean', default: false },\n 'write-key': { type: 'string' },\n snapshot: { type: 'string', default: SNAPSHOT_PATH },\n });\n\n // Stands alone: generating a key is what you do *before* there is a config to\n // put it in.\n if (a.flag('new-key')) {\n const key = newKey();\n console.log(key);\n console.log(\n `\\n Add to your site config:\\n \"seo\": { \"indexing\": { \"indexNow\": { \"key\": \"${key}\" } } }`,\n );\n console.log(` Then serve it at /${key}.txt — \\`vitops search notify --write-key public\\`.`);\n return;\n }\n\n const site = await siteConfig(a);\n const config = toIndexingConfig(site, a.str('site-env'));\n\n if (a.values['write-key'] !== undefined) {\n const key = config.indexNow?.key;\n if (!key) fail('no site.seo.indexing.indexNow.key in the config — generate one with --new-key');\n const file = join(resolve(a.str('write-key')), `${key}.txt`);\n writeOut(file, keyFileContents(key));\n console.log(`✓ IndexNow key file → ${file}`);\n console.log(` It must be served at ${resolveKeyLocation(config) ?? `/${key}.txt`}`);\n return;\n }\n\n const sitemapUrl = a.opt('sitemap') ?? resolveSitemapUrl(config);\n if (!sitemapUrl)\n fail(\n 'no sitemap to read — set seo.indexing.sitemapUrl or domains.canonical, or pass --sitemap',\n );\n\n const explicitUrls = a\n .opt('urls')\n ?.split(',')\n .map((u) => u.trim())\n .filter(Boolean);\n\n // The sitemap is only needed when the URL set comes from it.\n const current =\n explicitUrls?.length && !a.flag('check')\n ? []\n : (await collectEntries(sitemapUrl, readSitemap)).entries;\n\n const snapshotPath = a.path('snapshot');\n const p = plan({\n config: { ...config, sitemapUrl },\n current,\n previous: readSnapshot(snapshotPath),\n explicitUrls,\n all: a.flag('all'),\n });\n\n const keepGoing = printPlanAndDryGate({\n planText: formatPlan(p),\n // Not a failure. Running this against a `noindex` environment is a coherent\n // thing for a deploy script to do — the config said not to index it, and the\n // command honoured that. Failing the build would punish the correct setup.\n blocked: p.blocked,\n dry: a.flag('dry'),\n dryMessage: 'nothing was submitted',\n });\n if (!keepGoing) return;\n\n const credential = loadSearchCredential();\n let failed = false;\n\n // ── --check: read-only, and nothing else runs ───────────────────────────────\n if (a.flag('check')) {\n if (!p.searchConsole.siteUrl)\n fail('--check needs seo.indexing.searchConsole.siteUrl (the Search Console property)');\n if (!credential) fail(`--check needs a Search Console credential — ${CREDENTIAL_HINT}`);\n if (p.check.length === 0)\n fail('--check needs seo.indexing.priorityUrls — the pages whose indexing matters');\n\n const auth = searchAuth(await googleAccessToken(credential), credential, site, a.str('input'));\n console.log(`\\nInspecting ${p.check.length} priority URL(s):`);\n for (const url of p.check) {\n const r = await inspectUrl(auth, p.searchConsole.siteUrl, url);\n if (r.error) {\n console.log(` ? ${url} — ${r.error}`);\n failed = true;\n } else {\n console.log(\n ` ${r.indexed ? '✓' : '✗'} ${url} — ${r.coverageState ?? r.verdict ?? 'unknown'}`,\n );\n if (!r.indexed) failed = true;\n }\n }\n if (failed) {\n console.error('\\n✖ one or more priority URLs are not indexed');\n process.exit(1);\n }\n console.log('\\n✓ every priority URL is indexed');\n return;\n }\n\n // ── IndexNow ────────────────────────────────────────────────────────────────\n if (p.indexNow.enabled) {\n const { key, keyLocation } = p.indexNow;\n const check = await verifyKeyFile(keyLocation!, key!);\n if (!check.ok) {\n // Hard stop rather than \"submit and hope\": IndexNow answers 202 and then\n // discards the batch when the key doesn't verify, so submitting anyway\n // would print a success it did not earn.\n console.error(`✖ IndexNow key file check failed — ${check.reason}`);\n console.error(` Write it with: vitops search notify --write-key <public dir>`);\n failed = true;\n } else {\n let sent = 0;\n for (const batch of p.indexNow.batches) {\n const r = await submitBatch(p.indexNow, batch);\n if (!r.ok) {\n console.error(`✖ IndexNow: ${r.message ?? r.status}`);\n failed = true;\n break;\n }\n sent += r.urls;\n }\n if (!failed) console.log(`✓ IndexNow: ${sent} URL(s) submitted`);\n }\n }\n\n // ── Search Console ──────────────────────────────────────────────────────────\n if (p.searchConsole.enabled) {\n if (!credential) {\n console.log(`· Search Console: skipped — no credential (${CREDENTIAL_HINT})`);\n } else {\n const auth = searchAuth(\n await googleAccessToken(credential),\n credential,\n site,\n a.str('input'),\n );\n const r = await submitSitemap(auth, p.searchConsole.siteUrl!, sitemapUrl);\n if (r.ok) console.log(`✓ Search Console: sitemap resubmitted`);\n else {\n console.error(`✖ Search Console: ${r.message}`);\n failed = true;\n }\n }\n }\n\n if (failed) process.exit(1);\n\n /*\n * The snapshot is written last, and only on success.\n *\n * Writing it eagerly would record URLs as notified that never were — and since\n * the next run diffs against it, a single transient 503 would drop those pages\n * from every future run, silently and permanently.\n */\n if (current.length > 0) {\n writeSnapshot(snapshotPath, toSnapshot(sitemapUrl, current, new Date().toISOString()));\n console.log(` state → ${relative(process.cwd(), snapshotPath)}`);\n }\n if (p.check.length > 0)\n console.log(\n `\\n Indexing is not instant. Run \\`vitops search notify --check\\` in a day or two to see what Google actually did.`,\n );\n}\n\n/**\n * Onboard domains into Google Search Console as domain properties.\n *\n * Idempotent by construction: the planner decides each step from the domain's live\n * state, so a re-run of a fully-onboarded domain is all skips. `--check` reports\n * drift and exits non-zero without mutating; `--dry` prints the plan and stops.\n * Neither is offline: the state gather runs before the planner either way, so both\n * read from Cloudflare and Google and both need credentials.\n * DNS is only ever *created* — the verification TXT — never edited or removed.\n *\n * Search Console has no user/permission API, so granting a Google Group Full-User\n * access stays manual and is surfaced as a reminder, not attempted.\n */\nasync function cmdSearchSetup(argv: string[]) {\n const a = parse(argv, {\n ...SITE_OPTS,\n domain: { type: 'string' },\n check: { type: 'boolean', default: false },\n dry: { type: 'boolean', default: false },\n });\n\n const cfg = await siteConfig(a);\n const domains = toSearchConsoleSetup(cfg, a.opt('domain'));\n if (domains.length === 0)\n fail('no domains in site.searchConsole — add domains to onboard (keyed by bare hostname)');\n\n const cfToken = loadCloudflareToken();\n const oauth = loadGoogleOAuth();\n const project = cfg.site?.google?.project;\n const usableOauth = isOauthUsable(oauth, project);\n\n // A dry run that mutates nothing should not demand the credentials to mutate.\n // It cannot READ live state without them either, so it plans from \"nothing is\n // done yet\" and says so — which is still the complete account of what a first\n // run would do, and is what makes the plan showable before anyone has\n // provisioned anything. `--check` is different: reporting drift means\n // comparing against reality, so it keeps needing to see it.\n if (a.flag('dry') && (!cfToken || !usableOauth)) {\n const blank: DomainState = {\n zoneId: '',\n txtPresent: false,\n verified: false,\n currentOwners: [],\n propertyExists: false,\n };\n console.log(\n formatSetupPlan(planSetup({ domains }, new Map(domains.map((d) => [d.domain, blank])))),\n );\n const missing = [\n ...(cfToken ? [] : ['CLOUDFLARE_API_TOKEN (Zone:DNS:Edit + Zone:Read)']),\n ...(usableOauth\n ? []\n : oauth\n ? [`site.google.project in ${a.str('input')} (this ADC login has no project)`]\n : [\n 'VITOPS_GOOGLE_CLIENT_ID / VITOPS_GOOGLE_CLIENT_SECRET / VITOPS_GOOGLE_REFRESH_TOKEN',\n ]),\n ];\n console.log(\n `\\n(--dry, and no credentials set: planned from scratch rather than from live state, ` +\n `so nothing here is reported as already done.\\n A real run needs ${missing.join(' and ')}.)`,\n );\n return;\n }\n\n if (!cfToken) fail(CLOUDFLARE_TOKEN_HINT);\n if (!oauth)\n fail(\n 'set VITOPS_GOOGLE_CLIENT_ID / VITOPS_GOOGLE_CLIENT_SECRET / VITOPS_GOOGLE_REFRESH_TOKEN — a user OAuth token scoped to siteverification + webmasters — or run ' +\n '`gcloud auth application-default login` with the siteverification + webmasters + cloud-platform scopes (see --help)',\n );\n\n // Always a user credential here — `setup` requires one — so the site's project is\n // what makes the calls attributable. Without it a shared-client credential (a gcloud\n // ADC login) is refused outright; a credential from your own OAuth client carries its\n // project in the client id and works either way, which is why this isn't required.\n // Checked BEFORE the grant: a run that cannot succeed should spend no requests.\n requireAdcProject(project, a.str('input'));\n\n const token = await refreshTokenGrant(oauth);\n const auth: GoogleAuthLike = project ? { token, quotaProject: project } : token;\n\n // ── Observe live state per domain (the executors gather; the planner decides) ──\n const states = new Map<string, DomainState>();\n const verifyTokens = new Map<string, string>();\n const webIds = new Map<string, string>();\n for (const d of domains) {\n const zone = await findZoneId(cfToken, d.domain);\n if (!zone.ok || !zone.zoneId) fail(`${d.domain}: ${zone.message}`);\n\n const vt = await getVerificationToken(auth, d.domain);\n if (!vt.ok || !vt.token)\n fail(`${d.domain}: could not obtain a verification token — ${vt.message}`);\n verifyTokens.set(d.domain, vt.token);\n\n const txt = await listApexTxt(cfToken, zone.zoneId, d.domain);\n if (!txt.ok) fail(`${d.domain}: ${txt.message}`);\n\n const wr = await getWebResource(auth, d.domain);\n if (!wr.ok) fail(`${d.domain}: ${wr.message}`);\n if (wr.id) webIds.set(d.domain, wr.id);\n\n const site = await getSite(auth, siteUrlFor(d.domain));\n if (!site.ok) fail(`${d.domain}: ${site.message}`);\n\n states.set(d.domain, {\n zoneId: zone.zoneId,\n txtPresent: txt.contents.includes(vt.token),\n verified: wr.exists,\n currentOwners: wr.owners,\n propertyExists: site.exists,\n });\n }\n\n const p = planSetup({ domains }, states);\n console.log(formatSetupPlan(p));\n\n if (a.flag('check')) {\n if (hasDrift(p)) {\n console.error('\\n✖ drift — one or more domains are not fully onboarded');\n process.exit(1);\n }\n console.log('\\n✓ every domain is onboarded');\n return;\n }\n if (a.flag('dry')) {\n console.log('\\n(--dry: nothing was changed)');\n return;\n }\n\n // ── Execute ─────────────────────────────────────────────────────────────────\n const results: DomainResult[] = [];\n let failed = false;\n const delays = backoffSchedule();\n\n for (const d of domains) {\n const dp = p.domains.find((x) => x.domain === d.domain)!;\n const state = states.get(d.domain)!;\n const result: DomainResult = {\n domain: d.domain,\n txt: '—',\n verified: '—',\n property: '—',\n reminders: dp.reminders,\n };\n\n // 1. Ensure the apex TXT record (create only).\n if (dp.txt.action === 'skip') result.txt = 'present';\n else {\n const r = await createApexTxt(cfToken, state.zoneId!, d.domain, verifyTokens.get(d.domain)!);\n if (!r.ok) {\n console.error(`✖ ${d.domain} TXT: ${r.message}`);\n result.txt = 'failed';\n failed = true;\n results.push(result);\n continue;\n }\n result.txt = 'created';\n }\n\n // 2. Verify ownership, retrying with backoff while DNS propagates.\n let verified = state.verified;\n if (verified) result.verified = 'yes';\n else {\n for (let attempt = 0; attempt < delays.length; attempt++) {\n const v = await verifyWebResource(auth, d.domain);\n if (v.ok) {\n verified = true;\n if (v.id) webIds.set(d.domain, v.id);\n // Verification is what CREATES the web resource, so this response is the\n // first sight of its owner list — and it contains the verifying account.\n // The pre-verification observation could only report `[]`. Dropping this\n // makes the union below a bare `ownersToAdd`, and the PUT then removes\n // the very account that just proved ownership.\n state.currentOwners = v.owners;\n break;\n }\n if (attempt < delays.length - 1) {\n const wait = delays[attempt]!;\n console.log(\n ` … ${d.domain}: not verified yet (attempt ${attempt + 1}/${delays.length}); retrying in ${wait / 1000}s`,\n );\n await sleep(wait);\n }\n }\n // PENDING, not failed: propagation is slow, not broken. Re-run later.\n result.verified = verified ? 'yes' : 'pending';\n }\n if (!verified) {\n results.push(result);\n continue;\n }\n\n // 3. Add the Search Console property.\n if (dp.property.action === 'skip') result.property = 'present';\n else {\n const r = await addSite(auth, dp.siteUrl);\n if (!r.ok) {\n console.error(`✖ ${d.domain} property: ${r.message}`);\n result.property = 'failed';\n failed = true;\n } else result.property = 'added';\n }\n\n // 4. Add delegated owners to the verified web resource (additive union).\n if (dp.owners.action === 'update' && dp.ownersToAdd.length) {\n const id = webIds.get(d.domain);\n if (!id) {\n console.error(`✖ ${d.domain} owners: no web-resource id (verify may be too fresh)`);\n failed = true;\n } else {\n const r = await updateOwners(\n token,\n id,\n d.domain,\n ownerUnion(state.currentOwners, dp.ownersToAdd),\n );\n if (!r.ok) {\n console.error(`✖ ${d.domain} owners: ${r.message}`);\n failed = true;\n } else console.log(`✓ ${d.domain}: added ${dp.ownersToAdd.length} owner(s)`);\n }\n }\n\n results.push(result);\n }\n\n console.log(`\\n${formatSummary(results)}`);\n if (failed) process.exit(1);\n}\n\nexport async function cmdSearch(argv: string[]) {\n const [sub, ...rest] = argv;\n switch (sub) {\n case 'setup':\n return cmdSearchSetup(rest);\n case 'notify':\n return cmdSearchNotify(rest);\n case undefined:\n case '-h':\n case '--help':\n console.log(SEARCH_HELP);\n return;\n default:\n fail(`unknown \"search\" subcommand \"${sub}\" (expected: setup | notify). Try: vitops --help`);\n }\n}\n","/**\n * Asking for the config values `vitops ads setup` cannot derive, and writing the\n * answers back.\n *\n * The realistic first run has no verification token in the config, because the\n * token does not exist until someone opens the platform UI and asks for one. That\n * makes a missing token a **question**, not an error — but only when there is\n * somebody there to answer it, which is why every decision about *whether* to ask\n * is made here and the planner stays pure.\n *\n * The split mirrors the rest of the toolchain: `questionFor` and `patchRaw` are\n * pure and tested; `ask` and `writeConfigPatch` are the I/O, and they decide\n * nothing.\n *\n * Two rules hold the whole file together:\n *\n * - **A prompt must never hang a CI run.** Asking requires a TTY and an explicit\n * absence of `--dry` / `--check` / `--no-prompt`; otherwise the caller gets the\n * same named error it would have got before this file existed.\n * - **A fact about the site goes in the config; a credential stays in the\n * environment.** A verification token is published in DNS — the platform\n * fetching it back is the ownership proof — so persisting it is right, and it is\n * the only thing here that gets written. `CLOUDFLARE_API_TOKEN` may be typed at\n * a prompt and lives only in the process.\n */\nimport { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport { createInterface } from 'node:readline/promises';\nimport { AD_PLATFORMS, type AdProvider, type MissingField } from '@getvitops/utils/ads';\n\nexport interface Question {\n provider: AdProvider;\n needs: MissingField;\n /** One line naming what is being asked for. */\n title: string;\n /** Where to get it — UI path first, then the URL. */\n hints: string[];\n /** The inline label the answer is typed after. */\n label: string;\n}\n\n/**\n * What to ask for one gap, and where the operator finds it.\n *\n * The hints are the point. \"meta.domainVerification is missing\" is true and\n * useless; the UI path plus the console URL is the difference between a prompt\n * someone can answer now and one they abandon to go searching.\n */\nexport function questionFor(provider: AdProvider, needs: MissingField): Question {\n const platform = AD_PLATFORMS[provider];\n if (needs === 'domainVerification') {\n return {\n provider,\n needs,\n title: `${platform.name} — domain verification token`,\n hints: [\n ...(platform.verification.where ? [platform.verification.where] : []),\n platform.consoleUrl,\n ],\n // The prefix is shown as part of the prompt so it is obvious the platform's\n // bare token is what goes here, not the whole record. `txtRecord` accepts\n // either, but showing the prefix stops the ambiguity arising at all.\n label: platform.verification.txtPrefix ?? 'token',\n };\n }\n const what =\n needs === 'pixelId'\n ? provider === 'google'\n ? 'conversion ID (AW-…, not the customer ID)'\n : 'pixel / tag ID'\n : 'advertising account ID';\n return {\n provider,\n needs,\n title: `${platform.name} — ${what}`,\n hints: [platform.consoleUrl],\n label: needs,\n };\n}\n\n/** The error a non-interactive run gets instead of a prompt. */\nexport function missingFieldMessage(provider: AdProvider, needs: MissingField): string {\n const q = questionFor(provider, needs);\n return (\n `site.ads.${provider}.${needs} is not set — ${q.title}.\\n` +\n q.hints.map((h) => ` ${h}`).join('\\n') +\n `\\n Run this interactively to be prompted for it, or add it to the config yourself.`\n );\n}\n\n/** Whether this run is allowed to ask. Everything that would hang CI is checked here. */\nexport function canPrompt(opts: {\n dry: boolean;\n check: boolean;\n noPrompt: boolean;\n isTty?: boolean | undefined;\n}): boolean {\n if (opts.dry || opts.check || opts.noPrompt) return false;\n return opts.isTty ?? Boolean(process.stdin.isTTY && process.stdout.isTTY);\n}\n\n/**\n * Ask one question. An empty answer means \"skip this one\" — reported as still\n * blocked rather than treated as a value, so nothing is written and nothing is\n * created on the strength of a stray Enter.\n */\nexport async function ask(q: Question): Promise<string | undefined> {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n try {\n console.log(`\\n${q.title}`);\n for (const hint of q.hints) console.log(` ${hint}`);\n const answer = (await rl.question(` ${q.label} `)).trim();\n return answer || undefined;\n } finally {\n rl.close();\n }\n}\n\n/**\n * Merge one ad-property field into a **raw, on-disk** config object.\n *\n * Raw is load-bearing, and it is the rule `@getvitops/vite`'s `designSystemPath()`\n * already follows for the theme editor's save: `resolveConfig` normalises shorthand\n * shapes and resolves `extends` in memory, so writing from the resolved object\n * would grow keys beside the author's and silently edit a copy nothing builds from.\n * Everything outside `site.ads.<provider>.<field>` is left exactly as it was.\n */\nexport function patchRaw(\n raw: unknown,\n provider: AdProvider,\n field: MissingField,\n value: string,\n): unknown {\n if (typeof raw !== 'object' || raw == null || Array.isArray(raw))\n throw new Error('config is not an object');\n const root = raw as Record<string, unknown>;\n const site = (root['site'] ?? {}) as Record<string, unknown>;\n const ads = (site['ads'] ?? {}) as Record<string, unknown>;\n const entry = (ads[provider] ?? {}) as Record<string, unknown>;\n return {\n ...root,\n site: { ...site, ads: { ...ads, [provider]: { ...entry, [field]: value } } },\n };\n}\n\n/** Indentation the file already uses, so a patch doesn't reformat the whole document. */\nfunction detectIndent(source: string): string | number {\n const match = /\\n([ \\t]+)\"/.exec(source);\n if (!match?.[1]) return 2;\n return match[1].includes('\\t') ? '\\t' : match[1].length;\n}\n\n/**\n * Persist one answered field into the config file.\n *\n * JSON only: a `.js`/`.ts` config is code, and rewriting code to insert a value is\n * a different (and much worse) problem than merging an object — so the caller is\n * told to paste it instead. Returns whether it wrote.\n */\nexport function writeConfigPatch(\n path: string,\n provider: AdProvider,\n field: MissingField,\n value: string,\n): { written: boolean; reason?: string } {\n if (!path.endsWith('.json'))\n return {\n written: false,\n reason: `${path} is a module, not JSON — add \"${field}\": \"${value}\" to site.ads.${provider} yourself`,\n };\n if (!existsSync(path)) return { written: false, reason: `${path} no longer exists` };\n const source = readFileSync(path, 'utf8');\n const raw: unknown = JSON.parse(source);\n const next = patchRaw(raw, provider, field, value);\n const trailingNewline = source.endsWith('\\n') ? '\\n' : '';\n writeFileSync(path, JSON.stringify(next, null, detectIndent(source)) + trailingNewline);\n return { written: true };\n}\n","/**\n * `vitops ads` — everything that links this site to an ad platform. Three\n * subcommands, split by what they touch: `setup` writes DNS, `tags` writes\n * nothing and prints markup, `lint` reads and reports.\n */\nimport {\n AD_PLATFORMS,\n AD_PROVIDER_KEYS,\n formatPlan as formatPlanAds,\n formatSummary as formatAdsSummary,\n hasDrift as hasAdsDrift,\n isAdProvider,\n missingFields,\n normalizeConversionActionIds,\n normalizeConversionLabels,\n plan as planAds,\n renderTag,\n tagId,\n type AdDomainState,\n type AdPropertyResult,\n type AdPropertySetup,\n type AdProvider,\n type MissingField,\n} from '@getvitops/utils/ads';\nimport { createApexTxt, findZoneId, listApexTxt } from '@getvitops/utils/onboarding';\nimport { PLATFORM_PARAMS } from '@getvitops/utils/tracking';\nimport type { Config } from '@getvitops/generator';\nimport { parse } from '../args.ts';\nimport { CLOUDFLARE_TOKEN_HINT, loadCloudflareToken } from '../credentials.ts';\nimport { ask, canPrompt, missingFieldMessage, questionFor, writeConfigPatch } from '../prompt.ts';\nimport { fail, loadConfig, pickOne, siteConfig, SITE_OPTS } from './shared.ts';\nimport { ADS_HELP } from '../help.ts';\n\n/**\n * Adapt a `Config` to the ads module's own option shape.\n *\n * `AdPropertySetup` is the `site.ads.<provider>` block plus its key, so this spreads the block.\n * (It used to copy a hand-picked subset of fields, which silently dropped `conversionActionId` and\n * `reporting` on the way to the planner.) The domain default is the one derived value: a platform\n * verifies the host the site is published at, so\n * `domains.canonical` answers it, and only an ad account verified against some\n * other host needs `site.ads.<provider>.domain` stated.\n */\nfunction toAdProperties(cfg: Config, providerFilter: string | undefined): AdPropertySetup[] {\n const canonicalHost = (() => {\n const url = cfg.site.domains?.canonical;\n if (!url) return undefined;\n try {\n return new URL(url).hostname;\n } catch {\n return undefined;\n }\n })();\n\n const entries = cfg.site.ads ?? {};\n const properties: AdPropertySetup[] = AD_PROVIDER_KEYS.filter((p) => entries[p] != null).map(\n (provider) => {\n const e = entries[provider]!;\n const domain = e.domain ?? canonicalHost;\n return { ...e, provider, ...(domain ? { domain } : {}) };\n },\n );\n\n if (providerFilter && !isAdProvider(providerFilter))\n fail(\n `--provider \"${providerFilter}\" is not a known ad platform (expected: ${AD_PROVIDER_KEYS.join(' | ')})`,\n );\n return pickOne(properties, providerFilter, (p) => p.provider, {\n flag: 'provider',\n source: 'site.ads',\n });\n}\n\n/**\n * Link each configured ad property: ensure the platform's domain-verification TXT,\n * report what only a human can finish.\n *\n * Idempotent by construction — the planner decides each step from live DNS, so a\n * re-run of a linked property is all skips. `--check` reports drift and exits\n * non-zero without mutating; `--dry` prints the plan and stops. Neither is offline:\n * the DNS observation runs before the planner either way.\n *\n * The one thing this does that `search setup` does not is **ask**. A first run has\n * no verification token, because the token doesn't exist until someone fetches it\n * from the platform UI — so a blocked step becomes a prompt, the answer is folded\n * into the setup, and the planner runs again on it. That re-plan matters: what gets\n * executed is always a plan the pure planner produced, never a special case built\n * out of an answer.\n */\nasync function cmdAdsSetup(argv: string[]) {\n const a = parse(argv, {\n ...SITE_OPTS,\n provider: { type: 'string' },\n check: { type: 'boolean', default: false },\n dry: { type: 'boolean', default: false },\n 'no-prompt': { type: 'boolean', default: false },\n 'no-write': { type: 'boolean', default: false },\n });\n\n const input = a.path('input');\n const cfg = await loadConfig(input, a.str('site-env'));\n let properties = toAdProperties(cfg, a.opt('provider'));\n if (properties.length === 0)\n fail('no properties in site.ads — add the ad platforms this site is linked to');\n\n const dry = a.flag('dry');\n const check = a.flag('check');\n\n // Only the DNS-verifying platforms need a Cloudflare token; a config of\n // tag-only platforms (Google Ads, LinkedIn, Reddit, Microsoft) has no DNS work\n // at all, and demanding a credential for a run that writes nothing would be an\n // obstacle standing in front of an empty room.\n const needsDns = properties.some(\n (p) => AD_PLATFORMS[p.provider].verification.method === 'dns-txt',\n );\n const cfToken = loadCloudflareToken();\n // As in `search setup`: a dry run mutates nothing, so it must not demand the\n // credential to mutate. Without one it cannot read live DNS either, so\n // `observe()` returns an empty map and the plan is \"from scratch\" — still the\n // complete account of a first run, and showable before anything is provisioned.\n if (needsDns && !cfToken && dry !== true) fail(CLOUDFLARE_TOKEN_HINT);\n\n /** Observe live DNS per domain. Cached by domain: several platforms share one. */\n const observe = async (): Promise<Map<string, AdDomainState>> => {\n const states = new Map<string, AdDomainState>();\n if (!cfToken) return states;\n for (const p of properties) {\n const domain = p.domain;\n if (!domain || states.has(domain)) continue;\n if (AD_PLATFORMS[p.provider].verification.method !== 'dns-txt') continue;\n const zone = await findZoneId(cfToken, domain);\n if (!zone.ok || !zone.zoneId) fail(`${domain}: ${zone.message}`);\n const txt = await listApexTxt(cfToken, zone.zoneId, domain);\n if (!txt.ok) fail(`${domain}: ${txt.message}`);\n states.set(domain, { zoneId: zone.zoneId, txtContents: txt.contents });\n }\n return states;\n };\n\n let states = await observe();\n let adsPlan = planAds({ properties }, states);\n\n // ── Ask for what the config is missing, then re-plan on the answers ──────────\n const interactive = canPrompt({\n dry,\n check,\n noPrompt: a.flag('no-prompt'),\n });\n const gaps = missingFields(adsPlan);\n if (gaps.length && interactive) {\n const answers: { provider: AdProvider; needs: MissingField; value: string }[] = [];\n for (const gap of gaps) {\n const value = await ask(questionFor(gap.provider as AdProvider, gap.needs));\n if (value) answers.push({ provider: gap.provider as AdProvider, needs: gap.needs, value });\n }\n if (answers.length) {\n properties = properties.map((p) => {\n const mine = answers.filter((a) => a.provider === p.provider);\n return mine.reduce((acc, a) => ({ ...acc, [a.needs]: a.value }), p);\n });\n adsPlan = planAds({ properties }, states);\n if (!a.flag('no-write')) {\n for (const ans of answers) {\n const res = writeConfigPatch(input, ans.provider, ans.needs, ans.value);\n if (res.written) console.log(`✓ wrote site.ads.${ans.provider}.${ans.needs} to ${input}`);\n else console.error(`! ${res.reason}`);\n }\n }\n }\n // A token answered now may name a record we haven't looked for yet.\n states = await observe();\n adsPlan = planAds({ properties }, states);\n } else if (gaps.length && !dry && !check) {\n for (const gap of gaps)\n console.error(`! ${missingFieldMessage(gap.provider as AdProvider, gap.needs)}`);\n }\n\n if (dry || check) {\n console.log(formatPlanAds(adsPlan));\n if (check && hasAdsDrift(adsPlan)) {\n console.error('\\n✖ ad properties are not fully linked');\n process.exit(1);\n }\n return;\n }\n\n // ── Apply ────────────────────────────────────────────────────────────────────\n const results: AdPropertyResult[] = [];\n let failed = false;\n for (const p of adsPlan.properties) {\n const result: AdPropertyResult = {\n provider: p.provider,\n domain: p.domain ?? '—',\n txt: 'n/a',\n tag: p.tag.action === 'skip' ? 'ready' : 'blocked',\n reminders: p.reminders,\n };\n // A property still missing a field after the prompt round is not linked, and\n // the exit code has to say so — a run that silently succeeds while a pixel has\n // no id is the failure this command exists to make visible.\n if (p.txt.action === 'blocked' || p.tag.action === 'blocked') failed = true;\n if (p.txt.action === 'blocked') result.txt = 'blocked';\n else if (p.txt.action === 'skip')\n result.txt = AD_PLATFORMS[p.provider].verification.method === 'dns-txt' ? 'present' : 'n/a';\n else {\n const zoneId = p.domain ? states.get(p.domain)?.zoneId : undefined;\n if (!cfToken || !zoneId || !p.domain || !p.txtContent) {\n console.error(`✖ ${p.provider}: no Cloudflare zone for ${p.domain ?? '(no domain)'}`);\n result.txt = 'failed';\n failed = true;\n } else {\n const r = await createApexTxt(cfToken, zoneId, p.domain, p.txtContent);\n if (!r.ok) {\n console.error(`✖ ${p.provider} TXT: ${r.message}`);\n result.txt = 'failed';\n failed = true;\n } else {\n console.log(`✓ ${p.provider}: created TXT on ${p.domain}`);\n result.txt = 'created';\n }\n }\n }\n results.push(result);\n }\n\n console.log(`\\n${formatAdsSummary(results)}`);\n if (results.some((r) => r.txt === 'created'))\n console.log('\\n DNS takes minutes to propagate before a platform can see the record.');\n if (results.some((r) => r.reminders.length))\n console.log(' Finish the reminders above in the platform UI — no API covers those.');\n if (failed) process.exit(1);\n}\n\n/**\n * Print each configured pixel as an inert, consent-gated `<script>`.\n *\n * The universal delivery path, the same reasoning as `vitops legal`: every consumer\n * has this CLI whatever their stack, so Bricks, WordPress and a hand-built site all\n * get the correct gated markup without integration code.\n */\nasync function cmdAdsTags(argv: string[]) {\n const a = parse(argv, {\n ...SITE_OPTS,\n provider: { type: 'string' },\n strategy: { type: 'string', default: 'idle' },\n });\n\n const strategy = a.str('strategy');\n if (!['idle', 'async', 'interaction'].includes(strategy))\n fail(`--strategy \"${strategy}\" is not one of: idle | async | interaction`);\n\n const cfg = await siteConfig(a);\n const properties = toAdProperties(cfg, a.opt('provider'));\n if (properties.length === 0) fail('no properties in site.ads — nothing to emit');\n\n const out: string[] = [];\n for (const p of properties) {\n if (p.enabled === false) continue;\n const tag = renderTag(p, strategy as 'idle' | 'async' | 'interaction');\n if (!tag) {\n console.error(\n `! ${p.provider}: no ${AD_PLATFORMS[p.provider].tag.needs} — skipped (run \\`vitops ads setup\\` to be prompted for it)`,\n );\n continue;\n }\n out.push(tag);\n }\n if (out.length === 0) fail('no ad property has a tag id yet');\n console.log(out.join('\\n\\n'));\n}\n\n/**\n * Report the ad-property gaps that are invisible at runtime.\n *\n * Each finding is a silent failure, which is the bar for being here at all: an\n * uncaptured click ID looks exactly like organic traffic, and a pixel on a site\n * with tracking off produces conversions no campaign gets credit for.\n */\nasync function cmdAdsLint(argv: string[]) {\n const a = parse(argv, SITE_OPTS);\n\n const cfg = await siteConfig(a);\n const properties = toAdProperties(cfg, undefined);\n const phones = cfg.site.tracking?.phoneNumbers;\n if (properties.length === 0 && !phones) {\n console.log('· no site.ads properties — nothing to check');\n return;\n }\n\n const findings: string[] = [];\n const notes: string[] = [];\n const trackingOn = cfg.site.tracking?.enabled === true;\n\n for (const p of properties) {\n const platform = AD_PLATFORMS[p.provider];\n if (p.enabled === false) continue;\n\n if (!tagId(p))\n findings.push(\n `${p.provider}: no ${platform.tag.needs} — the property is on record but no tag can be emitted`,\n );\n\n const uncaptured = platform.clickIdParams.filter((param) => !(param in PLATFORM_PARAMS));\n if (uncaptured.length)\n findings.push(\n `${p.provider}: click ID ${uncaptured.join(', ')} is not in the capture vocabulary — every conversion from this platform will arrive unattributed`,\n );\n\n if (!trackingOn)\n findings.push(\n `${p.provider}: site.tracking.enabled is not true, so ${platform.clickIdParams.join('/')} is never captured — conversions will be unattributed`,\n );\n\n // `reporting: 'server'` with no matching `conversionActionId` isn't a bug —\n // `gtagBootstrap` keeps that action firing client-side rather than\n // dropping it — but it does mean the setting is a no-op today, worth\n // surfacing rather than leaving someone to wonder why nothing changed.\n if (p.provider === 'google' && p.reporting === 'server') {\n const labels = normalizeConversionLabels(p.conversionLabel);\n const actionIds = normalizeConversionActionIds(p.conversionActionId);\n const configuredNoId = (Object.keys(labels) as (keyof typeof labels)[]).filter(\n (action) => labels[action] && !actionIds[action],\n );\n if (configuredNoId.length)\n findings.push(\n `google: reporting is \"server\" but conversionActionId.${configuredNoId.join(', .')} ` +\n `is not set — ${configuredNoId.length === 1 ? 'that action' : 'those actions'} still ` +\n `fire client-side, not a failure but not what \"server\" asked for either`,\n );\n }\n }\n\n // Call tracking. A tracking-number provider records every caller's number, so it is a\n // processor the generated privacy policy must name; nothing else in the config says it\n // is there, which makes an undeclared one a silent compliance gap.\n if (phones) {\n const declared = (cfg.site.legal?.privacyPolicy?.processors ?? []).map((p) =>\n p.name.toLowerCase(),\n );\n if (!phones.provider)\n findings.push(\n 'site.tracking.phoneNumbers has no provider — state which call-tracking service holds these numbers (twilio, callrail, …) so the privacy policy can name it',\n );\n else if (!declared.some((n) => n.includes(phones.provider!.toLowerCase())))\n findings.push(\n `site.tracking.phoneNumbers.provider \"${phones.provider}\" records callers' numbers but is not in site.legal.privacyPolicy.processors — the generated privacy policy will not name it`,\n );\n }\n const google = properties.find((p) => p.provider === 'google' && p.enabled !== false);\n if (google?.callTracking && normalizeConversionLabels(google.conversionLabel).call)\n notes.push(\n 'google: callTracking and conversionLabel.call are both set — one call can count twice. In Google Ads, make one of the two conversion actions secondary.',\n );\n for (const n of notes) console.warn(`! ${n}`);\n\n if (findings.length === 0) {\n console.log(\n `✓ ${properties.length} ad propert${properties.length === 1 ? 'y' : 'ies'} check out`,\n );\n return;\n }\n for (const f of findings) console.error(`✖ ${f}`);\n process.exit(1);\n}\n\nexport async function cmdAds(argv: string[]) {\n const [sub, ...rest] = argv;\n switch (sub) {\n case 'setup':\n return cmdAdsSetup(rest);\n case 'tags':\n return cmdAdsTags(rest);\n case 'lint':\n return cmdAdsLint(rest);\n case undefined:\n case '-h':\n case '--help':\n console.log(ADS_HELP);\n return;\n default:\n fail(`unknown \"ads\" subcommand \"${sub}\" (expected: setup | tags | lint). Try: vitops --help`);\n }\n}\n","/**\n * `vitops audit` — performance + accessibility budgets over a built site.\n *\n * The planner (page discovery, status handling, budgets, report rendering) is pure and\n * lives in `@getvitops/utils/audit` beside `ads/`, `indexing/` and `onboarding/`, as\n * `plan.ts`; this file is wiring only — flags in, files + exit code out. The browser\n * tooling (Playwright, axe, Lighthouse) is lazy and optional, see `audit/run.ts`.\n */\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\nimport {\n DEFAULT_BUDGETS,\n buildReport,\n fetchSitemapPaths,\n preflight,\n formatSummary,\n parseImpacts,\n pathsFromSeed,\n planPages,\n reportFiles,\n runAudit,\n runCommand,\n startPreview,\n type AuditBudgets,\n type FormFactor,\n} from '@getvitops/utils/audit';\nimport { msg, parse } from '../args.ts';\nimport { fail } from './shared.ts';\n\n/** Dedicated port: not Astro's 4321, which a developer's own `astro dev` is likely on. */\nconst DEFAULT_PORT = 4398;\n\nfunction num(flag: string, raw: string | undefined, fallback: number): number {\n if (raw === undefined) return fallback;\n const n = Number(raw);\n if (!Number.isFinite(n) || n <= 0) fail(`audit: --${flag} must be a positive number`);\n return n;\n}\n\nexport async function cmdAudit(argv: string[]): Promise<void> {\n const args = parse(argv, {\n dir: { type: 'string', default: '.' },\n url: { type: 'string' },\n build: { type: 'boolean', default: false },\n port: { type: 'string' },\n pages: { type: 'string' },\n 'no-defaults': { type: 'boolean', default: false },\n seed: { type: 'string' },\n out: { type: 'string', short: 'o', default: '.vitops/audit' },\n lcp: { type: 'string' },\n cls: { type: 'string' },\n tbt: { type: 'string' },\n impact: { type: 'string' },\n 'form-factor': { type: 'string', default: 'mobile' },\n warn: { type: 'boolean', default: false },\n });\n\n const dir = resolve(args.str('dir'));\n const formFactor = args.str('form-factor') as FormFactor;\n if (formFactor !== 'mobile' && formFactor !== 'desktop')\n fail('audit: --form-factor must be mobile or desktop');\n\n let impacts = DEFAULT_BUDGETS.blockingImpacts;\n const impactFlag = args.opt('impact');\n if (impactFlag) {\n try {\n impacts = parseImpacts(impactFlag);\n } catch (err) {\n fail(`audit: ${msg(err)}`);\n }\n }\n const budgets: AuditBudgets = {\n lcpMs: num('lcp', args.opt('lcp'), DEFAULT_BUDGETS.lcpMs),\n cls: num('cls', args.opt('cls'), DEFAULT_BUDGETS.cls),\n tbtMs: num('tbt', args.opt('tbt'), DEFAULT_BUDGETS.tbtMs),\n blockingImpacts: impacts,\n };\n\n // Menu-derived candidates: the EmDash seed, when there is one. Explicit `--seed` must\n // exist; the default location is only looked at, never required.\n const seedFlag = args.opt('seed');\n const seedPath = seedFlag ? resolve(seedFlag) : join(dir, 'seed', 'seed.json');\n let menuPaths: string[] = [];\n if (existsSync(seedPath)) {\n try {\n menuPaths = pathsFromSeed(JSON.parse(readFileSync(seedPath, 'utf8')));\n } catch (err) {\n fail(`audit: could not read menus from ${seedPath}: ${msg(err)}`);\n }\n } else if (seedFlag) fail(`audit: seed not found: ${seedPath}`);\n\n if (args.flag('build') && !args.opt('url')) {\n console.log('▸ building (astro build)');\n try {\n await runCommand('npx', ['--no-install', 'astro', 'build'], dir);\n } catch (err) {\n fail(`audit: build failed: ${msg(err)}`);\n }\n }\n\n // Missing peers or Chromium fail here, before any server exists to be left running.\n try {\n await preflight();\n } catch (err) {\n fail(`audit: ${msg(err)}`);\n }\n\n let server: Awaited<ReturnType<typeof startPreview>> | undefined;\n let baseUrl = args.opt('url');\n // `fail` and an uncaught throw skip `finally`, and Ctrl+C skips it too: stop the detached\n // preview on every way out, or the next run aborts with \"port already in use\".\n const stopServer = () => server?.stop();\n const onSignal = (signal: NodeJS.Signals) => {\n stopServer();\n process.exit(signal === 'SIGINT' ? 130 : 143);\n };\n process.once('SIGINT', onSignal);\n process.once('SIGTERM', onSignal);\n process.once('exit', stopServer);\n try {\n if (!baseUrl) {\n const port = num('port', args.opt('port'), DEFAULT_PORT);\n console.log(`▸ astro preview on :${port}`);\n server = await startPreview({ cwd: dir, port });\n baseUrl = server.baseUrl;\n }\n // Planned once the server is up: the built sitemap is the page list when there is one.\n const sitemapPaths = await fetchSitemapPaths(baseUrl);\n const paths = planPages({\n sitemapPaths,\n menuPaths,\n extra: (args.opt('pages') ?? '')\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean),\n includeDefaults: !args.flag('no-defaults'),\n });\n console.log(\n sitemapPaths.length\n ? `▸ ${sitemapPaths.length} page(s) from the sitemap`\n : '▸ no sitemap found; using menus and defaults',\n );\n console.log(`▸ auditing ${paths.length} candidate page(s) at ${baseUrl} (${formFactor})`);\n const pages = await runAudit({\n baseUrl,\n paths,\n budgets,\n formFactor,\n onProgress: (m) => console.log(` ${m}`),\n });\n const report = buildReport({\n generatedAt: new Date().toISOString(),\n baseUrl,\n formFactor,\n budgets,\n pages,\n });\n\n const out = resolve(dir, args.str('out'));\n for (const [name, contents] of Object.entries(reportFiles(report))) {\n const file = join(out, name);\n mkdirSync(dirname(file), { recursive: true });\n writeFileSync(file, contents);\n }\n console.log(`\\n${formatSummary(report)}\\n\\nReport: ${join(out, 'report.md')}`);\n\n if (!report.passed) {\n if (args.flag('warn')) console.log('(--warn: reporting only, exit 0)');\n else process.exitCode = 1;\n }\n } catch (err) {\n // `fail` exits the process, which skips `finally` — stop the server first or the\n // detached preview outlives the run.\n server?.stop();\n fail(`audit: ${msg(err)}`);\n } finally {\n server?.stop();\n }\n}\n","/**\n * `vitops crm` — the two things `site.crm` needs a human-in-the-loop for.\n *\n * `check` is the drift guard, and the reason this command exists at all:\n * HubSpot *rejects* a submission naming an unrecognised property, and Bigin\n * silently *ignores* one — so a typo'd `fieldMap`/`trackingFields` entry is\n * invisible in one CRM and fatal in the other, and nothing short of fetching\n * the live field list can tell you which. `setup` creates the missing\n * properties HubSpot's API allows creating; Bigin has no such endpoint, so it\n * always resolves to a manual reminder, matching how `ads setup` handles a\n * platform with no verification API. Jobber currently renders `fieldMap`/\n * `trackingFields` values as note-text lines rather than app-configured\n * custom fields (see `@getvitops/utils/crm`'s `jobber.ts` docblock for why\n * that surface is deferred), so there is nothing live to check yet — both\n * subcommands say so rather than guessing at an unverified API.\n *\n * Modelled on `commands/ads.ts`: read-and-create only, never destructive.\n */\nimport {\n zohoAccountsHost,\n zohoApiHost,\n type CrmConfig,\n type CrmProvider,\n} from '@getvitops/utils/crm';\nimport { parse } from '../args.ts';\nimport { loadHubSpotToken, loadZohoOAuth } from '../credentials.ts';\nimport { CRM_HELP } from '../help.ts';\nimport { fail, siteConfig, SITE_OPTS } from './shared.ts';\n\n/** Every CRM property name this site's config asserts exists, from both maps —\n * `fieldMap`/`trackingFields` values are property names, not the submitted\n * field names on the left. */\nfunction configuredProperties(crm: CrmConfig): string[] {\n return [...Object.values(crm.fieldMap ?? {}), ...Object.values(crm.trackingFields ?? {})];\n}\n\nasync function hubspotContactProperties(accessToken: string): Promise<Set<string>> {\n const res = await fetch('https://api.hubapi.com/crm/v3/properties/contacts', {\n headers: { authorization: `Bearer ${accessToken}` },\n });\n if (!res.ok) fail(`HubSpot properties lookup failed (${res.status}): ${await res.text()}`);\n const json = (await res.json()) as { results?: { name: string }[] };\n return new Set((json.results ?? []).map((p) => p.name));\n}\n\nasync function biginContactFields(\n oauth: { clientId: string; clientSecret: string; refreshToken: string },\n dataCenter: string,\n): Promise<Set<string>> {\n const body = new URLSearchParams({\n grant_type: 'refresh_token',\n client_id: oauth.clientId,\n client_secret: oauth.clientSecret,\n refresh_token: oauth.refreshToken,\n });\n const tokenRes = await fetch(`https://${zohoAccountsHost(dataCenter)}/oauth/v2/token`, {\n method: 'POST',\n headers: { 'content-type': 'application/x-www-form-urlencoded' },\n body: body.toString(),\n });\n if (!tokenRes.ok)\n fail(`Zoho token exchange failed (${tokenRes.status}): ${await tokenRes.text()}`);\n const { access_token } = (await tokenRes.json()) as { access_token?: string };\n if (!access_token) fail('Zoho token exchange returned no access_token');\n\n const res = await fetch(\n `https://${zohoApiHost(dataCenter)}/bigin/v2/settings/fields?module=Contacts`,\n { headers: { authorization: `Zoho-oauthtoken ${access_token}` } },\n );\n if (!res.ok) fail(`Bigin fields lookup failed (${res.status}): ${await res.text()}`);\n const json = (await res.json()) as { fields?: { api_name: string }[] };\n return new Set((json.fields ?? []).map((f) => f.api_name));\n}\n\n/** What each provider's `check` needs to say when it doesn't fail outright.\n * Kept as a table rather than an `if/else if` chain so a fourth provider is\n * a missing entry (a type error), not a silently-wrong branch. */\nconst CHECK_NOUNS: Record<CrmProvider, string> = {\n hubspot: 'properties',\n bigin: 'fields',\n jobber: 'fields',\n};\nconst CHECK_REJECTION: Record<CrmProvider, string> = {\n hubspot: 'HubSpot will reject the whole submission',\n bigin: 'Bigin will silently drop this value',\n jobber: 'Jobber will silently drop this value',\n};\n\n/**\n * Fetch the live field/property vocabulary and report every `fieldMap`/\n * `trackingFields` target that doesn't exist there.\n */\nasync function cmdCrmCheck(argv: string[]) {\n const a = parse(argv, SITE_OPTS);\n const cfg = await siteConfig(a);\n const crm = cfg.site.crm;\n if (!crm) {\n console.log('· no site.crm — nothing to check');\n return;\n }\n if (crm.enabled === false) {\n console.log('· site.crm.enabled is false — nothing to check');\n return;\n }\n\n const wanted = configuredProperties(crm);\n if (wanted.length === 0) {\n console.log('· site.crm has no fieldMap/trackingFields entries — nothing to check');\n return;\n }\n\n if (crm.provider === 'jobber') {\n console.log(\n '· no live check for Jobber yet — fieldMap/trackingFields values currently render as ' +\n 'note-text lines, not app-configured custom fields, so nothing there can silently ' +\n 'mismatch (see @getvitops/utils/crm jobber.ts docblock).',\n );\n return;\n }\n\n let known: Set<string>;\n if (crm.provider === 'hubspot') {\n const token = loadHubSpotToken();\n if (!token) fail('set VITOPS_HUBSPOT_ACCESS_TOKEN to check the live property list');\n known = await hubspotContactProperties(token);\n } else {\n const oauth = loadZohoOAuth();\n if (!oauth)\n fail(\n 'set VITOPS_ZOHO_CLIENT_ID / VITOPS_ZOHO_CLIENT_SECRET / VITOPS_ZOHO_REFRESH_TOKEN to check the live field list',\n );\n known = await biginContactFields(oauth, crm.bigin!.dataCenter);\n }\n\n const missing = wanted.filter((p) => !known.has(p));\n if (missing.length === 0) {\n console.log(`✓ ${wanted.length} mapped ${CHECK_NOUNS[crm.provider]} exist`);\n return;\n }\n for (const p of missing)\n console.error(\n `✖ \"${p}\" is not a ${crm.provider === 'hubspot' ? 'HubSpot contact property' : 'Bigin Contacts field'} — ${CHECK_REJECTION[crm.provider]}`,\n );\n process.exit(1);\n}\n\n/**\n * Create the HubSpot properties `fieldMap`/`trackingFields` name but don't\n * exist yet. Bigin has no property-creation API and Jobber's app-configured\n * custom fields aren't wired up yet (see the module docblock) — both resolve\n * to `blocked`/a manual reminder, matching `ads setup`'s handling of a\n * platform with no verification API.\n */\nasync function cmdCrmSetup(argv: string[]) {\n const a = parse(argv, { ...SITE_OPTS, dry: { type: 'boolean', default: false } });\n const cfg = await siteConfig(a);\n const crm = cfg.site.crm;\n if (!crm) fail('no site.crm configured — nothing to set up');\n\n const wanted = configuredProperties(crm);\n\n if (crm.provider === 'jobber') {\n console.log(\n '· Jobber app-configured custom fields are not wired up yet — fieldMap/trackingFields ' +\n 'values render as note-text lines instead, which need no setup. If you do want them as ' +\n \"real Jobber custom fields, create them manually in Jobber's Developer Center first.\",\n );\n return;\n }\n\n if (crm.provider === 'bigin') {\n if (wanted.length === 0) {\n console.log('· no fieldMap/trackingFields entries to check against Bigin');\n return;\n }\n console.log(\n `? Bigin has no API to create Contacts fields. Create these manually in Zoho Bigin's ` +\n `field editor, then re-run \\`vitops crm check\\`:\\n` +\n wanted.map((p) => ` - ${p}`).join('\\n'),\n );\n return;\n }\n\n const token = loadHubSpotToken();\n if (!token) fail('set VITOPS_HUBSPOT_ACCESS_TOKEN to create HubSpot properties');\n const known = await hubspotContactProperties(token);\n const missing = wanted.filter((p) => !known.has(p));\n\n if (missing.length === 0) {\n console.log('✓ every mapped property already exists');\n return;\n }\n\n if (a.flag('dry')) {\n console.log(\n `would create ${missing.length} HubSpot contact propert${missing.length === 1 ? 'y' : 'ies'}:`,\n );\n for (const p of missing) console.log(` + ${p}`);\n return;\n }\n\n for (const p of missing) {\n const res = await fetch('https://api.hubapi.com/crm/v3/properties/contacts', {\n method: 'POST',\n headers: { authorization: `Bearer ${token}`, 'content-type': 'application/json' },\n body: JSON.stringify({\n name: p,\n label: p,\n type: 'string',\n fieldType: 'text',\n groupName: 'contactinformation',\n }),\n });\n if (!res.ok) {\n console.error(`✖ ${p}: ${res.status} ${await res.text()}`);\n process.exit(1);\n }\n console.log(`✓ created ${p}`);\n }\n}\n\nexport async function cmdCrm(argv: string[]) {\n const [sub, ...rest] = argv;\n switch (sub) {\n case 'check':\n return cmdCrmCheck(rest);\n case 'setup':\n return cmdCrmSetup(rest);\n case undefined:\n case '-h':\n case '--help':\n console.log(CRM_HELP);\n return;\n default:\n fail(`unknown \"crm\" subcommand \"${sub}\" (expected: check | setup). Try: vitops --help`);\n }\n}\n","/**\n * `vitops email` — connect `site.notifications.email.from` to whether its domain\n * is actually onboarded onto an outbound email provider.\n *\n * The silent failure this exists to catch: `EMAIL.send()` throws\n * `E_SENDER_NOT_VERIFIED` at runtime with nothing at build or deploy time saying\n * a word, because nothing previously checked the `from` domain against Cloudflare\n * Email Sending's actual onboarding state. Two subcommands, split by what they\n * do: `setup` onboards a domain and waits for its DNS to resolve; `check` is the\n * non-blocking deploy guard.\n */\nimport { existsSync } from 'node:fs';\nimport { createInterface } from 'node:readline/promises';\nimport {\n blockedDomains,\n dnsStatus,\n enableSending,\n findZoneForHost,\n formatPlan as formatEmailPlan,\n formatSummary as formatEmailSummary,\n hasDrift as hasEmailDrift,\n listSendingSubdomains,\n planSendingDomains,\n resolveTxt,\n sendingDns,\n backoffSchedule,\n collectFromAddresses,\n type DomainResult,\n type FromCandidate,\n type ZoneSendingState,\n} from '@getvitops/utils/email';\nimport { normalizeEmailChannel, resolveSender } from '@getvitops/utils/notify';\nimport { scanFiles } from '@getvitops/utils';\nimport type { Config } from '@getvitops/generator';\nimport { parse } from '../args.ts';\nimport { CLOUDFLARE_TOKEN_HINT, loadCloudflareToken } from '../credentials.ts';\nimport { fail, loadConfig, siteConfig, SITE_OPTS } from './shared.ts';\nimport { EMAIL_HELP } from '../help.ts';\n\nconst sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));\n\n/**\n * Every `from` domain worth checking: the explicit config value first, its\n * derived `noreply@<canonical>` default second, and a best-effort scan of\n * `--src` as a supplement — never the primary source, since a computed value in\n * source is reported as dynamic rather than guessed at (see `email/scan.ts`).\n */\nfunction gatherCandidates(\n cfg: Config,\n srcDir: string,\n): { candidates: FromCandidate[]; dynamicNotes: string[] } {\n const candidates: FromCandidate[] = [];\n const dynamicNotes: string[] = [];\n\n const channel = normalizeEmailChannel(cfg.site.notifications?.email);\n const canonical = cfg.site.domains?.canonical;\n const address = resolveSender(channel ?? { provider: 'cloudflare' }, { canonical });\n if (address) {\n const domain = address.split('@')[1];\n if (domain) candidates.push({ address, domain, source: channel?.from ? 'config' : 'derived' });\n }\n\n if (existsSync(srcDir)) {\n const { addresses, dynamic } = collectFromAddresses(scanFiles(srcDir));\n for (const a of addresses) {\n const domain = a.split('@')[1];\n if (domain && !candidates.some((c) => c.address === a))\n candidates.push({ address: a, domain, source: 'scan' });\n }\n for (const d of dynamic)\n dynamicNotes.push(`! ${d.file}:${d.line}: from is computed (\\`${d.expr}\\`) — not checked`);\n }\n\n return { candidates, dynamicNotes };\n}\n\n/** Observe each candidate domain's zone, keyed by the domain itself (not the zone name). */\nasync function observeZones(\n token: string | undefined,\n domains: string[],\n): Promise<{ states: Map<string, ZoneSendingState>; failures: string[] }> {\n const states = new Map<string, ZoneSendingState>();\n const failures: string[] = [];\n if (!token) return { states, failures };\n for (const domain of domains) {\n const zone = await findZoneForHost(token, domain);\n if (!zone.ok || !zone.zoneId) {\n failures.push(`${domain}: ${zone.message}`);\n continue;\n }\n const res = await listSendingSubdomains(token, zone.zoneId);\n if (!res.ok) {\n failures.push(`${domain}: ${res.message}`);\n continue;\n }\n states.set(domain, { zoneId: zone.zoneId, subdomains: res.subdomains });\n }\n return { states, failures };\n}\n\nasync function confirm(question: string): Promise<boolean> {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n try {\n const answer = (await rl.question(`${question} [y/N] `)).trim().toLowerCase();\n return answer === 'y' || answer === 'yes';\n } finally {\n rl.close();\n }\n}\n\n/**\n * Onboard a `from` domain and wait for its DNS to resolve.\n *\n * Idempotent by construction — a re-run of a fully-onboarded domain is all\n * skips. `--dry` prints the plan and mutates nothing (and needs no credential,\n * planning from scratch); `--check` reports drift and exits non-zero.\n */\nasync function cmdEmailSetup(argv: string[]) {\n const a = parse(\n argv,\n {\n ...SITE_OPTS,\n src: { type: 'string', default: 'src' },\n dry: { type: 'boolean', default: false },\n check: { type: 'boolean', default: false },\n 'no-prompt': { type: 'boolean', default: false },\n timeout: { type: 'string', default: '5' },\n },\n true,\n );\n\n const cfg = await loadConfig(a.path('input'), a.str('site-env'));\n const explicitDomain = a.positionals[0];\n const dry = a.flag('dry');\n const check = a.flag('check');\n\n const { candidates, dynamicNotes } = explicitDomain\n ? {\n candidates: [\n {\n address: `noreply@${explicitDomain}`,\n domain: explicitDomain,\n source: 'config' as const,\n },\n ],\n dynamicNotes: [],\n }\n : gatherCandidates(cfg, a.path('src'));\n\n if (candidates.length === 0)\n fail(\n 'no from domain to check — set site.notifications.email.from or site.domains.canonical, or pass a domain: vitops email setup <domain>',\n );\n\n const cfToken = loadCloudflareToken();\n if (!cfToken && !dry) fail(CLOUDFLARE_TOKEN_HINT);\n\n const domains = [...new Set(candidates.map((c) => c.domain))];\n const { states, failures } = await observeZones(cfToken, domains);\n const emailPlan = planSendingDomains({ candidates, provider: 'cloudflare' }, states);\n\n if (dry || check) {\n console.log(formatEmailPlan(emailPlan));\n for (const n of dynamicNotes) console.log(n);\n for (const f of failures) console.error(`! ${f}`);\n if (check && hasEmailDrift(emailPlan)) {\n console.error('\\n✖ a from domain is not fully onboarded');\n process.exit(1);\n }\n return;\n }\n\n const interactive = !a.flag('no-prompt') && Boolean(process.stdin.isTTY && process.stdout.isTTY);\n const timeoutMs = Number(a.str('timeout')) * 1000;\n\n const results: DomainResult[] = [];\n let failed = false;\n for (const d of emailPlan.domains) {\n if (d.onboard.action === 'skip') {\n results.push({ domain: d.domain, onboard: 'present', reminders: [] });\n continue;\n }\n if (d.onboard.action === 'blocked') {\n console.error(`? ${d.domain}: ${d.onboard.detail}`);\n results.push({ domain: d.domain, onboard: 'blocked', reminders: [] });\n failed = true;\n continue;\n }\n\n console.log(`\\n${d.domain}: ${d.onboard.detail}`);\n if (interactive && !(await confirm(`Enable Email Sending for \"${d.domain}\"?`))) {\n results.push({ domain: d.domain, onboard: 'blocked', reminders: ['skipped by operator'] });\n continue;\n }\n\n const zone = await findZoneForHost(cfToken!, d.domain);\n if (!zone.ok || !zone.zoneId) {\n console.error(`✖ ${d.domain}: ${zone.message}`);\n results.push({ domain: d.domain, onboard: 'failed', reminders: [] });\n failed = true;\n continue;\n }\n const enabled = await enableSending(cfToken!, zone.zoneId, d.domain);\n if (!enabled.ok) {\n console.error(`✖ ${d.domain}: ${enabled.message}`);\n results.push({ domain: d.domain, onboard: 'failed', reminders: [] });\n failed = true;\n continue;\n }\n console.log(`✓ ${d.domain}: enabled`);\n\n const reminders: string[] = [];\n if (enabled.tag) {\n const dns = await sendingDns(cfToken!, zone.zoneId, enabled.tag);\n const expected = dns.records.filter((r) => r.type === 'TXT');\n if (dns.ok && expected.length) {\n const schedule = backoffSchedule();\n let pending = expected;\n for (let attempt = 0; attempt < schedule.length && pending.length; attempt++) {\n await sleep(Math.min(schedule[attempt]!, timeoutMs));\n const resolved = new Map<string, string[]>();\n for (const name of new Set(pending.map((r) => r.name))) {\n const r = await resolveTxt(name);\n if (r.ok) resolved.set(name, r.contents);\n }\n const status = dnsStatus(pending, resolved);\n pending = status.filter((s) => !s.present).map((s) => s.record);\n console.log(\n ` DNS: ${expected.length - pending.length}/${expected.length} records resolving (attempt ${attempt + 1}/${schedule.length})`,\n );\n }\n if (pending.length)\n reminders.push(\n `${pending.length} DNS record(s) not yet resolving — check again shortly.`,\n );\n }\n }\n results.push({ domain: d.domain, onboard: 'created', reminders });\n }\n\n console.log(`\\n${formatEmailSummary(results)}`);\n for (const n of dynamicNotes) console.log(n);\n for (const f of failures) console.error(`! ${f}`);\n if (failed) process.exit(1);\n}\n\n/**\n * The non-blocking deploy guard: warn when a `from` domain isn't onboarded,\n * exit 0 by default. `--strict` makes it a real gate.\n *\n * Runs with no credential too — it then reports \"not checked\", exits 0, and a\n * CI job with no CLOUDFLARE_API_TOKEN never fails on the check itself.\n */\nasync function cmdEmailCheck(argv: string[]) {\n const a = parse(argv, {\n ...SITE_OPTS,\n src: { type: 'string', default: 'src' },\n strict: { type: 'boolean', default: false },\n skip: { type: 'boolean', default: false },\n });\n\n if (a.flag('skip') || process.env['VITOPS_SKIP_EMAIL_CHECK']) {\n console.log('· vitops email check: skipped (--skip / VITOPS_SKIP_EMAIL_CHECK)');\n return;\n }\n\n const cfg = await siteConfig(a);\n const emailCfg = cfg.site.notifications?.email;\n if (typeof emailCfg === 'object' && emailCfg.verifySender === false) {\n console.log('· vitops email check: skipped (site.notifications.email.verifySender is false)');\n return;\n }\n\n const { candidates, dynamicNotes } = gatherCandidates(cfg, a.path('src'));\n if (candidates.length === 0) {\n console.log('· no from domain configured — nothing to check');\n return;\n }\n\n const cfToken = loadCloudflareToken();\n if (!cfToken) {\n console.log('· vitops email check: not checked — no CLOUDFLARE_API_TOKEN');\n return;\n }\n\n const domains = [...new Set(candidates.map((c) => c.domain))];\n const { states, failures } = await observeZones(cfToken, domains);\n const emailPlan = planSendingDomains({ candidates, provider: 'cloudflare' }, states);\n\n for (const n of dynamicNotes) console.log(n);\n for (const f of failures) console.error(`! ${f}`);\n\n const blocked = blockedDomains(emailPlan);\n if (blocked.length === 0) {\n console.log(`✓ every from domain is onboarded`);\n return;\n }\n for (const b of blocked) console.error(`! ${b.domain}: ${b.onboard.detail}`);\n if (a.flag('strict')) {\n console.error('\\n✖ a from domain is not onboarded (--strict)');\n process.exit(1);\n }\n}\n\nexport async function cmdEmail(argv: string[]) {\n const [sub, ...rest] = argv;\n switch (sub) {\n case 'setup':\n return cmdEmailSetup(rest);\n case 'check':\n return cmdEmailCheck(rest);\n case undefined:\n case '-h':\n case '--help':\n console.log(EMAIL_HELP);\n return;\n default:\n fail(`unknown \"email\" subcommand \"${sub}\" (expected: setup | check). Try: vitops --help`);\n }\n}\n","/**\n * The pure half of `vitops emdash check-prerender`: is a prerendered `.html` body a real page?\n *\n * `astro build` succeeds even when a prerendered route's response was an error. Against a local\n * database with pending migrations EmDash answers with a one-line \"Database migrations are\n * required\" body, which is written to `index.html` like any other page, and the deploy then\n * publishes it.\n */\nimport { hasFormDelivery } from '@getvitops/utils/notify';\n\nexport const PENDING_MIGRATIONS = /Database migrations are required/i;\n\n/** Why `html` is not a real page, or `undefined` when it looks like one. */\nexport function prerenderProblem(html: string): string | undefined {\n if (PENDING_MIGRATIONS.test(html)) return 'carries the pending-migrations message';\n if (!/<html[\\s>]/i.test(html)) return 'is not an HTML document';\n if (!/<title>[^<]+<\\/title>/i.test(html)) return 'has no <title>';\n const unrendered = unrenderedBlocks(html);\n if (unrendered.length)\n return `dropped block(s) with no renderer: ${unrendered.join(', ')} (see \\`PortableText [components.type] is missing\\` in the build log)`;\n return undefined;\n}\n\n/**\n * `vitops_*` block types a page's nested body dropped. `vitops_section` and widget areas emit a\n * `data-vitops-unrendered=\"<type>\"` marker for each, so a page whose sections rendered as\n * headings over empty bodies fails here instead of passing for a real page. (The build-log check in\n * `vitops emdash build` also catches top-level bodies, which carry no marker.)\n */\nexport function unrenderedBlocks(html: string): string[] {\n const types = new Set<string>();\n for (const m of html.matchAll(/data-vitops-unrendered\\s*=\\s*\"([^\"]+)\"/gi)) types.add(m[1]!);\n return [...types].sort();\n}\n\n/** The type-role utilities (`font-<role>`) the framework defines; a closed list from its docs. */\nconst TYPE_ROLES = [\n 'display',\n 'title',\n 'heading',\n 'body',\n 'quote',\n 'caption',\n 'eyebrow',\n 'code',\n 'lead',\n 'footnote',\n 'tag',\n];\nconst TYPE_ROLE_CLASS = new RegExp(`^font-(?:${TYPE_ROLES.join('|')})$`);\n\n/** Every `class=\"…\"` token in a page. */\nfunction classesIn(html: string): Set<string> {\n const out = new Set<string>();\n for (const m of html.matchAll(/\\sclass\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)')/gi))\n for (const token of (m[1] ?? m[2] ?? '').split(/\\s+/)) if (token) out.add(token);\n return out;\n}\n\n/**\n * Type-role classes a page uses that the built stylesheet has no rule for.\n *\n * Tailwind v4 does not scan `node_modules`, so a utility used only by a package's own renderer\n * (`font-display` in `vitops_stats`) was never generated, and the page rendered at body size with\n * no error. The type roles are the one family checkable without the site's config: the list is\n * closed. A class is \"defined\" when the CSS names it as a selector.\n */\nexport function missingTypeRoleRules(html: string, css: string): string[] {\n const defined = (cls: string) => new RegExp(`\\\\.${cls}(?![\\\\w-])`).test(css);\n return [...classesIn(html)].filter((c) => TYPE_ROLE_CLASS.test(c) && !defined(c)).sort();\n}\n\n/**\n * Block types EmDash's `<PortableText>` reported as having no component, from a build's output.\n *\n * `astro build` prints `PortableText [components.type] is missing \"vitops_cards\"` once per\n * occurrence and carries on, rendering nothing for that block. A single missing renderer is a\n * page published with its content gone, and the prerender guard cannot see it in the HTML. The\n * known cause is an import cycle that empties the plugin's block map for a whole build, so the\n * warning arrives dozens of times; it is reported once per type.\n */\nexport function missingBlockComponents(log: string): string[] {\n // eslint-disable-next-line no-control-regex\n const plain = log.replace(/\\u001b\\[[0-9;]*m/g, '');\n const seen = new Set<string>();\n for (const m of plain.matchAll(/PortableText \\[components\\.type\\] is missing \"([^\"]+)\"/g))\n seen.add(m[1]!);\n return [...seen].sort();\n}\n\n/**\n * How many `<h1>` a page has. A page with none has no title in its outline; a page with two (a\n * hero left on its `h1` default beside a layout title) has a muddled one. Counted outside\n * `<template>`, `<script>` and comments so inert markup does not skew it.\n */\nexport function h1Count(html: string): number {\n const live = html\n .replace(/<!--[\\s\\S]*?-->/g, '')\n .replace(/<(script|template|style)[\\s\\S]*?<\\/\\1>/gi, '');\n return (live.match(/<h1[\\s>]/gi) ?? []).length;\n}\n\n/** The URL path a built `.html` file is served at: `a/index.html` and `a.html` are both `/a`. */\nexport function routeOf(relativeFile: string): string {\n const path =\n '/' +\n relativeFile\n .replace(/\\\\/g, '/')\n .replace(/(^|\\/)index\\.html$/, '$1')\n .replace(/\\.html$/, '');\n return path.length > 1 ? path.replace(/\\/+$/, '') : '/';\n}\n\n/**\n * Site-relative links in a page that point at a route the build did not produce: a planned link\n * (`/pricing` in a menu before the page exists) that would 404. Anchors, query-only links, assets\n * (anything with a file extension), `/_emdash/*` and `/api/*` (Worker routes) are not judged.\n */\nexport function brokenInternalLinks(html: string, routes: ReadonlySet<string>): string[] {\n const live = html\n .replace(/<!--[\\s\\S]*?-->/g, '')\n .replace(/<(script|template|style)[\\s\\S]*?<\\/\\1>/gi, '');\n const broken = new Set<string>();\n for (const m of live.matchAll(/<a\\s[^>]*?href\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)')/gi)) {\n const href = (m[1] ?? m[2] ?? '').trim();\n if (!href.startsWith('/') || href.startsWith('//')) continue;\n const path = href.split(/[?#]/)[0] || '/';\n if (/\\.[a-z0-9]{1,8}$/i.test(path) || path.startsWith('/_') || path.startsWith('/api/'))\n continue;\n const norm = path.length > 1 ? path.replace(/\\/+$/, '') : path;\n if (!routes.has(norm)) broken.add(norm);\n }\n return [...broken].sort();\n}\n\n/**\n * URLs a sitemap lists that the build did not produce a page for. A collection with no\n * `urlPattern` is listed as `/<collection>/<slug>` while the site serves `/<slug>`, so the\n * sitemap submitted to a search engine would be all dead links. Only same-path `<loc>`s are judged.\n */\nexport function sitemapOrphans(xml: string, routes: ReadonlySet<string>): string[] {\n const orphans = new Set<string>();\n for (const m of xml.matchAll(/<loc>\\s*([^<\\s]+)\\s*<\\/loc>/gi)) {\n let path: string;\n try {\n path = new URL(m[1]!).pathname;\n } catch {\n continue;\n }\n if (/\\.[a-z0-9]{1,8}$/i.test(path)) continue; // nested sitemaps, assets\n const norm = path.length > 1 ? path.replace(/\\/+$/, '') : path;\n if (!routes.has(norm)) orphans.add(norm);\n }\n return [...orphans].sort();\n}\n\n/** Whether a built page carries the `vitops_form` block's post (to the conversion route). */\nexport function hasConversionForm(html: string): boolean {\n return /<form[^>]*\\saction\\s*=\\s*[\"']\\/api\\/track[\"']/i.test(html);\n}\n\n/**\n * Why a site with a form has nowhere to send it, or `undefined` when something is configured.\n * `createConversionRoute` refuses a post with no recipient and no CRM rather than accept a message\n * nobody receives, so the first visitor to submit would be told it failed. A recipient is\n * `site.notifications.email` (a bare address, or `{ to }`) or a location `email`; a CRM also counts.\n */\nexport function deliveryProblem(config: unknown): string | undefined {\n const c = (config ?? {}) as {\n site?: { notifications?: { email?: unknown }; crm?: unknown };\n organization?: { locations?: Record<string, { email?: unknown }> | Array<{ email?: unknown }> };\n };\n const locations = c.organization?.locations;\n const locationEmails = (Array.isArray(locations) ? locations : Object.values(locations ?? {}))\n .map((l) => l?.email)\n .filter((e): e is string => typeof e === 'string' && e !== '');\n // The same test the route makes (`hasFormDelivery`), so the build and the route cannot disagree.\n return hasFormDelivery(\n { notifications: c.site?.notifications as never, locationEmails },\n c.site?.crm,\n )\n ? undefined\n : 'no recipient: set `site.notifications.email`, or give an organization location an `email`, or configure `site.crm`';\n}\n\nconst NOT_COLLECTED = new Set([\n '_next',\n 'website',\n 'honeypot',\n '_honeypot',\n 'cf-turnstile-response',\n]);\n\n/**\n * The visitor-supplied field names of the form that posts to `/api/track`, from the built HTML:\n * every named control except the hidden ones, the honeypot and the Turnstile token, which are not\n * personal information the visitor gave. Repeated names (a tick-box group) count once.\n */\nexport function formFieldNames(html: string): string[] {\n const names = new Set<string>();\n for (const form of html.matchAll(\n /<form[^>]*\\saction\\s*=\\s*[\"']\\/api\\/track[\"'][^>]*>([\\s\\S]*?)<\\/form>/gi,\n )) {\n for (const m of form[1]!.matchAll(/<(input|textarea|select)\\b([^>]*)>/gi)) {\n const attrs = m[2]!;\n if (/\\stype\\s*=\\s*[\"']hidden[\"']/i.test(attrs)) continue;\n const name = /\\sname\\s*=\\s*[\"']([^\"']+)[\"']/i.exec(attrs)?.[1];\n if (name && !NOT_COLLECTED.has(name)) names.add(name);\n }\n }\n return [...names].sort();\n}\n\n/**\n * Form fields that are on a built page but not in any `site.templates` form entry. `vitops legal`\n * derives \"what personal information we collect\" from those entries, not from the editor's block,\n * so a field added in the admin is invisible to the generated privacy policy until mirrored.\n */\nexport function uninventoriedFields(config: unknown, built: string[]): string[] {\n const templates = (\n config as {\n site?: { templates?: Record<string, { type?: string; fields?: { name?: string }[] }> };\n }\n )?.site?.templates;\n const known = new Set<string>();\n for (const tpl of Object.values(templates ?? {}))\n if (tpl?.type === 'form') for (const f of tpl.fields ?? []) if (f?.name) known.add(f.name);\n return built.filter((name) => !known.has(name));\n}\n","/**\n * `vitops emdash form-template`: copy a saved `vitops_form` block into `site.templates` so the\n * generated privacy policy and `check-prerender` know what the form collects. The block → entry\n * conversion is `@getvitops/emdash`'s `formTemplateFromBlock` (the same resolver the renderer\n * uses, so names match what is submitted); this module is the part that finds blocks and edits\n * the config, kept pure so it is testable without a project.\n */\n\n/** Every `vitops_form` block anywhere in a JSON document (a seed, a content export). */\nexport function findFormBlocks(doc: unknown): Record<string, unknown>[] {\n const found: Record<string, unknown>[] = [];\n const walk = (node: unknown) => {\n if (Array.isArray(node)) return node.forEach(walk);\n if (!node || typeof node !== 'object') return;\n const obj = node as Record<string, unknown>;\n if (obj['_type'] === 'vitops_form') found.push(obj);\n Object.values(obj).forEach(walk);\n };\n walk(doc);\n return found;\n}\n\n/**\n * Set `site.templates[name]` on the RAW on-disk config, leaving everything else as it was (the\n * rule `designSystemPath()` follows: never normalise the file into a copy nothing builds from).\n * Returns the updated object and whether it changed, so a re-run is a no-op.\n */\nexport function withTemplate(\n config: unknown,\n name: string,\n template: unknown,\n): { config: Record<string, unknown>; changed: boolean } {\n const root = (config && typeof config === 'object' ? config : {}) as Record<string, unknown>;\n const site = (root['site'] && typeof root['site'] === 'object' ? root['site'] : {}) as Record<\n string,\n unknown\n >;\n const templates = (\n site['templates'] && typeof site['templates'] === 'object' ? site['templates'] : {}\n ) as Record<string, unknown>;\n const changed = JSON.stringify(templates[name]) !== JSON.stringify(template);\n return {\n config: { ...root, site: { ...site, templates: { ...templates, [name]: template } } },\n changed,\n };\n}\n","/**\n * `vitops emdash` — build-time steps for an EmDash site on the Cloudflare adapter.\n *\n * These used to be copied into every scaffold as `scripts/*.mjs`, which froze each fix at\n * scaffold time. As commands they arrive with `@getvitops/cli`, so a site's `package.json`\n * keeps one-line entries and `vp update` delivers the logic (the same move `vitops setup`\n * made for the template's old `init-github`/`init-hosting` scripts).\n *\n * prepare-db migrate and seed the LOCAL D1 the build prerenders from\n * check-prerender fail if a prerendered page is not a real page\n * form-template copy a vitops_form block into site.templates\n * build `astro build`, then the same checks, plus a failure for any block with no renderer\n *\n * Why both exist: `astro build` prerenders every page inside a local workerd against a local D1\n * (`.wrangler/state`), never the deployed database. The template sets\n * `migrations.runtime: 'check'`, so a build against a local database with pending migrations\n * prerenders EmDash's one-line \"Database migrations are required\" response into every page,\n * and the build still succeeds. A fresh CI runner, a first deploy, a `SKIP_PULL_CONTENT=1`\n * build and an upgrade deploy all hand the build exactly that database.\n */\nimport { spawn, spawnSync } from 'node:child_process';\nimport {\n closeSync,\n existsSync,\n mkdirSync,\n openSync,\n readFileSync,\n readdirSync,\n writeFileSync,\n} from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { pathToFileURL } from 'node:url';\nimport { validateConfig } from '@getvitops/generator';\nimport { createServer } from 'node:net';\nimport { join, relative } from 'node:path';\nimport {\n brokenInternalLinks,\n deliveryProblem,\n formFieldNames,\n uninventoriedFields,\n hasConversionForm,\n h1Count,\n missingBlockComponents,\n missingTypeRoleRules,\n prerenderProblem,\n PENDING_MIGRATIONS,\n routeOf,\n sitemapOrphans,\n} from '../prerender-check.ts';\nimport { findFormBlocks, withTemplate } from '../form-template.ts';\nimport { fail } from './shared.ts';\nimport { EMDASH_HELP } from '../help.ts';\n\nfunction freePort(): Promise<number> {\n return new Promise((resolve, reject) => {\n const server = createServer();\n server.once('error', reject);\n server.listen(0, '127.0.0.1', () => {\n const { port } = (server.address() as { port: number }) ?? { port: 0 };\n server.close(() => resolve(port));\n });\n });\n}\n\n/**\n * Poll until `test` passes. `died` is asked after each miss and may return why waiting is pointless\n * (the dev server printed an error and is gone), so a broken config fails in seconds, not after the\n * full timeout.\n */\nasync function until(\n test: () => Promise<boolean>,\n what: string,\n ms = 120_000,\n died?: () => string | undefined,\n) {\n const deadline = Date.now() + ms;\n while (Date.now() < deadline) {\n try {\n if (await test()) return;\n } catch {\n // not up yet\n }\n const why = died?.();\n if (why) throw new Error(`${what} did not start: ${why}`);\n await new Promise((r) => setTimeout(r, 1000));\n }\n throw new Error(`timed out waiting for ${what}`);\n}\n\nconst log = (msg: string) => console.log(`prepare-db: ${msg}`);\n\n/**\n * `astro dev` daemonizes: the child outlives the command that started it, so capturing its\n * output through pipes would leave this process waiting on the daemon's inherited handles,\n * which looks exactly like a hang. Output is ignored and every call has a timeout.\n */\nconst DEV_LOG = '.vitops/prepare-db.log';\nconst astro = (args: string[]) => {\n // Output goes to a file, not a pipe: the daemon keeps the handle open, and a pipe would make\n // this process wait on it (which looks exactly like a hang). The file is read back on failure.\n mkdirSync('.vitops', { recursive: true });\n const fd = openSync(DEV_LOG, 'a');\n try {\n return spawnSync(\n existsSync('node_modules/.bin/astro') ? 'node_modules/.bin/astro' : 'astro',\n args,\n { stdio: ['ignore', fd, fd], timeout: 300_000, env: { ...process.env, CI: '1' } },\n );\n } finally {\n closeSync(fd);\n }\n};\nconst logTail = (lines = 30) => {\n try {\n return readFileSync(DEV_LOG, 'utf8').trimEnd().split('\\n').slice(-lines).join('\\n');\n } catch {\n return '';\n }\n};\n// An error line from the dev server, once it has printed one, means it is not coming up.\nconst devError = () => {\n const tail = logTail(60);\n const line = tail.split('\\n').find((l) => /\\[(?:ERROR|error)\\]|^\\s*(?:\\w+)?Error:|ERR_/.test(l));\n return line?.trim();\n};\n\n/** `site.json` must parse and validate before a dev server is asked to read it. */\nfunction checkSiteConfig(siteFile = 'site.json') {\n if (!existsSync(siteFile)) return;\n let parsed: unknown;\n try {\n parsed = JSON.parse(readFileSync(siteFile, 'utf8'));\n } catch (e) {\n fail(`${siteFile} is not valid JSON: ${(e as Error).message}`);\n }\n const result = validateConfig(parsed);\n if (!result.ok)\n fail(\n `${siteFile} is not a valid site config:\\n` +\n result.errors\n .map((i) => ` ${(i.path ?? []).join('.') || '(root)'}: ${i.message}`)\n .join('\\n') +\n '\\n\\nThe dev server would exit on this without saying why. Fix it, or run `vitops validate`.',\n );\n}\n\n/**\n * Starts `astro dev` once, calls EmDash's `dev-bypass` setup endpoint (the non-interactive\n * setup wizard: it runs migrations, `dev: 'auto'`, and applies the seed), checks the home page\n * renders, stops. Only ever writes the local `.wrangler/state`, which is disposable; the dev\n * administrator it creates lives in that copy and is never deployed. Content already there\n * (from `pull-content`) is kept: the seed applies with `onConflict: skip`.\n */\nasync function cmdPrepareDb() {\n if (process.env.SKIP_PREPARE_LOCAL_DB) {\n log('SKIP_PREPARE_LOCAL_DB set — leaving the local database as it is.');\n return;\n }\n checkSiteConfig();\n writeFileSync(DEV_LOG, '');\n const port = await freePort();\n const base = `http://127.0.0.1:${port}`;\n\n log(`starting astro dev on ${base} ...`);\n const started = astro(['dev', '--port', String(port), '--host', '127.0.0.1']);\n if ((started.error as NodeJS.ErrnoException | undefined)?.code === 'ENOENT')\n fail('`astro` is not on PATH. Run it through the package scripts (`vpr prepare-local-db`).');\n if (started.status !== 0)\n fail(\n `could not start astro dev (${started.signal ?? `exit ${started.status}`}). ` +\n 'It is killed after 5 minutes (exit 143 = SIGTERM). Run `astro dev` by hand to see why.',\n );\n\n let failure: Error | null = null;\n try {\n await until(\n async () => (await fetch(`${base}/_emdash/api/setup/status`)).ok,\n 'astro dev',\n 120_000,\n devError,\n );\n log('dev server is up; migrating and seeding (first run can take a minute) ...');\n // A database that is already set up may decline; the home page check decides if it matters.\n const setup = await fetch(`${base}/_emdash/api/setup/dev-bypass?redirect=/`, {\n redirect: 'manual',\n signal: AbortSignal.timeout(120_000),\n });\n if (setup.status >= 400)\n console.warn(\n `prepare-db: dev-bypass answered ${setup.status}; checking the home page anyway.`,\n );\n\n const home = await fetch(`${base}/`);\n const html = await home.text();\n if (!home.ok)\n throw new Error(`astro dev answered ${home.status} on / — the dev runtime is broken`);\n if (PENDING_MIGRATIONS.test(html))\n throw new Error('the local database still has pending migrations');\n log('the local database is migrated and the home page renders.');\n } catch (e) {\n failure = e as Error;\n } finally {\n astro(['dev', 'stop']);\n }\n if (failure) {\n const tail = logTail();\n if (tail) console.error(`prepare-db: last of ${DEV_LOG}:\\n${tail}\\n`);\n }\n if (failure)\n fail(\n `${failure.message}\\nRefusing to continue: prerendering now would publish error pages as the site.`,\n );\n}\n\nfunction* filesWith(dir: string, ext: string): Generator<string> {\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n const path = join(dir, entry.name);\n if (entry.isDirectory()) yield* filesWith(path, ext);\n else if (entry.name.endsWith(ext)) yield path;\n }\n}\nconst htmlFiles = (dir: string) => filesWith(dir, '.html');\nconst cssFiles = (dir: string) => [...filesWith(dir, '.css')];\n\nfunction cmdCheckPrerender(argv: string[]) {\n const root = argv.find((a) => !a.startsWith('-')) ?? 'dist/client';\n const strict = argv.includes('--strict');\n if (!existsSync(root)) fail(`${root} does not exist (run \\`astro build\\` first).`);\n const bad: string[] = [];\n const unstyled: string[] = [];\n const outline: string[] = [];\n const dead: string[] = [];\n let formPage: string | undefined;\n const formFields = new Set<string>();\n const routes = new Set([...htmlFiles(root)].map((f) => routeOf(relative(root, f))));\n const css = cssFiles(root)\n .map((f) => readFileSync(f, 'utf8'))\n .join('\\n');\n let count = 0;\n for (const file of htmlFiles(root)) {\n // The toolkit's own assets (the theme editor page, …) are not site pages: no h1, no outline.\n if (relative(root, file).startsWith('vitops/')) continue;\n count += 1;\n const html = readFileSync(file, 'utf8');\n const why = prerenderProblem(html);\n if (why) bad.push(` ${file} ${why}`);\n else {\n const h1s = h1Count(html);\n if (h1s !== 1 && !file.endsWith('/404.html')) outline.push(` ${file}: ${h1s} <h1>`);\n }\n if (!why && hasConversionForm(html)) {\n formPage ??= file;\n for (const n of formFieldNames(html)) formFields.add(n);\n }\n if (!why) {\n const links = brokenInternalLinks(html, routes);\n if (links.length) dead.push(` ${file}: ${links.join(', ')}`);\n }\n if (!why && css) {\n const missing = missingTypeRoleRules(html, css);\n if (missing.length) unstyled.push(` ${file}: ${missing.join(', ')}`);\n }\n }\n if (count === 0) fail(`no .html files under ${root}.`);\n if (bad.length)\n fail(\n `the build prerendered pages that are not real pages:\\n${bad.join('\\n')}\\n\\n` +\n 'The local database the build reads is probably unmigrated. Run `vpr prepare-local-db` ' +\n '(it migrates and seeds the local D1 through `astro dev`), then build again.',\n );\n if (unstyled.length) {\n const note =\n `type-role classes with no rule in the built CSS (they render at body size):\\n${unstyled.join('\\n')}\\n\\n` +\n 'Tailwind does not scan node_modules. Use @getvitops/astro >= the release that adds the ' +\n \"`@source` lines, or add `@source` for the package that renders them to the site's stylesheet.\";\n if (strict) fail(note);\n console.warn(`check-prerender: warning: ${note}`);\n }\n if (formPage) {\n const siteFile = argv.find((a) => a.startsWith('--site='))?.slice(7) ?? 'site.json';\n let siteConfig: unknown;\n try {\n if (existsSync(siteFile)) siteConfig = JSON.parse(readFileSync(siteFile, 'utf8'));\n } catch {\n siteConfig = undefined; // an unreadable config is `vitops validate`'s to report\n }\n if (siteConfig) {\n const problem = deliveryProblem(siteConfig);\n if (problem) {\n const note =\n `${formPage} has a form, but ${problem}. The route refuses submissions until one is set, ` +\n 'so the first visitor to send a message would be told it failed. Run `vitops email setup` ' +\n 'for the sender domain once it is.';\n if (strict) fail(note);\n console.warn(`check-prerender: warning: ${note}`);\n }\n const missing = uninventoriedFields(siteConfig, [...formFields].sort());\n if (missing.length) {\n const note =\n `the form collects ${missing.join(', ')}, which no site.templates form entry in ${siteFile} ` +\n 'lists. `vitops legal` builds the privacy policy\\'s \"what we collect\" from those entries, ' +\n 'so the generated policy would under-report. Mirror the fields in a `type: \"form\"` template.';\n if (strict) fail(note);\n console.warn(`check-prerender: warning: ${note}`);\n }\n }\n }\n const orphaned = [...filesWith(root, '.xml')]\n .filter((f) => /sitemap[^/\\\\]*\\.xml$/.test(f))\n .flatMap((f) => sitemapOrphans(readFileSync(f, 'utf8'), routes));\n if (orphaned.length) {\n const note =\n `the sitemap lists URLs the build did not produce (a search engine would be sent to 404s):\\n` +\n `${orphaned\n .slice(0, 10)\n .map((u) => ` ${u}`)\n .join('\\n')}${orphaned.length > 10 ? `\\n … and ${orphaned.length - 10} more` : ''}\\n\\n` +\n 'A collection with no `urlPattern` is listed as /<collection>/<slug>. Set the pattern on the ' +\n \"collection (the seed's `urlPattern`, or Content types in the admin) to the path the site serves.\";\n if (strict) fail(note);\n console.warn(`check-prerender: warning: ${note}`);\n }\n if (dead.length) {\n const note =\n `links to pages the build did not produce (they would 404):\\n${dead.join('\\n')}\\n\\n` +\n 'Publish the page, or mark the menu item as a planned link by adding `draft` to its CSS ' +\n 'classes so it is not rendered until the page exists.';\n if (strict) fail(note);\n console.warn(`check-prerender: warning: ${note}`);\n }\n if (outline.length) {\n const note =\n `pages whose outline has not exactly one <h1>:\\n${outline.join('\\n')}\\n\\n` +\n 'Give each page one: set a `vitops_hero` (or section) heading level to h1, or let the ' +\n 'layout render the title, and make other display headings h2 or p.';\n if (strict) fail(note);\n console.warn(`check-prerender: warning: ${note}`);\n }\n console.log(`check-prerender: ${count} prerendered page(s) look like real pages.`);\n}\n\n/**\n * `vitops emdash form-template --from <seed.json|export.json> [--name <key>] [--site <site.json>]`:\n * copy a `vitops_form` block's fields into `site.templates`, so the generated privacy policy and\n * `check-prerender` know what the form collects. Hand-written entries are easy to get wrong (a\n * field type the policy's schema does not have fails with only a path); this uses the renderer's\n * own resolver, so the names are the ones the form submits.\n */\nasync function cmdFormTemplate(argv: string[]) {\n const flag = (name: string) => {\n const i = argv.indexOf(`--${name}`);\n if (i >= 0) return argv[i + 1];\n return argv.find((a) => a.startsWith(`--${name}=`))?.slice(name.length + 3);\n };\n const from = flag('from') ?? (existsSync('seed/seed.json') ? 'seed/seed.json' : undefined);\n const siteFile = flag('site') ?? 'site.json';\n const dry = argv.includes('--dry');\n if (!from) fail('give --from <seed.json or content export containing a vitops_form block>.');\n if (!existsSync(from!)) fail(`${from} does not exist.`);\n if (!existsSync(siteFile)) fail(`${siteFile} does not exist (use --site <path>).`);\n\n const blocks = findFormBlocks(JSON.parse(readFileSync(from!, 'utf8')));\n if (blocks.length === 0) fail(`no vitops_form block found in ${from}.`);\n const requested = flag('name');\n if (blocks.length > 1 && !requested)\n fail(\n `${blocks.length} vitops_form blocks in ${from}; this writes one entry. Name it with --name <key> ` +\n 'and narrow --from to the one you mean (a content export of a single entry).',\n );\n const name = requested ?? 'contact';\n\n let emdash: { formTemplateFromBlock: (b: unknown) => unknown };\n try {\n const resolved = createRequire(join(process.cwd(), 'noop.js')).resolve('@getvitops/emdash');\n emdash = await import(pathToFileURL(resolved).href);\n } catch {\n fail('@getvitops/emdash is not installed in this project (run from the site root).');\n }\n const template = emdash!.formTemplateFromBlock(blocks[0]);\n const { config, changed } = withTemplate(\n JSON.parse(readFileSync(siteFile, 'utf8')),\n name,\n template,\n );\n const check = validateConfig(config);\n if (!check.ok)\n fail(\n `the template would make ${siteFile} invalid:\\n` +\n check.errors.map((i) => ` ${(i.path ?? []).join('.')}: ${i.message}`).join('\\n'),\n );\n if (!changed) return console.log(`form-template: site.templates.${name} is already up to date.`);\n if (dry) return console.log(JSON.stringify({ [name]: template }, null, 2));\n writeFileSync(siteFile, `${JSON.stringify(config, null, 2)}\\n`);\n console.log(\n `form-template: wrote site.templates.${name} to ${siteFile}. Run your formatter on it.`,\n );\n}\n\n/**\n * `astro build` and then `check-prerender`, failing on any block type EmDash reported as having\n * no component. That report is a build-log warning, not an exit code, so the build's output is\n * streamed through unchanged and scanned afterwards.\n */\nasync function cmdBuild(argv: string[]) {\n const bin = existsSync('node_modules/.bin/astro') ? 'node_modules/.bin/astro' : 'astro';\n let log = '';\n const code = await new Promise<number>((resolve) => {\n const child = spawn(bin, ['build'], { stdio: ['inherit', 'pipe', 'pipe'] });\n child.stdout.on('data', (d) => {\n log += d;\n process.stdout.write(d);\n });\n child.stderr.on('data', (d) => {\n log += d;\n process.stderr.write(d);\n });\n child.on('error', () => resolve(1));\n child.on('close', (c) => resolve(c ?? 1));\n });\n if (code !== 0) process.exit(code);\n const missing = missingBlockComponents(log);\n if (missing.length)\n fail(\n `the build rendered nothing for these block types (no component registered):\\n` +\n missing.map((t) => ` ${t}`).join('\\n') +\n '\\n\\nEvery page using them was published without that content. If they are `vitops_*` ' +\n 'blocks, the plugin block map was empty for this build: check that no module imported by ' +\n '`@getvitops/emdash/astro` imports `emdash/ui` statically (an import cycle), and that the ' +\n 'plugin is registered in astro.config.mjs.',\n );\n cmdCheckPrerender(argv);\n}\n\nexport async function cmdEmdash(argv: string[]) {\n const [sub, ...rest] = argv;\n switch (sub) {\n case 'prepare-db':\n return cmdPrepareDb();\n case 'check-prerender':\n return cmdCheckPrerender(rest);\n case 'build':\n return cmdBuild(rest);\n case 'form-template':\n return cmdFormTemplate(rest);\n case undefined:\n case '-h':\n case '--help':\n console.log(EMDASH_HELP);\n return;\n default:\n fail(\n `unknown \"emdash\" subcommand \"${sub}\" (expected: prepare-db | check-prerender | build | form-template). Try: vitops --help`,\n );\n }\n}\n","/**\n * `vitops setup` — the scripted path from an empty scaffold to a live site.\n * Subcommands: `repo | hosting | domain | search | ads | all`.\n *\n * Absorbs what each template's `scripts/init-{github,hosting}.mjs` used to do\n * template-locally, so the capability reaches every consumer, not just a fresh\n * `vp create` scaffold. `repo`/`hosting` follow the pure-planner/executor split in\n * `@getvitops/utils/hosting` — see that module's docblock. `search`/`ads` are not\n * reimplemented: this shells out to the CLI's own `search setup`/`ads setup`\n * subcommands as child processes, so their behaviour (prompting, `--dry`,\n * `--check`, writing the config patch back) stays exactly what it already is,\n * with no risk of the two copies drifting.\n *\n * `domain` only ever plans and checks the workers.dev-vs-custom-domain\n * consistency — it never touches `wrangler.jsonc` or deploys anything. Cutover\n * overwrites the zone's DNS record and can take an existing live site down; that\n * is a human's call, made in `SETUP.html`, not a step this command applies.\n */\nimport { execFileSync } from 'node:child_process';\nimport { existsSync, readFileSync, writeFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport {\n adminCollaboratorIds,\n branches as ghBranches,\n createD1,\n createDevEnvironment,\n createProdEnvironment,\n createR2Bucket,\n createRepo,\n defaultBranch as ghDefaultBranch,\n environments as ghEnvironments,\n findD1,\n findR2Bucket,\n formatPlan,\n formatSummary,\n ghAuthed,\n mainProtected as ghMainProtected,\n originRemote,\n plan as planHosting,\n planDomainConsistency,\n protectMain,\n pushBranches,\n repoNwo as ghRepoNwo,\n secretsPresent,\n setDefaultBranch,\n setSecret,\n wranglerWhoami,\n type DomainState,\n type HostingResult,\n type HostingSetup,\n type HostingState,\n} from '@getvitops/utils/hosting';\nimport {\n createWidget,\n deriveDomains,\n formatPlan as formatTurnstilePlan,\n formatSummary as formatTurnstileSummary,\n hasDrift as hasTurnstileDrift,\n listWidgets,\n patchSiteKey,\n plan as planTurnstile,\n resolveAccountId,\n type TurnstileResult,\n} from '@getvitops/utils/turnstile';\nimport { parse, msg } from '../args.ts';\nimport { CLOUDFLARE_TOKEN_HINT, loadCloudflareToken } from '../credentials.ts';\nimport { fail, loadConfig, SITE_OPTS } from './shared.ts';\n\nconst SETUP_HELP = `vitops setup <sub> [options]\n\n vitops setup repo [--dry] [--check] [--public]\n vitops setup hosting [--dry] [--check] [--platform cloudflare|node]\n vitops setup domain [--dry] [--check] [--domain <apex>]\n vitops setup turnstile [--dry] [--check] [--no-write] [--no-secret]\n vitops setup search [options] (forwards to \\`vitops search setup\\`)\n vitops setup ads [options] (forwards to \\`vitops ads setup\\`)\n vitops setup all [--dry] [--check] [--public] [--platform <p>]\n\n--dry (or --dry-run) prints the plan and stops. --check reports drift/blocked steps and exits\nnon-zero, mutating nothing. Neither creates the Cloudflare API token or performs\na custom-domain cutover — both are named as manual steps; see SETUP.html.`;\n\nfunction projectName(): string {\n const path = resolve('package.json');\n if (!existsSync(path)) fail('package.json not found — run this from the project root');\n const pkg = JSON.parse(readFileSync(path, 'utf8')) as { name?: string };\n if (!pkg.name) fail('package.json has no \"name\" field');\n return pkg.name!.replace(/^@[^/]+\\//, '');\n}\n\nasync function gatherHostingState(\n name: string,\n platform: 'cloudflare' | 'node',\n): Promise<HostingState> {\n const [wrangler, gh, origin] = await Promise.all([\n platform === 'cloudflare' ? wranglerWhoami() : Promise.resolve({ authed: false } as const),\n ghAuthed(),\n originRemote(),\n ]);\n const nwo = origin ? await ghRepoNwo() : undefined;\n const [branchesList, def, protectedMain, envs, secrets, d1Id, r2] = await Promise.all([\n ghBranches(),\n nwo ? ghDefaultBranch(nwo) : Promise.resolve(undefined),\n nwo ? ghMainProtected(nwo) : Promise.resolve(false),\n nwo ? ghEnvironments(nwo) : Promise.resolve([]),\n nwo ? secretsPresent(nwo) : Promise.resolve([]),\n platform === 'cloudflare' ? findD1(`${name}-dev`) : Promise.resolve(undefined),\n platform === 'cloudflare' ? findR2Bucket(`${name}-media-dev`) : Promise.resolve(false),\n ]);\n return {\n wranglerAuthed: wrangler.authed,\n ...('accountId' in wrangler && wrangler.accountId ? { accountId: wrangler.accountId } : {}),\n d1Present: Boolean(d1Id),\n ...(d1Id ? { d1Id } : {}),\n r2Present: r2,\n ghAuthed: gh,\n ...(origin ? { originRemote: origin } : {}),\n ...(nwo ? { repoNwo: nwo } : {}),\n branches: branchesList,\n ...(def ? { defaultBranch: def } : {}),\n mainProtected: protectedMain,\n environments: envs,\n secretsPresent: secrets,\n };\n}\n\nasync function cmdSetupRepo(argv: string[]) {\n const a = parse(argv, {\n dry: { type: 'boolean', default: false },\n check: { type: 'boolean', default: false },\n public: { type: 'boolean', default: false },\n platform: { type: 'string', default: 'cloudflare' },\n 'no-dev-resources': { type: 'boolean', default: false },\n });\n const platform = a.str('platform');\n if (platform !== 'cloudflare' && platform !== 'node')\n fail(`--platform \"${platform}\" must be cloudflare or node`);\n const name = projectName();\n const setup: HostingSetup = {\n platform,\n projectName: name,\n repoVisibility: a.flag('public') ? 'public' : 'private',\n devResources: !a.flag('no-dev-resources'),\n };\n const state = await gatherHostingState(name, setup.platform);\n const p = planHosting(setup, state);\n const dry = a.flag('dry');\n const check = a.flag('check');\n\n if (dry || check) {\n console.log(formatPlan(p));\n if (\n check &&\n (p.repo.action !== 'skip' ||\n p.branches.action !== 'skip' ||\n p.defaultBranch.action !== 'skip')\n ) {\n console.error('\\n✖ repo/branch setup is not complete');\n process.exit(1);\n }\n return;\n }\n\n const results: HostingResult[] = [];\n if (p.repo.action === 'create') {\n const r = await createRepo(name, setup.repoVisibility);\n results.push({\n step: 'repo',\n status: r.ok ? 'created' : 'failed',\n detail: r.ok ? name : (r.message ?? 'failed'),\n });\n if (r.ok) {\n const push = await pushBranches();\n results.push({\n step: 'branches',\n status: push.ok ? 'created' : 'failed',\n detail: push.ok ? 'main + dev pushed' : (push.message ?? 'failed'),\n });\n }\n } else {\n results.push({ step: 'repo', status: 'present', detail: p.repo.detail });\n }\n\n const nwo = state.repoNwo ?? (await ghRepoNwo());\n if (nwo) {\n if (p.defaultBranch.action !== 'skip') {\n const r = await setDefaultBranch(nwo, 'dev');\n results.push({\n step: 'default branch',\n status: r.ok ? 'updated' : 'failed',\n detail: r.ok ? 'dev' : (r.message ?? 'failed'),\n });\n } else results.push({ step: 'default branch', status: 'present', detail: 'dev' });\n\n if (p.mainProtection.action === 'blocked') {\n const r = await protectMain(nwo);\n results.push({\n step: 'main protection',\n status: r.ok ? 'created' : 'blocked',\n detail: r.ok ? 'protected' : (r.message ?? p.mainProtection.detail),\n });\n } else\n results.push({ step: 'main protection', status: 'present', detail: p.mainProtection.detail });\n\n if (p.prodEnvironment.action === 'blocked') {\n const ids = await adminCollaboratorIds(nwo);\n const r = await createProdEnvironment(nwo, ids);\n results.push({\n step: 'production env',\n status: r.ok ? 'created' : 'blocked',\n detail: r.ok ? `${ids.length} reviewer(s)` : (r.message ?? p.prodEnvironment.detail),\n });\n } else\n results.push({ step: 'production env', status: 'present', detail: p.prodEnvironment.detail });\n\n if (p.devEnvironment.action === 'create') {\n const r = await createDevEnvironment(nwo);\n results.push({\n step: 'dev env',\n status: r.ok ? 'created' : 'failed',\n detail: r.ok ? 'no required reviewers' : (r.message ?? 'failed'),\n });\n } else results.push({ step: 'dev env', status: 'present', detail: p.devEnvironment.detail });\n } else {\n results.push({\n step: 'branch policy',\n status: 'blocked',\n detail: 'no GitHub repo yet — run this again once \"repo\" succeeds',\n });\n }\n\n console.log(formatSummary(results));\n if (results.some((r) => r.status === 'failed')) process.exit(1);\n}\n\nasync function cmdSetupHosting(argv: string[]) {\n const a = parse(argv, {\n dry: { type: 'boolean', default: false },\n check: { type: 'boolean', default: false },\n platform: { type: 'string', default: 'cloudflare' },\n 'no-dev-resources': { type: 'boolean', default: false },\n });\n const platform = a.str('platform');\n if (platform !== 'cloudflare' && platform !== 'node')\n fail(`--platform \"${platform}\" must be cloudflare or node`);\n const name = projectName();\n // --no-dev-resources: a pure-static Workers Assets project (no D1/R2 at all)\n // still needs the account id + eventual token, but has nothing to provision.\n const setup: HostingSetup = {\n platform,\n projectName: name,\n repoVisibility: 'private',\n devResources: !a.flag('no-dev-resources'),\n };\n const state = await gatherHostingState(name, platform);\n const p = planHosting(setup, state);\n const dry = a.flag('dry');\n const check = a.flag('check');\n\n if (platform === 'node') {\n console.log(\n 'Hosting target \"node\" has no cloud resources to provision — see the template README\\'s \"Hosting\" section.',\n );\n return;\n }\n if (!state.wranglerAuthed && !dry)\n fail('wrangler is not logged in — run `npx wrangler login` first');\n\n if (dry || check) {\n console.log(formatPlan(p));\n if (check && (p.d1.action !== 'skip' || p.r2.action !== 'skip')) {\n console.error('\\n✖ hosting resources are not fully provisioned');\n process.exit(1);\n }\n return;\n }\n\n const results: HostingResult[] = [];\n let d1Id = state.d1Id;\n if (p.d1.action === 'create') {\n const r = await createD1(`${name}-dev`);\n results.push({\n step: 'dev D1',\n status: r.ok ? 'created' : 'failed',\n detail: r.ok ? (r.id ?? '') : (r.message ?? 'failed'),\n });\n if (r.ok) d1Id = r.id;\n } else results.push({ step: 'dev D1', status: 'present', detail: p.d1.detail });\n\n if (p.r2.action === 'create') {\n const r = await createR2Bucket(`${name}-media-dev`);\n results.push({\n step: 'dev R2',\n status: r.ok ? 'created' : 'failed',\n detail: r.ok ? `${name}-media-dev` : (r.message ?? 'failed'),\n });\n } else results.push({ step: 'dev R2', status: 'present', detail: p.r2.detail });\n\n const nwo = state.repoNwo ?? (await ghRepoNwo());\n if (nwo && state.ghAuthed) {\n if (d1Id && !state.secretsPresent.includes('DEV_D1_ID')) {\n const r = await setSecret('DEV_D1_ID', d1Id);\n results.push({\n step: 'secret DEV_D1_ID',\n status: r.ok ? 'created' : 'failed',\n detail: r.ok ? 'set' : (r.message ?? 'failed'),\n });\n } else\n results.push({\n step: 'secret DEV_D1_ID',\n status: state.secretsPresent.includes('DEV_D1_ID') ? 'present' : 'blocked',\n detail: d1Id ?? 'D1 id unknown',\n });\n\n if (state.accountId && !state.secretsPresent.includes('CLOUDFLARE_ACCOUNT_ID')) {\n const r = await setSecret('CLOUDFLARE_ACCOUNT_ID', state.accountId);\n results.push({\n step: 'secret CLOUDFLARE_ACCOUNT_ID',\n status: r.ok ? 'created' : 'failed',\n detail: r.ok ? 'set' : (r.message ?? 'failed'),\n });\n } else\n results.push({\n step: 'secret CLOUDFLARE_ACCOUNT_ID',\n status: state.secretsPresent.includes('CLOUDFLARE_ACCOUNT_ID') ? 'present' : 'blocked',\n detail: state.accountId ?? 'unknown',\n });\n } else {\n results.push({\n step: 'secrets',\n status: 'blocked',\n detail: 'no GitHub repo wired up yet — run `vitops setup repo` first',\n });\n }\n\n results.push({\n step: 'secret CLOUDFLARE_API_TOKEN',\n status: 'blocked',\n detail: p.secrets['CLOUDFLARE_API_TOKEN']?.detail ?? 'create it by hand',\n });\n\n console.log(formatSummary(results));\n for (const n of p.notes) console.log(`! ${n}`);\n if (results.some((r) => r.status === 'failed')) process.exit(1);\n}\n\nasync function cmdSetupDomain(argv: string[]) {\n const a = parse(argv, {\n ...SITE_OPTS,\n dry: { type: 'boolean', default: false },\n check: { type: 'boolean', default: false },\n domain: { type: 'string' },\n });\n const wranglerPath = resolve('wrangler.jsonc');\n if (!existsSync(wranglerPath)) fail('wrangler.jsonc not found — run this from the project root');\n const raw = readFileSync(wranglerPath, 'utf8');\n const customDomainMatch = /^\\s*\"routes\":\\s*\\[\\s*\\{\\s*\"pattern\":\\s*\"([^\"]+)\"/m.exec(\n raw\n .split('\\n')\n .filter((l) => !l.trim().startsWith('//'))\n .join('\\n'),\n );\n const customDomain = customDomainMatch?.[1] ?? (a.opt('domain') ? undefined : undefined);\n\n const cfg = await loadConfig(a.path('input'), a.str('site-env'));\n const env = cfg.site.environments[a.str('site-env')] ?? cfg.site.environments['production'];\n\n const state: DomainState = {\n wranglerDev: !raw.includes('\"routes\"') || Boolean(customDomainMatch) === false,\n ...(customDomain ? { customDomain } : {}),\n ...(env?.url ? { configuredUrl: env.url } : {}),\n ...(cfg.site.domains?.canonical ? { canonical: cfg.site.domains.canonical } : {}),\n ...(env?.robots ? { robots: env.robots } : {}),\n };\n\n const step = planDomainConsistency(state);\n console.log(`${step.action === 'skip' ? '·' : '?'} domain consistency ${step.detail}`);\n if (a.opt('domain'))\n console.log(\n `\\nCutover is never applied by this command — see SETUP.html stage 4b. To cut over: uncomment ` +\n `routes in wrangler.jsonc with \"pattern\": \"${a.opt('domain')}\", deploy, then update ` +\n `site.domains.canonical and site.environments.${a.str('site-env')}.url to match.`,\n );\n if (a.flag('check') && step.action !== 'skip') process.exit(1);\n}\n\n/**\n * Create the Cloudflare Turnstile widget for the site and record its PUBLIC site key.\n *\n * The secret is returned by Cloudflare only at creation. It is never written to a config\n * file: it is piped to `wrangler secret put VITOPS_TURNSTILE_SECRET` on stdin (never argv,\n * never logged). If that fails, it is shown once on an interactive terminal and otherwise\n * not at all, with the rotate instruction. Never edits, rotates or deletes a widget.\n */\nasync function cmdSetupTurnstile(argv: string[]) {\n const a = parse(argv, {\n ...SITE_OPTS,\n dry: { type: 'boolean', default: false },\n check: { type: 'boolean', default: false },\n 'no-write': { type: 'boolean', default: false },\n 'no-secret': { type: 'boolean', default: false },\n });\n const input = a.path('input');\n const cfg = await loadConfig(input, a.str('site-env'));\n const dry = a.flag('dry');\n const check = a.flag('check');\n const token = loadCloudflareToken();\n // Like `ads setup`: a dry run mutates nothing, so it must not demand the credential.\n if (!token && !dry)\n fail(\n `${CLOUDFLARE_TOKEN_HINT}\\n For this command the token needs \"Account > Turnstile > Edit\" ` +\n `(also set CLOUDFLARE_ACCOUNT_ID, or the token must be able to list accounts).`,\n );\n\n const setup = {\n name: (() => {\n // `organization.name` may be a per-locale map; any one value names the widget.\n const n = cfg.organization?.name;\n return (typeof n === 'string' ? n : n ? Object.values(n)[0] : undefined) ?? projectName();\n })(),\n domains: deriveDomains(cfg.site),\n siteKey: cfg.site.security?.turnstile?.siteKey,\n };\n let accountId: string | undefined;\n let widgets: Awaited<ReturnType<typeof listWidgets>>['widgets'] = [];\n if (token) {\n const acct = await resolveAccountId(token, process.env.CLOUDFLARE_ACCOUNT_ID || undefined);\n if (!acct.ok) {\n if (!dry) fail(`Cloudflare account: ${acct.message}`);\n } else {\n accountId = acct.accountId;\n const listed = await listWidgets(token, accountId!);\n if (!listed.ok) fail(`listing Turnstile widgets: ${listed.message}`);\n widgets = listed.widgets;\n }\n }\n const p = planTurnstile(setup, { accountId, widgets });\n\n if (dry || check) {\n console.log(formatTurnstilePlan(p));\n if (check && hasTurnstileDrift(p)) {\n console.error('\\n✖ Turnstile is not set up');\n process.exit(1);\n }\n return;\n }\n\n const results: TurnstileResult[] = [];\n let siteKey = p.adopt?.sitekey;\n if (p.widget.action === 'blocked') {\n results.push({ step: 'widget', status: 'blocked', detail: p.widget.detail });\n } else if (p.widget.action === 'create') {\n const r = await createWidget(token!, accountId!, { name: setup.name, domains: p.domains });\n if (!r.ok) results.push({ step: 'widget', status: 'failed', detail: r.message ?? 'failed' });\n else {\n siteKey = r.sitekey;\n results.push({ step: 'widget', status: 'created', detail: r.sitekey! });\n if (a.flag('no-secret')) {\n console.error(\n '! --no-secret: the secret was NOT saved and cannot be read back. Rotate it in the dashboard, then `wrangler secret put VITOPS_TURNSTILE_SECRET`.',\n );\n } else {\n try {\n execFileSync('npx', ['wrangler', 'secret', 'put', 'VITOPS_TURNSTILE_SECRET'], {\n input: r.secret!,\n stdio: ['pipe', 'inherit', 'inherit'],\n });\n results.push({\n step: 'secret',\n status: 'created',\n detail: 'VITOPS_TURNSTILE_SECRET (Worker secret)',\n });\n } catch {\n results.push({ step: 'secret', status: 'failed', detail: 'wrangler secret put failed' });\n if (process.stdout.isTTY) {\n console.error(\n `! Save this now, it is shown once: VITOPS_TURNSTILE_SECRET=${r.secret}\\n Then: npx wrangler secret put VITOPS_TURNSTILE_SECRET`,\n );\n } else {\n console.error(\n '! The secret was not saved and is not printed (non-interactive). Rotate it in the dashboard, then `wrangler secret put VITOPS_TURNSTILE_SECRET`.',\n );\n }\n }\n }\n }\n } else results.push({ step: 'widget', status: 'present', detail: p.widget.detail });\n\n if (siteKey && p.config.action === 'create' && !a.flag('no-write')) {\n if (!input.endsWith('.json'))\n results.push({\n step: 'site.json',\n status: 'blocked',\n detail: `${input} is a module — add site.security.turnstile.siteKey: \"${siteKey}\" yourself`,\n });\n else {\n const source = readFileSync(input, 'utf8');\n const indent = /\\n([ \\t]+)\"/.exec(source)?.[1];\n writeFileSync(\n input,\n JSON.stringify(\n patchSiteKey(JSON.parse(source), siteKey),\n null,\n indent?.includes('\\t') ? '\\t' : (indent?.length ?? 2),\n ) + (source.endsWith('\\n') ? '\\n' : ''),\n );\n results.push({\n step: 'site.json',\n status: 'created',\n detail: 'site.security.turnstile.siteKey',\n });\n }\n } else if (p.config.action === 'skip')\n results.push({ step: 'site.json', status: 'present', detail: p.config.detail });\n\n console.log(`\\n${formatTurnstileSummary(results)}`);\n for (const r of p.reminders) console.log(` ${r}`);\n if (results.some((r) => r.status === 'failed' || r.status === 'blocked')) process.exit(1);\n}\n\n/** Forward to the CLI's own `search setup` / `ads setup` as a child process — see\n * this module's docblock for why (their behaviour must not be re-derived). */\nfunction forward(sub: 'search' | 'ads', argv: string[]): boolean {\n try {\n execFileSync(process.execPath, [process.argv[1]!, sub, 'setup', ...argv], { stdio: 'inherit' });\n return true;\n } catch (err) {\n const e = err as { status?: number };\n if (e.status != null) return false;\n fail(msg(err));\n }\n}\n\nexport async function cmdSetup(argv: string[]): Promise<void> {\n // `--dry-run` is the spelling most people reach for; the flag is `--dry` everywhere else.\n const [sub, ...rest] = argv.map((a) => (a === '--dry-run' ? '--dry' : a));\n switch (sub) {\n case 'repo':\n return cmdSetupRepo(rest);\n case 'hosting':\n return cmdSetupHosting(rest);\n case 'domain':\n return cmdSetupDomain(rest);\n case 'turnstile':\n return cmdSetupTurnstile(rest);\n case 'search':\n if (!forward('search', rest)) process.exit(1);\n return;\n case 'ads':\n if (!forward('ads', rest)) process.exit(1);\n return;\n case 'all': {\n const a = parse(rest, {\n dry: { type: 'boolean', default: false },\n check: { type: 'boolean', default: false },\n public: { type: 'boolean', default: false },\n platform: { type: 'string', default: 'cloudflare' },\n 'no-dev-resources': { type: 'boolean', default: false },\n });\n const dryCheckFlags = [\n ...(a.flag('dry') ? ['--dry'] : []),\n ...(a.flag('check') ? ['--check'] : []),\n ];\n const hostingFlags = [\n ...dryCheckFlags,\n '--platform',\n a.str('platform'),\n ...(a.flag('no-dev-resources') ? ['--no-dev-resources'] : []),\n ];\n console.log('── repo ──');\n await cmdSetupRepo([...hostingFlags, ...(a.flag('public') ? ['--public'] : [])]);\n console.log('\\n── hosting ──');\n await cmdSetupHosting(hostingFlags);\n console.log('\\n── search ──');\n const searchOk = forward('search', dryCheckFlags);\n console.log('\\n── ads ──');\n const adsOk = forward('ads', dryCheckFlags);\n if (!searchOk || !adsOk) process.exit(1);\n return;\n }\n case undefined:\n case '-h':\n case '--help':\n console.log(SETUP_HELP);\n return;\n default:\n fail(\n `unknown \"setup\" subcommand \"${sub}\" (expected: repo | hosting | domain | turnstile | search | ads | all). Try: vitops --help`,\n );\n }\n}\n","/**\n * Side-effect-free helpers for the `vitops agents` / `vitops docs` commands.\n * Kept out of cli.ts (which runs main() on import) so they are unit-testable.\n */\nimport {\n existsSync,\n lstatSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n readlinkSync,\n rmSync,\n symlinkSync,\n} from 'node:fs';\nimport { dirname, join, parse, relative, resolve } from 'node:path';\n\nexport const SKILL_NAME = 'vitops-design-system';\n\n/** Marker the 0.5.0-era generated skill carried; lets us migrate that layout. */\nexport const GENERATED_MARKER = '<!-- GENERATED by @getvitops/cli';\n\n/**\n * `vitops docs` topics → docs-bundle paths (as returned by `generateDocs`).\n * Order is the `--all` concatenation order.\n */\nexport const TOPICS: Record<string, { path: string; summary: string; external?: 'emdash' }> = {\n classes: {\n path: 'css/classes.md',\n summary: 'CSS framework class vocabulary (colour, type, space, layout, animation, patterns)',\n },\n authoring: {\n path: 'authoring.md',\n summary: 'every design-system.json field, rendered from the JSON Schema',\n },\n config: {\n path: 'config.md',\n summary:\n 'every field of the three-section config (designSystem / organization / site), rendered from the JSON Schema',\n },\n formats: {\n path: 'formats.md',\n summary: \"tailwind vs css vs bricks: what's emitted vs what the platform provides\",\n },\n color: {\n path: 'concepts/color.md',\n summary: 'seeded OKLCH scales, functional tokens, automatic dark mode',\n },\n scales: {\n path: 'concepts/scales.md',\n summary: 'fluid modular type & space scales',\n },\n icons: {\n path: 'concepts/icons.md',\n summary: 'semantic icon names across sets, bundle derivation, sprite delivery',\n },\n patterns: {\n path: 'concepts/patterns.md',\n summary: 'pattern CSS: token cascade, override hooks, states, role variants',\n },\n components: {\n path: 'concepts/components.md',\n summary: 'which of the four tiers provides each pattern, and the call to make',\n },\n consent: {\n path: 'concepts/consent.md',\n summary: 'the consent gate: inert gated tags, demand-driven prompting, three-valued choices',\n },\n tracking: {\n path: 'concepts/tracking.md',\n summary: 'ad-click attribution, the `_ac` cookie, conversion notifications',\n },\n search: {\n path: 'concepts/search.md',\n summary: 'what search engines accept, Search Console onboarding, deploy notification',\n },\n legal: {\n path: 'concepts/legal.md',\n summary: 'privacy policy / terms / cookie notice derived from config facts',\n },\n blocks: {\n path: '',\n external: 'emdash',\n summary: 'EmDash Portable Text blocks and every field (needs @getvitops/emdash installed)',\n },\n elements: {\n path: 'bricks/elements.md',\n summary: 'Bricks Builder custom elements, controls, defaults',\n },\n};\n\n/**\n * Find the installed package's skill directory by walking up from `cwd` looking\n * for `node_modules/@getvitops/cli/skill/SKILL.md`. Returns the LOGICAL path\n * (through the `node_modules/@getvitops/cli` link, unresolved) or null.\n *\n * Deliberately not derived from import.meta.url: under pnpm the running module's\n * URL is the realpath in `node_modules/.pnpm/@getvitops+cli@<version>/…`, which\n * changes on every version bump — a symlink to it would dangle after upgrades.\n * The logical path is recreated by the package manager on every install, so a\n * link through it always resolves to the currently installed version.\n */\nexport function findSkillTarget(cwd: string): string | null {\n let dir = resolve(cwd);\n const { root } = parse(dir);\n for (;;) {\n const skillDir = join(dir, 'node_modules', '@getvitops', 'cli', 'skill');\n if (existsSync(join(skillDir, 'SKILL.md'))) return skillDir;\n if (dir === root) return null;\n dir = dirname(dir);\n }\n}\n\n/**\n * Idempotently symlink `linkPath` → `targetDir` (relative target). Replaces a\n * stale symlink; migrates a real directory left by the old emitting `vitops\n * agents` (identified by GENERATED_MARKER in its SKILL.md); refuses to clobber\n * anything else. Returns a warning string, or null on success / no-op.\n */\nexport function linkSkill(linkPath: string, targetDir: string): string | null {\n const target = relative(dirname(linkPath), resolve(targetDir));\n try {\n const st = lstatSync(linkPath, { throwIfNoEntry: false });\n if (st && !st.isSymbolicLink()) {\n const oldSkill = join(linkPath, 'SKILL.md');\n const isGenerated =\n st.isDirectory() &&\n existsSync(oldSkill) &&\n readFileSync(oldSkill, 'utf8').includes(GENERATED_MARKER);\n if (!isGenerated)\n return `left ${linkPath} alone (exists and is not a symlink) — link it to ${targetDir} yourself if wanted`;\n rmSync(linkPath, { recursive: true }); // old generated skill layout → replace\n } else if (st) {\n if (readlinkSync(linkPath) === target) return null;\n rmSync(linkPath);\n } else {\n mkdirSync(dirname(linkPath), { recursive: true });\n }\n symlinkSync(target, linkPath);\n return null;\n } catch (err) {\n return `could not symlink ${linkPath}: ${(err as Error).message}`;\n }\n}\n\nexport const AGENTS_START = '<!-- vitops:start -->';\nexport const AGENTS_END = '<!-- vitops:end -->';\n\n/** Whether the project depends on EmDash: the `emdash-*` skills only make sense there. */\nexport function isEmdashSite(cwd: string): boolean {\n try {\n const pkg = JSON.parse(readFileSync(resolve(cwd, 'package.json'), 'utf8'));\n return Boolean({ ...pkg.dependencies, ...pkg.devDependencies }['emdash']);\n } catch {\n return false;\n }\n}\n\n/** Skill names carry their scope: `emdash-*` needs an EmDash site, `vitops-*` and the rest apply anywhere. */\nexport const appliesTo = (skill: string, emdash: boolean) => emdash || !skill.startsWith('emdash-');\n\n/** A skill the block links to. */\nexport interface BlockSkill {\n name: string;\n /** Path from the project root, as the link target. */\n path: string;\n /** One short line: when to load it. */\n summary: string;\n}\n\n/** A frontmatter `description`, whether a plain line or a folded (`>-`) scalar. */\nfunction descriptionOf(skillMd: string): string {\n const fm = /^---\\n([\\s\\S]*?)\\n---/.exec(skillMd)?.[1] ?? '';\n const m = /^description:[ \\t]*(.*)((?:\\n[ \\t]+.*)*)/m.exec(fm);\n if (!m) return '';\n const first = m[1]!.trim();\n const folded = /^[>|][-+]?$/.test(first);\n return [folded ? '' : first, ...m[2]!.split('\\n').map((l) => l.trim())].filter(Boolean).join(' ');\n}\n\n/**\n * One short line saying when to load a skill: its \"Use when/for/before …\" sentence if it has one\n * (that is the part that answers \"is this the one?\"), else its first sentence.\n */\nexport function skillSummary(skillMd: string, max = 120): string {\n const description = descriptionOf(skillMd);\n const use = /\\bUse (?:when|for|before|ONLY)\\b[^.]*/.exec(description)?.[0];\n const line = use ?? description.split(/(?<=[.!?])\\s/)[0] ?? description;\n if (line.length <= max) return line;\n // Cut at the last clause boundary that fits rather than mid-word: the part that survives is what\n // tells an agent whether this is the skill, so it should still read as a whole phrase.\n const head = line.slice(0, max);\n const cut = Math.max(head.lastIndexOf(', '), head.lastIndexOf(' or '), head.lastIndexOf(' — '));\n let out = cut > max / 2 ? head.slice(0, cut) : head.slice(0, head.lastIndexOf(' '));\n // Never leave an opened parenthesis or a dangling comma behind.\n if ((out.match(/\\(/g) ?? []).length > (out.match(/\\)/g) ?? []).length)\n out = out.slice(0, out.lastIndexOf('('));\n return out.replace(/[\\s,;:—-]+$/, '');\n}\n\n/**\n * The skills that belong in the block: those under `.agents/skills` that are links into an installed\n * `@getvitops/*` package. A site's own skills and the EmDash ones are not ours to list, and the block\n * stays short.\n */\nexport function discoverBlockSkills(cwd: string): BlockSkill[] {\n const dir = resolve(cwd, '.agents', 'skills');\n if (!existsSync(dir)) return [];\n const emdash = isEmdashSite(cwd);\n const out: BlockSkill[] = [];\n for (const name of readdirSync(dir).sort()) {\n if (!appliesTo(name, emdash)) continue;\n const link = join(dir, name);\n const skillFile = join(link, 'SKILL.md');\n if (!lstatSync(link).isSymbolicLink() || !existsSync(skillFile)) continue;\n if (!readlinkSync(link).replace(/\\\\/g, '/').includes('node_modules/@getvitops/')) continue;\n out.push({\n name,\n path: `.agents/skills/${name}/SKILL.md`,\n summary: skillSummary(readFileSync(skillFile, 'utf8')),\n });\n }\n return out;\n}\n\n/**\n * Link every skill an installed `@getvitops/skills` ships into `.agents/skills` (and `.claude/skills`),\n * the same logical-path links `skills experimental_sync` makes, so `vitops agents sync` leaves a project\n * with the skills the block names even where that CLI has not run. Returns warnings.\n */\nexport function linkPackagedSkills(cwd: string): string[] {\n const warnings: string[] = [];\n const emdash = isEmdashSite(cwd);\n let dir = resolve(cwd);\n const { root } = parse(dir);\n for (;;) {\n const skills = join(dir, 'node_modules', '@getvitops', 'skills', 'skills');\n if (existsSync(skills)) {\n for (const name of readdirSync(skills)) {\n const target = join(skills, name);\n if (!existsSync(join(target, 'SKILL.md')) || !appliesTo(name, emdash)) continue;\n for (const base of ['.agents', '.claude']) {\n const warn = linkSkill(resolve(cwd, base, 'skills', name), target);\n if (warn) warnings.push(warn);\n }\n }\n return warnings;\n }\n if (dir === root) return warnings;\n dir = dirname(dir);\n }\n}\n\n/**\n * The managed block for the top of an `AGENTS.md`: what the project is built with, the one rule that\n * matters, and links to the skills that carry the detail. Deliberately short: depth lives in the\n * skills and in `vitops docs`, which agents load on demand rather than on every session.\n */\nexport function renderAgentsBlock(opts: { config: string; skills: BlockSkill[] }): string {\n const lines = [\n AGENTS_START,\n `Built with the Vitops design system (\\`@getvitops/*\\`); tokens and site facts live in \\`${opts.config}\\`.`,\n 'Prefer its classes and patterns over hand-written CSS. Load the skill that matches the task:',\n '',\n ...opts.skills.map((s) => `- [${s.name}](${s.path})${s.summary ? ` — ${s.summary}` : ''}`),\n ...(opts.skills.length ? [''] : []),\n 'Reference, rendered from this project: `vp exec vitops docs [topic]` (no topic lists them).',\n AGENTS_END,\n ];\n return lines.join('\\n');\n}\n\n/**\n * Put `block` into an AGENTS.md body. An existing fenced block is replaced in place and nothing else\n * is touched; with none, the block goes at the TOP, so it is the first thing an agent reads.\n */\nexport function upsertAgentsBlock(\n current: string | undefined,\n block: string,\n): { text: string; action: 'created' | 'updated' | 'added' } {\n if (current === undefined) return { text: `${block}\\n`, action: 'created' };\n const s = current.indexOf(AGENTS_START);\n const e = current.indexOf(AGENTS_END);\n if (s !== -1 && e !== -1 && e > s)\n return {\n text: current.slice(0, s) + block + current.slice(e + AGENTS_END.length),\n action: 'updated',\n };\n return { text: `${block}\\n\\n${current.replace(/^\\s+/, '')}`, action: 'added' };\n}\n","/**\n * A minimal reader for hand-written CSS: rule selector, declarations as authored,\n * and the at-rule conditions the rule sits inside.\n *\n * **Why not lightningcss.** The generator owns it, and exposing an `analyzeCss`\n * from there was the obvious move — but lightningcss parses declarations into a\n * typed value model, and the typed model is exactly what these rules need to see\n * through. `display: flex` comes back as `pair`, `grid-template-columns: 1fr 1fr`\n * as `track-list`, and `margin-inline: auto` as an empty structured node. Every\n * rule here matches on *authored text*, so recovering it property-by-property\n * would mean re-implementing a serialiser for each one.\n *\n * A brace-depth scanner keeps the text intact, needs no native binary in the\n * CLI's dependency tree, and follows what `generate.ts` already does twice\n * (`stripForTailwind`, `isVariantBlock`).\n *\n * It is deliberately shallow: no specificity, no cascade, no `@supports`\n * semantics. It answers \"what did someone type in this rule\", which is all a\n * reuse suggestion needs.\n */\n\nexport interface CssDeclaration {\n property: string;\n value: string;\n}\n\nexport interface CssRule {\n selector: string;\n declarations: CssDeclaration[];\n /** Enclosing at-rule preludes, outermost first (`@media (min-width: 48rem)`). */\n conditions: string[];\n /** 1-indexed line of the selector. */\n line: number;\n}\n\n/** Strip comments, preserving newlines so line numbers survive. */\nfunction stripComments(css: string): string {\n return css.replace(/\\/\\*[\\s\\S]*?\\*\\//g, (m) => m.replace(/[^\\n]/g, ''));\n}\n\nconst lineAt = (src: string, index: number) => src.slice(0, index).split('\\n').length;\n\n/**\n * Parse declarations from a rule body. Splits on top-level `;` only, so a\n * `var(--x, a; b)` or a data URI cannot end a declaration early.\n */\nfunction parseDeclarations(body: string): CssDeclaration[] {\n const out: CssDeclaration[] = [];\n let depth = 0;\n let start = 0;\n const push = (chunk: string) => {\n const colon = chunk.indexOf(':');\n if (colon < 0) return;\n const property = chunk.slice(0, colon).trim();\n const value = chunk.slice(colon + 1).trim();\n // A nested block leaks in as a property containing `{`; skip it rather than\n // reporting a declaration that was never written.\n if (!property || property.includes('{') || !value) return;\n out.push({ property, value });\n };\n for (let i = 0; i < body.length; i++) {\n const ch = body[i];\n if (ch === '(' || ch === '[') depth++;\n else if (ch === ')' || ch === ']') depth--;\n else if (ch === ';' && depth === 0) {\n push(body.slice(start, i));\n start = i + 1;\n }\n }\n push(body.slice(start));\n return out;\n}\n\n/**\n * Scan a stylesheet into its style rules.\n *\n * Never throws: this runs over consumer source that may be a fragment, and a\n * linter that dies on one unparseable file is one nobody keeps in CI.\n */\nexport function scanCss(css: string): CssRule[] {\n const src = stripComments(css);\n const rules: CssRule[] = [];\n // Each open brace pushes its prelude; at-rule preludes become `conditions` for\n // everything nested inside, style-rule preludes become a selector.\n const stack: { prelude: string; index: number }[] = [];\n let depth = 0;\n let preludeStart = 0;\n\n for (let i = 0; i < src.length; i++) {\n const ch = src[i];\n if (ch === '\"' || ch === \"'\") {\n // Skip strings whole — a brace inside content: \"{\" would desync the depth.\n const quote = ch;\n i++;\n while (i < src.length && src[i] !== quote) {\n if (src[i] === '\\\\') i++;\n i++;\n }\n continue;\n }\n if (ch === '{') {\n const raw = src.slice(preludeStart, i);\n const prelude = raw.trim();\n // Offset of the first non-whitespace character, not of `preludeStart` —\n // that sits where the previous rule ended, so a rule's reported line would\n // be the blank line above it.\n stack.push({ prelude, index: preludeStart + raw.indexOf(prelude.slice(0, 1)) });\n depth++;\n preludeStart = i + 1;\n continue;\n }\n if (ch === '}') {\n const frame = stack.pop();\n depth--;\n if (frame && !frame.prelude.startsWith('@')) {\n // A style rule: its body is everything since it opened, minus any nested\n // blocks, which `parseDeclarations` discards.\n const bodyStart = src.indexOf('{', frame.index) + 1;\n rules.push({\n selector: frame.prelude,\n declarations: parseDeclarations(src.slice(bodyStart, i)),\n conditions: stack.filter((f) => f.prelude.startsWith('@')).map((f) => f.prelude),\n line: lineAt(src, frame.index),\n });\n }\n preludeStart = i + 1;\n continue;\n }\n }\n // Unbalanced braces (a fragment, a truncated file) leave frames on the stack;\n // returning what was parsed beats reporting nothing.\n return rules.filter((r) => r.selector && depth >= 0);\n}\n\n/** `<style>` block contents from an .astro/.vue/.svelte/.html file. */\nexport function extractStyleBlocks(src: string): { css: string; line: number }[] {\n const out: { css: string; line: number }[] = [];\n for (const m of src.matchAll(/<style\\b[^>]*>([\\s\\S]*?)<\\/style>/gi)) {\n // Offset so a finding's line number points into the original file, not into\n // the extracted block.\n const line = lineAt(src, (m.index ?? 0) + m[0].indexOf('>') + 1);\n out.push({ css: m[1] ?? '', line });\n }\n return out;\n}\n","/**\n * The inverse of `lint.ts`: hand-written CSS that re-implements a framework\n * primitive.\n *\n * `lint.ts` catches a class that resolves to nothing — you named something and it\n * isn't there. This catches the costlier direction, where the code *works*: a\n * centred container written by hand, a two-column split behind a media query, a\n * flex row. Nothing is broken, so nothing ever surfaces it, and the design system\n * quietly stops being the place those decisions live.\n *\n * Every finding here is a `suggestion`, never an `error`. These are judgement\n * calls — a rule can be right about the pattern and wrong about the intent — and\n * a reuse hint that failed CI on the day it shipped would be a worse defect than\n * the drift it reports. `--strict` is there for consumers who want the ratchet.\n *\n * Rules stay conservative on purpose: each requires the *combination* that makes\n * the intent unambiguous. `margin-inline: auto` alone is not a centred track;\n * `display: flex` alone is not a cluster.\n */\nimport type { Format } from '@getvitops/generator';\nimport { type CssRule, extractStyleBlocks, scanCss } from './css-scan.ts';\nimport type { LintFinding } from './lint.ts';\n\n/**\n * The framework's own track widths. Declared in two places today\n * (`core/css/layout.css` and the tailwind emitter), so a rule keyed to the token\n * NAME rather than to a value survives either one moving.\n */\nconst TRACK_WIDTHS = ['--width-measure', '--width-breakout', '--width-spotlight'];\n\n/**\n * Class names that mean \"the thing that constrains page width\".\n *\n * The token-anchored rule below only fires once someone already knows about\n * `--width-measure`, which is precisely the author who was never going to\n * hand-roll a container. The reported failure was the other author: agents\n * inventing a `.wrap` from scratch, with a plain pixel `max-width`, on site after\n * site. `wrap` is also the more *available* name for the concept, so it wins the\n * naming race against `.centered` every time unless something says otherwise.\n *\n * Matched on the name only in combination with a width-constraining declaration —\n * a `.wrapper` that merely sets `display: flex` is not this pattern.\n */\nconst CONTAINER_NAMES = [\n 'wrap',\n 'wrapper',\n 'container',\n 'inner',\n 'outer',\n 'content-wrap',\n 'content-wrapper',\n 'page-wrap',\n 'page-wrapper',\n 'site-wrap',\n 'site-wrapper',\n 'shell',\n 'constrain',\n 'measure',\n];\n\n/**\n * Page scale, as a rem-equivalent, above which a width cap is a *container*\n * rather than an element.\n *\n * Keyed to the framework's own `md` breakpoint (48rem) rather than to a number\n * picked for this rule, and it is what keeps a capped figure quiet — someone\n * writing `max-width: 40rem` on an image is not reinventing `.centered`.\n */\nconst PAGE_SCALE_REM = 48;\n/** The same line in `ch`, where a width cap is a reading measure by construction. */\nconst PAGE_SCALE_CH = 40;\n\n/**\n * The largest length in a width value, as a rem-equivalent — so `min(100%,\n * 1200px)` and `clamp(20rem, 90vw, 75rem)` are read at their cap rather than\n * skipped for not being a bare length.\n *\n * Relative-to-viewport units (`%`, `vw`, `vmin`) are deliberately ignored: they\n * express \"as wide as there is\", which is not a container cap.\n */\nfunction widthScale(value: string): { rem: number; ch: number } {\n let rem = 0;\n let ch = 0;\n for (const m of value.matchAll(/([\\d.]+)(rem|px|em|ch)\\b/g)) {\n const n = Number(m[1]);\n if (!Number.isFinite(n)) continue;\n if (m[2] === 'ch') ch = Math.max(ch, n);\n else rem = Math.max(rem, m[2] === 'px' ? n / 16 : n);\n }\n return { rem, ch };\n}\n\n/**\n * Column ratios that correspond to a `split-<a>-<b>` class.\n *\n * `.split` is FLEX, not grid — `display: flex` with `flex: 1` children, and a\n * ratio class setting `--_split-a`/`--_split-b`. Equal columns are the base\n * `.split` with no ratio class, which is why `1fr 1fr` is deliberately absent:\n * suggesting `split-1-1` would name a class that does not exist, and this file\n * would then be committing the very defect `lint.ts` reports.\n */\nconst SPLITS: Record<string, string> = {\n '1fr 2fr': '1-2',\n '2fr 1fr': '2-1',\n '1fr 3fr': '1-3',\n '3fr 1fr': '3-1',\n '1fr 4fr': '1-4',\n '4fr 1fr': '4-1',\n '2fr 3fr': '2-3',\n '3fr 2fr': '3-2',\n};\n\nconst decl = (rule: CssRule, property: string) =>\n rule.declarations.find((d) => d.property === property)?.value;\n\n/** Is this rule inside a `min-width` media or container query? */\nconst inMinWidthQuery = (rule: CssRule) => rule.conditions.some((c) => /min-width\\s*:/.test(c));\n\n/** The framework breakpoint a `min-width` condition corresponds to, if any. */\nfunction breakpointOf(rule: CssRule): string | null {\n const rem = rule.conditions.map((c) => /min-width\\s*:\\s*([\\d.]+)rem/.exec(c)?.[1]).find(Boolean);\n if (!rem) return null;\n const n = Number(rem);\n // The framework's container breakpoints: sm/md/lg/xl = 30/48/64/80rem.\n return n >= 80 ? 'xl' : n >= 64 ? 'lg' : n >= 48 ? 'md' : n >= 30 ? 'sm' : null;\n}\n\n/** Spell a responsive utility the way the target format actually emits it. */\nconst responsive = (bp: string, base: string, format: Format) =>\n format === 'tailwind' ? `@${bp}:${base}` : `${bp}-${base}`;\n\ntype Verdict = Omit<LintFinding, 'file' | 'line'>;\n\n/**\n * One rule = one check. Kept as data rather than a chain of ifs so adding a\n * primitive is adding an entry, and so each carries its own explanation.\n */\nconst RULES: ((rule: CssRule, format: Format) => Verdict | null)[] = [\n // ── a centred track, written by hand ──────────────────────────────────────\n //\n // Three ways in, one verdict. They are one rule rather than three because they\n // describe the same mistake and a rule reports once — a `.wrap` with a pixel cap\n // and auto margins trips all three, and three findings on one line would read as\n // three problems.\n (rule) => {\n const cap = decl(rule, 'max-inline-size') ?? decl(rule, 'max-width') ?? decl(rule, 'width');\n const centred = /\\bauto\\b/.test(decl(rule, 'margin-inline') ?? decl(rule, 'margin') ?? '');\n if (!cap) return null;\n\n const token = TRACK_WIDTHS.find((t) => cap.includes(t));\n const { rem, ch } = widthScale(cap);\n const pageScale = rem >= PAGE_SCALE_REM || ch >= PAGE_SCALE_CH;\n // A container-shaped NAME lowers the bar to any cap at all: the name is the\n // stated intent, so the value no longer has to prove it.\n const named = CONTAINER_NAMES.some((n) =>\n // `[-_]+` rather than `[-_]`, so BEM's `__` separator is one boundary:\n // `.site-header__inner` is the container here, and requiring a single\n // separator missed every element-notation selector.\n new RegExp(`[.#]([a-z0-9]+[-_]+)*${n}([-_]+[a-z0-9]+)*\\\\b`, 'i').test(rule.selector),\n );\n\n // What makes each trigger unambiguous. A framework track token or a\n // container-shaped name is enough on its own; a bare length has to be both\n // page-scale AND centred, or it is just a capped element.\n const why = token\n ? `it caps width at \\`var(${token})\\`, a framework track`\n : named\n ? `\\`${rule.selector.trim()}\\` names a page container`\n : pageScale && centred\n ? `it caps width at page scale and centres with auto margins`\n : null;\n if (!why) return null;\n if (token && !centred) return null;\n\n return {\n cls: `${rule.selector} { ${cap.length > 40 ? 'width cap' : `max-width: ${cap}`} }`,\n severity: 'suggestion',\n reason: `\\`.centered\\` is this pattern — ${why}`,\n suggestion:\n 'use `.centered`. It is a grid of named tracks, not a max-width box, so a child ' +\n 'opts into a wider track with `breakout`, `spotlight` or `fullbleed` on itself — ' +\n 'a hand-rolled container has no answer for the full-bleed image except more CSS',\n };\n },\n\n // ── a two-column split behind a breakpoint ────────────────────────────────\n (rule, format) => {\n const cols = decl(rule, 'grid-template-columns');\n if (!cols || !inMinWidthQuery(rule)) return null;\n const ratio = SPLITS[cols.replace(/\\s+/g, ' ').trim()];\n const bp = ratio && breakpointOf(rule);\n if (!ratio || !bp) return null;\n return {\n cls: `${rule.selector} { grid-template-columns: ${cols} }`,\n severity: 'suggestion',\n reason: `a ${cols} split behind a min-width query is the \\`split-${ratio}\\` utility`,\n suggestion:\n `${responsive(bp, `split-${ratio}`, format)} on the container — the responsive form is ` +\n 'equal below the breakpoint and takes the ratio above it, which is what the query is doing by hand',\n };\n },\n\n // ── a repeated-item grid, which is what `.subgrid` is for ─────────────────\n //\n // Deliberately NOT gated on a media query, which is what keeps it off the split\n // rule's territory: a `repeat()` is a set of like items, an explicit `1fr 2fr`\n // is two different panels. The two cannot both fire on one rule anyway — this\n // requires `repeat()` and the split requires a `min-width` condition.\n (rule) => {\n const cols = decl(rule, 'grid-template-columns');\n if (!cols || !/^\\s*repeat\\(/i.test(cols)) return null;\n if (!/\\bgrid\\b/.test(decl(rule, 'display') ?? '')) return null;\n // `subgrid` in the same rule means this IS the framework pattern, being\n // configured or extended rather than replaced.\n if (rule.declarations.some((d) => /subgrid/.test(d.value))) return null;\n\n const count = /^\\s*repeat\\(\\s*(\\d+)/i.exec(cols)?.[1];\n const auto = /^\\s*repeat\\(\\s*auto-(fit|fill)/i.test(cols);\n if (!count && !auto) return null;\n if (count && Number(count) < 2) return null;\n\n return {\n cls: `${rule.selector} { grid-template-columns: ${cols} }`,\n severity: 'suggestion',\n reason:\n 'a grid of repeated items is `.subgrid` — a plain grid aligns the outer boxes but ' +\n 'not the tranches inside them, so headings, bodies and footers land at different ' +\n 'heights across the set',\n suggestion: auto\n ? '`.subgrid subgrid-responsive` (2 columns under 60rem, 1 under 40rem) with ' +\n '`--subgrid-row-span` set to the number of row bands each item contains — or ' +\n '`.grid-auto` with `--grid-min`, which is the framework class for this exact ' +\n 'auto-fit track and the right answer when the items have no tranches to align'\n : `\\`.subgrid subgrid-cols-${count}\\`, with \\`--subgrid-row-span\\` (or ` +\n '`subgrid-rows-<n>`) set to the number of row bands each item contains — and ' +\n '`.subgrid-card` on the items to get an edge-bled `__media` and a pinned `__footer`',\n };\n },\n\n // ── a flex row with centred items ─────────────────────────────────────────\n (rule, format) => {\n if (decl(rule, 'display') !== 'flex') return null;\n if (decl(rule, 'align-items') !== 'center') return null;\n if (/column/.test(decl(rule, 'flex-direction') ?? '')) return null;\n return {\n cls: `${rule.selector} { display: flex; align-items: center }`,\n severity: 'suggestion',\n reason: 'the framework has this as utilities',\n suggestion:\n format === 'tailwind'\n ? \"Tailwind's own `flex items-center` (the framework drops these names in this format)\"\n : '`flex items-center`, or `.cluster-start` if this is a row of related controls',\n };\n },\n];\n\n/**\n * Lint stylesheets and the `<style>` blocks of component files.\n *\n * `files` carries raw text, same as `lintSource` — this module stays free of the\n * filesystem so the caller owns discovery.\n */\n/**\n * The banner every generated stylesheet opens with.\n *\n * Linting the generator's own output is the worst false positive this tool can\n * produce: the emitted bundle contains the framework's real implementations of\n * these very patterns, so it reports `.split` as reinventing `.split`. The\n * `css` format writes `styles.css` into the consumer's source tree (`src/styles`\n * by default), which is squarely inside what `--src` scans.\n *\n * Matched on the banner rather than on a path, because the output directory is\n * configurable and a path rule would silently stop working when someone changes\n * it.\n */\n// Case-sensitive on purpose: the banner shouts `GENERATED`, and matching it\n// case-insensitively also swallowed an authored `/* generated content here */`.\nconst GENERATED = /^\\s*\\/\\*!?\\s*GENERATED\\b|do not edit by hand\\./;\n\nexport function lintCss(files: { path: string; text: string }[], format: Format): LintFinding[] {\n const findings: LintFinding[] = [];\n const seen = new Set<string>();\n for (const file of files) {\n if (GENERATED.test(file.text.slice(0, 400))) continue;\n // A .css file is one block starting at line 1; a component file may hold\n // several, each offset to where its <style> opens so a reported line points\n // into the original file rather than into the extracted block.\n const blocks = file.path.endsWith('.css')\n ? [{ css: file.text, line: 1 }]\n : extractStyleBlocks(file.text);\n for (const block of blocks)\n for (const rule of scanCss(block.css))\n for (const check of RULES) {\n const verdict = check(rule, format);\n if (!verdict) continue;\n const line = block.line + rule.line - 1;\n const key = `${file.path}:${line}:${verdict.reason}`;\n if (seen.has(key)) continue;\n seen.add(key);\n findings.push({ file: file.path, line, ...verdict });\n break; // one suggestion per rule — the first match wins\n }\n }\n return findings;\n}\n","/**\n * The third linting direction: markup that lays out a repeated set of things\n * without the framework's pattern for it.\n *\n * `lint.ts` catches a class that resolves to nothing. `lint-css.ts` catches\n * hand-written CSS that re-implements a primitive. Neither reaches the most common\n * reported drift, because it involves no bad class and no hand-written CSS at all:\n * a page renders a list of cards with `class=\"grid\"`, or with nothing, and every\n * class in it is real. The design system has `.subgrid` for exactly this and it\n * goes unused, so across three cards the headings sit at three heights and the\n * CTAs float wherever the copy ended.\n *\n * That cannot be judged from a class token in isolation, so this module is the one\n * that looks at *shape*: is there a repeated card construct here, and is it inside\n * a subgrid?\n *\n * **It is heuristic, and the heuristics are chosen to under-report.** Every finding\n * is a `suggestion`, at most one per file, and each requires a repetition signal\n * strong enough to state in the message: a loop that renders a card, or three or\n * more sibling cards written out. Two cards in a file is not enough — they are as\n * likely to be in unrelated sections, and a rule that fires there would teach\n * people to stop reading the output, which costs more than the drift.\n */\nimport { extname } from 'node:path';\nimport type { LintFinding } from './lint.ts';\n\n/**\n * Only real markup is examined.\n *\n * Markdown is excluded on purpose, and it is the exclusion that matters most: a\n * `.md` file is as likely to *discuss* `.card` as to use it. Run against this\n * repo's own docs site, the first version reported `changelog.md` and two\n * generated reference pages — prose about the framework, counted as drift by it.\n * A consumer's markdown content can hold real markup, so this does give up a\n * little coverage; that is the right side to err on for a heuristic.\n */\nconst MARKUP_EXT = new Set([\n '.astro',\n '.html',\n '.htm',\n '.jsx',\n '.tsx',\n '.vue',\n '.svelte',\n '.ts',\n '.js',\n]);\n\n/**\n * A class attribute's value, in the spellings the supported frameworks use.\n * Mirrors `lint.ts`'s extractor — the token must be in a real `class`, not merely\n * somewhere in the file.\n */\nconst CLASS_ATTR =\n /(?:class|className|class:list)\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)'|\\{`([^`]*)`\\}|\\{([^}]*)\\})/g;\n\n/**\n * The card-ish classes worth caring about.\n *\n * Anchored to `card` as a whole word or suffix, which covers the framework's own\n * `card` / `subgrid-card` / `pricing-card` and a consumer's `service-card` alike.\n * Not `*-item` or `*-tile`: too many of those are not laid out as a set.\n */\nconst CARD_CLASS = /(?:^|[\\s\"'`])((?:[a-z0-9]+-)*card)(?:__[a-z0-9-]+)?(?=[\\s\"'`]|$)/i;\n\n/** Every card class named in a real class attribute, with its offset in `src`. */\nfunction cardsInClassAttrs(src: string): { name: string; index: number }[] {\n const out: { name: string; index: number }[] = [];\n for (const m of src.matchAll(CLASS_ATTR)) {\n const value = m[1] ?? m[2] ?? m[3] ?? m[4] ?? '';\n for (const tok of value.split(/[\\s'\"`]+/)) {\n const card = CARD_CLASS.exec(` ${tok} `);\n // Element classes (`card__body`) belong to a card already counted by its\n // block class — counting them would report every single-card file as three.\n if (card && !tok.includes('__')) out.push({ name: card[1] as string, index: m.index ?? 0 });\n }\n }\n return out;\n}\n\n/**\n * Already using the pattern — in any tier's spelling.\n *\n * `<Cards>` and `<wc-cards>` count: `<Cards>` renders a `Subgrid`, and `<wc-cards>`\n * only ever wraps one. Omitting them reported a correct card grid as a missed one.\n */\nconst HAS_SUBGRID = /\\bsubgrid\\b|<Subgrid\\b|<Cards\\b|<wc-cards\\b/;\n\n/**\n * Constructs that render one item many times.\n *\n * The JS/Astro/JSX `.map(`, and the block forms Svelte/Vue/Handlebars use. A loop\n * whose body contains a card is a repeated card set by construction — no counting\n * and no sibling analysis needed, which is why it is the primary signal.\n */\nconst LOOPS = [/\\.\\s*map\\s*\\(/g, /\\{#each\\b/g, /v-for\\s*=/g, /\\{%\\s*for\\b/g];\n\n/** Line number of a character offset. */\nconst lineAt = (src: string, index: number) => src.slice(0, index).split('\\n').length;\n\n/**\n * The body of a loop, approximated by taking the text that follows it.\n *\n * A real brace-matcher would be more precise and is not worth it: the question is\n * only \"does a card appear inside this repetition\", and a card 600 characters past\n * a `.map(` is inside it in every realistic template. Over-reaching here can only\n * produce a suggestion on a file that does render cards somewhere.\n */\nconst LOOP_WINDOW = 600;\n\ninterface CardSignal {\n line: number;\n /** What made this a repeated set, phrased for the message. */\n evidence: string;\n cls: string;\n}\n\n/** The strongest repetition signal in one file, or none. */\nfunction cardSignal(text: string): CardSignal | null {\n const cards = cardsInClassAttrs(text);\n if (!cards.length) return null;\n\n // ── a loop that renders a card ────────────────────────────────────────────\n // The stronger signal, and it needs no counting: one card inside a repetition\n // is a set by construction.\n for (const loop of LOOPS)\n for (const m of text.matchAll(loop)) {\n const start = (m.index ?? 0) + m[0].length;\n const card = cards.find((c) => c.index >= start && c.index < start + LOOP_WINDOW);\n if (!card) continue;\n return {\n line: lineAt(text, m.index ?? 0),\n evidence: `a \\`${m[0].trim()}\\` loop renders \\`.${card.name}\\``,\n cls: `${m[0].trim()} … .${card.name}`,\n };\n }\n\n // ── three or more cards written out ──────────────────────────────────────\n if (cards.length >= 3) {\n const first = cards[0] as { name: string; index: number };\n return {\n line: lineAt(text, first.index),\n evidence: `\\`.${first.name}\\` appears on ${cards.length} elements in this file`,\n cls: `.${first.name} ×${cards.length}`,\n };\n }\n return null;\n}\n\n/**\n * Lint markup for repeated card sets laid out without `.subgrid`.\n *\n * Takes raw text like the other linters — discovery stays with the caller. One\n * finding per file: the drift is a decision about a layout, not about a line, and\n * repeating it per card would bury every other finding the command prints.\n */\n/**\n * An anchor carrying a card class inside a subgrid.\n *\n * The shape everyone reaches for when the whole card is a link — and it renders\n * fine, which is why it survives. The `<li>` is the grid item, so the anchor is an\n * ordinary block inside it and the tranches within the anchor never reach the\n * parent's shared row lines: the alignment `.subgrid` exists for silently does not\n * happen, and the anchor doesn't fill the cell either. This is the one markup\n * finding that reports a *broken* result rather than a missed primitive.\n */\nconst LINK_CARD = /<a\\b[^>]*\\bclass(?:Name)?\\s*=\\s*[\"'{][^\"'}]*\\b(?:[a-z0-9]+-)*card\\b/gi;\n\n/**\n * Is this offset inside an `<li>`?\n *\n * The rule's claim is \"the anchor is nested inside the grid item rather than being\n * it\", and only an anchor within an `<li>` is that. Checking the file merely\n * *mentions* subgrid is far too coarse: a page documenting several patterns tripped\n * on a standalone `<a class=\"tile card\">`, which is a perfectly good anchor-as-card —\n * outside a grid there is no grid item for it to compete with.\n */\nfunction insideListItem(text: string, index: number): boolean {\n const before = text.slice(0, index);\n return before.lastIndexOf('<li') > before.lastIndexOf('</li');\n}\n\nfunction linkCardFinding(file: { path: string; text: string }): LintFinding | null {\n const m = [...file.text.matchAll(LINK_CARD)].find((hit) =>\n insideListItem(file.text, hit.index ?? 0),\n );\n if (!m || m.index === undefined) return null;\n return {\n file: file.path,\n line: lineAt(file.text, m.index),\n cls: '<a class=\"…card\">',\n severity: 'suggestion',\n reason:\n 'a card class on an `<a>` inside a `subgrid` does not align — the `<li>` is the grid ' +\n \"item, so the anchor's tranches never reach the parent's shared row lines, and the \" +\n 'anchor does not fill the cell either. It renders, it just silently is not a subgrid',\n suggestion:\n 'keep the card as the item (`<li class=\"card subgrid-card\">`) and make the whole card ' +\n 'clickable from inside it — either `class=\"stretched-link\"` on a link (no JS, but the ' +\n \"card's text stops being selectable) or `<Cards>` / `<wc-cards>` around the list \" +\n '(keeps selection, needs JS, falls back to the link). Both keep the accessible name on ' +\n 'real text. Lift anything that must stay above a stretched overlay with `raised`',\n };\n}\n\n/**\n * `.stretched-link` inside `<wc-cards>` — the two whole-card-click mechanisms layered.\n *\n * They are alternatives, not layers, and combining them is silently the worse of the\n * two: the overlay takes the pointer-drag, so the card's text stops being selectable\n * — which is the only reason to reach for `<wc-cards>` — and the element's\n * click-vs-drag logic never gets a chance to run, because the browser follows the\n * overlaid link natively first.\n */\nconst WC_CARDS = /<wc-cards\\b|<Cards\\b/;\nconst STRETCHED = /\\bclass(?:Name)?\\s*=\\s*[\"'{][^\"'}]*\\bstretched-link\\b/;\n\nfunction layeredCardLinkFinding(file: { path: string; text: string }): LintFinding | null {\n if (!WC_CARDS.test(file.text)) return null;\n const m = STRETCHED.exec(file.text);\n if (!m) return null;\n return {\n file: file.path,\n line: lineAt(file.text, m.index),\n cls: 'stretched-link inside wc-cards',\n severity: 'suggestion',\n reason:\n '`.stretched-link` and `<wc-cards>` are two ways to do the same thing, and layering ' +\n \"them silently picks the worse one — the overlay takes the pointer-drag, so the card's \" +\n 'text is no longer selectable, which is the only reason to use `<wc-cards>` at all',\n suggestion:\n 'drop one. Keep `stretched-link` and use `<Subgrid>` if you want zero JS and can accept ' +\n 'unselectable card text; keep `<Cards>` / `<wc-cards>` and remove `stretched-link` if ' +\n 'selectable text matters — the plain `link` class on the heading is then the fallback',\n };\n}\n\nexport function lintMarkup(files: { path: string; text: string }[]): LintFinding[] {\n const findings: LintFinding[] = [];\n for (const file of files) {\n if (!MARKUP_EXT.has(extname(file.path).toLowerCase())) continue;\n // Layering the two whole-card-click mechanisms is judged wherever it appears —\n // `<wc-cards>` governs any container, not only a subgrid.\n const layered = layeredCardLinkFinding(file);\n if (layered) findings.push(layered);\n if (HAS_SUBGRID.test(file.text)) {\n // Already using the pattern, so the missed-primitive rule does not apply —\n // but the link-card mistake only exists *inside* a subgrid, so it is checked\n // here rather than skipped with everything else.\n const broken = linkCardFinding(file);\n if (broken) findings.push(broken);\n continue;\n }\n const signal = cardSignal(file.text);\n if (!signal) continue;\n // `.grid-auto` and `.grid` ARE framework classes, so a message implying no\n // framework layout was used would be wrong on exactly the sites that are\n // trying — and a linter that misreads what you wrote stops being read. The\n // point stands either way; only the framing changes.\n const usingGrid = /\\bgrid-auto\\b|class(?:Name)?=\"[^\"]*\\bgrid\\b/.test(file.text);\n findings.push({\n file: file.path,\n line: signal.line,\n cls: signal.cls,\n severity: 'suggestion',\n reason:\n `${signal.evidence}, ${\n usingGrid\n ? 'laid out with a plain grid rather than a `subgrid`'\n : 'and nothing here is a `subgrid`'\n } — more than one card is what \\`.subgrid\\` is for. A plain grid aligns the outer boxes ` +\n \"but not the tranches inside them, so the set's headings, bodies and footers land at \" +\n 'different heights',\n suggestion:\n '`<ul class=\"subgrid subgrid-cols-3\" role=\"list\">` with ' +\n '`<li class=\"card subgrid-card\">` items (a set of cards is a list; `role=\"list\"` is ' +\n 'needed because `.subgrid` is markerless and Safari stops announcing a marker-less ' +\n '`ul` as a list); set `--subgrid-row-span` to the number of row bands each card ' +\n 'contains. In Astro, `<Subgrid />` adds the role for you. If the items genuinely have ' +\n 'no internal tranches to align (a bare image gallery), `.grid-auto` is the right call ' +\n 'and this is a false positive',\n });\n }\n return findings;\n}\n","/**\n * `vitops lint` — find pre-1.0 colour-grammar token references in consumer\n * source, and rewrite them.\n *\n * The class linter (`lint.ts`) reads `class` attributes; this reads `var(--…)`\n * out of CSS and `<style>` blocks, which is the other half of where a design\n * system is referenced and the half nothing checked. `validate()` covers the\n * same grammar inside a `design-system.json`; a consumer's own stylesheets are\n * outside that file and were entirely unguarded.\n *\n * **Why a codemod and not just a report.** The 1.0 rename is mechanical, and a\n * downstream consumer did it by hand — correctly, but only because they noticed\n * that applying the renames *sequentially* compounds them. A surface role's\n * backgrounds rotate (`--surface-bg` → `--color-bg-surface-muted`,\n * `--surface-bg-bold` → `--color-bg-surface`), so a naive\n * find-and-replace-each-in-turn walks a value through two renames and lands\n * somewhere neither table entry names. `applyRenames` builds one alternation and\n * rewrites in a single pass, which makes simultaneity structural rather than\n * something the operator has to know.\n */\nimport type { LintFinding } from './lint.ts';\n\n/** A rewrite this tool can perform without a judgement call. */\nexport interface TokenFix {\n from: string;\n to: string;\n}\n\n/**\n * Rewrite every old name in one pass.\n *\n * Longest-first alternation so a name that is a prefix of another (`--x-bg` vs\n * `--x-bg-bold`) cannot be matched short, and a single `replace` so no output of\n * one rename is ever input to the next.\n */\nexport function applyRenames(text: string, renames: Record<string, string>): string {\n const names = Object.keys(renames).sort((a, b) => b.length - a.length);\n if (!names.length) return text;\n const escaped = names.map((n) => n.replaceAll(/[.*+?^${}()|[\\]\\\\]/g, String.raw`\\$&`));\n // `(?![\\w-])` so `--brand-bg` does not match inside `--brand-bg-bold` — the\n // longest-first ordering handles the listed names, this handles unlisted ones.\n const re = new RegExp(`(${escaped.join('|')})(?![\\\\w-])`, 'g');\n return text.replace(re, (m) => renames[m] ?? m);\n}\n\n/** Every `var(--name)` in a text, with its line number. */\nfunction varRefs(src: string): { name: string; line: number }[] {\n const out: { name: string; line: number }[] = [];\n for (const m of src.matchAll(/var\\(\\s*(--[\\w-]+)/g))\n out.push({ name: m[1] as string, line: src.slice(0, m.index).split('\\n').length });\n return out;\n}\n\n/**\n * Report old-grammar token references in source files.\n *\n * `renames` comes from the generator's `movedTokens()`, built from the\n * consumer's own role names — there is no fixed list, which is why nothing\n * detected these before.\n */\nexport function lintTokens(\n files: { path: string; text: string }[],\n renames: Record<string, string>,\n): (LintFinding & { fix: TokenFix })[] {\n const findings: (LintFinding & { fix: TokenFix })[] = [];\n const seen = new Set<string>();\n for (const f of files)\n for (const { name, line } of varRefs(f.text)) {\n const to = renames[name];\n if (to == null) continue;\n const key = `${f.path}:${line}:${name}`;\n if (seen.has(key)) continue;\n seen.add(key);\n findings.push({\n file: f.path,\n line,\n cls: `var(${name})`,\n severity: 'error',\n reason: `\\`${name}\\` is the pre-1.0 colour grammar and resolves to nothing`,\n suggestion: `var(${to})`,\n fix: { from: name, to },\n });\n }\n return findings;\n}\n","/**\n * `vitops lint` — find framework classes in consumer source that resolve to\n * nothing in the format being built.\n *\n * The problem this exists for: an unknown utility class is indistinguishable\n * from a working one. Nothing errors, nothing warns, the element just doesn't\n * get the style. Two shapes of that were found in a real consumer site:\n *\n * • `bg-navy-d` — a leftover from the named-step scale (`d`/`xd`/`xxd`)\n * deleted in favour of numeric steps. The hue is real, the step is not.\n * It appeared ~10× and looked fine only because the `.card` it sat on\n * supplied its own background.\n * • `md-flex-row` — the css/bricks spelling of a responsive variant, used in\n * a `tailwind`-format project where that class is never emitted (Tailwind\n * regenerates those on demand as `@md:`). Silently inert.\n *\n * DESIGN: this deliberately only inspects classes ANCHORED TO THE CONSUMER'S\n * OWN CONFIG — a hue name, a role name, a type role, a shadow, a pattern. It\n * never tries to enumerate \"all valid classes\", because in the tailwind format\n * that would mean knowing Tailwind's entire built-in vocabulary, and every\n * unknown would be a false positive. Anchoring means a finding is always\n * actionable: you named something from your design system, and it doesn't\n * resolve.\n */\nimport { NUMERIC_STEPS, type DesignSystem, type Format } from '@getvitops/generator';\n\n/**\n * `error` — you named something from your design system and it resolves to\n * nothing. Always actionable, and fails the command.\n *\n * `suggestion` — the code works, but the framework already has a primitive for\n * it. Advisory by design: these are judgement calls, and a new rule that failed\n * CI on first run would be a worse defect than the drift it reports. `--strict`\n * promotes them for consumers who want the ratchet.\n */\nexport type LintSeverity = 'error' | 'suggestion';\n\nexport interface LintFinding {\n file: string;\n line: number;\n /** The class token, or for a CSS rule the declaration that triggered it. */\n cls: string;\n reason: string;\n suggestion?: string;\n severity: LintSeverity;\n}\n\nconst UTIL_FAMILIES = ['bg', 'text', 'icon', 'border', 'outline', 'fill', 'stroke'];\n/** Class families the framework owns in every format, for variant-spelling checks. */\nconst FRAMEWORK_FAMILY = new RegExp(\n `^(${[...UTIL_FAMILIES, 'font', 'drop-shadow', 'split', 'flex', 'items', 'justify', 'gap', 'space'].join('|')})(-|$)`,\n);\n\n/**\n * The modifiers actually emitted for one family + role, read back out of the\n * generator's own class list.\n *\n * This used to be a hand-maintained array, which is the wrong shape for a\n * suggestion: the two role kinds emit different modifiers (a chromatic role has\n * `solid` and no bare form; a surface role the reverse), so a single list was\n * wrong for one of them no matter what it said.\n */\nconst emittedModifiers = (roleClasses: Set<string>, fam: string, role: string): string[] => {\n const prefix = `${fam}-${role}`;\n return [...roleClasses]\n .filter((c) => c === prefix || c.startsWith(`${prefix}-`))\n .map((c) => c.slice(prefix.length + 1))\n .filter(Boolean)\n .sort();\n};\n\n/** Everything the config makes namable, so we only judge classes we can judge. */\ninterface Vocabulary {\n roles: string[];\n hues: string[];\n steps: string[];\n typeRoles: string[];\n shadows: string[];\n patterns: string[];\n /** Every role utility the generator actually emits, e.g. `bg-surface-x-muted`. */\n roleClasses: Set<string>;\n}\n\nexport function vocabulary(ds: DesignSystem, roleClasses: Iterable<string>): Vocabulary {\n return {\n roles: Object.keys(ds.colors?.roles ?? {}),\n hues: Object.keys(ds.colors?.palette ?? {}),\n steps: [...NUMERIC_STEPS],\n typeRoles: Object.keys(ds.typography?.roles ?? {}),\n shadows: Object.keys(ds.shadows ?? {}),\n patterns: Object.keys(ds.patterns?.items ?? {}),\n roleClasses: new Set(roleClasses),\n };\n}\n\n/**\n * Strip variant prefixes (`@md:`, `hover:`, `dark:`) from a candidate.\n *\n * Format-aware, because the container-query spelling only exists in tailwind.\n * Stripping `@md:` unconditionally made the check asymmetric: `md-split-1-2` in a\n * tailwind project was caught, but `@md:split-1-2` in a css/bricks project became\n * a bare `split-1-2` that matched nothing and returned null — the same silent\n * no-op, in the other direction, unreported.\n */\nconst stripVariants = (cls: string, format: Format): string => {\n let out = cls;\n while (/^@?[a-zA-Z0-9_-]+:/.test(out)) {\n if (format !== 'tailwind' && out.startsWith('@')) break;\n out = out.slice(out.indexOf(':') + 1);\n }\n return out;\n};\n\nconst nearest = (want: string, pool: string[]): string | undefined =>\n pool.find((p) => p.startsWith(want) || want.startsWith(p));\n\n/**\n * Judge one class token. Returns null when the class is none of our business —\n * which is the common case and the whole reason this is low-noise.\n */\nexport function judge(\n raw: string,\n v: Vocabulary,\n format: Format,\n): Omit<LintFinding, 'file' | 'line'> | null {\n const cls = stripVariants(raw, format);\n\n // ── the `@<bp>:` container-query spelling, in a format that has none ───────\n // Tailwind's engine expands these; css/bricks ship pre-expanded `<bp>-` classes\n // instead, so this spelling resolves to nothing there.\n const cq = /^@(sm|md|lg|xl):(.+)$/.exec(cls);\n if (cq && format !== 'tailwind')\n return {\n cls: raw,\n severity: 'error' as const,\n reason: `\\`@${cq[1]}:\\` container-query variants are a tailwind-format spelling; the ${format} format emits pre-expanded classes`,\n suggestion: `${cq[1]}-${cq[2]}`,\n };\n\n // ── Tailwind state variants (`hover:`, `focus:`, `dark:` …) in a format that\n // emits none. css/bricks pre-expand behaviour into hyphenated classes\n // (`md-flex-row`, `hover-fade-in`); a colon-spelled state variant on a framework\n // class resolves to nothing there. Only framework families are judged, so a\n // consumer's own `hover:` rule in their own CSS is not ours.\n const sv =\n /^(hover|focus|focus-visible|focus-within|active|disabled|dark|group-hover):(.+)$/.exec(raw);\n if (sv && format !== 'tailwind' && FRAMEWORK_FAMILY.test(sv[2] as string))\n return {\n cls: raw,\n severity: 'error' as const,\n reason: `\\`${sv[1]}:\\` state variants are a tailwind-format spelling; the ${format} format emits no such class`,\n suggestion: `${sv[1]}-${sv[2]} where an effect class exists, otherwise a pattern state`,\n };\n\n // ── the `<bp>-` responsive spelling ────────────────────────────────────────\n const bp = /^(sm|md|lg|xl)-(.+)$/.exec(cls);\n if (bp && format === 'tailwind')\n return {\n cls: raw,\n severity: 'error' as const,\n reason: `\\`${bp[1]}-\\` responsive classes are not emitted in the tailwind format`,\n suggestion: `@${bp[1]}:${bp[2]} (framework breakpoints) or ${bp[1]}:${bp[2]} (Tailwind's)`,\n };\n // In css/bricks the bare-prefix form is real; fall through and judge the base\n // against the rest of the vocabulary.\n const base = bp && format !== 'tailwind' ? (bp[2] as string) : cls;\n\n // ── <util>-<hue>-<step> ────────────────────────────────────────────────────\n const util = new RegExp(`^(${UTIL_FAMILIES.join('|')})-(.+)$`).exec(base);\n if (util) {\n const [, fam, rest] = util as unknown as [string, string, string];\n // Anything the generator actually emits is valid, full stop. Checking this\n // first also settles the ambiguity below for every well-formed class.\n if (v.roleClasses.has(base)) return null;\n\n // Role and hue names can share a prefix — `defaultConfig()` has the hue\n // `brand` and the role `brand-primary`, so matching hues first would read\n // `bg-brand-primary-x-muted` as hue `brand` + step `primary-x-muted` and\n // report a bogus bad step. Take the LONGEST match across both vocabularies.\n const prefix = (names: string[]) =>\n names\n .filter((n) => rest === n || rest.startsWith(`${n}-`))\n .sort((a, b) => b.length - a.length)[0];\n const hue = prefix(v.hues);\n const role = prefix(v.roles);\n const useRole = role && (!hue || role.length > hue.length);\n\n if (useRole) {\n const mod = rest.slice((role as string).length + 1);\n const valid = emittedModifiers(v.roleClasses, fam as string, role as string);\n return {\n cls: raw,\n severity: 'error' as const,\n reason: `\\`${fam}-${role}${mod ? `-${mod}` : ''}\\` is not emitted for the \\`${role}\\` role`,\n suggestion: valid.length\n ? `valid modifiers: ${valid.join(', ')}`\n : `the \\`${role}\\` role emits no \\`${fam}-*\\` utilities`,\n };\n }\n if (hue) {\n const step = rest.slice(hue.length + 1);\n if (step === '')\n return {\n cls: raw,\n severity: 'error' as const,\n reason: `\\`${fam}-${hue}\\` names a palette hue with no step`,\n suggestion: `${fam}-${hue}-500, or a role such as ${fam}-${v.roles[0] ?? 'neutral'}`,\n };\n if (!v.steps.includes(step))\n return {\n cls: raw,\n severity: 'error' as const,\n reason: `\\`${step}\\` is not a step on the \\`${hue}\\` scale (steps are 50…950)`,\n suggestion: `${fam}-${hue}-500, or a role such as ${fam}-${v.roles[0] ?? 'neutral'} which also flips in dark mode`,\n };\n return null; // valid raw step\n }\n return null; // not anchored to this config — not ours to judge\n }\n\n // ── font-<type-role> ───────────────────────────────────────────────────────\n const font = /^font-(.+)$/.exec(base);\n if (font && v.typeRoles.length && !v.typeRoles.includes(font[1] as string)) {\n const near = nearest(font[1] as string, v.typeRoles);\n // Only complain when it looks like a type role attempt, not e.g. font-bold.\n if (near)\n return {\n cls: raw,\n severity: 'error' as const,\n reason: `\\`${font[1]}\\` is not a typography role`,\n suggestion: `font-${near}`,\n };\n }\n\n // ── drop-shadow-<name> ─────────────────────────────────────────────────────\n const ds = /^drop-shadow-(.+)$/.exec(base);\n if (ds && v.shadows.length && !v.shadows.includes(ds[1] as string))\n return {\n cls: raw,\n severity: 'error' as const,\n reason: `\\`${ds[1]}\\` is not a defined shadow`,\n suggestion: `drop-shadow-${v.shadows[0]}`,\n };\n\n return null;\n}\n\nconst CLASS_ATTR = /(?:class|className|:class)\\s*=\\s*(?:\"([^\"]*)\"|'([^']*)'|\\{`([^`]*)`\\})/g;\n\n/** Extract class tokens from a source file, with line numbers. */\nexport function extractClasses(src: string): { cls: string; line: number }[] {\n const out: { cls: string; line: number }[] = [];\n for (const m of src.matchAll(CLASS_ATTR)) {\n const value = m[1] ?? m[2] ?? m[3] ?? '';\n const line = src.slice(0, m.index).split('\\n').length;\n for (const tok of value.split(/\\s+/))\n // Skip template holes and dynamic fragments — a partial token would be\n // judged as a typo and this must not cry wolf.\n if (tok && !tok.includes('${') && !tok.includes('{') && /^[@\\w:[\\]().%/-]+$/.test(tok))\n out.push({ cls: tok, line });\n }\n return out;\n}\n\nexport function lintSource(\n files: { path: string; text: string }[],\n v: Vocabulary,\n format: Format,\n): LintFinding[] {\n const findings: LintFinding[] = [];\n const seen = new Set<string>();\n for (const f of files)\n for (const { cls, line } of extractClasses(f.text)) {\n const verdict = judge(cls, v, format);\n if (!verdict) continue;\n const key = `${f.path}:${line}:${cls}`;\n if (seen.has(key)) continue;\n seen.add(key);\n findings.push({ file: f.path, line, ...verdict });\n }\n return findings;\n}\n","#!/usr/bin/env node\n/**\n * `vitops` — CLI for the Vitops design-system generator.\n *\n * vitops generate --input <json> --format <bricks|css|tailwind|design[,…]> --out <dir>\n * vitops init [--out design-system.json] [--force] [--site [--name <n>] [--url <u>]]\n * vitops validate <design-system.json|site.json>\n * vitops favicon --input <svg|png> --out <dir> [--low-res <svg|png>] [--background <hex>]\n * vitops agents [--input <json>] [--out AGENTS.md] [--docs-dir <dir>]\n * vitops docs [topic] [--input <json>] [--all]\n * vitops lint [--input <json>] [--format <fmt>] [--src <dir>]\n * vitops legal [--input <site.json>] [--doc <name>] [--format <md|html|portable-text>] [--out <dir>]\n * vitops robots [--input <site.json>] [--site-env <name>] [--out <dir>]\n * vitops reviews [--input <site.json>] [--location <key>] [--dry] [--check] [--out <dir>]\n * vitops search setup [--domain <name>] [--dry] [--check] | notify [--dry] [--all] [--check]\n * vitops ads setup [--provider <name>] [--dry] [--check] | tags | lint\n * vitops setup repo | hosting | domain | search | ads | all [--dry] [--check]\n * vitops email setup [domain] [--dry] [--check] | check [--strict] [--skip]\n * vitops emdash prepare-db | check-prerender [dir]\n * vitops media [--raw <dir>] [--out <dir>] [--force] [--dry]\n * vitops audit [--url <base>] [--build] [--pages <list>] [--warn] [--out <dir>]\n *\n * Thin wrapper over @getvitops/generator (generation) and @getvitops/utils (favicons).\n * Every client brings their own consumer-editable design-system.json.\n *\n * `search`/`ads` live in commands/search.ts + commands/ads.ts — the only two\n * command groups big enough, and self-contained enough, to earn their own file.\n * Everything else stays here.\n */\nimport { parseArgs } from 'node:util';\nimport { pathToFileURL } from 'node:url';\nimport { writeFileSync, existsSync, readFileSync, mkdirSync } from 'node:fs';\nimport { resolve, join, dirname, relative, extname } from 'node:path';\nimport { createRequire } from 'node:module';\nimport {\n buildIconSprite,\n generate,\n validate,\n defaultConfig,\n defaultSiteConfig,\n generateDocs,\n generateLegal,\n enabledDocs,\n processorsMissingLocation,\n JURISDICTION_COUNTRIES,\n isConfig,\n resolveInput,\n resolveConfig,\n resolveTheme,\n roleColorUtilities,\n functionalRole,\n expandPalette,\n roleHue,\n roleKind,\n movedTokens,\n renderRobotsTxt,\n SCHEMA_LOCAL_PATH,\n CONFIG_SCHEMA_LOCAL_PATH,\n type Format,\n type StylesheetFormat,\n type DesignSystem,\n type LegalDoc,\n type LegalOutput,\n type ResolvedInput,\n type Config,\n} from '@getvitops/generator';\nimport {\n collectIconRefs,\n generateFavicons,\n generateIconInclude,\n resolveIcon,\n scanFiles,\n} from '@getvitops/utils';\n// Its own subpath: `indexing` is the only network-touching module in utils, and the\n// other commands should not pull it in. Only `googleAccessToken`/`GoogleAuthLike`\n// are needed here — everything else that used to be imported for `search notify`\n// now lives in commands/search.ts.\nimport { googleAccessToken, type GoogleAuthLike } from '@getvitops/utils/indexing';\n// Its own subpath for the same reason: `reviews` is network-touching (Google\n// Business Profile). `formatPlan` is aliased against `@getvitops/utils/media`'s\n// own `formatPlan`, imported below.\nimport {\n REVIEWS_CACHE_PATH,\n fetchReviews,\n formatPlan as formatReviewsPlan,\n mergeCache,\n plan as planReviews,\n readCache as readReviewsCache,\n selectReviews,\n toCache,\n writeCache as writeReviewsCache,\n type LocationReviews,\n type ReviewsConfig,\n} from '@getvitops/utils/reviews';\n// Its own subpath for the same reason: `media` shells out to ffmpeg, and no other\n// command should carry it.\nimport {\n MEDIA_MANIFEST_PATH,\n formatPlan as formatMediaPlan,\n processMedia,\n type MediaConfig,\n type OutputKind,\n} from '@getvitops/utils/media';\nimport { msg, parse } from './args.ts';\nimport { loadGoogleOAuth, requireAdcProject } from './credentials.ts';\nimport {\n fail,\n loadConfig,\n readJsonOrFail,\n siteConfig,\n SITE_OPTS,\n writeOut,\n} from './commands/shared.ts';\nimport { cmdSearch } from './commands/search.ts';\nimport { cmdAds } from './commands/ads.ts';\nimport { cmdAudit } from './commands/audit.ts';\nimport { cmdCrm } from './commands/crm.ts';\nimport { cmdEmail } from './commands/email.ts';\nimport { cmdEmdash } from './commands/emdash.ts';\nimport { cmdSetup } from './commands/setup.ts';\nimport {\n AGENTS_END,\n AGENTS_START,\n discoverBlockSkills,\n findSkillTarget,\n linkPackagedSkills,\n linkSkill,\n renderAgentsBlock,\n SKILL_NAME,\n TOPICS,\n upsertAgentsBlock,\n} from './agents.ts';\nimport { COMMANDS, HELP, helpFor, wantsHelp } from './help.ts';\nimport { lintCss } from './lint-css.ts';\nimport { lintMarkup } from './lint-markup.ts';\nimport { applyRenames, lintTokens } from './lint-tokens.ts';\nimport { lintSource, vocabulary } from './lint.ts';\n\nconst FORMATS = new Set<Format>(['bricks', 'css', 'tailwind', 'design']);\n// `lint` judges class names against what a build emits, and the `design` format\n// emits no CSS at all — so it is a valid target for `generate` and a category\n// error for `lint`.\nconst LINT_FORMATS = new Set<StylesheetFormat>(['bricks', 'css', 'tailwind']);\n\n// `legal` reads a *site* config, not a design-system.json, and has its own\n// output set — it renders documents, not stylesheets.\nconst LEGAL_DOCS = new Set<LegalDoc>(['privacy', 'terms', 'cookies']);\nconst LEGAL_OUTPUTS = new Set<LegalOutput>(['md', 'html', 'portable-text']);\n\nasync function cmdGenerate(argv: string[]) {\n const { values } = parseArgs({\n args: argv,\n options: {\n input: { type: 'string', short: 'i', default: 'design-system.json' },\n format: { type: 'string', short: 'f', default: 'bricks' },\n out: { type: 'string', short: 'o', default: 'dist' },\n site: { type: 'string' },\n theme: { type: 'string' },\n 'site-env': { type: 'string' },\n },\n allowPositionals: false,\n });\n const input = resolve(values.input as string);\n if (!existsSync(input)) fail(`config not found: ${input}`);\n const site = values.site ? resolve(values.site as string) : undefined;\n if (site && !existsSync(site)) fail(`site config not found: ${site}`);\n const formats = (values.format as string)\n .split(',')\n .map((f) => f.trim())\n .filter(Boolean);\n for (const f of formats)\n if (!FORMATS.has(f as Format))\n fail(`unknown format \"${f}\" (expected: bricks | css | tailwind | design)`);\n\n for (const format of formats as Format[]) {\n try {\n const res = await generate({\n input,\n format,\n outDir: values.out as string,\n ...(site ? { site } : {}),\n ...(values.theme ? { theme: values.theme as string } : {}),\n ...(values['site-env'] ? { siteEnv: values['site-env'] as string } : {}),\n });\n console.log(`✓ ${format} → ${res.outDir} (${res.written.length} paths)`);\n } catch (err) {\n fail(msg(err));\n }\n }\n}\n\n/**\n * `vitops init` writes a starter `design-system.json`; `--site` writes a starter\n * `site.json` instead — the umbrella `Config` that embeds the design system under\n * `designSystem.themes.default`, since there is no file-level `extends` and a\n * scaffolded config can't merely reference a sibling file (see `defaultSiteConfig`'s\n * docblock in `@getvitops/generator`). `--name`/`--url` fill the two facts a\n * consumer almost certainly has on hand; everything else stays the documented\n * placeholder, edited by hand or via `vitops setup` (see AGENTS.md's \"Client\n * onboarding\" section).\n */\nfunction cmdInit(argv: string[]) {\n const { values } = parseArgs({\n args: argv,\n options: {\n out: { type: 'string', short: 'o' },\n force: { type: 'boolean', default: false },\n site: { type: 'boolean', default: false },\n name: { type: 'string' },\n url: { type: 'string' },\n },\n allowPositionals: false,\n });\n const out = resolve((values.out as string) ?? (values.site ? 'site.json' : 'design-system.json'));\n if (existsSync(out) && !values.force) fail(`${out} already exists (use --force to overwrite)`);\n if (values.site) {\n const config = defaultSiteConfig({\n name: values.name as string | undefined,\n url: values.url as string | undefined,\n });\n writeFileSync(out, JSON.stringify(config, null, 2) + '\\n');\n console.log(`✓ wrote ${out}`);\n console.log(` Edit it, then: vitops validate ${relative('.', out)}`);\n console.log(\n ` Editors read the \"$schema\" (${CONFIG_SCHEMA_LOCAL_PATH}) for autocomplete + validation — ` +\n `the installed copy, so it matches the toolchain you build with.`,\n );\n return;\n }\n writeFileSync(out, JSON.stringify(defaultConfig(), null, 2) + '\\n');\n console.log(`✓ wrote ${out}`);\n console.log(\n ` Edit it, then: vitops generate --input ${relative('.', out)} --format tailwind --out src/styles`,\n );\n console.log(\n ` Editors read the \"$schema\" (${SCHEMA_LOCAL_PATH}) for autocomplete + validation — ` +\n `the installed copy, so it matches the toolchain you build with.`,\n );\n}\n\n/**\n * Validate a config against the schema its shape says it is.\n *\n * Routing on kind rather than asking the caller matters here: pointed at a site\n * config, the design-system schema used to report a single `unrecognized_keys`\n * for `designSystem` and nothing about the file's actual contents — a wrong\n * answer that reads like a right one. A site config is validated whole, which\n * also covers the cross-field integrity JSON Schema can't express.\n */\nfunction cmdValidate(argv: string[]) {\n const { values, positionals } = parseArgs({\n args: argv,\n options: { input: { type: 'string', short: 'i' }, 'site-env': { type: 'string' } },\n allowPositionals: true,\n });\n const path = resolve((positionals[0] ?? values.input ?? 'design-system.json') as string);\n const raw = readJsonOrFail(path, 'config');\n\n if (isConfig(raw)) {\n let site: Config;\n try {\n site = resolveConfig(raw, values['site-env'] as string | undefined);\n } catch (err) {\n // Already formatted as one issue per line, with field paths.\n console.error(`✖ ${path} is invalid as a site config:`);\n console.error(msg(err).replace(/^Invalid site config:\\n/, ''));\n process.exit(1);\n }\n // Each theme is validated in turn: `validateConfig` checks the `extends` chain\n // resolves, not that what it resolves to is a complete design system, so a\n // theme could pass there and still fail to build.\n const themes = (site.designSystem?.themes ?? {}) as Parameters<typeof resolveTheme>[0];\n let bad = false;\n for (const name of Object.keys(themes)) {\n const result = validate(resolveTheme(themes, name));\n for (const w of result.warnings) console.warn(` ! designSystem.themes.${name}: ${w}`);\n if (result.ok) continue;\n bad = true;\n console.error(`✖ ${path} — designSystem.themes.${name} is not a complete design system:`);\n for (const e of result.errors)\n console.error(` • ${e.path.join('.') || '(root)'}: ${e.message}`);\n }\n if (bad) process.exit(1);\n const n = Object.keys(themes).length;\n console.log(`✓ ${path} is a valid site config (${n} theme${n === 1 ? '' : 's'})`);\n return;\n }\n\n const result = validate(raw);\n for (const w of result.warnings) console.warn(` ! ${w}`);\n if (result.ok) {\n console.log(`✓ ${path} is valid`);\n return;\n }\n console.error(`✖ ${path} is invalid:`);\n for (const e of result.errors) console.error(` • ${e.path.join('.') || '(root)'}: ${e.message}`);\n process.exit(1);\n}\n\nasync function cmdFavicon(argv: string[]) {\n const { values } = parseArgs({\n args: argv,\n options: {\n input: { type: 'string', short: 'i' },\n out: { type: 'string', short: 'o', default: 'public' },\n 'low-res': { type: 'string' },\n // The maskable outputs must be opaque — the OS crops them to its own shape,\n // so transparency there becomes a black frame, not \"no background\".\n background: { type: 'string' },\n },\n allowPositionals: false,\n });\n if (!values.input) fail('favicon: --input <svg|png> is required');\n const source = resolve(values.input as string);\n if (!existsSync(source)) fail(`source not found: ${source}`);\n try {\n const written = await generateFavicons({\n source,\n outputDir: values.out as string,\n ...(values['low-res'] ? { lowResSource: resolve(values['low-res'] as string) } : {}),\n ...(values.background ? { backgroundColor: values.background as string } : {}),\n });\n console.log(`✓ favicons → ${values.out} (${written.length} files)`);\n } catch (err) {\n fail(msg(err));\n }\n}\n\nconst OUTPUT_KINDS = new Set<OutputKind>(['webm', 'mp4', 'poster']);\n\n/**\n * `vitops media` — encode a directory of raw video into web-ready outputs.\n *\n * Flags only, with no config file, following `favicon`: `site.favicon` exists in\n * the schema and `cmdFavicon` still doesn't read it. The same reasoning applies\n * more strongly here — `legal`, `icons` and `indexing` are anchored to a\n * `Config` because what they emit *describes the site*, and an encoder setting\n * describes a build step.\n */\nasync function cmdMedia(argv: string[]) {\n const { values } = parseArgs({\n args: argv,\n options: {\n raw: { type: 'string', default: 'raw' },\n out: { type: 'string', short: 'o', default: 'src/assets/processed' },\n 'max-width': { type: 'string' },\n crf: { type: 'string' },\n 'max-bitrate': { type: 'string' },\n audio: { type: 'boolean', default: false },\n 'poster-time': { type: 'string' },\n outputs: { type: 'string' },\n manifest: { type: 'string', default: MEDIA_MANIFEST_PATH },\n force: { type: 'boolean', default: false },\n dry: { type: 'boolean', default: false },\n },\n allowPositionals: false,\n });\n\n // Parsed here rather than passed through as strings, so a typo is caught before\n // ffmpeg spends minutes encoding at a quality nobody asked for.\n const num = (flag: string, raw: string | undefined): number | undefined => {\n if (raw === undefined) return undefined;\n const n = Number(raw);\n if (!Number.isFinite(n) || n < 0) fail(`media: --${flag} must be a non-negative number`);\n return n;\n };\n\n let outputs: OutputKind[] | undefined;\n if (values.outputs) {\n outputs = (values.outputs as string).split(',').map((s) => s.trim()) as OutputKind[];\n for (const kind of outputs)\n if (!OUTPUT_KINDS.has(kind))\n fail(`media: unknown output \"${kind}\" (expected: webm | mp4 | poster)`);\n }\n\n const maxWidth = num('max-width', values['max-width'] as string | undefined);\n const crf = num('crf', values.crf as string | undefined);\n const posterTime = num('poster-time', values['poster-time'] as string | undefined);\n\n const config: MediaConfig = {\n ...(maxWidth !== undefined ? { maxWidth } : {}),\n ...(crf !== undefined ? { crf } : {}),\n ...(values['max-bitrate'] ? { maxBitrate: values['max-bitrate'] as string } : {}),\n ...(values.audio ? { audio: true } : {}),\n ...(posterTime !== undefined ? { posterTime } : {}),\n ...(outputs ? { outputs } : {}),\n };\n\n try {\n const result = await processMedia({\n raw: values.raw as string,\n out: values.out as string,\n config,\n manifest: values.manifest as string,\n force: values.force as boolean,\n dry: values.dry as boolean,\n // An encode is minutes of silence otherwise, which reads as a hang.\n onProgress: (message) => console.error(` … ${message}`),\n });\n\n if (values.dry) {\n console.log(formatMediaPlan(result.plan));\n return;\n }\n console.log(\n `✓ media → ${values.out} (${result.written.length} written, ${result.skipped} unchanged)`,\n );\n for (const note of result.plan.notes) console.error(` ! ${note}`);\n } catch (err) {\n fail(msg(err));\n }\n}\n\n/**\n * Read + parse + validate the design system at `path`, or exit with a message.\n *\n * `path` may be a `design-system.json` **or** the larger site config that embeds\n * one — every command that judges source against the design system (`docs`,\n * `lint`, `agents`) accepts either, so a consumer who keeps their tokens in\n * `company.json` doesn't have to maintain a second file for the tooling's sake.\n * `resolveInput` tells them apart by shape and resolves the theme.\n */\nfunction loadDesignSystem(path: string, theme?: string): DesignSystem {\n const raw = readJsonOrFail(path, 'config');\n let resolved: ResolvedInput;\n try {\n resolved = resolveInput(raw, theme != null ? { theme } : {});\n } catch (err) {\n // resolveConfig formats one issue per line; a bad --theme is one line.\n fail(`${path}: ${msg(err)}`);\n }\n const result = validate(resolved.designSystem);\n if (!result.ok) {\n const where = resolved.theme != null ? ` (designSystem.themes.${resolved.theme})` : '';\n console.error(`✖ ${path}${where} is invalid:`);\n for (const e of result.errors)\n console.error(` • ${e.path.join('.') || '(root)'}: ${e.message}`);\n process.exit(1);\n }\n // Warnings go to stderr, not stdout: `vitops docs <topic>` is designed to be\n // piped, so anything here must not land in the piped document. They used to\n // be dropped entirely on this path, which meant `docs` and `agents` stayed\n // silent about collisions that `generate` and `validate` both reported.\n for (const w of result.warnings) console.warn(` ! ${w}`);\n return result.data;\n}\n\n/**\n * Report the icon vocabulary a project actually uses, and optionally build the\n * sprite from it.\n *\n * A sibling of `legal` rather than a widening of `lint`: `lint` judges classes\n * against a design-system.json, whereas icons are anchored to a Config.\n *\n * Exit codes carry the same distinction the Astro integration makes. A name the\n * config DECLARES but that doesn't resolve is a config error and fails the\n * command; a name only the SCAN found is reported and tolerated, because a bare\n * unmapped name is more often a local src/icons/*.svg than a mistake. Runtime-\n * computed names are listed with file and line so they can be declared, never\n * guessed at.\n */\nasync function cmdIcons(argv: string[]) {\n const a = parse(argv, {\n ...SITE_OPTS,\n src: { type: 'string', default: 'src' },\n sprite: { type: 'boolean', default: false },\n out: { type: 'string', short: 'o', default: 'dist' },\n json: { type: 'boolean', default: false },\n });\n\n const sitePath = a.path('input');\n const icons = existsSync(sitePath)\n ? (((await loadConfig(sitePath, a.str('site-env'))).site.icons ?? {}) as Record<\n string,\n unknown\n >)\n : {};\n const ui = (icons.ui as string) ?? 'fa7-solid';\n const brand = (icons.brand as string) ?? 'simple-icons';\n const weight = icons.weight as string | undefined;\n const weightOpt = weight ? { weight } : {};\n\n // Declared first — this throws on an unresolvable name, by design.\n let include: Record<string, string[]> = {};\n try {\n include = generateIconInclude({ ...(icons as object) } as Parameters<\n typeof generateIconInclude\n >[0]);\n } catch (e) {\n fail(msg(e));\n }\n const add = (prefix: string, name: string) => {\n const list = (include[prefix] ??= []);\n if (!list.includes(name)) list.push(name);\n };\n\n const srcDir = a.path('src');\n const scanned = existsSync(srcDir) ? collectIconRefs(scanFiles(srcDir)) : null;\n const unmapped: string[] = [];\n for (const name of scanned?.names ?? []) {\n const colon = name.indexOf(':');\n if (colon > 0) {\n add(name.slice(0, colon), name.slice(colon + 1));\n continue;\n }\n try {\n const q = resolveIcon(name, ui, weightOpt);\n add(q.slice(0, q.indexOf(':')), q.slice(q.indexOf(':') + 1));\n } catch {\n unmapped.push(name);\n }\n }\n\n const dynamic = (scanned?.dynamic ?? []).map((d) => ({\n file: relative(process.cwd(), d.file),\n line: d.line,\n expr: d.expr,\n }));\n const total = Object.values(include).flat().length;\n\n let sprite: { ids: string[]; missing: string[] } | null = null;\n if (a.flag('sprite')) {\n const aliases: Record<string, string> = {};\n for (const name of (icons.semantic as string[]) ?? []) {\n try {\n aliases[`icon-${name}`] = resolveIcon(name, ui, weightOpt);\n } catch {\n /* already reported by generateIconInclude above */\n }\n }\n const built = await buildIconSprite({ include, aliases });\n writeOut(join(a.path('out'), 'icons.svg'), built.svg);\n sprite = { ids: built.ids, missing: built.missing };\n }\n\n if (a.flag('json')) {\n process.stdout.write(\n `${JSON.stringify({ ui, brand, weight: weight ?? null, include, unmapped, dynamic, sprite }, null, 2)}\\n`,\n );\n } else {\n console.log(`icon sets: ui=${ui} brand=${brand}${weight ? ` weight=${weight}` : ''}`);\n for (const [prefix, names] of Object.entries(include))\n console.log(` ${prefix}: ${names.length} — ${names.join(', ')}`);\n console.log(`✓ ${total} icon(s) across ${Object.keys(include).length} set(s)`);\n if (sprite) console.log(`✓ sprite → ${a.path('out')}/icons.svg (${sprite.ids.length} symbols)`);\n for (const d of dynamic)\n console.log(` ! ${d.file}:${d.line} ${d.expr} — computed at runtime, declare it in icons`);\n if (unmapped.length)\n console.log(\n ` ! not in the '${ui}' map: ${unmapped.join(', ')} (fine for a local svg or sprite id)`,\n );\n if (sprite?.missing.length)\n console.log(` ! absent from the sprite: ${sprite.missing.join(', ')}`);\n }\n\n // Dynamic holes and unmapped bare names are warnings, not failures — same\n // tolerance `lint` shows template holes, but visible rather than silent.\n if (sprite?.missing.length) process.exitCode = 1;\n}\n\nasync function cmdLegal(argv: string[]) {\n const a = parse(argv, {\n ...SITE_OPTS,\n doc: { type: 'string', short: 'd', multiple: true },\n format: { type: 'string', short: 'f', default: 'md' },\n out: { type: 'string', short: 'o' },\n });\n\n const output = a.str('format') as LegalOutput;\n if (!LEGAL_OUTPUTS.has(output))\n fail(`unknown format \"${output}\" (expected: ${[...LEGAL_OUTPUTS].join(' | ')})`);\n\n const requested = a.list('doc');\n for (const d of requested)\n if (!LEGAL_DOCS.has(d as LegalDoc))\n fail(`unknown doc \"${d}\" (expected: ${[...LEGAL_DOCS].join(' | ')})`);\n\n const site = await siteConfig(a);\n const docs = requested.length ? (requested as LegalDoc[]) : enabledDocs(site);\n if (docs.length === 0)\n fail(\n 'no legal documents are enabled — set site.legal.privacyPolicy.enabled (or termsOfService / cookieConsent) in your config, or name one with --doc',\n );\n\n const files = generateLegal(site, { docs, output });\n\n /**\n * A processor placed nowhere cannot appear in the cross-border disclosure — there\n * is nothing true to say about it — so it drops out of that section silently,\n * which is the failure that looks tidy. Reported here rather than in the document:\n * a policy must not editorialise about its own gaps.\n *\n * On **stderr** because stdout is the document and gets piped or redirected.\n */\n if (docs.includes('privacy'))\n for (const name of processorsMissingLocation(site))\n console.error(\n ` ! ${name}: no country, storage or operatorCountry declared — it will not appear in the cross-border transfer disclosure. Add one, or state storage: [{ country: \"${JURISDICTION_COUNTRIES[site.site.legal?.jurisdiction ?? 'ca']}\" }] to say it does not leave the country.`,\n );\n\n const REVIEW =\n ' These are generated from your config and are not legal advice — review them before publishing.';\n\n if (!a.opt('out')) {\n process.stdout.write(Object.values(files).join('\\n'));\n // On stderr, so it survives a redirect of the document itself. It used to be\n // omitted entirely on this path — the one path where the output is most\n // likely being piped straight into a page.\n console.error(REVIEW);\n return;\n }\n const outDir = a.path('out');\n mkdirSync(outDir, { recursive: true });\n // Enabling a document is a config edit with consequences outside the config —\n // a new file needs a page, a route, a menu link, a policyUrl. Flipping\n // `cookieConsent.enabled` silently produced `cookie-notice.*` and said only\n // \"3 files\", so nothing pointed at the work that had just appeared.\n const isNew = (name: string) => !existsSync(join(outDir, name));\n const added = Object.keys(files).filter(isNew);\n for (const [name, content] of Object.entries(files)) writeFileSync(join(outDir, name), content);\n console.log(`✓ legal → ${outDir}`);\n for (const name of Object.keys(files))\n console.log(` ${name}${added.includes(name) ? ' (new)' : ''}`);\n if (added.length)\n console.log(\n added.length === 1\n ? `\\n That document is new. It needs somewhere to live: a page or route that renders it, ` +\n `and a link to it${added[0]?.startsWith('cookie') ? ' — including the URL your consent banner points at' : ''}.`\n : `\\n Those ${added.length} documents are new. Each needs somewhere to live: a page or ` +\n `route that renders it, and a link to it — for a cookie notice, including the URL your ` +\n `consent banner points at.`,\n );\n console.log(REVIEW);\n}\n\n/**\n * `robots.txt` — anchored to a `Config` like `legal`/`icons`/`search`, because\n * what it emits describes a site (which environment, which sitemap) rather\n * than a token set.\n */\nasync function cmdRobots(argv: string[]) {\n const a = parse(argv, {\n ...SITE_OPTS,\n out: { type: 'string', short: 'o' },\n });\n\n const siteEnv = a.str('site-env');\n const site = await loadConfig(a.path('input'), siteEnv);\n const content = renderRobotsTxt(site, { env: siteEnv });\n\n if (!a.opt('out')) {\n process.stdout.write(content);\n return;\n }\n const file = join(a.path('out'), 'robots.txt');\n writeOut(file, content);\n console.log(`✓ robots.txt → ${file}`);\n}\n\n/**\n * Fetch and cache a client's Google Business Profile reviews.\n *\n * Reviews live only on the legacy v4 Business Profile API, and the calling GCP\n * project must be allowlisted by Google before any call succeeds — see\n * `@getvitops/utils/reviews`'s `gbp.ts`. Auth is always user OAuth (the same\n * VITOPS_GOOGLE_* / ADC credential `search setup` uses): reading a client's own\n * reviews is a person acting with owner/manager rights, not a deploy robot.\n *\n * The cache (`.vitops/reviews.json` by default) is meant to be committed — a\n * build should not need a live Google credential to render reviews that change\n * slowly. `--check` fetches and diffs against it without writing, for CI to flag\n * a stale commit; a real run merges only the locations it fetched, so one\n * location's failure leaves every other location's cached reviews untouched.\n */\nasync function cmdReviews(argv: string[]) {\n const a = parse(argv, {\n ...SITE_OPTS,\n location: { type: 'string' },\n cache: { type: 'string', default: REVIEWS_CACHE_PATH },\n out: { type: 'string', short: 'o' },\n dry: { type: 'boolean', default: false },\n check: { type: 'boolean', default: false },\n });\n\n const site = await siteConfig(a);\n const reviewsConfig = site.site.reviews as ReviewsConfig | undefined;\n const knownLocations = new Set(\n Object.entries(site.organization?.locations ?? {})\n .filter(([, loc]) => loc.listings?.google)\n .map(([key]) => key),\n );\n\n // Env-only, no network — safe to resolve before the --dry check, the same way\n // `search setup`'s credential presence shapes its own plan.\n const oauth = loadGoogleOAuth();\n\n const p = planReviews({\n config: reviewsConfig,\n knownLocations,\n hasCredential: !!oauth,\n ...(a.opt('location') ? { onlyLocation: a.str('location') } : {}),\n });\n\n console.log(formatReviewsPlan(p));\n if (p.blocked) return;\n\n const active = p.locations.filter((l) => !l.skip);\n if (a.flag('dry')) {\n console.log('\\n(--dry: nothing was fetched)');\n return;\n }\n\n const cachePath = a.path('cache');\n const existing = readReviewsCache(cachePath);\n\n const project = site.site?.google?.project;\n requireAdcProject(project, a.str('input'));\n const auth: GoogleAuthLike = {\n token: await googleAccessToken({ kind: 'oauth', oauth: oauth! }),\n ...(project ? { quotaProject: project } : {}),\n };\n\n const fetched: Record<string, LocationReviews> = {};\n let failed = false;\n for (const loc of active) {\n const r = await fetchReviews(auth, loc.accountId!, loc.locationId!);\n if (!r.ok) {\n console.error(`✖ ${loc.key}: ${r.message}`);\n failed = true;\n continue;\n }\n console.log(`✓ ${loc.key}: ${r.reviews.length} review(s)`);\n fetched[loc.key] = {\n locationId: loc.locationId!,\n accountId: loc.accountId!,\n fetchedAt: new Date().toISOString(),\n ...(r.averageRating !== undefined ? { averageRating: r.averageRating } : {}),\n ...(r.totalReviewCount !== undefined ? { totalReviewCount: r.totalReviewCount } : {}),\n reviews: r.reviews,\n };\n }\n\n // ── --check: read-only, and nothing is written ──────────────────────────────\n if (a.flag('check')) {\n let drift = false;\n for (const [key, loc] of Object.entries(fetched)) {\n const before = existing?.locations[key];\n const beforeIds = new Set((before?.reviews ?? []).map((rv) => rv.id));\n const afterIds = loc.reviews.map((rv) => rv.id);\n const changed =\n beforeIds.size !== afterIds.length || afterIds.some((id) => !beforeIds.has(id));\n if (changed) {\n drift = true;\n console.log(\n ` ~ ${key}: cache is stale (${before?.reviews.length ?? 0} → ${loc.reviews.length})`,\n );\n }\n }\n if (drift || failed) process.exit(1);\n console.log('\\n✓ cache matches live reviews');\n return;\n }\n\n if (Object.keys(fetched).length) {\n const merged = toCache(mergeCache(existing, fetched).locations);\n writeReviewsCache(cachePath, merged);\n console.log(` cache → ${relative(process.cwd(), cachePath)}`);\n\n if (a.opt('out')) {\n const outDir = a.path('out');\n mkdirSync(outDir, { recursive: true });\n for (const [key, loc] of Object.entries(merged.locations)) {\n const locConfig = reviewsConfig?.locations[key];\n const selected = locConfig ? selectReviews(loc.reviews, locConfig) : loc.reviews;\n writeFileSync(join(outDir, `${key}.json`), `${JSON.stringify(selected, null, 2)}\\n`);\n }\n console.log(` selected reviews → ${outDir}`);\n }\n }\n\n if (failed) process.exit(1);\n}\n\n// The framework asset root shipped inside @getvitops/generator (holds the Bricks\n// PHP that generateDocs reads for the element reference).\nfunction generatorAssetsDir(): string {\n const pkg = createRequire(import.meta.url).resolve('@getvitops/generator/package.json');\n return join(dirname(pkg), 'assets');\n}\n\n/**\n * Input path for the read-only commands (`docs`, `lint`) when `-i` is absent.\n *\n * `site.json` is the file `vitops init --site` and every scaffold write, so it\n * is the default; a bare `design-system.json` is the fallback for projects that\n * keep tokens on their own. An explicit `-i` is never second-guessed. If\n * neither exists the legacy name is returned so the \"config not found\" message\n * names a path a reader recognises.\n */\nfunction defaultDesignInput(argv: string[], value: string): string {\n const explicit = argv.some((a) => a === '-i' || a === '--input' || a.startsWith('--input='));\n if (explicit) return value;\n for (const name of ['site.json', 'design-system.json'])\n if (existsSync(resolve(name))) return name;\n return value;\n}\n\n/** Print a live reference doc (rendered from the project's config) to stdout. */\nasync function cmdDocs(argv: string[]) {\n const { values, positionals } = parseArgs({\n args: argv,\n options: {\n input: { type: 'string', short: 'i', default: 'design-system.json' },\n theme: { type: 'string' },\n all: { type: 'boolean', default: false },\n },\n allowPositionals: true,\n });\n const topic = positionals[0];\n const topicList = () =>\n Object.entries(TOPICS)\n .map(([name, t]) => ` ${name.padEnd(10)} ${t.summary}`)\n .join('\\n');\n if (!topic && !values.all) {\n console.log(\n `vitops docs <topic> — print a live design-system reference to stdout\\n\\nTopics:\\n${topicList()}\\n\\nAlso: vitops docs --all (every topic, concatenated)`,\n );\n return;\n }\n if (topic && !TOPICS[topic]) fail(`unknown topic \"${topic}\". Valid topics:\\n${topicList()}`);\n const names = values.all ? Object.keys(TOPICS) : [topic as string];\n // An external topic (blocks) needs no design system, so don't demand a config for it.\n const docs = names.every((n) => TOPICS[n]!.external)\n ? {}\n : generateDocs(\n loadDesignSystem(\n resolve(defaultDesignInput(argv, values.input as string)),\n values.theme as string | undefined,\n ),\n generatorAssetsDir(),\n );\n const parts: string[] = [];\n for (const name of names) {\n const t = TOPICS[name]!;\n if (!t.external) {\n parts.push((docs as Record<string, string>)[t.path]!);\n continue;\n }\n // Rendered by the plugin that owns the data; the generator cannot import it.\n const doc = await externalDoc(t.external);\n if (doc) parts.push(doc);\n else if (!values.all)\n fail(`topic \"${name}\" needs @getvitops/${t.external} installed in this project`);\n }\n process.stdout.write(parts.join('\\n'));\n}\n\n/** Render a topic owned by an optional package, resolved from the project, not from the CLI. */\nasync function externalDoc(pkg: 'emdash'): Promise<string | null> {\n try {\n // `require.resolve` rejects an ESM-only `exports` map, but `./package.json` is exported\n // for exactly this: find the package, then read its `import` entry from the manifest.\n const manifest = createRequire(join(process.cwd(), 'package.json')).resolve(\n `@getvitops/${pkg}/package.json`,\n );\n const exp = (\n JSON.parse(readFileSync(manifest, 'utf8')) as {\n exports: Record<string, { import: string }>;\n }\n ).exports['.']!.import;\n const entry = join(dirname(manifest), exp);\n const mod = (await import(pathToFileURL(entry).href)) as { renderBlocksDoc?: () => string };\n return mod.renderBlocksDoc?.() ?? null;\n } catch {\n return null;\n }\n}\n\nfunction cmdLint(argv: string[]) {\n const { values, positionals } = parseArgs({\n args: argv,\n options: {\n input: { type: 'string', short: 'i', default: 'design-system.json' },\n theme: { type: 'string' },\n format: { type: 'string', short: 'f', default: 'bricks' },\n src: { type: 'string', short: 's', default: 'src' },\n // Suggestions are advisory: a reuse rule that failed CI the day it shipped\n // would be a worse defect than the drift it reports.\n strict: { type: 'boolean', default: false },\n fix: { type: 'boolean', default: false },\n },\n // Positionals are explicit files to lint instead of scanning `--src`. This is\n // what makes the command usable as a pre-commit hook: `vp`'s `staged` key\n // appends the staged paths to whatever it runs, so a command that refused\n // positionals could not be wired into the one place the feedback actually\n // lands — at the moment the code is written, rather than whenever someone\n // remembers to run a linter.\n allowPositionals: true,\n });\n const format = values.format as StylesheetFormat;\n if (!LINT_FORMATS.has(format))\n fail(`unknown format \"${format}\" (expected: bricks | css | tailwind)`);\n const ds = loadDesignSystem(\n resolve(defaultDesignInput(argv, values.input as string)),\n values.theme as string | undefined,\n );\n const srcDir = resolve(values.src as string);\n if (!positionals.length && !existsSync(srcDir)) fail(`source directory not found: ${srcDir}`);\n\n // Ask the generator what it actually emits rather than re-deriving the rules\n // here — a linter that models the vocabulary separately just drifts into\n // reporting classes that do exist.\n const palette = expandPalette(ds.colors.palette);\n const roleClasses = roleColorUtilities(\n Object.entries(ds.colors.roles).map(([role, spec]) =>\n functionalRole(role, roleHue(spec), palette[roleHue(spec)]!, roleKind(spec)),\n ),\n ds.colors.utilities ?? ['bg', 'text', 'icon', 'border'],\n ).map((u) => u.cls);\n\n // Explicit paths win over the scan. A pre-commit hook is handed whatever was\n // staged, which includes deletions and files of every kind, so unreadable and\n // uninteresting paths are skipped rather than fatal — a hook that fails because\n // someone staged a PNG is a hook that gets removed.\n const LINTABLE = new Set(['.astro', '.html', '.js', '.jsx', '.ts', '.tsx', '.vue', '.svelte']);\n const named = positionals\n .filter((p) => LINTABLE.has(extname(p)) || extname(p) === '.css')\n .flatMap((p) => {\n try {\n return [{ path: p, text: readFileSync(resolve(p), 'utf8') }];\n } catch {\n return [];\n }\n });\n // `.css` is not in the default extension set — the class linter reads markup,\n // not stylesheets — so the reuse rules get their own pass. Component files are\n // scanned twice on purpose: once for their class attributes, once for any\n // `<style>` block, which is where hand-rolled layout tends to accumulate.\n const files = positionals.length\n ? named.filter((f) => extname(f.path) !== '.css')\n : scanFiles(srcDir);\n const cssFiles = positionals.length\n ? named\n : [...files, ...scanFiles(srcDir, { exts: ['.css'] })];\n // Nothing staged that this command has an opinion about — say so and succeed,\n // rather than reporting a clean run over zero files as if it had checked them.\n if (positionals.length && !cssFiles.length) {\n console.log('✓ no lintable files among the paths given');\n return;\n }\n // The pre-1.0 colour grammar, built from this config's own role names. Read\n // from CSS and <style> blocks — the half of a codebase that references the\n // design system by token rather than by class, and the half nothing checked.\n const renames = movedTokens(ds.colors.roles);\n const tokenFindings = lintTokens(cssFiles, renames);\n const findings = [\n ...lintSource(files, vocabulary(ds, roleClasses), format),\n ...lintCss(cssFiles, format),\n // Markup shape, which is the only one of the three that can see a repeated\n // card set laid out with no bad class and no hand-written CSS — every class\n // real, the pattern simply unused. Reads `files`, not `cssFiles`: a stylesheet\n // has no markup, and passing the doubled list would report each component\n // twice.\n ...lintMarkup(files),\n ...tokenFindings,\n ];\n\n if (values.fix === true) {\n // Only the token renames are rewritten. Everything else this command\n // reports is a judgement call — `bg-navy-d`'s \"suggestion\" is the linter's\n // best guess at what you meant, and silently writing a guess into source is\n // worse than printing it.\n const byFile = new Map<string, Set<string>>();\n for (const f of tokenFindings)\n (byFile.get(f.file) ?? byFile.set(f.file, new Set()).get(f.file)!).add(f.fix.from);\n for (const [file, names] of byFile) {\n const subset = Object.fromEntries([...names].map((n) => [n, renames[n] as string]));\n writeFileSync(file, applyRenames(readFileSync(file, 'utf8'), subset));\n }\n const remaining = findings.length - tokenFindings.length;\n console.log(\n `✓ rewrote ${tokenFindings.length} token reference${tokenFindings.length === 1 ? '' : 's'} ` +\n `in ${byFile.size} file${byFile.size === 1 ? '' : 's'}` +\n (remaining\n ? `\\n! ${remaining} other finding${remaining === 1 ? '' : 's'} need a decision — ` +\n `re-run without --fix to see ${remaining === 1 ? 'it' : 'them'}`\n : ''),\n );\n if (remaining) process.exitCode = 1;\n return;\n }\n\n // Name what was actually checked. With explicit paths `--src` was never read,\n // and reporting it would claim a scan that did not happen.\n const scope = positionals.length ? 'the files given' : (values.src as string);\n const scanned = positionals.length ? cssFiles.length : files.length;\n\n if (!findings.length) {\n console.log(\n `✓ no unresolvable framework classes or missed primitives in ${scope} ` +\n `(${scanned} file${scanned === 1 ? '' : 's'} checked)`,\n );\n return;\n }\n\n const errors = findings.filter((f) => f.severity === 'error');\n const suggestions = findings.filter((f) => f.severity === 'suggestion');\n // Errors first: a class that resolves to nothing is a defect, a reuse\n // suggestion is a judgement call, and mixing them buries the former.\n for (const f of [...errors, ...suggestions]) {\n console.error(`${f.file}:${f.line} ${f.severity === 'error' ? f.cls : `(${f.cls})`}`);\n console.error(` ${f.reason}`);\n if (f.suggestion) console.error(` try: ${f.suggestion}`);\n }\n\n const count = (n: number, one: string, many: string) => `${n} ${n === 1 ? one : many}`;\n const parts: string[] = [];\n if (errors.length) parts.push(count(errors.length, 'unresolvable class', 'unresolvable classes'));\n if (suggestions.length) parts.push(count(suggestions.length, 'suggestion', 'suggestions'));\n const failing = errors.length > 0 || (values.strict === true && suggestions.length > 0);\n console.error(\n `\\n${failing ? '✖' : '!'} ${parts.join(', ')} in ` +\n `${scanned} file${scanned === 1 ? '' : 's'}` +\n (suggestions.length && !values.strict ? ' (suggestions do not fail; use --strict)' : ''),\n );\n if (failing) process.exit(1);\n}\n\n/**\n * Find the config when `design-system.json` isn't there.\n *\n * Keyed to SHAPE, not just to the filename — `resolveInput` already tells a\n * design system from the config that embeds one, and consumers name that file\n * whatever they like (`company.json`, `site.json`). The candidate list is\n * ordered, and a tie is resolved by taking the first: this only ever supplies a\n * default that the run then prints, so a wrong guess is visible rather than\n * silent, and `-i` overrides it.\n */\nfunction discoverConfig(cwd: string): string | undefined {\n const candidates = [\n 'design-system.json',\n 'vitops.json',\n 'site.json',\n 'company.json',\n 'organization.json',\n ];\n for (const name of candidates) {\n const p = resolve(cwd, name);\n if (!existsSync(p)) continue;\n try {\n const raw = JSON.parse(readFileSync(p, 'utf8'));\n // Either kind counts: a bare design system, or a config that embeds one.\n if (isConfig(raw) || (raw && typeof raw === 'object' && 'colors' in raw)) return name;\n } catch {\n // Unparseable: not a candidate, and the real error surfaces if -i names it.\n }\n }\n return undefined;\n}\n\nfunction cmdAgents(rawArgv: string[]) {\n // `vitops agents sync` is the explicit verb; bare `vitops agents` does the same.\n const argv = rawArgv[0] === 'sync' ? rawArgv.slice(1) : rawArgv;\n const { values } = parseArgs({\n args: argv,\n options: {\n input: { type: 'string', short: 'i', default: 'design-system.json' },\n theme: { type: 'string' },\n out: { type: 'string', short: 'o', default: 'AGENTS.md' },\n 'docs-dir': { type: 'string' },\n },\n allowPositionals: false,\n });\n // This command WRITES documentation, so getting the input path wrong is not a\n // missing feature, it is a false statement committed to the repo: it used to\n // warn and then write \"tokens live in design-system.json\" into a project whose\n // tokens are in company.json, with the emitted commands broken to match. So\n // when the default path is absent, look for the config rather than assuming\n // it — and fail if there is nothing to name.\n const explicitInput = argv.includes('-i') || argv.includes('--input');\n let inputRel = values.input as string;\n if (!explicitInput && !existsSync(resolve(inputRel))) {\n const found = discoverConfig(process.cwd());\n if (found) {\n inputRel = found;\n console.warn(` ⚠ no design-system.json here — using ${found} (pass -i to choose another)`);\n } else {\n fail(\n `config not found: ${resolve(inputRel)}\\n` +\n ` This command writes the path into ${values.out}, so it will not guess one. ` +\n `Pass -i <path> if your tokens live elsewhere (a site config works), or run \\`vitops init\\`.`,\n );\n }\n }\n // Validate the config (fail on an invalid one). Docs themselves are rendered\n // live by `vitops docs`, so nothing is generated into the repo from it here.\n let ds: DesignSystem | null = null;\n if (existsSync(resolve(inputRel)))\n ds = loadDesignSystem(resolve(inputRel), values.theme as string | undefined);\n else console.warn(` ⚠ ${inputRel} not found — run \\`vitops init\\` to scaffold one`);\n\n // Explicit --docs-dir = legacy layout: write the docs bundle as files, no skill.\n const legacyDocsDir = values['docs-dir'] as string | undefined;\n let summary: string;\n let legacyBlock: string | undefined;\n\n if (legacyDocsDir) {\n if (!ds) fail(`--docs-dir needs a config to render docs from (${inputRel} not found)`);\n let docs: Record<string, string> = {};\n try {\n docs = generateDocs(ds, generatorAssetsDir());\n } catch (err) {\n fail(`could not generate docs: ${msg(err)}`);\n }\n for (const [rel, content] of Object.entries(docs)) {\n const p = resolve(legacyDocsDir, rel);\n mkdirSync(dirname(p), { recursive: true });\n writeFileSync(p, content);\n }\n summary = `wrote ${Object.keys(docs).length} docs to ${legacyDocsDir}/`;\n legacyBlock = [\n AGENTS_START,\n `Built with the Vitops design system (\\`@getvitops/*\\`); tokens and site facts live in \\`${inputRel}\\`.`,\n `Prefer its classes and patterns over hand-written CSS. Reference docs are in \\`${legacyDocsDir}/\\``,\n `(\\`css/classes.md\\`, \\`authoring.md\\`, \\`formats.md\\`, \\`bricks/elements.md\\`); regenerate with \\`vitops agents --docs-dir ${legacyDocsDir}\\`.`,\n AGENTS_END,\n ].join('\\n');\n } else {\n // Link the packaged skill (ships inside @getvitops/cli) into the agent\n // discovery locations; nothing is generated into the repo.\n for (const warn of linkPackagedSkills(process.cwd())) console.warn(` ⚠ ${warn}`);\n const target = findSkillTarget(process.cwd());\n if (target) {\n for (const dir of ['.agents', '.claude']) {\n const warn = linkSkill(resolve(dir, 'skills', SKILL_NAME), target);\n if (warn) console.warn(` ⚠ ${warn}`);\n }\n summary = `linked {.agents,.claude}/skills/${SKILL_NAME} → ${target}`;\n } else {\n console.warn(\n ` ⚠ could not find node_modules/@getvitops/cli/skill — install @getvitops/cli as a devDependency, then re-run \\`vitops agents\\``,\n );\n summary = 'no skill linked';\n }\n }\n\n const block =\n legacyBlock ??\n renderAgentsBlock({\n config: inputRel,\n skills: discoverBlockSkills(process.cwd()),\n });\n\n const outPath = resolve(values.out as string);\n const existing = existsSync(outPath) ? readFileSync(outPath, 'utf8') : undefined;\n const { text, action } = upsertAgentsBlock(existing, block);\n if (existing === undefined) mkdirSync(dirname(outPath), { recursive: true });\n writeFileSync(outPath, text);\n const verb = { created: 'created', updated: 'updated block in', added: 'added block to top of' }[\n action\n ];\n console.log(`✓ ${verb} ${values.out} + ${summary}`);\n}\n\nasync function main() {\n const [command, ...rest] = process.argv.slice(2);\n if (!command || command === '-h' || command === '--help' || command === 'help') {\n console.log(HELP);\n return;\n }\n // Answered before dispatch, because `parseArgs` is strict and every\n // subcommand would otherwise reject `--help` as an unknown option.\n if (wantsHelp(rest)) {\n console.log(helpFor(command, rest));\n return;\n }\n switch (command) {\n case 'generate':\n return cmdGenerate(rest);\n case 'init':\n return cmdInit(rest);\n case 'validate':\n return cmdValidate(rest);\n case 'favicon':\n return cmdFavicon(rest);\n case 'agents':\n return cmdAgents(rest);\n case 'docs':\n return cmdDocs(rest);\n case 'lint':\n return cmdLint(rest);\n case 'legal':\n return cmdLegal(rest);\n case 'robots':\n return cmdRobots(rest);\n case 'reviews':\n return cmdReviews(rest);\n case 'icons':\n return cmdIcons(rest);\n case 'search':\n return cmdSearch(rest);\n case 'ads':\n return cmdAds(rest);\n case 'crm':\n return cmdCrm(rest);\n case 'email':\n return cmdEmail(rest);\n case 'emdash':\n return cmdEmdash(rest);\n case 'setup':\n return cmdSetup(rest);\n case 'media':\n return cmdMedia(rest);\n case 'audit':\n return cmdAudit(rest);\n default:\n fail(`unknown command \"${command}\" (expected: ${COMMANDS.join(' | ')}). Try: vitops --help`);\n }\n}\n\nmain().catch((err) => fail(msg(err)));\n"],"mappings":"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"}
|