vigthoria-cli 1.10.37 → 1.10.48

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.
@@ -1,25 +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 { WorkspaceBrainService } from '../utils/workspace-brain-service.js';
15
- 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");
16
55
  const DEFAULT_V3_AGENT_TIMEOUT_MS = (() => {
17
56
  const rawValue = process.env.VIGTHORIA_AGENT_TIMEOUT_MS || process.env.V3_AGENT_TIMEOUT_MS;
18
57
  if (!rawValue) {
19
- return 0;
58
+ return 300000;
20
59
  }
21
60
  const parsed = Number.parseInt(rawValue, 10);
22
- return Number.isFinite(parsed) && parsed >= 0 ? parsed : 0;
61
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : 300000;
23
62
  })();
24
63
  const DEFAULT_V3_AGENT_IDLE_TIMEOUT_MS = (() => {
25
64
  const rawValue = process.env.VIGTHORIA_AGENT_IDLE_TIMEOUT_MS || process.env.V3_AGENT_IDLE_TIMEOUT_MS;
@@ -37,7 +76,7 @@ const DEFAULT_V3_AGENT_SOFT_TIMEOUT_MS = (() => {
37
76
  const parsed = Number.parseInt(rawValue, 10);
38
77
  return Number.isFinite(parsed) && parsed > 0 ? parsed : 180000;
39
78
  })();
40
- export class ChatCommand {
79
+ class ChatCommand {
41
80
  config;
42
81
  logger;
43
82
  api;
@@ -45,7 +84,6 @@ export class ChatCommand {
45
84
  tools = null;
46
85
  sessionManager;
47
86
  projectMemory = null;
48
- workspaceBrain = null;
49
87
  currentSession = null;
50
88
  agentMode = false;
51
89
  currentProjectPath = process.cwd();
@@ -114,32 +152,32 @@ export class ChatCommand {
114
152
  message.includes('aborted');
115
153
  }
116
154
  toUserFacingApiError(error, context) {
117
- const classified = classifyError(error);
155
+ const classified = (0, api_js_1.classifyError)(error);
118
156
  const status = classified.statusCode || (this.isJwtExpirationError(error) ? 401 : 500);
119
157
  if (this.isJwtExpirationError(error) || classified.category === 'auth') {
120
- 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 });
121
159
  }
122
160
  // Preserve structured API classification first (auth/model/network/etc.)
123
161
  // so upstream responses are not relabeled by message heuristics.
124
162
  if (classified.category === 'timeout') {
125
- 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 });
126
164
  }
127
165
  if (classified.category === 'network') {
128
- 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 });
129
167
  }
130
168
  if (classified.category === 'model_backend') {
131
- 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 });
132
170
  }
133
- const message = sanitizeUserFacingErrorText(classified.message || `${context} failed`);
134
- 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 });
135
173
  }
136
174
  handleApiError(error, context) {
137
175
  const userFacingError = this.toUserFacingApiError(error, context);
138
176
  if (!this.jsonOutput) {
139
- console.error(chalk.red(`${context} failed: ${userFacingError.message}`));
177
+ console.error(chalk_1.default.red(`${context} failed: ${userFacingError.message}`));
140
178
  }
141
179
  const original = error && typeof error === 'object' ? error : { message: String(error) };
142
- propagateError({
180
+ (0, api_js_1.propagateError)({
143
181
  ...original,
144
182
  message: userFacingError.message,
145
183
  statusCode: userFacingError.statusCode,
@@ -268,7 +306,7 @@ export class ChatCommand {
268
306
  explicitModel: true,
269
307
  heavyTask,
270
308
  cloudEligible,
271
- 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',
272
310
  routeReason: 'explicit-model-selection',
273
311
  };
274
312
  }
@@ -301,7 +339,7 @@ export class ChatCommand {
301
339
  routeReason: 'default-v3-agent',
302
340
  };
303
341
  }
304
- getMessagesForModel(options) {
342
+ getMessagesForModel() {
305
343
  const messages = [...this.messages];
306
344
  const personaOverlay = this.buildActivePersonaOverlay();
307
345
  if (personaOverlay && !messages.some((message) => message.role === 'system' && message.content.includes('Optional persona overlay: Wiener Grantler mode.'))) {
@@ -335,147 +373,15 @@ export class ChatCommand {
335
373
  messages.splice(insertionIndex, 0, memoryMessage);
336
374
  }
337
375
  }
338
- const codebaseContext = this.workspaceBrain?.buildCodebaseContext(this.getLastUserPrompt()) || '';
339
- if (codebaseContext && !messages.some((message) => message.role === 'system' && message.content.includes('Vigthoria codebase index context.'))) {
340
- const insertionIndex = messages.findIndex((message) => message.role !== 'system');
341
- const codebaseMessage = { role: 'system', content: codebaseContext };
342
- if (insertionIndex === -1) {
343
- messages.push(codebaseMessage);
344
- }
345
- else {
346
- messages.splice(insertionIndex, 0, codebaseMessage);
347
- }
348
- }
349
- if (options?.compact) {
350
- const compactLimit = 6000;
351
- const systemMessages = messages.filter((message) => message.role === 'system').map((message) => ({
352
- ...message,
353
- content: message.content.length > compactLimit
354
- ? `${message.content.slice(0, compactLimit)}\n...[trimmed for first local agent turn]`
355
- : message.content,
356
- }));
357
- const lastUser = [...messages].reverse().find((message) => message.role === 'user');
358
- return lastUser ? [...systemMessages.slice(0, 2), lastUser] : systemMessages.slice(0, 2);
359
- }
360
376
  return messages;
361
377
  }
362
- normalizeClientV3ToolPath(rawPath) {
363
- let normalized = String(rawPath || '.').trim().replace(/\\/g, '/');
364
- if (!normalized || normalized === '/') {
365
- return '.';
366
- }
367
- normalized = normalized.replace(/^vigthoria:\/\/workspace\/?/i, '');
368
- normalized = normalized.replace(/^\.\/+/, '');
369
- normalized = normalized.replace(/^workspace\/?/i, '');
370
- const workspaceRoot = this.currentProjectPath || process.cwd();
371
- const workspaceName = path.basename(workspaceRoot);
372
- if (workspaceName) {
373
- const workspaceKey = workspaceName.toLowerCase().replace(/[\s_./\\-]+/g, '');
374
- const parts = normalized.split('/').filter(Boolean);
375
- if (parts.length > 0) {
376
- const firstKey = parts[0].toLowerCase().replace(/[\s_./\\-]+/g, '');
377
- if (firstKey === workspaceKey) {
378
- normalized = parts.slice(1).join('/') || '.';
379
- }
380
- }
381
- }
382
- normalized = normalized.replace(/^\/+/, '').replace(/\/+$/, '');
383
- return normalized || '.';
384
- }
385
- resolveClientV3ToolPath(rawPath) {
386
- const root = this.currentProjectPath || process.cwd();
387
- const normalized = this.normalizeClientV3ToolPath(rawPath);
388
- const absoluteTarget = path.resolve(root, normalized === '.' ? '' : normalized);
389
- if (fs.existsSync(absoluteTarget)) {
390
- return normalized;
391
- }
392
- const parts = normalized.split('/').filter(Boolean);
393
- if (parts.length === 0) {
394
- return '.';
395
- }
396
- try {
397
- const entries = fs.readdirSync(root, { withFileTypes: true });
398
- const firstKey = parts[0].toLowerCase().replace(/[\s_./\\-]+/g, '');
399
- for (const entry of entries) {
400
- const entryKey = entry.name.toLowerCase().replace(/[\s_./\\-]+/g, '');
401
- if (entryKey === firstKey || entryKey.includes(firstKey) || firstKey.includes(entryKey)) {
402
- const rest = parts.slice(1).join('/');
403
- const candidate = rest ? `${entry.name}/${rest}` : entry.name;
404
- if (fs.existsSync(path.resolve(root, candidate))) {
405
- return candidate.replace(/\\/g, '/');
406
- }
407
- if (!rest) {
408
- return entry.name.replace(/\\/g, '/');
409
- }
410
- }
411
- }
412
- }
413
- catch {
414
- // Ignore unreadable workspace roots during path repair.
415
- }
416
- return normalized;
417
- }
418
- normalizeClientV3ToolArgs(args) {
419
- const normalizedArgs = { ...args };
420
- for (const key of ['path', 'file_path', 'file', 'target']) {
421
- if (typeof normalizedArgs[key] === 'string' && normalizedArgs[key].trim()) {
422
- normalizedArgs[key] = this.resolveClientV3ToolPath(normalizedArgs[key]);
423
- }
424
- }
425
- return normalizedArgs;
426
- }
427
- async executeClientV3Tool(event) {
428
- if (!this.tools) {
429
- return { success: false, output: '', error: 'Local agent tools are not initialized.' };
430
- }
431
- const name = String(event.name || '').trim();
432
- const args = (event.arguments && typeof event.arguments === 'object')
433
- ? event.arguments
434
- : {};
435
- let toolName = name;
436
- let toolArgs = {};
437
- for (const [key, value] of Object.entries(args)) {
438
- toolArgs[key] = value == null ? '' : String(value);
439
- }
440
- toolArgs = this.normalizeClientV3ToolArgs(toolArgs);
441
- if (name === 'list_directory') {
442
- toolName = 'list_dir';
443
- toolArgs = {
444
- path: toolArgs.path || '.',
445
- ...(toolArgs.recursive === 'true' || toolArgs.recursive === '1' ? { recursive: 'true' } : {}),
446
- };
447
- }
448
- else if (name === 'search_files') {
449
- toolName = 'grep';
450
- toolArgs = {
451
- path: toolArgs.path || '.',
452
- pattern: toolArgs.pattern || toolArgs.query || '',
453
- ...(toolArgs.file_pattern ? { includePattern: toolArgs.file_pattern } : {}),
454
- };
455
- }
456
- else if (name === 'read_file') {
457
- toolName = 'read_file';
458
- }
459
- else if (name === 'write_file' || name === 'edit_file') {
460
- toolName = name;
461
- }
462
- if (!toolName) {
463
- return { success: false, output: '', error: 'Missing V3 client tool name.' };
464
- }
465
- const result = await this.tools.execute({ tool: toolName, args: toolArgs });
466
- return {
467
- success: result.success === true,
468
- output: String(result.output || result.message || ''),
469
- error: result.error ? String(result.error) : '',
470
- };
471
- }
472
378
  getActivePersonaMode() {
473
379
  if (this.personaOverride)
474
380
  return this.personaOverride;
475
- return normalizePersonaMode(this.config.get('persona')) || 'default';
381
+ return (0, persona_js_1.normalizePersonaMode)(this.config.get('persona')) || 'default';
476
382
  }
477
383
  buildActivePersonaOverlay() {
478
- return buildPersonaOverlay(this.getActivePersonaMode(), this.getLastUserPrompt());
384
+ return (0, persona_js_1.buildPersonaOverlay)(this.getActivePersonaMode(), this.getLastUserPrompt());
479
385
  }
480
386
  isDiagnosticPrompt(prompt) {
481
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);
@@ -485,201 +391,52 @@ export class ChatCommand {
485
391
  * question — these should use analysis_only workflow, not full_autonomy.
486
392
  */
487
393
  isAnalysisLookupPrompt(prompt) {
488
- if (this.isImplementationPrompt(prompt)) {
489
- return false;
490
- }
491
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());
492
395
  }
493
- isImplementationPrompt(prompt) {
494
- const trimmed = String(prompt || '').trim();
495
- if (!trimmed)
496
- return false;
497
- 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)) {
498
- return true;
499
- }
500
- if (/\bwhat(?:'s|\s+is|\s+are)\s+missing\b/i.test(trimmed)) {
501
- return true;
502
- }
503
- if (/\b(game|html5|pacman|rogue|app|site)\b/i.test(trimmed) && /\b(missing|broken|fix|implement|working|playable)\b/i.test(trimmed)) {
504
- return true;
505
- }
506
- if (/continue the previous agent run/i.test(trimmed) && /\b(implement|fix|missing|blocker|remaining)\b/i.test(trimmed)) {
507
- return true;
508
- }
509
- return false;
510
- }
511
- getWindowsPromptPathRoots() {
512
- const roots = new Set();
513
- const add = (value) => {
514
- const raw = String(value || '').trim();
515
- if (!raw)
516
- return;
517
- try {
518
- const resolved = path.resolve(raw);
519
- if (fs.existsSync(resolved) && fs.statSync(resolved).isDirectory()) {
520
- roots.add(resolved);
521
- }
522
- }
523
- catch {
524
- // Ignore unreadable discovery roots.
525
- }
526
- };
527
- const home = os.homedir();
528
- const cwdRoot = path.parse(process.cwd()).root || path.parse(home).root || '';
529
- add(process.cwd());
530
- add(home);
531
- add(path.join(home, 'Desktop'));
532
- add(path.join(home, 'Documents'));
533
- add(process.env.USERPROFILE);
534
- add(process.env.OneDrive);
535
- add(process.env.OneDriveCommercial);
536
- add(process.env.OneDriveConsumer);
537
- if (process.env.OneDrive) {
538
- add(path.join(process.env.OneDrive, 'Desktop'));
539
- add(path.join(process.env.OneDrive, 'Documents'));
540
- }
541
- if (cwdRoot) {
542
- add(cwdRoot);
543
- add(path.join(cwdRoot, 'vigthoria'));
544
- add(path.join(cwdRoot, 'Vigthoria'));
545
- }
546
- return Array.from(roots);
547
- }
548
- findPromptDirectoryByName(rawPath) {
549
- if (os.platform() !== 'win32')
550
- return null;
551
- const normalized = String(rawPath || '').trim().replace(/^\/+/, '').replace(/[\\/]+/g, path.sep);
552
- if (!normalized)
553
- return null;
554
- const wantedParts = normalized.split(/[\\/]+/).filter(Boolean);
555
- const wantedName = wantedParts[wantedParts.length - 1];
556
- if (!wantedName)
557
- return null;
558
- const maxDepth = 3;
559
- const maxEntries = 2500;
560
- const roots = this.getWindowsPromptPathRoots();
561
- for (const root of roots) {
562
- let visited = 0;
563
- 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();
564
402
  try {
565
- if (fs.existsSync(direct) && fs.statSync(direct).isDirectory()) {
566
- return direct;
403
+ const resolved = require('path').resolve(candidatePath);
404
+ const fs = require('fs');
405
+ if (fs.existsSync(resolved)) {
406
+ return resolved;
567
407
  }
568
408
  }
569
409
  catch {
570
- // Continue with bounded search.
571
- }
572
- const stack = [{ dir: root, depth: 0 }];
573
- while (stack.length > 0 && visited < maxEntries) {
574
- const current = stack.pop();
575
- if (!current || current.depth > maxDepth)
576
- continue;
577
- let entries;
578
- try {
579
- entries = fs.readdirSync(current.dir, { withFileTypes: true });
580
- }
581
- catch {
582
- continue;
583
- }
584
- visited += entries.length;
585
- for (const entry of entries) {
586
- if (!entry.isDirectory())
587
- continue;
588
- if (entry.name === 'node_modules' || entry.name === '.git' || entry.name.startsWith('$'))
589
- continue;
590
- const fullPath = path.join(current.dir, entry.name);
591
- if (entry.name.toLowerCase() === wantedName.toLowerCase()) {
592
- if (wantedParts.length === 1 || fullPath.toLowerCase().endsWith(normalized.toLowerCase())) {
593
- 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;
594
430
  }
595
431
  }
596
- if (current.depth < maxDepth) {
597
- stack.push({ dir: fullPath, depth: current.depth + 1 });
432
+ catch {
433
+ // Path resolution failed
598
434
  }
599
435
  }
600
436
  }
601
437
  }
602
438
  return null;
603
439
  }
604
- extractExplicitLocalPath(prompt) {
605
- const resolveExistingPath = (rawPath) => {
606
- const trimmed = String(rawPath || '').trim().replace(/^['"]|['"]$/g, '');
607
- if (!trimmed)
608
- return null;
609
- const candidates = [];
610
- candidates.push(path.resolve(trimmed));
611
- if (os.platform() === 'win32' && /^\/[A-Za-z0-9._ -]/.test(trimmed)) {
612
- const withoutLeadingSlash = trimmed.replace(/^\/+/, '');
613
- const cwdRoot = path.parse(process.cwd()).root || '';
614
- // Allow prompts like "/Vigthoria Games" to resolve as "C:/Vigthoria/Games".
615
- const asSegments = withoutLeadingSlash.replace(/\s+/g, path.sep);
616
- for (const root of this.getWindowsPromptPathRoots()) {
617
- candidates.push(path.resolve(root, withoutLeadingSlash));
618
- candidates.push(path.resolve(root, asSegments));
619
- const segmentParts = asSegments.split(/[\\/]+/).filter(Boolean);
620
- if (segmentParts.length > 1 && path.basename(root).toLowerCase() === segmentParts[0].toLowerCase()) {
621
- candidates.push(path.resolve(root, ...segmentParts.slice(1)));
622
- }
623
- }
624
- if (cwdRoot && !candidates.includes(path.resolve(cwdRoot, withoutLeadingSlash))) {
625
- candidates.push(path.resolve(cwdRoot, withoutLeadingSlash));
626
- candidates.push(path.resolve(cwdRoot, asSegments));
627
- }
628
- }
629
- for (const candidate of candidates) {
630
- try {
631
- if (fs.existsSync(candidate)) {
632
- return candidate;
633
- }
634
- }
635
- catch {
636
- // Continue trying other normalized candidates.
637
- }
638
- }
639
- const discovered = this.findPromptDirectoryByName(trimmed);
640
- if (discovered)
641
- return discovered;
642
- return null;
643
- };
644
- // Quoted paths first (supports spaces safely).
645
- const quotedPatterns = [
646
- /"([A-Za-z]:[\\/][^"\r\n]+)"/,
647
- /'([A-Za-z]:[\\/][^'\r\n]+)'/,
648
- /"(\/[^"]+)"/,
649
- /'(\/[^']+)'/,
650
- ];
651
- for (const pattern of quotedPatterns) {
652
- const match = prompt.match(pattern);
653
- if (match?.[1]) {
654
- const resolved = resolveExistingPath(match[1]);
655
- if (resolved)
656
- return resolved;
657
- }
658
- }
659
- // Unquoted Windows path with optional spaces, stopping before instruction connectors.
660
- 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);
661
- if (windowsMatch?.[1]) {
662
- const resolved = resolveExistingPath(windowsMatch[1]);
663
- if (resolved)
664
- return resolved;
665
- }
666
- // Unix-style absolute path with optional spaces (but not URLs).
667
- if (!/(https?|ftp):\/\//i.test(prompt)) {
668
- 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);
669
- if (unixMatch?.[1]) {
670
- const candidatePath = unixMatch[1]
671
- .replace(/\s+(and|or|at|in|the|to|for|with|from|by|on)$/i, '')
672
- .replace(/[.,;!?:)\]]*$/, '')
673
- .trim();
674
- if (candidatePath.length > 1) {
675
- const resolved = resolveExistingPath(candidatePath);
676
- if (resolved)
677
- return resolved;
678
- }
679
- }
680
- }
681
- return null;
682
- }
683
440
  isUnscopedPromptPathOverrideAllowed() {
684
441
  return /^(1|true|yes)$/i.test(String(process.env.VIGTHORIA_ALLOW_UNSCOPED_PROMPT_PATHS || ''));
685
442
  }
