helix-assistant-cli 0.2.1 → 0.2.7

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/dist/index.js CHANGED
@@ -1,5 +1,29 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ // ═══════════════════════════════════════════════════════════════
4
+ // Cross-Platform Terminal & Stream Encoding Setup
5
+ // Compatible with: Windows CMD (CP850/437/1252/65001), PowerShell,
6
+ // Linux terminal (xterm/kitty/gnome), and Bash (MSYS2/Git Bash/WSL).
7
+ // ═══════════════════════════════════════════════════════════════
8
+ if (process.stdin.setEncoding) {
9
+ process.stdin.setEncoding('utf-8');
10
+ }
11
+ if (process.stdout.setDefaultEncoding) {
12
+ process.stdout.setDefaultEncoding('utf-8');
13
+ }
14
+ if (process.stderr.setDefaultEncoding) {
15
+ process.stderr.setDefaultEncoding('utf-8');
16
+ }
17
+ // On Windows, force console code page to 65001 (UTF-8)
18
+ if (process.platform === 'win32') {
19
+ try {
20
+ const { execSync } = require('child_process');
21
+ execSync('chcp 65001 >nul 2>&1', { stdio: 'ignore' });
22
+ }
23
+ catch {
24
+ // Silently continue if chcp is unavailable
25
+ }
26
+ }
3
27
  const commander_1 = require("commander");
4
28
  const repl_1 = require("./ui/repl");
5
29
  const env_1 = require("./config/env");
@@ -7,18 +31,55 @@ const agent_client_1 = require("./client/agent-client");
7
31
  const apikey_1 = require("./commands/apikey");
8
32
  const colors_1 = require("./ui/colors");
9
33
  const banner_1 = require("./ui/banner");
34
+ async function readStdin() {
35
+ if (process.stdin.isTTY) {
36
+ return '';
37
+ }
38
+ return new Promise((resolve) => {
39
+ let input = '';
40
+ const onData = (chunk) => {
41
+ input += chunk.toString();
42
+ };
43
+ process.stdin.on('data', onData);
44
+ process.stdin.once('end', () => {
45
+ cleanup();
46
+ if (input.charCodeAt(0) === 0xfeff)
47
+ input = input.slice(1);
48
+ resolve(input.trim());
49
+ });
50
+ process.stdin.once('error', () => {
51
+ cleanup();
52
+ resolve('');
53
+ });
54
+ // If no data arrives and stream is not ended within 300ms, abort to prevent hanging
55
+ const timer = setTimeout(() => {
56
+ cleanup();
57
+ if (input.charCodeAt(0) === 0xfeff)
58
+ input = input.slice(1);
59
+ resolve(input.trim());
60
+ }, 300);
61
+ function cleanup() {
62
+ clearTimeout(timer);
63
+ process.stdin.removeListener('data', onData);
64
+ process.stdin.pause();
65
+ }
66
+ process.stdin.resume();
67
+ });
68
+ }
10
69
  const program = new commander_1.Command();
11
70
  program
12
71
  .name('helix')
13
72
  .description('HELIX - Interactive Terminal CLI for Windows AI Desktop Agent')
14
- .version('0.1.0')
73
+ .version('0.2.7')
15
74
  .option('-u, --ws-url <url>', 'URL del servidor WebSocket del agente', (0, env_1.getWsUrl)())
16
75
  .option('-t, --token <token>', 'Token de autenticación WebSocket de HELIX', (0, env_1.getWsToken)())
76
+ .option('--plain', 'Desactivar formato enriquecido de Markdown y mostrar texto plano sin asteriscos')
17
77
  .action(async (options) => {
18
78
  // Default action: start interactive chat REPL
19
79
  const repl = new repl_1.HelixRepl({
20
80
  wsUrl: options.wsUrl || (0, env_1.getWsUrl)(),
21
81
  wsToken: options.token || (0, env_1.getWsToken)(),
82
+ plain: Boolean(options.plain),
22
83
  });
23
84
  await repl.start();
24
85
  });
@@ -30,19 +91,29 @@ program
30
91
  const repl = new repl_1.HelixRepl({
31
92
  wsUrl: opts.wsUrl || (0, env_1.getWsUrl)(),
32
93
  wsToken: opts.token || (0, env_1.getWsToken)(),
94
+ plain: Boolean(opts.plain),
33
95
  });
