gufi-cli 0.1.27 → 0.1.31

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 CHANGED
@@ -10,12 +10,11 @@
10
10
  tools/cli/
11
11
  ├── src/
12
12
  │ ├── index.ts # Entry point CLI
13
- │ ├── mcp.ts # MCP Server (44 tools)
13
+ │ ├── mcp.ts # MCP Server (12 tools)
14
14
  │ ├── commands/ # Comandos CLI
15
15
  │ │ ├── context.ts # gufi context
16
16
  │ │ ├── pull.ts # gufi view:pull
17
17
  │ │ ├── push.ts # gufi view:push
18
- │ │ ├── watch.ts # gufi view:watch
19
18
  │ │ └── ...
20
19
  │ └── lib/
21
20
  │ ├── api.ts # Cliente API
@@ -42,7 +41,7 @@ gufi config:prod
42
41
 
43
42
  **IMPORTANTE**:
44
43
  - Todos los tools MCP (`gufi_*`) respetan el entorno configurado
45
- - `gufi_whoami()` muestra el entorno actual (`environment: "local"` o `"prod"`)
44
+ - `gufi_whoami()` muestra el entorno actual y empresas disponibles
46
45
  - Antes de hacer cambios, verifica que estás en el entorno correcto
47
46
 
48
47
  ### Config file (~/.gufi/config.json)
@@ -56,27 +55,30 @@ gufi config:prod
56
55
  }
57
56
  ```
58
57
 
59
- ## MCP Server
58
+ ## MCP Server - 12 Tools
60
59
 
61
- El servidor MCP está en `src/mcp.ts` y expone 44 tools para que Claude interactúe con Gufi.
60
+ El servidor MCP está en `src/mcp.ts` y expone **12 tools** para que Claude interactúe con Gufi.
62
61
 
63
- ### Tools principales
62
+ ### Tools organizados por categoría
64
63
 
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
64
+ | Categoría | Tool | Descripción |
65
+ |-----------|------|-------------|
66
+ | **Context** | `gufi_context` | Schema completo (company, view, package) |
67
+ | | `gufi_whoami` | Usuario, entorno, empresas |
68
+ | | `gufi_docs` | Documentación (`docs/mcp/`) |
69
+ | **Schema** | `gufi_schema_modify` | Operaciones atomicas (add/update/remove) |
70
+ | **Automations** | `gufi_automation` | Scripts: list, get, create |
71
+ | | `gufi_triggers` | Ver/configurar triggers |
72
+ | | `gufi_executions` | Historial de ejecuciones |
73
+ | **Data** | `gufi_data` | CRUD: list, get, create, update, delete |
74
+ | **Env** | `gufi_env` | Variables: list, set, delete |
75
+ | **Views** | `gufi_view_pull` | Descargar vista a local |
76
+ | | `gufi_view_push` | Subir cambios a draft |
77
+ | **Packages** | `gufi_package` | list, get, create, delete, add_module, remove_module, publish |
76
78
 
77
79
  ### Añadir un nuevo tool MCP
78
80
 
79
- 1. **Definir tool** en array `TOOLS` (~línea 200):
81
+ 1. **Definir tool** en array `TOOLS` (~línea 340):
80
82
  ```typescript
81
83
  {
82
84
  name: "gufi_nuevo_tool",
@@ -91,7 +93,7 @@ El servidor MCP está en `src/mcp.ts` y expone 44 tools para que Claude interact
91
93
  },
92
94
  ```
93
95
 
94
- 2. **Añadir handler** en `toolHandlers` (~línea 870):
96
+ 2. **Añadir handler** en `toolHandlers` (~línea 700):
95
97
  ```typescript
