create-bunspace 0.3.1 → 0.5.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 (78) hide show
  1. package/dist/bin.js +335 -34
  2. package/dist/templates/fumadocs/MUST-FOLLOW-GUIDELINES.md +1616 -40
  3. package/dist/templates/monorepo/MUST-FOLLOW-GUIDELINES.md +1616 -40
  4. package/dist/templates/react-starter/MUST-FOLLOW-GUIDELINES.md +1845 -0
  5. package/dist/templates/react-starter/README.md +100 -0
  6. package/dist/templates/react-starter/bun.lock +1298 -0
  7. package/dist/templates/react-starter/components.json +27 -0
  8. package/dist/templates/react-starter/eslint.config.js +23 -0
  9. package/dist/templates/react-starter/index.html +36 -0
  10. package/dist/templates/react-starter/package.json +57 -0
  11. package/dist/templates/react-starter/public/registry.json +115 -0
  12. package/dist/templates/react-starter/public/themes/darkmatteviolet-dark.css +34 -0
  13. package/dist/templates/react-starter/public/themes/darkmatteviolet-light.css +34 -0
  14. package/dist/templates/react-starter/public/themes/default-dark.css +33 -0
  15. package/dist/templates/react-starter/public/themes/default-light.css +34 -0
  16. package/dist/templates/react-starter/public/themes/graphite-dark.css +34 -0
  17. package/dist/templates/react-starter/public/themes/graphite-light.css +34 -0
  18. package/dist/templates/react-starter/public/themes/synthwave84-dark.css +34 -0
  19. package/dist/templates/react-starter/public/themes/synthwave84-light.css +34 -0
  20. package/dist/templates/react-starter/public/vite.svg +1 -0
  21. package/dist/templates/react-starter/src/App.tsx +245 -0
  22. package/dist/templates/react-starter/src/assets/react.svg +1 -0
  23. package/dist/templates/react-starter/src/components/ThemeSelector.tsx +106 -0
  24. package/dist/templates/react-starter/src/components/animate-ui/components/buttons/icon.tsx +86 -0
  25. package/dist/templates/react-starter/src/components/animate-ui/components/buttons/theme-toggler.tsx +92 -0
  26. package/dist/templates/react-starter/src/components/animate-ui/primitives/animate/slot.tsx +96 -0
  27. package/dist/templates/react-starter/src/components/animate-ui/primitives/buttons/button.tsx +31 -0
  28. package/dist/templates/react-starter/src/components/animate-ui/primitives/effects/particles.tsx +155 -0
  29. package/dist/templates/react-starter/src/components/animate-ui/primitives/effects/theme-toggler.tsx +148 -0
  30. package/dist/templates/react-starter/src/components/component-example.tsx +444 -0
  31. package/dist/templates/react-starter/src/components/example.tsx +56 -0
  32. package/dist/templates/react-starter/src/index.css +131 -0
  33. package/dist/templates/react-starter/src/main.tsx +13 -0
  34. package/dist/templates/react-starter/src/providers/ThemeProvider.tsx +27 -0
  35. package/dist/templates/react-starter/tsconfig.app.json +36 -0
  36. package/dist/templates/react-starter/tsconfig.json +13 -0
  37. package/dist/templates/react-starter/tsconfig.node.json +26 -0
  38. package/dist/templates/react-starter/vite.config.ts +17 -0
  39. package/dist/templates/telegram-bot/MUST-FOLLOW-GUIDELINES.md +1845 -0
  40. package/package.json +6 -3
  41. package/templates/fumadocs/MUST-FOLLOW-GUIDELINES.md +1616 -40
  42. package/templates/monorepo/MUST-FOLLOW-GUIDELINES.md +1616 -40
  43. package/templates/react-starter/MUST-FOLLOW-GUIDELINES.md +1845 -0
  44. package/templates/react-starter/README.md +100 -0
  45. package/templates/react-starter/bun.lock +1298 -0
  46. package/templates/react-starter/components.json +27 -0
  47. package/templates/react-starter/eslint.config.js +23 -0
  48. package/templates/react-starter/index.html +36 -0
  49. package/templates/react-starter/package.json +57 -0
  50. package/templates/react-starter/public/registry.json +115 -0
  51. package/templates/react-starter/public/themes/darkmatteviolet-dark.css +34 -0
  52. package/templates/react-starter/public/themes/darkmatteviolet-light.css +34 -0
  53. package/templates/react-starter/public/themes/default-dark.css +33 -0
  54. package/templates/react-starter/public/themes/default-light.css +34 -0
  55. package/templates/react-starter/public/themes/graphite-dark.css +34 -0
  56. package/templates/react-starter/public/themes/graphite-light.css +34 -0
  57. package/templates/react-starter/public/themes/synthwave84-dark.css +34 -0
  58. package/templates/react-starter/public/themes/synthwave84-light.css +34 -0
  59. package/templates/react-starter/public/vite.svg +1 -0
  60. package/templates/react-starter/src/App.tsx +245 -0
  61. package/templates/react-starter/src/assets/react.svg +1 -0
  62. package/templates/react-starter/src/components/ThemeSelector.tsx +106 -0
  63. package/templates/react-starter/src/components/animate-ui/components/buttons/icon.tsx +86 -0
  64. package/templates/react-starter/src/components/animate-ui/components/buttons/theme-toggler.tsx +92 -0
  65. package/templates/react-starter/src/components/animate-ui/primitives/animate/slot.tsx +96 -0
  66. package/templates/react-starter/src/components/animate-ui/primitives/buttons/button.tsx +31 -0
  67. package/templates/react-starter/src/components/animate-ui/primitives/effects/particles.tsx +155 -0
  68. package/templates/react-starter/src/components/animate-ui/primitives/effects/theme-toggler.tsx +148 -0
  69. package/templates/react-starter/src/components/component-example.tsx +444 -0
  70. package/templates/react-starter/src/components/example.tsx +56 -0
  71. package/templates/react-starter/src/index.css +131 -0
  72. package/templates/react-starter/src/main.tsx +13 -0
  73. package/templates/react-starter/src/providers/ThemeProvider.tsx +27 -0
  74. package/templates/react-starter/tsconfig.app.json +36 -0
  75. package/templates/react-starter/tsconfig.json +13 -0
  76. package/templates/react-starter/tsconfig.node.json +26 -0
  77. package/templates/react-starter/vite.config.ts +17 -0
  78. package/templates/telegram-bot/MUST-FOLLOW-GUIDELINES.md +1845 -0
@@ -18,6 +18,91 @@
18
18
  | **Versioning** | Changesets v2.27.11 | Versionado de packages |
19
19
  | **Logging** | @mks2508/better-logger v4.0.0 | Logging estructurado |
20
20
  | **Error Handling** | @mks2508/no-throw v0.1.0 | Result pattern |
21
+ | **Commit Generation** | gemini-commit-wizard v1.1.3 | Commits estructurados con IA multi-provider |
22
+
23
+ ---
24
+
25
+ ## Commit Workflow — gemini-commit-wizard
26
+
27
+ **OBLIGATORIO**: Usar `bun run commit` para TODOS los commits. NUNCA hacer commits manuales sin formato estructurado.
28
+
29
+ ### Providers Disponibles
30
+
31
+ | Provider | Variable de Entorno | Modelo Default | Velocidad |
32
+ |----------|-------------------|---------------|-----------|
33
+ | **Gemini SDK** | `GEMINI_API_KEY` | `gemini-2.5-flash` | Rapido |
34
+ | **Groq** | `GROQ_API_KEY` | `llama-3.3-70b-versatile` | Ultra-rapido |
35
+ | **OpenRouter** | `OPENROUTER_API_KEY` | `anthropic/claude-sonnet-4` | Variable |
36
+ | **Gemini CLI** | _(necesita binario `gemini`)_ | CLI default | Moderado |
37
+
38
+ **Auto-deteccion**: Gemini SDK > Groq > OpenRouter > Gemini CLI. El primer provider con API key valida se usa automaticamente.
39
+
40
+ ### Comandos
41
+
42
+ ```bash
43
+ # Commit interactivo con IA (auto-detecta provider)
44
+ bun run commit
45
+
46
+ # Modo rapido sin prompts
47
+ bun run commit:quick
48
+
49
+ # Auto-aprobar sin push
50
+ bun run commit:auto
51
+
52
+ # Provider especifico
53
+ bun run commit -- --provider groq
54
+
55
+ # Provider + modelo especifico
56
+ bun run commit -- --provider openrouter --model anthropic/claude-sonnet-4
57
+ ```
58
+
59
+ ### Formato de Commit Estructurado
60
+
61
+ ```
62
+ type(scope): description
63
+
64
+ Body text (en idioma configurado)
65
+
66
+ <technical>
67
+ - Detalles tecnicos: archivos, funciones, tipos modificados
68
+ </technical>
69
+
70
+ <changelog>
71
+ ## [Type] [Emoji]
72
+ Entrada de changelog orientada al usuario
73
+ </changelog>
74
+ ```
75
+
76
+ **Types validos**: `feat`, `fix`, `refactor`, `docs`, `test`, `feat-phase` (feature incompleta)
77
+
78
+ ### Configuracion por Proyecto
79
+
80
+ Crear `.commit-wizard.json` en la raiz del proyecto:
81
+
82
+ ```json
83
+ {
84
+ "name": "mi-proyecto",
85
+ "description": "Descripcion breve",
86
+ "techStack": ["TypeScript", "Bun"],
87
+ "components": [
88
+ { "id": "api", "path": "src/api/", "name": "REST API" }
89
+ ],
90
+ "commitFormat": {
91
+ "titleLanguage": "english",
92
+ "bodyLanguage": "spanish",
93
+ "includeTechnical": true,
94
+ "includeChangelog": true
95
+ },
96
+ "provider": "groq",
97
+ "model": "llama-3.3-70b-versatile"
98
+ }
99
+ ```
100
+
101
+ ### Prohibido
102
+
103
+ - `git commit -m "mensaje"` sin formato estructurado
104
+ - Commits sin `<technical>` y `<changelog>` sections
105
+ - Usar providers sin API key configurada (el wizard valida automaticamente)
21
106
 
