@soleri/core 9.13.0 → 9.14.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 (154) hide show
  1. package/dist/engine/bin/soleri-engine.js +7 -2
  2. package/dist/engine/bin/soleri-engine.js.map +1 -1
  3. package/dist/flows/types.d.ts +34 -30
  4. package/dist/flows/types.d.ts.map +1 -1
  5. package/dist/index.d.ts +3 -2
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +3 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/knowledge-packs/community/.gitkeep +0 -0
  10. package/dist/knowledge-packs/knowledge-packs/community/.gitkeep +0 -0
  11. package/dist/knowledge-packs/knowledge-packs/salvador/salvador-craft/soleri-pack.json +10 -0
  12. package/dist/knowledge-packs/knowledge-packs/salvador/salvador-craft/vault/accessibility.json +53 -0
  13. package/dist/knowledge-packs/knowledge-packs/salvador/salvador-craft/vault/design-tokens.json +26 -0
  14. package/dist/knowledge-packs/knowledge-packs/salvador/salvador-craft/vault/design.json +33 -0
  15. package/dist/knowledge-packs/knowledge-packs/salvador/salvador-craft/vault/styling.json +44 -0
  16. package/dist/knowledge-packs/knowledge-packs/salvador/salvador-craft/vault/ux-laws.json +36 -0
  17. package/dist/knowledge-packs/knowledge-packs/salvador/salvador-craft/vault/ux.json +36 -0
  18. package/dist/knowledge-packs/knowledge-packs/salvador/salvador-engineering/soleri-pack.json +10 -0
  19. package/dist/knowledge-packs/knowledge-packs/salvador/salvador-engineering/vault/architecture.json +143 -0
  20. package/dist/knowledge-packs/knowledge-packs/salvador/salvador-engineering/vault/commercial.json +16 -0
  21. package/dist/knowledge-packs/knowledge-packs/salvador/salvador-engineering/vault/communication.json +33 -0
  22. package/dist/knowledge-packs/knowledge-packs/salvador/salvador-engineering/vault/component.json +16 -0
  23. package/dist/knowledge-packs/knowledge-packs/salvador/salvador-engineering/vault/express.json +34 -0
  24. package/dist/knowledge-packs/knowledge-packs/salvador/salvador-engineering/vault/leadership.json +33 -0
  25. package/dist/knowledge-packs/knowledge-packs/salvador/salvador-engineering/vault/methodology.json +33 -0
  26. package/dist/knowledge-packs/knowledge-packs/salvador/salvador-engineering/vault/monorepo.json +33 -0
  27. package/dist/knowledge-packs/knowledge-packs/salvador/salvador-engineering/vault/other.json +73 -0
  28. package/dist/knowledge-packs/knowledge-packs/salvador/salvador-engineering/vault/performance.json +35 -0
  29. package/dist/knowledge-packs/knowledge-packs/salvador/salvador-engineering/vault/prisma.json +33 -0
  30. package/dist/knowledge-packs/knowledge-packs/salvador/salvador-engineering/vault/product-strategy.json +42 -0
  31. package/dist/knowledge-packs/knowledge-packs/salvador/salvador-engineering/vault/react.json +47 -0
  32. package/dist/knowledge-packs/knowledge-packs/salvador/salvador-engineering/vault/security.json +34 -0
  33. package/dist/knowledge-packs/knowledge-packs/salvador/salvador-engineering/vault/testing.json +33 -0
  34. package/dist/knowledge-packs/knowledge-packs/salvador/salvador-engineering/vault/tooling.json +85 -0
  35. package/dist/knowledge-packs/knowledge-packs/salvador/salvador-engineering/vault/typescript.json +34 -0
  36. package/dist/knowledge-packs/knowledge-packs/salvador/salvador-engineering/vault/workflow.json +46 -0
  37. package/dist/knowledge-packs/knowledge-packs/salvador/salvador-uipro/soleri-pack.json +10 -0
  38. package/dist/knowledge-packs/knowledge-packs/salvador/salvador-uipro/vault/design.json +2589 -0
  39. package/dist/knowledge-packs/knowledge-packs/starter/api-design/soleri-pack.json +9 -0
  40. package/dist/knowledge-packs/knowledge-packs/starter/api-design/vault/patterns.json +137 -0
  41. package/dist/knowledge-packs/knowledge-packs/starter/architecture/soleri-pack.json +10 -0
  42. package/dist/knowledge-packs/knowledge-packs/starter/architecture/vault/patterns.json +137 -0
  43. package/dist/knowledge-packs/knowledge-packs/starter/design/soleri-pack.json +10 -0
  44. package/dist/knowledge-packs/knowledge-packs/starter/design/vault/patterns.json +137 -0
  45. package/dist/knowledge-packs/knowledge-packs/starter/nodejs/soleri-pack.json +9 -0
  46. package/dist/knowledge-packs/knowledge-packs/starter/nodejs/vault/patterns.json +137 -0
  47. package/dist/knowledge-packs/knowledge-packs/starter/react/soleri-pack.json +9 -0
  48. package/dist/knowledge-packs/knowledge-packs/starter/react/vault/patterns.json +164 -0
  49. package/dist/knowledge-packs/knowledge-packs/starter/security/soleri-pack.json +10 -0
  50. package/dist/knowledge-packs/knowledge-packs/starter/security/vault/patterns.json +137 -0
  51. package/dist/knowledge-packs/knowledge-packs/starter/testing/soleri-pack.json +9 -0
  52. package/dist/knowledge-packs/knowledge-packs/starter/testing/vault/patterns.json +128 -0
  53. package/dist/knowledge-packs/knowledge-packs/starter/typescript/soleri-pack.json +9 -0
  54. package/dist/knowledge-packs/knowledge-packs/starter/typescript/vault/patterns.json +164 -0
  55. package/dist/knowledge-packs/salvador/salvador-craft/soleri-pack.json +10 -0
  56. package/dist/knowledge-packs/salvador/salvador-craft/vault/accessibility.json +53 -0
  57. package/dist/knowledge-packs/salvador/salvador-craft/vault/design-tokens.json +26 -0
  58. package/dist/knowledge-packs/salvador/salvador-craft/vault/design.json +33 -0
  59. package/dist/knowledge-packs/salvador/salvador-craft/vault/styling.json +44 -0
  60. package/dist/knowledge-packs/salvador/salvador-craft/vault/ux-laws.json +36 -0
  61. package/dist/knowledge-packs/salvador/salvador-craft/vault/ux.json +36 -0
  62. package/dist/knowledge-packs/salvador/salvador-engineering/soleri-pack.json +10 -0
  63. package/dist/knowledge-packs/salvador/salvador-engineering/vault/architecture.json +143 -0
  64. package/dist/knowledge-packs/salvador/salvador-engineering/vault/commercial.json +16 -0
  65. package/dist/knowledge-packs/salvador/salvador-engineering/vault/communication.json +33 -0
  66. package/dist/knowledge-packs/salvador/salvador-engineering/vault/component.json +16 -0
  67. package/dist/knowledge-packs/salvador/salvador-engineering/vault/express.json +34 -0
  68. package/dist/knowledge-packs/salvador/salvador-engineering/vault/leadership.json +33 -0
  69. package/dist/knowledge-packs/salvador/salvador-engineering/vault/methodology.json +33 -0
  70. package/dist/knowledge-packs/salvador/salvador-engineering/vault/monorepo.json +33 -0
  71. package/dist/knowledge-packs/salvador/salvador-engineering/vault/other.json +73 -0
  72. package/dist/knowledge-packs/salvador/salvador-engineering/vault/performance.json +35 -0
  73. package/dist/knowledge-packs/salvador/salvador-engineering/vault/prisma.json +33 -0
  74. package/dist/knowledge-packs/salvador/salvador-engineering/vault/product-strategy.json +42 -0
  75. package/dist/knowledge-packs/salvador/salvador-engineering/vault/react.json +47 -0
  76. package/dist/knowledge-packs/salvador/salvador-engineering/vault/security.json +34 -0
  77. package/dist/knowledge-packs/salvador/salvador-engineering/vault/testing.json +33 -0
  78. package/dist/knowledge-packs/salvador/salvador-engineering/vault/tooling.json +85 -0
  79. package/dist/knowledge-packs/salvador/salvador-engineering/vault/typescript.json +34 -0
  80. package/dist/knowledge-packs/salvador/salvador-engineering/vault/workflow.json +46 -0
  81. package/dist/knowledge-packs/salvador/salvador-uipro/soleri-pack.json +10 -0
  82. package/dist/knowledge-packs/salvador/salvador-uipro/vault/design.json +2589 -0
  83. package/dist/knowledge-packs/starter/architecture/soleri-pack.json +10 -0
  84. package/dist/knowledge-packs/starter/architecture/vault/patterns.json +137 -0
  85. package/dist/knowledge-packs/starter/design/soleri-pack.json +10 -0
  86. package/dist/knowledge-packs/starter/design/vault/patterns.json +137 -0
  87. package/dist/knowledge-packs/starter/security/soleri-pack.json +10 -0
  88. package/dist/knowledge-packs/starter/security/vault/patterns.json +137 -0
  89. package/dist/packs/index.d.ts +1 -1
  90. package/dist/packs/index.d.ts.map +1 -1
  91. package/dist/packs/index.js +1 -1
  92. package/dist/packs/index.js.map +1 -1
  93. package/dist/packs/resolver.d.ts +6 -0
  94. package/dist/packs/resolver.d.ts.map +1 -1
  95. package/dist/packs/resolver.js +20 -1
  96. package/dist/packs/resolver.js.map +1 -1
  97. package/dist/runtime/admin-setup-ops.js +1 -1
  98. package/dist/runtime/admin-setup-ops.js.map +1 -1
  99. package/dist/runtime/capture-ops.d.ts.map +1 -1
  100. package/dist/runtime/capture-ops.js +2 -1
  101. package/dist/runtime/capture-ops.js.map +1 -1
  102. package/dist/runtime/intake-ops.d.ts.map +1 -1
  103. package/dist/runtime/intake-ops.js +5 -5
  104. package/dist/runtime/intake-ops.js.map +1 -1
  105. package/dist/runtime/orchestrate-ops.d.ts.map +1 -1
  106. package/dist/runtime/orchestrate-ops.js +26 -2
  107. package/dist/runtime/orchestrate-ops.js.map +1 -1
  108. package/dist/runtime/planning-extra-ops.d.ts.map +1 -1
  109. package/dist/runtime/planning-extra-ops.js +5 -7
  110. package/dist/runtime/planning-extra-ops.js.map +1 -1
  111. package/dist/runtime/playbook-ops.d.ts.map +1 -1
  112. package/dist/runtime/playbook-ops.js +2 -1
  113. package/dist/runtime/playbook-ops.js.map +1 -1
  114. package/dist/runtime/schema-helpers.d.ts +7 -0
  115. package/dist/runtime/schema-helpers.d.ts.map +1 -0
  116. package/dist/runtime/schema-helpers.js +21 -0
  117. package/dist/runtime/schema-helpers.js.map +1 -0
  118. package/dist/runtime/sync-ops.d.ts.map +1 -1
  119. package/dist/runtime/sync-ops.js +3 -4
  120. package/dist/runtime/sync-ops.js.map +1 -1
  121. package/dist/runtime/vault-extra-ops.d.ts.map +1 -1
  122. package/dist/runtime/vault-extra-ops.js +5 -4
  123. package/dist/runtime/vault-extra-ops.js.map +1 -1
  124. package/dist/skills/sync-skills.d.ts +26 -7
  125. package/dist/skills/sync-skills.d.ts.map +1 -1
  126. package/dist/skills/sync-skills.js +126 -32
  127. package/dist/skills/sync-skills.js.map +1 -1
  128. package/dist/skills/validate-skill-docs.d.ts +24 -0
  129. package/dist/skills/validate-skill-docs.d.ts.map +1 -0
  130. package/dist/skills/validate-skill-docs.js +476 -0
  131. package/dist/skills/validate-skill-docs.js.map +1 -0
  132. package/package.json +2 -2
  133. package/src/__tests__/deviation-detection.test.ts +49 -0
  134. package/src/enforcement/adapters/claude-code.test.ts +9 -9
  135. package/src/engine/bin/soleri-engine.ts +7 -2
  136. package/src/flows/types.ts +4 -0
  137. package/src/index.ts +15 -2
  138. package/src/packs/index.ts +6 -1
  139. package/src/packs/resolver.ts +24 -1
  140. package/src/runtime/admin-setup-ops.test.ts +2 -0
  141. package/src/runtime/admin-setup-ops.ts +1 -1
  142. package/src/runtime/capture-ops.ts +2 -1
  143. package/src/runtime/intake-ops.ts +7 -7
  144. package/src/runtime/orchestrate-ops.ts +29 -2
  145. package/src/runtime/planning-extra-ops.ts +35 -37
  146. package/src/runtime/playbook-ops.ts +2 -1
  147. package/src/runtime/schema-helpers.test.ts +45 -0
  148. package/src/runtime/schema-helpers.ts +19 -0
  149. package/src/runtime/sync-ops.ts +8 -9
  150. package/src/runtime/vault-extra-ops.ts +5 -4
  151. package/src/skills/__tests__/sync-skills.test.ts +102 -29
  152. package/src/skills/__tests__/validate-skill-docs.test.ts +58 -0
  153. package/src/skills/sync-skills.ts +146 -32
  154. package/src/skills/validate-skill-docs.ts +562 -0
