@mrciphersmith/keryx 0.2.164 → 0.3.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.
Files changed (100) hide show
  1. package/dist/cli.js +85355 -56749
  2. package/dist/core.js +28605 -18901
  3. package/package.json +2 -2
  4. package/src/gdgraph/affected-report.ts +141 -0
  5. package/src/gdgraph/build.ts +170 -23
  6. package/src/gdgraph/service.ts +6 -0
  7. package/src/gdgraph/staleness.ts +253 -45
  8. package/src/gdskills/bundled/agents/codebase-navigator.md +55 -0
  9. package/src/gdskills/bundled/agents/design-advisor.md +64 -0
  10. package/src/gdskills/bundled/agents/docs-maintainer.md +56 -0
  11. package/src/gdskills/bundled/agents/end-to-end-tester.md +56 -0
  12. package/src/gdskills/bundled/agents/error-path-auditor.md +57 -0
  13. package/src/gdskills/bundled/agents/go-build-fixer.md +52 -0
  14. package/src/gdskills/bundled/agents/go-code-auditor.md +49 -0
  15. package/src/gdskills/bundled/agents/performance-auditor.md +63 -0
  16. package/src/gdskills/bundled/agents/python-build-fixer.md +52 -0
  17. package/src/gdskills/bundled/agents/python-code-auditor.md +49 -0
  18. package/src/gdskills/bundled/agents/refactoring-steward.md +61 -0
  19. package/src/gdskills/bundled/agents/security-auditor.md +62 -0
  20. package/src/gdskills/bundled/agents/test-first-driver.md +61 -0
  21. package/src/gdskills/bundled/agents/work-planner.md +62 -0
  22. package/src/gdskills/bundled/install-manifest.json +530 -0
  23. package/src/gdskills/bundled/rules/core/skill-lifecycle.mdc +29 -1
  24. package/src/gdskills/bundled/rules/core/skills-storage-workflow.mdc +2 -2
  25. package/src/gdskills/bundled/skills/review/code-style-review/SKILL.md +1 -1
  26. package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.md +74 -246
  27. package/src/gdskills/bundled/skills/review/review-orchestrator/output-contract.schema.json +19 -0
  28. package/src/gdskills/bundled/skills/review/review-orchestrator/reviewer-finding.schema.json +10 -0
  29. package/src/gdskills/bundled/skills/review/review-orchestrator/reviewer-input.schema.json +5 -0
  30. package/src/gdskills/bundled/skills/review/review-orchestrator/templates/pr-comment-backend.md +50 -0
  31. package/src/gdskills/bundled/skills/review/review-orchestrator/templates/pr-comment-frontend.md +52 -0
  32. package/src/gdskills/bundled/skills/review/review-orchestrator/templates/review-report.md +143 -0
  33. package/src/gdskills/bundled/stacks/go/agent-refs.json +3 -0
  34. package/src/gdskills/bundled/stacks/go/governance/eval.json +1745 -0
  35. package/src/gdskills/bundled/stacks/go/governance/scout.json +31 -0
  36. package/src/gdskills/bundled/stacks/go/pack.json +41 -0
  37. package/src/gdskills/bundled/stacks/go/rules/coding-style.mdc +85 -0
  38. package/src/gdskills/bundled/stacks/go/rules/patterns.mdc +65 -0
  39. package/src/gdskills/bundled/stacks/go/rules/security.mdc +73 -0
  40. package/src/gdskills/bundled/stacks/go/rules/testing.mdc +68 -0
  41. package/src/gdskills/bundled/stacks/go/skills/go-build-fix/SKILL.md +138 -0
  42. package/src/gdskills/bundled/stacks/go/skills/go-build-fix/evals.json +75 -0
  43. package/src/gdskills/bundled/stacks/go/skills/go-code-review/SKILL.md +121 -0
  44. package/src/gdskills/bundled/stacks/go/skills/go-code-review/evals.json +72 -0
  45. package/src/gdskills/bundled/stacks/go/skills/go-implementation/SKILL.md +122 -0
  46. package/src/gdskills/bundled/stacks/go/skills/go-implementation/evals.json +76 -0
  47. package/src/gdskills/bundled/stacks/go/skills/go-testing/SKILL.md +126 -0
  48. package/src/gdskills/bundled/stacks/go/skills/go-testing/evals.json +73 -0
  49. package/src/gdskills/bundled/stacks/python/agent-refs.json +3 -0
  50. package/src/gdskills/bundled/stacks/python/governance/eval.json +1758 -0
  51. package/src/gdskills/bundled/stacks/python/governance/scout.json +34 -0
  52. package/src/gdskills/bundled/stacks/python/pack.json +41 -0
  53. package/src/gdskills/bundled/stacks/python/rules/coding-style.mdc +63 -0
  54. package/src/gdskills/bundled/stacks/python/rules/patterns.mdc +88 -0
  55. package/src/gdskills/bundled/stacks/python/rules/security.mdc +84 -0
  56. package/src/gdskills/bundled/stacks/python/rules/testing.mdc +77 -0
  57. package/src/gdskills/bundled/stacks/python/skills/python-build-fix/SKILL.md +144 -0
  58. package/src/gdskills/bundled/stacks/python/skills/python-build-fix/evals.json +74 -0
  59. package/src/gdskills/bundled/stacks/python/skills/python-code-review/SKILL.md +155 -0
  60. package/src/gdskills/bundled/stacks/python/skills/python-code-review/evals.json +72 -0
  61. package/src/gdskills/bundled/stacks/python/skills/python-implementation/SKILL.md +143 -0
  62. package/src/gdskills/bundled/stacks/python/skills/python-implementation/evals.json +78 -0
  63. package/src/gdskills/bundled/stacks/python/skills/python-testing/SKILL.md +132 -0
  64. package/src/gdskills/bundled/stacks/python/skills/python-testing/evals.json +73 -0
  65. package/src/gdskills/bundled/stacks/react/agent-refs.json +4 -0
  66. package/src/gdskills/bundled/stacks/react/governance/eval.json +2188 -0
  67. package/src/gdskills/bundled/stacks/react/governance/scout.json +40 -0
  68. package/src/gdskills/bundled/stacks/react/pack.json +42 -0
  69. package/src/gdskills/bundled/stacks/react/rules/coding-style.mdc +58 -0
  70. package/src/gdskills/bundled/stacks/react/rules/patterns.mdc +79 -0
  71. package/src/gdskills/bundled/stacks/react/rules/security.mdc +70 -0
  72. package/src/gdskills/bundled/stacks/react/rules/testing.mdc +60 -0
  73. package/src/gdskills/bundled/stacks/react/skills/react-build-fix/SKILL.md +139 -0
  74. package/src/gdskills/bundled/stacks/react/skills/react-build-fix/evals.json +72 -0
  75. package/src/gdskills/bundled/stacks/react/skills/react-code-review/SKILL.md +148 -0
  76. package/src/gdskills/bundled/stacks/react/skills/react-code-review/evals.json +74 -0
  77. package/src/gdskills/bundled/stacks/react/skills/react-implementation/SKILL.md +140 -0
  78. package/src/gdskills/bundled/stacks/react/skills/react-implementation/evals.json +74 -0
  79. package/src/gdskills/bundled/stacks/react/skills/react-testing/SKILL.md +142 -0
  80. package/src/gdskills/bundled/stacks/react/skills/react-testing/evals.json +83 -0
  81. package/src/gdskills/bundled/stacks/react/skills/react-upgrade-migration/SKILL.md +155 -0
  82. package/src/gdskills/bundled/stacks/react/skills/react-upgrade-migration/evals.json +74 -0
  83. package/src/gdskills/bundled/stacks/ts-js-node/agent-refs.json +4 -0
  84. package/src/gdskills/bundled/stacks/ts-js-node/governance/eval.json +2155 -0
  85. package/src/gdskills/bundled/stacks/ts-js-node/governance/scout.json +40 -0
  86. package/src/gdskills/bundled/stacks/ts-js-node/pack.json +41 -0
  87. package/src/gdskills/bundled/stacks/ts-js-node/rules/coding-style.mdc +73 -0
  88. package/src/gdskills/bundled/stacks/ts-js-node/rules/patterns.mdc +61 -0
  89. package/src/gdskills/bundled/stacks/ts-js-node/rules/security.mdc +71 -0
  90. package/src/gdskills/bundled/stacks/ts-js-node/rules/testing.mdc +63 -0
  91. package/src/gdskills/bundled/stacks/ts-js-node/skills/nodejs-build-fix/SKILL.md +137 -0
  92. package/src/gdskills/bundled/stacks/ts-js-node/skills/nodejs-build-fix/evals.json +73 -0
  93. package/src/gdskills/bundled/stacks/ts-js-node/skills/nodejs-code-review/SKILL.md +124 -0
  94. package/src/gdskills/bundled/stacks/ts-js-node/skills/nodejs-code-review/evals.json +74 -0
  95. package/src/gdskills/bundled/stacks/ts-js-node/skills/nodejs-esm-migration/SKILL.md +152 -0
  96. package/src/gdskills/bundled/stacks/ts-js-node/skills/nodejs-esm-migration/evals.json +71 -0
  97. package/src/gdskills/bundled/stacks/ts-js-node/skills/nodejs-implementation/SKILL.md +127 -0
  98. package/src/gdskills/bundled/stacks/ts-js-node/skills/nodejs-implementation/evals.json +72 -0
  99. package/src/gdskills/bundled/stacks/ts-js-node/skills/nodejs-testing/SKILL.md +134 -0
  100. package/src/gdskills/bundled/stacks/ts-js-node/skills/nodejs-testing/evals.json +70 -0