22
107
  ---
23
108
 
@@ -25,7 +110,7 @@
25
110
 
26
111
  ### Root del Monorepo
27
112
  ```
28
- mks-fumadocs-template/
113
+ mks-dev-environment/
29
114
  ├── docs/ # Documentacion del proyecto
30
115
  ├── tools/ # Scripts y herramientas de desarrollo
31
116
  ├── core/
@@ -105,29 +190,248 @@ export async function myFunction(
105
190
 
106
191
  ## REGLA 2: Logging - NUNCA console.log
107
192
 
108
- ### Obligatorio
193
+ Usar `@mks2508/better-logger` para todo el logging.
194
+
195
+ ### Imports y Setup Basico
109
196
 
110
197
  ```typescript
111
- import { createLogger } from 'mks-fumadocs-template/utils/logger';
198
+ // Singleton (recomendado para la mayoria de casos)
199
+ import logger from '@mks2508/better-logger';
112
200
 
113
- const log = createLogger('MyComponent');
201
+ // O crear instancia personalizada
202
+ import { Logger } from '@mks2508/better-logger';
203
+ const log = new Logger({
204
+ verbosity: 'debug', // 'debug' | 'info' | 'warn' | 'error' | 'silent'
205
+ enableStackTrace: true, // Muestra archivo:linea
206
+ bufferSize: 1000, // Para exportacion de logs
207
+ });
208
+ ```
114
209
 
115
- // CORRECTO
116
- log.info('Started');
117
- log.success('Completed');
118
- log.warn('High memory usage');
119
- log.error('Failed to connect', { error });
120
- log.critical('System failure');
210
+ ### Metodos de Logging
211
+
212
+ ```typescript
213
+ // Niveles basicos
214
+ logger.debug('Debug message', { context });
215
+ logger.info('Info message');
216
+ logger.warn('Warning message');
217
+ logger.error('Error message', errorObject);
218
+ logger.success('Operation completed');
219
+ logger.critical('System failure!');
220
+ logger.trace('Trace from nested function');
221
+ ```
222
+
223
+ ### Scoped Loggers (USAR SIEMPRE en servicios)
224
+
225
+ ```typescript
226
+ // ComponentLogger - Para componentes UI y servicios
227
+ const authLog = logger.component('AuthService');
228
+ authLog.info('Usuario autenticando...'); // [COMPONENT] [AuthService] Usuario...
229
+ authLog.success('Login exitoso');
230
+ authLog.lifecycle('mount', 'Component mounted');
231
+ authLog.stateChange('idle', 'loading');
232
+
233
+ // APILogger - Para endpoints y llamadas HTTP
234
+ const apiLog = logger.api('UserAPI');
235
+ apiLog.info('GET /users'); // [API] [UserAPI] GET /users
236
+ apiLog.slow('Response slow', 2500); // [API] [SLOW] Response slow (2500ms)
237
+ apiLog.rateLimit('Too many requests'); // [API] [RATE_LIMIT]
238
+ apiLog.deprecated('Use /v2/users instead');
239
+
240
+ // ScopedLogger - Generico con contextos anidados
241
+ const dbLog = logger.scope('Database');
242
+ dbLog.info('Query executing');
243
+ dbLog.context('transactions').run(() => {
244
+ dbLog.info('Inside transaction'); // [Database:transactions] Inside...
245
+ });
246
+ ```
247
+
248
+ ### Timing y Performance
249
+
250
+ ```typescript
251
+ // Medir operaciones
252
+ logger.time('db-query');
253
+ await db.query('SELECT * FROM users');
254
+ logger.timeEnd('db-query'); // Timer: db-query - 234.56ms
255
+
256
+ // En scoped loggers
257
+ const serviceLog = logger.component('ProductService');
258
+ serviceLog.time('fetch-products');
259
+ const products = await fetchProducts();
260
+ serviceLog.timeEnd('fetch-products');
261
+ ```
262
+
263
+ ### Badges para Contexto
264
+
265
+ ```typescript
266
+ // Badges encadenables
267
+ logger.badges(['CACHE', 'HIT']).info('Data from cache');
268
+ logger.badge('v2').badge('stable').info('API response');
269
+
270
+ // En scoped loggers
271
+ const api = logger.api('GraphQL');
272
+ api.badges(['mutation', 'user']).info('createUser executed');
273
+ ```
274
+
275
+ ### Transports (Envio de Logs)
276
+
277
+ ```typescript
278
+ import logger, {
279
+ FileTransport,
280
+ HttpTransport,
281
+ addTransport
282
+ } from '@mks2508/better-logger';
283
+
284
+ // Transport a archivo (solo Node.js/Bun)
285
+ logger.addTransport({
286
+ target: 'file',
287
+ options: {
288
+ destination: '/var/log/app.log',
289
+ batchSize: 100,
290
+ flushInterval: 5000 // ms
291
+ },
292
+ level: 'warn' // Solo warn+ van al archivo
293
+ });
294
+
295
+ // Transport HTTP (envia a servidor de logs)
296
+ logger.addTransport({
297
+ target: 'http',
298
+ options: {
299
+ url: 'https://logs.example.com/ingest',
300
+ headers: { 'Authorization': 'Bearer xxx' },
301
+ batchSize: 50,
302
+ flushInterval: 10000
303
+ }
304
+ });
305
+
306
+ // Flush manual antes de cerrar
307
+ await logger.flushTransports();
308
+ await logger.closeTransports();
309
+ ```
310
+
311
+ ### Hooks y Middleware
312
+
313
+ ```typescript
314
+ // Hook beforeLog - agregar metadata
315
+ logger.on('beforeLog', (entry) => {
316
+ entry.correlationId = getCorrelationId();
317
+ entry.userId = getCurrentUserId();
318
+ return entry;
319
+ });
320
+
321
+ // Hook afterLog - side effects
322
+ logger.on('afterLog', (entry) => {
323
+ if (entry.level === 'error') {
324
+ sendToErrorTracking(entry);
325
+ }
326
+ });
327
+
328
+ // Middleware - pipeline de procesamiento
329
+ logger.use((entry, next) => {
330
+ // Enriquecer con request context
331
+ const store = asyncLocalStorage.getStore();
332
+ if (store?.requestId) {
333
+ entry.requestId = store.requestId;
334
+ }
335
+ next();
336
+ });
337
+ ```
338
+
339
+ ### Serializers Personalizados
340
+
341
+ ```typescript
342
+ // Serializar errores de forma estructurada
343
+ logger.addSerializer(Error, (err) => ({
344
+ name: err.name,
345
+ message: err.message,
346
+ stack: err.stack?.split('\n').slice(0, 5),
347
+ code: (err as any).code
348
+ }));
349
+
350
+ // Serializar objetos custom
351
+ logger.addSerializer(User, (user) => ({
352
+ id: user.id,
353
+ email: '[REDACTED]',
354
+ role: user.role
355
+ }));
356
+ ```
357
+
358
+ ### Configuracion Frontend vs Backend
359
+
360
+ ```typescript
361
+ // === BACKEND (Node.js/Bun) ===
362
+ import logger from '@mks2508/better-logger';
363
+
364
+ // Preset optimizado para terminal
365
+ logger.preset('cyberpunk');
366
+ logger.showTimestamp();
367
+ logger.showLocation();
368
+
369
+ // Transport a archivo
370
+ logger.addTransport({
371
+ target: 'file',
372
+ options: { destination: './logs/app.log' }
373
+ });
374
+
375
+ // === FRONTEND (Browser) ===
376
+ import logger from '@mks2508/better-logger';
377
+
378
+ // Preset con colores CSS
379
+ logger.preset('default');
380
+ logger.hideLocation(); // No util en browser
381
+
382
+ // Transport HTTP para enviar errores
383
+ logger.addTransport({
384
+ target: 'http',
385
+ options: { url: '/api/logs' },
386
+ level: 'error' // Solo errores al servidor
387
+ });
388
+ ```
389
+
390
+ ### Verbosity y Filtrado
391
+
392
+ ```typescript
393
+ // Cambiar nivel de verbosidad
394
+ logger.setVerbosity('warn'); // Solo warn, error, critical
395
+ logger.setVerbosity('silent'); // Desactiva todo
396
+ logger.setVerbosity('debug'); // Muestra todo
397
+
398
+ // Configuracion condicional
399
+ if (process.env.NODE_ENV === 'production') {
400
+ logger.setVerbosity('warn');
401
+ logger.hideLocation();
402
+ }
403
+ ```
404
+
405
+ ### Utilidades de Grupos y Tablas
406
+
407
+ ```typescript
408
+ // Tablas de datos
409
+ logger.table([
410
+ { name: 'Alice', age: 30 },
411
+ { name: 'Bob', age: 25 }
412
+ ]);
413
+
414
+ // Grupos colapsables
415
+ logger.group('Database Operations');
416
+ logger.info('Connecting...');
417
+ logger.info('Querying...');
418
+ logger.groupEnd();
419
+
420
+ // Grupo colapsado por defecto
421
+ logger.group('Debug Details', true);
422
+ logger.debug('Verbose info here');
423
+ logger.groupEnd();
121
424
  ```
122
425
 
123
426
  ### Prohibido
124
427
 
125
428
  ```typescript