@@ -726,24 +483,18 @@ export class ChatCommand {
726
483
  if (this.isUnscopedPromptPathOverrideAllowed()) {
727
484
  return candidate;
728
485
  }
729
- const runtime = this.getRuntimeEnvironmentContext();
730
- // On local-machine CLI sessions, allow explicit absolute paths from user prompts.
731
- // The path still must exist on disk (validated by extractExplicitLocalPath).
732
- if (runtime.machineScope === 'local-machine') {
733
- return candidate;
734
- }
735
486
  const allowedRoots = this.getPromptPathAllowedRoots(baseWorkspace);
736
487
  const isAllowed = allowedRoots.some((root) => this.isPathWithinRoot(candidate, root));
737
488
  if (isAllowed) {
738
489
  return candidate;
739
490
  }
740
491
  if (!this.jsonOutput) {
741
- 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}`));
742
493
  if (allowedRoots.length > 0) {
743
494
  const displayRoots = allowedRoots.map((root) => root.replace(/\\/g, '/')).join(', ');
744
- console.log(chalk.gray(`Allowed roots: ${displayRoots}`));
495
+ console.log(chalk_1.default.gray(`Allowed roots: ${displayRoots}`));
745
496
  }
746
- 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.'));
747
498
  }
748
499
  return null;
749
500
  }
@@ -794,36 +545,20 @@ export class ChatCommand {
794
545
  inferAgentTaskType(prompt) {
795
546
  if (this.isDiagnosticPrompt(prompt))
796
547
  return 'debugging';
797
- if (this.isImplementationPrompt(prompt)) {
798
- return /\b(game|html5|pacman|rogue|playable)\b/i.test(prompt) ? 'game-build' : 'implementation';
799
- }
800
- 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)) {
801
551
  return 'analysis';
802
- return 'implementation';
803
- }
804
- bindPromptWorkspace(prompt) {
805
- const resolved = this.resolvePromptWorkspacePath(prompt, this.currentProjectPath);
806
- if (!resolved) {
807
- return;
808
- }
809
- const normalizedResolved = path.resolve(resolved);
810
- const normalizedCurrent = path.resolve(this.currentProjectPath);
811
- if (normalizedResolved === normalizedCurrent) {
812
- return;
813
552
  }
814
- if (!this.jsonOutput) {
815
- 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';
816
556
  }
817
- this.currentProjectPath = normalizedResolved;
818
- this.tools?.setWorkspaceRoot(normalizedResolved);
819
- this.projectMemory = new ProjectMemoryService(normalizedResolved);
557
+ return 'analysis';
820
558
  }
821
559
  buildTaskShapingInstructions(prompt) {
822
560
  const instructions = [];
823
561
  const runtime = this.getRuntimeEnvironmentContext();
824
- if (runtime.machineScope === 'local-machine') {
825
- 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.');
826
- }
827
562
  // Platform-aware routing hints
828
563
  if (runtime.platform === 'windows') {
829
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.');
@@ -834,6 +569,9 @@ export class ChatCommand {
834
569
  else if (runtime.platform === 'linux') {
835
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.');
836
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
+ }
837
575
  if (this.isDiagnosticPrompt(prompt)) {
838
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.');
839
577
  }
@@ -858,7 +596,7 @@ export class ChatCommand {
858
596
  }
859
597
  try {
860
598
  if (!this.projectMemory) {
861
- this.projectMemory = new ProjectMemoryService(this.currentProjectPath);
599
+ this.projectMemory = new project_memory_js_1.ProjectMemoryService(this.currentProjectPath);
862
600
  }
863
601
  const status = this.projectMemory.getStatus();
864
602
  const context = this.projectMemory.buildContextForPrompt(prompt);
@@ -884,7 +622,7 @@ export class ChatCommand {
884
622
  }
885
623
  try {
886
624
  if (!this.projectMemory) {
887
- this.projectMemory = new ProjectMemoryService(this.currentProjectPath);
625
+ this.projectMemory = new project_memory_js_1.ProjectMemoryService(this.currentProjectPath);
888
626
  }
889
627
  this.projectMemory.remember(type, text, { source: 'vigthoria-cli', mode, model: this.currentModel });
890
628
  }
@@ -892,82 +630,6 @@ export class ChatCommand {
892
630
  // Project Brain memory must not break chat, GoA, or operator execution.
893
631
  }
894
632
  }
895
- initializeWorkspaceBrain() {
896
- if (this.isProjectBrainRuntimeDisabled()) {
897
- return;
898
- }
899
- this.workspaceBrain = new WorkspaceBrainService({
900
- workspacePath: this.currentProjectPath,
901
- apiBase: String(this.config.get('apiUrl') || 'https://coder.vigthoria.io'),
902
- getAuthToken: () => this.config.get('authToken'),
903
- });
904
- this.tools?.setIndexedCodebaseSearch((query, maxResults) => (this.workspaceBrain?.searchCodebase(query, maxResults) || ''));
905
- }
906
- async bootstrapWorkspaceBrain(interactive) {
907
- if (!this.workspaceBrain) {
908
- return;
909
- }
910
- const promptIfMissing = interactive
911
- && !this.jsonOutput
912
- && process.stdin.isTTY
913
- && !/^(0|false|no)$/i.test(String(process.env.VIGTHORIA_PROMPT_INDEX || '1'));
914
- const result = await this.workspaceBrain.ensureIndexed({
915
- promptIfMissing,
916
- askToIndex: promptIfMissing
917
- ? async (fileCount, workspaceName) => new Promise((resolve) => {
918
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
919
- rl.question(chalk.cyan(`Index "${workspaceName}" for Vigthoria Brain? (~${fileCount} source files) [Y/n] `), (answer) => {
920
- rl.close();
921
- const normalized = answer.trim().toLowerCase();
922
- resolve(normalized === 'n' || normalized === 'no' ? 'later' : 'index_now');
923
- });
924
- })
925
- : undefined,
926
- });
927
- if (result.indexed && result.fileCount > 0 && !this.jsonOutput) {
928
- console.log(chalk.gray(`Brain index ready: ${result.fileCount} files, ${result.chunkCount} chunks`));
929
- }
930
- else if (result.prompted === 'later' && !this.jsonOutput) {
931
- console.log(chalk.gray('Workspace indexing skipped. Use /index anytime.'));
932
- }
933
- }
934
- async reindexWorkspaceBrain() {
935
- if (!this.workspaceBrain) {
936
- this.initializeWorkspaceBrain();
937
- }
938
- if (!this.workspaceBrain) {
939
- console.log(chalk.yellow('Workspace Brain is disabled.'));
940
- return;
941
- }
942
- const spinner = createSpinner({ text: 'Indexing workspace for Vigthoria Brain...', spinner: 'clock' }).start();
943
- try {
944
- const meta = await this.workspaceBrain.reindexWorkspace();
945
- spinner.stop();
946
- console.log(chalk.green(`Indexed ${meta.indexedFileCount} files (${meta.totalChunks} chunks). Brain Hub sync attempted.`));
947
- }
948
- catch (error) {
949
- spinner.stop();
950
- this.logger.error(error instanceof Error ? error.message : String(error));
951
- }
952
- }
953
- showBrainIndexStatus() {
954
- if (!this.workspaceBrain) {
955
- console.log(chalk.yellow('Workspace Brain is disabled (VIGTHORIA_NO_BRAIN=1).'));
956
- return;
957
- }
958
- const status = this.workspaceBrain.getStatus();
959
- console.log();
960
- console.log(chalk.white('Workspace Brain Index:'));
961
- console.log(chalk.gray(`Workspace: ${status.workspacePath}`));
962
- console.log(chalk.gray(`Files indexed: ${status.indexedFileCount}`));
963
- console.log(chalk.gray(`Chunks: ${status.totalChunks}`));
964
- if (status.meta?.indexedAt) {
965
- console.log(chalk.gray(`Last indexed: ${status.meta.indexedAt}`));
966
- }
967
- if (status.meta?.indexHash) {
968
- console.log(chalk.gray(`Index hash: ${status.meta.indexHash}`));
969
- }
970
- }
971
633
  async getPromptRuntimeContext(prompt) {
972
634
  const runtimeContext = {
973
635
  agentRuntime: this.getRuntimeEnvironmentContext(),
@@ -976,29 +638,13 @@ export class ChatCommand {
976
638
  if (brainContext) {
977
639
  runtimeContext.vigthoriaBrain = brainContext;
978
640
  }
979
- if (this.workspaceBrain) {
980
- const accountBrain = await this.workspaceBrain.fetchAccountBrainContext();
981
- if (accountBrain) {
982
- runtimeContext.accountBrainContext = accountBrain;
983
- }
984
- const indexStatus = this.workspaceBrain.getStatus();
985
- runtimeContext.codebaseIndex = {
986
- indexedFileCount: indexStatus.indexedFileCount,
987
- totalChunks: indexStatus.totalChunks,
988
- indexHash: indexStatus.meta?.indexHash || null,
989
- };
990
- const indexedContext = this.workspaceBrain.buildCodebaseContext(prompt);
991
- if (indexedContext) {
992
- runtimeContext.codebaseContext = indexedContext;
993
- }
994
- }
995
641
  if (!this.isBrowserTaskPrompt(prompt)) {
996
642
  return runtimeContext;
997
643
  }
998
644
  const devtoolsBridgeAllowed = /^(1|true|yes)$/i.test(String(process.env.VIGTHORIA_DEVTOOLS_BRIDGE_ALLOWED || process.env.VIGTHORIA_BRIDGE_ALLOWED || ''));
999
645
  if (!devtoolsBridgeAllowed) {
1000
646
  if (!this.jsonOutput) {
1001
- 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.'));
1002
648
  }
1003
649
  return {
1004
650
  ...runtimeContext,
@@ -1010,10 +656,10 @@ export class ChatCommand {
1010
656
  }
1011
657
  const bridgeStatus = await this.callApi('Checking DevTools Bridge status', () => this.api.getDevtoolsBridgeStatus(), 0);
1012
658
  if (!this.jsonOutput && bridgeStatus.ok) {
1013
- 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}.`));
1014
660
  }
