solid2-agent-kit 0.7.0 → 0.8.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.
package/README.md CHANGED
@@ -16,7 +16,7 @@ Supported agents: **Cursor** and **Claude Code**.
16
16
  | Always-applied hard rules | `.cursor/rules/solid-2.mdc` (glob-attached to `*.tsx`/`*.jsx`) | managed block in `CLAUDE.md` | 23 hard rules + banned Solid 1.x API table + eslint-plugin-solid lint-heritage table. Injected without the agent having to decide to read anything |
17
17
  | Agent skill | `.cursor/skills/solid-2/` | `.claude/skills/solid-2/` | Execution-model primer, decision tables, canonical patterns, a “still runs / write the other form” table, review checklist, official-doc URL index |
18
18
  | Shared agent context | managed block in `AGENTS.md` | same (Claude Code users can reference it from `CLAUDE.md`) | Core principles and pointers, always in context |
19
- | Mechanical gate | `solid2-kit check` | same | Comment-stripped regex guard over `src/**/*.{ts,tsx,jsx}` (or explicit `[files...]`): fails on props destructuring (including multi-line signatures), `{ ...props }` rest copies, `{list().map(...)}` in JSX, React imports/hooks/JSX props/`React.lazy`/`useOptimistic`/`onDoubleClick`, Solid 1.x imports/APIs/components/JSX namespaces, `vite-plugin-solid`, old-router `<Route>`/`<A>`/`<HashRouter>`/`<FileRoutes>`, `MetaProvider`, `Context.Provider`, camelCase style keys, `key` props, `value()!` hand-narrowing, hand-rolled loading signals, `=== undefined` readiness branches, SolidStart leftovers (`@solidjs/start`, `h3`, `createAsync`, `useSubmission`, `"use client"`), Next.js imports/`useRouter`/`typeof window`, `history.pushState`, `render(<App />)` (pass a function), `isPending(user())` / `latest(user())` (pass the accessor), and `<Dynamic>` (use `dynamic()`) |
19
+ | Mechanical gate | `solid2-kit check` | same | Comment-stripped regex guard over `src/**/*.{ts,tsx,jsx}` (or explicit `[files...]`): fails on props destructuring (including multi-line signatures), `{ ...props }` rest copies, `{list().map(...)}` in JSX, React imports/hooks/JSX props/`React.lazy`/`useOptimistic`/`onDoubleClick`, Solid 1.x imports/APIs/components/JSX namespaces, `vite-plugin-solid`, old-router `<Route>`/`<A>`/`<HashRouter>`/`<FileRoutes>`, `MetaProvider`, `Context.Provider`, camelCase style keys, `key` props, `value()!` hand-narrowing, hand-rolled loading signals, `=== undefined` readiness branches, SolidStart leftovers (`@solidjs/start`, `h3`, `createAsync`, `useSubmission`, `"use client"`), Next.js imports/`useRouter`/`typeof window`, `history.pushState`, `render(<App />)` (pass a function), `isPending(user())` / `latest(user())` (pass the accessor), `<Dynamic>` (use `dynamic()`), `import type { JSX } from "solid-js"` (no `JSX` namespace there — `Element` from `solid-js`, DOM `JSX` types from `@solidjs/web`), and two-phase effects whose apply only writes a local signal (use a writable derivation) |
20
20
  | Edit-time hook | `postToolUse` entry in `.cursor/hooks.json` | `PostToolUse` entry in `.claude/settings.json` (matcher includes `Bash`) | Runs the mechanical gate automatically on **every agent file edit — including edits made through shell commands** (sed, heredocs, codemods; source paths mentioned in the command are checked after it ran) — and feeds findings straight back into the conversation (Cursor: `additional_context`; Claude Code: stderr + exit 2), so enforcement does not depend on the agent remembering to run `check`. Wired only when the kit is a local devDependency; opt out with `--no-hooks` |
21
21
  | Turn-end gate | — | `Stop` entry in `.claude/settings.json` | Whole-project `check` + `doctor` when the agent tries to end its turn: with findings in place the stop is blocked (exit 2) and the findings are fed back, so a turn cannot finish with React/Solid 1.x patterns left behind. Loop-safe via `stop_hook_active` (one forced continuation, never an infinite loop) |
