@imfusion/web-ui 0.5.1-dev.25.gf0207a31 → 0.5.1-dev.28.g1156ad00

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -60,7 +60,7 @@ the directories it already installed into, without asking again — `--reconfigu
60
60
  | `/imf-web-ui-components` | Component reference — what exists and how it's meant to be used. |
61
61
  | `/imf-web-ui-ux` | UX guidance for building interfaces with the library. |
62
62
  | `/imf-web-ui-frontend-conventions` | The frontend conventions baseline, including the sanctioned styling seams. |
63
- | `/imf-web-ui-frontend-setup` | Set up or audit an ImFusion frontend's tooling against the house baseline. |
63
+ | `/imf-web-ui-frontend-setup` | Assess an ImFusion frontend and write a reviewable setup report. |
64
64
  | `/imf-web-ui-agent-setup` | Install or update the vendored skills, agent hooks, and TanStack Intent. |
65
65
  | `/imf-web-ui-update` | Update the library, skills, and optional hooks, then verify before committing. |
66
66
 
@@ -1,6 +1,7 @@
1
1
  import { beforeEach, describe, expect, it } from "vitest";
2
2
  import { spawnSync } from "node:child_process";
3
3
  import {
4
+ chmodSync,
4
5
  existsSync,
5
6
  lstatSync,
6
7
  mkdirSync,
@@ -150,6 +151,36 @@ describe("web-ui-install", () => {
150
151
  expect(staleness.stdout).toBe("");
151
152
  });
152
153
 
154
+ it("runs post-edit checks from the package containing the edited file", () => {
155
+ runInstall(consumer, "--hooks");
156
+
157
+ const frontend = join(consumer, "frontend");
158
+ const source = join(frontend, "src", "example.ts");
159
+ const binDir = join(frontend, "node_modules", ".bin");
160
+ const log = join(consumer, "hook.log");
161
+ mkdirSync(join(frontend, "src"), { recursive: true });
162
+ mkdirSync(binDir, { recursive: true });
163
+ writeFileSync(join(frontend, "package.json"), JSON.stringify({ private: true }));
164
+ writeFileSync(source, "export const example = true;\n");
165
+
166
+ for (const tool of ["eslint", "prettier"]) {
167
+ const path = join(binDir, tool);
168
+ writeFileSync(path, '#!/usr/bin/env sh\nprintf "%s|%s\\n" "$PWD" "$*" >> "$HOOK_LOG"\n');
169
+ chmodSync(path, 0o755);
170
+ }
171
+
172
+ const result = spawnSync("sh", [join(consumer, ".agents", "hooks", "imf-web-ui", "post-tool-use.sh")], {
173
+ cwd: consumer,
174
+ env: { ...process.env, HOOK_LOG: log },
175
+ input: JSON.stringify({ tool_input: { file_path: source } }),
176
+ encoding: "utf-8"
177
+ });
178
+
179
+ expect(result.status).toBe(0);
180
+ const calls = readFileSync(log, "utf-8").trim().split("\n");
181
+ expect(calls).toEqual([`${frontend}|--cache ${source}`, `${frontend}|--check ${source}`]);
182
+ });
183
+
153
184
  it("warns from the staleness script when skill markers lag the package", () => {
154
185
  runInstall(consumer, "--skills", "--hooks", "--target", "agents");
155
186
  const pkgDir = join(consumer, "node_modules", "@imfusion", "web-ui");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@imfusion/web-ui",
3
- "version": "0.5.1-dev.25.gf0207a31",
3
+ "version": "0.5.1-dev.28.g1156ad00",
4
4
  "description": "The official Web UI component library for ImFusion web apps",
5
5
  "author": "ImFusion GmbH",
6
6
  "license": "UNLICENSED",
@@ -8,6 +8,7 @@ description:
8
8
  consumer app, when skills are stale, when hooks should be installed or adapted, or when asked to set up TanStack Intent or
9
9
  dependency-shipped Agent Skills. Not for project tooling (imf-web-ui-frontend-setup) or library wiring
10
10
  (imf-web-ui-library-setup)."
11
+ allowed-tools: Read Bash
11
12
  ---
12
13
 
13
14
  # imf-web-ui-agent-setup
@@ -29,8 +30,9 @@ Three agent lifecycle hooks ship as templates in [`templates/hooks/`](templates/
29
30
  - **SessionStart** — once per session: the conventions baseline is vendored, load it before writing.
30
31
  - **UserPromptSubmit** — a one-line conventions reminder per prompt. Per-prompt, not per-tool-call: a PreToolUse reminder
31
32
  would re-inject the same text on every edit.
32
- - **PostToolUse** (advisory) — file-scope checks on the touched file after every Edit/Write, failures fed straight back.
33
- Never exits non-zero.
33
+ - **PostToolUse** (advisory) — file-scope checks on the touched file after every Edit/Write, failures fed straight back. It
34
+ resolves the nearest package from the edited file, so a frontend nested below a monorepo root uses its own config and
35
+ dependencies. Never exits non-zero.
34
36
  - **`baseline-staleness.sh`** — not registered as an agent hook; the repo's pre-commit calls it
35
37
  ([`git.md`](../imf-web-ui-frontend-conventions/references/git.md), Staleness at commit time).
36
38
 
@@ -42,6 +44,14 @@ Three agent lifecycle hooks ship as templates in [`templates/hooks/`](templates/
42
44
  it. Use the template as a starting point instead: fold the missing behaviour into the repo's existing script, or adapt the
43
45
  template and register that. Surface the situation and let the human pick.
44
46
 
47
+ ## Assessment reference
48
+
49
+ A static frontend setup assessment checks that the complete consumer skill bundle and version markers match the declared
50
+ `@imfusion/web-ui` version; the AGENTS fence exists; SessionStart, UserPromptSubmit, and PostToolUse behavior is installed
51
+ and registered or consciously adapted; PostToolUse resolves the edited file's package and its tools in monorepos; and the
52
+ tracked pre-commit path calls `baseline-staleness.sh`. Read files and settings as text—do not run installers, hooks, or local
53
+ config queries during assessment.
54
+
45
55
  ## Dependency-shipped skills
46
56
 
47
57
  Some libraries ship Agent Skills inside their npm package; TanStack does across much of the suite.
@@ -67,5 +77,6 @@ guidance you have: use `npx @tanstack/cli` for TanStack docs, and never guess at
67
77
 
68
78
  ## Not this skill
69
79
 
70
- - Project tooling, docs structure, the audit checklist → `imf-web-ui-frontend-setup` (which delegates agent tooling here)
80
+ - Project tooling, docs structure, the audit checklist → `imf-web-ui-frontend-setup` (which reads this skill as its
81
+ agent-tooling assessment contract)
71
82
  - Library wiring (styles import, provider) → `imf-web-ui-library-setup`
@@ -1,21 +1,33 @@
1
1
  #!/usr/bin/env sh
2
2
  # PostToolUse (Edit|Write): advisory file-scope checks on the touched file. Never exits
3
- # non-zero, so a mid-flight refactor can't trap the agent. Resolve the repo from the edited
4
- # file, not cwd, which is not guaranteed for hooks.
5
- FILE=$(jq -r '.tool_input.file_path // empty' 2>/dev/null)
3
+ # non-zero, so a mid-flight refactor can't trap the agent. Resolve the package from the
4
+ # edited file, not cwd or the Git root, which may belong to a parent monorepo.
5
+ FILE=$(sed -n 's/.*"file_path"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -n1)
6
6
  [ -f "$FILE" ] || exit 0
7
7
 
8
- ROOT=$(cd "$(dirname "$FILE")" && git rev-parse --show-toplevel 2>/dev/null) || exit 0
9
- cd "$ROOT" || exit 0
8
+ FILE_DIR=$(cd "$(dirname "$FILE")" && pwd) || exit 0
9
+ FILE="$FILE_DIR/$(basename "$FILE")"
10
+ PACKAGE_ROOT=$FILE_DIR
11
+ while [ "$PACKAGE_ROOT" != "/" ] && [ ! -f "$PACKAGE_ROOT/package.json" ]; do
12
+ PACKAGE_ROOT=$(dirname "$PACKAGE_ROOT")
13
+ done
14
+ [ -f "$PACKAGE_ROOT/package.json" ] || exit 0
15
+
16
+ TOOL_ROOT=$PACKAGE_ROOT
17
+ while [ "$TOOL_ROOT" != "/" ] && [ ! -d "$TOOL_ROOT/node_modules/.bin" ]; do
18
+ TOOL_ROOT=$(dirname "$TOOL_ROOT")
19
+ done
20
+ [ -d "$TOOL_ROOT/node_modules/.bin" ] || exit 0
21
+ cd "$PACKAGE_ROOT" || exit 0
10
22
 
11
23
  case "$FILE" in
12
24
  *.ts | *.tsx | *.js | *.jsx)
13
- [ -x ./node_modules/.bin/eslint ] && ./node_modules/.bin/eslint --cache "$FILE" 2>&1
25
+ [ -x "$TOOL_ROOT/node_modules/.bin/eslint" ] && "$TOOL_ROOT/node_modules/.bin/eslint" --cache "$FILE" 2>&1
14
26
  ;;
15
27
  esac
16
28
  case "$FILE" in
17
29
  *.ts | *.tsx | *.js | *.jsx | *.css | *.json | *.md)
18
- [ -x ./node_modules/.bin/prettier ] && ./node_modules/.bin/prettier --check "$FILE" 2>&1
30
+ [ -x "$TOOL_ROOT/node_modules/.bin/prettier" ] && "$TOOL_ROOT/node_modules/.bin/prettier" --check "$FILE" 2>&1
19
31
  ;;
20
32
  esac
21
33
  exit 0
@@ -4,6 +4,7 @@ description:
4
4
  "Look up @imfusion/web-ui component APIs without reading their source: the two-hop lookup (identity index -> prop data),
5
5
  compound components, and integrations. Load when you need the props, sub-components, or defaults of a specific component —
6
6
  not for choosing between components (imf-web-ui-ux) or first-time setup (imf-web-ui-library-setup)."
7
+ allowed-tools: Bash
7
8
  ---
8
9
 
9
10
  # imf-web-ui-components
@@ -2,10 +2,10 @@
2
2
  name: imf-web-ui-frontend-conventions
3
3
  description:
4
4
  "The ImFusion frontend conventions baseline — in-house conventions, valid in every ImFusion frontend and usable by anyone
5
- who likes them. A router over topic references: library boundary, React, components, TypeScript, styling, data layer,
6
- project structure, testing, stack, npm project, tooling config, git, assets, docs structure. Load when writing wrapper
7
- components, custom UI, styling beyond the defaults, adding new files to a consumer app, writing repo docs, touching tool
8
- config, or choosing any dependency."
5
+ who likes them. A router over topic references: library boundary, React, components, TypeScript, styling, validation, data
6
+ layer, project structure, testing, stack, npm project, tooling config, git, assets, docs structure. Load when writing
7
+ wrapper components, custom UI, styling beyond the defaults, validating external data, adding new files to a consumer app,
8
+ writing repo docs, touching tool config, or choosing any dependency."
9
9
  ---
10
10
 
11
11
  # imf-web-ui-frontend-conventions
@@ -32,7 +32,8 @@ Each topic lives in one reference. Read the one whose moment you're in; starting
32
32
  | [typescript.md](references/typescript.md) | functional style, types, naming | writing any code |
33
33
  | [styling.md](references/styling.md) | native CSS Modules, tokens, the override contract | writing CSS or styling beyond the defaults |
34
34
  | [class-names.md](references/class-names.md) | CVA variants, `cx`, merging the incoming `className` | writing a component with variants or a `className` prop |
35
- | [data.md](references/data.md) | `api/`+`http/` shape, Zod boundary, query/mutation patterns | adding an API topic, a fetch, or a mutation |
35
+ | [validation.md](references/validation.md) | runtime schemas, boundary parsing, schema-derived types | accepting data the frontend does not own |
36
+ | [data.md](references/data.md) | `api/`+`http/` shape, query/mutation patterns and invalidation | adding an API topic, a fetch, or a mutation |
36
37
  | [project-structure.md](references/project-structure.md) | the `src/` tree, file naming, imports | adding files rather than editing existing ones |
37
38
  | [testing.md](references/testing.md) | what's worth testing and what isn't | writing or reviewing tests |
38
39
  | [stack.md](references/stack.md) | the topic→tool map, when a library owns a layer | choosing or adding any dependency |
@@ -20,12 +20,9 @@ transport-level (base client, error normalisation) lives in `http/` and nowhere
20
20
 
21
21
  ## The network boundary validates
22
22
 
23
- The network is the only place untyped data enters the app, so it is the only place that validates. Everything a backend sends
24
- is parsed against a Zod schema at that edge; the TypeScript types are derived from the schemas with `z.infer`, never
25
- hand-written. A hand-written response type is a claim about the backend that nothing checks.
26
-
27
- The transport in `http/` is deliberately untyped: it returns `unknown` and leaves the parse to the caller, so no call site
28
- can accidentally skip validation.
23
+ The transport in `http/` returns `unknown`; the topic schema parses the response in its query or mutation function before the
24
+ value enters the app. The schema is also the source of its TypeScript type. The full boundary, type-derivation, and failure
25
+ handling rules are [validation.md](validation.md).
29
26
 
30
27
  ## Errors are values
31
28
 
@@ -65,7 +62,8 @@ export const userQueryOptions = () =>
65
62
  ```