1015
661
  else if (!this.jsonOutput) {
1016
- 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}.`));
1017
663
  }
1018
664
  return {
1019
665
  ...runtimeContext,
@@ -1027,9 +673,6 @@ export class ChatCommand {
1027
673
  v3ToolCallCount = 0;
1028
674
  v3LastActivity = Date.now();
1029
675
  v3StreamingStarted = false;
1030
- v3StreamedTextBuffer = '';
1031
- v3LiveToolEvidence = [];
1032
- v3PendingToolCalls = [];
1033
676
  /**
1034
677
  * Strip server-internal path prefixes from tool output strings.
1035
678
  * Prevents exposing paths like /var/www/V3-Code-Agent/... to end users.
@@ -1037,7 +680,7 @@ export class ChatCommand {
1037
680
  sanitizeServerPath(text) {
1038
681
  if (!text)
1039
682
  return text;
1040
- return sanitizeUserFacingPathText(this.stripHiddenThoughtBlocks(text));
683
+ return (0, api_js_1.sanitizeUserFacingPathText)(this.stripHiddenThoughtBlocks(text));
1041
684
  }
1042
685
  stripHiddenThoughtBlocks(text) {
1043
686
  if (!text)
@@ -1079,12 +722,12 @@ export class ChatCommand {
1079
722
  const source = (event && typeof event === 'object' && !Buffer.isBuffer(event) && !(event instanceof Uint8Array))
1080
723
  ? (event.body ?? event.stream ?? event.response ?? event)
1081
724
  : event;
1082
- for await (const chunk of robustifyStreamResponse(source)) {
725
+ for await (const chunk of (0, tools_js_1.robustifyStreamResponse)(source)) {
1083
726
  this.v3LastActivity = Date.now();
1084
727
  if (chunk.type === 'error') {
1085
728
  if (spinner.isSpinning)
1086
729
  spinner.stop();
1087
- process.stderr.write(chalk.red(`\nStream error: ${chunk.content}\n`));
730
+ process.stderr.write(chalk_1.default.red(`\nStream error: ${chunk.content}\n`));
1088
731
  continue;
1089
732
  }
1090
733
  if (chunk.content) {
@@ -1096,7 +739,7 @@ export class ChatCommand {
1096
739
  const message = error instanceof Error ? error.message : String(error);
1097
740
  if (spinner.isSpinning)
1098
741
  spinner.stop();
1099
- process.stderr.write(chalk.red(`\nStream error: ${message}\n`));
742
+ process.stderr.write(chalk_1.default.red(`\nStream error: ${message}\n`));
1100
743
  }
1101
744
  }
1102
745
  writeV3StreamText(spinner, text) {
@@ -1104,7 +747,6 @@ export class ChatCommand {
1104
747
  if (!safeText) {
1105
748
  return;
1106
749
  }
1107
- this.v3StreamedTextBuffer += safeText;
1108
750
  if (!this.v3StreamingStarted) {
1109
751
  this.v3StreamingStarted = true;
1110
752
  spinner.stop();
@@ -1117,124 +759,13 @@ export class ChatCommand {
1117
759
  }
1118
760
  process.stdout.write(safeText);
1119
761
  }
1120
- isGenericV3AgentContent(text) {
1121
- const value = String(text || '').trim();
1122
- return !value || /^(v3 agent workflow completed\.?|task completed|agent run finished|workflow completed\.?)$/i.test(value);
1123
- }
1124
- hasAlreadyStreamedV3Content(text) {
1125
- const value = String(text || '').trim();
1126
- if (!value)
1127
- return true;
1128
- return this.v3StreamedTextBuffer.includes(value) || value.includes(this.v3StreamedTextBuffer.trim());
1129
- }
1130
- isThinV3Summary(text) {
1131
- const value = String(text || '').trim();
1132
- if (!value)
1133
- return true;
1134
- if (/^#\s*Workspace overview/m.test(value) || /^## Workspace analysis \(from local file inspection\)/m.test(value)) {
1135
- return false;
1136
- }
1137
- if (this.isGenericV3AgentContent(value))
1138
- return true;
1139
- if (/^The V3 agent finished without emitting a dedicated final answer/i.test(value)) {
1140
- return !/(## Files read|## Directories inspected|### )/i.test(value);
1141
- }
1142
- return value.length < 120;
1143
- }
1144
- shouldPrintV3FinalContent(text) {
1145
- if (!text || this.isGenericV3AgentContent(text)) {
1146
- return false;
1147
- }
1148
- const value = text.trim();
1149
- if (/^## Workspace analysis \(from local file inspection\)/m.test(value)) {
1150
- return true;
1151
- }
1152
- if (/Reconstructed task summary|Workspace analysis \(from local file inspection\)/i.test(value)) {
1153
- return true;
1154
- }
1155
- if (!this.v3StreamingStarted) {
1156
- return true;
1157
- }
1158
- return !this.hasAlreadyStreamedV3Content(value);
1159
- }
1160
- rememberV3ToolEvidence(event, args = {}) {
1161
- const output = typeof event?.output === 'string' ? event.output.trim() : '';
1162
- const errorText = typeof event?.error === 'string' ? event.error.trim() : '';
1163
- const combined = output || errorText;
1164
- if (!combined) {
1165
- return;
1166
- }
1167
- const target = String(args.path || args.file_path || args.file || args.target || event?.target || '').trim();
1168
- this.v3LiveToolEvidence.push({
1169
- name: String(event?.name || event?.tool || 'unknown_tool'),
1170
- target: target || undefined,
1171
- arguments: args,
1172
- output: combined,
1173
- success: event?.success !== false,
1174
- });
1175
- }
1176
- buildUserFacingV3RunReport(prompt, workspacePath, options = {}) {
1177
- const evidenceBody = this.api.formatV3AgentResponse({
1178
- events: [],
1179
- liveToolEvidence: this.v3LiveToolEvidence,
1180
- });
1181
- const successes = this.v3LiveToolEvidence.filter((entry) => entry.success !== false);
1182
- const failures = this.v3LiveToolEvidence.filter((entry) => entry.success === false);
1183
- if (!evidenceBody && successes.length === 0 && failures.length === 0) {
1184
- return [
1185
- '# Agent run summary',
1186
- '',
1187
- `**Workspace:** ${workspacePath}`,
1188
- `**Your request:** ${prompt.trim()}`,
1189
- '',
1190
- 'The agent finished without successfully reading local files, so no grounded overview could be built.',
1191
- options.serverNote ? `\n**Server note:** ${options.serverNote}` : '',
1192
- '',
1193
- 'Try `/continue` with: "Read Vigthoria-dominion/package.json, game.js, src/Game.js, and src/factions/, then write a full overview."',
1194
- ].filter(Boolean).join('\n');
1195
- }
1196
- const lines = [
1197
- '# Workspace overview',
1198
- '',
1199
- `**Workspace:** ${workspacePath}`,
1200
- `**Your request:** ${prompt.trim()}`,
1201
- options.partial
1202
- ? '**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.'
1203
- : '**Status:** Complete — summary rebuilt from local file inspection.',
1204
- '',
1205
- evidenceBody || '_No readable file excerpts were captured._',
1206
- ];
1207
- if (failures.length > 0) {
1208
- const uniqueFails = new Map();
1209
- for (const entry of failures) {
1210
- const failPath = entry.target || entry.arguments?.path || entry.name;
1211
- if (!uniqueFails.has(failPath)) {
1212
- uniqueFails.set(failPath, entry.output.split('\n').find(Boolean)?.slice(0, 140) || entry.output.slice(0, 140));
1213
- }
1214
- }
1215
- if (uniqueFails.size > 0 && !/## Paths not found/i.test(evidenceBody || '')) {
1216
- 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._');
1217
- }
1218
- }
1219
- lines.push('', '---', 'Use `/continue` if you want a longer narrative summary or a deep dive into a specific subfolder.');
1220
- return lines.join('\n');
1221
- }
1222
- printV3UserReport(report) {
1223
- const value = String(report || '').trim();
1224
- if (!value || this.isGenericV3AgentContent(value)) {
1225
- return;
1226
- }
1227
- console.log();
1228
- console.log(value);
1229
- console.log();
1230
- }
1231
762
  updateV3AgentSpinner(spinner, event) {
1232
763
  if (this.isRawV3StreamPayload(event)) {
1233
764
  this.consumeV3StreamPayload(spinner, event).catch((error) => {
1234
765
  const message = error instanceof Error ? error.message : String(error);
1235
766
  if (spinner.isSpinning)
1236
767
  spinner.stop();
1237
- process.stderr.write(chalk.red(`\nStream error: ${message}\n`));
768
+ process.stderr.write(chalk_1.default.red(`\nStream error: ${message}\n`));
1238
769
  });
1239
770
  return;
1240
771
  }
@@ -1245,38 +776,23 @@ export class ChatCommand {
1245
776
  if (event.type === 'tool_call') {
1246
777
  this.v3ToolCallCount += 1;
1247
778
  const toolDesc = this.describeV3AgentTool(event.tool || event.name || event.tool_name);
1248
- 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 || '';
1249
780
  const sanitizedTarget = this.sanitizeServerPath(String(toolTarget));
1250
781
  const shortTarget = sanitizedTarget ? ` → ${sanitizedTarget.replace(/\\/g, '/').split('/').slice(-2).join('/')}` : '';
1251
- const stepLabel = chalk.cyan(` [${this.v3IterationCount}/${this.v3ToolCallCount}]`) + ` ${toolDesc}${shortTarget}`;
782
+ const stepLabel = chalk_1.default.cyan(` [${this.v3IterationCount}/${this.v3ToolCallCount}]`) + ` ${toolDesc}${shortTarget}`;
1252
783
  if (spinner.isSpinning)
1253
784
  spinner.stop();
1254
785
  process.stderr.write(stepLabel + '\n');
1255
786
  // Show extra detail for key tools
1256
787
  const args = event.arguments || {};
1257
788
  const toolName = event.name || event.tool || '';
1258
- this.v3PendingToolCalls.push({
1259
- name: String(toolName || 'unknown_tool'),
1260
- args: args && typeof args === 'object' ? args : {},
1261
- });
1262
- if (toolName === 'search_files') {
1263
- const pattern = String(args.pattern || args.query || '').trim();
1264
- const searchPath = String(args.path || '.').trim();
1265
- process.stderr.write(chalk.gray(` search: ${pattern || '(empty)'} in ${this.sanitizeServerPath(searchPath)}\n`));
1266
- }
1267
- else if (toolName === 'list_directory') {
1268
- process.stderr.write(chalk.gray(` list: ${this.sanitizeServerPath(String(args.path || '.'))}${args.recursive ? ' (recursive)' : ''}\n`));
1269
- }
1270
- else if (toolName === 'read_file') {
1271
- process.stderr.write(chalk.gray(` read: ${this.sanitizeServerPath(String(args.path || args.file_path || ''))}\n`));
1272
- }
1273
789
  if ((toolName === 'write_file' || toolName === 'edit_file') && typeof args.content === 'string') {
1274
790
  const len = args.content.length;
1275
- 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`));
1276
792
  }
1277
793
  else if (toolName === 'bash' && typeof args.command === 'string') {
1278
794
  const command = this.sanitizeServerPath(args.command);
1279
- 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`));
1280
796
  }
1281
797
  spinner.start();
1282
798
  spinner.text = `Running ${toolDesc}...`;
@@ -1285,28 +801,21 @@ export class ChatCommand {
1285
801
  if (event.type === 'tool_result') {
1286
802
  const success = event.success !== false;
1287
803
  const toolName = event.name || event.tool || '';
1288
- const indicator = success ? chalk.green(' ✓') : chalk.red(' ✗');
804
+ const indicator = success ? chalk_1.default.green(' ✓') : chalk_1.default.red(' ✗');
1289
805
  if (spinner.isSpinning)
1290
806
  spinner.stop();
1291
807
  process.stderr.write(`${indicator} ${toolName}\n`);
1292
808
  // Show output for failures, or brief summary for successes — sanitize server paths
1293
809
  const rawOutput = typeof event.output === 'string' ? event.output.trim() : '';
1294
810
  const output = this.sanitizeServerPath(rawOutput);
1295
- const pendingIndex = this.v3PendingToolCalls.findIndex((call) => call.name === toolName);
1296
- const pendingCall = pendingIndex >= 0
1297
- ? this.v3PendingToolCalls.splice(pendingIndex, 1)[0]
1298
- : this.v3PendingToolCalls.shift();
1299
- this.rememberV3ToolEvidence(event, pendingCall?.args || {});
1300
811
  if (!success && output) {
1301
812
  const sanitizedError = this.sanitizeServerPath(typeof event.error === 'string' ? event.error : output);
1302
813
  const lines = sanitizedError.split('\n').slice(0, 4);
1303
- process.stderr.write(chalk.red(` ${lines.join('\n ')}\n`));
814
+ process.stderr.write(chalk_1.default.red(` ${lines.join('\n ')}\n`));
1304
815
  }
1305
816
  else if (success && output && output.length > 0) {
1306
- const outputLines = output.split('\n').map((line) => line.trimEnd()).filter(Boolean);
1307
- const firstMeaningfulLine = outputLines.find((line) => !/^## Untrusted workspace data boundary/i.test(line)) || outputLines[0] || '';
1308
- const brief = firstMeaningfulLine.slice(0, 160);
1309
- 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`));
1310
819
  }
1311
820
  spinner.start();
1312
821
  spinner.text = 'Next step...';
@@ -1316,27 +825,12 @@ export class ChatCommand {
1316
825
  spinner.text = this.sanitizeServerPath(String(event.content || 'Routing to V3 Agent...'));
1317
826
  return;
1318
827
  }
1319
- if (event.type === 'queued') {
1320
- if (spinner.isSpinning)
1321
- spinner.stop();
1322
- process.stderr.write(chalk.yellow(' [Queue] ') + this.sanitizeServerPath(String(event.message || 'Waiting for V3 capacity...')) + '\n');
1323
- spinner.start();
1324
- spinner.text = 'Waiting for V3...';
1325
- return;
1326
- }
1327
- if (event.type === 'heartbeat') {
1328
- spinner.text = 'V3 Agent is still thinking...';
1329
- return;
1330
- }
1331
828
  if (event.type === 'thinking') {
1332
829
  this.v3IterationCount += 1;
1333
830
  const iterText = this.sanitizeServerPath(event.content || '');
1334
831
  if (spinner.isSpinning)
1335
832
  spinner.stop();
1336
- process.stderr.write(chalk.cyan(`\n── Iteration ${this.v3IterationCount}${iterText ? '' : '...'} ──\n`));
1337
- if (iterText) {
1338
- process.stderr.write(chalk.gray(`${iterText}\n`));
1339
- }
833
+ process.stderr.write(chalk_1.default.cyan(`\n── ${iterText || `Iteration ${this.v3IterationCount}`} ──\n`));
1340
834
  spinner.start();
1341
835
  spinner.text = 'Analyzing...';
1342
836
  return;
@@ -1350,7 +844,7 @@ export class ChatCommand {
1350
844
  this.writeV3StreamText(spinner, text);
1351
845
  }
1352
846
  else {
1353
- spinner.text = chalk.cyan('[Response] ') + 'Writing response...';
847
+ spinner.text = chalk_1.default.cyan('[Response] ') + 'Writing response...';
1354
848
  }
1355
849
  return;
1356
850
  }
@@ -1368,15 +862,15 @@ export class ChatCommand {
1368
862
  const fail = event.tasks_failed ?? 0;
1369
863
  statLine += ` — ${ok} tasks done`;
1370
864
  if (fail > 0)
1371
- statLine += chalk.yellow(`, ${fail} failed`);
865
+ statLine += chalk_1.default.yellow(`, ${fail} failed`);
1372
866
  }
1373
- process.stderr.write(chalk.green(`\n✓ Complete`) + ` — ${statLine}\n`);
867
+ process.stderr.write(chalk_1.default.green(`\n✓ Complete`) + ` — ${statLine}\n`);
1374
868
  // Show seal quality score if available
1375
869
  if (event.seal_score && typeof event.seal_score.overall === 'number') {
1376
870
  const score = event.seal_score.overall;
1377
871
  const tier = event.seal_score.tier || '';
1378
- const scoreColor = score >= 7 ? chalk.green : score >= 5 ? chalk.yellow : chalk.red;
1379
- 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');
1380
874
  }
1381
875
  return;
1382
876
  }
@@ -1386,35 +880,40 @@ export class ChatCommand {
1386
880
  const quality = this.sanitizeServerPath(plan.quality_profile || '');
1387
881
  const status = typeof plan.status === 'string' ? plan.status : '';
1388
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
+ }
1389
888
  if (spinner.isSpinning)
1390
889
  spinner.stop();
1391
- process.stderr.write(chalk.cyan(` [Plan] `) + `Task: ${planKind || 'analyzing'}`);
890
+ process.stderr.write(chalk_1.default.cyan(` [Plan] `) + `Task: ${planKind || 'analyzing'}`);
1392
891
  if (quality)
1393
- process.stderr.write(chalk.gray(` (${quality})`));
892
+ process.stderr.write(chalk_1.default.gray(` (${quality})`));
1394
893
  process.stderr.write('\n');
1395
894
  if (summary) {
1396
- process.stderr.write(chalk.gray(` ${summary}\n`));
895
+ process.stderr.write(chalk_1.default.gray(` ${summary}\n`));
1397
896
  }
1398
897
  if (status === 'planning' && Number.isFinite(Number(plan.elapsed_seconds))) {
1399
- 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`));
1400
899
  }
1401
900
  if (Array.isArray(plan.tasks) && plan.tasks.length > 0) {
1402
- 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`));
1403
902
  for (const t of plan.tasks.slice(0, 10)) {
1404
903
  const targets = Array.isArray(t.targets) && t.targets.length ? ` → ${t.targets.map((target) => this.sanitizeServerPath(String(target))).join(', ')}` : '';
1405
- 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`));
1406
905
  }
1407
906
  if (plan.tasks.length > 10) {
1408
- 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`));
1409
908
  }
1410
909
  }
1411
910
  if (Array.isArray(plan.notes) && plan.notes.length > 0) {
1412
911
  for (const note of plan.notes.slice(0, 3)) {
1413
- 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`));
1414
913
  }
1415
914
  }
1416
915
  if (Array.isArray(plan.target_files) && plan.target_files.length > 0) {
1417
- 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`));
1418
917
  }
1419
918
  spinner.start();
1420
919
  spinner.text = status === 'planning' ? 'Planning...' : 'Executing plan...';
@@ -1423,7 +922,7 @@ export class ChatCommand {
1423
922
  if (event.type === 'executor_start') {
1424
923
  if (spinner.isSpinning)
1425
924
  spinner.stop();
1426
- 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))}` : ''}
1427
926
  `);
1428
927
  spinner.start();
1429
928
  spinner.text = 'Vigthoria Executor running...';
@@ -1432,8 +931,8 @@ export class ChatCommand {
1432
931
  if (event.type === 'executor_error') {
1433
932
  if (spinner.isSpinning)
1434
933
  spinner.stop();
1435
- const msg = sanitizeUserFacingErrorText(String(event.error || 'Executor error')) || 'Executor error';
1436
- 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}
1437
936
  `);
1438
937
  spinner.start();
1439
938
  spinner.text = 'Recovering executor...';
@@ -1446,11 +945,11 @@ export class ChatCommand {
1446
945
  const status = String(summary.status || 'completed');
1447
946
  const changed = Array.isArray(summary.changed_files) ? summary.changed_files.length : 0;
1448
947
  if (status === 'failed') {
1449
- 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})` : ''}.
1450
949
  `);
1451
950
  }
1452
951
  else {
1453
- 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` : ''}.
1454
953
  `);
1455
954
  }
1456
955
  spinner.start();
@@ -1460,11 +959,11 @@ export class ChatCommand {
1460
959
  if (event.type === 'file_mutation') {
1461
960
  const rawPath = typeof event.path === 'string' ? this.sanitizeServerPath(event.path) : '';
1462
961
  const filePath = rawPath ? rawPath.replace(/\\/g, '/').split('/').slice(-2).join('/') : '';
1463
- 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');
1464
963
  if (filePath) {
1465
964
  if (spinner.isSpinning)
1466
965
  spinner.stop();
1467
- process.stderr.write(chalk.cyan(' [File] ') + `${action} ${filePath}\n`);
966
+ process.stderr.write(chalk_1.default.cyan(' [File] ') + `${action} ${filePath}\n`);
1468
967
  spinner.start();
1469
968
  }
1470
969
  return;
@@ -1473,23 +972,23 @@ export class ChatCommand {
1473
972
  if (event.checkpointed) {
1474
973
  if (spinner.isSpinning)
1475
974
  spinner.stop();
1476
- 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');
1477
976
  spinner.start();
1478
977
  }
1479
978
  else {
1480
979
  if (spinner.isSpinning)
1481
980
  spinner.stop();
1482
- 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';
1483
982
  const plannerLike = /plan|planner|dependency graph/i.test(message);
1484
983
  const executorLike = /executor|task failed|iteration/i.test(message);
1485
984
  if (plannerLike) {
1486
- 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`);
1487
986
  }