@@ -0,0 +1,164 @@
1
+ [
2
+ {
3
+ "id": "react-001",
4
+ "type": "rule",
5
+ "domain": "react",
6
+ "title": "Rules of Hooks — Call Order and No Conditionals",
7
+ "severity": "critical",
8
+ "description": "Hooks must be called at the top level of a component or custom hook, in the same order every render. Never call hooks inside conditionals, loops, or nested functions — React relies on call order to track state.\n\nBad:\n```jsx\nif (isLoggedIn) {\n const [name, setName] = useState('');\n}\n```\n\nGood:\n```jsx\nconst [name, setName] = useState('');\n// use `name` conditionally in the render, not the hook itself\n```",
9
+ "tags": ["react", "hooks", "rules"]
10
+ },
11
+ {
12
+ "id": "react-002",
13
+ "type": "pattern",
14
+ "domain": "react",
15
+ "title": "Extract Custom Hooks for Reusable Logic",
16
+ "severity": "suggestion",
17
+ "description": "When two or more components share stateful logic, extract it into a custom hook. Custom hooks compose — they can call other hooks. Name them `use*` so React enforces hook rules.\n\nExample:\n```jsx\nfunction useDebounce(value, delay) {\n const [debounced, setDebounced] = useState(value);\n useEffect(() => {\n const id = setTimeout(() => setDebounced(value), delay);\n return () => clearTimeout(id);\n }, [value, delay]);\n return debounced;\n}\n\n// Usage in any component\nconst debouncedSearch = useDebounce(query, 300);\n```",
18
+ "tags": ["react", "hooks", "reusability", "composition"]
19
+ },
20
+ {
21
+ "id": "react-003",
22
+ "type": "pattern",
23
+ "domain": "react",
24
+ "title": "Composition Over Prop Drilling",
25
+ "severity": "warning",
26
+ "description": "Instead of passing props through 3+ layers of components, restructure using composition. Pass components as children or props so intermediate layers don't need to know about the data.\n\nBad:\n```jsx\n<Layout user={user}>\n <Sidebar user={user}>\n <UserMenu user={user} />\n </Sidebar>\n</Layout>\n```\n\nGood:\n```jsx\n<Layout sidebar={<Sidebar><UserMenu user={user} /></Sidebar>}>\n {children}\n</Layout>\n```\n\nThe Layout and Sidebar no longer need to know about `user` at all.",
27
+ "tags": ["react", "composition", "props", "architecture"]
28
+ },
29
+ {
30
+ "id": "react-004",
31
+ "type": "pattern",
32
+ "domain": "react",
33
+ "title": "Controlled vs Uncontrolled Components",
34
+ "severity": "suggestion",
35
+ "description": "Controlled components derive their value from React state (`value` + `onChange`). Uncontrolled components use the DOM as the source of truth (`defaultValue` + `ref`). Default to controlled for forms that need validation, conditional logic, or shared state. Use uncontrolled for simple isolated inputs where you only need the value on submit.\n\nControlled:\n```jsx\nconst [email, setEmail] = useState('');\n<input value={email} onChange={e => setEmail(e.target.value)} />\n```\n\nUncontrolled:\n```jsx\nconst inputRef = useRef(null);\n<input defaultValue=\"\" ref={inputRef} />\n// read inputRef.current.value on submit\n```",
36
+ "tags": ["react", "forms", "state", "components"]
37
+ },
38
+ {
39
+ "id": "react-005",
40
+ "type": "anti-pattern",
41
+ "domain": "react",
42
+ "title": "Premature useMemo and useCallback",
43
+ "severity": "warning",
44
+ "description": "Don't wrap every value in `useMemo` or every function in `useCallback` by default. Memoization has a cost — the hook comparison runs every render. It only helps when: (1) the value is passed to a `React.memo`-wrapped child, (2) the computation is genuinely expensive, or (3) the value is a dependency of another hook.\n\nBad:\n```jsx\nconst label = useMemo(() => `Hello ${name}`, [name]); // trivial computation\nconst handleClick = useCallback(() => setOpen(true), []); // no memo'd child\n```\n\nGood — only memoize when it matters:\n```jsx\nconst sorted = useMemo(() => items.sort(compareFn), [items]); // expensive\n<MemoizedList onSelect={useCallback(fn, [deps])} /> // memo'd child\n```",
45
+ "tags": ["react", "hooks", "performance", "memoization"]
46
+ },
47
+ {
48
+ "id": "react-006",
49
+ "type": "anti-pattern",
50
+ "domain": "react",
51
+ "title": "Avoid Premature React.memo",
52
+ "severity": "warning",
53
+ "description": "Don't wrap components in `React.memo` without measuring first. Memo adds a shallow comparison on every render. If the component receives objects or functions as props (which are new references every render), memo provides zero benefit while adding overhead. Profile first, then memo components that: (1) render often with the same props, (2) are expensive to render, and (3) receive stable prop references.\n\nBad:\n```jsx\nexport default React.memo(SmallStatelessLabel); // trivial render\n```\n\nGood:\n```jsx\n// Profile shows this list item re-renders 500 times with same props\nexport default React.memo(ExpensiveListItem);\n```",
54
+ "tags": ["react", "performance", "memoization"]
55
+ },
56
+ {
57
+ "id": "react-007",
58
+ "type": "pattern",
59
+ "domain": "react",
60
+ "title": "Error Boundaries for Graceful Failure",
61
+ "severity": "warning",
62
+ "description": "Wrap major UI sections in error boundaries so a crash in one widget doesn't take down the entire page. Place them at route level, around third-party components, and around data-dependent sections.\n\n```jsx\nclass ErrorBoundary extends React.Component {\n state = { hasError: false };\n static getDerivedStateFromError() { return { hasError: true }; }\n componentDidCatch(error, info) { logError(error, info); }\n render() {\n if (this.state.hasError) return <FallbackUI onRetry={() => this.setState({ hasError: false })} />;\n return this.props.children;\n }\n}\n\n// Usage\n<ErrorBoundary><Dashboard /></ErrorBoundary>\n```\n\nError boundaries catch errors in rendering, lifecycle methods, and constructors — not in event handlers or async code.",
63
+ "tags": ["react", "error-handling", "resilience"]
64
+ },
65
+ {
66
+ "id": "react-008",
67
+ "type": "rule",
68
+ "domain": "react",
69
+ "title": "Keys Must Be Stable, Unique, and Data-Derived",
70
+ "severity": "critical",
71
+ "description": "List keys must be stable across renders, unique among siblings, and derived from the data — never from the array index (unless the list is truly static and never reordered).\n\nBad:\n```jsx\n{items.map((item, index) => <Item key={index} {...item} />)}\n// Reordering or inserting corrupts component state\n```\n\nBad:\n```jsx\n{items.map(item => <Item key={Math.random()} {...item} />)}\n// Forces full remount every render\n```\n\nGood:\n```jsx\n{items.map(item => <Item key={item.id} {...item} />)}\n```",
72
+ "tags": ["react", "lists", "keys", "reconciliation"]
73
+ },
74
+ {
75
+ "id": "react-009",
76
+ "type": "pattern",
77
+ "domain": "react",
78
+ "title": "State Colocation — Keep State Close to Where It's Used",
79
+ "severity": "warning",
80
+ "description": "Start with state in the component that uses it. Only lift state up when a sibling genuinely needs it. Global state managers are for truly global concerns (auth, theme, feature flags) — not for form inputs or toggle states.\n\nBad:\n```jsx\n// Redux store holds isDropdownOpen for a single component\nconst isOpen = useSelector(s => s.ui.headerDropdownOpen);\n```\n\nGood:\n```jsx\n// Local state — only this component cares\nconst [isOpen, setIsOpen] = useState(false);\n```\n\nRule of thumb: if only one component reads the state, it belongs in that component.",
81
+ "tags": ["react", "state", "architecture", "colocation"]
82
+ },
83
+ {
84
+ "id": "react-010",
85
+ "type": "anti-pattern",
86
+ "domain": "react",
87
+ "title": "Derived State — Compute, Don't Sync",
88
+ "severity": "warning",
89
+ "description": "Never use `useEffect` to synchronize state that can be computed from other state or props. Compute it inline during render, or use `useMemo` if the computation is expensive.\n\nBad:\n```jsx\nconst [items, setItems] = useState([]);\nconst [count, setCount] = useState(0);\nuseEffect(() => setCount(items.length), [items]);\n```\n\nGood:\n```jsx\nconst [items, setItems] = useState([]);\nconst count = items.length; // derived — no state needed\n```\n\nThe anti-pattern causes an extra render cycle and can lead to visual tearing where the UI briefly shows stale derived values.",
90
+ "tags": ["react", "hooks", "state", "useEffect"]
91
+ },
92
+ {
93
+ "id": "react-011",
94
+ "type": "pattern",
95
+ "domain": "react",
96
+ "title": "useRef for Mutable Values That Don't Trigger Renders",
97
+ "severity": "suggestion",
98
+ "description": "Use `useRef` for values that need to persist across renders but shouldn't cause re-renders when they change: timer IDs, previous values, DOM references, instance-level flags.\n\n```jsx\nfunction useInterval(callback, delay) {\n const savedCallback = useRef(callback);\n // Update ref on every render so the interval always calls latest callback\n useEffect(() => { savedCallback.current = callback; });\n useEffect(() => {\n const id = setInterval(() => savedCallback.current(), delay);\n return () => clearInterval(id);\n }, [delay]);\n}\n```\n\nAnti-pattern: using `useState` for values the UI never reads — it causes unnecessary re-renders.",
99
+ "tags": ["react", "hooks", "useRef", "performance"]
100
+ },
101
+ {
102
+ "id": "react-012",
103
+ "type": "pattern",
104
+ "domain": "react",
105
+ "title": "Suspense Boundaries for Async Loading",
106
+ "severity": "suggestion",
107
+ "description": "Use React Suspense to declaratively handle loading states for lazy-loaded components and data fetching (with supporting libraries). Place Suspense boundaries strategically — one per meaningful loading zone, not one per component.\n\n```jsx\nconst Dashboard = React.lazy(() => import('./Dashboard'));\n\nfunction App() {\n return (\n <Suspense fallback={<DashboardSkeleton />}>\n <Dashboard />\n </Suspense>\n );\n}\n```\n\nNest Suspense boundaries for granular loading: a page-level boundary shows a full skeleton, while a widget-level boundary shows individual widget placeholders.",
108
+ "tags": ["react", "suspense", "performance", "loading"]
109
+ },
110
+ {
111
+ "id": "react-013",
112
+ "type": "anti-pattern",
113
+ "domain": "react",
114
+ "title": "Context API Pitfall — Frequently Changing Values",
115
+ "severity": "warning",
116
+ "description": "Every component consuming a context re-renders when the context value changes — even if it only reads a stable slice. Don't put frequently changing values (mouse position, animation frames, rapidly updating form state) in context.\n\nBad:\n```jsx\n<AppContext.Provider value={{ user, theme, mousePosition }}>\n// Every mousemove re-renders ALL consumers\n```\n\nGood — split contexts by update frequency:\n```jsx\n<UserContext.Provider value={user}>\n <ThemeContext.Provider value={theme}>\n {children}\n </ThemeContext.Provider>\n</UserContext.Provider>\n```\n\nFor high-frequency values, use a ref-based subscription pattern or a dedicated state library.",
117
+ "tags": ["react", "context", "performance", "state"]
118
+ },
119
+ {
120
+ "id": "react-014",
121
+ "type": "rule",
122
+ "domain": "react",
123
+ "title": "One Component Per File",
124
+ "severity": "suggestion",
125
+ "description": "Each React component gets its own file, named to match the component. Co-locate related files (styles, tests, types) in the same directory. Small helper components used only by one parent can live in the same file, but if they grow or get reused, extract them.\n\n```\nButton/\n Button.tsx // component\n Button.test.tsx // tests\n Button.module.css // styles\n index.ts // re-export\n```\n\nThis makes components easy to find, move, and delete. A file with 5 components is a file where no one can find anything.",
126
+ "tags": ["react", "file-structure", "organization", "conventions"]
127
+ },
128
+ {
129
+ "id": "react-015",
130
+ "type": "rule",
131
+ "domain": "react",
132
+ "title": "Event Handler Naming Conventions",
133
+ "severity": "suggestion",
134
+ "description": "Use `handle*` for the function definition and `on*` for the prop name. This creates a clear contract: the parent says what event it cares about, the child says how it handles it.\n\n```jsx\n// Parent\nfunction Form() {\n const handleSubmit = (data) => { /* ... */ };\n return <SearchField onSubmit={handleSubmit} />;\n}\n\n// Child\nfunction SearchField({ onSubmit }) {\n const handleKeyDown = (e) => {\n if (e.key === 'Enter') onSubmit(e.target.value);\n };\n return <input onKeyDown={handleKeyDown} />;\n}\n```\n\nConsistent naming makes it immediately clear which functions are event handlers during code review.",
135
+ "tags": ["react", "conventions", "naming", "events"]
136
+ },
137
+ {
138
+ "id": "react-016",
139
+ "type": "anti-pattern",
140
+ "domain": "react",
141
+ "title": "useEffect as Event Handler",
142
+ "severity": "warning",
143
+ "description": "Don't use `useEffect` to respond to events. Effects are for synchronizing with external systems, not for reacting to user actions. If something should happen when a button is clicked, put it in the click handler.\n\nBad:\n```jsx\nconst [submitted, setSubmitted] = useState(false);\nuseEffect(() => {\n if (submitted) { sendAnalytics(); navigate('/thanks'); }\n}, [submitted]);\nconst handleSubmit = () => setSubmitted(true);\n```\n\nGood:\n```jsx\nconst handleSubmit = () => {\n sendAnalytics();\n navigate('/thanks');\n};\n```\n\nThe effect version adds an unnecessary state variable, an extra render, and makes the flow harder to trace.",
144
+ "tags": ["react", "hooks", "useEffect", "events"]
145
+ },
146
+ {
147
+ "id": "react-017",
148
+ "type": "pattern",
149
+ "domain": "react",
150
+ "title": "Prefer Enums and Union Types Over Boolean Props",
151
+ "severity": "suggestion",
152
+ "description": "When a component has mutually exclusive modes, use a union-typed prop instead of multiple booleans. This prevents impossible states and is self-documenting.\n\nBad:\n```jsx\n<Button isPrimary isOutlined isGhost /> // which one wins?\n```\n\nGood:\n```jsx\n<Button variant=\"primary\" />\n<Button variant=\"outlined\" />\n<Button variant=\"ghost\" />\n// type Variant = 'primary' | 'outlined' | 'ghost'\n```\n\nThis extends to size, status, and any prop where only one value should apply at a time.",
153
+ "tags": ["react", "typescript", "api-design", "components"]
154
+ },
155
+ {
156
+ "id": "react-018",
157
+ "type": "anti-pattern",
158
+ "domain": "react",
159
+ "title": "Fetching Data in useEffect Without Cleanup",
160
+ "severity": "warning",
161
+ "description": "When fetching data in `useEffect`, always handle cleanup to avoid setting state on unmounted components and to cancel stale requests.\n\nBad:\n```jsx\nuseEffect(() => {\n fetch(`/api/user/${id}`).then(r => r.json()).then(setUser);\n}, [id]);\n// Race condition: fast id changes cause stale responses to overwrite fresh ones\n```\n\nGood:\n```jsx\nuseEffect(() => {\n const controller = new AbortController();\n fetch(`/api/user/${id}`, { signal: controller.signal })\n .then(r => r.json())\n .then(setUser)\n .catch(e => { if (e.name !== 'AbortError') throw e; });\n return () => controller.abort();\n}, [id]);\n```\n\nBetter yet, use a data-fetching library (TanStack Query, SWR) that handles caching, deduplication, and race conditions.",
162
+ "tags": ["react", "hooks", "useEffect", "data-fetching", "async"]
163
+ }
164
+ ]
@@ -0,0 +1,10 @@
1
+ {
2
+ "id": "starter-security",
3
+ "name": "Security Starter Pack",
4
+ "version": "1.0.0",
5
+ "description": "OWASP Top 10, auth patterns, input validation, secrets management — essential security patterns and anti-patterns for any codebase.",
6
+ "domains": ["security"],
7
+ "vault": {
8
+ "dir": "vault"
9
+ }
10
+ }
@@ -0,0 +1,137 @@
1
+ [
2
+ {
3
+ "id": "sec-001",
4
+ "type": "pattern",
5
+ "domain": "security",
6
+ "title": "Parameterized Queries",
7
+ "description": "Always use parameterized queries or prepared statements for database access. Never concatenate user input into SQL strings. This prevents SQL injection, the most common and most dangerous web vulnerability.",
8
+ "severity": "critical",
9
+ "tags": ["sql-injection", "owasp", "database"]
10
+ },
11
+ {
12
+ "id": "sec-002",
13
+ "type": "pattern",
14
+ "domain": "security",
15
+ "title": "Input Validation at System Boundaries",
16
+ "description": "Validate and sanitize all input at system boundaries: HTTP handlers, CLI args, file readers, message consumers. Use allowlists over denylists. Validate type, length, format, and range. Reject early, fail loudly.",
17
+ "severity": "critical",
18
+ "tags": ["input-validation", "owasp", "boundaries"]
19
+ },
20
+ {
21
+ "id": "sec-003",
22
+ "type": "pattern",
23
+ "domain": "security",
24
+ "title": "Secrets in Environment Variables",
25
+ "description": "Store secrets (API keys, database passwords, tokens) in environment variables or a secrets manager. Never store them in source code, config files committed to git, or client-side bundles. Use .env files locally with .gitignore protection.",
26
+ "severity": "critical",
27
+ "tags": ["secrets", "configuration", "environment"]
28
+ },
29
+ {
30
+ "id": "sec-004",
31
+ "type": "pattern",
32
+ "domain": "security",
33
+ "title": "Principle of Least Privilege",
34
+ "description": "Grant the minimum permissions necessary for each component, service, or user. Database connections should use read-only credentials when writes are not needed. API tokens should be scoped to required operations only.",
35
+ "severity": "warning",
36
+ "tags": ["access-control", "permissions", "zero-trust"]
37
+ },
38
+ {
39
+ "id": "sec-005",
40
+ "type": "pattern",
41
+ "domain": "security",
42
+ "title": "CSRF Protection with Tokens",
43
+ "description": "Protect state-changing endpoints with CSRF tokens. Use the synchronizer token pattern or double-submit cookie. SameSite cookie attributes provide defense-in-depth but are not sufficient alone.",
44
+ "severity": "warning",
45
+ "tags": ["csrf", "owasp", "cookies"]
46
+ },
47
+ {
48
+ "id": "sec-006",
49
+ "type": "pattern",
50
+ "domain": "security",
51
+ "title": "Content Security Policy Headers",
52
+ "description": "Set Content-Security-Policy headers to prevent XSS by controlling which scripts, styles, and resources can load. Start with a strict policy and relax as needed. Avoid unsafe-inline and unsafe-eval directives.",
53
+ "severity": "warning",
54
+ "tags": ["xss", "csp", "headers", "owasp"]
55
+ },
56
+ {
57
+ "id": "sec-007",
58
+ "type": "pattern",
59
+ "domain": "security",
60
+ "title": "Rate Limiting on Authentication Endpoints",
61
+ "description": "Apply rate limiting to login, password reset, and registration endpoints. Use exponential backoff or sliding window algorithms. Lock accounts after repeated failures. This prevents brute-force and credential stuffing attacks.",
62
+ "severity": "warning",
63
+ "tags": ["authentication", "rate-limiting", "brute-force"]
64
+ },
65
+ {
66
+ "id": "sec-008",
67
+ "type": "anti-pattern",
68
+ "domain": "security",
69
+ "title": "Hardcoded Credentials",
70
+ "description": "Never hardcode passwords, API keys, or tokens in source code. They end up in version control, logs, and error messages. Use environment variables or a secrets manager instead.",
71
+ "severity": "critical",
72
+ "tags": ["secrets", "credentials", "owasp"]
73
+ },
74
+ {
75
+ "id": "sec-009",
76
+ "type": "anti-pattern",
77
+ "domain": "security",
78
+ "title": "Rolling Your Own Crypto",
79
+ "description": "Never implement custom encryption, hashing, or token generation. Use well-tested libraries: bcrypt or argon2 for passwords, AES-256-GCM for encryption, crypto.randomUUID() for tokens. Custom crypto has subtle flaws that attackers exploit.",
80
+ "severity": "critical",
81
+ "tags": ["cryptography", "hashing", "encryption"]
82
+ },
83
+ {
84
+ "id": "sec-010",
85
+ "type": "anti-pattern",
86
+ "domain": "security",
87
+ "title": "Verbose Error Messages in Production",
88
+ "description": "Do not expose stack traces, SQL queries, or internal paths in production error responses. Return generic error messages to users and log details server-side. Information disclosure helps attackers map your system.",
89
+ "severity": "warning",
90
+ "tags": ["error-handling", "information-disclosure", "owasp"]
91
+ },
92
+ {
93
+ "id": "sec-011",
94
+ "type": "anti-pattern",
95
+ "domain": "security",
96
+ "title": "Disabling SSL Certificate Verification",
97
+ "description": "Never disable TLS certificate verification in production. This opens the door to man-in-the-middle attacks. Fix certificate issues at the source: install proper CA certificates, use correct hostnames.",
98
+ "severity": "critical",
99
+ "tags": ["tls", "ssl", "mitm"]
100
+ },
101
+ {
102
+ "id": "sec-012",
103
+ "type": "rule",
104
+ "domain": "security",
105
+ "title": "Dependency Audit on Every PR",
106
+ "description": "Run npm audit or equivalent on every pull request. Block merges with known critical vulnerabilities. Keep dependencies updated since most breaches exploit known vulnerabilities in outdated packages.",
107
+ "severity": "warning",
108
+ "tags": ["dependencies", "supply-chain", "ci-cd"]
109
+ },
110
+ {
111
+ "id": "sec-013",
112
+ "type": "pattern",
113
+ "domain": "security",
114
+ "title": "Output Encoding for XSS Prevention",
115
+ "description": "Encode output based on context: HTML entity encoding for HTML body, JavaScript encoding for script contexts, URL encoding for URL parameters. Use framework auto-escaping (React JSX, template engines) and avoid injecting raw HTML.",
116
+ "severity": "critical",
117
+ "tags": ["xss", "encoding", "owasp"]
118
+ },
119
+ {
120
+ "id": "sec-014",
121
+ "type": "pattern",
122
+ "domain": "security",
123
+ "title": "Secure Session Management",
124
+ "description": "Use HttpOnly, Secure, and SameSite flags on session cookies. Regenerate session IDs after authentication. Set appropriate expiration. Store sessions server-side with cryptographically random identifiers.",
125
+ "severity": "warning",
126
+ "tags": ["sessions", "cookies", "authentication"]
127
+ },
128
+ {
129
+ "id": "sec-015",
130
+ "type": "anti-pattern",
131
+ "domain": "security",
132
+ "title": "JWT Stored in localStorage",
133
+ "description": "Do not store JWTs or sensitive tokens in localStorage because it is accessible to any JavaScript on the page, making XSS attacks devastating. Use HttpOnly cookies instead, or store in memory with refresh token rotation.",
134
+ "severity": "warning",
135
+ "tags": ["jwt", "xss", "storage", "authentication"]
136
+ }
137
+ ]
@@ -0,0 +1,9 @@
1
+ {
2
+ "id": "starter-testing",
3
+ "name": "Testing Starter Pack",
4
+ "version": "1.0.0",
5
+ "description": "Test pyramid, mocking strategies, TDD, flaky test prevention, assertion patterns — essential testing patterns.",
6
+ "domains": ["testing"],
7
+ "tier": "default",
8
+ "vault": { "dir": "vault" }
9
+ }
@@ -0,0 +1,128 @@
1
+ [
2
+ {
3
+ "id": "test-001",
4
+ "type": "pattern",
5
+ "domain": "testing",
6
+ "title": "Test Pyramid — Many Unit, Some Integration, Few E2E",
7
+ "description": "Structure your test suite as a pyramid: ~70% unit tests (fast, isolated, cheap), ~20% integration tests (verify module interactions), ~10% end-to-end tests (critical user flows only). Unit tests catch logic errors fast; integration tests catch wiring bugs; e2e tests catch real-world failures. An inverted pyramid (mostly e2e) leads to slow, brittle, expensive suites.\n\nExample ratio for a 500-test suite:\n- 350 unit tests (run in <10s)\n- 100 integration tests (run in <60s)\n- 50 e2e tests (run in <5min)",
8
+ "severity": "warning",
9
+ "tags": ["testing", "test-pyramid", "architecture"]
10
+ },
11
+ {
12
+ "id": "test-002",
13
+ "type": "pattern",
14
+ "domain": "testing",
15
+ "title": "Arrange-Act-Assert Structure for Every Test",
16
+ "description": "Every test should follow the AAA pattern for readability:\n\n```ts\ntest('calculates total with discount', () => {\n // Arrange — set up preconditions\n const cart = createCart([item(10), item(20)]);\n const discount = percent(15);\n\n // Act — execute the behavior under test\n const total = cart.applyDiscount(discount);\n\n // Assert — verify the outcome\n expect(total).toBe(25.5);\n});\n```\n\nIf you can't clearly separate these three phases, the test is likely doing too much.",
17
+ "severity": "suggestion",
18
+ "tags": ["testing", "readability", "structure"]
19
+ },
20
+ {
21
+ "id": "test-003",
22
+ "type": "rule",
23
+ "domain": "testing",
24
+ "title": "Test Behavior, Not Implementation Details",
25
+ "description": "Tests should verify what the code does, not how it does it. Testing implementation details (private methods, internal state, call order) creates brittle tests that break on every refactor.\n\nBad — tests implementation:\n```ts\nexpect(service._cache.size).toBe(1);\nexpect(fetchSpy).toHaveBeenCalledTimes(1);\n```\n\nGood — tests behavior:\n```ts\nconst first = await service.getUser(1);\nconst second = await service.getUser(1);\nexpect(first).toEqual(second); // caching works\n```\n\nAsk: 'If I refactor internals without changing behavior, will this test break?' If yes, it's testing implementation.",
26
+ "severity": "warning",
27
+ "tags": ["testing", "refactoring", "maintainability"]
28
+ },
29
+ {
30
+ "id": "test-004",
31
+ "type": "rule",
32
+ "domain": "testing",
33
+ "title": "No Test Interdependence — Each Test Runs Alone",
34
+ "description": "Every test must pass when run in isolation, in any order. Never rely on shared mutable state, test execution order, or side effects from other tests.\n\nAnti-pattern:\n```ts\nlet user;\ntest('creates user', () => { user = createUser(); });\ntest('updates user', () => { updateUser(user.id, { name: 'New' }); }); // fails if run alone\n```\n\nFix: each test creates its own data in beforeEach or in the test body. Use fresh fixtures per test. Run tests with --randomize flag to catch hidden dependencies.",
35
+ "severity": "critical",
36
+ "tags": ["testing", "isolation", "reliability"]
37
+ },
38
+ {
39
+ "id": "test-005",
40
+ "type": "pattern",
41
+ "domain": "testing",
42
+ "title": "Mock at Boundaries, Not Internals",
43
+ "description": "Mock external dependencies (HTTP APIs, databases, file system, clocks) at the boundary of your system. Don't mock internal modules or utility functions — let them run with real code.\n\n```ts\n// Good — mock the boundary (HTTP client)\nconst api = mockHttpClient({ '/users/1': { name: 'Alice' } });\nconst service = new UserService(api);\nconst user = await service.getById(1);\nexpect(user.name).toBe('Alice');\n\n// Bad — mocking internal helper\njest.mock('./formatName'); // now you're testing nothing useful\n```\n\nBoundary mocks make tests fast and deterministic without sacrificing coverage of real logic.",
44
+ "severity": "warning",
45
+ "tags": ["testing", "mocking", "boundaries", "dependency-injection"]
46
+ },
47
+ {
48
+ "id": "test-006",
49
+ "type": "pattern",
50
+ "domain": "testing",
51
+ "title": "Use Test Factories for Complex Object Setup",
52
+ "description": "Create factory functions that produce valid test objects with sensible defaults. Override only what matters for each test.\n\n```ts\nfunction buildUser(overrides: Partial<User> = {}): User {\n return {\n id: randomId(),\n name: 'Test User',\n email: 'test@example.com',\n role: 'viewer',\n createdAt: new Date('2024-01-01'),\n ...overrides,\n };\n}\n\n// In tests — only specify what matters\ntest('admins can delete', () => {\n const admin = buildUser({ role: 'admin' });\n expect(canDelete(admin)).toBe(true);\n});\n```\n\nFactories reduce noise, prevent fragile tests, and make required vs incidental data obvious.",
53
+ "severity": "suggestion",
54
+ "tags": ["testing", "factories", "test-data", "maintainability"]
55
+ },
56
+ {
57
+ "id": "test-007",
58
+ "type": "pattern",
59
+ "domain": "testing",
60
+ "title": "One Logical Assertion Per Test",
61
+ "description": "Each test should verify one behavior. Multiple assertions are fine when they verify different aspects of the same behavior. Multiple assertions testing different behaviors should be separate tests.\n\nGood — one behavior, multiple asserts:\n```ts\ntest('creates user with defaults', () => {\n const user = createUser({ name: 'Alice' });\n expect(user.name).toBe('Alice');\n expect(user.role).toBe('viewer'); // default\n expect(user.active).toBe(true); // default\n});\n```\n\nBad — two behaviors in one test:\n```ts\ntest('user lifecycle', () => {\n const user = createUser({ name: 'Alice' });\n expect(user.active).toBe(true);\n deactivateUser(user.id);\n expect(user.active).toBe(false); // separate behavior\n});\n```",
62
+ "severity": "suggestion",
63
+ "tags": ["testing", "assertions", "readability"]
64
+ },
65
+ {
66
+ "id": "test-008",
67
+ "type": "anti-pattern",
68
+ "domain": "testing",
69
+ "title": "Flaky Tests — Never Use Sleep, Use Polling",
70
+ "description": "Never use fixed delays (sleep, setTimeout) to wait for async operations. They're either too slow (wasted CI time) or too fast (flaky failures).\n\nBad:\n```ts\nawait sleep(2000);\nexpect(screen.getByText('Done')).toBeVisible();\n```\n\nGood:\n```ts\nawait waitFor(() => {\n expect(screen.getByText('Done')).toBeVisible();\n}, { timeout: 5000 });\n```\n\nOther flaky test sources: shared state, network calls without mocks, time-dependent logic without fake clocks, random data without seeds. Fix the root cause — never retry-until-green.",
71
+ "severity": "critical",
72
+ "tags": ["testing", "flaky-tests", "async", "ci"]
73
+ },
74
+ {
75
+ "id": "test-009",
76
+ "type": "pattern",
77
+ "domain": "testing",
78
+ "title": "Descriptive Test Names: should_expected_when_condition",
79
+ "description": "Test names should describe the expected behavior and the condition that triggers it. Use a consistent naming convention across the suite.\n\nGood names:\n```ts\ntest('should return 404 when user does not exist', ...)\ntest('should apply discount when cart total exceeds 100', ...)\ntest('should reject empty email on registration', ...)\n```\n\nBad names:\n```ts\ntest('test1', ...)\ntest('works correctly', ...)\ntest('handles edge case', ...)\n```\n\nWhen a test fails, its name should tell you what broke without reading the code.",
80
+ "severity": "suggestion",
81
+ "tags": ["testing", "naming", "readability"]
82
+ },
83
+ {
84
+ "id": "test-010",
85
+ "type": "anti-pattern",
86
+ "domain": "testing",
87
+ "title": "Snapshot Testing Overuse",
88
+ "description": "Snapshot tests capture output and compare against a stored reference. They're useful for catching unexpected changes but dangerous when overused — developers blindly update snapshots without reviewing diffs.\n\nGood use: serialized data structures, CLI output, small component renders.\nBad use: large component trees, frequently changing UI, API responses.\n\nRules:\n- Keep snapshots small and focused\n- Always review snapshot diffs in code review\n- Use inline snapshots for small values: expect(result).toMatchInlineSnapshot()\n- If a snapshot updates on every change, replace it with explicit assertions",
89
+ "severity": "warning",
90
+ "tags": ["testing", "snapshots", "code-review"]
91
+ },
92
+ {
93
+ "id": "test-011",
94
+ "type": "rule",
95
+ "domain": "testing",
96
+ "title": "Code Coverage Is a Guide, Not a Goal",
97
+ "description": "Aim for ~80% code coverage as a healthy baseline. Don't chase 100% — it leads to low-value tests that test getters, setters, and trivial code.\n\nWhat to cover:\n- Business logic and domain rules\n- Error handling paths\n- Edge cases and boundary conditions\n- Integration points\n\nWhat to skip:\n- Simple getters/setters\n- Framework boilerplate\n- Generated code\n- Type-only files\n\nCoverage tells you what's NOT tested. High coverage doesn't mean good tests — you can have 100% coverage with zero meaningful assertions.",
98
+ "severity": "warning",
99
+ "tags": ["testing", "coverage", "metrics"]
100
+ },
101
+ {
102
+ "id": "test-012",
103
+ "type": "pattern",
104
+ "domain": "testing",
105
+ "title": "TDD Red-Green-Refactor Cycle",
106
+ "description": "Write tests before implementation in three steps:\n\n1. RED — Write a failing test for the next piece of behavior:\n```ts\ntest('should hash password before saving', async () => {\n const user = await createUser({ password: 'plain' });\n expect(user.password).not.toBe('plain');\n expect(user.password).toMatch(/^\\$2b\\$/);\n});\n```\n\n2. GREEN — Write the minimum code to make it pass. No more.\n\n3. REFACTOR — Clean up the implementation while keeping tests green.\n\nTDD catches design issues early — if a test is hard to write, the API is hard to use. Small cycles (5-10 min) keep you focused and prevent over-engineering.",
107
+ "severity": "suggestion",
108
+ "tags": ["testing", "tdd", "workflow"]
109
+ },
110
+ {
111
+ "id": "test-013",
112
+ "type": "anti-pattern",
113
+ "domain": "testing",
114
+ "title": "Testing Through the UI for Logic Validation",
115
+ "description": "Don't write e2e or browser tests to verify business logic. If you need to check that a discount calculation is correct, test the calculation function directly — not by filling out a form, clicking submit, and reading the total from the DOM.\n\n```ts\n// Bad — slow, brittle, tests too many things at once\ntest('discount works', async () => {\n await page.fill('#amount', '100');\n await page.click('#apply-discount');\n expect(await page.textContent('#total')).toBe('$85.00');\n});\n\n// Good — fast, focused, tests the actual logic\ntest('applies 15% discount', () => {\n expect(applyDiscount(100, 0.15)).toBe(85);\n});\n```\n\nPush logic tests down to the unit level. Use e2e only for user flow validation.",
116
+ "severity": "warning",
117
+ "tags": ["testing", "test-pyramid", "e2e", "unit-tests"]
118
+ },
119
+ {
120
+ "id": "test-014",
121
+ "type": "pattern",
122
+ "domain": "testing",
123
+ "title": "Parameterized Tests for Multiple Input Cases",
124
+ "description": "When testing the same behavior with different inputs, use parameterized tests (test.each / it.each) instead of duplicating test bodies.\n\n```ts\ntest.each([\n ['empty string', '', false],\n ['valid email', 'a@b.com', true],\n ['missing @', 'ab.com', false],\n ['missing domain', 'a@', false],\n ['unicode local', 'ñ@b.com', true],\n])('isValidEmail(%s) returns %s', (_label, input, expected) => {\n expect(isValidEmail(input)).toBe(expected);\n});\n```\n\nBenefits: less duplication, easy to add cases, each case reports separately on failure. Use for validators, parsers, formatters, and any pure function with varied inputs.",
125
+ "severity": "suggestion",
126
+ "tags": ["testing", "parameterized", "dry", "readability"]
127
+ }
128
+ ]
@@ -0,0 +1,9 @@
1
+ {
2
+ "id": "starter-typescript",
3
+ "name": "TypeScript Starter Pack",
4
+ "version": "1.0.0",
5
+ "description": "Type narrowing, generics, module patterns, strict config, discriminated unions — essential TypeScript patterns for production code.",
6
+ "domains": ["typescript"],
7
+ "tier": "default",
8
+ "vault": { "dir": "vault" }
9
+ }