@yuaone/core 0.3.3 → 0.4.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 (126) hide show
  1. package/dist/agent-loop.d.ts +62 -0
  2. package/dist/agent-loop.d.ts.map +1 -1
  3. package/dist/agent-loop.js +705 -18
  4. package/dist/agent-loop.js.map +1 -1
  5. package/dist/background-agent.d.ts +110 -0
  6. package/dist/background-agent.d.ts.map +1 -0
  7. package/dist/background-agent.js +255 -0
  8. package/dist/background-agent.js.map +1 -0
  9. package/dist/coding-standards.d.ts +45 -0
  10. package/dist/coding-standards.d.ts.map +1 -0
  11. package/dist/coding-standards.js +1152 -0
  12. package/dist/coding-standards.js.map +1 -0
  13. package/dist/constants.d.ts.map +1 -1
  14. package/dist/constants.js +2 -6
  15. package/dist/constants.js.map +1 -1
  16. package/dist/context-manager.d.ts +6 -0
  17. package/dist/context-manager.d.ts.map +1 -1
  18. package/dist/context-manager.js +23 -4
  19. package/dist/context-manager.js.map +1 -1
  20. package/dist/index.d.ts +28 -4
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +26 -2
  23. package/dist/index.js.map +1 -1
  24. package/dist/llm-client.d.ts +8 -3
  25. package/dist/llm-client.d.ts.map +1 -1
  26. package/dist/llm-client.js +64 -13
  27. package/dist/llm-client.js.map +1 -1
  28. package/dist/plugin-auto-loader.d.ts +108 -0
  29. package/dist/plugin-auto-loader.d.ts.map +1 -0
  30. package/dist/plugin-auto-loader.js +743 -0
  31. package/dist/plugin-auto-loader.js.map +1 -0
  32. package/dist/plugin-registry.d.ts +112 -0
  33. package/dist/plugin-registry.d.ts.map +1 -0
  34. package/dist/plugin-registry.js +319 -0
  35. package/dist/plugin-registry.js.map +1 -0
  36. package/dist/plugin-types.d.ts +388 -0
  37. package/dist/plugin-types.d.ts.map +1 -0
  38. package/dist/plugin-types.js +8 -0
  39. package/dist/plugin-types.js.map +1 -0
  40. package/dist/plugin-validator.d.ts +54 -0
  41. package/dist/plugin-validator.d.ts.map +1 -0
  42. package/dist/plugin-validator.js +129 -0
  43. package/dist/plugin-validator.js.map +1 -0
  44. package/dist/repo-knowledge-graph.d.ts +112 -0
  45. package/dist/repo-knowledge-graph.d.ts.map +1 -0
  46. package/dist/repo-knowledge-graph.js +561 -0
  47. package/dist/repo-knowledge-graph.js.map +1 -0
  48. package/dist/role-registry.js +1 -1
  49. package/dist/role-registry.js.map +1 -1
  50. package/dist/self-debug-loop.d.ts +257 -0
  51. package/dist/self-debug-loop.d.ts.map +1 -0
  52. package/dist/self-debug-loop.js +870 -0
  53. package/dist/self-debug-loop.js.map +1 -0
  54. package/dist/skill-learner.d.ts +136 -0
  55. package/dist/skill-learner.d.ts.map +1 -0
  56. package/dist/skill-learner.js +382 -0
  57. package/dist/skill-learner.js.map +1 -0
  58. package/dist/skill-loader.d.ts +90 -0
  59. package/dist/skill-loader.d.ts.map +1 -0
  60. package/dist/skill-loader.js +309 -0
  61. package/dist/skill-loader.js.map +1 -0
  62. package/dist/specialist-registry.d.ts +132 -0
  63. package/dist/specialist-registry.d.ts.map +1 -0
  64. package/dist/specialist-registry.js +413 -0
  65. package/dist/specialist-registry.js.map +1 -0
  66. package/dist/sub-agent-prompts.d.ts +45 -0
  67. package/dist/sub-agent-prompts.d.ts.map +1 -0
  68. package/dist/sub-agent-prompts.js +177 -0
  69. package/dist/sub-agent-prompts.js.map +1 -0
  70. package/dist/sub-agent-router.d.ts +75 -0
  71. package/dist/sub-agent-router.d.ts.map +1 -0
  72. package/dist/sub-agent-router.js +174 -0
  73. package/dist/sub-agent-router.js.map +1 -0
  74. package/dist/sub-agent.d.ts +48 -0
  75. package/dist/sub-agent.d.ts.map +1 -1
  76. package/dist/sub-agent.js +108 -5
  77. package/dist/sub-agent.js.map +1 -1
  78. package/dist/system-prompt.d.ts +26 -0
  79. package/dist/system-prompt.d.ts.map +1 -1
  80. package/dist/system-prompt.js +177 -7
  81. package/dist/system-prompt.js.map +1 -1
  82. package/dist/task-classifier.d.ts +25 -1
  83. package/dist/task-classifier.d.ts.map +1 -1
  84. package/dist/task-classifier.js +171 -1
  85. package/dist/task-classifier.js.map +1 -1
  86. package/dist/tool-planner.d.ts +160 -0
  87. package/dist/tool-planner.d.ts.map +1 -0
  88. package/dist/tool-planner.js +501 -0
  89. package/dist/tool-planner.js.map +1 -0
  90. package/dist/types.d.ts +1 -1
  91. package/dist/types.d.ts.map +1 -1
  92. package/package.json +2 -1
  93. package/plugins/git/patterns/branch-patterns.json +101 -0
  94. package/plugins/git/patterns/commit-patterns.json +186 -0
  95. package/plugins/git/plugin.yaml +128 -0
  96. package/plugins/git/skills/branch-strategy.md +172 -0
  97. package/plugins/git/skills/commit-conv.md +178 -0
  98. package/plugins/git/skills/conflict-resolve.md +159 -0
  99. package/plugins/git/skills/history-clean.md +199 -0
  100. package/plugins/git/skills/pr-review.md +196 -0
  101. package/plugins/git/strategies/conflict-resolve.json +244 -0
  102. package/plugins/git/strategies/release-flow.json +292 -0
  103. package/plugins/git/validators/rules.json +348 -0
  104. package/plugins/react/patterns/anti-patterns.json +88 -0
  105. package/plugins/react/patterns/components.json +80 -0
  106. package/plugins/react/patterns/hooks.json +72 -0
  107. package/plugins/react/plugin.yaml +229 -0
  108. package/plugins/react/skills/bugfix.md +208 -0
  109. package/plugins/react/skills/component-gen.md +206 -0
  110. package/plugins/react/skills/hook-extract.md +208 -0
  111. package/plugins/react/skills/ssr.md +256 -0
  112. package/plugins/react/skills/test.md +273 -0
  113. package/plugins/react/strategies/build-fix.json +43 -0
  114. package/plugins/react/strategies/hook-loop-fix.json +36 -0
  115. package/plugins/react/strategies/hydration-fix.json +42 -0
  116. package/plugins/react/validators/rules.json +92 -0
  117. package/plugins/typescript/patterns/best-practices.json +25 -0
  118. package/plugins/typescript/patterns/common-errors.json +32 -0
  119. package/plugins/typescript/plugin.yaml +74 -0
  120. package/plugins/typescript/skills/debug.md +23 -0
  121. package/plugins/typescript/skills/migration.md +24 -0
  122. package/plugins/typescript/skills/refactor.md +22 -0
  123. package/plugins/typescript/skills/strict-mode.md +23 -0
  124. package/plugins/typescript/strategies/strict-migration.json +37 -0
  125. package/plugins/typescript/strategies/type-error-fix.json +37 -0
  126. package/plugins/typescript/validators/rules.json +28 -0
