vigthoria-cli 1.6.60 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,7 +12,6 @@ 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;
16
15
  private doCredentialLogin;
17
16
  private loginWithToken;
18
17
  private loginWithBrowser;
@@ -50,41 +50,48 @@ const api_js_1 = require("../utils/api.js");
50
50
  * which triggers ERR_USE_AFTER_CLOSE on Node 20 Windows TTY when a second
51
51
  * prompt is attempted on the same process.stdin. Using raw readline avoids
52
52
  * this entirely.
53
+ *
54
+ * IMPORTANT: We reuse a single readline.Interface for all prompts in the
55
+ * login flow to avoid the triple-rl stdin contention that causes stalls
56
+ * on Windows TTY and piped terminals.
53
57
  */
54
58
  function ask(rl, question) {
55
59
  return new Promise((resolve) => rl.question(question, resolve));
56
60
  }
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
+ function askHidden(rl, question) {
62
+ // In non-interactive (piped) terminals, fall back to plain rl.question
63
+ // because raw data events won't fire after stdin reaches EOF.
64
+ if (!process.stdin.isTTY) {
65
+ return new Promise((resolve) => {
66
+ rl.question(question, (answer) => {
67
+ resolve(answer.trim());
68
+ });
69
+ });
70
+ }
71
+ return new Promise((resolve) => {
61
72
  const stdout = process.stdout;
62
73
  let answer = '';
63
- process.stdout.write(question);
64
- if (process.stdin.isTTY) {
65
- process.stdin.setRawMode(true);
66
- }
74
+ // Pause rl so it doesn't compete for stdin data events
75
+ rl.pause();
76
+ stdout.write(question);
77
+ process.stdin.setRawMode(true);
67
78
  process.stdin.resume();
68
79
  const onData = (ch) => {
69
80
  const c = ch.toString('utf8');
70
81
  if (c === '\n' || c === '\r' || c === '\u0004') {
71
- if (process.stdin.isTTY) {
72
- process.stdin.setRawMode(false);
73
- }
82
+ process.stdin.setRawMode(false);
74
83
  process.stdin.removeListener('data', onData);
75
84
  stdout.write('\n');
76
- rl.close();
85
+ rl.resume();
77
86
  resolve(answer);
78
87
  }
79
88
  else if (c === '\u007f' || c === '\b') {
80
- // backspace
81
89
  if (answer.length > 0) {
82
90
  answer = answer.slice(0, -1);
83
91
  stdout.write('\b \b');
84
92
  }
85
93
  }
86
94
  else if (c === '\u0003') {
87
- // Ctrl-C
88
95
  rl.close();
89
96
  process.exit(1);
90
97
  }
@@ -124,17 +131,18 @@ class AuthCommand {
124
131
  console.log(chalk_1.default.white(' 3) Browser Login'));
125
132
  console.log();
126
133
  const choice = (await ask(rl, chalk_1.default.cyan('? ') + 'Choose login method (1/2/3): ')).trim();
127
- rl.close();
128
134
  switch (choice) {
129
135
  case '1':
130
136
  case 'credentials': {
131
- const email = await this.askInput('Email: ');
137
+ const email = (await ask(rl, chalk_1.default.cyan('? ') + 'Email: ')).trim();
132
138
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
133
139
  if (!emailRegex.test(email)) {
134
140
  this.logger.error('Please enter a valid email');
141
+ rl.close();
135
142
  return;
136
143
  }
137
- const password = await askHidden(chalk_1.default.cyan('? ') + 'Password: ');
144
+ const password = await askHidden(rl, chalk_1.default.cyan('? ') + 'Password: ');
145
+ rl.close();
138
146
  if (password.length < 6) {
139
147
  this.logger.error('Password must be at least 6 characters');
140
148
  return;
@@ -144,7 +152,8 @@ class AuthCommand {
144
152
  }
145
153
  case '2':
146
154
  case 'token': {
147
- const token = await askHidden(chalk_1.default.cyan('? ') + 'API Token: ');
155
+ const token = await askHidden(rl, chalk_1.default.cyan('? ') + 'API Token: ');
156
+ rl.close();
148
157
  if (!token) {
149
158
  this.logger.error('Please enter your API token');
150
159
  return;
@@ -154,9 +163,11 @@ class AuthCommand {
154
163
  }
155
164
  case '3':
156
165
  case 'browser':
166
+ rl.close();
157
167
  await this.loginWithBrowser();
158
168
  break;
159
169
  default:
170
+ rl.close();
160
171
  this.logger.error('Invalid choice. Please enter 1, 2, or 3.');
161
172
  break;
162
173
  }
@@ -166,15 +177,6 @@ class AuthCommand {
166
177
  throw err;
167
178
  }
168
179
  }
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
180
  async doCredentialLogin(email, password) {
179
181
  const spinner = (0, logger_js_1.createSpinner)('Logging in...').start();
180
182
  const success = await this.api.login(email, password);
@@ -326,10 +326,19 @@ class ChatCommand {
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
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
329
  if (spinner.isSpinning)
331
330
  spinner.stop();
332
331
  process.stderr.write(stepLabel + '\n');
332
+ // Show extra detail for key tools
333
+ const args = event.arguments || {};
334
+ const toolName = event.name || event.tool || '';
335
+ if ((toolName === 'write_file' || toolName === 'edit_file') && typeof args.content === 'string') {
336
+ const len = args.content.length;
337
+ process.stderr.write(chalk_1.default.gray(` ${len > 1000 ? Math.round(len / 1024) + ' KB' : len + ' bytes'} content\n`));
338
+ }
339
+ else if (toolName === 'bash' && typeof args.command === 'string') {
340
+ process.stderr.write(chalk_1.default.gray(` $ ${args.command.slice(0, 120)}${args.command.length > 120 ? '…' : ''}\n`));
341
+ }
333
342
  spinner.start();
334
343
  spinner.text = `Running ${toolDesc}...`;
335
344
  return;
@@ -341,6 +350,16 @@ class ChatCommand {
341
350
  if (spinner.isSpinning)
342
351
  spinner.stop();
343
352
  process.stderr.write(`${indicator} ${toolName}\n`);
353
+ // Show output for failures, or brief summary for successes
354
+ const output = typeof event.output === 'string' ? event.output.trim() : '';
355
+ if (!success && output) {
356
+ const lines = output.split('\n').slice(0, 4);
357
+ process.stderr.write(chalk_1.default.red(` ${lines.join('\n ')}\n`));
358
+ }
359
+ else if (success && output && output.length > 0) {
360
+ const brief = output.split('\n')[0].slice(0, 120);
361
+ process.stderr.write(chalk_1.default.gray(` ${brief}${output.length > 120 ? '…' : ''}\n`));
362
+ }
344
363
  spinner.start();
345
364
  spinner.text = 'Next step...';
346
365
  return;
@@ -348,11 +367,9 @@ class ChatCommand {
348
367
  if (event.type === 'thinking') {
349
368
  this.v3IterationCount += 1;
350
369
  const iterText = event.content || '';
351
- const iterMatch = iterText.match(/Iteration (\d+)/i);
352
- const iterNum = iterMatch ? iterMatch[1] : String(this.v3IterationCount);
353
370
  if (spinner.isSpinning)
354
371
  spinner.stop();
355
- process.stderr.write(chalk_1.default.cyan(`\n── Iteration ${iterNum} ──\n`));
372
+ process.stderr.write(chalk_1.default.cyan(`\n── ${iterText || `Iteration ${this.v3IterationCount}`} ──\n`));
356
373
  spinner.start();
357
374
  spinner.text = 'Analyzing...';
358
375
  return;
@@ -385,18 +402,66 @@ class ChatCommand {
385
402
  const tools = event.tool_calls || this.v3ToolCallCount;
386
403
  if (spinner.isSpinning)
387
404
  spinner.stop();
388
- process.stderr.write(chalk_1.default.green(`\n✓ Complete`) + ` — ${iters} iterations, ${tools} tool calls${elapsed ? `, ${elapsed}` : ''}\n`);
405
+ let statLine = `${iters} iterations, ${tools} tool calls`;
406
+ if (elapsed)
407
+ statLine += `, ${elapsed}`;
408
+ if (event.mode === 'planner-executor') {
409
+ const ok = event.tasks_completed ?? '?';
410
+ const fail = event.tasks_failed ?? 0;
411
+ statLine += ` — ${ok} tasks done`;
412
+ if (fail > 0)
413
+ statLine += chalk_1.default.yellow(`, ${fail} failed`);
414
+ }
415
+ process.stderr.write(chalk_1.default.green(`\n✓ Complete`) + ` — ${statLine}\n`);
416
+ // Show seal quality score if available
417
+ if (event.seal_score && typeof event.seal_score.overall === 'number') {
418
+ const score = event.seal_score.overall;
419
+ const tier = event.seal_score.tier || '';
420
+ const scoreColor = score >= 7 ? chalk_1.default.green : score >= 5 ? chalk_1.default.yellow : chalk_1.default.red;
421
+ process.stderr.write(chalk_1.default.cyan(' [Quality] ') + scoreColor(`${score}/10`) + (tier ? chalk_1.default.gray(` (${tier})`) : '') + '\n');
422
+ }
389
423
  return;
390
424
  }
391
425
  if (event.type === 'plan') {
392
- const planKind = event.plan?.task_kind || event.task_kind || '';
426
+ const plan = event.plan || {};
427
+ const planKind = plan.task_kind || event.task_kind || '';
428
+ const quality = plan.quality_profile || '';
393
429
  if (spinner.isSpinning)
394
430
  spinner.stop();
395
- process.stderr.write(chalk_1.default.cyan(` [Plan] `) + `Task: ${planKind || 'analyzing'}...\n`);
431
+ process.stderr.write(chalk_1.default.cyan(` [Plan] `) + `Task: ${planKind || 'analyzing'}`);
432
+ if (quality)
433
+ process.stderr.write(chalk_1.default.gray(` (${quality})`));
434
+ process.stderr.write('\n');
435
+ // Planner-executor: show task list
436
+ if (Array.isArray(plan.tasks) && plan.tasks.length > 0) {
437
+ process.stderr.write(chalk_1.default.gray(` ${plan.total_tasks || plan.tasks.length} tasks:\n`));
438
+ for (const t of plan.tasks.slice(0, 10)) {
439
+ const targets = Array.isArray(t.targets) && t.targets.length ? ` → ${t.targets.join(', ')}` : '';
440
+ process.stderr.write(chalk_1.default.gray(` • ${t.title || t.id}${targets}\n`));
441
+ }
442
+ if (plan.tasks.length > 10) {
443
+ process.stderr.write(chalk_1.default.gray(` ... and ${plan.tasks.length - 10} more\n`));
444
+ }
445
+ }
446
+ // Monolithic: show target files
447
+ if (Array.isArray(plan.target_files) && plan.target_files.length > 0) {
448
+ process.stderr.write(chalk_1.default.gray(` Files: ${plan.target_files.join(', ')}\n`));
449
+ }
396
450
  spinner.start();
397
451
  spinner.text = 'Planning...';
398
452
  return;
399
453
  }
454
+ if (event.type === 'file_mutation') {
455
+ const filePath = typeof event.path === 'string' ? event.path.split('/').slice(-2).join('/') : '';
456
+ const action = event.action === 'delete' ? chalk_1.default.red('deleted') : chalk_1.default.green('wrote');
457
+ if (filePath) {
458
+ if (spinner.isSpinning)
459
+ spinner.stop();
460
+ process.stderr.write(chalk_1.default.cyan(' [File] ') + `${action} ${filePath}\n`);
461
+ spinner.start();
462
+ }
463
+ return;
464
+ }
400
465
  if (event.type === 'error') {
401
466
  if (event.checkpointed) {
402
467
  if (spinner.isSpinning)
@@ -62,6 +62,8 @@ class HistoryCommand {
62
62
  const params = new URLSearchParams({
63
63
  limit: String(limit),
64
64
  workspace_root: workspace,
65
+ local_workspace_path: project,
66
+ project_path: project,
65
67
  });
66
68
  const resp = await fetch(`${baseUrl}/api/runs?${params}`, {
67
69
  headers: this.getHeaders(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vigthoria-cli",
3
- "version": "1.6.60",
3
+ "version": "1.8.0",
4
4
  "description": "Vigthoria Coder CLI - AI-powered terminal coding assistant",
5
5
  "main": "dist/index.js",
6
6
  "files": [