vigthoria-cli 1.6.26 → 1.6.28

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.
@@ -421,11 +421,33 @@ class ChatCommand {
421
421
  return;
422
422
  }
423
423
  if (options.prompt) {
424
+ // Wrap in a timeout to guarantee bridge cleanup even if the agent
425
+ // loop hangs (e.g. model takes forever on a turn).
426
+ const BRIDGE_TIMEOUT_MS = 180_000; // 3 minutes max for a single prompt
427
+ let timedOut = false;
428
+ const timeoutId = options.bridge
429
+ ? setTimeout(() => {
430
+ timedOut = true;
431
+ const b = (0, bridge_client_js_1.getBridgeClient)();
432
+ if (b) {
433
+ b.emitEnd({ reason: 'timeout' });
434
+ b.destroy();
435
+ }
436
+ if (!this.jsonOutput) {
437
+ this.logger.error('Bridge prompt timed out after 3 minutes.');
438
+ }
439
+ process.exitCode = 1;
440
+ }, BRIDGE_TIMEOUT_MS)
441
+ : null;
424
442
  await this.handleDirectPrompt(options.prompt);
425
- const bridge = (0, bridge_client_js_1.getBridgeClient)();
426
- if (bridge) {
427
- bridge.emitEnd({ reason: 'prompt-complete' });
428
- bridge.destroy();
443
+ if (timeoutId)
444
+ clearTimeout(timeoutId);
445
+ if (!timedOut) {
446
+ const bridge = (0, bridge_client_js_1.getBridgeClient)();
447
+ if (bridge) {
448
+ bridge.emitEnd({ reason: 'prompt-complete' });
449
+ bridge.destroy();
450
+ }
429
451
  }
430
452
  return;
431
453
  }
@@ -980,13 +1002,15 @@ class ChatCommand {
980
1002
  if (toolCalls.length === 0) {
981
1003
  // Phase 5: Quality gate — if the agent tries to conclude on the first
982
1004
  // turn without any discovery, push it to gather evidence first.
983
- if (turn === 0 && this.agentToolEvidence.discovery === 0 && this.isDiagnosticPrompt(prompt)) {
1005
+ // Applies to diagnostic prompts AND any direct-prompt agent call where
1006
+ // the model failed to invoke tools (prevents truncated output).
1007
+ if (turn === 0 && this.agentToolEvidence.discovery === 0 && (this.isDiagnosticPrompt(prompt) || this.directPromptMode)) {
984
1008
  this.messages.push({
985
1009
  role: 'system',
986
1010
  content: [
987
1011
  'Quality gate: you concluded without using any discovery tools (list_dir, glob, read_file, grep).',
988
- 'Before answering a diagnostic or audit question, you MUST inspect the project with tools.',
989
- 'Use list_dir and read_file to gather concrete evidence, then provide your answer.',
1012
+ 'You MUST use tools to gather concrete evidence before providing your answer.',
1013
+ 'Use list_dir to explore the workspace, then read_file or grep to inspect relevant files.',
990
1014
  ].join('\n'),
991
1015
  });
992
1016
  this.directToolContinuationCount += 1;
@@ -23,9 +23,11 @@ export declare class EditCommand {
23
23
  private extractCode;
24
24
  /**
25
25
  * Detect and remove duplicated content in model output.
26
- * Models sometimes output the code twice (original + modified).
26
+ * Models sometimes output the code twice or three times.
27
+ * Applies iteratively until no further duplicates are found.
27
28
  */
28
29
  private deduplicateCode;
30
+ private deduplicateOnce;
29
31
  private showDiffAndConfirm;
30
32
  private showFullDiff;
31
33
  private applyFix;
@@ -133,7 +133,24 @@ Return the complete modified file content:`,
133
133
  try {
134
134
  const result = await this.api.fixCode(file.content, file.language, options.type);
135
135
  spinner.stop();
136
+ // Deduplicate the fixed output — models sometimes repeat code 2-3x
137
+ result.fixed = this.deduplicateCode(result.fixed);
136
138
  if (result.changes.length === 0) {
139
+ // Even if the diff engine found no structured changes, the fixed
140
+ // code may still differ (e.g. single-char operator swap). Show a
141
+ // diff and let the user decide instead of silently discarding.
142
+ if (result.fixed && result.fixed !== file.content) {
143
+ this.logger.section('Found 1 issue(s)');
144
+ console.log(chalk_1.default.yellow('1. Operator/character-level fix detected'));
145
+ console.log();
146
+ if (options.apply) {
147
+ await this.applyFix(file.path, file.content, result.fixed);
148
+ }
149
+ else {
150
+ await this.showDiffAndConfirm(file.path, file.content, result.fixed);
151
+ }
152
+ return;
153
+ }
137
154
  this.logger.success('No issues found!');
138
155
  return;
139
156
  }
@@ -180,14 +197,39 @@ Return the complete modified file content:`,
180
197
  }
181
198
  /**
182
199
  * Detect and remove duplicated content in model output.
183
- * Models sometimes output the code twice (original + modified).
200
+ * Models sometimes output the code twice or three times.
201
+ * Applies iteratively until no further duplicates are found.
184
202
  */
185
203
  deduplicateCode(code) {
204
+ let result = code;
205
+ // Iterate up to 3 times to handle triple+ duplication
206
+ for (let pass = 0; pass < 3; pass++) {
207
+ const deduped = this.deduplicateOnce(result);
208
+ if (deduped === result)
209
+ break;
210
+ result = deduped;
211
+ }
212
+ return result;
213
+ }
214
+ deduplicateOnce(code) {
186
215
  const lines = code.split('\n');
187
216
  const len = lines.length;
188
217
  if (len < 4)
189
218
  return code;
190
- // Check if the second half is a near-duplicate of the first half
219
+ // Pass 1: Remove adjacent duplicate non-empty lines (model stutter)
220
+ const deduped = [];
221
+ for (let i = 0; i < lines.length; i++) {
222
+ const trimmed = lines[i].trim();
223
+ const prevTrimmed = deduped.length > 0 ? deduped[deduped.length - 1].trim() : null;
224
+ if (trimmed && trimmed === prevTrimmed) {
225
+ continue; // skip consecutive duplicate
226
+ }
227
+ deduped.push(lines[i]);
228
+ }
229
+ if (deduped.length < lines.length) {
230
+ return deduped.join('\n');
231
+ }
232
+ // Pass 2: Check if the second half is a near-duplicate of the first half
191
233
  for (let splitAt = Math.floor(len * 0.4); splitAt <= Math.ceil(len * 0.6); splitAt++) {
192
234
  const firstHalf = lines.slice(0, splitAt);
193
235
  const secondHalf = lines.slice(splitAt).filter(l => l.trim() !== '');
package/dist/index.js CHANGED
@@ -98,7 +98,7 @@ function getVersion() {
98
98
  catch (e) {
99
99
  // Fallback to hardcoded version
100
100
  }
101
- return '1.6.26';
101
+ return '1.6.27';
102
102
  }
103
103
  const VERSION = getVersion();
104
104
  /**
@@ -576,10 +576,9 @@ async function main() {
576
576
  force: options.force
577
577
  });
578
578
  });
579
- // Default repo action shows list
580
- repoCommand.action(async () => {
581
- const repo = new repo_js_1.RepoCommand(config, logger);
582
- await repo.list({});
579
+ // Default repo action shows help with available subcommands
580
+ repoCommand.action(() => {
581
+ repoCommand.outputHelp();
583
582
  });
584
583
  // ==================== DEPLOY COMMANDS ====================
585
584
  // Deploy command - Host projects on Vigthoria
package/dist/utils/api.js CHANGED
@@ -50,7 +50,7 @@ class APIClient {
50
50
  // Main Vigthoria Coder API (coder.vigthoria.io)
51
51
  this.client = axios_1.default.create({
52
52
  baseURL: config.get('apiUrl'),
53
- timeout: 120000,
53
+ timeout: 300000, // 5 minutes — covers review, fix, and agent relay flows
54
54
  httpsAgent,
55
55
  headers: {
56
56
  'Content-Type': 'application/json',
@@ -3374,7 +3374,7 @@ document.addEventListener('DOMContentLoaded', () => {
3374
3374
  * for a pure programming language like JavaScript, Python, etc.
3375
3375
  */
3376
3376
  codeContainsDomPollution(code) {
3377
- const domPatterns = /document\.(createElement|querySelector|getElementById|getElementsBy|body|head|addEventListener)|innerHTML|\.style\.(cssText|position|transform|animation)|@keyframes|\.appendChild|\.removeChild|window\.(onload|addEventListener)/;
3377
+ const domPatterns = /document\.(createElement|querySelector|getElementById|getElementsBy|body|head|addEventListener)|innerHTML|\.style\.(cssText|position|transform|animation)|@keyframes|\.appendChild|\.removeChild|window\.(onload|addEventListener)|CSSAnimation|new\s+Animation\b|\.getAnimations\s*\(|\.animate\s*\(/;
3378
3378
  return domPatterns.test(code);
3379
3379
  }
3380
3380
  /**
@@ -3388,7 +3388,7 @@ document.addEventListener('DOMContentLoaded', () => {
3388
3388
  let braceDepth = 0;
3389
3389
  for (const line of lines) {
3390
3390
  // Detect start of DOM blocks
3391
- if (/document\.|\.style\.|\.appendChild|\.removeChild|\.textContent\s*=|@keyframes|addEventListener/.test(line) && !insideDomBlock) {
3391
+ if (/document\.|\.style\.|\.appendChild|\.removeChild|\.textContent\s*=|@keyframes|addEventListener|CSSAnimation|\.getAnimations|\.animate\s*\(/.test(line) && !insideDomBlock) {
3392
3392
  insideDomBlock = true;
3393
3393
  braceDepth = 0;
3394
3394
  }
@@ -3456,7 +3456,18 @@ document.addEventListener('DOMContentLoaded', () => {
3456
3456
  // even when the server only reports style issues like console.log.
3457
3457
  const heuristic = this.heuristicCodeIssues(code, language);
3458
3458
  for (const h of heuristic) {
3459
- // Avoid duplicating issues the server already reported on the same line
3459
+ // Always include critical logic bugs (severity error) from heuristics
3460
+ // regardless of server results — these catch wrong-operator bugs the
3461
+ // server frequently misses.
3462
+ if (h.severity === 'error') {
3463
+ const exactDuplicate = issues.some((existing) => existing.line === h.line && existing.message === h.message);
3464
+ if (!exactDuplicate) {
3465
+ issues.push(h);
3466
+ }
3467
+ continue;
3468
+ }
3469
+ // For non-critical heuristics, avoid duplicating issues the server
3470
+ // already reported on the same line with the same type.
3460
3471
  const isDuplicate = issues.some((existing) => existing.line === h.line && existing.type === h.type);
3461
3472
  if (!isDuplicate) {
3462
3473
  issues.push(h);
@@ -3578,7 +3589,7 @@ document.addEventListener('DOMContentLoaded', () => {
3578
3589
  if (fixType === 'bugs' || fixType === 'logic')
3579
3590
  return issue.type === 'logic' || issue.severity === 'error';
3580
3591
  if (fixType === 'syntax')
3581
- return issue.severity === 'error';
3592
+ return issue.type !== 'logic' && issue.severity === 'error';
3582
3593
  if (fixType === 'style')
3583
3594
  return issue.type === 'style' || issue.type === 'quality';
3584
3595
  if (fixType === 'security')
@@ -3619,7 +3630,11 @@ document.addEventListener('DOMContentLoaded', () => {
3619
3630
  // a semantic diff using LCS so inserted/removed lines don't cause
3620
3631
  // every subsequent line to appear as changed.
3621
3632
  if (changes.length === 0 && fixed !== code) {
3622
- changes = this.computeSemanticDiff(code, fixed, allHints);
3633
+ // Use a clean reason string — strip verbose static-analysis hints
3634
+ const cleanReason = relevantIssues.length > 0
3635
+ ? relevantIssues.map(i => i.message).join('; ')
3636
+ : 'AI-suggested fix';
3637
+ changes = this.computeSemanticDiff(code, fixed, cleanReason);
3623
3638
  }
3624
3639
  return { fixed, changes };
3625
3640
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vigthoria-cli",
3
- "version": "1.6.26",
3
+ "version": "1.6.28",
4
4
  "description": "Vigthoria Coder CLI - AI-powered terminal coding assistant",
5
5
  "main": "dist/index.js",
6
6
  "files": [