66
63
 
67
64
  Components consume query options directly with `useSuspenseQuery` (prefetched routes) or `useQuery` (secondary data). No
68
- wrapper hooks — the options function is the reusable unit.
65
+ wrapper hooks — the options function is the reusable unit. Call sites may spread the returned options only to add
66
+ component-specific callbacks or overrides.
69
67
 
70
68
  Mutations follow the same shape with `mutationOptions`, the dirtied topic first in the `mutationKey`:
71
69
 
@@ -132,8 +130,8 @@ function MePage() {
132
130
  }
133
131
  ```
134
132
 
135
- Mutations use `useMutation` with the options factory. Manual invalidation is unnecessary when automatic invalidation is on
136
- (see below):
133
+ Mutations use `useMutation` with the options factory instead of reconstructing `mutationKey` and `mutationFn` at the call
134
+ site. Manual invalidation is unnecessary when automatic invalidation is on (see below):
137
135
 
138
136
  ```tsx
139
137
  // components/user-settings.tsx
@@ -189,8 +187,15 @@ targeted optimistic update), and invalidate precisely through the key factory in
189
187
  [query invalidation](https://tanstack.com/query/latest/docs/framework/react/guides/query-invalidation) and
190
188
  [automatic invalidation after mutations](https://tkdodo.eu/blog/automatic-query-invalidation-after-mutations).
191
189
 
190
+ ## Audit
191
+
192
+ Inspect the complete path, not the presence of TanStack Query alone: transport returns `unknown`; topic schemas parse
193
+ responses; API types derive from those schemas; keys come from topic factories; API topics expose reusable `queryOptions` and
194
+ `mutationOptions`; hooks consume those options with only local overrides; mutation keys match the configured invalidation
195
+ strategy.
196
+
192
197
  ## Testing the data layer
193
198
 
194
199
  Stub the network at the `fetch` boundary and let the real query client run: the test then exercises the same parse and error
195
- path production does. Schemas, clients, and query options are where behaviour worth asserting lives — the general philosophy
196
- is [testing.md](testing.md).
200
+ path production does. Schemas, clients, and query options are where behaviour worth asserting lives — schema cases are in
201
+ [validation.md](validation.md), and the general philosophy is [testing.md](testing.md).
@@ -58,7 +58,8 @@ components. When state must be shared between siblings, lift it to the nearest c
58
58
  State comes in kinds, and each kind has an owner. Work down this list and stop at the first match:
59
59
 
60
60
  1. **URL state** — shareable via the address bar (filters, sort, pagination, active tab) → TanStack Router search params.
61
- Back button and copied links are UX features you get for free.
61
+ Back button and copied links are UX features you get for free. URL values cross an external boundary; parse them according
62
+ to [validation.md](validation.md).
62
63
  2. **Server state** — comes from an API → TanStack Query's cache ([data.md](data.md)). Never copy server data into `useState`
63
64
  — that's how stale-UI bugs are born.
64
65
  3. **Subtree state** — scoped to a subtree, resets on leave (wizard progress) → React context.
@@ -66,7 +67,8 @@ State comes in kinds, and each kind has an owner. Work down this list and stop a
66
67
  5. **Local state** — one component's own (input value, open/closed) → `useState`.
67
68
 
68
69
  `useState` is the right tool for local UI state, and most state is local: whether a panel is open, which tab is active, a
69
- draft value being typed, a hover flag. Keep those in the component and don't reach for a library.
70
+ draft value being typed, a hover flag. Keep those in the component and don't reach for a library. When a form turns drafts
71
+ into a submitted domain value, validate that boundary as described in [validation.md](validation.md).
70
72
 
71
73
  Most frontends need far less of tier 4 than they think; tiers 1–2 usually dissolve the "we need a store" instinct. For
72
74
  structuring the state itself, [Choosing the State Structure](https://react.dev/learn/choosing-the-state-structure) is the
@@ -11,6 +11,8 @@ Test the **decisions**, not the rendering.
11
11
  - **Extract the decision out of a hook and test that.** A hook whose interesting part is a plain function is easier to test
12
12
  as a plain function than through a render harness. A hook that only wraps a browser API has no decision to extract.
13
13
 
14
- The data layer has its own recipe — stub `fetch`, run the real query client — in [data.md](data.md).
14
+ The data layer has its own recipe — stub `fetch`, run the real query client — in [data.md](data.md). A schema whose
15
+ constraints or transforms encode product behavior gets focused accepted, rejected, and transformed cases; see
16
+ [validation.md](validation.md).
15
17
 
16
18
  The measure isn't coverage percentage. It's whether a test failing tells you something you didn't already know.
@@ -19,7 +19,8 @@ const labels: Record<Size, string> = { sm: "S", md: "M", lg: "L" }; // compiler
19
19
  ```
20
20
 
21
21
  Across boundaries the same rule: library props via `React.ComponentProps<typeof Button>`
22
- ([library-boundary.md](library-boundary.md)), API types via `z.infer` ([data.md](data.md)).
22
+ ([library-boundary.md](library-boundary.md)), untrusted data via a runtime schema and `z.infer`
23
+ ([validation.md](validation.md)).
23
24
 
24
25
  - Function signatures: 1–2 positional arguments; at 3+, one destructured object.
25
26
 
@@ -0,0 +1,88 @@
1
+ # Validation
2
+
3
+ Runtime validation belongs where data crosses from an untrusted representation into frontend-owned values. The schema is the
4
+ single source of truth for both the runtime check and the TypeScript type.
5
+
6
+ ## Boundaries
7
+
8
+ Validate once, at the edge:
9
+
10
+ - network responses when they enter the frontend;
11
+ - URL path and search parameters before business logic uses them;
12
+ - persisted browser data when it is read;
13
+ - user input when it becomes a submitted domain value or request value.
14
+
15
+ Code inside that boundary receives parsed values and does not repeat defensive shape checks. An outgoing request built from
16
+ an already parsed domain value is serialized, not validated a second time. A typed client returning a handwritten generic is
17
+ not validation: it only asserts that an untrusted response has the requested type.
18
+
19
+ ## Schema first, type derived
20
+
21
+ Use Zod for the runtime schema and derive the type with `z.infer`:
22
+
23
+ ```ts
24
+ import { z } from "zod";
25
+
26
+ export const userSchema = z.object({
27
+ id: z.string(),
28
+ email: z.email()
29
+ });
30
+
31
+ export type User = z.infer<typeof userSchema>;
32
+ ```
33
+
34
+ Do not maintain a handwritten `User` beside `userSchema`. Request and response shapes follow the same rule when the frontend
35
+ owns or consumes their runtime representation.
36
+
37
+ ## TanStack Router search params
38
+
39
+ TanStack Router v1 accepts a Zod v4 schema directly in `validateSearch`; no adapter or parsing wrapper is needed:
40
+
41
+ ```tsx
42
+ import { createFileRoute } from "@tanstack/react-router";
43
+ import { z } from "zod";
44
+
45
+ const searchSchema = z.object({
46
+ page: z.number().int().positive().catch(1),
47
+ filter: z.string().catch("")
48
+ });
49
+
50
+ type UserSearch = z.infer<typeof searchSchema>;
51
+
52
+ export const Route = createFileRoute("/users")({
53
+ validateSearch: searchSchema,
54
+ component: Users
55
+ });
56
+
57
+ function Users() {
58
+ const search = Route.useSearch();
59
+ return <UserList page={search.page} filter={search.filter} />;
60
+ }
61
+ ```
62
+
63
+ `Route.useSearch()` returns `UserSearch` by inference from `validateSearch`. Use `.catch()` when malformed URL input should
64
+ fall back without interrupting navigation. Use `.default()` only when a missing value gets a default while malformed values
65
+ should still follow the route's validation error path.
66
+
67
+ ## Failure handling
68
+
69
+ Use `schema.parse(value)` when invalid data is a contract failure that should follow the normal error path, such as a
70
+ malformed backend response reaching a route error boundary. Use `schema.safeParse(value)` when failure is expected and the
71
+ caller must render or otherwise handle validation issues, such as submitted user input.
72
+
73
+ Transforms and coercion belong in the boundary schema when they are part of entering the domain. Do not scatter trimming,
74
+ number conversion, or defaulting through downstream components.
75
+
76
+ ## Audit
77
+
78
+ A boundary is aligned when:
79
+
80
+ - the untrusted source is represented as `unknown` until parsed;
81
+ - a Zod schema parses it at the point of entry;
82
+ - exported TypeScript types use `z.infer<typeof schema>`;
83
+ - downstream code consumes the parsed value without duplicate checks or assertions;
84
+ - parse failures reach the intended error or user-feedback path;
85
+ - behavior-changing schemas have focused tests for accepted, rejected, and transformed values.
86
+
87
+ TanStack Query placement is in [data.md](data.md), general type derivation in [typescript.md](typescript.md), URL and form
88
+ ownership in [react.md](react.md), and test selection in [testing.md](testing.md).
@@ -1,89 +1,66 @@
1
1
  ---
2
2
  name: imf-web-ui-frontend-setup
3
3
  description:
4
- "Set up or audit an ImFusion frontend's project tooling: the stack, package.json scripts, formatting, linting, typecheck,
5
- staged-file and pre-commit hooks, verification scopes, dependency pinning, tsconfig, docs structure, agent wiring. House
6
- conventions, not industry standards. Load when starting a new ImFusion frontend, when asked what an existing one's setup is
7
- missing, when asked to align a repo with the baseline, or when asked to check or set up one named topic from it (e.g. CSS
8
- class names, Prettier). Not for a bare 'add this config file' request — that's just the edit. Not for wiring the library
9
- itself (imf-web-ui-library-setup)."
4
+ "Assess an ImFusion frontend against the project baseline and write a reviewable FRONTEND_SETUP_REPORT.md: new-project
5
+ setup needs, existing-project gaps, alignment migrations, or one named topic (for example data, lifecycle hooks, CSS class
6
+ names, or Prettier). Covers stack, package scripts, tooling, verification, project shape, data, docs, and agent wiring.
7
+ House conventions, not industry standards. This skill inspects and plans; approved implementation is a separate task. Not
8
+ for wiring the library itself (imf-web-ui-library-setup)."
10
9
  argument-hint: "[new|audit|align|<topic>]"
10
+ allowed-tools: Read Glob Grep
11
11
  ---
12
12
 
13
13
  # imf-web-ui-frontend-setup
14
14
 
15
- First-time setup and audit against the ImFusion frontend baseline. The conventions live in `imf-web-ui-frontend-conventions`
16
- — this skill is the process that checks a repo against them and wires up what they assume. In-house conventions, not industry
17
- standards: report findings as "missing against the ImFusion baseline", never "against best practice". Built for ImFusion
18
- frontends; anyone else who likes the baseline can run it too.
19
-
20
- Four modes, same checklist:
21
-
22
- - **New project** — work down the checklist and set each piece up.
23
- - **Audit** — read the repo (don't ask what it has), report present / missing / broken, change nothing until the human picks.
24
- An established repo is where a forgotten piece hides. **The project wins:** where the repo already decided, that stands —
25
- report what's _absent_; a working convention you'd have chosen differently is not a finding.
26
- - **Align** — when the user asks to _align_ the repo with the baseline ("align"/"alignment" is the flag), the project-wins
27
- guard lifts: deviations become migration findings, proposed as a plan, still nothing changed until approved.
28
- - **One topic** — any other argument names a topic instead of a mode (`class names`, `prettier`, `tooling`). Resolve it to
29
- the checklist rows it touches and run the audit process against those only, reading their references as usual. Say which
30
- rows you resolved it to before reporting, and if nothing matches, say so and list the rows rather than guessing or sweeping
31
- everything. Same output as an audit: findings, nothing changed until the human picks.
32
-
33
- **Producer scope.** The web-ui repo itself produces this baseline; it is not a consumer frontend. Consumer-only rows — the
34
- AGENTS.md fence, vendored-skill staleness, the app stack and app `src/` tree — don't apply there. Audit it against the shared
35
- rows only: scripts, tooling, git, docs.
36
-
37
- ## The checklist
38
-
39
- Each row is a reference in `../imf-web-ui-frontend-conventions/references/` — read it, then check the repo against it. A
40
- topic argument narrows this table to the rows it names; every other mode works down all of it.
41
-
42
- | Reference | Set up / audit |
43
- | ---------------------- | ------------------------------------------------------------------------------------------- |
44
- | `stack.md` | the dependencies match the topic→tool map; devtools siblings present |
45
- | `npm-project.md` | script names table, `type`/`private`, exact pins, `.npmrc`, Node pinning |
46
- | `tooling.md` | Prettier values, ESLint flat config, tsconfig, staged-file runner, readable CSS class names |
47
- | `git.md` | `git:config` run and hooks directory present, verify scopes, staleness hooks |
48
- | `project-structure.md` | the `src/` tree, file naming, `#/` alias wiring |
49
- | `components.md` | component folders and colocation |
50
- | `styling.md` | CSS Modules, tokens, no CSS-in-JS or utility framework |
51
- | `docs-structure.md` | docs shape and content rules (see Docs below) |
52
- | — agent tooling | delegated to `imf-web-ui-agent-setup` (see Agent tooling below) |
53
-
54
- ## Docs
55
-
56
- `README.md`, `AGENTS.md`, and `docs/` with a `docs/index.md` that registers every doc. Scaffold from
57
- [`templates/README.md`](templates/README.md) and [`templates/AGENTS.md`](templates/AGENTS.md); missing structure is a
58
- finding.
59
-
60
- Keep `AGENTS.md` lean. The decision test for every line: would the agent make a costly mistake without it? If it would just
61
- need to read a file first, cut it — dev commands, path aliases, and tool config are discoverable from the files themselves.
62
-
63
- `AGENTS.md` contains one installer-owned section: the `<!-- imf-web-ui:begin -->` … `<!-- imf-web-ui:end -->` fence.
64
- `npx web-ui-install` refreshes what's inside on every skills install; everything outside the fence is the repo's own. A
65
- missing fence in an existing `AGENTS.md` is a finding — without it the baseline note can't be kept current.
66
-
67
- Judge existing docs only against `docs-structure.md`: repo-unique content stays, restated baseline becomes a pointer,
68
- deviations get named as deviations. Don't rewrite a repo's docs uninvited — report, and let the human pick.
69
-
70
- ## Agent tooling
71
-
72
- The agent side — vendored skills and their freshness, the lifecycle hooks, the settings registrations — is
73
- `imf-web-ui-agent-setup`. Delegate to it: in a new project after the docs step, in an audit as one checklist row (skills
74
- present and current, hooks wired or consciously adapted). Findings it produces report here like any other.
75
-
76
- ## Optional
77
-
78
- Recommend when the shape calls for it; absence is not a finding.
79
-
80
- - **knip** — once several people delete things independently.
81
- - **`eslint-plugin-jsx-a11y`** — anything user-facing.
82
-
83
- Out of scope, project-specific: CI, env and secrets, error tracking, deploy, dependency updates.
84
-
85
- ## Not this skill
86
-
87
- - Library wiring (styles import, provider) → `imf-web-ui-library-setup`
88
- - The conventions themselves → `imf-web-ui-frontend-conventions` and its references — this skill checks the structure exists,
89
- that skill owns what goes inside it
15
+ You are the frontend setup auditor. Investigate the repository statically, record every supported conclusion in
16
+ `FRONTEND_SETUP_REPORT.md`, then stop for human review. You must follow the applicable `imf-web-ui-frontend-conventions`
17
+ references, cite repository evidence, distinguish defects from working deviations, and never present the baseline as
18
+ universal best practice.
19
+
20
+ ## Workflow
21
+
22
+ 1. Resolve the requested mode and scope.
23
+ 2. Read every applicable convention reference below. For agent tooling, also read the vendored
24
+ `imf-web-ui-agent-setup/SKILL.md` and its templates.
25
+ 3. Inspect the repository and write or refresh `FRONTEND_SETUP_REPORT.md` from
26
+ [`templates/FRONTEND_SETUP_REPORT.md`](templates/FRONTEND_SETUP_REPORT.md). The template is the report contract. Preserve
27
+ everything under `## Reviewer notes` verbatim.
28
+ 4. Return the report path and a short verdict. Change nothing else; implementation is a separate, approved task.
29
+
30
+ ## Safety
31
+
32
+ Use only static inspection: Read, Glob, Grep, and equivalent non-executing search tools. Do not use a shell or invoke Node,
33
+ npm, npx, package scripts, hooks, config imports, linters, tests, builds, Git commands, or project binaries. Read config as
34
+ text and report runtime or machine-local state that cannot be established statically as unverified.
35
+
36
+ Only `FRONTEND_SETUP_REPORT.md` may be written. Write is intentionally not pre-approved in `allowed-tools`; the report
37
+ follows the host's ordinary write approval. Host-managed hooks may run after that write; the skill neither invokes nor
38
+ suppresses them, but it does report broken or unexpected hook behavior found during static inspection.
39
+
40
+ ## Modes
41
+
42
+ - **New** — record what exists and what setup work is needed.
43
+ - **Audit** — report broken and missing pieces. A working project convention wins; differences are deviations, not defects.
44
+ - **Align** — use the same evidence, but make deviations explicit migration proposals.
45
+ - **One topic** — resolve any other argument to matching rows below and assess only those. If none match, list the available
46
+ rows instead of guessing or widening scope.
47
+
48
+ ## References
49
+
50
+ | Reference | Assess |
51
+ | --------------------------------- | ------------------------------------------------------------------------- |
52
+ | `stack.md` | dependencies, dead-code detection, and matching devtools |
53
+ | `npm-project.md` | package metadata, scripts, pins, npm and Node config |
54
+ | `tooling.md` | Prettier, ESLint, TypeScript, staged files, CSS class names |
55
+ | `git.md` | tracked hooks, verification scopes, staleness wiring |
56
+ | `project-structure.md` | source tree, naming, imports |
57
+ | `components.md` | component folders and colocation |
58
+ | `styling.md` | CSS Modules, tokens, prohibited styling systems |
59
+ | `validation.md` | runtime schemas, boundary parsing, derived types |
60
+ | `data.md` | transport, query/mutation options, keys, invalidation |
61
+ | `docs-structure.md` | README, AGENTS, docs index and content boundaries |
62
+ | `imf-web-ui-agent-setup/SKILL.md` | installed skills, AGENTS fence, lifecycle hooks, registrations, staleness |
63
+
64
+ Knip is required for dead-code detection. `eslint-plugin-jsx-a11y` remains a recommendation when the project is user-facing;
65
+ its absence is not a finding. CI, env and secrets, error tracking, deploy, dependency updates, and library wiring are out of
66
+ scope.
@@ -0,0 +1,45 @@
1
+ # Frontend Setup Report
2
+
3
+ Mode: `<new|audit|align|topic>` Scope: `<all applicable references or resolved topic references>`
4
+
5
+ <!--
6
+ This template is the report contract. Keep every H2 below once and in this order.
7
+ Use `None.` for an empty section.
8
+
9
+ Broken, Missing, Deviations, and Unverified entries use:
10
+ - **[high|medium|low] reference-or-agent-tooling — Short title**
11
+ - Evidence: `path:line`
12
+ - Impact: concrete consequence
13
+ - Next action: smallest selectable follow-up
14
+
15
+ Present entries name the reference and evidence path. Deviations are selectable follow-up work, not defects; in align mode,
16
+ their next actions are migration proposals. Optional tools are not missing findings.
17
+ -->
18
+
19
+ ## Verdict
20
+
21
+ <One short assessment of the current frontend setup.>
22
+
23
+ ## Broken
24
+
25
+ None.
26
+
27
+ ## Missing
28
+
29
+ None.
30
+
31
+ ## Deviations
32
+
33
+ None.
34
+
35
+ ## Present
36
+
37
+ None.
38
+
39
+ ## Unverified
40
+
41
+ None.
42
+
43
+ ## Reviewer notes
44
+
45
+ <!-- Human-owned. Preserve everything under this heading verbatim when refreshing the report. -->
@@ -5,6 +5,7 @@ description:
5
5
  lifecycle hooks, verify the base update, then audit and optionally migrate affected or custom consumer components in a
6
6
  separate approved commit."
7
7
  argument-hint: "[--dry-run] [optional version or reason]"
8
+ allowed-tools: Bash Read Grep
8
9
  ---
9
10
 
10
11
  # imf-web-ui-update