22
22
  | Pre-execution guard | `preToolUse` + `beforeShellExecution` entries in `.cursor/hooks.json` | `PreToolUse` entry in `.claude/settings.json` | Denies actions **before they run**: edits/deletes of kit-owned guardrail files (an agent blocked by a gate will try to remove the gate), edits that would strip the kit's hook entries or managed blocks, uninspectable shell rewrites of those files, and `npm/pnpm/yarn/bun install` of banned dependencies (`react`, `vite-plugin-solid`, `eslint-plugin-solid`, SolidStart, ...) — caught proactively instead of post-hoc by `doctor` |
@@ -274,6 +274,86 @@ function init() {
274
274
 
275
275
  // --- check ------------------------------------------------------------------
276
276
 
277
+ // Balanced-scan helpers for the effect-sync check: regexes cannot pair the
278
+ // parentheses of a createEffect(...) call, so scan (string-aware; comments
279
+ // are already stripped) for the matching closer from an opening bracket.
280
+ function scanBalanced(content, openIndex) {
281
+ let depth = 0;
282
+ let quote = null;
283
+ for (let i = openIndex; i < content.length; i += 1) {
284
+ const ch = content[i];
285
+ if (quote !== null) {
286
+ if (ch === '\\') i += 1;
287
+ else if (ch === quote) quote = null;
288
+ } else if (ch === "'" || ch === '"' || ch === '`') {
289
+ quote = ch;
290
+ } else if (ch === '(' || ch === '{' || ch === '[') {
291
+ depth += 1;
292
+ } else if (ch === ')' || ch === '}' || ch === ']') {
293
+ depth -= 1;
294
+ if (depth === 0) return i;
295
+ }
296
+ }
297
+ return -1;
298
+ }
299
+
300
+ function splitTopLevelArgs(argsText) {
301
+ const parts = [];
302
+ let depth = 0;
303
+ let quote = null;
304
+ let start = 0;
305
+ for (let i = 0; i < argsText.length; i += 1) {
306
+ const ch = argsText[i];
307
+ if (quote !== null) {
308
+ if (ch === '\\') i += 1;
309
+ else if (ch === quote) quote = null;
310
+ } else if (ch === "'" || ch === '"' || ch === '`') {
311
+ quote = ch;
312
+ } else if (ch === '(' || ch === '{' || ch === '[') {
313
+ depth += 1;
314
+ } else if (ch === ')' || ch === '}' || ch === ']') {
315
+ depth -= 1;
316
+ } else if (ch === ',' && depth === 0) {
317
+ parts.push(argsText.slice(start, i));
318
+ start = i + 1;
319
+ }
320
+ }
321
+ parts.push(argsText.slice(start));
322
+ return parts;
323
+ }
324
+
325
+ // An apply phase that is nothing but one (optionally if-guarded) call to a
326
+ // local signal setter is "a signal plus an effect that syncs it" — rule 4.
327
+ // Restricting to setters declared via createSignal destructuring in the same
328
+ // file keeps external set* functions (setDefaultOptions, setTimeout) out.
329
+ function soleLocalSetterApply(apply, localSetters) {
330
+ const head = apply.match(
331
+ /^(?:async\s*)?(?:\([^()]*\)|[\w$]+)\s*=>\s*\{?\s*(?:if\s*\((?:[^()]|\([^()]*\))*\)\s*\{?\s*)?(set[A-Z][\w$]*)\s*\(/,
332
+ );
333
+ if (!head || !localSetters.has(head[1])) return false;
334
+ const close = scanBalanced(apply, head[0].length - 1);
335
+ if (close === -1) return false;
336
+ return /^[\s;}]*$/.test(apply.slice(close + 1));
337
+ }
338
+
339
+ const LOCAL_SETTER_DECL = /\[\s*[\w$]+\s*,\s*(set[A-Z][\w$]*)\s*\]\s*=\s*createSignal\b/g;
340
+
341
+ function effectSyncFindings(content) {
342
+ const localSetters = new Set([...content.matchAll(LOCAL_SETTER_DECL)].map((m) => m[1]));
343
+ if (localSetters.size === 0) return [];
344
+ const matches = [];
345
+ for (const call of content.matchAll(/\bcreateEffect\s*(?:<[^>()]*>)?\s*\(/g)) {
346
+ const open = call.index + call[0].length - 1;
347
+ const close = scanBalanced(content, open);
348
+ if (close === -1) continue;
349
+ const args = splitTopLevelArgs(content.slice(open + 1, close));
350
+ if (args.length >= 2 && soleLocalSetterApply(args[1].trim(), localSetters)) {
351
+ matches.push({ index: call.index });
352
+ }
353
+ }
354
+ return matches;
355
+ }
356
+
277
357
  const CHECKS = [
278
358
  {
279
359
  id: 'props-destructure-param',
@@ -298,6 +378,20 @@ const CHECKS = [
298
378
  message:
299
379
  'Solid 1.x import path. Stores/merge/omit come from "solid-js"; render/hydrate/Portal/Dynamic come from "@solidjs/web".',
300
380
  },
381
+ {
382
+ id: 'jsx-namespace-import',
383
+ pattern: /import\s+(?:type\s+)?\{[^}]*\bJSX\b[^}]*\}\s*from\s+['"]solid-js['"]/g,
384
+ message:
385
+ 'Solid 2 does not export a `JSX` namespace from "solid-js" (TS2305). Children/return types are `Element` from "solid-js"; DOM-specific JSX types (JSX.IntrinsicElements, JSX.CSSProperties) come from "@solidjs/web".',
386
+ },
387
+ {
388
+ // A two-phase createEffect whose apply is nothing but one (optionally
389
+ // if-guarded) call to a setter declared via createSignal in this file.
390
+ id: 'effect-sync-signal',
391
+ find: effectSyncFindings,
392
+ message:
393
+ 'Effect that only syncs a local signal — "state + effect" (rule 4). Derive the value instead; to reset state when another reactive value changes, use a writable derivation: createSignal(() => { source(); return initial; }).',
394
+ },
301
395
  {
302
396
  id: 'react-jsx-prop',
303
397
  pattern: /\b(?:className|htmlFor)=/g,
@@ -572,7 +666,7 @@ function fileFindings(file, relativeTo) {
572
666
  const lines = raw.split('\n');
573
667
  const findings = [];
574
668
  for (const rule of CHECKS) {
575
- for (const match of content.matchAll(rule.pattern)) {
669
+ for (const match of rule.find ? rule.find(content) : content.matchAll(rule.pattern)) {
576
670
  const lineNumber = content.slice(0, match.index).split('\n').length;
577
671
  findings.push(
578
672
  `${relative(relativeTo, file) || file}:${lineNumber} [${rule.id}] ${rule.message}\n > ${lines[lineNumber - 1].trim()}`,
@@ -5,4 +5,4 @@
5
5
  - After editing TSX, run the `solid2-kit check` mechanical gate — it fails on React/Solid 1.x tokens and props destructuring. Use the project's `lint:solid` script if one exists; otherwise `npx solid2-agent-kit check` (with the kit as a devDependency) or `npx github:lightsound/solid2-agent-kit check`. An edit-time hook may also run this gate automatically after each file edit — treat its findings as errors to fix immediately, not warnings. Do not install `eslint-plugin-solid` (built for Solid 1.x; misreads Solid 2 idioms).
6
6
  - After editing `package.json`, `tsconfig*.json`, or root config files (Vite, ESLint), run `solid2-kit doctor` — it fails on React/Solid 1.x wiring: `react`/`vite-plugin-solid`/`eslint-plugin-solid`/SolidStart dependencies, 1.x `solid-js` ranges, and `jsx`/`jsxImportSource` values other than `preserve`/`@solidjs/web`.
7
7
  - The kit's guidance and gate files (`.cursor/rules/solid-2.mdc`, the `solid-2` skill directories, hook entries, managed blocks in this file and `CLAUDE.md`) are guardrails, not obstacles. Never edit, weaken, bypass, or delete them to make a failure go away — fix the flagged code. If a guardrail seems wrong, stop and tell the user.
8
- - Solid 2.0 shipped recently and breaks with 1.x — never trust pre-2.0 Solid docs, tutorials, or training knowledge. Verify APIs against https://v2-rebuild--solid-docs-v2.netlify.app/llms.txt (markdown mirror of https://v2.solidjs.com/, which blocks non-browser fetchers). If the project has `@solidjs/router`, server functions, or `@solidjs/meta`, use those official pages (see the skill's official-docs index) — not React Router, Next.js, SolidStart 1.x, or Solid Router 0.x/1.x JSX `<Route>`/`<A>`. Core `action` from `solid-js` is a generator transaction; router `action`/`query` from `@solidjs/router` are POST forms and a read cache. Core `refresh(source)`, router `revalidate(key)`, and server-function `return reload(...)` are three different APIs. Pass the accessor to `isPending(user)`, not `isPending(user())`. Pass a function to `render`/`hydrate`: `render(() => <App />, root)`. Component teardown is `onSettled`, not `onCleanup`. One router instance; in-app navigation is `useNavigate` / `<a href>`, not `window.location`.
8
+ - Solid 2.0 shipped recently and breaks with 1.x — never trust pre-2.0 Solid docs, tutorials, or training knowledge. Verify APIs against https://v2-rebuild--solid-docs-v2.netlify.app/llms.txt (markdown mirror of https://v2.solidjs.com/, which blocks non-browser fetchers). If the project has `@solidjs/router`, server functions, or `@solidjs/meta`, use those official pages (see the skill's official-docs index) — not React Router, Next.js, SolidStart 1.x, or Solid Router 0.x/1.x JSX `<Route>`/`<A>`. Core `action` from `solid-js` is a generator transaction; router `action`/`query` from `@solidjs/router` are POST forms and a read cache. Core `refresh(source)`, router `revalidate(key)`, and server-function `return reload(...)` are three different APIs. Pass the accessor to `isPending(user)`, not `isPending(user())`. Pass a function to `render`/`hydrate`: `render(() => <App />, root)`. Component teardown is `onSettled`, not `onCleanup`. One router instance; in-app navigation is `useNavigate` / `<a href>`, not `window.location`. Types: `solid-js` exports no `JSX` namespace — children/return types are `Element` from `solid-js`; DOM `JSX` types come from `@solidjs/web`. Reset-a-signal-on-change is a writable derivation (`createSignal(() => { source(); return initial; })`), never an effect calling the setter. Client mode mounts with `render()` into an empty body — no hydration mismatch exists, so never defer initial values to post-mount React-style (that can overwrite persisted state).
@@ -25,7 +25,13 @@ Claude Code) for full patterns, decision tables, and official documentation URLs
25
25
  (the function itself) into JSX and call it there.
26
26
  3. Signals are read by **calling** them: `{count()}` in JSX, not `{count}`.
27
27
  4. **Derived state is a function, not state + effect.** `const full = () => a() + b()`.
28
- Never write "a signal plus an effect that syncs it". Use `createMemo` only for expensive
28
+ Never write "a signal plus an effect that syncs it". That includes **reset**: to clear
29
+ a signal when another reactive value changes, use a writable derivation —
30
+ `createSignal(() => { source(); return initial; })` (re-runs when `source()` changes;
31
+ the setter still works) — never a `createEffect` whose apply calls the setter. The
32
+ derivation also moves the `source()` read to the read site, under its boundaries.
33
+ Mechanically enforced by `solid2-kit check` (effects whose apply only writes a local
34
+ signal). Use `createMemo` only for expensive
29
35
  computations, multiple consumers, or an equality boundary. When several writes can land
30
36
  before a flush, use the setter updater (`setCount((c) => c + 1)`); `setCount(count() + 1)`
31
37
  reads the last *committed* value and drops the other staged writes. To store a *function*
@@ -175,7 +181,12 @@ Claude Code) for full patterns, decision tables, and official documentation URLs
175
181
  visible content — `clientOnly` is for components that must never run on the server.
176
182
  Pass a **function** to `render` / `hydrate` / `renderToString` / `renderToStream`
177
183
  (`render(() => <App />, root)`), never `render(<App />, root)`. `hydrate` when the
178
- container already holds SSR HTML; `render` for an empty mount. `renderToString`
184
+ container already holds SSR HTML; `render` for an empty mount. Client mode (the
185
+ `@solidjs/vite-plugin` default) mounts with `render()` into an empty body — there is
186
+ **no server HTML and no hydration mismatch**, so React's mismatch defenses
187
+ ("server-safe" initial values adopted after mount, two-pass rendering) are unnecessary
188
+ and can clobber persisted state; read `localStorage` / `matchMedia` directly when
189
+ creating the signal. `renderToString`
179
190
  emits `<Loading>` fallbacks; async or lazy-route trees need
180
191
  `await renderToStream(() => <App />)` (one consumer: `pipe` / `pipeTo` / `readable`).
181
192
  When the app owns the document, emit `<HydrationScript />` once before app markup;
@@ -256,6 +267,7 @@ directory (default `src/`).
256
267
  | Never write (Solid 1.x) | Write instead (Solid 2) |
257
268
  |---|---|
258
269
  | `import ... from "solid-js/store"` or `"solid-js/web"` | stores/`merge`/`omit` from `"solid-js"`; `render`/`hydrate`/`Portal`/`Dynamic` from `"@solidjs/web"` |
270
+ | `import type { JSX } from "solid-js"` / `JSX.Element` as the children type | `solid-js` exports no `JSX` namespace: children/returns are `Element` from `"solid-js"`; DOM-specific `JSX` types (`JSX.IntrinsicElements`, `JSX.CSSProperties`) from `"@solidjs/web"` |
259
271
  | `createResource` | async `createMemo` + `<Loading>`/`<Errored>`; `refresh()`, `latest()`, `isPending()` |
260
272
  | `createEffect(fn)` (one arg), `on(...)` | `createEffect(compute, apply)`; deps belong in `compute` |
261
273
  | `onMount` | `onSettled` (return cleanup from its callback) |
@@ -55,6 +55,7 @@ Most React `useEffect` code should NOT become `createEffect`:
55
55
  | Fetch data in an effect, copy into state | `createMemo(async () => ...)` + `<Loading>` |
56
56
  | React to a user interaction | do the work in the event handler or an `action` |
57
57
  | Sync one state into another | delete the second state; derive it |
58
+ | Reset/clear state when another reactive value changes | writable derivation: `createSignal(() => { source(); return initial; })` — not an effect calling the setter |
58
59
  | Push a settled reactive value into a non-Solid system (DOM API, third-party widget, analytics, subscription) | `createEffect(compute, apply)` — this is the only real use |
59
60
  | One-time setup after mount (`onMount`) | `onSettled(() => { ...; return cleanup })` |
60
61
  | Measure DOM / observe size after paint | ref **directive factory** + `onSettled` (writes a *new* input; not an effect that copies state) |
@@ -222,6 +223,28 @@ Reads inside `apply` do not track. Extract every needed reactive value in `compu
222
223
  and reading `user.name` there *runs once* and never retriggers. Compute-phase errors can be
223
224
  intercepted with the bundle form `createEffect(compute, { effect, error })`.
224
225
 
226
+ A two-phase effect whose apply only calls a local signal setter is "state + effect" in
227
+ disguise — formally legal Solid 2, still rule 4 (`solid2-kit check` flags it). The reset
228
+ form of a writable derivation replaces it, and moves the source read to the read site
229
+ (under that site's boundaries) instead of an effect compute:
230
+
231
+ ```tsx
232
+ // WRONG — signal synced by an effect; organizer() errors bypass the read site's <Errored>
233
+ const [signInError, setSignInError] = createSignal<string | null>(null);
234
+ createEffect(
235
+ () => organizer(),
236
+ (id) => {
237
+ if (id !== null) setSignInError(null);
238
+ },
239
+ );
240
+
241
+ // CORRECT — writable derivation: resets when organizer() changes; the setter still works
242
+ const [signInError, setSignInError] = createSignal<string | null>(() => {
243
+ organizer();
244
+ return null;
245
+ });
246
+ ```
247
+
225
248
  ### Async data
226
249
 
227
250
  ```tsx
@@ -533,6 +556,20 @@ To turn those dev diagnostics into a hard gate, patch `console.warn` in the test
533
556
  file to rethrow unexpected warnings — then any top-level reactive read or unowned write
534
557
  an agent sneaks in fails the suite instead of scrolling by.
535
558
 
559
+ ## Client mode: no server HTML, no hydration reflexes
560
+
561
+ The `@solidjs/vite-plugin` default (client mode, the `bare` tier) serves an empty-body
562
+ shell and mounts with `render()`. Nothing is server-rendered and nothing hydrates, so
563
+ **React's hydration-mismatch playbook does not transfer**: do not start signals with a
564
+ "server-safe" initial value and adopt `localStorage` / `matchMedia` after mount, do not
565
+ two-pass render, and there is no `suppressHydrationWarning`. Read client sources directly
566
+ when creating state — `createSignal(storedPreference())` is correct in client mode. A
567
+ deferred "safe" initial value is worse than unnecessary: if anything persists the value
568
+ (an effect writing `localStorage`), the placeholder **overwrites the user's stored
569
+ preference** before the real value is adopted. Mismatch only becomes a concept in
570
+ start/SSR mode, where `hydrate()` mounts onto server HTML — and the Solid answer there is
571
+ `isServer` / `clientOnly`, still not React-style deferred adoption.
572
+
536
573
  ## App stack (only if the project has these packages)
537
574
 
538
575
  This kit's always-applied rules cover `solid-js` + `@solidjs/web` TSX. Official
@@ -547,8 +584,12 @@ Solid Router 0.x/1.x stand-ins.** Patterns below; fetch the matching pages from
547
584
  { "compilerOptions": { "jsx": "preserve", "jsxImportSource": "@solidjs/web" } }
548
585
  ```
549
586
 
550
- Import DOM `JSX` types from `@solidjs/web`; renderer-neutral `Component` /
551
- `ParentProps` from `solid-js`. Vite: `import solid from "@solidjs/vite-plugin"`
587
+ `solid-js` does **not** export a `JSX` namespace — `import type { JSX } from "solid-js"`
588
+ is a type error (TS2305), and `JSX.Element` as the children type is a React reflex.
589
+ Children/return types are the renderer-neutral `Element` from `solid-js`
590
+ (`children?: Element`), alongside `Component` / `ParentProps` / `Accessor` / `Setter`.
591
+ DOM-specific `JSX` types (`JSX.IntrinsicElements`, `JSX.CSSProperties`) and
592
+ `ComponentProps` come from `@solidjs/web`. Vite: `import solid from "@solidjs/vite-plugin"`
552
593
  with `start: true` / `ssr: true` / `serverFunctions: true` only when the
553
594
  project already uses start mode. Tiers are `bare` → `basic` (router) →
554
595
  `fullstack` (SSR + server functions); do not jump a tier without cause.
@@ -794,6 +835,8 @@ how to reuse. Prefer the form on the right.
794
835
  | `createMemo(() => { void save(); return x(); })` / an effect that calls an action | invoke actions from handlers, not from memos or effects |
795
836
  | `createSignal(props.value)` / `createStore(props.items)` | `createSignal(() => props.value)` / `createStore(() => props.items, fallback)` — a bare `props.x` at setup is a snapshot |
796
837
  | `createRenderEffect` / `createTrackedEffect` as the default effect | two-phase `createEffect(compute, apply)` |
838
+ | `createEffect(() => source(), () => setError(null))` to reset a signal | `createSignal(() => { source(); return null; })` — the writable derivation resets on change; a sole-setter apply is rule 4 |
839
+ | a "server-safe" initial value adopted after mount (React hydration-mismatch fix) | client mode mounts with `render()` into an empty body — no hydration, no mismatch; read `localStorage` / `matchMedia` directly at signal creation |
797
840
 
798
841
  ```tsx
799
842
  // WRONG — post-await read never subscribes; production can hang pending
@@ -913,7 +956,9 @@ always-applied rules installed alongside this skill.
913
956
  - [ ] No props destructuring; no `props.` at component-body top level.
914
957
  - [ ] Every reactive read (`signal()`, `props.x`, `store.x`) sits in JSX, a memo, an effect
915
958
  compute, or a boundary — not the component body.
916
- - [ ] No signal-synced-by-effect; derived values are functions or memos.
959
+ - [ ] No signal-synced-by-effect; derived values are functions or memos. Reset-on-change
960
+ is a writable derivation (`createSignal(() => { source(); return initial; })`), not
961
+ an effect calling the setter.
917
962
  - [ ] Event handlers wrap setters (`onClick={() => setX(...)}`), use `currentTarget`,
918
963
  and `onInput` for keystrokes. `innerHTML` is sanitized and not mixed with children.
919
964
  - [ ] Async memos are passed as accessors (`user={user}`), not `user={user()}`, so
@@ -933,6 +978,8 @@ always-applied rules installed alongside this skill.
933
978
  - [ ] Rest props via `omit`, not `{ ...props }`. Conditional `class` via objects, not string concat.
934
979
  - [ ] Input filters write back to the DOM on reject (inputs do not rewind).
935
980
  - [ ] No Solid 1.x imports or APIs (`solid-js/store`, `createResource`, `onMount`, `Suspense`, ...).
981
+ No `JSX` imported from `solid-js` — children/return types are `Element` from
982
+ `solid-js`; DOM `JSX` types come from `@solidjs/web`.
936
983
  - [ ] Inspect children with `children()`; code-split with `lazy` + `<Loading>`; reactive
937
984
  component choice with `dynamic()`. No `React.lazy`, no effects inside ref callbacks.
938
985
  - [ ] Browser-only code uses `isServer` / `clientOnly`, not `typeof window`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "solid2-agent-kit",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "Teach AI coding agents (Cursor, Claude Code) to write correct Solid 2.0 instead of React or Solid 1.x — always-applied rules, an agent skill, and a mechanical pattern gate.",
5
5
  "type": "module",
6
6
  "license": "MIT",