helix-assistant-cli 0.2.14 → 0.6.1

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/README.md ADDED
@@ -0,0 +1,25 @@
1
+ # HELIX CLI
2
+
3
+ Interactive terminal client for the local HELIX agent.
4
+
5
+ ```powershell
6
+ npx helix-assistant-cli
7
+ ```
8
+
9
+ The CLI requires a reachable HELIX agent and a configured model provider for
10
+ AI-backed requests.
11
+
12
+ ## Subagents
13
+
14
+ - `/subagents` displays the ASCII profile/status panel.
15
+ - `/subagents chat <profile_id> <message>` sends a direct message to a profile.
16
+
17
+ ## Browser snapshots
18
+
19
+ - `/browser open <url>` asks the agent to open a page.
20
+ - `/browser live on` enables the ASCII snapshot viewer; `/browser live off`
21
+ disables it.
22
+
23
+ Frames are JPEG snapshots emitted after successful browser actions, not a
24
+ continuous video stream. Browser-frame routing is limited to the requesting
25
+ conversation.
@@ -12,7 +12,7 @@ async function handleBrowserCommand(client, args) {
12
12
  const targetUrl = parts[1];
13
13
  if (subCommand === 'open' && targetUrl) {
14
14
  console.log((0, colors_1.CYAN)(`\n Solicitando al agente abrir en navegador: ${targetUrl}...`));
15
- client.sendUserText(`Por favor abre la siguiente URL en el navegador: ${targetUrl}`, `browser_${Date.now()}`);
15
+ client.sendUserText(`Por favor abre la siguiente URL en el navegador: ${targetUrl}`, `cli_browser_${Date.now()}`);
16
16
  console.log((0, colors_1.GREEN)(` ✔ Comando enviado al agente.\n`));
17
17
  return;
18
18
  }
@@ -27,7 +27,8 @@ async function handleBrowserCommand(client, args) {
27
27
  console.log(` Dominios: ${settings.block_untrusted_domains ? (0, colors_1.YELLOW)('Filtrado Estricto') : (0, colors_1.GRAY)('Abierto')}`);
28
28
  console.log(`\n ${(0, colors_1.DIM)('Comandos disponibles:')}`);
29
29
  console.log(` ${(0, colors_1.CYAN)('/browser')} ${(0, colors_1.DIM)('- Ver estado de navegación')}`);
30
- console.log(` ${(0, colors_1.CYAN)('/browser open <url>')} ${(0, colors_1.DIM)('- Abrir URL con el agente')}\n`);
30
+ console.log(` ${(0, colors_1.CYAN)('/browser open <url>')} ${(0, colors_1.DIM)('- Abrir URL con el agente')}`);
31
+ console.log(` ${(0, colors_1.CYAN)('/browser live [on|off]')} ${(0, colors_1.DIM)('- Alternar la vista ASCII de capturas')}\n`);
31
32
  }
32
33
  catch (err) {
33
34
  console.log((0, colors_1.RED)(` Error al consultar estado del navegador: ${err.message}\n`));
@@ -0,0 +1,6 @@
1
+ import type { AgentClient } from '../client/agent-client';
2
+ export declare function handleMemoryStats(client: AgentClient): Promise<void>;
3
+ export declare function handleMemoryRules(client: AgentClient): Promise<void>;
4
+ export declare function handleMemoryReset(client: AgentClient): Promise<void>;
5
+ /** Top-level dispatcher for /memory <args> */
6
+ export declare function handleMemoryCommand(client: AgentClient, args: string): Promise<void>;
@@ -0,0 +1,129 @@
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.handleMemoryStats = handleMemoryStats;
7
+ exports.handleMemoryRules = handleMemoryRules;
8
+ exports.handleMemoryReset = handleMemoryReset;
9
+ exports.handleMemoryCommand = handleMemoryCommand;
10
+ /**
11
+ * /memory — CLI command for inspecting the Adaptive Learning Harness.
12
+ *
13
+ * Subcommands:
14
+ * /memory — show quick stats summary
15
+ * /memory stats — detailed episode + rule counts
16
+ * /memory rules — list top learned rules
17
+ * /memory reset — run maintenance / pruning cycle
18
+ */
19
+ const chalk_1 = __importDefault(require("chalk"));
20
+ const colors_1 = require("../ui/colors");
21
+ async function fetchLearningStats(client) {
22
+ try {
23
+ return await client.request('GET_LEARNING_STATS', {});
24
+ }
25
+ catch {
26
+ return null;
27
+ }
28
+ }
29
+ // ── Renderers ─────────────────────────────────────────────────────────────────
30
+ function renderStats(stats) {
31
+ console.log('');
32
+ console.log(` ${chalk_1.default.hex('#FF8C00').bold('⬡ Sistema de Aprendizaje Adaptativo')}`);
33
+ console.log(` ${chalk_1.default.gray('─'.repeat(48))}`);
34
+ const ep = stats.episode_count;
35
+ const ru = stats.rule_count;
36
+ const co = stats.correction_count;
37
+ const sk = stats.skill_count;
38
+ console.log(` ${chalk_1.default.gray('Episodios registrados')} ${chalk_1.default.white.bold(ep.toString())}`);
39
+ console.log(` ${chalk_1.default.gray('Correcciones del usuario')} ${co > 0 ? chalk_1.default.yellow.bold(co.toString()) : chalk_1.default.white(co.toString())}`);
40
+ console.log(` ${chalk_1.default.gray('Reglas aprendidas')} ${chalk_1.default.cyan.bold(ru.toString())}`);
41
+ console.log(` ${chalk_1.default.gray('Skills auto-generadas')} ${chalk_1.default.magenta.bold(sk.toString())}`);
42
+ if (stats.last_episode_at) {
43
+ const d = new Date(stats.last_episode_at);
44
+ console.log(` ${chalk_1.default.gray('Último episodio')} ${(0, colors_1.DIM)(d.toLocaleString())}`);
45
+ }
46
+ const userMdStatus = stats.user_md_exists
47
+ ? chalk_1.default.green('✔ USER.md activo')
48
+ : chalk_1.default.gray('○ Sin perfil generado aún');
49
+ console.log(` ${chalk_1.default.gray('Perfil feedforward')} ${userMdStatus}`);
50
+ console.log('');
51
+ }
52
+ function renderRules(stats) {
53
+ if (!stats.top_rules || stats.top_rules.length === 0) {
54
+ console.log((0, colors_1.GRAY)(' No hay reglas aprendidas todavía. Interactúa con HELIX para que aprenda tus preferencias.\n'));
55
+ return;
56
+ }
57
+ console.log('');
58
+ console.log(` ${chalk_1.default.hex('#FF8C00').bold(`✦ Top ${stats.top_rules.length} Reglas Aprendidas`)}`);
59
+ console.log(` ${chalk_1.default.gray('─'.repeat(56))}`);
60
+ for (const rule of stats.top_rules) {
61
+ const conf = Math.round(rule.confidence * 100);
62
+ const bar = '█'.repeat(Math.min(10, Math.floor(conf / 10)));
63
+ const confColor = conf >= 80 ? chalk_1.default.green : conf >= 50 ? chalk_1.default.yellow : chalk_1.default.gray;
64
+ console.log(` ${chalk_1.default.cyan(`[${rule.category}]`).padEnd(24)} ${chalk_1.default.white(rule.rule)}`);
65
+ console.log(` ${(0, colors_1.DIM)('Confianza:')} ${confColor(`${conf}%`)} ${chalk_1.default.gray(bar)} ${(0, colors_1.DIM)(`(${rule.uses} usos)`)}`);
66
+ console.log('');
67
+ }
68
+ }
69
+ // ── Command handlers ──────────────────────────────────────────────────────────
70
+ async function handleMemoryStats(client) {
71
+ const stats = await fetchLearningStats(client);
72
+ if (!stats) {
73
+ console.log((0, colors_1.YELLOW)(' ⚠ El harness de aprendizaje no está disponible (agente desconectado o versión antigua).\n'));
74
+ return;
75
+ }
76
+ renderStats(stats);
77
+ if (stats.top_rules?.length) {
78
+ console.log((0, colors_1.DIM)(' Usa /memory rules para ver las reglas detalladas.'));
79
+ console.log('');
80
+ }
81
+ }
82
+ async function handleMemoryRules(client) {
83
+ const stats = await fetchLearningStats(client);
84
+ if (!stats) {
85
+ console.log((0, colors_1.YELLOW)(' ⚠ El harness de aprendizaje no está disponible.\n'));
86
+ return;
87
+ }
88
+ renderStats(stats);
89
+ renderRules(stats);
90
+ }
91
+ async function handleMemoryReset(client) {
92
+ try {
93
+ const result = await client.request('RUN_LEARNING_MAINTENANCE', {});
94
+ console.log('');
95
+ console.log((0, colors_1.GREEN)(' ✔ Mantenimiento del harness completado.'));
96
+ console.log(` ${chalk_1.default.gray('Reglas podadas:')} ${chalk_1.default.white.bold(String(result.pruned_rules_count ?? 0))}`);
97
+ const pruned = result.pruned_skills ?? [];
98
+ if (pruned.length) {
99
+ console.log(` ${chalk_1.default.gray('Skills podadas:')} ${chalk_1.default.white(pruned.join(', '))}`);
100
+ }
101
+ else {
102
+ console.log(` ${chalk_1.default.gray('Skills podadas:')} ${(0, colors_1.DIM)('ninguna')}`);
103
+ }
104
+ console.log('');
105
+ }
106
+ catch (err) {
107
+ console.log((0, colors_1.RED)(` ✖ Error durante mantenimiento: ${err.message}\n`));
108
+ }
109
+ }
110
+ /** Top-level dispatcher for /memory <args> */
111
+ async function handleMemoryCommand(client, args) {
112
+ const sub = args.trim().split(/\s+/)[0]?.toLowerCase() || '';
113
+ switch (sub) {
114
+ case '':
115
+ case 'stats':
116
+ await handleMemoryStats(client);
117
+ break;
118
+ case 'rules':
119
+ await handleMemoryRules(client);
120
+ break;
121
+ case 'reset':
122
+ case 'maintenance':
123
+ case 'prune':
124
+ await handleMemoryReset(client);
125
+ break;
126
+ default:
127
+ console.log((0, colors_1.YELLOW)(` Subcomando desconocido: "${sub}". Opciones: stats, rules, reset\n`));
128
+ }
129
+ }
@@ -0,0 +1,24 @@
1
+ import type { AgentClient } from '../client/agent-client';
2
+ export interface ModelEntry {
3
+ id: string;
4
+ label: string;
5
+ tier: 'free' | 'local' | 'cloud';
6
+ provider: string;
7
+ badge?: string;
8
+ }
9
+ export declare const FREE_MODELS: ModelEntry[];
10
+ export declare const LOCAL_MODELS: ModelEntry[];
11
+ export declare const CLOUD_MODELS: ModelEntry[];
12
+ export declare const ALL_MODELS: ModelEntry[];
13
+ /** /model — show current model */
14
+ export declare function handleModelStatus(client: AgentClient): Promise<void>;
15
+ /** /model list — print the full model catalog */
16
+ export declare function handleModelList(): void;
17
+ /** /model set <id> — switch the active model */
18
+ export declare function handleModelSet(client: AgentClient, modelId: string): Promise<void>;
19
+ /** /model effort <low|medium|high> — set effort/quality level */
20
+ export declare function handleModelEffort(client: AgentClient, level: string): Promise<void>;
21
+ /** /model free-list — list free models fetched from bridge (live) */
22
+ export declare function handleFreeModelList(client: AgentClient): Promise<void>;
23
+ /** Top-level dispatcher for /model <args> */
24
+ export declare function handleModelCommand(client: AgentClient, args: string): Promise<void>;
@@ -0,0 +1,220 @@
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.ALL_MODELS = exports.CLOUD_MODELS = exports.LOCAL_MODELS = exports.FREE_MODELS = void 0;
7
+ exports.handleModelStatus = handleModelStatus;
8
+ exports.handleModelList = handleModelList;
9
+ exports.handleModelSet = handleModelSet;
10
+ exports.handleModelEffort = handleModelEffort;
11
+ exports.handleFreeModelList = handleFreeModelList;
12
+ exports.handleModelCommand = handleModelCommand;
13
+ /**
14
+ * /model — CLI command for viewing and switching the active AI model.
15
+ *
16
+ * Subcommands:
17
+ * /model — show current model + provider
18
+ * /model list — list all model tiers (Free, Local Ollama, Cloud)
19
+ * /model set <id> — switch to a model by id
20
+ * /model effort <low|medium|high> — set effort/quality level
21
+ */
22
+ const chalk_1 = __importDefault(require("chalk"));
23
+ const colors_1 = require("../ui/colors");
24
+ exports.FREE_MODELS = [
25
+ {
26
+ id: 'meta-llama/llama-3.2-3b-instruct:free',
27
+ label: 'Llama 3.2 3B Instruct',
28
+ tier: 'free',
29
+ provider: 'free',
30
+ badge: '0-cost',
31
+ },
32
+ {
33
+ id: 'qwen/qwen-2.5-coder-32b-instruct:free',
34
+ label: 'Qwen 2.5 Coder 32B',
35
+ tier: 'free',
36
+ provider: 'free',
37
+ badge: '0-cost',
38
+ },
39
+ {
40
+ id: 'deepseek/deepseek-r1:free',
41
+ label: 'DeepSeek R1',
42
+ tier: 'free',
43
+ provider: 'free',
44
+ badge: '0-cost · razonamiento',
45
+ },
46
+ {
47
+ id: 'google/gemini-flash-1.5:free',
48
+ label: 'Gemini Flash 1.5',
49
+ tier: 'free',
50
+ provider: 'free',
51
+ badge: '0-cost · rápido',
52
+ },
53
+ ];
54
+ exports.LOCAL_MODELS = [
55
+ { id: 'ollama/llama3', label: 'Llama 3 (local)', tier: 'local', provider: 'ollama' },
56
+ { id: 'ollama/mistral', label: 'Mistral 7B (local)', tier: 'local', provider: 'ollama' },
57
+ { id: 'ollama/deepseek-coder', label: 'DeepSeek Coder (local)', tier: 'local', provider: 'ollama' },
58
+ { id: 'ollama/phi3', label: 'Phi-3 Mini (local)', tier: 'local', provider: 'ollama' },
59
+ { id: 'ollama/qwen2.5-coder', label: 'Qwen 2.5 Coder (local)', tier: 'local', provider: 'ollama' },
60
+ ];
61
+ exports.CLOUD_MODELS = [
62
+ { id: 'gpt-4o', label: 'GPT-4o', tier: 'cloud', provider: 'openai' },
63
+ { id: 'gpt-4o-mini', label: 'GPT-4o Mini', tier: 'cloud', provider: 'openai' },
64
+ { id: 'claude-3-5-sonnet-20241022', label: 'Claude 3.5 Sonnet', tier: 'cloud', provider: 'anthropic' },
65
+ { id: 'claude-3-haiku-20240307', label: 'Claude 3 Haiku', tier: 'cloud', provider: 'anthropic' },
66
+ { id: 'gemini-1.5-pro', label: 'Gemini 1.5 Pro', tier: 'cloud', provider: 'gemini' },
67
+ { id: 'gemini-1.5-flash', label: 'Gemini 1.5 Flash', tier: 'cloud', provider: 'gemini' },
68
+ ];
69
+ exports.ALL_MODELS = [...exports.FREE_MODELS, ...exports.LOCAL_MODELS, ...exports.CLOUD_MODELS];
70
+ // ── Helpers ───────────────────────────────────────────────────────────────────
71
+ function providerBadge(entry) {
72
+ if (entry.tier === 'free')
73
+ return chalk_1.default.green.bold('[0-COST]');
74
+ if (entry.tier === 'local')
75
+ return chalk_1.default.cyan.bold('[LOCAL]');
76
+ return chalk_1.default.yellow.bold('[CLOUD]');
77
+ }
78
+ function findModel(id) {
79
+ return exports.ALL_MODELS.find((m) => m.id === id || m.id.startsWith(id));
80
+ }
81
+ // ── Command handlers ──────────────────────────────────────────────────────────
82
+ /** /model — show current model */
83
+ async function handleModelStatus(client) {
84
+ try {
85
+ const settings = await client.getSettings();
86
+ const modelId = settings.default_model || '(none)';
87
+ const provider = settings.preferred_provider || '(none)';
88
+ const entry = findModel(modelId);
89
+ console.log('');
90
+ console.log(` ${chalk_1.default.hex('#FF8C00').bold('⬡ Modelo Activo')}`);
91
+ console.log(` ${chalk_1.default.gray('─'.repeat(44))}`);
92
+ console.log(` ${chalk_1.default.gray('ID')} ${chalk_1.default.white.bold(modelId)}`);
93
+ console.log(` ${chalk_1.default.gray('Proveedor')} ${chalk_1.default.white(provider)}`);
94
+ if (entry) {
95
+ console.log(` ${chalk_1.default.gray('Nombre')} ${entry.label}`);
96
+ console.log(` ${chalk_1.default.gray('Nivel')} ${providerBadge(entry)} ${entry.badge ? (0, colors_1.DIM)(`(${entry.badge})`) : ''}`);
97
+ }
98
+ const speed = settings.speed_preference || 'balanced';
99
+ const quality = settings.quality_preference || 'balanced';
100
+ console.log(` ${chalk_1.default.gray('Velocidad')} ${chalk_1.default.cyan(speed)}`);
101
+ console.log(` ${chalk_1.default.gray('Calidad')} ${chalk_1.default.cyan(quality)}`);
102
+ console.log('');
103
+ console.log((0, colors_1.DIM)(' Usa /model list para ver opciones, /model set <id> para cambiar.'));
104
+ console.log('');
105
+ }
106
+ catch (err) {
107
+ console.log((0, colors_1.RED)(` Error al obtener modelo: ${err.message}\n`));
108
+ }
109
+ }
110
+ /** /model list — print the full model catalog */
111
+ function handleModelList() {
112
+ const printSection = (title, models) => {
113
+ console.log(`\n ${chalk_1.default.hex('#FF8C00').bold(title)}`);
114
+ console.log(` ${chalk_1.default.gray('─'.repeat(60))}`);
115
+ for (const m of models) {
116
+ const badge = providerBadge(m);
117
+ const extra = m.badge ? (0, colors_1.DIM)(` · ${m.badge}`) : '';
118
+ console.log(` ${badge} ${chalk_1.default.white.bold(m.label.padEnd(32))} ${(0, colors_1.GRAY)(m.id)}${extra}`);
119
+ }
120
+ };
121
+ console.log('');
122
+ console.log(` ${(0, colors_1.BOLD)('🧠 Catálogo de Modelos HELIX')}`);
123
+ printSection('✦ Nivel 1 — Sin costo (OpenRouter Free)', exports.FREE_MODELS);
124
+ printSection('✦ Nivel 2 — Local Ollama (sin internet)', exports.LOCAL_MODELS);
125
+ printSection('✦ Nivel 3 — Nube con API Key propia', exports.CLOUD_MODELS);
126
+ console.log('');
127
+ console.log((0, colors_1.DIM)(' Usa: /model set <model-id> para activar un modelo'));
128
+ console.log((0, colors_1.DIM)(' Usa: /model effort <low|medium|high> para ajustar esfuerzo'));
129
+ console.log('');
130
+ }
131
+ /** /model set <id> — switch the active model */
132
+ async function handleModelSet(client, modelId) {
133
+ if (!modelId) {
134
+ console.log((0, colors_1.YELLOW)(' Uso: /model set <model-id>\n'));
135
+ return;
136
+ }
137
+ const entry = findModel(modelId);
138
+ const provider = entry?.provider || (modelId.startsWith('ollama/') ? 'ollama' : 'openai');
139
+ try {
140
+ await client.saveSettings({
141
+ default_model: entry?.id ?? modelId,
142
+ preferred_provider: provider,
143
+ });
144
+ const badge = entry ? providerBadge(entry) : '';
145
+ console.log('');
146
+ console.log((0, colors_1.GREEN)(' ✔ Modelo actualizado correctamente.'));
147
+ console.log(` ${chalk_1.default.gray('Modelo:')} ${chalk_1.default.white.bold(entry?.id ?? modelId)} ${badge}`);
148
+ console.log(` ${chalk_1.default.gray('Proveedor:')} ${chalk_1.default.white(provider)}`);
149
+ console.log('');
150
+ }
151
+ catch (err) {
152
+ console.log((0, colors_1.RED)(` ✖ Error al cambiar modelo: ${err.message}\n`));
153
+ }
154
+ }
155
+ /** /model effort <low|medium|high> — set effort/quality level */
156
+ async function handleModelEffort(client, level) {
157
+ const valid = ['low', 'medium', 'high'];
158
+ const normalized = level?.toLowerCase();
159
+ if (!normalized || !valid.includes(normalized)) {
160
+ console.log((0, colors_1.YELLOW)(' Uso: /model effort <low|medium|high>\n'));
161
+ return;
162
+ }
163
+ const speedMap = { low: 'fast', medium: 'balanced', high: 'balanced' };
164
+ const qualityMap = { low: 'fast', medium: 'balanced', high: 'best' };
165
+ try {
166
+ await client.saveSettings({
167
+ speed_preference: speedMap[normalized],
168
+ quality_preference: qualityMap[normalized],
169
+ });
170
+ console.log('');
171
+ console.log((0, colors_1.GREEN)(` ✔ Nivel de esfuerzo → ${chalk_1.default.white.bold(normalized.toUpperCase())}`));
172
+ console.log((0, colors_1.DIM)(` Velocidad: ${speedMap[normalized]} | Calidad: ${qualityMap[normalized]}`));
173
+ console.log('');
174
+ }
175
+ catch (err) {
176
+ console.log((0, colors_1.RED)(` ✖ Error al ajustar esfuerzo: ${err.message}\n`));
177
+ }
178
+ }
179
+ /** /model free-list — list free models fetched from bridge (live) */
180
+ async function handleFreeModelList(client) {
181
+ try {
182
+ const result = await client
183
+ .request('LIST_FREE_MODELS', {});
184
+ if (!result?.models?.length) {
185
+ // Fallback to static catalog
186
+ handleModelList();
187
+ return;
188
+ }
189
+ console.log(`\n ${(0, colors_1.BOLD)('✦ Modelos gratuitos disponibles (live):')} \n`);
190
+ for (const m of result.models) {
191
+ console.log(` ${(0, colors_1.GREEN)('[0-COST]')} ${chalk_1.default.white.bold(m.label)} ${(0, colors_1.GRAY)(m.id)}`);
192
+ }
193
+ console.log('');
194
+ }
195
+ catch {
196
+ // Agent not connected or handler not present — fall back to static
197
+ handleModelList();
198
+ }
199
+ }
200
+ /** Top-level dispatcher for /model <args> */
201
+ async function handleModelCommand(client, args) {
202
+ const parts = args.trim().split(/\s+/);
203
+ const sub = parts[0]?.toLowerCase() || '';
204
+ switch (sub) {
205
+ case '':
206
+ await handleModelStatus(client);
207
+ break;
208
+ case 'list':
209
+ handleModelList();
210
+ break;
211
+ case 'set':
212
+ await handleModelSet(client, parts.slice(1).join(' '));
213
+ break;
214
+ case 'effort':
215
+ await handleModelEffort(client, parts[1] || '');
216
+ break;
217
+ default:
218
+ console.log((0, colors_1.YELLOW)(` Subcomando desconocido: "${sub}". Opciones: list, set, effort\n`));
219
+ }
220
+ }
@@ -0,0 +1,15 @@
1
+ import type { AgentClient } from '../client/agent-client';
2
+ interface SubagentProfile {
3
+ profile_id: string;
4
+ name: string;
5
+ }
6
+ interface SubagentStatus {
7
+ profile_id?: string;
8
+ name?: string;
9
+ status?: string;
10
+ progress?: number;
11
+ current_action?: string;
12
+ }
13
+ export declare function renderSubagentPanel(profiles: SubagentProfile[], statuses: SubagentStatus[]): string;
14
+ export declare function handleSubagentsCommand(client: AgentClient, args?: string): Promise<void>;
15
+ export {};
@@ -0,0 +1,123 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.renderSubagentPanel = renderSubagentPanel;
4
+ exports.handleSubagentsCommand = handleSubagentsCommand;
5
+ const colors_1 = require("../ui/colors");
6
+ function boundedText(value, maxLength) {
7
+ if (typeof value !== 'string')
8
+ return '';
9
+ const clean = value.replace(/[\r\n|]/g, ' ').trim();
10
+ return clean.length > maxLength ? `${clean.slice(0, maxLength - 3)}...` : clean;
11
+ }
12
+ function statusLabel(status) {
13
+ const normalized = status?.toLowerCase() || 'idle';
14
+ if (['running', 'active', 'in_progress'].includes(normalized))
15
+ return 'RUNNING';
16
+ if (['failed', 'error'].includes(normalized))
17
+ return 'FAILED';
18
+ if (['completed', 'done'].includes(normalized))
19
+ return 'DONE';
20
+ if (['cancelled', 'canceled'].includes(normalized))
21
+ return 'STOPPED';
22
+ return 'IDLE';
23
+ }
24
+ function renderSubagentPanel(profiles, statuses) {
25
+ const statusByProfile = new Map();
26
+ for (const status of statuses) {
27
+ if (status.profile_id)
28
+ statusByProfile.set(status.profile_id, status);
29
+ }
30
+ const rows = profiles.map((profile) => {
31
+ const status = statusByProfile.get(profile.profile_id);
32
+ const profileId = boundedText(profile.profile_id, 22);
33
+ const name = boundedText(profile.name, 18);
34
+ const state = statusLabel(status?.status);
35
+ const progress = Number.isFinite(status?.progress) ? ` ${status?.progress}%` : '';
36
+ const action = boundedText(status?.current_action, 26);
37
+ return [
38
+ profileId.padEnd(22),
39
+ name.padEnd(18),
40
+ `${state}${progress}`,
41
+ action,
42
+ ];
43
+ });
44
+ const headings = ['PROFILE ID', 'NAME', 'STATE', 'CURRENT ACTION'];
45
+ const widths = [22, 18, 16, 26];
46
+ const border = `+${widths.map((width) => '-'.repeat(width + 2)).join('+')}+`;
47
+ const row = (values) => `| ${values.map((value, index) => value.padEnd(widths[index])).join(' | ')} |`;
48
+ const lines = [
49
+ border,
50
+ row(headings),
51
+ border,
52
+ ...(rows.length > 0
53
+ ? rows.map(row)
54
+ : [row(['No profiles', 'available', '', ''])]),
55
+ border,
56
+ ];
57
+ return lines.join('\n');
58
+ }
59
+ function asArray(value, label, isItem) {
60
+ if (!Array.isArray(value) || !value.every(isItem)) {
61
+ throw new Error(`Agent returned an invalid ${label} list.`);
62
+ }
63
+ return value;
64
+ }
65
+ function isSubagentProfile(value) {
66
+ if (value === null || typeof value !== 'object' || Array.isArray(value))
67
+ return false;
68
+ const profile = value;
69
+ return typeof profile.profile_id === 'string'
70
+ && typeof profile.name === 'string';
71
+ }
72
+ function isSubagentStatus(value) {
73
+ if (value === null || typeof value !== 'object' || Array.isArray(value))
74
+ return false;
75
+ const status = value;
76
+ return (status.profile_id === undefined || typeof status.profile_id === 'string')
77
+ && (status.status === undefined || typeof status.status === 'string')
78
+ && (status.progress === undefined || typeof status.progress === 'number')
79
+ && (status.current_action === undefined || typeof status.current_action === 'string');
80
+ }
81
+ async function handleSubagentsCommand(client, args = '') {
82
+ const trimmed = args.trim();
83
+ const chatMatch = trimmed.match(/^chat\s+(\S+)\s+([\s\S]+)$/i);
84
+ if (chatMatch) {
85
+ const [, profileId, message] = chatMatch;
86
+ try {
87
+ const result = await client.request('CHAT_SUBAGENT', { profile_id: profileId, message: message.trim() }, 120000);
88
+ console.log('\n' + (0, colors_1.CYAN_BOLD)(` SUBAGENT ${profileId}`));
89
+ if (result !== null
90
+ && typeof result === 'object'
91
+ && 'response' in result
92
+ && typeof result.response === 'string') {
93
+ console.log(result.response);
94
+ }
95
+ else {
96
+ console.log(JSON.stringify(result, null, 2));
97
+ }
98
+ console.log('');
99
+ }
100
+ catch (error) {
101
+ console.log((0, colors_1.RED)(` No se pudo contactar al subagente: ${error.message}\n`));
102
+ }
103
+ return;
104
+ }
105
+ if (trimmed && !/^list$/i.test(trimmed)) {
106
+ console.log((0, colors_1.YELLOW)(' Uso: /subagents [list] | /subagents chat <id> <mensaje>\n'));
107
+ return;
108
+ }
109
+ try {
110
+ const [profilesValue, statusesValue] = await Promise.all([
111
+ client.request('GET_SUBAGENT_PROFILES'),
112
+ client.request('GET_SUBAGENT_STATUS'),
113
+ ]);
114
+ const profiles = asArray(profilesValue, 'subagent profile', isSubagentProfile);
115
+ const statuses = asArray(statusesValue, 'subagent status', isSubagentStatus);
116
+ console.log(`\n${(0, colors_1.CYAN_BOLD)(' HELIX SUBAGENTS')}`);
117
+ console.log(renderSubagentPanel(profiles, statuses));
118
+ console.log((0, colors_1.DIM)(' Chat: /subagents chat <profile_id> <mensaje>\n'));
119
+ }
120
+ catch (error) {
121
+ console.log((0, colors_1.RED)(` No se pudo cargar el panel de subagentes: ${error.message}\n`));
122
+ }
123
+ }
@@ -3,10 +3,19 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MCP_DEFAULTS = void 0;
4
4
  /** Catalog only: entries are never enabled or installed automatically. */
5
5
  exports.MCP_DEFAULTS = [
6
- { id: 'filesystem', label: 'Archivos locales', purpose: 'Carpeta elegida por el usuario', transport: 'stdio', activation: 'local-opt-in', privacy: 'Solo rutas autorizadas' },
6
+ { id: 'filesystem', label: 'Archivos locales', purpose: 'Carpetas y archivos del equipo del usuario', transport: 'stdio', activation: 'local-opt-in', privacy: 'Solo rutas autorizadas' },
7
7
  { id: 'obsidian', label: 'Obsidian / Helix Vault', purpose: 'Leer y gestionar una bóveda concreta', transport: 'stdio', activation: 'local-opt-in', privacy: 'Solo la bóveda seleccionada' },
8
8
  { id: 'vscode', label: 'VS Code', purpose: 'Trabajar con el workspace seleccionado', transport: 'stdio', activation: 'local-opt-in', privacy: 'Solo el workspace seleccionado' },
9
- { id: 'github', label: 'GitHub', purpose: 'Repositorios, issues y pull requests', transport: 'stdio', activation: 'oauth-opt-in', privacy: 'Requiere token u OAuth del usuario' },
10
- { id: 'gmail', label: 'Gmail', purpose: 'Correo y búsqueda de mensajes', transport: 'connector', activation: 'oauth-opt-in', privacy: 'Requiere OAuth explícito del usuario' },
11
- { id: 'notion', label: 'Notion', purpose: 'Páginas y bases de datos', transport: 'connector', activation: 'oauth-opt-in', privacy: 'Requiere integración autorizada' },
9
+ { id: 'cursor', label: 'Cursor', purpose: 'Proyectos y editor de código del usuario', transport: 'stdio', activation: 'local-opt-in', privacy: 'Solo el workspace autorizado' },
10
+ { id: 'github', label: 'GitHub', purpose: 'Repositorios, issues, pull requests y código', transport: 'stdio', activation: 'oauth-opt-in', privacy: 'Requiere token u OAuth del usuario' },
11
+ { id: 'linear', label: 'Linear', purpose: 'Issues, roadmap y trabajo del equipo', transport: 'connector', activation: 'oauth-opt-in', privacy: 'Requiere acceso autorizado del workspace' },
12
+ { id: 'gmail', label: 'Gmail', purpose: 'Correo, búsquedas y mensajes relevantes', transport: 'connector', activation: 'oauth-opt-in', privacy: 'Requiere OAuth explícito del usuario' },
13
+ { id: 'google-drive', label: 'Google Drive', purpose: 'Documentos y archivos compartidos', transport: 'connector', activation: 'oauth-opt-in', privacy: 'Requiere acceso autorizado a la cuenta' },
14
+ { id: 'notion', label: 'Notion', purpose: 'Páginas, bases de datos y conocimiento', transport: 'connector', activation: 'oauth-opt-in', privacy: 'Requiere integración autorizada' },
15
+ { id: 'chatgpt', label: 'ChatGPT', purpose: 'Acceso a tareas y contexto de chat del usuario', transport: 'connector', activation: 'oauth-opt-in', privacy: 'Requiere conexión autorizada del usuario' },
16
+ { id: 'claude-desktop', label: 'Claude Desktop', purpose: 'Contexto y trabajo en la app desktop de Claude', transport: 'stdio', activation: 'local-opt-in', privacy: 'Solo los recursos autorizados por el usuario' },
17
+ { id: 'word', label: 'Microsoft Word', purpose: 'Documentos y edición de texto', transport: 'stdio', activation: 'local-opt-in', privacy: 'Solo archivos autorizados' },
18
+ { id: 'excel', label: 'Microsoft Excel', purpose: 'Hojas de cálculo, análisis y tabulación', transport: 'stdio', activation: 'local-opt-in', privacy: 'Solo archivos autorizados' },
19
+ { id: 'powerpoint', label: 'Microsoft PowerPoint', purpose: 'Presentaciones y material visual', transport: 'stdio', activation: 'local-opt-in', privacy: 'Solo archivos autorizados' },
20
+ { id: 'antigravity', label: 'Antigravity', purpose: 'Workspace y flujos de automatización del usuario', transport: 'connector', activation: 'oauth-opt-in', privacy: 'Requiere conexión explícita del usuario' },
12
21
  ];
package/dist/index.js CHANGED
@@ -1,4 +1,7 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  // ═══════════════════════════════════════════════════════════════
4
7
  // Cross-Platform Terminal & Stream Encoding Setup
@@ -31,6 +34,7 @@ const agent_client_1 = require("./client/agent-client");
31
34
  const apikey_1 = require("./commands/apikey");
32
35
  const colors_1 = require("./ui/colors");
33
36
  const banner_1 = require("./ui/banner");
37
+ const package_json_1 = __importDefault(require("../package.json"));
34
38
  async function readStdin() {
35
39
  if (process.stdin.isTTY) {
36
40
  return '';
@@ -70,7 +74,7 @@ const program = new commander_1.Command();
70
74
  program
71
75
  .name('helix')
72
76
  .description('HELIX - Interactive Terminal CLI for Windows AI Desktop Agent')
73
- .version('0.2.14')
77
+ .version(package_json_1.default.version)
74
78
  .option('-u, --ws-url <url>', 'URL del servidor WebSocket del agente', (0, env_1.getWsUrl)())
75
79
  .option('-t, --token <token>', 'Token de autenticación WebSocket de HELIX', (0, env_1.getWsToken)())
76
80
  .option('--plain', 'Desactivar formato enriquecido de Markdown y mostrar texto plano sin asteriscos')