1488
987
  else if (executorLike) {
1489
- 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`);
1490
989
  }
1491
990
  else {
1492
- process.stderr.write(chalk.red(' [Error] ') + this.sanitizeServerPath(message) + '\n');
991
+ process.stderr.write(chalk_1.default.red(' [Error] ') + this.sanitizeServerPath(message) + '\n');
1493
992
  }
1494
993
  }
1495
994
  return;
@@ -1497,7 +996,7 @@ export class ChatCommand {
1497
996
  if (event.type === 'context') {
1498
997
  if (spinner.isSpinning)
1499
998
  spinner.stop();
1500
- process.stderr.write(chalk.cyan(' [Context] ') + 'Workspace bound\n');
999
+ process.stderr.write(chalk_1.default.cyan(' [Context] ') + 'Workspace bound\n');
1501
1000
  spinner.start();
1502
1001
  spinner.text = 'Starting agent...';
1503
1002
  return;
@@ -1507,7 +1006,7 @@ export class ChatCommand {
1507
1006
  this.v3ToolCallCount = 0;
1508
1007
  if (spinner.isSpinning)
1509
1008
  spinner.stop();
1510
- process.stderr.write(chalk.cyan(' [Start] ') + 'Agent initialized\n');
1009
+ process.stderr.write(chalk_1.default.cyan(' [Start] ') + 'Agent initialized\n');
1511
1010
  spinner.start();
1512
1011
  spinner.text = 'Working...';
1513
1012
  return;
@@ -1521,7 +1020,7 @@ export class ChatCommand {
1521
1020
  if (spinner.isSpinning)
1522
1021
  spinner.stop();
1523
1022
  if (fallbackMessage) {
1524
- process.stderr.write(chalk.cyan(' [V3] ') + `${fallbackMessage}\n`);
1023
+ process.stderr.write(chalk_1.default.cyan(' [V3] ') + `${fallbackMessage}\n`);
1525
1024
  }
1526
1025
  spinner.start();
1527
1026
  spinner.text = fallbackStatus || fallbackStage || 'Working...';
@@ -1535,7 +1034,7 @@ export class ChatCommand {
1535
1034
  if (event.type === 'started') {
1536
1035
  if (spinner.isSpinning)
1537
1036
  spinner.stop();
1538
- process.stderr.write(chalk.cyan(' [Operator] ') + 'Starting BMAD workflow...\n');
1037
+ process.stderr.write(chalk_1.default.cyan(' [Operator] ') + 'Starting BMAD workflow...\n');
1539
1038
  spinner.start();
1540
1039
  spinner.text = 'Connecting...';
1541
1040
  return;
@@ -1543,7 +1042,7 @@ export class ChatCommand {
1543
1042
  if (event.type === 'connected') {
1544
1043
  if (spinner.isSpinning)
1545
1044
  spinner.stop();
1546
- process.stderr.write(chalk.green(' ✓') + ' Connected to BMAD stream\n');
1045
+ process.stderr.write(chalk_1.default.green(' ✓') + ' Connected to BMAD stream\n');
1547
1046
  spinner.start();
1548
1047
  spinner.text = 'Working...';
1549
1048
  return;
@@ -1552,7 +1051,7 @@ export class ChatCommand {
1552
1051
  const agentName = event.agent || 'BMAD agent';
1553
1052
  if (spinner.isSpinning)
1554
1053
  spinner.stop();
1555
- process.stderr.write(chalk.cyan(` [Agent] `) + agentName + '\n');
1054
+ process.stderr.write(chalk_1.default.cyan(` [Agent] `) + agentName + '\n');
1556
1055
  spinner.start();
1557
1056
  spinner.text = `Running ${agentName}...`;
1558
1057
  return;
@@ -1562,7 +1061,7 @@ export class ChatCommand {
1562
1061
  const statusText = `${event.status || 'Working'}${progress}`;
1563
1062
  if (spinner.isSpinning)
1564
1063
  spinner.stop();
1565
- process.stderr.write(chalk.cyan(' [Status] ') + statusText + '\n');
1064
+ process.stderr.write(chalk_1.default.cyan(' [Status] ') + statusText + '\n');
1566
1065
  spinner.start();
1567
1066
  spinner.text = statusText;
1568
1067
  return;
@@ -1570,15 +1069,15 @@ export class ChatCommand {
1570
1069
  if (event.type === 'result') {
1571
1070
  if (spinner.isSpinning)
1572
1071
  spinner.stop();
1573
- process.stderr.write(chalk.green('\n ✓ Operator workflow complete\n'));
1072
+ process.stderr.write(chalk_1.default.green('\n ✓ Operator workflow complete\n'));
1574
1073
  return;
1575
1074
  }
1576
1075
  }
1577
1076
  constructor(config, logger) {
1578
1077
  this.config = config;
1579
1078
  this.logger = logger;
1580
- this.api = new APIClient(config, logger);
1581
- this.sessionManager = new SessionManager();
1079
+ this.api = new api_js_1.APIClient(config, logger);
1080
+ this.sessionManager = new session_js_1.SessionManager();
1582
1081
  }
1583
1082
  async run(options) {
1584
1083
  if (!this.config.isAuthenticated()) {
@@ -1591,7 +1090,6 @@ export class ChatCommand {
1591
1090
  }
1592
1091
  return;
1593
1092
  }
1594
- await this.config.refreshHubModelPreferences().catch(() => null);
1595
1093
  this.agentMode = options.agent === true;
1596
1094
  this.operatorMode = options.operator === true;
1597
1095
  this.workflowTarget = typeof options.workflow === 'string' && options.workflow.trim()
@@ -1605,19 +1103,30 @@ export class ChatCommand {
1605
1103
  this.currentModel = this.resolveInitialModel(options);
1606
1104
  this.applyNoAgentGovernance(String(options.model || this.currentModel || ''));
1607
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
+ }
1608
1119
  if (this.jsonOutput && !options.prompt) {
1609
1120
  throw new Error('--json is only supported together with --prompt.');
1610
1121
  }
1611
1122
  this.ensureProjectWorkspace();
1612
1123
  this.directPromptMode = Boolean(options.prompt);
1613
1124
  this.directToolContinuationCount = 0;
1614
- 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);
1615
1126
  this.initializeSession(options.resume === true);
1616
- this.initializeWorkspaceBrain();
1617
- await this.bootstrapWorkspaceBrain(!options.prompt);
1618
1127
  // ── Commando Bridge: connect if --bridge was specified ──────────
1619
1128
  if (options.bridge) {
1620
- const bridgeClient = new BridgeClient({
1129
+ const bridgeClient = new bridge_client_js_1.BridgeClient({
1621
1130
  bridgeUrl: options.bridge,
1622
1131
  apiKey: this.config.get('authToken'),
1623
1132
  onAdminCommand: (cmd) => this.handleAdminCommand(cmd),
@@ -1652,7 +1161,7 @@ export class ChatCommand {
1652
1161
  const timeoutId = options.bridge && bridgePromptTimeoutMs > 0
1653
1162
  ? setTimeout(() => {
1654
1163
  timedOut = true;
1655
- const b = getBridgeClient();
1164
+ const b = (0, bridge_client_js_1.getBridgeClient)();
1656
1165
  if (b) {
1657
1166
  b.emitEnd({ reason: 'timeout' });
1658
1167
  b.destroy();
@@ -1667,7 +1176,7 @@ export class ChatCommand {
1667
1176
  if (timeoutId)
1668
1177
  clearTimeout(timeoutId);
1669
1178
  if (!timedOut) {
1670
- const bridge = getBridgeClient();
1179
+ const bridge = (0, bridge_client_js_1.getBridgeClient)();
1671
1180
  if (bridge) {
1672
1181
  bridge.emitEnd({ reason: 'prompt-complete' });
1673
1182
  bridge.destroy();
@@ -1679,7 +1188,7 @@ export class ChatCommand {
1679
1188
  process.exit(process.exitCode ?? 0);
1680
1189
  }
1681
1190
  await this.startInteractiveChat();
1682
- const bridge = getBridgeClient();
1191
+ const bridge = (0, bridge_client_js_1.getBridgeClient)();
1683
1192
  if (bridge) {
1684
1193
  bridge.emitEnd({ reason: 'interactive-exit' });
1685
1194
  bridge.destroy();
@@ -1689,24 +1198,24 @@ export class ChatCommand {
1689
1198
  handleAdminCommand(cmd) {
1690
1199
  switch (cmd.action) {
1691
1200
  case 'ping':
1692
- 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' });
1693
1202
  break;
1694
1203
  case 'set-model':
1695
1204
  if (cmd.params?.value && typeof cmd.params.value === 'string') {
1696
1205
  this.currentModel = cmd.params.value;
1697
- 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 });
1698
1207
  if (!this.jsonOutput)
1699
- console.log(chalk.yellow(`[bridge] Model changed to: ${this.currentModel}`));
1208
+ console.log(chalk_1.default.yellow(`[bridge] Model changed to: ${this.currentModel}`));
1700
1209
  }
1701
1210
  break;
1702
1211
  case 'abort':
1703
1212
  if (!this.jsonOutput)
1704
- console.log(chalk.red(`[bridge] Abort requested by admin`));
1705
- 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' });
1706
1215
  process.exit(0);
1707
1216
  break;
1708
1217
  default:
1709
- getBridgeClient()?.emitError({ message: `Unknown admin command: ${cmd.action}` });
1218
+ (0, bridge_client_js_1.getBridgeClient)()?.emitError({ message: `Unknown admin command: ${cmd.action}` });
1710
1219
  }
1711
1220
  }
1712
1221
  ensureProjectWorkspace() {
@@ -1742,13 +1251,13 @@ export class ChatCommand {
1742
1251
  // Guardrail: do not watch broad home/root scopes on interactive shells.
1743
1252
  if (normalized.toLowerCase() === normalizedHome.toLowerCase()) {
1744
1253
  if (!this.jsonOutput) {
1745
- 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.'));
1746
1255
  }
1747
1256
  return false;
1748
1257
  }
1749
1258
  if (/^[a-zA-Z]:\/$/.test(normalized) || normalized === '/') {
1750
1259
  if (!this.jsonOutput) {
1751
- 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.'));
1752
1261
  }
1753
1262
  return false;
1754
1263
  }
@@ -1764,7 +1273,7 @@ export class ChatCommand {
1764
1273
  const explicitPath = this.resolvePromptWorkspacePath(options.prompt, process.cwd());
1765
1274
  if (explicitPath) {
1766
1275
  if (!this.jsonOutput) {
1767
- console.log(chalk.gray(`📁 Using project path from prompt: ${explicitPath}`));
1276
+ console.log(chalk_1.default.gray(`📁 Using project path from prompt: ${explicitPath}`));
1768
1277
  }
1769
1278
  return explicitPath;
1770
1279
  }
@@ -1866,7 +1375,7 @@ export class ChatCommand {
1866
1375
  return path.join(rootPath, `${folderName}-${Date.now()}`);
1867
1376
  }
1868
1377
  initializeSession(resume) {
1869
- this.projectMemory = new ProjectMemoryService(this.currentProjectPath);
1378
+ this.projectMemory = new project_memory_js_1.ProjectMemoryService(this.currentProjectPath);
1870
1379
  if (resume) {
1871
1380
  this.currentSession = this.sessionManager.getLatest(this.currentProjectPath);
1872
1381
  if (this.currentSession) {
@@ -1912,11 +1421,11 @@ export class ChatCommand {
1912
1421
  // Suppress all setup banners in direct-prompt mode so only the final
1913
1422
  // answer reaches stdout. Interactive (REPL) mode still shows them.
1914
1423
  if (!this.jsonOutput) {
1915
- console.log(chalk.cyan('Running single prompt in direct mode.'));
1916
- console.log(chalk.gray(`Model: ${this.currentModel}`));
1917
- 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}`));
1918
1427
  if (this.workflowTarget) {
1919
- console.log(chalk.gray(`Workflow target: ${this.workflowTarget}`));
1428
+ console.log(chalk_1.default.gray(`Workflow target: ${this.workflowTarget}`));
1920
1429
  }
1921
1430
  console.log();
1922
1431
  }