@@ -0,0 +1,40 @@
1
+ [
2
+ {
3
+ "query": "Use when building or changing a React component, hook, or form in a .tsx/.jsx file -- covers function components, deriving state instead of syncing it with effects, Suspense and error boundaries, form Actions (useActionState, useOptimistic, useTransition), ref-as-prop, and React Compiler-aware memoization.",
4
+ "decision": "fork",
5
+ "topMatch": "react/react-build-fix",
6
+ "recordedAt": "2026-09-24T12:00:07.391Z",
7
+ "skillName": "react-implementation",
8
+ "justification": "top match react/react-build-fix (0.32) is a build/lint/type-error fix skill for already-broken code, not an authoring workflow for new component/hook code; react-code-review (0.24) is read-only review; react-testing (0.18) is test authoring, not component authoring. Below use threshold 0.55; none is a substitute for a React component-authoring skill, so create."
9
+ },
10
+ {
11
+ "query": "Use when a React component's test suite needs writing, extending, or fixing with React Testing Library -- covers query-by-role/label, user-event interaction, async findBy/waitFor, network mocking at the request boundary, and act warnings, on top of the project's Vitest or Jest runner.",
12
+ "decision": "fork",
13
+ "topMatch": "ts-js-node/nodejs-testing",
14
+ "recordedAt": "2026-09-24T12:00:14.111Z",
15
+ "skillName": "react-testing",
16
+ "justification": "top match ts-js-node/nodejs-testing (0.46) is the generic TS/Node testing skill with no React component, JSX rendering, or React Testing Library query/interaction guidance; review-testing-practices (0.29) is read-only review, not authoring. Below use threshold 0.55; neither is a substitute for a React-specific RTL authoring skill, so create."
17
+ },
18
+ {
19
+ "query": "Use when reviewing changed React component or hook code (.tsx/.jsx) for Rules of Hooks violations, effect dependency bugs, stale closures, key misuse, in-place state mutation, re-render hot spots, accessibility gaps, and dangerouslySetInnerHTML/URL XSS sinks -- read-only, no MobX store review and no repository convention-doc lookup.",
20
+ "decision": "create",
21
+ "topMatch": "react/react-implementation",
22
+ "recordedAt": "2026-09-24T12:00:23.775Z",
23
+ "skillName": "react-code-review"
24
+ },
25
+ {
26
+ "query": "Use when a React component fails to type-check, lint, or build -- JSX/TSX prop and children typing errors, event handler and ref types after the React 19 types changes, eslint-plugin-react-hooks failures, and Vite/webpack/Next.js bundler build failures or hydration mismatches. Fixes the root cause, never disables the hooks lint rule or casts to any.",
27
+ "decision": "fork",
28
+ "topMatch": "ts-js-node/nodejs-build-fix",
29
+ "recordedAt": "2026-09-24T12:00:23.917Z",
30
+ "skillName": "react-build-fix",
31
+ "justification": "top match ts-js-node/nodejs-build-fix (0.32) is the generic TS/Node build-fix skill with no JSX/TSX prop typing, react-hooks lint, or hydration-mismatch guidance; react-implementation (0.25) is an authoring skill, not a fix skill. Below use threshold 0.55; neither is a substitute for a React-specific build-fix skill, so create."
32
+ },
33
+ {
34
+ "query": "Use when upgrading a codebase across React major versions (e.g. 18 to 19) -- removing legacy APIs (string refs, legacy context, propTypes/defaultProps on function components, ReactDOM.render/hydrate), applying official codemods, updating the @types/react package, fixing the act import, and staging the rollout.",
35
+ "decision": "create",
36
+ "topMatch": "react/react-build-fix",
37
+ "recordedAt": "2026-09-24T12:00:24.073Z",
38
+ "skillName": "react-upgrade-migration"
39
+ }
40
+ ]
@@ -0,0 +1,42 @@
1
+ {
2
+ "id": "react",
3
+ "family": "framework",
4
+ "extends": "ts-js-node",
5
+ "modules": ["react-rules", "react-skills"],
6
+ "detectionMarkers": ["react"],
7
+ "provenance": {
8
+ "origin": "authored",
9
+ "sourceRef": "flow 314, Wave 4 batch 1"
10
+ },
11
+ "stability": "experimental",
12
+ "skills": {
13
+ "implement": ["react-implementation"],
14
+ "test": ["react-testing"],
15
+ "review": ["react-code-review"],
16
+ "build-fix": ["react-build-fix"],
17
+ "migrate": ["react-upgrade-migration"]
18
+ },
19
+ "agentProfile": {
20
+ "displayName": "React",
21
+ "auditFocus": [
22
+ "Rules of Hooks violations and conditional/loop-nested hook calls",
23
+ "effect dependency arrays that are wrong, missing, or paper over a needed effect",
24
+ "state or props mutated in place instead of replaced",
25
+ "keys on list items that are array index or missing where order can change",
26
+ "accessibility of interactive elements: semantic tags, labels, focus management",
27
+ "dangerouslySetInnerHTML or unsanitized user data reaching a DOM sink"
28
+ ],
29
+ "buildCommands": [
30
+ "the project's type-check script (e.g. tsc --noEmit, or its package.json equivalent)",
31
+ "the project's lint script with the react-hooks plugin enabled",
32
+ "the project's test script (Vitest/Jest run of React Testing Library suites)",
33
+ "the project's bundler build (Vite/webpack/Next.js production build)"
34
+ ],
35
+ "fixGuardrails": [
36
+ "never disable or downgrade an eslint-plugin-react-hooks rule to silence a warning",
37
+ "never cast a component's props, ref, or event handler to any to satisfy the type checker",
38
+ "never delete or loosen a failing assertion in a React Testing Library test to make it pass",
39
+ "never wrap a component in unnecessary memo/useMemo/useCallback as a build-fix when the project runs the React Compiler"
40
+ ]
41
+ }
42
+ }
@@ -0,0 +1,58 @@
1
+ ---
2
+ extends: common
3
+ paths: ["**/*.tsx", "**/*.jsx"]
4
+ metadata:
5
+ origin: authored
6
+ ---
7
+
8
+ # React coding style
9
+
10
+ Narrows `ts-js-node`'s coding style to React component files. Applies only
11
+ to `*.tsx`/`*.jsx` — non-component TypeScript/JavaScript still
12
+ comes from `ts-js-node`'s own coding-style rule, not this file.
13
+
14
+ ## Components
15
+
16
+ - Write function components; a class component is a maintenance
17
+ regression unless the project has no alternative (e.g. an error boundary,
18
+ which still requires a class in current React).
19
+ - One component per file for anything exported and reused; a small private
20
+ helper component used only inside its parent file can stay local.
21
+ - Name a component file after the component it exports (`UserCard.tsx`
22
+ exports `UserCard`), `PascalCase` for the component, `camelCase` for
23
+ hooks (`useUserCard`) and plain functions.
24
+ - Destructure props in the function signature; do not thread a whole
25
+ `props` object through the body when only a few fields are used.
26
+ - Type props with an explicit `interface`/`type`, not `React.FC` — `FC`'s
27
+ implicit `children` and weaker generic inference are a known footgun the
28
+ current React types intentionally do not paper over.
29
+
30
+ ## Hooks and state
31
+
32
+ - Call hooks only at the top level of a component or another hook, never
33
+ inside a condition, loop, or nested function — the react-hooks lint rule
34
+ enforces this; do not add an eslint-disable line to route around it (see
35
+ `rules/security.mdc` and `react-build-fix`'s guardrails).
36
+ - Order hooks so the same hooks run in the same order on every render;
37
+ branch inside a hook's own logic (`if (x) return early` after the hook
38
+ call), not around the hook call itself.
39
+ - Prefer several focused `useState` calls over one large state object when
40
+ the fields update independently; group into one state (or `useReducer`)
41
+ only when fields change together.
42
+ - Name a custom hook's return values by what they mean in the caller
43
+ (`{ data, isLoading, error }`), not by the hook's internal variable
44
+ names.
45
+
46
+ ## Imports and structure
47
+
48
+ - Group imports: React/framework, third-party, then local — the same
49
+ ordering `ts-js-node`'s coding-style rule already asks for; nothing
50
+ React-specific changes the grouping itself.
51
+ - Import hooks and utilities by name (`import { useState } from "react"`);
52
+ do not import the whole `React` namespace unless the project's JSX
53
+ transform requires it (check the project's `tsconfig`/bundler config —
54
+ the classic transform needs `import React from "react"` in scope, the
55
+ automatic transform introduced in React 17 does not).
56
+ - Co-locate a component's own styles, tests, and small subcomponents next
57
+ to it under one directory when the project already uses that layout;
58
+ match the existing convention rather than introducing a new one.
@@ -0,0 +1,79 @@
1
+ ---
2
+ extends: common
3
+ paths: ["**/*.tsx", "**/*.jsx"]
4
+ metadata:
5
+ origin: authored
6
+ ---
7
+
8
+ # React patterns
9
+
10
+ Idiomatic component design and anti-patterns for React 19-era code,
11
+ narrowed to `*.tsx`/`*.jsx`.
12
+
13
+ ## Derive, don't sync
14
+
15
+ - If a value can be computed from existing props/state during render,
16
+ compute it during render — do not `useState` + `useEffect` to mirror one
17
+ piece of state into another. A `useEffect` that only calls `setState`
18
+ from a change in props/state is almost always replaceable by a plain
19
+ computed value (optionally memoized with `useMemo` when the computation
20
+ is expensive).
21
+ - Reset local state on a prop change by giving the component a `key` tied
22
+ to that prop (React unmounts and remounts a fresh component), not by
23
+ watching the prop in an effect and calling a setter.
24
+ - Effects are for synchronizing with something outside React: the DOM,
25
+ a subscription, a network request, browser storage, a third-party widget.
26
+ An effect with no external system on the other end is a sign the logic
27
+ belongs in an event handler or in render itself.
28
+
29
+ ## Lists and keys
30
+
31
+ - Every element in a list rendered with `.map` gets a stable `key` drawn
32
+ from the data (an id), never the array index, unless the list is
33
+ provably static and never reordered/filtered/inserted into.
34
+ - Do not use `key` as a prop for anything other than list identity inside
35
+ the component receiving it — reading `props.key` returns `undefined`.
36
+
37
+ ## Forms and events
38
+
39
+ - Prefer a controlled input (`value` + `onChange`) for anything the
40
+ component needs to validate, transform, or react to as the user types;
41
+ use an uncontrolled input (`defaultValue` + a ref) only for simple
42
+ fire-and-forget fields where per-keystroke reactivity is not needed.
43
+ - For form submission with a pending/error/optimistic result, prefer an
44
+ Action (a function passed to a `<form action={...}>`,
45
+ `useActionState`, and `useOptimistic` for the optimistic update) over
46
+ hand-rolled `isSubmitting`/`error` state plus a submit handler that calls
47
+ `preventDefault` — the Actions API already tracks pending and error state
48
+ and integrates with Suspense.
49
+ - Reach for `useTransition` to mark a state update as non-urgent (keeps the
50
+ UI responsive during an expensive re-render) rather than debouncing or
51
+ deferring the update by hand.
52
+
53
+ ## Refs, Suspense, and composition
54
+
55
+ - In React 19, `ref` is an ordinary prop on function components — no
56
+ `forwardRef` wrapper is needed for a new component that accepts a ref.
57
+ Keep `forwardRef` only in code that must still support pre-19 React.
58
+ - Read an async resource inside render with the `use` API (for a promise
59
+ or a context) instead of stuffing the result into state via an effect;
60
+ pair it with a `Suspense` boundary for the pending state and an error
61
+ boundary for the rejected state.
62
+ - Prefer composition (children, render props, or a small set of focused
63
+ components) over one component branching on a `type`/`variant` prop into
64
+ unrelated render trees.
65
+ - In a framework that supports server and client components, keep data
66
+ fetching and secrets in server components; a component needs
67
+ `"use client"` only once it uses state, effects, refs, or browser-only
68
+ APIs — do not mark a whole subtree client just because one leaf needs it.
69
+
70
+ ## Compiler awareness
71
+
72
+ - When the project has the React Compiler enabled (check its babel/swc
73
+ config or `babel-plugin-react-compiler`/`react-compiler` dependency),
74
+ do not hand-add `memo`, `useMemo`, or `useCallback` purely to prevent
75
+ re-renders — the compiler already memoizes automatically, and manual
76
+ memoization on compiled code is dead weight that can also mask a bug the
77
+ compiler would otherwise have caught. Without the compiler, manual
78
+ memoization is still a valid, deliberate optimization for a measured
79
+ re-render cost.
@@ -0,0 +1,70 @@
1
+ ---
2
+ extends: common
3
+ paths: ["**/*.tsx", "**/*.jsx"]
4
+ metadata:
5
+ origin: authored
6
+ ---
7
+
8
+ # React security
9
+
10
+ Stack-specific risks for React component files (`*.tsx`/`*.jsx`),
11
+ narrowing `ts-js-node`'s generic security rule. Not a general OWASP list —
12
+ only the sinks React's rendering model makes reachable.
13
+
14
+ ## `dangerouslySetInnerHTML`
15
+
16
+ - Treat `dangerouslySetInnerHTML` as the default XSS sink: any user-, API-,
17
+ or CMS-sourced string passed to it renders as live HTML/JS. Avoid it —
18
+ render the content as text/JSX instead — unless the project genuinely
19
+ needs to render rich HTML (e.g. a CMS body).
20
+ - When it is unavoidable, sanitize the string through the project's own
21
+ sanitizer (e.g. DOMPurify) immediately before the prop is built, not
22
+ earlier in the pipeline where a later edit could drop the sanitize step;
23
+ never pass a raw string straight through.
24
+ - Never build the `__html` value by concatenating user input into a
25
+ template string of markup — that is the same sink with extra steps.
26
+
27
+ ## URLs from user data
28
+
29
+ - Reject or scheme-allowlist a URL that becomes an `href`/`src` when it
30
+ comes from user input, a query param, or an API response —
31
+ `javascript:`, `data:`, and `vbscript:` schemes execute on click/load.
32
+ Validate against an allowlist (`http:`, `https:`, `mailto:`) before
33
+ binding it to the element.
34
+ - The same applies to a dynamic `src` on `<img>`, `<iframe>`, or `<script>`
35
+ built from anything not fully controlled by the app.
36
+
37
+ ## Secrets and client bundles
38
+
39
+ - Anything read via the framework's public-env convention (e.g. a
40
+ `NEXT_PUBLIC_`/`VITE_`/`REACT_APP_`-prefixed variable, or any value the
41
+ bundler inlines into client output) ships in the browser bundle in plain
42
+ text — never put an API secret, private key, or unscoped token behind
43
+ one of these prefixes. A value that must stay server-only belongs in a
44
+ server component, a route handler/API route, or a server function/
45
+ action, never a client component or a plain (non-prefixed-and-still-
46
+ bundled) constant imported into client code.
47
+ - Grep a diff for the project's public-env prefix before approving a new
48
+ environment variable used inside a client component.
49
+ - A Server Function/Action is not merely a safe home for a server-only
50
+ value — calling it from the client turns it into a public, directly
51
+ reachable HTTP endpoint (its own route, invocable by anything that can
52
+ reach the app, not only through the form/button that references it in
53
+ the source). It gets none of the caller-identity or authorization
54
+ checks a UI-only flow implies; each Server Function/Action must
55
+ authenticate the caller, authorize the specific operation, and validate
56
+ every argument itself, exactly as a hand-written API route would,
57
+ before it touches data or a mutation.
58
+
59
+ ## Third-party scripts and client-side auth
60
+
61
+ - Loading a third-party script by injecting a `<script>` tag with a
62
+ dynamic or user-influenced `src` is the same class of risk as an
63
+ unvalidated `href`/`src` above; pin third-party script URLs to a fixed,
64
+ reviewed list.
65
+ - Do not gate a protected view purely on client-side state (a store flag,
66
+ a decoded-but-unverified JWT, a route guard component) — a client
67
+ component can always be bypassed by disabling JavaScript or calling the
68
+ underlying API directly. Client-side checks are UX only; the real
69
+ authorization check must happen server-side (server component, route
70
+ handler, or backend).
@@ -0,0 +1,60 @@
1
+ ---
2
+ extends: common
3
+ paths: ["**/*.tsx", "**/*.jsx"]
4
+ metadata:
5
+ origin: authored
6
+ ---
7
+
8
+ # React testing
9
+
10
+ Test layout, runner, and assertion conventions for React component files
11
+ (`*.tsx`/`*.jsx`), narrowing `ts-js-node`'s generic testing rule.
12
+
13
+ ## Layout and runner
14
+
15
+ - Use the project's already-configured runner (Vitest or Jest) and React
16
+ Testing Library (`@testing-library/react` + `@testing-library/user-event`)
17
+ — do not introduce Enzyme or a shallow-render helper into a project that
18
+ does not already use one.
19
+ - Co-locate a component's test next to it (`UserCard.test.tsx`) when the
20
+ project already follows that layout, or under a mirrored `__tests__`
21
+ tree when that is the existing convention; match, do not introduce.
22
+
23
+ ## Queries and interaction
24
+
25
+ - Query by role/label/text the way a user or assistive technology would
26
+ (`getByRole`, `getByLabelText`, `getByText`) — reach for `data-testid`
27
+ only when no accessible query exists, and treat that as a sign the
28
+ markup itself may be missing an accessible name.
29
+ - Never assert on a component's internal state, instance fields, or
30
+ implementation details (no reaching into a ref's internals, no snapshot
31
+ of a component's private props) — assert on rendered output and
32
+ behavior only.
33
+ - Drive interaction through `@testing-library/user-event`
34
+ (`userEvent.click`/`.type`/`.tab`), not `fireEvent`, unless the project
35
+ is pinned to an older `user-event` major that cannot express the
36
+ interaction — `user-event` fires the fuller sequence of browser events a
37
+ real interaction would.
38
+
39
+ ## Async and network
40
+
41
+ - Wait for async UI with `findBy*` queries or `waitFor`, never a manual
42
+ `setTimeout`/`sleep` — an arbitrary delay is both slow and flaky under
43
+ load.
44
+ - Mock network calls at the request boundary (an MSW handler, or the
45
+ project's existing equivalent) rather than mocking the component's own
46
+ data-fetching function — a boundary mock still exercises the real
47
+ fetch/parse/error-handling code path.
48
+ - Wrap a state update triggered outside React Testing Library's own query
49
+ helpers (e.g. a manually invoked callback) in `act` only when RTL's
50
+ built-in wrapping does not already cover it; an `act` warning is a
51
+ signal to await the right query, not to silence the console.
52
+
53
+ ## Coverage expectations
54
+
55
+ - Cover the rendered states a component can be in: initial/loading,
56
+ success, error, and empty, plus any conditional branch a prop or piece
57
+ of state gates.
58
+ - For a custom hook with logic worth testing on its own, use
59
+ `renderHook` from `@testing-library/react` rather than mounting a
60
+ throwaway component to exercise it.
@@ -0,0 +1,139 @@
1
+ ---
2
+ name: react-build-fix
3
+ description: "Use when a React component fails to type-check, lint, or build -- JSX/TSX prop and children typing errors, event handler and ref types after the React 19 types changes, eslint-plugin-react-hooks failures, and Vite/webpack/Next.js bundler build failures or hydration mismatches. Fixes the root cause, never disables the hooks lint rule or casts to any."
4
+ triggers:
5
+ - "fix this tsx type error"
6
+ - "react-hooks lint is failing"
7
+ - "vite build is failing on this component"
8
+ - "hydration mismatch error"
9
+ - "ref type error after upgrading react types"
10
+ - "children prop type error"
11
+ metadata:
12
+ origin: authored
13
+ category: build-fix
14
+ version: "1.0.0"
15
+ compatible_harnesses: "claude,codex,cursor,zed,opencode"
16
+ license: "MIT"
17
+ ---
18
+
19
+ # React build fix
20
+
21
+ Fix a failing type-check, lint, or bundler build caused by React component
22
+ code. Scoped to failures that originate in `.tsx`/`.jsx` component code or
23
+ its JSX/hooks typing — a failure with no React-specific cause (a plain
24
+ module import error, a Node config issue) is `ts-js-node`'s build-fix
25
+ skill, not this one.
26
+
27
+ ## Workflow
28
+
29
+ ### Step 1: Reproduce and classify
30
+
31
+ Run, in order, until one fails (use `agentProfile.buildCommands` from
32
+ `pack.json` as the starting point, adjusted to the project's actual
33
+ scripts):
34
+
35
+ 1. Type-check (`tsc --noEmit` or the project's script).
36
+ 2. Lint, with `eslint-plugin-react-hooks` enabled.
37
+ 3. Test run.
38
+ 4. Bundler build (Vite/webpack/Next.js).
39
+
40
+ Read the first failure's full message — do not guess from the file name.
41
+ Classify it as: JSX/TSX typing (props, children, events, refs), hooks-lint
42
+ (`react-hooks/rules-of-hooks`, `react-hooks/exhaustive-deps`), or bundler/
43
+ hydration.
44
+
45
+ ### Step 2: Fix JSX/TSX typing errors
46
+
47
+ - Props typing error: fix the `interface`/`type` to match how the
48
+ component is actually used, or fix the call site — do not widen the
49
+ prop type to `any`/`unknown` to make the error disappear.
50
+ - Children typing: type `children` explicitly (`React.ReactNode` for
51
+ arbitrary children, a narrower type when only specific children are
52
+ valid) rather than accepting `any`.
53
+ - Event handler typing: use the specific React event type for the element
54
+ and event (`React.ChangeEvent<HTMLInputElement>`,
55
+ `React.MouseEvent<HTMLButtonElement>`), not a hand-rolled loose type.
56
+ - Ref typing after the React 19 types change (`ref` as an ordinary prop
57
+ on function components): type it as
58
+ `React.Ref<ElementType>`/`React.RefObject<ElementType>` matching what
59
+ the component actually forwards; if the project is still on
60
+ `forwardRef`, match `forwardRef`'s own generic signature instead of
61
+ mixing the two patterns in one component.
62
+
63
+ ### Step 3: Fix hooks-lint errors
64
+
65
+ - `rules-of-hooks` violation: restructure so the hook call is
66
+ unconditional and top-level — move the condition inside the hook's own
67
+ logic, or split into two components/hooks. Never add an
68
+ `eslint-disable-next-line react-hooks/rules-of-hooks` comment.
69
+ - `exhaustive-deps` violation: add the missing reactive dependency, or, if
70
+ the value is intentionally stable (a ref, a dispatch function, a value
71
+ the author has confirmed never needs to trigger the effect), use the
72
+ lint rule's own escape hatch documented for that case rather than a
73
+ blanket disable comment — and only after confirming the value truly
74
+ cannot change in a way the effect needs to react to.
75
+
76
+ ### Step 4: Fix bundler and hydration failures
77
+
78
+ - Bundler build failure: read the actual bundler error (missing export,
79
+ bad dynamic import, unsupported syntax for the configured target) and
80
+ fix the source or import, not the bundler config, unless the config
81
+ itself is provably wrong for the project's stated target.
82
+ - Hydration mismatch: find what differs between server and client render
83
+ — a `Date`/`Math.random`/`typeof window` branch evaluated during render,
84
+ locale/timezone-dependent formatting, or state initialized differently
85
+ on each side. Fix by making the server and first client render produce
86
+ identical output (move the divergent value into an effect that runs
87
+ post-hydration, or pass it down as a prop already resolved server-side)
88
+ — do not silence the warning by wrapping the whole tree in a
89
+ client-only guard unless the divergent content is genuinely
90
+ client-only.
91
+
92
+ ### Step 5: Verify and report
93
+
94
+ ```bash
95
+ keryx test run --changed --strict
96
+ ```
97
+
98
+ Re-run the exact command that failed in Step 1 and confirm it now exits 0,
99
+ then re-run the full chain (type-check, lint, test, build) once to confirm
100
+ the fix did not break an earlier step.
101
+
102
+ ```
103
+ Fixed: src/components/UserCard.tsx:18 — onSave typed as
104
+ React.MouseEvent<HTMLButtonElement> to match the actual click handler
105
+ signature; tsc --noEmit now passes.
106
+ ```
107
+
108
+ ## Rules
109
+
110
+ - ALWAYS fix the root cause with the smallest change that resolves it.
111
+ - NEVER disable or downgrade `eslint-plugin-react-hooks` rules to make a
112
+ lint failure disappear.
113
+ - NEVER cast a prop, ref, or event to `any`/`as any` to silence a type
114
+ error.
115
+ - NEVER delete or loosen a failing test assertion to make a build/test
116
+ step pass.
117
+ - Stop after 3 fix iterations on the same failure and report what remains,
118
+ rather than escalating to a disable/cast/deletion.
119
+
120
+ ## Red Flags
121
+
122
+ | Rationalization | Why it is wrong |
123
+ |---|---|
124
+ | "I'll add eslint-disable-next-line to get lint green" | Silences a rule designed to catch a real class of runtime bug (stale closures, hook-order crashes); fix the dependency or hook placement instead |
125
+ | "This type error is annoying, I'll cast to any" | Removes the type check entirely at that point, including for the next person's edit; fix the actual type mismatch |
126
+ | "The hydration warning is harmless, I'll wrap everything in a client-only check" | Over-broad client-only guards flash empty/loading content for content that could have rendered on the server; find the actual server/client divergence first |
127
+ | "Test's failing after my fix, I'll just skip it" | Skipping hides a real regression the build-fix may have introduced; investigate before moving on |
128
+
129
+ ## Verification
130
+
131
+ Do not report the work done until all of the following hold:
132
+
133
+ - The exact command that failed in Step 1 now exits 0.
134
+ - The full chain (type-check, lint, test, build) still passes after the
135
+ fix — a fix to one step did not regress an earlier one.
136
+ - No `eslint-disable` comment was added for a hooks rule, and no `as any`/
137
+ `: any` was added to route around a type error.
138
+ - `git status` shows only the files whose failure was being fixed (plus
139
+ their types, if the fix was a type definition change).
@@ -0,0 +1,72 @@
1
+ {
2
+ "triggers": {
3
+ "positive": [
4
+ "This component's ref prop is throwing a TSX type error after the React 19 upgrade, fix it",
5
+ "eslint-plugin-react-hooks is failing on exhaustive-deps in this file",
6
+ "The Vite build fails with a JSX syntax error in this component",
7
+ "I'm getting a hydration mismatch warning on this page, fix it",
8
+ "Fix this children prop type error in the component",
9
+ "The tsc build fails because this event handler's type doesn't match onChange"
10
+ ],
11
+ "negative": [
12
+ "Write a new test for this component's error state",
13
+ "Review this component for Rules of Hooks violations",
14
+ "Migrate this codebase from React 18 to React 19",
15
+ "Build a new dashboard component with charts",
16
+ "Fix a Node.js module resolution error unrelated to any component",
17
+ "This component re-renders too often, help me optimize it"
18
+ ]
19
+ },
20
+ "scenarios": [
21
+ {
22
+ "id": "no-disable-hooks-lint",
23
+ "prompt": "react-hooks/exhaustive-deps is failing on my useEffect because it's missing a dependency. What should I do to fix the build?",
24
+ "strictness": "high",
25
+ "expected_behavior": [
26
+ {
27
+ "grader": "judge",
28
+ "rubric": "A correct answer fixes the exhaustive-deps failure by adding the missing reactive dependency to the effect's dependency array, or, if the value is genuinely stable (a ref, a dispatch function, a value confirmed never to change in a way the effect needs), uses the lint rule's own documented escape hatch after explaining why the value is stable. It treats the failure as a real staleness bug to fix, not noise to silence.",
29
+ "pass_criteria": [
30
+ "Names the specific missing dependency the effect reads and shows the corrected code — either the completed dependency array including it or a useCallback/useRef restructuring so the value no longer needs to be listed. When the prompt itself shows no code (as here), a concrete illustrative example of such a corrected effect/dependency array satisfies this requirement. A plain 'add the dependency' with no named value and no code shown does not satisfy this.",
31
+ "Explains the staleness consequence of the missing dependency (the effect would keep using an old/stale value from an earlier render instead of reacting to the current one), not just that the fix 'resolves the issue' or 'stops the build complaining'."
32
+ ],
33
+ "fail_criteria": [
34
+ "Recommends disabling or suppressing the exhaustive-deps rule (for example an eslint-disable-next-line comment) as the fix, instead of adding the missing dependency or justifying a stability-based exception; this includes mentioning eslint-disable only as a caveat or aside without a concrete named-dependency fix alongside it."
35
+ ]
36
+ }
37
+ ],
38
+ "calibration": {
39
+ "known_right": "Add the dependency the effect actually reads to its dependency array — that's what exhaustive-deps is flagging, and it's usually a real staleness bug, not a false positive. For example, if the effect fetches data using a prop:\n\nuseEffect(() => {\n fetchUser(userId);\n}, [userId]);\n\nWithout userId in the array, the effect only runs once and keeps using whatever userId was passed on the first render — if the prop changes later, the effect won't re-fire and fetchUser keeps being called with a stale id instead of the current one, which is exactly the staleness bug the rule exists to catch. If adding the value would cause the effect to re-run on every render because it's a function or object recreated each render, wrap that value in useCallback/useMemo in its own definition rather than dropping it from the array:\n\nconst fetchUser = useCallback((id: string) => { /* ... */ }, []);\nuseEffect(() => {\n fetchUser(userId);\n}, [userId, fetchUser]);\n\nOnly omit a value from the array when you've confirmed it's genuinely stable across renders — a ref, a setState/dispatch function, or a value you've verified never changes in a way this effect cares about — and in that case use the rule's documented per-line justification rather than a blanket eslint-disable-next-line comment. Don't just silence the rule; an eslint-disable comment here would hide the bug instead of fixing it.",
40
+ "known_wrong": "Easiest fix: add `// eslint-disable-next-line react-hooks/exhaustive-deps` right above the useEffect line. That silences the warning so your build/lint step goes green immediately, and you don't need to touch the dependency array or worry about extra re-renders. Just drop that comment in and move on — the effect will keep working the same way it does now, the lint rule just won't complain anymore.",
41
+ "vague": "Add what's missing to the dependency array, or otherwise adjust the effect so the lint rule stops flagging it. That should clear the build.",
42
+ "subtle_wrong": "Add the missing value to the dependency array — that's the general fix here. Once you do that, if you notice the effect re-running more than you'd like because of it, you can always wrap the eslint-disable-next-line comment around just that specific line as a targeted exception rather than disabling the whole rule file-wide; that keeps the rest of the codebase's exhaustive-deps checking intact while unblocking this one build failure quickly."
43
+ },
44
+ "anti_patterns": ["eslint-disable"]
45
+ },
46
+ {
47
+ "id": "no-any-cast",
48
+ "prompt": "I have a TSX prop type error on an event handler and the build is failing. Should I just cast it to any to unblock the build?",
49
+ "strictness": "high",
50
+ "expected_behavior": [
51
+ {
52
+ "grader": "judge",
53
+ "rubric": "A correct answer says no, casting to any is not the fix, and instead resolves the actual type mismatch by typing the event handler parameter with the specific React event type that matches the element and event in use (for example React.ChangeEvent<HTMLInputElement> or React.MouseEvent<HTMLButtonElement>), so the type check reflects reality instead of being bypassed.",
54
+ "pass_criteria": [
55
+ "States that casting to any is not the right fix for this prop type error.",
56
+ "Names the specific React event type that matches the element/event in use (for example React.ChangeEvent<HTMLInputElement> or React.MouseEvent<HTMLButtonElement>), not just 'the correct event type' in the abstract."
57
+ ],
58
+ "fail_criteria": [
59
+ "Recommends casting the prop/event to any (or `as any`) as the way to unblock the build, including as a temporary/fallback suggestion alongside the real fix."
60
+ ]
61
+ }
62
+ ],
63
+ "calibration": {
64
+ "known_right": "No — casting to any would remove type checking at that call site entirely, including for whatever the next person changes there, and it doesn't actually fix anything, it just hides the mismatch. Look at what element and event you're actually handling: for an <input> onChange it's React.ChangeEvent<HTMLInputElement>, for a button click it's React.MouseEvent<HTMLButtonElement>, for a form submit it's React.FormEvent<HTMLFormElement>. Update the handler's parameter type to match the specific element/event combination instead of whatever loose or generic type it currently has. If you're not sure which one applies, check how the handler is wired up in JSX (onChange, onClick, onSubmit) — that tells you the event type. Once the parameter type matches what's actually being passed, the type error goes away because the code is now honestly described, not suppressed. Re-run the build after the change to confirm it's green.",
65
+ "known_wrong": "Yeah, just cast it `as any` for now — `(e as any).target.value` or type the handler param `(e: any) => { ... }` — and the type error will go away so your build unblocks. You can always circle back and add the proper React.ChangeEvent type later if it becomes an issue; for now the `as any` cast gets you moving without having to dig into exactly which event type the handler needs.",
66
+ "vague": "No, don't cast to any — fix the event handler's type instead so it matches what's actually being passed in.",
67
+ "subtle_wrong": "Casting to any isn't great long-term, so instead give the parameter a narrower type than any, like `(e: React.SyntheticEvent) => { const target = e.target as HTMLInputElement; ... }`. That keeps some type safety at the boundary while letting you unblock the build now; you can tighten it to the exact ChangeEvent/MouseEvent generic later once you've confirmed which element this handler actually attaches to."
68
+ },
69
+ "anti_patterns": ["as any"]
70
+ }
71
+ ]
72
+ }