vigthoria-cli 1.6.58 → 1.6.60

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,8 +12,8 @@ export declare class AuthCommand {
12
12
  private api;
13
13
  constructor(config: Config, logger: Logger);
14
14
  login(options: LoginOptions): Promise<void>;
15
- private loginWithCredentials;
16
- private loginWithTokenPrompt;
15
+ private askInput;
16
+ private doCredentialLogin;
17
17
  private loginWithToken;
18
18
  private loginWithBrowser;
19
19
  logout(): Promise<void>;
@@ -2,15 +2,100 @@
2
2
  /**
3
3
  * Auth Command - Authentication management
4
4
  */
5
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ var desc = Object.getOwnPropertyDescriptor(m, k);
8
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
+ desc = { enumerable: true, get: function() { return m[k]; } };
10
+ }
11
+ Object.defineProperty(o, k2, desc);
12
+ }) : (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ o[k2] = m[k];
15
+ }));
16
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
17
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
18
+ }) : function(o, v) {
19
+ o["default"] = v;
20
+ });
21
+ var __importStar = (this && this.__importStar) || (function () {
22
+ var ownKeys = function(o) {
23
+ ownKeys = Object.getOwnPropertyNames || function (o) {
24
+ var ar = [];
25
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
26
+ return ar;
27
+ };
28
+ return ownKeys(o);
29
+ };
30
+ return function (mod) {
31
+ if (mod && mod.__esModule) return mod;
32
+ var result = {};
33
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
34
+ __setModuleDefault(result, mod);
35
+ return result;
36
+ };
37
+ })();
5
38
  var __importDefault = (this && this.__importDefault) || function (mod) {
6
39
  return (mod && mod.__esModule) ? mod : { "default": mod };
7
40
  };
8
41
  Object.defineProperty(exports, "__esModule", { value: true });
9
42
  exports.AuthCommand = void 0;
10
43
  const chalk_1 = __importDefault(require("chalk"));
11
- const inquirer_1 = __importDefault(require("inquirer"));
44
+ const readline = __importStar(require("readline"));
12
45
  const logger_js_1 = require("../utils/logger.js");
13
46
  const api_js_1 = require("../utils/api.js");
