vibes-plug 1.0.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 (141) hide show
  1. package/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  2. package/BLUEPRINT.md +125 -0
  3. package/CHANGELOG.md +195 -0
  4. package/CONTRIBUTING.md +199 -0
  5. package/LICENSE +21 -0
  6. package/README.md +263 -0
  7. package/SECURITY.md +21 -0
  8. package/banner.png +0 -0
  9. package/package.json +25 -0
  10. package/plugin.json +8 -0
  11. package/scripts/update_skills.js +75 -0
  12. package/skills/ai-llm-integration-expert/SKILL.md +162 -0
  13. package/skills/api-design-expert/SKILL.md +310 -0
  14. package/skills/app-analyzer-optimizer/SKILL.md +189 -0
  15. package/skills/asisten_ramah/SKILL.md +41 -0
  16. package/skills/authentication-identity-expert/SKILL.md +45 -0
  17. package/skills/auto-doc-updater/SKILL.md +204 -0
  18. package/skills/bootstrap-to-modern/SKILL.md +87 -0
  19. package/skills/brainstorming/SKILL.md +353 -0
  20. package/skills/bun-runtime-expert/SKILL.md +211 -0
  21. package/skills/ci-cd-devops-architect/SKILL.md +45 -0
  22. package/skills/cloud-hosting-expert/SKILL.md +244 -0
  23. package/skills/coderabbit/SKILL.md +192 -0
  24. package/skills/data-telemetry-expert/SKILL.md +213 -0
  25. package/skills/database-orm-expert/SKILL.md +294 -0
  26. package/skills/design-system-architect/SKILL.md +243 -0
  27. package/skills/e2e-testing-expert/SKILL.md +315 -0
  28. package/skills/event-driven-architect/SKILL.md +81 -0
  29. package/skills/firebase-security-expert/SKILL.md +195 -0
  30. package/skills/fullstack-expert/SKILL.md +202 -0
  31. package/skills/fullstack-expert/references/api_design_guide.md +466 -0
  32. package/skills/fullstack-expert/references/devops_infrastructure.md +477 -0
  33. package/skills/fullstack-expert/references/multi_language_backend.md +528 -0
  34. package/skills/fullstack-expert/references/system_design_patterns.md +358 -0
  35. package/skills/fullstack-expert/scripts/api_contract_validator.py +253 -0
  36. package/skills/fullstack-expert/scripts/architecture_analyzer.py +326 -0
  37. package/skills/gemini-agent-booster/SKILL.md +135 -0
  38. package/skills/global-a11y-i18n-expert/SKILL.md +81 -0
  39. package/skills/go-programming-expert/SKILL.md +295 -0
  40. package/skills/hig/SKILL.md +188 -0
  41. package/skills/js-backend-expert/SKILL.md +192 -0
  42. package/skills/mcp-server-architect/SKILL.md +194 -0
  43. package/skills/mobile-expo-expert/SKILL.md +186 -0
  44. package/skills/monday-design-aesthetic/SKILL.md +67 -0
  45. package/skills/monorepo-architect/SKILL.md +227 -0
  46. package/skills/mpa-orchestrator/SKILL.md +101 -0
  47. package/skills/multi-agent-orchestration/SKILL.md +234 -0
  48. package/skills/multiple-entry-points/SKILL.md +55 -0
  49. package/skills/mvc-expert/SKILL.md +231 -0
  50. package/skills/payment-gateway-expert/SKILL.md +45 -0
  51. package/skills/performance-web-vitals/SKILL.md +332 -0
  52. package/skills/prd-architect/SKILL.md +191 -0
  53. package/skills/production-ready-hardener/SKILL.md +469 -0
  54. package/skills/production-ready-hardener/references/performance_optimization.md +441 -0
  55. package/skills/production-ready-hardener/references/production_checklist.md +161 -0
  56. package/skills/production-ready-hardener/references/security_hardening_guide.md +379 -0
  57. package/skills/production-ready-hardener/scripts/production_readiness_scanner.py +875 -0
  58. package/skills/python-programming-expert/SKILL.md +271 -0
  59. package/skills/realtime-collaboration-expert/SKILL.md +45 -0
  60. package/skills/rust-programming-expert/SKILL.md +235 -0
  61. package/skills/saas-billing/SKILL.md +377 -0
  62. package/skills/saas-multi-tenant/SKILL.md +237 -0
  63. package/skills/saas-mvp-launcher/SKILL.md +231 -0
  64. package/skills/saas-transformer/SKILL.md +446 -0
  65. package/skills/saas-transformer/references/billing_integration_guide.md +401 -0
  66. package/skills/saas-transformer/references/feature_gating_patterns.md +137 -0
  67. package/skills/saas-transformer/references/saas_transformation_checklist.md +121 -0
  68. package/skills/saas-transformer/scripts/saas_transformation_scanner.py +254 -0
  69. package/skills/scalability-clean-code/SKILL.md +229 -0
  70. package/skills/secure-fuzz-testing/SKILL.md +201 -0
  71. package/skills/senior-frontend/SKILL.md +161 -0
  72. package/skills/senior-frontend/references/frontend_best_practices.md +806 -0
  73. package/skills/senior-frontend/references/nextjs_optimization_guide.md +724 -0
  74. package/skills/senior-frontend/references/react_patterns.md +746 -0
  75. package/skills/senior-frontend/scripts/bundle_analyzer.py +407 -0
  76. package/skills/senior-frontend/scripts/component_generator.py +329 -0
  77. package/skills/senior-frontend/scripts/frontend_scaffolder.py +1005 -0
  78. package/skills/senior-fullstack/SKILL.md +167 -0
  79. package/skills/senior-fullstack/references/architecture_patterns.md +160 -0
  80. package/skills/senior-fullstack/references/development_workflows.md +222 -0
  81. package/skills/senior-fullstack/references/tech_stack_guide.md +190 -0
  82. package/skills/senior-fullstack/scripts/code_quality_analyzer.py +114 -0
  83. package/skills/senior-fullstack/scripts/fullstack_scaffolder.py +114 -0
  84. package/skills/senior-fullstack/scripts/project_scaffolder.py +114 -0
  85. package/skills/seo/SKILL.md +225 -0
  86. package/skills/seo/references/cwv-thresholds.md +108 -0
  87. package/skills/seo/references/eeat-framework.md +214 -0
  88. package/skills/seo/references/quality-gates.md +155 -0
  89. package/skills/seo/references/schema-types.md +118 -0
  90. package/skills/seo-aeo-landing-page-writer/SKILL.md +97 -0
  91. package/skills/seo-geo/SKILL.md +188 -0
  92. package/skills/session-handoff-resume/SKILL.md +158 -0
  93. package/skills/skill_baru/SKILL.md +147 -0
  94. package/skills/spa-orchestrator/SKILL.md +288 -0
  95. package/skills/state-management-expert/SKILL.md +272 -0
  96. package/skills/supabase-migration/SKILL.md +45 -0
  97. package/skills/supabase-security-expert/SKILL.md +243 -0
  98. package/skills/tailwind-expert/SKILL.md +188 -0
  99. package/skills/tanstack-query-expert/SKILL.md +199 -0
  100. package/skills/tauri-expert/SKILL.md +97 -0
  101. package/skills/token-saver/SKILL.md +111 -0
  102. package/skills/typescript-expert/SKILL.md +279 -0
  103. package/skills/ui-components-expert/SKILL.md +63 -0
  104. package/skills/ui-ux-pro-max/SKILL.md +201 -0
  105. package/skills/ui-ux-pro-max/data/charts.csv +26 -0
  106. package/skills/ui-ux-pro-max/data/colors.csv +97 -0
  107. package/skills/ui-ux-pro-max/data/icons.csv +101 -0
  108. package/skills/ui-ux-pro-max/data/landing.csv +31 -0
  109. package/skills/ui-ux-pro-max/data/products.csv +97 -0
  110. package/skills/ui-ux-pro-max/data/prompts.csv +24 -0
  111. package/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
  112. package/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
  113. package/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
  114. package/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
  115. package/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
  116. package/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
  117. package/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
  118. package/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
  119. package/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
  120. package/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
  121. package/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
  122. package/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
  123. package/skills/ui-ux-pro-max/data/styles.csv +59 -0
  124. package/skills/ui-ux-pro-max/data/typography.csv +58 -0
  125. package/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
  126. package/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
  127. package/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
  128. package/skills/ui-ux-pro-max/scripts/__pycache__/core.cpython-310.pyc +0 -0
  129. package/skills/ui-ux-pro-max/scripts/__pycache__/core.cpython-312.pyc +0 -0
  130. package/skills/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-310.pyc +0 -0
  131. package/skills/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-312.pyc +0 -0
  132. package/skills/ui-ux-pro-max/scripts/core.py +257 -0
  133. package/skills/ui-ux-pro-max/scripts/design_system.py +493 -0
  134. package/skills/ui-ux-pro-max/scripts/search.py +81 -0
  135. package/skills/ui_ux_expert/SKILL.md +114 -0
  136. package/skills/vibe-code-gardener/SKILL.md +173 -0
  137. package/skills/web-scraper/SKILL.md +205 -0
  138. package/skills/web-scraper/references/data-transforms.md +397 -0
  139. package/skills/web-scraper/references/extraction-patterns.md +475 -0
  140. package/skills/web-scraper/references/output-templates.md +481 -0
  141. package/skills/zero-to-prod-orchestrator/SKILL.md +180 -0