@@ -1980,7 +1489,7 @@ export class ChatCommand {
1980
1489
  const runtimeContext = await this.getPromptRuntimeContext(prompt);
1981
1490
  const resolvedWorkflow = await this.callApi('Resolve VigFlow workflow', () => this.api.resolveVigFlowWorkflow(selector));
1982
1491
  const invocationMode = this.operatorMode ? 'operator' : this.agentMode ? 'agent' : 'chat';
1983
- 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();
1984
1493
  try {
1985
1494
  const execution = await this.callApi('Run VigFlow workflow', () => this.api.runVigFlowWorkflow(resolvedWorkflow.id, {
1986
1495
  data: {
@@ -2029,9 +1538,9 @@ export class ChatCommand {
2029
1538
  return;
2030
1539
  }
2031
1540
  this.logger.success(`Workflow target ${resolvedWorkflow.name} ${execution.status}`);
2032
- console.log(chalk.gray(`Workflow ID: ${resolvedWorkflow.id}`));
2033
- console.log(chalk.gray(`Execution ID: ${execution.executionId}`));
2034
- 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}`));
2035
1544
  if (content) {
2036
1545
  console.log(content);
2037
1546
  }
@@ -2074,9 +1583,9 @@ export class ChatCommand {
2074
1583
  await this.runLocalAgentLoop(prompt);
2075
1584
  return;
2076
1585
  }
2077
- getBridgeClient()?.emitPrompt({ prompt, mode: 'operator', model: this.currentModel });
1586
+ (0, bridge_client_js_1.getBridgeClient)()?.emitPrompt({ prompt, mode: 'operator', model: this.currentModel });
2078
1587
  const runtimeContext = await this.getPromptRuntimeContext(prompt);
2079
- 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();
2080
1589
  const workflowType = 'full';
2081
1590
  const executionPrompt = this.buildExecutionPrompt(prompt);
2082
1591
  try {
@@ -2104,7 +1613,7 @@ export class ChatCommand {
2104
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)
2105
1614
  || (responseText.length < 200 && /follow the instructions|next instruction|ready to assist/i.test(responseText));
2106
1615
  if (isPolicyAck) {
2107
- 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');
2108
1617
  }
2109
1618
  if (this.jsonOutput) {
2110
1619
  console.log(JSON.stringify({
@@ -2120,7 +1629,7 @@ export class ChatCommand {
2120
1629
  else {
2121
1630
  console.log(response.content || 'Operator workflow completed.');
2122
1631
  if (response.savedWorkflow?.id) {
2123
- 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})` : ''}`));
2124
1633
  }
2125
1634
  }
2126
1635
  this.rememberBrainEvent('validation', `GoA operator workflow completed${response.workflowId ? ` workflow ${response.workflowId}` : ''}${response.savedWorkflow?.id ? ` saved VigFlow ${response.savedWorkflow.id}` : ''}.`, 'operator');
@@ -2131,8 +1640,8 @@ export class ChatCommand {
2131
1640
  if (spinner) {
2132
1641
  spinner.stop();
2133
1642
  }
2134
- const cliErr = error instanceof CLIError ? error : classifyError(error);
2135
- 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);
2136
1645
  if (!this.jsonOutput) {
2137
1646
  this.logger.error('Operator workflow failed');
2138
1647
  }
@@ -2159,8 +1668,8 @@ export class ChatCommand {
2159
1668
  * BMAD orchestrator to scan the workspace.
2160
1669
  */
2161
1670
  async runOperatorDirectAnswer(prompt) {
2162
- getBridgeClient()?.emitPrompt({ prompt, mode: 'operator-direct', model: this.currentModel });
2163
- 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();
2164
1673
  try {
2165
1674
  let operatorGrounding = [
2166
1675
  'You are Vigthoria Operator, a DevOps and infrastructure analysis assistant.',
@@ -2192,7 +1701,7 @@ export class ChatCommand {
2192
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)
2193
1702
  || (content.length < 200 && /follow the instructions|next instruction|ready to assist/i.test(content));
2194
1703
  if (isPolicyAck || !content) {
2195
- 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');
2196
1705
  }
2197
1706
  if (this.jsonOutput) {
2198
1707
  console.log(JSON.stringify({
@@ -2212,8 +1721,8 @@ export class ChatCommand {
2212
1721
  catch (error) {
2213
1722
  if (spinner)
2214
1723
  spinner.stop();
2215
- const cliErr = error instanceof CLIError ? error : classifyError(error);
2216
- 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);
2217
1726
  if (this.jsonOutput) {
2218
1727
  process.exitCode = 1;
2219
1728
  console.log(JSON.stringify({
@@ -2245,8 +1754,8 @@ export class ChatCommand {
2245
1754
  this.currentSession.agentMode = true;
2246
1755
  this.currentSession.model = this.currentModel;
2247
1756
  }
2248
- console.log(chalk.yellow('This request needs file access, so I am switching to Agent mode and working in this workspace now.'));
2249
- 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.'));
2250
1759
  await this.runAgentTurn(promptToRun);
2251
1760
  return;
2252
1761
  }
@@ -2305,8 +1814,8 @@ export class ChatCommand {
2305
1814
  }
2306
1815
  }
2307
1816
  this.messages.push({ role: 'user', content: this.buildExecutionPrompt(prompt) });
2308
- getBridgeClient()?.emitPrompt({ prompt, mode: 'chat', model: this.currentModel });
2309
- 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();
2310
1819
  try {
2311
1820
  const response = await this.callApi('Send chat message', () => this.api.chat(this.getMessagesForModel(), this.currentModel));
2312
1821
  if (spinner)
@@ -2330,7 +1839,7 @@ export class ChatCommand {
2330
1839
  console.log(finalText);
2331
1840
  }
2332
1841
  else {
2333
- 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.'));
2334
1843
  }
2335
1844
  this.messages.push({ role: 'assistant', content: response.message || '' });
2336
1845
  this.saveSession();
@@ -2338,8 +1847,8 @@ export class ChatCommand {
2338
1847
  catch (error) {
2339
1848
  if (spinner)
2340
1849
  spinner.stop();
2341
- const cliErr = error instanceof CLIError ? error : classifyError(error);
2342
- 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);
2343
1852
  if (this.jsonOutput) {
2344
1853
  process.exitCode = 1;
2345
1854
  console.log(JSON.stringify({
@@ -2356,11 +1865,10 @@ export class ChatCommand {
2356
1865
  }
2357
1866
  }
2358
1867
  }
2359
- async runAgentTurn(prompt, options = {}) {
1868
+ async runAgentTurn(prompt) {
2360
1869
  if (!this.tools) {
2361
1870
  throw new Error('Agent tools are not initialized.');
2362
1871
  }
2363
- this.bindPromptWorkspace(prompt);
2364
1872
  const requiresV3Workflow = this.shouldRequireV3AgentWorkflow(prompt);
2365
1873
  const handledByDirectFileFlow = await this.tryDirectSingleFileFlow(prompt);
2366
1874
  if (handledByDirectFileFlow) {
@@ -2371,17 +1879,11 @@ export class ChatCommand {
2371
1879
  // bypassed (e.g. HTML files routed to V3) so the local agent loop has file
2372
1880
  // awareness and the ⚙ Executing: read_file banner is always emitted.
2373
1881
  await this.primeBypassedTargetFileContext(prompt);
2374
- if (!options.skipLocalLoop && !options.forceV3 && this.shouldPreferLocalAgentLoop(prompt)) {
2375
- const completed = await this.runLocalAgentLoop(prompt);
2376
- if (completed) {
2377
- this.saveSession();
2378
- return;
2379
- }
2380
- if (!this.jsonOutput) {
2381
- console.log(chalk.yellow('Local chat backend unavailable (self-hosted inference may be offline). Trying V3 agent workflow...'));
2382
- }
1882
+ if (this.shouldPreferLocalAgentLoop(prompt)) {
1883
+ await this.runLocalAgentLoop(prompt);
1884
+ return;
2383
1885
  }
2384
- const handledByV3Workflow = await this.tryV3AgentWorkflow(prompt, options);
1886
+ const handledByV3Workflow = await this.tryV3AgentWorkflow(prompt);
2385
1887
  if (handledByV3Workflow) {
2386
1888
  this.saveSession();
2387
1889
  return;
@@ -2395,110 +1897,24 @@ export class ChatCommand {
2395
1897
  }
2396
1898
  await this.runLocalAgentLoop(prompt);
2397
1899
  }
2398
- localAgentIterationCount = 0;
2399
- buildLocalAgentChatOptions(preflight, onRouteAttempt) {
2400
- const preferredRoute = preflight?.endpoint
2401
- ? this.api.mapPreflightEndpointToRoute(preflight.endpoint)
2402
- : undefined;
2403
- return {
2404
- fastFail: true,
2405
- singleRoute: true,
2406
- stream: true,
2407
- connectTimeoutMs: Number.parseInt(process.env.VIGTHORIA_CHAT_CONNECT_TIMEOUT_MS || '20000', 10),
2408
- idleTimeoutMs: Number.parseInt(process.env.VIGTHORIA_CHAT_IDLE_TIMEOUT_MS || '120000', 10),
2409
- preferredRoute: preferredRoute || 'coder',
2410
- onRouteAttempt,
2411
- };
2412
- }
2413
- printChatModelPreflight(preflight) {
2414
- if (this.jsonOutput) {
2415
- return;
2416
- }
2417
- console.log(chalk.gray('Model backend preflight:'));
2418
- for (const route of preflight.routes) {
2419
- const marker = route.ok ? chalk.green(' ✓') : chalk.red(' ✗');
2420
- const detail = route.error ? chalk.gray(` — ${route.error}`) : '';
2421
- console.log(`${marker} ${route.name}${detail}`);
2422
- }
2423
- if (preflight.healthy) {
2424
- console.log(chalk.gray(`Using: ${preflight.endpoint}`));
2425
- }
2426
- else if (preflight.error) {
2427
- console.log(chalk.yellow(preflight.error));
2428
- }
2429
- console.log();
2430
- }
2431
1900
  async runLocalAgentLoop(prompt) {
2432
1901
  if (!this.tools) {
2433
1902
  throw new Error('Agent tools are not initialized.');
2434
1903
  }
2435
- this.localAgentIterationCount = 0;
2436
- const runtime = this.getRuntimeEnvironmentContext();
2437
- if (!this.jsonOutput) {
2438
- console.log();
2439
- console.log(chalk.gray('━━━ ROUTING DECISION ━━━'));
2440
- console.log(chalk.gray('Reason: local-machine-agent-loop'));
2441
- console.log(chalk.gray(`Platform: ${runtime.platform}`));
2442
- console.log(chalk.gray(`Machine scope: ${runtime.machineScope}`));
2443
- console.log(chalk.gray(`Workspace: ${runtime.workspacePath}`));
2444
- console.log(chalk.gray('Tools execute on your local filesystem.'));
2445
- console.log(chalk.gray('━'.repeat(30)));
2446
- console.log();
2447
- }
2448
1904
  this.lastActionableUserInput = prompt;
2449
1905
  this.directToolContinuationCount = 0;
2450
1906
  this.agentToolEvidence = { discovery: 0, mutation: 0, searchFailed: 0 };
2451
1907
  this.tools.clearSessionApprovals();
2452
- 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 });
2453
1909
  this.ensureAgentSystemPrompt();
2454
- const scopedPrompt = this.buildScopedUserPrompt(this.buildContextualAgentPrompt(prompt));
2455
- this.messages.push({ role: 'user', content: scopedPrompt });
2456
- await this.primeAgentWorkspaceDiscovery(prompt);
1910
+ this.messages.push({ role: 'user', content: this.buildScopedUserPrompt(prompt) });
2457
1911
  this.saveSession();
2458
- const preflightSpinner = this.jsonOutput
2459
- ? null
2460
- : createSpinner({ text: 'Checking model connection (preflight)...', spinner: 'clock' }).start();
2461
- let preflight;
2462
- try {
2463
- preflight = await this.api.runChatModelPreflight(this.currentModel);
2464
- }
2465
- finally {
2466
- if (preflightSpinner) {
2467
- preflightSpinner.stop();
2468
- }
2469
- }
2470
- this.printChatModelPreflight(preflight);
2471
- if (!preflight.healthy) {
2472
- return false;
2473
- }
2474
1912
  const maxTurns = 10;
2475
- const preferredRoute = preflight?.endpoint
2476
- ? (this.api.mapPreflightEndpointToRoute(preflight.endpoint) || 'coder')
2477
- : 'coder';
2478
1913
  for (let turn = 0; turn < maxTurns; turn += 1) {
2479
- const spinner = this.jsonOutput ? null : createSpinner({ text: turn === 0 ? 'Planning first tool step...' : 'Continuing...', spinner: 'clock' }).start();
2480
- let streamedVisible = '';
1914
+ const spinner = this.jsonOutput ? null : (0, logger_js_1.createSpinner)({ text: turn === 0 ? 'Planning...' : 'Continuing...', spinner: 'clock' }).start();
2481
1915
  let response;
2482
1916
  try {
2483
- response = await this.callApi('Send agent chat message', () => this.api.chat(this.getMessagesForModel({ compact: turn === 0 }), this.currentModel, false, {
2484
- fastFail: true,
2485
- singleRoute: true,
2486
- stream: true,
2487
- connectTimeoutMs: Number.parseInt(process.env.VIGTHORIA_CHAT_CONNECT_TIMEOUT_MS || '20000', 10),
2488
- idleTimeoutMs: Number.parseInt(process.env.VIGTHORIA_CHAT_IDLE_TIMEOUT_MS || '120000', 10),
2489
- preferredRoute,
2490
- onRouteAttempt: (routeLabel) => {
2491
- if (spinner) {
2492
- spinner.text = routeLabel;
2493
- }
2494
- },
2495
- onStreamDelta: (chunk) => {
2496
- streamedVisible += chunk;
2497
- if (spinner) {
2498
- spinner.text = streamedVisible.trim().slice(-72) || 'Streaming model output...';
2499
- }
2500
- },
2501
- }), 0);
1917
+ response = await this.callApi('Send agent chat message', () => this.api.chat(this.getMessagesForModel(), this.currentModel), 0);
2502
1918
  }
2503
1919
  catch (firstErr) {
2504
1920
  // If we already gathered evidence and the model API fails on a
@@ -2507,14 +1923,7 @@ export class ChatCommand {
2507
1923
  this.logger.debug('Agent continuation API call failed, retrying once...');
2508
1924
  try {
2509
1925
  await new Promise(r => setTimeout(r, 2000));
2510
- response = await this.callApi('Retry agent chat message', () => this.api.chat(this.getMessagesForModel(), this.currentModel, false, {
2511
- fastFail: true,
2512
- singleRoute: true,
2513
- stream: true,
2514
- connectTimeoutMs: Number.parseInt(process.env.VIGTHORIA_CHAT_CONNECT_TIMEOUT_MS || '20000', 10),
2515
- idleTimeoutMs: Number.parseInt(process.env.VIGTHORIA_CHAT_IDLE_TIMEOUT_MS || '120000', 10),
2516
- preferredRoute,
2517
- }), 0);
1926
+ response = await this.callApi('Retry agent chat message', () => this.api.chat(this.getMessagesForModel(), this.currentModel), 0);
2518
1927
  }
2519
1928
  catch (retryErr) {
2520
1929
  // Retry also failed — synthesize an answer from evidence
@@ -2537,40 +1946,17 @@ export class ChatCommand {
2537
1946
  console.log(fallbackContent);
2538
1947
  }
2539
1948
  this.saveSession();
2540
- return true;
1949
+ return;
2541
1950
  }
2542
1951
  throw retryErr;
2543
1952
  }
2544
1953
  }
2545
1954
  else {
2546
- const cliErr = firstErr instanceof CLIError ? firstErr : classifyError(firstErr);
2547
- 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);
2548
1957
  if (spinner)
2549
1958
  spinner.stop();
2550
- this.rememberBrainEvent('issue', 'Agent model request failed: ' + formatted, 'agent');
2551
- if (turn === 0 && (cliErr.category === 'model_backend' || cliErr.category === 'network' || cliErr.category === 'timeout')) {
2552
- if (!this.jsonOutput) {
2553
- this.logger.error(formatted);
2554
- const transport = this.api.getLastChatTransportErrors();
2555
- if (transport.length > 0) {
2556
- console.log(chalk.gray(`Routes tried: ${transport.slice(0, 3).join(' | ')}`));
2557
- }
2558
- }
2559
- else {
2560
- process.exitCode = 1;
2561
- console.log(JSON.stringify({
2562
- success: false,
2563
- mode: 'agent',
2564
- model: this.currentModel,
2565
- partial: false,
2566
- content: '',
2567
- error: formatted,
2568
- errorCategory: cliErr.category,
2569
- metadata: { executionPath: 'local-agent-loop' },
2570
- }, null, 2));
2571
- }
2572
- return false;
2573
- }
1959
+ this.rememberBrainEvent('issue', 'Agent API preflight failed: ' + formatted, 'agent');
2574
1960
  if (this.jsonOutput) {
2575
1961
  process.exitCode = 1;
2576
1962
  console.log(JSON.stringify({
@@ -2587,7 +1973,7 @@ export class ChatCommand {
2587
1973
  else {
2588
1974
  this.logger.error(formatted);
2589
1975
  }
2590
- return true;
1976
+ return;
2591
1977
  }
2592
1978
  }
2593
1979
  if (spinner)
@@ -2597,12 +1983,7 @@ export class ChatCommand {
2597
1983
  this.messages.push({ role: 'assistant', content: assistantMessage });
2598
1984
  const toolCalls = this.extractToolCalls(assistantMessage);
2599
1985
  const visibleText = this.stripToolPayloads(assistantMessage).trim();
2600
- if (visibleText && !this.jsonOutput) {
2601
- this.localAgentIterationCount += 1;
2602
- console.log(chalk.cyan(`\n── Iteration ${this.localAgentIterationCount} ──`));
2603
- console.log(chalk.gray(visibleText));
2604
- }
2605
- getBridgeClient()?.emitModelResponse({
1986
+ (0, bridge_client_js_1.getBridgeClient)()?.emitModelResponse({
2606
1987
  model: this.currentModel,
2607
1988
  chars: assistantMessage.length,
2608
1989
  hasToolCalls: toolCalls.length > 0,
@@ -2620,25 +2001,18 @@ export class ChatCommand {
2620
2001
  // Detect resignation: model gives up saying files/things were "not found"
2621
2002
  // without having tried list_dir to discover the correct path.
2622
2003
  const isResignation = /(?:not found|cannot be (?:determined|compared|completed)|do not exist|does not exist|unable to locate|neither.*exist|could not (?:find|locate)|no (?:such|matching) file)/i.test(sanitized) && this.agentToolEvidence.discovery < 4;
2623
- const actionablePrompt = this.lastActionableUserInput || prompt;
2624
- const needsRepoWork = this.isDiagnosticPrompt(actionablePrompt) || this.isImplementationPrompt(actionablePrompt);
2625
2004
  // Gate 1: First turn with no discovery at all
2626
- const gate1 = turn === 0 && this.agentToolEvidence.discovery === 0 && (needsRepoWork || this.directPromptMode || this.agentMode || isPolicyAck);
2005
+ const gate1 = turn === 0 && this.agentToolEvidence.discovery === 0 && (this.isDiagnosticPrompt(prompt) || this.directPromptMode || isPolicyAck);
2627
2006
  // Gate 2: Any turn where the response is just a follow-up question,
2628
2007
  // tool-failure echoes, or premature resignation (the model gave up
2629
2008
  // instead of retrying with list_dir to find the correct paths)
2630
- const gate2 = (this.directPromptMode || this.agentMode) && turn < 6 && (isPolicyAck || isFollowUp || isEmptyAfterSanitize || isResignation);
2631
- const gateNoToolsNoWork = needsRepoWork
2632
- && this.agentToolEvidence.discovery === 0
2633
- && this.agentToolEvidence.mutation === 0
2634
- && turn < maxTurns - 1
2635
- && (isEmptyAfterSanitize || isPolicyAck || isFollowUp);
2009
+ const gate2 = this.directPromptMode && turn < 6 && (isPolicyAck || isFollowUp || isEmptyAfterSanitize || isResignation);
2636
2010
  // Gate 3: Model outputs code blocks as text instead of using write_file.
2637
2011
  // If the response contains ``` code fences but no write_file was called,
2638
2012
  // reject and instruct the model to use write_file.
2639
2013
  const hasCodeBlocks = (sanitized.match(/```/g) || []).length >= 2;
2640
2014
  const gate3 = hasCodeBlocks && this.agentToolEvidence.mutation === 0 && turn < 6;
2641
- if (gate1 || gate2 || gate3 || gateNoToolsNoWork) {
2015
+ if (gate1 || gate2 || gate3) {
2642
2016
  // Remove the useless response from history
2643
2017
  if (isPolicyAck || isFollowUp || isEmptyAfterSanitize || isResignation || gate3) {
2644
2018
  this.messages.pop();
@@ -2677,7 +2051,7 @@ export class ChatCommand {
2677
2051
  console.log(finalContent);
2678
2052
  }
2679
2053
  this.saveSession();
2680
- return true;
2054
+ return;
2681
2055
  }
2682
2056
  await this.executeToolCalls(toolCalls);
2683
2057
  this.directToolContinuationCount += 1;
@@ -2690,8 +2064,8 @@ export class ChatCommand {
2690
2064
  catch (error) {
2691
2065
  if (spinner)
2692
2066
  spinner.stop();
2693
- const cliErr = error instanceof CLIError ? error : classifyError(error);
2694
- 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);
2695
2069
  this.rememberBrainEvent('issue', `Agent turn failed: ${errorMsg}`, 'agent');
2696
2070
  if (this.jsonOutput) {
2697
2071
  process.exitCode = 1;
@@ -2711,7 +2085,7 @@ export class ChatCommand {
2711
2085
  else {
2712
2086
  this.logger.error(errorMsg);
2713
2087
  }
2714
- return true;
2088
+ return;
2715
2089
  }
2716
2090
  }
2717
2091
  if (this.jsonOutput) {
@@ -2732,37 +2106,6 @@ export class ChatCommand {
2732
2106
  console.log('Task complete.');
2733
2107
  }
2734
2108
  this.saveSession();
2735
- return true;
2736
- }
2737
- async primeAgentWorkspaceDiscovery(prompt) {
2738
- if (!this.tools || this.agentToolEvidence.discovery > 0) {
2739
- return;
2740
- }
2741
- const actionablePrompt = this.buildContextualAgentPrompt(prompt);
2742
- if (!this.isDiagnosticPrompt(actionablePrompt) && !this.isImplementationPrompt(actionablePrompt)) {
2743
- return;
2744
- }
2745
- const listCall = {
2746
- tool: 'list_dir',
2747
- args: { path: '.' },
2748
- };
2749
- if (!this.jsonOutput) {
2750
- console.log(chalk.cyan('⚙ Executing: list_dir → workspace root (agent bootstrap)'));
2751
- }
2752
- const result = await this.tools.execute(listCall);
2753
- const summary = this.formatToolResult(listCall, result);
2754
- if (!this.jsonOutput) {
2755
- console.log(result.success ? chalk.gray(summary) : chalk.red(summary));
2756
- }
2757
- this.messages.push({ role: 'system', content: summary });
2758
- getBridgeClient()?.emitToolResult({
2759
- tool: listCall.tool,
2760
- success: result.success,
2761
- preview: (result.output || result.error || '').slice(0, 300),
2762
- });
2763
- if (result.success) {
2764
- this.agentToolEvidence.discovery += 1;
2765
- }
2766
2109
  }
2767
2110
  async primeBypassedTargetFileContext(prompt) {
2768
2111
  if (!this.directPromptMode || !this.tools) {
@@ -2782,12 +2125,12 @@ export class ChatCommand {
2782
2125
  args: { path: targetFile },
2783
2126
  };
2784
2127
  if (!this.jsonOutput) {
2785
- console.log(chalk.cyan(`⚙ Executing: ${readCall.tool}`));
2128
+ console.log(chalk_1.default.cyan(`⚙ Executing: ${readCall.tool}`));
2786
2129
  }
2787
2130
  const readResult = await this.tools.execute(readCall);
2788
2131
  const readSummary = this.formatToolResult(readCall, readResult);
2789
2132
  if (!this.jsonOutput) {
2790
- 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));
2791
2134
  }
2792
2135
  if (readResult.success && readResult.output) {
2793
2136
  this.messages.push({ role: 'system', content: readSummary });
@@ -2809,12 +2152,12 @@ export class ChatCommand {
2809
2152
  args: { path: targetFile },
2810
2153
  };
2811
2154
  if (!this.jsonOutput) {
2812
- console.log(chalk.cyan(`⚙ Executing: ${readCall.tool}`));
2155
+ console.log(chalk_1.default.cyan(`⚙ Executing: ${readCall.tool}`));
2813
2156
  }
2814
2157
  const readResult = await this.tools.execute(readCall);
2815
2158
  const readSummary = this.formatToolResult(readCall, readResult);
2816
2159
  if (!this.jsonOutput) {
2817
- 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));
2818
2161
  }
2819
2162
  this.messages.push({ role: 'system', content: readSummary });
2820
2163
  if (!readResult.success || !readResult.output) {
@@ -2857,12 +2200,12 @@ export class ChatCommand {
2857
2200
  },
2858
2201
  };
2859
2202
  if (!this.jsonOutput) {
2860
- console.log(chalk.cyan(`⚙ Executing: ${writeCall.tool}`));
2203
+ console.log(chalk_1.default.cyan(`⚙ Executing: ${writeCall.tool}`));
2861
2204
  }
2862
2205
  const writeResult = await this.tools.execute(writeCall);
2863
2206
  const writeSummary = this.formatToolResult(writeCall, writeResult);
2864
2207
  if (!this.jsonOutput) {
2865
- 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));
2866
2209
  }
2867
2210
  this.messages.push({ role: 'system', content: writeSummary });
2868
2211
  if (!writeResult.success) {
@@ -2898,10 +2241,10 @@ export class ChatCommand {
2898
2241
  console.log(`Updated ${targetFile}.`);
2899
2242
  if (previewGate.required) {
2900
2243
  if (previewGate.passed) {
2901
- 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'}`));
2902
2245
  }
2903
2246
  else {
2904
- 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}` : ''}`));
2905
2248
  }
