vigthoria-cli 1.10.36 → 1.10.47

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.
Files changed (62) hide show
  1. package/dist/commands/agent-session-menu.d.ts +19 -0
  2. package/dist/commands/agent-session-menu.js +155 -0
  3. package/dist/commands/auth.js +68 -51
  4. package/dist/commands/bridge.js +19 -12
  5. package/dist/commands/cancel.js +22 -15
  6. package/dist/commands/chat.d.ts +0 -22
  7. package/dist/commands/chat.js +402 -1084
  8. package/dist/commands/config.js +73 -33
  9. package/dist/commands/deploy.js +123 -83
  10. package/dist/commands/device.js +61 -21
  11. package/dist/commands/edit.js +39 -32
  12. package/dist/commands/explain.js +25 -18
  13. package/dist/commands/generate.js +44 -37
  14. package/dist/commands/hub.js +102 -95
  15. package/dist/commands/index.js +46 -41
  16. package/dist/commands/legion.js +186 -146
  17. package/dist/commands/review.js +36 -29
  18. package/dist/commands/security.js +12 -5
  19. package/dist/commands/wallet.js +35 -28
  20. package/dist/commands/workflow.js +20 -13
  21. package/dist/utils/brain-hub-client.d.ts +32 -0
  22. package/dist/utils/brain-hub-client.js +52 -0
  23. package/dist/utils/bridge-client.js +52 -11
  24. package/dist/utils/codebase-indexer.d.ts +59 -0
  25. package/dist/utils/codebase-indexer.js +351 -0
  26. package/dist/utils/context-ranker.js +21 -15
  27. package/dist/utils/files.js +42 -5
  28. package/dist/utils/logger.js +50 -42
  29. package/dist/utils/persona.js +8 -3
  30. package/dist/utils/post-write-validator.js +29 -22
  31. package/dist/utils/project-memory.js +23 -16
  32. package/dist/utils/task-display.js +20 -13
  33. package/dist/utils/workspace-brain-service.d.ts +43 -0
  34. package/dist/utils/workspace-brain-service.js +158 -0
  35. package/dist/utils/workspace-cache.js +26 -18
  36. package/dist/utils/workspace-stream.js +63 -21
  37. package/package.json +3 -6
  38. package/scripts/release/validate-no-go-gates.sh +1 -1
  39. package/dist/commands/fork.d.ts +0 -17
  40. package/dist/commands/fork.js +0 -164
  41. package/dist/commands/history.d.ts +0 -17
  42. package/dist/commands/history.js +0 -113
  43. package/dist/commands/preview.d.ts +0 -55
  44. package/dist/commands/preview.js +0 -467
  45. package/dist/commands/replay.d.ts +0 -18
  46. package/dist/commands/replay.js +0 -156
  47. package/dist/commands/repo.d.ts +0 -97
  48. package/dist/commands/repo.js +0 -773
  49. package/dist/commands/update.d.ts +0 -9
  50. package/dist/commands/update.js +0 -201
  51. package/dist/index.d.ts +0 -21
  52. package/dist/index.js +0 -1823
  53. package/dist/utils/api.d.ts +0 -572
  54. package/dist/utils/api.js +0 -6548
  55. package/dist/utils/cli-state.d.ts +0 -54
  56. package/dist/utils/cli-state.js +0 -185
  57. package/dist/utils/config.d.ts +0 -85
  58. package/dist/utils/config.js +0 -267
  59. package/dist/utils/session.d.ts +0 -118
  60. package/dist/utils/session.js +0 -423
  61. package/dist/utils/tools.d.ts +0 -274
  62. package/dist/utils/tools.js +0 -3502
@@ -1,24 +1,64 @@
1
- import chalk from 'chalk';
2
- import * as fs from 'fs';
3
- import * as os from 'os';
4
- import * as path from 'path';
5
- import * as readline from 'readline';
6
- import { createSpinner } from '../utils/logger.js';
7
- import { APIClient, CLIError, classifyError, formatCLIError, sanitizeUserFacingErrorText, sanitizeUserFacingPathText, propagateError, VIGTHORIA_SERVER_TEMPORARILY_UNAVAILABLE_MESSAGE } from '../utils/api.js';
8
- import { AgenticTools, robustifyStreamResponse } from '../utils/tools.js';
9
- import { SessionManager } from '../utils/session.js';
10
- import { BridgeClient, getBridgeClient } from '../utils/bridge-client.js';
11
- import { WorkspaceWatcher, WorkspaceWSClient } from '../utils/workspace-stream.js';
12
- import { TaskDisplay } from '../utils/task-display.js';
13
- import { ProjectMemoryService } from '../utils/project-memory.js';
14
- import { buildPersonaOverlay, normalizePersonaMode } from '../utils/persona.js';
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.ChatCommand = void 0;
40
+ const chalk_1 = __importDefault(require("chalk"));
41
+ const fs = __importStar(require("fs"));
42
+ const os = __importStar(require("os"));
43
+ const path = __importStar(require("path"));
44
+ const readline = __importStar(require("readline"));
45
+ const logger_js_1 = require("../utils/logger.js");
46
+ const api_js_1 = require("../utils/api.js");
47
+ const tools_js_1 = require("../utils/tools.js");
48
+ const session_js_1 = require("../utils/session.js");
49
+ const bridge_client_js_1 = require("../utils/bridge-client.js");
50
+ const workspace_stream_js_1 = require("../utils/workspace-stream.js");
51
+ const task_display_js_1 = require("../utils/task-display.js");
52
+ const project_memory_js_1 = require("../utils/project-memory.js");
53
+ const persona_js_1 = require("../utils/persona.js");
54
+ const agent_session_menu_js_1 = require("./agent-session-menu.js");
15
55
  const DEFAULT_V3_AGENT_TIMEOUT_MS = (() => {
16
56
  const rawValue = process.env.VIGTHORIA_AGENT_TIMEOUT_MS || process.env.V3_AGENT_TIMEOUT_MS;
17
57
  if (!rawValue) {
18
- return 0;
58
+ return 300000;
19
59
  }
20
60
  const parsed = Number.parseInt(rawValue, 10);
21
- return Number.isFinite(parsed) && parsed >= 0 ? parsed : 0;
61
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : 300000;
22
62
  })();