126
- // INCORRECTO
429
+ // INCORRECTO - NUNCA usar
127
430
  console.log('Started');
128
431
  console.error('Failed');
129
432
  console.info('Info');
130
433
  console.warn('Warning');
434
+ console.debug('Debug');
131
435
  ```
132
436
 
133
437
  ---
@@ -136,43 +440,212 @@ console.warn('Warning');
136
440
 
137
441
  ### Obligatorio
138
442
 
139
- TODA operacion que pueda fallar DEBE usar `Result<T, E>` del package `mks-fumadocs-template/utils/result`:
443
+ TODA operacion que pueda fallar DEBE usar `Result<T, E>` del package `@mks2508/no-throw`:
140
444
 
141
445
  ```typescript
142
446
  import {
143
- ok,
144
- tryCatch,
145
- type Result
146
- } from 'mks-fumadocs-template/utils/result';
147
- import {
148
- createAppError,
149
- AppErrorCode
150
- } from 'mks-fumadocs-template/utils/result';
447
+ ok, err, fail,
448
+ isOk, isErr,
449
+ map, mapErr, flatMap,
450
+ match,
451
+ tryCatch, tryCatchAsync, fromPromise,
452
+ unwrap, unwrapOr, unwrapOrElse,
453
+ tap, tapErr,
454
+ collect, all,
455
+ type Result, type ResultError
456
+ } from '@mks2508/no-throw';
457
+ ```
151
458
 
152
- async function fetchData(
153
- url: string
154
- ): Promise<Result<string, AppError>> {
155
- const result = await tryCatch(
459
+ ### Constructores
460
+
461
+ ```typescript
462
+ // Crear resultado exitoso
463
+ const success = ok(42); // Result<number, never>
464
+ const success2 = ok({ name: 'John' }); // Result<{name: string}, never>
465
+
466
+ // Crear resultado de error
467
+ const error = err('Something failed'); // Result<never, string>
468
+
469
+ // Crear error estructurado con fail()
470
+ const structuredError = fail(
471
+ 'NETWORK_ERROR', // code
472
+ 'Failed to fetch data', // message
473
+ originalError // cause (opcional)
474
+ );
475
+ // Retorna: Result<never, ResultError<'NETWORK_ERROR'>>
476
+ ```
477
+
478
+ ### Type Guards
479
+
480
+ ```typescript
481
+ const result = await fetchData(url);
482
+
483
+ if (isOk(result)) {
484
+ console.log(result.value); // Tipo: T
485
+ }
486
+
487
+ if (isErr(result)) {
488
+ console.log(result.error); // Tipo: E
489
+ }
490
+ ```
491
+
492
+ ### Transformaciones
493
+
494
+ ```typescript
495
+ // map - transforma el valor si es Ok
496
+ const doubled = map(result, (n) => n * 2);
497
+
498
+ // mapErr - transforma el error si es Err
499
+ const mappedErr = mapErr(result, (e) => ({ ...e, timestamp: Date.now() }));
500
+
501
+ // flatMap - encadena operaciones que retornan Result
502
+ const chained = flatMap(result, (value) => {
503
+ if (value > 100) return err('Too large');
504
+ return ok(value * 2);
505
+ });
506
+ ```
507
+
508
+ ### Pattern Matching
509
+
510
+ ```typescript
511
+ const message = match(result, {
512
+ ok: (value) => `Success: ${value}`,
513
+ err: (error) => `Error: ${error.message}`
514
+ });
515
+ ```
516
+
517
+ ### Manejo de Excepciones
518
+
519
+ ```typescript
520
+ // tryCatch - para operaciones sincronas
521
+ const syncResult = tryCatch(
522
+ () => JSON.parse(jsonString),
523
+ 'PARSE_ERROR'
524
+ );
525
+
526
+ // tryCatchAsync - para operaciones async
527
+ const asyncResult = await tryCatchAsync(
528
+ async () => {
529
+ const response = await fetch(url);
530
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
531
+ return response.json();
532
+ },
533
+ 'NETWORK_ERROR'
534
+ );
535
+
536
+ // fromPromise - convierte Promise a Result
537
+ const promiseResult = await fromPromise(
538
+ fetch(url).then(r => r.json()),
539
+ 'FETCH_ERROR'
540
+ );
541
+ ```
542
+
543
+ ### Unwrap
544
+
545
+ ```typescript
546
+ // unwrap - obtiene valor o lanza error
547
+ const value = unwrap(result); // Throws si es Err
548
+
549
+ // unwrapOr - valor por defecto
550
+ const valueOrDefault = unwrapOr(result, 0);
551
+
552
+ // unwrapOrElse - valor calculado
553
+ const valueOrComputed = unwrapOrElse(result, (error) => {
554
+ log.error('Using fallback due to:', error);
555
+ return defaultValue;
556
+ });
557
+ ```
558
+
559
+ ### Efectos Secundarios
560
+
561
+ ```typescript
562
+ // tap - ejecuta efecto si es Ok (no modifica resultado)
563
+ const logged = tap(result, (value) => {
564
+ log.info('Got value:', value);
565
+ });
566
+
567
+ // tapErr - ejecuta efecto si es Err
568
+ const errorLogged = tapErr(result, (error) => {
569
+ log.error('Operation failed:', error);
570
+ });
571
+ ```
572
+
573
+ ### Colecciones
574
+
575
+ ```typescript
576
+ // collect - convierte array de Results en Result de array
577
+ const results: Result<number, string>[] = [ok(1), ok(2), ok(3)];
578
+ const collected = collect(results); // Result<number[], string>
579
+
580
+ // all - igual que collect (alias)
581
+ const allResults = all([ok(1), ok(2), ok(3)]);
582
+ ```
583
+
584
+ ### Ejemplo Completo
585
+
586
+ ```typescript
587
+ import { ok, fail, isErr, tryCatchAsync, match } from '@mks2508/no-throw';
588
+ import logger from '@mks2508/better-logger';
589
+
590
+ const log = logger.component('UserService');
591
+
592
+ async function fetchUser(
593
+ id: string
594
+ ): Promise<Result<IUser, ResultError<'NOT_FOUND' | 'NETWORK_ERROR'>>> {
595
+ const result = await tryCatchAsync(
156
596
  async () => {
157
- const response = await fetch(url);
597
+ const response = await fetch(`/api/users/${id}`);
598
+ if (response.status === 404) {
599
+ throw { code: 'NOT_FOUND' };
600
+ }
158
601
  if (!response.ok) {
159
602
  throw new Error(`HTTP ${response.status}`);
160
603
  }
161
- return await response.text();
604
+ return await response.json();
162
605
  },
163
- AppErrorCode.NetworkError
606
+ 'NETWORK_ERROR'
164
607
  );
165
608
 
166
- if (result.isErr()) {
167
- return createAppError(
168
- AppErrorCode.NetworkError,
169
- `Failed to fetch from ${url}`,
170
- result.error
171
- );
609
+ if (isErr(result)) {
610
+ const error = result.error;
611
+ if (error.cause?.code === 'NOT_FOUND') {
612
+ return fail('NOT_FOUND', `User ${id} not found`);
613
+ }
614
+ return fail('NETWORK_ERROR', `Failed to fetch user ${id}`, error);
172
615
  }
173
616
 
174
617
  return ok(result.value);
175
618
  }
619
+
620
+ // Uso
621
+ const userResult = await fetchUser('123');
622
+ const message = match(userResult, {
623
+ ok: (user) => {
624
+ log.success(`User loaded: ${user.name}`);
625
+ return user;
626
+ },
627
+ err: (error) => {
628
+ log.error(`Failed: ${error.message}`);
629
+ return null;
630
+ }
631
+ });
632
+ ```
633
+
634
+ ### Prohibido
635
+
636
+ ```typescript
637
+ // INCORRECTO - NUNCA usar try/catch directo sin Result
638
+ try {
639
+ const data = await fetchData();
640
+ } catch (e) {
641
+ console.error(e);
642
+ }
643
+
644
+ // INCORRECTO - NUNCA lanzar excepciones
645
+ throw new Error('Something failed');
646
+
647
+ // CORRECTO - Siempre retornar Result
648
+ return fail('ERROR_CODE', 'Description', cause);
176
649
  ```
177
650
 
178
651
  ---
@@ -259,11 +732,1114 @@ Antes de hacer commit de codigo, verificar:
259
732
 
260
733
  ---
261
734
 
262
- ## Fuentes de Referencia
735
+ ## REGLA 7: TransactionState - Estados de UI
263
736
 
264
- - **CLAUDE.md** - Guia de arquitectura del monorepo
265
- - **@mks2508/better-logger** - Documentacion del logger
266
- - **@mks2508/no-throw** - Documentacion del Result pattern
267
- - **Arktype** - https://arktype.io/
268
- - **Rolldown** - https://rollup.rs/
269
- - **Oxlint** - https://oxlint.com/
737
+ ### Estados Soportados
738
+
739
+ | Estado | Uso |
740
+ |--------|-----|
741
+ | `initial` | Estado inicial, sin datos |
742
+ | `loading` | Primera carga en progreso |
743
+ | `revalidating` | Recargando con datos previos |
744
+ | `success` | Operacion completada con exito |
745
+ | `failed` | Error en la operacion |
746
+
747
+ ### Factories y Guards
748
+
749
+ ```typescript
750
+ // Factories
751
+ createInitialState()
752
+ createLoadingState(message?: string)
753
+ createRevalidatingState(previousData: T)
754
+ createSuccessState(data: T, message?: string)
755
+ createFailedState(error: E)
756
+
757
+ // Guards
758
+ isInitial(state) // true si initial
759
+ isLoading(state) // true si loading
760
+ isRevalidating(state)
761
+ isSuccess(state)
762
+ isFailed(state)
763
+ isPending(state) // loading OR revalidating
764
+ isCompleted(state) // success OR failed
765
+ hasData(state) // success OR revalidating
766
+ ```
767
+
768
+ ### Patron en Hooks
769
+
770
+ ```typescript
771
+ const [state, setState] = useState<TransactionState<T, E>>(createInitialState);
772
+
773
+ const load = useCallback(async () => {
774
+ setState(createLoadingState('Cargando...'));
775
+ const result = await service.fetch();
776
+ if (isSuccess(result)) {
777
+ setState(createSuccessState(result.data));
778
+ } else {
779
+ setState(createFailedState(result.error));
780
+ }
781
+ }, []);
782
+ ```
783
+
784
+ ---
785
+
786
+ ## REGLA 8: Arquitectura BLO (Business Logic Layer)
787
+
788
+ ### Principios Fundamentales
789
+
790
+ | Principio | Descripcion |
791
+ |-----------|-------------|
792
+ | **Separacion clara** | UI y logica de negocio en capas distintas |
793
+ | **Handlers** | Clases puras TypeScript sin dependencias React |
794
+ | **Hooks** | Puente reactivo entre handlers y componentes |
795
+ | **Componentes** | Solo UI y eventos, sin logica de negocio |
796
+
797
+ ### Clasificacion de Componentes
798
+
799
+ #### Componentes UI Reutilizables (`components/ui/`)
800
+ - **Alta reusabilidad** en diferentes contextos
801
+ - **Baja complejidad** y focalizacion especifica
802
+ - **Estructura simplificada** (archivo unico o pocos archivos)
803
+ - Ejemplos: Button, Input, Card, Modal
804
+
805
+ #### Componentes de Dominio (`components/`)
806
+ - **Alta complejidad** y logica de negocio especifica
807
+ - **Estructura completa** con handler y hook
808
+ - **Subcomponentes** si es necesario
809
+ - Ejemplos: ProductCard, UserProfile, OrderDetails
810
+
811
+ ### Estructura Completa (Componentes Complejos)
812
+
813
+ ```
814
+ components/
815
+ └── ProductCard/
816
+ ├── index.tsx # Componente React puro
817
+ ├── ProductCard.types.ts # Interfaces y tipos
818
+ ├── ProductCard.styles.ts # Clases Tailwind con CVA
819
+ ├── ProductCard.handler.ts # Logica de negocio (BLO)
820
+ ├── ProductCard.hook.ts # Hook React con estado
821
+ └── components/ # Subcomponentes (opcional)
822
+ ├── ProductImage/
823
+ └── ProductActions/
824
+ ```
825
+
826
+ ### Componente React (`index.tsx`)
827
+
828
+ ```typescript
829
+ import React from 'react';
830
+ import { IProductCardProps } from './ProductCard.types';
831
+ import { styles } from './ProductCard.styles';
832
+ import { useProductCard } from './ProductCard.hook';
833
+
834
+ export const ProductCard: React.FC<IProductCardProps> = (props) => {
835
+ const { state, actions } = useProductCard(props);
836
+
837
+ return (
838
+ <div className={styles.container}>
839
+ {/* Solo UI y eventos - sin logica de negocio */}
840
+ <h3 className={styles.title}>{state.data?.name}</h3>
841
+ <button onClick={actions.addToCart}>Agregar</button>
842
+ </div>
843
+ );
844
+ };
845
+ ```
846
+
847
+ **Reglas del componente:**
848
+ - ✅ Solo UI: renderizado y eventos
849
+ - ✅ Props inmutables: no modificar props directamente
850
+ - ✅ Estado delegado: usar hook para estado y acciones
851
+ - ❌ Sin logica: no business logic en el componente
852
+
853
+ ### Tipos TypeScript (`.types.ts`)
854
+
855
+ ```typescript
856
+ export interface IProductCardProps {
857
+ productId: string;
858
+ initialData?: IProduct;
859
+ autoLoad?: boolean;
860
+ className?: string;
861
+ onAddToCart?: (id: string) => void;
862
+ }
863
+
864
+ export interface IProductCardState {
865
+ isLoading: boolean;
866
+ data: IProduct | null;
867
+ error: string | null;
868
+ }
869
+
870
+ export interface IProductCardActions {
871
+ loadProduct: () => Promise<void>;
872
+ addToCart: () => void;
873
+ reset: () => void;
874
+ }
875
+ ```
876
+
877
+ ### Estilos Tailwind con CVA (`.styles.ts`)
878
+
879
+ ```typescript
880
+ import { cva } from 'class-variance-authority';
881
+
882
+ export const containerVariants = cva(
883
+ "w-full p-4 bg-white border rounded-lg transition-shadow",
884
+ {
885
+ variants: {
886
+ variant: {
887
+ default: "border-gray-200 hover:shadow-md",
888
+ featured: "border-blue-200 bg-blue-50 hover:shadow-lg",
889
+ compact: "p-2 border-gray-100",
890
+ },
891
+ size: {
892
+ sm: "max-w-xs",
893
+ md: "max-w-sm",
894
+ lg: "max-w-md",
895
+ }
896
+ },
897
+ defaultVariants: {
898
+ variant: "default",
899
+ size: "md",
900
+ }
901
+ }
902
+ );
903
+
904
+ export const styles = {
905
+ container: containerVariants,
906
+ header: "flex items-center justify-between mb-4",
907
+ title: "text-lg font-semibold text-gray-900",
908
+ content: "text-gray-600 min-h-[100px]",
909
+ actions: "flex gap-2 mt-4 pt-4 border-t border-gray-200",
910
+ loading: "flex items-center justify-center py-8 text-gray-500",
911
+ error: "flex items-center justify-center py-8 text-red-500 bg-red-50 rounded",
912
+ };
913
+ ```
914
+
915
+ ### Handler BLO (`.handler.ts`)
916
+
917
+ ```typescript
918
+ import { IProductCardState } from './ProductCard.types';
919
+
920
+ export class ProductCardHandler {
921
+ private state: IProductCardState = {
922
+ isLoading: false,
923
+ data: null,
924
+ error: null,
925
+ };
926
+
927
+ constructor(initialData?: IProduct) {
928
+ if (initialData) {
929
+ this.state.data = initialData;
930
+ }
931
+ }
932
+
933
+ getState(): IProductCardState {
934
+ return { ...this.state }; // Siempre retornar copia
935
+ }
936
+
937
+ async loadProduct(productId: string): Promise<void> {
938
+ this.state.isLoading = true;
939
+ this.state.error = null;
940
+
941
+ try {
942
+ // Logica de negocio pura - sin React
943
+ const response = await fetch(`/api/products/${productId}`);
944
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
945
+ this.state.data = await response.json();
946
+ } catch (error) {
947
+ this.state.error = error instanceof Error ? error.message : 'Error';
948
+ } finally {
949
+ this.state.isLoading = false;
950
+ }
951
+ }
952
+
953
+ reset(): void {
954
+ this.state = { isLoading: false, data: null, error: null };
955
+ }
956
+ }
957
+ ```
958
+
959
+ **Reglas del handler:**
960
+ - ✅ Clase pura TypeScript: sin dependencias de React
961
+ - ✅ Estado inmutable: siempre retornar copias
962
+ - ✅ Logica de negocio: validaciones, transformaciones, API calls
963
+ - ✅ Testing facil: sin dependencias externas
964
+ - ❌ Sin React: no hooks, no JSX, no estado React
965
+
966
+ ### Hook React (`.hook.ts`)
967
+
968
+ ```typescript
969
+ import { useState, useCallback, useEffect } from 'react';
970
+ import { IProductCardProps, IProductCardState, IProductCardActions } from './ProductCard.types';
971
+ import { ProductCardHandler } from './ProductCard.handler';
972
+
973
+ export const useProductCard = (props: IProductCardProps) => {
974
+ const [handler] = useState(() => new ProductCardHandler(props.initialData));
975
+ const [state, setState] = useState<IProductCardState>(handler.getState());
976
+
977
+ const loadProduct = useCallback(async () => {
978
+ await handler.loadProduct(props.productId);
979
+ setState(handler.getState());
980
+ }, [handler, props.productId]);
981
+
982
+ const addToCart = useCallback(() => {
983
+ if (state.data && props.onAddToCart) {
984
+ props.onAddToCart(state.data.id);
985
+ }
986
+ }, [state.data, props.onAddToCart]);
987
+
988
+ const reset = useCallback(() => {
989
+ handler.reset();
990
+ setState(handler.getState());
991
+ }, [handler]);
992
+
993
+ useEffect(() => {
994
+ if (props.autoLoad) {
995
+ loadProduct();
996
+ }
997
+ }, [props.autoLoad, loadProduct]);
998
+
999
+ return {
1000
+ state,
1001
+ actions: { loadProduct, addToCart, reset } as IProductCardActions,
1002
+ };
1003
+ };
1004
+ ```
1005
+
1006
+ **Reglas del hook:**
1007
+ - ✅ Puente reactivo: entre handler y componente
1008
+ - ✅ Estado React: sincronizado con handler
1009
+ - ✅ Callbacks memorizados: useCallback para optimizacion
1010
+ - ✅ Efectos controlados: auto-load, dependencies
1011
+ - ✅ Interface consistente: siempre retorna `{ state, actions }`
1012
+
1013
+ ### Nombres de Archivos
1014
+
1015
+ ```typescript
1016
+ // CORRECTO - PascalCase
1017
+ ProductCard.tsx
1018
+ UserProfile.tsx
1019
+ DataTable.tsx
1020
+
1021
+ // INCORRECTO
1022
+ my-component.tsx
1023
+ user_profile.tsx
1024
+ dataTable.tsx
1025
+ ```
1026
+
1027
+ ### Generacion con mks-ui CLI
1028
+
1029
+ ```bash
1030
+ # Componente UI simple (archivo unico)
1031
+ mks-ui component Button --ui
1032
+
1033
+ # Componente complejo con BLO
1034
+ mks-ui component ProductCard --complex
1035
+
1036
+ # Vista previa sin crear
1037
+ mks-ui component TestComponent --dry-run
1038
+
1039
+ # Servicio backend
1040
+ mks-ui service ProductService
1041
+ ```
1042
+
1043
+ ---
1044
+
1045
+ ## REGLA 9: Zustand con Slices
1046
+
1047
+ ### Store Central por Dominio
1048
+
1049
+ ```typescript
1050
+ // stores/app.store.ts
1051
+ import { create } from 'zustand';
1052
+
1053
+ interface IAppStore {
1054
+ // UI Slice
1055
+ ui: { sidebarOpen: boolean; theme: 'light' | 'dark' };
1056
+ toggleSidebar: () => void;
1057
+
1058
+ // Data Slice
1059
+ users: IUser[];
1060
+ setUsers: (users: IUser[]) => void;
1061
+ }
1062
+
1063
+ export const useAppStore = create<IAppStore>((set) => ({
1064
+ ui: { sidebarOpen: true, theme: 'light' },
1065
+ toggleSidebar: () => set((s) => ({
1066
+ ui: { ...s.ui, sidebarOpen: !s.ui.sidebarOpen }
1067
+ })),
1068
+
1069
+ users: [],
1070
+ setUsers: (users) => set({ users }),
1071
+ }));
1072
+ ```
1073
+
1074
+ ### Selectores Puros
1075
+
1076
+ ```typescript
1077
+ // CORRECTO - Selector especifico previene renders
1078
+ const theme = useAppStore((s) => s.ui.theme);
1079
+
1080
+ // INCORRECTO - Re-render en cualquier cambio
1081
+ const store = useAppStore();
1082
+ const theme = store.ui.theme;
1083
+ ```
1084
+
1085
+ ---
1086
+
1087
+ ## REGLA 10: Handler Pattern
1088
+
1089
+ ### Fachada de Orquestacion
1090
+
1091
+ ```typescript
1092
+ // handlers/useUserHandler.ts
1093
+ export const useUserHandler = (service: UserService) => {
1094
+ const { state, load } = useUserLoader(service);
1095
+ const setUsers = useAppStore((s) => s.setUsers);
1096
+
1097
+ const refresh = useCallback(async (id: string) => {
1098
+ const result = await load(id);
1099
+ if (isSuccess(result)) {
1100
+ setUsers([result.data]);
1101
+ }
1102
+ return result;
1103
+ }, [load, setUsers]);
1104
+
1105
+ return useMemo(() => ({
1106
+ isLoading: isPending(state),
1107
+ hasError: isFailed(state),
1108
+ user: hasData(state) ? state.data : null,
1109
+ refresh,
1110
+ }), [state, refresh]);
1111
+ };
1112
+ ```
1113
+
1114
+ ### Reglas del Handler
1115
+
1116
+ - API publica memorizada con `useMemo`
1117
+ - Callbacks con `useCallback`
1118
+ - No exponer detalles internos (state crudo)
1119
+ - No usar `useEffect` salvo sincronizacion externa
1120
+
1121
+ ---
1122
+
1123
+ ## REGLA 11: BaseService para HTTP
1124
+
1125
+ ### Clase Base
1126
+
1127
+ ```typescript
1128
+ export abstract class BaseService {
1129
+ constructor(
1130
+ protected readonly baseUrl: string,
1131
+ protected readonly fetchImpl = fetch
1132
+ ) {}
1133
+
1134
+ protected async request<T>({
1135
+ path,
1136
+ method = 'GET',
1137
+ body,
1138
+ timeoutMs = 15000,
1139
+ signal,
1140
+ }: IRequestOptions): Promise<Response> {
1141
+ const controller = new AbortController();
1142
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
1143
+
1144
+ try {
1145
+ return await this.fetchImpl(`${this.baseUrl}${path}`, {
1146
+ method,
1147
+ headers: { 'content-type': 'application/json' },
1148
+ body: body ? JSON.stringify(body) : undefined,
1149
+ signal: signal ?? controller.signal,
1150
+ });
1151
+ } finally {
1152
+ clearTimeout(timeoutId);
1153
+ }
1154
+ }
1155
+ }
1156
+ ```
1157
+
1158
+ ### Error Taxonomy
1159
+
1160
+ ```typescript
1161
+ export type ServiceError =
1162
+ | { kind: 'timeout'; message: string }
1163
+ | { kind: 'unauthorized'; message: string } // 401
1164
+ | { kind: 'forbidden'; message: string } // 403
1165
+ | { kind: 'not_found'; message: string } // 404
1166
+ | { kind: 'conflict'; message: string } // 409
1167
+ | { kind: 'too_many_requests'; message: string } // 429
1168
+ | { kind: 'server_error'; message: string; status: number } // 5xx
1169
+ | { kind: 'decode_error'; message: string }
1170
+ | { kind: 'unexpected'; message: string };
1171
+ ```
1172
+
1173
+ ### Servicio Tipado
1174
+
1175
+ ```typescript
1176
+ export class UserService extends BaseService {
1177
+ async getUser(id: string): Promise<Result<IUser, ServiceError>> {
1178
+ try {
1179
+ const res = await this.request({ path: `/users/${id}` });
1180
+
1181
+ if (res.status === 401) return err({ kind: 'unauthorized', message: 'No autenticado' });
1182
+ if (res.status === 403) return err({ kind: 'forbidden', message: 'No autorizado' });
1183
+ if (res.status === 404) return err({ kind: 'not_found', message: 'Usuario no encontrado' });
1184
+ if (!res.ok) return err({ kind: 'server_error', message: 'Error servidor', status: res.status });
1185
+
1186
+ const json = await res.json();
1187
+ const parsed = UserSchema.safeParse(json);
1188
+ if (!parsed.success) return err({ kind: 'decode_error', message: parsed.error.message });
1189
+
1190
+ return ok(parsed.data);
1191
+ } catch (e) {
1192
+ if ((e as Error)?.name === 'AbortError') {
1193
+ return err({ kind: 'timeout', message: 'Timeout' });
1194
+ }
1195
+ return err({ kind: 'unexpected', message: (e as Error)?.message ?? 'Error inesperado' });
1196
+ }
1197
+ }
1198
+ }
1199
+ ```
1200
+
1201
+ ---
1202
+
1203
+ ## REGLA 12: React Best Practices
1204
+
1205
+ ### Minimizar useEffect
1206
+
1207
+ ```typescript
1208
+ // CORRECTO - useMemo para derivar datos
1209
+ const filteredUsers = useMemo(
1210
+ () => users.filter(u => u.active),
1211
+ [users]
1212
+ );
1213
+
1214
+ // INCORRECTO - useEffect para derivar datos
1215
+ const [filteredUsers, setFiltered] = useState([]);
1216
+ useEffect(() => {
1217
+ setFiltered(users.filter(u => u.active));
1218
+ }, [users]);
1219
+ ```
1220
+
1221
+ ### memo para Componentes Puros
1222
+
1223
+ ```typescript
1224
+ // CORRECTO
1225
+ export const UserCard = memo(({ user }: IUserCardProps) => (
1226
+ <div>{user.name}</div>
1227
+ ));
1228
+
1229
+ // useState solo para estado efimero local
1230
+ const [inputValue, setInputValue] = useState('');
1231
+ ```
1232
+
1233
+ ---
1234
+
1235
+ ## Checklist Pre-Commit
1236
+
1237
+ Antes de hacer commit de codigo, verificar:
1238
+
1239
+ - [ ] Todo codigo nuevo tiene JSDoc completo
1240
+ - [ ] No hay `console.log/debug/error/info/warn`
1241
+ - [ ] Todo lo que puede fallar usa `Result<T, E>`
1242
+ - [ ] Interfaces tienen prefijo `I`
1243
+ - [ ] Barrel exports en todas las carpetas
1244
+ - [ ] Async/await en lugar de Promise chaining
1245
+ - [ ] Componentes UI siguen estructura de archivos
1246
+ - [ ] Estados de UI usan TransactionState
1247
+ - [ ] Stores usan selectores puros
1248
+ - [ ] Handlers exponen API memorizada
1249
+ - [ ] `bun run typecheck` pasa
1250
+ - [ ] `bun run lint` pasa
1251
+ - [ ] `bun run format` aplicado
1252
+
1253
+ ---
1254
+
1255
+ ## REGLA 13: UI Components - Shadcn MCP + React Bits + lucide-animated
1256
+
1257
+ ### Stack de Componentes UI
1258
+
1259
+ **OBLIGATORIO**: Usar Shadcn MCP Server para instalar componentes via registries externos.
1260
+
1261
+ | Registry | URL | Tipo | Prioridad |
1262
+ |----------|-----|------|-----------|
1263
+ | **@animate-ui** | `https://animate-ui.com/r/{name}.json` | Componentes Shadcn con animaciones | **1º PREFERIDO** |
1264
+ | **@react-bits** | `https://reactbits.dev/r/{name}.json` | Componentes interactivos/efectos | **2º PREFERIDO** |
1265
+ | **@lucide-animated** | `https://lucide-animated.com/r/{name}.json` | Iconos animados (350+) | **PREFERIDO** |
1266
+ | **@prompt-kit** | `https://prompt-kit.com/c/{name}.json` | Chat/Prompt UI | **PREFERIDO** para agents |
1267
+ | **@shadcn** | Registry base | Componentes base estándar | Fallback |
1268
+
1269
+ ### Configuracion MCP Server (Primera vez)
1270
+
1271
+ ```bash
1272
+ # En el directorio del proyecto
1273
+ npx shadcn@latest mcp init --client claude
1274
+
1275
+ # Esto genera .mcp.json en la raiz del proyecto:
1276
+ {
1277
+ "mcpServers": {
1278
+ "shadcn": {
1279
+ "command": "npx",
1280
+ "args": ["shadcn@latest", "mcp"]
1281
+ }
1282
+ }
1283
+ }
1284
+
1285
+ # Reiniciar Claude Code y verificar con /mcp
1286
+ ```
1287
+
1288
+ ### Configuracion de components.json
1289
+
1290
+ **OBLIGATORIO**: Agregar registries externos al `components.json`:
1291
+
1292
+ ```json
1293
+ {
1294
+ "$schema": "https://ui.shadcn.com/schema.json",
1295
+ "style": "new-york",
1296
+ "rsc": false,
1297
+ "tsx": true,
1298
+ "tailwind": {
1299
+ "config": "",
1300
+ "css": "src/app/globals.css",
1301
+ "baseColor": "zinc",
1302
+ "cssVariables": true,
1303
+ "prefix": ""
1304
+ },
1305
+ "iconLibrary": "lucide",
1306
+ "aliases": {
1307
+ "components": "@/components",
1308
+ "utils": "@/lib/utils",
1309
+ "ui": "@/components/ui",
1310
+ "lib": "@/lib",
1311
+ "hooks": "@/hooks"
1312
+ },
1313
+ "registries": {
1314
+ "@animate-ui": "https://animate-ui.com/r/{name}.json",
1315
+ "@react-bits": "https://reactbits.dev/r/{name}.json",
1316
+ "@lucide-animated": "https://lucide-animated.com/r/{name}.json",
1317
+ "@prompt-kit": "https://prompt-kit.com/c/{name}.json"
1318
+ }
1319
+ }
1320
+ ```
1321
+
1322
+ ### Comandos de Instalacion
1323
+
1324
+ ```bash
1325
+ # 1º Animate UI (componentes Shadcn con animaciones - PREFERIDO)
1326
+ bunx --bun shadcn@latest add @animate-ui/sliding-number
1327
+ bunx --bun shadcn@latest add @animate-ui/morphing-text
1328
+ bunx --bun shadcn@latest add @animate-ui/animated-card
1329
+ bunx --bun shadcn@latest add @animate-ui/progress-reveal
1330
+
1331
+ # 2º React Bits (efectos interactivos)
1332
+ bunx --bun shadcn@latest add @react-bits/fade-content
1333
+ bunx --bun shadcn@latest add @react-bits/magnet
1334
+ bunx --bun shadcn@latest add @react-bits/dock
1335
+
1336
+ # 3º Iconos animados lucide-animated
1337
+ bunx --bun shadcn@latest add @lucide-animated/check-circle
1338
+ bunx --bun shadcn@latest add @lucide-animated/loader
1339
+ bunx --bun shadcn@latest add @lucide-animated/play
1340
+
1341
+ # 4º prompt-kit (chat/agent UI)
1342
+ bunx --bun shadcn@latest add @prompt-kit/prompt-input
1343
+ bunx --bun shadcn@latest add @prompt-kit/response-stream
1344
+
1345
+ # 5º Shadcn base (solo si no hay alternativa animada)
1346
+ bunx --bun shadcn@latest add button card dialog
1347
+ ```
1348
+
1349
+ ### Uso con Claude Code MCP
1350
+
1351
+ Despues de configurar el MCP, usar prompts naturales:
1352
+
1353
+ ```
1354
+ # React Bits
1355
+ "Show me all available backgrounds from @react-bits"
1356
+ "Add the Dither background from React Bits to the page, make it purple"
1357
+ "Add FadeContent from @react-bits for scroll animations"
1358
+
1359
+ # lucide-animated
1360
+ "Show me all available icons from @lucide-animated"
1361
+ "Add the check-circle animated icon from lucide-animated"
1362
+
1363
+ # Combinados
1364
+ "Create a hero section with FadeContent animation and animated icons"
1365
+ ```
1366
+
1367
+ ### Componentes React Bits Recomendados
1368
+
1369
+ | Componente | Categoria | Uso |
1370
+ |-----------|-----------|-----|
1371
+ | `fade-content` | Animations | Fade in/out con scroll |
1372
+ | `text-pressure` | Text Effects | Texto con efecto presion |
1373
+ | `magnet` | Interactions | Efecto magnetico cursor |
1374
+ | `dock` | Components | Dock estilo macOS |
1375
+ | `tilted-card` | Components | Cards con tilt 3D |
1376
+ | `spotlight-card` | Components | Cards con spotlight |
1377
+ | `dither` | Backgrounds | Fondo con efecto dither |
1378
+ | `hyperspeed` | Backgrounds | Efecto velocidad |
1379
+
1380
+ ### Iconos lucide-animated Recomendados
1381
+
1382
+ | Icono | Uso |
1383
+ |-------|-----|
1384
+ | `check-circle` | Confirmacion, exito |
1385
+ | `x-circle` | Error, cerrar |
1386
+ | `loader` | Loading states |
1387
+ | `play` / `pause` | Media controls |
1388
+ | `arrow-up` / `arrow-down` | Navegacion |
1389
+ | `settings` | Configuracion |
1390
+ | `bell` | Notificaciones |
1391
+
1392
+ ### Patrones de Importacion
1393
+
1394
+ ```typescript
1395
+ // CORRECTO - Componentes React Bits
1396
+ import { FadeContent } from '@/components/ui/fade-content';
1397
+ import { TextPressure } from '@/components/ui/text-pressure';
1398
+ import { Dock } from '@/components/ui/dock';
1399
+
1400
+ // CORRECTO - Iconos animados lucide-animated
1401
+ import { CheckCircle } from '@/components/icons/check-circle';
1402
+ import { Loader } from '@/components/icons/loader';
1403
+
1404
+ // CORRECTO - Componentes base Shadcn
1405
+ import { Button } from '@/components/ui/button';
1406
+ import { Card } from '@/components/ui/card';
1407
+
1408
+ // CORRECTO - Iconos estaticos (fallback)
1409
+ import { Settings } from 'lucide-react';
1410
+
1411
+ // INCORRECTO - Importar directo desde node_modules
1412
+ import { FadeContent } from 'react-bits';
1413
+ ```
1414
+
1415
+ ### Estructura de Carpetas
1416
+
1417
+ ```
1418
+ components/
1419
+ ├── ui/ # Componentes UI (Shadcn + React Bits)
1420
+ │ ├── button.tsx
1421
+ │ ├── card.tsx
1422
+ │ ├── fade-content.tsx # React Bits
1423
+ │ ├── text-pressure.tsx # React Bits
1424
+ │ ├── dock.tsx # React Bits
1425
+ │ └── index.ts # Barrel export
1426
+ ├── icons/ # Iconos animados (lucide-animated)
1427
+ │ ├── check-circle.tsx
1428
+ │ ├── loader.tsx
1429
+ │ ├── play.tsx
1430
+ │ └── index.ts # Barrel export
1431
+ └── agent/ # Componentes de dominio
1432
+ └── ...
1433
+ ```
1434
+
1435
+ ### Ejemplo Completo de Uso
1436
+
1437
+ ```typescript
1438
+ // components/agent/MessageItem.tsx
1439
+ import { FadeContent } from '@/components/ui/fade-content';
1440
+ import { Card } from '@/components/ui/card';
1441
+ import { CheckCircle } from '@/components/icons/check-circle';
1442
+ import { Loader } from '@/components/icons/loader';
1443
+ import type { IMessageItemProps } from './MessageItem.types';
1444
+
1445
+ export function MessageItem({ message, isLoading }: IMessageItemProps) {
1446
+ return (
1447
+ <FadeContent direction="up" duration={0.4}>
1448
+ <Card className="p-4">
1449
+ <div className="flex items-center gap-2">
1450
+ {isLoading ? (
1451
+ <Loader className="w-5 h-5 animate-spin" />
1452
+ ) : (
1453
+ <CheckCircle className="w-5 h-5 text-green-500" />
1454
+ )}
1455
+ <span>{message.content}</span>
1456
+ </div>
1457
+ </Card>
1458
+ </FadeContent>
1459
+ );
1460
+ }
1461
+ ```
1462
+
1463
+ ### Debug MCP
1464
+
1465
+ ```bash
1466
+ # En Claude Code, usar /mcp para verificar conexion
1467
+ /mcp
1468
+
1469
+ # Debe mostrar "shadcn" en la lista de servers activos
1470
+ # Si hay problemas, reiniciar Claude Code
1471
+ ```
1472
+
1473
+ ### Referencias
1474
+
1475
+ - **React Bits**: https://reactbits.dev/
1476
+ - **lucide-animated**: https://lucide-animated.com/
1477
+ - **Shadcn MCP**: https://ui.shadcn.com/docs/mcp
1478
+ - **Shadcn Registry**: https://ui.shadcn.com/
1479
+
1480
+ ---
1481
+
1482
+ ## REGLA 14: Package UI Compartido
1483
+
1484
+ ### Estructura del Package UI
1485
+
1486
+ El monorepo tiene un package UI compartido en `core/packages/ui/` para componentes reutilizables:
1487
+
1488
+ ```
1489
+ core/packages/ui/
1490
+ ├── src/
1491
+ │ ├── components/
1492
+ │ │ ├── ui/ # Componentes Shadcn + React Bits
1493
+ │ │ ├── icons/ # Iconos lucide-animated
1494
+ │ │ └── agent/ # Componentes de dominio
1495
+ │ ├── lib/
1496
+ │ │ └── utils.ts # cn() y utilidades
1497
+ │ ├── hooks/ # Hooks compartidos
1498
+ │ └── index.ts # Barrel export
1499
+ ├── components.json # Config Shadcn con registries
1500
+ ├── package.json
1501
+ ├── rolldown.config.ts
1502
+ └── tsconfig.json
1503
+ ```
1504
+
1505
+ ### Uso del Package UI
1506
+
1507
+ ```typescript
1508
+ // Importar desde el package compartido
1509
+ import { Button, Card, FadeContent } from 'mks-dev-environment/ui';
1510
+ import { CheckCircle, Loader } from 'mks-dev-environment/ui/icons';
1511
+ import { cn } from 'mks-dev-environment/ui/lib/utils';
1512
+ ```
1513
+
1514
+ ### Instalacion de Componentes en Package UI
1515
+
1516
+ ```bash
1517
+ # Ir al directorio del package UI
1518
+ cd core/packages/ui
1519
+
1520
+ # Instalar componentes (se guardan en src/components/ui/)
1521
+ bunx --bun shadcn@latest add @react-bits/fade-content
1522
+ bunx --bun shadcn@latest add @lucide-animated/check-circle
1523
+ bunx --bun shadcn@latest add button card
1524
+ ```
1525
+
1526
+ ### Referencias
1527
+
1528
+ - **Shadcn/UI Documentation**: https://ui.shadcn.com/
1529
+ - **Shadcn MCP Registry**: https://ui.shadcn.com/docs/mcp
1530
+ - **Animate UI Gallery**: https://animate.ui/
1531
+ - **Radix UI Primitives**: https://www.radix-ui.com/
1532
+ - **CLI Documentation**: https://ui.shadcn.com/docs/cli
1533
+
1534
+ ---
1535
+
1536
+ ## REGLA 15: Testing - Vitest (NO bun test)
1537
+
1538
+ ### Runner Correcto
1539
+
1540
+ **OBLIGATORIO**: Usar `vitest` o `bunx vitest` para ejecutar tests. NUNCA `bun test`.
1541
+
1542
+ ```bash
1543
+ # CORRECTO - Vitest runner
1544
+ vitest # Modo watch
1545
+ vitest run # Single run
1546
+ bunx vitest run # Con bunx
1547
+ bunx vitest run src/hooks/ # Tests específicos
1548
+
1549
+ # INCORRECTO - Bun test runner
1550
+ bun test # NO USAR
1551
+ bun test src/ # NO USAR
1552
+ ```
1553
+
1554
+ ### Configuración Base
1555
+
1556
+ ```typescript
1557
+ // vitest.config.ts
1558
+ import { defineConfig } from 'vitest/config';
1559
+ import react from '@vitejs/plugin-react';
1560
+ import tsconfigPaths from 'vite-tsconfig-paths';
1561
+
1562
+ export default defineConfig({
1563
+ plugins: [react(), tsconfigPaths()],
1564
+ test: {
1565
+ environment: 'jsdom',
1566
+ setupFiles: ['./src/tests/setupTests.ts'],
1567
+ globals: true,
1568
+ },
1569
+ });
1570
+ ```
1571
+
1572
+ ### Setup con MSW
1573
+
1574
+ ```typescript
1575
+ // src/tests/setupTests.ts
1576
+ import '@testing-library/jest-dom';
1577
+ import { beforeAll, afterEach, afterAll } from 'vitest';
1578
+ import { server } from './mocks/server';
1579
+
1580
+ beforeAll(() => server.listen());
1581
+ afterEach(() => {
1582
+ server.resetHandlers();
1583
+ localStorage.clear();
1584
+ });
1585
+ afterAll(() => server.close());
1586
+ ```
1587
+
1588
+ ### Documentación Completa
1589
+
1590
+ Ver documentación detallada en:
1591
+ - `~/dotfiles/mks-ui/docs/testing-architecture.md`
1592
+ - `~/dotfiles/mks-ui/docs/testing-strategy.md`
1593
+
1594
+ ---
1595
+
1596
+ ## REGLA 16: Sistema de Themes - CSS Variables + Theme Manager
1597
+
1598
+ ### Stack de Theming
1599
+
1600
+ **OBLIGATORIO**: Usar sistema de CSS variables con tokens apropiados.
1601
+
1602
+ | Package | Uso | Instalación |
1603
+ |---------|-----|-------------|
1604
+ | **@mks2508/theme-manager-react** | Theme switching con animaciones | `bun add @mks2508/theme-manager-react` |
1605
+ | **@mks2508/shadcn-basecoat-theme-manager** | Core de gestión de temas | `bun add @mks2508/shadcn-basecoat-theme-manager` |
1606
+
1607
+ ### Variables CSS Requeridas
1608
+
1609
+ ```css
1610
+ :root {
1611
+ /* Colores base */
1612
+ --background: 0 0% 100%;
1613
+ --foreground: 222.2 84% 4.9%;
1614
+ --primary: 222.2 47.4% 11.2%;
1615
+ --primary-foreground: 210 40% 98%;
1616
+ --secondary: 210 40% 96%;
1617
+ --secondary-foreground: 222.2 84% 4.9%;
1618
+ --muted: 210 40% 96%;
1619
+ --muted-foreground: 215.4 16.3% 46.9%;
1620
+ --accent: 210 40% 96%;
1621
+ --accent-foreground: 222.2 84% 4.9%;
1622
+ --destructive: 0 84.2% 60.2%;
1623
+ --destructive-foreground: 210 40% 98%;
1624
+
1625
+ /* UI Elements */
1626
+ --card: var(--background);
1627
+ --card-foreground: var(--foreground);
1628
+ --popover: var(--background);
1629
+ --popover-foreground: var(--foreground);
1630
+ --border: 214.3 31.8% 91.4%;
1631
+ --input: 214.3 31.8% 91.4%;
1632
+ --ring: 222.2 84% 4.9%;
1633
+ --radius: 0.5rem;
1634
+ }
1635
+
1636
+ .dark {
1637
+ --background: 222.2 84% 4.9%;
1638
+ --foreground: 210 40% 98%;
1639
+ /* ... dark variants */
1640
+ }
1641
+ ```
1642
+
1643
+ ### Uso con Next.js
1644
+
1645
+ ```tsx
1646
+ // app/layout.tsx
1647
+ import { ThemeProvider } from '@mks2508/theme-manager-react/nextjs'
1648
+
1649
+ export default function RootLayout({ children }) {
1650
+ return (
1651
+ <html lang="en">
1652
+ <body>
1653
+ <ThemeProvider
1654
+ registryUrl="/themes/registry.json"
1655
+ defaultTheme="default"
1656
+ defaultMode="auto"
1657
+ enableTransitions={true}
1658
+ >
1659
+ {children}
1660
+ </ThemeProvider>
1661
+ </body>
1662
+ </html>
1663
+ )
1664
+ }
1665
+ ```
1666
+
1667
+ ### Componentes de Theme
1668
+
1669
+ ```tsx
1670
+ import {
1671
+ ModeToggle, // Toggle light/dark
1672
+ AnimatedThemeToggler, // Toggle con animaciones
1673
+ ThemeSelector // Selector completo de temas
1674
+ } from '@mks2508/theme-manager-react/nextjs'
1675
+ ```
1676
+
1677
+ ---
1678
+
1679
+ ## REGLA 17: Sidebar y Bottom Navigation - sidebar-headless
1680
+
1681
+ ### Package Oficial
1682
+
1683
+ **OBLIGATORIO**: Usar `@mks2508/sidebar-headless` para sidebars y bottom navigation.
1684
+
1685
+ ```bash
1686
+ bun add @mks2508/sidebar-headless
1687
+ ```
1688
+
1689
+ ### Características
1690
+
1691
+ - Headless sidebar y mobile bottom navigation
1692
+ - Animaciones fluidas
1693
+ - Keyboard navigation completa
1694
+ - WAI-ARIA accessibility
1695
+ - Soporte glassmorphism
1696
+ - Mobile-first design
1697
+
1698
+ ### Ejemplo de Uso
1699
+
1700
+ ```tsx
1701
+ import {
1702
+ Sidebar,
1703
+ SidebarItem,
1704
+ BottomNavigation
1705
+ } from '@mks2508/sidebar-headless';
1706
+
1707
+ export function AppLayout({ children }) {
1708
+ return (
1709
+ <div className="flex">
1710
+ {/* Desktop Sidebar */}
1711
+ <Sidebar className="hidden md:flex">
1712
+ <SidebarItem icon={<Home />} label="Home" href="/" />
1713
+ <SidebarItem icon={<Settings />} label="Settings" href="/settings" />
1714
+ </Sidebar>
1715
+
1716
+ {/* Mobile Bottom Nav */}
1717
+ <BottomNavigation className="md:hidden">
1718
+ <SidebarItem icon={<Home />} label="Home" href="/" />
1719
+ <SidebarItem icon={<Settings />} label="Settings" href="/settings" />
1720
+ </BottomNavigation>
1721
+
1722
+ <main>{children}</main>
1723
+ </div>
1724
+ );
1725
+ }
1726
+ ```
1727
+
1728
+ ---
1729
+
1730
+ ## REGLA 18: Glassmorphism - Guía de Implementación
1731
+
1732
+ ### Principios Fundamentales
1733
+
1734
+ Glassmorphism es la combinación controlada de:
1735
+ - **Transparencia parcial** (RGBA / alpha 10-30%)
1736
+ - **Backdrop blur** (difuminar lo que hay detrás)
1737
+ - **Bordes sutiles** (simular refracción)
1738
+ - **Sombras suaves** (profundidad)
1739
+ - **isolation: isolate** (stacking context)
1740
+
1741
+ ### Receta Base (Tailwind)
1742
+
1743
+ ```html
1744
+ <div class="
1745
+ isolate
1746
+ rounded-xl
1747
+ bg-white/20
1748
+ backdrop-blur-lg
1749
+ border border-white/30
1750
+ shadow-xl shadow-black/10
1751
+ ">
1752
+ </div>
1753
+ ```
1754
+
1755
+ ### Clases Glassmorphism Disponibles
1756
+
1757
+ ```css
1758
+ /* Paneles principales */
1759
+ .glass-panel /* blur-16px, borde primary */
1760
+ .glass-panel-subtle /* blur-8px, más transparente */
1761
+ .glass-panel-heavy /* blur-24px, más opaco */
1762
+
1763
+ /* Cards de producto */
1764
+ .product-card-glass /* blur-12px con hover */
1765
+ .product-card-glass-enhanced /* blur-16px con glow */
1766
+
1767
+ /* Badges */
1768
+ .badge-glass-featured /* Primary con glow */
1769
+ .badge-glass-subtle /* Sutil, borde transparente */
1770
+ .badge-glass-outline /* Solo borde */
1771
+
1772
+ /* Inputs y formularios */
1773
+ .input-glass /* Blur sutil con focus glow */
1774
+
1775
+ /* Headers */
1776
+ .bg-glass-header /* Desktop header */
1777
+ .bg-glass-header-mobile /* Mobile header más transparente */
1778
+ ```
1779
+
1780
+ ### Reglas de Uso
1781
+
1782
+ | Usar en | Evitar en |
1783
+ |---------|-----------|
1784
+ | Headers flotantes | Tablas densas |
1785
+ | Modales | Formularios largos |
1786
+ | Sidebars | Texto extenso |
1787
+ | Cards destacadas | Listas largas |
1788
+
1789
+ ### Performance
1790
+
1791
+ ```css
1792
+ /* Fallback obligatorio */
1793
+ @supports not (backdrop-filter: blur(1px)) {
1794
+ .glass-panel { background: rgba(255,255,255,0.9); }
1795
+ }
1796
+
1797
+ /* Reduced motion */
1798
+ @media (prefers-reduced-motion: reduce) {
1799
+ .glass-panel { transition: none; }
1800
+ }
1801
+ ```
1802
+
1803
+ ### Documentación Completa
1804
+
1805
+ - `~/dotfiles/mks-ui/docs/glassmorphism-guide.md` - Guía teórica
1806
+ - `~/dotfiles/mks-ui/docs/glassmorphism.css` - Implementación CSS completa
1807
+
1808
+ ---
1809
+
1810
+ ## Fuentes de Referencia
1811
+
1812
+ - **CLAUDE.md** - Guia de arquitectura del monorepo
1813
+ - **@mks2508/better-logger** - Documentacion del logger
1814
+ - **@mks2508/no-throw** - Documentacion del Result pattern
1815
+ - **@mks2508/theme-manager-react** - Sistema de themes
1816
+ - **@mks2508/sidebar-headless** - Sidebar y bottom navigation
1817
+ - **Arktype** - https://arktype.io/
1818
+ - **Rolldown** - https://rollup.rs/
1819
+ - **Oxlint** - https://oxlint.com/
1820
+ - **Zustand** - https://zustand-demo.pmnd.rs/
1821
+ - **Shadcn/UI** - https://ui.shadcn.com/
1822
+ - **Shadcn MCP** - https://ui.shadcn.com/docs/mcp
1823
+ - **React Bits** - https://reactbits.dev/
1824
+ - **lucide-animated** - https://lucide-animated.com/
1825
+
1826
+ ### Documentación en Dotfiles
1827
+
1828
+ | Documento | Ubicación | Contenido |
1829
+ |-----------|-----------|-----------|
1830
+ | **Glassmorphism Guide** | `~/dotfiles/mks-ui/docs/glassmorphism-guide.md` | Teoría y best practices |
1831
+ | **Glassmorphism CSS** | `~/dotfiles/mks-ui/docs/glassmorphism.css` | Implementación completa |
1832
+ | **Testing Architecture** | `~/dotfiles/mks-ui/docs/testing-architecture.md` | Arquitectura de tests |
1833
+ | **Testing Strategy** | `~/dotfiles/mks-ui/docs/testing-strategy.md` | Estrategia de testing |
1834
+
1835
+ ### CLI mks-ui
1836
+
1837
+ Generador de componentes disponible globalmente:
1838
+
1839
+ ```bash
1840
+ mks-ui component Button --ui # Componente UI simple
1841
+ mks-ui component ProductCard --complex # BLO completo
1842
+ mks-ui service ProductService # Servicio Elysia
1843
+ mks-ui hook useProducts # Custom hook
1844
+ mks-ui type Product # Tipos TypeScript
1845
+ ```