2906
2249
  }
2907
2250
  }
@@ -2909,7 +2252,7 @@ export class ChatCommand {
2909
2252
  }
2910
2253
  isConfirmationFollowUp(prompt) {
2911
2254
  const normalized = prompt.trim().toLowerCase().replace(/[.!?]+$/g, '').replace(/\s+/g, ' ');
2912
- return /^(ja|ja bitte|ja bitte mach das|mach das|bitte mach das|genau|ok|okay|yes|yes please|please do|do it|go ahead|go on|continue|proceed|make it so|keep going|next)$/.test(normalized);
2255
+ return /^(ja|ja bitte|ja bitte mach das|mach das|bitte mach das|genau|ok|okay|yes|yes please|please do|do it|go ahead|continue|proceed|make it so)$/.test(normalized);
2913
2256
  }
2914
2257
  getPreviousActionablePrompt() {
2915
2258
  if (this.lastActionableUserInput && !this.isConfirmationFollowUp(this.lastActionableUserInput)) {
@@ -2948,11 +2291,14 @@ export class ChatCommand {
2948
2291
  'Do not reinterpret this confirmation as a new website, landing page, template, or index.html task.',
2949
2292
  ].join('\n');
2950
2293
  }
2951
- async tryV3AgentWorkflow(prompt, options = {}) {
2294
+ async tryV3AgentWorkflow(prompt) {
2952
2295
  // Extract explicit workspace path from prompt (if provided by user)
2953
2296
  let promptWorkspacePath = null;
2954
2297
  try {
2955
2298
  promptWorkspacePath = this.resolvePromptWorkspacePath(prompt, this.currentProjectPath);
2299
+ if (promptWorkspacePath && !this.jsonOutput) {
2300
+ console.log(chalk_1.default.cyan(`📁 Workspace from prompt: ${promptWorkspacePath}`));
2301
+ }
2956
2302
  }
2957
2303
  catch {
2958
2304
  // Path extraction failed, use default workspace
@@ -2968,15 +2314,15 @@ export class ChatCommand {
2968
2314
  // STREAMING: Log routing decision transparently to user
2969
2315
  if (!this.jsonOutput) {
2970
2316
  console.log();
2971
- console.log(chalk.gray('━━━ ROUTING DECISION ━━━'));
2972
- console.log(chalk.gray(`Reason: ${routingPolicy.routeReason}`));
2973
- console.log(chalk.gray(`Model: ${routingPolicy.selectedModel}`));
2974
- console.log(chalk.gray(`Cloud Eligible: ${routingPolicy.cloudEligible}`));
2975
- 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}`));
2976
2322
  if (routingPolicy.heavyTask) {
2977
- console.log(chalk.gray(`Task Complexity: HEAVY`));
2323
+ console.log(chalk_1.default.gray(`Task Complexity: HEAVY`));
2978
2324
  }
2979
- console.log(chalk.gray('━'.repeat(30)));
2325
+ console.log(chalk_1.default.gray('━'.repeat(30)));
2980
2326
  console.log();
2981
2327
  }
2982
2328
  // Reset streaming counters for new workflow
@@ -2984,12 +2330,9 @@ export class ChatCommand {
2984
2330
  this.v3ToolCallCount = 0;
2985
2331
  this.v3LastActivity = Date.now();
2986
2332
  this.v3StreamingStarted = false;
2987
- this.v3StreamedTextBuffer = '';
2988
- this.v3LiveToolEvidence = [];
2989
- this.v3PendingToolCalls = [];
2990
- 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);
2991
2334
  taskDisplay.start(0);
2992
- const spinner = this.jsonOutput ? null : createSpinner({
2335
+ const spinner = this.jsonOutput ? null : (0, logger_js_1.createSpinner)({
2993
2336
  text: routingPolicy.cloudSelected ? 'Routing heavy task to Vigthoria Cloud...' : 'Routing to V3 Agent...',
2994
2337
  spinner: 'clock',
2995
2338
  }).start();
@@ -3048,41 +2391,20 @@ export class ChatCommand {
3048
2391
  }
3049
2392
  };
3050
2393
  const executionPrompt = this.buildExecutionPrompt(contextualPrompt);
3051
- const agentTaskType = this.inferAgentTaskType(contextualPrompt);
2394
+ const agentTaskType = this.inferAgentTaskType(prompt);
3052
2395
  const workspaceContext = {
3053
2396
  workspacePath: workspacePath,
3054
2397
  projectPath: workspacePath,
3055
2398
  targetPath: workspacePath,
3056
2399
  ...runtimeContext,
3057
2400
  };
3058
- if (!this.jsonOutput && !this.directPromptMode) {
3059
- try {
3060
- const snapshot = this.api.getAgentWorkspaceSnapshot(workspacePath);
3061
- console.log(chalk.gray(`Workspace sync: ${snapshot.fileCount} files indexed from ${workspacePath}`));
3062
- if (snapshot.paths.length > 0) {
3063
- console.log(chalk.gray(`Workspace index: ${snapshot.paths.slice(0, 5).map((filePath) => filePath.replace(/\\/g, '/')).join(', ')}${snapshot.paths.length > 5 ? ', ...' : ''}`));
3064
- }
3065
- }
3066
- catch {
3067
- console.log(chalk.gray(`Workspace sync: preparing ${workspacePath}`));
3068
- }
3069
- }
3070
2401
  // Start workspace watcher for bidirectional real-time sync
3071
2402
  let watcher = null;
3072
- let workspaceWs = null;
3073
- const stopWorkspaceSync = () => {
3074
- watcher?.stop();
3075
- if (workspaceWs) {
3076
- workspaceWs.disconnect();
3077
- workspaceWs = null;
3078
- }
3079
- };
3080
2403
  if (this.shouldStartWorkspaceWatcher(workspacePath)) {
3081
- watcher = new WorkspaceWatcher({
2404
+ watcher = new workspace_stream_js_1.WorkspaceWatcher({
3082
2405
  workspaceRoot: workspacePath,
3083
2406
  onFileChange: (relativePath, content, action) => {
3084
2407
  this.logger.debug(`Local change detected: ${action} ${relativePath}`);
3085
- workspaceWs?.syncFile(relativePath, content, action);
3086
2408
  },
3087
2409
  });
3088
2410
  watcher.start();
@@ -3092,15 +2414,15 @@ export class ChatCommand {
3092
2414
  await this.api.ensureV3ServiceKey();
3093
2415
  if (spinner)
3094
2416
  this.updateV3AgentSpinner(spinner, { type: 'v3_status', content: 'Checking V3 connection (preflight)...' });
3095
- 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();
3096
2419
  if (!healthCheck.healthy) {
3097
- this.logger.warn('V3 health probe did not confirm readiness; starting SSE agent stream anyway.');
3098
- if (spinner)
3099
- 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);
3100
2423
  }
3101
- else if (spinner) {
2424
+ if (spinner)
3102
2425
  this.updateV3AgentSpinner(spinner, { type: 'v3_status', content: 'Connected to V3. Starting agent execution...' });
3103
- }
3104
2426
  const workflowPromise = this.api.runV3AgentWorkflow(executionPrompt, {
3105
2427
  workspace: { path: workspacePath },
3106
2428
  ...workspaceContext,
@@ -3118,40 +2440,23 @@ export class ChatCommand {
3118
2440
  rawPrompt: prompt,
3119
2441
  contextualPrompt,
3120
2442
  history: this.getMessagesForModel(),
3121
- clientToolExecution: true,
3122
- liveToolEvidence: this.v3LiveToolEvidence,
3123
- onClientToolExecute: (event) => this.executeClientV3Tool(event),
3124
- onWorkspaceContext: async ({ contextId, serverWorkspaceRoot }) => {
3125
- if (workspaceWs || !contextId || !serverWorkspaceRoot || !this.shouldStartWorkspaceWatcher(workspacePath)) {
3126
- return;
3127
- }
3128
- const headers = await this.api.getV3AgentHeaders();
3129
- const token = String(headers.Authorization?.replace(/^Bearer\s+/i, '')
3130
- || headers['X-V3-Service-Key']
3131
- || headers['x-v3-service-key']
3132
- || '').trim();
3133
- if (!token) {
3134
- return;
3135
- }
3136
- const baseUrl = this.api.getV3AgentBaseUrls(false)[0] || 'https://coder.vigthoria.io';
3137
- const serverUrl = baseUrl.replace(/^http/i, 'ws');
3138
- workspaceWs = new WorkspaceWSClient({
3139
- serverUrl,
3140
- token,
3141
- contextId,
3142
- workspaceRoot: serverWorkspaceRoot,
3143
- });
3144
- workspaceWs.connect();
3145
- this.logger.debug(`Workspace WS sync bound to context ${contextId}`);
3146
- },
3147
2443
  ...runtimeContext,
3148
2444
  onStreamEvent: (event) => {
3149
2445
  if (event.type === 'plan') {
3150
- taskDisplay.complete(0);
3151
- taskDisplay.start(1);
3152
- const tasks = event?.plan?.tasks;
3153
- if (Array.isArray(tasks))
3154
- 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
+ }
3155
2460
  }
3156
2461
  else if (event.type === 'executor_start') {
3157
2462
  taskDisplay.start(1);
@@ -3203,34 +2508,6 @@ export class ChatCommand {
3203
2508
  if (this.v3StreamingStarted) {
3204
2509
  process.stdout.write('\n');
3205
2510
  }
3206
- let finalContent = String(response.content || '').trim();
3207
- const needsUserReport = this.inferAgentTaskType(prompt) === 'analysis'
3208
- || this.isThinV3Summary(finalContent)
3209
- || this.v3LiveToolEvidence.length > 0;
3210
- if (needsUserReport) {
3211
- const userReport = this.buildUserFacingV3RunReport(prompt, workspacePath, {
3212
- partial: response.partial === true || this.isThinV3Summary(finalContent),
3213
- serverNote: finalContent && !this.isThinV3Summary(finalContent) ? finalContent : undefined,
3214
- });
3215
- if (userReport && !this.isThinV3Summary(userReport)) {
3216
- finalContent = userReport;
3217
- }
3218
- }
3219
- else if (this.isThinV3Summary(finalContent) && this.v3LiveToolEvidence.length > 0) {
3220
- const evidenceSummary = this.api.formatV3AgentResponse({
3221
- events: [],
3222
- liveToolEvidence: this.v3LiveToolEvidence,
3223
- });
3224
- if (evidenceSummary && !this.isThinV3Summary(evidenceSummary)) {
3225
- finalContent = evidenceSummary;
3226
- }
3227
- }
3228
- response.content = finalContent || response.content;
3229
- let v3UserReportPrinted = false;
3230
- if (!this.jsonOutput && finalContent && needsUserReport) {
3231
- this.printV3UserReport(finalContent);
3232
- v3UserReportPrinted = true;
3233
- }
3234
2511
  const previewGate = (response.metadata?.previewGate || null);
3235
2512
  const workspaceHasOutput = this.api.hasAgentWorkspaceOutput(workspaceContext);
3236
2513
  const success = previewGate?.required === true
@@ -3243,7 +2520,7 @@ export class ChatCommand {
3243
2520
  }
3244
2521
  this.logger.warn('Falling back to legacy CLI agent loop');
3245
2522
  this.logger.debug(`V3 agent workflow returned an incomplete result: ${previewGate?.error || 'workspace changes were not fully validated'}`);
3246
- stopWorkspaceSync();
2523
+ watcher?.stop();
3247
2524
  return false;
3248
2525
  }
3249
2526
  const errorMessage = `V3 agent workflow returned an incomplete result and legacy fallback is disabled. ${previewGate?.error || 'Workspace changes were not fully validated.'}`;
@@ -3264,11 +2541,11 @@ export class ChatCommand {
3264
2541
  metadata: { executionPath: 'v3-agent', previewGate },
3265
2542
  }, null, 2));
3266
2543
  }
3267
- stopWorkspaceSync();
2544
+ watcher?.stop();
3268
2545
  return true;
3269
2546
  }
3270
2547
  if (!this.jsonOutput && previewGate?.required && previewGate?.passed !== true && workspaceHasOutput) {
3271
- 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}` : '.'}`));
3272
2549
  }
3273
2550
  if (this.jsonOutput) {
3274
2551
  console.log(JSON.stringify({
@@ -3284,59 +2561,43 @@ export class ChatCommand {
3284
2561
  }, null, 2));
3285
2562
  }
3286
2563
  else if (this.v3StreamingStarted) {
3287
- // Content may have been streamed already, but some V3 runs only stream
3288
- // status/tool messages and keep the useful final report in response.content.
3289
- if (!v3UserReportPrinted && !this.jsonOutput) {
3290
- console.log(chalk.gray(`Agent routing: ${routingPolicy.cloudSelected ? 'Vigthoria Cloud' : 'V3 Agent'}`));
3291
- }
3292
- if (!v3UserReportPrinted && response.content && this.shouldPrintV3FinalContent(response.content)) {
3293
- console.log(response.content);
3294
- }
3295
- else if (!v3UserReportPrinted && response.content && this.isThinV3Summary(response.content)) {
3296
- console.log(chalk.yellow('V3 agent finished after reading local files, but no final narrative summary was emitted.'));
3297
- 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'}`));
3298
2567
  }
3299
2568
  }