23
63
  const DEFAULT_V3_AGENT_IDLE_TIMEOUT_MS = (() => {
24
64
  const rawValue = process.env.VIGTHORIA_AGENT_IDLE_TIMEOUT_MS || process.env.V3_AGENT_IDLE_TIMEOUT_MS;
@@ -36,7 +76,7 @@ const DEFAULT_V3_AGENT_SOFT_TIMEOUT_MS = (() => {
36
76
  const parsed = Number.parseInt(rawValue, 10);
37
77
  return Number.isFinite(parsed) && parsed > 0 ? parsed : 180000;
38
78
  })();
39
- export class ChatCommand {
79
+ class ChatCommand {
40
80
  config;
41
81
  logger;
42
82
  api;
@@ -112,32 +152,32 @@ export class ChatCommand {
112
152
  message.includes('aborted');
113
153
  }
114
154
  toUserFacingApiError(error, context) {
115
- const classified = classifyError(error);
155
+ const classified = (0, api_js_1.classifyError)(error);
116
156
  const status = classified.statusCode || (this.isJwtExpirationError(error) ? 401 : 500);
117
157
  if (this.isJwtExpirationError(error) || classified.category === 'auth') {
118
- return new CLIError('Your Vigthoria session has expired. Run `vigthoria login` to authenticate again.', 'auth', { statusCode: 401 });
158
+ return new api_js_1.CLIError('Your Vigthoria session has expired. Run `vigthoria login` to authenticate again.', 'auth', { statusCode: 401 });
119
159
  }
120
160
  // Preserve structured API classification first (auth/model/network/etc.)
121
161
  // so upstream responses are not relabeled by message heuristics.
122
162
  if (classified.category === 'timeout') {
123
- return new CLIError(`${context} timed out. Check your connection and try again.`, 'timeout', { statusCode: status });
163
+ return new api_js_1.CLIError(`${context} timed out. Check your connection and try again.`, 'timeout', { statusCode: status });
124
164
  }
125
165
  if (classified.category === 'network') {
126
- return new CLIError(`${context} could not reach the Vigthoria API. Check your network connection and try again.`, 'network', { statusCode: status });
166
+ return new api_js_1.CLIError(`${context} could not reach the Vigthoria API. Check your network connection and try again.`, 'network', { statusCode: status });
127
167
  }
128
168
  if (classified.category === 'model_backend') {
129
- return new CLIError(VIGTHORIA_SERVER_TEMPORARILY_UNAVAILABLE_MESSAGE, 'model_backend', { statusCode: status, endpoint: classified.endpoint });
169
+ return new api_js_1.CLIError(api_js_1.VIGTHORIA_SERVER_TEMPORARILY_UNAVAILABLE_MESSAGE, 'model_backend', { statusCode: status, endpoint: classified.endpoint });
130
170
  }
131
- const message = sanitizeUserFacingErrorText(classified.message || `${context} failed`);
132
- return new CLIError(message, 'model_backend', { statusCode: status, endpoint: classified.endpoint });
171
+ const message = (0, api_js_1.sanitizeUserFacingErrorText)(classified.message || `${context} failed`);
172
+ return new api_js_1.CLIError(message, 'model_backend', { statusCode: status, endpoint: classified.endpoint });
133
173
  }
134
174
  handleApiError(error, context) {
135
175
  const userFacingError = this.toUserFacingApiError(error, context);
136
176
  if (!this.jsonOutput) {
137
- console.error(chalk.red(`${context} failed: ${userFacingError.message}`));
177
+ console.error(chalk_1.default.red(`${context} failed: ${userFacingError.message}`));
138
178
  }
139
179
  const original = error && typeof error === 'object' ? error : { message: String(error) };
140
- propagateError({
180
+ (0, api_js_1.propagateError)({
141
181
  ...original,
142
182
  message: userFacingError.message,
143
183
  statusCode: userFacingError.statusCode,
@@ -266,7 +306,7 @@ export class ChatCommand {
266
306
  explicitModel: true,
267
307
  heavyTask,
268
308
  cloudEligible,
269
- cloudSelected: ['cloud', 'cloud-reason', 'ultra', 'cloud-fast', 'cloud-balanced', 'cloud-code', 'cloud-power', 'cloud-maximum'].includes(this.currentModel),
309
+ cloudSelected: this.currentModel === 'cloud' || this.currentModel === 'cloud-reason' || this.currentModel === 'ultra',
270
310
  routeReason: 'explicit-model-selection',
271
311
  };
272
312
  }
@@ -299,7 +339,7 @@ export class ChatCommand {
299
339
  routeReason: 'default-v3-agent',
300
340
  };
301
341
  }
302
- getMessagesForModel(options) {
342
+ getMessagesForModel() {
303
343
  const messages = [...this.messages];
304
344
  const personaOverlay = this.buildActivePersonaOverlay();
305
345
  if (personaOverlay && !messages.some((message) => message.role === 'system' && message.content.includes('Optional persona overlay: Wiener Grantler mode.'))) {
@@ -333,136 +373,15 @@ export class ChatCommand {
333
373
  messages.splice(insertionIndex, 0, memoryMessage);
334
374
  }
335
375
  }
336
- if (options?.compact) {
337
- const compactLimit = 6000;
338
- const systemMessages = messages.filter((message) => message.role === 'system').map((message) => ({
339
- ...message,
340
- content: message.content.length > compactLimit
341
- ? `${message.content.slice(0, compactLimit)}\n...[trimmed for first local agent turn]`
342
- : message.content,
343
- }));
344
- const lastUser = [...messages].reverse().find((message) => message.role === 'user');
345
- return lastUser ? [...systemMessages.slice(0, 2), lastUser] : systemMessages.slice(0, 2);
346
- }
347
376
  return messages;
348
377
  }
349
- normalizeClientV3ToolPath(rawPath) {
350
- let normalized = String(rawPath || '.').trim().replace(/\\/g, '/');
351
- if (!normalized || normalized === '/') {
352
- return '.';
353
- }
354
- normalized = normalized.replace(/^vigthoria:\/\/workspace\/?/i, '');
355
- normalized = normalized.replace(/^\.\/+/, '');
356
- normalized = normalized.replace(/^workspace\/?/i, '');
357
- const workspaceRoot = this.currentProjectPath || process.cwd();
358
- const workspaceName = path.basename(workspaceRoot);
359
- if (workspaceName) {
360
- const workspaceKey = workspaceName.toLowerCase().replace(/[\s_./\\-]+/g, '');
361
- const parts = normalized.split('/').filter(Boolean);
362
- if (parts.length > 0) {
363
- const firstKey = parts[0].toLowerCase().replace(/[\s_./\\-]+/g, '');
364
- if (firstKey === workspaceKey) {
365
- normalized = parts.slice(1).join('/') || '.';
366
- }
367
- }
368
- }
369
- normalized = normalized.replace(/^\/+/, '').replace(/\/+$/, '');
370
- return normalized || '.';
371
- }
372
- resolveClientV3ToolPath(rawPath) {
373
- const root = this.currentProjectPath || process.cwd();
374
- const normalized = this.normalizeClientV3ToolPath(rawPath);
375
- const absoluteTarget = path.resolve(root, normalized === '.' ? '' : normalized);
376
- if (fs.existsSync(absoluteTarget)) {
377
- return normalized;
378
- }
379
- const parts = normalized.split('/').filter(Boolean);
380
- if (parts.length === 0) {
381
- return '.';
382
- }
383
- try {
384
- const entries = fs.readdirSync(root, { withFileTypes: true });
385
- const firstKey = parts[0].toLowerCase().replace(/[\s_./\\-]+/g, '');
386
- for (const entry of entries) {
387
- const entryKey = entry.name.toLowerCase().replace(/[\s_./\\-]+/g, '');
388
- if (entryKey === firstKey || entryKey.includes(firstKey) || firstKey.includes(entryKey)) {
389
- const rest = parts.slice(1).join('/');
390
- const candidate = rest ? `${entry.name}/${rest}` : entry.name;
391
- if (fs.existsSync(path.resolve(root, candidate))) {
392
- return candidate.replace(/\\/g, '/');
393
- }
394
- if (!rest) {
395
- return entry.name.replace(/\\/g, '/');
396
- }
397
- }
398
- }
399
- }
400
- catch {
401
- // Ignore unreadable workspace roots during path repair.
402
- }
403
- return normalized;
404
- }
405
- normalizeClientV3ToolArgs(args) {
406
- const normalizedArgs = { ...args };
407
- for (const key of ['path', 'file_path', 'file', 'target']) {
408
- if (typeof normalizedArgs[key] === 'string' && normalizedArgs[key].trim()) {
409
- normalizedArgs[key] = this.resolveClientV3ToolPath(normalizedArgs[key]);
410
- }
411
- }
412
- return normalizedArgs;
413
- }
414
- async executeClientV3Tool(event) {
415
- if (!this.tools) {
416
- return { success: false, output: '', error: 'Local agent tools are not initialized.' };
417
- }
418
- const name = String(event.name || '').trim();
419
- const args = (event.arguments && typeof event.arguments === 'object')
420
- ? event.arguments
421
- : {};
422
- let toolName = name;
423
- let toolArgs = {};
424
- for (const [key, value] of Object.entries(args)) {
425
- toolArgs[key] = value == null ? '' : String(value);
426
- }
427
- toolArgs = this.normalizeClientV3ToolArgs(toolArgs);
428
- if (name === 'list_directory') {
429
- toolName = 'list_dir';
430
- toolArgs = {
431
- path: toolArgs.path || '.',
432
- ...(toolArgs.recursive === 'true' || toolArgs.recursive === '1' ? { recursive: 'true' } : {}),
433
- };
434
- }
435
- else if (name === 'search_files') {
436
- toolName = 'grep';
437
- toolArgs = {
438
- path: toolArgs.path || '.',
439
- pattern: toolArgs.pattern || toolArgs.query || '',
440
- ...(toolArgs.file_pattern ? { includePattern: toolArgs.file_pattern } : {}),
441
- };
442
- }
443
- else if (name === 'read_file') {
444
- toolName = 'read_file';
445
- }
446
- else if (name === 'write_file' || name === 'edit_file') {
447
- toolName = name;
448
- }
449
- if (!toolName) {
450
- return { success: false, output: '', error: 'Missing V3 client tool name.' };
451
- }
452
- const result = await this.tools.execute({ tool: toolName, args: toolArgs });
453
- return {
454
- success: result.success === true,
455
- output: String(result.output || result.message || ''),
456
- error: result.error ? String(result.error) : '',
457
- };
458
- }
459
378
  getActivePersonaMode() {
460
379
  if (this.personaOverride)
461
380
  return this.personaOverride;
462
- return normalizePersonaMode(this.config.get('persona')) || 'default';
381
+ return (0, persona_js_1.normalizePersonaMode)(this.config.get('persona')) || 'default';
463
382
  }
464
383
  buildActivePersonaOverlay() {
465
- return buildPersonaOverlay(this.getActivePersonaMode(), this.getLastUserPrompt());
384
+ return (0, persona_js_1.buildPersonaOverlay)(this.getActivePersonaMode(), this.getLastUserPrompt());
466
385
  }
467
386
  isDiagnosticPrompt(prompt) {
468
387
  return /(startup|start up|won'?t start|doesn'?t start|crash|crashes|error|errors|failing|fails|issue|issues|bug|bugs|diagnos|debug|runtime|log|logs|exception|traceback|stack trace|yaml|blocking|blocker)/i.test(prompt);
@@ -472,201 +391,52 @@ export class ChatCommand {
472
391
  * question — these should use analysis_only workflow, not full_autonomy.
473
392
  */
474
393
  isAnalysisLookupPrompt(prompt) {
475
- if (this.isImplementationPrompt(prompt)) {
476
- return false;
477
- }
478
394
  return /^(what|which|where|how many|who|find|list|show|check|inspect|analyze|analyse|audit|explain|describe|summarize|summarise|review|overview|count|read|look at|tell me|locate|search for|does .* exist)/i.test(prompt.trim());
479
395
  }
480
- isImplementationPrompt(prompt) {
481
- const trimmed = String(prompt || '').trim();
482
- if (!trimmed)
483
- return false;
484
- if (/\b(implement|add|fix|repair|complete|finish|build|create|write|patch|update|modify|edit|make\s+(?:it\s+)?work(?:ing)?|get\s+(?:a\s+)?working)\b/i.test(trimmed)) {
485
- return true;
486
- }
487
- if (/\bwhat(?:'s|\s+is|\s+are)\s+missing\b/i.test(trimmed)) {
488
- return true;
489
- }
490
- if (/\b(game|html5|pacman|rogue|app|site)\b/i.test(trimmed) && /\b(missing|broken|fix|implement|working|playable)\b/i.test(trimmed)) {
491
- return true;
492
- }
493
- if (/continue the previous agent run/i.test(trimmed) && /\b(implement|fix|missing|blocker|remaining)\b/i.test(trimmed)) {
494
- return true;
495
- }
496
- return false;
497
- }
498
- getWindowsPromptPathRoots() {
499
- const roots = new Set();
500
- const add = (value) => {
501
- const raw = String(value || '').trim();
502
- if (!raw)
503
- return;
504
- try {
505
- const resolved = path.resolve(raw);
506
- if (fs.existsSync(resolved) && fs.statSync(resolved).isDirectory()) {
507
- roots.add(resolved);
508
- }
509
- }
510
- catch {
511
- // Ignore unreadable discovery roots.
512
- }
513
- };
514
- const home = os.homedir();
515
- const cwdRoot = path.parse(process.cwd()).root || path.parse(home).root || '';
516
- add(process.cwd());
517
- add(home);
518
- add(path.join(home, 'Desktop'));
519
- add(path.join(home, 'Documents'));
520
- add(process.env.USERPROFILE);
521
- add(process.env.OneDrive);
522
- add(process.env.OneDriveCommercial);
523
- add(process.env.OneDriveConsumer);
524
- if (process.env.OneDrive) {
525
- add(path.join(process.env.OneDrive, 'Desktop'));
526
- add(path.join(process.env.OneDrive, 'Documents'));
527
- }
528
- if (cwdRoot) {
529
- add(cwdRoot);
530
- add(path.join(cwdRoot, 'vigthoria'));
531
- add(path.join(cwdRoot, 'Vigthoria'));
532
- }
533
- return Array.from(roots);
534
- }
535
- findPromptDirectoryByName(rawPath) {
536
- if (os.platform() !== 'win32')
537
- return null;
538
- const normalized = String(rawPath || '').trim().replace(/^\/+/, '').replace(/[\\/]+/g, path.sep);
539
- if (!normalized)
540
- return null;
541
- const wantedParts = normalized.split(/[\\/]+/).filter(Boolean);
542
- const wantedName = wantedParts[wantedParts.length - 1];
543
- if (!wantedName)
544
- return null;
545
- const maxDepth = 3;
546
- const maxEntries = 2500;
547
- const roots = this.getWindowsPromptPathRoots();
548
- for (const root of roots) {
549
- let visited = 0;
550
- const direct = path.join(root, normalized);
396
+ extractExplicitLocalPath(prompt) {
397
+ // Try to extract Windows paths (C:\ D:\ etc.)
398
+ // Match drive letter followed by colon and path
399
+ const windowsMatch = prompt.match(/([A-Za-z]:[\\\/][^\s<>"|?*]*)/);
400
+ if (windowsMatch) {
401
+ const candidatePath = windowsMatch[1].replace(/\\/g, '\\').trim();
551
402
  try {
552
- if (fs.existsSync(direct) && fs.statSync(direct).isDirectory()) {
553
- return direct;
403
+ const resolved = require('path').resolve(candidatePath);
404
+ const fs = require('fs');
405
+ if (fs.existsSync(resolved)) {
406
+ return resolved;
554
407
  }
555
408
  }
556
409
  catch {
557
- // Continue with bounded search.
558
- }
559
- const stack = [{ dir: root, depth: 0 }];
560
- while (stack.length > 0 && visited < maxEntries) {
561
- const current = stack.pop();
562
- if (!current || current.depth > maxDepth)
563
- continue;
564
- let entries;
565
- try {
566
- entries = fs.readdirSync(current.dir, { withFileTypes: true });
567
- }
568
- catch {
569
- continue;
570
- }
571
- visited += entries.length;
572
- for (const entry of entries) {
573
- if (!entry.isDirectory())
574
- continue;
575
- if (entry.name === 'node_modules' || entry.name === '.git' || entry.name.startsWith('$'))
576
- continue;
577
- const fullPath = path.join(current.dir, entry.name);
578
- if (entry.name.toLowerCase() === wantedName.toLowerCase()) {
579
- if (wantedParts.length === 1 || fullPath.toLowerCase().endsWith(normalized.toLowerCase())) {
580
- return fullPath;
410
+ // Path resolution failed
411
+ }
412
+ }
413
+ // Try to extract Unix paths: / followed by any characters until we hit quotes, angle brackets, pipes, or sentence boundaries
414
+ // Supports spaces, special chars, camelCase, etc. (anything a filesystem path might have)
415
+ if (!prompt.includes('http://') && !prompt.includes('https://') && !prompt.includes('ftp://')) {
416
+ // Match / and everything after it until we hit delimiters or common sentence words
417
+ const unixMatch = prompt.match(/(\/(?:[^\s<>"|?*]|\s+(?![^\s<>"|?*]))*)/);
418
+ if (unixMatch) {
419
+ let candidatePath = unixMatch[1].trim();
420
+ // Remove trailing common English words that aren't part of path
421
+ candidatePath = candidatePath.replace(/\s+(and|or|at|in|the|to|for|with|from|by|on)$/i, '').trim();
422
+ // Remove trailing punctuation
423
+ candidatePath = candidatePath.replace(/[.,;!?:)\]]*$/, '').trim();
424
+ if (candidatePath.length > 1) {
425
+ try {
426
+ const resolved = require('path').resolve(candidatePath);
427
+ const fs = require('fs');
428
+ if (fs.existsSync(resolved)) {
429
+ return resolved;
581
430
  }
582
431
  }
583
- if (current.depth < maxDepth) {
584
- stack.push({ dir: fullPath, depth: current.depth + 1 });
432
+ catch {
433
+ // Path resolution failed
585
434
  }
586
435
  }
587
436
  }
588
437
  }
589
438
  return null;
590
439
  }
591
- extractExplicitLocalPath(prompt) {
592
- const resolveExistingPath = (rawPath) => {
593
- const trimmed = String(rawPath || '').trim().replace(/^['"]|['"]$/g, '');
594
- if (!trimmed)
595
- return null;
596
- const candidates = [];
597
- candidates.push(path.resolve(trimmed));
598
- if (os.platform() === 'win32' && /^\/[A-Za-z0-9._ -]/.test(trimmed)) {
599
- const withoutLeadingSlash = trimmed.replace(/^\/+/, '');
600
- const cwdRoot = path.parse(process.cwd()).root || '';
601
- // Allow prompts like "/Vigthoria Games" to resolve as "C:/Vigthoria/Games".
602
- const asSegments = withoutLeadingSlash.replace(/\s+/g, path.sep);
603
- for (const root of this.getWindowsPromptPathRoots()) {
604
- candidates.push(path.resolve(root, withoutLeadingSlash));
605
- candidates.push(path.resolve(root, asSegments));
606
- const segmentParts = asSegments.split(/[\\/]+/).filter(Boolean);
607
- if (segmentParts.length > 1 && path.basename(root).toLowerCase() === segmentParts[0].toLowerCase()) {
608
- candidates.push(path.resolve(root, ...segmentParts.slice(1)));
609
- }
610
- }
611
- if (cwdRoot && !candidates.includes(path.resolve(cwdRoot, withoutLeadingSlash))) {
612
- candidates.push(path.resolve(cwdRoot, withoutLeadingSlash));
613
- candidates.push(path.resolve(cwdRoot, asSegments));
614
- }
615
- }
616
- for (const candidate of candidates) {
617
- try {
618
- if (fs.existsSync(candidate)) {
619
- return candidate;
620
- }
621
- }
622
- catch {
623
- // Continue trying other normalized candidates.
624
- }
625
- }
626
- const discovered = this.findPromptDirectoryByName(trimmed);
627
- if (discovered)
628
- return discovered;
629
- return null;
630
- };
631
- // Quoted paths first (supports spaces safely).
632
- const quotedPatterns = [
633
- /"([A-Za-z]:[\\/][^"\r\n]+)"/,
634
- /'([A-Za-z]:[\\/][^'\r\n]+)'/,
635
- /"(\/[^"]+)"/,
636
- /'(\/[^']+)'/,
637
- ];
638
- for (const pattern of quotedPatterns) {
639
- const match = prompt.match(pattern);
640
- if (match?.[1]) {
641
- const resolved = resolveExistingPath(match[1]);
642
- if (resolved)
643
- return resolved;
644
- }
645
- }
646
- // Unquoted Windows path with optional spaces, stopping before instruction connectors.
647
- const windowsMatch = prompt.match(/(?:^|\s)([A-Za-z]:[\\/][^<>"|?*\r\n]+?)(?=(?:\s+(?:and|then|to|for|with|where|that|which|who|make|create|build|analyse|analyze)\b|\s{2,}|[.,;!?)]|$))/i);
648
- if (windowsMatch?.[1]) {
649
- const resolved = resolveExistingPath(windowsMatch[1]);
650
- if (resolved)
651
- return resolved;
652
- }
653
- // Unix-style absolute path with optional spaces (but not URLs).
654
- if (!/(https?|ftp):\/\//i.test(prompt)) {
655
- const unixMatch = prompt.match(/(?:^|\s)(\/[a-zA-Z0-9._\-/ ]+?)(?=(?:\s+(?:and|then|to|for|with|where|that|which|who|make|create|build|analyse|analyze)\b|\s{2,}|[.,;!?)]|$))/i);
656
- if (unixMatch?.[1]) {
657
- const candidatePath = unixMatch[1]
658
- .replace(/\s+(and|or|at|in|the|to|for|with|from|by|on)$/i, '')
659
- .replace(/[.,;!?:)\]]*$/, '')
660
- .trim();
661
- if (candidatePath.length > 1) {
662
- const resolved = resolveExistingPath(candidatePath);
663
- if (resolved)
664
- return resolved;
665
- }
666
- }
667
- }
668
- return null;
669
- }
670
440
  isUnscopedPromptPathOverrideAllowed() {
671
441
  return /^(1|true|yes)$/i.test(String(process.env.VIGTHORIA_ALLOW_UNSCOPED_PROMPT_PATHS || ''));
672
442
  }
@@ -713,24 +483,18 @@ export class ChatCommand {
713
483
  if (this.isUnscopedPromptPathOverrideAllowed()) {
714
484
  return candidate;
715
485
  }
716
- const runtime = this.getRuntimeEnvironmentContext();
717
- // On local-machine CLI sessions, allow explicit absolute paths from user prompts.
718
- // The path still must exist on disk (validated by extractExplicitLocalPath).
719
- if (runtime.machineScope === 'local-machine') {
720
- return candidate;
721
- }
722
486
  const allowedRoots = this.getPromptPathAllowedRoots(baseWorkspace);
723
487
  const isAllowed = allowedRoots.some((root) => this.isPathWithinRoot(candidate, root));
724
488
  if (isAllowed) {
725
489
  return candidate;
726
490
  }
727
491
  if (!this.jsonOutput) {
728
- console.log(chalk.yellow(`Ignoring path outside allowed workspace roots: ${candidate}`));
492
+ console.log(chalk_1.default.yellow(`Ignoring path outside allowed workspace roots: ${candidate}`));
729
493
  if (allowedRoots.length > 0) {
730
494
  const displayRoots = allowedRoots.map((root) => root.replace(/\\/g, '/')).join(', ');
731
- console.log(chalk.gray(`Allowed roots: ${displayRoots}`));
495
+ console.log(chalk_1.default.gray(`Allowed roots: ${displayRoots}`));
732
496
  }
733
- console.log(chalk.gray('To allow unrestricted prompt path overrides, set VIGTHORIA_ALLOW_UNSCOPED_PROMPT_PATHS=1.'));
497
+ console.log(chalk_1.default.gray('To allow unrestricted prompt path overrides, set VIGTHORIA_ALLOW_UNSCOPED_PROMPT_PATHS=1.'));
734
498
  }
735
499
  return null;
736
500
  }
@@ -781,36 +545,20 @@ export class ChatCommand {
781
545
  inferAgentTaskType(prompt) {
782
546
  if (this.isDiagnosticPrompt(prompt))
783
547
  return 'debugging';
784
- if (this.isImplementationPrompt(prompt)) {
785
- return /\b(game|html5|pacman|rogue|playable)\b/i.test(prompt) ? 'game-build' : 'implementation';
786
- }
787
- if (/^(what|which|how many|list|show|check|inspect|analyze|analyse|audit|explain|describe|summarize|summarise|review|overview|find|count|read|look at|tell me)/i.test(prompt.trim()))
548
+ if (this.isAnalysisLookupPrompt(prompt))
549
+ return 'analysis';
550
+ if (/\b(analy[sz]e|analyse|audit|review|inspect|identify\s+gaps?|gap\s+analysis|production\s+blockers?|read[\s-]?only)\b/i.test(prompt)) {
788
551
  return 'analysis';
789
- return 'implementation';
790
- }
791
- bindPromptWorkspace(prompt) {
792
- const resolved = this.resolvePromptWorkspacePath(prompt, this.currentProjectPath);
793
- if (!resolved) {
794
- return;
795
- }
796
- const normalizedResolved = path.resolve(resolved);
797
- const normalizedCurrent = path.resolve(this.currentProjectPath);
798
- if (normalizedResolved === normalizedCurrent) {
799
- return;
800
552
  }
801
- if (!this.jsonOutput) {
802
- console.log(chalk.cyan(`📁 Workspace from prompt: ${normalizedResolved}`));
553
+ if (/\b(implement|build|create|write|fix|repair|scaffold|deploy|add feature)\b/i.test(prompt)
554
+ && !/\b(analy[sz]e|analyse|audit|review|inspect)\b/i.test(prompt)) {
555
+ return /\b(game|html5|pacman|rogue|playable)\b/i.test(prompt) ? 'game-build' : 'implementation';
803
556
  }
804
- this.currentProjectPath = normalizedResolved;
805
- this.tools?.setWorkspaceRoot(normalizedResolved);
806
- this.projectMemory = new ProjectMemoryService(normalizedResolved);
557
+ return 'analysis';
807
558
  }
808
559
  buildTaskShapingInstructions(prompt) {
809
560
  const instructions = [];
810
561
  const runtime = this.getRuntimeEnvironmentContext();
811
- if (runtime.machineScope === 'local-machine') {
812
- instructions.push(`Execution environment: local user machine (${runtime.platform}).`, `Project workspace root on this machine: ${runtime.workspacePath}.`, 'All list_dir, read_file, grep, glob, and bash tools operate on this local filesystem — not a remote server copy.', 'Use paths relative to that root only. Never prefix paths with workspace/ or repeat the workspace folder name.', 'Start with list_dir on "." to discover the real folder structure before reading files.', 'If read_file fails, use list_dir or glob to locate the correct relative path, then retry read_file.');
813
- }
814
562
  // Platform-aware routing hints
815
563
  if (runtime.platform === 'windows') {
816
564
  instructions.push('Platform: Windows. Use list_dir, glob, read_file, and the grep tool for searching.', 'The grep tool handles Windows automatically — do not use bash to call grep, findstr, or Select-String manually.', 'Do not use bash for Unix commands (cat, head, tail, awk, sed, wc).', 'Use read_file to inspect file contents instead of shell commands.', 'All file paths use forward slashes internally.');
@@ -821,6 +569,9 @@ export class ChatCommand {
821
569
  else if (runtime.platform === 'linux') {
822
570
  instructions.push('Platform: Linux. Prefer list_dir, grep, and read_file for grounded repository inspection.', 'Use exact file paths from tool output when reporting findings or fixes.');
823
571
  }
572
+ if (this.isAnalysisLookupPrompt(prompt)) {
573
+ instructions.push('Read-only analysis mode is active.', 'Inspect the workspace with tools and produce a grounded report of findings, gaps, and blockers.', 'Do not create, modify, or delete files unless the user explicitly asked for changes.');
574
+ }
824
575
  if (this.isDiagnosticPrompt(prompt)) {
825
576
  instructions.push('Diagnostic mode is active.', 'Treat this as a debugging task, not a generic code review or feature request.', 'Start with concrete evidence: logs, runtime errors, config, launch files, and exact symbol references.', 'If log files exist, inspect them before proposing fixes.', 'Do not claim a file, definition, asset, or symbol is missing until you verify that with tools.', 'If a prior diagnosis mentioned a missing symbol or YAML entry, re-check the actual files before repeating it.', 'Prefer grep plus read_file around the exact references involved in the failure.', 'Separate your reasoning into: Evidence, Confirmed Cause, and Remaining Hypotheses.', 'Do not suggest speculative fixes when the current evidence contradicts them.', 'CRITICAL GROUNDING RULE: Every key name, variable name, symbol, or identifier you mention in your final answer MUST appear verbatim in the tool output you received. If a key/symbol does NOT appear in tool output, you MUST NOT mention it as involved in any conflict or issue.', 'CROSS-FILE ATTRIBUTION: When reporting conflicts between two files, a key/symbol is conflicting ONLY if it appears in BOTH files. Read each file carefully and list only the exact keys that appear in the relevant handler/function of EACH file. Do not assume that because one file handles a key, the other file does too.', 'When reporting conflicts between files, cite the exact file name, line number, and the exact string/key from the tool output. Do not paraphrase or substitute key names.', 'Before concluding, re-check: (1) does every key/symbol in my answer actually appear in the evidence I gathered? (2) for each claimed conflict, did I verify the key appears in BOTH files? If not, correct your answer.');
826
577
  }
@@ -845,7 +596,7 @@ export class ChatCommand {
845
596
  }
846
597
  try {
847
598
  if (!this.projectMemory) {
848
- this.projectMemory = new ProjectMemoryService(this.currentProjectPath);
599
+ this.projectMemory = new project_memory_js_1.ProjectMemoryService(this.currentProjectPath);
849
600
  }
850
601
  const status = this.projectMemory.getStatus();
851
602
  const context = this.projectMemory.buildContextForPrompt(prompt);
@@ -871,7 +622,7 @@ export class ChatCommand {
871
622
  }
872
623
  try {
873
624
  if (!this.projectMemory) {
874
- this.projectMemory = new ProjectMemoryService(this.currentProjectPath);
625
+ this.projectMemory = new project_memory_js_1.ProjectMemoryService(this.currentProjectPath);
875
626
  }
876
627
  this.projectMemory.remember(type, text, { source: 'vigthoria-cli', mode, model: this.currentModel });
877
628
  }
@@ -893,7 +644,7 @@ export class ChatCommand {
893
644
  const devtoolsBridgeAllowed = /^(1|true|yes)$/i.test(String(process.env.VIGTHORIA_DEVTOOLS_BRIDGE_ALLOWED || process.env.VIGTHORIA_BRIDGE_ALLOWED || ''));
894
645
  if (!devtoolsBridgeAllowed) {
895
646
  if (!this.jsonOutput) {
896
- console.log(chalk.yellow('Browser task detected. DevTools Bridge is opt-in; the agent will ask before relying on local browser tooling.'));
647
+ console.log(chalk_1.default.yellow('Browser task detected. DevTools Bridge is opt-in; the agent will ask before relying on local browser tooling.'));
897
648
  }
898
649
  return {
899
650
  ...runtimeContext,
@@ -905,10 +656,10 @@ export class ChatCommand {
905
656
  }
906
657
  const bridgeStatus = await this.callApi('Checking DevTools Bridge status', () => this.api.getDevtoolsBridgeStatus(), 0);
907
658
  if (!this.jsonOutput && bridgeStatus.ok) {
908
- console.log(chalk.gray(`Browser task detected. DevTools Bridge is reachable at ${bridgeStatus.endpoint}.`));
659
+ console.log(chalk_1.default.gray(`Browser task detected. DevTools Bridge is reachable at ${bridgeStatus.endpoint}.`));
909
660
  }
910
661
  else if (!this.jsonOutput) {
911
- console.log(chalk.yellow(`Browser task detected. DevTools Bridge is not running at ${bridgeStatus.endpoint}.`));
662
+ console.log(chalk_1.default.yellow(`Browser task detected. DevTools Bridge is not running at ${bridgeStatus.endpoint}.`));
912
663
  }
913
664
  return {
914
665
  ...runtimeContext,
@@ -922,9 +673,6 @@ export class ChatCommand {
922
673
  v3ToolCallCount = 0;
923
674
  v3LastActivity = Date.now();
924
675
  v3StreamingStarted = false;
925
- v3StreamedTextBuffer = '';
926
- v3LiveToolEvidence = [];
927
- v3PendingToolCalls = [];
928
676
  /**
929
677
  * Strip server-internal path prefixes from tool output strings.
930
678
  * Prevents exposing paths like /var/www/V3-Code-Agent/... to end users.
@@ -932,7 +680,7 @@ export class ChatCommand {
932
680
  sanitizeServerPath(text) {
933
681
  if (!text)
934
682
  return text;
935
- return sanitizeUserFacingPathText(this.stripHiddenThoughtBlocks(text));
683
+ return (0, api_js_1.sanitizeUserFacingPathText)(this.stripHiddenThoughtBlocks(text));
936
684
  }
937
685
  stripHiddenThoughtBlocks(text) {
938
686
  if (!text)
@@ -974,12 +722,12 @@ export class ChatCommand {
974
722
  const source = (event && typeof event === 'object' && !Buffer.isBuffer(event) && !(event instanceof Uint8Array))
975
723
  ? (event.body ?? event.stream ?? event.response ?? event)
976
724
  : event;
977
- for await (const chunk of robustifyStreamResponse(source)) {
725
+ for await (const chunk of (0, tools_js_1.robustifyStreamResponse)(source)) {
978
726
  this.v3LastActivity = Date.now();
979
727
  if (chunk.type === 'error') {
980
728
  if (spinner.isSpinning)
981
729
  spinner.stop();
982
- process.stderr.write(chalk.red(`\nStream error: ${chunk.content}\n`));
730
+ process.stderr.write(chalk_1.default.red(`\nStream error: ${chunk.content}\n`));
983
731
  continue;
984
732
  }
985
733
  if (chunk.content) {
@@ -991,7 +739,7 @@ export class ChatCommand {
991
739
  const message = error instanceof Error ? error.message : String(error);
992
740
  if (spinner.isSpinning)
993
741
  spinner.stop();
994
- process.stderr.write(chalk.red(`\nStream error: ${message}\n`));
742
+ process.stderr.write(chalk_1.default.red(`\nStream error: ${message}\n`));
995
743
  }
996
744
  }
997
745
  writeV3StreamText(spinner, text) {
@@ -999,7 +747,6 @@ export class ChatCommand {
999
747
  if (!safeText) {
1000
748
  return;
1001
749
  }
1002
- this.v3StreamedTextBuffer += safeText;
1003
750
  if (!this.v3StreamingStarted) {
1004
751
  this.v3StreamingStarted = true;
1005
752
  spinner.stop();
@@ -1012,124 +759,13 @@ export class ChatCommand {
1012
759
  }
1013
760
  process.stdout.write(safeText);
1014
761
  }
1015
- isGenericV3AgentContent(text) {
1016
- const value = String(text || '').trim();
1017
- return !value || /^(v3 agent workflow completed\.?|task completed|agent run finished|workflow completed\.?)$/i.test(value);
1018
- }
1019
- hasAlreadyStreamedV3Content(text) {
1020
- const value = String(text || '').trim();
1021
- if (!value)
1022
- return true;
1023
- return this.v3StreamedTextBuffer.includes(value) || value.includes(this.v3StreamedTextBuffer.trim());
1024
- }
1025
- isThinV3Summary(text) {
1026
- const value = String(text || '').trim();
1027
- if (!value)
1028
- return true;
1029
- if (/^#\s*Workspace overview/m.test(value) || /^## Workspace analysis \(from local file inspection\)/m.test(value)) {
1030
- return false;
1031
- }
1032
- if (this.isGenericV3AgentContent(value))
1033
- return true;
1034
- if (/^The V3 agent finished without emitting a dedicated final answer/i.test(value)) {
1035
- return !/(## Files read|## Directories inspected|### )/i.test(value);
1036
- }
1037
- return value.length < 120;
1038
- }
1039
- shouldPrintV3FinalContent(text) {
1040
- if (!text || this.isGenericV3AgentContent(text)) {
1041
- return false;
1042
- }
1043
- const value = text.trim();
1044
- if (/^## Workspace analysis \(from local file inspection\)/m.test(value)) {
1045
- return true;
1046
- }
1047
- if (/Reconstructed task summary|Workspace analysis \(from local file inspection\)/i.test(value)) {
1048
- return true;
1049
- }
1050
- if (!this.v3StreamingStarted) {
1051
- return true;
1052
- }
1053
- return !this.hasAlreadyStreamedV3Content(value);
1054
- }
1055
- rememberV3ToolEvidence(event, args = {}) {
1056
- const output = typeof event?.output === 'string' ? event.output.trim() : '';
1057
- const errorText = typeof event?.error === 'string' ? event.error.trim() : '';
1058
- const combined = output || errorText;
1059
- if (!combined) {
1060
- return;
1061
- }
1062
- const target = String(args.path || args.file_path || args.file || args.target || event?.target || '').trim();
1063
- this.v3LiveToolEvidence.push({
1064
- name: String(event?.name || event?.tool || 'unknown_tool'),
1065
- target: target || undefined,
1066
- arguments: args,
1067
- output: combined,
1068
- success: event?.success !== false,
1069
- });
1070
- }
1071
- buildUserFacingV3RunReport(prompt, workspacePath, options = {}) {
1072
- const evidenceBody = this.api.formatV3AgentResponse({
1073
- events: [],
1074
- liveToolEvidence: this.v3LiveToolEvidence,
1075
- });
1076
- const successes = this.v3LiveToolEvidence.filter((entry) => entry.success !== false);
1077
- const failures = this.v3LiveToolEvidence.filter((entry) => entry.success === false);
1078
- if (!evidenceBody && successes.length === 0 && failures.length === 0) {
1079
- return [
1080
- '# Agent run summary',
1081
- '',
1082
- `**Workspace:** ${workspacePath}`,
1083
- `**Your request:** ${prompt.trim()}`,
1084
- '',
1085
- 'The agent finished without successfully reading local files, so no grounded overview could be built.',
1086
- options.serverNote ? `\n**Server note:** ${options.serverNote}` : '',
1087
- '',
1088
- 'Try `/continue` with: "Read Vigthoria-dominion/package.json, game.js, src/Game.js, and src/factions/, then write a full overview."',
1089
- ].filter(Boolean).join('\n');
1090
- }
1091
- const lines = [
1092
- '# Workspace overview',
1093
- '',
1094
- `**Workspace:** ${workspacePath}`,
1095
- `**Your request:** ${prompt.trim()}`,
1096
- options.partial
1097
- ? '**Status:** Partial — the agent used its iteration budget before writing a final narrative. The report below is rebuilt from files it actually read on your machine.'
1098
- : '**Status:** Complete — summary rebuilt from local file inspection.',
1099
- '',
1100
- evidenceBody || '_No readable file excerpts were captured._',
1101
- ];
1102
- if (failures.length > 0) {
1103
- const uniqueFails = new Map();
1104
- for (const entry of failures) {
1105
- const failPath = entry.target || entry.arguments?.path || entry.name;
1106
- if (!uniqueFails.has(failPath)) {
1107
- uniqueFails.set(failPath, entry.output.split('\n').find(Boolean)?.slice(0, 140) || entry.output.slice(0, 140));
1108
- }
1109
- }
1110
- if (uniqueFails.size > 0 && !/## Paths not found/i.test(evidenceBody || '')) {
1111
- lines.push('', '## Paths not found (exploration misses)', ...[...uniqueFails.entries()].slice(0, 12).map(([failPath, detail]) => `- \`${failPath}\`${detail ? ` — ${detail}` : ''}`), '', '_These are usually incorrect guesses (for example `Vigthoria-dominion/entities` instead of `Vigthoria-dominion/src/entities`). They do not mean your project is broken._');
1112
- }
1113
- }
1114
- lines.push('', '---', 'Use `/continue` if you want a longer narrative summary or a deep dive into a specific subfolder.');
1115
- return lines.join('\n');
1116
- }
1117
- printV3UserReport(report) {
1118
- const value = String(report || '').trim();
1119
- if (!value || this.isGenericV3AgentContent(value)) {
1120
- return;
1121
- }
1122
- console.log();
1123
- console.log(value);
1124
- console.log();
1125
- }
1126
762
  updateV3AgentSpinner(spinner, event) {
1127
763
  if (this.isRawV3StreamPayload(event)) {
1128
764
  this.consumeV3StreamPayload(spinner, event).catch((error) => {
1129
765
  const message = error instanceof Error ? error.message : String(error);
1130
766
  if (spinner.isSpinning)
1131
767
  spinner.stop();
1132
- process.stderr.write(chalk.red(`\nStream error: ${message}\n`));
768
+ process.stderr.write(chalk_1.default.red(`\nStream error: ${message}\n`));
1133
769
  });
1134
770
  return;
1135
771
  }
@@ -1140,38 +776,23 @@ export class ChatCommand {
1140
776
  if (event.type === 'tool_call') {
1141
777
  this.v3ToolCallCount += 1;
1142
778
  const toolDesc = this.describeV3AgentTool(event.tool || event.name || event.tool_name);
1143
- const toolTarget = event.arguments?.path || event.arguments?.file_path || event.arguments?.pattern || event.arguments?.query || '';
779
+ const toolTarget = event.arguments?.path || event.arguments?.file_path || event.arguments?.pattern || '';
1144
780
  const sanitizedTarget = this.sanitizeServerPath(String(toolTarget));
1145
781
  const shortTarget = sanitizedTarget ? ` → ${sanitizedTarget.replace(/\\/g, '/').split('/').slice(-2).join('/')}` : '';
1146
- const stepLabel = chalk.cyan(` [${this.v3IterationCount}/${this.v3ToolCallCount}]`) + ` ${toolDesc}${shortTarget}`;
782
+ const stepLabel = chalk_1.default.cyan(` [${this.v3IterationCount}/${this.v3ToolCallCount}]`) + ` ${toolDesc}${shortTarget}`;
1147
783
  if (spinner.isSpinning)
1148
784
  spinner.stop();
1149
785
  process.stderr.write(stepLabel + '\n');
1150
786
  // Show extra detail for key tools
1151
787
  const args = event.arguments || {};
1152
788
  const toolName = event.name || event.tool || '';
1153
- this.v3PendingToolCalls.push({
1154
- name: String(toolName || 'unknown_tool'),
1155
- args: args && typeof args === 'object' ? args : {},
1156
- });
1157
- if (toolName === 'search_files') {
1158
- const pattern = String(args.pattern || args.query || '').trim();
1159
- const searchPath = String(args.path || '.').trim();
1160
- process.stderr.write(chalk.gray(` search: ${pattern || '(empty)'} in ${this.sanitizeServerPath(searchPath)}\n`));
1161
- }
1162
- else if (toolName === 'list_directory') {
1163
- process.stderr.write(chalk.gray(` list: ${this.sanitizeServerPath(String(args.path || '.'))}${args.recursive ? ' (recursive)' : ''}\n`));
1164
- }
1165
- else if (toolName === 'read_file') {
1166
- process.stderr.write(chalk.gray(` read: ${this.sanitizeServerPath(String(args.path || args.file_path || ''))}\n`));
1167
- }
1168
789
  if ((toolName === 'write_file' || toolName === 'edit_file') && typeof args.content === 'string') {
1169
790
  const len = args.content.length;
1170
- process.stderr.write(chalk.gray(` ${len > 1000 ? Math.round(len / 1024) + ' KB' : len + ' bytes'} content\n`));
791
+ process.stderr.write(chalk_1.default.gray(` ${len > 1000 ? Math.round(len / 1024) + ' KB' : len + ' bytes'} content\n`));
1171
792
  }
1172
793
  else if (toolName === 'bash' && typeof args.command === 'string') {
1173
794
  const command = this.sanitizeServerPath(args.command);
1174
- process.stderr.write(chalk.gray(` $ ${command.slice(0, 120)}${command.length > 120 ? '…' : ''}\n`));
795
+ process.stderr.write(chalk_1.default.gray(` $ ${command.slice(0, 120)}${command.length > 120 ? '…' : ''}\n`));
1175
796
  }
1176
797
  spinner.start();
1177
798
  spinner.text = `Running ${toolDesc}...`;
@@ -1180,28 +801,21 @@ export class ChatCommand {
1180
801
  if (event.type === 'tool_result') {
1181
802
  const success = event.success !== false;
1182
803
  const toolName = event.name || event.tool || '';
1183
- const indicator = success ? chalk.green(' ✓') : chalk.red(' ✗');
804
+ const indicator = success ? chalk_1.default.green(' ✓') : chalk_1.default.red(' ✗');
1184
805
  if (spinner.isSpinning)
1185
806
  spinner.stop();
1186
807
  process.stderr.write(`${indicator} ${toolName}\n`);
1187
808
  // Show output for failures, or brief summary for successes — sanitize server paths
1188
809
  const rawOutput = typeof event.output === 'string' ? event.output.trim() : '';
1189
810
  const output = this.sanitizeServerPath(rawOutput);
1190
- const pendingIndex = this.v3PendingToolCalls.findIndex((call) => call.name === toolName);
1191
- const pendingCall = pendingIndex >= 0
1192
- ? this.v3PendingToolCalls.splice(pendingIndex, 1)[0]
1193
- : this.v3PendingToolCalls.shift();
1194
- this.rememberV3ToolEvidence(event, pendingCall?.args || {});
1195
811
  if (!success && output) {
1196
812
  const sanitizedError = this.sanitizeServerPath(typeof event.error === 'string' ? event.error : output);
1197
813
  const lines = sanitizedError.split('\n').slice(0, 4);
1198
- process.stderr.write(chalk.red(` ${lines.join('\n ')}\n`));
814
+ process.stderr.write(chalk_1.default.red(` ${lines.join('\n ')}\n`));
1199
815
  }
1200
816
  else if (success && output && output.length > 0) {
1201
- const outputLines = output.split('\n').map((line) => line.trimEnd()).filter(Boolean);
1202
- const firstMeaningfulLine = outputLines.find((line) => !/^## Untrusted workspace data boundary/i.test(line)) || outputLines[0] || '';
1203
- const brief = firstMeaningfulLine.slice(0, 160);
1204
- process.stderr.write(chalk.gray(` ${brief}${output.length > 120 ? '…' : ''}\n`));
817
+ const brief = output.split('\n')[0].slice(0, 120);
818
+ process.stderr.write(chalk_1.default.gray(` ${brief}${output.length > 120 ? '…' : ''}\n`));
1205
819
  }
1206
820
  spinner.start();
1207
821
  spinner.text = 'Next step...';
@@ -1211,27 +825,12 @@ export class ChatCommand {
1211
825
  spinner.text = this.sanitizeServerPath(String(event.content || 'Routing to V3 Agent...'));
1212
826
  return;
1213
827
  }
1214
- if (event.type === 'queued') {
1215
- if (spinner.isSpinning)
1216
- spinner.stop();
1217
- process.stderr.write(chalk.yellow(' [Queue] ') + this.sanitizeServerPath(String(event.message || 'Waiting for V3 capacity...')) + '\n');
1218
- spinner.start();
1219
- spinner.text = 'Waiting for V3...';
1220
- return;
1221
- }
1222
- if (event.type === 'heartbeat') {
1223
- spinner.text = 'V3 Agent is still thinking...';
1224
- return;
1225
- }
1226
828
  if (event.type === 'thinking') {
1227
829
  this.v3IterationCount += 1;
1228
830
  const iterText = this.sanitizeServerPath(event.content || '');
1229
831
  if (spinner.isSpinning)
1230
832
  spinner.stop();
1231
- process.stderr.write(chalk.cyan(`\n── Iteration ${this.v3IterationCount}${iterText ? '' : '...'} ──\n`));
1232
- if (iterText) {
1233
- process.stderr.write(chalk.gray(`${iterText}\n`));
1234
- }
833
+ process.stderr.write(chalk_1.default.cyan(`\n── ${iterText || `Iteration ${this.v3IterationCount}`} ──\n`));
1235
834
  spinner.start();
1236
835
  spinner.text = 'Analyzing...';
1237
836
  return;
@@ -1245,7 +844,7 @@ export class ChatCommand {
1245
844
  this.writeV3StreamText(spinner, text);
1246
845
  }
1247
846
  else {
1248
- spinner.text = chalk.cyan('[Response] ') + 'Writing response...';
847
+ spinner.text = chalk_1.default.cyan('[Response] ') + 'Writing response...';
1249
848
  }
1250
849
  return;
1251
850
  }
@@ -1263,15 +862,15 @@ export class ChatCommand {
1263
862
  const fail = event.tasks_failed ?? 0;
1264
863
  statLine += ` — ${ok} tasks done`;
1265
864
  if (fail > 0)
1266
- statLine += chalk.yellow(`, ${fail} failed`);
865
+ statLine += chalk_1.default.yellow(`, ${fail} failed`);
1267
866
  }
1268
- process.stderr.write(chalk.green(`\n✓ Complete`) + ` — ${statLine}\n`);
867
+ process.stderr.write(chalk_1.default.green(`\n✓ Complete`) + ` — ${statLine}\n`);
1269
868
  // Show seal quality score if available
1270
869
  if (event.seal_score && typeof event.seal_score.overall === 'number') {
1271
870
  const score = event.seal_score.overall;
1272
871
  const tier = event.seal_score.tier || '';
1273
- const scoreColor = score >= 7 ? chalk.green : score >= 5 ? chalk.yellow : chalk.red;
1274
- process.stderr.write(chalk.cyan(' [Quality] ') + scoreColor(`${score}/10`) + (tier ? chalk.gray(` (${tier})`) : '') + '\n');
872
+ const scoreColor = score >= 7 ? chalk_1.default.green : score >= 5 ? chalk_1.default.yellow : chalk_1.default.red;
873
+ process.stderr.write(chalk_1.default.cyan(' [Quality] ') + scoreColor(`${score}/10`) + (tier ? chalk_1.default.gray(` (${tier})`) : '') + '\n');
1275
874
  }
1276
875
  return;
1277
876
  }
@@ -1281,35 +880,40 @@ export class ChatCommand {
1281
880
  const quality = this.sanitizeServerPath(plan.quality_profile || '');
1282
881
  const status = typeof plan.status === 'string' ? plan.status : '';
1283
882
  const summary = typeof plan.summary === 'string' ? this.sanitizeServerPath(plan.summary) : '';
883
+ if (status === 'planning') {
884
+ const elapsed = Number.isFinite(Number(plan.elapsed_seconds)) ? Number(plan.elapsed_seconds) : 0;
885
+ spinner.text = elapsed > 0 ? `Planning... (${elapsed}s elapsed)` : 'Planning...';
886
+ return;
887
+ }
1284
888
  if (spinner.isSpinning)
1285
889
  spinner.stop();
1286
- process.stderr.write(chalk.cyan(` [Plan] `) + `Task: ${planKind || 'analyzing'}`);
890
+ process.stderr.write(chalk_1.default.cyan(` [Plan] `) + `Task: ${planKind || 'analyzing'}`);
1287
891
  if (quality)
1288
- process.stderr.write(chalk.gray(` (${quality})`));
892
+ process.stderr.write(chalk_1.default.gray(` (${quality})`));
1289
893
  process.stderr.write('\n');
1290
894
  if (summary) {
1291
- process.stderr.write(chalk.gray(` ${summary}\n`));
895
+ process.stderr.write(chalk_1.default.gray(` ${summary}\n`));
1292
896
  }
1293
897
  if (status === 'planning' && Number.isFinite(Number(plan.elapsed_seconds))) {
1294
- process.stderr.write(chalk.gray(` elapsed: ${plan.elapsed_seconds}s\n`));
898
+ process.stderr.write(chalk_1.default.gray(` elapsed: ${plan.elapsed_seconds}s\n`));
1295
899
  }
1296
900
  if (Array.isArray(plan.tasks) && plan.tasks.length > 0) {
1297
- process.stderr.write(chalk.gray(` ${plan.total_tasks || plan.tasks.length} tasks:\n`));
901
+ process.stderr.write(chalk_1.default.gray(` ${plan.total_tasks || plan.tasks.length} tasks:\n`));
1298
902
  for (const t of plan.tasks.slice(0, 10)) {
1299
903
  const targets = Array.isArray(t.targets) && t.targets.length ? ` → ${t.targets.map((target) => this.sanitizeServerPath(String(target))).join(', ')}` : '';
1300
- process.stderr.write(chalk.gray(` • ${this.sanitizeServerPath(String(t.title || t.id))}${targets}\n`));
904
+ process.stderr.write(chalk_1.default.gray(` • ${this.sanitizeServerPath(String(t.title || t.id))}${targets}\n`));
1301
905
  }
1302
906
  if (plan.tasks.length > 10) {
1303
- process.stderr.write(chalk.gray(` ... and ${plan.tasks.length - 10} more\n`));
907
+ process.stderr.write(chalk_1.default.gray(` ... and ${plan.tasks.length - 10} more\n`));
1304
908
  }
1305
909
  }
1306
910
  if (Array.isArray(plan.notes) && plan.notes.length > 0) {
1307
911
  for (const note of plan.notes.slice(0, 3)) {
1308
- process.stderr.write(chalk.gray(` note: ${this.sanitizeServerPath(String(note))}\n`));
912
+ process.stderr.write(chalk_1.default.gray(` note: ${this.sanitizeServerPath(String(note))}\n`));
1309
913
  }
1310
914
  }
1311
915
  if (Array.isArray(plan.target_files) && plan.target_files.length > 0) {
1312
- process.stderr.write(chalk.gray(` Files: ${plan.target_files.map((filePath) => this.sanitizeServerPath(String(filePath))).join(', ')}\n`));
916
+ process.stderr.write(chalk_1.default.gray(` Files: ${plan.target_files.map((filePath) => this.sanitizeServerPath(String(filePath))).join(', ')}\n`));
1313
917
  }
1314
918
  spinner.start();
1315
919
  spinner.text = status === 'planning' ? 'Planning...' : 'Executing plan...';
@@ -1318,7 +922,7 @@ export class ChatCommand {
1318
922
  if (event.type === 'executor_start') {
1319
923
  if (spinner.isSpinning)
1320
924
  spinner.stop();
1321
- process.stderr.write(chalk.cyan(' [Executor] ') + `Starting ${this.sanitizeServerPath(String(event.task_id || 'task'))}${event.title ? ` - ${this.sanitizeServerPath(String(event.title))}` : ''}
925
+ process.stderr.write(chalk_1.default.cyan(' [Executor] ') + `Starting ${this.sanitizeServerPath(String(event.task_id || 'task'))}${event.title ? ` - ${this.sanitizeServerPath(String(event.title))}` : ''}
1322
926
  `);
1323
927
  spinner.start();
1324
928
  spinner.text = 'Vigthoria Executor running...';
@@ -1327,8 +931,8 @@ export class ChatCommand {
1327
931
  if (event.type === 'executor_error') {
1328
932
  if (spinner.isSpinning)
1329
933
  spinner.stop();
1330
- const msg = sanitizeUserFacingErrorText(String(event.error || 'Executor error')) || 'Executor error';
1331
- process.stderr.write(chalk.red(' [Executor] ') + `Vigthoria Executor encountered an issue: ${msg}
934
+ const msg = (0, api_js_1.sanitizeUserFacingErrorText)(String(event.error || 'Executor error')) || 'Executor error';
935
+ process.stderr.write(chalk_1.default.red(' [Executor] ') + `Vigthoria Executor encountered an issue: ${msg}
1332
936
  `);
1333
937
  spinner.start();
1334
938
  spinner.text = 'Recovering executor...';
@@ -1341,11 +945,11 @@ export class ChatCommand {
1341
945
  const status = String(summary.status || 'completed');
1342
946
  const changed = Array.isArray(summary.changed_files) ? summary.changed_files.length : 0;
1343
947
  if (status === 'failed') {
1344
- process.stderr.write(chalk.red(' [Executor] ') + `Vigthoria Executor task failed${summary.task_id ? ` (${summary.task_id})` : ''}.
948
+ process.stderr.write(chalk_1.default.red(' [Executor] ') + `Vigthoria Executor task failed${summary.task_id ? ` (${summary.task_id})` : ''}.
1345
949
  `);
1346
950
  }
1347
951
  else {
1348
- process.stderr.write(chalk.green(' [Executor] ') + `Task completed${summary.task_id ? ` (${summary.task_id})` : ''}${changed ? `, ${changed} files changed` : ''}.
952
+ process.stderr.write(chalk_1.default.green(' [Executor] ') + `Task completed${summary.task_id ? ` (${summary.task_id})` : ''}${changed ? `, ${changed} files changed` : ''}.
1349
953
  `);
1350
954
  }
1351
955
  spinner.start();
@@ -1355,11 +959,11 @@ export class ChatCommand {
1355
959
  if (event.type === 'file_mutation') {
1356
960
  const rawPath = typeof event.path === 'string' ? this.sanitizeServerPath(event.path) : '';
1357
961
  const filePath = rawPath ? rawPath.replace(/\\/g, '/').split('/').slice(-2).join('/') : '';
1358
- const action = event.action === 'delete' ? chalk.red('deleted') : chalk.green('wrote');
962
+ const action = event.action === 'delete' ? chalk_1.default.red('deleted') : chalk_1.default.green('wrote');
1359
963
  if (filePath) {
1360
964
  if (spinner.isSpinning)
1361
965
  spinner.stop();
1362
- process.stderr.write(chalk.cyan(' [File] ') + `${action} ${filePath}\n`);
966
+ process.stderr.write(chalk_1.default.cyan(' [File] ') + `${action} ${filePath}\n`);
1363
967
  spinner.start();
1364
968
  }
1365
969
  return;
@@ -1368,23 +972,23 @@ export class ChatCommand {
1368
972
  if (event.checkpointed) {
1369
973
  if (spinner.isSpinning)
1370
974
  spinner.stop();
1371
- process.stderr.write(chalk.yellow(' [Checkpoint] ') + 'Budget reached — auto-continuing...\n');
975
+ process.stderr.write(chalk_1.default.yellow(' [Checkpoint] ') + 'Budget reached — auto-continuing...\n');
1372
976
  spinner.start();
1373
977
  }
1374
978
  else {
1375
979
  if (spinner.isSpinning)
1376
980
  spinner.stop();
1377
- const message = sanitizeUserFacingErrorText(String(event.message || 'Agent error')) || 'Agent error';
981
+ const message = (0, api_js_1.sanitizeUserFacingErrorText)(String(event.message || 'Agent error')) || 'Agent error';
1378
982
  const plannerLike = /plan|planner|dependency graph/i.test(message);
1379
983
  const executorLike = /executor|task failed|iteration/i.test(message);
1380
984
  if (plannerLike) {
1381
- process.stderr.write(chalk.red(' [Planner] ') + `Vigthoria Planner encountered an issue: ${this.sanitizeServerPath(message)}\n`);
985
+ process.stderr.write(chalk_1.default.red(' [Planner] ') + `Vigthoria Planner encountered an issue: ${this.sanitizeServerPath(message)}\n`);
1382
986
  }
1383
987
  else if (executorLike) {
1384
- process.stderr.write(chalk.red(' [Executor] ') + `Vigthoria Executor encountered an issue: ${this.sanitizeServerPath(message)}\n`);
988
+ process.stderr.write(chalk_1.default.red(' [Executor] ') + `Vigthoria Executor encountered an issue: ${this.sanitizeServerPath(message)}\n`);
1385
989
  }
1386
990
  else {
1387
- process.stderr.write(chalk.red(' [Error] ') + this.sanitizeServerPath(message) + '\n');
991
+ process.stderr.write(chalk_1.default.red(' [Error] ') + this.sanitizeServerPath(message) + '\n');
1388
992
  }
1389
993
  }
1390
994
  return;
@@ -1392,7 +996,7 @@ export class ChatCommand {
1392
996
  if (event.type === 'context') {
1393
997
  if (spinner.isSpinning)
1394
998
  spinner.stop();
1395
- process.stderr.write(chalk.cyan(' [Context] ') + 'Workspace bound\n');
999
+ process.stderr.write(chalk_1.default.cyan(' [Context] ') + 'Workspace bound\n');
1396
1000
  spinner.start();
1397
1001
  spinner.text = 'Starting agent...';
1398
1002
  return;
@@ -1402,7 +1006,7 @@ export class ChatCommand {
1402
1006
  this.v3ToolCallCount = 0;
1403
1007
  if (spinner.isSpinning)
1404
1008
  spinner.stop();
1405
- process.stderr.write(chalk.cyan(' [Start] ') + 'Agent initialized\n');
1009
+ process.stderr.write(chalk_1.default.cyan(' [Start] ') + 'Agent initialized\n');
1406
1010
  spinner.start();
1407
1011
  spinner.text = 'Working...';
1408
1012
  return;
@@ -1416,7 +1020,7 @@ export class ChatCommand {
1416
1020
  if (spinner.isSpinning)
1417
1021
  spinner.stop();
1418
1022
  if (fallbackMessage) {
1419
- process.stderr.write(chalk.cyan(' [V3] ') + `${fallbackMessage}\n`);
1023
+ process.stderr.write(chalk_1.default.cyan(' [V3] ') + `${fallbackMessage}\n`);
1420
1024
  }
1421
1025
  spinner.start();
1422
1026
  spinner.text = fallbackStatus || fallbackStage || 'Working...';
@@ -1430,7 +1034,7 @@ export class ChatCommand {
1430
1034
  if (event.type === 'started') {
1431
1035
  if (spinner.isSpinning)
1432
1036
  spinner.stop();
1433
- process.stderr.write(chalk.cyan(' [Operator] ') + 'Starting BMAD workflow...\n');
1037
+ process.stderr.write(chalk_1.default.cyan(' [Operator] ') + 'Starting BMAD workflow...\n');
1434
1038
  spinner.start();
1435
1039
  spinner.text = 'Connecting...';
1436
1040
  return;
@@ -1438,7 +1042,7 @@ export class ChatCommand {
1438
1042
  if (event.type === 'connected') {
1439
1043
  if (spinner.isSpinning)
1440
1044
  spinner.stop();
1441
- process.stderr.write(chalk.green(' ✓') + ' Connected to BMAD stream\n');
1045
+ process.stderr.write(chalk_1.default.green(' ✓') + ' Connected to BMAD stream\n');
1442
1046
  spinner.start();
1443
1047
  spinner.text = 'Working...';
1444
1048
  return;
@@ -1447,7 +1051,7 @@ export class ChatCommand {
1447
1051
  const agentName = event.agent || 'BMAD agent';
1448
1052
  if (spinner.isSpinning)
1449
1053
  spinner.stop();
1450
- process.stderr.write(chalk.cyan(` [Agent] `) + agentName + '\n');
1054
+ process.stderr.write(chalk_1.default.cyan(` [Agent] `) + agentName + '\n');
1451
1055
  spinner.start();
1452
1056
  spinner.text = `Running ${agentName}...`;
1453
1057
  return;
@@ -1457,7 +1061,7 @@ export class ChatCommand {
1457
1061
  const statusText = `${event.status || 'Working'}${progress}`;
1458
1062
  if (spinner.isSpinning)
1459
1063
  spinner.stop();
1460
- process.stderr.write(chalk.cyan(' [Status] ') + statusText + '\n');
1064
+ process.stderr.write(chalk_1.default.cyan(' [Status] ') + statusText + '\n');
1461
1065
  spinner.start();
1462
1066
  spinner.text = statusText;
1463
1067
  return;
@@ -1465,15 +1069,15 @@ export class ChatCommand {
1465
1069
  if (event.type === 'result') {
1466
1070
  if (spinner.isSpinning)
1467
1071
  spinner.stop();
1468
- process.stderr.write(chalk.green('\n ✓ Operator workflow complete\n'));
1072
+ process.stderr.write(chalk_1.default.green('\n ✓ Operator workflow complete\n'));
1469
1073
  return;
1470
1074
  }
1471
1075
  }
1472
1076
  constructor(config, logger) {
1473
1077
  this.config = config;
1474
1078
  this.logger = logger;
1475
- this.api = new APIClient(config, logger);
1476
- this.sessionManager = new SessionManager();
1079
+ this.api = new api_js_1.APIClient(config, logger);
1080
+ this.sessionManager = new session_js_1.SessionManager();
1477
1081
  }
1478
1082
  async run(options) {
1479
1083
  if (!this.config.isAuthenticated()) {
@@ -1486,7 +1090,6 @@ export class ChatCommand {
1486
1090
  }
1487
1091
  return;
1488
1092
  }
1489
- await this.config.refreshHubModelPreferences().catch(() => null);
1490
1093
  this.agentMode = options.agent === true;
1491
1094
  this.operatorMode = options.operator === true;
1492
1095
  this.workflowTarget = typeof options.workflow === 'string' && options.workflow.trim()
@@ -1500,17 +1103,30 @@ export class ChatCommand {
1500
1103
  this.currentModel = this.resolveInitialModel(options);
1501
1104
  this.applyNoAgentGovernance(String(options.model || this.currentModel || ''));
1502
1105
  this.currentProjectPath = this.resolveProjectPath(options);
1106
+ if ((this.agentMode || this.operatorMode) && (0, agent_session_menu_js_1.shouldShowAgentSessionMenu)(options)) {
1107
+ const sessionConfig = await (0, agent_session_menu_js_1.runAgentSessionMenu)({
1108
+ workspacePath: this.currentProjectPath,
1109
+ autoApprove: this.autoApprove,
1110
+ });
1111
+ this.currentProjectPath = sessionConfig.workspacePath;
1112
+ this.autoApprove = sessionConfig.autoApprove || this.autoApprove;
1113
+ options.autoApprove = this.autoApprove;
1114
+ if (sessionConfig.debugMode) {
1115
+ process.env.DEBUG = process.env.DEBUG || 'vigthoria:*';
1116
+ }
1117
+ console.log(chalk_1.default.green(`Using workspace: ${this.currentProjectPath}`));
1118
+ }
1503
1119
  if (this.jsonOutput && !options.prompt) {
1504
1120
  throw new Error('--json is only supported together with --prompt.');
1505
1121
  }
1506
1122
  this.ensureProjectWorkspace();
1507
1123
  this.directPromptMode = Boolean(options.prompt);
1508
1124
  this.directToolContinuationCount = 0;
1509
- this.tools = new AgenticTools(this.logger, this.currentProjectPath, async (action) => this.requestPermission(action), this.autoApprove);
1125
+ this.tools = new tools_js_1.AgenticTools(this.logger, this.currentProjectPath, async (action) => this.requestPermission(action), this.autoApprove);
1510
1126
  this.initializeSession(options.resume === true);
1511
1127
  // ── Commando Bridge: connect if --bridge was specified ──────────
1512
1128
  if (options.bridge) {
1513
- const bridgeClient = new BridgeClient({
1129
+ const bridgeClient = new bridge_client_js_1.BridgeClient({
1514
1130
  bridgeUrl: options.bridge,
1515
1131
  apiKey: this.config.get('authToken'),
1516
1132
  onAdminCommand: (cmd) => this.handleAdminCommand(cmd),
@@ -1545,7 +1161,7 @@ export class ChatCommand {
1545
1161
  const timeoutId = options.bridge && bridgePromptTimeoutMs > 0
1546
1162
  ? setTimeout(() => {
1547
1163
  timedOut = true;
1548
- const b = getBridgeClient();
1164
+ const b = (0, bridge_client_js_1.getBridgeClient)();
1549
1165
  if (b) {
1550
1166
  b.emitEnd({ reason: 'timeout' });
1551
1167
  b.destroy();
@@ -1560,7 +1176,7 @@ export class ChatCommand {
1560
1176
  if (timeoutId)
1561
1177
  clearTimeout(timeoutId);
1562
1178
  if (!timedOut) {
1563
- const bridge = getBridgeClient();
1179
+ const bridge = (0, bridge_client_js_1.getBridgeClient)();
1564
1180
  if (bridge) {
1565
1181
  bridge.emitEnd({ reason: 'prompt-complete' });
1566
1182
  bridge.destroy();
@@ -1572,7 +1188,7 @@ export class ChatCommand {
1572
1188
  process.exit(process.exitCode ?? 0);
1573
1189
  }
1574
1190
  await this.startInteractiveChat();
1575
- const bridge = getBridgeClient();
1191
+ const bridge = (0, bridge_client_js_1.getBridgeClient)();
1576
1192
  if (bridge) {
1577
1193
  bridge.emitEnd({ reason: 'interactive-exit' });
1578
1194
  bridge.destroy();
@@ -1582,24 +1198,24 @@ export class ChatCommand {
1582
1198
  handleAdminCommand(cmd) {
1583
1199
  switch (cmd.action) {
1584
1200
  case 'ping':
1585
- getBridgeClient()?.emitModelResponse({ model: this.currentModel, chars: 0, hasToolCalls: false, preview: 'pong' });
1201
+ (0, bridge_client_js_1.getBridgeClient)()?.emitModelResponse({ model: this.currentModel, chars: 0, hasToolCalls: false, preview: 'pong' });
1586
1202
  break;
1587
1203
  case 'set-model':
1588
1204
  if (cmd.params?.value && typeof cmd.params.value === 'string') {
1589
1205
  this.currentModel = cmd.params.value;
1590
- getBridgeClient()?.emitModeChange({ mode: this.operatorMode ? 'operator' : this.agentMode ? 'agent' : 'chat', model: this.currentModel });
1206
+ (0, bridge_client_js_1.getBridgeClient)()?.emitModeChange({ mode: this.operatorMode ? 'operator' : this.agentMode ? 'agent' : 'chat', model: this.currentModel });
1591
1207
  if (!this.jsonOutput)
1592
- console.log(chalk.yellow(`[bridge] Model changed to: ${this.currentModel}`));
1208
+ console.log(chalk_1.default.yellow(`[bridge] Model changed to: ${this.currentModel}`));
1593
1209
  }
1594
1210
  break;
1595
1211
  case 'abort':
1596
1212
  if (!this.jsonOutput)
1597
- console.log(chalk.red(`[bridge] Abort requested by admin`));
1598
- getBridgeClient()?.emitEnd({ reason: 'admin-abort' });
1213
+ console.log(chalk_1.default.red(`[bridge] Abort requested by admin`));
1214
+ (0, bridge_client_js_1.getBridgeClient)()?.emitEnd({ reason: 'admin-abort' });
1599
1215
  process.exit(0);
1600
1216
  break;
1601
1217
  default:
1602
- getBridgeClient()?.emitError({ message: `Unknown admin command: ${cmd.action}` });
1218
+ (0, bridge_client_js_1.getBridgeClient)()?.emitError({ message: `Unknown admin command: ${cmd.action}` });
1603
1219
  }
1604
1220
  }
1605
1221
  ensureProjectWorkspace() {
@@ -1635,13 +1251,13 @@ export class ChatCommand {
1635
1251
  // Guardrail: do not watch broad home/root scopes on interactive shells.
1636
1252
  if (normalized.toLowerCase() === normalizedHome.toLowerCase()) {
1637
1253
  if (!this.jsonOutput) {
1638
- console.log(chalk.gray('Info: workspace watcher disabled for home directory scope.'));
1254
+ console.log(chalk_1.default.gray('Info: workspace watcher disabled for home directory scope.'));
1639
1255
  }
1640
1256
  return false;
1641
1257
  }
1642
1258
  if (/^[a-zA-Z]:\/$/.test(normalized) || normalized === '/') {
1643
1259
  if (!this.jsonOutput) {
1644
- console.log(chalk.gray('Info: workspace watcher disabled for filesystem root scope.'));
1260
+ console.log(chalk_1.default.gray('Info: workspace watcher disabled for filesystem root scope.'));
1645
1261
  }
1646
1262
  return false;
1647
1263
  }
@@ -1657,7 +1273,7 @@ export class ChatCommand {
1657
1273
  const explicitPath = this.resolvePromptWorkspacePath(options.prompt, process.cwd());
1658
1274
  if (explicitPath) {
1659
1275
  if (!this.jsonOutput) {
1660
- console.log(chalk.gray(`📁 Using project path from prompt: ${explicitPath}`));
1276
+ console.log(chalk_1.default.gray(`📁 Using project path from prompt: ${explicitPath}`));
1661
1277
  }
1662
1278
  return explicitPath;
1663
1279
  }
@@ -1759,7 +1375,7 @@ export class ChatCommand {
1759
1375
  return path.join(rootPath, `${folderName}-${Date.now()}`);
1760
1376
  }
1761
1377
  initializeSession(resume) {
1762
- this.projectMemory = new ProjectMemoryService(this.currentProjectPath);
1378
+ this.projectMemory = new project_memory_js_1.ProjectMemoryService(this.currentProjectPath);
1763
1379
  if (resume) {
1764
1380
  this.currentSession = this.sessionManager.getLatest(this.currentProjectPath);
1765
1381
  if (this.currentSession) {
@@ -1805,11 +1421,11 @@ export class ChatCommand {
1805
1421
  // Suppress all setup banners in direct-prompt mode so only the final
1806
1422
  // answer reaches stdout. Interactive (REPL) mode still shows them.
1807
1423
  if (!this.jsonOutput) {
1808
- console.log(chalk.cyan('Running single prompt in direct mode.'));
1809
- console.log(chalk.gray(`Model: ${this.currentModel}`));
1810
- console.log(chalk.gray(`Project: ${this.currentProjectPath}`));
1424
+ console.log(chalk_1.default.cyan('Running single prompt in direct mode.'));
1425
+ console.log(chalk_1.default.gray(`Model: ${this.currentModel}`));
1426
+ console.log(chalk_1.default.gray(`Project: ${this.currentProjectPath}`));
1811
1427
  if (this.workflowTarget) {
1812
- console.log(chalk.gray(`Workflow target: ${this.workflowTarget}`));
1428
+ console.log(chalk_1.default.gray(`Workflow target: ${this.workflowTarget}`));
1813
1429
  }
1814
1430
  console.log();
1815
1431
  }
@@ -1873,7 +1489,7 @@ export class ChatCommand {
1873
1489
  const runtimeContext = await this.getPromptRuntimeContext(prompt);
1874
1490
  const resolvedWorkflow = await this.callApi('Resolve VigFlow workflow', () => this.api.resolveVigFlowWorkflow(selector));
1875
1491
  const invocationMode = this.operatorMode ? 'operator' : this.agentMode ? 'agent' : 'chat';
1876
- const spinner = this.jsonOutput ? null : createSpinner({ text: `Running workflow ${resolvedWorkflow.name}...`, spinner: 'clock' }).start();
1492
+ const spinner = this.jsonOutput ? null : (0, logger_js_1.createSpinner)({ text: `Running workflow ${resolvedWorkflow.name}...`, spinner: 'clock' }).start();
1877
1493
  try {
1878
1494
  const execution = await this.callApi('Run VigFlow workflow', () => this.api.runVigFlowWorkflow(resolvedWorkflow.id, {
1879
1495
  data: {
@@ -1922,9 +1538,9 @@ export class ChatCommand {
1922
1538
  return;
1923
1539
  }
1924
1540
  this.logger.success(`Workflow target ${resolvedWorkflow.name} ${execution.status}`);
1925
- console.log(chalk.gray(`Workflow ID: ${resolvedWorkflow.id}`));
1926
- console.log(chalk.gray(`Execution ID: ${execution.executionId}`));
1927
- console.log(chalk.gray(`Mode: ${invocationMode}`));
1541
+ console.log(chalk_1.default.gray(`Workflow ID: ${resolvedWorkflow.id}`));
1542
+ console.log(chalk_1.default.gray(`Execution ID: ${execution.executionId}`));
1543
+ console.log(chalk_1.default.gray(`Mode: ${invocationMode}`));
1928
1544
  if (content) {
1929
1545
  console.log(content);
1930
1546
  }
@@ -1967,9 +1583,9 @@ export class ChatCommand {
1967
1583
  await this.runLocalAgentLoop(prompt);
1968
1584
  return;
1969
1585
  }
1970
- getBridgeClient()?.emitPrompt({ prompt, mode: 'operator', model: this.currentModel });
1586
+ (0, bridge_client_js_1.getBridgeClient)()?.emitPrompt({ prompt, mode: 'operator', model: this.currentModel });
1971
1587
  const runtimeContext = await this.getPromptRuntimeContext(prompt);
1972
- const spinner = this.jsonOutput ? null : createSpinner({ text: 'Thinking like an operator...', spinner: 'clock' }).start();
1588
+ const spinner = this.jsonOutput ? null : (0, logger_js_1.createSpinner)({ text: 'Thinking like an operator...', spinner: 'clock' }).start();
1973
1589
  const workflowType = 'full';
1974
1590
  const executionPrompt = this.buildExecutionPrompt(prompt);
1975
1591
  try {
@@ -1997,7 +1613,7 @@ export class ChatCommand {
1997
1613
  const isPolicyAck = /^(i will follow|i understand|i('ll| will) adhere|understood[.,!]|sure[.,!]|provide your|waiting for|i('ll| will) proceed)/i.test(responseText)
1998
1614
  || (responseText.length < 200 && /follow the instructions|next instruction|ready to assist/i.test(responseText));
1999
1615
  if (isPolicyAck) {
2000
- throw new CLIError('Operator workflow returned a non-actionable acknowledgement instead of a grounded result.', 'model_backend');
1616
+ throw new api_js_1.CLIError('Operator workflow returned a non-actionable acknowledgement instead of a grounded result.', 'model_backend');
2001
1617
  }
2002
1618
  if (this.jsonOutput) {
2003
1619
  console.log(JSON.stringify({
@@ -2013,7 +1629,7 @@ export class ChatCommand {
2013
1629
  else {
2014
1630
  console.log(response.content || 'Operator workflow completed.');
2015
1631
  if (response.savedWorkflow?.id) {
2016
- console.log(chalk.gray(`Saved VigFlow workflow: ${response.savedWorkflow.id}${response.savedWorkflow.name ? ` (${response.savedWorkflow.name})` : ''}`));
1632
+ console.log(chalk_1.default.gray(`Saved VigFlow workflow: ${response.savedWorkflow.id}${response.savedWorkflow.name ? ` (${response.savedWorkflow.name})` : ''}`));
2017
1633
  }
2018
1634
  }
2019
1635
  this.rememberBrainEvent('validation', `GoA operator workflow completed${response.workflowId ? ` workflow ${response.workflowId}` : ''}${response.savedWorkflow?.id ? ` saved VigFlow ${response.savedWorkflow.id}` : ''}.`, 'operator');
@@ -2024,8 +1640,8 @@ export class ChatCommand {
2024
1640
  if (spinner) {
2025
1641
  spinner.stop();
2026
1642
  }
2027
- const cliErr = error instanceof CLIError ? error : classifyError(error);
2028
- const errorMsg = formatCLIError(cliErr);
1643
+ const cliErr = error instanceof api_js_1.CLIError ? error : (0, api_js_1.classifyError)(error);
1644
+ const errorMsg = (0, api_js_1.formatCLIError)(cliErr);
2029
1645
  if (!this.jsonOutput) {
2030
1646
  this.logger.error('Operator workflow failed');
2031
1647
  }
@@ -2052,8 +1668,8 @@ export class ChatCommand {
2052
1668
  * BMAD orchestrator to scan the workspace.
2053
1669
  */
2054
1670
  async runOperatorDirectAnswer(prompt) {
2055
- getBridgeClient()?.emitPrompt({ prompt, mode: 'operator-direct', model: this.currentModel });
2056
- const spinner = this.jsonOutput ? null : createSpinner({ text: 'Operator (direct)...', spinner: 'clock' }).start();
1671
+ (0, bridge_client_js_1.getBridgeClient)()?.emitPrompt({ prompt, mode: 'operator-direct', model: this.currentModel });
1672
+ const spinner = this.jsonOutput ? null : (0, logger_js_1.createSpinner)({ text: 'Operator (direct)...', spinner: 'clock' }).start();
2057
1673
  try {
2058
1674
  let operatorGrounding = [
2059
1675
  'You are Vigthoria Operator, a DevOps and infrastructure analysis assistant.',
@@ -2085,7 +1701,7 @@ export class ChatCommand {
2085
1701
  const isPolicyAck = /^(i will follow|i understand|i('ll| will) adhere|understood[.,!]|sure[.,!]|provide your|waiting for|i('ll| will) proceed)/i.test(content)
2086
1702
  || (content.length < 200 && /follow the instructions|next instruction|ready to assist/i.test(content));
2087
1703
  if (isPolicyAck || !content) {
2088
- throw new CLIError('Operator returned a non-actionable acknowledgement instead of a grounded result.', 'model_backend');
1704
+ throw new api_js_1.CLIError('Operator returned a non-actionable acknowledgement instead of a grounded result.', 'model_backend');
2089
1705
  }
2090
1706
  if (this.jsonOutput) {
2091
1707
  console.log(JSON.stringify({
@@ -2105,8 +1721,8 @@ export class ChatCommand {
2105
1721
  catch (error) {
2106
1722
  if (spinner)
2107
1723
  spinner.stop();
2108
- const cliErr = error instanceof CLIError ? error : classifyError(error);
2109
- const errorMsg = formatCLIError(cliErr);
1724
+ const cliErr = error instanceof api_js_1.CLIError ? error : (0, api_js_1.classifyError)(error);
1725
+ const errorMsg = (0, api_js_1.formatCLIError)(cliErr);
2110
1726
  if (this.jsonOutput) {
2111
1727
  process.exitCode = 1;
2112
1728
  console.log(JSON.stringify({
@@ -2138,8 +1754,8 @@ export class ChatCommand {
2138
1754
  this.currentSession.agentMode = true;
2139
1755
  this.currentSession.model = this.currentModel;
2140
1756
  }
2141
- console.log(chalk.yellow('This request needs file access, so I am switching to Agent mode and working in this workspace now.'));
2142
- console.log(chalk.gray('You do not need to confirm with "yes"; I will continue until the agent run finishes or reports a blocker.'));
1757
+ console.log(chalk_1.default.yellow('This request needs file access, so I am switching to Agent mode and working in this workspace now.'));
1758
+ console.log(chalk_1.default.gray('You do not need to confirm with "yes"; I will continue until the agent run finishes or reports a blocker.'));
2143
1759
  await this.runAgentTurn(promptToRun);
2144
1760
  return;
2145
1761
  }
@@ -2198,8 +1814,8 @@ export class ChatCommand {
2198
1814
  }
2199
1815
  }
2200
1816
  this.messages.push({ role: 'user', content: this.buildExecutionPrompt(prompt) });
2201
- getBridgeClient()?.emitPrompt({ prompt, mode: 'chat', model: this.currentModel });
2202
- const spinner = this.jsonOutput ? null : createSpinner({ text: 'Thinking...', spinner: 'clock' }).start();
1817
+ (0, bridge_client_js_1.getBridgeClient)()?.emitPrompt({ prompt, mode: 'chat', model: this.currentModel });
1818
+ const spinner = this.jsonOutput ? null : (0, logger_js_1.createSpinner)({ text: 'Thinking...', spinner: 'clock' }).start();
2203
1819
  try {
2204
1820
  const response = await this.callApi('Send chat message', () => this.api.chat(this.getMessagesForModel(), this.currentModel));
2205
1821
  if (spinner)
@@ -2223,7 +1839,7 @@ export class ChatCommand {
2223
1839
  console.log(finalText);
2224
1840
  }
2225
1841
  else {
2226
- console.log(chalk.yellow('The model returned an empty response. Try rephrasing your question, or use --agent mode for grounded repo analysis.'));
1842
+ console.log(chalk_1.default.yellow('The model returned an empty response. Try rephrasing your question, or use --agent mode for grounded repo analysis.'));
2227
1843
  }
2228
1844
  this.messages.push({ role: 'assistant', content: response.message || '' });
2229
1845
  this.saveSession();
@@ -2231,8 +1847,8 @@ export class ChatCommand {
2231
1847
  catch (error) {
2232
1848
  if (spinner)
2233
1849
  spinner.stop();
2234
- const cliErr = error instanceof CLIError ? error : classifyError(error);
2235
- const errorMsg = formatCLIError(cliErr);
1850
+ const cliErr = error instanceof api_js_1.CLIError ? error : (0, api_js_1.classifyError)(error);
1851
+ const errorMsg = (0, api_js_1.formatCLIError)(cliErr);
2236
1852
  if (this.jsonOutput) {
2237
1853
  process.exitCode = 1;
2238
1854
  console.log(JSON.stringify({
@@ -2249,11 +1865,10 @@ export class ChatCommand {
2249
1865
  }
2250
1866
  }
2251
1867
  }
2252
- async runAgentTurn(prompt, options = {}) {
1868
+ async runAgentTurn(prompt) {
2253
1869
  if (!this.tools) {
2254
1870
  throw new Error('Agent tools are not initialized.');
2255
1871
  }
2256
- this.bindPromptWorkspace(prompt);
2257
1872
  const requiresV3Workflow = this.shouldRequireV3AgentWorkflow(prompt);
2258
1873
  const handledByDirectFileFlow = await this.tryDirectSingleFileFlow(prompt);
2259
1874
  if (handledByDirectFileFlow) {
@@ -2264,17 +1879,11 @@ export class ChatCommand {
2264
1879
  // bypassed (e.g. HTML files routed to V3) so the local agent loop has file
2265
1880
  // awareness and the ⚙ Executing: read_file banner is always emitted.
2266
1881
  await this.primeBypassedTargetFileContext(prompt);
2267
- if (!options.skipLocalLoop && !options.forceV3 && this.shouldPreferLocalAgentLoop(prompt)) {
2268
- const completed = await this.runLocalAgentLoop(prompt);
2269
- if (completed) {
2270
- this.saveSession();
2271
- return;
2272
- }
2273
- if (!this.jsonOutput) {
2274
- console.log(chalk.yellow('Local chat backend unavailable (self-hosted inference may be offline). Trying V3 agent workflow...'));
2275
- }
1882
+ if (this.shouldPreferLocalAgentLoop(prompt)) {
1883
+ await this.runLocalAgentLoop(prompt);
1884
+ return;
2276
1885
  }
2277
- const handledByV3Workflow = await this.tryV3AgentWorkflow(prompt, options);
1886
+ const handledByV3Workflow = await this.tryV3AgentWorkflow(prompt);
2278
1887
  if (handledByV3Workflow) {
2279
1888
  this.saveSession();
2280
1889
  return;
@@ -2288,108 +1897,24 @@ export class ChatCommand {
2288
1897
  }
2289
1898
  await this.runLocalAgentLoop(prompt);
2290
1899
  }
2291
- localAgentIterationCount = 0;
2292
- buildLocalAgentChatOptions(preflight, onRouteAttempt) {
2293
- const preferredRoute = preflight?.endpoint
2294
- ? this.api.mapPreflightEndpointToRoute(preflight.endpoint)
2295
- : undefined;
2296
- return {
2297
- fastFail: true,
2298
- singleRoute: true,
2299
- stream: true,
2300
- connectTimeoutMs: Number.parseInt(process.env.VIGTHORIA_CHAT_CONNECT_TIMEOUT_MS || '20000', 10),
2301
- idleTimeoutMs: Number.parseInt(process.env.VIGTHORIA_CHAT_IDLE_TIMEOUT_MS || '120000', 10),
2302
- preferredRoute: preferredRoute || 'coder',
2303
- onRouteAttempt,
2304
- };
2305
- }
2306
- printChatModelPreflight(preflight) {
2307
- if (this.jsonOutput) {
2308
- return;
2309
- }
2310
- console.log(chalk.gray('Model backend preflight:'));
2311
- for (const route of preflight.routes) {
2312
- const marker = route.ok ? chalk.green(' ✓') : chalk.red(' ✗');
2313
- const detail = route.error ? chalk.gray(` — ${route.error}`) : '';
2314
- console.log(`${marker} ${route.name}${detail}`);
2315
- }
2316
- if (preflight.healthy) {
2317
- console.log(chalk.gray(`Using: ${preflight.endpoint}`));
2318
- }
2319
- else if (preflight.error) {
2320
- console.log(chalk.yellow(preflight.error));
2321
- }
2322
- console.log();
2323
- }
2324
1900
  async runLocalAgentLoop(prompt) {
2325
1901
  if (!this.tools) {
2326
1902
  throw new Error('Agent tools are not initialized.');
2327
1903
  }
2328
- this.localAgentIterationCount = 0;
2329
- const runtime = this.getRuntimeEnvironmentContext();
2330
- if (!this.jsonOutput) {
2331
- console.log();
2332
- console.log(chalk.gray('━━━ ROUTING DECISION ━━━'));
2333
- console.log(chalk.gray('Reason: local-machine-agent-loop'));
2334
- console.log(chalk.gray(`Platform: ${runtime.platform}`));
2335
- console.log(chalk.gray(`Machine scope: ${runtime.machineScope}`));
2336
- console.log(chalk.gray(`Workspace: ${runtime.workspacePath}`));
2337
- console.log(chalk.gray('Tools execute on your local filesystem.'));
2338
- console.log(chalk.gray('━'.repeat(30)));
2339
- console.log();
2340
- }
2341
1904
  this.lastActionableUserInput = prompt;
2342
1905
  this.directToolContinuationCount = 0;
2343
1906
  this.agentToolEvidence = { discovery: 0, mutation: 0, searchFailed: 0 };
2344
1907
  this.tools.clearSessionApprovals();
2345
- getBridgeClient()?.emitPrompt({ prompt, mode: this.operatorMode ? 'operator' : 'agent', model: this.currentModel });
1908
+ (0, bridge_client_js_1.getBridgeClient)()?.emitPrompt({ prompt, mode: this.operatorMode ? 'operator' : 'agent', model: this.currentModel });
2346
1909
  this.ensureAgentSystemPrompt();
2347
1910
  this.messages.push({ role: 'user', content: this.buildScopedUserPrompt(prompt) });
2348
1911
  this.saveSession();
2349
- const preflightSpinner = this.jsonOutput
2350
- ? null
2351
- : createSpinner({ text: 'Checking model connection (preflight)...', spinner: 'clock' }).start();
2352
- let preflight;
2353
- try {
2354
- preflight = await this.api.runChatModelPreflight(this.currentModel);
2355
- }
2356
- finally {
2357
- if (preflightSpinner) {
2358
- preflightSpinner.stop();
2359
- }
2360
- }
2361
- this.printChatModelPreflight(preflight);
2362
- if (!preflight.healthy) {
2363
- return false;
2364
- }
2365
1912
  const maxTurns = 10;
2366
- const preferredRoute = preflight?.endpoint
2367
- ? (this.api.mapPreflightEndpointToRoute(preflight.endpoint) || 'coder')
2368
- : 'coder';
2369
1913
  for (let turn = 0; turn < maxTurns; turn += 1) {
2370
- const spinner = this.jsonOutput ? null : createSpinner({ text: turn === 0 ? 'Planning first tool step...' : 'Continuing...', spinner: 'clock' }).start();
2371
- let streamedVisible = '';
1914
+ const spinner = this.jsonOutput ? null : (0, logger_js_1.createSpinner)({ text: turn === 0 ? 'Planning...' : 'Continuing...', spinner: 'clock' }).start();
2372
1915
  let response;
2373
1916
  try {
2374
- response = await this.callApi('Send agent chat message', () => this.api.chat(this.getMessagesForModel({ compact: turn === 0 }), this.currentModel, false, {
2375
- fastFail: true,
2376
- singleRoute: true,
2377
- stream: true,
2378
- connectTimeoutMs: Number.parseInt(process.env.VIGTHORIA_CHAT_CONNECT_TIMEOUT_MS || '20000', 10),
2379
- idleTimeoutMs: Number.parseInt(process.env.VIGTHORIA_CHAT_IDLE_TIMEOUT_MS || '120000', 10),
2380
- preferredRoute,
2381
- onRouteAttempt: (routeLabel) => {
2382
- if (spinner) {
2383
- spinner.text = routeLabel;
2384
- }
2385
- },
2386
- onStreamDelta: (chunk) => {
2387
- streamedVisible += chunk;
2388
- if (spinner) {
2389
- spinner.text = streamedVisible.trim().slice(-72) || 'Streaming model output...';
2390
- }
2391
- },
2392
- }), 0);
1917
+ response = await this.callApi('Send agent chat message', () => this.api.chat(this.getMessagesForModel(), this.currentModel), 0);
2393
1918
  }
2394
1919
  catch (firstErr) {
2395
1920
  // If we already gathered evidence and the model API fails on a
@@ -2398,14 +1923,7 @@ export class ChatCommand {
2398
1923
  this.logger.debug('Agent continuation API call failed, retrying once...');
2399
1924
  try {
2400
1925
  await new Promise(r => setTimeout(r, 2000));
2401
- response = await this.callApi('Retry agent chat message', () => this.api.chat(this.getMessagesForModel(), this.currentModel, false, {
2402
- fastFail: true,
2403
- singleRoute: true,
2404
- stream: true,
2405
- connectTimeoutMs: Number.parseInt(process.env.VIGTHORIA_CHAT_CONNECT_TIMEOUT_MS || '20000', 10),
2406
- idleTimeoutMs: Number.parseInt(process.env.VIGTHORIA_CHAT_IDLE_TIMEOUT_MS || '120000', 10),
2407
- preferredRoute,
2408
- }), 0);
1926
+ response = await this.callApi('Retry agent chat message', () => this.api.chat(this.getMessagesForModel(), this.currentModel), 0);
2409
1927
  }
2410
1928
  catch (retryErr) {
2411
1929
  // Retry also failed — synthesize an answer from evidence
@@ -2428,40 +1946,17 @@ export class ChatCommand {
2428
1946
  console.log(fallbackContent);
2429
1947
  }
2430
1948
  this.saveSession();
2431
- return true;
1949
+ return;
2432
1950
  }
2433
1951
  throw retryErr;
2434
1952
  }
2435
1953
  }
2436
1954
  else {
2437
- const cliErr = firstErr instanceof CLIError ? firstErr : classifyError(firstErr);
2438
- const formatted = formatCLIError(cliErr);
1955
+ const cliErr = firstErr instanceof api_js_1.CLIError ? firstErr : (0, api_js_1.classifyError)(firstErr);
1956
+ const formatted = (0, api_js_1.formatCLIError)(cliErr);
2439
1957
  if (spinner)
2440
1958
  spinner.stop();
2441
- this.rememberBrainEvent('issue', 'Agent model request failed: ' + formatted, 'agent');
2442
- if (turn === 0 && (cliErr.category === 'model_backend' || cliErr.category === 'network' || cliErr.category === 'timeout')) {
2443
- if (!this.jsonOutput) {
2444
- this.logger.error(formatted);
2445
- const transport = this.api.getLastChatTransportErrors();
2446
- if (transport.length > 0) {
2447
- console.log(chalk.gray(`Routes tried: ${transport.slice(0, 3).join(' | ')}`));
2448
- }
2449
- }
2450
- else {
2451
- process.exitCode = 1;
2452
- console.log(JSON.stringify({
2453
- success: false,
2454
- mode: 'agent',
2455
- model: this.currentModel,
2456
- partial: false,
2457
- content: '',
2458
- error: formatted,
2459
- errorCategory: cliErr.category,
2460
- metadata: { executionPath: 'local-agent-loop' },
2461
- }, null, 2));
2462
- }
2463
- return false;
2464
- }
1959
+ this.rememberBrainEvent('issue', 'Agent API preflight failed: ' + formatted, 'agent');
2465
1960
  if (this.jsonOutput) {
2466
1961
  process.exitCode = 1;
2467
1962
  console.log(JSON.stringify({
@@ -2478,7 +1973,7 @@ export class ChatCommand {
2478
1973
  else {
2479
1974
  this.logger.error(formatted);
2480
1975
  }
2481
- return true;
1976
+ return;
2482
1977
  }
2483
1978
  }
2484
1979
  if (spinner)
@@ -2488,12 +1983,7 @@ export class ChatCommand {
2488
1983
  this.messages.push({ role: 'assistant', content: assistantMessage });
2489
1984
  const toolCalls = this.extractToolCalls(assistantMessage);
2490
1985
  const visibleText = this.stripToolPayloads(assistantMessage).trim();
2491
- if (visibleText && !this.jsonOutput) {
2492
- this.localAgentIterationCount += 1;
2493
- console.log(chalk.cyan(`\n── Iteration ${this.localAgentIterationCount} ──`));
2494
- console.log(chalk.gray(visibleText));
2495
- }
2496
- getBridgeClient()?.emitModelResponse({
1986
+ (0, bridge_client_js_1.getBridgeClient)()?.emitModelResponse({
2497
1987
  model: this.currentModel,
2498
1988
  chars: assistantMessage.length,
2499
1989
  hasToolCalls: toolCalls.length > 0,
@@ -2561,7 +2051,7 @@ export class ChatCommand {
2561
2051
  console.log(finalContent);
2562
2052
  }
2563
2053
  this.saveSession();
2564
- return true;
2054
+ return;
2565
2055
  }
2566
2056
  await this.executeToolCalls(toolCalls);
2567
2057
  this.directToolContinuationCount += 1;
@@ -2574,8 +2064,8 @@ export class ChatCommand {
2574
2064
  catch (error) {
2575
2065
  if (spinner)
2576
2066
  spinner.stop();
2577
- const cliErr = error instanceof CLIError ? error : classifyError(error);
2578
- const errorMsg = formatCLIError(cliErr);
2067
+ const cliErr = error instanceof api_js_1.CLIError ? error : (0, api_js_1.classifyError)(error);
2068
+ const errorMsg = (0, api_js_1.formatCLIError)(cliErr);
2579
2069
  this.rememberBrainEvent('issue', `Agent turn failed: ${errorMsg}`, 'agent');
2580
2070
  if (this.jsonOutput) {
2581
2071
  process.exitCode = 1;
@@ -2595,7 +2085,7 @@ export class ChatCommand {
2595
2085
  else {
2596
2086
  this.logger.error(errorMsg);
2597
2087
  }
2598
- return true;
2088
+ return;
2599
2089
  }
2600
2090
  }
2601
2091
  if (this.jsonOutput) {
@@ -2616,7 +2106,6 @@ export class ChatCommand {
2616
2106
  console.log('Task complete.');
2617
2107
  }
2618
2108
  this.saveSession();
2619
- return true;
2620
2109
  }
2621
2110
  async primeBypassedTargetFileContext(prompt) {
2622
2111
  if (!this.directPromptMode || !this.tools) {
@@ -2636,12 +2125,12 @@ export class ChatCommand {
2636
2125
  args: { path: targetFile },
2637
2126
  };
2638
2127
  if (!this.jsonOutput) {
2639
- console.log(chalk.cyan(`⚙ Executing: ${readCall.tool}`));
2128
+ console.log(chalk_1.default.cyan(`⚙ Executing: ${readCall.tool}`));
2640
2129
  }
2641
2130
  const readResult = await this.tools.execute(readCall);
2642
2131
  const readSummary = this.formatToolResult(readCall, readResult);
2643
2132
  if (!this.jsonOutput) {
2644
- console.log(readResult.success ? chalk.gray(readSummary) : chalk.red(readSummary));
2133
+ console.log(readResult.success ? chalk_1.default.gray(readSummary) : chalk_1.default.red(readSummary));
2645
2134
  }
2646
2135
  if (readResult.success && readResult.output) {
2647
2136
  this.messages.push({ role: 'system', content: readSummary });
@@ -2663,12 +2152,12 @@ export class ChatCommand {
2663
2152
  args: { path: targetFile },
2664
2153
  };
2665
2154
  if (!this.jsonOutput) {
2666
- console.log(chalk.cyan(`⚙ Executing: ${readCall.tool}`));
2155
+ console.log(chalk_1.default.cyan(`⚙ Executing: ${readCall.tool}`));
2667
2156
  }
2668
2157
  const readResult = await this.tools.execute(readCall);
2669
2158
  const readSummary = this.formatToolResult(readCall, readResult);
2670
2159
  if (!this.jsonOutput) {
2671
- console.log(readResult.success ? chalk.gray(readSummary) : chalk.red(readSummary));
2160
+ console.log(readResult.success ? chalk_1.default.gray(readSummary) : chalk_1.default.red(readSummary));
2672
2161
  }
2673
2162
  this.messages.push({ role: 'system', content: readSummary });
2674
2163
  if (!readResult.success || !readResult.output) {
@@ -2711,12 +2200,12 @@ export class ChatCommand {
2711
2200
  },
2712
2201
  };
2713
2202
  if (!this.jsonOutput) {
2714
- console.log(chalk.cyan(`⚙ Executing: ${writeCall.tool}`));
2203
+ console.log(chalk_1.default.cyan(`⚙ Executing: ${writeCall.tool}`));
2715
2204
  }
2716
2205
  const writeResult = await this.tools.execute(writeCall);
2717
2206
  const writeSummary = this.formatToolResult(writeCall, writeResult);
2718
2207
  if (!this.jsonOutput) {
2719
- console.log(writeResult.success ? chalk.gray(writeSummary) : chalk.red(writeSummary));
2208
+ console.log(writeResult.success ? chalk_1.default.gray(writeSummary) : chalk_1.default.red(writeSummary));
2720
2209
  }
2721
2210
  this.messages.push({ role: 'system', content: writeSummary });
2722
2211
  if (!writeResult.success) {
@@ -2752,10 +2241,10 @@ export class ChatCommand {
2752
2241
  console.log(`Updated ${targetFile}.`);
2753
2242
  if (previewGate.required) {
2754
2243
  if (previewGate.passed) {
2755
- console.log(chalk.gray(`Template Service preview gate: passed via ${previewGate.backendUrl || 'unknown backend'}`));
2244
+ console.log(chalk_1.default.gray(`Template Service preview gate: passed via ${previewGate.backendUrl || 'unknown backend'}`));
2756
2245
  }
2757
2246
  else {
2758
- console.log(chalk.yellow(`Template Service preview gate: failed${previewGate.error ? ` - ${previewGate.error}` : ''}`));
2247
+ console.log(chalk_1.default.yellow(`Template Service preview gate: failed${previewGate.error ? ` - ${previewGate.error}` : ''}`));
2759
2248
  }
2760
2249
  }
2761
2250
  }
@@ -2802,11 +2291,14 @@ export class ChatCommand {
2802
2291
  'Do not reinterpret this confirmation as a new website, landing page, template, or index.html task.',
2803
2292
  ].join('\n');
2804
2293
  }
2805
- async tryV3AgentWorkflow(prompt, options = {}) {
2294
+ async tryV3AgentWorkflow(prompt) {
2806
2295
  // Extract explicit workspace path from prompt (if provided by user)
2807
2296
  let promptWorkspacePath = null;
2808
2297
  try {
2809
2298
  promptWorkspacePath = this.resolvePromptWorkspacePath(prompt, this.currentProjectPath);
2299
+ if (promptWorkspacePath && !this.jsonOutput) {
2300
+ console.log(chalk_1.default.cyan(`📁 Workspace from prompt: ${promptWorkspacePath}`));
2301
+ }
2810
2302
  }
2811
2303
  catch {
2812
2304
  // Path extraction failed, use default workspace
@@ -2822,15 +2314,15 @@ export class ChatCommand {
2822
2314
  // STREAMING: Log routing decision transparently to user
2823
2315
  if (!this.jsonOutput) {
2824
2316
  console.log();
2825
- console.log(chalk.gray('━━━ ROUTING DECISION ━━━'));
2826
- console.log(chalk.gray(`Reason: ${routingPolicy.routeReason}`));
2827
- console.log(chalk.gray(`Model: ${routingPolicy.selectedModel}`));
2828
- console.log(chalk.gray(`Cloud Eligible: ${routingPolicy.cloudEligible}`));
2829
- console.log(chalk.gray(`Cloud Selected: ${routingPolicy.cloudSelected}`));
2317
+ console.log(chalk_1.default.gray('━━━ ROUTING DECISION ━━━'));
2318
+ console.log(chalk_1.default.gray(`Reason: ${routingPolicy.routeReason}`));
2319
+ console.log(chalk_1.default.gray(`Model: ${routingPolicy.selectedModel}`));
2320
+ console.log(chalk_1.default.gray(`Cloud Eligible: ${routingPolicy.cloudEligible}`));
2321
+ console.log(chalk_1.default.gray(`Cloud Selected: ${routingPolicy.cloudSelected}`));
2830
2322
  if (routingPolicy.heavyTask) {
2831
- console.log(chalk.gray(`Task Complexity: HEAVY`));
2323
+ console.log(chalk_1.default.gray(`Task Complexity: HEAVY`));
2832
2324
  }
2833
- console.log(chalk.gray('━'.repeat(30)));
2325
+ console.log(chalk_1.default.gray('━'.repeat(30)));
2834
2326
  console.log();
2835
2327
  }
2836
2328
  // Reset streaming counters for new workflow
@@ -2838,12 +2330,9 @@ export class ChatCommand {
2838
2330
  this.v3ToolCallCount = 0;
2839
2331
  this.v3LastActivity = Date.now();
2840
2332
  this.v3StreamingStarted = false;
2841
- this.v3StreamedTextBuffer = '';
2842
- this.v3LiveToolEvidence = [];
2843
- this.v3PendingToolCalls = [];
2844
- const taskDisplay = new TaskDisplay(['Analyse workspace', 'Execute tasks', 'Validate output', 'Self-heal'], !this.jsonOutput);
2333
+ const taskDisplay = new task_display_js_1.TaskDisplay(['Analyse workspace', 'Execute tasks', 'Validate output', 'Self-heal'], !this.jsonOutput);
2845
2334
  taskDisplay.start(0);
2846
- const spinner = this.jsonOutput ? null : createSpinner({
2335
+ const spinner = this.jsonOutput ? null : (0, logger_js_1.createSpinner)({
2847
2336
  text: routingPolicy.cloudSelected ? 'Routing heavy task to Vigthoria Cloud...' : 'Routing to V3 Agent...',
2848
2337
  spinner: 'clock',
2849
2338
  }).start();
@@ -2902,41 +2391,20 @@ export class ChatCommand {
2902
2391
  }
2903
2392
  };
2904
2393
  const executionPrompt = this.buildExecutionPrompt(contextualPrompt);
2905
- const agentTaskType = this.inferAgentTaskType(contextualPrompt);
2394
+ const agentTaskType = this.inferAgentTaskType(prompt);
2906
2395
  const workspaceContext = {
2907
2396
  workspacePath: workspacePath,
2908
2397
  projectPath: workspacePath,
2909
2398
  targetPath: workspacePath,
2910
2399
  ...runtimeContext,
2911
2400
  };
2912
- if (!this.jsonOutput && !this.directPromptMode) {
2913
- try {
2914
- const snapshot = this.api.getAgentWorkspaceSnapshot(workspacePath);
2915
- console.log(chalk.gray(`Workspace sync: ${snapshot.fileCount} files indexed from ${workspacePath}`));
2916
- if (snapshot.paths.length > 0) {
2917
- console.log(chalk.gray(`Workspace index: ${snapshot.paths.slice(0, 5).map((filePath) => filePath.replace(/\\/g, '/')).join(', ')}${snapshot.paths.length > 5 ? ', ...' : ''}`));
2918
- }
2919
- }
2920
- catch {
2921
- console.log(chalk.gray(`Workspace sync: preparing ${workspacePath}`));
2922
- }
2923
- }
2924
2401
  // Start workspace watcher for bidirectional real-time sync
2925
2402
  let watcher = null;
2926
- let workspaceWs = null;
2927
- const stopWorkspaceSync = () => {
2928
- watcher?.stop();
2929
- if (workspaceWs) {
2930
- workspaceWs.disconnect();
2931
- workspaceWs = null;
2932
- }
2933
- };
2934
2403
  if (this.shouldStartWorkspaceWatcher(workspacePath)) {
2935
- watcher = new WorkspaceWatcher({
2404
+ watcher = new workspace_stream_js_1.WorkspaceWatcher({
2936
2405
  workspaceRoot: workspacePath,
2937
2406
  onFileChange: (relativePath, content, action) => {
2938
2407
  this.logger.debug(`Local change detected: ${action} ${relativePath}`);
2939
- workspaceWs?.syncFile(relativePath, content, action);
2940
2408
  },
2941
2409
  });
2942
2410
  watcher.start();
@@ -2946,15 +2414,15 @@ export class ChatCommand {
2946
2414
  await this.api.ensureV3ServiceKey();
2947
2415
  if (spinner)
2948
2416
  this.updateV3AgentSpinner(spinner, { type: 'v3_status', content: 'Checking V3 connection (preflight)...' });
2949
- const healthCheck = await this.api.runV3HealthCheck({ soft: true });
2417
+ // Early health check: fail fast if V3 is not available
2418
+ const healthCheck = await this.api.runV3HealthCheck();
2950
2419
  if (!healthCheck.healthy) {
2951
- this.logger.warn('V3 health probe did not confirm readiness; starting SSE agent stream anyway.');
2952
- if (spinner)
2953
- this.updateV3AgentSpinner(spinner, { type: 'v3_status', content: 'Starting V3 agent stream...' });
2420
+ const errorMsg = healthCheck.error || 'V3 agent service is not responding. The service may be down or unreachable.';
2421
+ this.logger.error('V3 agent health check failed: ' + errorMsg);
2422
+ throw new Error(errorMsg);
2954
2423
  }
2955
- else if (spinner) {
2424
+ if (spinner)
2956
2425
  this.updateV3AgentSpinner(spinner, { type: 'v3_status', content: 'Connected to V3. Starting agent execution...' });
2957
- }
2958
2426
  const workflowPromise = this.api.runV3AgentWorkflow(executionPrompt, {
2959
2427
  workspace: { path: workspacePath },
2960
2428
  ...workspaceContext,
@@ -2972,40 +2440,23 @@ export class ChatCommand {
2972
2440
  rawPrompt: prompt,
2973
2441
  contextualPrompt,
2974
2442
  history: this.getMessagesForModel(),
2975
- clientToolExecution: true,
2976
- liveToolEvidence: this.v3LiveToolEvidence,
2977
- onClientToolExecute: (event) => this.executeClientV3Tool(event),
2978
- onWorkspaceContext: async ({ contextId, serverWorkspaceRoot }) => {
2979
- if (workspaceWs || !contextId || !serverWorkspaceRoot || !this.shouldStartWorkspaceWatcher(workspacePath)) {
2980
- return;
2981
- }
2982
- const headers = await this.api.getV3AgentHeaders();
2983
- const token = String(headers.Authorization?.replace(/^Bearer\s+/i, '')
2984
- || headers['X-V3-Service-Key']
2985
- || headers['x-v3-service-key']
2986
- || '').trim();
2987
- if (!token) {
2988
- return;
2989
- }
2990
- const baseUrl = this.api.getV3AgentBaseUrls(false)[0] || 'https://coder.vigthoria.io';
2991
- const serverUrl = baseUrl.replace(/^http/i, 'ws');
2992
- workspaceWs = new WorkspaceWSClient({
2993
- serverUrl,
2994
- token,
2995
- contextId,
2996
- workspaceRoot: serverWorkspaceRoot,
2997
- });
2998
- workspaceWs.connect();
2999
- this.logger.debug(`Workspace WS sync bound to context ${contextId}`);
3000
- },
3001
2443
  ...runtimeContext,
3002
2444
  onStreamEvent: (event) => {
3003
2445
  if (event.type === 'plan') {
3004
- taskDisplay.complete(0);
3005
- taskDisplay.start(1);
3006
- const tasks = event?.plan?.tasks;
3007
- if (Array.isArray(tasks))
3008
- liveOutcome.tasksTotal = tasks.length;
2446
+ const plan = event?.plan || {};
2447
+ const isPlanningKeepalive = plan.status === 'planning' && !Array.isArray(plan.tasks);
2448
+ if (isPlanningKeepalive) {
2449
+ const elapsed = Number.isFinite(Number(plan.elapsed_seconds)) ? Number(plan.elapsed_seconds) : 0;
2450
+ taskDisplay.start(0, elapsed > 0 ? `planning (${elapsed}s)` : 'planning...');
2451
+ }
2452
+ else {
2453
+ taskDisplay.complete(0);
2454
+ const tasks = plan?.tasks;
2455
+ if (Array.isArray(tasks) && tasks.length > 0) {
2456
+ taskDisplay.start(1);
2457
+ liveOutcome.tasksTotal = tasks.length;
2458
+ }
2459
+ }
3009
2460
  }
3010
2461
  else if (event.type === 'executor_start') {
3011
2462
  taskDisplay.start(1);
@@ -3057,34 +2508,6 @@ export class ChatCommand {
3057
2508
  if (this.v3StreamingStarted) {
3058
2509
  process.stdout.write('\n');
3059
2510
  }
3060
- let finalContent = String(response.content || '').trim();
3061
- const needsUserReport = this.inferAgentTaskType(prompt) === 'analysis'
3062
- || this.isThinV3Summary(finalContent)
3063
- || this.v3LiveToolEvidence.length > 0;
3064
- if (needsUserReport) {
3065
- const userReport = this.buildUserFacingV3RunReport(prompt, workspacePath, {
3066
- partial: response.partial === true || this.isThinV3Summary(finalContent),
3067
- serverNote: finalContent && !this.isThinV3Summary(finalContent) ? finalContent : undefined,
3068
- });
3069
- if (userReport && !this.isThinV3Summary(userReport)) {
3070
- finalContent = userReport;
3071
- }
3072
- }
3073
- else if (this.isThinV3Summary(finalContent) && this.v3LiveToolEvidence.length > 0) {
3074
- const evidenceSummary = this.api.formatV3AgentResponse({
3075
- events: [],
3076
- liveToolEvidence: this.v3LiveToolEvidence,
3077
- });
3078
- if (evidenceSummary && !this.isThinV3Summary(evidenceSummary)) {
3079
- finalContent = evidenceSummary;
3080
- }
3081
- }
3082
- response.content = finalContent || response.content;
3083
- let v3UserReportPrinted = false;
3084
- if (!this.jsonOutput && finalContent && needsUserReport) {
3085
- this.printV3UserReport(finalContent);
3086
- v3UserReportPrinted = true;
3087
- }
3088
2511
  const previewGate = (response.metadata?.previewGate || null);
3089
2512
  const workspaceHasOutput = this.api.hasAgentWorkspaceOutput(workspaceContext);
3090
2513
  const success = previewGate?.required === true
@@ -3097,7 +2520,7 @@ export class ChatCommand {
3097
2520
  }
3098
2521
  this.logger.warn('Falling back to legacy CLI agent loop');
3099
2522
  this.logger.debug(`V3 agent workflow returned an incomplete result: ${previewGate?.error || 'workspace changes were not fully validated'}`);
3100
- stopWorkspaceSync();
2523
+ watcher?.stop();
3101
2524
  return false;
3102
2525
  }
3103
2526
  const errorMessage = `V3 agent workflow returned an incomplete result and legacy fallback is disabled. ${previewGate?.error || 'Workspace changes were not fully validated.'}`;
@@ -3118,11 +2541,11 @@ export class ChatCommand {
3118
2541
  metadata: { executionPath: 'v3-agent', previewGate },
3119
2542
  }, null, 2));
3120
2543
  }
3121
- stopWorkspaceSync();
2544
+ watcher?.stop();
3122
2545
  return true;
3123
2546
  }
3124
2547
  if (!this.jsonOutput && previewGate?.required && previewGate?.passed !== true && workspaceHasOutput) {
3125
- console.log(chalk.yellow(`Template Service preview gate did not fully validate this output, but generated workspace files were preserved${previewGate?.error ? `: ${previewGate.error}` : '.'}`));
2548
+ console.log(chalk_1.default.yellow(`Template Service preview gate did not fully validate this output, but generated workspace files were preserved${previewGate?.error ? `: ${previewGate.error}` : '.'}`));
3126
2549
  }
3127
2550
  if (this.jsonOutput) {
3128
2551
  console.log(JSON.stringify({
@@ -3138,59 +2561,43 @@ export class ChatCommand {
3138
2561
  }, null, 2));
3139
2562
  }
3140
2563
  else if (this.v3StreamingStarted) {
3141
- // Content may have been streamed already, but some V3 runs only stream
3142
- // status/tool messages and keep the useful final report in response.content.
3143
- if (!v3UserReportPrinted && !this.jsonOutput) {
3144
- console.log(chalk.gray(`Agent routing: ${routingPolicy.cloudSelected ? 'Vigthoria Cloud' : 'V3 Agent'}`));
3145
- }
3146
- if (!v3UserReportPrinted && response.content && this.shouldPrintV3FinalContent(response.content)) {
3147
- console.log(response.content);
3148
- }
3149
- else if (!v3UserReportPrinted && response.content && this.isThinV3Summary(response.content)) {
3150
- console.log(chalk.yellow('V3 agent finished after reading local files, but no final narrative summary was emitted.'));
3151
- console.log(chalk.gray('Use /continue and ask for a written overview.'));
2564
+ // Content was already streamed to stdout in real-time; skip duplicate print.
2565
+ if (!this.jsonOutput) {
2566
+ console.log(chalk_1.default.gray(`Agent routing: ${routingPolicy.cloudSelected ? 'Vigthoria Cloud' : 'V3 Agent'}`));
3152
2567
  }
3153
2568
  }
3154
2569
  else if (response.content) {
3155
- if (!v3UserReportPrinted && !this.directPromptMode) {
3156
- console.log(chalk.gray(`Agent routing: ${routingPolicy.cloudSelected ? 'Vigthoria Cloud' : 'V3 Agent'}`));
3157
- }
3158
- if (!v3UserReportPrinted && this.shouldPrintV3FinalContent(response.content)) {
3159
- console.log(response.content);
3160
- }
3161
- else if (!v3UserReportPrinted && this.isThinV3Summary(response.content)) {
3162
- console.log('V3 agent workflow completed, but no final task summary was emitted.');
3163
- }
3164
- else if (!v3UserReportPrinted) {
3165
- console.log(response.content);
2570
+ if (!this.directPromptMode) {
2571
+ console.log(chalk_1.default.gray(`Agent routing: ${routingPolicy.cloudSelected ? 'Vigthoria Cloud' : 'V3 Agent'}`));
3166
2572
  }
2573
+ console.log(response.content);
3167
2574
  }
3168
2575
  else {
3169
2576
  if (!this.directPromptMode) {
3170
- console.log(chalk.gray(`Agent routing: ${routingPolicy.cloudSelected ? 'Vigthoria Cloud' : 'V3 Agent'}`));
2577
+ console.log(chalk_1.default.gray(`Agent routing: ${routingPolicy.cloudSelected ? 'Vigthoria Cloud' : 'V3 Agent'}`));
3171
2578
  }
3172
2579
  console.log('V3 agent workflow completed.');
3173
2580
  }
3174
2581
  if (!this.jsonOutput && previewGate?.required) {
3175
2582
  if (previewGate.passed) {
3176
- console.log(chalk.gray(`Template Service preview gate: passed via ${previewGate.backendUrl || 'unknown backend'}`));
2583
+ console.log(chalk_1.default.gray(`Template Service preview gate: passed via ${previewGate.backendUrl || 'unknown backend'}`));
3177
2584
  }
3178
2585
  else {
3179
- console.log(chalk.yellow(`Template Service preview gate: failed${previewGate.error ? ` - ${previewGate.error}` : ''}`));
2586
+ console.log(chalk_1.default.yellow(`Template Service preview gate: failed${previewGate.error ? ` - ${previewGate.error}` : ''}`));
3180
2587
  }
3181
2588
  }
3182
2589
  // Show change summary for files touched by the agent
3183
2590
  if (!this.jsonOutput && !this.directPromptMode && response.changedFiles) {
3184
2591
  const fileCount = Object.keys(response.changedFiles).length;
3185
2592
  if (fileCount > 0) {
3186
- console.log(chalk.gray(`\nFiles changed: ${fileCount}`));
2593
+ console.log(chalk_1.default.gray(`\nFiles changed: ${fileCount}`));
3187
2594
  for (const relPath of Object.keys(response.changedFiles).slice(0, 15)) {
3188
- console.log(chalk.gray(` ${chalk.green('+')} ${relPath}`));
2595
+ console.log(chalk_1.default.gray(` ${chalk_1.default.green('+')} ${relPath}`));
3189
2596
  }
3190
2597
  if (fileCount > 15) {
3191
- console.log(chalk.gray(` ... and ${fileCount - 15} more`));
2598
+ console.log(chalk_1.default.gray(` ... and ${fileCount - 15} more`));
3192
2599
  }
3193
- console.log(chalk.gray(`Run ${chalk.cyan('vigthoria preview --diff')} for full visual diffs.`));
2600
+ console.log(chalk_1.default.gray(`Run ${chalk_1.default.cyan('vigthoria preview --diff')} for full visual diffs.`));
3194
2601
  }
3195
2602
  }
3196
2603
  // Resolve the Execute-tasks spinner: only mark it ✓ when the run actually
@@ -3228,8 +2635,8 @@ export class ChatCommand {
3228
2635
  }
3229
2636
  selfHealTool = healResult.tool || null;
3230
2637
  if (!this.directPromptMode) {
3231
- const hs = healResult.passed ? chalk.green('passed') : chalk.yellow('partial');
3232
- console.log(chalk.gray(`Self-healing: ${hs} (${healResult.tool})`));
2638
+ const hs = healResult.passed ? chalk_1.default.green('passed') : chalk_1.default.yellow('partial');
2639
+ console.log(chalk_1.default.gray(`Self-healing: ${hs} (${healResult.tool})`));
3233
2640
  }
3234
2641
  }
3235
2642
  else {
@@ -3266,8 +2673,8 @@ export class ChatCommand {
3266
2673
  hasOutput: workspaceHasOutput,
3267
2674
  selfHealStatus,
3268
2675
  selfHealTool,
3269
- plannerError: liveOutcome.plannerError ? sanitizeUserFacingErrorText(liveOutcome.plannerError) : null,
3270
- executorError: liveOutcome.executorError ? sanitizeUserFacingErrorText(liveOutcome.executorError) : null,
2676
+ plannerError: liveOutcome.plannerError ? (0, api_js_1.sanitizeUserFacingErrorText)(liveOutcome.plannerError) : null,
2677
+ executorError: liveOutcome.executorError ? (0, api_js_1.sanitizeUserFacingErrorText)(liveOutcome.executorError) : null,
3271
2678
  finishedAt: Date.now(),
3272
2679
  };
3273
2680
  if (!this.jsonOutput && !this.directPromptMode) {
@@ -3275,11 +2682,11 @@ export class ChatCommand {
3275
2682
  this.printAgentRunSummary(this.lastAgentRunOutcome, executorSucceeded, changedFileCount);
3276
2683
  }
3277
2684
  this.messages.push({ role: 'assistant', content: response.content || 'V3 agent workflow completed.' });
3278
- stopWorkspaceSync();
2685
+ watcher?.stop();
3279
2686
  return true;
3280
2687
  }
3281
2688
  catch (error) {
3282
- stopWorkspaceSync();
2689
+ watcher?.stop();
3283
2690
  if (!this.api.hasAgentWorkspaceOutput(workspaceContext)) {
3284
2691
  const recovered = await this.tryRecoverV3ServiceAndRetry(executionPrompt, prompt, workspaceContext, routingPolicy, spinner, error);
3285
2692
  if (recovered) {
@@ -3291,13 +2698,13 @@ export class ChatCommand {
3291
2698
  spinner.stop();
3292
2699
  }
3293
2700
  this.logger.warn('Falling back to legacy CLI agent loop');
3294
- this.logger.debug(`V3 agent workflow unavailable: ${sanitizeUserFacingErrorText(error.message || '')}`);
2701
+ this.logger.debug(`V3 agent workflow unavailable: ${(0, api_js_1.sanitizeUserFacingErrorText)(error.message || '')}`);
3295
2702
  return false;
3296
2703
  }
3297
2704
  if (spinner) {
3298
2705
  spinner.stop();
3299
2706
  }
3300
- const safeDetail = sanitizeUserFacingErrorText(error.message || '');
2707
+ const safeDetail = (0, api_js_1.sanitizeUserFacingErrorText)(error.message || '');
3301
2708
  const errorMessage = safeDetail
3302
2709
  ? `Agent mode is unavailable right now. ${safeDetail}`
3303
2710
  : 'Agent mode is unavailable right now. Please retry shortly or run vigthoria login if the issue persists.';
@@ -3326,8 +2733,8 @@ export class ChatCommand {
3326
2733
  hasOutput: this.api.hasAgentWorkspaceOutput(workspaceContext),
3327
2734
  selfHealStatus: 'skipped',
3328
2735
  selfHealTool: null,
3329
- plannerError: liveOutcome.plannerError ? sanitizeUserFacingErrorText(liveOutcome.plannerError) : null,
3330
- executorError: liveOutcome.executorError ? sanitizeUserFacingErrorText(liveOutcome.executorError) : safeDetail || null,
2736
+ plannerError: liveOutcome.plannerError ? (0, api_js_1.sanitizeUserFacingErrorText)(liveOutcome.plannerError) : null,
2737
+ executorError: liveOutcome.executorError ? (0, api_js_1.sanitizeUserFacingErrorText)(liveOutcome.executorError) : safeDetail || null,
3331
2738
  finishedAt: Date.now(),
3332
2739
  };
3333
2740
  if (this.jsonOutput) {
@@ -3357,7 +2764,7 @@ export class ChatCommand {
3357
2764
  spinner.stop();
3358
2765
  }
3359
2766
  if (!this.jsonOutput) {
3360
- console.log(chalk.yellow(`V3 recovery: ${recovery.message} Retrying once...`));
2767
+ console.log(chalk_1.default.yellow(`V3 recovery: ${recovery.message} Retrying once...`));
3361
2768
  }
3362
2769
  this.v3IterationCount = 0;
3363
2770
  this.v3ToolCallCount = 0;
@@ -3401,7 +2808,7 @@ export class ChatCommand {
3401
2808
  }, null, 2));
3402
2809
  }
3403
2810
  else if (!this.v3StreamingStarted && retryResponse.content) {
3404
- console.log(chalk.gray(`Agent routing: ${routingPolicy.cloudSelected ? 'Vigthoria Cloud' : 'V3 Agent'}`));
2811
+ console.log(chalk_1.default.gray(`Agent routing: ${routingPolicy.cloudSelected ? 'Vigthoria Cloud' : 'V3 Agent'}`));
3405
2812
  console.log(retryResponse.content);
3406
2813
  }
3407
2814
  this.messages.push({ role: 'assistant', content: retryResponse.content || 'V3 agent workflow completed after recovery.' });
@@ -3418,10 +2825,10 @@ export class ChatCommand {
3418
2825
  ? 'Interactive Agent Chat'
3419
2826
  : 'Interactive Chat';
3420
2827
  this.logger.section(this.workflowTarget ? `${chatTitle} Via Workflow Target` : chatTitle);
3421
- console.log(chalk.gray('Type /help for commands. Type /exit to quit.'));
3422
- console.log(chalk.gray('Multi-line: end a line with \\ or start a block with {{{ and end with }}}'));
2828
+ console.log(chalk_1.default.gray('Type /help for commands. Type /exit to quit.'));
2829
+ console.log(chalk_1.default.gray('Multi-line: end a line with \\ or start a block with {{{ and end with }}}'));
3423
2830
  if (this.workflowTarget) {
3424
- console.log(chalk.gray(`Workflow target: ${this.workflowTarget}`));
2831
+ console.log(chalk_1.default.gray(`Workflow target: ${this.workflowTarget}`));
3425
2832
  }
3426
2833
  const rl = readline.createInterface({
3427
2834
  input: process.stdin,
@@ -3431,17 +2838,17 @@ export class ChatCommand {
3431
2838
  const readMultiLineInput = async () => {
3432
2839
  const lines = [];
3433
2840
  let firstLine = await new Promise((resolve) => {
3434
- rl.question(chalk.blue('> '), resolve);
2841
+ rl.question(chalk_1.default.blue('> '), resolve);
3435
2842
  });
3436
2843
  // Check for {{{ block mode
3437
2844
  if (firstLine.trim() === '{{{' || firstLine.trim().endsWith('{{{')) {
3438
2845
  if (firstLine.trim() !== '{{{') {
3439
2846
  lines.push(firstLine.trim().replace(/\{\{\{$/, '').trim());
3440
2847
  }
3441
- console.log(chalk.gray(' (multi-line mode: type }}} on its own line to finish)'));
2848
+ console.log(chalk_1.default.gray(' (multi-line mode: type }}} on its own line to finish)'));
3442
2849
  while (true) {
3443
2850
  const line = await new Promise((resolve) => {
3444
- rl.question(chalk.gray(' '), resolve);
2851
+ rl.question(chalk_1.default.gray(' '), resolve);
3445
2852
  });
3446
2853
  if (line.trim() === '}}}')
3447
2854
  break;
@@ -3453,7 +2860,7 @@ export class ChatCommand {
3453
2860
  while (firstLine.endsWith('\\')) {
3454
2861
  lines.push(firstLine.slice(0, -1));
3455
2862
  firstLine = await new Promise((resolve) => {
3456
- rl.question(chalk.gray(' '), resolve);
2863
+ rl.question(chalk_1.default.gray(' '), resolve);
3457
2864
  });
3458
2865
  }
3459
2866
  lines.push(firstLine);
@@ -3494,8 +2901,8 @@ export class ChatCommand {
3494
2901
  else {
3495
2902
  this.syncInteractiveModeModel('chat');
3496
2903
  }
3497
- console.log(chalk.yellow(`Agent mode: ${this.agentMode ? 'ON' : 'OFF'}`));
3498
- console.log(chalk.gray(`Model: ${this.currentModel}`));
2904
+ console.log(chalk_1.default.yellow(`Agent mode: ${this.agentMode ? 'ON' : 'OFF'}`));
2905
+ console.log(chalk_1.default.gray(`Model: ${this.currentModel}`));
3499
2906
  if (this.currentSession) {
3500
2907
  this.currentSession.agentMode = this.agentMode;
3501
2908
  this.currentSession.operatorMode = this.operatorMode;
@@ -3517,8 +2924,8 @@ export class ChatCommand {
3517
2924
  else {
3518
2925
  this.syncInteractiveModeModel('chat');
3519
2926
  }
3520
- console.log(chalk.yellow(`Operator mode: ${this.operatorMode ? 'ON' : 'OFF'}`));
3521
- console.log(chalk.gray(`Model: ${this.currentModel}`));
2927
+ console.log(chalk_1.default.yellow(`Operator mode: ${this.operatorMode ? 'ON' : 'OFF'}`));
2928
+ console.log(chalk_1.default.gray(`Model: ${this.currentModel}`));
3522
2929
  if (this.currentSession) {
3523
2930
  this.currentSession.agentMode = this.agentMode;
3524
2931
  this.currentSession.operatorMode = this.operatorMode;
@@ -3529,7 +2936,7 @@ export class ChatCommand {
3529
2936
  }
3530
2937
  if (trimmed === '/clear') {
3531
2938
  this.messages = [];
3532
- console.log(chalk.yellow('Conversation cleared.'));
2939
+ console.log(chalk_1.default.yellow('Conversation cleared.'));
3533
2940
  continue;
3534
2941
  }
3535
2942
  if (trimmed === '/status') {
@@ -3539,7 +2946,7 @@ export class ChatCommand {
3539
2946
  if (trimmed === '/retry') {
3540
2947
  const followUp = this.buildRetryPrompt();
3541
2948
  if (!followUp) {
3542
- console.log(chalk.yellow('Nothing to retry — run an agent task first.'));
2949
+ console.log(chalk_1.default.yellow('Nothing to retry — run an agent task first.'));
3543
2950
  continue;
3544
2951
  }
3545
2952
  const nextSignature = this.computeRetryPromptSignature();
@@ -3553,11 +2960,11 @@ export class ChatCommand {
3553
2960
  if (this.retryPromptStreak >= 3) {
3554
2961
  const continuePrompt = this.buildContinuePrompt();
3555
2962
  if (continuePrompt) {
3556
- console.log(chalk.yellow('Repeated /retry detected with the same failing task set. Escalating to /continue to avoid planner loops.'));
2963
+ console.log(chalk_1.default.yellow('Repeated /retry detected with the same failing task set. Escalating to /continue to avoid planner loops.'));
3557
2964
  if (!this.agentMode) {
3558
2965
  this.agentMode = true;
3559
2966
  this.syncInteractiveModeModel('agent');
3560
- console.log(chalk.gray('Agent mode re-enabled for continuation.'));
2967
+ console.log(chalk_1.default.gray('Agent mode re-enabled for continuation.'));
3561
2968
  }
3562
2969
  await this.runAgentTurn(continuePrompt);
3563
2970
  continue;
@@ -3566,7 +2973,7 @@ export class ChatCommand {
3566
2973
  if (!this.agentMode) {
3567
2974
  this.agentMode = true;
3568
2975
  this.syncInteractiveModeModel('agent');
3569
- console.log(chalk.gray('Agent mode re-enabled for retry.'));
2976
+ console.log(chalk_1.default.gray('Agent mode re-enabled for retry.'));
3570
2977
  }
3571
2978
  await this.runAgentTurn(followUp);
3572
2979
  continue;
@@ -3574,32 +2981,26 @@ export class ChatCommand {
3574
2981
  if (trimmed === '/continue') {
3575
2982
  const followUp = this.buildContinuePrompt();
3576
2983
  if (!followUp) {
3577
- console.log(chalk.yellow('Nothing to continue — run an agent task first.'));
2984
+ console.log(chalk_1.default.yellow('Nothing to continue — run an agent task first.'));
3578
2985
  continue;
3579
2986
  }
3580
2987
  if (!this.agentMode) {
3581
2988
  this.agentMode = true;
3582
2989
  this.syncInteractiveModeModel('agent');
3583
- console.log(chalk.gray('Agent mode re-enabled for continuation.'));
2990
+ console.log(chalk_1.default.gray('Agent mode re-enabled for continuation.'));
3584
2991
  }
3585
- await this.runAgentTurn(followUp, { skipLocalLoop: true, forceV3: true });
2992
+ await this.runAgentTurn(followUp);
3586
2993
  continue;
3587
2994
  }
3588
- if (/^(?:\/logout|logout)$/i.test(trimmed)) {
3589
- const { logout } = await import('./auth.js');
3590
- await logout();
3591
- console.log(chalk.gray('Session ended.'));
3592
- break;
3593
- }
3594
2995
  if (trimmed === '/save') {
3595
2996
  this.saveSession();
3596
- console.log(chalk.green('Session saved.'));
2997
+ console.log(chalk_1.default.green('Session saved.'));
3597
2998
  continue;
3598
2999
  }
3599
3000
  if (trimmed.startsWith('/model ')) {
3600
3001
  this.currentModel = trimmed.slice(7).trim() || this.currentModel;
3601
3002
  this.modelExplicitlySelected = true;
3602
- console.log(chalk.yellow(`Model changed to: ${this.currentModel}`));
3003
+ console.log(chalk_1.default.yellow(`Model changed to: ${this.currentModel}`));
3603
3004
  if (this.currentSession) {
3604
3005
  this.currentSession.model = this.currentModel;
3605
3006
  this.saveSession();
@@ -3647,74 +3048,71 @@ export class ChatCommand {
3647
3048
  * - Never leave the spinners (`⟳`, `–`) ambiguous when the prompt returns.
3648
3049
  */
3649
3050
  printAgentRunSummary(outcome, executorSucceeded, changedFileCount) {
3650
- const bar = chalk.gray('─'.repeat(63));
3651
- const ok = chalk.green('✓');
3652
- const warn = chalk.yellow('⚠');
3653
- const bad = chalk.red('✗');
3051
+ const bar = chalk_1.default.gray('─'.repeat(63));
3052
+ const ok = chalk_1.default.green('✓');
3053
+ const warn = chalk_1.default.yellow('⚠');
3054
+ const bad = chalk_1.default.red('✗');
3654
3055
  const failedList = outcome.failedTaskIds.slice(0, 8);
3655
3056
  const unfinishedList = outcome.unfinishedTaskIds.slice(0, 8);
3656
3057
  const hasTaskInfo = outcome.tasksTotal > 0 || failedList.length > 0 || unfinishedList.length > 0;
3657
3058
  console.log('');
3658
3059
  console.log(bar);
3659
3060
  if (executorSucceeded && outcome.selfHealStatus !== 'partial' && outcome.selfHealStatus !== 'failed' && failedList.length === 0) {
3660
- console.log(`${ok} ${chalk.bold('Agent run finished')}${changedFileCount ? chalk.gray(` — ${changedFileCount} file${changedFileCount === 1 ? '' : 's'} changed`) : ''}`);
3061
+ console.log(`${ok} ${chalk_1.default.bold('Agent run finished')}${changedFileCount ? chalk_1.default.gray(` — ${changedFileCount} file${changedFileCount === 1 ? '' : 's'} changed`) : ''}`);
3661
3062
  }
3662
3063
  else if (executorSucceeded) {
3663
- console.log(`${warn} ${chalk.bold('Agent run finished with warnings')}${changedFileCount ? chalk.gray(` — ${changedFileCount} file${changedFileCount === 1 ? '' : 's'} changed`) : ''}`);
3064
+ console.log(`${warn} ${chalk_1.default.bold('Agent run finished with warnings')}${changedFileCount ? chalk_1.default.gray(` — ${changedFileCount} file${changedFileCount === 1 ? '' : 's'} changed`) : ''}`);
3664
3065
  }
3665
3066
  else {
3666
- console.log(`${bad} ${chalk.bold('Agent run did not complete')}${changedFileCount ? chalk.gray(` — ${changedFileCount} file${changedFileCount === 1 ? '' : 's'} changed`) : ''}`);
3067
+ console.log(`${bad} ${chalk_1.default.bold('Agent run did not complete')}${changedFileCount ? chalk_1.default.gray(` — ${changedFileCount} file${changedFileCount === 1 ? '' : 's'} changed`) : ''}`);
3667
3068
  }
3668
3069
  if (hasTaskInfo) {
3669
3070
  const succ = outcome.tasksTotal > 0 ? `${outcome.tasksSucceeded}/${outcome.tasksTotal}` : `${outcome.tasksSucceeded}`;
3670
- console.log(chalk.gray(` Tasks completed: ${succ}`));
3071
+ console.log(chalk_1.default.gray(` Tasks completed: ${succ}`));
3671
3072
  if (failedList.length > 0) {
3672
- const more = outcome.failedTaskIds.length > failedList.length ? chalk.gray(` (+${outcome.failedTaskIds.length - failedList.length} more)`) : '';
3673
- console.log(chalk.gray(' Failed: ') + chalk.red(failedList.join(', ')) + more);
3073
+ const more = outcome.failedTaskIds.length > failedList.length ? chalk_1.default.gray(` (+${outcome.failedTaskIds.length - failedList.length} more)`) : '';
3074
+ console.log(chalk_1.default.gray(' Failed: ') + chalk_1.default.red(failedList.join(', ')) + more);
3674
3075
  }
3675
3076
  if (unfinishedList.length > 0) {
3676
- const more = outcome.unfinishedTaskIds.length > unfinishedList.length ? chalk.gray(` (+${outcome.unfinishedTaskIds.length - unfinishedList.length} more)`) : '';
3677
- console.log(chalk.gray(' Pending: ') + chalk.yellow(unfinishedList.join(', ')) + more);
3077
+ const more = outcome.unfinishedTaskIds.length > unfinishedList.length ? chalk_1.default.gray(` (+${outcome.unfinishedTaskIds.length - unfinishedList.length} more)`) : '';
3078
+ console.log(chalk_1.default.gray(' Pending: ') + chalk_1.default.yellow(unfinishedList.join(', ')) + more);
3678
3079
  }
3679
3080
  }
3680
- else if (!executorSucceeded) {
3681
- console.log(chalk.gray(' The detailed overview is printed above when available.'));
3682
- }
3683
3081
  if (typeof outcome.qualityScore === 'number') {
3684
3082
  const score = outcome.qualityScore.toFixed(1);
3685
- const colour = outcome.qualityScore >= 70 ? chalk.green : outcome.qualityScore >= 30 ? chalk.yellow : chalk.red;
3686
- console.log(chalk.gray(' Quality: ') + colour(`${score}/100`));
3083
+ const colour = outcome.qualityScore >= 70 ? chalk_1.default.green : outcome.qualityScore >= 30 ? chalk_1.default.yellow : chalk_1.default.red;
3084
+ console.log(chalk_1.default.gray(' Quality: ') + colour(`${score}/100`));
3687
3085
  }
3688
3086
  if (outcome.qualityBlockers.length > 0) {
3689
3087
  const list = outcome.qualityBlockers.slice(0, 3).join('; ');
3690
- const more = outcome.qualityBlockers.length > 3 ? chalk.gray(` (+${outcome.qualityBlockers.length - 3} more)`) : '';
3691
- console.log(chalk.gray(' Blockers: ') + chalk.yellow(list) + more);
3088
+ const more = outcome.qualityBlockers.length > 3 ? chalk_1.default.gray(` (+${outcome.qualityBlockers.length - 3} more)`) : '';
3089
+ console.log(chalk_1.default.gray(' Blockers: ') + chalk_1.default.yellow(list) + more);
3692
3090
  }
3693
3091
  if (outcome.qualityMissing.length > 0) {
3694
3092
  const list = outcome.qualityMissing.slice(0, 5).join(', ');
3695
- const more = outcome.qualityMissing.length > 5 ? chalk.gray(` (+${outcome.qualityMissing.length - 5} more)`) : '';
3696
- console.log(chalk.gray(' Missing: ') + chalk.yellow(list) + more);
3093
+ const more = outcome.qualityMissing.length > 5 ? chalk_1.default.gray(` (+${outcome.qualityMissing.length - 5} more)`) : '';
3094
+ console.log(chalk_1.default.gray(' Missing: ') + chalk_1.default.yellow(list) + more);
3697
3095
  }
3698
3096
  if (outcome.plannerError) {
3699
- console.log(chalk.gray(' Planner: ') + chalk.red(outcome.plannerError.slice(0, 140)));
3097
+ console.log(chalk_1.default.gray(' Planner: ') + chalk_1.default.red(outcome.plannerError.slice(0, 140)));
3700
3098
  }
3701
3099
  if (outcome.executorError) {
3702
- console.log(chalk.gray(' Executor: ') + chalk.red(outcome.executorError.slice(0, 140)));
3100
+ console.log(chalk_1.default.gray(' Executor: ') + chalk_1.default.red(outcome.executorError.slice(0, 140)));
3703
3101
  }
3704
3102
  console.log('');
3705
- console.log(chalk.gray('What you can do next:'));
3103
+ console.log(chalk_1.default.gray('What you can do next:'));
3706
3104
  if (failedList.length > 0 || unfinishedList.length > 0 || !executorSucceeded) {
3707
- console.log(' ' + chalk.cyan('/retry') + chalk.gray(' resume the failed/unfinished tasks only'));
3708
- console.log(' ' + chalk.cyan('/continue') + chalk.gray(' ask the agent to keep working from this state'));
3105
+ console.log(' ' + chalk_1.default.cyan('/retry') + chalk_1.default.gray(' resume the failed/unfinished tasks only'));
3106
+ console.log(' ' + chalk_1.default.cyan('/continue') + chalk_1.default.gray(' ask the agent to keep working from this state'));
3709
3107
  }
3710
3108
  else {
3711
- console.log(' ' + chalk.cyan('/continue') + chalk.gray(' add a follow-up instruction in this thread'));
3109
+ console.log(' ' + chalk_1.default.cyan('/continue') + chalk_1.default.gray(' add a follow-up instruction in this thread'));
3712
3110
  }
3713
3111
  if (changedFileCount > 0) {
3714
- console.log(' ' + chalk.cyan('vigthoria preview --diff') + chalk.gray(' inspect the file changes'));
3112
+ console.log(' ' + chalk_1.default.cyan('vigthoria preview --diff') + chalk_1.default.gray(' inspect the file changes'));
3715
3113
  }
3716
- console.log(' ' + chalk.cyan('/status') + chalk.gray(' re-print this summary later'));
3717
- console.log(' ' + chalk.cyan('/exit') + chalk.gray(' leave interactive chat'));
3114
+ console.log(' ' + chalk_1.default.cyan('/status') + chalk_1.default.gray(' re-print this summary later'));
3115
+ console.log(' ' + chalk_1.default.cyan('/exit') + chalk_1.default.gray(' leave interactive chat'));
3718
3116
  console.log(bar);
3719
3117
  console.log('');
3720
3118
  }
@@ -3768,18 +3166,15 @@ export class ChatCommand {
3768
3166
  const missingLine = o.qualityMissing.length > 0
3769
3167
  ? `\nMissing pieces: ${o.qualityMissing.slice(0, 6).join(', ')}.`
3770
3168
  : '';
3771
- return `Continue the previous agent run from the current workspace state without re-doing already-completed work.${taskList}${blockerLine}${missingLine}
3772
- Original request was: ${o.prompt}
3773
-
3774
- Now implement the missing fixes by editing the local workspace files. Use write/edit tools to apply the required code and HTML changes so the project runs.`;
3169
+ return `Continue the previous agent run from the current workspace state without re-doing already-completed work.${taskList}${blockerLine}${missingLine}\nOriginal request was: ${o.prompt}`;
3775
3170
  }
3776
3171
  /**
3777
3172
  * Re-print the last agent run summary, or guide the user when there isn't one.
3778
3173
  */
3779
3174
  showAgentRunStatus() {
3780
3175
  if (!this.lastAgentRunOutcome) {
3781
- console.log(chalk.yellow('No agent run has finished in this session yet.'));
3782
- console.log(chalk.gray('Toggle agent mode with /agent and send a request to start one.'));
3176
+ console.log(chalk_1.default.yellow('No agent run has finished in this session yet.'));
3177
+ console.log(chalk_1.default.gray('Toggle agent mode with /agent and send a request to start one.'));
3783
3178
  return;
3784
3179
  }
3785
3180
  const o = this.lastAgentRunOutcome;
@@ -3792,45 +3187,45 @@ Now implement the missing fixes by editing the local workspace files. Use write/
3792
3187
  }
3793
3188
  showContext() {
3794
3189
  if (!this.currentSession) {
3795
- console.log(chalk.yellow('No active session.'));
3190
+ console.log(chalk_1.default.yellow('No active session.'));
3796
3191
  return;
3797
3192
  }
3798
- console.log(chalk.cyan(this.getCurrentSessionInfo()));
3193
+ console.log(chalk_1.default.cyan(this.getCurrentSessionInfo()));
3799
3194
  if (this.currentSession.memorySummary?.trim()) {
3800
3195
  console.log();
3801
- console.log(chalk.white('Compact Session Memory:'));
3802
- console.log(chalk.gray(this.currentSession.memorySummary.trim()));
3196
+ console.log(chalk_1.default.white('Compact Session Memory:'));
3197
+ console.log(chalk_1.default.gray(this.currentSession.memorySummary.trim()));
3803
3198
  }
3804
3199
  else {
3805
- console.log(chalk.gray('No compact session memory summary yet.'));
3200
+ console.log(chalk_1.default.gray('No compact session memory summary yet.'));
3806
3201
  }
3807
3202
  const runtime = this.getRuntimeEnvironmentContext();
3808
3203
  console.log();
3809
- console.log(chalk.white('Runtime Environment:'));
3810
- console.log(chalk.gray(`Platform: ${runtime.platform} (${runtime.osPlatform})`));
3811
- console.log(chalk.gray(`Machine scope: ${runtime.machineScope}`));
3812
- console.log(chalk.gray(`Workspace: ${runtime.workspacePath}`));
3813
- console.log(chalk.gray(`CWD: ${runtime.cwd}`));
3814
- console.log(chalk.gray(`Server-bindable workspace: ${runtime.serverBindableWorkspace ? 'yes' : 'no'}`));
3204
+ console.log(chalk_1.default.white('Runtime Environment:'));
3205
+ console.log(chalk_1.default.gray(`Platform: ${runtime.platform} (${runtime.osPlatform})`));
3206
+ console.log(chalk_1.default.gray(`Machine scope: ${runtime.machineScope}`));
3207
+ console.log(chalk_1.default.gray(`Workspace: ${runtime.workspacePath}`));
3208
+ console.log(chalk_1.default.gray(`CWD: ${runtime.cwd}`));
3209
+ console.log(chalk_1.default.gray(`Server-bindable workspace: ${runtime.serverBindableWorkspace ? 'yes' : 'no'}`));
3815
3210
  this.showProjectMemory();
3816
3211
  }
3817
3212
  showProjectMemory() {
3818
3213
  if (!this.projectMemory) {
3819
- this.projectMemory = new ProjectMemoryService(this.currentProjectPath);
3214
+ this.projectMemory = new project_memory_js_1.ProjectMemoryService(this.currentProjectPath);
3820
3215
  }
3821
3216
  const status = this.projectMemory.getStatus();
3822
3217
  console.log();
3823
- console.log(chalk.white('Project Brain:'));
3824
- console.log(chalk.gray(`Path: ${status.memoryDir}`));
3825
- console.log(chalk.gray(`Items: ${status.itemCount}`));
3218
+ console.log(chalk_1.default.white('Project Brain:'));
3219
+ console.log(chalk_1.default.gray(`Path: ${status.memoryDir}`));
3220
+ console.log(chalk_1.default.gray(`Items: ${status.itemCount}`));
3826
3221
  const typeSummary = Object.entries(status.typeCounts).map(([type, count]) => `${type}=${count}`).join(', ');
3827
3222
  if (typeSummary) {
3828
- console.log(chalk.gray(`Types: ${typeSummary}`));
3223
+ console.log(chalk_1.default.gray(`Types: ${typeSummary}`));
3829
3224
  }
3830
3225
  }
3831
3226
  compactCurrentSession() {
3832
3227
  if (!this.currentSession) {
3833
- console.log(chalk.yellow('No active session.'));
3228
+ console.log(chalk_1.default.yellow('No active session.'));
3834
3229
  return;
3835
3230
  }
3836
3231
  this.currentSession.messages = [...this.messages];
@@ -3838,7 +3233,7 @@ Now implement the missing fixes by editing the local workspace files. Use write/
3838
3233
  this.messages = [...this.currentSession.messages];
3839
3234
  this.sessionManager.save(this.currentSession);
3840
3235
  if (!this.projectMemory) {
3841
- this.projectMemory = new ProjectMemoryService(this.currentProjectPath);
3236
+ this.projectMemory = new project_memory_js_1.ProjectMemoryService(this.currentProjectPath);
3842
3237
  }
3843
3238
  this.projectMemory.rememberConversation(this.messages, {
3844
3239
  source: 'manual-compact',
@@ -3846,7 +3241,7 @@ Now implement the missing fixes by editing the local workspace files. Use write/
3846
3241
  model: this.currentModel,
3847
3242
  sessionSummary: this.currentSession.memorySummary || '',
3848
3243
  });
3849
- console.log(chalk.green('Session compacted into memory summary and project brain.'));
3244
+ console.log(chalk_1.default.green('Session compacted into memory summary and project brain.'));
3850
3245
  }
3851
3246
  ensureAgentSystemPrompt() {
3852
3247
  const hasSystemPrompt = this.messages.some((message) => message.role === 'system' && message.content.includes('Vigthoria CLI agent operating contract'));
@@ -3859,8 +3254,7 @@ Now implement the missing fixes by editing the local workspace files. Use write/
3859
3254
  });
3860
3255
  }
3861
3256
  buildAgentSystemPrompt() {
3862
- const runtime = this.getRuntimeEnvironmentContext();
3863
- const toolCatalog = AgenticTools.getToolDefinitions()
3257
+ const toolCatalog = tools_js_1.AgenticTools.getToolDefinitions()
3864
3258
  .map((tool) => {
3865
3259
  const params = tool.parameters
3866
3260
  .map((param) => `${param.name}${param.required ? ' (required)' : ''}`)
@@ -3871,7 +3265,6 @@ Now implement the missing fixes by editing the local workspace files. Use write/
3871
3265
  return [
3872
3266
  'Vigthoria CLI agent operating contract.',
3873
3267
  `You are operating inside the project root: ${this.currentProjectPath}`,
3874
- `Execution platform: ${runtime.platform} (${runtime.osPlatform}). Machine scope: ${runtime.machineScope}.`,
3875
3268
  `You are operating on the user's LOCAL machine. This CLI is not a server-only runtime.`,
3876
3269
  `All file reads/writes and command execution must target the local project/workspace path unless the user explicitly requests remote/server execution.`,
3877
3270
  'For command execution: ask for confirmation before risky commands; never redirect normal local work to server paths.',
@@ -3904,12 +3297,10 @@ Now implement the missing fixes by editing the local workspace files. Use write/
3904
3297
  ].join('\n');
3905
3298
  }
3906
3299
  buildScopedUserPrompt(prompt) {
3907
- const runtime = this.getRuntimeEnvironmentContext();
3908
3300
  return [
3909
3301
  this.buildExecutionPrompt(prompt),
3910
3302
  '',
3911
3303
  `Project root: ${this.currentProjectPath}`,
3912
- `Execution platform: ${runtime.platform}. Machine scope: ${runtime.machineScope}.`,
3913
3304
  'Stay within this project root unless the user explicitly expands scope.',
3914
3305
  'Finish the request and stop once it is complete.',
3915
3306
  ].join('\n');
@@ -3991,32 +3382,14 @@ Now implement the missing fixes by editing the local workspace files. Use write/
3991
3382
  if (this.isBrowserTaskPrompt(prompt)) {
3992
3383
  return false;
3993
3384
  }
3994
- const runtime = this.getRuntimeEnvironmentContext();
3995
- const forceRemoteAgent = /^(1|true|yes)$/i.test(String(process.env.VIGTHORIA_PREFER_V3_AGENT_LOOP
3996
- || process.env.VIGTHORIA_FORCE_REMOTE_AGENT
3997
- || ''));
3998
- // CLI sessions on a user's local machine must execute tools locally.
3999
- // Remote V3 only sees a partial hydrated copy and cannot reach real paths.
4000
- if (runtime.machineScope === 'local-machine') {
4001
- if (forceRemoteAgent) {
4002
- return false;
4003
- }
4004
- if (this.isImplementationPrompt(prompt)) {
4005
- return true;
4006
- }
4007
- const preferLocalOptIn = /^(1|true|yes)$/i.test(String(process.env.VIGTHORIA_PREFER_LOCAL_AGENT_LOOP || ''));
4008
- // Read-only analysis on a Windows/macOS/Linux desktop is faster and more
4009
- // reliable through V3 + client-side read tools than a blocking chat route.
4010
- if (this.isAnalysisLookupPrompt(prompt) && !preferLocalOptIn) {
4011
- return false;
4012
- }
4013
- return true;
4014
- }
4015
- // Server-bindable workspaces keep V3 as the default execution path.
3385
+ // Product default: Agent mode routes through V3.
3386
+ // Local loop is opt-in for explicit offline/debug scenarios only.
3387
+ // Explicit local paths now influence project root resolution, not routing mode.
4016
3388
  const preferLocalOptIn = /^(1|true|yes)$/i.test(String(process.env.VIGTHORIA_PREFER_LOCAL_AGENT_LOOP || ''));
4017
3389
  if (!preferLocalOptIn) {
4018
3390
  return false;
4019
3391
  }
3392
+ const runtime = this.getRuntimeEnvironmentContext();
4020
3393
  if (this.directPromptMode && this.isRepoGroundedPrompt(prompt)) {
4021
3394
  return false;
4022
3395
  }
@@ -4525,57 +3898,6 @@ Now implement the missing fixes by editing the local workspace files. Use write/
4525
3898
  .trim();
4526
3899
  return normalized || null;
4527
3900
  }
4528
- async tryLocalToolFallback(call, result) {
4529
- if (result.success || !this.tools) {
4530
- return result;
4531
- }
4532
- const runtime = this.getRuntimeEnvironmentContext();
4533
- if (runtime.machineScope !== 'local-machine') {
4534
- return result;
4535
- }
4536
- if (call.tool === 'read_file' && call.args.path) {
4537
- const parent = path.dirname(call.args.path);
4538
- const listPath = parent === '.' || parent === '' ? '.' : parent;
4539
- const listResult = await this.tools.execute({
4540
- tool: 'list_dir',
4541
- args: { path: listPath },
4542
- });
4543
- if (listResult.success && listResult.output) {
4544
- return {
4545
- ...result,
4546
- suggestion: `File not found at "${call.args.path}". Parent directory "${listPath}" contains:\n${listResult.output}`,
4547
- };
4548
- }
4549
- const baseName = path.basename(call.args.path);
4550
- const globResult = await this.tools.execute({
4551
- tool: 'glob',
4552
- args: { pattern: `**/${baseName}` },
4553
- });
4554
- if (globResult.success && globResult.output?.trim()) {
4555
- return {
4556
- ...result,
4557
- suggestion: `File not found at "${call.args.path}". Matching paths:\n${globResult.output}`,
4558
- };
4559
- }
4560
- }
4561
- if (call.tool === 'list_dir') {
4562
- const listPath = call.args.path || '.';
4563
- const command = runtime.platform === 'windows'
4564
- ? `dir /b "${path.join(this.currentProjectPath, listPath)}"`
4565
- : `ls -la "${path.join(this.currentProjectPath, listPath)}"`;
4566
- const shellResult = await this.tools.execute({
4567
- tool: 'bash',
4568
- args: { command, cwd: this.currentProjectPath },
4569
- });
4570
- if (shellResult.success && shellResult.output) {
4571
- return {
4572
- ...result,
4573
- suggestion: `list_dir failed for "${listPath}". Terminal listing:\n${shellResult.output}`,
4574
- };
4575
- }
4576
- }
4577
- return result;
4578
- }
4579
3901
  async executeToolCalls(toolCalls) {
4580
3902
  if (!this.tools) {
4581
3903
  throw new Error('Agent tools are not initialized.');
@@ -4585,20 +3907,15 @@ Now implement the missing fixes by editing the local workspace files. Use write/
4585
3907
  const verbose = !this.jsonOutput;
4586
3908
  for (const call of toolCalls) {
4587
3909
  if (verbose) {
4588
- const target = call.args.path || call.args.pattern || call.args.command || '';
4589
- const detail = target ? chalk.gray(` → ${String(target).replace(/\\/g, '/')}`) : '';
4590
- console.log(chalk.cyan(`⚙ Executing: ${call.tool}`) + detail);
3910
+ console.log(chalk_1.default.cyan(`⚙ Executing: ${call.tool}`));
4591
3911
  }
4592
- getBridgeClient()?.emitToolCall({ tool: call.tool, args: call.args });
3912
+ (0, bridge_client_js_1.getBridgeClient)()?.emitToolCall({ tool: call.tool, args: call.args });
4593
3913
  let result = await this.tools.execute(call);
4594
- if (!result.success) {
4595
- result = await this.tryLocalToolFallback(call, result);
4596
- }
4597
3914
  // Phase 2: If a search tool failed (search_failed), retry with alternate approach
4598
3915
  const searchStatus = result.metadata?.searchStatus;
4599
3916
  if (call.tool === 'grep' && searchStatus === 'search_failed') {
4600
3917
  if (verbose) {
4601
- console.log(chalk.yellow(`⚠ Search backend failed, retrying with alternate method...`));
3918
+ console.log(chalk_1.default.yellow(`⚠ Search backend failed, retrying with alternate method...`));
4602
3919
  }
4603
3920
  // Force Node-native fallback by re-executing with a note
4604
3921
  const fallbackResult = await this.tools.execute({
@@ -4611,10 +3928,10 @@ Now implement the missing fixes by editing the local workspace files. Use write/
4611
3928
  }
4612
3929
  const summary = this.formatToolResult(call, result);
4613
3930
  if (verbose) {
4614
- console.log(result.success ? chalk.gray(summary) : chalk.red(summary));
3931
+ console.log(result.success ? chalk_1.default.gray(summary) : chalk_1.default.red(summary));
4615
3932
  }
4616
3933
  this.messages.push({ role: 'system', content: summary });
4617
- getBridgeClient()?.emitToolResult({ tool: call.tool, success: result.success, preview: (result.output || result.error || '').slice(0, 300) });
3934
+ (0, bridge_client_js_1.getBridgeClient)()?.emitToolResult({ tool: call.tool, success: result.success, preview: (result.output || result.error || '').slice(0, 300) });
4618
3935
  // Phase 5: Track tool evidence for quality gates
4619
3936
  const finalStatus = result.metadata?.searchStatus;
4620
3937
  if (finalStatus === 'search_failed') {
@@ -4679,7 +3996,7 @@ Now implement the missing fixes by editing the local workspace files. Use write/
4679
3996
  this.messages = [...this.currentSession.messages];
4680
3997
  this.sessionManager.save(this.currentSession);
4681
3998
  if (!this.projectMemory) {
4682
- this.projectMemory = new ProjectMemoryService(this.currentProjectPath);
3999
+ this.projectMemory = new project_memory_js_1.ProjectMemoryService(this.currentProjectPath);
4683
4000
  }
4684
4001
  this.projectMemory.rememberConversation(this.messages, {
4685
4002
  source: 'cli-session',
@@ -4698,7 +4015,7 @@ Now implement the missing fixes by editing the local workspace files. Use write/
4698
4015
  });
4699
4016
  console.log(action);
4700
4017
  const answer = await new Promise((resolve) => {
4701
- rl.question(chalk.yellow('Approve? [y]es / [n]o / [a]ll this turn / [p]ersist: '), resolve);
4018
+ rl.question(chalk_1.default.yellow('Approve? [y]es / [n]o / [a]ll this turn / [p]ersist: '), resolve);
4702
4019
  });
4703
4020
  rl.close();
4704
4021
  const normalized = answer.trim().toLowerCase();
@@ -4721,3 +4038,4 @@ Now implement the missing fixes by editing the local workspace files. Use write/
4721
4038
  return [...this.messages];
4722
4039
  }
4723
4040
  }
4041
+ exports.ChatCommand = ChatCommand;