@drewpayment/mink 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +347 -0
- package/package.json +32 -0
- package/src/cli.ts +176 -0
- package/src/commands/bug-search.ts +32 -0
- package/src/commands/config.ts +109 -0
- package/src/commands/cron.ts +295 -0
- package/src/commands/daemon.ts +46 -0
- package/src/commands/dashboard.ts +21 -0
- package/src/commands/designqc.ts +160 -0
- package/src/commands/detect-waste.ts +81 -0
- package/src/commands/framework-advisor.ts +52 -0
- package/src/commands/init.ts +159 -0
- package/src/commands/post-read.ts +123 -0
- package/src/commands/post-write.ts +157 -0
- package/src/commands/pre-read.ts +109 -0
- package/src/commands/pre-write.ts +136 -0
- package/src/commands/reflect.ts +39 -0
- package/src/commands/restore.ts +31 -0
- package/src/commands/scan.ts +101 -0
- package/src/commands/session-start.ts +21 -0
- package/src/commands/session-stop.ts +115 -0
- package/src/commands/status.ts +152 -0
- package/src/commands/update.ts +121 -0
- package/src/core/action-log.ts +341 -0
- package/src/core/backup.ts +122 -0
- package/src/core/bug-memory.ts +223 -0
- package/src/core/cron-parser.ts +94 -0
- package/src/core/daemon.ts +152 -0
- package/src/core/dashboard-api.ts +280 -0
- package/src/core/dashboard-server.ts +580 -0
- package/src/core/description.ts +232 -0
- package/src/core/design-eval/capture.ts +269 -0
- package/src/core/design-eval/route-detect.ts +165 -0
- package/src/core/design-eval/server-detect.ts +91 -0
- package/src/core/framework-advisor/catalog.ts +360 -0
- package/src/core/framework-advisor/decision-tree.ts +287 -0
- package/src/core/framework-advisor/generate.ts +132 -0
- package/src/core/framework-advisor/migration-prompts.ts +502 -0
- package/src/core/framework-advisor/validate.ts +137 -0
- package/src/core/fs-utils.ts +30 -0
- package/src/core/global-config.ts +74 -0
- package/src/core/index-store.ts +72 -0
- package/src/core/learning-memory.ts +120 -0
- package/src/core/paths.ts +86 -0
- package/src/core/pattern-engine.ts +108 -0
- package/src/core/project-id.ts +19 -0
- package/src/core/project-registry.ts +64 -0
- package/src/core/reflection.ts +256 -0
- package/src/core/scanner.ts +99 -0
- package/src/core/scheduler.ts +352 -0
- package/src/core/seed.ts +239 -0
- package/src/core/session.ts +128 -0
- package/src/core/stdin.ts +13 -0
- package/src/core/task-registry.ts +202 -0
- package/src/core/token-estimate.ts +36 -0
- package/src/core/token-ledger.ts +185 -0
- package/src/core/waste-detection.ts +214 -0
- package/src/core/write-exclusions.ts +24 -0
- package/src/types/action-log.ts +20 -0
- package/src/types/backup.ts +6 -0
- package/src/types/bug-memory.ts +24 -0
- package/src/types/config.ts +59 -0
- package/src/types/dashboard.ts +104 -0
- package/src/types/design-eval.ts +64 -0
- package/src/types/file-index.ts +38 -0
- package/src/types/framework-advisor.ts +97 -0
- package/src/types/hook-input.ts +27 -0
- package/src/types/learning-memory.ts +36 -0
- package/src/types/scheduler.ts +82 -0
- package/src/types/session.ts +50 -0
- package/src/types/token-ledger.ts +43 -0
- package/src/types/waste-detection.ts +21 -0
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
// ── Migration Prompts for All 14 Frameworks (Spec 14) ──────────────────────
|
|
2
|
+
|
|
3
|
+
import type { MigrationPrompt } from "../../types/framework-advisor";
|
|
4
|
+
|
|
5
|
+
export const MIGRATION_PROMPTS: MigrationPrompt[] = [
|
|
6
|
+
{
|
|
7
|
+
frameworkId: "tailwind-headlessui",
|
|
8
|
+
sections: [
|
|
9
|
+
{
|
|
10
|
+
key: "install",
|
|
11
|
+
content:
|
|
12
|
+
"Install both packages with `npm install tailwindcss @headlessui/react`. Run `npx tailwindcss init -p` to generate `tailwind.config.js` and `postcss.config.js`. Requires React 18+ for Headless UI v2.",
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
key: "configure",
|
|
16
|
+
content:
|
|
17
|
+
"Add your source paths to the `content` array in `tailwind.config.js`. Enable the forms and typography plugins if needed via `@tailwindcss/forms` and `@tailwindcss/typography`. Import Tailwind directives (`@tailwind base; @tailwind components; @tailwind utilities;`) in your root CSS file.",
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
key: "migrate-components",
|
|
21
|
+
content:
|
|
22
|
+
"Replace custom dropdowns, modals, and toggles with Headless UI's `Menu`, `Dialog`, and `Switch` components. These provide accessible behavior with zero styling — you add all styles via Tailwind classes. Map old components: Modal → Dialog, Select → Listbox, Tooltip → Popover.",
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
key: "migrate-styles",
|
|
26
|
+
content:
|
|
27
|
+
"Remove existing CSS-in-JS libraries or component CSS files. Convert all styles to Tailwind utility classes directly on elements. Use the `@apply` directive sparingly in CSS files for repeated patterns. Leverage Tailwind's `group` and `peer` modifiers for interactive states.",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
key: "gotchas",
|
|
31
|
+
content:
|
|
32
|
+
"Headless UI components are unstyled by default — you must provide all visual styles yourself. The `Transition` component requires careful ordering of enter/leave classes. PurgeCSS (built into Tailwind) may remove dynamically constructed class names, so avoid string concatenation for classes.",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
key: "verification",
|
|
36
|
+
content:
|
|
37
|
+
"Run `npx tailwindcss --content ./src/**/*.tsx --output /dev/null` to verify class scanning works. Test all Headless UI components for keyboard navigation and screen reader behavior. Check that the production build tree-shakes unused Tailwind classes correctly.",
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
frameworkId: "shadcn-ui",
|
|
43
|
+
sections: [
|
|
44
|
+
{
|
|
45
|
+
key: "install",
|
|
46
|
+
content:
|
|
47
|
+
"Run `npx shadcn@latest init` to scaffold the project. Install individual components with `npx shadcn@latest add button dialog` etc. Requires React 18+ and Tailwind CSS 3.4+.",
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
key: "configure",
|
|
51
|
+
content:
|
|
52
|
+
"The init command creates `components.json` for path aliases and styling preferences. Configure your `tailwind.config.ts` to include the shadcn preset. Set up CSS variables in `globals.css` for theming.",
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
key: "migrate-components",
|
|
56
|
+
content:
|
|
57
|
+
"Replace existing UI primitives one-by-one. shadcn components are copied into your codebase at `components/ui/`, so you own the source. Map old components: Button → Button, Modal → Dialog, Dropdown → DropdownMenu.",
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
key: "migrate-styles",
|
|
61
|
+
content:
|
|
62
|
+
"Remove old CSS-in-JS or component library styles. shadcn uses Tailwind utility classes with CSS variables for theming. Update className props to use the `cn()` utility for conditional classes.",
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
key: "gotchas",
|
|
66
|
+
content:
|
|
67
|
+
"Components are copied, not installed as a package — updates require re-running the add command or manually merging. Some components depend on Radix UI primitives under the hood. Dark mode requires the `dark` class strategy in Tailwind config.",
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
key: "verification",
|
|
71
|
+
content:
|
|
72
|
+
"Run `npx tsc --noEmit` to check for type errors. Verify each migrated component renders correctly in both light and dark modes. Run your existing test suite to catch regressions.",
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
frameworkId: "mui",
|
|
78
|
+
sections: [
|
|
79
|
+
{
|
|
80
|
+
key: "install",
|
|
81
|
+
content:
|
|
82
|
+
"Install the core packages with `npm install @mui/material @emotion/react @emotion/styled`. For icons, add `@mui/icons-material`. MUI v6 requires React 18+.",
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
key: "configure",
|
|
86
|
+
content:
|
|
87
|
+
"Create a custom theme with `createTheme()` and wrap your app in `<ThemeProvider theme={theme}>`. Add `<CssBaseline />` inside the provider to normalize browser styles. Configure typography, palette, and breakpoints in the theme object.",
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
key: "migrate-components",
|
|
91
|
+
content:
|
|
92
|
+
"MUI provides near-complete coverage of common UI patterns. Map old components: Button → Button, Modal → Dialog, Input → TextField, Select → Select, Tabs → Tabs. Use `sx` prop for one-off styles and `styled()` for reusable styled components.",
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
key: "migrate-styles",
|
|
96
|
+
content:
|
|
97
|
+
"Replace CSS modules or plain CSS with MUI's `sx` prop or the `styled()` API from `@mui/material/styles`. Access theme values directly in `sx` via callbacks like `sx={{ p: (t) => t.spacing(2) }}`. Remove old global stylesheets that conflict with MUI's baseline.",
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
key: "gotchas",
|
|
101
|
+
content:
|
|
102
|
+
"MUI's bundle size is significant — use path imports like `@mui/material/Button` instead of named imports for better tree-shaking. Emotion's SSR requires additional setup with `createCache` and `CacheProvider`. The `sx` prop only works on MUI components, not native HTML elements.",
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
key: "verification",
|
|
106
|
+
content:
|
|
107
|
+
"Run `npx tsc --noEmit` and verify no type conflicts between MUI's theme types and your custom declarations. Test SSR pages for FOUC (flash of unstyled content) caused by missing Emotion cache. Confirm the production bundle size with `npx source-map-explorer`.",
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
frameworkId: "chakra-ui",
|
|
113
|
+
sections: [
|
|
114
|
+
{
|
|
115
|
+
key: "install",
|
|
116
|
+
content:
|
|
117
|
+
"Install with `npm install @chakra-ui/react @emotion/react @emotion/styled framer-motion`. Chakra UI v3 drops the framer-motion peer dependency, so check your target version. Requires React 18+.",
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
key: "configure",
|
|
121
|
+
content:
|
|
122
|
+
"Wrap your app in `<ChakraProvider>` which includes a default theme and CSS reset. Extend the default theme with `extendTheme()` to set custom colors, fonts, and component variants. For Chakra v3, use `createSystem()` and `defaultConfig` instead.",
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
key: "migrate-components",
|
|
126
|
+
content:
|
|
127
|
+
"Map old components: Modal → Modal, Input → Input, Button → Button, Select → Select, Toast → useToast hook. Chakra's `Stack`, `Flex`, and `Grid` components replace most custom layout CSS. Use the `as` prop to render semantic HTML elements.",
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
key: "migrate-styles",
|
|
131
|
+
content:
|
|
132
|
+
"Replace CSS files with Chakra's style props directly on components (e.g., `<Box p={4} bg='gray.100'>`). Use the `useColorModeValue` hook for dark mode style switching. Token-based spacing and color scales are available out of the box.",
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
key: "gotchas",
|
|
136
|
+
content:
|
|
137
|
+
"Chakra v2 to v3 is a breaking migration — the theming API changed from `extendTheme` to a system-based approach. Framer Motion adds ~30KB gzipped to the bundle in v2. Color mode may flash on SSR without proper cookie-based persistence setup.",
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
key: "verification",
|
|
141
|
+
content:
|
|
142
|
+
"Toggle between light and dark modes and verify no style flicker on page load. Run accessibility checks with `axe-core` — Chakra components should pass WCAG 2.1 AA. Verify that custom theme tokens apply correctly across all migrated components.",
|
|
143
|
+
},
|
|
144
|
+
],
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
frameworkId: "ant-design",
|
|
148
|
+
sections: [
|
|
149
|
+
{
|
|
150
|
+
key: "install",
|
|
151
|
+
content:
|
|
152
|
+
"Install with `npm install antd`. Ant Design v5 removed the need for separate CSS imports — styles are injected via CSS-in-JS at runtime. For icons, install `@ant-design/icons` separately.",
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
key: "configure",
|
|
156
|
+
content:
|
|
157
|
+
"Wrap your app in `<ConfigProvider theme={{ token: { colorPrimary: '#1677ff' } }}>` to customize the design token system. Use `theme.algorithm` to switch between `theme.defaultAlgorithm`, `theme.darkAlgorithm`, and `theme.compactAlgorithm`. No separate CSS import is needed in v5+.",
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
key: "migrate-components",
|
|
161
|
+
content:
|
|
162
|
+
"Ant Design covers a very wide range of enterprise components. Map: Modal → Modal, Table → Table (with built-in sort/filter/pagination), Form → Form (with built-in validation via rules), DatePicker → DatePicker. Use `App.useApp()` for message/notification/modal imperative APIs.",
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
key: "migrate-styles",
|
|
166
|
+
content:
|
|
167
|
+
"Remove any v4 `import 'antd/dist/antd.css'` imports. In v5, override styles through the `ConfigProvider` token system or component-level `styles` and `classNames` props. Avoid `!important` overrides — use the `theme.components` config for per-component tokens.",
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
key: "gotchas",
|
|
171
|
+
content:
|
|
172
|
+
"Ant Design's CSS-in-JS runtime (cssinjs) can cause SSR hydration mismatches — use `extractStyle` from `@ant-design/cssinjs` for server rendering. The `dayjs` locale must be imported manually for DatePicker localization. Bundle size is large; use `babel-plugin-import` or tree-shaking for optimization.",
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
key: "verification",
|
|
176
|
+
content:
|
|
177
|
+
"Verify SSR pages don't flash unstyled content by checking the HTML source for inline `<style>` tags. Run `npx tsc --noEmit` to catch any v4-to-v5 API breakages. Test Form validation rules and Table pagination with realistic data sets.",
|
|
178
|
+
},
|
|
179
|
+
],
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
frameworkId: "radix-ui",
|
|
183
|
+
sections: [
|
|
184
|
+
{
|
|
185
|
+
key: "install",
|
|
186
|
+
content:
|
|
187
|
+
"Install individual primitives as needed: `npm install @radix-ui/react-dialog @radix-ui/react-dropdown-menu @radix-ui/react-tooltip`. Each primitive is a separate package for optimal tree-shaking. Requires React 18+.",
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
key: "configure",
|
|
191
|
+
content:
|
|
192
|
+
"Radix primitives are unstyled and require no global configuration. Wrap your app in `<Theme>` from `@radix-ui/themes` only if using the pre-styled Radix Themes layer. For primitives alone, configure your own CSS strategy (Tailwind, CSS Modules, etc.).",
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
key: "migrate-components",
|
|
196
|
+
content:
|
|
197
|
+
"Map old components: Modal → Dialog, Dropdown → DropdownMenu, Popover → Popover, Switch → Switch, Tabs → Tabs. Each Radix component uses a compound pattern (e.g., `Dialog.Root`, `Dialog.Trigger`, `Dialog.Content`). Compose your own styled wrappers around the primitives.",
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
key: "migrate-styles",
|
|
201
|
+
content:
|
|
202
|
+
"Radix exposes data attributes like `data-state='open'` that you target with CSS selectors or Tailwind's `data-[state=open]:` modifier. Animations should use CSS transitions on these data attributes rather than JS-based animation. Remove any prior library's scoped styles that conflict.",
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
key: "gotchas",
|
|
206
|
+
content:
|
|
207
|
+
"Radix primitives manage focus trapping, arrow-key navigation, and escape-to-close internally — don't add your own handlers that conflict. Portal-based components render outside the DOM hierarchy, which can break CSS inheritance. Some primitives require `asChild` to forward props to custom trigger elements.",
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
key: "verification",
|
|
211
|
+
content:
|
|
212
|
+
"Test keyboard navigation for every Radix primitive: Tab, Enter, Escape, and arrow keys should work as expected. Verify that portaled content (Dialog, Popover) renders in the correct stacking context. Run screen reader tests to confirm ARIA attributes are applied correctly.",
|
|
213
|
+
},
|
|
214
|
+
],
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
frameworkId: "mantine",
|
|
218
|
+
sections: [
|
|
219
|
+
{
|
|
220
|
+
key: "install",
|
|
221
|
+
content:
|
|
222
|
+
"Install with `npm install @mantine/core @mantine/hooks @mantine/form`. Add `postcss-preset-mantine` and `postcss-simple-vars` as dev dependencies for the PostCSS-based styling. Requires React 18+.",
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
key: "configure",
|
|
226
|
+
content:
|
|
227
|
+
"Wrap your app in `<MantineProvider>` and create a theme with `createTheme()`. Configure PostCSS by adding `postcss-preset-mantine` to your `postcss.config.js`. Import `@mantine/core/styles.css` in your root entry point.",
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
key: "migrate-components",
|
|
231
|
+
content:
|
|
232
|
+
"Mantine has 100+ components covering most needs. Map: Modal → Modal, Button → Button, Input → TextInput, Select → Select, Tabs → Tabs, Toast → notifications (from `@mantine/notifications`). Use `@mantine/dates` for date pickers and `@mantine/dropzone` for file uploads.",
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
key: "migrate-styles",
|
|
236
|
+
content:
|
|
237
|
+
"Use Mantine's `classNames` and `styles` props for component overrides. For custom styling, use CSS Modules (the recommended approach) with `.module.css` files. Access theme tokens in CSS via `var(--mantine-color-blue-6)` CSS variables.",
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
key: "gotchas",
|
|
241
|
+
content:
|
|
242
|
+
"Mantine v7 switched from Emotion (CSS-in-JS) to PostCSS — migrating from v6 requires replacing all `createStyles` calls. The `@mantine/core/styles.css` import is required or components render unstyled. SSR requires `ColorSchemeScript` in the HTML head to prevent color scheme flicker.",
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
key: "verification",
|
|
246
|
+
content:
|
|
247
|
+
"Verify the PostCSS build outputs correct CSS by inspecting the compiled styles. Test color scheme switching between light, dark, and auto modes. Run `npx tsc --noEmit` to check that Mantine's strict prop types are satisfied.",
|
|
248
|
+
},
|
|
249
|
+
],
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
frameworkId: "daisyui",
|
|
253
|
+
sections: [
|
|
254
|
+
{
|
|
255
|
+
key: "install",
|
|
256
|
+
content:
|
|
257
|
+
"Install with `npm install daisyui` and add it as a Tailwind plugin in `tailwind.config.js`: `plugins: [require('daisyui')]`. DaisyUI requires Tailwind CSS 3.0+ as a peer dependency.",
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
key: "configure",
|
|
261
|
+
content:
|
|
262
|
+
"Configure available themes in `tailwind.config.js` under `daisyui: { themes: ['light', 'dark', 'cupcake'] }`. Set a default theme with `data-theme` attribute on your `<html>` element. Create custom themes by providing a theme object in the themes array.",
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
key: "migrate-components",
|
|
266
|
+
content:
|
|
267
|
+
"DaisyUI provides semantic CSS classes like `btn`, `card`, `modal`, `input`, `navbar`. Replace custom component styles with these classes directly on HTML elements. Map old components to class-based markup: Button → `<button class='btn btn-primary'>`, Modal → `<dialog class='modal'>`.",
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
key: "migrate-styles",
|
|
271
|
+
content:
|
|
272
|
+
"Replace existing component CSS with DaisyUI's semantic classes. Combine DaisyUI classes with Tailwind utilities for fine-tuning (e.g., `btn btn-primary w-full mt-4`). Remove redundant color and spacing definitions that are now handled by themes.",
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
key: "gotchas",
|
|
276
|
+
content:
|
|
277
|
+
"DaisyUI is CSS-only with no JavaScript — interactive behaviors like modal open/close must be handled by your framework. Some class names like `btn` may conflict with other libraries. Theme switching requires JavaScript to update the `data-theme` attribute on the root element.",
|
|
278
|
+
},
|
|
279
|
+
{
|
|
280
|
+
key: "verification",
|
|
281
|
+
content:
|
|
282
|
+
"Cycle through all configured themes and verify consistent rendering. Check that DaisyUI classes don't conflict with existing Tailwind utility classes. Verify interactive components (modals, dropdowns) work correctly with your own JS event handlers.",
|
|
283
|
+
},
|
|
284
|
+
],
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
frameworkId: "vuetify",
|
|
288
|
+
sections: [
|
|
289
|
+
{
|
|
290
|
+
key: "install",
|
|
291
|
+
content:
|
|
292
|
+
"Install with `npm install vuetify` and add the Vite plugin via `npm install -D vite-plugin-vuetify`. For a new project, use `npm create vuetify@latest`. Requires Vue 3.3+.",
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
key: "configure",
|
|
296
|
+
content:
|
|
297
|
+
"Create a Vuetify instance with `createVuetify()` in a plugin file and pass it to `app.use(vuetify)`. Configure the Vite plugin in `vite.config.ts` for automatic component tree-shaking. Import `vuetify/styles` in your main entry file and optionally add `@mdi/font` for Material Design Icons.",
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
key: "migrate-components",
|
|
301
|
+
content:
|
|
302
|
+
"Vuetify provides a comprehensive Material Design component set. Map: Modal → v-dialog, Button → v-btn, Input → v-text-field, Select → v-select, Tabs → v-tabs, DataTable → v-data-table. All components use the `v-` prefix and follow Material Design 3 guidelines.",
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
key: "migrate-styles",
|
|
306
|
+
content:
|
|
307
|
+
"Remove existing component CSS and let Vuetify's SASS-based theme handle styling. Customize via the `theme` option in `createVuetify()` with `colors`, `variables`, and dark/light variations. Use the `class` and `style` props alongside Vuetify's built-in utility classes like `ma-4`, `pa-2`, `text-h6`.",
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
key: "gotchas",
|
|
311
|
+
content:
|
|
312
|
+
"Vuetify v3 is Vue 3 only — there is no backward compatibility with Vue 2. The `vite-plugin-vuetify` is essential for tree-shaking; without it, the full library is bundled (~300KB gzipped). SSR with Nuxt requires the `vuetify-nuxt-module` for proper style injection.",
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
key: "verification",
|
|
316
|
+
content:
|
|
317
|
+
"Run `npx vue-tsc --noEmit` to verify TypeScript types. Check the bundle analyzer output to confirm tree-shaking is working via the Vite plugin. Test responsive breakpoints using Vuetify's `useDisplay()` composable.",
|
|
318
|
+
},
|
|
319
|
+
],
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
frameworkId: "primevue",
|
|
323
|
+
sections: [
|
|
324
|
+
{
|
|
325
|
+
key: "install",
|
|
326
|
+
content:
|
|
327
|
+
"Install with `npm install primevue @primevue/themes`. For icons, add `npm install primeicons`. PrimeVue v4 requires Vue 3.4+ and uses a new theming architecture.",
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
key: "configure",
|
|
331
|
+
content:
|
|
332
|
+
"Register PrimeVue in your app with `app.use(PrimeVue, { theme: { preset: Aura } })` using one of the built-in presets (Aura, Lara, Nora). Import `primeicons/primeicons.css` for the icon font. Configure individual component defaults through the `pt` (pass-through) option.",
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
key: "migrate-components",
|
|
336
|
+
content:
|
|
337
|
+
"PrimeVue offers 90+ components including advanced data components. Map: Modal → Dialog, Button → Button, Input → InputText, Select → Dropdown, DataTable → DataTable (with sort, filter, pagination, row expansion). Use the `AutoComplete`, `TreeTable`, and `OrganizationChart` for complex UI needs.",
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
key: "migrate-styles",
|
|
341
|
+
content:
|
|
342
|
+
"PrimeVue v4 uses a design token system via the `@primevue/themes` package. Override tokens at the global or component level using `definePreset()`. Remove old PrimeVue v3 CSS theme imports (`primevue/resources/themes/...`) as v4 handles theming differently.",
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
key: "gotchas",
|
|
346
|
+
content:
|
|
347
|
+
"PrimeVue v3 to v4 is a major migration — the theme architecture completely changed from CSS to a design-token system. The `pt` (pass-through) API replaces direct CSS class overrides. Some component prop names changed between v3 and v4, so consult the migration guide.",
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
key: "verification",
|
|
351
|
+
content:
|
|
352
|
+
"Run `npx vue-tsc --noEmit` to verify type safety. Test DataTable with large datasets (1000+ rows) to confirm virtual scrolling performance. Verify that the chosen preset applies consistently across all components in both light and dark modes.",
|
|
353
|
+
},
|
|
354
|
+
],
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
frameworkId: "skeleton-svelte",
|
|
358
|
+
sections: [
|
|
359
|
+
{
|
|
360
|
+
key: "install",
|
|
361
|
+
content:
|
|
362
|
+
"Install with `npm install @skeletonlabs/skeleton @skeletonlabs/tw-plugin`. Skeleton requires Tailwind CSS 3.0+ and SvelteKit as the recommended framework. Add the plugin to your Tailwind config.",
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
key: "configure",
|
|
366
|
+
content:
|
|
367
|
+
"Add `skeleton()` to the plugins array in `tailwind.config.ts` and register a theme: `skeleton({ themes: { preset: ['skeleton', 'wintry', 'modern'] } })`. Import Skeleton's base styles with `import '@skeletonlabs/skeleton/styles/all.css'` in your root layout. Set the active theme on the `<body>` element with `data-theme`.",
|
|
368
|
+
},
|
|
369
|
+
{
|
|
370
|
+
key: "migrate-components",
|
|
371
|
+
content:
|
|
372
|
+
"Map old components: Modal → Modal (via `getModalStore()`), Button → button with Skeleton classes, Input → input elements with `input` class, Tabs → TabGroup/Tab, Table → Table with `tableMapperValues`. Skeleton uses Svelte actions and stores for interactive behaviors.",
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
key: "migrate-styles",
|
|
376
|
+
content:
|
|
377
|
+
"Replace component CSS with Skeleton's Tailwind-integrated utility classes and design tokens. Use token classes like `variant-filled-primary`, `variant-ghost-secondary` for consistent theming. Access theme CSS variables like `var(--color-primary-500)` for custom styling.",
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
key: "gotchas",
|
|
381
|
+
content:
|
|
382
|
+
"Skeleton v2 introduced breaking changes to the theming system and component APIs from v1. Interactive components rely on Svelte stores (e.g., `modalStore`, `toastStore`) which must be initialized in the root layout. Skeleton is Svelte-only — there is no React or Vue version.",
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
key: "verification",
|
|
386
|
+
content:
|
|
387
|
+
"Switch between installed themes using the `data-theme` attribute and verify consistent rendering. Test store-based components (Modal, Toast, Drawer) for proper initialization and cleanup. Verify SvelteKit SSR renders Skeleton components without hydration errors.",
|
|
388
|
+
},
|
|
389
|
+
],
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
frameworkId: "angular-material",
|
|
393
|
+
sections: [
|
|
394
|
+
{
|
|
395
|
+
key: "install",
|
|
396
|
+
content:
|
|
397
|
+
"Run `ng add @angular/material` which installs the package, sets up animations, and configures a theme. This schematic modifies `angular.json`, `app.module.ts`, and `styles.css` automatically. Requires Angular 17+.",
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
key: "configure",
|
|
401
|
+
content:
|
|
402
|
+
"Import individual component modules (e.g., `MatButtonModule`, `MatDialogModule`) in your feature modules or use standalone component imports. Configure a custom theme in your SCSS by calling `@use '@angular/material' as mat` and defining custom palettes with `mat.define-theme()`. Add `provideAnimationsAsync()` to your app config.",
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
key: "migrate-components",
|
|
406
|
+
content:
|
|
407
|
+
"Map old components: Modal → MatDialog (opened via `MatDialog.open()`), Button → mat-button/mat-raised-button, Input → matInput directive on `<input>`, Table → mat-table with matSort and matPaginator. Angular Material uses directives heavily — most components are attribute selectors on native elements.",
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
key: "migrate-styles",
|
|
411
|
+
content:
|
|
412
|
+
"Use Angular Material's SCSS theming API to define `$theme: mat.define-theme(...)` and apply it with `@include mat.all-component-themes($theme)`. Override individual component styles using `::ng-deep` sparingly or the component's `encapsulation: ViewEncapsulation.None`. Use CSS custom properties exposed by M3 theme for custom component styling.",
|
|
413
|
+
},
|
|
414
|
+
{
|
|
415
|
+
key: "gotchas",
|
|
416
|
+
content:
|
|
417
|
+
"Angular Material v17+ defaults to Material Design 3 (M3) — M2 compatibility is available but deprecated. The `MatDialog` service injects via DI, not template markup, which is a different pattern from most frameworks. `ViewEncapsulation` can prevent style overrides; prefer `::ng-deep` or global theme variables.",
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
key: "verification",
|
|
421
|
+
content:
|
|
422
|
+
"Run `ng build --configuration production` and check for compilation errors and warnings. Test that all MatDialog instances open and close correctly with proper focus management. Verify the custom SCSS theme compiles without errors using `ng serve`.",
|
|
423
|
+
},
|
|
424
|
+
],
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
frameworkId: "flowbite",
|
|
428
|
+
sections: [
|
|
429
|
+
{
|
|
430
|
+
key: "install",
|
|
431
|
+
content:
|
|
432
|
+
"Install with `npm install flowbite` and add it as a Tailwind plugin: `plugins: [require('flowbite/plugin')]`. For React, use `npm install flowbite-react` instead. Add `node_modules/flowbite/**/*.js` to the Tailwind `content` array.",
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
key: "configure",
|
|
436
|
+
content:
|
|
437
|
+
"The Flowbite Tailwind plugin extends your config with additional colors and component classes. For Flowbite React, wrap your app in `<Flowbite>` provider for theme customization. Configure dark mode by setting `darkMode: 'class'` in your Tailwind config.",
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
key: "migrate-components",
|
|
441
|
+
content:
|
|
442
|
+
"Map old components: Modal → Modal, Button → Button, Input → TextInput, Navbar → Navbar, Card → Card, Dropdown → Dropdown. Flowbite React provides pre-built components, while vanilla Flowbite uses Tailwind classes with optional JS for interactivity via `flowbite.js`.",
|
|
443
|
+
},
|
|
444
|
+
{
|
|
445
|
+
key: "migrate-styles",
|
|
446
|
+
content:
|
|
447
|
+
"Flowbite builds on Tailwind utility classes, so existing Tailwind styles remain compatible. Customize component themes through the `theme` prop in Flowbite React or by overriding the default Tailwind classes directly. Use the Flowbite color palette tokens or replace them with your brand colors in `tailwind.config.js`.",
|
|
448
|
+
},
|
|
449
|
+
{
|
|
450
|
+
key: "gotchas",
|
|
451
|
+
content:
|
|
452
|
+
"Vanilla Flowbite requires including the `flowbite.js` script for interactive components (dropdowns, modals) — this is not needed with Flowbite React. The Tailwind `content` config must include Flowbite's JS files or the interactive component classes get purged. Some Flowbite React components differ in API from the vanilla HTML version.",
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
key: "verification",
|
|
456
|
+
content:
|
|
457
|
+
"Verify interactive components (dropdowns, modals, tooltips) work with keyboard and mouse. Test dark mode toggle across all Flowbite components. Check the Tailwind production build to ensure Flowbite classes are included in the purged output.",
|
|
458
|
+
},
|
|
459
|
+
],
|
|
460
|
+
},
|
|
461
|
+
{
|
|
462
|
+
frameworkId: "park-ui",
|
|
463
|
+
sections: [
|
|
464
|
+
{
|
|
465
|
+
key: "install",
|
|
466
|
+
content:
|
|
467
|
+
"Install with `npm install @park-ui/panda-preset` for Panda CSS or `npm install @park-ui/tailwind-plugin` for Tailwind. Park UI works with React, Vue, and Solid — install the matching Ark UI primitive package (e.g., `@ark-ui/react`). Components are copied into your project similar to shadcn.",
|
|
468
|
+
},
|
|
469
|
+
{
|
|
470
|
+
key: "configure",
|
|
471
|
+
content:
|
|
472
|
+
"For Panda CSS: add the Park UI preset to `panda.config.ts` via `presets: [parkUiPreset]`. For Tailwind: add the plugin to `tailwind.config.ts` and configure the color palette. Set up path aliases so generated components resolve correctly in your project.",
|
|
473
|
+
},
|
|
474
|
+
{
|
|
475
|
+
key: "migrate-components",
|
|
476
|
+
content:
|
|
477
|
+
"Map old components: Modal → Dialog, Button → Button, Input → Input, Tabs → Tabs, Select → Select. Components are generated into your codebase via the CLI or manual copy. Park UI is built on Ark UI primitives, which provide the accessible behavior layer.",
|
|
478
|
+
},
|
|
479
|
+
{
|
|
480
|
+
key: "migrate-styles",
|
|
481
|
+
content:
|
|
482
|
+
"Park UI supports both Panda CSS (utility-first, zero-runtime) and Tailwind CSS. For Panda, use style recipes and token-based props. For Tailwind, use the provided utility classes alongside your existing Tailwind workflow. Theming is token-driven with support for multiple color palettes.",
|
|
483
|
+
},
|
|
484
|
+
{
|
|
485
|
+
key: "gotchas",
|
|
486
|
+
content:
|
|
487
|
+
"Park UI is relatively new — the component library is smaller than mature alternatives. The choice between Panda CSS and Tailwind as the styling engine affects your config and available features. Ark UI (the primitive layer) has its own API patterns that differ from Radix UI despite similar goals.",
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
key: "verification",
|
|
491
|
+
content:
|
|
492
|
+
"Run the type checker (`npx tsc --noEmit`) to validate component prop types from Ark UI. Test all interactive components for accessibility — Park UI inherits Ark UI's WAI-ARIA compliance. Verify that the chosen styling engine (Panda or Tailwind) compiles without conflicts.",
|
|
493
|
+
},
|
|
494
|
+
],
|
|
495
|
+
},
|
|
496
|
+
];
|
|
497
|
+
|
|
498
|
+
export function getMigrationPrompt(
|
|
499
|
+
frameworkId: string,
|
|
500
|
+
): MigrationPrompt | undefined {
|
|
501
|
+
return MIGRATION_PROMPTS.find((p) => p.frameworkId === frameworkId);
|
|
502
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
FrameworkAdvisorKnowledge,
|
|
3
|
+
FrameworkEntry,
|
|
4
|
+
ValidationResult,
|
|
5
|
+
} from "../../types/framework-advisor";
|
|
6
|
+
import { MIGRATION_SECTION_KEYS } from "../../types/framework-advisor";
|
|
7
|
+
|
|
8
|
+
const REQUIRED_FRAMEWORK_STRINGS: (keyof FrameworkEntry)[] = [
|
|
9
|
+
"id",
|
|
10
|
+
"name",
|
|
11
|
+
"description",
|
|
12
|
+
"cssApproach",
|
|
13
|
+
"accessibilityRating",
|
|
14
|
+
"bundleSize",
|
|
15
|
+
"learningCurve",
|
|
16
|
+
"typescriptSupport",
|
|
17
|
+
"ecosystem",
|
|
18
|
+
"officialUrl",
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
export function validateKnowledge(
|
|
22
|
+
k: FrameworkAdvisorKnowledge
|
|
23
|
+
): ValidationResult {
|
|
24
|
+
const errors: string[] = [];
|
|
25
|
+
|
|
26
|
+
// ── Framework completeness ──────────────────────────────────────────────
|
|
27
|
+
if (k.frameworks.length < 12) {
|
|
28
|
+
errors.push(
|
|
29
|
+
`Framework catalog has ${k.frameworks.length} entries, minimum 12 required`
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const ids = new Set<string>();
|
|
34
|
+
for (const fw of k.frameworks) {
|
|
35
|
+
if (ids.has(fw.id)) {
|
|
36
|
+
errors.push(`Duplicate framework ID: "${fw.id}"`);
|
|
37
|
+
}
|
|
38
|
+
ids.add(fw.id);
|
|
39
|
+
|
|
40
|
+
for (const field of REQUIRED_FRAMEWORK_STRINGS) {
|
|
41
|
+
const val = fw[field];
|
|
42
|
+
if (typeof val === "string" && val.trim() === "") {
|
|
43
|
+
errors.push(`Framework "${fw.id}" has empty field: ${field}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (fw.bestFor.length === 0) {
|
|
48
|
+
errors.push(`Framework "${fw.id}" has no bestFor entries`);
|
|
49
|
+
}
|
|
50
|
+
if (fw.limitations.length === 0) {
|
|
51
|
+
errors.push(`Framework "${fw.id}" has no limitations entries`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ── Decision tree integrity ─────────────────────────────────────────────
|
|
56
|
+
const nodeMap = new Map(k.decisionTree.map((n) => [n.id, n]));
|
|
57
|
+
|
|
58
|
+
for (const node of k.decisionTree) {
|
|
59
|
+
for (const opt of node.options) {
|
|
60
|
+
if (opt.nextNodeId !== null && !nodeMap.has(opt.nextNodeId)) {
|
|
61
|
+
errors.push(
|
|
62
|
+
`Decision tree node "${node.id}" references unknown node: "${opt.nextNodeId}"`
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
if (opt.nextNodeId === null && (!opt.recommends || opt.recommends.length === 0)) {
|
|
66
|
+
errors.push(
|
|
67
|
+
`Decision tree node "${node.id}" option "${opt.value}" is terminal but has no recommendations`
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
if (opt.recommends) {
|
|
71
|
+
for (const fwId of opt.recommends) {
|
|
72
|
+
if (!ids.has(fwId)) {
|
|
73
|
+
errors.push(
|
|
74
|
+
`Decision tree recommends unknown framework: "${fwId}"`
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Verify all paths from root reach a terminal
|
|
83
|
+
if (k.decisionTree.length > 0) {
|
|
84
|
+
const rootId = k.decisionTree[0].id;
|
|
85
|
+
const visited = new Set<string>();
|
|
86
|
+
|
|
87
|
+
function checkPath(nodeId: string): void {
|
|
88
|
+
if (visited.has(nodeId)) return;
|
|
89
|
+
visited.add(nodeId);
|
|
90
|
+
const node = nodeMap.get(nodeId);
|
|
91
|
+
if (!node) return;
|
|
92
|
+
for (const opt of node.options) {
|
|
93
|
+
if (opt.nextNodeId !== null) {
|
|
94
|
+
checkPath(opt.nextNodeId);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
checkPath(rootId);
|
|
100
|
+
|
|
101
|
+
for (const node of k.decisionTree) {
|
|
102
|
+
if (!visited.has(node.id)) {
|
|
103
|
+
errors.push(`Decision tree node "${node.id}" is unreachable from root`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ── Migration prompt coverage ───────────────────────────────────────────
|
|
109
|
+
const promptMap = new Map(k.migrationPrompts.map((p) => [p.frameworkId, p]));
|
|
110
|
+
|
|
111
|
+
for (const fw of k.frameworks) {
|
|
112
|
+
const prompt = promptMap.get(fw.id);
|
|
113
|
+
if (!prompt) {
|
|
114
|
+
errors.push(`Framework "${fw.id}" has no migration prompt`);
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const sectionKeys = new Set(prompt.sections.map((s) => s.key));
|
|
119
|
+
for (const required of MIGRATION_SECTION_KEYS) {
|
|
120
|
+
if (!sectionKeys.has(required)) {
|
|
121
|
+
errors.push(
|
|
122
|
+
`Migration prompt for "${fw.id}" is missing section: "${required}"`
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
for (const section of prompt.sections) {
|
|
128
|
+
if (section.content.trim() === "") {
|
|
129
|
+
errors.push(
|
|
130
|
+
`Migration prompt for "${fw.id}" has empty section: "${section.key}"`
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return { valid: errors.length === 0, errors };
|
|
137
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { writeFileSync, readFileSync, appendFileSync, renameSync, mkdirSync } from "fs";
|
|
2
|
+
import { dirname } from "path";
|
|
3
|
+
|
|
4
|
+
export function atomicWriteJson(filePath: string, data: unknown): void {
|
|
5
|
+
const tmp = filePath + ".tmp";
|
|
6
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
7
|
+
writeFileSync(tmp, JSON.stringify(data, null, 2));
|
|
8
|
+
renameSync(tmp, filePath);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function atomicWriteText(filePath: string, content: string): void {
|
|
12
|
+
const tmp = filePath + ".tmp";
|
|
13
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
14
|
+
writeFileSync(tmp, content);
|
|
15
|
+
renameSync(tmp, filePath);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function safeAppendText(filePath: string, content: string): void {
|
|
19
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
20
|
+
appendFileSync(filePath, content);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function safeReadJson(filePath: string): unknown | null {
|
|
24
|
+
try {
|
|
25
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
26
|
+
return JSON.parse(raw);
|
|
27
|
+
} catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|