gufi-cli 0.1.22 → 0.1.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +158 -177
- package/dist/mcp.js +246 -14
- package/package.json +1 -1
package/CLAUDE.md
CHANGED
|
@@ -1,213 +1,194 @@
|
|
|
1
|
-
# Gufi CLI
|
|
1
|
+
# Gufi CLI & MCP Server
|
|
2
2
|
|
|
3
|
-
> **
|
|
3
|
+
> **Para documentación de USO del CLI**, ve a `docs/mcp/`
|
|
4
4
|
>
|
|
5
|
-
> Este archivo es
|
|
6
|
-
> - [docs/claude/06-cli.md](../../docs/claude/06-cli.md) - Comandos del CLI
|
|
7
|
-
> - [docs/claude/00-index.md](../../docs/claude/00-index.md) - Overview de Gufi
|
|
8
|
-
> - [docs/claude/05-automations.md](../../docs/claude/05-automations.md) - Automations
|
|
9
|
-
> - [docs/claude/04-views.md](../../docs/claude/04-views.md) - Sistema de vistas
|
|
5
|
+
> Este archivo es para desarrollo INTERNO del CLI (código en `tools/cli/src/`)
|
|
10
6
|
|
|
11
|
-
##
|
|
7
|
+
## Estructura del CLI
|
|
12
8
|
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
#
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
#
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
# Automations
|
|
32
|
-
gufi automations # Ver scripts
|
|
33
|
-
gufi automation 15 --edit # Editar script
|
|
34
|
-
gufi entity:automations 4136 # Ver triggers
|
|
35
|
-
|
|
36
|
-
# Vistas
|
|
37
|
-
gufi view:pull 13 # Descargar
|
|
38
|
-
gufi view:push # Subir cambios
|
|
39
|
-
gufi view:watch # Auto-sync
|
|
40
|
-
|
|
41
|
-
# Datos
|
|
42
|
-
gufi rows m360_t16192 # Listar registros
|
|
43
|
-
gufi row:create table --data '{...}'
|
|
9
|
+
```
|
|
10
|
+
tools/cli/
|
|
11
|
+
├── src/
|
|
12
|
+
│ ├── index.ts # Entry point CLI
|
|
13
|
+
│ ├── mcp.ts # MCP Server (44 tools)
|
|
14
|
+
│ ├── commands/ # Comandos CLI
|
|
15
|
+
│ │ ├── context.ts # gufi context
|
|
16
|
+
│ │ ├── pull.ts # gufi view:pull
|
|
17
|
+
│ │ ├── push.ts # gufi view:push
|
|
18
|
+
│ │ ├── watch.ts # gufi view:watch
|
|
19
|
+
│ │ └── ...
|
|
20
|
+
│ └── lib/
|
|
21
|
+
│ ├── api.ts # Cliente API
|
|
22
|
+
│ ├── config.ts # Gestión de config (~/.gufi/)
|
|
23
|
+
│ └── sync.ts # Sincronización de vistas
|
|
24
|
+
├── package.json
|
|
25
|
+
└── tsconfig.json
|
|
44
26
|
```
|
|
45
27
|
|
|
46
|
-
##
|
|
28
|
+
## Entornos: Local vs Producción
|
|
47
29
|
|
|
48
|
-
|
|
30
|
+
El CLI y MCP funcionan **tanto en local como en producción**. El entorno se configura así:
|
|
49
31
|
|
|
50
32
|
```bash
|
|
51
|
-
|
|
52
|
-
gufi
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
gufi
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
gufi
|
|
59
|
-
gufi packages --json # Lista packages en JSON
|
|
60
|
-
gufi package 14 --json # Detalles del package en JSON
|
|
33
|
+
# Ver entorno actual
|
|
34
|
+
gufi whoami
|
|
35
|
+
|
|
36
|
+
# Cambiar a local (http://localhost:3000)
|
|
37
|
+
gufi config:local
|
|
38
|
+
|
|
39
|
+
# Cambiar a producción (https://gogufi.com)
|
|
40
|
+
gufi config:prod
|
|
61
41
|
```
|
|
62
42
|
|
|
63
|
-
|
|
43
|
+
**IMPORTANTE**:
|
|
44
|
+
- Todos los tools MCP (`gufi_*`) respetan el entorno configurado
|
|
45
|
+
- `gufi_whoami()` muestra el entorno actual (`environment: "local"` o `"prod"`)
|
|
46
|
+
- Antes de hacer cambios, verifica que estás en el entorno correcto
|
|
64
47
|
|
|
65
|
-
|
|
48
|
+
### Config file (~/.gufi/config.json)
|
|
66
49
|
|
|
67
|
-
|
|
50
|
+
```json
|
|
51
|
+
{
|
|
52
|
+
"env": "local", // "local" o "prod"
|
|
53
|
+
"email": "user@example.com",
|
|
54
|
+
"accessToken": "...",
|
|
55
|
+
"refreshToken": "..."
|
|
56
|
+
}
|
|
57
|
+
```
|
|
68
58
|
|
|
69
|
-
|
|
59
|
+
## MCP Server
|
|
70
60
|
|
|
71
|
-
|
|
72
|
-
|
|
61
|
+
El servidor MCP está en `src/mcp.ts` y expone 44 tools para que Claude interactúe con Gufi.
|
|
62
|
+
|
|
63
|
+
### Tools principales
|
|
64
|
+
|
|
65
|
+
- **`gufi_context`** - Genera contexto inteligente:
|
|
66
|
+
- `summary`: Resumen ejecutivo
|
|
67
|
+
- `next_actions`: Pasos sugeridos
|
|
68
|
+
- Auto-detecta si está en codebase, vista local, o muestra packages
|
|
69
|
+
- **Usar con**: `{ package_id }`, `{ view_id }`, `{ company_id }`
|
|
70
|
+
|
|
71
|
+
- **`gufi_docs`** - Lee documentación de `docs/mcp/`
|
|
72
|
+
- **`gufi_schema`** - Schema de BD (módulos, tablas, campos)
|
|
73
|
+
- **`gufi_rows/row/row_create/row_update`** - CRUD de datos
|
|
74
|
+
- **`gufi_module/module_update`** - Gestión de módulos JSON
|
|
75
|
+
- **`gufi_automation/automation_create`** - Gestión de automations
|
|
76
|
+
|
|
77
|
+
### Añadir un nuevo tool MCP
|
|
78
|
+
|
|
79
|
+
1. **Definir tool** en array `TOOLS` (~línea 200):
|
|
80
|
+
```typescript
|
|
81
|
+
{
|
|
82
|
+
name: "gufi_nuevo_tool",
|
|
83
|
+
description: getDesc("gufi_nuevo_tool"),
|
|
84
|
+
inputSchema: {
|
|
85
|
+
type: "object",
|
|
86
|
+
properties: {
|
|
87
|
+
param1: { type: "string", description: "..." },
|
|
88
|
+
},
|
|
89
|
+
required: ["param1"],
|
|
90
|
+
},
|
|
91
|
+
},
|
|
73
92
|
```
|
|
74
93
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
**Companies:**
|
|
84
|
-
- `gufi_companies` - Listar companies
|
|
85
|
-
- `gufi_company_create` - Crear company
|
|
86
|
-
|
|
87
|
-
**Módulos (estructura de datos):**
|
|
88
|
-
- `gufi_modules` - Listar módulos de una company
|
|
89
|
-
- `gufi_module` - Ver JSON completo de un módulo
|
|
90
|
-
- `gufi_module_update` - **Modificar módulo** (añadir campos, entidades)
|
|
91
|
-
- `gufi_module_create` - Crear módulo nuevo
|
|
92
|
-
|
|
93
|
-
**Automations (lógica de negocio):**
|
|
94
|
-
- `gufi_automations` - Listar scripts
|
|
95
|
-
- `gufi_automation` - Ver código JS de un script
|
|
96
|
-
- `gufi_automation_create` - **Crear/actualizar script**
|
|
97
|
-
- `gufi_entity_automations` - Ver triggers de una entidad
|
|
98
|
-
- `gufi_entity_automations_update` - **Configurar triggers**
|
|
99
|
-
- `gufi_automations_executions` - Ver historial de ejecuciones
|
|
100
|
-
|
|
101
|
-
**Datos (CRUD):**
|
|
102
|
-
- `gufi_rows` - Listar registros de una tabla
|
|
103
|
-
- `gufi_row` - Ver un registro
|
|
104
|
-
- `gufi_row_create` - Crear registro
|
|
105
|
-
- `gufi_row_update` - Actualizar registro
|
|
106
|
-
- `gufi_row_delete` - Eliminar registro
|
|
107
|
-
|
|
108
|
-
**Environment Variables:**
|
|
109
|
-
- `gufi_env` - Listar variables
|
|
110
|
-
- `gufi_env_set` - Crear/actualizar variable
|
|
111
|
-
- `gufi_env_delete` - Eliminar variable
|
|
112
|
-
|
|
113
|
-
**Vistas (React/TSX):**
|
|
114
|
-
- `gufi_view_files` - Ver archivos de una vista
|
|
115
|
-
- `gufi_view_file_update` - **Modificar archivo de vista**
|
|
116
|
-
- `gufi_view_files_update` - Modificar múltiples archivos
|
|
117
|
-
|
|
118
|
-
**Packages (Marketplace):**
|
|
119
|
-
- `gufi_packages` - Listar packages
|
|
120
|
-
- `gufi_package` - Ver detalles
|
|
121
|
-
- `gufi_package_create` - Crear package
|
|
122
|
-
- `gufi_package_publish` - Publicar
|
|
123
|
-
- `gufi_package_sync` - Sincronizar versión
|
|
124
|
-
- `gufi_package_add_module` / `gufi_package_remove_module`
|
|
125
|
-
- `gufi_package_add_view` / `gufi_package_remove_view`
|
|
126
|
-
|
|
127
|
-
**Package Migrations:**
|
|
128
|
-
- `gufi_package_migrations` - Listar migraciones
|
|
129
|
-
- `gufi_package_migration_create` - Crear migración SQL
|
|
130
|
-
- `gufi_package_entities` - Listar entidades para target_entity
|
|
131
|
-
|
|
132
|
-
### Ejemplos de uso con Claude
|
|
94
|
+
2. **Añadir handler** en `toolHandlers` (~línea 870):
|
|
95
|
+
```typescript
|
|
96
|
+
async gufi_nuevo_tool(params: { param1: string }) {
|
|
97
|
+
// Implementación
|
|
98
|
+
return { result: "..." };
|
|
99
|
+
},
|
|
100
|
+
```
|
|
133
101
|
|
|
102
|
+
3. **Añadir descripción** en `docs/mcp/tool-descriptions.json`:
|
|
103
|
+
```json
|
|
104
|
+
"gufi_nuevo_tool": {
|
|
105
|
+
"description": "Descripción clara del tool..."
|
|
106
|
+
}
|
|
134
107
|
```
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
Claude: [usa gufi_schema para encontrar tabla de clientes]
|
|
148
|
-
[usa gufi_rows para consultar]
|
|
149
|
-
"Hay 234 clientes en la tabla m360_t4136"
|
|
108
|
+
|
|
109
|
+
### API Endpoints
|
|
110
|
+
|
|
111
|
+
Los endpoints del backend están montados así:
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
/api/auth/* - Autenticación (login, refresh)
|
|
115
|
+
/api/company/* - Recursos de empresa (modules, schema, tables)
|
|
116
|
+
/api/developer/* - Recursos de developer (packages, views)
|
|
117
|
+
/api/marketplace/* - Marketplace público
|
|
118
|
+
/api/automation-scripts - Scripts de automatización
|
|
119
|
+
/api/cli/* - Endpoints específicos del CLI
|
|
150
120
|
```
|
|
151
121
|
|
|
152
|
-
|
|
122
|
+
**CRÍTICO**: Los módulos usan `/api/company/modules/`, NO `/api/modules/`
|
|
153
123
|
|
|
154
|
-
|
|
124
|
+
### Funciones helper importantes
|
|
155
125
|
|
|
156
|
-
|
|
157
|
-
-
|
|
158
|
-
-
|
|
159
|
-
-
|
|
160
|
-
-
|
|
161
|
-
-
|
|
126
|
+
- `detectIfInGufiCodebase()` - Detecta si está en el repo gogufi
|
|
127
|
+
- `loadViewMetaFromCwd()` - Carga `.gufi-view.json` del directorio actual
|
|
128
|
+
- `generateViewContextMcp()` - Genera contexto de una vista
|
|
129
|
+
- `generatePackageContextMcp()` - Genera contexto de un package
|
|
130
|
+
- `apiRequest(endpoint, options, companyId)` - Peticiones a API con auth
|
|
131
|
+
- `developerRequest(path, options)` - Peticiones a /api/developer/
|
|
162
132
|
|
|
163
|
-
|
|
164
|
-
- multiselect: `["value1", "value2"]`
|
|
165
|
-
- users: `[16, 23]`
|
|
133
|
+
## CLI Commands
|
|
166
134
|
|
|
167
|
-
|
|
168
|
-
- currency: `{ "currency": "EUR", "amount": 150.00 }`
|
|
169
|
-
- phone: `{ "prefix": "+34", "number": "612345678" }`
|
|
170
|
-
- location: `{ "street": "Mayor", "number": "15", "city": "Madrid", "lat": 40.41 }`
|
|
171
|
-
- file: `[{ "url": "company_130/doc.pdf", "name": "doc.pdf" }]`
|
|
135
|
+
Cada comando está en `src/commands/`. Patrón común:
|
|
172
136
|
|
|
173
|
-
En vistas usa `gufi.CB.*` para formatear correctamente:
|
|
174
137
|
```typescript
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
138
|
+
export async function miComando(args: string[], flags: Flags) {
|
|
139
|
+
// 1. Parsear argumentos
|
|
140
|
+
// 2. Validar
|
|
141
|
+
// 3. Hacer petición API
|
|
142
|
+
// 4. Mostrar resultado
|
|
143
|
+
}
|
|
179
144
|
```
|
|
180
145
|
|
|
181
|
-
###
|
|
146
|
+
### Comandos principales
|
|
182
147
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
148
|
+
| Comando | Descripción |
|
|
149
|
+
|---------|-------------|
|
|
150
|
+
| `gufi login` | Login interactivo |
|
|
151
|
+
| `gufi whoami` | Ver usuario y entorno |
|
|
152
|
+
| `gufi config:local` | Cambiar a entorno local |
|
|
153
|
+
| `gufi config:prod` | Cambiar a producción |
|
|
154
|
+
| `gufi context` | Generar contexto para Claude |
|
|
155
|
+
| `gufi view:pull <id>` | Descargar vista a local |
|
|
156
|
+
| `gufi view:push` | Subir cambios de vista |
|
|
157
|
+
| `gufi view:watch` | Auto-sync de cambios |
|
|
158
|
+
| `gufi docs <topic>` | Ver documentación |
|
|
186
159
|
|
|
187
|
-
##
|
|
160
|
+
## Config (~/.gufi/)
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
~/.gufi/
|
|
164
|
+
├── config.json # Credenciales y entornos
|
|
165
|
+
└── gufi-dev/ # Vistas descargadas
|
|
166
|
+
└── view_13/
|
|
167
|
+
├── .gufi-view.json # Metadata de sync
|
|
168
|
+
├── index.tsx
|
|
169
|
+
└── ...
|
|
170
|
+
```
|
|
188
171
|
|
|
189
|
-
|
|
190
|
-
|-----------|---------|
|
|
191
|
-
| **Contexto para Claude** | `gufi context` |
|
|
192
|
-
| **Diagnóstico** | `gufi doctor` |
|
|
193
|
-
| **Leer documentación** | `gufi docs fields` |
|
|
194
|
-
| **Buscar en docs** | `gufi docs --search "currency"` |
|
|
195
|
-
| Ver companies | `gufi companies` |
|
|
196
|
-
| Ver módulos | `gufi modules 146` |
|
|
197
|
-
| Editar módulo | `gufi module 360 --edit` |
|
|
198
|
-
| Ver automations | `gufi automations` |
|
|
199
|
-
| Desarrollar vista | `gufi view:pull 13` → `gufi view:push` |
|
|
172
|
+
## Testing
|
|
200
173
|
|
|
201
|
-
|
|
174
|
+
```bash
|
|
175
|
+
cd tools/cli
|
|
176
|
+
npm test
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Build
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
npm run build # Compila TypeScript
|
|
183
|
+
npm link # Instala globalmente para testing
|
|
184
|
+
```
|
|
202
185
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
4. **Entornos separados**: Cada uno con sus credenciales
|
|
207
|
-
5. **Auto-login**: CLI refresca tokens automáticamente
|
|
186
|
+
**IMPORTANTE**: Después de cambios en mcp.ts, hay que:
|
|
187
|
+
1. `npm run build` en tools/cli/
|
|
188
|
+
2. Reiniciar el MCP server (o reiniciar Claude Code)
|
|
208
189
|
|
|
209
|
-
## Documentación
|
|
190
|
+
## Documentación
|
|
210
191
|
|
|
211
|
-
Para
|
|
212
|
-
-
|
|
213
|
-
- `docs/
|
|
192
|
+
- **Para usuarios del CLI**: `docs/mcp/` (usado por `gufi_docs`)
|
|
193
|
+
- **Para desarrollo del CLI**: Este archivo
|
|
194
|
+
- **Tool descriptions**: `docs/mcp/tool-descriptions.json`
|
package/dist/mcp.js
CHANGED
|
@@ -142,6 +142,11 @@ const TOOLS = [
|
|
|
142
142
|
// ─────────────────────────────────────────────────────────────────────────
|
|
143
143
|
// Context & Info
|
|
144
144
|
// ─────────────────────────────────────────────────────────────────────────
|
|
145
|
+
{
|
|
146
|
+
name: "gufi_start",
|
|
147
|
+
description: getDesc("gufi_start"),
|
|
148
|
+
inputSchema: { type: "object", properties: {} },
|
|
149
|
+
},
|
|
145
150
|
{
|
|
146
151
|
name: "gufi_context",
|
|
147
152
|
description: getDesc("gufi_context"),
|
|
@@ -337,7 +342,8 @@ const TOOLS = [
|
|
|
337
342
|
inputSchema: {
|
|
338
343
|
type: "object",
|
|
339
344
|
properties: {
|
|
340
|
-
table: { type: "string", description: "Table name (
|
|
345
|
+
table: { type: "string", description: "Table name (physical ID like m360_t16192, NOT logical names)" },
|
|
346
|
+
company_id: { type: "string", description: "Company ID (required if table is not physical ID format)" },
|
|
341
347
|
limit: { type: "number", description: "Number of rows (default 20)" },
|
|
342
348
|
offset: { type: "number", description: "Offset for pagination (default 0)" },
|
|
343
349
|
sort: { type: "string", description: "Field to sort by (default: id)" },
|
|
@@ -353,7 +359,8 @@ const TOOLS = [
|
|
|
353
359
|
inputSchema: {
|
|
354
360
|
type: "object",
|
|
355
361
|
properties: {
|
|
356
|
-
table: { type: "string", description: "Table name (
|
|
362
|
+
table: { type: "string", description: "Table name (physical ID like m360_t16192)" },
|
|
363
|
+
company_id: { type: "string", description: "Company ID (required if table is not physical ID format)" },
|
|
357
364
|
id: { type: "number", description: "Row ID" },
|
|
358
365
|
},
|
|
359
366
|
required: ["table", "id"],
|
|
@@ -365,7 +372,8 @@ const TOOLS = [
|
|
|
365
372
|
inputSchema: {
|
|
366
373
|
type: "object",
|
|
367
374
|
properties: {
|
|
368
|
-
table: { type: "string", description: "Table name" },
|
|
375
|
+
table: { type: "string", description: "Table name (physical ID like m360_t16192)" },
|
|
376
|
+
company_id: { type: "string", description: "Company ID (required if table is not physical ID format)" },
|
|
369
377
|
data: { type: "object", description: "Row data as key-value pairs" },
|
|
370
378
|
},
|
|
371
379
|
required: ["table", "data"],
|
|
@@ -377,7 +385,8 @@ const TOOLS = [
|
|
|
377
385
|
inputSchema: {
|
|
378
386
|
type: "object",
|
|
379
387
|
properties: {
|
|
380
|
-
table: { type: "string", description: "Table name" },
|
|
388
|
+
table: { type: "string", description: "Table name (physical ID like m360_t16192)" },
|
|
389
|
+
company_id: { type: "string", description: "Company ID (required if table is not physical ID format)" },
|
|
381
390
|
id: { type: "number", description: "Row ID" },
|
|
382
391
|
data: { type: "object", description: "Fields to update" },
|
|
383
392
|
},
|
|
@@ -390,7 +399,8 @@ const TOOLS = [
|
|
|
390
399
|
inputSchema: {
|
|
391
400
|
type: "object",
|
|
392
401
|
properties: {
|
|
393
|
-
table: { type: "string", description: "Table name" },
|
|
402
|
+
table: { type: "string", description: "Table name (physical ID like m360_t16192)" },
|
|
403
|
+
company_id: { type: "string", description: "Company ID (required if table is not physical ID format)" },
|
|
394
404
|
id: { type: "number", description: "Row ID" },
|
|
395
405
|
},
|
|
396
406
|
required: ["table", "id"],
|
|
@@ -742,11 +752,117 @@ async function detectCompanyFromTable(table) {
|
|
|
742
752
|
const result = await detectCompanyFromModule(moduleId);
|
|
743
753
|
return result?.companyId;
|
|
744
754
|
}
|
|
755
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
756
|
+
// Context Auto-Detection Helpers
|
|
757
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
758
|
+
/**
|
|
759
|
+
* Detect if we're inside the gogufi codebase (for internal development)
|
|
760
|
+
* Checks for presence of key directories: backend/, frontend/, tools/cli/, CLAUDE.md
|
|
761
|
+
*/
|
|
762
|
+
function detectIfInGufiCodebase() {
|
|
763
|
+
try {
|
|
764
|
+
const cwd = process.cwd();
|
|
765
|
+
const indicators = [
|
|
766
|
+
path.join(cwd, "backend"),
|
|
767
|
+
path.join(cwd, "frontend"),
|
|
768
|
+
path.join(cwd, "tools", "cli"),
|
|
769
|
+
path.join(cwd, "CLAUDE.md"),
|
|
770
|
+
];
|
|
771
|
+
const matches = indicators.filter((p) => fs.existsSync(p));
|
|
772
|
+
return matches.length >= 3; // If 3+ indicators exist, it's the codebase
|
|
773
|
+
}
|
|
774
|
+
catch {
|
|
775
|
+
return false;
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
function loadViewMetaFromCwd() {
|
|
779
|
+
try {
|
|
780
|
+
const metaPath = path.join(process.cwd(), ".gufi-view.json");
|
|
781
|
+
if (fs.existsSync(metaPath)) {
|
|
782
|
+
return JSON.parse(fs.readFileSync(metaPath, "utf-8"));
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
catch { }
|
|
786
|
+
return null;
|
|
787
|
+
}
|
|
745
788
|
// Tool handler implementations
|
|
746
789
|
const toolHandlers = {
|
|
747
790
|
// ─────────────────────────────────────────────────────────────────────────
|
|
748
791
|
// Context & Info
|
|
749
792
|
// ─────────────────────────────────────────────────────────────────────────
|
|
793
|
+
/**
|
|
794
|
+
* Smart entrypoint that auto-detects context and provides guidance
|
|
795
|
+
*/
|
|
796
|
+
async gufi_start() {
|
|
797
|
+
// 1. Check if we're in the gogufi codebase (internal development)
|
|
798
|
+
if (detectIfInGufiCodebase()) {
|
|
799
|
+
return {
|
|
800
|
+
detected: "gogufi_codebase",
|
|
801
|
+
summary: "You are inside the Gufi codebase. This is for INTERNAL platform development.",
|
|
802
|
+
what_to_read: {
|
|
803
|
+
first: "docs/claude/01-critical-rules.md - MUST READ (deployment, notificaciones, traducciones)",
|
|
804
|
+
architecture: "docs/claude/02-architecture.md",
|
|
805
|
+
patterns: "docs/claude/07-patterns.md - Common errors and patterns",
|
|
806
|
+
},
|
|
807
|
+
important_rules: {
|
|
808
|
+
hot_reload: "Frontend and backend auto-reload on save. DON'T restart manually.",
|
|
809
|
+
column_types: "After libs/column-types changes: cd libs/column-types && npm run build",
|
|
810
|
+
no_deploy: "NEVER deploy to production without explicit user request.",
|
|
811
|
+
notifications: "Use toastSuccess/toastError from @/shared/notifications/toast (NOT shadcn)",
|
|
812
|
+
translations: "Add to es.ts AND en.ts BEFORE using tUI()",
|
|
813
|
+
},
|
|
814
|
+
for_client_work: "If you need to work on packages/views, use: gufi_context({ package_id: 'X' })",
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
// 2. Check if we're in a view directory (.gufi-view.json)
|
|
818
|
+
const viewMeta = loadViewMetaFromCwd();
|
|
819
|
+
if (viewMeta) {
|
|
820
|
+
const context = await generateViewContextMcp(viewMeta.viewId, false);
|
|
821
|
+
return {
|
|
822
|
+
detected: "view_directory",
|
|
823
|
+
summary: `Working on view "${viewMeta.viewName}" (ID: ${viewMeta.viewId})`,
|
|
824
|
+
...context,
|
|
825
|
+
workflow: {
|
|
826
|
+
edit: "Edit files with your editor, changes will sync",
|
|
827
|
+
watch: "Run: gufi view:watch (auto-syncs on save)",
|
|
828
|
+
push: "Manual push: gufi view:push",
|
|
829
|
+
logs: "View logs: gufi view:logs",
|
|
830
|
+
},
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
// 3. Fallback: List packages and guide
|
|
834
|
+
try {
|
|
835
|
+
const response = await developerRequest("/my-packages");
|
|
836
|
+
const packages = response.data || [];
|
|
837
|
+
return {
|
|
838
|
+
detected: "none",
|
|
839
|
+
summary: `No specific context detected. You have ${packages.length} package(s).`,
|
|
840
|
+
packages: packages.map((p) => ({
|
|
841
|
+
id: p.pk_id,
|
|
842
|
+
name: p.name,
|
|
843
|
+
version: p.version,
|
|
844
|
+
status: p.status,
|
|
845
|
+
})),
|
|
846
|
+
how_to_start: [
|
|
847
|
+
"Get package context: gufi_context({ package_id: 'X' })",
|
|
848
|
+
"Get view context: gufi_context({ view_id: 'Y' })",
|
|
849
|
+
"Pull a view locally: gufi view:pull <id>",
|
|
850
|
+
"Then gufi_start will auto-detect",
|
|
851
|
+
],
|
|
852
|
+
if_building_new: [
|
|
853
|
+
"Create package: gufi_package_create({ name: 'My Package' })",
|
|
854
|
+
"Then add modules and views",
|
|
855
|
+
],
|
|
856
|
+
};
|
|
857
|
+
}
|
|
858
|
+
catch {
|
|
859
|
+
return {
|
|
860
|
+
detected: "none",
|
|
861
|
+
summary: "Could not fetch packages. Make sure you're logged in.",
|
|
862
|
+
hint: "Run: gufi login",
|
|
863
|
+
};
|
|
864
|
+
}
|
|
865
|
+
},
|
|
750
866
|
async gufi_whoami() {
|
|
751
867
|
const config = loadConfig();
|
|
752
868
|
return {
|
|
@@ -768,18 +884,57 @@ const toolHandlers = {
|
|
|
768
884
|
return generateCompanyContextMcp(parseInt(params.company_id), params.full);
|
|
769
885
|
}
|
|
770
886
|
else {
|
|
771
|
-
//
|
|
887
|
+
// Auto-detect context from current directory
|
|
888
|
+
// 1. Check if we're in the gogufi codebase
|
|
889
|
+
if (detectIfInGufiCodebase()) {
|
|
890
|
+
return {
|
|
891
|
+
type: "codebase",
|
|
892
|
+
summary: "You are inside the Gufi codebase (gogufi/). This is for INTERNAL development of the platform.",
|
|
893
|
+
what_to_do: {
|
|
894
|
+
for_codebase_work: [
|
|
895
|
+
"Read docs/claude/01-critical-rules.md FIRST (deployment rules, notificaciones, traducciones)",
|
|
896
|
+
"Architecture: docs/claude/02-architecture.md",
|
|
897
|
+
"Patterns & errors: docs/claude/07-patterns.md",
|
|
898
|
+
],
|
|
899
|
+
for_client_work: [
|
|
900
|
+
"To work on a specific package: gufi_context({ package_id: 'X' })",
|
|
901
|
+
"To work on a specific view: gufi_context({ view_id: 'Y' })",
|
|
902
|
+
"List your packages: gufi_packages()",
|
|
903
|
+
],
|
|
904
|
+
},
|
|
905
|
+
important_rules: {
|
|
906
|
+
hot_reload: "Frontend and backend auto-reload on file save. DON'T restart manually.",
|
|
907
|
+
column_types: "After changes to libs/column-types: cd libs/column-types && npm run build",
|
|
908
|
+
no_deploy: "NEVER deploy to production without explicit request from user.",
|
|
909
|
+
},
|
|
910
|
+
docs_structure: {
|
|
911
|
+
"docs/claude/": "For INTERNAL codebase development (you are here)",
|
|
912
|
+
"docs/mcp/": "For platform USERS (consultants building ERPs) - used by gufi_docs tool",
|
|
913
|
+
},
|
|
914
|
+
};
|
|
915
|
+
}
|
|
916
|
+
// 2. Check if we're in a view directory (.gufi-view.json)
|
|
917
|
+
const viewMeta = loadViewMetaFromCwd();
|
|
918
|
+
if (viewMeta) {
|
|
919
|
+
return generateViewContextMcp(viewMeta.viewId, params.full);
|
|
920
|
+
}
|
|
921
|
+
// 3. Fallback: Return packages overview
|
|
772
922
|
const response = await developerRequest("/my-packages");
|
|
773
923
|
const packages = response.data || [];
|
|
774
924
|
return {
|
|
775
925
|
type: "packages_overview",
|
|
926
|
+
summary: `You have ${packages.length} package(s). Specify one to get detailed context.`,
|
|
776
927
|
packages: packages.map((p) => ({
|
|
777
928
|
id: p.pk_id,
|
|
778
929
|
name: p.name,
|
|
779
930
|
version: p.version,
|
|
780
931
|
status: p.status,
|
|
781
932
|
})),
|
|
782
|
-
|
|
933
|
+
next_actions: [
|
|
934
|
+
"Get package context: gufi_context({ package_id: 'X' })",
|
|
935
|
+
"Get view context: gufi_context({ view_id: 'Y' })",
|
|
936
|
+
"Pull a view locally: gufi view:pull <id> (then gufi_context will auto-detect)",
|
|
937
|
+
],
|
|
783
938
|
};
|
|
784
939
|
}
|
|
785
940
|
},
|
|
@@ -991,7 +1146,7 @@ const toolHandlers = {
|
|
|
991
1146
|
if (!result) {
|
|
992
1147
|
throw new Error(`Module ${params.module_id} not found`);
|
|
993
1148
|
}
|
|
994
|
-
const response = await apiRequest(`/api/modules/${params.module_id}`, {
|
|
1149
|
+
const response = await apiRequest(`/api/company/modules/${params.module_id}`, {
|
|
995
1150
|
method: "PUT",
|
|
996
1151
|
body: JSON.stringify({ json_definition: params.definition }),
|
|
997
1152
|
headers: { "X-Company-ID": result.companyId },
|
|
@@ -999,7 +1154,7 @@ const toolHandlers = {
|
|
|
999
1154
|
return { success: true, module_id: params.module_id };
|
|
1000
1155
|
},
|
|
1001
1156
|
async gufi_module_create(params) {
|
|
1002
|
-
const response = await apiRequest("/api/modules", {
|
|
1157
|
+
const response = await apiRequest("/api/company/modules", {
|
|
1003
1158
|
method: "POST",
|
|
1004
1159
|
body: JSON.stringify({ json_definition: params.definition }),
|
|
1005
1160
|
headers: { "X-Company-ID": params.company_id },
|
|
@@ -1073,7 +1228,7 @@ const toolHandlers = {
|
|
|
1073
1228
|
// Data (CRUD)
|
|
1074
1229
|
// ─────────────────────────────────────────────────────────────────────────
|
|
1075
1230
|
async gufi_rows(params) {
|
|
1076
|
-
const companyId = await detectCompanyFromTable(params.table);
|
|
1231
|
+
const companyId = params.company_id || await detectCompanyFromTable(params.table);
|
|
1077
1232
|
const limit = params.limit || 20;
|
|
1078
1233
|
const offset = params.offset || 0;
|
|
1079
1234
|
const body = {
|
|
@@ -1098,7 +1253,7 @@ const toolHandlers = {
|
|
|
1098
1253
|
};
|
|
1099
1254
|
},
|
|
1100
1255
|
async gufi_row(params) {
|
|
1101
|
-
const companyId = await detectCompanyFromTable(params.table);
|
|
1256
|
+
const companyId = params.company_id || await detectCompanyFromTable(params.table);
|
|
1102
1257
|
const result = await apiRequest(`/api/tables/${params.table}/getOne`, {
|
|
1103
1258
|
method: "POST",
|
|
1104
1259
|
body: JSON.stringify({ id: params.id }),
|
|
@@ -1110,7 +1265,7 @@ const toolHandlers = {
|
|
|
1110
1265
|
};
|
|
1111
1266
|
},
|
|
1112
1267
|
async gufi_row_create(params) {
|
|
1113
|
-
const companyId = await detectCompanyFromTable(params.table);
|
|
1268
|
+
const companyId = params.company_id || await detectCompanyFromTable(params.table);
|
|
1114
1269
|
const result = await apiRequest(`/api/tables/${params.table}`, {
|
|
1115
1270
|
method: "POST",
|
|
1116
1271
|
body: JSON.stringify(params.data),
|
|
@@ -1122,7 +1277,7 @@ const toolHandlers = {
|
|
|
1122
1277
|
};
|
|
1123
1278
|
},
|
|
1124
1279
|
async gufi_row_update(params) {
|
|
1125
|
-
const companyId = await detectCompanyFromTable(params.table);
|
|
1280
|
+
const companyId = params.company_id || await detectCompanyFromTable(params.table);
|
|
1126
1281
|
const result = await apiRequest(`/api/tables/${params.table}/${params.id}`, {
|
|
1127
1282
|
method: "PUT",
|
|
1128
1283
|
body: JSON.stringify(params.data),
|
|
@@ -1130,7 +1285,7 @@ const toolHandlers = {
|
|
|
1130
1285
|
return { success: true, row: result.data || result };
|
|
1131
1286
|
},
|
|
1132
1287
|
async gufi_row_delete(params) {
|
|
1133
|
-
const companyId = await detectCompanyFromTable(params.table);
|
|
1288
|
+
const companyId = params.company_id || await detectCompanyFromTable(params.table);
|
|
1134
1289
|
await apiRequest(`/api/tables/${params.table}/${params.id}`, {
|
|
1135
1290
|
method: "DELETE",
|
|
1136
1291
|
}, companyId);
|
|
@@ -1416,8 +1571,33 @@ async function generateViewContextMcp(viewId, includeConcepts) {
|
|
|
1416
1571
|
tableStructures[dsKey] = tableInfo;
|
|
1417
1572
|
}
|
|
1418
1573
|
}
|
|
1574
|
+
// Build summary and next_actions
|
|
1575
|
+
const dsCount = Object.keys(dataSources).length;
|
|
1576
|
+
const autoCount = automations.length;
|
|
1577
|
+
const firstTable = Object.values(dataSources)[0];
|
|
1578
|
+
const summary = [
|
|
1579
|
+
`View "${view.name}" (ID: ${viewId})`,
|
|
1580
|
+
pkg ? `Package: "${pkg.name}"` : "Independent view",
|
|
1581
|
+
dsCount > 0 ? `${dsCount} dataSource(s): ${Object.keys(dataSources).join(", ")}` : "No dataSources configured",
|
|
1582
|
+
autoCount > 0 ? `${autoCount} automation(s) available` : "No automations",
|
|
1583
|
+
].join(" | ");
|
|
1584
|
+
const nextActions = [];
|
|
1585
|
+
// Always first: pull for local editing
|
|
1586
|
+
nextActions.push(`Edit locally: gufi view:pull ${viewId} && gufi view:watch`);
|
|
1587
|
+
// If has tables, suggest viewing data
|
|
1588
|
+
if (firstTable) {
|
|
1589
|
+
nextActions.push(`View data: gufi_rows({ table: "${firstTable}", limit: 5 })`);
|
|
1590
|
+
}
|
|
1591
|
+
// If has automations, suggest viewing them
|
|
1592
|
+
if (autoCount > 0 && viewCompanyId) {
|
|
1593
|
+
nextActions.push(`See automations: gufi_automations({ company_id: "${viewCompanyId}" })`);
|
|
1594
|
+
}
|
|
1595
|
+
// Always: docs for field types
|
|
1596
|
+
nextActions.push(`Field types reference: gufi_docs({ topic: "fields" })`);
|
|
1419
1597
|
const result = {
|
|
1420
1598
|
type: "view",
|
|
1599
|
+
summary,
|
|
1600
|
+
next_actions: nextActions,
|
|
1421
1601
|
view_id: viewId,
|
|
1422
1602
|
name: view.name,
|
|
1423
1603
|
view_type: view.view_type || "marketplace_view",
|
|
@@ -1463,8 +1643,33 @@ async function generatePackageContextMcp(packageId, includeConcepts) {
|
|
|
1463
1643
|
source_module_id: mod.source_module_id,
|
|
1464
1644
|
});
|
|
1465
1645
|
}
|
|
1646
|
+
// Build summary and next_actions
|
|
1647
|
+
const summary = [
|
|
1648
|
+
`Package "${pkg.name}" (ID: ${packageId})`,
|
|
1649
|
+
`Version: ${pkg.version || "0.1.0"}`,
|
|
1650
|
+
`Status: ${pkg.status || "draft"}`,
|
|
1651
|
+
`${modulesInfo.length} module(s)`,
|
|
1652
|
+
`${views.length} view(s)`,
|
|
1653
|
+
].join(" | ");
|
|
1654
|
+
const nextActions = [];
|
|
1655
|
+
// If has views, suggest editing one
|
|
1656
|
+
if (views.length > 0) {
|
|
1657
|
+
const firstView = views[0];
|
|
1658
|
+
nextActions.push(`Edit a view: gufi view:pull ${firstView.view_id} && gufi view:watch`);
|
|
1659
|
+
nextActions.push(`Get view context: gufi_context({ view_id: "${firstView.view_id}" })`);
|
|
1660
|
+
}
|
|
1661
|
+
// If has sandbox company, suggest exploring data
|
|
1662
|
+
if (pkg.sandbox_company_id) {
|
|
1663
|
+
nextActions.push(`Explore sandbox data: gufi_schema({ company_id: "${pkg.sandbox_company_id}" })`);
|
|
1664
|
+
}
|
|
1665
|
+
// If draft, suggest publishing workflow
|
|
1666
|
+
if (pkg.status === "draft" || !pkg.status) {
|
|
1667
|
+
nextActions.push(`When ready: gufi package:sync ${packageId} && gufi package:publish ${packageId}`);
|
|
1668
|
+
}
|
|
1466
1669
|
const result = {
|
|
1467
1670
|
type: "package",
|
|
1671
|
+
summary,
|
|
1672
|
+
next_actions: nextActions,
|
|
1468
1673
|
package_id: packageId,
|
|
1469
1674
|
name: pkg.name,
|
|
1470
1675
|
version: pkg.version || "0.1.0",
|
|
@@ -1538,8 +1743,35 @@ async function generateCompanyContextMcp(companyId, includeConcepts) {
|
|
|
1538
1743
|
entities,
|
|
1539
1744
|
});
|
|
1540
1745
|
}
|
|
1746
|
+
// Build summary and next_actions
|
|
1747
|
+
const totalEntities = modulesInfo.reduce((acc, m) => acc + (m.entities?.length || 0), 0);
|
|
1748
|
+
const summary = [
|
|
1749
|
+
`Company ID: ${companyId}`,
|
|
1750
|
+
`${modulesInfo.length} module(s)`,
|
|
1751
|
+
`${totalEntities} table(s)`,
|
|
1752
|
+
`${automations.length} automation(s)`,
|
|
1753
|
+
].join(" | ");
|
|
1754
|
+
const nextActions = [];
|
|
1755
|
+
// Suggest viewing data from first table
|
|
1756
|
+
if (modulesInfo.length > 0 && modulesInfo[0].entities?.length > 0) {
|
|
1757
|
+
const firstTable = modulesInfo[0].entities[0].table;
|
|
1758
|
+
if (firstTable) {
|
|
1759
|
+
nextActions.push(`View data: gufi_rows({ table: "${firstTable}", limit: 5 })`);
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
// If has modules, suggest editing one
|
|
1763
|
+
if (modulesInfo.length > 0 && modulesInfo[0].id) {
|
|
1764
|
+
nextActions.push(`Edit module structure: gufi_module({ module_id: "${modulesInfo[0].id}" })`);
|
|
1765
|
+
}
|
|
1766
|
+
// If has automations, suggest viewing them
|
|
1767
|
+
if (automations.length > 0) {
|
|
1768
|
+
nextActions.push(`View automation code: gufi_automation({ automation_id: "${automations[0].id}" })`);
|
|
1769
|
+
}
|
|
1770
|
+
nextActions.push(`Field types reference: gufi_docs({ topic: "fields" })`);
|
|
1541
1771
|
const result = {
|
|
1542
1772
|
type: "company",
|
|
1773
|
+
summary,
|
|
1774
|
+
next_actions: nextActions,
|
|
1543
1775
|
company_id: companyId,
|
|
1544
1776
|
modules: modulesInfo,
|
|
1545
1777
|
automations: automations.map((a) => ({
|