gufi-cli 0.1.49 → 0.1.51

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 (36) hide show
  1. package/dist/commands/docs.js +1 -5
  2. package/dist/lib/docs-resolver.d.ts +8 -0
  3. package/dist/lib/docs-resolver.js +27 -0
  4. package/dist/lib/security.js +5 -0
  5. package/dist/mcp.js +56 -43
  6. package/docs/dev-guide/1-01-architecture.md +358 -0
  7. package/docs/dev-guide/1-02-multi-tenant.md +415 -0
  8. package/docs/dev-guide/1-03-column-types.md +594 -0
  9. package/docs/dev-guide/1-04-json-config.md +442 -0
  10. package/docs/dev-guide/1-05-authentication.md +427 -0
  11. package/docs/dev-guide/2-01-api-reference.md +564 -0
  12. package/docs/dev-guide/2-02-automations.md +508 -0
  13. package/docs/dev-guide/2-03-gufi-cli.md +568 -0
  14. package/docs/dev-guide/2-04-realtime.md +401 -0
  15. package/docs/dev-guide/2-05-permissions.md +497 -0
  16. package/docs/dev-guide/2-06-integrations-overview.md +104 -0
  17. package/docs/dev-guide/2-07-stripe.md +173 -0
  18. package/docs/dev-guide/2-08-nayax.md +297 -0
  19. package/docs/dev-guide/2-09-ourvend.md +226 -0
  20. package/docs/dev-guide/2-10-tns.md +177 -0
  21. package/docs/dev-guide/2-11-custom-http.md +268 -0
  22. package/docs/dev-guide/3-01-custom-views.md +555 -0
  23. package/docs/dev-guide/3-02-webhooks-api.md +446 -0
  24. package/docs/mcp/00-overview.md +329 -0
  25. package/docs/mcp/01-architecture.md +226 -0
  26. package/docs/mcp/02-modules.md +285 -0
  27. package/docs/mcp/03-fields.md +357 -0
  28. package/docs/mcp/04-views.md +613 -0
  29. package/docs/mcp/05-automations.md +461 -0
  30. package/docs/mcp/06-api.md +531 -0
  31. package/docs/mcp/07-packages.md +246 -0
  32. package/docs/mcp/08-common-errors.md +284 -0
  33. package/docs/mcp/09-examples.md +453 -0
  34. package/docs/mcp/README.md +71 -0
  35. package/docs/mcp/tool-descriptions.json +64 -0
  36. package/package.json +3 -2