47
+ /**
48
+ * Prompt helpers using Node's built-in readline.
49
+ * inquirer 9.x destroys the readline interface after each prompt() call
50
+ * which triggers ERR_USE_AFTER_CLOSE on Node 20 Windows TTY when a second
51
+ * prompt is attempted on the same process.stdin. Using raw readline avoids
52
+ * this entirely.
53
+ */
54
+ function ask(rl, question) {
55
+ return new Promise((resolve) => rl.question(question, resolve));
56
+ }
57
+ function askHidden(question) {
58
+ return new Promise((resolve, reject) => {
59
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
60
+ // Temporarily mute output to hide typed characters
61
+ const stdout = process.stdout;
62
+ let answer = '';
63
+ process.stdout.write(question);
64
+ if (process.stdin.isTTY) {
65
+ process.stdin.setRawMode(true);
66
+ }
67
+ process.stdin.resume();
68
+ const onData = (ch) => {
69
+ const c = ch.toString('utf8');
70
+ if (c === '\n' || c === '\r' || c === '\u0004') {
71
+ if (process.stdin.isTTY) {
72
+ process.stdin.setRawMode(false);
73
+ }
74
+ process.stdin.removeListener('data', onData);
75
+ stdout.write('\n');
76
+ rl.close();
77
+ resolve(answer);
78
+ }
79
+ else if (c === '\u007f' || c === '\b') {
80
+ // backspace
81
+ if (answer.length > 0) {
82
+ answer = answer.slice(0, -1);
83
+ stdout.write('\b \b');
84
+ }
85
+ }
86
+ else if (c === '\u0003') {
87
+ // Ctrl-C
88
+ rl.close();
89
+ process.exit(1);
90
+ }
91
+ else {
92
+ answer += c;
93
+ stdout.write('*');
94
+ }
95
+ };
96
+ process.stdin.on('data', onData);
97
+ });
98
+ }
14
99
  class AuthCommand {
15
100
  config;
16
101
  logger;
@@ -29,52 +114,70 @@ class AuthCommand {
29
114
  console.log();
30
115
  console.log(chalk_1.default.cyan(`${logger_js_1.CH.hDouble.repeat(3)} Vigthoria Login ${logger_js_1.CH.hDouble.repeat(3)}`));
31
116
  console.log();
32
- // Prompt for credentials
33
- const answers = await inquirer_1.default.prompt([
34
- {
35
- type: 'list',
36
- name: 'method',
37
- message: 'Choose login method:',
38
- choices: [
39
- { name: 'Email & Password', value: 'credentials' },
40
- { name: 'API Token', value: 'token' },
41
- { name: 'Browser Login', value: 'browser' },
42
- ],
43
- },
44
- ]);
45
- switch (answers.method) {
46
- case 'credentials':
47
- await this.loginWithCredentials();
48
- break;
49
- case 'token':
50
- await this.loginWithTokenPrompt();
51
- break;
52
- case 'browser':
53
- await this.loginWithBrowser();
54
- break;
117
+ // Use Node's built-in readline instead of inquirer.
118
+ // inquirer 9.x destroys and recreates readline interfaces per prompt
119
+ // which causes ERR_USE_AFTER_CLOSE on Node 20 Windows TTY.
120
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
121
+ try {
122
+ console.log(chalk_1.default.white(' 1) Email & Password'));
123
+ console.log(chalk_1.default.white(' 2) API Token'));
124
+ console.log(chalk_1.default.white(' 3) Browser Login'));
125
+ console.log();
126
+ const choice = (await ask(rl, chalk_1.default.cyan('? ') + 'Choose login method (1/2/3): ')).trim();
127
+ rl.close();
128
+ switch (choice) {
129
+ case '1':
130
+ case 'credentials': {
131
+ const email = await this.askInput('Email: ');
132
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
133
+ if (!emailRegex.test(email)) {
134
+ this.logger.error('Please enter a valid email');
135
+ return;
136
+ }
137
+ const password = await askHidden(chalk_1.default.cyan('? ') + 'Password: ');
138
+ if (password.length < 6) {
139
+ this.logger.error('Password must be at least 6 characters');
140
+ return;
141
+ }
142
+ await this.doCredentialLogin(email, password);
143
+ break;
144
+ }
145
+ case '2':
146
+ case 'token': {
147
+ const token = await askHidden(chalk_1.default.cyan('? ') + 'API Token: ');
148
+ if (!token) {
149
+ this.logger.error('Please enter your API token');
150
+ return;
151
+ }
152
+ await this.loginWithToken(token);
153
+ break;
154
+ }
155
+ case '3':
156
+ case 'browser':
157
+ await this.loginWithBrowser();
158
+ break;
159
+ default:
160
+ this.logger.error('Invalid choice. Please enter 1, 2, or 3.');
161
+ break;
162
+ }
163
+ }
164
+ catch (err) {
165
+ rl.close();
166
+ throw err;
55
167
  }
56
168
  }
57
- async loginWithCredentials() {
58
- const credentials = await inquirer_1.default.prompt([
59
- {
60
- type: 'input',
61
- name: 'email',
62
- message: 'Email:',
63
- validate: (input) => {
64
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
65
- return emailRegex.test(input) || 'Please enter a valid email';
66
- },
67
- },
68
- {
69
- type: 'password',
70
- name: 'password',
71
- message: 'Password:',
72
- mask: '*',
73
- validate: (input) => input.length >= 6 || 'Password must be at least 6 characters',
74
- },
75
- ]);
169
+ askInput(label) {
170
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
171
+ return new Promise((resolve) => {
172
+ rl.question(chalk_1.default.cyan('? ') + label, (answer) => {
173
+ rl.close();
174
+ resolve(answer.trim());
175
+ });
176
+ });
177
+ }
178
+ async doCredentialLogin(email, password) {
76
179
  const spinner = (0, logger_js_1.createSpinner)('Logging in...').start();
77
- const success = await this.api.login(credentials.email, credentials.password);
180
+ const success = await this.api.login(email, password);
78
181
  spinner.stop();
79
182
  if (success) {
80
183
  this.printLoginSuccess();
@@ -83,18 +186,6 @@ class AuthCommand {
83
186
  this.logger.error('Login failed. Please check your credentials.');
84
187
  }
85
188
  }
86
- async loginWithTokenPrompt() {
87
- const { token } = await inquirer_1.default.prompt([
88
- {
89
- type: 'password',
90
- name: 'token',
91
- message: 'API Token:',
92
- mask: '*',
93
- validate: (input) => input.length > 0 || 'Please enter your API token',
94
- },
95
- ]);
96
- await this.loginWithToken(token);
97
- }
98
189
  async loginWithToken(token) {
99
190
  const spinner = (0, logger_js_1.createSpinner)('Validating token...').start();
100
191
  const success = await this.api.loginWithToken(token);
@@ -117,14 +208,10 @@ class AuthCommand {
117
208
  console.log();
118
209
  }
119
210
  async logout() {
120
- const { confirm } = await inquirer_1.default.prompt([
121
- {
122
- type: 'confirm',
123
- name: 'confirm',
124
- message: 'Are you sure you want to logout?',
125
- default: false,
126
- },
127
- ]);
211
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
212
+ const answer = await ask(rl, chalk_1.default.cyan('? ') + 'Are you sure you want to logout? (y/N): ');
213
+ rl.close();
214
+ const confirm = /^y(es)?$/i.test(answer.trim());
128
215
  if (confirm) {
129
216
  this.config.clearAuth();
130
217
  this.logger.success('Logged out successfully');
@@ -74,6 +74,7 @@ export declare class ChatCommand {
74
74
  private v3IterationCount;
75
75
  private v3ToolCallCount;
76
76
  private v3LastActivity;
77
+ private v3StreamingStarted;
77
78
  private describeV3AgentTool;
78
79
  private updateV3AgentSpinner;
79
80
  private updateOperatorSpinner;
@@ -298,6 +298,7 @@ class ChatCommand {
298
298
  v3IterationCount = 0;
299
299
  v3ToolCallCount = 0;
300
300
  v3LastActivity = Date.now();
301
+ v3StreamingStarted = false;
301
302
  describeV3AgentTool(toolName) {
302
303
  const normalized = String(toolName || '').toLowerCase();
303
304
  if (/read|grep|search|list|find|glob/.test(normalized)) {
@@ -324,14 +325,24 @@ class ChatCommand {
324
325
  const toolDesc = this.describeV3AgentTool(event.tool || event.name || event.tool_name);
325
326
  const toolTarget = event.arguments?.path || event.arguments?.file_path || event.arguments?.pattern || '';
326
327
  const shortTarget = toolTarget ? ` → ${String(toolTarget).split('/').slice(-2).join('/')}` : '';
327
- spinner.text = chalk_1.default.cyan(`[${this.v3IterationCount}/${this.v3ToolCallCount}] `) + `${toolDesc}${shortTarget}`;
328
+ const stepLabel = chalk_1.default.cyan(` [${this.v3IterationCount}/${this.v3ToolCallCount}]`) + ` ${toolDesc}${shortTarget}`;
329
+ // Print each tool call as a persistent log line so the user sees progress
330
+ if (spinner.isSpinning)
331
+ spinner.stop();
332
+ process.stderr.write(stepLabel + '\n');
333
+ spinner.start();
334
+ spinner.text = `Running ${toolDesc}...`;
328
335
  return;
329
336
  }
330
337
  if (event.type === 'tool_result') {
331
338
  const success = event.success !== false;
332
339
  const toolName = event.name || event.tool || '';
333
- const indicator = success ? chalk_1.default.green('✓') : chalk_1.default.red('✗');
334
- spinner.text = `${indicator} ${toolName} complete — next step...`;
340
+ const indicator = success ? chalk_1.default.green(' ✓') : chalk_1.default.red(' ✗');
341
+ if (spinner.isSpinning)
342
+ spinner.stop();
343
+ process.stderr.write(`${indicator} ${toolName}\n`);
344
+ spinner.start();
345
+ spinner.text = 'Next step...';
335
346
  return;
336
347
  }
337
348
  if (event.type === 'thinking') {
@@ -339,43 +350,83 @@ class ChatCommand {
339
350
  const iterText = event.content || '';
340
351
  const iterMatch = iterText.match(/Iteration (\d+)/i);
341
352
  const iterNum = iterMatch ? iterMatch[1] : String(this.v3IterationCount);
342
- spinner.text = chalk_1.default.cyan(`[Iteration ${iterNum}] `) + 'Analyzing...';
353
+ if (spinner.isSpinning)
354
+ spinner.stop();
355
+ process.stderr.write(chalk_1.default.cyan(`\n── Iteration ${iterNum} ──\n`));
356
+ spinner.start();
357
+ spinner.text = 'Analyzing...';
343
358
  return;
344
359
  }
345
- if (event.type === 'message') {
346
- const preview = String(event.content || '').slice(0, 80).replace(/\n/g, ' ');
347
- spinner.text = chalk_1.default.cyan('[Response] ') + (preview || 'Writing response...');
360
+ if (event.type === 'message' || event.type === 'assistant' || event.type === 'text' || event.type === 'content_block_delta') {
361
+ const text = event.type === 'content_block_delta'
362
+ ? (event.delta?.text || '')
363
+ : (event.content || '');
364
+ if (text) {
365
+ if (!this.v3StreamingStarted) {
366
+ this.v3StreamingStarted = true;
367
+ spinner.stop();
368
+ if (!this.directPromptMode) {
369
+ console.log();
370
+ }
371
+ }
372
+ else {
373
+ spinner.stop();
374
+ }
375
+ process.stdout.write(text);
376
+ }
377
+ else {
378
+ spinner.text = chalk_1.default.cyan('[Response] ') + 'Writing response...';
379
+ }
348
380
  return;
349
381
  }
350
382
  if (event.type === 'complete') {
351
383
  const elapsed = event.elapsed || '';
352
384
  const iters = event.iterations || this.v3IterationCount;
353
385
  const tools = event.tool_calls || this.v3ToolCallCount;
354
- spinner.text = chalk_1.default.green('✓ ') + `Complete — ${iters} iterations, ${tools} tool calls${elapsed ? `, ${elapsed}` : ''}`;
386
+ if (spinner.isSpinning)
387
+ spinner.stop();
388
+ process.stderr.write(chalk_1.default.green(`\n✓ Complete`) + ` — ${iters} iterations, ${tools} tool calls${elapsed ? `, ${elapsed}` : ''}\n`);
355
389
  return;
356
390
  }
357
391
  if (event.type === 'plan') {
358
392
  const planKind = event.plan?.task_kind || event.task_kind || '';
359
- spinner.text = chalk_1.default.cyan('[Plan] ') + `Task: ${planKind || 'analyzing'}...`;
393
+ if (spinner.isSpinning)
394
+ spinner.stop();
395
+ process.stderr.write(chalk_1.default.cyan(` [Plan] `) + `Task: ${planKind || 'analyzing'}...\n`);
396
+ spinner.start();
397
+ spinner.text = 'Planning...';
360
398
  return;
361
399
  }
362
400
  if (event.type === 'error') {
363
401
  if (event.checkpointed) {
364
- spinner.text = chalk_1.default.yellow('[Checkpoint] ') + 'Budget reached — auto-continuing...';
402
+ if (spinner.isSpinning)
403
+ spinner.stop();
404
+ process.stderr.write(chalk_1.default.yellow(' [Checkpoint] ') + 'Budget reached — auto-continuing...\n');
405
+ spinner.start();
365
406
  }
366
407
  else {
367
- spinner.text = chalk_1.default.red('[Error] ') + (event.message || 'Agent error');
408
+ if (spinner.isSpinning)
409
+ spinner.stop();
410
+ process.stderr.write(chalk_1.default.red(' [Error] ') + (event.message || 'Agent error') + '\n');
368
411
  }
369
412
  return;
370
413
  }
371
414
  if (event.type === 'context') {
372
- spinner.text = chalk_1.default.cyan('[Context] ') + 'Workspace bound...';
415
+ if (spinner.isSpinning)
416
+ spinner.stop();
417
+ process.stderr.write(chalk_1.default.cyan(' [Context] ') + 'Workspace bound\n');
418
+ spinner.start();
419
+ spinner.text = 'Starting agent...';
373
420
  return;
374
421
  }
375
422
  if (event.type === 'start') {
376
423
  this.v3IterationCount = 0;
377
424
  this.v3ToolCallCount = 0;
378
- spinner.text = chalk_1.default.cyan('[Start] ') + 'Agent initialized...';
425
+ if (spinner.isSpinning)
426
+ spinner.stop();
427
+ process.stderr.write(chalk_1.default.cyan(' [Start] ') + 'Agent initialized\n');
428
+ spinner.start();
429
+ spinner.text = 'Working...';
379
430
  }
380
431
  }
381
432
  updateOperatorSpinner(spinner, event) {
@@ -383,24 +434,44 @@ class ChatCommand {
383
434
  return;
384
435
  }
385
436
  if (event.type === 'started') {
386
- spinner.text = 'Starting BMAD workflow...';
437
+ if (spinner.isSpinning)
438
+ spinner.stop();
439
+ process.stderr.write(chalk_1.default.cyan(' [Operator] ') + 'Starting BMAD workflow...\n');
440
+ spinner.start();
441
+ spinner.text = 'Connecting...';
387
442
  return;
388
443
  }
389
444
  if (event.type === 'connected') {
390
- spinner.text = 'Connected to BMAD stream...';
445
+ if (spinner.isSpinning)
446
+ spinner.stop();
447
+ process.stderr.write(chalk_1.default.green(' ✓') + ' Connected to BMAD stream\n');
448
+ spinner.start();
449
+ spinner.text = 'Working...';
391
450
  return;
392
451
  }
393
452
  if (event.type === 'agent') {
394
- spinner.text = `Running ${event.agent || 'BMAD agent'}...`;
453
+ const agentName = event.agent || 'BMAD agent';
454
+ if (spinner.isSpinning)
455
+ spinner.stop();
456
+ process.stderr.write(chalk_1.default.cyan(` [Agent] `) + agentName + '\n');
457
+ spinner.start();
458
+ spinner.text = `Running ${agentName}...`;
395
459
  return;
396
460
  }
397
461
  if (event.type === 'status') {
398
462
  const progress = Number.isFinite(Number(event.progress)) ? ` (${event.progress}%)` : '';
399
- spinner.text = `${event.status || 'Working'}${progress}`;
463
+ const statusText = `${event.status || 'Working'}${progress}`;
464
+ if (spinner.isSpinning)
465
+ spinner.stop();
466
+ process.stderr.write(chalk_1.default.cyan(' [Status] ') + statusText + '\n');
467
+ spinner.start();
468
+ spinner.text = statusText;
400
469
  return;
401
470
  }
402
471
  if (event.type === 'result') {
403
- spinner.text = 'Finishing operator workflow...';
472
+ if (spinner.isSpinning)
473
+ spinner.stop();
474
+ process.stderr.write(chalk_1.default.green('\n ✓ Operator workflow complete\n'));
404
475
  return;
405
476
  }
406
477
  }
@@ -828,13 +899,19 @@ class ChatCommand {
828
899
  await this.runOperatorDirectAnswer(prompt);
829
900
  return;
830
901
  }
831
- // ── Repo-grounded operator path: use the agent loop with tools ──
902
+ // ── Repo-grounded operator path: try V3 agent first, then fall back ──
832
903
  // Prompts that reference files, code symbols, or analysis verbs need
833
904
  // tool access to read actual file contents. Route these through the
834
- // local agent loop (which has read_file, grep, etc.) instead of the
835
- // toolless BMAD SSE workflow which cannot read files and will fabricate.
905
+ // V3 agent (which archives runs for history/replay/fork and supports
906
+ // workspace hydration) first. Falls back to local agent loop if V3
907
+ // is unreachable.
836
908
  if (this.isRepoGroundedPrompt(prompt) && this.tools) {
837
909
  this.operatorMode = true;
910
+ const handledByV3 = await this.tryV3AgentWorkflow(prompt);
911
+ if (handledByV3) {
912
+ this.saveSession();
913
+ return;
914
+ }
838
915
  await this.runLocalAgentLoop(prompt);
839
916
  return;
840
917
  }
@@ -1431,6 +1508,7 @@ class ChatCommand {
1431
1508
  this.v3IterationCount = 0;
1432
1509
  this.v3ToolCallCount = 0;
1433
1510
  this.v3LastActivity = Date.now();
1511
+ this.v3StreamingStarted = false;
1434
1512
  const spinner = this.jsonOutput ? null : (0, logger_js_1.createSpinner)({
1435
1513
  text: routingPolicy.cloudSelected ? 'Routing heavy task to Vigthoria Cloud...' : 'Routing to V3 Agent...',
1436
1514
  spinner: 'clock',
@@ -1484,6 +1562,10 @@ class ChatCommand {
1484
1562
  if (spinner) {
1485
1563
  spinner.stop();
1486
1564
  }
1565
+ // End streaming line if we streamed text inline
1566
+ if (this.v3StreamingStarted) {
1567
+ process.stdout.write('\n');
1568
+ }
1487
1569
  const previewGate = (response.metadata?.previewGate || null);
1488
1570
  const workspaceHasOutput = this.api.hasAgentWorkspaceOutput(workspaceContext);
1489
1571
  const success = previewGate?.required === true
@@ -1536,6 +1618,12 @@ class ChatCommand {
1536
1618
  metadata: response.metadata || {},
1537
1619
  }, null, 2));
1538
1620
  }
1621
+ else if (this.v3StreamingStarted) {
1622
+ // Content was already streamed to stdout in real-time; skip duplicate print.
1623
+ if (!this.directPromptMode) {
1624
+ console.log(chalk_1.default.gray(`Agent routing: ${routingPolicy.cloudSelected ? 'Vigthoria Cloud' : 'V3 Agent'} via model ${routingPolicy.selectedModel}`));
1625
+ }
1626
+ }
1539
1627
  else if (response.content) {
1540
1628
  if (!this.directPromptMode) {
1541
1629
  console.log(chalk_1.default.gray(`Agent routing: ${routingPolicy.cloudSelected ? 'Vigthoria Cloud' : 'V3 Agent'} via model ${routingPolicy.selectedModel}`));
@@ -1976,10 +2064,11 @@ class ChatCommand {
1976
2064
  if (!this.directPromptMode) {
1977
2065
  return false;
1978
2066
  }
1979
- if (this.isServerBindableWorkspace(this.currentProjectPath)) {
1980
- return false;
1981
- }
1982
- return /(analyse|analyze|audit|overview|inspect|explain|review|debug|diagnos|read|summari[sz]e|investigate|list|show|find|search|check|scan|count|what|describe)/i.test(prompt);
2067
+ // Always prefer V3 agent first — it supports workspace hydration for
2068
+ // non-server-bindable workspaces (Windows paths, remote machines).
2069
+ // The V3 path will fall back to local-agent-loop if V3 is unreachable.
2070
+ // This ensures runs get archived for history/replay/fork.
2071
+ return false;
1983
2072
  }
1984
2073
  shouldRequireV3AgentWorkflow(prompt) {
1985
2074
  if (!this.directPromptMode) {
@@ -127,6 +127,7 @@ class DeployCommand {
127
127
  * Deploy to preview URL (free)
128
128
  */
129
129
  async deployToPreview(projectPath) {
130
+ this.requireAuth();
130
131
  const spinner = (0, logger_js_1.createSpinner)('Deploying to preview...').start();
131
132
  try {
132
133
  const projectDir = projectPath || process.cwd();
@@ -155,6 +156,7 @@ class DeployCommand {
155
156
  this.logger.error('Deploy failed');
156
157
  const errMsg = error instanceof Error ? error.message : 'Unknown error';
157
158
  console.log(chalk_1.default.red(`\n❌ Error: ${errMsg}\n`));
159
+ process.exitCode = 1;
158
160
  }
159
161
  }
160
162
  /**
@@ -11,41 +11,46 @@ exports.createSpinner = createSpinner;
11
11
  const chalk_1 = __importDefault(require("chalk"));
12
12
  const ora_1 = __importDefault(require("ora"));
13
13
  /**
14
- * Platform-aware character map. Windows cmd.exe / PowerShell with legacy
15
- * code pages (437/850) cannot render Unicode box-drawing or emoji, so we
16
- * fall back to ASCII equivalents.
14
+ * Platform-aware character map.
15
+ *
16
+ * Modern Windows terminals (Windows Terminal, PowerShell 7, VS Code,
17
+ * cmd.exe with chcp 65001) render Unicode box-drawing correctly.
18
+ * Only truly legacy code-page 437/850 consoles cannot, and those are
19
+ * vanishingly rare on Node 20+. We now default to full Unicode
20
+ * everywhere and only strip emoji (multi-codepoint) on Windows where
21
+ * monospace rendering can break.
17
22
  */
18
23
  const isWin = process.platform === 'win32';
19
24
  exports.CH = {
20
- info: isWin ? 'i' : 'ℹ',
21
- warn: isWin ? '!' : '⚠',
22
- error: isWin ? 'x' : '✗',
23
- success: isWin ? '+' : '✓',
25
+ info: 'ℹ',
26
+ warn: '⚠',
27
+ error: '✗',
28
+ success: '✓',
24
29
  ai: isWin ? '>' : '🤖',
25
30
  user: isWin ? '$' : '👤',
26
- hLine: isWin ? '-' : '─',
27
- hDouble: isWin ? '=' : '═',
28
- vLine: isWin ? '|' : '│',
29
- tl: isWin ? '+' : '┌',
30
- tr: isWin ? '+' : '┐',
31
- bl: isWin ? '+' : '└',
32
- br: isWin ? '+' : '┘',
33
- teeR: isWin ? '+' : '├',
34
- teeL: isWin ? '+' : '┤',
35
- cross: isWin ? '+' : '┼',
36
- bullet: isWin ? '*' : '•',
31
+ hLine: '─',
32
+ hDouble: '═',
33
+ vLine: '│',
34
+ tl: '┌',
35
+ tr: '┐',
36
+ bl: '└',
37
+ br: '┘',
38
+ teeR: '├',
39
+ teeL: '┤',
40
+ cross: '┼',
41
+ bullet: '•',
37
42
  cloud: isWin ? '[cloud]' : '☁️ ',
38
43
  home: isWin ? '[local]' : '🏠 ',
39
44
  rocket: isWin ? '>>' : '🚀',
40
45
  lock: isWin ? '[lock]' : '🔒',
41
46
  warnEmoji: isWin ? '[!]' : '⚠️',
42
47
  // Double-line box drawing (used for main banner)
43
- dTl: isWin ? '+' : '╔',
44
- dTr: isWin ? '+' : '╗',
45
- dBl: isWin ? '+' : '╚',
46
- dBr: isWin ? '+' : '╝',
47
- dH: isWin ? '=' : '═',
48
- dV: isWin ? '|' : '║',
48
+ dTl: '╔',
49
+ dTr: '╗',
50
+ dBl: '╚',
51
+ dBr: '╝',
52
+ dH: '═',
53
+ dV: '║',
49
54
  };
50
55
  /**
51
56
  * Create an ora spinner that writes to stderr so it never
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vigthoria-cli",
3
- "version": "1.6.58",
3
+ "version": "1.6.60",
4
4
  "description": "Vigthoria Coder CLI - AI-powered terminal coding assistant",
5
5
  "main": "dist/index.js",
6
6
  "files": [