3300
2569
  else if (response.content) {
3301
- if (!v3UserReportPrinted && !this.directPromptMode) {
3302
- console.log(chalk.gray(`Agent routing: ${routingPolicy.cloudSelected ? 'Vigthoria Cloud' : 'V3 Agent'}`));
3303
- }
3304
- if (!v3UserReportPrinted && this.shouldPrintV3FinalContent(response.content)) {
3305
- console.log(response.content);
3306
- }
3307
- else if (!v3UserReportPrinted && this.isThinV3Summary(response.content)) {
3308
- console.log('V3 agent workflow completed, but no final task summary was emitted.');
3309
- }
3310
- else if (!v3UserReportPrinted) {
3311
- console.log(response.content);
2570
+ if (!this.directPromptMode) {
2571
+ console.log(chalk_1.default.gray(`Agent routing: ${routingPolicy.cloudSelected ? 'Vigthoria Cloud' : 'V3 Agent'}`));
3312
2572
  }
2573
+ console.log(response.content);
3313
2574
  }
3314
2575
  else {
3315
2576
  if (!this.directPromptMode) {
3316
- 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'}`));
3317
2578
  }
3318
2579
  console.log('V3 agent workflow completed.');
3319
2580
  }
3320
2581
  if (!this.jsonOutput && previewGate?.required) {
3321
2582
  if (previewGate.passed) {
3322
- 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'}`));
3323
2584
  }
3324
2585
  else {
3325
- 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}` : ''}`));
3326
2587
  }
3327
2588
  }
3328
2589
  // Show change summary for files touched by the agent
3329
2590
  if (!this.jsonOutput && !this.directPromptMode && response.changedFiles) {
3330
2591
  const fileCount = Object.keys(response.changedFiles).length;
3331
2592
  if (fileCount > 0) {
3332
- console.log(chalk.gray(`\nFiles changed: ${fileCount}`));
2593
+ console.log(chalk_1.default.gray(`\nFiles changed: ${fileCount}`));
3333
2594
  for (const relPath of Object.keys(response.changedFiles).slice(0, 15)) {
3334
- console.log(chalk.gray(` ${chalk.green('+')} ${relPath}`));
2595
+ console.log(chalk_1.default.gray(` ${chalk_1.default.green('+')} ${relPath}`));
3335
2596
  }
3336
2597
  if (fileCount > 15) {
3337
- console.log(chalk.gray(` ... and ${fileCount - 15} more`));
2598
+ console.log(chalk_1.default.gray(` ... and ${fileCount - 15} more`));
3338
2599
  }
3339
- 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.`));
3340
2601
  }
3341
2602
  }
3342
2603
  // Resolve the Execute-tasks spinner: only mark it ✓ when the run actually
@@ -3374,8 +2635,8 @@ export class ChatCommand {
3374
2635
  }
3375
2636
  selfHealTool = healResult.tool || null;
3376
2637
  if (!this.directPromptMode) {
3377
- const hs = healResult.passed ? chalk.green('passed') : chalk.yellow('partial');
3378
- 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})`));
3379
2640
  }
3380
2641
  }
3381
2642
  else {
@@ -3412,8 +2673,8 @@ export class ChatCommand {
3412
2673
  hasOutput: workspaceHasOutput,
3413
2674
  selfHealStatus,
3414
2675
  selfHealTool,
3415
- plannerError: liveOutcome.plannerError ? sanitizeUserFacingErrorText(liveOutcome.plannerError) : null,
3416
- 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,
3417
2678
  finishedAt: Date.now(),
3418
2679
  };
3419
2680
  if (!this.jsonOutput && !this.directPromptMode) {
@@ -3421,11 +2682,11 @@ export class ChatCommand {
3421
2682
  this.printAgentRunSummary(this.lastAgentRunOutcome, executorSucceeded, changedFileCount);
3422
2683
  }
3423
2684
  this.messages.push({ role: 'assistant', content: response.content || 'V3 agent workflow completed.' });
3424
- stopWorkspaceSync();
2685
+ watcher?.stop();
3425
2686
  return true;
3426
2687
  }
3427
2688
  catch (error) {
3428
- stopWorkspaceSync();
2689
+ watcher?.stop();
3429
2690
  if (!this.api.hasAgentWorkspaceOutput(workspaceContext)) {
3430
2691
  const recovered = await this.tryRecoverV3ServiceAndRetry(executionPrompt, prompt, workspaceContext, routingPolicy, spinner, error);
3431
2692
  if (recovered) {
@@ -3437,13 +2698,13 @@ export class ChatCommand {
3437
2698
  spinner.stop();
3438
2699
  }
3439
2700
  this.logger.warn('Falling back to legacy CLI agent loop');
3440
- 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 || '')}`);
3441
2702
  return false;
3442
2703
  }
3443
2704
  if (spinner) {
3444
2705
  spinner.stop();
3445
2706
  }
3446
- const safeDetail = sanitizeUserFacingErrorText(error.message || '');
2707
+ const safeDetail = (0, api_js_1.sanitizeUserFacingErrorText)(error.message || '');
3447
2708
  const errorMessage = safeDetail
3448
2709
  ? `Agent mode is unavailable right now. ${safeDetail}`
3449
2710
  : 'Agent mode is unavailable right now. Please retry shortly or run vigthoria login if the issue persists.';
@@ -3472,8 +2733,8 @@ export class ChatCommand {
3472
2733
  hasOutput: this.api.hasAgentWorkspaceOutput(workspaceContext),
3473
2734
  selfHealStatus: 'skipped',
3474
2735
  selfHealTool: null,
3475
- plannerError: liveOutcome.plannerError ? sanitizeUserFacingErrorText(liveOutcome.plannerError) : null,
3476
- 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,
3477
2738
  finishedAt: Date.now(),
3478
2739
  };
3479
2740
  if (this.jsonOutput) {
@@ -3503,7 +2764,7 @@ export class ChatCommand {
3503
2764
  spinner.stop();
3504
2765
  }
3505
2766
  if (!this.jsonOutput) {
3506
- console.log(chalk.yellow(`V3 recovery: ${recovery.message} Retrying once...`));
2767
+ console.log(chalk_1.default.yellow(`V3 recovery: ${recovery.message} Retrying once...`));
3507
2768
  }
3508
2769
  this.v3IterationCount = 0;
3509
2770
  this.v3ToolCallCount = 0;
@@ -3547,7 +2808,7 @@ export class ChatCommand {
3547
2808
  }, null, 2));
3548
2809
  }
3549
2810
  else if (!this.v3StreamingStarted && retryResponse.content) {
3550
- 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'}`));
3551
2812
  console.log(retryResponse.content);
3552
2813
  }
3553
2814
  this.messages.push({ role: 'assistant', content: retryResponse.content || 'V3 agent workflow completed after recovery.' });
@@ -3564,10 +2825,10 @@ export class ChatCommand {
3564
2825
  ? 'Interactive Agent Chat'
3565
2826
  : 'Interactive Chat';
3566
2827
  this.logger.section(this.workflowTarget ? `${chatTitle} Via Workflow Target` : chatTitle);
3567
- console.log(chalk.gray('Type /help for commands. Type /exit to quit.'));
3568
- 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 }}}'));
3569
2830
  if (this.workflowTarget) {
3570
- console.log(chalk.gray(`Workflow target: ${this.workflowTarget}`));
2831
+ console.log(chalk_1.default.gray(`Workflow target: ${this.workflowTarget}`));
3571
2832
  }