@@ -0,0 +1,453 @@
1
+ # Ejemplos Practicos
2
+
3
+ Ejemplos listos para copiar/adaptar.
4
+
5
+ ## Agregar campo a entidad existente
6
+
7
+ ```javascript
8
+ gufi_schema_modify({
9
+ company_id: "116",
10
+ operations: [{
11
+ op: "add_field",
12
+ entity: "ventas.pedidos", // modulo.entidad
13
+ field: {
14
+ name: "fecha_entrega",
15
+ type: "date",
16
+ label: "Fecha de Entrega",
17
+ required: false
18
+ }
19
+ }]
20
+ })
21
+ ```
22
+
23
+ ## Crear entidad completa
24
+
25
+ ```javascript
26
+ gufi_schema_modify({
27
+ company_id: "116",
28
+ operations: [{
29
+ op: "add_entity",
30
+ module: "inventario",
31
+ entity: {
32
+ name: "productos",
33
+ label: "Productos",
34
+ kind: "table",
35
+ fields: [
36
+ { name: "nombre", type: "text", label: "Nombre", required: true },
37
+ { name: "sku", type: "barcode", label: "SKU" },
38
+ { name: "descripcion", type: "text", label: "Descripcion" },
39
+ { name: "precio", type: "currency", label: "Precio" },
40
+ { name: "stock", type: "number_int", label: "Stock" },
41
+ { name: "categoria", type: "select", label: "Categoria", options: [
42
+ { value: "electronica", label: "Electronica", color: "blue" },
43
+ { value: "hogar", label: "Hogar", color: "green" },
44
+ { value: "ropa", label: "Ropa", color: "purple" }
45
+ ]},
46
+ { name: "activo", type: "boolean", label: "Activo" }
47
+ ],
48
+ permissions: {
49
+ admin: "*",
50
+ almacen: ["entity:view", "entity:create", "entity:update"],
51
+ ventas: ["entity:view"]
52
+ }
53
+ }
54
+ }]
55
+ })
56
+ ```
57
+
58
+ ## Automation: Email al crear
59
+
60
+ ```javascript
61
+ // 1. Crear el script
62
+ gufi_automation_scripts({
63
+ action: "create",
64
+ company_id: "116",
65
+ name: "notificar_pedido_nuevo",
66
+ description: "Envia email cuando se crea un pedido",
67
+ code: `
68
+ async function notificar_pedido_nuevo(gufi) {
69
+ const { row, env } = gufi.context;
70
+
71
+ gufi.logger.info('Nuevo pedido', { id: row.id });
72
+
73
+ await gufi.integrations.notifications.email({
74
+ to: env.VENTAS_EMAIL,
75
+ subject: \`Nuevo pedido #\${row.id}\`,
76
+ html: \`
77
+ <h2>Nuevo Pedido</h2>
78
+ <p><strong>Cliente:</strong> \${row.cliente__display || 'N/A'}</p>
79
+ <p><strong>Total:</strong> \${row.total || 0} EUR</p>
80
+ <p><strong>Estado:</strong> \${row.estado__display || row.estado}</p>
81
+ <p><a href="https://app.gogufi.com/pedidos/\${row.id}">Ver pedido</a></p>
82
+ \`,
83
+ user: env.EMAIL_USER,
84
+ pass: env.EMAIL_PASS,
85
+ from: env.EMAIL_FROM,
86
+ });
87
+
88
+ return { success: true, message: 'Email enviado' };
89
+ }
90
+ `
91
+ })
92
+
93
+ // 2. Vincular a entidad
94
+ gufi_automation_meta({
95
+ entity_id: "16192",
96
+ company_id: "116",
97
+ automations: [
98
+ { trigger: "insert", function_name: "notificar_pedido_nuevo" }
99
+ ]
100
+ })
101
+ ```
102
+
103
+ ## Automation: Webhook externo
104
+
105
+ ```javascript
106
+ gufi_automation_scripts({
107
+ action: "create",
108
+ company_id: "116",
109
+ name: "sync_to_external",
110
+ code: `
111
+ async function sync_to_external(gufi) {
112
+ const { row, old_row, env } = gufi.context;
113
+
114
+ // Solo si cambio el estado
115
+ if (row.estado === old_row?.estado) {
116
+ return { success: true, message: 'Sin cambios' };
117
+ }
118
+
119
+ try {
120
+ const response = await gufi.http(env.WEBHOOK_URL, {
121
+ method: 'POST',
122
+ headers: {
123
+ 'Authorization': \`Bearer \${env.API_TOKEN}\`,
124
+ 'Content-Type': 'application/json'
125
+ },
126
+ body: JSON.stringify({
127
+ event: 'status_changed',
128
+ record_id: row.id,
129
+ old_status: old_row?.estado,
130
+ new_status: row.estado,
131
+ timestamp: new Date().toISOString()
132
+ })
133
+ });
134
+
135
+ gufi.logger.info('Webhook response', response);
136
+ return { success: true };
137
+
138
+ } catch (err) {
139
+ gufi.logger.error('Webhook failed', { error: err.message });
140
+ return { success: false, message: err.message };
141
+ }
142
+ }
143
+ `
144
+ });
145
+ ```
146
+
147
+ ## Vista: Lista con filtros
148
+
149
+ ```typescript
150
+ // index.tsx
151
+ import React, { useState, useEffect, useMemo } from 'react';
152
+ import { Search, Filter } from 'lucide-react';
153
+ import {
154
+ cn,
155
+ useDebouncedValue,
156
+ formatNumber,
157
+ formatCurrency,
158
+ toastError,
159
+ tUI,
160
+ type FeatureProps
161
+ } from '@/sdk';
162
+
163
+ export { featureConfig } from './core/dataProvider';
164
+
165
+ export default function ListaFiltros({ id, gufi }: FeatureProps) {
166
+ const { dataProvider, context, seedData } = gufi;
167
+ const isPreview = context?.isPreview;
168
+
169
+ // 💜 Nombre lógico - dataProvider lo resuelve automáticamente
170
+ const itemsTable = 'ventas.items';
171
+
172
+ const [data, setData] = useState<any[]>([]);
173
+ const [loading, setLoading] = useState(true);
174
+ const [search, setSearch] = useState('');
175
+ const [filter, setFilter] = useState<string>('all');
176
+
177
+ const debouncedSearch = useDebouncedValue(search, 200);
178
+
179
+ // Cargar datos
180
+ useEffect(() => {
181
+ const load = async () => {
182
+ setLoading(true);
183
+
184
+ if (isPreview && seedData?.data) {
185
+ setData(seedData.data.items || []);
186
+ setLoading(false);
187
+ return;
188
+ }
189
+
190
+ if (!dataProvider) {
191
+ setLoading(false);
192
+ return;
193
+ }
194
+
195
+ try {
196
+ const res = await dataProvider.getList({
197
+ resource: itemsTable, // 💜 Nombre lógico
198
+ pagination: { current: 1, pageSize: 200 },
199
+ sort: { field: 'created_at', order: 'DESC' }
200
+ });
201
+ setData(res.data || []);
202
+ } catch (err) {
203
+ toastError('Error cargando datos');
204
+ }
205
+ setLoading(false);
206
+ };
207
+
208
+ load();
209
+ }, [dataProvider, itemsTable, seedData, isPreview]);
210
+
211
+ // Filtrar datos
212
+ const filtered = useMemo(() => {
213
+ let result = data;
214
+
215
+ // Filtro por estado
216
+ if (filter !== 'all') {
217
+ result = result.filter(item => item.estado === filter);
218
+ }
219
+
220
+ // Busqueda
221
+ if (debouncedSearch) {
222
+ const term = debouncedSearch.toLowerCase();
223
+ result = result.filter(item =>
224
+ item.nombre?.toLowerCase().includes(term) ||
225
+ item.cliente__display?.toLowerCase().includes(term)
226
+ );
227
+ }
228
+
229
+ return result;
230
+ }, [data, filter, debouncedSearch]);
231
+
232
+ if (loading) {
233
+ return (
234
+ <div className="p-4 space-y-2">
235
+ {[...Array(5)].map((_, i) => (
236
+ <div key={i} className="h-16 bg-gray-100 rounded animate-pulse" />
237
+ ))}
238
+ </div>
239
+ );
240
+ }
241
+
242
+ return (
243
+ <div className="flex flex-col h-[100dvh] bg-gradient-to-br from-violet-50/40 via-white to-purple-50/40">
244
+ {/* Header con busqueda */}
245
+ <div className="p-4 border-b bg-white/80 backdrop-blur">
246
+ <div className="flex gap-2">
247
+ <div className="flex-1 relative">
248
+ <Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400" />
249
+ <input
250
+ type="text"
251
+ value={search}
252
+ onChange={(e) => setSearch(e.target.value)}
253
+ placeholder={tUI('common.search')}
254
+ className="w-full pl-10 pr-4 py-2 border rounded-lg focus:ring-2 focus:ring-violet-500"
255
+ />
256
+ </div>
257
+ <select
258
+ value={filter}
259
+ onChange={(e) => setFilter(e.target.value)}
260
+ className="px-3 py-2 border rounded-lg"
261
+ >
262
+ <option value="all">Todos</option>
263
+ <option value="pendiente">Pendientes</option>
264
+ <option value="completado">Completados</option>
265
+ </select>
266
+ </div>
267
+ </div>
268
+
269
+ {/* Lista */}
270
+ <div className="flex-1 overflow-auto">
271
+ <div className="p-4 text-sm text-gray-500">
272
+ {formatNumber(filtered.length)} resultados
273
+ </div>
274
+
275
+ {filtered.map(item => (
276
+ <div
277
+ key={item.id}
278
+ className="mx-4 mb-2 p-4 bg-white rounded-lg border hover:border-violet-300 transition-colors"
279
+ >
280
+ <div className="flex justify-between items-start">
281
+ <div>
282
+ <h3 className="font-medium">{item.nombre}</h3>
283
+ <p className="text-sm text-gray-500">{item.cliente__display}</p>
284
+ </div>
285
+ <div className="text-right">
286
+ <div className="font-semibold text-violet-600">
287
+ {formatCurrency(item.total || 0, 'EUR')}
288
+ </div>
289
+ <span className={cn(
290
+ "text-xs px-2 py-0.5 rounded-full",
291
+ item.estado === 'completado' ? "bg-green-100 text-green-700" : "bg-yellow-100 text-yellow-700"
292
+ )}>
293
+ {item.estado__display || item.estado}
294
+ </span>
295
+ </div>
296
+ </div>
297
+ </div>
298
+ ))}
299
+ </div>
300
+ </div>
301
+ );
302
+ }
303
+ ```
304
+
305
+ ```typescript
306
+ // core/dataProvider.ts
307
+ import type { FeatureConfig } from '@/sdk';
308
+
309
+ export const featureConfig: FeatureConfig = {
310
+ dataSources: {
311
+ mainTable: {
312
+ type: 'table',
313
+ module: 'ventas',
314
+ entity: 'pedidos',
315
+ label: 'Pedidos'
316
+ }
317
+ },
318
+ inputs: {},
319
+ seedData: {
320
+ description: { es: 'Lista de pedidos', en: 'Orders list' },
321
+ data: {
322
+ items: [
323
+ { id: 1, nombre: 'Pedido #001', cliente__display: 'Empresa A', total: 1500, estado: 'pendiente', estado__display: 'Pendiente' },
324
+ { id: 2, nombre: 'Pedido #002', cliente__display: 'Empresa B', total: 2300, estado: 'completado', estado__display: 'Completado' }
325
+ ]
326
+ },
327
+ order: ['items']
328
+ }
329
+ };
330
+ ```
331
+
332
+ ## Crear registro con todos los tipos
333
+
334
+ ```javascript
335
+ gufi_data({
336
+ action: "create",
337
+ table: "ventas.productos",
338
+ company_id: "116",
339
+ data: {
340
+ // Texto
341
+ nombre: "Mi Producto",
342
+ email: "contacto@empresa.com",
343
+ url: "https://ejemplo.com",
344
+ codigo_barras: "8414533043847",
345
+
346
+ // Numeros
347
+ cantidad: 100,
348
+ precio: 150.50,
349
+ descuento: 0.15, // 15%
350
+
351
+ // Fechas
352
+ fecha_alta: "2024-01-15",
353
+ hora_apertura: "09:00:00",
354
+
355
+ // Seleccion (incluir __display!)
356
+ estado: "activo",
357
+ estado__display: "Activo",
358
+ categorias: ["electronica", "hogar"],
359
+ activo: true,
360
+
361
+ // Relaciones (incluir __display!)
362
+ cliente_id: 45,
363
+ cliente_id__display: "Empresa ABC",
364
+ responsables: [16, 23],
365
+
366
+ // Complejos
367
+ telefono: { prefix: "+34", number: "612345678" },
368
+ direccion: {
369
+ street: "Gran Via",
370
+ number: "1",
371
+ city: "Madrid",
372
+ postal_code: "28013",
373
+ country: "ES"
374
+ }
375
+ }
376
+ })
377
+ ```
378
+
379
+ ## Consulta SQL en automation
380
+
381
+ ```javascript
382
+ gufi_automation_scripts({
383
+ action: "create",
384
+ company_id: "116",
385
+ name: "reporte_ventas",
386
+ code: `
387
+ async function reporte_ventas(gufi) {
388
+ const { env } = gufi.context;
389
+
390
+ // Obtener resumen de ventas del mes
391
+ const rows = await gufi.query(\`
392
+ SELECT
393
+ DATE_TRUNC('day', created_at) as fecha,
394
+ COUNT(*) as num_pedidos,
395
+ SUM(total) as total_ventas,
396
+ AVG(total) as ticket_medio
397
+ FROM m360_t16192
398
+ WHERE created_at >= DATE_TRUNC('month', CURRENT_DATE)
399
+ GROUP BY DATE_TRUNC('day', created_at)
400
+ ORDER BY fecha DESC
401
+ \`);
402
+
403
+ // Formatear reporte
404
+ const reporteHtml = rows.map(r => \`
405
+ <tr>
406
+ <td>\${new Date(r.fecha).toLocaleDateString('es-ES')}</td>
407
+ <td>\${r.num_pedidos}</td>
408
+ <td>\${Number(r.total_ventas).toFixed(2)} EUR</td>
409
+ <td>\${Number(r.ticket_medio).toFixed(2)} EUR</td>
410
+ </tr>
411
+ \`).join('');
412
+
413
+ await gufi.integrations.notifications.email({
414
+ to: env.ADMIN_EMAIL,
415
+ subject: 'Reporte de Ventas Mensual',
416
+ html: \`
417
+ <h2>Reporte de Ventas</h2>
418
+ <table border="1" cellpadding="5">
419
+ <tr><th>Fecha</th><th>Pedidos</th><th>Total</th><th>Ticket Medio</th></tr>
420
+ \${reporteHtml}
421
+ </table>
422
+ \`,
423
+ user: env.EMAIL_USER,
424
+ pass: env.EMAIL_PASS,
425
+ from: env.EMAIL_FROM,
426
+ });
427
+
428
+ return { success: true, message: \`Reporte enviado con \${rows.length} dias\` };
429
+ }
430
+ `
431
+ });
432
+ ```
433
+
434
+ ## Editar vista localmente
435
+
436
+ ```javascript
437
+ // 1. Descargar vista a ~/gufi-dev/view_13/
438
+ gufi_view_pull({ view_id: 13 })
439
+
440
+ // 2. Editar archivos con Read/Edit tools
441
+ // Read("~/gufi-dev/view_13/index.tsx")
442
+ // Edit("~/gufi-dev/view_13/index.tsx", ...)
443
+
444
+ // 3. Subir cambios
445
+ gufi_view_push({ view_id: 13, message: "Changed table name" })
446
+
447
+ // 4. Testear en headless browser (OBLIGATORIO antes de publicar)
448
+ gufi_view_test({ view_id: 13, company_id: "116" })
449
+ // Ver errores JS, console logs, screenshot
450
+
451
+ // 5. Si todo OK, publicar package
452
+ gufi_package({ action: "publish", id: "19" })
453
+ ```
@@ -0,0 +1,71 @@
1
+ # Documentación Gufi para Consultores y Developers
2
+
3
+ > **Para usuarios de la plataforma Gufi**: Si estás creando ERPs personalizados con Gufi (módulos, vistas, automations), esta es tu documentación.
4
+
5
+ Esta carpeta contiene la documentación para trabajar con Gufi usando el CLI y MCP (Model Context Protocol) con Claude AI.
6
+
7
+ ## 🚀 Comienza aquí
8
+
9
+ **Nuevo en Gufi?** Lee `00-overview.md` primero para entender qué es y cómo funciona.
10
+
11
+ **Ya conoces Gufi?** Salta directo al archivo que necesites según la tabla de abajo.
12
+
13
+ ## 📚 Índice de Documentación
14
+
15
+ | Archivo | Qué aprenderás | Cuándo consultarlo |
16
+ |---------|----------------|-------------------|
17
+ | **[00-overview.md](./00-overview.md)** | Qué es Gufi, arquitectura básica, flujo de trabajo típico | 👉 **Empieza aquí** si eres nuevo |
18
+ | **[01-architecture.md](./01-architecture.md)** | Arquitectura técnica completa (backend, BD, real-time) | Necesitas entender cómo conectan los componentes |
19
+ | **[02-modules.md](./02-modules.md)** | Crear/modificar módulos y entidades (estructura de datos) | Vas a crear o cambiar tablas |
20
+ | **[03-fields.md](./03-fields.md)** | Tipos de campos y `gufi.CB.*` builders | Vas a crear/actualizar registros |
21
+ | **[04-views.md](./04-views.md)** | Sistema de vistas React (UI personalizada) | Vas a crear o modificar interfaces |
22
+ | **[05-automations.md](./05-automations.md)** | Escribir automations (lógica de negocio) | Necesitas automatizar procesos |
23
+ | **[06-api.md](./06-api.md)** | API endpoints y métodos disponibles | Vas a integrar con APIs externas |
24
+ | **[07-packages.md](./07-packages.md)** | Crear y publicar packages en Marketplace | Vas a distribuir tu solución |
25
+ | **[08-common-errors.md](./08-common-errors.md)** | Errores comunes y cómo solucionarlos | 🐛 Algo no funciona |
26
+ | **[09-examples.md](./09-examples.md)** | Ejemplos prácticos completos | Necesitas código de referencia |
27
+
28
+ ## 🛠️ Cómo usar esta documentación
29
+
30
+ ### Desde CLI
31
+
32
+ ```bash
33
+ # Ver contexto completo de un package
34
+ gufi context --package 14
35
+
36
+ # Consultar documentación específica
37
+ gufi docs overview # Lee 00-overview.md
38
+ gufi docs fields # Lee 03-fields.md
39
+ gufi docs automations # Lee 05-automations.md
40
+ ```
41
+
42
+ ### Desde Claude AI (MCP)
43
+
44
+ Si usas Claude con el plugin Gufi MCP:
45
+
46
+ ```typescript
47
+ // Claude puede consultar automáticamente:
48
+ gufi_docs({ topic: "views" }) // Lee 04-views.md
49
+ gufi_docs({ topic: "fields" }) // Lee 03-fields.md
50
+ gufi_docs({ search: "currency" }) // Busca término en todos los docs
51
+ ```
52
+
53
+ ## 💡 Casos de uso típicos
54
+
55
+ | Quiero... | Lee esto |
56
+ |-----------|----------|
57
+ | Crear un módulo de inventario | `02-modules.md` → `03-fields.md` |
58
+ | Hacer una vista personalizada de reportes | `04-views.md` → `09-examples.md` |
59
+ | Automatizar envío de emails al crear pedido | `05-automations.md` |
60
+ | Entender errores de `gufi.CB.currency()` | `03-fields.md` → `08-common-errors.md` |
61
+ | Publicar mi solución en Marketplace | `07-packages.md` |
62
+
63
+ ## ⚠️ Nota importante
64
+
65
+ Esta documentación es para **usar Gufi** (crear ERPs), no para **desarrollar el codebase de Gufi**.
66
+
67
+ Si estás desarrollando el codebase interno, consulta `/docs/claude/` en vez de `/docs/mcp/`.
68
+
69
+ ---
70
+
71
+ **Última actualización**: 2025-01-14
@@ -0,0 +1,64 @@
1
+ {
2
+ "_comment": "Gufi MCP - 15 tools. Para usuarios que construyen ERPs con Gufi.",
3
+ "_env_note": "El entorno se configura con 'gufi config:local' o 'gufi config:prod' + 'gufi login'. El parametro env:'dev' es un OVERRIDE que requiere tener credenciales guardadas para ese entorno.",
4
+
5
+ "gufi_context": {
6
+ "description": "THE ONE TOOL FOR CONTEXT. Llama esto PRIMERO.\n\nUsos:\n gufi_context({ company_id: '146' }) - Schema COMPLETO de empresa\n gufi_context({ view_id: '13' }) - Contexto de vista\n gufi_context({ package_id: '14' }) - Contexto de package\n\nSiempre devuelve contexto completo: campos, tipos, relaciones, automations, permisos."
7
+ },
8
+
9
+ "gufi_whoami": {
10
+ "description": "Ver usuario actual y entorno (dev/prod). Usa para verificar autenticación."
11
+ },
12
+
13
+ "gufi_schema_modify": {
14
+ "description": "Modificar schema. Operaciones atómicas para módulos, entities, fields y sections.\n\nMÓDULOS:\n add_module: { op: 'add_module', module: { name: 'inventario', label: 'Inventario' } }\n remove_module: { op: 'remove_module', module: 'inventario' } ⚠️ Borra TODO el módulo y sus datos\n\nSECCIONES (submodules):\n add_submodule: { op: 'add_submodule', module: 'ventas', submodule: { name: 'reportes', label: 'Reportes' } }\n remove_submodule: { op: 'remove_submodule', module: 'ventas', submodule: 'reportes' } → Mueve entities a 'general'\n\nENTIDADES:\n add_entity: { op: 'add_entity', module: 'ventas', entity: { name: 'facturas', label: 'Facturas' } }\n update_entity: { op: 'update_entity', entity: 'clientes', changes: { label: 'Clientes VIP' } }\n remove_entity: { op: 'remove_entity', entity: 'clientes' } ⚠️ Borra tabla y datos\n\n💜 CREAR VISTAS (Custom Views):\n add_entity con kind='marketplace_view': { op: 'add_entity', module: 'ventas', entity: { name: 'mi_dashboard', label: 'Mi Dashboard', kind: 'marketplace_view' } }\n Luego usa gufi_view_pull({ view_id: <id> }) para descargar el template y empezar a codear.\n\nCAMPOS:\n add_field: { op: 'add_field', entity: 'clientes', field: { name: 'phone', type: 'phone', label: 'Teléfono' } }\n update_field: { op: 'update_field', entity: 'clientes', field_name: 'phone', changes: { required: true } }\n remove_field: { op: 'remove_field', entity: 'clientes', field_name: 'old_field' } ⚠️ Borra columna y datos\n\nCAMPOS RELATION (FK):\n { op: 'add_field', entity: 'pedidos', field: { name: 'cliente_id', type: 'relation', ref: 'ventas.clientes', display: 'nombre', label: 'Cliente' } }\n ⚠️ IMPORTANTE: ref usa formato 'modulo.entidad', NO { entity, module }\n\nTipos de campo: text, email, number_int, number_float, currency, date, select, multiselect, relation, phone, location, file, boolean, json\nAliases automáticos: textarea→text, varchar→text, int→number_int, float→number_float, bool→boolean, timestamp→datetime\n\nReferencias: Usa nombre lógico ('ventas.clientes') o ID numérico.\nPreview: Usa preview: true para ver qué haría sin ejecutar.\nBatch: Usa skip_existing: true para saltar campos/entidades que ya existen (no falla)."
15
+ },
16
+
17
+ "gufi_docs": {
18
+ "description": "Leer documentación de Gufi. USA ESTO cuando no sepas cómo funciona algo.\n\nTopics: overview, fields, views, automations, errors, examples, api, modules, packages"
19
+ },
20
+
21
+ "gufi_automation_scripts": {
22
+ "description": "CRUD de automation scripts.\n\nAcciones:\n list: gufi_automation_scripts({ action: 'list', company_id: '146' })\n get: gufi_automation_scripts({ action: 'get', company_id: '146', id: '5' })\n create: gufi_automation_scripts({ action: 'create', company_id: '146', name: 'mi_script', code: '...' })\n update: gufi_automation_scripts({ action: 'update', company_id: '146', name: 'mi_script', code: '...' })\n delete: gufi_automation_scripts({ action: 'delete', company_id: '146', name: 'mi_script' })\n\nIMPORTANTE: api.query() retorna rows directamente (no { rows })."
23
+ },
24
+
25
+ "gufi_automation_script_test": {
26
+ "description": "Ejecutar/testear un automation script manualmente.\n\nParámetros:\n- entity: Nombre lógico (module.entity). Opcional para scripts standalone.\n- row: Datos de la fila (incluir id). Opcional si usas input.\n- input: Datos adicionales (van a context.input). Opcional si usas row.\n\nEjemplo con entidad:\ngufi_automation_script_test({\n company_id: '116',\n script_name: 'send_email',\n entity: 'ventas.facturas',\n trigger_event: 'on_click',\n row: { id: 1, total: 150 },\n input: { email_to: 'test@example.com' }\n})\n\nEjemplo standalone (sin entidad):\ngufi_automation_script_test({\n company_id: '116',\n script_name: 'daily_report',\n trigger_event: 'scheduled',\n input: { date: '2024-01-15' }\n})"
27
+ },
28
+
29
+ "gufi_automation_meta": {
30
+ "description": "Ver/configurar triggers de entidades.\n\nVer: gufi_automation_meta({ entity_id: '123', company_id: '146' })\nSet: gufi_automation_meta({ entity_id: '123', company_id: '146', automations: [{ trigger: 'insert', function_name: 'my_script' }] })\n\nTriggers válidos: insert, update, delete, click, scheduled, custom\n\n💜 CUSTOM: Para vistas, no configures manualmente. Usa core/automations.ts y gufi_view_push lo registra automáticamente."
31
+ },
32
+
33
+ "gufi_automation_integrations": {
34
+ "description": "List Gufi's built-in integrations (Stripe, Nayax, etc.) and their api.* methods. These are pre-built by Gufi - users can also create custom integrations directly in their automations using api.http() for any external service. Credentials are passed using env.* variables."
35
+ },
36
+
37
+ "gufi_automation_executions": {
38
+ "description": "Ver historial de ejecuciones de automations. Útil para debugging."
39
+ },
40
+
41
+ "gufi_data": {
42
+ "description": "CRUD unificado para datos.\n\nAcciones:\n list: gufi_data({ action: 'list', table: 'ventas.productos', company_id: '146' })\n get: gufi_data({ action: 'get', table: '...', id: 123 })\n create: gufi_data({ action: 'create', table: '...', data: {...} })\n update: gufi_data({ action: 'update', table: '...', id: 123, data: {...} })\n delete: gufi_data({ action: 'delete', table: '...', id: 123 })\n\nAnalytics:\n aggregate: gufi_data({ action: 'aggregate', table: 'ventas.pedidos', agg: 'sum', field: 'total' })\n - agg: 'count', 'sum', 'avg', 'min', 'max'\n - groupBy: gufi_data({ action: 'aggregate', table: '...', agg: 'sum', field: 'total', groupBy: 'cliente_id' })\n - filter: gufi_data({ action: 'aggregate', table: '...', agg: 'count', filter: 'estado=activo' })\n - dateField: gufi_data({ action: 'aggregate', table: '...', agg: 'sum', field: 'total', groupBy: 'cliente_id', dateField: 'fecha', dateFrom: '2025-12-01', dateTo: '2025-12-31' })\n\nOpciones list:\n - fields: ['id', 'nombre'] - Solo columnas específicas\n - compact: true - Respuesta mínima (solo data)"
43
+ },
44
+
45
+ "gufi_env": {
46
+ "description": "Variables de entorno. Se usan en automations como process.env.KEY.\n\nAcciones:\n list: gufi_env({ action: 'list', company_id: '146' })\n set: gufi_env({ action: 'set', company_id: '146', key: 'API_KEY', value: '...' })\n delete: gufi_env({ action: 'delete', company_id: '146', key: 'API_KEY' })"
47
+ },
48
+
49
+ "gufi_view_pull": {
50
+ "description": "📥 Descargar vista a local para editarla.\n\nDescarga a: ~/gufi-dev/view_<id>/\n\n💜 FLUJO COMPLETO (crear + desarrollar):\n1. CREAR vista con gufi_schema_modify:\n gufi_schema_modify({ company_id: '146', operations: [{ op: 'add_entity', module: 'ventas', entity: { name: 'mi_dashboard', label: 'Mi Dashboard', kind: 'marketplace_view' } }] })\n → Retorna el view_id de la nueva vista\n2. gufi_view_pull({ view_id: <id> }) - Descargar template\n3. Editar archivos con Read/Edit\n4. gufi_view_push({ view_id: <id> }) - Subir\n5. gufi_view_test({ view_id: <id>, company_id: '146' }) - Verificar\n6. gufi_package({ action: 'publish', id: '19' }) - Publicar\n\nEjemplo: gufi_view_pull({ view_id: 13 })"
51
+ },
52
+
53
+ "gufi_view_push": {
54
+ "description": "📤 Subir cambios (sync completo).\n\nSube TODOS los archivos locales. El servidor borra los que no vengan.\n\n💜 AUTOMATIONS: Si existe core/automations.ts, las automations declaradas se registran en __automation_meta__ con trigger_event='CUSTOM'. Solo automations declaradas pueden llamarse con gufi.automation().\n\n⚠️ IMPORTANTE: Después de push, SIEMPRE usar gufi_view_test para verificar que la vista carga sin errores antes de publicar.\n\nFlujo:\n1. Crear scripts: gufi_automation_scripts({ action: 'create', name: 'mi_script' })\n2. Declarar en core/automations.ts: export const automations = [{ name: 'mi_script' }]\n3. gufi_view_push({ view_id: 13 }) → Registra automations\n4. gufi_view_test({ view_id: 13, company_id: '116' }) ← OBLIGATORIO\n5. Si OK → gufi_package({ action: 'publish', id: '19' })\n\nEjemplo: gufi_view_push({ view_id: 13, message: 'Fix bug' })"
55
+ },
56
+
57
+ "gufi_view_test": {
58
+ "description": "🧪 Test view in headless browser. Returns console logs, JS errors, API calls, and screenshot.\n\n⚠️ SIEMPRE ejecutar después de gufi_view_push para verificar que todo funciona.\n\nFlujo típico:\n1. Editar código localmente\n2. gufi_view_push({ view_id: 45 }) - Subir\n3. gufi_view_test({ view_id: 45, company_id: '150' }) - Verificar\n4. Ver errores/screenshot → Arreglar si hay problemas\n5. Repetir hasta que funcione\n6. gufi_package({ action: 'publish' }) - Publicar\n\nAcciones opcionales después de cargar:\n actions: [\n { type: 'click', selector: '.my-button' },\n { type: 'fill', selector: 'input[name=search]', value: 'test' },\n { type: 'wait', selector: '.results' },\n { type: 'delay', ms: 1000 }\n ]\n\nEjemplo: gufi_view_test({ view_id: 45, company_id: '150' })"
59
+ },
60
+
61
+ "gufi_package": {
62
+ "description": "Gestión de packages.\n\nAcciones:\n list: gufi_package({ action: 'list' })\n get: gufi_package({ action: 'get', id: '19' })\n create: gufi_package({ action: 'create', name: 'Mi Package' })\n delete: gufi_package({ action: 'delete', id: '19' })\n add_module: gufi_package({ action: 'add_module', id: '19', module_id: '5', company_id: '146' })\n remove_module: gufi_package({ action: 'remove_module', id: '19', module_id: '5' })\n publish: gufi_package({ action: 'publish', id: '19' })"
63
+ }
64
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gufi-cli",
3
- "version": "0.1.49",
3
+ "version": "0.1.51",
4
4
  "description": "CLI for developing Gufi Marketplace views locally with Claude Code",
5
5
  "bin": {
6
6
  "gufi": "./bin/gufi.js"
@@ -8,6 +8,7 @@
8
8
  "files": [
9
9
  "bin",
10
10
  "dist",
11
+ "docs",
11
12
  "CLAUDE.md"
12
13
  ],
13
14
  "repository": {
@@ -17,7 +18,7 @@
17
18
  "homepage": "https://gogufi.com",
18
19
  "type": "module",
19
20
  "scripts": {
20
- "build": "tsc",
21
+ "build": "tsc && mkdir -p docs/mcp docs/dev-guide && cp -r ../../docs/mcp/* docs/mcp/ && cp -r ../../docs/dev-guide/* docs/dev-guide/",
21
22
  "dev": "tsc -w",
22
23
  "start": "node bin/gufi.js"
23
24
  },