@erclx/aitk 0.96.0 → 0.97.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "aitk",
3
3
  "description": "Automated governance, versioning, and discovery tools for Claude Code.",
4
- "version": "0.96.0",
4
+ "version": "0.97.0",
5
5
  "author": {
6
6
  "name": "Eric Le",
7
7
  "url": "https://github.com/erclx"
@@ -0,0 +1,26 @@
1
+ ---
2
+ description: Require a route capture against a running preview after a page or route surface changes
3
+ paths:
4
+ - '**/routes/**/*.{tsx,jsx,vue,svelte,astro}'
5
+ - '**/pages/**/*.{tsx,jsx,vue,svelte,astro}'
6
+ - '**/app/**/page.{tsx,jsx}'
7
+ ---
8
+
9
+ # Surface capture standards
10
+
11
+ ## When to capture
12
+
13
+ - Run `bun run screenshot` after changing what a route renders.
14
+ - Capture against a running preview server. Do not capture against a dev server.
15
+ - Capture every theme the route ships. Do not capture the default theme alone.
16
+
17
+ ## What a capture covers
18
+
19
+ - Capture the full page at the viewport its case declares. Do not capture a component in isolation.
20
+ - Add a case to the capture record when adding a route.
21
+ - Remove a route's case in the change that removes the route.
22
+
23
+ ## Sharing a capture
24
+
25
+ - Attach a capture to the pull request by hand when a reviewer needs to see it.
26
+ - Do not commit a capture. Do not remove the capture folder from `.gitignore`.
@@ -1,2 +1,2 @@
1
1
  extends = "node"
2
- rules = ["210-astro", "350-security-web", "400-ui", "410-a11y", "430-ux-completeness"]
2
+ rules = ["210-astro", "350-security-web", "400-ui", "410-a11y", "430-ux-completeness", "440-surface-capture"]
@@ -1,2 +1,2 @@
1
1
  extends = "node"
2
- rules = ["200-react", "230-nextjs", "250-tailwind", "300-testing-ts", "310-zod", "350-security-web", "400-ui", "410-a11y", "420-forms", "430-ux-completeness"]
2
+ rules = ["200-react", "230-nextjs", "250-tailwind", "300-testing-ts", "310-zod", "350-security-web", "400-ui", "410-a11y", "420-forms", "430-ux-completeness", "440-surface-capture"]
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@erclx/aitk",
3
3
  "type": "module",
4
- "version": "0.96.0",
4
+ "version": "0.97.0",
5
5
  "description": "Infrastructure and quality tooling for developer workflows",
6
6
  "license": "MIT",
7
7
  "bin": {
@@ -4,6 +4,8 @@
4
4
 
5
5
  The base layer covers every project the toolkit scaffolds, whatever language sits on top. It ships formatting, spelling, shell linting, conventional commits, git hooks, CI, and three maintenance scripts. Every other stack extends it, so a decision made here is one every stack inherits.
6
6
 
7
+ A repository with several language roots layers this stack once at the root and skips it per subtree, since git honors only one `core.hooksPath` and a re-dropped husky silently breaks. `docs/target-projects.md` carries the recipe.
8
+
7
9
  ## What ships as golden configs
8
10
 
9
11
  Golden config files live in `tooling/base/configs/` and are copied into the target on `aitk tooling sync base .`. They are the source of truth. The reference covers rationale and tradeoffs. Configs show the concrete setup.
@@ -1,41 +1,53 @@
1
1
  import type { Page } from '@playwright/test'
2
-
3
- const ROUTES = [{ name: 'home', path: '/', width: 1280, height: 800 }]
4
-
5
- type State = { name: string; setup?: (page: Page) => Promise<void> }
6
- const STATES: State[] = [
7
- { name: 'default' },
8
- { name: 'dark', setup: async (p) => p.emulateMedia({ colorScheme: 'dark' }) },
9
- ]
10
-
11
2
  import { chromium } from '@playwright/test'
12
3
  import { mkdir } from 'fs/promises'
13
4
  import path from 'path'
14
5
 
6
+ interface CaptureCase {
7
+ section: string
8
+ theme: string
9
+ route: string
10
+ width: number
11
+ height: number
12
+ setup?: (page: Page) => Promise<void>
13
+ }
14
+
15
+ const CASES: CaptureCase[] = [
16
+ { section: 'home', theme: 'default', route: '/', width: 1280, height: 800 },
17
+ {
18
+ section: 'home',
19
+ theme: 'dark',
20
+ route: '/',
21
+ width: 1280,
22
+ height: 800,
23
+ setup: (page) => page.emulateMedia({ colorScheme: 'dark' }),
24
+ },
25
+ ]
26
+
15
27
  const BASE_URL = process.env.SCREENSHOT_BASE_URL ?? 'http://localhost:4173'
16
28
  const OUT_DIR = 'screenshots'
17
29
 
18
30
  const browser = await chromium.launch()
19
- await mkdir(OUT_DIR, { recursive: true })
20
31
 
21
- for (const route of ROUTES) {
22
- for (const state of STATES) {
23
- const ctx = await browser.newContext({
24
- viewport: { width: route.width, height: route.height },
25
- })
26
- const page = await ctx.newPage()
32
+ for (const captureCase of CASES) {
33
+ const context = await browser.newContext({
34
+ viewport: { width: captureCase.width, height: captureCase.height },
35
+ })
36
+ const page = await context.newPage()
37
+
38
+ if (captureCase.setup) await captureCase.setup(page)
27
39
 
28
- if (state.setup) await state.setup(page)
40
+ await page.goto(`${BASE_URL}${captureCase.route}`)
41
+ await page.waitForLoadState('networkidle')
29
42
 
30
- await page.goto(`${BASE_URL}${route.path}`)
31
- await page.waitForLoadState('networkidle')
43
+ const sectionDir = path.join(OUT_DIR, captureCase.section)
44
+ await mkdir(sectionDir, { recursive: true })
32
45
 
33
- const file = path.join(OUT_DIR, `${route.name}-${state.name}.png`)
34
- await page.screenshot({ path: file, fullPage: true })
35
- console.log(`captured ${file}`)
46
+ const file = path.join(sectionDir, `${captureCase.theme}.png`)
47
+ await page.screenshot({ path: file, fullPage: true })
48
+ console.log(`captured ${file}`)
36
49
 
37
- await ctx.close()
38
- }
50
+ await context.close()
39
51
  }
40
52
 
41
53
  await browser.close()
@@ -12,7 +12,7 @@ Golden config files live in `tooling/web/configs/` and are copied into the targe
12
12
 
13
13
  - `eslint.config.js`: flat config with `@eslint/js`, `typescript-eslint`, React hooks, import sort, check-file, vitest rules scoped to test files, `eslint-config-prettier` last.
14
14
  - `src/test/setup.ts`: `@testing-library/jest-dom` import, `cleanup` after each test.
15
- - `e2e/screenshot.ts`: capture template. `ROUTES` and `STATES` consts at the top carry one route in a default and a dark state, and the loop below them writes `screenshots/<name>-<state>.png`. Per-project routes and states extend the two consts.
15
+ - `e2e/screenshot.ts`: capture template. A single `CASES` record at the top carries one entry per output file, each naming a section, a theme, a route, and its own viewport, and the loop below writes `screenshots/<section>/<theme>.png`. Per-project cases extend the one record. A route's themes sit together under its section folder, so the filename carries the theme alone.
16
16
  - `.vscode/extensions.json` and `.vscode/settings.json`: editor wiring for ESLint, Tailwind, Playwright, Vitest.
17
17
  - `.github/workflows/verify.yml`: `static-checks`, `unit-tests`, `build-verify`, and `e2e-tests` jobs.
18
18
  - `scripts/verify.sh`: extends base verify with typecheck, lint, unit tests, and build in the full order.
@@ -102,7 +102,9 @@ Append rows:
102
102
  | `bun run test:e2e` | Run Playwright E2E tests. |
103
103
  | `bun run screenshot` | Build, preview, then capture screenshots. |
104
104
 
105
- `aitk tooling verify <stack>` is the only automated caller of `bun run screenshot`, running it for any stack whose `package.json` declares the script and asserting that PNG files land under `screenshots/`. No ship chain captures a screenshot, so the output path the seed writes is a contract that one verifier reads rather than a default a ship step depends on.
105
+ `aitk tooling verify <stack>` is the only automated caller of `bun run screenshot`, running it for any stack whose `package.json` declares the script and asserting that PNG files land under `screenshots/`. It counts them with a recursive find carrying no depth limit, so the section folders the seed writes satisfy the assertion without a change to it. Do not flatten the layout to protect that check. No ship chain captures a screenshot, so the output path the seed writes is a contract that one verifier reads rather than a default a ship step depends on.
106
+
107
+ `governance/rules/ui/440-surface-capture.md` is what asks a session to run the capture after a route changes. It fires on route and page files rather than on every component, so a shared component changing every screen fires nothing and the operator runs the capture by hand.
106
108
 
107
109
  ## Verify script
108
110