3572
2833
  const rl = readline.createInterface({
3573
2834
  input: process.stdin,
@@ -3577,17 +2838,17 @@ export class ChatCommand {
3577
2838
  const readMultiLineInput = async () => {
3578
2839
  const lines = [];
3579
2840
  let firstLine = await new Promise((resolve) => {
3580
- rl.question(chalk.blue('> '), resolve);
2841
+ rl.question(chalk_1.default.blue('> '), resolve);
3581
2842
  });
3582
2843
  // Check for {{{ block mode
3583
2844
  if (firstLine.trim() === '{{{' || firstLine.trim().endsWith('{{{')) {
3584
2845
  if (firstLine.trim() !== '{{{') {
3585
2846
  lines.push(firstLine.trim().replace(/\{\{\{$/, '').trim());
3586
2847
  }
3587
- 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)'));
3588
2849
  while (true) {
3589
2850
  const line = await new Promise((resolve) => {
3590
- rl.question(chalk.gray(' '), resolve);
2851
+ rl.question(chalk_1.default.gray(' '), resolve);
3591
2852
  });
3592
2853
  if (line.trim() === '}}}')
3593
2854
  break;
@@ -3599,7 +2860,7 @@ export class ChatCommand {
3599
2860
  while (firstLine.endsWith('\\')) {
3600
2861
  lines.push(firstLine.slice(0, -1));
3601
2862
  firstLine = await new Promise((resolve) => {
3602
- rl.question(chalk.gray(' '), resolve);
2863
+ rl.question(chalk_1.default.gray(' '), resolve);
3603
2864
  });
3604
2865
  }
3605
2866
  lines.push(firstLine);
@@ -3627,14 +2888,6 @@ export class ChatCommand {
3627
2888
  this.showProjectMemory();
3628
2889
  continue;
3629
2890
  }
3630
- if (trimmed === '/index') {
3631
- await this.reindexWorkspaceBrain();
3632
- continue;
3633
- }
3634
- if (trimmed === '/brain') {
3635
- this.showBrainIndexStatus();
3636
- continue;
3637
- }
3638
2891
  if (trimmed === '/compact') {
3639
2892
  this.compactCurrentSession();
3640
2893
  continue;
@@ -3648,8 +2901,8 @@ export class ChatCommand {
3648
2901
  else {
3649
2902
  this.syncInteractiveModeModel('chat');
3650
2903
  }
3651
- console.log(chalk.yellow(`Agent mode: ${this.agentMode ? 'ON' : 'OFF'}`));
3652
- 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}`));
3653
2906
  if (this.currentSession) {
3654
2907
  this.currentSession.agentMode = this.agentMode;
3655
2908
  this.currentSession.operatorMode = this.operatorMode;
@@ -3671,8 +2924,8 @@ export class ChatCommand {
3671
2924
  else {
3672
2925
  this.syncInteractiveModeModel('chat');
3673
2926
  }
3674
- console.log(chalk.yellow(`Operator mode: ${this.operatorMode ? 'ON' : 'OFF'}`));
3675
- 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}`));
3676
2929
  if (this.currentSession) {
3677
2930
  this.currentSession.agentMode = this.agentMode;
3678
2931
  this.currentSession.operatorMode = this.operatorMode;
@@ -3683,7 +2936,7 @@ export class ChatCommand {
3683
2936
  }
3684
2937
  if (trimmed === '/clear') {
3685
2938
  this.messages = [];
3686
- console.log(chalk.yellow('Conversation cleared.'));
2939
+ console.log(chalk_1.default.yellow('Conversation cleared.'));
3687
2940
  continue;
3688
2941
  }
3689
2942
  if (trimmed === '/status') {
@@ -3693,7 +2946,7 @@ export class ChatCommand {
3693
2946
  if (trimmed === '/retry') {
3694
2947
  const followUp = this.buildRetryPrompt();
3695
2948
  if (!followUp) {
3696
- 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.'));
3697
2950
  continue;
3698
2951
  }
3699
2952
  const nextSignature = this.computeRetryPromptSignature();
@@ -3707,11 +2960,11 @@ export class ChatCommand {
3707
2960
  if (this.retryPromptStreak >= 3) {
3708
2961
  const continuePrompt = this.buildContinuePrompt();
3709
2962
  if (continuePrompt) {
3710
- 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.'));
3711
2964
  if (!this.agentMode) {
3712
2965
  this.agentMode = true;
3713
2966
  this.syncInteractiveModeModel('agent');
3714
- console.log(chalk.gray('Agent mode re-enabled for continuation.'));
2967
+ console.log(chalk_1.default.gray('Agent mode re-enabled for continuation.'));
3715
2968
  }
3716
2969
  await this.runAgentTurn(continuePrompt);
3717
2970
  continue;
@@ -3720,7 +2973,7 @@ export class ChatCommand {
3720
2973
  if (!this.agentMode) {
3721
2974
  this.agentMode = true;
3722
2975
  this.syncInteractiveModeModel('agent');
3723
- console.log(chalk.gray('Agent mode re-enabled for retry.'));
2976
+ console.log(chalk_1.default.gray('Agent mode re-enabled for retry.'));
3724
2977
  }
3725
2978
  await this.runAgentTurn(followUp);
3726
2979
  continue;
@@ -3728,32 +2981,26 @@ export class ChatCommand {
3728
2981
  if (trimmed === '/continue') {
3729
2982
  const followUp = this.buildContinuePrompt();
3730
2983
  if (!followUp) {
3731
- 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.'));
3732
2985
  continue;
3733
2986
  }
3734
2987
  if (!this.agentMode) {
3735
2988
  this.agentMode = true;
3736
2989
  this.syncInteractiveModeModel('agent');
3737
- console.log(chalk.gray('Agent mode re-enabled for continuation.'));
2990
+ console.log(chalk_1.default.gray('Agent mode re-enabled for continuation.'));
3738
2991
  }
3739
- await this.runAgentTurn(followUp, { skipLocalLoop: true, forceV3: true });
2992
+ await this.runAgentTurn(followUp);
3740
2993
  continue;
3741
2994
  }
3742
- if (/^(?:\/logout|logout)$/i.test(trimmed)) {
3743
- const { logout } = await import('./auth.js');
3744
- await logout();
3745
- console.log(chalk.gray('Session ended.'));
3746
- break;
3747
- }
3748
2995
  if (trimmed === '/save') {
3749
2996
  this.saveSession();
3750
- console.log(chalk.green('Session saved.'));
2997
+ console.log(chalk_1.default.green('Session saved.'));
3751
2998
  continue;
3752
2999
  }
3753
3000
  if (trimmed.startsWith('/model ')) {
3754
3001
  this.currentModel = trimmed.slice(7).trim() || this.currentModel;
3755
3002
  this.modelExplicitlySelected = true;
3756
- console.log(chalk.yellow(`Model changed to: ${this.currentModel}`));
3003
+ console.log(chalk_1.default.yellow(`Model changed to: ${this.currentModel}`));
3757
3004
  if (this.currentSession) {
3758
3005
  this.currentSession.model = this.currentModel;
3759
3006
  this.saveSession();
@@ -3783,8 +3030,6 @@ export class ChatCommand {
3783
3030
  console.log(' /operator Toggle BMAD operator mode');
3784
3031
  console.log(' /context Show current session and project memory');
3785
3032
  console.log(' /memory Show Vigthoria project brain status');
3786
- console.log(' /brain Show workspace codebase index status');
3787
- console.log(' /index Re-index workspace and sync to Brain Hub');
3788
3033
  console.log(' /compact Compact current session into memory summary');
3789
3034
  console.log(' /clear Clear conversation');
3790
3035
  console.log(' /save Save session');
@@ -3803,74 +3048,71 @@ export class ChatCommand {
3803
3048
  * - Never leave the spinners (`⟳`, `–`) ambiguous when the prompt returns.
3804
3049
  */
3805
3050
  printAgentRunSummary(outcome, executorSucceeded, changedFileCount) {
3806
- const bar = chalk.gray('─'.repeat(63));
3807
- const ok = chalk.green('✓');
3808
- const warn = chalk.yellow('⚠');
3809
- 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('✗');
3810
3055
  const failedList = outcome.failedTaskIds.slice(0, 8);
3811
3056
  const unfinishedList = outcome.unfinishedTaskIds.slice(0, 8);
3812
3057
  const hasTaskInfo = outcome.tasksTotal > 0 || failedList.length > 0 || unfinishedList.length > 0;
3813
3058
  console.log('');
3814
3059
  console.log(bar);
3815
3060
  if (executorSucceeded && outcome.selfHealStatus !== 'partial' && outcome.selfHealStatus !== 'failed' && failedList.length === 0) {
3816
- 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`) : ''}`);
3817
3062
  }
3818
3063
  else if (executorSucceeded) {
3819
- 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`) : ''}`);
3820
3065
  }
3821
3066
  else {
3822
- 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`) : ''}`);
3823
3068
  }
3824
3069
  if (hasTaskInfo) {
3825
3070
  const succ = outcome.tasksTotal > 0 ? `${outcome.tasksSucceeded}/${outcome.tasksTotal}` : `${outcome.tasksSucceeded}`;
3826
- console.log(chalk.gray(` Tasks completed: ${succ}`));
3071
+ console.log(chalk_1.default.gray(` Tasks completed: ${succ}`));
3827
3072
  if (failedList.length > 0) {
3828
- const more = outcome.failedTaskIds.length > failedList.length ? chalk.gray(` (+${outcome.failedTaskIds.length - failedList.length} more)`) : '';
3829
- 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);
3830
3075
  }
3831
3076
  if (unfinishedList.length > 0) {
3832
- const more = outcome.unfinishedTaskIds.length > unfinishedList.length ? chalk.gray(` (+${outcome.unfinishedTaskIds.length - unfinishedList.length} more)`) : '';
3833
- 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);
3834
3079
  }
3835
3080
  }
3836
- else if (!executorSucceeded) {
3837
- console.log(chalk.gray(' The detailed overview is printed above when available.'));
3838
- }
3839
3081
  if (typeof outcome.qualityScore === 'number') {
3840
3082
  const score = outcome.qualityScore.toFixed(1);
3841
- const colour = outcome.qualityScore >= 70 ? chalk.green : outcome.qualityScore >= 30 ? chalk.yellow : chalk.red;
3842
- 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`));
3843
3085
  }
3844
3086
  if (outcome.qualityBlockers.length > 0) {
3845
3087
  const list = outcome.qualityBlockers.slice(0, 3).join('; ');
3846
- const more = outcome.qualityBlockers.length > 3 ? chalk.gray(` (+${outcome.qualityBlockers.length - 3} more)`) : '';
3847
- 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);
3848
3090
  }
3849
3091
  if (outcome.qualityMissing.length > 0) {
3850
3092
  const list = outcome.qualityMissing.slice(0, 5).join(', ');
3851
- const more = outcome.qualityMissing.length > 5 ? chalk.gray(` (+${outcome.qualityMissing.length - 5} more)`) : '';
3852
- 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);
3853
3095
  }
3854
3096
  if (outcome.plannerError) {
3855
- 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)));
3856
3098
  }
3857
3099
  if (outcome.executorError) {
3858
- 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)));
3859
3101
  }
3860
3102
  console.log('');
3861
- console.log(chalk.gray('What you can do next:'));
3103
+ console.log(chalk_1.default.gray('What you can do next:'));
3862
3104
  if (failedList.length > 0 || unfinishedList.length > 0 || !executorSucceeded) {
3863
- console.log(' ' + chalk.cyan('/retry') + chalk.gray(' resume the failed/unfinished tasks only'));
3864
- 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'));
3865
3107
  }
3866
3108
  else {
3867
- 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'));
3868
3110
  }
3869
3111
  if (changedFileCount > 0) {
3870
- 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'));
3871
3113
  }
3872
- console.log(' ' + chalk.cyan('/status') + chalk.gray(' re-print this summary later'));
3873
- 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'));
3874
3116
  console.log(bar);
3875
3117
  console.log('');
3876
3118
  }
@@ -3924,18 +3166,15 @@ export class ChatCommand {
3924
3166
  const missingLine = o.qualityMissing.length > 0
3925
3167
  ? `\nMissing pieces: ${o.qualityMissing.slice(0, 6).join(', ')}.`
3926
3168
  : '';
3927
- return `Continue the previous agent run from the current workspace state without re-doing already-completed work.${taskList}${blockerLine}${missingLine}
3928
- Original request was: ${o.prompt}
3929
-
3930
- 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}`;
3931
3170
  }
3932
3171
  /**
3933
3172
  * Re-print the last agent run summary, or guide the user when there isn't one.
3934
3173
  */
3935
3174
  showAgentRunStatus() {
3936
3175
  if (!this.lastAgentRunOutcome) {
3937
- console.log(chalk.yellow('No agent run has finished in this session yet.'));
3938
- 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.'));
3939
3178
  return;
3940
3179
  }
3941
3180
  const o = this.lastAgentRunOutcome;
@@ -3948,45 +3187,45 @@ Now implement the missing fixes by editing the local workspace files. Use write/
3948
3187
  }
3949
3188
  showContext() {
3950
3189
  if (!this.currentSession) {
3951
- console.log(chalk.yellow('No active session.'));
3190
+ console.log(chalk_1.default.yellow('No active session.'));
3952
3191
  return;
3953
3192
  }
3954
- console.log(chalk.cyan(this.getCurrentSessionInfo()));
3193
+ console.log(chalk_1.default.cyan(this.getCurrentSessionInfo()));
3955
3194
  if (this.currentSession.memorySummary?.trim()) {
3956
3195
  console.log();
3957
- console.log(chalk.white('Compact Session Memory:'));
3958
- 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()));
3959
3198
  }
3960
3199
  else {
3961
- console.log(chalk.gray('No compact session memory summary yet.'));
3200
+ console.log(chalk_1.default.gray('No compact session memory summary yet.'));
3962
3201
  }
3963
3202
  const runtime = this.getRuntimeEnvironmentContext();
3964
3203
  console.log();
3965
- console.log(chalk.white('Runtime Environment:'));
3966
- console.log(chalk.gray(`Platform: ${runtime.platform} (${runtime.osPlatform})`));
3967
- console.log(chalk.gray(`Machine scope: ${runtime.machineScope}`));
3968
- console.log(chalk.gray(`Workspace: ${runtime.workspacePath}`));
3969
- console.log(chalk.gray(`CWD: ${runtime.cwd}`));
3970
- 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'}`));
3971
3210
  this.showProjectMemory();
3972
3211
  }
3973
3212
  showProjectMemory() {
3974
3213
  if (!this.projectMemory) {
3975
- this.projectMemory = new ProjectMemoryService(this.currentProjectPath);
3214
+ this.projectMemory = new project_memory_js_1.ProjectMemoryService(this.currentProjectPath);
3976
3215
  }
3977
3216
  const status = this.projectMemory.getStatus();
3978
3217
  console.log();
3979
- console.log(chalk.white('Project Brain:'));
3980
- console.log(chalk.gray(`Path: ${status.memoryDir}`));
3981
- 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}`));
3982
3221
  const typeSummary = Object.entries(status.typeCounts).map(([type, count]) => `${type}=${count}`).join(', ');
3983
3222
  if (typeSummary) {
3984
- console.log(chalk.gray(`Types: ${typeSummary}`));
3223
+ console.log(chalk_1.default.gray(`Types: ${typeSummary}`));
3985
3224
  }
3986
3225
  }
3987
3226
  compactCurrentSession() {
3988
3227
  if (!this.currentSession) {
3989
- console.log(chalk.yellow('No active session.'));
3228
+ console.log(chalk_1.default.yellow('No active session.'));
3990
3229
  return;
3991
3230
  }
3992
3231
  this.currentSession.messages = [...this.messages];
@@ -3994,7 +3233,7 @@ Now implement the missing fixes by editing the local workspace files. Use write/
3994
3233
  this.messages = [...this.currentSession.messages];
3995
3234
  this.sessionManager.save(this.currentSession);
3996
3235
  if (!this.projectMemory) {
3997
- this.projectMemory = new ProjectMemoryService(this.currentProjectPath);
3236
+ this.projectMemory = new project_memory_js_1.ProjectMemoryService(this.currentProjectPath);
3998
3237
  }
3999
3238
  this.projectMemory.rememberConversation(this.messages, {
4000
3239
  source: 'manual-compact',
@@ -4002,7 +3241,7 @@ Now implement the missing fixes by editing the local workspace files. Use write/
4002
3241
  model: this.currentModel,
4003
3242
  sessionSummary: this.currentSession.memorySummary || '',
4004
3243
  });
4005
- 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.'));
4006
3245
  }
4007
3246
  ensureAgentSystemPrompt() {
4008
3247
  const hasSystemPrompt = this.messages.some((message) => message.role === 'system' && message.content.includes('Vigthoria CLI agent operating contract'));
@@ -4015,8 +3254,7 @@ Now implement the missing fixes by editing the local workspace files. Use write/
4015
3254
  });
4016
3255
  }
4017
3256
  buildAgentSystemPrompt() {
4018
- const runtime = this.getRuntimeEnvironmentContext();
4019
- const toolCatalog = AgenticTools.getToolDefinitions()
3257
+ const toolCatalog = tools_js_1.AgenticTools.getToolDefinitions()
4020
3258
  .map((tool) => {
4021
3259
  const params = tool.parameters
4022
3260
  .map((param) => `${param.name}${param.required ? ' (required)' : ''}`)
@@ -4027,7 +3265,6 @@ Now implement the missing fixes by editing the local workspace files. Use write/
4027
3265
  return [
4028
3266
  'Vigthoria CLI agent operating contract.',
4029
3267
  `You are operating inside the project root: ${this.currentProjectPath}`,
4030
- `Execution platform: ${runtime.platform} (${runtime.osPlatform}). Machine scope: ${runtime.machineScope}.`,
4031
3268
  `You are operating on the user's LOCAL machine. This CLI is not a server-only runtime.`,
4032
3269
  `All file reads/writes and command execution must target the local project/workspace path unless the user explicitly requests remote/server execution.`,
4033
3270
  'For command execution: ask for confirmation before risky commands; never redirect normal local work to server paths.',
@@ -4060,12 +3297,10 @@ Now implement the missing fixes by editing the local workspace files. Use write/
4060
3297
  ].join('\n');
4061
3298
  }
4062
3299
  buildScopedUserPrompt(prompt) {
4063
- const runtime = this.getRuntimeEnvironmentContext();
4064
3300
  return [
4065
3301
  this.buildExecutionPrompt(prompt),
4066
3302
  '',
4067
3303
  `Project root: ${this.currentProjectPath}`,
4068
- `Execution platform: ${runtime.platform}. Machine scope: ${runtime.machineScope}.`,
4069
3304
  'Stay within this project root unless the user explicitly expands scope.',
4070
3305
  'Finish the request and stop once it is complete.',
4071
3306
  ].join('\n');
@@ -4147,32 +3382,14 @@ Now implement the missing fixes by editing the local workspace files. Use write/
4147
3382
  if (this.isBrowserTaskPrompt(prompt)) {
4148
3383
  return false;
4149
3384
  }
4150
- const runtime = this.getRuntimeEnvironmentContext();
4151
- const forceRemoteAgent = /^(1|true|yes)$/i.test(String(process.env.VIGTHORIA_PREFER_V3_AGENT_LOOP
4152
- || process.env.VIGTHORIA_FORCE_REMOTE_AGENT
4153
- || ''));
4154
- // CLI sessions on a user's local machine must execute tools locally.
4155
- // Remote V3 only sees a partial hydrated copy and cannot reach real paths.
4156
- if (runtime.machineScope === 'local-machine') {
4157
- if (forceRemoteAgent) {
4158
- return false;
4159
- }
4160
- if (this.isImplementationPrompt(prompt)) {
4161
- return true;
4162
- }
4163
- const preferLocalOptIn = /^(1|true|yes)$/i.test(String(process.env.VIGTHORIA_PREFER_LOCAL_AGENT_LOOP || ''));
4164
- // Read-only analysis on a Windows/macOS/Linux desktop is faster and more
4165
- // reliable through V3 + client-side read tools than a blocking chat route.
4166
- if (this.isAnalysisLookupPrompt(prompt) && !preferLocalOptIn) {
4167
- return false;
4168
- }
4169
- return true;
4170
- }
4171
- // 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.
4172
3388
  const preferLocalOptIn = /^(1|true|yes)$/i.test(String(process.env.VIGTHORIA_PREFER_LOCAL_AGENT_LOOP || ''));
4173
3389
  if (!preferLocalOptIn) {
4174
3390
  return false;
4175
3391
  }
3392
+ const runtime = this.getRuntimeEnvironmentContext();
4176
3393
  if (this.directPromptMode && this.isRepoGroundedPrompt(prompt)) {
4177
3394
  return false;
4178
3395
  }
@@ -4513,15 +3730,6 @@ Now implement the missing fixes by editing the local workspace files. Use write/
4513
3730
  if (fallback) {
4514
3731
  return fallback;
4515
3732
  }
4516
- if (this.agentToolEvidence.discovery === 0 && this.agentToolEvidence.mutation === 0) {
4517
- if (this.isImplementationPrompt(prompt) || this.isDiagnosticPrompt(prompt)) {
4518
- return [
4519
- 'The agent did not inspect the workspace or run any tools before finishing.',
4520
- 'This usually means the model backend returned an empty response.',
4521
- 'Try the request again. If it keeps happening, check Vigthoria Coder / vLLM logs on the server.',
4522
- ].join(' ');
4523
- }
4524
- }
4525
3733
  return sanitized || 'Task complete.';
4526
3734
  }
4527
3735
  /**
@@ -4690,57 +3898,6 @@ Now implement the missing fixes by editing the local workspace files. Use write/
4690
3898
  .trim();
4691
3899
  return normalized || null;
4692
3900
  }
4693
- async tryLocalToolFallback(call, result) {
4694
- if (result.success || !this.tools) {
4695
- return result;
4696
- }
4697
- const runtime = this.getRuntimeEnvironmentContext();
4698
- if (runtime.machineScope !== 'local-machine') {
4699
- return result;
4700
- }
4701
- if (call.tool === 'read_file' && call.args.path) {
4702
- const parent = path.dirname(call.args.path);
4703
- const listPath = parent === '.' || parent === '' ? '.' : parent;
4704
- const listResult = await this.tools.execute({
4705
- tool: 'list_dir',
4706
- args: { path: listPath },
4707
- });
4708
- if (listResult.success && listResult.output) {
4709
- return {
4710
- ...result,
4711
- suggestion: `File not found at "${call.args.path}". Parent directory "${listPath}" contains:\n${listResult.output}`,
4712
- };
4713
- }
4714
- const baseName = path.basename(call.args.path);
4715
- const globResult = await this.tools.execute({
4716
- tool: 'glob',
4717
- args: { pattern: `**/${baseName}` },
4718
- });
4719
- if (globResult.success && globResult.output?.trim()) {
4720
- return {
4721
- ...result,
4722
- suggestion: `File not found at "${call.args.path}". Matching paths:\n${globResult.output}`,
4723
- };
4724
- }
4725
- }
4726
- if (call.tool === 'list_dir') {
4727
- const listPath = call.args.path || '.';
4728
- const command = runtime.platform === 'windows'
4729
- ? `dir /b "${path.join(this.currentProjectPath, listPath)}"`
4730
- : `ls -la "${path.join(this.currentProjectPath, listPath)}"`;
4731
- const shellResult = await this.tools.execute({
4732
- tool: 'bash',
4733
- args: { command, cwd: this.currentProjectPath },
4734
- });
4735
- if (shellResult.success && shellResult.output) {
4736
- return {
4737
- ...result,
4738
- suggestion: `list_dir failed for "${listPath}". Terminal listing:\n${shellResult.output}`,
4739
- };
4740
- }
4741
- }
4742
- return result;
4743
- }
4744
3901
  async executeToolCalls(toolCalls) {
4745
3902
  if (!this.tools) {
4746
3903
  throw new Error('Agent tools are not initialized.');
@@ -4750,20 +3907,15 @@ Now implement the missing fixes by editing the local workspace files. Use write/
4750
3907
  const verbose = !this.jsonOutput;
4751
3908
  for (const call of toolCalls) {
4752
3909
  if (verbose) {
4753
- const target = call.args.path || call.args.pattern || call.args.command || '';
4754
- const detail = target ? chalk.gray(` → ${String(target).replace(/\\/g, '/')}`) : '';
4755
- console.log(chalk.cyan(`⚙ Executing: ${call.tool}`) + detail);
3910
+ console.log(chalk_1.default.cyan(`⚙ Executing: ${call.tool}`));
4756
3911
  }
4757
- getBridgeClient()?.emitToolCall({ tool: call.tool, args: call.args });
3912
+ (0, bridge_client_js_1.getBridgeClient)()?.emitToolCall({ tool: call.tool, args: call.args });
4758
3913
  let result = await this.tools.execute(call);
4759
- if (!result.success) {
4760
- result = await this.tryLocalToolFallback(call, result);
4761
- }
4762
3914
  // Phase 2: If a search tool failed (search_failed), retry with alternate approach
4763
3915
  const searchStatus = result.metadata?.searchStatus;
4764
3916
  if (call.tool === 'grep' && searchStatus === 'search_failed') {
4765
3917
  if (verbose) {
4766
- 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...`));
4767
3919
  }
4768
3920
  // Force Node-native fallback by re-executing with a note
4769
3921
  const fallbackResult = await this.tools.execute({
@@ -4776,10 +3928,10 @@ Now implement the missing fixes by editing the local workspace files. Use write/
4776
3928
  }
4777
3929
  const summary = this.formatToolResult(call, result);
4778
3930
  if (verbose) {
4779
- 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));
4780
3932
  }
4781
3933
  this.messages.push({ role: 'system', content: summary });
4782
- 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) });
4783
3935
  // Phase 5: Track tool evidence for quality gates
4784
3936
  const finalStatus = result.metadata?.searchStatus;
4785
3937
  if (finalStatus === 'search_failed') {
@@ -4844,7 +3996,7 @@ Now implement the missing fixes by editing the local workspace files. Use write/
4844
3996
  this.messages = [...this.currentSession.messages];
4845
3997
  this.sessionManager.save(this.currentSession);
4846
3998
  if (!this.projectMemory) {
4847
- this.projectMemory = new ProjectMemoryService(this.currentProjectPath);
3999
+ this.projectMemory = new project_memory_js_1.ProjectMemoryService(this.currentProjectPath);
4848
4000
  }
4849
4001
  this.projectMemory.rememberConversation(this.messages, {
4850
4002
  source: 'cli-session',
@@ -4863,7 +4015,7 @@ Now implement the missing fixes by editing the local workspace files. Use write/
4863
4015
  });
4864
4016
  console.log(action);
4865
4017
  const answer = await new Promise((resolve) => {
4866
- 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);
4867
4019
  });
4868
4020
  rl.close();
4869
4021
  const normalized = answer.trim().toLowerCase();
@@ -4886,3 +4038,4 @@ Now implement the missing fixes by editing the local workspace files. Use write/
4886
4038
  return [...this.messages];
4887
4039
  }
4888
4040
  }
4041
+ exports.ChatCommand = ChatCommand;