96
98
  async gufi_nuevo_tool(params: { param1: string }) {
97
99
  // Implementación
@@ -117,16 +119,16 @@ Los endpoints del backend están montados así:
117
119
  /api/marketplace/* - Marketplace público
118
120
  /api/automation-scripts - Scripts de automatización
119
121
  /api/cli/* - Endpoints específicos del CLI
122
+ /api/schema/operations - Operaciones de schema
120
123
  ```
121
124
 
122
- **CRÍTICO**: Los módulos usan `/api/company/modules/`, NO `/api/modules/`
123
-
124
125
  ### Funciones helper importantes
125
126
 
126
127
  - `detectIfInGufiCodebase()` - Detecta si está en el repo gogufi
127
128
  - `loadViewMetaFromCwd()` - Carga `.gufi-view.json` del directorio actual
128
129
  - `generateViewContextMcp()` - Genera contexto de una vista
129
130
  - `generatePackageContextMcp()` - Genera contexto de un package
131
+ - `getClaudeOptimizedContext()` - Exporta schema en formato optimizado para Claude
130
132
  - `apiRequest(endpoint, options, companyId)` - Peticiones a API con auth
131
133
  - `developerRequest(path, options)` - Peticiones a /api/developer/
132
134
 
@@ -153,24 +155,20 @@ export async function miComando(args: string[], flags: Flags) {
153
155
  | `gufi config:prod` | Cambiar a producción |
154
156
  | `gufi context` | Generar contexto para Claude |
155
157
  | `gufi view:pull <id>` | Descargar vista a local |
156
- | `gufi view:push` | Subir cambios de vista (a **draft**) |
157
- | `gufi view:watch` | Auto-sync de cambios |
158
- | `gufi view:errors <id>` | Ver errores de compilación |
159
- | `gufi package:sync <id>` | Publicar draft → producción |
158
+ | `gufi view:push` | Sync completo (sube todo, servidor borra lo que no esté) |
159
+ | `gufi package:publish <id>` | Publicar draft producción |
160
160
  | `gufi docs <topic>` | Ver documentación |
161
161
 
162
- ### Draft vs Published (IMPORTANTE)
163
-
164
- Las vistas marketplace tienen **dos tablas**:
162
+ ### Views: Solo Pull/Push
165
163
 
166
- - `marketplace.view_files` → **Draft** (desarrollo)
167
- - `marketplace.published_view_files`**Publicado** (producción)
168
-
169
- **Flujo**:
170
- 1. `view:push` sube a **draft** (visible en Dev Center)
171
- 2. `package:sync <id>` copia draft → published (visible en vistas instaladas)
164
+ ```
165
+ gufi view:pull <id> ~/gufi-dev/view_<id>/
166
+ # Editar archivos locales
167
+ gufi view:push → Sync completo
168
+ gufi package:publish <id> Publicar
169
+ ```
172
170
 
173
- **Regla**: Los cambios de `view:push` NO afectan a vistas ya instaladas hasta hacer `package:sync`.
171
+ **Push = sync completo**: Sube TODOS los archivos, servidor borra los que no vengan.
174
172
 
175
173
  ## Config (~/.gufi/)
176
174
 
package/README.md CHANGED
@@ -301,8 +301,7 @@ gufi package <id> # Ver detalles
301
301
  gufi package:create "Nombre"
302
302
  gufi package:add-module <packageId>
303
303
  gufi package:add-view <packageId> <viewId>
304
- gufi package:publish <id>
305
- gufi package:sync <id> # Sincronizar versión
304
+ gufi package:publish <id> # Publicar cambios a producción
306
305
  ```
307
306
 
308
307
  ### Migraciones de Package
@@ -498,9 +497,8 @@ El MCP expone todas las funcionalidades del CLI como tools que Claude puede usar
498
497
  | Tool | Descripción |
499
498
  |------|-------------|
500
499
  | **Contexto** | |
501
- | `gufi_context` | Genera contexto del proyecto para Claude |
500
+ | `gufi_context` | THE ONE TOOL - Contexto unificado (schema, módulos, vistas, packages) |
502
501
  | `gufi_whoami` | Ver usuario y entorno actual |
503
- | `gufi_schema` | Ver estructura de tablas |
504
502
  | **Companies** | |
505
503
  | `gufi_companies` | Listar companies del usuario |
506
504
  | `gufi_company_create` | Crear nueva company |
@@ -0,0 +1,17 @@
1
+ /**
2
+ * gufi claude - Setup completo de Claude Code para Gufi
3
+ *
4
+ * 1. Configura workspace y MCP
5
+ * 2. Instala como servicio del sistema (arranca automáticamente)
6
+ * 3. Inicia el bridge
7
+ *
8
+ * Funciona igual en Mac, Linux y Windows.
9
+ */
10
+ /**
11
+ * 💜 gufi claude - Setup + Install + Run
12
+ */
13
+ export declare function claudeCommand(options: {
14
+ port?: number;
15
+ service?: boolean;
16
+ uninstall?: boolean;
17
+ }): Promise<void>;
@@ -0,0 +1,422 @@
1
+ /**
2
+ * gufi claude - Setup completo de Claude Code para Gufi
3
+ *
4
+ * 1. Configura workspace y MCP
5
+ * 2. Instala como servicio del sistema (arranca automáticamente)
6
+ * 3. Inicia el bridge
7
+ *
8
+ * Funciona igual en Mac, Linux y Windows.
9
+ */
10
+ import chalk from "chalk";
11
+ import fs from "fs";
12
+ import path from "path";
13
+ import os from "os";
14
+ import { spawn, execSync } from "child_process";
15
+ import { WebSocketServer, WebSocket } from "ws";
16
+ import { isLoggedIn } from "../lib/config.js";
17
+ const DEFAULT_PORT = 9876;
18
+ const WORKSPACE_DIR = path.join(os.homedir(), "gufi-workspace");
19
+ const GLOBAL_CLAUDE_JSON = path.join(os.homedir(), ".claude.json");
20
+ const GUFI_DIR = path.join(os.homedir(), ".gufi");
21
+ const SERVICE_NAME = "com.gufi.claude";
22
+ const WINDOWS_TASK_NAME = "GufiClaude";
23
+ /**
24
+ * Check if service is already installed
25
+ */
26
+ function isServiceInstalled() {
27
+ const platform = os.platform();
28
+ if (platform === "darwin") {
29
+ const plistPath = path.join(os.homedir(), "Library/LaunchAgents", `${SERVICE_NAME}.plist`);
30
+ return fs.existsSync(plistPath);
31
+ }
32
+ else if (platform === "linux") {
33
+ const servicePath = path.join(os.homedir(), ".config/systemd/user/gufi-claude.service");
34
+ return fs.existsSync(servicePath);
35
+ }
36
+ else if (platform === "win32") {
37
+ try {
38
+ execSync(`schtasks /query /tn "${WINDOWS_TASK_NAME}"`, { stdio: "ignore" });
39
+ return true;
40
+ }
41
+ catch {
42
+ return false;
43
+ }
44
+ }
45
+ return false;
46
+ }
47
+ /**
48
+ * Check if bridge is already running on port
49
+ */
50
+ function isPortInUse(port) {
51
+ try {
52
+ if (os.platform() === "win32") {
53
+ const result = execSync(`netstat -ano | findstr :${port}`, { encoding: "utf8" });
54
+ return result.includes("LISTENING");
55
+ }
56
+ else {
57
+ const result = execSync(`lsof -i:${port} -t`, { encoding: "utf8" });
58
+ return result.trim().length > 0;
59
+ }
60
+ }
61
+ catch {
62
+ return false;
63
+ }
64
+ }
65
+ /**
66
+ * Ensure workspace exists and MCP is configured
67
+ */
68
+ function ensureWorkspace() {
69
+ // Create workspace if needed
70
+ if (!fs.existsSync(WORKSPACE_DIR)) {
71
+ fs.mkdirSync(WORKSPACE_DIR, { recursive: true });
72
+ }
73
+ // Create .gufi dir if needed
74
+ if (!fs.existsSync(GUFI_DIR)) {
75
+ fs.mkdirSync(GUFI_DIR, { recursive: true });
76
+ }
77
+ // Create CLAUDE.md if needed
78
+ const claudeMdPath = path.join(WORKSPACE_DIR, "CLAUDE.md");
79
+ if (!fs.existsSync(claudeMdPath)) {
80
+ fs.writeFileSync(claudeMdPath, `# Gufi Workspace
81
+
82
+ Este es tu espacio de trabajo para Gufi ERP.
83
+
84
+ ## MCP Tools Disponibles
85
+
86
+ Usa las herramientas de Gufi:
87
+ - \`gufi_context\` - Contexto de empresa/módulos
88
+ - \`gufi_companies\` - Ver empresas
89
+ - \`gufi_rows\` - Consultar datos
90
+ - \`gufi_docs\` - Documentación
91
+
92
+ ## Quick Start
93
+
94
+ Pregúntame sobre tus empresas, módulos o datos.
95
+ Tengo acceso completo a Gufi via MCP.
96
+ `);
97
+ }
98
+ // Configure MCP in global ~/.claude.json
99
+ if (fs.existsSync(GLOBAL_CLAUDE_JSON)) {
100
+ try {
101
+ const claudeConfig = JSON.parse(fs.readFileSync(GLOBAL_CLAUDE_JSON, "utf8"));
102
+ if (!claudeConfig.projects)
103
+ claudeConfig.projects = {};
104
+ const workspaceProject = claudeConfig.projects[WORKSPACE_DIR];
105
+ if (!workspaceProject?.mcpServers?.gufi) {
106
+ claudeConfig.projects[WORKSPACE_DIR] = {
107
+ ...workspaceProject,
108
+ mcpServers: {
109
+ ...(workspaceProject?.mcpServers || {}),
110
+ gufi: { type: "stdio", command: "gufi", args: ["mcp"], env: {} },
111
+ },
112
+ hasTrustDialogAccepted: true,
113
+ };
114
+ fs.writeFileSync(GLOBAL_CLAUDE_JSON, JSON.stringify(claudeConfig, null, 2));
115
+ }
116
+ }
117
+ catch { }
118
+ }
119
+ }
120
+ /**
121
+ * Find gufi executable path
122
+ */
123
+ async function findGufiPath() {
124
+ return new Promise((resolve) => {
125
+ const cmd = os.platform() === "win32" ? "where" : "which";
126
+ const proc = spawn(cmd, ["gufi"], { shell: true });
127
+ let output = "";
128
+ proc.stdout.on("data", (data) => { output += data.toString(); });
129
+ proc.on("close", (code) => {
130
+ if (code === 0 && output.trim()) {
131
+ resolve(output.trim().split("\n")[0]); // First result
132
+ }
133
+ else {
134
+ resolve("gufi"); // Fallback
135
+ }
136
+ });
137
+ });
138
+ }
139
+ /**
140
+ * Install as system service
141
+ */
142
+ async function installService(gufiPath, port) {
143
+ const platform = os.platform();
144
+ if (platform === "darwin") {
145
+ await installMac(gufiPath, port);
146
+ }
147
+ else if (platform === "linux") {
148
+ await installLinux(gufiPath, port);
149
+ }
150
+ else if (platform === "win32") {
151
+ await installWindows(gufiPath, port);
152
+ }
153
+ }
154
+ async function installMac(gufiPath, port) {
155
+ const plistPath = path.join(os.homedir(), "Library/LaunchAgents", `${SERVICE_NAME}.plist`);
156
+ const plistDir = path.dirname(plistPath);
157
+ if (!fs.existsSync(plistDir)) {
158
+ fs.mkdirSync(plistDir, { recursive: true });
159
+ }
160
+ // Stop existing if running
161
+ try {
162
+ execSync(`launchctl unload "${plistPath}"`, { stdio: "ignore" });
163
+ }
164
+ catch { }
165
+ const plist = `<?xml version="1.0" encoding="UTF-8"?>
166
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
167
+ <plist version="1.0">
168
+ <dict>
169
+ <key>Label</key>
170
+ <string>${SERVICE_NAME}</string>
171
+ <key>ProgramArguments</key>
172
+ <array>
173
+ <string>${gufiPath}</string>
174
+ <string>claude</string>
175
+ <string>--service</string>
176
+ <string>--port</string>
177
+ <string>${port}</string>
178
+ </array>
179
+ <key>RunAtLoad</key>
180
+ <true/>
181
+ <key>KeepAlive</key>
182
+ <true/>
183
+ <key>StandardOutPath</key>
184
+ <string>${GUFI_DIR}/claude.log</string>
185
+ <key>StandardErrorPath</key>
186
+ <string>${GUFI_DIR}/claude.error.log</string>
187
+ <key>EnvironmentVariables</key>
188
+ <dict>
189
+ <key>PATH</key>
190
+ <string>/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin:${process.env.PATH}</string>
191
+ </dict>
192
+ </dict>
193
+ </plist>`;
194
+ fs.writeFileSync(plistPath, plist);
195
+ execSync(`launchctl load "${plistPath}"`, { stdio: "ignore" });
196
+ console.log(chalk.green(" ✓ Servicio instalado (arranca con el sistema)"));
197
+ }
198
+ async function installLinux(gufiPath, port) {
199
+ const serviceDir = path.join(os.homedir(), ".config/systemd/user");
200
+ const servicePath = path.join(serviceDir, "gufi-claude.service");
201
+ if (!fs.existsSync(serviceDir)) {
202
+ fs.mkdirSync(serviceDir, { recursive: true });
203
+ }
204
+ const service = `[Unit]
205
+ Description=Gufi Claude Code Bridge
206
+ After=network.target
207
+
208
+ [Service]
209
+ Type=simple
210
+ ExecStart=${gufiPath} claude --service --port ${port}
211
+ Restart=always
212
+ RestartSec=10
213
+
214
+ [Install]
215
+ WantedBy=default.target
216
+ `;
217
+ fs.writeFileSync(servicePath, service);
218
+ execSync("systemctl --user daemon-reload", { stdio: "ignore" });
219
+ execSync("systemctl --user enable gufi-claude", { stdio: "ignore" });
220
+ execSync("systemctl --user start gufi-claude", { stdio: "ignore" });
221
+ console.log(chalk.green(" ✓ Servicio instalado (arranca con el sistema)"));
222
+ }
223
+ async function installWindows(gufiPath, port) {
224
+ // VBS script to run hidden
225
+ const vbsPath = path.join(GUFI_DIR, "gufi-claude.vbs");
226
+ const vbs = `Set WshShell = CreateObject("WScript.Shell")
227
+ WshShell.Run """${gufiPath}"" claude --service --port ${port}", 0, False`;
228
+ fs.writeFileSync(vbsPath, vbs);
229
+ // Delete existing task
230
+ try {
231
+ execSync(`schtasks /delete /tn "${WINDOWS_TASK_NAME}" /f`, { stdio: "ignore" });
232
+ }
233
+ catch { }
234
+ // Create task
235
+ try {
236
+ execSync(`schtasks /create /tn "${WINDOWS_TASK_NAME}" /tr "wscript.exe \\"${vbsPath}\\"" /sc onlogon /rl highest /f`, { stdio: "ignore" });
237
+ // Start now
238
+ spawn("wscript.exe", [vbsPath], { detached: true, stdio: "ignore" }).unref();
239
+ console.log(chalk.green(" ✓ Servicio instalado (arranca con Windows)"));
240
+ }
241
+ catch {
242
+ console.log(chalk.yellow(" ⚠️ Ejecuta como administrador para instalar servicio"));
243
+ }
244
+ }
245
+ /**
246
+ * 💜 gufi claude - Setup + Install + Run
247
+ */
248
+ export async function claudeCommand(options) {
249
+ const port = options.port || DEFAULT_PORT;
250
+ // Handle uninstall
251
+ if (options.uninstall) {
252
+ await uninstallService();
253
+ return;
254
+ }
255
+ // If running as service (called by launchd/systemd), just start server
256
+ if (options.service) {
257
+ await startBridgeServer(port);
258
+ return;
259
+ }
260
+ // Interactive mode - show UI
261
+ console.log(chalk.magenta(`
262
+ ╭──────────────────────────────────────────────────────╮
263
+ │ 🐿️ Gufi Claude Code │
264
+ ╰──────────────────────────────────────────────────────╯
265
+ `));
266
+ // Check login
267
+ if (!isLoggedIn()) {
268
+ console.log(chalk.yellow(" ⚠️ No estás logueado. Ejecuta: gufi login\n"));
269
+ }
270
+ // Check Claude Code installed
271
+ const claudeOk = await checkClaudeInstalled();
272
+ if (!claudeOk) {
273
+ console.log(chalk.red(" ✗ Claude Code no encontrado."));
274
+ console.log(chalk.gray(" Instala: npm install -g @anthropic-ai/claude-code\n"));
275
+ process.exit(1);
276
+ }
277
+ console.log(chalk.green(" ✓ Claude Code detectado"));
278
+ // Setup workspace
279
+ ensureWorkspace();
280
+ console.log(chalk.green(" ✓ Workspace configurado"));
281
+ console.log(chalk.gray(` 📁 ${WORKSPACE_DIR}`));
282
+ // Check if already running
283
+ if (isPortInUse(port)) {
284
+ console.log(chalk.green(` ✓ Bridge ya está corriendo en puerto ${port}`));
285
+ console.log(chalk.cyan(`
286
+ ¡Todo listo! Abre Gufi en tu navegador.
287
+ El botón de Claude Code conectará automáticamente.
288
+ `));
289
+ return;
290
+ }
291
+ // Install service if not installed
292
+ if (!isServiceInstalled()) {
293
+ console.log(chalk.gray(" 📦 Instalando servicio..."));
294
+ const gufiPath = await findGufiPath();
295
+ await installService(gufiPath, port);
296
+ }
297
+ else {
298
+ console.log(chalk.green(" ✓ Servicio instalado"));
299
+ }
300
+ // Wait a moment for service to start
301
+ await new Promise((r) => setTimeout(r, 2000));
302
+ if (isPortInUse(port)) {
303
+ console.log(chalk.green(` ✓ Bridge corriendo en puerto ${port}`));
304
+ }
305
+ else {
306
+ console.log(chalk.yellow(` ⚠️ Iniciando bridge manualmente...`));
307
+ await startBridgeServer(port);
308
+ return;
309
+ }
310
+ console.log(chalk.cyan(`
311
+ ¡Todo listo!
312
+
313
+ El bridge de Claude Code:
314
+ • Arranca automáticamente con tu sistema
315
+ • Corre en segundo plano en puerto ${port}
316
+
317
+ Abre Gufi → Click en el botón Claude Code
318
+
319
+ Comandos:
320
+ • gufi claude --uninstall - Desinstalar servicio
321
+ • cat ~/.gufi/claude.log - Ver logs
322
+ `));
323
+ }
324
+ async function uninstallService() {
325
+ const platform = os.platform();
326
+ console.log(chalk.magenta(`
327
+ ╭──────────────────────────────────────────────────────╮
328
+ │ 🐿️ Desinstalando Gufi Claude │
329
+ ╰──────────────────────────────────────────────────────╯
330
+ `));
331
+ if (platform === "darwin") {
332
+ const plistPath = path.join(os.homedir(), "Library/LaunchAgents", `${SERVICE_NAME}.plist`);
333
+ try {
334
+ execSync(`launchctl unload "${plistPath}"`, { stdio: "ignore" });
335
+ }
336
+ catch { }
337
+ if (fs.existsSync(plistPath))
338
+ fs.unlinkSync(plistPath);
339
+ }
340
+ else if (platform === "linux") {
341
+ try {
342
+ execSync("systemctl --user stop gufi-claude", { stdio: "ignore" });
343
+ execSync("systemctl --user disable gufi-claude", { stdio: "ignore" });
344
+ }
345
+ catch { }
346
+ const servicePath = path.join(os.homedir(), ".config/systemd/user/gufi-claude.service");
347
+ if (fs.existsSync(servicePath))
348
+ fs.unlinkSync(servicePath);
349
+ }
350
+ else if (platform === "win32") {
351
+ try {
352
+ execSync(`schtasks /delete /tn "${WINDOWS_TASK_NAME}" /f`, { stdio: "ignore" });
353
+ }
354
+ catch { }
355
+ const vbsPath = path.join(GUFI_DIR, "gufi-claude.vbs");
356
+ if (fs.existsSync(vbsPath))
357
+ fs.unlinkSync(vbsPath);
358
+ }
359
+ console.log(chalk.green(" ✓ Servicio desinstalado\n"));
360
+ }
361
+ async function startBridgeServer(port) {
362
+ // Dynamically import PTY
363
+ let pty;
364
+ try {
365
+ pty = await import("@homebridge/node-pty-prebuilt-multiarch");
366
+ }
367
+ catch {
368
+ console.error("Error loading PTY");
369
+ process.exit(1);
370
+ }
371
+ const wss = new WebSocketServer({ port });
372
+ console.log(`Servidor en ws://localhost:${port}`);
373
+ wss.on("connection", (ws) => {
374
+ const shellCmd = os.platform() === "win32" ? "cmd.exe" : "/bin/zsh";
375
+ const shellArgs = os.platform() === "win32" ? ["/c", "claude"] : ["-l", "-c", "claude"];
376
+ const shell = pty.spawn(shellCmd, shellArgs, {
377
+ name: "xterm-256color",
378
+ cols: 120,
379
+ rows: 40,
380
+ cwd: WORKSPACE_DIR,
381
+ env: { ...process.env, TERM: "xterm-256color" },
382
+ });
383
+ shell.onData((data) => {
384
+ if (ws.readyState === WebSocket.OPEN) {
385
+ ws.send(JSON.stringify({ type: "output", content: data }));
386
+ }
387
+ });
388
+ shell.onExit(({ exitCode }) => {
389
+ if (ws.readyState === WebSocket.OPEN) {
390
+ ws.send(JSON.stringify({ type: "exit", code: exitCode }));
391
+ }
392
+ });
393
+ ws.on("message", (data) => {
394
+ try {
395
+ const msg = JSON.parse(data.toString());
396
+ if (msg.type === "input")
397
+ shell.write(msg.content);
398
+ else if (msg.type === "resize")
399
+ shell.resize(msg.cols || 120, msg.rows || 40);
400
+ }
401
+ catch { }
402
+ });
403
+ ws.on("close", () => shell.kill());
404
+ ws.on("error", () => shell.kill());
405
+ });
406
+ wss.on("error", (err) => {
407
+ if (err.code === "EADDRINUSE") {
408
+ console.error(`Puerto ${port} en uso`);
409
+ process.exit(1);
410
+ }
411
+ });
412
+ process.on("SIGINT", () => { wss.close(); process.exit(0); });
413
+ process.on("SIGTERM", () => { wss.close(); process.exit(0); });
414
+ }
415
+ async function checkClaudeInstalled() {
416
+ return new Promise((resolve) => {
417
+ const proc = spawn("claude", ["--version"], { stdio: ["ignore", "pipe", "pipe"], shell: true });
418
+ proc.on("close", (code) => resolve(code === 0));
419
+ proc.on("error", () => resolve(false));
420
+ setTimeout(() => { proc.kill(); resolve(false); }, 5000);
421
+ });
422
+ }
@@ -265,8 +265,7 @@ async function generatePackageContext(packageId, includeConcepts) {
265
265
  md += `## Comandos\n`;
266
266
  md += "```bash\n";
267
267
  md += `gufi package ${packageId} # Ver detalles\n`;
268
- md += `gufi package:sync ${packageId} # Sincronizar cambios\n`;
269
- md += `gufi package:publish ${packageId} # Publicar\n`;
268
+ md += `gufi package:publish ${packageId} # Publicar cambios\n`;
270
269
  if (pkg.views?.[0]) {
271
270
  md += `gufi view:pull ${pkg.views[0].view_id || pkg.views[0].pk_id} # Descargar primera vista\n`;
272
271
  }