@estiva-app/ui 0.43.0 → 0.44.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.
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/gates/token-lint.ts", "../../src/gates/token-joined.ts", "../../src/gates/token-style.ts", "../../src/gates/ci.ts", "../../src/gates/count.ts", "../../src/gates/hook.ts", "../../src/gates/name-escapes.ts", "../../src/gates/escapes.ts", "../../src/eslint/escape.ts", "../../src/gates/status.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 * And `tokenConfig`, the two on their own as an app's `lint:tokens` runs them\n * (UIG-37), with TypeScript's rule names known so its directives pass.\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 * A class list joined by hand, `[\u2026].join(' ')`, is read by `token-joined`\n * (`./token-joined.ts`, UIG-42) with the same patterns.\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 { ESLint, Linter } from 'eslint'\nimport betterTailwindcss from 'eslint-plugin-better-tailwindcss'\nimport { getDefaultSelectors } from 'eslint-plugin-better-tailwindcss/defaults'\nimport { parser as typescriptParser, plugin as typescriptPlugin } from 'typescript-eslint'\nimport { type Restriction, tokenJoinedPlugin } from './token-joined'\nimport { INLINE_STYLE_MESSAGE, INLINE_STYLE_TOKEN_KEYS, tokenStylePlugin } from './token-style'\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\nexport interface TokenConfigOptions extends TokenLintOptions {\n /**\n * Plugins whose `eslint-disable` directives appear in the source, registered\n * with every rule off. ESLint refuses a directive for a rule it does not\n * know. TypeScript's are registered already.\n */\n quiet?: Record<string, ESLint.Plugin>\n /** Folders the token lint never reads, beside build output. */\n ignores?: string[]\n}\n\n/** What the token lint never reads: build output. */\nexport const TOKEN_LINT_IGNORES = ['dist/', 'storybook-static/', 'node_modules/']\n\n/**\n * Plugins a check that runs on its own registers, every rule off, so the\n * directives another lint writes do not fail it (UIG-37). TypeScript's lint is\n * in every app and writes `eslint-disable-next-line @typescript-eslint/...`;\n * Peek PR #324 failed the token lint on one. Only the checks that run alone\n * take this \u2014 a full lint that loads TypeScript's rules from its own copy would\n * refuse a second plugin under the same name.\n */\nexport const KNOWN_PLUGINS: Record<string, ESLint.Plugin> = { '@typescript-eslint': typescriptPlugin as ESLint.Plugin }\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 merge: \"Merge classes with the package's cn(), which the app re-exports. A plain tailwind-merge does not know the type ramp, and drops text-body-2 beside a text colour.\",\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 merge: \"Merge classes with this package's cn() (src/cn.ts), the one place tailwind-merge is taught the type ramp. A plain tailwind-merge drops text-body-2 beside a text colour.\",\n },\n} satisfies Record<TokenAudience, Record<string, string>>\n\n/**\n * The variants in front of a class, arbitrary ones too (`[&_pre]:`,\n * `data-[state=open]:`, `group-hover/row:`), and the `!` that makes a class\n * important. Until UIG-42 the four class rules read only plain variants, so\n * `[&_pre]:text-sm` and `group-hover/row:bg-gray-100` passed.\n */\nconst V = '^(?:(?:[^:\\\\[\\\\]\\\\s]|\\\\[[^\\\\]]*\\\\])+:)*!?'\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 const restrict: Restriction[] = [\n { pattern: `${V}text-(?:xs|sm|base|lg|xl|[2-9]xl)$`, message: words.ramp },\n {\n pattern: `${V}(?: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: `${V}[a-z-]+-\\\\[[^\\\\]]*(?:#[0-9a-fA-F]{3}|rgba?\\\\(|hsla?\\\\(|oklch\\\\()`, message: words.raw },\n {\n pattern: `${V}(?: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 return {\n files: ['**/*.{ts,tsx}'],\n languageOptions: {\n parser: typescriptParser,\n parserOptions: { ecmaFeatures: { jsx: true } },\n },\n plugins: { 'better-tailwindcss': betterTailwindcss, 'token-joined': tokenJoinedPlugin },\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': ['error', { restrict }],\n // A class list joined by hand, which the plugin above cannot read (UIG-42).\n 'token-joined/classes': ['error', { restrict }],\n // UIG-21: \"use the package's cn(), never a plain twMerge\" was a sentence\n // in all three repos' CLAUDE.md, and nothing checked it.\n 'no-restricted-imports': ['error', { paths: [{ name: 'tailwind-merge', message: words.merge }] }],\n },\n }\n}\n\n// UIG-28's patterns. A colour inside `text-[\u2026]` is left to the raw-colour rule\n// of `tokenLint`.\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 = String(INLINE_STYLE_TOKEN_KEYS)\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 const values: Restriction[] = [\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 const spacing: Restriction[] = [\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 return {\n files: ['**/*.{ts,tsx}'],\n ignores: ['**/*.test.ts', '**/*.test.tsx'],\n plugins: { 'token-values': betterTailwindcss, 'token-spacing': betterTailwindcss, 'token-style': tokenStylePlugin, 'token-joined': tokenJoinedPlugin },\n rules: {\n // A style kept in a const and passed by name (R9): the selector below cannot follow it.\n 'token-style/no-token-style': 'error',\n 'token-values/no-restricted-classes': ['error', { restrict: values }],\n 'token-spacing/no-restricted-classes': ['warn', { restrict: spacing }],\n // The same two inside a class list joined by hand (UIG-42).\n 'token-joined/values': ['error', { restrict: values }],\n 'token-joined/spacing': ['warn', { restrict: spacing }],\n 'no-restricted-syntax': [\n 'error',\n {\n selector: `JSXAttribute[name.name=\"style\"] > JSXExpressionContainer > ObjectExpression > Property[key.name=${INLINE_STYLE_TOKEN_PROPERTIES}]`,\n message: INLINE_STYLE_MESSAGE,\n },\n // The same key in quotes, `style={{ 'background': 'red' }}` (R9).\n {\n selector: `JSXAttribute[name.name=\"style\"] > JSXExpressionContainer > ObjectExpression > Property[key.value=${INLINE_STYLE_TOKEN_PROPERTIES}]`,\n message: INLINE_STYLE_MESSAGE,\n },\n ],\n },\n }\n}\n\n/**\n * The token lint on its own \u2014 an app's `eslint.tokens.config.js`, `npm run\n * lint:tokens`, the CI step. It fails on the token contract and on nothing\n * else, so a backlog in the full lint never blocks it. Peek built this file by\n * hand until UIG-37.\n */\nexport function tokenConfig({ quiet = {}, ignores = [], ...options }: TokenConfigOptions = {}): Linter.Config[] {\n return [\n { ignores: [...TOKEN_LINT_IGNORES, ...ignores] },\n {\n plugins: { ...KNOWN_PLUGINS, ...quiet },\n // Every other lint'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 tokenLint(options),\n tokenValues({ audience: options.audience }),\n ]\n}\n", "/**\n * The token contract, read inside a class list joined by hand (UIG-42):\n *\n * <div className={['text-sm', open && 'bg-gray-100'].join(' ')} />\n *\n * The Tailwind plugin stops at a function call, so it never reads the list in\n * front of `.join(' ')`, and no setting can make it: a call it is told about is\n * read at its arguments, which here is the `' '`. This rule reads the list and\n * tests each class against the same patterns, with the same messages, as the\n * rule it stands in for. It registers once per rule it mirrors, so an error\n * stays an error and a warning a warning (`token-joined/classes`, `/values`,\n * `/spacing`).\n *\n * A list joined with anything but one space is not a class list, and a\n * sentence built this way passes unless one of its words is itself such a\n * class, as in `'set text-sm here'`. Only strings written in the list are\n * read, directly or behind `cond && \u2026`, `a ? \u2026 : \u2026` and a template's fixed\n * parts; a list worked out while it runs cannot be read.\n */\nimport type { ESLint, Rule } from 'eslint'\n\n/** One pattern and its message, the shape `no-restricted-classes` takes. `$0` is the class. */\nexport interface Restriction {\n pattern: string\n message: string\n}\n\ninterface Node {\n type: string\n [key: string]: unknown\n}\n\nconst isOneSpace = (node: Node | undefined) => node?.type === 'Literal' && node.value === ' '\n\n/** `.join` on a list, through any `.filter(\u2026)` between them. */\nfunction joinedList(call: Node): Node | undefined {\n const callee = call.callee as Node\n if (callee.type !== 'MemberExpression' || callee.computed || (callee.property as Node).name !== 'join') return undefined\n if ((call.arguments as Node[]).length !== 1 || !isOneSpace((call.arguments as Node[])[0])) return undefined\n let list = callee.object as Node\n while (list.type === 'CallExpression' && (list.callee as Node).type === 'MemberExpression' && ((list.callee as Node).property as Node).name === 'filter') {\n list = (list.callee as Node).object as Node\n }\n return list.type === 'ArrayExpression' ? list : undefined\n}\n\n/** The strings written in one item of the list, each with the node an escape sits above. */\nfunction stringsIn(node: Node | null, out: { node: Node; text: string }[] = []) {\n if (!node) return out\n if (node.type === 'Literal' && typeof node.value === 'string') out.push({ node, text: node.value })\n else if (node.type === 'TemplateLiteral') for (const quasi of node.quasis as Node[]) out.push({ node: quasi, text: String((quasi.value as { cooked: string }).cooked ?? '') })\n else if (node.type === 'ConditionalExpression') stringsIn(node.consequent as Node, out), stringsIn(node.alternate as Node, out)\n else if (node.type === 'LogicalExpression') {\n if (node.operator !== '&&') stringsIn(node.left as Node, out)\n stringsIn(node.right as Node, out)\n } else if (node.type === 'TSAsExpression' || node.type === 'TSSatisfiesExpression') stringsIn(node.expression as Node, out)\n return out\n}\n\nconst joinedListRule: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: { description: 'The token contract, inside a class list joined with .join(\" \")' },\n schema: [\n {\n type: 'object',\n properties: { restrict: { type: 'array', items: { type: 'object', properties: { pattern: { type: 'string' }, message: { type: 'string' } }, required: ['pattern', 'message'] } } },\n required: ['restrict'],\n additionalProperties: false,\n },\n ],\n },\n create(context) {\n const { restrict } = context.options[0] as { restrict: Restriction[] }\n const rules = restrict.map(({ pattern, message }) => ({ pattern: new RegExp(pattern), message }))\n return {\n CallExpression(ruleNode: Rule.Node) {\n const list = joinedList(ruleNode as unknown as Node)\n if (!list) return\n for (const item of list.elements as (Node | null)[]) {\n for (const { node, text } of stringsIn(item)) {\n for (const name of text.split(/\\s+/)) {\n const rule = name && rules.find(({ pattern }) => pattern.test(name))\n if (rule) context.report({ node: node as unknown as Rule.Node, message: rule.message.replaceAll('$0', name) })\n }\n }\n }\n },\n }\n },\n}\n\n/** The plugin, as the token lint registers it: one rule, under three names. */\nexport const tokenJoinedPlugin: ESLint.Plugin = { rules: { classes: joinedListRule, values: joinedListRule, spacing: joinedListRule } }\n", "/// <reference types=\"node\" />\n/**\n * The token contract's inline-style check, where a selector cannot reach\n * (the second review's R9, 25 September). `no-restricted-syntax` reads a\n * `style={{ \u2026 }}` written in place; a style kept in a `const` and passed by name\n * got through every lint in all three repos:\n *\n * const look = { background: 'red' }\n * <div style={look} />\n *\n * This follows the name to its `const` in the same file and reads the keys\n * there, quoted or not. It reports on the key, where the value is written, so\n * an escape sits beside it. A style the code works out while it runs passes:\n * nothing can read it.\n */\nimport type { ESLint, Rule, Scope } from 'eslint'\n\n/** The style keys outside the token contract: colours, a font size, borders and shadows. */\nexport const INLINE_STYLE_TOKEN_KEYS = /^(color|background|backgroundColor|backgroundImage|fill|stroke|fontSize|border|borderTop|borderRight|borderBottom|borderLeft|borderBlock|borderInline|borderStyle|boxShadow|textShadow)$|Color$/\n\nexport const INLINE_STYLE_MESSAGE =\n '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\ninterface Node {\n type: string\n [key: string]: unknown\n}\n\nconst keyOf = (property: Node): string | undefined => {\n if (property.computed) return undefined\n const key = property.key as Node\n return key.type === 'Identifier' ? (key.name as string) : key.type === 'Literal' && typeof key.value === 'string' ? key.value : undefined\n}\n\n/** The object a `const` of this name holds, in this scope or one around it. */\nfunction constObject(name: string, scope: Scope.Scope | null): Node | undefined {\n for (let current = scope; current; current = current.upper) {\n const variable = current.set.get(name)\n if (!variable) continue\n const definition = variable.defs[0] as unknown as { type: string; node: Node; parent?: Node } | undefined\n if (variable.defs.length !== 1 || definition?.type !== 'Variable' || definition.parent?.kind !== 'const') return undefined\n let init = definition.node.init as Node | undefined\n while (init && (init.type === 'TSAsExpression' || init.type === 'TSSatisfiesExpression')) init = init.expression as Node\n return init?.type === 'ObjectExpression' ? init : undefined\n }\n return undefined\n}\n\nconst noTokenStyle: Rule.RuleModule = {\n meta: {\n type: 'problem',\n docs: { description: 'No colour, font size, border or shadow in a style kept in a const' },\n schema: [],\n messages: { token: INLINE_STYLE_MESSAGE },\n },\n create(context) {\n const reported = new Set<unknown>()\n return {\n JSXAttribute(ruleNode: Rule.Node) {\n const attribute = ruleNode as unknown as Node\n if ((attribute.name as Node).name !== 'style') return\n const value = attribute.value as Node | null\n const expression = value?.type === 'JSXExpressionContainer' ? (value.expression as Node) : undefined\n if (expression?.type !== 'Identifier') return\n const object = constObject(expression.name as string, context.sourceCode.getScope(ruleNode))\n for (const property of (object?.properties as Node[] | undefined) ?? []) {\n if (property.type !== 'Property' || reported.has(property)) continue\n const key = keyOf(property)\n if (!key || !INLINE_STYLE_TOKEN_KEYS.test(key)) continue\n reported.add(property)\n context.report({ node: property as unknown as Rule.Node, messageId: 'token' })\n }\n },\n }\n },\n}\n\n/** The plugin, as the token lint registers it: `token-style/no-token-style`. */\nexport const tokenStylePlugin: ESLint.Plugin = { rules: { 'no-token-style': noTokenStyle } }\n", "/// <reference types=\"node\" />\n/**\n * `estiva-gates ci` \u2014 CI's job `gate`, as one command (Katerina's ruling R13,\n * 25 September). Until 0.36.0 each repo listed the gate's steps in its own\n * workflow, and the lists had drifted: Peek ran `lint:tokens`, Ship the whole\n * `lint`, and no repo checked the two things below. Now the steps live here,\n * and a repo's `gate` job runs this and nothing else of the gate's.\n *\n * 1. The gate lint (`npm run lint:rules`), which writes `.gates-count.json`.\n * 2. The count is committed (B2): a pull request that changes what the gate\n * finds and leaves the old count behind fails, so the file is the truth.\n * 3. The token contract (`npm run lint:tokens`, or `npm run lint` where a repo\n * has no token lint of its own).\n * 4. The catalogue (`npm run registry:check`).\n * 5. The wall's own settings (B13): the committed `.claude/settings.json` runs\n * the editor hook before every Write and Edit, and that hook refuses a raw\n * button; every folder the gate skips is named on the debt page, with why.\n *\n * Every step runs, so one red step does not hide the others.\n */\nimport { spawnSync } from 'node:child_process'\nimport { existsSync, readFileSync, statSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { pathToFileURL } from 'node:url'\nimport { TOKEN_LINT_IGNORES } from './token-lint'\n\nexport interface CiOptions {\n /** The repository's top folder, where the gate lints from. */\n root?: string\n /** `package` is this package's own gate: its skipped folders are named in docs/GATES.md. */\n audience?: 'app' | 'package'\n /** Print as the steps run. Defaults to stdout. */\n print?: (line: string) => void\n}\n\n/** The part a CI run writes to the probe: a raw button, which every gate refuses. */\nconst PROBE = '/** A probe CI writes to check the editor hook; never on disk. */\\nexport function GateProbe() {\\n return <button type=\"button\">x</button>\\n}\\n'\n\nconst npm = (root: string, script: string) => spawnSync('npm', ['run', script], { cwd: root, stdio: 'inherit', shell: true }).status === 0\n\n/** `.gates-count.json` as the gate lint left it matches the committed file; the lines that say otherwise. */\nexport function countCommitted(root: string): string[] {\n const run = spawnSync('git', ['status', '--porcelain', '--', '.gates-count.json'], { cwd: root, encoding: 'utf8' })\n if (run.status !== 0) return [`git could not read .gates-count.json's state: ${run.stderr.trim()}`]\n if (!run.stdout.trim()) return []\n const diff = spawnSync('git', ['diff', '--', '.gates-count.json'], { cwd: root, encoding: 'utf8' }).stdout.trim()\n return [\n 'The gate found something other than the committed count: run `npm run lint:rules` and commit `.gates-count.json` with the change that moved it.',\n ...(diff ? diff.split('\\n').filter((l) => /^[+-]\\s/.test(l)).map((l) => ` ${l}`) : [' .gates-count.json is not committed at all']),\n ]\n}\n\ninterface HookEntry {\n matcher?: string\n hooks?: { type?: string; command?: string }[]\n}\n\n/** The committed hook runs before every Write and Edit and refuses a raw button; the lines that say otherwise. */\nexport function hookRuns(root: string): string[] {\n const file = join(root, '.claude', 'settings.json')\n if (!existsSync(file)) return ['.claude/settings.json is missing: the editor hook is not switched on (docs/GATES.md \u00A723).']\n let entries: HookEntry[]\n try {\n entries = ((JSON.parse(readFileSync(file, 'utf8')) as { hooks?: { PreToolUse?: HookEntry[] } }).hooks?.PreToolUse ?? [])\n } catch (error) {\n return [`.claude/settings.json does not read as JSON: ${error instanceof Error ? error.message : String(error)}`]\n }\n const covers = (matcher: string | undefined, tool: string) => {\n if (!matcher || matcher === '*') return true\n try {\n return new RegExp(`^(?:${matcher})$`).test(tool)\n } catch {\n return false\n }\n }\n const input = JSON.stringify({ tool_name: 'Write', session_id: 'estiva-gates-ci', cwd: root, tool_input: { file_path: join(root, 'src', 'GateProbe.tsx'), content: PROBE } })\n const tried: string[] = []\n for (const entry of entries) {\n if (!covers(entry.matcher, 'Write') || !covers(entry.matcher, 'Edit')) continue\n for (const hook of entry.hooks ?? []) {\n if (hook.type !== 'command' || !hook.command) continue\n // The project folder, written in: a Windows shell does not expand `$VAR`.\n const command = hook.command.replace(/\\$\\{?CLAUDE_PROJECT_DIR\\}?/g, root.split('\\\\').join('/'))\n const run = spawnSync(command, { cwd: root, input, encoding: 'utf8', shell: true, env: { ...process.env, CLAUDE_PROJECT_DIR: root }, timeout: 120_000 })\n if (run.status === 2 && /UI Guardrails/.test(run.stderr)) return []\n tried.push(` ${hook.command} \u2192 exit ${run.status ?? 'none'}${run.stderr.trim() ? `: ${run.stderr.trim().split('\\n')[0]}` : ''}`)\n }\n }\n return [\n tried.length\n ? 'The editor hook let a raw button through: no PreToolUse hook in .claude/settings.json refused it.'\n : 'No PreToolUse hook in .claude/settings.json runs before both Write and Edit: the editor gate is off.',\n ...tried,\n ]\n}\n\n/** A glob as the words a page would name it by: `demo-scenarios/**` \u2192 `demo-scenarios`. */\nconst plain = (glob: string) => glob.replace(/\\/\\*\\*.*$|\\/$/, '')\n\n/** The folders a gate config skips beyond the package's own three, from the config itself. */\nexport async function skippedFolders(root: string): Promise<string[]> {\n const found = new Set<string>()\n for (const name of ['eslint.gates.config.js', 'eslint.tokens.config.js']) {\n const file = join(root, name)\n if (!existsSync(file)) continue\n // The file as it is now: Node keeps a module it has imported once, by its URL.\n const loaded = (await import(`${pathToFileURL(file).href}?at=${statSync(file).mtimeMs}`)) as { default?: unknown }\n const configs = [loaded.default].flat(Infinity) as { ignores?: string[]; [key: string]: unknown }[]\n for (const c of configs) {\n // Only a config with nothing but `ignores` skips a folder for every rule.\n if (!c || typeof c !== 'object' || !Array.isArray(c.ignores) || Object.keys(c).some((k) => k !== 'ignores' && k !== 'name')) continue\n for (const glob of c.ignores) if (!TOKEN_LINT_IGNORES.includes(glob)) found.add(glob)\n }\n }\n return [...found]\n}\n\n/** Every skipped folder is named on the debt page; the lines that say otherwise. */\nexport async function skipsListed(root: string, audience: 'app' | 'package'): Promise<string[]> {\n const page = audience === 'package' ? 'docs/GATES.md' : 'docs/GATES-DEBT.md'\n const skipped = await skippedFolders(root)\n if (!skipped.length) return []\n const text = existsSync(join(root, page)) ? readFileSync(join(root, page), 'utf8') : ''\n const missing = skipped.filter((glob) => !text.includes(plain(glob)))\n if (!missing.length) return []\n return [`The gate skips folders ${page} does not name. Name each there, with why it is not checked (B11):`, ...missing.map((g) => ` ${g}`)]\n}\n\nexport async function runCi({ root = process.cwd(), audience = 'app', print = (line) => process.stdout.write(`${line}\\n`) }: CiOptions = {}): Promise<number> {\n const scripts = (JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')) as { scripts?: Record<string, string> }).scripts ?? {}\n const tokens = scripts['lint:tokens'] ? 'lint:tokens' : 'lint'\n const failed: string[] = []\n const step = async (title: string, run: () => boolean | string[] | Promise<string[]>) => {\n print(`\\n\u25B8 ${title}`)\n const result = await run()\n const problems = result === true ? [] : result === false ? [`${title}: failed (above)`] : result\n if (problems.length) {\n failed.push(title)\n for (const line of problems) print(process.env.GITHUB_ACTIONS && !line.startsWith(' ') ? `::error::${line}` : line)\n } else print(' \u2713')\n }\n\n await step('The gate lint (npm run lint:rules)', () => npm(root, 'lint:rules'))\n await step('The count is committed (.gates-count.json)', () => countCommitted(root))\n await step(`The token contract (npm run ${tokens})`, () => npm(root, tokens))\n await step('The catalogue (npm run registry:check)', () => npm(root, 'registry:check'))\n await step('The editor hook refuses a raw button (.claude/settings.json)', () => hookRuns(root))\n await step('Every folder the gate skips is named on the debt page', () => skipsListed(root, audience))\n\n print(failed.length ? `\\nThe gate failed: ${failed.join('; ')}.` : '\\nThe gate passed.')\n return failed.length ? 1 : 0\n}\n", "/// <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, naming the rule: // @estiva-escape(<rule>): <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. Since 0.36.0 it runs the\n * token contract too, keeping only that contract's errors (B4), and a copied\n * look, which only warns, reaches Claude as a note on a write let through (R10). 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 *\n * **Search first** (UIG-20). A Write that would create a new file drawing a new\n * part, and that the gate lets through, is stopped once: the hook runs\n * `estiva-ui find` on the part's name and hands the matches back, so a session\n * sees what already exists before it adds another. The same file written again\n * in the same session goes through. The skill asks for the search; this makes\n * sure it happened. A search that cannot run stops nothing.\n */\nimport { spawnSync } from 'node:child_process'\nimport { createHash } from 'node:crypto'\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { tmpdir } from 'node:os'\nimport { basename, isAbsolute, join, relative, resolve, sep } from 'node:path'\nimport { fileURLToPath, pathToFileURL } from 'node:url'\nimport type { ESLint as ESLintClass, Linter } from 'eslint'\nimport { countGates } from '../eslint/index'\nimport { tokenConfig } from './token-lint'\nimport { repositoryOf } from '../registry/skill'\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`: `.` unless it sits in a folder of its own. */\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 /** Where a session's searches are remembered. Defaults to a folder in the system's temp. */\n seen?: string\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 /** A note for Claude on a write let through: a copied look, which only warns. */\n note?: string\n}\n\n/** The token contract's own rules: what the hook keeps from its lint, and nothing else. */\nconst TOKEN_RULE_IDS = new Set(tokenConfig().flatMap((c) => Object.keys(c.rules ?? {})))\n\n/**\n * The token contract's errors for the text about to be written. The app's own\n * token config when it has one (`eslint.tokens.config.js`, which knows the app's\n * other plugins' names); otherwise the package's `tokenConfig`. Only the token\n * rules' errors count, so an app's wider lint (TypeScript's, React's) never\n * blocks a write that is still being made.\n */\nasync function tokenErrors(ESLint: typeof ESLintClass, appDir: string, file: string, text: string, audience: 'app' | 'package'): Promise<Linter.LintMessage[]> {\n const own = join(appDir, 'eslint.tokens.config.js')\n const eslint = existsSync(own)\n ? new ESLint({ cwd: appDir, overrideConfigFile: own })\n : new ESLint({ cwd: appDir, overrideConfigFile: true, overrideConfig: tokenConfig({ audience }) })\n const results = await eslint.lintText(text, { filePath: file })\n return results.flatMap((r) => r.messages.filter((m) => m.severity === 2 && m.ruleId && TOKEN_RULE_IDS.has(m.ruleId)))\n}\n\ninterface ToolCall {\n tool_name?: string\n session_id?: 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/** An app's source the gate reads: every script type, with or without JSX. */\nconst APP_SOURCE = /^src\\/.+\\.[cm]?[jt]sx?$/\n\nexport async function runHook({ root, app = '.', audience = 'app', input, seen }: 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 // Every script under an app's src/, as the gate reads it (a raw <button> in a .jsx passed\n // before, the re-review after the audit before UIG-26); the package's own gate is .tsx only.\n const source = audience === 'package' ? /^src\\/.+\\.tsx$/ : APP_SOURCE\n if (rel.startsWith('../') || isAbsolute(rel) || !source.test(rel) || /\\.test\\.[cm]?[jt]sx?$/.test(rel) || /\\.d\\.[cm]?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 // The token contract too (Katerina's ruling B4, 25 September): a colour or a size\n // is refused before the write, as CI's gate refuses it, not only at the PR.\n errors.push(...(await tokenErrors(ESLint, appDir, file, text, audience)))\n const { disabled } = countGates(results)\n if (errors.length === 0 && disabled.length === 0) {\n const stopped = searchFirst(call, file, text, appDir, top, seen)\n if (stopped) return stopped\n // A copied look is a warning (C2): it never blocks, and Claude hears of it now,\n // not only in CI (R10, 25 September).\n const copies = results.flatMap((r) => r.messages.filter((m) => m.severity === 1 && m.ruleId === 'estiva/no-copied-look'))\n return copies.length ? { code: 0, message: '', note: copies.map((m) => `${relative(top, file).split(sep).join('/')}:${m.line} ${m.message}`).join('\\n') } : PASS\n }\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(${d.ruleId.slice(d.ruleId.lastIndexOf('/') + 1)}): <reason>`)\n return { code: 2, message: `${lines.join('\\n')}\\n` }\n}\n\n/** The part a Write would add in a new file, or null: a capitalised export, in a file that draws. */\nexport function newPart(tool: string | undefined, file: string, text: string): string | null {\n if (tool !== 'Write' || existsSync(file) || !file.endsWith('.tsx') || /\\.stories\\.tsx$/.test(file)) return null\n if (!/<\\/[A-Za-z]|\\/>/.test(text)) return null\n const named = /export\\s+(?:default\\s+)?(?:async\\s+)?(?:function|const|class)\\s+([A-Z][A-Za-z0-9]*)/.exec(text)?.[1]\n if (named) return named\n return /export\\s+default\\s+function\\s*\\(/.test(text) ? basename(file, '.tsx') : null\n}\n\n/** `PanelHeader` \u2192 `panel header`: the words the search is asked. */\nexport const wordsOf = (name: string) => name.replace(/([a-z0-9])([A-Z])/g, '$1 $2').replace(/([A-Z])([A-Z][a-z])/g, '$1 $2').toLowerCase()\n\n/** The catalogue command: beside this file once built, or the built one from source (tests). */\nfunction findCommand(): string | null {\n for (const at of ['../registry/cli.js', '../../dist/registry/cli.js']) {\n const path = fileURLToPath(new URL(at, import.meta.url))\n if (existsSync(path)) return path\n }\n return null\n}\n\n/** Stop a new part's first Write with what already exists; null lets it through. */\nfunction searchFirst(call: ToolCall, file: string, text: string, appDir: string, top: string, seen?: string): HookResult | null {\n const part = newPart(call.tool_name, file, text)\n const command = findCommand()\n if (!part || !command) return null\n const folder = seen ?? join(tmpdir(), 'estiva-ui-search-first')\n const mark = join(folder, createHash('sha1').update(`${call.session_id ?? ''}\\n${resolve(file)}`).digest('hex'))\n if (existsSync(mark)) return null\n const words = wordsOf(part)\n const repo = basename(repositoryOf(appDir))\n const run = spawnSync(process.execPath, [command, 'find', ...words.split(' '), '--root', appDir, '--repo', repo, '--limit', '5'], { encoding: 'utf8', timeout: 60_000 })\n if (run.status !== 0 || !run.stdout.trim()) return null\n try {\n mkdirSync(folder, { recursive: true })\n writeFileSync(mark, '')\n } catch {\n return null\n }\n const shown = relative(top, file).split(sep).join('/')\n return {\n code: 2,\n message:\n `${shown} was not written yet: it adds a new part, ${part}. Search before building (UIG-20). What already exists for \"${words}\":\\n\\n` +\n `${run.stdout.trim()}\\n\\n` +\n `If one of these does the job, use it, and read its page first. If the words were wrong, run \\`npm run ui:find <other words>\\`. ` +\n `If nothing fits, tell the person, then write the file again: it goes through.\\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 * `estiva-gates name-escapes`: write into every escape marker the rules it keeps\n * off, once (Katerina's ruling B3, 25 September).\n *\n * A marker used to keep off every rule on its element; now it keeps off only the\n * rules it names, `// @estiva-escape(no-raw-element): <reason>`. So that nothing\n * that was escaped breaks, this asks the gate itself which rules each unnamed\n * marker stands for: linted with the new reader, an unnamed marker is reported\n * once per rule that finds its element (\"Name the rule \u2026 `@estiva-escape(<rule>)`\"),\n * and those names are written into the marker. Nothing is guessed, and a marker\n * no rule reaches is left as it is and listed.\n */\nimport { 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'\n\nexport interface NameEscapesOptions {\n /** The folder the gate lints from. */\n root?: string\n /** The gate's config file, relative to `root`. */\n config?: string\n}\n\nexport interface NameEscapesResult {\n /** One line per marker rewritten: `file:line rule, rule`. */\n named: string[]\n /** Markers no rule reached, left unnamed. */\n unreached: string[]\n}\n\nconst UNNAMED = /@estiva-escape(?!\\s*\\()/\nconst NAMED_IN_MESSAGE = /@estiva-escape\\(([^)]+)\\)/\n\nexport async function nameEscapes({ root = process.cwd(), config = 'eslint.gates.config.js' }: NameEscapesOptions = {}): Promise<NameEscapesResult> {\n const resolved = createRequire(join(root, 'package.json')).resolve('eslint')\n const { ESLint } = (await import(pathToFileURL(resolved).href)) as { ESLint: typeof ESLintClass }\n const eslint = new ESLint({ cwd: root, overrideConfigFile: join(root, config) })\n const results = await eslint.lintFiles(['.'])\n\n const named: string[] = []\n const unreached: string[] = []\n for (const result of results) {\n // The rules each unnamed marker stands for, by the line its comment starts on.\n const byLine = new Map<number, Set<string>>()\n for (const m of result.messages) {\n if (m.messageId !== 'escapeWithoutRule') continue\n const rule = NAMED_IN_MESSAGE.exec(m.message)?.[1]\n if (!rule) continue\n byLine.set(m.line, (byLine.get(m.line) ?? new Set()).add(rule))\n }\n const source = result.source ?? readFileSync(result.filePath, 'utf8')\n const eol = source.includes('\\r\\n') ? '\\r\\n' : '\\n'\n const lines = source.split(/\\r?\\n/)\n let changed = false\n const file = relative(root, result.filePath).split('\\\\').join('/')\n for (const [line, rules] of byLine) {\n // The marker is on the comment's first line, or within the next few (a block comment).\n for (let i = line - 1; i < Math.min(lines.length, line + 3); i++) {\n if (!UNNAMED.test(lines[i])) continue\n const names = [...rules].sort().join(', ')\n lines[i] = lines[i].replace(UNNAMED, `@estiva-escape(${names})`)\n named.push(`${file}:${i + 1} ${names}`)\n changed = true\n break\n }\n }\n if (changed) writeFileSync(result.filePath, lines.join(eol), 'utf8')\n // Unnamed markers the gate did not report: nothing of the gate's reaches that element.\n lines.forEach((text, i) => {\n if (UNNAMED.test(text) && !/eslint-disable/.test(text) && /\\/\\/|\\/\\*/.test(text)) unreached.push(`${file}:${i + 1}`)\n })\n }\n return { named, unreached }\n}\n", "/// <reference types=\"node\" />\n/**\n * Every escape in a repository, listed (Katerina's ruling B1, 25 September):\n * where it is, which rules it keeps off, why, and how old it is.\n *\n * The count (`.gates-count.json`) holds numbers per rule; the plan asked for\n * escapes \"listed in a report, reviewed\", and the hand-written debt pages had\n * drifted from the count in all three repos. This reads the markers themselves,\n * both forms:\n *\n * - the gate's, `// @estiva-escape(<rule>): <reason>` (or in a JSX comment);\n * - the token lint's, `// eslint-disable-next-line <rule> -- @estiva-escape: <reason>`\n * (ruling A2, 15 September), which no count sees.\n *\n * Age comes from git (the day the marker's line was last written), so it cannot\n * be forgotten or faked. Review is by age, monthly: `gates:status` shows every\n * escape older than {@link REVIEW_AFTER_DAYS} days, and Katerina keeps it (with\n * a new reason) or it gets fixed.\n */\nimport { execFileSync } from 'node:child_process'\nimport { existsSync, readdirSync, readFileSync, statSync } from 'node:fs'\nimport { join, relative } from 'node:path'\nimport { readMarker } from '../eslint/escape'\n\n/** An escape older than this many days is shown for review. */\nexport const REVIEW_AFTER_DAYS = 30\n\nexport interface Escape {\n file: string\n line: number\n /** The rules it keeps off, as the marker names them. */\n rules: string[]\n reason: string\n /** Whole days since the marker's line was written, or null outside git. */\n days: number | null\n /** `gate` for the gate's marker, `token` for the token lint's directive. */\n kind: 'gate' | 'token'\n}\n\nconst SOURCE = /\\.[cm]?[jt]sx?$/\nconst SKIP = new Set(['node_modules', 'dist', '.git', 'storybook-static', 'coverage'])\nconst DIRECTIVE = /eslint-disable(?:-next-line|-line)?\\s+(.*?)\\s+--\\s+@estiva-escape\\s*:?\\s*(.*)$/\n\nfunction sourceFiles(dir: string, out: string[] = []): string[] {\n if (!existsSync(dir)) return out\n for (const name of readdirSync(dir)) {\n if (SKIP.has(name)) continue\n const path = join(dir, name)\n if (statSync(path).isDirectory()) sourceFiles(path, out)\n else if (SOURCE.test(name) && !/\\.test\\.[cm]?[jt]sx?$/.test(name)) out.push(path)\n }\n return out\n}\n\n/** The day each line of a file was last written, from `git blame`, in seconds since 1970. */\nfunction lineTimes(root: string, file: string): Map<number, number> {\n const times = new Map<number, number>()\n try {\n const out = execFileSync('git', ['blame', '--line-porcelain', '--', relative(root, file)], { cwd: root, stdio: ['ignore', 'pipe', 'ignore'], maxBuffer: 64 << 20 }).toString()\n let line = 0\n for (const row of out.split('\\n')) {\n const header = /^[0-9a-f]{40} \\d+ (\\d+)/.exec(row)\n if (header) line = Number(header[1])\n else if (row.startsWith('author-time ')) times.set(line, Number(row.slice(12)))\n }\n } catch {\n // Not in git, or a file git does not track yet: its escapes have no age.\n }\n return times\n}\n\n/** Every escape under `src`, and in any other folder named, oldest first. */\nexport function listEscapes(root: string, { folders = ['src'], now = Date.now() }: { folders?: string[]; now?: number } = {}): Escape[] {\n const found: Escape[] = []\n for (const file of folders.flatMap((f) => sourceFiles(join(root, f)))) {\n const lines = readFileSync(file, 'utf8').split(/\\r?\\n/)\n const hits: Omit<Escape, 'days'>[] = []\n lines.forEach((text, i) => {\n if (!text.includes('@estiva-escape')) return\n const rel = relative(root, file).split('\\\\').join('/')\n // A real marker opens its comment; documentation mentions one mid-sentence, or with a placeholder.\n if (/<(reason|why|rule)\\b/.test(text)) return\n const directive = /^\\s*(?:\\/\\/|\\{?\\/\\*)\\s*eslint-disable/.test(text) ? DIRECTIVE.exec(text) : null\n if (directive) {\n hits.push({ file: rel, line: i + 1, rules: directive[1].split(/[\\s,]+/).filter(Boolean), reason: directive[2].replace(/\\*\\/\\s*}?\\s*$/, '').trim(), kind: 'token' })\n return\n }\n if (!/^\\s*(?:\\/\\/+|\\{?\\/\\*+)?\\s*@estiva-escape(?:\\s*\\(|\\s*:)/.test(text)) return\n const marker = readMarker(text)\n if (!marker) return\n hits.push({ file: rel, line: i + 1, rules: marker.rules, reason: marker.reason.replace(/\\*\\/\\s*}?\\s*$/, '').trim(), kind: 'gate' })\n })\n if (!hits.length) continue\n const times = lineTimes(root, file)\n for (const hit of hits) {\n const at = times.get(hit.line)\n found.push({ ...hit, days: at === undefined ? null : Math.floor((now - at * 1000) / 86_400_000) })\n }\n }\n return found.sort((a, b) => (b.days ?? -1) - (a.days ?? -1) || a.file.localeCompare(b.file) || a.line - b.line)\n}\n\n/** The lines `gates:status` prints: a total, and every escape due for review (or every one, with `all`). */\nexport function escapeLines(escapes: Escape[], { all = false }: { all?: boolean } = {}): string[] {\n const due = escapes.filter((e) => (e.days ?? 0) > REVIEW_AFTER_DAYS)\n const shown = all ? escapes : due\n const gate = escapes.filter((e) => e.kind === 'gate').length\n const out = [`Escapes: ${escapes.length} (${gate} of the gate's, ${escapes.length - gate} of the token lint's) \u00B7 ${due.length} older than ${REVIEW_AFTER_DAYS} days, to review: keep with a new reason, or fix${all ? '' : ' \u00B7 every escape: add -- --escapes'}`]\n for (const e of shown) out.push(` ${e.file}:${e.line} ${e.rules.join(', ') || '(no rule named)'} \u00B7 ${e.days === null ? 'not in git yet' : `${e.days} days`}\\n ${e.reason}`)\n return out\n}\n", "import type { AST, Rule } from 'eslint'\n\n/**\n * The escape marker (UIG-3, seam S4 in docs/GATES.md \u00A716): one written reason\n * why one element stays as it is.\n *\n * A line comment directly above the element, naming the rule it keeps off,\n *\n * // @estiva-escape(no-raw-element): <reason>\n *\n * or, where the element is a JSX child, the JSX comment on the line above,\n *\n * {/* @estiva-escape(no-raw-element): <reason> *\\/}\n *\n * with at least ten characters of reason, spaces not counted. It escapes only\n * the rules it names (Katerina's ruling B3, 25 September: one reason used to\n * switch off every rule on the element, so a raw button that also wrote a\n * dialog's role by hand passed under one reason). Two rules on one element are\n * named together, `@estiva-escape(no-raw-element, no-rebuilt-behaviour)`, each\n * on purpose. A marker that names no rule escapes nothing, and says which name\n * to write.\n *\n * Not `eslint-disable`. A directive switches a rule off without saying it was\n * meant, and the count of escapes (`.gates-count.json`) could not see it. A\n * marker written inside a directive that switches one of these rules off is\n * reported for that reason.\n *\n * The token lint's older notes (`eslint-disable-next-line <rule> -- @estiva-escape:\n * <reason>`, Katerina's ruling A2 of 15 September) are not read here: they\n * escape the token lint's rules, which are not this plugin's, and they stay as\n * they are (Katerina, 15 September).\n */\nexport const ESCAPE_MARKER = '@estiva-escape'\n\n/** Characters of reason, spaces not counted. */\nexport const MIN_REASON = 10\n\n/** The key under ESLint's `settings` this plugin reads. */\nexport const SETTINGS_KEY = 'estiva'\n\n/**\n * Every rule spreads these into its `meta.messages`, because `isEscaped`\n * reports through the rule that called it.\n */\nexport const ESCAPE_MESSAGES = {\n escapeWithoutReason: `An escape needs its reason, at least ${MIN_REASON} characters: \\`// ${ESCAPE_MARKER}({{rule}}): <why this stays>\\`. An escape with no reason hides nothing.`,\n escapeInDirective: `Write the escape as its own comment on the line above: \\`// ${ESCAPE_MARKER}({{rule}}): <reason>\\`. Inside an eslint-disable comment it switches the rule off instead of recording why.`,\n escapeWithoutRule: `Name the rule this escape keeps off: \\`${ESCAPE_MARKER}({{rule}}): <reason>\\`. An escape now keeps off only the rules it names.`,\n escaped: 'Escaped: {{reason}}',\n} as const\n\n/** A rule's name as a marker writes it: `no-raw-element` for `estiva/no-raw-element`. */\nexport const shortRuleName = (id: string) => id.slice(id.lastIndexOf('/') + 1)\n\n/**\n * What a marker says, read from a comment's text: the rules it names (none when\n * it names none) and its reason. Null when the comment carries no marker.\n */\nexport function readMarker(text: string): { rules: string[]; reason: string } | null {\n const at = text.indexOf(ESCAPE_MARKER)\n if (at === -1) return null\n const rest = text.slice(at + ESCAPE_MARKER.length)\n const named = /^\\s*\\(([^)]*)\\)/.exec(rest)\n const rules = named ? named[1].split(/[\\s,]+/).filter(Boolean).map(shortRuleName) : []\n const reason = (named ? rest.slice(named[0].length) : rest).replace(/^\\s*:/, '').trim()\n return { rules, reason }\n}\n\nexport interface EstivaSettings {\n /**\n * Report every sanctioned escape as a message with the id `escaped`, so a\n * count can read them. Off in the lint anyone runs; on only in the count.\n */\n reportEscapes?: boolean\n}\n\ninterface Located {\n loc?: AST.SourceLocation | null\n range?: [number, number]\n}\n\nconst DIRECTIVE = /^\\s*eslint-disable(?:-next-line|-line)?(?=\\s|$)([^]*)$/\n\n/** The rules a directive names: what comes before its ` -- ` description. None means every rule. */\nfunction directiveRules(rest: string): string[] {\n const dashes = rest.search(/(?:^|\\s)--(?:\\s|$)/)\n return (dashes === -1 ? rest : rest.slice(0, dashes)).split(/[\\s,]+/).filter(Boolean)\n}\n\n/** Only spaces and a JSX expression's closing brace between the marker and the element. */\nconst BETWEEN = /^[\\s}]*$/\n\n/**\n * Whether the element at `node` carries a valid escape on the line above.\n *\n * Every rule of this plugin calls it before it reports. It reports, through\n * that rule, a marker with too short a reason and a marker inside an\n * eslint-disable directive for this rule; either way the element is not\n * escaped, so the rule reports it too.\n */\nexport function isEscaped(context: Rule.RuleContext, node: Located): boolean {\n const { sourceCode } = context\n if (!node.loc || !node.range) return false\n const line = node.loc.start.line\n const nodeStart = node.range[0]\n\n const comment = sourceCode\n .getAllComments()\n .find((c) => c.loc && c.range && c.loc.end.line === line - 1 && BETWEEN.test(sourceCode.text.slice(c.range[1], nodeStart)))\n if (!comment?.loc) return false\n\n const marker = readMarker(comment.value)\n if (!marker) return false\n const rule = shortRuleName(context.id)\n\n const directive = DIRECTIVE.exec(comment.value)\n if (directive) {\n const rules = directiveRules(directive[1])\n // A directive for other rules (the token lint's notes) is not an escape of this one.\n if (rules.length > 0 && !rules.includes(context.id)) return false\n context.report({ loc: comment.loc, messageId: 'escapeInDirective', data: { rule } })\n return false\n }\n\n // Named, but not this rule: another rule on the element may be the one it keeps off.\n if (marker.rules.length > 0 && !marker.rules.includes(rule)) return false\n if (marker.rules.length === 0) {\n context.report({ loc: comment.loc, messageId: 'escapeWithoutRule', data: { rule } })\n return false\n }\n\n const { reason } = marker\n if (reason.replace(/\\s/g, '').length < MIN_REASON) {\n context.report({ loc: comment.loc, messageId: 'escapeWithoutReason', data: { rule } })\n return false\n }\n\n const settings = context.settings[SETTINGS_KEY] as EstivaSettings | undefined\n if (settings?.reportEscapes) context.report({ loc: comment.loc, messageId: 'escaped', data: { reason } })\n return true\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/` (Ship's did, until PER-19)\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: in an app that sits in a folder of\n * its own, the checks file sits there too (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, spawnSync } 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'\nimport { escapeLines, listEscapes } from './escapes'\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: `.` unless the app sits in a folder of its own. */\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 /** Build the catalogue of the app in `appDir` (UIG-13): passes when every part is described and sorted. */\n catalogue(appDir: string): Promise<CheckResult>\n /** Every reusable part keeps the usage-page contract (UIG-19): the package's one copy of it. */\n contract(appDir: string): Promise<CheckResult>\n /**\n * What loads into every Claude session stays small (UIG-21): `CLAUDE.md` and\n * every file it imports with `@` come to fewer than `limit` lines, and each\n * `.claude/rules` file has `paths:`, so it loads only where it applies.\n */\n instructions(limit?: number): 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, when not the top. */\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 scripts `estiva-gates ci` runs (R13): a job that runs it runs these. */\nconst GATE_CI_SCRIPTS = new Set(['lint:rules', 'lint:tokens', 'lint', 'registry:check'])\n\n/** `estiva-gates ci`, as a workflow runs it, when it runs `script`; null otherwise. A comment line does not count. */\nfunction runsGateCi(text: string, script: string | RegExp): string | null {\n if (typeof script !== 'string' || !GATE_CI_SCRIPTS.has(script)) return null\n const m = /^(?!\\s*#).*?((?:npx )?estiva-gates ci\\b|npm run gates:ci\\b|cli\\.js\"? ci\\b)/m.exec(text)\n return m ? m[1] : null\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 // The registry bundle's own builder, loaded only when a check asks for it:\n // it reads TypeScript with TypeScript, which the other checks never need.\n async catalogue(appDir) {\n try {\n const { buildAppRegistry, validateRegistry } = await import('../registry/index')\n const registry = buildAppRegistry({ root: abs(appDir) })\n const problems = validateRegistry(registry)\n if (problems.length) return FAIL(`${problems.length} problems, the first: ${problems[0]}`)\n return PASS(`${registry.entries.length} parts in ${registry.builtFrom.files} files, each described and sorted`)\n } catch (error) {\n const lines = String(error instanceof Error ? error.message : error).split('\\n')\n return FAIL(lines.length > 1 ? `${lines.length - 1} problems, the first: ${lines[1].trim()}` : lines[0])\n }\n },\n\n async contract(appDir) {\n try {\n const { buildAppRegistry, contractProblems, CONTRACT_KINDS } = await import('../registry/index')\n const registry = buildAppRegistry({ root: abs(appDir) })\n const problems = contractProblems(registry, abs(appDir))\n const owed = registry.entries.filter((entry) => (CONTRACT_KINDS as readonly string[]).includes(entry.app?.class ?? '')).length\n if (problems.length) return FAIL(`${problems.length} of ${owed}; the first: ${problems[0]}`)\n return PASS(`all ${owed} reusable parts keep the usage-page contract and are drawn somewhere`)\n } catch (error) {\n return FAIL(String(error instanceof Error ? error.message : error).split('\\n')[0])\n }\n },\n\n instructions(limit = 200) {\n if (!exists('CLAUDE.md')) return FAIL('there is no CLAUDE.md')\n // As `wc -l` counts them: a last line ending in a newline is not one more.\n const lines = (rel: string) => read(rel).replace(/\\r?\\n$/, '').split(/\\r?\\n/).length\n const imports = [...read('CLAUDE.md').matchAll(/^@(\\S+)\\s*$/gm)].map((m) => m[1])\n const missing = imports.filter((rel) => !exists(rel))\n if (missing.length) return FAIL(`CLAUDE.md imports ${missing.join(', ')}, which is not there`)\n const total = imports.reduce((n, rel) => n + lines(rel), lines('CLAUDE.md'))\n const what = imports.length ? `CLAUDE.md with ${imports.join(', ')}` : 'CLAUDE.md'\n if (total >= limit) return FAIL(`${what}: ${total} lines load into every session; the limit is ${limit}`)\n const rules = listFiles('.claude/rules', (n) => n.endsWith('.md'))\n const unscoped = rules.filter((rel) => {\n const front = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---/.exec(read(rel))\n return !front || !/^paths:/m.test(front[1])\n })\n if (unscoped.length) return FAIL(`${unscoped.join(', ')} has no paths:, so it loads into every session`)\n return PASS(`${what}: ${total} lines in every session; ${rules.length ? `${rules.length} rule files, each loading only for its paths` : 'no rule files'}`)\n },\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 const all = runsGateCi(read(f), script)\n if (all) return PASS(`${f} runs ${all}, which runs npm run ${script}`)\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 if (step.test(body)) return PASS(`${f}: the job ${job} runs npm run ${script}`)\n const all = runsGateCi(body, script)\n return all ? PASS(`${f}: the job ${job} runs ${all}, which 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?: { matcher?: string; hooks?: { command?: string }[] }[] } }\n try {\n s = JSON.parse(read(settingsRel))\n } catch {\n return FAIL(`${settingsRel} is not valid JSON`)\n }\n const entries = (s.hooks?.PreToolUse ?? []).flatMap((m) => (m.hooks ?? []).map((x) => ({ matcher: m.matcher ?? '', command: x.command ?? '' })))\n const hit = entries.find((e) => e.command.includes(needle))\n if (!hit) return FAIL(entries.length ? `${settingsRel} has PreToolUse hooks, none running ${needle}` : `${settingsRel} has no PreToolUse hook`)\n // A hook that is written but never fires, or fires and cannot run, guards\n // nothing \u2014 and Claude Code lets a write through on any exit but 2 (audit\n // A2: Ship's hook pointed at an install its checkout did not have). So the\n // hook is run, as Claude Code would run it, on a raw <button>.\n let matches: boolean\n try {\n matches = hit.matcher === '' || hit.matcher === '*' || new RegExp(`^(?:${hit.matcher})$`).test('Write')\n } catch {\n return FAIL(`${settingsRel}: the hook's matcher \"${hit.matcher}\" is not a pattern`)\n }\n if (!matches) return FAIL(`${settingsRel}: the hook runs only for \"${hit.matcher}\", never for a Write`)\n const top = ROOT.split('\\\\').join('/')\n const app = /--app\\s+(\"?)([^\"\\s]+)\\1/.exec(hit.command)?.[2] ?? '.'\n const input = JSON.stringify({\n hook_event_name: 'PreToolUse',\n cwd: top,\n tool_name: 'Write',\n tool_input: { file_path: `${resolve(ROOT, app, 'src', '__gates_hook_probe__.tsx').split('\\\\').join('/')}`, content: 'export function Probe() {\\n return <div><button type=\"button\">x</button></div>\\n}\\n' },\n })\n const command = hit.command.replace(/\\$\\{?CLAUDE_PROJECT_DIR\\}?/g, top)\n const run = spawnSync(command, { cwd: ROOT, input, shell: true, encoding: 'utf8', timeout: 120_000, env: { ...process.env, CLAUDE_PROJECT_DIR: top } })\n // Refused for the button, not for something else about a new file (in the package a new\n // file with no page is refused too): nested, so the package's inward rule reads it as well.\n if (run.status === 2 && /<button>/.test(`${run.stderr ?? ''}${run.stdout ?? ''}`)) return PASS(`${settingsRel}: the hook ran and refused a raw <button> (exit 2)`)\n const why = `${run.stderr ?? ''}${run.stdout ?? ''}`.trim().split(/\\r?\\n/)[0] || (run.error ? String(run.error.message) : 'no output')\n return FAIL(`${settingsRel}: the hook ran and let a raw <button> through (exit ${run.status ?? 'none'}): ${why}`)\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: for an app in a folder of its own, that install is there, not in\n // the repo's top folder (UIG-32). Everything it names is still read from the 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, when not the top, 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\n/**\n * How far a checkout is behind its remote's main, after fetching it. Status reads\n * the checkout, not main: a copy seven commits behind once reported a finished\n * ticket as not started (audit A4). Empty when it is level; a warning otherwise.\n */\nexport function behindMain(dir: string): string {\n const git = (...a: string[]) => {\n try {\n return execFileSync('git', a, { cwd: dir, stdio: ['ignore', 'pipe', 'ignore'], timeout: 30000 }).toString().trim()\n } catch {\n return null\n }\n }\n if (git('rev-parse', '--is-inside-work-tree') !== 'true') return ''\n const fetched = git('fetch', '-q', 'origin') !== null\n const main = (git('symbolic-ref', '--short', 'refs/remotes/origin/HEAD') ?? 'origin/main').trim()\n const count = Number(git('rev-list', '--count', `HEAD..${main}`))\n const stale = fetched ? '' : ' (could not fetch: compared with the last fetch)'\n if (!Number.isFinite(count) || count === 0) return stale ? ` \u00B7${stale}` : ''\n return ` \u00B7 \u26A0\uFE0F ${count} commit${count === 1 ? '' : 's'} behind ${main}${stale}: pull, or this reads old code`\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`: `.` unless it sits in a folder of its own. */\n app?: string\n json?: boolean\n detail?: boolean\n /** List every escape, not only those due for review (B1). */\n escapes?: boolean\n}\n\n/**\n * What an app's catalogue leaves for Katerina: the parts nothing uses, by name\n * (her UIG-13 ruling: an unused part is listed for her, never deleted by the\n * catalogue; R20), and the parts with no link into Storybook (R22). A reusable\n * part with none fails `estiva-ui check`; a one-off needs none (B10), so its\n * number is shown and nothing more.\n */\nasync function catalogueLines(appDir: string): Promise<string[]> {\n try {\n const { buildAppRegistry } = await import('../registry/index')\n const entries = buildAppRegistry({ root: appDir }).entries\n const unused = entries.filter((e) => e.app?.class === 'unused')\n const oneOffs = entries.filter((e) => e.app?.class === 'one-off')\n const unlinked = oneOffs.filter((e) => !e.storyId && !e.docsId)\n return [\n `Unused parts: ${unused.length}${unused.length ? ` \u00B7 Katerina rules on each: ${unused.map((e) => `${e.name} (${e.sourceFile})`).join(', ')}` : ''}`,\n `Storybook links: every reusable part has one (estiva-ui check holds it) \u00B7 one-offs with none: ${unlinked.length} of ${oneOffs.length}, which need none`,\n ]\n } catch (error) {\n return [`Catalogue: could not be built here: ${error instanceof Error ? error.message.split('\\n')[0] : String(error)}`]\n }\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, escapes = 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}${behindMain(ROOT)}`)\n // Every escape, listed and aged (Katerina's rulings B1, 25 September): the ones due for review always.\n out.push('', ...escapeLines(listEscapes(join(ROOT, app)), { all: escapes }))\n\n if (!spec.all) {\n out.push('', ...(await catalogueLines(join(ROOT, app))))\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}${behindMain(dir)}` })\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": ["require", "existsSync", "readFileSync", "createRequire", "join", "pathToFileURL", "spawnSync", "existsSync", "readFileSync", "writeFileSync", "createRequire", "join", "relative", "fileURLToPath", "pathToFileURL", "countGates", "join", "existsSync", "readFileSync", "relative", "createRequire", "pathToFileURL", "countGates", "fileURLToPath", "spawnSync", "writeFileSync", "readFileSync", "writeFileSync", "createRequire", "join", "relative", "pathToFileURL", "existsSync", "readFileSync", "statSync", "join", "relative", "existsSync", "join", "statSync", "relative", "readFileSync", "execFileSync", "spawnSync", "existsSync", "readdirSync", "readFileSync", "statSync", "createRequire", "join", "relative", "resolve", "pathToFileURL", "createRequire", "PASS", "resolve", "existsSync", "readFileSync", "execFileSync", "readdirSync", "join", "statSync", "relative", "pathToFileURL", "spawnSync", "r", "p", "f"]
7
+ }
package/dist/gates/cli.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  runHook,
6
6
  runStatus,
