vigthoria-cli 1.6.59 → 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,6 +12,7 @@ export declare class AuthCommand {
12
12
  private api;
13
13
  constructor(config: Config, logger: Logger);
14
14
  login(options: LoginOptions): Promise<void>;
15
+ private askInput;
15
16
  private doCredentialLogin;
16
17
  private loginWithToken;
17
18
  private loginWithBrowser;
@@ -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,60 +114,66 @@ 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
- // Collect ALL interactive input in a single inquirer.prompt() call.
33
- // inquirer 9.x creates and destroys a readline interface per prompt()
34
- // call; on Node 20 TTY the second readline on the same process.stdin
35
- // throws ERR_USE_AFTER_CLOSE. Using `when` conditionals keeps one
36
- // readline alive for the entire login flow.
37
- const answers = await inquirer_1.default.prompt([
38
- {
39
- type: 'list',
40
- name: 'method',
41
- message: 'Choose login method:',
42
- choices: [
43
- { name: 'Email & Password', value: 'credentials' },
44
- { name: 'API Token', value: 'token' },
45
- { name: 'Browser Login', value: 'browser' },
46
- ],
47
- },
48
- {
49
- type: 'input',
50
- name: 'email',
51
- message: 'Email:',
52
- when: (ans) => ans.method === 'credentials',
53
- validate: (input) => {
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: ');
54
132
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
55
- return emailRegex.test(input) || 'Please enter a valid email';
56
- },
57
- },
58
- {
59
- type: 'password',
60
- name: 'password',
61
- message: 'Password:',
62
- mask: '*',
63
- when: (ans) => ans.method === 'credentials',
64
- validate: (input) => input.length >= 6 || 'Password must be at least 6 characters',
65
- },
66
- {
67
- type: 'password',
68
- name: 'token',
69
- message: 'API Token:',
70
- mask: '*',
71
- when: (ans) => ans.method === 'token',
72
- validate: (input) => input.length > 0 || 'Please enter your API token',
73
- },
74
- ]);
75
- switch (answers.method) {
76
- case 'credentials':
77
- await this.doCredentialLogin(answers.email, answers.password);
78
- break;
79
- case 'token':
80
- await this.loginWithToken(answers.token);
81
- break;
82
- case 'browser':
83
- await this.loginWithBrowser();
84
- break;
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
+ }
85
163
  }
164
+ catch (err) {
165
+ rl.close();
166
+ throw err;
167
+ }
168
+ }
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
+ });
86
177
  }
87
178
  async doCredentialLogin(email, password) {
88
179
  const spinner = (0, logger_js_1.createSpinner)('Logging in...').start();
@@ -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');
@@ -325,20 +325,24 @@ class ChatCommand {
325
325
  const toolDesc = this.describeV3AgentTool(event.tool || event.name || event.tool_name);
326
326
  const toolTarget = event.arguments?.path || event.arguments?.file_path || event.arguments?.pattern || '';
327
327
  const shortTarget = toolTarget ? ` → ${String(toolTarget).split('/').slice(-2).join('/')}` : '';
328
- if (this.v3StreamingStarted && !spinner.isSpinning) {
329
- spinner.start();
330
- }
331
- 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}...`;
332
335
  return;
333
336
  }
334
337
  if (event.type === 'tool_result') {
335
338
  const success = event.success !== false;
336
339
  const toolName = event.name || event.tool || '';
337
- const indicator = success ? chalk_1.default.green('✓') : chalk_1.default.red('✗');
338
- if (this.v3StreamingStarted && !spinner.isSpinning) {
339
- spinner.start();
340
- }
341
- 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...';
342
346
  return;
343
347
  }
344
348
  if (event.type === 'thinking') {
@@ -346,10 +350,11 @@ class ChatCommand {
346
350
  const iterText = event.content || '';
347
351
  const iterMatch = iterText.match(/Iteration (\d+)/i);
348
352
  const iterNum = iterMatch ? iterMatch[1] : String(this.v3IterationCount);
349
- if (this.v3StreamingStarted && !spinner.isSpinning) {
350
- spinner.start();
351
- }
352
- 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...';
353
358
  return;
354
359
  }
355
360
  if (event.type === 'message' || event.type === 'assistant' || event.type === 'text' || event.type === 'content_block_delta') {
@@ -378,31 +383,50 @@ class ChatCommand {
378
383
  const elapsed = event.elapsed || '';
379
384
  const iters = event.iterations || this.v3IterationCount;
380
385
  const tools = event.tool_calls || this.v3ToolCallCount;
381
- 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`);
382
389
  return;
383
390
  }
384
391
  if (event.type === 'plan') {
385
392
  const planKind = event.plan?.task_kind || event.task_kind || '';
386
- 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...';
387
398
  return;
388
399
  }
389
400
  if (event.type === 'error') {
390
401
  if (event.checkpointed) {
391
- 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();
392
406
  }
393
407
  else {
394
- 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');
395
411
  }
396
412
  return;
397
413
  }
398
414
  if (event.type === 'context') {
399
- 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...';
400
420
  return;
401
421
  }
402
422
  if (event.type === 'start') {
403
423
  this.v3IterationCount = 0;
404
424
  this.v3ToolCallCount = 0;
405
- 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...';
406
430
  }
407
431
  }
408
432
  updateOperatorSpinner(spinner, event) {
@@ -410,24 +434,44 @@ class ChatCommand {
410
434
  return;
411
435
  }
412
436
  if (event.type === 'started') {
413
- 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...';
414
442
  return;
415
443
  }
416
444
  if (event.type === 'connected') {
417
- 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...';
418
450
  return;
419
451
  }
420
452
  if (event.type === 'agent') {
421
- 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}...`;
422
459
  return;
423
460
  }
424
461
  if (event.type === 'status') {
425
462
  const progress = Number.isFinite(Number(event.progress)) ? ` (${event.progress}%)` : '';
426
- 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;
427
469
  return;
428
470
  }
429
471
  if (event.type === 'result') {
430
- 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'));
431
475
  return;
432
476
  }
433
477
  }
@@ -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.59",
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": [