34
96
  await repl.start();
35
97
  });
36
98
  program
37
- .command('run <prompt>')
38
- .description('Ejecutar un prompt directamente y esperar la respuesta')
39
- .action(async (promptText) => {
99
+ .command('run [prompt...]')
100
+ .description('Ejecutar un prompt directamente y esperar la respuesta (soporta pipes: cat file | helix run)')
101
+ .action(async (promptParts) => {
102
+ let promptText = (promptParts || []).join(' ').trim();
103
+ if (!promptText || promptText === '-') {
104
+ promptText = await readStdin();
105
+ }
106
+ if (!promptText) {
107
+ console.error((0, colors_1.RED)('Error: Debes proporcionar un prompt como argumento o a través de stdin (ej: echo "hola" | helix run).'));
108
+ process.exit(1);
109
+ }
40
110
  const opts = program.opts();
41
111
  const client = new agent_client_1.AgentClient({
42
112
  wsUrl: opts.wsUrl || (0, env_1.getWsUrl)(),
43
113
  wsToken: opts.token || (0, env_1.getWsToken)(),
44
114
  autoReconnect: false,
45
115
  });
116
+ console.log((0, colors_1.DIM)(`Prompt: ${promptText}\n`));
46
117
  console.log((0, colors_1.CYAN)(`Conectando con HELIX en ${client.url}...`));
47
118
  try {
48
119
  await client.connect();
@@ -51,7 +122,6 @@ program
51
122
  console.error((0, colors_1.RED)(`No se pudo conectar al agente HELIX: ${err.message}`));
52
123
  process.exit(1);
53
124
  }
54
- console.log((0, colors_1.DIM)(`Prompt: ${promptText}\n`));
55
125
  const convId = `cli_run_${Date.now()}`;
56
126
  client.on('react_step', (step) => {
57
127
  process.stdout.write(`\r${(0, colors_1.DIM)(`[ReAct Step ${step?.iteration || 1}...]`)}`);
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Resolves the only user-controlled directory used by the CLI skill catalog.
3
+ * HELIX_SKILLS_DIR is explicit; otherwise it follows the agent's settings path.
4
+ */
5
+ export declare function getUserSkillsDirectory(): string;
6
+ /**
7
+ * Returns names only. The file contents are deliberately never read: skill
8
+ * instructions can contain secrets and are not needed for autocomplete.
9
+ * Discovery is shallow and rejects symlinked entries to avoid following paths
10
+ * outside the user's skills directory.
11
+ */
12
+ export declare function discoverUserSkillNames(skillsDirectory?: string): string[];
@@ -0,0 +1,52 @@
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.getUserSkillsDirectory = getUserSkillsDirectory;
7
+ exports.discoverUserSkillNames = discoverUserSkillNames;
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const env_1 = require("../config/env");
11
+ const SKILL_NAME = /^[a-z0-9][a-z0-9_-]{0,63}$/;
12
+ /**
13
+ * Resolves the only user-controlled directory used by the CLI skill catalog.
14
+ * HELIX_SKILLS_DIR is explicit; otherwise it follows the agent's settings path.
15
+ */
16
+ function getUserSkillsDirectory() {
17
+ const configured = process.env.HELIX_SKILLS_DIR?.trim();
18
+ if (configured)
19
+ return path_1.default.resolve(configured);
20
+ return path_1.default.join(path_1.default.dirname((0, env_1.getSettingsFilePath)()), 'skills');
21
+ }
22
+ /**
23
+ * Returns names only. The file contents are deliberately never read: skill
24
+ * instructions can contain secrets and are not needed for autocomplete.
25
+ * Discovery is shallow and rejects symlinked entries to avoid following paths
26
+ * outside the user's skills directory.
27
+ */
28
+ function discoverUserSkillNames(skillsDirectory = getUserSkillsDirectory()) {
29
+ const root = path_1.default.resolve(skillsDirectory);
30
+ let entries;
31
+ try {
32
+ entries = fs_1.default.readdirSync(root, { withFileTypes: true });
33
+ }
34
+ catch {
35
+ return [];
36
+ }
37
+ const names = [];
38
+ for (const entry of entries) {
39
+ if (!entry.isDirectory() || entry.isSymbolicLink() || !SKILL_NAME.test(entry.name))
40
+ continue;
41
+ const skillFile = path_1.default.join(root, entry.name, 'SKILL.md');
42
+ try {
43
+ const file = fs_1.default.lstatSync(skillFile);
44
+ if (file.isFile() && !file.isSymbolicLink())
45
+ names.push(entry.name);
46
+ }
47
+ catch {
48
+ // Ignore incomplete, inaccessible, or malformed user entries.
49
+ }
50
+ }
51
+ return names.sort((a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' }));
52
+ }
@@ -8,8 +8,12 @@ export declare const SLASH_COMMANDS: CommandDefinition[];
8
8
  export declare class Autocompleter {
9
9
  private client?;
10
10
  private skills;
11
+ private remoteSkills;
12
+ private localSkills;
11
13
  private mcpTools;
12
- constructor(client?: AgentClient | undefined);
14
+ private readonly userSkillsDirectory;
15
+ constructor(client?: AgentClient | undefined, userSkillsDirectory?: string);
16
+ private refreshLocalSkills;
13
17
  refreshSuggestions(): Promise<void>;
14
18
  getSkills(): string[];
15
19
  getMcpTools(): string[];
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.Autocompleter = exports.SLASH_COMMANDS = void 0;
7
7
  const chalk_1 = __importDefault(require("chalk"));
8
8
  const colors_1 = require("./colors");
9
+ const user_skill_discovery_1 = require("../skills/user-skill-discovery");
9
10
  exports.SLASH_COMMANDS = [
10
11
  { command: '/create-skill', description: 'Crear una nueva skill interactiva para HELIX' },
11
12
  { command: '/skill', description: 'Listar o inspeccionar skills registradas', usage: '/skill [nombre]' },
@@ -26,9 +27,20 @@ exports.SLASH_COMMANDS = [
26
27
  class Autocompleter {
27
28
  client;
28
29
  skills = [];
30
+ remoteSkills = [];
31
+ localSkills = [];
29
32
  mcpTools = [];
30
- constructor(client) {
33
+ userSkillsDirectory;
34
+ constructor(client, userSkillsDirectory = (0, user_skill_discovery_1.getUserSkillsDirectory)()) {
31
35
  this.client = client;
36
+ this.userSkillsDirectory = userSkillsDirectory;
37
+ this.refreshLocalSkills();
38
+ }
39
+ refreshLocalSkills() {
40
+ this.localSkills = (0, user_skill_discovery_1.discoverUserSkillNames)(this.userSkillsDirectory);
41
+ const baseSkills = this.client ? this.remoteSkills : this.skills;
42
+ this.skills = [...new Set([...baseSkills, ...this.localSkills])]
43
+ .sort((a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' }));
32
44
  }
33
45
  async refreshSuggestions() {
34
46
  if (!this.client || !this.client.connected)
@@ -36,7 +48,7 @@ class Autocompleter {
36
48
  try {
37
49
  const skills = await this.client.getSkills();
38
50
  if (skills) {
39
- this.skills = skills.map((s) => s.name);
51
+ this.remoteSkills = skills.map((s) => s.name);
40
52
  }
41
53
  }
42
54
  catch {
@@ -60,6 +72,7 @@ class Autocompleter {
60
72
  catch {
61
73
  // Ignored
62
74
  }
75
+ this.refreshLocalSkills();
63
76
  }
64
77
  getSkills() {
65
78
  return this.skills;
@@ -72,6 +85,9 @@ class Autocompleter {
72
85
  * (line: string) => [string[], string]
73
86
  */
74
87
  completer(line) {
88
+ // A skill may be created by another HELIX surface while the REPL is open.
89
+ // Keep Tab completion current without exposing the skill file contents.
90
+ this.refreshLocalSkills();
75
91
  const trimmed = line.trimStart();
76
92
  // 1. Skill sub-arguments for /skill <name>
77
93
  if (trimmed.startsWith('/skill ')) {
@@ -7,18 +7,9 @@ export interface BannerStatusOptions {
7
7
  activeTasksCount?: number;
8
8
  }
9
9
  export declare function getAsciiLogo(terminalWidth?: number): string;
10
- /**
11
- * Compact, badge-style status bar.
12
- */
13
10
  export declare function getStatusBar(options: BannerStatusOptions): string;
14
- /**
15
- * Renders the persistent header. Clears screen only if clearScreen is explicitly true.
16
- */
17
11
  export declare function renderHeader(options: BannerStatusOptions, clearScreen?: boolean): void;
18
- /**
19
- * Voice-capture indicator badge (for inline use in prompts).
20
- */
21
12
  export declare function getVoiceIndicator(isCapturing: boolean, voiceEnabled: boolean): string;
22
- export declare const HELIX_LOGO_ASCII: number[][][];
23
- export declare const HELIX_BIG_ART: number[][][];
24
- export declare const HELIX_HARD_BLOCKS: number[][][];
13
+ export declare const HELIX_LOGO_ASCII: typeof getAsciiLogo;
14
+ export declare const HELIX_BIG_ART: typeof getAsciiLogo;
15
+ export declare const HELIX_HARD_BLOCKS: typeof getAsciiLogo;
package/dist/ui/banner.js CHANGED
@@ -9,126 +9,27 @@ exports.getStatusBar = getStatusBar;
9
9
  exports.renderHeader = renderHeader;
10
10
  exports.getVoiceIndicator = getVoiceIndicator;
11
11
  const chalk_1 = __importDefault(require("chalk"));
12
- // ═══════════════════════════════════════════════════════════════
13
- // HELIX — Pixel-art dot-matrix logo.
14
- // Each letter is drawn on an 8-row × 5-col grid.
15
- // "On" pixels → "██" block, "off" pixels → " " (space).
16
- // Gradient runs top → bottom:
17
- // rows 0-1 : bright white/cream
18
- // rows 2-3 : bright yellow
19
- // rows 4-5 : orange
20
- // rows 6-7 : deep red
21
- // Matches the pixel-art HELIX image with yellow→orange→red gradient.
22
- // ═══════════════════════════════════════════════════════════════
23
- // ── Letter bitmaps — 1=on, 0=off, 8 rows × 5 cols each ────────
24
- const H = [
25
- [1, 0, 0, 0, 1],
26
- [1, 0, 0, 0, 1],
27
- [1, 1, 1, 1, 1],
28
- [1, 1, 1, 1, 1],
29
- [1, 1, 1, 1, 1],
30
- [1, 0, 0, 0, 1],
31
- [1, 0, 0, 0, 1],
32
- [1, 0, 0, 0, 1],
33
- ];
34
- const E = [
35
- [1, 1, 1, 1, 1],
36
- [1, 0, 0, 0, 0],
37
- [1, 1, 1, 1, 0],
38
- [1, 1, 1, 1, 0],
39
- [1, 1, 1, 1, 0],
40
- [1, 0, 0, 0, 0],
41
- [1, 0, 0, 0, 0],
42
- [1, 1, 1, 1, 1],
43
- ];
44
- const L = [
45
- [1, 0, 0, 0, 0],
46
- [1, 0, 0, 0, 0],
47
- [1, 0, 0, 0, 0],
48
- [1, 0, 0, 0, 0],
49
- [1, 0, 0, 0, 0],
50
- [1, 0, 0, 0, 0],
51
- [1, 0, 0, 0, 0],
52
- [1, 1, 1, 1, 1],
53
- ];
54
- const I = [
55
- [1, 1, 1, 1, 1],
56
- [0, 0, 1, 0, 0],
57
- [0, 0, 1, 0, 0],
58
- [0, 0, 1, 0, 0],
59
- [0, 0, 1, 0, 0],
60
- [0, 0, 1, 0, 0],
61
- [0, 0, 1, 0, 0],
62
- [1, 1, 1, 1, 1],
63
- ];
64
- const X = [
65
- [1, 0, 0, 0, 1],
66
- [1, 1, 0, 1, 1],
67
- [0, 1, 1, 1, 0],
68
- [0, 0, 1, 0, 0],
69
- [0, 0, 1, 0, 0],
70
- [0, 1, 1, 1, 0],
71
- [1, 1, 0, 1, 1],
72
- [1, 0, 0, 0, 1],
73
- ];
74
- const LETTERS = [H, E, L, I, X];
75
- const LETTER_GAP = 1; // 1 empty col between letters
76
- const ROWS = 8;
77
- const COLS_PER_LETTER = 5;
78
- // ── Top-to-bottom gradient — cream/white → yellow → orange → red ──
79
- // Row 0 = top, row 7 = bottom
80
- const ROW_COLORS = [
81
- '#FFF9E0', // 0 — near-white cream
82
- '#FFE84D', // 1 — bright yellow
83
- '#FFD200', // 2 — gold yellow
84
- '#FFB300', // 3 — amber
85
- '#FF8C00', // 4 — orange
86
- '#FF6600', // 5 — deep orange
87
- '#E63000', // 6 — red-orange
88
- '#CC1A00', // 7 — deep red
89
- ];
90
- const BLOCK_ON = '██'; // filled square block
91
- const BLOCK_OFF = ' '; // empty (black background)
92
- // ───────────────────────────────────────────────────────────────
93
- // Public API
94
- // ───────────────────────────────────────────────────────────────
12
+ // Exact bash logo with ANSI RGB codes - DO NOT MODIFY
95
13
  function getAsciiLogo(terminalWidth = 80) {
96
- // total character width: each cell = 2 chars
97
- const totalCols = LETTERS.length * COLS_PER_LETTER + (LETTERS.length - 1) * LETTER_GAP;
98
- const totalChars = totalCols * 2;
99
- const leftPad = Math.max(0, Math.floor((terminalWidth - totalChars) / 2));
100
- const pad = ' '.repeat(leftPad);
101
- const lines = [];
102
- for (let row = 0; row < ROWS; row++) {
103
- let line = pad;
104
- for (let li = 0; li < LETTERS.length; li++) {
105
- const letter = LETTERS[li];
106
- for (let col = 0; col < COLS_PER_LETTER; col++) {
107
- if (letter[row][col]) {
108
- line += chalk_1.default.hex(ROW_COLORS[row]).bold(BLOCK_ON);
109
- }
110
- else {
111
- line += BLOCK_OFF;
112
- }
113
- }
114
- // gap between letters
115
- if (li < LETTERS.length - 1) {
116
- line += BLOCK_OFF.repeat(LETTER_GAP);
117
- }
118
- }
119
- lines.push(line);
120
- }
121
- return lines.join('\n');
14
+ const lines = [
15
+ ' \x1b[48;2;255;227;143m\x1b[38;2;255;255;255m▀\x1b[49m\x1b[38;2;255;227;143m█ \x1b[48;2;255;227;143m\x1b[38;2;255;255;255m▀\x1b[49m\x1b[38;2;255;227;143m█\x1b[0m',
16
+ ' \x1b[38;2;255;210;77m██ ██ \x1b[38;2;255;255;255m██████ ██ ██ ██ ██\x1b[0m',
17
+ '\x1b[38;2;255;215;119m██ \x1b[38;2;255;191;31m██ ██ \x1b[38;2;255;215;119m██ \x1b[38;2;246;246;246m██ ██ ██ ██ ██\x1b[0m',
18
+ '\x1b[38;2;255;191;36m██ \x1b[38;2;255;171;10m███\x1b[48;2;255;171;10m\x1b[38;2;255;255;255m▀\x1b[49m\x1b[38;2;255;171;10m███ \x1b[38;2;255;191;36m██ \x1b[38;2;236;236;236m████ ██ ██ ██\x1b[0m',
19
+ '\x1b[38;2;255;160;18m██ \x1b[38;2;255;145;20m███████ \x1b[38;2;255;160;18m██ \x1b[38;2;226;226;226m██ ██ ██ ██ ██\x1b[0m',
20
+ '\x1b[38;2;255;127;26m██ \x1b[38;2;255;122;26m██ ██ \x1b[38;2;255;127;26m██ \x1b[38;2;216;216;216m██████ ██████ ██ ██ ██\x1b[0m',
21
+ '\x1b[38;2;240;98;15m██ \x1b[38;2;255;95;18m██ ██ \x1b[38;2;240;98;15m██\x1b[0m',
22
+ ' \x1b[38;2;220;74;12m██ ██\x1b[0m'
23
+ ];
24
+ const visibleWidth = 62;
25
+ const leftPad = Math.max(0, Math.floor((terminalWidth - visibleWidth) / 2));
26
+ const padding = ' '.repeat(leftPad);
27
+ return lines.map(line => padding + line).join('\n');
122
28
  }
123
- /**
124
- * Compact, badge-style status bar.
125
- */
126
29
  function getStatusBar(options) {
127
- // ── Connection ──
128
30
  const conn = options.connected
129
31
  ? `${chalk_1.default.green('●')} ${chalk_1.default.greenBright.bold('CONECTADO')} ${chalk_1.default.gray(options.wsUrl)}`
130
32
  : `${chalk_1.default.red('●')} ${chalk_1.default.redBright.bold('DESCONECTADO')} ${chalk_1.default.gray(options.wsUrl)}`;
131
- // ── Microphone ──
132
33
  let mic;
133
34
  if (options.isCapturingVoice) {
134
35
  mic = chalk_1.default.bgRed.white.bold(' 🎙️ CAPTURANDO ');
@@ -139,42 +40,26 @@ function getStatusBar(options) {
139
40
  else {
140
41
  mic = `${chalk_1.default.red('●')} ${chalk_1.default.gray('Mic Off')}`;
141
42
  }
142
- // ── Model ──
143
43
  const model = options.currentModel
144
44
  ? `${chalk_1.default.hex('#FF9F1C')('⚡')} ${chalk_1.default.white(options.currentModel)}`
145
45
  : `${chalk_1.default.hex('#FF9F1C')('⚡')} ${chalk_1.default.white('Local ReAct')}`;
146
- // ── Active tasks ──
147
46
  const tasks = (options.activeTasksCount ?? 0) > 0
148
47
  ? ` ${chalk_1.default.yellow('⏳')} ${chalk_1.default.yellowBright(String(options.activeTasksCount))}`
149
48
  : '';
150
49
  const sep = chalk_1.default.gray(' │ ');
151
50
  return ` ${conn}${sep}${mic}${sep}${model}${tasks}`;
152
51
  }
153
- /**
154
- * Renders the persistent header. Clears screen only if clearScreen is explicitly true.
155
- */
156
52
  function renderHeader(options, clearScreen = false) {
157
53
  if (clearScreen) {
158
- // Clear screen + move cursor to top-left
159
54
  process.stdout.write('\x1bc');
160
55
  }
161
56
  const width = process.stdout.columns || 80;
162
- // ── Logo ──
163
57
  const logo = getAsciiLogo(width);
164
- // ── Tagline ──
165
58
  const tagline = 'Persistent AI Desktop Agent';
166
59
  const tagPad = Math.max(0, Math.floor((width - tagline.length - 8) / 2));
167
- const subtitle = ' '.repeat(tagPad) +
168
- chalk_1.default.gray('╶───') +
169
- ' ' +
170
- chalk_1.default.hex('#FF9F1C')(tagline) +
171
- ' ' +
172
- chalk_1.default.gray('───╴');
173
- // ── Status bar ──
60
+ const subtitle = ' '.repeat(tagPad) + chalk_1.default.gray('╶───') + ' ' + chalk_1.default.hex('#FF9F1C')(tagline) + ' ' + chalk_1.default.gray('───╴');
174
61
  const statusBar = getStatusBar(options);
175
- // ── Visual lines ──
176
62
  const thinLine = chalk_1.default.gray('─'.repeat(width));
177
- // ── Render ──
178
63
  console.log('');
179
64
  console.log(logo);
180
65
  console.log('');
@@ -183,25 +68,17 @@ function renderHeader(options, clearScreen = false) {
183
68
  console.log(thinLine);
184
69
  console.log(statusBar);
185
70
  console.log(thinLine);
186
- console.log(` ${chalk_1.default.hex('#FF8C00')('@skill')} ${chalk_1.default.gray('Skills')} ${chalk_1.default.gray('│')} ` +
187
- `${chalk_1.default.cyan('#mcp')} ${chalk_1.default.gray('Herramientas')} ${chalk_1.default.gray('│')} ` +
188
- `${chalk_1.default.magenta('/help')} ${chalk_1.default.gray('Comandos')}`);
71
+ console.log(` ${chalk_1.default.hex('#FF8C00')('@skill')} ${chalk_1.default.gray('Skills')} ${chalk_1.default.gray('│')} ${chalk_1.default.cyan('#mcp')} ${chalk_1.default.gray('Herramientas')} ${chalk_1.default.gray('│')} ${chalk_1.default.magenta('/help')} ${chalk_1.default.gray('Comandos')}`);
189
72
  console.log(thinLine);
190
73
  console.log('');
191
74
  }
192
- /**
193
- * Voice-capture indicator badge (for inline use in prompts).
194
- */
195
75
  function getVoiceIndicator(isCapturing, voiceEnabled) {
196
- if (isCapturing) {
76
+ if (isCapturing)
197
77
  return chalk_1.default.bgRed.white.bold(' 🎙️ VOZ DETECTADA [HEY HELIX] 🔊 ');
198
- }
199
- if (voiceEnabled) {
78
+ if (voiceEnabled)
200
79
  return chalk_1.default.green('🟢 [Mic Activo]');
201
- }
202
80
  return chalk_1.default.red('🔴 [Mic Inactivo]');
203
81
  }
204
- // Legacy named exports kept for compatibility with other modules
205
- exports.HELIX_LOGO_ASCII = LETTERS;
206
- exports.HELIX_BIG_ART = LETTERS;
207
- exports.HELIX_HARD_BLOCKS = LETTERS;
82
+ exports.HELIX_LOGO_ASCII = getAsciiLogo;
83
+ exports.HELIX_BIG_ART = getAsciiLogo;
84
+ exports.HELIX_HARD_BLOCKS = getAsciiLogo;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Formats Markdown text for optimal display in terminal emulators,
3
+ * resolving visual noise from unparsed asterisks (*, **) and headers.
4
+ */
5
+ export declare function formatTerminalMarkdown(content: string, plain?: boolean): string;
@@ -0,0 +1,49 @@
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.formatTerminalMarkdown = formatTerminalMarkdown;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ /**
9
+ * Formats Markdown text for optimal display in terminal emulators,
10
+ * resolving visual noise from unparsed asterisks (*, **) and headers.
11
+ */
12
+ function formatTerminalMarkdown(content, plain = false) {
13
+ if (!content)
14
+ return '';
15
+ const lines = content.split('\n');
16
+ const formattedLines = lines.map((line) => {
17
+ let formatted = line;
18
+ // Headers (#, ##, ###)
19
+ const headerMatch = formatted.match(/^(#{1,6})\s+(.*)$/);
20
+ if (headerMatch) {
21
+ const title = headerMatch[2].trim();
22
+ if (plain) {
23
+ return title.toUpperCase();
24
+ }
25
+ return chalk_1.default.hex('#FF9F1C').bold(title);
26
+ }
27
+ // Bullet lists (* item, - item)
28
+ if (/^\s*[*+-]\s+/.test(formatted)) {
29
+ formatted = formatted.replace(/^(\s*)[*+-]\s+/, (_m, indent) => {
30
+ return plain ? `${indent}- ` : `${indent}${chalk_1.default.hex('#FF8C00')('•')} `;
31
+ });
32
+ }
33
+ // Inline bold (**text** or __text__)
34
+ formatted = formatted.replace(/(\*\*|__)(.*?)\1/g, (_m, _delim, text) => {
35
+ return plain ? text : chalk_1.default.bold(text);
36
+ });
37
+ // Inline italics (*text* or _text_)
38
+ formatted = formatted.replace(/(?<!\*)\*([^*]+)\*(?!\*)|(?<!_)_([^_]+)_(?!_)/g, (_m, t1, t2) => {
39
+ const text = t1 || t2;
40
+ return plain ? text : chalk_1.default.whiteBright(text);
41
+ });
42
+ // Inline code (`code`)
43
+ formatted = formatted.replace(/`([^`]+)`/g, (_m, code) => {
44
+ return plain ? code : chalk_1.default.cyan(code);
45
+ });
46
+ return formatted;
47
+ });
48
+ return formattedLines.join('\n');
49
+ }
package/dist/ui/repl.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export interface ReplOptions {
2
2
  wsUrl: string;
3
3
  wsToken?: string;
4
+ plain?: boolean;
4
5
  }
5
6
  export declare class HelixRepl {
6
7
  private options;
@@ -15,6 +16,7 @@ export declare class HelixRepl {
15
16
  private activeTaskId;
16
17
  private activeTasksCount;
17
18
  private isProcessingPrompt;
19
+ private plain;
18
20
  constructor(options: ReplOptions);
19
21
  start(): Promise<void>;
20
22
  private _syncSettings;