7
7
  writeGateCount
8
- } from "./chunk-SYVKGOWM.js";
8
+ } from "./chunk-P5W6O5TF.js";
9
9
  import "./chunk-YKZXKVR2.js";
10
10
 
11
11
  // src/gates/cli.ts
@@ -1 +1 @@
1
- {"version":3,"file":"gate-config.d.ts","sourceRoot":"","sources":["../../src/gates/gate-config.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAO5C,MAAM,WAAW,iBAAiB;IAChC,gGAAgG;IAChG,QAAQ,CAAC,EAAE,KAAK,GAAG,SAAS,CAAA;IAC5B;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAA;IACrC,yDAAyD;IACzD,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CACnB;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,EAAE,QAAgB,EAAE,GAAE,IAAI,CAAC,iBAAiB,EAAE,UAAU,CAAM,GAAG,MAAM,CAAC,MAAM,CAYtG;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EAAE,QAAgB,EAAE,KAAU,EAAE,OAAY,EAAE,GAAE,iBAAsB,GAAG,MAAM,CAAC,MAAM,EAAE,CAmBlH"}
1
+ {"version":3,"file":"gate-config.d.ts","sourceRoot":"","sources":["../../src/gates/gate-config.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAQ5C,MAAM,WAAW,iBAAiB;IAChC,gGAAgG;IAChG,QAAQ,CAAC,EAAE,KAAK,GAAG,SAAS,CAAA;IAC5B;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAA;IACrC,yDAAyD;IACzD,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CACnB;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,EAAE,QAAgB,EAAE,GAAE,IAAI,CAAC,iBAAiB,EAAE,UAAU,CAAM,GAAG,MAAM,CAAC,MAAM,CAYtG;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EAAE,QAAgB,EAAE,KAAU,EAAE,OAAY,EAAE,GAAE,iBAAsB,GAAG,MAAM,CAAC,MAAM,EAAE,CAoBlH"}
@@ -16,11 +16,12 @@ import {
16
16
  skippedFolders,
17
17
  skipsListed,
18
18
  tokenConfig,
19
+ tokenJoinedPlugin,
19
20
  tokenLint,
20
21
  tokenStylePlugin,
21
22
  tokenValues,
22
23
  writeGateCount
23
- } from "./chunk-SYVKGOWM.js";
24
+ } from "./chunk-P5W6O5TF.js";
24
25
  import {
25
26
  appFiles,
26
27
  askNpm,
@@ -56,7 +57,8 @@ function gateConfig({ audience = "app", quiet = {}, ignores = [] } = {}) {
56
57
  "better-tailwindcss": betterTailwindcss,
57
58
  "token-values": betterTailwindcss,
58
59
  "token-spacing": betterTailwindcss,
59
- "token-style": tokenStylePlugin
60
+ "token-style": tokenStylePlugin,
61
+ "token-joined": tokenJoinedPlugin
60
62
  },
61
63
  // Every other config's directives are unused here, because their rules are
62
64
  // off here. Reporting them would be this config complaining about the
@@ -89,7 +91,8 @@ var APP_TICKET_TITLES = {
89
91
  "UIG-28": "Close the two holes in the token contract \u2014 arbitrary values, and inline style",
90
92
  "UIG-30": "RichText \u2014 one component that draws a message's text, for both apps",
91
93
  "UIG-32": "Peek and Ship take their gate pieces from the package",
92
- "UIG-37": "The gates refuse a TypeScript eslint-disable comment \u2014 know the rule names, rules off"
94
+ "UIG-37": "The gates refuse a TypeScript eslint-disable comment \u2014 know the rule names, rules off",
95
+ "UIG-42": "What the gate can't see yet \u2014 a box around a part, a screen drawn by hand, two blind spots in the token check"
93
96
  };
94
97
  function all(h, steps, label) {
95
98
  return async () => {
@@ -294,15 +297,24 @@ ${component('<Button title="Delete">x</Button>')}`, "error", "WithTooltip") }
294
297
  return probe(code, "none", void 0, "src/components/__gates_probe__.test.tsx", config)();
295
298
  }), "a test file is not checked for hand-written values, while source is") }
296
299
  ]),
300
+ // Built 28 September: the token check's two blind spots, and a box that places a page EmptyState.
301
+ ticket("UIG-42", [
302
+ { what: "a class behind an arbitrary variant is an error", run: tokenProbe(component('<div className="group-hover/row:bg-gray-100">x</div>'), "error") },
303
+ { what: "a class list joined by hand is read", run: tokenProbe(component("<div className={['text-sm', 'p-2'].join(' ')}>x</div>"), "error") },
304
+ { what: "a box that centres a page EmptyState is an error naming it", run: probe(`import { EmptyState } from '@estiva-app/ui'
305
+ ${component('<div className="flex items-center justify-center"><EmptyState message="Nothing here" /></div>')}`, "error", "page-scope EmptyState") }
306
+ ]),
297
307
  // Built 28 September: both apps drew the protocol's text tree by hand, and drifted apart.
298
308
  // A file that reads the protocol and draws a quote or a numbered list itself is a third copy.
299
309
  ticket("UIG-30", [
300
310
  { what: "the installed package has RichText", run: () => h.contains(installed, /\bRichText\b/, "the installed @estiva-app/ui exports RichText") },
301
311
  { what: "no file draws the protocol's text tree by hand", run: () => {
302
312
  const sources = h.listFiles(at("src"), (n) => /\.tsx$/.test(n) && !/\.(test|stories)\.tsx$/.test(n));
313
+ const TREE = /\b(?:toRenderTree|RenderBlock|RenderInline|parseInlineMarks|parseBodySegments|markersToInlineNodes|InlineTextNode|InlineMarkSpan)\b/;
314
+ const readsTree = (text) => [...text.matchAll(/import\s+(?:type\s+)?\{([^}]*)\}\s*from\s*['"]@estiva-app\/protocol['"]/g)].some((m) => TREE.test(m[1]));
303
315
  const own = sources.filter((f) => {
304
316
  const text = h.read(f);
305
- return /from ['"]@estiva-app\/protocol['"]/.test(text) && /<(?:blockquote|ol)\b/.test(text);
317
+ return readsTree(text) && /<(?:blockquote|ol)\b/.test(text);
306
318
  });
307
319
  return own.length ? h.FAIL(`draws the text tree itself (${own.join(", ")}): pass the tree to RichText`) : h.PASS("formatted text is drawn by RichText only");
308
320
  } }