@@ -0,0 +1,1005 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Frontend Project Scaffolder
4
+
5
+ Generates a complete Next.js/React project structure with TypeScript,
6
+ Tailwind CSS, and best practice configurations.
7
+
8
+ Usage:
9
+ python frontend_scaffolder.py my-app --template nextjs
10
+ python frontend_scaffolder.py dashboard --template react --features auth,api
11
+ python frontend_scaffolder.py landing --template nextjs --dry-run
12
+ """
13
+
14
+ import argparse
15
+ import json
16
+ import os
17
+ import sys
18
+ from pathlib import Path
19
+ from typing import Dict, List, Optional
20
+
21
+
22
+ # Project templates
23
+ TEMPLATES = {
24
+ "nextjs": {
25
+ "name": "Next.js 15+ App Router",
26
+ "description": "Modern Next.js with App Router, Server Components, and TypeScript",
27
+ "structure": {
28
+ "app": {
29
+ "layout.tsx": "ROOT_LAYOUT",
30
+ "page.tsx": "HOME_PAGE",
31
+ "globals.css": "GLOBALS_CSS",
32
+ "(auth)": {
33
+ "login": {"page.tsx": "AUTH_PAGE"},
34
+ "register": {"page.tsx": "AUTH_PAGE"},
35
+ },
36
+ "api": {
37
+ "health": {"route.ts": "HEALTH_ROUTE"},
38
+ },
39
+ },
40
+ "components": {
41
+ "ui": {
42
+ "button.tsx": "UI_BUTTON",
43
+ "input.tsx": "UI_INPUT",
44
+ "card.tsx": "UI_CARD",
45
+ "index.ts": "UI_INDEX",
46
+ },
47
+ "layout": {
48
+ "header.tsx": "LAYOUT_HEADER",
49
+ "footer.tsx": "LAYOUT_FOOTER",
50
+ "sidebar.tsx": "LAYOUT_SIDEBAR",
51
+ },
52
+ },
53
+ "lib": {
54
+ "utils.ts": "UTILS",
55
+ "constants.ts": "CONSTANTS",
56
+ },
57
+ "hooks": {
58
+ "use-debounce.ts": "HOOK_DEBOUNCE",
59
+ "use-local-storage.ts": "HOOK_LOCAL_STORAGE",
60
+ },
61
+ "types": {
62
+ "index.ts": "TYPES_INDEX",
63
+ },
64
+ "public": {
65
+ ".gitkeep": "EMPTY",
66
+ },
67
+ },
68
+ "config_files": [
69
+ "next.config.js",
70
+ "tailwind.config.ts",
71
+ "tsconfig.json",
72
+ "postcss.config.js",
73
+ ".eslintrc.json",
74
+ ".prettierrc",
75
+ ".gitignore",
76
+ "package.json",
77
+ ],
78
+ },
79
+ "react": {
80
+ "name": "React + Vite",
81
+ "description": "Modern React with Vite, TypeScript, and Tailwind CSS",
82
+ "structure": {
83
+ "src": {
84
+ "App.tsx": "REACT_APP",
85
+ "main.tsx": "REACT_MAIN",
86
+ "index.css": "GLOBALS_CSS",
87
+ "components": {
88
+ "ui": {
89
+ "button.tsx": "UI_BUTTON",
90
+ "input.tsx": "UI_INPUT",
91
+ "card.tsx": "UI_CARD",
92
+ "index.ts": "UI_INDEX",
93
+ },
94
+ },
95
+ "hooks": {
96
+ "use-debounce.ts": "HOOK_DEBOUNCE",
97
+ "use-local-storage.ts": "HOOK_LOCAL_STORAGE",
98
+ },
99
+ "lib": {
100
+ "utils.ts": "UTILS",
101
+ },
102
+ "types": {
103
+ "index.ts": "TYPES_INDEX",
104
+ },
105
+ },
106
+ "public": {
107
+ ".gitkeep": "EMPTY",
108
+ },
109
+ },
110
+ "config_files": [
111
+ "vite.config.ts",
112
+ "tailwind.config.ts",
113
+ "tsconfig.json",
114
+ "postcss.config.js",
115
+ ".eslintrc.json",
116
+ ".prettierrc",
117
+ ".gitignore",
118
+ "package.json",
119
+ "index.html",
120
+ ],
121
+ },
122
+ }
123
+
124
+ # Feature modules that can be added
125
+ FEATURES = {
126
+ "auth": {
127
+ "description": "Authentication with session management",
128
+ "files": {
129
+ "lib/auth.ts": "AUTH_LIB",
130
+ "middleware.ts": "AUTH_MIDDLEWARE",
131
+ "components/auth/login-form.tsx": "LOGIN_FORM",
132
+ "components/auth/register-form.tsx": "REGISTER_FORM",
133
+ },
134
+ "dependencies": ["next-auth", "@auth/core"],
135
+ },
136
+ "api": {
137
+ "description": "API client with React Query",
138
+ "files": {
139
+ "lib/api-client.ts": "API_CLIENT",
140
+ "lib/query-client.ts": "QUERY_CLIENT",
141
+ "providers/query-provider.tsx": "QUERY_PROVIDER",
142
+ },
143
+ "dependencies": ["@tanstack/react-query", "axios"],
144
+ },
145
+ "forms": {
146
+ "description": "Form handling with React Hook Form + Zod",
147
+ "files": {
148
+ "lib/form-utils.ts": "FORM_UTILS",
149
+ "components/forms/form-field.tsx": "FORM_FIELD",
150
+ },
151
+ "dependencies": ["react-hook-form", "@hookform/resolvers", "zod"],
152
+ },
153
+ "testing": {
154
+ "description": "Testing setup with Vitest and Testing Library",
155
+ "files": {
156
+ "vitest.config.ts": "VITEST_CONFIG",
157
+ "src/test/setup.ts": "TEST_SETUP",
158
+ "src/test/utils.tsx": "TEST_UTILS",
159
+ },
160
+ "dependencies": ["vitest", "@testing-library/react", "@testing-library/jest-dom"],
161
+ },
162
+ "storybook": {
163
+ "description": "Component documentation with Storybook",
164
+ "files": {
165
+ ".storybook/main.ts": "STORYBOOK_MAIN",
166
+ ".storybook/preview.ts": "STORYBOOK_PREVIEW",
167
+ },
168
+ "dependencies": ["@storybook/react-vite", "@storybook/addon-essentials"],
169
+ },
170
+ }
171
+
172
+ # File content templates
173
+ FILE_CONTENTS = {
174
+ "ROOT_LAYOUT": '''import type { Metadata } from 'next';
175
+ import { Inter } from 'next/font/google';
176
+ import './globals.css';
177
+
178
+ const inter = Inter({ subsets: ['latin'], variable: '--font-inter' });
179
+
180
+ export const metadata: Metadata = {
181
+ title: 'My App',
182
+ description: 'Built with Next.js',
183
+ };
184
+
185
+ export default function RootLayout({
186
+ children,
187
+ }: {
188
+ children: React.ReactNode;
189
+ }) {
190
+ return (
191
+ <html lang="en">
192
+ <body className={`${inter.variable} font-sans antialiased`}>
193
+ {children}
194
+ </body>
195
+ </html>
196
+ );
197
+ }
198
+ ''',
199
+ "HOME_PAGE": '''export default function Home() {
200
+ return (
201
+ <main className="flex min-h-screen flex-col items-center justify-center p-24">
202
+ <h1 className="text-4xl font-bold">Welcome</h1>
203
+ <p className="mt-4 text-lg text-gray-600">
204
+ Get started by editing app/page.tsx
205
+ </p>
206
+ </main>
207
+ );
208
+ }
209
+ ''',
210
+ "GLOBALS_CSS": '''@tailwind base;
211
+ @tailwind components;
212
+ @tailwind utilities;
213
+
214
+ @layer base {
215
+ :root {
216
+ --background: 0 0% 100%;
217
+ --foreground: 222.2 84% 4.9%;
218
+ --primary: 222.2 47.4% 11.2%;
219
+ --primary-foreground: 210 40% 98%;
220
+ --secondary: 210 40% 96.1%;
221
+ --secondary-foreground: 222.2 47.4% 11.2%;
222
+ --muted: 210 40% 96.1%;
223
+ --muted-foreground: 215.4 16.3% 46.9%;
224
+ --accent: 210 40% 96.1%;
225
+ --accent-foreground: 222.2 47.4% 11.2%;
226
+ --destructive: 0 84.2% 60.2%;
227
+ --destructive-foreground: 210 40% 98%;
228
+ --border: 214.3 31.8% 91.4%;
229
+ --ring: 222.2 84% 4.9%;
230
+ --radius: 0.5rem;
231
+ }
232
+
233
+ .dark {
234
+ --background: 222.2 84% 4.9%;
235
+ --foreground: 210 40% 98%;
236
+ }
237
+ }
238
+
239
+ @layer base {
240
+ * {
241
+ @apply border-border;
242
+ }
243
+ body {
244
+ @apply bg-background text-foreground;
245
+ }
246
+ }
247
+ ''',
248
+ "UI_BUTTON": '''import { forwardRef } from 'react';
249
+ import { cn } from '@/lib/utils';
250
+
251
+ interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
252
+ variant?: 'default' | 'destructive' | 'outline' | 'ghost';
253
+ size?: 'default' | 'sm' | 'lg';
254
+ }
255
+
256
+ const Button = forwardRef<HTMLButtonElement, ButtonProps>(
257
+ ({ className, variant = 'default', size = 'default', ...props }, ref) => {
258
+ return (
259
+ <button
260
+ className={cn(
261
+ 'inline-flex items-center justify-center rounded-md font-medium transition-colors',
262
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
263
+ 'disabled:pointer-events-none disabled:opacity-50',
264
+ {
265
+ 'bg-primary text-primary-foreground hover:bg-primary/90': variant === 'default',
266
+ 'bg-destructive text-destructive-foreground hover:bg-destructive/90': variant === 'destructive',
267
+ 'border border-input bg-background hover:bg-accent': variant === 'outline',
268
+ 'hover:bg-accent hover:text-accent-foreground': variant === 'ghost',
269
+ },
270
+ {
271
+ 'h-10 px-4 py-2': size === 'default',
272
+ 'h-9 px-3': size === 'sm',
273
+ 'h-11 px-8': size === 'lg',
274
+ },
275
+ className
276
+ )}
277
+ ref={ref}
278
+ {...props}
279
+ />
280
+ );
281
+ }
282
+ );
283
+
284
+ Button.displayName = 'Button';
285
+
286
+ export { Button, type ButtonProps };
287
+ ''',
288
+ "UI_INPUT": '''import { forwardRef } from 'react';
289
+ import { cn } from '@/lib/utils';
290
+
291
+ interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
292
+ error?: string;
293
+ }
294
+
295
+ const Input = forwardRef<HTMLInputElement, InputProps>(
296
+ ({ className, error, ...props }, ref) => {
297
+ return (
298
+ <div className="w-full">
299
+ <input
300
+ className={cn(
301
+ 'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2',
302
+ 'text-sm ring-offset-background file:border-0 file:bg-transparent',
303
+ 'file:text-sm file:font-medium placeholder:text-muted-foreground',
304
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',
305
+ 'disabled:cursor-not-allowed disabled:opacity-50',
306
+ error && 'border-destructive focus-visible:ring-destructive',
307
+ className
308
+ )}
309
+ ref={ref}
310
+ {...props}
311
+ />
312
+ {error && <p className="mt-1 text-sm text-destructive">{error}</p>}
313
+ </div>
314
+ );
315
+ }
316
+ );
317
+
318
+ Input.displayName = 'Input';
319
+
320
+ export { Input, type InputProps };
321
+ ''',
322
+ "UI_CARD": '''import { cn } from '@/lib/utils';
323
+
324
+ interface CardProps extends React.HTMLAttributes<HTMLDivElement> {}
325
+
326
+ function Card({ className, ...props }: CardProps) {
327
+ return (
328
+ <div
329
+ className={cn(
330
+ 'rounded-lg border bg-card text-card-foreground shadow-sm',
331
+ className
332
+ )}
333
+ {...props}
334
+ />
335
+ );
336
+ }
337
+
338
+ function CardHeader({ className, ...props }: CardProps) {
339
+ return <div className={cn('flex flex-col space-y-1.5 p-6', className)} {...props} />;
340
+ }
341
+
342
+ function CardTitle({ className, ...props }: React.HTMLAttributes<HTMLHeadingElement>) {
343
+ return <h3 className={cn('text-2xl font-semibold leading-none', className)} {...props} />;
344
+ }
345
+
346
+ function CardContent({ className, ...props }: CardProps) {
347
+ return <div className={cn('p-6 pt-0', className)} {...props} />;
348
+ }
349
+
350
+ function CardFooter({ className, ...props }: CardProps) {
351
+ return <div className={cn('flex items-center p-6 pt-0', className)} {...props} />;
352
+ }
353
+
354
+ export { Card, CardHeader, CardTitle, CardContent, CardFooter };
355
+ ''',
356
+ "UI_INDEX": '''export { Button } from './button';
357
+ export { Input } from './input';
358
+ export { Card, CardHeader, CardTitle, CardContent, CardFooter } from './card';
359
+ ''',
360
+ "UTILS": '''import { type ClassValue, clsx } from 'clsx';
361
+ import { twMerge } from 'tailwind-merge';
362
+
363
+ export function cn(...inputs: ClassValue[]) {
364
+ return twMerge(clsx(inputs));
365
+ }
366
+
367
+ export function formatDate(date: Date | string): string {
368
+ return new Intl.DateTimeFormat('en-US', {
369
+ month: 'short',
370
+ day: 'numeric',
371
+ year: 'numeric',
372
+ }).format(new Date(date));
373
+ }
374
+
375
+ export function sleep(ms: number): Promise<void> {
376
+ return new Promise((resolve) => setTimeout(resolve, ms));
377
+ }
378
+ ''',
379
+ "CONSTANTS": '''export const APP_NAME = 'My App';
380
+ export const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000/api';
381
+
382
+ export const ROUTES = {
383
+ home: '/',
384
+ login: '/login',
385
+ register: '/register',
386
+ dashboard: '/dashboard',
387
+ } as const;
388
+
389
+ export const QUERY_KEYS = {
390
+ user: ['user'],
391
+ products: ['products'],
392
+ } as const;
393
+ ''',
394
+ "HOOK_DEBOUNCE": '''import { useState, useEffect } from 'react';
395
+
396
+ export function useDebounce<T>(value: T, delay: number = 500): T {
397
+ const [debouncedValue, setDebouncedValue] = useState<T>(value);
398
+
399
+ useEffect(() => {
400
+ const timer = setTimeout(() => setDebouncedValue(value), delay);
401
+ return () => clearTimeout(timer);
402
+ }, [value, delay]);
403
+
404
+ return debouncedValue;
405
+ }
406
+ ''',
407
+ "HOOK_LOCAL_STORAGE": '''import { useState, useEffect } from 'react';
408
+
409
+ export function useLocalStorage<T>(
410
+ key: string,
411
+ initialValue: T
412
+ ): [T, (value: T | ((prev: T) => T)) => void] {
413
+ const [storedValue, setStoredValue] = useState<T>(() => {
414
+ if (typeof window === 'undefined') return initialValue;
415
+
416
+ try {
417
+ const item = window.localStorage.getItem(key);
418
+ return item ? JSON.parse(item) : initialValue;
419
+ } catch {
420
+ return initialValue;
421
+ }
422
+ });
423
+
424
+ useEffect(() => {
425
+ if (typeof window !== 'undefined') {
426
+ window.localStorage.setItem(key, JSON.stringify(storedValue));
427
+ }
428
+ }, [key, storedValue]);
429
+
430
+ return [storedValue, setStoredValue];
431
+ }
432
+ ''',
433
+ "TYPES_INDEX": '''export interface User {
434
+ id: string;
435
+ email: string;
436
+ name: string;
437
+ createdAt: Date;
438
+ }
439
+
440
+ export interface ApiResponse<T> {
441
+ data: T;
442
+ message?: string;
443
+ error?: string;
444
+ }
445
+
446
+ export interface PaginatedResponse<T> {
447
+ data: T[];
448
+ total: number;
449
+ page: number;
450
+ pageSize: number;
451
+ totalPages: number;
452
+ }
453
+ ''',
454
+ "HEALTH_ROUTE": '''import { NextResponse } from 'next/server';
455
+
456
+ export async function GET() {
457
+ return NextResponse.json({
458
+ status: 'ok',
459
+ timestamp: new Date().toISOString(),
460
+ });
461
+ }
462
+ ''',
463
+ "AUTH_PAGE": ''''use client';
464
+
465
+ export default function AuthPage() {
466
+ return (
467
+ <div className="flex min-h-screen items-center justify-center">
468
+ <div className="w-full max-w-md p-8">
469
+ <h1 className="text-2xl font-bold text-center">Authentication</h1>
470
+ </div>
471
+ </div>
472
+ );
473
+ }
474
+ ''',
475
+ "LAYOUT_HEADER": '''import Link from 'next/link';
476
+
477
+ export function Header() {
478
+ return (
479
+ <header className="sticky top-0 z-50 w-full border-b bg-background/95 backdrop-blur">
480
+ <div className="container flex h-14 items-center">
481
+ <Link href="/" className="font-bold">
482
+ Logo
483
+ </Link>
484
+ <nav className="ml-auto flex gap-4">
485
+ <Link href="/about" className="text-sm text-muted-foreground hover:text-foreground">
486
+ About
487
+ </Link>
488
+ </nav>
489
+ </div>
490
+ </header>
491
+ );
492
+ }
493
+ ''',
494
+ "LAYOUT_FOOTER": '''export function Footer() {
495
+ return (
496
+ <footer className="border-t py-6">
497
+ <div className="container text-center text-sm text-muted-foreground">
498
+ <p>&copy; {new Date().getFullYear()} My App. All rights reserved.</p>
499
+ </div>
500
+ </footer>
501
+ );
502
+ }
503
+ ''',
504
+ "LAYOUT_SIDEBAR": '''interface SidebarProps {
505
+ children?: React.ReactNode;
506
+ }
507
+
508
+ export function Sidebar({ children }: SidebarProps) {
509
+ return (
510
+ <aside className="fixed left-0 top-14 z-30 h-[calc(100vh-3.5rem)] w-64 border-r bg-background">
511
+ <div className="p-4">{children}</div>
512
+ </aside>
513
+ );
514
+ }
515
+ ''',
516
+ "REACT_APP": '''import { Button } from './components/ui';
517
+
518
+ function App() {
519
+ return (
520
+ <main className="flex min-h-screen flex-col items-center justify-center p-24">
521
+ <h1 className="text-4xl font-bold">Welcome</h1>
522
+ <p className="mt-4 text-lg text-gray-600">
523
+ Get started by editing src/App.tsx
524
+ </p>
525
+ <Button className="mt-6">Get Started</Button>
526
+ </main>
527
+ );
528
+ }
529
+
530
+ export default App;
531
+ ''',
532
+ "REACT_MAIN": '''import React from 'react';
533
+ import ReactDOM from 'react-dom/client';
534
+ import App from './App';
535
+ import './index.css';
536
+
537
+ ReactDOM.createRoot(document.getElementById('root')!).render(
538
+ <React.StrictMode>
539
+ <App />
540
+ </React.StrictMode>
541
+ );
542
+ ''',
543
+ "EMPTY": "",
544
+ }
545
+
546
+
547
+ def generate_structure(
548
+ base_path: Path,
549
+ structure: Dict,
550
+ dry_run: bool = False
551
+ ) -> List[str]:
552
+ """Generate directory structure recursively."""
553
+ created_files = []
554
+
555
+ for name, content in structure.items():
556
+ current_path = base_path / name
557
+
558
+ if isinstance(content, dict):
559
+ # It's a directory
560
+ if not dry_run:
561
+ current_path.mkdir(parents=True, exist_ok=True)
562
+ created_files.extend(generate_structure(current_path, content, dry_run))
563
+ else:
564
+ # It's a file
565
+ if not dry_run:
566
+ current_path.parent.mkdir(parents=True, exist_ok=True)
567
+ file_content = FILE_CONTENTS.get(content, "")
568
+ current_path.write_text(file_content)
569
+ created_files.append(str(current_path))
570
+
571
+ return created_files
572
+
573
+
574
+ def generate_config_files(
575
+ project_path: Path,
576
+ template: str,
577
+ project_name: str,
578
+ features: List[str],
579
+ dry_run: bool = False
580
+ ) -> List[str]:
581
+ """Generate configuration files."""
582
+ created_files = []
583
+ config_templates = get_config_templates(project_name, template, features)
584
+
585
+ template_config = TEMPLATES[template]
586
+ for config_file in template_config["config_files"]:
587
+ file_path = project_path / config_file
588
+ if config_file in config_templates:
589
+ if not dry_run:
590
+ file_path.write_text(config_templates[config_file])
591
+ created_files.append(str(file_path))
592
+
593
+ return created_files
594
+
595
+
596
+ def get_config_templates(name: str, template: str, features: List[str]) -> Dict[str, str]:
597
+ """Get configuration file contents."""
598
+ deps = {
599
+ "nextjs": {
600
+ "dependencies": {
601
+ "next": "^14.0.0",
602
+ "react": "^18.2.0",
603
+ "react-dom": "^18.2.0",
604
+ "clsx": "^2.0.0",
605
+ "tailwind-merge": "^2.0.0",
606
+ },
607
+ "devDependencies": {
608
+ "@types/node": "^20.0.0",
609
+ "@types/react": "^18.2.0",
610
+ "@types/react-dom": "^18.2.0",
611
+ "autoprefixer": "^10.0.0",
612
+ "eslint": "^8.0.0",
613
+ "eslint-config-next": "^14.0.0",
614
+ "postcss": "^8.0.0",
615
+ "prettier": "^3.0.0",
616
+ "tailwindcss": "^3.4.0",
617
+ "typescript": "^5.0.0",
618
+ },
619
+ },
620
+ "react": {
621
+ "dependencies": {
622
+ "react": "^18.2.0",
623
+ "react-dom": "^18.2.0",
624
+ "clsx": "^2.0.0",
625
+ "tailwind-merge": "^2.0.0",
626
+ },
627
+ "devDependencies": {
628
+ "@types/react": "^18.2.0",
629
+ "@types/react-dom": "^18.2.0",
630
+ "@vitejs/plugin-react": "^4.0.0",
631
+ "autoprefixer": "^10.0.0",
632
+ "eslint": "^8.0.0",
633
+ "postcss": "^8.0.0",
634
+ "prettier": "^3.0.0",
635
+ "tailwindcss": "^3.4.0",
636
+ "typescript": "^5.0.0",
637
+ "vite": "^5.0.0",
638
+ },
639
+ },
640
+ }
641
+
642
+ # Add feature dependencies
643
+ for feature in features:
644
+ if feature in FEATURES:
645
+ for dep in FEATURES[feature].get("dependencies", []):
646
+ deps[template]["dependencies"][dep] = "latest"
647
+
648
+ package_json = {
649
+ "name": name,
650
+ "version": "0.1.0",
651
+ "private": True,
652
+ "scripts": {
653
+ "dev": "next dev" if template == "nextjs" else "vite",
654
+ "build": "next build" if template == "nextjs" else "vite build",
655
+ "start": "next start" if template == "nextjs" else "vite preview",
656
+ "lint": "eslint . --ext .ts,.tsx",
657
+ "format": "prettier --write .",
658
+ },
659
+ "dependencies": deps[template]["dependencies"],
660
+ "devDependencies": deps[template]["devDependencies"],
661
+ }
662
+
663
+ return {
664
+ "package.json": json.dumps(package_json, indent=2),
665
+ "tsconfig.json": '''{
666
+ "compilerOptions": {
667
+ "target": "ES2020",
668
+ "lib": ["dom", "dom.iterable", "esnext"],
669
+ "allowJs": true,
670
+ "skipLibCheck": true,
671
+ "strict": true,
672
+ "noEmit": true,
673
+ "esModuleInterop": true,
674
+ "module": "esnext",
675
+ "moduleResolution": "bundler",
676
+ "resolveJsonModule": true,
677
+ "isolatedModules": true,
678
+ "jsx": "preserve",
679
+ "incremental": true,
680
+ "plugins": [{ "name": "next" }],
681
+ "paths": {
682
+ "@/*": ["./*"]
683
+ }
684
+ },
685
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
686
+ "exclude": ["node_modules"]
687
+ }
688
+ ''',
689
+ "tailwind.config.ts": '''import type { Config } from 'tailwindcss';
690
+
691
+ const config: Config = {
692
+ content: [
693
+ './pages/**/*.{js,ts,jsx,tsx,mdx}',
694
+ './components/**/*.{js,ts,jsx,tsx,mdx}',
695
+ './app/**/*.{js,ts,jsx,tsx,mdx}',
696
+ './src/**/*.{js,ts,jsx,tsx,mdx}',
697
+ ],
698
+ theme: {
699
+ extend: {
700
+ colors: {
701
+ background: 'hsl(var(--background))',
702
+ foreground: 'hsl(var(--foreground))',
703
+ primary: {
704
+ DEFAULT: 'hsl(var(--primary))',
705
+ foreground: 'hsl(var(--primary-foreground))',
706
+ },
707
+ secondary: {
708
+ DEFAULT: 'hsl(var(--secondary))',
709
+ foreground: 'hsl(var(--secondary-foreground))',
710
+ },
711
+ destructive: {
712
+ DEFAULT: 'hsl(var(--destructive))',
713
+ foreground: 'hsl(var(--destructive-foreground))',
714
+ },
715
+ muted: {
716
+ DEFAULT: 'hsl(var(--muted))',
717
+ foreground: 'hsl(var(--muted-foreground))',
718
+ },
719
+ accent: {
720
+ DEFAULT: 'hsl(var(--accent))',
721
+ foreground: 'hsl(var(--accent-foreground))',
722
+ },
723
+ border: 'hsl(var(--border))',
724
+ ring: 'hsl(var(--ring))',
725
+ },
726
+ borderRadius: {
727
+ lg: 'var(--radius)',
728
+ md: 'calc(var(--radius) - 2px)',
729
+ sm: 'calc(var(--radius) - 4px)',
730
+ },
731
+ },
732
+ },
733
+ plugins: [],
734
+ };
735
+
736
+ export default config;
737
+ ''',
738
+ "postcss.config.js": '''module.exports = {
739
+ plugins: {
740
+ tailwindcss: {},
741
+ autoprefixer: {},
742
+ },
743
+ };
744
+ ''',
745
+ "next.config.js": '''/** @type {import('next').NextConfig} */
746
+ const nextConfig = {
747
+ images: {
748
+ remotePatterns: [],
749
+ formats: ['image/avif', 'image/webp'],
750
+ },
751
+ experimental: {
752
+ optimizePackageImports: ['lucide-react'],
753
+ },
754
+ };
755
+
756
+ module.exports = nextConfig;
757
+ ''',
758
+ "vite.config.ts": '''import { defineConfig } from 'vite';
759
+ import react from '@vitejs/plugin-react';
760
+ import path from 'path';
761
+
762
+ export default defineConfig({
763
+ plugins: [react()],
764
+ resolve: {
765
+ alias: {
766
+ '@': path.resolve(__dirname, './src'),
767
+ },
768
+ },
769
+ });
770
+ ''',
771
+ ".eslintrc.json": '''{
772
+ "extends": ["next/core-web-vitals", "prettier"],
773
+ "rules": {
774
+ "react/no-unescaped-entities": "off"
775
+ }
776
+ }
777
+ ''',
778
+ ".prettierrc": '''{
779
+ "semi": true,
780
+ "singleQuote": true,
781
+ "tabWidth": 2,
782
+ "trailingComma": "es5",
783
+ "printWidth": 100
784
+ }
785
+ ''',
786
+ ".gitignore": '''# Dependencies
787
+ node_modules/
788
+ .pnp
789
+ .pnp.js
790
+
791
+ # Build
792
+ .next/
793
+ out/
794
+ dist/
795
+ build/
796
+
797
+ # Environment
798
+ .env
799
+ .env.local
800
+ .env.*.local
801
+
802
+ # IDE
803
+ .vscode/
804
+ .idea/
805
+
806
+ # Debug
807
+ npm-debug.log*
808
+ yarn-debug.log*
809
+ yarn-error.log*
810
+
811
+ # OS
812
+ .DS_Store
813
+ Thumbs.db
814
+
815
+ # Testing
816
+ coverage/
817
+ ''',
818
+ "index.html": '''<!DOCTYPE html>
819
+ <html lang="en">
820
+ <head>
821
+ <meta charset="UTF-8" />
822
+ <link rel="icon" type="image/svg+xml" href="/vite.svg" />
823
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
824
+ <title>''' + name + '''</title>
825
+ </head>
826
+ <body>
827
+ <div id="root"></div>
828
+ <script type="module" src="/src/main.tsx"></script>
829
+ </body>
830
+ </html>
831
+ ''',
832
+ }
833
+
834
+
835
+ def scaffold_project(
836
+ name: str,
837
+ output_dir: Path,
838
+ template: str = "nextjs",
839
+ features: Optional[List[str]] = None,
840
+ dry_run: bool = False,
841
+ ) -> Dict:
842
+ """Scaffold a complete frontend project."""
843
+ features = features or []
844
+ project_path = output_dir / name
845
+
846
+ if project_path.exists() and not dry_run:
847
+ return {"error": f"Directory already exists: {project_path}"}
848
+
849
+ template_config = TEMPLATES.get(template)
850
+ if not template_config:
851
+ return {"error": f"Unknown template: {template}"}
852
+
853
+ created_files = []
854
+
855
+ # Create project directory
856
+ if not dry_run:
857
+ project_path.mkdir(parents=True, exist_ok=True)
858
+
859
+ # Generate base structure
860
+ created_files.extend(
861
+ generate_structure(project_path, template_config["structure"], dry_run)
862
+ )
863
+
864
+ # Generate config files
865
+ created_files.extend(
866
+ generate_config_files(project_path, template, name, features, dry_run)
867
+ )
868
+
869
+ # Add feature files
870
+ for feature in features:
871
+ if feature in FEATURES:
872
+ for file_path, content_key in FEATURES[feature]["files"].items():
873
+ full_path = project_path / file_path
874
+ if not dry_run:
875
+ full_path.parent.mkdir(parents=True, exist_ok=True)
876
+ content = FILE_CONTENTS.get(content_key, f"// TODO: Implement {content_key}")
877
+ full_path.write_text(content)
878
+ created_files.append(str(full_path))
879
+
880
+ return {
881
+ "name": name,
882
+ "template": template,
883
+ "template_name": template_config["name"],
884
+ "features": features,
885
+ "path": str(project_path),
886
+ "files_created": len(created_files),
887
+ "files": created_files,
888
+ "next_steps": [
889
+ f"cd {name}",
890
+ "npm install",
891
+ "npm run dev",
892
+ ],
893
+ }
894
+
895
+
896
+ def print_result(result: Dict) -> None:
897
+ """Print scaffolding result."""
898
+ if "error" in result:
899
+ print(f"Error: {result['error']}", file=sys.stderr)
900
+ return
901
+
902
+ print(f"\n{'='*60}")
903
+ print(f"Project Scaffolded: {result['name']}")
904
+ print(f"{'='*60}")
905
+ print(f"Template: {result['template_name']}")
906
+ print(f"Location: {result['path']}")
907
+ print(f"Files Created: {result['files_created']}")
908
+
909
+ if result["features"]:
910
+ print(f"Features: {', '.join(result['features'])}")
911
+
912
+ print(f"\nNext Steps:")
913
+ for step in result["next_steps"]:
914
+ print(f" $ {step}")
915
+
916
+ print(f"{'='*60}\n")
917
+
918
+
919
+ def main():
920
+ parser = argparse.ArgumentParser(
921
+ description="Scaffold a frontend project with best practices"
922
+ )
923
+ parser.add_argument(
924
+ "name",
925
+ help="Project name (kebab-case recommended)"
926
+ )
927
+ parser.add_argument(
928
+ "--dir", "-d",
929
+ default=".",
930
+ help="Output directory (default: current directory)"
931
+ )
932
+ parser.add_argument(
933
+ "--template", "-t",
934
+ choices=list(TEMPLATES.keys()),
935
+ default="nextjs",
936
+ help="Project template (default: nextjs)"
937
+ )
938
+ parser.add_argument(
939
+ "--features", "-f",
940
+ help="Comma-separated features to add (auth,api,forms,testing,storybook)"
941
+ )
942
+ parser.add_argument(
943
+ "--list-templates",
944
+ action="store_true",
945
+ help="List available templates"
946
+ )
947
+ parser.add_argument(
948
+ "--list-features",
949
+ action="store_true",
950
+ help="List available features"
951
+ )
952
+ parser.add_argument(
953
+ "--dry-run",
954
+ action="store_true",
955
+ help="Show what would be created without creating files"
956
+ )
957
+ parser.add_argument(
958
+ "--json",
959
+ action="store_true",
960
+ help="Output in JSON format"
961
+ )
962
+
963
+ args = parser.parse_args()
964
+
965
+ if args.list_templates:
966
+ print("\nAvailable Templates:")
967
+ for key, template in TEMPLATES.items():
968
+ print(f" {key}: {template['name']}")
969
+ print(f" {template['description']}")
970
+ return
971
+
972
+ if args.list_features:
973
+ print("\nAvailable Features:")
974
+ for key, feature in FEATURES.items():
975
+ print(f" {key}: {feature['description']}")
976
+ deps = ", ".join(feature.get("dependencies", []))
977
+ if deps:
978
+ print(f" Adds: {deps}")
979
+ return
980
+
981
+ features = []
982
+ if args.features:
983
+ features = [f.strip() for f in args.features.split(",")]
984
+ invalid = [f for f in features if f not in FEATURES]
985
+ if invalid:
986
+ print(f"Unknown features: {', '.join(invalid)}", file=sys.stderr)
987
+ print(f"Valid features: {', '.join(FEATURES.keys())}")
988
+ sys.exit(1)
989
+
990
+ result = scaffold_project(
991
+ name=args.name,
992
+ output_dir=Path(args.dir),
993
+ template=args.template,
994
+ features=features,
995
+ dry_run=args.dry_run,
996
+ )
997
+
998
+ if args.json:
999
+ print(json.dumps(result, indent=2))
1000
+ else:
1001
+ print_result(result)
1002
+
1003
+
1004
+ if __name__ == "__main__":
1005
+ main()