helix-assistant-cli 0.2.14 → 0.6.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.
@@ -12,16 +12,19 @@ exports.SLASH_COMMANDS = [
12
12
  { command: '/skill', description: 'Listar o inspeccionar skills registradas', usage: '/skill [nombre]' },
13
13
  { command: '/mcp', description: 'Listar servidores MCP y sus herramientas', usage: '/mcp [defaults]' },
14
14
  { command: '/create-mcp', description: 'Registrar y configurar un nuevo servidor MCP' },
15
- { command: '/browser', description: 'Ver estado del navegador web o abrir URLs', usage: '/browser [open <url>]' },
15
+ { command: '/browser', description: 'Ver estado, abrir URLs o alternar la vista ASCII', usage: '/browser [open <url>|live [on|off]]' },
16
+ { command: '/subagents', description: 'Ver el panel ASCII o chatear con un subagente', usage: '/subagents [list|chat <id> <mensaje>]' },
16
17
  { command: '/config-apikey', description: 'Configurador interactivo de API Keys (OpenAI, Anthropic, Gemini, etc.)' },
17
18
  { command: '/show-apikey', description: 'Ver estado y validez de las API Keys configuradas' },
18
- { command: '/microphone', description: 'Ver o alternar estado del micrófono', usage: '/microphone [enable|disable]' },
19
+ { command: '/microphone', description: 'Ver o alternar estado del micrófono', usage: '/microphone [enable|disable]' },
19
20
  { command: '/tasks', description: 'Ver tareas activas y recientes del agente' },
21
+ { command: '/model', description: 'Ver o cambiar el modelo activo (free/local/cloud)', usage: '/model [list|set <id>|effort <low|medium|high>]' },
20
22
  { command: '/models', description: 'Listar modelos disponibles de proveedores configurados' },
23
+ { command: '/memory', description: 'Ver estadísticas del harness de aprendizaje adaptativo', usage: '/memory [stats|rules|reset]' },
21
24
  { command: '/status', description: 'Mostrar resumen del estado del sistema' },
22
25
  { command: '/providers', description: 'Ver proveedores configurados y salud de sus claves' },
23
26
  { command: '/capabilities', description: 'Ver capacidades activas: sandbox, subagentes, voz y browser' },
24
- { command: '/settings', description: 'Ver configuración efectiva sin exponer secretos' },
27
+ { command: '/settings', description: 'Ver configuración efectiva sin exponer secretos' },
25
28
  { command: '/clear', description: 'Limpiar pantalla y restaurar el banner de HELIX' },
26
29
  { command: '/stop', description: 'Parada de emergencia de todas las tareas activas' },
27
30
  { command: '/help', description: 'Mostrar ayuda de comandos y atajos' },
@@ -107,7 +110,19 @@ class Autocompleter {
107
110
  const hits = options.filter((o) => o.toLowerCase().startsWith(trimmed.toLowerCase()));
108
111
  return [hits.length ? hits : options, line];
109
112
  }
110
- // 3. Browser sub-arguments
113
+ // 3. Model sub-arguments
114
+ if (trimmed.startsWith('/model ')) {
115
+ const options = ['/model list', '/model set ', '/model effort low', '/model effort medium', '/model effort high'];
116
+ const hits = options.filter((o) => o.toLowerCase().startsWith(trimmed.toLowerCase()));
117
+ return [hits.length ? hits : options, line];
118
+ }
119
+ // 3b. Memory sub-arguments
120
+ if (trimmed.startsWith('/memory ')) {
121
+ const options = ['/memory stats', '/memory rules', '/memory reset'];
122
+ const hits = options.filter((o) => o.toLowerCase().startsWith(trimmed.toLowerCase()));
123
+ return [hits.length ? hits : options, line];
124
+ }
125
+ // 4. Browser sub-arguments
111
126
  if (trimmed.startsWith('/browser ')) {
112
127
  const options = ['/browser open https://'];
113
128
  return [options, line];
@@ -144,15 +159,15 @@ class Autocompleter {
144
159
  */
145
160
  renderHelp() {
146
161
  console.log('');
147
- console.log(` ${chalk_1.default.hex('#FF8C00').bold('⬡ Comandos Disponibles')} ${chalk_1.default.gray('(/ + [Tab] para autocompletar)')}`);
148
- console.log(` ${chalk_1.default.gray('─'.repeat(50))}`);
162
+ console.log(` ${chalk_1.default.hex('#FF8C00').bold('⬡ Comandos Disponibles')} ${chalk_1.default.gray('(/ + [Tab] para autocompletar)')}`);
163
+ console.log(` ${chalk_1.default.gray('─'.repeat(50))}`);
149
164
  for (const cmd of exports.SLASH_COMMANDS) {
150
165
  const usage = cmd.usage ? ` ${chalk_1.default.gray(cmd.usage)}` : '';
151
166
  console.log(` ${chalk_1.default.magenta(cmd.command.padEnd(16))} ${chalk_1.default.gray(cmd.description)}${usage}`);
152
167
  }
153
168
  console.log('');
154
- console.log(` ${chalk_1.default.hex('#FF8C00').bold('⬡ Prefijos Especiales')}`);
155
- console.log(` ${chalk_1.default.gray('─'.repeat(50))}`);
169
+ console.log(` ${chalk_1.default.hex('#FF8C00').bold('⬡ Prefijos Especiales')}`);
170
+ console.log(` ${chalk_1.default.gray('─'.repeat(50))}`);
156
171
  console.log(` ${(0, colors_1.formatSkill)('nombre')} ${chalk_1.default.gray('Invoca una skill registrada')}`);
157
172
  console.log(` ${(0, colors_1.formatMcp)('herramienta')} ${chalk_1.default.gray('Invoca herramientas MCP')}`);
158
173
  console.log('');
@@ -0,0 +1,2 @@
1
+ export declare function grayscaleToAscii(pixels: number[][], width?: number): string;
2
+ export declare function renderJpegAsAscii(base64: string, width?: number): string;
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.grayscaleToAscii = grayscaleToAscii;
7
+ exports.renderJpegAsAscii = renderJpegAsAscii;
8
+ const jpeg_js_1 = __importDefault(require("jpeg-js"));
9
+ const ASCII_RAMP = ' .:-=+*#%@';
10
+ const MAX_FRAME_WIDTH = 76;
11
+ const MAX_FRAME_HEIGHT = 34;
12
+ const MAX_JPEG_BYTES = 560_000;
13
+ function grayscaleToAscii(pixels, width = MAX_FRAME_WIDTH) {
14
+ if (!pixels.length || !pixels[0]?.length) {
15
+ throw new Error('Browser frame has no pixels to render.');
16
+ }
17
+ const boundedWidth = Math.max(1, Math.min(MAX_FRAME_WIDTH, Math.floor(width)));
18
+ const sourceHeight = pixels.length;
19
+ const sourceWidth = pixels[0].length;
20
+ const outputHeight = Math.max(1, Math.min(MAX_FRAME_HEIGHT, Math.round((sourceHeight / sourceWidth) * boundedWidth * 0.5)));
21
+ const lines = [];
22
+ for (let y = 0; y < outputHeight; y += 1) {
23
+ const sourceY = Math.min(sourceHeight - 1, Math.floor((y / outputHeight) * sourceHeight));
24
+ let line = '';
25
+ for (let x = 0; x < boundedWidth; x += 1) {
26
+ const sourceX = Math.min(sourceWidth - 1, Math.floor((x / boundedWidth) * sourceWidth));
27
+ const value = Math.max(0, Math.min(255, pixels[sourceY][sourceX]));
28
+ const index = Math.round(((255 - value) / 255) * (ASCII_RAMP.length - 1));
29
+ line += ASCII_RAMP[index];
30
+ }
31
+ lines.push(line);
32
+ }
33
+ return lines.join('\n');
34
+ }
35
+ function renderJpegAsAscii(base64, width = MAX_FRAME_WIDTH) {
36
+ if (typeof base64 !== 'string'
37
+ || !/^[A-Za-z0-9+/]+={0,2}$/.test(base64)
38
+ || base64.length > Math.ceil(MAX_JPEG_BYTES * 4 / 3)) {
39
+ throw new Error('Browser frame is not a valid, bounded Base64 JPEG.');
40
+ }
41
+ const bytes = Buffer.from(base64, 'base64');
42
+ if (bytes.length === 0 || bytes.length > MAX_JPEG_BYTES) {
43
+ throw new Error('Browser frame exceeds the supported JPEG size.');
44
+ }
45
+ const image = jpeg_js_1.default.decode(bytes, {
46
+ useTArray: true,
47
+ formatAsRGBA: true,
48
+ maxResolutionInMP: 4,
49
+ maxMemoryUsageInMB: 128,
50
+ });
51
+ const pixels = [];
52
+ const outputWidth = Math.min(MAX_FRAME_WIDTH, image.width);
53
+ const outputHeight = Math.max(1, Math.min(MAX_FRAME_HEIGHT, Math.round((image.height / image.width) * outputWidth * 0.5)));
54
+ for (let y = 0; y < outputHeight; y += 1) {
55
+ const row = [];
56
+ const sourceY = Math.min(image.height - 1, Math.floor((y / outputHeight) * image.height));
57
+ for (let x = 0; x < outputWidth; x += 1) {
58
+ const sourceX = Math.min(image.width - 1, Math.floor((x / outputWidth) * image.width));
59
+ const offset = (sourceY * image.width + sourceX) * 4;
60
+ row.push(image.data[offset] * 0.299
61
+ + image.data[offset + 1] * 0.587
62
+ + image.data[offset + 2] * 0.114);
63
+ }
64
+ pixels.push(row);
65
+ }
66
+ return grayscaleToAscii(pixels, Math.min(width, outputWidth));
67
+ }
package/dist/ui/repl.d.ts CHANGED
@@ -17,6 +17,7 @@ export declare class HelixRepl {
17
17
  private activeTasksCount;
18
18
  private isProcessingPrompt;
19
19
  private plain;
20
+ private browserLiveViewEnabled;
20
21
  constructor(options: ReplOptions);
21
22
  start(): Promise<void>;
22
23
  private _syncSettings;
package/dist/ui/repl.js CHANGED
@@ -16,8 +16,12 @@ const skills_1 = require("../commands/skills");
16
16
  const mcp_1 = require("../commands/mcp");
17
17
  const mic_1 = require("../commands/mic");
18
18
  const browser_1 = require("../commands/browser");
19
+ const subagents_1 = require("../commands/subagents");
19
20
  const markdown_1 = require("./markdown");
20
21
  const diagnostics_1 = require("../commands/diagnostics");
22
+ const browser_ascii_1 = require("./browser-ascii");
23
+ const model_1 = require("../commands/model");
24
+ const memory_1 = require("../commands/memory");
21
25
  class HelixRepl {
22
26
  options;
23
27
  rl = null;
@@ -32,6 +36,7 @@ class HelixRepl {
32
36
  activeTasksCount = 0;
33
37
  isProcessingPrompt = false;
34
38
  plain = false;
39
+ browserLiveViewEnabled = false;
35
40
  constructor(options) {
36
41
  this.options = options;
37
42
  this.conversationId = `cli_conv_${Date.now()}_${Math.random().toString(36).substring(2, 7)}`;
@@ -95,7 +100,7 @@ class HelixRepl {
95
100
  this.waitingLogged = false;
96
101
  await this._syncSettings();
97
102
  await this.autocompleter.refreshSuggestions();
98
- process.stdout.write(`\r\x1b[K${chalk_1.default.green('●')} ${chalk_1.default.greenBright.bold('Conectado al agente HELIX')} \n`);
103
+ process.stdout.write(`\r\x1b[K${chalk_1.default.green('●')} ${chalk_1.default.greenBright.bold('Conectado al agente HELIX')} \n`);
99
104
  this._promptUser();
100
105
  });
101
106
  this.client.on('disconnected', () => {
@@ -104,40 +109,40 @@ class HelixRepl {
104
109
  }
105
110
  if (!this.waitingLogged) {
106
111
  this.waitingLogged = true;
107
- console.log(`\n${(0, colors_1.RED)('●')} ${chalk_1.default.yellowBright('Esperando al servicio del agente HELIX en ws://127.0.0.1:8765...')}`);
108
- console.log(`${(0, colors_1.GRAY)(' (Si no responde tras 3 reintentos, el CLI lo iniciará automáticamente en segundo plano)')}\n`);
112
+ console.log(`\n${(0, colors_1.RED)('●')} ${chalk_1.default.yellowBright('Esperando al servicio del agente HELIX en ws://127.0.0.1:8765...')}`);
113
+ console.log(`${(0, colors_1.GRAY)(' (Si no responde tras 3 reintentos, el CLI lo iniciará automáticamente en segundo plano)')}\n`);
109
114
  }
110
115
  this._promptUser();
111
116
  });
112
117
  this.client.on('auto_spawning_agent', () => {
113
- process.stdout.write(`\n${(0, colors_1.CYAN_BOLD)('⚙️')} ${chalk_1.default.hex('#FF9F1C').bold('[AUTO-START]:')} Iniciando el servicio del agente HELIX en segundo plano...\n`);
114
- process.stdout.write(`${(0, colors_1.GRAY)(' Por favor espere mientras termina de inicializarse (no se realizarán reintentos hasta que esté listo).')}\n`);
118
+ process.stdout.write(`\n${(0, colors_1.CYAN_BOLD)('⚙️')} ${chalk_1.default.hex('#FF9F1C').bold('[AUTO-START]:')} Iniciando el servicio del agente HELIX en segundo plano...\n`);
119
+ process.stdout.write(`${(0, colors_1.GRAY)(' Por favor espere mientras termina de inicializarse (no se realizarán reintentos hasta que esté listo).')}\n`);
115
120
  });
116
121
  this.client.on('agent_starting_progress', (data) => {
117
- process.stdout.write(`\r ${chalk_1.default.hex('#FF9F1C')('⏳')} ${chalk_1.default.gray(`Inicializando subsistemas de HELIX en segundo plano... (${data.elapsedSec}s)`)}`);
122
+ process.stdout.write(`\r ${chalk_1.default.hex('#FF9F1C')('⏳')} ${chalk_1.default.gray(`Inicializando subsistemas de HELIX en segundo plano... (${data.elapsedSec}s)`)}`);
118
123
  });
119
124
  this.client.on('agent_ready', () => {
120
- process.stdout.write(`\r\x1b[K ${chalk_1.default.green('✔')} ${chalk_1.default.greenBright('Servicio de HELIX listo. Conectando...')}\n\n`);
125
+ process.stdout.write(`\r\x1b[K ${chalk_1.default.green('✔')} ${chalk_1.default.greenBright('Servicio de HELIX listo. Conectando...')}\n\n`);
121
126
  });
122
127
  this.client.on('agent_start_failed', (data) => {
123
- process.stdout.write(`\r\x1b[K\n ${chalk_1.default.red('✖')} ${chalk_1.default.redBright('No se pudo iniciar el servicio automáticamente:')} ${chalk_1.default.gray(data?.error || 'Tiempo agotado')}\n`);
128
+ process.stdout.write(`\r\x1b[K\n ${chalk_1.default.red('✖')} ${chalk_1.default.redBright('No se pudo iniciar el servicio automáticamente:')} ${chalk_1.default.gray(data?.error || 'Tiempo agotado')}\n`);
124
129
  this._promptUser();
125
130
  });
126
131
  // Voice Capture indicator
127
132
  this.client.on('voice_capture_started', () => {
128
133
  this.isCapturingVoice = true;
129
- process.stdout.write(`\n${chalk_1.default.bgRed.white.bold(' 🎙️ [VOZ DETECTADA - HEY HELIX]: Capturando audio... ')}\n`);
134
+ process.stdout.write(`\n${chalk_1.default.bgRed.white.bold(' 🎙️ [VOZ DETECTADA - HEY HELIX]: Capturando audio... ')}\n`);
130
135
  });
131
136
  this.client.on('voice_capture_stopped', () => {
132
137
  this.isCapturingVoice = false;
133
- process.stdout.write(`\n${chalk_1.default.bgCyan.black.bold(' 🔊 [AUDIO RECIBIDO]: Procesando voz... ')}\n`);
138
+ process.stdout.write(`\n${chalk_1.default.bgCyan.black.bold(' 🔊 [AUDIO RECIBIDO]: Procesando voz... ')}\n`);
134
139
  this._promptUser();
135
140
  });
136
141
  // ReAct Step Progress
137
142
  this.client.on('react_step', (step) => {
138
143
  const iter = step?.iteration ?? 1;
139
144
  const role = step?.role ? chalk_1.default.gray(` (${step.role})`) : '';
140
- process.stdout.write(`\r ${chalk_1.default.hex('#FF9F1C')('◆')} ${chalk_1.default.gray(`Paso ${iter}${role} — Razonando...`)}`);
145
+ process.stdout.write(`\r ${chalk_1.default.hex('#FF9F1C')('◆')} ${chalk_1.default.gray(`Paso ${iter}${role} — Razonando...`)}`);
141
146
  });
142
147
  // Agent assistant message
143
148
  this.client.on('agent_message', (payload) => {
@@ -153,12 +158,12 @@ class HelixRepl {
153
158
  process.stdout.write('\r\x1b[K');
154
159
  // Branded header
155
160
  console.log('');
156
- console.log(` ${chalk_1.default.hex('#FF8C00').bold('⬡ HELIX')}${modelBadge}`);
161
+ console.log(` ${chalk_1.default.hex('#FF8C00').bold('⬡ HELIX')}${modelBadge}`);
157
162
  // Content with subtle left border and clean Markdown formatting
158
163
  const formatted = (0, markdown_1.formatTerminalMarkdown)(content, this.plain);
159
164
  const contentLines = formatted.trim().split('\n');
160
165
  for (const line of contentLines) {
161
- console.log(` ${chalk_1.default.gray('│')} ${line}`);
166
+ console.log(` ${chalk_1.default.gray('│')} ${line}`);
162
167
  }
163
168
  console.log('');
164
169
  this.conversationHistory.push({ role: 'assistant', content });
@@ -174,32 +179,49 @@ class HelixRepl {
174
179
  this.activeTaskId = task.task_id || null;
175
180
  }
176
181
  });
182
+ this.client.on('browser_frame', (payload) => {
183
+ if (!this.browserLiveViewEnabled
184
+ || !payload?.conversation_id?.startsWith('cli_')
185
+ || payload.mime_type !== 'image/jpeg'
186
+ || typeof payload.image_base64 !== 'string') {
187
+ return;
188
+ }
189
+ try {
190
+ process.stdout.write('\r\x1b[K');
191
+ console.log(`\n${(0, colors_1.CYAN_BOLD)(' HELIX LIVE BROWSER')}`);
192
+ console.log((0, browser_ascii_1.renderJpegAsAscii)(payload.image_base64));
193
+ this._promptUser();
194
+ }
195
+ catch (error) {
196
+ console.log((0, colors_1.RED)(` No se pudo representar el cuadro del navegador: ${error.message}`));
197
+ }
198
+ });
177
199
  }
178
200
  async _handleConfirmationDialog(req) {
179
- console.log(`\n${(0, colors_1.YELLOW_BOLD)('⚠️ CONFIRMACIÓN DE ACCIÓN REQUERIDA')}`);
180
- console.log(` Acción: ${chalk_1.default.white.bold(req.action || req.tool || 'Acción del sistema')}`);
201
+ console.log(`\n${(0, colors_1.YELLOW_BOLD)('⚠️ CONFIRMACIÓN DE ACCIÓN REQUERIDA')}`);
202
+ console.log(` Acción: ${chalk_1.default.white.bold(req.action || req.tool || 'Acción del sistema')}`);
181
203
  if (req.description) {
182
- console.log(` Descripción: ${(0, colors_1.DIM)(req.description)}`);
204
+ console.log(` Descripción: ${(0, colors_1.DIM)(req.description)}`);
183
205
  }
184
206
  if (req.arguments && Object.keys(req.arguments).length > 0) {
185
- console.log(` Parámetros: ${(0, colors_1.GRAY)(JSON.stringify(req.arguments))}`);
207
+ console.log(` Parámetros: ${(0, colors_1.GRAY)(JSON.stringify(req.arguments))}`);
186
208
  }
187
209
  this.rl?.pause();
188
210
  try {
189
211
  const answer = await (0, prompts_1.default)({
190
212
  type: 'confirm',
191
213
  name: 'confirmed',
192
- message: '¿Permitir al agente ejecutar esta acción?',
214
+ message: '¿Permitir al agente ejecutar esta acción?',
193
215
  initial: true,
194
216
  });
195
217
  const reqId = req.request_id || req.id || '';
196
218
  if (answer && answer.confirmed) {
197
219
  this.client.confirmAction(reqId);
198
- console.log((0, colors_1.GREEN)(' ✔ Acción confirmada.\n'));
220
+ console.log((0, colors_1.GREEN)(' ✔ Acción confirmada.\n'));
199
221
  }
200
222
  else {
201
223
  this.client.rejectAction(reqId);
202
- console.log((0, colors_1.RED)(' ✖ Acción rechazada.\n'));
224
+ console.log((0, colors_1.RED)(' ✖ Acción rechazada.\n'));
203
225
  }
204
226
  }
205
227
  finally {
@@ -229,7 +251,7 @@ class HelixRepl {
229
251
  await this._handleInputLine(line.trim());
230
252
  });
231
253
  rl.on('close', () => {
232
- console.log((0, colors_1.DIM)('\nCerrando sesión de HELIX CLI...'));
254
+ console.log((0, colors_1.DIM)('\nCerrando sesión de HELIX CLI...'));
233
255
  this.client.close();
234
256
  process.exit(0);
235
257
  });
@@ -243,7 +265,7 @@ class HelixRepl {
243
265
  this._promptUser();
244
266
  }
245
267
  else {
246
- console.log((0, colors_1.DIM)('\n¡Hasta pronto!'));
268
+ console.log((0, colors_1.DIM)('\n¡Hasta pronto!'));
247
269
  this.client.close();
248
270
  process.exit(0);
249
271
  }
@@ -253,7 +275,7 @@ class HelixRepl {
253
275
  _promptUser() {
254
276
  if (!this.rl)
255
277
  return;
256
- const promptPrefix = ` ${chalk_1.default.hex('#FF8C00').bold('helix')} ${chalk_1.default.gray('❯')} `;
278
+ const promptPrefix = ` ${chalk_1.default.hex('#FF8C00').bold('helix')} ${chalk_1.default.gray('❯')} `;
257
279
  this.rl.setPrompt(promptPrefix);
258
280
  this.rl.prompt();
259
281
  }
@@ -263,7 +285,7 @@ class HelixRepl {
263
285
  return;
264
286
  }
265
287
  if (this.client.isStarting) {
266
- console.log(`\n ${chalk_1.default.yellow('⚠️')} ${chalk_1.default.yellowBright('El servicio de HELIX se está iniciando en segundo plano. Espera a que termine antes de enviar comandos o reintentar.')}\n`);
288
+ console.log(`\n ${chalk_1.default.yellow('⚠️')} ${chalk_1.default.yellowBright('El servicio de HELIX se está iniciando en segundo plano. Espera a que termine antes de enviar comandos o reintentar.')}\n`);
267
289
  this._promptUser();
268
290
  return;
269
291
  }
@@ -343,15 +365,40 @@ class HelixRepl {
343
365
  this.voiceEnabled = newState;
344
366
  break;
345
367
  }
346
- case '/browser':
368
+ case '/browser': {
369
+ const browserArgs = args.trim().split(/\s+/);
370
+ if (browserArgs[0]?.toLowerCase() === 'live') {
371
+ const requestedState = browserArgs[1]?.toLowerCase();
372
+ if (requestedState && !['on', 'off'].includes(requestedState)) {
373
+ console.log((0, colors_1.YELLOW)(' Uso: /browser live [on|off]\n'));
374
+ break;
375
+ }
376
+ this.browserLiveViewEnabled = requestedState
377
+ ? requestedState === 'on'
378
+ : !this.browserLiveViewEnabled;
379
+ console.log(this.browserLiveViewEnabled
380
+ ? (0, colors_1.GREEN)(' Vista ASCII del navegador activada.\n')
381
+ : (0, colors_1.YELLOW)(' Vista ASCII del navegador desactivada.\n'));
382
+ break;
383
+ }
347
384
  await (0, browser_1.handleBrowserCommand)(this.client, args);
348
385
  break;
386
+ }
387
+ case '/subagents':
388
+ await (0, subagents_1.handleSubagentsCommand)(this.client, args);
389
+ break;
349
390
  case '/tasks':
350
391
  await this._handleListTasks();
351
392
  break;
393
+ case '/model':
394
+ await (0, model_1.handleModelCommand)(this.client, args);
395
+ break;
352
396
  case '/models':
353
397
  await this._handleListModels();
354
398
  break;
399
+ case '/memory':
400
+ await (0, memory_1.handleMemoryCommand)(this.client, args);
401
+ break;
355
402
  case '/status':
356
403
  await this._handleStatus();
357
404
  break;
@@ -369,7 +416,7 @@ class HelixRepl {
369
416
  this.client.emergencyStop();
370
417
  this.isProcessingPrompt = false;
371
418
  this.activeTaskId = null;
372
- console.log((0, colors_1.GREEN)('✔ Todas las tareas han sido detenidas.\n'));
419
+ console.log((0, colors_1.GREEN)('✔ Todas las tareas han sido detenidas.\n'));
373
420
  break;
374
421
  case '/exit':
375
422
  case '/quit':
@@ -385,7 +432,7 @@ class HelixRepl {
385
432
  }
386
433
  // --- Direct chat message / @skill / #mcp ---
387
434
  if (!this.client.connected) {
388
- console.log((0, colors_1.YELLOW)('⚠ El agente no está conectado actualmente. Intentando enviar de todos modos...'));
435
+ console.log((0, colors_1.YELLOW)('⚠ El agente no está conectado actualmente. Intentando enviar de todos modos...'));
389
436
  }
390
437
  // Highlight skills in strong orange (#FF8C00) and mcps in cyan
391
438
  const knownSkills = this.autocompleter.getSkills();
@@ -394,12 +441,12 @@ class HelixRepl {
394
441
  this.conversationHistory.push({ role: 'user', content: line });
395
442
  this.isProcessingPrompt = true;
396
443
  this.activeTasksCount++;
397
- process.stdout.write(` ${chalk_1.default.gray('→')} ${formattedEcho}\n`);
398
- process.stdout.write(` ${chalk_1.default.hex('#FF9F1C')('◆')} ${chalk_1.default.gray('Procesando...')}`);
444
+ process.stdout.write(` ${chalk_1.default.gray('→')} ${formattedEcho}\n`);
445
+ process.stdout.write(` ${chalk_1.default.hex('#FF9F1C')('â—†')} ${chalk_1.default.gray('Procesando...')}`);
399
446
  const sent = this.client.sendUserText(line, this.conversationId, this.conversationHistory.slice(-10));
400
447
  if (!sent) {
401
448
  process.stdout.write('\r\x1b[K');
402
- console.log((0, colors_1.RED)('✖ Error: No se pudo enviar el mensaje (WebSocket desconectado).'));
449
+ console.log((0, colors_1.RED)('✖ Error: No se pudo enviar el mensaje (WebSocket desconectado).'));
403
450
  this.isProcessingPrompt = false;
404
451
  this.activeTasksCount = Math.max(0, this.activeTasksCount - 1);
405
452
  this._promptUser();
@@ -408,7 +455,7 @@ class HelixRepl {
408
455
  async _handleListTasks() {
409
456
  try {
410
457
  const tasks = await this.client.getTasks();
411
- console.log(`\n${(0, colors_1.BOLD)('📋 Tareas Registradas en HELIX:')}\n`);
458
+ console.log(`\n${(0, colors_1.BOLD)('📋 Tareas Registradas en HELIX:')}\n`);
412
459
  if (!tasks || tasks.length === 0) {
413
460
  console.log((0, colors_1.GRAY)(' No hay tareas recientes registradas.\n'));
414
461
  return;
@@ -416,13 +463,13 @@ class HelixRepl {
416
463
  for (const t of tasks.slice(-10)) {
417
464
  let statusBadge = chalk_1.default.gray(t.status);
418
465
  if (t.status === 'COMPLETED')
419
- statusBadge = chalk_1.default.green('✔ Completada');
466
+ statusBadge = chalk_1.default.green('✔ Completada');
420
467
  if (t.status === 'RUNNING')
421
- statusBadge = chalk_1.default.yellow('⏳ En ejecución');
468
+ statusBadge = chalk_1.default.yellow('⏳ En ejecución');
422
469
  if (t.status === 'FAILED')
423
- statusBadge = chalk_1.default.red('✖ Fallida');
470
+ statusBadge = chalk_1.default.red('✖ Fallida');
424
471
  if (t.status === 'CANCELLED')
425
- statusBadge = chalk_1.default.gray('○ Cancelada');
472
+ statusBadge = chalk_1.default.gray('â—‹ Cancelada');
426
473
  console.log(` ${statusBadge} ${(0, colors_1.DIM)(t.task_id)}`);
427
474
  if (t.prompt)
428
475
  console.log(` ${chalk_1.default.white(t.prompt)}`);
@@ -436,13 +483,13 @@ class HelixRepl {
436
483
  async _handleListModels() {
437
484
  try {
438
485
  const models = await this.client.getModels();
439
- console.log(`\n${(0, colors_1.BOLD)('🧠 Modelos Disponibles en Proveedores Configurados:')}\n`);
486
+ console.log(`\n${(0, colors_1.BOLD)('🧠 Modelos Disponibles en Proveedores Configurados:')}\n`);
440
487
  if (!models || models.length === 0) {
441
488
  console.log((0, colors_1.GRAY)(' No hay modelos disponibles. Configura tus API Keys con /config-apikey.\n'));
442
489
  return;
443
490
  }
444
491
  for (const m of models) {
445
- console.log(` ${(0, colors_1.CYAN)('•')} ${chalk_1.default.bold(m.id)} ${(0, colors_1.DIM)(`[${m.provider}]`)}`);
492
+ console.log(` ${(0, colors_1.CYAN)('•')} ${chalk_1.default.bold(m.id)} ${(0, colors_1.DIM)(`[${m.provider}]`)}`);
446
493
  }
447
494
  console.log('');
448
495
  }
@@ -452,11 +499,11 @@ class HelixRepl {
452
499
  }
453
500
  async _handleStatus() {
454
501
  console.log('');
455
- console.log(` ${chalk_1.default.hex('#FF8C00').bold('⬡ Estado del Sistema')}`);
456
- console.log(` ${chalk_1.default.gray('─'.repeat(40))}`);
457
- console.log(` ${chalk_1.default.gray('Agente WS')} ${this.client.connected ? chalk_1.default.green('● Conectado') : chalk_1.default.red('● Desconectado')} ${chalk_1.default.gray(this.client.url)}`);
458
- console.log(` ${chalk_1.default.gray('Micrófono')} ${this.voiceEnabled ? chalk_1.default.green('● Activo') + chalk_1.default.gray(' (hey helix)') : chalk_1.default.red('● Inactivo')}`);
459
- console.log(` ${chalk_1.default.gray('Conversación')} ${chalk_1.default.gray(this.conversationId)} ${chalk_1.default.gray(`(${this.conversationHistory.length} msgs)`)}`);
502
+ console.log(` ${chalk_1.default.hex('#FF8C00').bold('⬡ Estado del Sistema')}`);
503
+ console.log(` ${chalk_1.default.gray('─'.repeat(40))}`);
504
+ console.log(` ${chalk_1.default.gray('Agente WS')} ${this.client.connected ? chalk_1.default.green('● Conectado') : chalk_1.default.red('● Desconectado')} ${chalk_1.default.gray(this.client.url)}`);
505
+ console.log(` ${chalk_1.default.gray('Micrófono')} ${this.voiceEnabled ? chalk_1.default.green('● Activo') + chalk_1.default.gray(' (hey helix)') : chalk_1.default.red('● Inactivo')}`);
506
+ console.log(` ${chalk_1.default.gray('Conversación')} ${chalk_1.default.gray(this.conversationId)} ${chalk_1.default.gray(`(${this.conversationHistory.length} msgs)`)}`);
460
507
  console.log(` ${chalk_1.default.gray('Skills')} ${chalk_1.default.hex('#FF8C00').bold(this.autocompleter.getSkills().length.toString())} registradas`);
461
508
  console.log(` ${chalk_1.default.gray('MCP Tools')} ${chalk_1.default.cyan.bold(this.autocompleter.getMcpTools().length.toString())} disponibles`);
462
509
  console.log('');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
- {
1
+ {
2
2
  "name": "helix-assistant-cli",
3
- "version": "0.2.14",
3
+ "version": "0.6.0",
4
4
  "description": "HELIX - Interactive Terminal CLI for Windows AI Desktop Agent",
5
5
  "private": false,
6
6
  "publishConfig": {
@@ -21,10 +21,12 @@
21
21
  "chalk": "^4.1.2",
22
22
  "commander": "^12.0.0",
23
23
  "dotenv": "^16.4.5",
24
+ "jpeg-js": "^0.4.4",
24
25
  "prompts": "^2.4.2",
25
26
  "ws": "^8.16.0"
26
27
  },
27
28
  "devDependencies": {
29
+ "@types/jpeg-js": "^0.3.0",
28
30
  "@types/node": "^20.11.0",
29
31
  "@types/prompts": "^2.4.9",
30
32
  "@types/ws": "^8.5.10",
@@ -14,7 +14,7 @@ export async function handleBrowserCommand(
14
14
  console.log(CYAN(`\n Solicitando al agente abrir en navegador: ${targetUrl}...`));
15
15
  client.sendUserText(
16
16
  `Por favor abre la siguiente URL en el navegador: ${targetUrl}`,
17
- `browser_${Date.now()}`
17
+ `cli_browser_${Date.now()}`
18
18
  );
19
19
  console.log(GREEN(` ✔ Comando enviado al agente.\n`));
20
20
  return;
@@ -32,7 +32,8 @@ export async function handleBrowserCommand(
32
32
  console.log(` Dominios: ${settings.block_untrusted_domains ? YELLOW('Filtrado Estricto') : GRAY('Abierto')}`);
33
33
  console.log(`\n ${DIM('Comandos disponibles:')}`);
34
34
  console.log(` ${CYAN('/browser')} ${DIM('- Ver estado de navegación')}`);
35
- console.log(` ${CYAN('/browser open <url>')} ${DIM('- Abrir URL con el agente')}\n`);
35
+ console.log(` ${CYAN('/browser open <url>')} ${DIM('- Abrir URL con el agente')}`);
36
+ console.log(` ${CYAN('/browser live [on|off]')} ${DIM('- Alternar la vista ASCII de capturas')}\n`);
36
37
  } catch (err) {
37
38
  console.log(RED(` Error al consultar estado del navegador: ${(err as Error).message}\n`));
38
39
  }