@estiva-app/ui 0.21.0 → 0.21.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"app-checks.d.ts","sourceRoot":"","sources":["../../src/gates/app-checks.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,EAA0B,WAAW,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAE/E,MAAM,WAAW,eAAe;IAC9B,4EAA4E;IAC5E,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,2FAA2F;IAC3F,IAAI,EAAE,MAAM,CAAA;IACZ,8GAA8G;IAC9G,KAAK,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAA;IACvC,+DAA+D;IAC/D,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB,wGAAwG;IACxG,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CACnB;AAED,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAoBpD,CAAA;AAaD,wBAAgB,SAAS,CAAC,CAAC,EAAE,WAAW,EAAE,EAAE,GAAS,EAAE,IAAI,EAAE,KAAsC,EAAE,YAA8D,EAAE,OAA8C,EAAE,EAAE,eAAe,GAAG,UAAU,EAAE,CAyJpP"}
1
+ {"version":3,"file":"app-checks.d.ts","sourceRoot":"","sources":["../../src/gates/app-checks.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,EAA0B,WAAW,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAE/E,MAAM,WAAW,eAAe;IAC9B,4EAA4E;IAC5E,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,2FAA2F;IAC3F,IAAI,EAAE,MAAM,CAAA;IACZ,8GAA8G;IAC9G,KAAK,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAA;IACvC,+DAA+D;IAC/D,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;IACvB,wGAAwG;IACxG,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CACnB;AAED,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAoBpD,CAAA;AAaD,wBAAgB,SAAS,CAAC,CAAC,EAAE,WAAW,EAAE,EAAE,GAAS,EAAE,IAAI,EAAE,KAAsC,EAAE,YAA8D,EAAE,OAA8C,EAAE,EAAE,eAAe,GAAG,UAAU,EAAE,CA2JpP"}
@@ -318,8 +318,8 @@ function statusOf(checks) {
318
318
  if (pass + part > 0) return fail === 0 && unknown > 0 ? "unknown" : "started";
319
319
  return fail > 0 ? "none" : "unknown";
320
320
  }
321
- async function runRepo(ROOT, h) {
322
- const checksFile = join3(ROOT, "scripts", "gates-checks.mjs");
321
+ async function runRepo(ROOT, app, h) {
322
+ const checksFile = join3(ROOT, app, "scripts", "gates-checks.mjs");
323
323
  const define = (await import(pathToFileURL3(checksFile).href)).default;
324
324
  const spec = define(h);
325
325
  const git = (...a) => {
@@ -346,17 +346,18 @@ async function runRepo(ROOT, h) {
346
346
  }
347
347
  return { spec, report: { schema: 1, repo: spec.repo, branch: git("rev-parse", "--abbrev-ref", "HEAD"), commit: git("rev-parse", "--short", "HEAD"), engine: ENGINE, rows } };
348
348
  }
349
- function runSibling(dir, shown) {
349
+ function runSibling(dir, app, shown) {
350
350
  const copy = join3(dir, "scripts", "gates-status.mjs");
351
- const installed = createRequire3(join3(dir, "package.json"));
351
+ const installed = createRequire3(join3(dir, app, "package.json"));
352
352
  let script = copy;
353
353
  const args = ["--json"];
354
354
  if (!existsSync3(copy)) {
355
355
  try {
356
356
  script = join3(installed.resolve("@estiva-app/ui/package.json"), "..", "dist", "gates", "cli.js");
357
357
  args.unshift("status");
358
+ if (app !== ".") args.push("--app", app);
358
359
  } catch {
359
- return { error: `${shown} has neither its own gates-status.mjs nor @estiva-app/ui installed` };
360
+ return { error: `${shown} has neither its own gates-status.mjs nor @estiva-app/ui installed${app === "." ? "" : ` in ${app}`}` };
360
361
  }
361
362
  if (!existsSync3(script)) return { error: `${shown} has an @estiva-app/ui with no gates engine: install 0.21.0 or later` };
362
363
  }
@@ -392,11 +393,11 @@ function summary(rows) {
392
393
  const n = (s) => rows.filter((r) => r.status === s).length;
393
394
  return `${STATUS.done} ${n("done")} done \xB7 ${STATUS.started} ${n("started")} started \xB7 ${STATUS.none} ${n("none")} not started \xB7 ${STATUS.unknown} ${n("unknown")} could not check`;
394
395
  }
395
- async function runStatus({ root = process.cwd(), json = false, detail = false } = {}) {
396
+ async function runStatus({ root = process.cwd(), app = ".", json = false, detail = false } = {}) {
396
397
  const ROOT = resolve2(root);
397
398
  const h = helpers(ROOT);
398
399
  const shown = (dir) => (relative3(ROOT, dir) || dir).replace(/\\/g, "/");
399
- const { spec, report } = await runRepo(ROOT, h);
400
+ const { spec, report } = await runRepo(ROOT, app, h);
400
401
  if (json) return JSON.stringify(report);
401
402
  const out = [];
402
403
  out.push("UI Guardrails \xB7 gates:status", "Read from the code. Nothing here is a hand-ticked list.", "");
@@ -417,7 +418,7 @@ async function runStatus({ root = process.cwd(), json = false, detail = false }
417
418
  found.push({ name: s.name, note: `not found at ${shown(dir)} (set ${s.env} to point at it)`, missing: true });
418
419
  continue;
419
420
  }
420
- const r = runSibling(dir, shown(dir));
421
+ const r = runSibling(dir, s.app ?? ".", shown(dir));
421
422
  if (!r.report) {
422
423
  found.push({ name: s.name, note: r.error ?? "no report", missing: true });
423
424
  continue;
@@ -474,4 +475,4 @@ export {
474
475
  helpers,
475
476
  runStatus
476
477
  };
477
- //# sourceMappingURL=chunk-AWO7SZSA.js.map
478
+ //# sourceMappingURL=chunk-AUXD4GCY.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/gates/count.ts", "../../src/gates/hook.ts", "../../src/gates/status.ts"],
4
+ "sourcesContent": ["/// <reference types=\"node\" />\n/**\n * Write `.gates-count.json`: what the gate finds, per rule \u2014 errors, warnings\n * and escapes (seam S3 in docs/GATES.md \u00A716). Written once (\u00A723): until UIG-10\n * Peek, Ship and this package each carried the same script, differing only in\n * the repo's name and which rules it seeded.\n *\n * Runs after the gate lint (npm's `postlint:rules`), so CI runs it too. The\n * file is committed: nothing compares it yet, and the later ratchet needs the\n * history to start at the first commit. It lints with the gate's own config,\n * with escapes reported, so an escape is counted rather than silent. The file\n * is rewritten only when a count changes, so running the lint does not leave a\n * changed file behind every time.\n *\n * It fails when an `eslint-disable` switched a gate rule off: that is not an\n * escape, and the escape count cannot see it.\n */\nimport { existsSync, readFileSync, writeFileSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { join, relative } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport type { ESLint as ESLintClass } from 'eslint'\nimport { APP_RULE_IDS, countGates, PACKAGE_RULE_IDS, type GateCount } from '../eslint/index'\n\nexport interface CountOptions {\n /** The folder the gate lints from, where `.gates-count.json` is written. */\n root?: string\n /** The name written into the file: `peek`, `ship`, `estiva-ui`, or the app's. */\n repo: string\n /** `app` seeds the app rules; `package` this package's inward set (UIG-5). */\n audience?: 'app' | 'package'\n /** The gate's config file, relative to `root`. */\n config?: string\n}\n\nexport interface CountResult extends GateCount {\n changed: boolean\n /** What to print, line by line. */\n lines: string[]\n /** What to print to stderr when a rule was switched off; empty when none was. */\n failures: string[]\n}\n\n/** ESLint from where the gate lints, never from beside this file: an app's own install. */\nasync function eslintFrom(root: string): Promise<typeof ESLintClass> {\n const resolved = createRequire(join(root, 'package.json')).resolve('eslint')\n return ((await import(pathToFileURL(resolved).href)) as { ESLint: typeof ESLintClass }).ESLint\n}\n\nexport async function writeGateCount({ root = process.cwd(), repo, audience = 'app', config = 'eslint.gates.config.js' }: CountOptions): Promise<CountResult> {\n const out = join(root, '.gates-count.json')\n const ESLint = await eslintFrom(root)\n const eslint = new ESLint({\n cwd: root,\n overrideConfigFile: join(root, config),\n overrideConfig: { settings: { estiva: { reportEscapes: true } } },\n })\n // The set the gate runs, so a rule with nothing to report still has its row.\n const { rules, disabled } = countGates(await eslint.lintFiles(['.']), audience === 'package' ? PACKAGE_RULE_IDS : APP_RULE_IDS)\n\n const previous = existsSync(out) ? (JSON.parse(readFileSync(out, 'utf8')) as { rules?: unknown }) : null\n const changed = JSON.stringify(previous?.rules) !== JSON.stringify(rules)\n if (changed) {\n const count = { schemaVersion: 1, repo, generatedAt: new Date().toISOString(), rules }\n writeFileSync(out, `${JSON.stringify(count, null, 2)}\\n`)\n }\n const lines = [changed ? '.gates-count.json: written' : '.gates-count.json: unchanged']\n for (const [id, c] of Object.entries(rules)) lines.push(` ${id}: ${c.errors} errors, ${c.warnings} warnings, ${c.escapes} escapes`)\n\n const failures: string[] = []\n if (disabled.length > 0) {\n failures.push('', 'An eslint-disable switched a gate rule off. Write the reason above the element instead: // @estiva-escape: <reason>')\n for (const d of disabled) failures.push(` ${relative(root, d.filePath)}:${d.line} ${d.ruleId}`)\n }\n return { rules, disabled, changed, lines, failures }\n}\n", "/// <reference types=\"node\" />\n/**\n * The editor gate: before Claude writes a `.ts` or `.tsx` under `src/`, lint the\n * text it is about to write with the gate, and refuse the write on an error.\n * Written once (docs/GATES.md \u00A723): until UIG-10 Peek, Ship and this package\n * each carried their own copy of this script.\n *\n * A `PreToolUse` hook in the repository's committed `.claude/settings.json`\n * runs it. Claude Code documents exit 2 as blocking the tool call, with stderr\n * handed back to Claude; exit 0 raises no objection. It reads\n * `$CLAUDE_PROJECT_DIR`, the project root where the session started \u2014 a session\n * started in a folder above or below the repository gets no hook (UIG-3,\n * UIG-4 findings).\n *\n * It runs the gate's own config and reimplements nothing: a hook and a lint\n * that could disagree would be worse than neither. Tests are not checked\n * (Katerina, 13 September); stories are. This package's inward gate reads `.tsx`\n * only, as its lint does.\n *\n * Input, from Claude Code on stdin: `tool_name` and `tool_input` \u2014 `file_path`\n * and `content` for Write; `file_path`, `old_string`, `new_string` and\n * `replace_all` for Edit. An Edit carries no whole file, so the edit is applied\n * to the file on disk here. Anything it cannot read passes, and the tool itself\n * reports its own failure.\n */\nimport { existsSync, readFileSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { isAbsolute, join, relative, resolve, sep } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport type { ESLint as ESLintClass } from 'eslint'\nimport { countGates } from '../eslint/index'\n\nexport interface HookOptions {\n /** The repository's top folder. Defaults to `$CLAUDE_PROJECT_DIR`, then the tool call's `cwd`. */\n root?: string\n /** The folder that holds the app, relative to `root`: `web` in Ship, `.` elsewhere. */\n app?: string\n /** `package` gates `.tsx` only, as this package's inward lint does. */\n audience?: 'app' | 'package'\n /** The hook's input, when not read from stdin (tests). */\n input?: unknown\n}\n\nexport interface HookResult {\n /** 0 lets the write through; 2 refuses it. */\n code: 0 | 2\n /** What goes to stderr when refused. */\n message: string\n}\n\ninterface ToolCall {\n tool_name?: string\n cwd?: string\n tool_input?: { file_path?: unknown; content?: unknown; old_string?: unknown; new_string?: unknown; replace_all?: unknown }\n}\n\nconst PASS: HookResult = { code: 0, message: '' }\n\nexport async function runHook({ root, app = '.', audience = 'app', input }: HookOptions = {}): Promise<HookResult> {\n let call: ToolCall\n try {\n call = (input ?? JSON.parse(readFileSync(0, 'utf8'))) as ToolCall\n } catch {\n return PASS\n }\n const args = call.tool_input ?? {}\n if (typeof args.file_path !== 'string') return PASS\n\n const top = root ?? process.env.CLAUDE_PROJECT_DIR ?? call.cwd ?? process.cwd()\n const appDir = resolve(top, app)\n const file = isAbsolute(args.file_path) ? args.file_path : resolve(call.cwd ?? top, args.file_path)\n const rel = relative(appDir, file).split(sep).join('/')\n const source = audience === 'package' ? /^src\\/.+\\.tsx$/ : /^src\\/.+\\.tsx?$/\n if (rel.startsWith('../') || isAbsolute(rel) || !source.test(rel) || /\\.test\\.tsx?$/.test(rel) || /\\.d\\.ts$/.test(rel)) return PASS\n\n const text = proposed(call.tool_name, args, file)\n if (text === null) return PASS\n\n // ESLint from the app's own install: in Ship the top folder has none.\n const resolved = createRequire(join(appDir, 'package.json')).resolve('eslint')\n const { ESLint } = (await import(pathToFileURL(resolved).href)) as { ESLint: typeof ESLintClass }\n const config = join(appDir, 'eslint.gates.config.js')\n const eslint = new ESLint({ cwd: appDir, overrideConfigFile: config })\n const results = await eslint.lintText(text, { filePath: file })\n // A rule's errors only. Code that does not parse yet (one edit of several) is\n // the typecheck's to judge; the gate looks again at the edit that completes it.\n const errors = results.flatMap((r) => r.messages.filter((m) => m.severity === 2 && m.ruleId))\n const { disabled } = countGates(results)\n if (errors.length === 0 && disabled.length === 0) return PASS\n\n const shown = (p: string) => relative(top, p).split(sep).join('/')\n const lines = [`${shown(file)} was not written: the UI Guardrails refuse it (${shown(config)}).`]\n for (const m of errors) lines.push(` ${m.line}:${m.column} ${m.message}${m.ruleId ? ` (${m.ruleId})` : ''}`)\n const keep = audience === 'package' ? 'Keep it only' : 'Keep the element only'\n for (const d of disabled) lines.push(` ${d.line} an eslint-disable switches ${d.ruleId} off. ${keep} with its reason on the line above: // @estiva-escape: <reason>`)\n return { code: 2, message: `${lines.join('\\n')}\\n` }\n}\n\n/** The file as it will be after this tool call, or null when that cannot be known. */\nfunction proposed(tool: string | undefined, args: NonNullable<ToolCall['tool_input']>, file: string): string | null {\n if (tool === 'Write') return typeof args.content === 'string' ? args.content : null\n if (tool !== 'Edit' || typeof args.old_string !== 'string' || typeof args.new_string !== 'string') return null\n const { old_string: from, new_string: to } = args\n const current = existsSync(file) ? readFileSync(file, 'utf8') : ''\n // A checkout on Windows has CRLF on disk; an edit is written with LF.\n for (const text of [current, current.replace(/\\r\\n/g, '\\n')]) {\n if (!text.includes(from)) continue\n return args.replace_all === true ? text.split(from).join(to) : text.replace(from, () => to)\n }\n return null\n}\n", "/// <reference types=\"node\" />\n/**\n * gates:status \u2014 where the UI Guardrails project stands, read from the code.\n *\n * estiva-gates status one row per ticket\n * estiva-gates status --detail every check under every row\n * estiva-gates status --json machine output, read by estiva-ui's run\n * estiva-gates status --app web the app sits in `web/`, as Ship's does\n *\n * Every row is decided by checks on real files, a real lint run or a real\n * GitHub setting. Nothing here reads a list that someone ticks by hand.\n *\n * **One copy, in the package** (docs/GATES.md \u00A723). Until UIG-10 this engine was\n * `scripts/gates-status.mjs`, the same file copied by hand into estiva-ui, Peek\n * and Ship, with estiva-ui warning when the copies drifted. Now it ships here,\n * and a repo runs it with `estiva-gates status`. A repo still carrying its own\n * copy is named as one when estiva-ui runs it; UIG-32 moves Peek and Ship off\n * theirs.\n *\n * What differs per repo is `scripts/gates-checks.mjs`, beside the app: its\n * checks, or the checks every app runs (`appChecks`) plus its own. It sits with\n * the app because it imports the package \u2014 `web/scripts/` in Ship, whose app,\n * and whose install, are in `web/` (UIG-32).\n *\n * The rows and the reasons for each check are in estiva-ui docs/GATES.md \u00A715\n * and \u00A717.\n */\nimport { execFileSync } from 'node:child_process'\nimport { existsSync, readdirSync, readFileSync, statSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { join, relative, resolve } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport type { ESLint as ESLintClass } from 'eslint'\n\nexport type CheckResult = { result: 'pass' | 'fail' | 'part' | 'unknown'; detail: string }\ntype Maybe<T> = T | Promise<T>\n\nexport interface LintProbe {\n /** The folder ESLint runs from, relative to the repo: `web` in Ship. */\n cwd?: string\n /** The config file, relative to `cwd`. */\n config: string\n /** The path the text is linted as, relative to `cwd`. Nothing is written. */\n file: string\n code: string\n expect: 'error' | 'warning' | 'none'\n /** Words the expected message must contain. */\n mentions?: string\n}\n\n/** The helpers a checks file is written with. */\nexport interface GateHelpers {\n PASS(detail: string): CheckResult\n FAIL(detail: string): CheckResult\n PART(detail: string): CheckResult\n UNKNOWN(detail: string): CheckResult\n exists(rel: string): boolean\n read(rel: string): string\n listFiles(rel: string, test: (name: string, path: string) => boolean): string[]\n file(rel: string): CheckResult\n committed(rel: string): CheckResult\n contains(rel: string, needle: string | RegExp, label?: string): CheckResult\n lacks(rel: string, needle: string | RegExp, label?: string): CheckResult\n script(pkgRel: string, name: string): CheckResult\n loads(rel: string): Promise<CheckResult>\n ci(script: string | RegExp): CheckResult\n ciJob(job: string, script: string): CheckResult\n hook(settingsRel: string, needle: string): CheckResult\n json(rel: string, test?: (data: unknown) => boolean, label?: string): CheckResult\n lint(probe: LintProbe): Promise<CheckResult>\n protectedBranch(pattern: RegExp): CheckResult\n gh(args: string[], label: string): CheckResult\n share(files: string[], test: (file: string) => boolean, label: string): CheckResult\n}\n\nexport interface GateCheck {\n what: string\n run: () => Maybe<CheckResult>\n aggregate?: boolean\n}\nexport interface GateTicket {\n ref: string\n title?: string\n owner: boolean\n checks: GateCheck[]\n}\nexport interface TicketListEntry {\n ref: string\n owner: string\n title: string\n parts?: string[]\n aggregate?: boolean\n}\nexport interface GateSpec {\n repo: string\n tickets: GateTicket[]\n /** estiva-ui only: every ticket, and the repos it joins in. */\n all?: TicketListEntry[]\n /** estiva-ui only: the repos beside it. `app` is the folder its app sits in: `web` in Ship. */\n siblings?: { name: string; path: string; env: string; app?: string }[]\n}\n\ninterface Row {\n ref: string\n title?: string\n owner?: boolean\n ownerRepo?: string\n checks: (CheckResult & { repo: string; what: string })[]\n status?: string\n aggregate?: boolean\n}\ninterface Report {\n schema: 1\n repo: string\n branch: string | null\n commit: string | null\n engine: string\n rows: Row[]\n}\n\nconst { version } = createRequire(import.meta.url)('../../package.json') as { version: string }\n/** What a report says ran it. A copy of the old script reports a hash instead. */\nexport const ENGINE = `@estiva-app/ui@${version}`\n\nconst PASS = (detail: string): CheckResult => ({ result: 'pass', detail })\nconst FAIL = (detail: string): CheckResult => ({ result: 'fail', detail })\nconst PART = (detail: string): CheckResult => ({ result: 'part', detail })\nconst UNKNOWN = (detail: string): CheckResult => ({ result: 'unknown', detail })\nconst said = (e: unknown) => {\n const err = e as { stdout?: unknown; stderr?: unknown; message?: string; code?: string }\n return { text: `${err.stdout ?? ''}${err.stderr ?? ''}`, message: String(err.message ?? e), code: err.code }\n}\n\n/** The helpers, bound to one repository. */\nexport function helpers(ROOT: string): GateHelpers {\n const abs = (rel: string) => resolve(ROOT, rel)\n const exists = (rel: string) => existsSync(abs(rel))\n const read = (rel: string) => readFileSync(abs(rel), 'utf8')\n const git = (...a: string[]) => {\n try {\n return execFileSync('git', a, { cwd: ROOT, stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim()\n } catch {\n return null\n }\n }\n const listFiles = (rel: string, test: (name: string, path: string) => boolean) => {\n const out: string[] = []\n const walk = (dir: string) => {\n if (!existsSync(dir)) return\n for (const name of readdirSync(dir)) {\n if (['node_modules', '.git', 'dist', 'storybook-static', '.verify-shots'].includes(name)) continue\n const p = join(dir, name)\n if (statSync(p).isDirectory()) walk(p)\n else if (test(name, p)) out.push(relative(ROOT, p).replace(/\\\\/g, '/'))\n }\n }\n walk(abs(rel))\n return out\n }\n const escape = (s: string) => s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n\n return {\n PASS, FAIL, PART, UNKNOWN, exists, read, listFiles,\n\n file(rel) {\n return exists(rel) ? PASS(`${rel} exists`) : FAIL(`${rel} does not exist`)\n },\n\n committed(rel) {\n if (!exists(rel)) return FAIL(`${rel} does not exist`)\n return git('ls-files', '--error-unmatch', rel) ? PASS(`${rel} is committed`) : FAIL(`${rel} exists but is not committed`)\n },\n\n contains(rel, needle, label) {\n if (!exists(rel)) return FAIL(`${rel} does not exist`)\n const hit = typeof needle === 'string' ? read(rel).includes(needle) : needle.test(read(rel))\n return hit ? PASS(label ?? `${rel} has ${needle}`) : FAIL(label ? `not yet: ${label}` : `${rel} has no ${needle}`)\n },\n\n lacks(rel, needle, label) {\n if (!exists(rel)) return FAIL(`${rel} does not exist`)\n const hit = typeof needle === 'string' ? read(rel).includes(needle) : needle.test(read(rel))\n return hit ? FAIL(`${rel} still has ${needle}`) : PASS(label ?? `${rel} has no ${needle}`)\n },\n\n script(pkgRel, name) {\n if (!exists(pkgRel)) return FAIL(`${pkgRel} does not exist`)\n const cmd = (JSON.parse(read(pkgRel)) as { scripts?: Record<string, string> }).scripts?.[name]\n return cmd ? PASS(`${pkgRel} runs \"${name}\": ${cmd}`) : FAIL(`${pkgRel} has no \"${name}\" script`)\n },\n\n async loads(rel) {\n if (!exists(rel)) return FAIL(`${rel} does not exist`)\n try {\n const mod = (await import(pathToFileURL(abs(rel)).href)) as { default?: unknown }\n return mod.default ? PASS(`${rel} loads`) : FAIL(`${rel} has no default export`)\n } catch (e) {\n return FAIL(`${rel} does not load: ${said(e).message.split('\\n')[0]}`)\n }\n },\n\n /** A workflow step that runs `npm run <script>`; a regex matches the script name. */\n ci(script) {\n const files = listFiles('.github/workflows', (n) => /\\.ya?ml$/.test(n))\n const name = typeof script === 'string' ? escape(script) : script.source\n const step = new RegExp(`npm run (${name})(?![\\\\w:-])`)\n for (const f of files) {\n const m = read(f).match(step)\n if (m) return PASS(`${f} runs npm run ${m[1]}`)\n }\n return FAIL(`no workflow runs npm run ${script}`)\n },\n\n /**\n * A workflow job, by its id, with a step that runs `npm run <script>`. Branch\n * protection requires a whole job by its name, so a required check needs the\n * job, not the step somewhere (UIG-6). A job is a key two spaces in under\n * `jobs:`, and ends at the next line that is not indented further; a comment\n * line does not count.\n */\n ciJob(job, script) {\n const files = listFiles('.github/workflows', (n) => /\\.ya?ml$/.test(n))\n const header = new RegExp(`^ ${escape(job)}:\\\\s*(#.*)?$`)\n const step = new RegExp(`^(?!\\\\s*#).*npm run ${escape(script)}(?![\\\\w:-])`, 'm')\n for (const f of files) {\n const lines = read(f).split(/\\r?\\n/)\n const from = lines.findIndex((l) => /^jobs:\\s*$/.test(l))\n const start = from === -1 ? -1 : lines.findIndex((l, i) => i > from && header.test(l))\n if (start === -1) continue\n const end = lines.findIndex((l, i) => i > start && /^ {0,2}\\S/.test(l))\n const body = lines.slice(start + 1, end === -1 ? undefined : end).join('\\n')\n return step.test(body) ? PASS(`${f}: the job ${job} runs npm run ${script}`) : FAIL(`${f}: the job ${job} does not run npm run ${script}`)\n }\n return FAIL(`no workflow has a job ${job}`)\n },\n\n hook(settingsRel, needle) {\n if (!exists(settingsRel)) return FAIL(`${settingsRel} does not exist`)\n let s: { hooks?: { PreToolUse?: { hooks?: { command?: string }[] }[] } }\n try {\n s = JSON.parse(read(settingsRel))\n } catch {\n return FAIL(`${settingsRel} is not valid JSON`)\n }\n const commands = (s.hooks?.PreToolUse ?? []).flatMap((m) => (m.hooks ?? []).map((x) => x.command ?? ''))\n const hit = commands.find((c) => c.includes(needle))\n if (hit) return PASS(`${settingsRel} has a PreToolUse hook running ${needle}`)\n return FAIL(commands.length ? `${settingsRel} has PreToolUse hooks, none running ${needle}` : `${settingsRel} has no PreToolUse hook`)\n },\n\n json(rel, test = () => true, label) {\n if (!exists(rel)) return FAIL(`${rel} does not exist`)\n let data: unknown\n try {\n data = JSON.parse(read(rel))\n } catch {\n return FAIL(`${rel} is not valid JSON`)\n }\n if (!git('ls-files', '--error-unmatch', rel)) return FAIL(`${rel} is not committed`)\n return test(data) ? PASS(label ?? `${rel} is committed and parses`) : FAIL(`${rel} parses, but ${label ?? 'fails its test'}`)\n },\n\n /** Lints a line of code that is never written to disk, the way the hook will. */\n async lint({ cwd = '.', config, file, code, expect, mentions }) {\n const cfg = join(cwd, config)\n if (!exists(cfg)) return FAIL(`${cfg} does not exist`)\n let ESLint: typeof ESLintClass\n try {\n const req = createRequire(join(abs(cwd), 'package.json'))\n ;({ ESLint } = (await import(pathToFileURL(req.resolve('eslint')).href)) as { ESLint: typeof ESLintClass })\n } catch (e) {\n return UNKNOWN(`could not load eslint from ${cwd}: ${said(e).message.split('\\n')[0]}`)\n }\n let results: ESLintClass.LintResult[]\n try {\n const eslint = new ESLint({ cwd: abs(cwd), overrideConfigFile: config })\n results = await eslint.lintText(code, { filePath: join(abs(cwd), file), warnIgnored: true })\n } catch (e) {\n return UNKNOWN(`${cfg} could not lint ${file}: ${said(e).message.split('\\n')[0]}`)\n }\n const messages = results.flatMap((r) => r.messages)\n const ignored = messages.some((m) => /ignored/i.test(m.message) && !m.ruleId)\n const fatal = messages.find((m) => m.fatal && !/ignored/i.test(m.message))\n if (fatal) return UNKNOWN(`${file} did not parse: ${fatal.message}`)\n const want = expect === 'error' ? 2 : expect === 'warning' ? 1 : 0\n if (want === 0) {\n const any = messages.filter((m) => m.ruleId && m.severity === 2)\n return any.length ? FAIL(`${file} gets an error it should not: ${any[0].message}`) : PASS(`${file}: no error${ignored ? ' (not linted)' : ''}`)\n }\n if (ignored) return FAIL(`${cfg} does not lint ${file}`)\n const hit = messages.find((m) => m.ruleId && m.severity === want && (!mentions || m.message.includes(mentions)))\n const kind = want === 2 ? 'error' : 'warning'\n return hit ? PASS(`${file} gets ${want === 2 ? 'an' : 'a'} ${kind}: ${hit.message}`) : FAIL(`${file} gets no ${kind}${mentions ? ` naming ${mentions}` : ''}`)\n },\n\n /**\n * A GitHub setting, not a file, so it is asked of GitHub: the checks a merge\n * into the default branch must pass, from the rulesets in force on it\n * (`rules/branches/<branch>`, which anyone who can read the repository may\n * see) and from classic protection as `branches/<branch>` reports it. Not\n * `branches/<branch>/protection`: GitHub answers \"Not Found\" there to anyone\n * who is not an admin, and it knows nothing of rulesets (UIG-6).\n */\n protectedBranch(pattern) {\n const url = git('remote', 'get-url', 'origin')\n const slug = url?.match(/github\\.com[:/](.+?)(?:\\.git)?$/)?.[1]\n if (!slug) return UNKNOWN('no GitHub remote')\n const branch = (git('symbolic-ref', '--short', 'refs/remotes/origin/HEAD') ?? 'origin/main').replace(/^origin\\//, '')\n const ask = (path: string) => JSON.parse(execFileSync('gh', ['api', path], { stdio: ['ignore', 'pipe', 'pipe'], timeout: 20000 }).toString())\n let rules: { type: string; parameters?: { required_status_checks?: { context: string }[] } }[]\n let classic: { contexts?: string[]; checks?: { context: string }[] } | undefined\n try {\n rules = ask(`repos/${slug}/rules/branches/${branch}`)\n classic = ask(`repos/${slug}/branches/${branch}`).protection?.required_status_checks\n } catch (e) {\n const s = said(e)\n return UNKNOWN(`could not ask GitHub (${s.code === 'ENOENT' ? 'gh is not installed' : s.text.split('\\n')[0] || s.message})`)\n }\n const names = [\n ...rules.filter((r) => r.type === 'required_status_checks').flatMap((r) => r.parameters?.required_status_checks ?? []).map((c) => c.context),\n ...(classic?.contexts ?? []),\n ...(classic?.checks ?? []).map((c) => c.context),\n ]\n const hit = names.find((n) => pattern.test(n))\n if (hit) return PASS(`${slug} ${branch} requires \"${hit}\"`)\n return FAIL(names.length ? `${slug} ${branch} requires ${names.map((n) => `\"${n}\"`).join(', ')}, none matching ${pattern}` : `${slug} ${branch} requires no check to merge`)\n },\n\n gh(args, label) {\n try {\n execFileSync('gh', args, { stdio: ['ignore', 'pipe', 'pipe'], timeout: 20000 })\n return PASS(label)\n } catch (e) {\n const s = said(e)\n if (/could not resolve|not found|HTTP 404/i.test(s.text)) return FAIL(`not yet: ${label}`)\n return UNKNOWN(`could not ask GitHub (${s.code === 'ENOENT' ? 'gh is not installed' : s.text.split('\\n')[0] || s.message})`)\n }\n },\n\n /** How many of `files` pass `test`: all is a pass, some is a part. */\n share(files, test, label) {\n if (!files.length) return FAIL(`no files to check for: ${label}`)\n const ok = files.filter(test).length\n const detail = `${ok} of ${files.length}: ${label}`\n return ok === files.length ? PASS(detail) : ok ? PART(detail) : FAIL(detail)\n },\n }\n}\n\n// \u2500\u2500 running the checks \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst STATUS: Record<string, string> = { done: '\u2705', started: '\uD83D\uDEA7', none: '\u2B1C', unknown: '\u2754' }\n\nfunction statusOf(checks: CheckResult[]) {\n const n = (r: CheckResult['result']) => checks.filter((c) => c.result === r).length\n const pass = n('pass'), part = n('part'), fail = n('fail'), unknown = n('unknown')\n if (!checks.length) return 'unknown'\n if (pass === checks.length) return 'done'\n if (pass + part > 0) return fail === 0 && unknown > 0 ? 'unknown' : 'started'\n return fail > 0 ? 'none' : 'unknown'\n}\n\nasync function runRepo(ROOT: string, app: string, h: GateHelpers): Promise<{ spec: GateSpec; report: Report }> {\n // The checks file sits with the app, because it imports `appChecks` from the\n // package: in Ship that install is `web/node_modules`, not the repo's top\n // folder (UIG-32). Everything it names is still read from the repo's top.\n const checksFile = join(ROOT, app, 'scripts', 'gates-checks.mjs')\n const define = ((await import(pathToFileURL(checksFile).href)) as { default: (h: GateHelpers) => GateSpec }).default\n const spec = define(h)\n const git = (...a: string[]) => {\n try {\n return execFileSync('git', a, { cwd: ROOT, stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim()\n } catch {\n return null\n }\n }\n const rows: Row[] = []\n for (const t of spec.tickets) {\n const checks: Row['checks'] = []\n for (const c of t.checks ?? []) {\n if (c.aggregate) continue\n let r: CheckResult\n try {\n r = await c.run()\n } catch (e) {\n r = UNKNOWN(`the check threw: ${said(e).message}`)\n }\n checks.push({ repo: spec.repo, what: c.what, ...r })\n }\n rows.push({ ref: t.ref, title: t.title, owner: t.owner, checks })\n }\n return { spec, report: { schema: 1, repo: spec.repo, branch: git('rev-parse', '--abbrev-ref', 'HEAD'), commit: git('rev-parse', '--short', 'HEAD'), engine: ENGINE, rows } }\n}\n\n/**\n * Another repository's report. A repo on the package's engine runs it from its\n * own install; a repo still carrying `scripts/gates-status.mjs` runs that copy.\n *\n * `app` is the folder that repo's app sits in \u2014 `web` in Ship \u2014 where its\n * install and its checks file are. The report is still of the whole repo.\n */\nfunction runSibling(dir: string, app: string, shown: string): { report?: Report; error?: string } {\n const copy = join(dir, 'scripts', 'gates-status.mjs')\n const installed = createRequire(join(dir, app, 'package.json'))\n let script = copy\n const args = ['--json']\n if (!existsSync(copy)) {\n try {\n script = join(installed.resolve('@estiva-app/ui/package.json'), '..', 'dist', 'gates', 'cli.js')\n args.unshift('status')\n if (app !== '.') args.push('--app', app)\n } catch {\n return { error: `${shown} has neither its own gates-status.mjs nor @estiva-app/ui installed${app === '.' ? '' : ` in ${app}`}` }\n }\n if (!existsSync(script)) return { error: `${shown} has an @estiva-app/ui with no gates engine: install 0.21.0 or later` }\n }\n try {\n const out = execFileSync(process.execPath, [script, ...args], { cwd: dir, stdio: ['ignore', 'pipe', 'pipe'], timeout: 300000, maxBuffer: 16 * 1024 * 1024 })\n return { report: JSON.parse(out.toString()) as Report }\n } catch (e) {\n const err = e as { stderr?: unknown; message?: string }\n return { error: `its gates:status failed: ${String(err.stderr ?? err.message).split('\\n').filter(Boolean).slice(-1)[0]}` }\n }\n}\n\nfunction pad(s: string, n: number) {\n const len = [...s].length\n return len >= n ? s : s + ' '.repeat(n - len)\n}\n\nfunction printRows(rows: Row[], { showOwner, detail }: { showOwner: boolean; detail: boolean }) {\n const lines: string[] = []\n const width = Math.max(...rows.map((r) => [...(r.title ?? '')].length))\n for (const r of rows) {\n const pass = r.checks.filter((c) => c.result === 'pass').length\n const count = r.checks.length ? `${pass} of ${r.checks.length}` : 'no checks here'\n lines.push(`${STATUS[r.status ?? 'unknown']} ${pad(r.ref, 7)} ${pad(r.title ?? '', width)} ${showOwner ? pad(r.ownerRepo ?? '', 10) : ''}${count}`)\n if (detail || r.status === 'started' || r.status === 'unknown') {\n for (const c of r.checks) {\n const mark = { pass: '\u2713', part: '\u00BD', fail: '\u2717', unknown: '?' }[c.result]\n lines.push(` ${mark} ${showOwner ? `${pad(c.repo, 10)} ` : ''}${c.what} \u2014 ${c.detail}`)\n }\n }\n }\n return lines.join('\\n')\n}\n\nfunction summary(rows: Row[]) {\n const n = (s: string) => rows.filter((r) => r.status === s).length\n return `${STATUS.done} ${n('done')} done \u00B7 ${STATUS.started} ${n('started')} started \u00B7 ${STATUS.none} ${n('none')} not started \u00B7 ${STATUS.unknown} ${n('unknown')} could not check`\n}\n\nexport interface StatusOptions {\n /** The repository to report on. */\n root?: string\n /** The folder that holds the app, relative to `root`: `web` in Ship, `.` elsewhere. */\n app?: string\n json?: boolean\n detail?: boolean\n}\n\n/** Print where the project stands. Returns what was printed, or the JSON report. */\nexport async function runStatus({ root = process.cwd(), app = '.', json = false, detail = false }: StatusOptions = {}): Promise<string> {\n const ROOT = resolve(root)\n const h = helpers(ROOT)\n const shown = (dir: string) => (relative(ROOT, dir) || dir).replace(/\\\\/g, '/')\n const { spec, report } = await runRepo(ROOT, app, h)\n\n if (json) return JSON.stringify(report)\n\n const out: string[] = []\n out.push('UI Guardrails \u00B7 gates:status', 'Read from the code. Nothing here is a hand-ticked list.', '')\n out.push(`${pad(spec.repo, 10)} ${report.branch} @ ${report.commit}`)\n\n if (!spec.all) {\n // A sibling repo on its own: its own rows, then its parts of rows owned elsewhere.\n const own = report.rows.filter((r) => r.owner).map((r) => ({ ...r, ownerRepo: spec.repo, status: statusOf(r.checks) }))\n const parts = report.rows.filter((r) => !r.owner).map((r) => ({ ...r, ownerRepo: '', status: statusOf(r.checks) }))\n out.push('', `Tickets ${spec.repo} owns (${own.length}):`, printRows(own, { showOwner: false, detail }))\n out.push('', `Parts of tickets another repo owns, checked here (${parts.length}):`, printRows(parts, { showOwner: false, detail }))\n out.push('', 'For every ticket across the repos, run npm run gates:status in estiva-ui.')\n return out.join('\\n')\n }\n\n // estiva-ui: gather the sibling checkouts and join everything into one row per ticket.\n const reports: Report[] = [report]\n const found: { name: string; note: string; missing?: boolean }[] = []\n for (const s of spec.siblings ?? []) {\n const dir = resolve(ROOT, process.env[s.env] ?? s.path)\n if (!existsSync(join(dir, 'package.json'))) {\n found.push({ name: s.name, note: `not found at ${shown(dir)} (set ${s.env} to point at it)`, missing: true })\n continue\n }\n const r = runSibling(dir, s.app ?? '.', shown(dir))\n if (!r.report) {\n found.push({ name: s.name, note: r.error ?? 'no report', missing: true })\n continue\n }\n const copy = r.report.engine !== ENGINE ? (r.report.engine.startsWith('@estiva-app/ui@') ? ` \u00B7 on ${r.report.engine}'s engine` : ' \u00B7 runs its own copy of the status engine; UIG-32 moves it onto the package') : ''\n found.push({ name: s.name, note: `${r.report.branch} @ ${r.report.commit}, found at ${shown(dir)}${copy}` })\n reports.push(r.report)\n }\n for (const f of found) out.push(`${pad(f.name, 10)} ${f.note}`)\n\n const problems: string[] = []\n const known = new Set(spec.all.map((t) => t.ref))\n for (const rep of reports) {\n for (const r of rep.rows.filter((r) => !known.has(r.ref))) problems.push(`${rep.repo} reports ${r.ref}, which is not one of the tickets`)\n }\n const rows: Row[] = spec.all.map((t) => {\n const pieces = reports.flatMap((rep) => rep.rows.filter((r) => r.ref === t.ref).map((r) => ({ rep, r })))\n const claims = pieces.filter((p) => p.r.owner).map((p) => p.rep.repo)\n const ownerMissing = found.some((f) => f.name === t.owner && f.missing)\n if (!ownerMissing && (claims.length !== 1 || claims[0] !== t.owner)) {\n problems.push(`${t.ref}: should be owned by ${t.owner}, is claimed by ${claims.join(' and ') || 'no repo'}`)\n }\n for (const p of pieces.filter((p) => !p.r.owner && !(t.parts ?? []).includes(p.rep.repo))) {\n problems.push(`${t.ref}: ${p.rep.repo} checks a part of it, but the ticket list does not name ${p.rep.repo}`)\n }\n const checks = pieces.flatMap((p) => p.r.checks)\n for (const f of found.filter((f) => f.missing)) {\n if ((t.parts ?? []).includes(f.name) || t.owner === f.name) checks.push({ repo: f.name, what: `${f.name}'s part`, result: 'unknown', detail: f.note })\n }\n return { ref: t.ref, title: t.title, ownerRepo: t.owner, checks, aggregate: t.aggregate }\n })\n for (const row of rows) row.status = statusOf(row.checks)\n for (const row of rows.filter((r) => r.aggregate)) {\n const others = rows.filter((r) => r !== row)\n const done = others.filter((r) => r.status === 'done').length\n row.checks.push({ repo: 'all', what: 'every other ticket is done', ...(done === others.length ? PASS(`${done} of ${others.length} done`) : FAIL(`${done} of ${others.length} done`)) })\n row.status = statusOf(row.checks)\n }\n\n out.push('', printRows(rows, { showOwner: true, detail }), '', summary(rows))\n const owners = Object.entries(rows.reduce<Record<string, number>>((a, r) => ({ ...a, [r.ownerRepo ?? '']: (a[r.ownerRepo ?? ''] ?? 0) + 1 }), {}))\n .map(([k, v]) => `${k} ${v}`)\n .join(', ')\n out.push(`${rows.length} tickets. Owned by ${owners}.`)\n const unchecked = found.filter((f) => f.missing && spec.all!.some((t) => t.owner === f.name)).map((f) => f.name)\n if (problems.length) out.push(`\u26A0\uFE0F Ownership does not add up:\\n ${problems.join('\\n ')}`)\n else if (unchecked.length) out.push(`\u2754 Ownership not fully checked: ${unchecked.join(' and ')} could not be read.`)\n else out.push('\u2705 Each ticket is owned by exactly one repo, and every repo agrees.')\n if (!detail) out.push('Add -- --detail to see every check.')\n return out.join('\\n')\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["existsSync", "readFileSync", "createRequire", "join", "relative", "pathToFileURL", "countGates", "existsSync", "readFileSync", "createRequire", "join", "relative", "resolve", "pathToFileURL", "PASS", "r", "p", "f"]
7
+ }
package/dist/gates/cli.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  runHook,
4
4
  runStatus,
5
5
  writeGateCount
6
- } from "./chunk-AWO7SZSA.js";
6
+ } from "./chunk-AUXD4GCY.js";
7
7
 
8
8
  // src/gates/cli.ts
9
9
  var [command, ...rest] = process.argv.slice(2);
@@ -34,7 +34,7 @@ async function main() {
34
34
  return result.code;
35
35
  }
36
36
  case "status": {
37
- process.stdout.write(`${await runStatus({ json: flag("json"), detail: flag("detail") })}${flag("json") ? "" : "\n"}`);
37
+ process.stdout.write(`${await runStatus({ app: value("app"), json: flag("json"), detail: flag("detail") })}${flag("json") ? "" : "\n"}`);
38
38
  return 0;
39
39
  }
40
40
  default:
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/gates/cli.ts"],
4
- "sourcesContent": ["/// <reference types=\"node\" />\n/**\n * `estiva-gates` \u2014 the gate pieces as one command (docs/GATES.md \u00A723).\n *\n * estiva-gates count --repo <name> [--package] write .gates-count.json (postlint:rules)\n * estiva-gates hook [--app <dir>] [--package] the editor gate, run by a PreToolUse hook\n * estiva-gates status [--json] [--detail] gates:status\n *\n * An app's `package.json` runs the first and the last through npm, which finds\n * the command in `node_modules/.bin`. The hook is run by path, from the\n * committed `.claude/settings.json`:\n *\n * node \"$CLAUDE_PROJECT_DIR/node_modules/@estiva-app/ui/dist/gates/cli.js\" hook\n */\nimport { writeGateCount } from './count'\nimport { runHook } from './hook'\nimport { runStatus } from './status'\n\nconst [command, ...rest] = process.argv.slice(2)\nconst flag = (name: string) => rest.includes(`--${name}`)\nconst value = (name: string) => {\n const i = rest.indexOf(`--${name}`)\n return i === -1 ? undefined : rest[i + 1]\n}\nconst audience = flag('package') ? 'package' : 'app'\n\nasync function main(): Promise<number> {\n switch (command) {\n case 'count': {\n const repo = value('repo')\n if (!repo) {\n process.stderr.write('estiva-gates count needs --repo <name>, the name written into .gates-count.json\\n')\n return 1\n }\n const result = await writeGateCount({ repo, audience, config: value('config') })\n process.stdout.write(`${result.lines.join('\\n')}\\n`)\n if (result.failures.length) process.stderr.write(`${result.failures.join('\\n')}\\n`)\n return result.failures.length ? 1 : 0\n }\n case 'hook': {\n const result = await runHook({ app: value('app'), audience })\n if (result.code === 2) process.stderr.write(result.message)\n return result.code\n }\n case 'status': {\n process.stdout.write(`${await runStatus({ json: flag('json'), detail: flag('detail') })}${flag('json') ? '' : '\\n'}`)\n return 0\n }\n default:\n process.stderr.write('estiva-gates <count | hook | status>\\n')\n return 1\n }\n}\n\nprocess.exitCode = await main()\n"],
5
- "mappings": ";;;;;;;;AAkBA,IAAM,CAAC,SAAS,GAAG,IAAI,IAAI,QAAQ,KAAK,MAAM,CAAC;AAC/C,IAAM,OAAO,CAAC,SAAiB,KAAK,SAAS,KAAK,IAAI,EAAE;AACxD,IAAM,QAAQ,CAAC,SAAiB;AAC9B,QAAM,IAAI,KAAK,QAAQ,KAAK,IAAI,EAAE;AAClC,SAAO,MAAM,KAAK,SAAY,KAAK,IAAI,CAAC;AAC1C;AACA,IAAM,WAAW,KAAK,SAAS,IAAI,YAAY;AAE/C,eAAe,OAAwB;AACrC,UAAQ,SAAS;AAAA,IACf,KAAK,SAAS;AACZ,YAAM,OAAO,MAAM,MAAM;AACzB,UAAI,CAAC,MAAM;AACT,gBAAQ,OAAO,MAAM,mFAAmF;AACxG,eAAO;AAAA,MACT;AACA,YAAM,SAAS,MAAM,eAAe,EAAE,MAAM,UAAU,QAAQ,MAAM,QAAQ,EAAE,CAAC;AAC/E,cAAQ,OAAO,MAAM,GAAG,OAAO,MAAM,KAAK,IAAI,CAAC;AAAA,CAAI;AACnD,UAAI,OAAO,SAAS,OAAQ,SAAQ,OAAO,MAAM,GAAG,OAAO,SAAS,KAAK,IAAI,CAAC;AAAA,CAAI;AAClF,aAAO,OAAO,SAAS,SAAS,IAAI;AAAA,IACtC;AAAA,IACA,KAAK,QAAQ;AACX,YAAM,SAAS,MAAM,QAAQ,EAAE,KAAK,MAAM,KAAK,GAAG,SAAS,CAAC;AAC5D,UAAI,OAAO,SAAS,EAAG,SAAQ,OAAO,MAAM,OAAO,OAAO;AAC1D,aAAO,OAAO;AAAA,IAChB;AAAA,IACA,KAAK,UAAU;AACb,cAAQ,OAAO,MAAM,GAAG,MAAM,UAAU,EAAE,MAAM,KAAK,MAAM,GAAG,QAAQ,KAAK,QAAQ,EAAE,CAAC,CAAC,GAAG,KAAK,MAAM,IAAI,KAAK,IAAI,EAAE;AACpH,aAAO;AAAA,IACT;AAAA,IACA;AACE,cAAQ,OAAO,MAAM,wCAAwC;AAC7D,aAAO;AAAA,EACX;AACF;AAEA,QAAQ,WAAW,MAAM,KAAK;",
4
+ "sourcesContent": ["/// <reference types=\"node\" />\n/**\n * `estiva-gates` \u2014 the gate pieces as one command (docs/GATES.md \u00A723).\n *\n * estiva-gates count --repo <name> [--package] write .gates-count.json (postlint:rules)\n * estiva-gates hook [--app <dir>] [--package] the editor gate, run by a PreToolUse hook\n * estiva-gates status [--app <dir>] [--json] [--detail] gates:status\n *\n * An app's `package.json` runs the first and the last through npm, which finds\n * the command in `node_modules/.bin`. The hook is run by path, from the\n * committed `.claude/settings.json`:\n *\n * node \"$CLAUDE_PROJECT_DIR/node_modules/@estiva-app/ui/dist/gates/cli.js\" hook\n *\n * A repo whose app sits in a folder of its own runs both by path, from the\n * repo's top folder, and says where the app is \u2014 Ship (UIG-32):\n *\n * node \"$CLAUDE_PROJECT_DIR/web/node_modules/@estiva-app/ui/dist/gates/cli.js\" hook --app web\n * node web/node_modules/@estiva-app/ui/dist/gates/cli.js status --app web\n */\nimport { writeGateCount } from './count'\nimport { runHook } from './hook'\nimport { runStatus } from './status'\n\nconst [command, ...rest] = process.argv.slice(2)\nconst flag = (name: string) => rest.includes(`--${name}`)\nconst value = (name: string) => {\n const i = rest.indexOf(`--${name}`)\n return i === -1 ? undefined : rest[i + 1]\n}\nconst audience = flag('package') ? 'package' : 'app'\n\nasync function main(): Promise<number> {\n switch (command) {\n case 'count': {\n const repo = value('repo')\n if (!repo) {\n process.stderr.write('estiva-gates count needs --repo <name>, the name written into .gates-count.json\\n')\n return 1\n }\n const result = await writeGateCount({ repo, audience, config: value('config') })\n process.stdout.write(`${result.lines.join('\\n')}\\n`)\n if (result.failures.length) process.stderr.write(`${result.failures.join('\\n')}\\n`)\n return result.failures.length ? 1 : 0\n }\n case 'hook': {\n const result = await runHook({ app: value('app'), audience })\n if (result.code === 2) process.stderr.write(result.message)\n return result.code\n }\n case 'status': {\n process.stdout.write(`${await runStatus({ app: value('app'), json: flag('json'), detail: flag('detail') })}${flag('json') ? '' : '\\n'}`)\n return 0\n }\n default:\n process.stderr.write('estiva-gates <count | hook | status>\\n')\n return 1\n }\n}\n\nprocess.exitCode = await main()\n"],
5
+ "mappings": ";;;;;;;;AAwBA,IAAM,CAAC,SAAS,GAAG,IAAI,IAAI,QAAQ,KAAK,MAAM,CAAC;AAC/C,IAAM,OAAO,CAAC,SAAiB,KAAK,SAAS,KAAK,IAAI,EAAE;AACxD,IAAM,QAAQ,CAAC,SAAiB;AAC9B,QAAM,IAAI,KAAK,QAAQ,KAAK,IAAI,EAAE;AAClC,SAAO,MAAM,KAAK,SAAY,KAAK,IAAI,CAAC;AAC1C;AACA,IAAM,WAAW,KAAK,SAAS,IAAI,YAAY;AAE/C,eAAe,OAAwB;AACrC,UAAQ,SAAS;AAAA,IACf,KAAK,SAAS;AACZ,YAAM,OAAO,MAAM,MAAM;AACzB,UAAI,CAAC,MAAM;AACT,gBAAQ,OAAO,MAAM,mFAAmF;AACxG,eAAO;AAAA,MACT;AACA,YAAM,SAAS,MAAM,eAAe,EAAE,MAAM,UAAU,QAAQ,MAAM,QAAQ,EAAE,CAAC;AAC/E,cAAQ,OAAO,MAAM,GAAG,OAAO,MAAM,KAAK,IAAI,CAAC;AAAA,CAAI;AACnD,UAAI,OAAO,SAAS,OAAQ,SAAQ,OAAO,MAAM,GAAG,OAAO,SAAS,KAAK,IAAI,CAAC;AAAA,CAAI;AAClF,aAAO,OAAO,SAAS,SAAS,IAAI;AAAA,IACtC;AAAA,IACA,KAAK,QAAQ;AACX,YAAM,SAAS,MAAM,QAAQ,EAAE,KAAK,MAAM,KAAK,GAAG,SAAS,CAAC;AAC5D,UAAI,OAAO,SAAS,EAAG,SAAQ,OAAO,MAAM,OAAO,OAAO;AAC1D,aAAO,OAAO;AAAA,IAChB;AAAA,IACA,KAAK,UAAU;AACb,cAAQ,OAAO,MAAM,GAAG,MAAM,UAAU,EAAE,KAAK,MAAM,KAAK,GAAG,MAAM,KAAK,MAAM,GAAG,QAAQ,KAAK,QAAQ,EAAE,CAAC,CAAC,GAAG,KAAK,MAAM,IAAI,KAAK,IAAI,EAAE;AACvI,aAAO;AAAA,IACT;AAAA,IACA;AACE,cAAQ,OAAO,MAAM,wCAAwC;AAC7D,aAAO;AAAA,EACX;AACF;AAEA,QAAQ,WAAW,MAAM,KAAK;",
6
6
  "names": []
7
7
  }
@@ -5,7 +5,7 @@ import {
5
5
  runHook,
6
6
  runStatus,
7
7
  writeGateCount
8
- } from "./chunk-AWO7SZSA.js";
8
+ } from "./chunk-AUXD4GCY.js";
9
9
  import {
10
10
  appFiles,
11
11
  askNpm,
@@ -388,7 +388,9 @@ ${component('<Button title="Delete">x</Button>')}`, "error", "WithTooltip") }
388
388
  { what: "the gate pieces come from the package, and no repo keeps a copy", run: all(h, [
389
389
  () => h.contains(at("eslint.gates.config.js"), /from '@estiva-app\/ui\/gates'/, "the gate config imports @estiva-app/ui/gates"),
390
390
  () => h.hook(".claude/settings.json", "@estiva-app/ui/dist/gates/cli.js"),
391
- () => h.contains("package.json", /"gates:status":\s*"estiva-gates status"/, "gates:status runs estiva-gates"),
391
+ // `estiva-gates status` where npm can find the command; by path where it cannot,
392
+ // because the install is in the app's folder and the script is in the repo's top one (Ship).
393
+ () => h.contains("package.json", /"gates:status":\s*"[^"]*(?:estiva-gates|@estiva-app\/ui\/dist\/gates\/cli\.js)[^"]*status/, "gates:status runs the package's engine"),
392
394
  () => h.exists("scripts/gates-status.mjs") ? h.FAIL("scripts/gates-status.mjs is a copy of the status engine: run estiva-gates status instead") : h.PASS("no copy of the status engine")
393
395
  ], "the gate config, the hook and gates:status are the package's; no copy of the engine") }
394
396
  ])
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/gates/token-lint.ts", "../../src/gates/gate-config.ts", "../../src/gates/app-checks.ts"],
4
- "sourcesContent": ["/// <reference types=\"node\" />\n/**\n * The token contract as lint settings, written once (docs/GATES.md \u00A723).\n *\n * Until UIG-10 this block was pasted into every repo: `eslint.config.js` here,\n * `eslint.tokens.js` in Peek, `eslint.config.js` in Ship. Katerina ruled on 17 September that a\n * gate piece ships in the package and an app imports it, so this is that one\n * copy. Peek and Ship move onto it in UIG-32.\n *\n * Two configs, as the copies had:\n *\n * - `tokenLint` \u2014 a class Tailwind does not generate (the doubled token name,\n * `bg-bg-surface`), Tailwind's own type ramp or palette, a raw colour, an\n * opacity modifier on a token colour.\n * - `tokenValues` \u2014 UIG-28: type, corners and shadows written by hand, and an\n * inline style that sets a colour (errors); heights, spacing and border\n * widths written by hand (warnings, never blocking).\n *\n * The patterns are the same for everyone. Only the words differ: an app is told\n * to use \"the package\", the package is told to add the token to `tokens.css`.\n * Measured on 17 September: the UIG-28 block was byte for byte the same in Peek\n * and Ship, and this repository's had the same patterns with seven messages\n * worded for the package.\n *\n * Where the plugin looks: every `className`, every `cn()` / `clsx()` argument,\n * and the values of a class map whose name ends in `Styles` / `_STYLES` or\n * `Classes` / `_CLASSES`. A class map named any other way is invisible to it.\n *\n * Tests are out of `tokenValues` (ruling of 13 September): a test mounts markup\n * to test it. A kept hand-written value says why:\n * `// eslint-disable-next-line <rule> -- @estiva-escape: <reason>` (A2).\n */\nimport { createRequire } from 'node:module'\nimport { fileURLToPath } from 'node:url'\nimport type { Linter } from 'eslint'\nimport betterTailwindcss from 'eslint-plugin-better-tailwindcss'\nimport { getDefaultSelectors } from 'eslint-plugin-better-tailwindcss/defaults'\nimport { parser as typescriptParser } from 'typescript-eslint'\n\n/** Who reads the messages. */\nexport type TokenAudience = 'app' | 'package'\n\nexport interface TokenLintOptions {\n /** The Tailwind config the unknown-class rule reads, relative to where ESLint runs. */\n tailwindConfig?: string\n /** `app` (the default) names the package; `package` names `tokens.css` and the preset. */\n audience?: TokenAudience\n}\n\n/** What the token lint never reads: build output. */\nexport const TOKEN_LINT_IGNORES = ['dist/', 'storybook-static/', 'node_modules/']\n\nconst CLASS_MAP_SELECTOR = {\n kind: 'variable',\n name: '^(?:[A-Z][A-Z0-9_]*_(?:STYLES|CLASSES)|[a-z][a-zA-Z0-9]*(?:Styles|Classes))$',\n match: [{ type: 'strings' }, { type: 'objectValues' }],\n}\n\nconst WORDS = {\n app: {\n ramp: \"Tailwind's type ramp is not a token. Use the ramp from the package: text-body-2, text-caption, text-h3, text-btn-default...\",\n palette: \"Tailwind's palette is not a token. Use a colour from the package (bg-bg-surface, text-text-primary, border-border-subtle...); a colour with no token is a missing token.\",\n raw: 'A raw colour is a missing token: add it to the package (tokens.css in every theme block, and the preset), or say why not in an eslint-disable comment.',\n opacity: 'An opacity modifier on a token colour compiles to nothing. A transparent colour is a token of its own (D16): add it to the package.',\n type: '$0 is type written by hand. A token from the package sets the size, line height, letter spacing and weight together (text-body-2, text-caption, text-h5...); if none fits, add one to the package, or say why not in an eslint-disable comment.',\n corner: '$0 is a corner written by hand, and no token has it. Use a corner from the package (rounded-sm 4px, rounded-md 6px, rounded-lg 8px...), or say why not in an eslint-disable comment.',\n shadow: '$0 is a shadow written by hand. Use a shadow from the package (shadow-sm, shadow-md, shadow-focus-ring, drop-shadow-glow-success...); a shadow with no token is a missing token.',\n },\n package: {\n ramp: \"Tailwind's type ramp is not a token. Use the ramp in tailwind-preset.js: text-body-2, text-caption, text-h3, text-btn-default...\",\n palette: \"Tailwind's palette is not a token. Use a colour from tailwind-preset.js (bg-bg-surface, text-text-primary, border-border-subtle...); a colour with no token is a missing token.\",\n raw: 'A raw colour is a missing token: add it to tokens.css in every theme block and to the preset, or say why not in an eslint-disable comment.',\n opacity: 'An opacity modifier on a token colour compiles to nothing. A transparent colour is a token of its own (D16): add it to tokens.css in every theme block and to the preset.',\n type: '$0 is type written by hand. A token from tailwind-preset.js sets the size, line height, letter spacing and weight together (text-body-2, text-caption, text-h5...); if none fits, add one to the preset and to cn.ts, or say why not in an eslint-disable comment.',\n corner: '$0 is a corner written by hand, and no token has it. Use a corner from tailwind-preset.js (rounded-sm 4px, rounded-md 6px, rounded-lg 8px...), or say why not in an eslint-disable comment.',\n shadow: '$0 is a shadow written by hand. Use a shadow from tailwind-preset.js (shadow-sm, shadow-md, shadow-focus-ring, drop-shadow-glow-success...); a shadow with no token is a missing token.',\n },\n} satisfies Record<TokenAudience, Record<string, string>>\n\n/**\n * The class rules. `no-unknown-classes` reads the app's own Tailwind config, so\n * it knows exactly what the preset and the app generate.\n */\nexport function tokenLint({ tailwindConfig = 'tailwind.config.js', audience = 'app' }: TokenLintOptions = {}): Linter.Config {\n const words = WORDS[audience]\n return {\n files: ['**/*.{ts,tsx}'],\n languageOptions: {\n parser: typescriptParser,\n parserOptions: { ecmaFeatures: { jsx: true } },\n },\n plugins: { 'better-tailwindcss': betterTailwindcss },\n settings: {\n 'better-tailwindcss': {\n tailwindConfig,\n selectors: [...getDefaultSelectors(), CLASS_MAP_SELECTOR],\n },\n },\n rules: {\n 'better-tailwindcss/no-unknown-classes': 'error',\n 'better-tailwindcss/no-restricted-classes': [\n 'error',\n {\n restrict: [\n { pattern: '^(?:[a-z0-9-]+:)*text-(?:xs|sm|base|lg|xl|[2-9]xl)$', message: words.ramp },\n {\n pattern:\n '^(?:[a-z0-9-]+:)*(?:text|bg|border|ring|outline|fill|stroke|decoration|divide|placeholder|from|via|to|accent|caret|shadow)-(?:black|white|slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)(?:-[0-9]{2,3})?(?:/[0-9]{1,3})?$',\n message: words.palette,\n },\n { pattern: '^(?:[a-z0-9-]+:)*[a-z-]+-\\\\[[^\\\\]]*(?:#[0-9a-fA-F]{3}|rgba?\\\\(|hsla?\\\\(|oklch\\\\()', message: words.raw },\n {\n pattern: '^(?:[a-z0-9-]+:)*(?:text|bg|border|ring|outline|fill|stroke|decoration|divide|placeholder|from|via|to|accent|caret|shadow)-(?:bg|text|border|accent|info|warning|success|error)-[a-z-]+/[0-9]{1,3}$',\n message: words.opacity,\n },\n ],\n },\n ],\n },\n }\n}\n\n// UIG-28's patterns. The variant part reads arbitrary variants too\n// (`[&_pre]:`, `data-[state]:`, `group-hover/row:`). A colour inside `text-[\u2026]`\n// is left to the raw-colour rule of `tokenLint`.\nconst V = '^(?:(?:[^:\\\\[\\\\]\\\\s]|\\\\[[^\\\\]]*\\\\])+:)*!?'\nconst NOT_COLOUR = '(?!color:|#|rgba?\\\\(|hsla?\\\\(|oklch\\\\(|var\\\\()'\nconst HAND_WRITTEN = {\n type: `${V}(?:text-\\\\[${NOT_COLOUR}|text-\\\\[length:|leading-\\\\[|tracking-\\\\[)[^\\\\]]+\\\\](?:/\\\\S*)?$`,\n corner: `${V}rounded(?:-(?:t|r|b|l|s|e|tl|tr|br|bl|ss|se|es|ee))?-\\\\[[^\\\\]]+\\\\]$`,\n shadow: `${V}(?:shadow|drop-shadow)-\\\\[(?!color:)[^\\\\]]+\\\\]$`,\n spacing: `${V}-?(?:w|h|size|min-w|min-h|max-w|max-h|p[xytrblse]?|m[xytrblse]?|gap(?:-[xy])?|space-[xy]|inset(?:-[xy])?|top|right|bottom|left|translate-[xy]|basis|indent|scroll-[mp][xytrblse]?|border-spacing(?:-[xy])?)-\\\\[[^\\\\]]+\\\\]$`,\n width: `${V}(?:border(?:-[xytrblse])?|divide-[xy]|outline|ring|ring-offset)-\\\\[(?:length:)?-?[0-9.]+(?:px|rem|em)?\\\\]$`,\n}\n\nconst INLINE_STYLE_TOKEN_PROPERTIES =\n '/^(color|background|backgroundColor|backgroundImage|fill|stroke|fontSize|border|borderTop|borderRight|borderBottom|borderLeft|borderBlock|borderInline|borderStyle|boxShadow|textShadow)$|Color$/'\n\ninterface Preset {\n theme: { extend: { fontSize: Record<string, string | [string, unknown]>; borderRadius: Record<string, string> } }\n}\n\n/**\n * The preset, loaded the way Tailwind loads it: it imports `tailwindcss/plugin`,\n * which a plain `import` cannot resolve. Read from beside this module, so an app\n * and this repository read the one preset the package ships.\n */\nfunction loadPreset(): Preset {\n const require = createRequire(import.meta.url)\n const loadConfig = require('tailwindcss/loadConfig') as (path: string) => Preset\n return loadConfig(fileURLToPath(new URL('../../tailwind-preset.js', import.meta.url)))\n}\n\n/**\n * UIG-28: the values the tokens already name, written by hand (Katerina's\n * rulings B11, B12, C3 of 13 September and R2 of 15 September). Register it\n * after `tokenLint`, whose parser and plugin settings it borrows. Under its own\n * rule names, `token-values` and `token-spacing` (the same plugin twice, so one\n * runs as errors and one as warnings): an escape for a hand-written size then\n * cannot also silence a raw colour on the same line.\n */\nexport function tokenValues({ audience = 'app' }: Pick<TokenLintOptions, 'audience'> = {}): Linter.Config {\n const words = WORDS[audience]\n const preset = loadPreset()\n // `text-[14px]` names the token of that size, read from the preset, so the\n // message stays true when the ramp changes.\n const tokensBySize: Record<string, string[]> = {}\n for (const [name, value] of Object.entries(preset.theme.extend.fontSize)) {\n const size = Array.isArray(value) ? value[0] : value\n ;(tokensBySize[size] ??= []).push(`text-${name}`)\n }\n const cornersBySize: Record<string, string[]> = {}\n for (const [name, size] of Object.entries(preset.theme.extend.borderRadius)) (cornersBySize[size] ??= []).push(name === 'DEFAULT' ? 'rounded' : `rounded-${name}`)\n\n return {\n files: ['**/*.{ts,tsx}'],\n ignores: ['**/*.test.ts', '**/*.test.tsx'],\n plugins: { 'token-values': betterTailwindcss, 'token-spacing': betterTailwindcss },\n rules: {\n 'token-values/no-restricted-classes': [\n 'error',\n {\n restrict: [\n ...Object.entries(tokensBySize).map(([size, names]) => ({\n pattern: `${V}text-\\\\[${size.replace('.', '\\\\.')}\\\\](?:/\\\\S*)?$`,\n message: `$0 is the type ramp written by hand. Use ${names.join(' or ')}.`,\n })),\n {\n pattern: HAND_WRITTEN.type,\n message: words.type,\n },\n ...Object.entries(cornersBySize).map(([size, names]) => ({\n pattern: `${V}rounded(?:-(?:t|r|b|l|s|e|tl|tr|br|bl|ss|se|es|ee))?-\\\\[${size.replace('.', '\\\\.')}\\\\]$`,\n message: `$0 is a corner written by hand. Use ${names.join(' or ')} (with the same side, if it has one).`,\n })),\n {\n pattern: HAND_WRITTEN.corner,\n message: words.corner,\n },\n {\n pattern: HAND_WRITTEN.shadow,\n message: words.shadow,\n },\n ],\n },\n ],\n 'token-spacing/no-restricted-classes': [\n 'warn',\n {\n restrict: [\n {\n pattern: HAND_WRITTEN.spacing,\n message: '$0 is a size or a space written by hand. Use a step of the spacing scale (p-3, h-9, gap-2...) if one fits. Reported, not blocked.',\n },\n {\n pattern: HAND_WRITTEN.width,\n message: '$0 is a border or ring width written by hand. Use border, border-2, ring-1... if one fits. Reported, not blocked.',\n },\n ],\n },\n ],\n 'no-restricted-syntax': [\n 'error',\n {\n selector: `JSXAttribute[name.name=\"style\"] > JSXExpressionContainer > ObjectExpression > Property[key.name=${INLINE_STYLE_TOKEN_PROPERTIES}]`,\n message: 'An inline style that sets a colour, a font size, a border or a shadow is outside the token contract: use a token class. If the value is computed (a palette, a size from a prop), say why in an eslint-disable comment. Width, height and transforms are fine.',\n },\n ],\n },\n }\n}\n", "/// <reference types=\"node\" />\n/**\n * Where the UI Guardrails' rules apply, written once (docs/GATES.md \u00A723).\n *\n * Until UIG-10 every repo wrote this itself: `eslint.gates.js` (the rules and\n * where they apply) and `eslint.gates.config.js` (the gate: those rules alone)\n * in Peek, in Ship's `web/`, and here pointed inward. The rules were always the\n * package's; these two files were copies. Now they are two functions.\n *\n * Where: every `.ts` and `.tsx` under `src`, stories included (a story is what\n * people read and copy), tests not (a test mounts markup to test it) \u2014\n * Katerina's rulings of 13 September; `.ts` since UIG-8. This package's own\n * inward set reads `.tsx` only, as it always has.\n *\n * A place that keeps something says why, on the line above:\n * `// @estiva-escape: <reason>`, or `{/* @estiva-escape: <reason> *\\/}` as a\n * JSX child. Never `eslint-disable`: the count refuses it.\n */\nimport type { ESLint, Linter } from 'eslint'\nimport betterTailwindcss from 'eslint-plugin-better-tailwindcss'\nimport { parser as typescriptParser } from 'typescript-eslint'\nimport estiva from '../eslint/index'\nimport { TOKEN_LINT_IGNORES } from './token-lint'\n\nexport interface GateConfigOptions {\n /** `app` (the default) runs `configs.recommended`; `package` runs this package's inward set. */\n audience?: 'app' | 'package'\n /**\n * Plugins whose `eslint-disable` directives appear in the source, registered\n * here with every rule off. ESLint refuses a directive for a rule it does not\n * know, so without them the gate would fail on another lint's comments.\n * The token lint's names are registered already.\n */\n quiet?: Record<string, ESLint.Plugin>\n /** Folders the gate never reads, beside build output. */\n ignores?: string[]\n}\n\n/**\n * The rules and where they apply \u2014 one config object, to add beside\n * everything else an app's `eslint.config.js` runs, so an editor shows a\n * refusal where the element is typed.\n */\nexport function gateLint({ audience = 'app' }: Pick<GateConfigOptions, 'audience'> = {}): Linter.Config {\n return {\n ...(audience === 'package' ? estiva.configs.package : estiva.configs.recommended),\n files: audience === 'package' ? ['src/**/*.tsx'] : ['src/**/*.{ts,tsx}'],\n ignores: ['**/*.test.ts', '**/*.test.tsx'],\n languageOptions: {\n parser: typescriptParser,\n parserOptions: { ecmaFeatures: { jsx: true } },\n },\n }\n}\n\n/**\n * The gate: the rules on their own \u2014 `npm run lint:rules`, CI's job `gate`, and\n * the config the editor hook lints a proposed write with. It fails on a gate\n * rule and on nothing else, so a backlog in the full lint never blocks it.\n */\nexport function gateConfig({ audience = 'app', quiet = {}, ignores = [] }: GateConfigOptions = {}): Linter.Config[] {\n return [\n { ignores: [...TOKEN_LINT_IGNORES, ...ignores] },\n {\n plugins: {\n ...quiet,\n 'better-tailwindcss': betterTailwindcss,\n 'token-values': betterTailwindcss,\n 'token-spacing': betterTailwindcss,\n },\n // Every other config's directives are unused here, because their rules are\n // off here. Reporting them would be this config complaining about the\n // others' business.\n linterOptions: { reportUnusedDisableDirectives: 'off' },\n },\n gateLint({ audience }),\n ]\n}\n", "/// <reference types=\"node\" />\n/**\n * The gate checks every app runs, written once (docs/GATES.md \u00A723).\n *\n * Until UIG-10 Peek and Ship each wrote them into their own\n * `scripts/gates-checks.mjs`. 65 different checks for finished tickets, counted\n * on 17 September: 28 were identical in both, 21 were the same check written two\n * ways, 2 read each app's real page, and 14 read code only one app has. This is\n * the first three groups as one list. The fourth stays in the app's own checks\n * file, beside this list.\n *\n * Where Peek and Ship wrote one check two ways, the check here runs both, so it\n * passes wherever either did: the raw-link probe with both of their texts, the\n * token probes against each token config the app has.\n *\n * `scripts/gates-compare.mjs` holds this list to Peek's and Ship's: every check\n * of theirs must be here, or be about code only that app has, or the comparison\n * fails and names it.\n *\n * A check for a ticket not built yet is a first guess at the evidence, taken\n * from the ticket's own text; the ticket that builds it changes it here.\n */\nimport type { CheckResult, GateCheck, GateHelpers, GateTicket } from './status'\n\nexport interface AppCheckOptions {\n /** Where the app is inside the repository: `web` in Ship, `.` elsewhere. */\n app?: string\n /** A real page of the app, relative to the app, full of elements that are not controls. */\n page: string\n /** The ticket the gate chain is listed under, and whether this repo owns it: UIG-3 in Peek, UIG-4 in Ship. */\n chain?: { ref: string; owner: boolean }\n /** The token lint configs the app has, relative to the app. */\n tokenConfigs?: string[]\n /** The name of the app's header component a hand-made header row should name (UIG-22's first guess). */\n headers?: string[]\n}\n\nexport const APP_TICKET_TITLES: Record<string, string> = {\n 'UIG-2': 'The tracking rails \u2014 GATES.md, the status script, the guide committed',\n 'UIG-3': 'Tracer bullet \u2014 one rule, end to end, blocking in Peek',\n 'UIG-4': 'The same chain, blocking in Ship',\n 'UIG-6': 'Branch protection \u2014 the backstop, all three repos',\n 'UIG-7': 'Lint rule \u2014 every remaining raw element',\n 'UIG-8': 'Lint rule \u2014 forbid the reach',\n 'UIG-9': 'Lint rule \u2014 the className allow-list',\n 'UIG-13': \"The registry widens to Peek's 115 and Ship's 74, with classification\",\n 'UIG-19': 'Lock the contract in CI, and make the three Storybooks one search',\n 'UIG-20': 'The Claude skill, reading the registry',\n 'UIG-21': 'CLAUDE.md becomes an index, not a lecture',\n 'UIG-22': 'Fingerprint \u2014 a hand-made header row',\n 'UIG-23': 'Fingerprint \u2014 a hand-made empty state',\n 'UIG-24': 'Fingerprint \u2014 a browser tooltip where ours belongs',\n 'UIG-25': 'Fingerprint \u2014 a component copied out by hand',\n 'UIG-27': 'The components the apps had to build themselves \u2014 Link, ProgressBar, EmptyState padding',\n 'UIG-28': 'Close the two holes in the token contract \u2014 arbitrary values, and inline style',\n 'UIG-30': 'RichText \u2014 one component that draws a message\\'s text, for both apps',\n 'UIG-32': 'Peek and Ship take their gate pieces from the package',\n}\n\n/** Run several checks as one: the first that does not pass is the answer. */\nfunction all(h: GateHelpers, steps: (() => CheckResult | Promise<CheckResult>)[], label: string): () => Promise<CheckResult> {\n return async () => {\n for (const step of steps) {\n const r = await step()\n if (r.result !== 'pass') return r\n }\n return h.PASS(label)\n }\n}\n\nexport function appChecks(h: GateHelpers, { app = '.', page, chain = { ref: 'UIG-3', owner: false }, tokenConfigs = ['eslint.tokens.config.js', 'eslint.config.js'], headers = ['ContainerHeader', 'SectionHeader'] }: AppCheckOptions): GateTicket[] {\n const WEB = app === '.' ? '' : `${app.replace(/\\/$/, '')}/`\n const cwd = app === '.' ? undefined : app\n const PROBE = 'src/components/__gates_probe__.tsx'\n const GATES = 'eslint.gates.config.js'\n const at = (rel: string) => `${WEB}${rel}`\n const probe = (code: string, expect: 'error' | 'warning' | 'none', mentions?: string, file = PROBE, config = GATES) => () =>\n h.lint({ ...(cwd ? { cwd } : {}), config, file, code, expect, mentions })\n const component = (body: string) => `export function Probe() {\\n return ${body}\\n}\\n`\n const installed = at('node_modules/@estiva-app/ui/dist/index.d.ts')\n const realPage = (): Promise<CheckResult> => h.lint({ ...(cwd ? { cwd } : {}), config: GATES, file: page, code: h.read(at(page)), expect: 'none' })\n const button = component('<button type=\"button\">x</button>')\n const ticket = (ref: string, checks: GateCheck[], owner = false): GateTicket => ({ ref, title: APP_TICKET_TITLES[ref], owner, checks })\n\n const tokenProbe = (code: string, expect: 'error' | 'warning' | 'none', mentions?: string, file = PROBE) =>\n all(h, tokenConfigs.map((config) => probe(code, expect, mentions, file, config)), `${tokenConfigs.join(' and ')}: as expected`)\n\n return [\n ticket('UIG-2', [\n { what: 'npm run gates:status is wired', run: () => h.script('package.json', 'gates:status') },\n ]),\n ticket(chain.ref, [\n { what: 'the gate lint config loads', run: () => h.loads(at(GATES)) },\n { what: `${at('package.json')} has lint:rules`, run: () => h.script(at('package.json'), 'lint:rules') },\n { what: 'CI runs lint:rules', run: () => h.ci('lint:rules') },\n { what: 'the committed hook runs the gate lint', run: () => h.hook('.claude/settings.json', 'gates') },\n { what: '.gates-count.json is committed and parses', run: () => h.json(h.exists('.gates-count.json') ? '.gates-count.json' : at('.gates-count.json')) },\n { what: 'a raw <button> is an error naming Button', run: probe(button, 'error', 'Button') },\n { what: 'the same in a story', run: probe(button, 'error', 'Button', 'src/stories/__gates_probe__.stories.tsx') },\n { what: 'a test file is not checked, while source is', run: async () => {\n const source = await probe(button, 'error', 'Button')()\n if (source.result !== 'pass') return h.FAIL(`source is not checked yet, so this proves nothing: ${source.detail}`)\n return probe(button, 'none', undefined, 'src/components/__gates_probe__.test.tsx')()\n } },\n { what: 'the debt list exists', run: () => h.file('docs/GATES-DEBT.md') },\n ], chain.owner),\n ticket('UIG-6', [\n { what: 'GitHub requires the check gate to merge into main', run: () => h.protectedBranch(/^gate$/) },\n { what: \"CI's job gate runs lint:rules\", run: () => h.ciJob('gate', 'lint:rules') },\n ]),\n ticket('UIG-7', [\n { what: 'a raw <input> is an error naming TextInput', run: probe(component('<input />'), 'error', 'TextInput') },\n { what: 'a raw <a> is an error naming Link', run: all(h, [probe(component('<a href=\"/topics\">Topics</a>'), 'error', 'Link'), probe(component('<a href=\"/issues\">Issues</a>'), 'error', 'Link')], 'a raw <a> is an error naming Link') },\n { what: 'a raw <form> is an error naming Form, and a file input one naming FilePicker', run: async () => {\n const form = await probe(component('<form />'), 'error', '`Form`')()\n if (form.result !== 'pass') return form\n return probe(component('<input type=\"file\" />'), 'error', 'FilePicker')()\n } },\n { what: '<textarea>, <select>, <dialog> and <label> name Textarea, Select, DialogShell and Field', run: async () => {\n for (const [element, part] of [['textarea', 'Textarea'], ['select', 'Select'], ['dialog', 'DialogShell'], ['label', 'Field']]) {\n const one = await probe(component(`<${element} />`), 'error', `\\`${part}\\``)()\n if (one.result !== 'pass') return one\n }\n return h.PASS('all four are errors, each naming its part')\n } },\n { what: 'an element the package has no part for is an error that says to ask for one', run: probe(component('<iframe title=\"Map\" />'), 'error', 'ask Katerina') },\n { what: 'an escaped element is not an error, while the same element is', run: async () => {\n const raw = await probe(component('<form />'), 'error')()\n if (raw.result !== 'pass') return h.FAIL(`a raw <form> is not caught yet, so this proves nothing: ${raw.detail}`)\n return probe('export function Probe() {\\n return (\\n // @estiva-escape: a probe that keeps its element on purpose\\n <form />\\n )\\n}\\n', 'none')()\n } },\n { what: 'a real page, full of elements that are not controls, gets no error', run: realPage },\n ]),\n ticket('UIG-8', [\n { what: 'a Base UI import is an error naming the part built on it', run: probe(\"import { Popover } from '@base-ui/react/popover'\\nexport const Probe = Popover\\n\", 'error', '`Popover`') },\n { what: 'createPortal is an error naming the floating parts', run: probe(\"import { createPortal } from 'react-dom'\\nexport const Probe = (to: HTMLElement) => createPortal(<span />, to)\\n\", 'error', '`DialogShell`') },\n { what: 'a key listener on the whole page is an error, in a .ts file too', run: probe(\"export function listen() {\\n window.addEventListener('keydown', () => {})\\n}\\n\", 'error', '`DialogShell`', 'src/lib/__gates_probe__.ts') },\n { what: 'arrow keys by hand are an error; Enter in a field is not', run: async () => {\n const arrows = await probe(\"export const onKeyDown = (e: KeyboardEvent) => e.key === 'ArrowDown'\\n\", 'error', '`Menu`', 'src/lib/__gates_probe__.ts')()\n if (arrows.result !== 'pass') return arrows\n return probe(\"export const onKeyDown = (e: KeyboardEvent) => e.key === 'Enter'\\n\", 'none', undefined, 'src/lib/__gates_probe__.ts')()\n } },\n { what: 'a hand-written role=\"option\" is an error naming Select', run: probe(component('<div role=\"option\" />'), 'error', '`Select`') },\n { what: 'tabIndex={0} on a div is an error; tabIndex={-1} is not', run: async () => {\n const zero = await probe(component('<div tabIndex={0} />'), 'error', '`Button`')()\n if (zero.result !== 'pass') return h.FAIL(`tabIndex={0} is not caught yet, so this proves nothing: ${zero.detail}`)\n return probe(component('<div tabIndex={-1} />'), 'none')()\n } },\n { what: 'overflow-y-auto is an error naming ScrollArea', run: probe(component('<div className=\"h-64 overflow-y-auto\" />'), 'error', '`ScrollArea`') },\n { what: 'an escaped listener is not an error, while the same listener is', run: async () => {\n const raw = await probe(\"export function listen() {\\n document.addEventListener('mousedown', () => {})\\n}\\n\", 'error', undefined, 'src/lib/__gates_probe__.ts')()\n if (raw.result !== 'pass') return h.FAIL(`a page listener is not caught yet, so this proves nothing: ${raw.detail}`)\n return probe(\"export function listen() {\\n // @estiva-escape: a probe that keeps its listener on purpose\\n document.addEventListener('mousedown', () => {})\\n}\\n\", 'none', undefined, 'src/lib/__gates_probe__.ts')()\n } },\n { what: 'a real page gets no error', run: realPage },\n ]),\n ticket('UIG-9', [\n { what: 'a border passed into Button is an error naming Button', run: probe(`import { Button } from '@estiva-app/ui'\\n${component('<Button className=\"border\">x</Button>')}`, 'error', 'on `Button` changes how it looks') },\n { what: 'placement passed into Button is not', run: probe(`import { Button } from '@estiva-app/ui'\\n${component('<Button className=\"mt-2 w-full flex-1 relative\">x</Button>')}`, 'none') },\n { what: 'an escaped look is not an error, while the same look is', run: async () => {\n const raw = await probe(`import { Link } from '@estiva-app/ui'\\n${component('<Link href=\"/x\" className=\"after:absolute after:inset-0\">x</Link>')}`, 'error', '`Link`')()\n if (raw.result !== 'pass') return h.FAIL(`a restyled part is not caught yet, so this proves nothing: ${raw.detail}`)\n return probe(`import { Link } from '@estiva-app/ui'\\nexport function Probe() {\\n return (\\n // @estiva-escape: a probe that keeps its row link on purpose\\n <Link href=\"/x\" className=\"after:absolute after:inset-0\">x</Link>\\n )\\n}\\n`, 'none')()\n } },\n { what: 'a real page gets no error', run: realPage },\n ]),\n ticket('UIG-13', [\n { what: 'registry.json is committed and has entries', run: () => h.json(h.exists('registry.json') ? 'registry.json' : at('registry.json'), (d) => ((d as { entries?: unknown[]; components?: unknown[] }).entries ?? (d as { components?: unknown[] }).components ?? []).length > 0, 'registry.json has entries') },\n ]),\n ticket('UIG-19', [\n { what: 'the usage-page contract runs in CI', run: () => h.ci(/[\\w:-]*contract[\\w:-]*/) },\n ]),\n ticket('UIG-20', [\n { what: 'a committed skill runs ui:find', run: () => (h.listFiles('.claude/skills', (n) => n === 'SKILL.md').some((f) => h.read(f).includes('ui:find')) ? h.PASS('a skill runs ui:find') : h.FAIL('no skill in .claude/skills runs ui:find')) },\n ]),\n ticket('UIG-21', [\n { what: 'path-scoped instructions exist', run: () => (h.listFiles('.claude/rules', (n) => n.endsWith('.md')).some((f) => /^paths:/m.test(h.read(f))) ? h.PASS('.claude/rules has paths: instructions') : h.FAIL('no .claude/rules file with paths:')) },\n ]),\n ticket('UIG-22', [\n { what: 'a hand-made header row is an error naming the header part', run: all(h, headers.map((name) => probe(component(`<section className=\"flex flex-col\"><div className=\"flex items-center px-3 py-2\"><span>${name === 'SectionHeader' ? 'Issues' : 'Folders'}</span></div><div /></section>`), 'error', name)), 'a hand-made header row is an error naming the header part') },\n ]),\n ticket('UIG-23', [\n { what: 'a hand-made empty line is an error naming EmptyState', run: probe(\"export function Probe({ items }: { items: string[] }) {\\n return <div>{items.length === 0 ? <p className=\\\"text-text-secondary\\\">Nothing here</p> : items.map((i) => <span key={i}>{i}</span>)}</div>\\n}\\n\", 'error', 'EmptyState') },\n ]),\n ticket('UIG-24', [\n { what: 'a title= is an error naming WithTooltip', run: probe(`import { Button } from '@estiva-app/ui'\\n${component('<Button title=\"Delete\">x</Button>')}`, 'error', 'WithTooltip') },\n ]),\n ticket('UIG-25', [\n { what: \"SectionLabel's class list, typed by hand, is a warning\", run: probe(component('<span className=\"text-[10px] uppercase tracking-wide text-text-muted\">Label</span>'), 'warning', 'SectionLabel') },\n ]),\n ticket('UIG-27', [\n { what: 'the installed package has Link', run: () => h.contains(installed, /\\bLink\\b/, 'the installed @estiva-app/ui exports Link') },\n { what: 'the installed package has ProgressBar', run: () => h.contains(installed, /\\bProgressBar\\b/, 'the installed @estiva-app/ui exports ProgressBar') },\n ]),\n ticket('UIG-28', [\n { what: 'text-[14px] is an error', run: tokenProbe(component('<div className=\"text-[14px]\">x</div>'), 'error') },\n { what: 'a colour in an inline style is an error', run: tokenProbe(component(\"<div style={{ backgroundColor: '#ff0000' }}>x</div>\"), 'error') },\n { what: 'h-[240px] is a warning', run: tokenProbe(component('<div className=\"h-[240px]\">x</div>'), 'warning') },\n { what: 'h-[240px] is not an error', run: tokenProbe(component('<div className=\"h-[240px]\">x</div>'), 'none') },\n { what: 'a hand-written line height behind an arbitrary variant is an error', run: tokenProbe(component('<div className=\"[&_p]:leading-[1.4]\">x</div>'), 'error') },\n { what: 'a raw colour in a class is an error (the pattern that matched nothing, R1)', run: tokenProbe(component('<div className=\"bg-[#5c69dc]\">x</div>'), 'error', 'raw colour') },\n { what: 'a width and height from a prop pass', run: tokenProbe(\"export function Probe({ size }: { size: number }) {\\n return <div style={{ width: size, height: size }}>x</div>\\n}\\n\", 'none') },\n { what: 'a test file is not checked for hand-written values, while source is', run: all(h, tokenConfigs.map((config) => async () => {\n const code = component(\"<div className=\\\"text-[14px]\\\" style={{ color: 'red' }}>x</div>\")\n const source = await probe(code, 'error', undefined, PROBE, config)()\n if (source.result !== 'pass') return h.FAIL(`source is not checked yet, so this proves nothing: ${source.detail}`)\n return probe(code, 'none', undefined, 'src/components/__gates_probe__.test.tsx', config)()\n }), 'a test file is not checked for hand-written values, while source is') },\n ]),\n ticket('UIG-30', [\n { what: 'the installed package has RichText', run: () => h.contains(installed, /\\bRichText\\b/, 'the installed @estiva-app/ui exports RichText') },\n ]),\n // Katerina's ruling of 17 September (docs/GATES.md \u00A723): one copy, in the package. An app made\n // by create-estiva-app passes this from its first commit; Peek and Ship do once UIG-32 lands.\n ticket('UIG-32', [\n { what: 'the gate pieces come from the package, and no repo keeps a copy', run: all(h, [\n () => h.contains(at('eslint.gates.config.js'), /from '@estiva-app\\/ui\\/gates'/, 'the gate config imports @estiva-app/ui/gates'),\n () => h.hook('.claude/settings.json', '@estiva-app/ui/dist/gates/cli.js'),\n () => h.contains('package.json', /\"gates:status\":\\s*\"estiva-gates status\"/, 'gates:status runs estiva-gates'),\n () => (h.exists('scripts/gates-status.mjs') ? h.FAIL('scripts/gates-status.mjs is a copy of the status engine: run estiva-gates status instead') : h.PASS('no copy of the status engine')),\n ], 'the gate config, the hook and gates:status are the package\\'s; no copy of the engine') },\n ]),\n ]\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/// <reference types=\"node\" />\n/**\n * The token contract as lint settings, written once (docs/GATES.md \u00A723).\n *\n * Until UIG-10 this block was pasted into every repo: `eslint.config.js` here,\n * `eslint.tokens.js` in Peek, `eslint.config.js` in Ship. Katerina ruled on 17 September that a\n * gate piece ships in the package and an app imports it, so this is that one\n * copy. Peek and Ship move onto it in UIG-32.\n *\n * Two configs, as the copies had:\n *\n * - `tokenLint` \u2014 a class Tailwind does not generate (the doubled token name,\n * `bg-bg-surface`), Tailwind's own type ramp or palette, a raw colour, an\n * opacity modifier on a token colour.\n * - `tokenValues` \u2014 UIG-28: type, corners and shadows written by hand, and an\n * inline style that sets a colour (errors); heights, spacing and border\n * widths written by hand (warnings, never blocking).\n *\n * The patterns are the same for everyone. Only the words differ: an app is told\n * to use \"the package\", the package is told to add the token to `tokens.css`.\n * Measured on 17 September: the UIG-28 block was byte for byte the same in Peek\n * and Ship, and this repository's had the same patterns with seven messages\n * worded for the package.\n *\n * Where the plugin looks: every `className`, every `cn()` / `clsx()` argument,\n * and the values of a class map whose name ends in `Styles` / `_STYLES` or\n * `Classes` / `_CLASSES`. A class map named any other way is invisible to it.\n *\n * Tests are out of `tokenValues` (ruling of 13 September): a test mounts markup\n * to test it. A kept hand-written value says why:\n * `// eslint-disable-next-line <rule> -- @estiva-escape: <reason>` (A2).\n */\nimport { createRequire } from 'node:module'\nimport { fileURLToPath } from 'node:url'\nimport type { Linter } from 'eslint'\nimport betterTailwindcss from 'eslint-plugin-better-tailwindcss'\nimport { getDefaultSelectors } from 'eslint-plugin-better-tailwindcss/defaults'\nimport { parser as typescriptParser } from 'typescript-eslint'\n\n/** Who reads the messages. */\nexport type TokenAudience = 'app' | 'package'\n\nexport interface TokenLintOptions {\n /** The Tailwind config the unknown-class rule reads, relative to where ESLint runs. */\n tailwindConfig?: string\n /** `app` (the default) names the package; `package` names `tokens.css` and the preset. */\n audience?: TokenAudience\n}\n\n/** What the token lint never reads: build output. */\nexport const TOKEN_LINT_IGNORES = ['dist/', 'storybook-static/', 'node_modules/']\n\nconst CLASS_MAP_SELECTOR = {\n kind: 'variable',\n name: '^(?:[A-Z][A-Z0-9_]*_(?:STYLES|CLASSES)|[a-z][a-zA-Z0-9]*(?:Styles|Classes))$',\n match: [{ type: 'strings' }, { type: 'objectValues' }],\n}\n\nconst WORDS = {\n app: {\n ramp: \"Tailwind's type ramp is not a token. Use the ramp from the package: text-body-2, text-caption, text-h3, text-btn-default...\",\n palette: \"Tailwind's palette is not a token. Use a colour from the package (bg-bg-surface, text-text-primary, border-border-subtle...); a colour with no token is a missing token.\",\n raw: 'A raw colour is a missing token: add it to the package (tokens.css in every theme block, and the preset), or say why not in an eslint-disable comment.',\n opacity: 'An opacity modifier on a token colour compiles to nothing. A transparent colour is a token of its own (D16): add it to the package.',\n type: '$0 is type written by hand. A token from the package sets the size, line height, letter spacing and weight together (text-body-2, text-caption, text-h5...); if none fits, add one to the package, or say why not in an eslint-disable comment.',\n corner: '$0 is a corner written by hand, and no token has it. Use a corner from the package (rounded-sm 4px, rounded-md 6px, rounded-lg 8px...), or say why not in an eslint-disable comment.',\n shadow: '$0 is a shadow written by hand. Use a shadow from the package (shadow-sm, shadow-md, shadow-focus-ring, drop-shadow-glow-success...); a shadow with no token is a missing token.',\n },\n package: {\n ramp: \"Tailwind's type ramp is not a token. Use the ramp in tailwind-preset.js: text-body-2, text-caption, text-h3, text-btn-default...\",\n palette: \"Tailwind's palette is not a token. Use a colour from tailwind-preset.js (bg-bg-surface, text-text-primary, border-border-subtle...); a colour with no token is a missing token.\",\n raw: 'A raw colour is a missing token: add it to tokens.css in every theme block and to the preset, or say why not in an eslint-disable comment.',\n opacity: 'An opacity modifier on a token colour compiles to nothing. A transparent colour is a token of its own (D16): add it to tokens.css in every theme block and to the preset.',\n type: '$0 is type written by hand. A token from tailwind-preset.js sets the size, line height, letter spacing and weight together (text-body-2, text-caption, text-h5...); if none fits, add one to the preset and to cn.ts, or say why not in an eslint-disable comment.',\n corner: '$0 is a corner written by hand, and no token has it. Use a corner from tailwind-preset.js (rounded-sm 4px, rounded-md 6px, rounded-lg 8px...), or say why not in an eslint-disable comment.',\n shadow: '$0 is a shadow written by hand. Use a shadow from tailwind-preset.js (shadow-sm, shadow-md, shadow-focus-ring, drop-shadow-glow-success...); a shadow with no token is a missing token.',\n },\n} satisfies Record<TokenAudience, Record<string, string>>\n\n/**\n * The class rules. `no-unknown-classes` reads the app's own Tailwind config, so\n * it knows exactly what the preset and the app generate.\n */\nexport function tokenLint({ tailwindConfig = 'tailwind.config.js', audience = 'app' }: TokenLintOptions = {}): Linter.Config {\n const words = WORDS[audience]\n return {\n files: ['**/*.{ts,tsx}'],\n languageOptions: {\n parser: typescriptParser,\n parserOptions: { ecmaFeatures: { jsx: true } },\n },\n plugins: { 'better-tailwindcss': betterTailwindcss },\n settings: {\n 'better-tailwindcss': {\n tailwindConfig,\n selectors: [...getDefaultSelectors(), CLASS_MAP_SELECTOR],\n },\n },\n rules: {\n 'better-tailwindcss/no-unknown-classes': 'error',\n 'better-tailwindcss/no-restricted-classes': [\n 'error',\n {\n restrict: [\n { pattern: '^(?:[a-z0-9-]+:)*text-(?:xs|sm|base|lg|xl|[2-9]xl)$', message: words.ramp },\n {\n pattern:\n '^(?:[a-z0-9-]+:)*(?:text|bg|border|ring|outline|fill|stroke|decoration|divide|placeholder|from|via|to|accent|caret|shadow)-(?:black|white|slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)(?:-[0-9]{2,3})?(?:/[0-9]{1,3})?$',\n message: words.palette,\n },\n { pattern: '^(?:[a-z0-9-]+:)*[a-z-]+-\\\\[[^\\\\]]*(?:#[0-9a-fA-F]{3}|rgba?\\\\(|hsla?\\\\(|oklch\\\\()', message: words.raw },\n {\n pattern: '^(?:[a-z0-9-]+:)*(?:text|bg|border|ring|outline|fill|stroke|decoration|divide|placeholder|from|via|to|accent|caret|shadow)-(?:bg|text|border|accent|info|warning|success|error)-[a-z-]+/[0-9]{1,3}$',\n message: words.opacity,\n },\n ],\n },\n ],\n },\n }\n}\n\n// UIG-28's patterns. The variant part reads arbitrary variants too\n// (`[&_pre]:`, `data-[state]:`, `group-hover/row:`). A colour inside `text-[\u2026]`\n// is left to the raw-colour rule of `tokenLint`.\nconst V = '^(?:(?:[^:\\\\[\\\\]\\\\s]|\\\\[[^\\\\]]*\\\\])+:)*!?'\nconst NOT_COLOUR = '(?!color:|#|rgba?\\\\(|hsla?\\\\(|oklch\\\\(|var\\\\()'\nconst HAND_WRITTEN = {\n type: `${V}(?:text-\\\\[${NOT_COLOUR}|text-\\\\[length:|leading-\\\\[|tracking-\\\\[)[^\\\\]]+\\\\](?:/\\\\S*)?$`,\n corner: `${V}rounded(?:-(?:t|r|b|l|s|e|tl|tr|br|bl|ss|se|es|ee))?-\\\\[[^\\\\]]+\\\\]$`,\n shadow: `${V}(?:shadow|drop-shadow)-\\\\[(?!color:)[^\\\\]]+\\\\]$`,\n spacing: `${V}-?(?:w|h|size|min-w|min-h|max-w|max-h|p[xytrblse]?|m[xytrblse]?|gap(?:-[xy])?|space-[xy]|inset(?:-[xy])?|top|right|bottom|left|translate-[xy]|basis|indent|scroll-[mp][xytrblse]?|border-spacing(?:-[xy])?)-\\\\[[^\\\\]]+\\\\]$`,\n width: `${V}(?:border(?:-[xytrblse])?|divide-[xy]|outline|ring|ring-offset)-\\\\[(?:length:)?-?[0-9.]+(?:px|rem|em)?\\\\]$`,\n}\n\nconst INLINE_STYLE_TOKEN_PROPERTIES =\n '/^(color|background|backgroundColor|backgroundImage|fill|stroke|fontSize|border|borderTop|borderRight|borderBottom|borderLeft|borderBlock|borderInline|borderStyle|boxShadow|textShadow)$|Color$/'\n\ninterface Preset {\n theme: { extend: { fontSize: Record<string, string | [string, unknown]>; borderRadius: Record<string, string> } }\n}\n\n/**\n * The preset, loaded the way Tailwind loads it: it imports `tailwindcss/plugin`,\n * which a plain `import` cannot resolve. Read from beside this module, so an app\n * and this repository read the one preset the package ships.\n */\nfunction loadPreset(): Preset {\n const require = createRequire(import.meta.url)\n const loadConfig = require('tailwindcss/loadConfig') as (path: string) => Preset\n return loadConfig(fileURLToPath(new URL('../../tailwind-preset.js', import.meta.url)))\n}\n\n/**\n * UIG-28: the values the tokens already name, written by hand (Katerina's\n * rulings B11, B12, C3 of 13 September and R2 of 15 September). Register it\n * after `tokenLint`, whose parser and plugin settings it borrows. Under its own\n * rule names, `token-values` and `token-spacing` (the same plugin twice, so one\n * runs as errors and one as warnings): an escape for a hand-written size then\n * cannot also silence a raw colour on the same line.\n */\nexport function tokenValues({ audience = 'app' }: Pick<TokenLintOptions, 'audience'> = {}): Linter.Config {\n const words = WORDS[audience]\n const preset = loadPreset()\n // `text-[14px]` names the token of that size, read from the preset, so the\n // message stays true when the ramp changes.\n const tokensBySize: Record<string, string[]> = {}\n for (const [name, value] of Object.entries(preset.theme.extend.fontSize)) {\n const size = Array.isArray(value) ? value[0] : value\n ;(tokensBySize[size] ??= []).push(`text-${name}`)\n }\n const cornersBySize: Record<string, string[]> = {}\n for (const [name, size] of Object.entries(preset.theme.extend.borderRadius)) (cornersBySize[size] ??= []).push(name === 'DEFAULT' ? 'rounded' : `rounded-${name}`)\n\n return {\n files: ['**/*.{ts,tsx}'],\n ignores: ['**/*.test.ts', '**/*.test.tsx'],\n plugins: { 'token-values': betterTailwindcss, 'token-spacing': betterTailwindcss },\n rules: {\n 'token-values/no-restricted-classes': [\n 'error',\n {\n restrict: [\n ...Object.entries(tokensBySize).map(([size, names]) => ({\n pattern: `${V}text-\\\\[${size.replace('.', '\\\\.')}\\\\](?:/\\\\S*)?$`,\n message: `$0 is the type ramp written by hand. Use ${names.join(' or ')}.`,\n })),\n {\n pattern: HAND_WRITTEN.type,\n message: words.type,\n },\n ...Object.entries(cornersBySize).map(([size, names]) => ({\n pattern: `${V}rounded(?:-(?:t|r|b|l|s|e|tl|tr|br|bl|ss|se|es|ee))?-\\\\[${size.replace('.', '\\\\.')}\\\\]$`,\n message: `$0 is a corner written by hand. Use ${names.join(' or ')} (with the same side, if it has one).`,\n })),\n {\n pattern: HAND_WRITTEN.corner,\n message: words.corner,\n },\n {\n pattern: HAND_WRITTEN.shadow,\n message: words.shadow,\n },\n ],\n },\n ],\n 'token-spacing/no-restricted-classes': [\n 'warn',\n {\n restrict: [\n {\n pattern: HAND_WRITTEN.spacing,\n message: '$0 is a size or a space written by hand. Use a step of the spacing scale (p-3, h-9, gap-2...) if one fits. Reported, not blocked.',\n },\n {\n pattern: HAND_WRITTEN.width,\n message: '$0 is a border or ring width written by hand. Use border, border-2, ring-1... if one fits. Reported, not blocked.',\n },\n ],\n },\n ],\n 'no-restricted-syntax': [\n 'error',\n {\n selector: `JSXAttribute[name.name=\"style\"] > JSXExpressionContainer > ObjectExpression > Property[key.name=${INLINE_STYLE_TOKEN_PROPERTIES}]`,\n message: 'An inline style that sets a colour, a font size, a border or a shadow is outside the token contract: use a token class. If the value is computed (a palette, a size from a prop), say why in an eslint-disable comment. Width, height and transforms are fine.',\n },\n ],\n },\n }\n}\n", "/// <reference types=\"node\" />\n/**\n * Where the UI Guardrails' rules apply, written once (docs/GATES.md \u00A723).\n *\n * Until UIG-10 every repo wrote this itself: `eslint.gates.js` (the rules and\n * where they apply) and `eslint.gates.config.js` (the gate: those rules alone)\n * in Peek, in Ship's `web/`, and here pointed inward. The rules were always the\n * package's; these two files were copies. Now they are two functions.\n *\n * Where: every `.ts` and `.tsx` under `src`, stories included (a story is what\n * people read and copy), tests not (a test mounts markup to test it) \u2014\n * Katerina's rulings of 13 September; `.ts` since UIG-8. This package's own\n * inward set reads `.tsx` only, as it always has.\n *\n * A place that keeps something says why, on the line above:\n * `// @estiva-escape: <reason>`, or `{/* @estiva-escape: <reason> *\\/}` as a\n * JSX child. Never `eslint-disable`: the count refuses it.\n */\nimport type { ESLint, Linter } from 'eslint'\nimport betterTailwindcss from 'eslint-plugin-better-tailwindcss'\nimport { parser as typescriptParser } from 'typescript-eslint'\nimport estiva from '../eslint/index'\nimport { TOKEN_LINT_IGNORES } from './token-lint'\n\nexport interface GateConfigOptions {\n /** `app` (the default) runs `configs.recommended`; `package` runs this package's inward set. */\n audience?: 'app' | 'package'\n /**\n * Plugins whose `eslint-disable` directives appear in the source, registered\n * here with every rule off. ESLint refuses a directive for a rule it does not\n * know, so without them the gate would fail on another lint's comments.\n * The token lint's names are registered already.\n */\n quiet?: Record<string, ESLint.Plugin>\n /** Folders the gate never reads, beside build output. */\n ignores?: string[]\n}\n\n/**\n * The rules and where they apply \u2014 one config object, to add beside\n * everything else an app's `eslint.config.js` runs, so an editor shows a\n * refusal where the element is typed.\n */\nexport function gateLint({ audience = 'app' }: Pick<GateConfigOptions, 'audience'> = {}): Linter.Config {\n return {\n ...(audience === 'package' ? estiva.configs.package : estiva.configs.recommended),\n files: audience === 'package' ? ['src/**/*.tsx'] : ['src/**/*.{ts,tsx}'],\n ignores: ['**/*.test.ts', '**/*.test.tsx'],\n languageOptions: {\n parser: typescriptParser,\n parserOptions: { ecmaFeatures: { jsx: true } },\n },\n }\n}\n\n/**\n * The gate: the rules on their own \u2014 `npm run lint:rules`, CI's job `gate`, and\n * the config the editor hook lints a proposed write with. It fails on a gate\n * rule and on nothing else, so a backlog in the full lint never blocks it.\n */\nexport function gateConfig({ audience = 'app', quiet = {}, ignores = [] }: GateConfigOptions = {}): Linter.Config[] {\n return [\n { ignores: [...TOKEN_LINT_IGNORES, ...ignores] },\n {\n plugins: {\n ...quiet,\n 'better-tailwindcss': betterTailwindcss,\n 'token-values': betterTailwindcss,\n 'token-spacing': betterTailwindcss,\n },\n // Every other config's directives are unused here, because their rules are\n // off here. Reporting them would be this config complaining about the\n // others' business.\n linterOptions: { reportUnusedDisableDirectives: 'off' },\n },\n gateLint({ audience }),\n ]\n}\n", "/// <reference types=\"node\" />\n/**\n * The gate checks every app runs, written once (docs/GATES.md \u00A723).\n *\n * Until UIG-10 Peek and Ship each wrote them into their own\n * `scripts/gates-checks.mjs`. 65 different checks for finished tickets, counted\n * on 17 September: 28 were identical in both, 21 were the same check written two\n * ways, 2 read each app's real page, and 14 read code only one app has. This is\n * the first three groups as one list. The fourth stays in the app's own checks\n * file, beside this list.\n *\n * Where Peek and Ship wrote one check two ways, the check here runs both, so it\n * passes wherever either did: the raw-link probe with both of their texts, the\n * token probes against each token config the app has.\n *\n * `scripts/gates-compare.mjs` holds this list to Peek's and Ship's: every check\n * of theirs must be here, or be about code only that app has, or the comparison\n * fails and names it.\n *\n * A check for a ticket not built yet is a first guess at the evidence, taken\n * from the ticket's own text; the ticket that builds it changes it here.\n */\nimport type { CheckResult, GateCheck, GateHelpers, GateTicket } from './status'\n\nexport interface AppCheckOptions {\n /** Where the app is inside the repository: `web` in Ship, `.` elsewhere. */\n app?: string\n /** A real page of the app, relative to the app, full of elements that are not controls. */\n page: string\n /** The ticket the gate chain is listed under, and whether this repo owns it: UIG-3 in Peek, UIG-4 in Ship. */\n chain?: { ref: string; owner: boolean }\n /** The token lint configs the app has, relative to the app. */\n tokenConfigs?: string[]\n /** The name of the app's header component a hand-made header row should name (UIG-22's first guess). */\n headers?: string[]\n}\n\nexport const APP_TICKET_TITLES: Record<string, string> = {\n 'UIG-2': 'The tracking rails \u2014 GATES.md, the status script, the guide committed',\n 'UIG-3': 'Tracer bullet \u2014 one rule, end to end, blocking in Peek',\n 'UIG-4': 'The same chain, blocking in Ship',\n 'UIG-6': 'Branch protection \u2014 the backstop, all three repos',\n 'UIG-7': 'Lint rule \u2014 every remaining raw element',\n 'UIG-8': 'Lint rule \u2014 forbid the reach',\n 'UIG-9': 'Lint rule \u2014 the className allow-list',\n 'UIG-13': \"The registry widens to Peek's 115 and Ship's 74, with classification\",\n 'UIG-19': 'Lock the contract in CI, and make the three Storybooks one search',\n 'UIG-20': 'The Claude skill, reading the registry',\n 'UIG-21': 'CLAUDE.md becomes an index, not a lecture',\n 'UIG-22': 'Fingerprint \u2014 a hand-made header row',\n 'UIG-23': 'Fingerprint \u2014 a hand-made empty state',\n 'UIG-24': 'Fingerprint \u2014 a browser tooltip where ours belongs',\n 'UIG-25': 'Fingerprint \u2014 a component copied out by hand',\n 'UIG-27': 'The components the apps had to build themselves \u2014 Link, ProgressBar, EmptyState padding',\n 'UIG-28': 'Close the two holes in the token contract \u2014 arbitrary values, and inline style',\n 'UIG-30': 'RichText \u2014 one component that draws a message\\'s text, for both apps',\n 'UIG-32': 'Peek and Ship take their gate pieces from the package',\n}\n\n/** Run several checks as one: the first that does not pass is the answer. */\nfunction all(h: GateHelpers, steps: (() => CheckResult | Promise<CheckResult>)[], label: string): () => Promise<CheckResult> {\n return async () => {\n for (const step of steps) {\n const r = await step()\n if (r.result !== 'pass') return r\n }\n return h.PASS(label)\n }\n}\n\nexport function appChecks(h: GateHelpers, { app = '.', page, chain = { ref: 'UIG-3', owner: false }, tokenConfigs = ['eslint.tokens.config.js', 'eslint.config.js'], headers = ['ContainerHeader', 'SectionHeader'] }: AppCheckOptions): GateTicket[] {\n const WEB = app === '.' ? '' : `${app.replace(/\\/$/, '')}/`\n const cwd = app === '.' ? undefined : app\n const PROBE = 'src/components/__gates_probe__.tsx'\n const GATES = 'eslint.gates.config.js'\n const at = (rel: string) => `${WEB}${rel}`\n const probe = (code: string, expect: 'error' | 'warning' | 'none', mentions?: string, file = PROBE, config = GATES) => () =>\n h.lint({ ...(cwd ? { cwd } : {}), config, file, code, expect, mentions })\n const component = (body: string) => `export function Probe() {\\n return ${body}\\n}\\n`\n const installed = at('node_modules/@estiva-app/ui/dist/index.d.ts')\n const realPage = (): Promise<CheckResult> => h.lint({ ...(cwd ? { cwd } : {}), config: GATES, file: page, code: h.read(at(page)), expect: 'none' })\n const button = component('<button type=\"button\">x</button>')\n const ticket = (ref: string, checks: GateCheck[], owner = false): GateTicket => ({ ref, title: APP_TICKET_TITLES[ref], owner, checks })\n\n const tokenProbe = (code: string, expect: 'error' | 'warning' | 'none', mentions?: string, file = PROBE) =>\n all(h, tokenConfigs.map((config) => probe(code, expect, mentions, file, config)), `${tokenConfigs.join(' and ')}: as expected`)\n\n return [\n ticket('UIG-2', [\n { what: 'npm run gates:status is wired', run: () => h.script('package.json', 'gates:status') },\n ]),\n ticket(chain.ref, [\n { what: 'the gate lint config loads', run: () => h.loads(at(GATES)) },\n { what: `${at('package.json')} has lint:rules`, run: () => h.script(at('package.json'), 'lint:rules') },\n { what: 'CI runs lint:rules', run: () => h.ci('lint:rules') },\n { what: 'the committed hook runs the gate lint', run: () => h.hook('.claude/settings.json', 'gates') },\n { what: '.gates-count.json is committed and parses', run: () => h.json(h.exists('.gates-count.json') ? '.gates-count.json' : at('.gates-count.json')) },\n { what: 'a raw <button> is an error naming Button', run: probe(button, 'error', 'Button') },\n { what: 'the same in a story', run: probe(button, 'error', 'Button', 'src/stories/__gates_probe__.stories.tsx') },\n { what: 'a test file is not checked, while source is', run: async () => {\n const source = await probe(button, 'error', 'Button')()\n if (source.result !== 'pass') return h.FAIL(`source is not checked yet, so this proves nothing: ${source.detail}`)\n return probe(button, 'none', undefined, 'src/components/__gates_probe__.test.tsx')()\n } },\n { what: 'the debt list exists', run: () => h.file('docs/GATES-DEBT.md') },\n ], chain.owner),\n ticket('UIG-6', [\n { what: 'GitHub requires the check gate to merge into main', run: () => h.protectedBranch(/^gate$/) },\n { what: \"CI's job gate runs lint:rules\", run: () => h.ciJob('gate', 'lint:rules') },\n ]),\n ticket('UIG-7', [\n { what: 'a raw <input> is an error naming TextInput', run: probe(component('<input />'), 'error', 'TextInput') },\n { what: 'a raw <a> is an error naming Link', run: all(h, [probe(component('<a href=\"/topics\">Topics</a>'), 'error', 'Link'), probe(component('<a href=\"/issues\">Issues</a>'), 'error', 'Link')], 'a raw <a> is an error naming Link') },\n { what: 'a raw <form> is an error naming Form, and a file input one naming FilePicker', run: async () => {\n const form = await probe(component('<form />'), 'error', '`Form`')()\n if (form.result !== 'pass') return form\n return probe(component('<input type=\"file\" />'), 'error', 'FilePicker')()\n } },\n { what: '<textarea>, <select>, <dialog> and <label> name Textarea, Select, DialogShell and Field', run: async () => {\n for (const [element, part] of [['textarea', 'Textarea'], ['select', 'Select'], ['dialog', 'DialogShell'], ['label', 'Field']]) {\n const one = await probe(component(`<${element} />`), 'error', `\\`${part}\\``)()\n if (one.result !== 'pass') return one\n }\n return h.PASS('all four are errors, each naming its part')\n } },\n { what: 'an element the package has no part for is an error that says to ask for one', run: probe(component('<iframe title=\"Map\" />'), 'error', 'ask Katerina') },\n { what: 'an escaped element is not an error, while the same element is', run: async () => {\n const raw = await probe(component('<form />'), 'error')()\n if (raw.result !== 'pass') return h.FAIL(`a raw <form> is not caught yet, so this proves nothing: ${raw.detail}`)\n return probe('export function Probe() {\\n return (\\n // @estiva-escape: a probe that keeps its element on purpose\\n <form />\\n )\\n}\\n', 'none')()\n } },\n { what: 'a real page, full of elements that are not controls, gets no error', run: realPage },\n ]),\n ticket('UIG-8', [\n { what: 'a Base UI import is an error naming the part built on it', run: probe(\"import { Popover } from '@base-ui/react/popover'\\nexport const Probe = Popover\\n\", 'error', '`Popover`') },\n { what: 'createPortal is an error naming the floating parts', run: probe(\"import { createPortal } from 'react-dom'\\nexport const Probe = (to: HTMLElement) => createPortal(<span />, to)\\n\", 'error', '`DialogShell`') },\n { what: 'a key listener on the whole page is an error, in a .ts file too', run: probe(\"export function listen() {\\n window.addEventListener('keydown', () => {})\\n}\\n\", 'error', '`DialogShell`', 'src/lib/__gates_probe__.ts') },\n { what: 'arrow keys by hand are an error; Enter in a field is not', run: async () => {\n const arrows = await probe(\"export const onKeyDown = (e: KeyboardEvent) => e.key === 'ArrowDown'\\n\", 'error', '`Menu`', 'src/lib/__gates_probe__.ts')()\n if (arrows.result !== 'pass') return arrows\n return probe(\"export const onKeyDown = (e: KeyboardEvent) => e.key === 'Enter'\\n\", 'none', undefined, 'src/lib/__gates_probe__.ts')()\n } },\n { what: 'a hand-written role=\"option\" is an error naming Select', run: probe(component('<div role=\"option\" />'), 'error', '`Select`') },\n { what: 'tabIndex={0} on a div is an error; tabIndex={-1} is not', run: async () => {\n const zero = await probe(component('<div tabIndex={0} />'), 'error', '`Button`')()\n if (zero.result !== 'pass') return h.FAIL(`tabIndex={0} is not caught yet, so this proves nothing: ${zero.detail}`)\n return probe(component('<div tabIndex={-1} />'), 'none')()\n } },\n { what: 'overflow-y-auto is an error naming ScrollArea', run: probe(component('<div className=\"h-64 overflow-y-auto\" />'), 'error', '`ScrollArea`') },\n { what: 'an escaped listener is not an error, while the same listener is', run: async () => {\n const raw = await probe(\"export function listen() {\\n document.addEventListener('mousedown', () => {})\\n}\\n\", 'error', undefined, 'src/lib/__gates_probe__.ts')()\n if (raw.result !== 'pass') return h.FAIL(`a page listener is not caught yet, so this proves nothing: ${raw.detail}`)\n return probe(\"export function listen() {\\n // @estiva-escape: a probe that keeps its listener on purpose\\n document.addEventListener('mousedown', () => {})\\n}\\n\", 'none', undefined, 'src/lib/__gates_probe__.ts')()\n } },\n { what: 'a real page gets no error', run: realPage },\n ]),\n ticket('UIG-9', [\n { what: 'a border passed into Button is an error naming Button', run: probe(`import { Button } from '@estiva-app/ui'\\n${component('<Button className=\"border\">x</Button>')}`, 'error', 'on `Button` changes how it looks') },\n { what: 'placement passed into Button is not', run: probe(`import { Button } from '@estiva-app/ui'\\n${component('<Button className=\"mt-2 w-full flex-1 relative\">x</Button>')}`, 'none') },\n { what: 'an escaped look is not an error, while the same look is', run: async () => {\n const raw = await probe(`import { Link } from '@estiva-app/ui'\\n${component('<Link href=\"/x\" className=\"after:absolute after:inset-0\">x</Link>')}`, 'error', '`Link`')()\n if (raw.result !== 'pass') return h.FAIL(`a restyled part is not caught yet, so this proves nothing: ${raw.detail}`)\n return probe(`import { Link } from '@estiva-app/ui'\\nexport function Probe() {\\n return (\\n // @estiva-escape: a probe that keeps its row link on purpose\\n <Link href=\"/x\" className=\"after:absolute after:inset-0\">x</Link>\\n )\\n}\\n`, 'none')()\n } },\n { what: 'a real page gets no error', run: realPage },\n ]),\n ticket('UIG-13', [\n { what: 'registry.json is committed and has entries', run: () => h.json(h.exists('registry.json') ? 'registry.json' : at('registry.json'), (d) => ((d as { entries?: unknown[]; components?: unknown[] }).entries ?? (d as { components?: unknown[] }).components ?? []).length > 0, 'registry.json has entries') },\n ]),\n ticket('UIG-19', [\n { what: 'the usage-page contract runs in CI', run: () => h.ci(/[\\w:-]*contract[\\w:-]*/) },\n ]),\n ticket('UIG-20', [\n { what: 'a committed skill runs ui:find', run: () => (h.listFiles('.claude/skills', (n) => n === 'SKILL.md').some((f) => h.read(f).includes('ui:find')) ? h.PASS('a skill runs ui:find') : h.FAIL('no skill in .claude/skills runs ui:find')) },\n ]),\n ticket('UIG-21', [\n { what: 'path-scoped instructions exist', run: () => (h.listFiles('.claude/rules', (n) => n.endsWith('.md')).some((f) => /^paths:/m.test(h.read(f))) ? h.PASS('.claude/rules has paths: instructions') : h.FAIL('no .claude/rules file with paths:')) },\n ]),\n ticket('UIG-22', [\n { what: 'a hand-made header row is an error naming the header part', run: all(h, headers.map((name) => probe(component(`<section className=\"flex flex-col\"><div className=\"flex items-center px-3 py-2\"><span>${name === 'SectionHeader' ? 'Issues' : 'Folders'}</span></div><div /></section>`), 'error', name)), 'a hand-made header row is an error naming the header part') },\n ]),\n ticket('UIG-23', [\n { what: 'a hand-made empty line is an error naming EmptyState', run: probe(\"export function Probe({ items }: { items: string[] }) {\\n return <div>{items.length === 0 ? <p className=\\\"text-text-secondary\\\">Nothing here</p> : items.map((i) => <span key={i}>{i}</span>)}</div>\\n}\\n\", 'error', 'EmptyState') },\n ]),\n ticket('UIG-24', [\n { what: 'a title= is an error naming WithTooltip', run: probe(`import { Button } from '@estiva-app/ui'\\n${component('<Button title=\"Delete\">x</Button>')}`, 'error', 'WithTooltip') },\n ]),\n ticket('UIG-25', [\n { what: \"SectionLabel's class list, typed by hand, is a warning\", run: probe(component('<span className=\"text-[10px] uppercase tracking-wide text-text-muted\">Label</span>'), 'warning', 'SectionLabel') },\n ]),\n ticket('UIG-27', [\n { what: 'the installed package has Link', run: () => h.contains(installed, /\\bLink\\b/, 'the installed @estiva-app/ui exports Link') },\n { what: 'the installed package has ProgressBar', run: () => h.contains(installed, /\\bProgressBar\\b/, 'the installed @estiva-app/ui exports ProgressBar') },\n ]),\n ticket('UIG-28', [\n { what: 'text-[14px] is an error', run: tokenProbe(component('<div className=\"text-[14px]\">x</div>'), 'error') },\n { what: 'a colour in an inline style is an error', run: tokenProbe(component(\"<div style={{ backgroundColor: '#ff0000' }}>x</div>\"), 'error') },\n { what: 'h-[240px] is a warning', run: tokenProbe(component('<div className=\"h-[240px]\">x</div>'), 'warning') },\n { what: 'h-[240px] is not an error', run: tokenProbe(component('<div className=\"h-[240px]\">x</div>'), 'none') },\n { what: 'a hand-written line height behind an arbitrary variant is an error', run: tokenProbe(component('<div className=\"[&_p]:leading-[1.4]\">x</div>'), 'error') },\n { what: 'a raw colour in a class is an error (the pattern that matched nothing, R1)', run: tokenProbe(component('<div className=\"bg-[#5c69dc]\">x</div>'), 'error', 'raw colour') },\n { what: 'a width and height from a prop pass', run: tokenProbe(\"export function Probe({ size }: { size: number }) {\\n return <div style={{ width: size, height: size }}>x</div>\\n}\\n\", 'none') },\n { what: 'a test file is not checked for hand-written values, while source is', run: all(h, tokenConfigs.map((config) => async () => {\n const code = component(\"<div className=\\\"text-[14px]\\\" style={{ color: 'red' }}>x</div>\")\n const source = await probe(code, 'error', undefined, PROBE, config)()\n if (source.result !== 'pass') return h.FAIL(`source is not checked yet, so this proves nothing: ${source.detail}`)\n return probe(code, 'none', undefined, 'src/components/__gates_probe__.test.tsx', config)()\n }), 'a test file is not checked for hand-written values, while source is') },\n ]),\n ticket('UIG-30', [\n { what: 'the installed package has RichText', run: () => h.contains(installed, /\\bRichText\\b/, 'the installed @estiva-app/ui exports RichText') },\n ]),\n // Katerina's ruling of 17 September (docs/GATES.md \u00A723): one copy, in the package. An app made\n // by create-estiva-app passes this from its first commit; Peek and Ship do once UIG-32 lands.\n ticket('UIG-32', [\n { what: 'the gate pieces come from the package, and no repo keeps a copy', run: all(h, [\n () => h.contains(at('eslint.gates.config.js'), /from '@estiva-app\\/ui\\/gates'/, 'the gate config imports @estiva-app/ui/gates'),\n () => h.hook('.claude/settings.json', '@estiva-app/ui/dist/gates/cli.js'),\n // `estiva-gates status` where npm can find the command; by path where it cannot,\n // because the install is in the app's folder and the script is in the repo's top one (Ship).\n () => h.contains('package.json', /\"gates:status\":\\s*\"[^\"]*(?:estiva-gates|@estiva-app\\/ui\\/dist\\/gates\\/cli\\.js)[^\"]*status/, \"gates:status runs the package's engine\"),\n () => (h.exists('scripts/gates-status.mjs') ? h.FAIL('scripts/gates-status.mjs is a copy of the status engine: run estiva-gates status instead') : h.PASS('no copy of the status engine')),\n ], 'the gate config, the hook and gates:status are the package\\'s; no copy of the engine') },\n ]),\n ]\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["require", "betterTailwindcss", "typescriptParser", "typescriptParser", "betterTailwindcss"]
7
7
  }
@@ -62,10 +62,12 @@ export interface GateSpec {
62
62
  tickets: GateTicket[];
63
63
  /** estiva-ui only: every ticket, and the repos it joins in. */
64
64
  all?: TicketListEntry[];
65
+ /** estiva-ui only: the repos beside it. `app` is the folder its app sits in: `web` in Ship. */
65
66
  siblings?: {
66
67
  name: string;
67
68
  path: string;
68
69
  env: string;
70
+ app?: string;
69
71
  }[];
70
72
  }
71
73
  /** What a report says ran it. A copy of the old script reports a hash instead. */
@@ -75,10 +77,12 @@ export declare function helpers(ROOT: string): GateHelpers;
75
77
  export interface StatusOptions {
76
78
  /** The repository to report on. */
77
79
  root?: string;
80
+ /** The folder that holds the app, relative to `root`: `web` in Ship, `.` elsewhere. */
81
+ app?: string;
78
82
  json?: boolean;
79
83
  detail?: boolean;
80
84
  }
81
85
  /** Print where the project stands. Returns what was printed, or the JSON report. */
82
- export declare function runStatus({ root, json, detail }?: StatusOptions): Promise<string>;
86
+ export declare function runStatus({ root, app, json, detail }?: StatusOptions): Promise<string>;
83
87
  export {};
84
88
  //# sourceMappingURL=status.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/gates/status.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/gates/status.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@estiva-app/ui",
3
- "version": "0.21.0",
3
+ "version": "0.21.1",
4
4
  "description": "Estiva's design tokens (the contract) and a small set of primitives (a convenience) for every Estiva app.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -216,7 +216,9 @@ export function appChecks(h: GateHelpers, { app = '.', page, chain = { ref: 'UIG
216
216
  { what: 'the gate pieces come from the package, and no repo keeps a copy', run: all(h, [
217
217
  () => h.contains(at('eslint.gates.config.js'), /from '@estiva-app\/ui\/gates'/, 'the gate config imports @estiva-app/ui/gates'),
218
218
  () => h.hook('.claude/settings.json', '@estiva-app/ui/dist/gates/cli.js'),
219
- () => h.contains('package.json', /"gates:status":\s*"estiva-gates status"/, 'gates:status runs estiva-gates'),
219
+ // `estiva-gates status` where npm can find the command; by path where it cannot,
220
+ // because the install is in the app's folder and the script is in the repo's top one (Ship).
221
+ () => h.contains('package.json', /"gates:status":\s*"[^"]*(?:estiva-gates|@estiva-app\/ui\/dist\/gates\/cli\.js)[^"]*status/, "gates:status runs the package's engine"),
220
222
  () => (h.exists('scripts/gates-status.mjs') ? h.FAIL('scripts/gates-status.mjs is a copy of the status engine: run estiva-gates status instead') : h.PASS('no copy of the status engine')),
221
223
  ], 'the gate config, the hook and gates:status are the package\'s; no copy of the engine') },
222
224
  ]),
package/src/gates/cli.ts CHANGED
@@ -4,13 +4,19 @@
4
4
  *
5
5
  * estiva-gates count --repo <name> [--package] write .gates-count.json (postlint:rules)
6
6
  * estiva-gates hook [--app <dir>] [--package] the editor gate, run by a PreToolUse hook
7
- * estiva-gates status [--json] [--detail] gates:status
7
+ * estiva-gates status [--app <dir>] [--json] [--detail] gates:status
8
8
  *
9
9
  * An app's `package.json` runs the first and the last through npm, which finds
10
10
  * the command in `node_modules/.bin`. The hook is run by path, from the
11
11
  * committed `.claude/settings.json`:
12
12
  *
13
13
  * node "$CLAUDE_PROJECT_DIR/node_modules/@estiva-app/ui/dist/gates/cli.js" hook
14
+ *
15
+ * A repo whose app sits in a folder of its own runs both by path, from the
16
+ * repo's top folder, and says where the app is — Ship (UIG-32):
17
+ *
18
+ * node "$CLAUDE_PROJECT_DIR/web/node_modules/@estiva-app/ui/dist/gates/cli.js" hook --app web
19
+ * node web/node_modules/@estiva-app/ui/dist/gates/cli.js status --app web
14
20
  */
15
21
  import { writeGateCount } from './count'
16
22
  import { runHook } from './hook'
@@ -43,7 +49,7 @@ async function main(): Promise<number> {
43
49
  return result.code
44
50
  }
45
51
  case 'status': {
46
- process.stdout.write(`${await runStatus({ json: flag('json'), detail: flag('detail') })}${flag('json') ? '' : '\n'}`)
52
+ process.stdout.write(`${await runStatus({ app: value('app'), json: flag('json'), detail: flag('detail') })}${flag('json') ? '' : '\n'}`)
47
53
  return 0
48
54
  }
49
55
  default:
@@ -180,4 +180,15 @@ describe('gates:status', () => {
180
180
  const json = JSON.parse(await runStatus({ root: dir, json: true }))
181
181
  expect(json.engine).toMatch(/^@estiva-app\/ui@\d+\.\d+\.\d+$/)
182
182
  })
183
+
184
+ it('finds the checks file beside the app, and still reads the repo from its top (Ship, UIG-32)', async () => {
185
+ // The checks file sits in `web/`, where the install is; what it names is read from the top folder.
186
+ const top = join(scratch, 'status-web')
187
+ mkdirSync(join(top, 'web', 'scripts'), { recursive: true })
188
+ writeFileSync(join(top, 'package.json'), JSON.stringify({ name: 'status-web', scripts: { 'gates:status': 'node web/node_modules/@estiva-app/ui/dist/gates/cli.js status --app web' } }))
189
+ writeFileSync(join(top, 'web', 'scripts', 'gates-checks.mjs'), "export default (h) => ({ repo: 'shipish', tickets: [\n { ref: 'UIG-4', title: 'The chain', owner: true, checks: [{ what: 'the top folder is read', run: () => h.script('package.json', 'gates:status') }] },\n] })\n")
190
+ const out = await runStatus({ root: top, app: 'web' })
191
+ expect(out).toContain('Tickets shipish owns (1):')
192
+ expect(out).toContain('✅ UIG-4')
193
+ })
183
194
  })
@@ -5,6 +5,7 @@
5
5
  * estiva-gates status one row per ticket
6
6
  * estiva-gates status --detail every check under every row
7
7
  * estiva-gates status --json machine output, read by estiva-ui's run
8
+ * estiva-gates status --app web the app sits in `web/`, as Ship's does
8
9
  *
9
10
  * Every row is decided by checks on real files, a real lint run or a real
10
11
  * GitHub setting. Nothing here reads a list that someone ticks by hand.
@@ -16,8 +17,10 @@
16
17
  * copy is named as one when estiva-ui runs it; UIG-32 moves Peek and Ship off
17
18
  * theirs.
18
19
  *
19
- * What differs per repo is `scripts/gates-checks.mjs`, in the repo: its checks,
20
- * or the checks every app runs (`appChecks`) plus its own.
20
+ * What differs per repo is `scripts/gates-checks.mjs`, beside the app: its
21
+ * checks, or the checks every app runs (`appChecks`) plus its own. It sits with
22
+ * the app because it imports the package — `web/scripts/` in Ship, whose app,
23
+ * and whose install, are in `web/` (UIG-32).
21
24
  *
22
25
  * The rows and the reasons for each check are in estiva-ui docs/GATES.md §15
23
26
  * and §17.
@@ -93,7 +96,8 @@ export interface GateSpec {
93
96
  tickets: GateTicket[]
94
97
  /** estiva-ui only: every ticket, and the repos it joins in. */
95
98
  all?: TicketListEntry[]
96
- siblings?: { name: string; path: string; env: string }[]
99
+ /** estiva-ui only: the repos beside it. `app` is the folder its app sits in: `web` in Ship. */
100
+ siblings?: { name: string; path: string; env: string; app?: string }[]
97
101
  }
98
102
 
99
103
  interface Row {
@@ -356,8 +360,11 @@ function statusOf(checks: CheckResult[]) {
356
360
  return fail > 0 ? 'none' : 'unknown'
357
361
  }
358
362
 
359
- async function runRepo(ROOT: string, h: GateHelpers): Promise<{ spec: GateSpec; report: Report }> {
360
- const checksFile = join(ROOT, 'scripts', 'gates-checks.mjs')
363
+ async function runRepo(ROOT: string, app: string, h: GateHelpers): Promise<{ spec: GateSpec; report: Report }> {
364
+ // The checks file sits with the app, because it imports `appChecks` from the
365
+ // package: in Ship that install is `web/node_modules`, not the repo's top
366
+ // folder (UIG-32). Everything it names is still read from the repo's top.
367
+ const checksFile = join(ROOT, app, 'scripts', 'gates-checks.mjs')
361
368
  const define = ((await import(pathToFileURL(checksFile).href)) as { default: (h: GateHelpers) => GateSpec }).default
362
369
  const spec = define(h)
363
370
  const git = (...a: string[]) => {
@@ -388,18 +395,22 @@ async function runRepo(ROOT: string, h: GateHelpers): Promise<{ spec: GateSpec;
388
395
  /**
389
396
  * Another repository's report. A repo on the package's engine runs it from its
390
397
  * own install; a repo still carrying `scripts/gates-status.mjs` runs that copy.
398
+ *
399
+ * `app` is the folder that repo's app sits in — `web` in Ship — where its
400
+ * install and its checks file are. The report is still of the whole repo.
391
401
  */
392
- function runSibling(dir: string, shown: string): { report?: Report; error?: string } {
402
+ function runSibling(dir: string, app: string, shown: string): { report?: Report; error?: string } {
393
403
  const copy = join(dir, 'scripts', 'gates-status.mjs')
394
- const installed = createRequire(join(dir, 'package.json'))
404
+ const installed = createRequire(join(dir, app, 'package.json'))
395
405
  let script = copy
396
406
  const args = ['--json']
397
407
  if (!existsSync(copy)) {
398
408
  try {
399
409
  script = join(installed.resolve('@estiva-app/ui/package.json'), '..', 'dist', 'gates', 'cli.js')
400
410
  args.unshift('status')
411
+ if (app !== '.') args.push('--app', app)
401
412
  } catch {
402
- return { error: `${shown} has neither its own gates-status.mjs nor @estiva-app/ui installed` }
413
+ return { error: `${shown} has neither its own gates-status.mjs nor @estiva-app/ui installed${app === '.' ? '' : ` in ${app}`}` }
403
414
  }
404
415
  if (!existsSync(script)) return { error: `${shown} has an @estiva-app/ui with no gates engine: install 0.21.0 or later` }
405
416
  }
@@ -442,16 +453,18 @@ function summary(rows: Row[]) {
442
453
  export interface StatusOptions {
443
454
  /** The repository to report on. */
444
455
  root?: string
456
+ /** The folder that holds the app, relative to `root`: `web` in Ship, `.` elsewhere. */
457
+ app?: string
445
458
  json?: boolean
446
459
  detail?: boolean
447
460
  }
448
461
 
449
462
  /** Print where the project stands. Returns what was printed, or the JSON report. */
450
- export async function runStatus({ root = process.cwd(), json = false, detail = false }: StatusOptions = {}): Promise<string> {
463
+ export async function runStatus({ root = process.cwd(), app = '.', json = false, detail = false }: StatusOptions = {}): Promise<string> {
451
464
  const ROOT = resolve(root)
452
465
  const h = helpers(ROOT)
453
466
  const shown = (dir: string) => (relative(ROOT, dir) || dir).replace(/\\/g, '/')
454
- const { spec, report } = await runRepo(ROOT, h)
467
+ const { spec, report } = await runRepo(ROOT, app, h)
455
468
 
456
469
  if (json) return JSON.stringify(report)
457
470
 
@@ -478,7 +491,7 @@ export async function runStatus({ root = process.cwd(), json = false, detail = f
478
491
  found.push({ name: s.name, note: `not found at ${shown(dir)} (set ${s.env} to point at it)`, missing: true })
479
492
  continue
480
493
  }
481
- const r = runSibling(dir, shown(dir))
494
+ const r = runSibling(dir, s.app ?? '.', shown(dir))
482
495
  if (!r.report) {
483
496
  found.push({ name: s.name, note: r.error ?? 'no report', missing: true })
484
497
  continue
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/gates/count.ts", "../../src/gates/hook.ts", "../../src/gates/status.ts"],
4
- "sourcesContent": ["/// <reference types=\"node\" />\n/**\n * Write `.gates-count.json`: what the gate finds, per rule \u2014 errors, warnings\n * and escapes (seam S3 in docs/GATES.md \u00A716). Written once (\u00A723): until UIG-10\n * Peek, Ship and this package each carried the same script, differing only in\n * the repo's name and which rules it seeded.\n *\n * Runs after the gate lint (npm's `postlint:rules`), so CI runs it too. The\n * file is committed: nothing compares it yet, and the later ratchet needs the\n * history to start at the first commit. It lints with the gate's own config,\n * with escapes reported, so an escape is counted rather than silent. The file\n * is rewritten only when a count changes, so running the lint does not leave a\n * changed file behind every time.\n *\n * It fails when an `eslint-disable` switched a gate rule off: that is not an\n * escape, and the escape count cannot see it.\n */\nimport { existsSync, readFileSync, writeFileSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { join, relative } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport type { ESLint as ESLintClass } from 'eslint'\nimport { APP_RULE_IDS, countGates, PACKAGE_RULE_IDS, type GateCount } from '../eslint/index'\n\nexport interface CountOptions {\n /** The folder the gate lints from, where `.gates-count.json` is written. */\n root?: string\n /** The name written into the file: `peek`, `ship`, `estiva-ui`, or the app's. */\n repo: string\n /** `app` seeds the app rules; `package` this package's inward set (UIG-5). */\n audience?: 'app' | 'package'\n /** The gate's config file, relative to `root`. */\n config?: string\n}\n\nexport interface CountResult extends GateCount {\n changed: boolean\n /** What to print, line by line. */\n lines: string[]\n /** What to print to stderr when a rule was switched off; empty when none was. */\n failures: string[]\n}\n\n/** ESLint from where the gate lints, never from beside this file: an app's own install. */\nasync function eslintFrom(root: string): Promise<typeof ESLintClass> {\n const resolved = createRequire(join(root, 'package.json')).resolve('eslint')\n return ((await import(pathToFileURL(resolved).href)) as { ESLint: typeof ESLintClass }).ESLint\n}\n\nexport async function writeGateCount({ root = process.cwd(), repo, audience = 'app', config = 'eslint.gates.config.js' }: CountOptions): Promise<CountResult> {\n const out = join(root, '.gates-count.json')\n const ESLint = await eslintFrom(root)\n const eslint = new ESLint({\n cwd: root,\n overrideConfigFile: join(root, config),\n overrideConfig: { settings: { estiva: { reportEscapes: true } } },\n })\n // The set the gate runs, so a rule with nothing to report still has its row.\n const { rules, disabled } = countGates(await eslint.lintFiles(['.']), audience === 'package' ? PACKAGE_RULE_IDS : APP_RULE_IDS)\n\n const previous = existsSync(out) ? (JSON.parse(readFileSync(out, 'utf8')) as { rules?: unknown }) : null\n const changed = JSON.stringify(previous?.rules) !== JSON.stringify(rules)\n if (changed) {\n const count = { schemaVersion: 1, repo, generatedAt: new Date().toISOString(), rules }\n writeFileSync(out, `${JSON.stringify(count, null, 2)}\\n`)\n }\n const lines = [changed ? '.gates-count.json: written' : '.gates-count.json: unchanged']\n for (const [id, c] of Object.entries(rules)) lines.push(` ${id}: ${c.errors} errors, ${c.warnings} warnings, ${c.escapes} escapes`)\n\n const failures: string[] = []\n if (disabled.length > 0) {\n failures.push('', 'An eslint-disable switched a gate rule off. Write the reason above the element instead: // @estiva-escape: <reason>')\n for (const d of disabled) failures.push(` ${relative(root, d.filePath)}:${d.line} ${d.ruleId}`)\n }\n return { rules, disabled, changed, lines, failures }\n}\n", "/// <reference types=\"node\" />\n/**\n * The editor gate: before Claude writes a `.ts` or `.tsx` under `src/`, lint the\n * text it is about to write with the gate, and refuse the write on an error.\n * Written once (docs/GATES.md \u00A723): until UIG-10 Peek, Ship and this package\n * each carried their own copy of this script.\n *\n * A `PreToolUse` hook in the repository's committed `.claude/settings.json`\n * runs it. Claude Code documents exit 2 as blocking the tool call, with stderr\n * handed back to Claude; exit 0 raises no objection. It reads\n * `$CLAUDE_PROJECT_DIR`, the project root where the session started \u2014 a session\n * started in a folder above or below the repository gets no hook (UIG-3,\n * UIG-4 findings).\n *\n * It runs the gate's own config and reimplements nothing: a hook and a lint\n * that could disagree would be worse than neither. Tests are not checked\n * (Katerina, 13 September); stories are. This package's inward gate reads `.tsx`\n * only, as its lint does.\n *\n * Input, from Claude Code on stdin: `tool_name` and `tool_input` \u2014 `file_path`\n * and `content` for Write; `file_path`, `old_string`, `new_string` and\n * `replace_all` for Edit. An Edit carries no whole file, so the edit is applied\n * to the file on disk here. Anything it cannot read passes, and the tool itself\n * reports its own failure.\n */\nimport { existsSync, readFileSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { isAbsolute, join, relative, resolve, sep } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport type { ESLint as ESLintClass } from 'eslint'\nimport { countGates } from '../eslint/index'\n\nexport interface HookOptions {\n /** The repository's top folder. Defaults to `$CLAUDE_PROJECT_DIR`, then the tool call's `cwd`. */\n root?: string\n /** The folder that holds the app, relative to `root`: `web` in Ship, `.` elsewhere. */\n app?: string\n /** `package` gates `.tsx` only, as this package's inward lint does. */\n audience?: 'app' | 'package'\n /** The hook's input, when not read from stdin (tests). */\n input?: unknown\n}\n\nexport interface HookResult {\n /** 0 lets the write through; 2 refuses it. */\n code: 0 | 2\n /** What goes to stderr when refused. */\n message: string\n}\n\ninterface ToolCall {\n tool_name?: string\n cwd?: string\n tool_input?: { file_path?: unknown; content?: unknown; old_string?: unknown; new_string?: unknown; replace_all?: unknown }\n}\n\nconst PASS: HookResult = { code: 0, message: '' }\n\nexport async function runHook({ root, app = '.', audience = 'app', input }: HookOptions = {}): Promise<HookResult> {\n let call: ToolCall\n try {\n call = (input ?? JSON.parse(readFileSync(0, 'utf8'))) as ToolCall\n } catch {\n return PASS\n }\n const args = call.tool_input ?? {}\n if (typeof args.file_path !== 'string') return PASS\n\n const top = root ?? process.env.CLAUDE_PROJECT_DIR ?? call.cwd ?? process.cwd()\n const appDir = resolve(top, app)\n const file = isAbsolute(args.file_path) ? args.file_path : resolve(call.cwd ?? top, args.file_path)\n const rel = relative(appDir, file).split(sep).join('/')\n const source = audience === 'package' ? /^src\\/.+\\.tsx$/ : /^src\\/.+\\.tsx?$/\n if (rel.startsWith('../') || isAbsolute(rel) || !source.test(rel) || /\\.test\\.tsx?$/.test(rel) || /\\.d\\.ts$/.test(rel)) return PASS\n\n const text = proposed(call.tool_name, args, file)\n if (text === null) return PASS\n\n // ESLint from the app's own install: in Ship the top folder has none.\n const resolved = createRequire(join(appDir, 'package.json')).resolve('eslint')\n const { ESLint } = (await import(pathToFileURL(resolved).href)) as { ESLint: typeof ESLintClass }\n const config = join(appDir, 'eslint.gates.config.js')\n const eslint = new ESLint({ cwd: appDir, overrideConfigFile: config })\n const results = await eslint.lintText(text, { filePath: file })\n // A rule's errors only. Code that does not parse yet (one edit of several) is\n // the typecheck's to judge; the gate looks again at the edit that completes it.\n const errors = results.flatMap((r) => r.messages.filter((m) => m.severity === 2 && m.ruleId))\n const { disabled } = countGates(results)\n if (errors.length === 0 && disabled.length === 0) return PASS\n\n const shown = (p: string) => relative(top, p).split(sep).join('/')\n const lines = [`${shown(file)} was not written: the UI Guardrails refuse it (${shown(config)}).`]\n for (const m of errors) lines.push(` ${m.line}:${m.column} ${m.message}${m.ruleId ? ` (${m.ruleId})` : ''}`)\n const keep = audience === 'package' ? 'Keep it only' : 'Keep the element only'\n for (const d of disabled) lines.push(` ${d.line} an eslint-disable switches ${d.ruleId} off. ${keep} with its reason on the line above: // @estiva-escape: <reason>`)\n return { code: 2, message: `${lines.join('\\n')}\\n` }\n}\n\n/** The file as it will be after this tool call, or null when that cannot be known. */\nfunction proposed(tool: string | undefined, args: NonNullable<ToolCall['tool_input']>, file: string): string | null {\n if (tool === 'Write') return typeof args.content === 'string' ? args.content : null\n if (tool !== 'Edit' || typeof args.old_string !== 'string' || typeof args.new_string !== 'string') return null\n const { old_string: from, new_string: to } = args\n const current = existsSync(file) ? readFileSync(file, 'utf8') : ''\n // A checkout on Windows has CRLF on disk; an edit is written with LF.\n for (const text of [current, current.replace(/\\r\\n/g, '\\n')]) {\n if (!text.includes(from)) continue\n return args.replace_all === true ? text.split(from).join(to) : text.replace(from, () => to)\n }\n return null\n}\n", "/// <reference types=\"node\" />\n/**\n * gates:status \u2014 where the UI Guardrails project stands, read from the code.\n *\n * estiva-gates status one row per ticket\n * estiva-gates status --detail every check under every row\n * estiva-gates status --json machine output, read by estiva-ui's run\n *\n * Every row is decided by checks on real files, a real lint run or a real\n * GitHub setting. Nothing here reads a list that someone ticks by hand.\n *\n * **One copy, in the package** (docs/GATES.md \u00A723). Until UIG-10 this engine was\n * `scripts/gates-status.mjs`, the same file copied by hand into estiva-ui, Peek\n * and Ship, with estiva-ui warning when the copies drifted. Now it ships here,\n * and a repo runs it with `estiva-gates status`. A repo still carrying its own\n * copy is named as one when estiva-ui runs it; UIG-32 moves Peek and Ship off\n * theirs.\n *\n * What differs per repo is `scripts/gates-checks.mjs`, in the repo: its checks,\n * or the checks every app runs (`appChecks`) plus its own.\n *\n * The rows and the reasons for each check are in estiva-ui docs/GATES.md \u00A715\n * and \u00A717.\n */\nimport { execFileSync } from 'node:child_process'\nimport { existsSync, readdirSync, readFileSync, statSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { join, relative, resolve } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport type { ESLint as ESLintClass } from 'eslint'\n\nexport type CheckResult = { result: 'pass' | 'fail' | 'part' | 'unknown'; detail: string }\ntype Maybe<T> = T | Promise<T>\n\nexport interface LintProbe {\n /** The folder ESLint runs from, relative to the repo: `web` in Ship. */\n cwd?: string\n /** The config file, relative to `cwd`. */\n config: string\n /** The path the text is linted as, relative to `cwd`. Nothing is written. */\n file: string\n code: string\n expect: 'error' | 'warning' | 'none'\n /** Words the expected message must contain. */\n mentions?: string\n}\n\n/** The helpers a checks file is written with. */\nexport interface GateHelpers {\n PASS(detail: string): CheckResult\n FAIL(detail: string): CheckResult\n PART(detail: string): CheckResult\n UNKNOWN(detail: string): CheckResult\n exists(rel: string): boolean\n read(rel: string): string\n listFiles(rel: string, test: (name: string, path: string) => boolean): string[]\n file(rel: string): CheckResult\n committed(rel: string): CheckResult\n contains(rel: string, needle: string | RegExp, label?: string): CheckResult\n lacks(rel: string, needle: string | RegExp, label?: string): CheckResult\n script(pkgRel: string, name: string): CheckResult\n loads(rel: string): Promise<CheckResult>\n ci(script: string | RegExp): CheckResult\n ciJob(job: string, script: string): CheckResult\n hook(settingsRel: string, needle: string): CheckResult\n json(rel: string, test?: (data: unknown) => boolean, label?: string): CheckResult\n lint(probe: LintProbe): Promise<CheckResult>\n protectedBranch(pattern: RegExp): CheckResult\n gh(args: string[], label: string): CheckResult\n share(files: string[], test: (file: string) => boolean, label: string): CheckResult\n}\n\nexport interface GateCheck {\n what: string\n run: () => Maybe<CheckResult>\n aggregate?: boolean\n}\nexport interface GateTicket {\n ref: string\n title?: string\n owner: boolean\n checks: GateCheck[]\n}\nexport interface TicketListEntry {\n ref: string\n owner: string\n title: string\n parts?: string[]\n aggregate?: boolean\n}\nexport interface GateSpec {\n repo: string\n tickets: GateTicket[]\n /** estiva-ui only: every ticket, and the repos it joins in. */\n all?: TicketListEntry[]\n siblings?: { name: string; path: string; env: string }[]\n}\n\ninterface Row {\n ref: string\n title?: string\n owner?: boolean\n ownerRepo?: string\n checks: (CheckResult & { repo: string; what: string })[]\n status?: string\n aggregate?: boolean\n}\ninterface Report {\n schema: 1\n repo: string\n branch: string | null\n commit: string | null\n engine: string\n rows: Row[]\n}\n\nconst { version } = createRequire(import.meta.url)('../../package.json') as { version: string }\n/** What a report says ran it. A copy of the old script reports a hash instead. */\nexport const ENGINE = `@estiva-app/ui@${version}`\n\nconst PASS = (detail: string): CheckResult => ({ result: 'pass', detail })\nconst FAIL = (detail: string): CheckResult => ({ result: 'fail', detail })\nconst PART = (detail: string): CheckResult => ({ result: 'part', detail })\nconst UNKNOWN = (detail: string): CheckResult => ({ result: 'unknown', detail })\nconst said = (e: unknown) => {\n const err = e as { stdout?: unknown; stderr?: unknown; message?: string; code?: string }\n return { text: `${err.stdout ?? ''}${err.stderr ?? ''}`, message: String(err.message ?? e), code: err.code }\n}\n\n/** The helpers, bound to one repository. */\nexport function helpers(ROOT: string): GateHelpers {\n const abs = (rel: string) => resolve(ROOT, rel)\n const exists = (rel: string) => existsSync(abs(rel))\n const read = (rel: string) => readFileSync(abs(rel), 'utf8')\n const git = (...a: string[]) => {\n try {\n return execFileSync('git', a, { cwd: ROOT, stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim()\n } catch {\n return null\n }\n }\n const listFiles = (rel: string, test: (name: string, path: string) => boolean) => {\n const out: string[] = []\n const walk = (dir: string) => {\n if (!existsSync(dir)) return\n for (const name of readdirSync(dir)) {\n if (['node_modules', '.git', 'dist', 'storybook-static', '.verify-shots'].includes(name)) continue\n const p = join(dir, name)\n if (statSync(p).isDirectory()) walk(p)\n else if (test(name, p)) out.push(relative(ROOT, p).replace(/\\\\/g, '/'))\n }\n }\n walk(abs(rel))\n return out\n }\n const escape = (s: string) => s.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n\n return {\n PASS, FAIL, PART, UNKNOWN, exists, read, listFiles,\n\n file(rel) {\n return exists(rel) ? PASS(`${rel} exists`) : FAIL(`${rel} does not exist`)\n },\n\n committed(rel) {\n if (!exists(rel)) return FAIL(`${rel} does not exist`)\n return git('ls-files', '--error-unmatch', rel) ? PASS(`${rel} is committed`) : FAIL(`${rel} exists but is not committed`)\n },\n\n contains(rel, needle, label) {\n if (!exists(rel)) return FAIL(`${rel} does not exist`)\n const hit = typeof needle === 'string' ? read(rel).includes(needle) : needle.test(read(rel))\n return hit ? PASS(label ?? `${rel} has ${needle}`) : FAIL(label ? `not yet: ${label}` : `${rel} has no ${needle}`)\n },\n\n lacks(rel, needle, label) {\n if (!exists(rel)) return FAIL(`${rel} does not exist`)\n const hit = typeof needle === 'string' ? read(rel).includes(needle) : needle.test(read(rel))\n return hit ? FAIL(`${rel} still has ${needle}`) : PASS(label ?? `${rel} has no ${needle}`)\n },\n\n script(pkgRel, name) {\n if (!exists(pkgRel)) return FAIL(`${pkgRel} does not exist`)\n const cmd = (JSON.parse(read(pkgRel)) as { scripts?: Record<string, string> }).scripts?.[name]\n return cmd ? PASS(`${pkgRel} runs \"${name}\": ${cmd}`) : FAIL(`${pkgRel} has no \"${name}\" script`)\n },\n\n async loads(rel) {\n if (!exists(rel)) return FAIL(`${rel} does not exist`)\n try {\n const mod = (await import(pathToFileURL(abs(rel)).href)) as { default?: unknown }\n return mod.default ? PASS(`${rel} loads`) : FAIL(`${rel} has no default export`)\n } catch (e) {\n return FAIL(`${rel} does not load: ${said(e).message.split('\\n')[0]}`)\n }\n },\n\n /** A workflow step that runs `npm run <script>`; a regex matches the script name. */\n ci(script) {\n const files = listFiles('.github/workflows', (n) => /\\.ya?ml$/.test(n))\n const name = typeof script === 'string' ? escape(script) : script.source\n const step = new RegExp(`npm run (${name})(?![\\\\w:-])`)\n for (const f of files) {\n const m = read(f).match(step)\n if (m) return PASS(`${f} runs npm run ${m[1]}`)\n }\n return FAIL(`no workflow runs npm run ${script}`)\n },\n\n /**\n * A workflow job, by its id, with a step that runs `npm run <script>`. Branch\n * protection requires a whole job by its name, so a required check needs the\n * job, not the step somewhere (UIG-6). A job is a key two spaces in under\n * `jobs:`, and ends at the next line that is not indented further; a comment\n * line does not count.\n */\n ciJob(job, script) {\n const files = listFiles('.github/workflows', (n) => /\\.ya?ml$/.test(n))\n const header = new RegExp(`^ ${escape(job)}:\\\\s*(#.*)?$`)\n const step = new RegExp(`^(?!\\\\s*#).*npm run ${escape(script)}(?![\\\\w:-])`, 'm')\n for (const f of files) {\n const lines = read(f).split(/\\r?\\n/)\n const from = lines.findIndex((l) => /^jobs:\\s*$/.test(l))\n const start = from === -1 ? -1 : lines.findIndex((l, i) => i > from && header.test(l))\n if (start === -1) continue\n const end = lines.findIndex((l, i) => i > start && /^ {0,2}\\S/.test(l))\n const body = lines.slice(start + 1, end === -1 ? undefined : end).join('\\n')\n return step.test(body) ? PASS(`${f}: the job ${job} runs npm run ${script}`) : FAIL(`${f}: the job ${job} does not run npm run ${script}`)\n }\n return FAIL(`no workflow has a job ${job}`)\n },\n\n hook(settingsRel, needle) {\n if (!exists(settingsRel)) return FAIL(`${settingsRel} does not exist`)\n let s: { hooks?: { PreToolUse?: { hooks?: { command?: string }[] }[] } }\n try {\n s = JSON.parse(read(settingsRel))\n } catch {\n return FAIL(`${settingsRel} is not valid JSON`)\n }\n const commands = (s.hooks?.PreToolUse ?? []).flatMap((m) => (m.hooks ?? []).map((x) => x.command ?? ''))\n const hit = commands.find((c) => c.includes(needle))\n if (hit) return PASS(`${settingsRel} has a PreToolUse hook running ${needle}`)\n return FAIL(commands.length ? `${settingsRel} has PreToolUse hooks, none running ${needle}` : `${settingsRel} has no PreToolUse hook`)\n },\n\n json(rel, test = () => true, label) {\n if (!exists(rel)) return FAIL(`${rel} does not exist`)\n let data: unknown\n try {\n data = JSON.parse(read(rel))\n } catch {\n return FAIL(`${rel} is not valid JSON`)\n }\n if (!git('ls-files', '--error-unmatch', rel)) return FAIL(`${rel} is not committed`)\n return test(data) ? PASS(label ?? `${rel} is committed and parses`) : FAIL(`${rel} parses, but ${label ?? 'fails its test'}`)\n },\n\n /** Lints a line of code that is never written to disk, the way the hook will. */\n async lint({ cwd = '.', config, file, code, expect, mentions }) {\n const cfg = join(cwd, config)\n if (!exists(cfg)) return FAIL(`${cfg} does not exist`)\n let ESLint: typeof ESLintClass\n try {\n const req = createRequire(join(abs(cwd), 'package.json'))\n ;({ ESLint } = (await import(pathToFileURL(req.resolve('eslint')).href)) as { ESLint: typeof ESLintClass })\n } catch (e) {\n return UNKNOWN(`could not load eslint from ${cwd}: ${said(e).message.split('\\n')[0]}`)\n }\n let results: ESLintClass.LintResult[]\n try {\n const eslint = new ESLint({ cwd: abs(cwd), overrideConfigFile: config })\n results = await eslint.lintText(code, { filePath: join(abs(cwd), file), warnIgnored: true })\n } catch (e) {\n return UNKNOWN(`${cfg} could not lint ${file}: ${said(e).message.split('\\n')[0]}`)\n }\n const messages = results.flatMap((r) => r.messages)\n const ignored = messages.some((m) => /ignored/i.test(m.message) && !m.ruleId)\n const fatal = messages.find((m) => m.fatal && !/ignored/i.test(m.message))\n if (fatal) return UNKNOWN(`${file} did not parse: ${fatal.message}`)\n const want = expect === 'error' ? 2 : expect === 'warning' ? 1 : 0\n if (want === 0) {\n const any = messages.filter((m) => m.ruleId && m.severity === 2)\n return any.length ? FAIL(`${file} gets an error it should not: ${any[0].message}`) : PASS(`${file}: no error${ignored ? ' (not linted)' : ''}`)\n }\n if (ignored) return FAIL(`${cfg} does not lint ${file}`)\n const hit = messages.find((m) => m.ruleId && m.severity === want && (!mentions || m.message.includes(mentions)))\n const kind = want === 2 ? 'error' : 'warning'\n return hit ? PASS(`${file} gets ${want === 2 ? 'an' : 'a'} ${kind}: ${hit.message}`) : FAIL(`${file} gets no ${kind}${mentions ? ` naming ${mentions}` : ''}`)\n },\n\n /**\n * A GitHub setting, not a file, so it is asked of GitHub: the checks a merge\n * into the default branch must pass, from the rulesets in force on it\n * (`rules/branches/<branch>`, which anyone who can read the repository may\n * see) and from classic protection as `branches/<branch>` reports it. Not\n * `branches/<branch>/protection`: GitHub answers \"Not Found\" there to anyone\n * who is not an admin, and it knows nothing of rulesets (UIG-6).\n */\n protectedBranch(pattern) {\n const url = git('remote', 'get-url', 'origin')\n const slug = url?.match(/github\\.com[:/](.+?)(?:\\.git)?$/)?.[1]\n if (!slug) return UNKNOWN('no GitHub remote')\n const branch = (git('symbolic-ref', '--short', 'refs/remotes/origin/HEAD') ?? 'origin/main').replace(/^origin\\//, '')\n const ask = (path: string) => JSON.parse(execFileSync('gh', ['api', path], { stdio: ['ignore', 'pipe', 'pipe'], timeout: 20000 }).toString())\n let rules: { type: string; parameters?: { required_status_checks?: { context: string }[] } }[]\n let classic: { contexts?: string[]; checks?: { context: string }[] } | undefined\n try {\n rules = ask(`repos/${slug}/rules/branches/${branch}`)\n classic = ask(`repos/${slug}/branches/${branch}`).protection?.required_status_checks\n } catch (e) {\n const s = said(e)\n return UNKNOWN(`could not ask GitHub (${s.code === 'ENOENT' ? 'gh is not installed' : s.text.split('\\n')[0] || s.message})`)\n }\n const names = [\n ...rules.filter((r) => r.type === 'required_status_checks').flatMap((r) => r.parameters?.required_status_checks ?? []).map((c) => c.context),\n ...(classic?.contexts ?? []),\n ...(classic?.checks ?? []).map((c) => c.context),\n ]\n const hit = names.find((n) => pattern.test(n))\n if (hit) return PASS(`${slug} ${branch} requires \"${hit}\"`)\n return FAIL(names.length ? `${slug} ${branch} requires ${names.map((n) => `\"${n}\"`).join(', ')}, none matching ${pattern}` : `${slug} ${branch} requires no check to merge`)\n },\n\n gh(args, label) {\n try {\n execFileSync('gh', args, { stdio: ['ignore', 'pipe', 'pipe'], timeout: 20000 })\n return PASS(label)\n } catch (e) {\n const s = said(e)\n if (/could not resolve|not found|HTTP 404/i.test(s.text)) return FAIL(`not yet: ${label}`)\n return UNKNOWN(`could not ask GitHub (${s.code === 'ENOENT' ? 'gh is not installed' : s.text.split('\\n')[0] || s.message})`)\n }\n },\n\n /** How many of `files` pass `test`: all is a pass, some is a part. */\n share(files, test, label) {\n if (!files.length) return FAIL(`no files to check for: ${label}`)\n const ok = files.filter(test).length\n const detail = `${ok} of ${files.length}: ${label}`\n return ok === files.length ? PASS(detail) : ok ? PART(detail) : FAIL(detail)\n },\n }\n}\n\n// \u2500\u2500 running the checks \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst STATUS: Record<string, string> = { done: '\u2705', started: '\uD83D\uDEA7', none: '\u2B1C', unknown: '\u2754' }\n\nfunction statusOf(checks: CheckResult[]) {\n const n = (r: CheckResult['result']) => checks.filter((c) => c.result === r).length\n const pass = n('pass'), part = n('part'), fail = n('fail'), unknown = n('unknown')\n if (!checks.length) return 'unknown'\n if (pass === checks.length) return 'done'\n if (pass + part > 0) return fail === 0 && unknown > 0 ? 'unknown' : 'started'\n return fail > 0 ? 'none' : 'unknown'\n}\n\nasync function runRepo(ROOT: string, h: GateHelpers): Promise<{ spec: GateSpec; report: Report }> {\n const checksFile = join(ROOT, 'scripts', 'gates-checks.mjs')\n const define = ((await import(pathToFileURL(checksFile).href)) as { default: (h: GateHelpers) => GateSpec }).default\n const spec = define(h)\n const git = (...a: string[]) => {\n try {\n return execFileSync('git', a, { cwd: ROOT, stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim()\n } catch {\n return null\n }\n }\n const rows: Row[] = []\n for (const t of spec.tickets) {\n const checks: Row['checks'] = []\n for (const c of t.checks ?? []) {\n if (c.aggregate) continue\n let r: CheckResult\n try {\n r = await c.run()\n } catch (e) {\n r = UNKNOWN(`the check threw: ${said(e).message}`)\n }\n checks.push({ repo: spec.repo, what: c.what, ...r })\n }\n rows.push({ ref: t.ref, title: t.title, owner: t.owner, checks })\n }\n return { spec, report: { schema: 1, repo: spec.repo, branch: git('rev-parse', '--abbrev-ref', 'HEAD'), commit: git('rev-parse', '--short', 'HEAD'), engine: ENGINE, rows } }\n}\n\n/**\n * Another repository's report. A repo on the package's engine runs it from its\n * own install; a repo still carrying `scripts/gates-status.mjs` runs that copy.\n */\nfunction runSibling(dir: string, shown: string): { report?: Report; error?: string } {\n const copy = join(dir, 'scripts', 'gates-status.mjs')\n const installed = createRequire(join(dir, 'package.json'))\n let script = copy\n const args = ['--json']\n if (!existsSync(copy)) {\n try {\n script = join(installed.resolve('@estiva-app/ui/package.json'), '..', 'dist', 'gates', 'cli.js')\n args.unshift('status')\n } catch {\n return { error: `${shown} has neither its own gates-status.mjs nor @estiva-app/ui installed` }\n }\n if (!existsSync(script)) return { error: `${shown} has an @estiva-app/ui with no gates engine: install 0.21.0 or later` }\n }\n try {\n const out = execFileSync(process.execPath, [script, ...args], { cwd: dir, stdio: ['ignore', 'pipe', 'pipe'], timeout: 300000, maxBuffer: 16 * 1024 * 1024 })\n return { report: JSON.parse(out.toString()) as Report }\n } catch (e) {\n const err = e as { stderr?: unknown; message?: string }\n return { error: `its gates:status failed: ${String(err.stderr ?? err.message).split('\\n').filter(Boolean).slice(-1)[0]}` }\n }\n}\n\nfunction pad(s: string, n: number) {\n const len = [...s].length\n return len >= n ? s : s + ' '.repeat(n - len)\n}\n\nfunction printRows(rows: Row[], { showOwner, detail }: { showOwner: boolean; detail: boolean }) {\n const lines: string[] = []\n const width = Math.max(...rows.map((r) => [...(r.title ?? '')].length))\n for (const r of rows) {\n const pass = r.checks.filter((c) => c.result === 'pass').length\n const count = r.checks.length ? `${pass} of ${r.checks.length}` : 'no checks here'\n lines.push(`${STATUS[r.status ?? 'unknown']} ${pad(r.ref, 7)} ${pad(r.title ?? '', width)} ${showOwner ? pad(r.ownerRepo ?? '', 10) : ''}${count}`)\n if (detail || r.status === 'started' || r.status === 'unknown') {\n for (const c of r.checks) {\n const mark = { pass: '\u2713', part: '\u00BD', fail: '\u2717', unknown: '?' }[c.result]\n lines.push(` ${mark} ${showOwner ? `${pad(c.repo, 10)} ` : ''}${c.what} \u2014 ${c.detail}`)\n }\n }\n }\n return lines.join('\\n')\n}\n\nfunction summary(rows: Row[]) {\n const n = (s: string) => rows.filter((r) => r.status === s).length\n return `${STATUS.done} ${n('done')} done \u00B7 ${STATUS.started} ${n('started')} started \u00B7 ${STATUS.none} ${n('none')} not started \u00B7 ${STATUS.unknown} ${n('unknown')} could not check`\n}\n\nexport interface StatusOptions {\n /** The repository to report on. */\n root?: string\n json?: boolean\n detail?: boolean\n}\n\n/** Print where the project stands. Returns what was printed, or the JSON report. */\nexport async function runStatus({ root = process.cwd(), json = false, detail = false }: StatusOptions = {}): Promise<string> {\n const ROOT = resolve(root)\n const h = helpers(ROOT)\n const shown = (dir: string) => (relative(ROOT, dir) || dir).replace(/\\\\/g, '/')\n const { spec, report } = await runRepo(ROOT, h)\n\n if (json) return JSON.stringify(report)\n\n const out: string[] = []\n out.push('UI Guardrails \u00B7 gates:status', 'Read from the code. Nothing here is a hand-ticked list.', '')\n out.push(`${pad(spec.repo, 10)} ${report.branch} @ ${report.commit}`)\n\n if (!spec.all) {\n // A sibling repo on its own: its own rows, then its parts of rows owned elsewhere.\n const own = report.rows.filter((r) => r.owner).map((r) => ({ ...r, ownerRepo: spec.repo, status: statusOf(r.checks) }))\n const parts = report.rows.filter((r) => !r.owner).map((r) => ({ ...r, ownerRepo: '', status: statusOf(r.checks) }))\n out.push('', `Tickets ${spec.repo} owns (${own.length}):`, printRows(own, { showOwner: false, detail }))\n out.push('', `Parts of tickets another repo owns, checked here (${parts.length}):`, printRows(parts, { showOwner: false, detail }))\n out.push('', 'For every ticket across the repos, run npm run gates:status in estiva-ui.')\n return out.join('\\n')\n }\n\n // estiva-ui: gather the sibling checkouts and join everything into one row per ticket.\n const reports: Report[] = [report]\n const found: { name: string; note: string; missing?: boolean }[] = []\n for (const s of spec.siblings ?? []) {\n const dir = resolve(ROOT, process.env[s.env] ?? s.path)\n if (!existsSync(join(dir, 'package.json'))) {\n found.push({ name: s.name, note: `not found at ${shown(dir)} (set ${s.env} to point at it)`, missing: true })\n continue\n }\n const r = runSibling(dir, shown(dir))\n if (!r.report) {\n found.push({ name: s.name, note: r.error ?? 'no report', missing: true })\n continue\n }\n const copy = r.report.engine !== ENGINE ? (r.report.engine.startsWith('@estiva-app/ui@') ? ` \u00B7 on ${r.report.engine}'s engine` : ' \u00B7 runs its own copy of the status engine; UIG-32 moves it onto the package') : ''\n found.push({ name: s.name, note: `${r.report.branch} @ ${r.report.commit}, found at ${shown(dir)}${copy}` })\n reports.push(r.report)\n }\n for (const f of found) out.push(`${pad(f.name, 10)} ${f.note}`)\n\n const problems: string[] = []\n const known = new Set(spec.all.map((t) => t.ref))\n for (const rep of reports) {\n for (const r of rep.rows.filter((r) => !known.has(r.ref))) problems.push(`${rep.repo} reports ${r.ref}, which is not one of the tickets`)\n }\n const rows: Row[] = spec.all.map((t) => {\n const pieces = reports.flatMap((rep) => rep.rows.filter((r) => r.ref === t.ref).map((r) => ({ rep, r })))\n const claims = pieces.filter((p) => p.r.owner).map((p) => p.rep.repo)\n const ownerMissing = found.some((f) => f.name === t.owner && f.missing)\n if (!ownerMissing && (claims.length !== 1 || claims[0] !== t.owner)) {\n problems.push(`${t.ref}: should be owned by ${t.owner}, is claimed by ${claims.join(' and ') || 'no repo'}`)\n }\n for (const p of pieces.filter((p) => !p.r.owner && !(t.parts ?? []).includes(p.rep.repo))) {\n problems.push(`${t.ref}: ${p.rep.repo} checks a part of it, but the ticket list does not name ${p.rep.repo}`)\n }\n const checks = pieces.flatMap((p) => p.r.checks)\n for (const f of found.filter((f) => f.missing)) {\n if ((t.parts ?? []).includes(f.name) || t.owner === f.name) checks.push({ repo: f.name, what: `${f.name}'s part`, result: 'unknown', detail: f.note })\n }\n return { ref: t.ref, title: t.title, ownerRepo: t.owner, checks, aggregate: t.aggregate }\n })\n for (const row of rows) row.status = statusOf(row.checks)\n for (const row of rows.filter((r) => r.aggregate)) {\n const others = rows.filter((r) => r !== row)\n const done = others.filter((r) => r.status === 'done').length\n row.checks.push({ repo: 'all', what: 'every other ticket is done', ...(done === others.length ? PASS(`${done} of ${others.length} done`) : FAIL(`${done} of ${others.length} done`)) })\n row.status = statusOf(row.checks)\n }\n\n out.push('', printRows(rows, { showOwner: true, detail }), '', summary(rows))\n const owners = Object.entries(rows.reduce<Record<string, number>>((a, r) => ({ ...a, [r.ownerRepo ?? '']: (a[r.ownerRepo ?? ''] ?? 0) + 1 }), {}))\n .map(([k, v]) => `${k} ${v}`)\n .join(', ')\n out.push(`${rows.length} tickets. Owned by ${owners}.`)\n const unchecked = found.filter((f) => f.missing && spec.all!.some((t) => t.owner === f.name)).map((f) => f.name)\n if (problems.length) out.push(`\u26A0\uFE0F Ownership does not add up:\\n ${problems.join('\\n ')}`)\n else if (unchecked.length) out.push(`\u2754 Ownership not fully checked: ${unchecked.join(' and ')} could not be read.`)\n else out.push('\u2705 Each ticket is owned by exactly one repo, and every repo agrees.')\n if (!detail) out.push('Add -- --detail to see every check.')\n return out.join('\\n')\n}\n"],
5
- "mappings": "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",
6
- "names": ["existsSync", "readFileSync", "createRequire", "join", "relative", "pathToFileURL", "countGates", "existsSync", "readFileSync", "createRequire", "join", "relative", "resolve", "pathToFileURL", "PASS", "r", "p", "f"]
7
- }