@@ -0,0 +1,88 @@
1
+ {
2
+ "$schema": "https://yuaone.com/schemas/patterns.json",
3
+ "name": "React Anti-Patterns",
4
+ "version": "1.0.0",
5
+ "description": "Common React anti-patterns to detect and warn about",
6
+ "patterns": [
7
+ {
8
+ "id": "index-as-key",
9
+ "name": "Array Index as Key",
10
+ "description": "Using array index as key in lists causes bugs when items are reordered, inserted, or deleted.",
11
+ "detect": "\\.map\\s*\\(\\s*\\([^,)]+,\\s*(\\w+)\\)\\s*=>.*key\\s*=\\s*\\{\\s*\\1\\s*\\}",
12
+ "action": "warn",
13
+ "template": "// BAD: key={index}\nitems.map((item, index) => <Item key={index} />)\n\n// GOOD: key={item.id}\nitems.map(item => <Item key={item.id} />)\n\n// If no ID, generate one at creation time:\nconst newItem = { id: crypto.randomUUID(), ...data };"
14
+ },
15
+ {
16
+ "id": "use-effect-no-deps",
17
+ "name": "useEffect Without Dependency Array",
18
+ "description": "useEffect without dependency array runs on EVERY render. Usually a bug.",
19
+ "detect": "useEffect\\(\\s*\\(\\)\\s*=>\\s*\\{[^}]+\\}\\s*\\)\\s*;",
20
+ "action": "warn",
21
+ "template": "// BAD: runs every render\nuseEffect(() => { fetchData(); });\n\n// GOOD: runs once on mount\nuseEffect(() => { fetchData(); }, []);\n\n// GOOD: runs when dependency changes\nuseEffect(() => { fetchData(id); }, [id]);"
22
+ },
23
+ {
24
+ "id": "props-in-state",
25
+ "name": "Copying Props into State",
26
+ "description": "Copying props into state creates a stale data source. Compute derived values directly from props.",
27
+ "detect": "useState\\(\\s*props\\.|const\\s*\\[\\w+,\\s*set\\w+\\]\\s*=\\s*useState\\(\\w+\\)",
28
+ "action": "warn",
29
+ "template": "// BAD: derived state from props\nfunction UserGreeting({ user }) {\n const [name, setName] = useState(user.name); // Stale!\n return <h1>{name}</h1>;\n}\n\n// GOOD: compute directly from props\nfunction UserGreeting({ user }) {\n const displayName = user.name.toUpperCase(); // Always fresh\n return <h1>{displayName}</h1>;\n}\n\n// Exception: only use state for props as initial values\nfunction EditableField({ initialValue }) {\n const [value, setValue] = useState(initialValue);\n // User edits diverge from initial value intentionally\n}"
30
+ },
31
+ {
32
+ "id": "direct-dom-manipulation",
33
+ "name": "Direct DOM Manipulation",
34
+ "description": "Avoid direct DOM manipulation (querySelector, getElementById, innerHTML). Use refs and React state.",
35
+ "detect": "document\\.(querySelector|getElementById|getElementsBy|createElement)|innerHTML\\s*=",
36
+ "action": "warn",
37
+ "template": "// BAD: direct DOM manipulation\ndocument.getElementById('title').textContent = 'Hello';\ndocument.querySelector('.list').innerHTML = '<li>item</li>';\n\n// GOOD: use React state and refs\nconst [title, setTitle] = useState('Hello');\nconst listRef = useRef<HTMLUListElement>(null);\nreturn (\n <>\n <h1>{title}</h1>\n <ul ref={listRef}>{items.map(i => <li key={i.id}>{i.name}</li>)}</ul>\n </>\n);"
38
+ },
39
+ {
40
+ "id": "dangerous-inner-html",
41
+ "name": "dangerouslySetInnerHTML Without Sanitization",
42
+ "description": "Using dangerouslySetInnerHTML without sanitization exposes XSS vulnerabilities.",
43
+ "detect": "dangerouslySetInnerHTML\\s*=\\s*\\{\\s*\\{\\s*__html",
44
+ "action": "warn",
45
+ "template": "// BAD: unsanitized HTML\n<div dangerouslySetInnerHTML={{ __html: userInput }} />\n\n// GOOD: sanitize with DOMPurify\nimport DOMPurify from 'dompurify';\n<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userInput) }} />\n\n// BETTER: use a markdown renderer or structured content\nimport ReactMarkdown from 'react-markdown';\n<ReactMarkdown>{userContent}</ReactMarkdown>"
46
+ },
47
+ {
48
+ "id": "prop-drilling-deep",
49
+ "name": "Deep Prop Drilling",
50
+ "description": "Passing props through 3+ levels of components that don't use them. Use context or composition.",
51
+ "detect": "\\{\\s*\\.{3}props\\s*\\}|\\{\\s*\\.{3}rest\\s*\\}",
52
+ "action": "warn",
53
+ "template": "// BAD: prop drilling through intermediate components\n<App theme={theme}>\n <Layout theme={theme}>\n <Sidebar theme={theme}>\n <NavItem theme={theme} /> {/* Only NavItem needs theme */}\n </Sidebar>\n </Layout>\n</App>\n\n// GOOD: use context\nconst ThemeContext = createContext<Theme>(defaultTheme);\n\n// Provider at top:\n<ThemeContext.Provider value={theme}><App /></ThemeContext.Provider>\n\n// Consumer where needed:\nfunction NavItem() {\n const theme = useContext(ThemeContext);\n}\n\n// ALSO GOOD: composition\n<Layout sidebar={<Sidebar><NavItem theme={theme} /></Sidebar>} />"
54
+ },
55
+ {
56
+ "id": "use-effect-in-render",
57
+ "name": "Side Effect in Render Body",
58
+ "description": "Side effects (API calls, subscriptions, mutations) should be in useEffect, not in the render body.",
59
+ "detect": "function\\s+\\w+.*\\{[^}]*(fetch\\(|axios\\.|console\\.log|document\\.|window\\.addEventListener)",
60
+ "action": "warn",
61
+ "template": "// BAD: side effect during render\nfunction UserProfile({ id }) {\n fetch(`/api/users/${id}`); // Runs EVERY render!\n const data = localStorage.getItem('user'); // SSR unsafe!\n}\n\n// GOOD: side effect in useEffect\nfunction UserProfile({ id }) {\n const [data, setData] = useState(null);\n useEffect(() => {\n fetch(`/api/users/${id}`).then(r => r.json()).then(setData);\n }, [id]);\n}"
62
+ },
63
+ {
64
+ "id": "conditional-hook",
65
+ "name": "Conditional Hook Call",
66
+ "description": "Hooks must not be called conditionally, inside loops, or after early returns. React relies on call order.",
67
+ "detect": "if\\s*\\([^)]*\\)\\s*\\{[^}]*(useState|useEffect|useCallback|useMemo|useRef|useContext|useReducer)",
68
+ "action": "warn",
69
+ "template": "// BAD: hook inside condition\nif (isLoggedIn) {\n const [user, setUser] = useState(null); // Breaks Rules of Hooks!\n}\n\n// GOOD: always call, conditionally use\nconst [user, setUser] = useState<User | null>(null);\nuseEffect(() => {\n if (isLoggedIn) fetchUser().then(setUser);\n}, [isLoggedIn]);\n\n// ALSO GOOD: split into a separate component\n{isLoggedIn && <AuthenticatedSection />}"
70
+ },
71
+ {
72
+ "id": "new-object-in-jsx",
73
+ "name": "New Object/Array Literal in JSX Props",
74
+ "description": "Creating objects or arrays inline in JSX causes unnecessary re-renders of child components.",
75
+ "detect": "<\\w+[^>]*\\b(style|className|options|data|config)\\s*=\\s*\\{\\s*(\\{|\\[)",
76
+ "action": "warn",
77
+ "template": "// BAD: new object every render\n<Avatar style={{ width: 50, height: 50 }} />\n<Select options={[{ value: 'a' }, { value: 'b' }]} />\n\n// GOOD: hoist to module scope (static values)\nconst avatarStyle = { width: 50, height: 50 } as const;\nconst options = [{ value: 'a' }, { value: 'b' }] as const;\n\n// GOOD: useMemo for dynamic values\nconst style = useMemo(() => ({ width: size, height: size }), [size]);"
78
+ },
79
+ {
80
+ "id": "missing-error-boundary",
81
+ "name": "Missing Error Boundary",
82
+ "description": "Components that fetch data or render dynamic content should be wrapped in an error boundary.",
83
+ "detect": "throw\\s+new\\s+Error|Promise\\.reject|catch\\s*\\(.*\\{\\s*throw",
84
+ "action": "warn",
85
+ "template": "// BAD: unhandled error crashes entire app\n<App>\n <DataComponent /> {/* If this throws, entire app crashes */}\n</App>\n\n// GOOD: error boundary around risky components\n<App>\n <ErrorBoundary fallback={<ErrorFallback />}>\n <DataComponent />\n </ErrorBoundary>\n</App>\n\n// In Next.js App Router, use error.tsx for route-level error boundaries"
86
+ }
87
+ ]
88
+ }
@@ -0,0 +1,80 @@
1
+ {
2
+ "$schema": "https://yuaone.com/schemas/patterns.json",
3
+ "name": "React Component Patterns",
4
+ "version": "1.0.0",
5
+ "description": "Common React component patterns for detection and suggestion",
6
+ "patterns": [
7
+ {
8
+ "id": "compound-component",
9
+ "name": "Compound Component",
10
+ "description": "Parent provides context, children consume it. Great for Select, Tabs, Accordion, Menu.",
11
+ "detect": "(createContext|useContext).*\\.(Provider|Consumer)",
12
+ "action": "suggest",
13
+ "template": "const {Name}Context = createContext<{Name}ContextType | null>(null);\n\nfunction use{Name}Context() {\n const ctx = useContext({Name}Context);\n if (!ctx) throw new Error('{Name} compound components must be used within <{Name}>');\n return ctx;\n}\n\nfunction {Name}({ children }: { children: React.ReactNode }) {\n const [state, setState] = useState(initialState);\n return (\n <{Name}Context.Provider value={{ state, setState }}>\n {children}\n </{Name}Context.Provider>\n );\n}\n\n{Name}.Item = function {Name}Item({ value }: { value: string }) {\n const { state, setState } = use{Name}Context();\n return <div onClick={() => setState(value)}>{value}</div>;\n};\n\nexport { {Name} };"
14
+ },
15
+ {
16
+ "id": "hoc-pattern",
17
+ "name": "Higher-Order Component",
18
+ "description": "Wrap a component to inject props or behavior. Use sparingly -- prefer hooks for most cases.",
19
+ "detect": "function\\s+with[A-Z]\\w+\\s*\\(|const\\s+with[A-Z]\\w+\\s*=",
20
+ "action": "suggest",
21
+ "template": "function with{Behavior}<P extends object>(\n WrappedComponent: React.ComponentType<P & {Injected}Props>\n) {\n function Enhanced(props: P) {\n const injected = use{Behavior}();\n return <WrappedComponent {...props} {...injected} />;\n }\n Enhanced.displayName = `with{Behavior}(${WrappedComponent.displayName || WrappedComponent.name})`;\n return Enhanced;\n}"
22
+ },
23
+ {
24
+ "id": "render-props",
25
+ "name": "Render Props / Children as Function",
26
+ "description": "Component accepts a render function as children or prop. Useful for headless behavior injection.",
27
+ "detect": "children\\s*\\(|render\\s*=\\s*\\{\\s*\\(",
28
+ "action": "suggest",
29
+ "template": "interface {Name}Props<T> {\n children: (state: { data: T; loading: boolean; error: Error | null }) => React.ReactNode;\n}\n\nfunction {Name}<T>({ children }: {Name}Props<T>) {\n const { data, loading, error } = use{DataSource}<T>();\n return <>{children({ data, loading, error })}</>;\n}"
30
+ },
31
+ {
32
+ "id": "forward-ref",
33
+ "name": "forwardRef Component",
34
+ "description": "Component that forwards ref to an inner DOM element. Required for ref-based integrations.",
35
+ "detect": "React\\.forwardRef|forwardRef\\s*<",
36
+ "action": "suggest",
37
+ "template": "interface {Name}Props extends React.HTMLAttributes<HTMLDivElement> {\n variant?: 'primary' | 'secondary';\n}\n\nconst {Name} = React.forwardRef<HTMLDivElement, {Name}Props>(\n ({ variant = 'primary', className, children, ...props }, ref) => {\n return (\n <div ref={ref} className={cn(styles[variant], className)} {...props}>\n {children}\n </div>\n );\n }\n);\n\n{Name}.displayName = '{Name}';\nexport { {Name} };"
38
+ },
39
+ {
40
+ "id": "generic-component",
41
+ "name": "Generic TypeScript Component",
42
+ "description": "Component with generic type parameter for type-safe data rendering (List, Table, Select).",
43
+ "detect": "function\\s+\\w+\\s*<\\s*T\\s*(extends|,|>)",
44
+ "action": "suggest",
45
+ "template": "interface {Name}Props<T> {\n items: T[];\n renderItem: (item: T, index: number) => React.ReactNode;\n keyExtractor: (item: T) => string;\n emptyMessage?: string;\n}\n\nfunction {Name}<T>({ items, renderItem, keyExtractor, emptyMessage }: {Name}Props<T>) {\n if (items.length === 0) {\n return <p>{emptyMessage ?? 'No items'}</p>;\n }\n return (\n <ul>\n {items.map((item, i) => (\n <li key={keyExtractor(item)}>{renderItem(item, i)}</li>\n ))}\n </ul>\n );\n}"
46
+ },
47
+ {
48
+ "id": "controlled-uncontrolled",
49
+ "name": "Controlled + Uncontrolled Dual Mode",
50
+ "description": "Component supports both controlled (value+onChange) and uncontrolled (defaultValue) modes.",
51
+ "detect": "(value|defaultValue).*onChange|controlled.*uncontrolled",
52
+ "action": "suggest",
53
+ "template": "interface {Name}Props {\n value?: string;\n defaultValue?: string;\n onChange?: (value: string) => void;\n}\n\nfunction {Name}({ value: controlledValue, defaultValue = '', onChange }: {Name}Props) {\n const [internalValue, setInternalValue] = useState(defaultValue);\n const isControlled = controlledValue !== undefined;\n const currentValue = isControlled ? controlledValue : internalValue;\n\n const handleChange = (newValue: string) => {\n if (!isControlled) setInternalValue(newValue);\n onChange?.(newValue);\n };\n\n return <input value={currentValue} onChange={(e) => handleChange(e.target.value)} />;\n}"
54
+ },
55
+ {
56
+ "id": "polymorphic-component",
57
+ "name": "Polymorphic Component (as prop)",
58
+ "description": "Component that renders as a different HTML element or component via 'as' prop.",
59
+ "detect": "as\\s*[=:]\\s*(keyof|ElementType|ComponentType)",
60
+ "action": "suggest",
61
+ "template": "type {Name}Props<C extends React.ElementType = 'div'> = {\n as?: C;\n children: React.ReactNode;\n} & Omit<React.ComponentPropsWithoutRef<C>, 'as' | 'children'>;\n\nfunction {Name}<C extends React.ElementType = 'div'>({ as, children, ...props }: {Name}Props<C>) {\n const Component = as || 'div';\n return <Component {...props}>{children}</Component>;\n}\n\n// Usage: <Box as=\"section\" /> or <Box as={Link} href=\"/\" />"
62
+ },
63
+ {
64
+ "id": "error-boundary",
65
+ "name": "Error Boundary",
66
+ "description": "Class component that catches JavaScript errors in child tree and shows fallback UI.",
67
+ "detect": "componentDidCatch|getDerivedStateFromError|ErrorBoundary",
68
+ "action": "suggest",
69
+ "template": "interface ErrorBoundaryProps {\n fallback: React.ReactNode | ((error: Error, reset: () => void) => React.ReactNode);\n children: React.ReactNode;\n onError?: (error: Error, errorInfo: React.ErrorInfo) => void;\n}\n\ninterface ErrorBoundaryState {\n error: Error | null;\n}\n\nclass ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {\n state: ErrorBoundaryState = { error: null };\n\n static getDerivedStateFromError(error: Error) {\n return { error };\n }\n\n componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {\n this.props.onError?.(error, errorInfo);\n }\n\n reset = () => this.setState({ error: null });\n\n render() {\n if (this.state.error) {\n const { fallback } = this.props;\n return typeof fallback === 'function'\n ? fallback(this.state.error, this.reset)\n : fallback;\n }\n return this.props.children;\n }\n}"
70
+ },
71
+ {
72
+ "id": "slot-pattern",
73
+ "name": "Slot Pattern (Named Children)",
74
+ "description": "Component accepts named slots for flexible layout composition.",
75
+ "detect": "(header|footer|sidebar|left|right|icon|prefix|suffix)\\s*[=:]\\s*React\\.ReactNode",
76
+ "action": "suggest",
77
+ "template": "interface {Name}Props {\n header?: React.ReactNode;\n footer?: React.ReactNode;\n sidebar?: React.ReactNode;\n children: React.ReactNode;\n}\n\nfunction {Name}({ header, footer, sidebar, children }: {Name}Props) {\n return (\n <div className=\"layout\">\n {header && <header className=\"layout-header\">{header}</header>}\n <div className=\"layout-body\">\n {sidebar && <aside className=\"layout-sidebar\">{sidebar}</aside>}\n <main className=\"layout-main\">{children}</main>\n </div>\n {footer && <footer className=\"layout-footer\">{footer}</footer>}\n </div>\n );\n}"
78
+ }
79
+ ]
80
+ }
@@ -0,0 +1,72 @@
1
+ {
2
+ "$schema": "https://yuaone.com/schemas/patterns.json",
3
+ "name": "React Hook Patterns",
4
+ "version": "1.0.0",
5
+ "description": "Common React hook patterns for detection and suggestion",
6
+ "patterns": [
7
+ {
8
+ "id": "use-state-batching",
9
+ "name": "useState Batching",
10
+ "description": "Multiple related setState calls should use useReducer or a single state object to avoid intermediate renders.",
11
+ "detect": "set\\w+\\([^)]+\\);\\s*set\\w+\\(",
12
+ "action": "suggest",
13
+ "template": "// Instead of multiple setState calls:\n// setLoading(true); setError(null); setData(null);\n\n// Use useReducer for related state transitions:\ntype State = { data: T | null; loading: boolean; error: Error | null };\ntype Action =\n | { type: 'FETCH_START' }\n | { type: 'FETCH_SUCCESS'; data: T }\n | { type: 'FETCH_ERROR'; error: Error };\n\nfunction reducer(state: State, action: Action): State {\n switch (action.type) {\n case 'FETCH_START': return { data: null, loading: true, error: null };\n case 'FETCH_SUCCESS': return { data: action.data, loading: false, error: null };\n case 'FETCH_ERROR': return { data: null, loading: false, error: action.error };\n }\n}\n\nconst [state, dispatch] = useReducer(reducer, { data: null, loading: false, error: null });"
14
+ },
15
+ {
16
+ "id": "use-callback-deps",
17
+ "name": "useCallback with Dependencies",
18
+ "description": "useCallback should have correct dependency array. Functions passed to child components should be memoized.",
19
+ "detect": "useCallback\\(\\s*\\([^)]*\\)\\s*=>",
20
+ "action": "suggest",
21
+ "template": "// Stable callback reference to prevent child re-renders:\nconst handleClick = useCallback((id: string) => {\n // Use functional updater to avoid deps on state:\n setItems(prev => prev.filter(item => item.id !== id));\n}, []); // Empty deps because functional updater doesn't need current state\n\n// When you DO need external values:\nconst handleSubmit = useCallback(() => {\n submitForm(formData);\n}, [formData]); // formData in deps"
22
+ },
23
+ {
24
+ "id": "use-memo-computation",
25
+ "name": "useMemo for Expensive Computation",
26
+ "description": "useMemo should be used for expensive computations, not for every variable. Profile before memoizing.",
27
+ "detect": "useMemo\\(\\s*\\(\\)\\s*=>",
28
+ "action": "suggest",
29
+ "template": "// Good: expensive computation that depends on data\nconst sortedItems = useMemo(\n () => items.slice().sort((a, b) => a.name.localeCompare(b.name)),\n [items]\n);\n\n// Good: object/array passed to child component (stable reference)\nconst contextValue = useMemo(\n () => ({ user, settings, theme }),\n [user, settings, theme]\n);\n\n// BAD: simple value derivation (no memoization needed)\n// const fullName = useMemo(() => `${first} ${last}`, [first, last]);\n// Just use: const fullName = `${first} ${last}`;"
30
+ },
31
+ {
32
+ "id": "use-ref-patterns",
33
+ "name": "useRef Patterns",
34
+ "description": "useRef for DOM access, mutable values that don't trigger re-renders, and previous value tracking.",
35
+ "detect": "useRef\\s*<",
36
+ "action": "suggest",
37
+ "template": "// 1. DOM reference\nconst inputRef = useRef<HTMLInputElement>(null);\nuseEffect(() => { inputRef.current?.focus(); }, []);\n\n// 2. Mutable value (no re-render on change)\nconst renderCount = useRef(0);\nuseEffect(() => { renderCount.current += 1; });\n\n// 3. Previous value tracking\nfunction usePrevious<T>(value: T): T | undefined {\n const ref = useRef<T>();\n useEffect(() => { ref.current = value; });\n return ref.current;\n}\n\n// 4. Latest callback ref (avoid stale closures in intervals)\nconst callbackRef = useRef(callback);\nuseLayoutEffect(() => { callbackRef.current = callback; });"
38
+ },
39
+ {
40
+ "id": "use-effect-fetch",
41
+ "name": "useEffect Data Fetching",
42
+ "description": "Data fetching in useEffect with abort controller, loading state, and error handling.",
43
+ "detect": "useEffect\\(.*fetch\\(|useEffect\\(.*axios",
44
+ "action": "suggest",
45
+ "template": "function useDataFetch<T>(url: string) {\n const [data, setData] = useState<T | null>(null);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n const controller = new AbortController();\n setLoading(true);\n setError(null);\n\n fetch(url, { signal: controller.signal })\n .then(res => {\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\n return res.json();\n })\n .then(setData)\n .catch(err => {\n if (err.name !== 'AbortError') setError(err);\n })\n .finally(() => setLoading(false));\n\n return () => controller.abort();\n }, [url]);\n\n return { data, loading, error };\n}"
46
+ },
47
+ {
48
+ "id": "use-debounce",
49
+ "name": "useDebounce Hook",
50
+ "description": "Debounce a value to limit update frequency. Common for search inputs and API calls.",
51
+ "detect": "useDebounce|debounce.*useState|setTimeout.*setState",
52
+ "action": "suggest",
53
+ "template": "function useDebounce<T>(value: T, delay: number): T {\n const [debouncedValue, setDebouncedValue] = useState(value);\n\n useEffect(() => {\n const timer = setTimeout(() => setDebouncedValue(value), delay);\n return () => clearTimeout(timer);\n }, [value, delay]);\n\n return debouncedValue;\n}\n\n// Usage:\nconst [search, setSearch] = useState('');\nconst debouncedSearch = useDebounce(search, 300);\n\nuseEffect(() => {\n if (debouncedSearch) fetchResults(debouncedSearch);\n}, [debouncedSearch]);"
54
+ },
55
+ {
56
+ "id": "use-local-storage",
57
+ "name": "useLocalStorage Hook",
58
+ "description": "Persist state to localStorage with SSR safety and type-safe serialization.",
59
+ "detect": "localStorage\\.(get|set)Item|useLocalStorage",
60
+ "action": "suggest",
61
+ "template": "function useLocalStorage<T>(key: string, initialValue: T): [T, (value: T | ((prev: T) => T)) => void] {\n const [storedValue, setStoredValue] = useState<T>(() => {\n if (typeof window === 'undefined') return initialValue;\n try {\n const item = window.localStorage.getItem(key);\n return item ? JSON.parse(item) : initialValue;\n } catch {\n return initialValue;\n }\n });\n\n const setValue = useCallback((value: T | ((prev: T) => T)) => {\n setStoredValue(prev => {\n const newValue = value instanceof Function ? value(prev) : value;\n try {\n window.localStorage.setItem(key, JSON.stringify(newValue));\n } catch (e) {\n console.warn(`Failed to save ${key} to localStorage`, e);\n }\n return newValue;\n });\n }, [key]);\n\n return [storedValue, setValue];\n}"
62
+ },
63
+ {
64
+ "id": "use-media-query",
65
+ "name": "useMediaQuery Hook",
66
+ "description": "SSR-safe media query matching for responsive behavior in JavaScript.",
67
+ "detect": "matchMedia|useMediaQuery|window\\.innerWidth",
68
+ "action": "suggest",
69
+ "template": "function useMediaQuery(query: string): boolean {\n const [matches, setMatches] = useState(false);\n\n useEffect(() => {\n const media = window.matchMedia(query);\n setMatches(media.matches);\n\n const listener = (e: MediaQueryListEvent) => setMatches(e.matches);\n media.addEventListener('change', listener);\n return () => media.removeEventListener('change', listener);\n }, [query]);\n\n return matches;\n}\n\n// Usage:\nconst isMobile = useMediaQuery('(max-width: 768px)');\nconst prefersDark = useMediaQuery('(prefers-color-scheme: dark)');"
70
+ }
71
+ ]
72
+ }
@@ -0,0 +1,229 @@
1
+ # @yuaone/plugin-react — React/Next.js Expert Agent Skill Pack
2
+ # SSOT for plugin metadata, skills, triggers, tools, and configuration.
3
+
4
+ id: "@yuaone/plugin-react"
5
+ name: "React Expert"
6
+ version: "1.0.0"
7
+ description: "React/Next.js expert agent skill pack — bugfix, component generation, hook extraction, testing, and SSR/hydration expertise"
8
+ author: "yuaone"
9
+ license: "MIT"
10
+ category: "coding"
11
+ type: "hybrid"
12
+ trustLevel: "official"
13
+ sandbox: "restricted"
14
+ triggerMode: "auto"
15
+ pluginApiVersion: 1
16
+ estimatedPromptTokens: 3500
17
+ engineVersion: ">=0.2.0"
18
+
19
+ # Auto-detect — any match triggers plugin load
20
+ detect:
21
+ files:
22
+ - "package.json"
23
+ dependencies:
24
+ - "react"
25
+ - "react-dom"
26
+ glob:
27
+ - "src/**/*.tsx"
28
+ - "src/**/*.jsx"
29
+ - "app/**/*.tsx"
30
+ - "pages/**/*.tsx"
31
+ - "components/**/*.tsx"
32
+
33
+ # Skills — domain knowledge units
34
+ skills:
35
+ - id: "react-bugfix"
36
+ name: "React Bugfix"
37
+ description: "React bug diagnosis and fix — hydration, hook loops, state issues, key props, memory leaks"
38
+ trigger:
39
+ kind: "auto"
40
+ pattern: "(hydration|Maximum update depth|Cannot read propert|React Hook|memory leak|Warning:.*key)"
41
+ confidence: 0.85
42
+ cooldown: 5000
43
+ template: "skills/bugfix.md"
44
+ enabled: true
45
+ tags: ["debug", "error", "fix", "react", "hooks", "state"]
46
+
47
+ - id: "react-component-gen"
48
+ name: "Component Generator"
49
+ description: "React component generation — atomic design, compound patterns, forwardRef, generics"
50
+ trigger:
51
+ kind: "command"
52
+ command: "/react-component"
53
+ template: "skills/component-gen.md"
54
+ enabled: true
55
+ tags: ["generate", "component", "atomic", "compound", "react"]
56
+
57
+ - id: "react-hook-extract"
58
+ name: "Hook Extractor"
59
+ description: "Extract custom hooks from components — state+effect grouping, hook composition, testability"
60
+ trigger:
61
+ kind: "command"
62
+ command: "/extract-hook"
63
+ template: "skills/hook-extract.md"
64
+ enabled: true
65
+ tags: ["refactor", "hook", "extract", "composition"]
66
+
67
+ - id: "react-test"
68
+ name: "React Test Generator"
69
+ description: "Generate React tests — RTL best practices, MSW API mocking, user events, async patterns"
70
+ trigger:
71
+ kind: "command"
72
+ command: "/react-test"
73
+ template: "skills/test.md"
74
+ enabled: true
75
+ tags: ["test", "jest", "rtl", "msw", "vitest"]
76
+
77
+ - id: "react-ssr"
78
+ name: "SSR/Hydration Expert"
79
+ description: "SSR and hydration specialist — Next.js App Router, Server Components, streaming SSR, hydration debugging"
80
+ trigger:
81
+ kind: "auto"
82
+ pattern: "(hydrat|server.?component|use.?client|use.?server|getServerSideProps|getStaticProps)"
83
+ confidence: 0.80
84
+ cooldown: 10000
85
+ template: "skills/ssr.md"
86
+ enabled: true
87
+ tags: ["ssr", "hydration", "server", "nextjs", "streaming"]
88
+
89
+ # Triggers — error/task patterns mapped to skills and strategies
90
+ triggers:
91
+ - pattern: "Text content does not match|Hydration failed|hydration mismatch"
92
+ kind: "error"
93
+ skill: "react-ssr"
94
+ strategy: "hydration-fix"
95
+ priority: 10
96
+ triggerMode: "auto"
97
+
98
+ - pattern: "Maximum update depth exceeded|Too many re-renders"
99
+ kind: "error"
100
+ skill: "react-bugfix"
101
+ strategy: "hook-loop-fix"
102
+ priority: 10
103
+ triggerMode: "auto"
104
+
105
+ - pattern: "Module not found|Cannot find module|Failed to compile"
106
+ kind: "error"
107
+ skill: "react-bugfix"
108
+ strategy: "build-fix"
109
+ priority: 5
110
+ triggerMode: "auto"
111
+
112
+ - pattern: "Cannot read properties of (null|undefined)"
113
+ kind: "error"
114
+ skill: "react-bugfix"
115
+ priority: 3
116
+ triggerMode: "auto"
117
+
118
+ - pattern: "Invalid hook call|Hooks can only be called inside"
119
+ kind: "error"
120
+ skill: "react-bugfix"
121
+ priority: 8
122
+ triggerMode: "auto"
123
+
124
+ - pattern: "Each child in a list should have a unique.*key"
125
+ kind: "error"
126
+ skill: "react-bugfix"
127
+ priority: 4
128
+ triggerMode: "auto"
129
+
130
+ - pattern: "Can't perform a React state update on an unmounted component"
131
+ kind: "error"
132
+ skill: "react-bugfix"
133
+ priority: 6
134
+ triggerMode: "auto"
135
+
136
+ - pattern: "Server Error.*getServerSideProps|Error.*server.?component"
137
+ kind: "error"
138
+ skill: "react-ssr"
139
+ strategy: "hydration-fix"
140
+ priority: 7
141
+ triggerMode: "auto"
142
+
143
+ # Tools — analysis utilities
144
+ tools:
145
+ - name: "jsx_analyzer"
146
+ description: "Analyze JSX/TSX files for React patterns, anti-patterns, and component structure"
147
+ inputSchema:
148
+ type: "object"
149
+ properties:
150
+ filePath:
151
+ type: "string"
152
+ description: "Path to the JSX/TSX file to analyze"
153
+ analysisType:
154
+ type: "string"
155
+ enum: ["patterns", "anti-patterns", "structure", "all"]
156
+ description: "Type of analysis to perform"
157
+ required: ["filePath"]
158
+ requiresApproval: false
159
+ riskLevel: "low"
160
+ sideEffectLevel: "read"
161
+
162
+ - name: "component_tree"
163
+ description: "Build and analyze React component tree from source files"
164
+ inputSchema:
165
+ type: "object"
166
+ properties:
167
+ rootDir:
168
+ type: "string"
169
+ description: "Root directory to scan for components"
170
+ depth:
171
+ type: "number"
172
+ description: "Maximum depth of component tree (default: 5)"
173
+ includeProps:
174
+ type: "boolean"
175
+ description: "Include prop types in the tree output"
176
+ required: ["rootDir"]
177
+ requiresApproval: false
178
+ riskLevel: "low"
179
+ sideEffectLevel: "read"
180
+
181
+ # Strategy files
182
+ strategies:
183
+ - "strategies/hydration-fix.json"
184
+ - "strategies/hook-loop-fix.json"
185
+ - "strategies/build-fix.json"
186
+
187
+ # Validator files
188
+ validators:
189
+ - "validators/rules.json"
190
+
191
+ # Pattern files
192
+ patterns:
193
+ - "patterns/components.json"
194
+ - "patterns/hooks.json"
195
+ - "patterns/anti-patterns.json"
196
+
197
+ # Permissions
198
+ permissions:
199
+ fileRead: true
200
+ fileWrite: true
201
+ shellExec: true
202
+ networkAccess: false
203
+ gitOps: false
204
+ maxTokensPerCall: 8000
205
+
206
+ # User-configurable settings
207
+ config:
208
+ auto_load:
209
+ type: "boolean"
210
+ default: true
211
+ description: "Automatically load when React project detected"
212
+ inject_into_prompt:
213
+ type: "boolean"
214
+ default: true
215
+ description: "Inject relevant skill knowledge into system prompt"
216
+ max_patterns_in_context:
217
+ type: "number"
218
+ default: 5
219
+ description: "Maximum number of patterns to inject into context"
220
+ preferred_test_runner:
221
+ type: "select"
222
+ default: "vitest"
223
+ description: "Preferred test runner for generated tests"
224
+ options: ["vitest", "jest"]
225
+ next_version:
226
+ type: "select"
227
+ default: "14"
228
+ description: "Target Next.js major version for SSR strategies"
229
+ options: ["13", "14", "15"]