vigthoria-cli 1.9.10 → 1.9.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +4 -4
  2. package/dist/commands/auth.js +48 -65
  3. package/dist/commands/bridge.js +12 -19
  4. package/dist/commands/cancel.js +15 -22
  5. package/dist/commands/chat.d.ts +11 -0
  6. package/dist/commands/chat.js +404 -248
  7. package/dist/commands/config.js +31 -71
  8. package/dist/commands/deploy.js +83 -123
  9. package/dist/commands/device.d.ts +35 -0
  10. package/dist/commands/device.js +239 -0
  11. package/dist/commands/edit.js +32 -39
  12. package/dist/commands/explain.js +18 -25
  13. package/dist/commands/fork.js +22 -27
  14. package/dist/commands/generate.js +37 -44
  15. package/dist/commands/history.js +20 -25
  16. package/dist/commands/hub.js +95 -102
  17. package/dist/commands/index.js +41 -46
  18. package/dist/commands/legion.d.ts +1 -0
  19. package/dist/commands/legion.js +162 -209
  20. package/dist/commands/preview.js +60 -98
  21. package/dist/commands/replay.js +27 -32
  22. package/dist/commands/repo.js +103 -141
  23. package/dist/commands/review.js +29 -36
  24. package/dist/commands/security.js +5 -12
  25. package/dist/commands/update.js +15 -49
  26. package/dist/commands/workflow.d.ts +8 -1
  27. package/dist/commands/workflow.js +53 -19
  28. package/dist/index.js +409 -234
  29. package/dist/utils/api.d.ts +5 -0
  30. package/dist/utils/api.js +373 -166
  31. package/dist/utils/bridge-client.js +11 -52
  32. package/dist/utils/cli-state.d.ts +54 -0
  33. package/dist/utils/cli-state.js +185 -0
  34. package/dist/utils/config.d.ts +5 -0
  35. package/dist/utils/config.js +35 -14
  36. package/dist/utils/context-ranker.js +15 -21
  37. package/dist/utils/files.js +5 -42
  38. package/dist/utils/logger.js +42 -50
  39. package/dist/utils/post-write-validator.js +22 -29
  40. package/dist/utils/project-memory.d.ts +56 -0
  41. package/dist/utils/project-memory.js +289 -0
  42. package/dist/utils/session.d.ts +29 -3
  43. package/dist/utils/session.js +137 -85
  44. package/dist/utils/task-display.js +13 -20
  45. package/dist/utils/tools.d.ts +19 -0
  46. package/dist/utils/tools.js +84 -87
  47. package/dist/utils/workspace-cache.js +18 -26
  48. package/dist/utils/workspace-stream.js +26 -64
  49. package/install.ps1 +14 -0
  50. package/package.json +5 -3
  51. package/scripts/release/LOCAL_MACHINE_USER_VERIFICATION.md +1 -1
  52. package/scripts/release/validate-no-go-gates.sh +2 -2
@@ -1,54 +1,16 @@
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");
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 } 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 } from '../utils/workspace-stream.js';
12
+ import { TaskDisplay } from '../utils/task-display.js';
13
+ import { ProjectMemoryService } from '../utils/project-memory.js';
52
14
  const DEFAULT_V3_AGENT_TIMEOUT_MS = (() => {
53
15
  const rawValue = process.env.VIGTHORIA_AGENT_TIMEOUT_MS || process.env.V3_AGENT_TIMEOUT_MS;
54
16
  if (!rawValue) {
@@ -73,13 +35,14 @@ const DEFAULT_V3_AGENT_SOFT_TIMEOUT_MS = (() => {
73
35
  const parsed = Number.parseInt(rawValue, 10);
74
36
  return Number.isFinite(parsed) && parsed >= 0 ? parsed : 0;
75
37
  })();
76
- class ChatCommand {
38
+ export class ChatCommand {
77
39
  config;
78
40
  logger;
79
41
  api;
80
42
  messages = [];
81
43
  tools = null;
82
44
  sessionManager;
45
+ projectMemory = null;
83
46
  currentSession = null;
84
47
  agentMode = false;
85
48
  currentProjectPath = process.cwd();
@@ -143,27 +106,27 @@ class ChatCommand {
143
106
  message.includes('aborted');
144
107
  }
145
108
  toUserFacingApiError(error, context) {
146
- const classified = (0, api_js_1.classifyError)(error);
109
+ const classified = classifyError(error);
147
110
  const status = classified.statusCode || (this.isJwtExpirationError(error) ? 401 : 500);
148
111
  if (this.isJwtExpirationError(error)) {
149
- return new api_js_1.CLIError('Your Vigthoria session has expired. Run `vigthoria login` to authenticate again.', 'auth', { statusCode: 401 });
112
+ return new CLIError('Your Vigthoria session has expired. Run `vigthoria login` to authenticate again.', 'auth', { statusCode: 401 });
150
113
  }
151
114
  if (this.isTimeoutError(error)) {
152
- return new api_js_1.CLIError(`${context} timed out. Check your connection and try again.`, 'timeout', { statusCode: status });
115
+ return new CLIError(`${context} timed out. Check your connection and try again.`, 'timeout', { statusCode: status });
153
116
  }
154
117
  if (this.isNetworkError(error)) {
155
- return new api_js_1.CLIError(`${context} could not reach the Vigthoria API. Check your network connection and try again.`, 'network', { statusCode: status });
118
+ return new CLIError(`${context} could not reach the Vigthoria API. Check your network connection and try again.`, 'network', { statusCode: status });
156
119
  }
157
- const message = (0, api_js_1.sanitizeUserFacingErrorText)(classified.message || `${context} failed`);
158
- return new api_js_1.CLIError(message, status === 401 ? 'auth' : 'model_backend', { statusCode: status });
120
+ const message = sanitizeUserFacingErrorText(classified.message || `${context} failed`);
121
+ return new CLIError(message, status === 401 ? 'auth' : 'model_backend', { statusCode: status });
159
122
  }
160
123
  handleApiError(error, context) {
161
124
  const userFacingError = this.toUserFacingApiError(error, context);
162
125
  if (!this.jsonOutput) {
163
- console.error(chalk_1.default.red(`${context} failed: ${userFacingError.message}`));
126
+ console.error(chalk.red(`${context} failed: ${userFacingError.message}`));
164
127
  }
165
128
  const original = error && typeof error === 'object' ? error : { message: String(error) };
166
- (0, api_js_1.propagateError)({
129
+ propagateError({
167
130
  ...original,
168
131
  message: userFacingError.message,
169
132
  statusCode: userFacingError.statusCode,
@@ -186,7 +149,7 @@ class ChatCommand {
186
149
  catch (error) {
187
150
  if (!this.jsonOutput) {
188
151
  const transient = this.isTimeoutError(error) ? 'timeout' : this.isNetworkError(error) ? 'network error' : 'API error';
189
- console.error(chalk_1.default.red(`${context} failed with ${transient}: ${this.toUserFacingApiError(error, context).message}`));
152
+ console.error(chalk.red(`${context} failed with ${transient}: ${this.toUserFacingApiError(error, context).message}`));
190
153
  }
191
154
  lastError = error;
192
155
  if (this.isJwtExpirationError(error)) {
@@ -329,25 +292,28 @@ class ChatCommand {
329
292
  };
330
293
  }
331
294
  getMessagesForModel() {
332
- const memoryContext = this.sessionManager.buildMemoryContext(this.currentSession);
333
295
  const messages = [...this.messages];
334
- if (!memoryContext) {
335
- return messages;
336
- }
337
- const alreadyInjected = messages.some((message) => message.role === 'system' && message.content.includes('Session memory summary from earlier conversation turns.'));
338
- if (alreadyInjected) {
339
- return messages;
340
- }
341
- const insertionIndex = messages.findIndex((message) => message.role !== 'system');
342
- const memoryMessage = {
343
- role: 'system',
344
- content: memoryContext,
345
- };
346
- if (insertionIndex === -1) {
347
- messages.push(memoryMessage);
348
- return messages;
296
+ const memoryContexts = [
297
+ this.sessionManager.buildMemoryContext(this.currentSession),
298
+ this.projectMemory?.buildContextForPrompt(this.getLastUserPrompt()) || '',
299
+ ].filter((entry) => entry && entry.trim());
300
+ for (const memoryContext of memoryContexts) {
301
+ const marker = memoryContext.includes('Vigthoria project brain memory.')
302
+ ? 'Vigthoria project brain memory.'
303
+ : 'Session memory summary from earlier conversation turns.';
304
+ const alreadyInjected = messages.some((message) => message.role === 'system' && message.content.includes(marker));
305
+ if (alreadyInjected) {
306
+ continue;
307
+ }
308
+ const insertionIndex = messages.findIndex((message) => message.role !== 'system');
309
+ const memoryMessage = { role: 'system', content: memoryContext };
310
+ if (insertionIndex === -1) {
311
+ messages.push(memoryMessage);
312
+ }
313
+ else {
314
+ messages.splice(insertionIndex, 0, memoryMessage);
315
+ }
349
316
  }
350
- messages.splice(insertionIndex, 0, memoryMessage);
351
317
  return messages;
352
318
  }
353
319
  isDiagnosticPrompt(prompt) {
@@ -432,18 +398,67 @@ class ChatCommand {
432
398
  const shaping = this.buildTaskShapingInstructions(prompt);
433
399
  return shaping ? `${prompt}\n\n${shaping}` : prompt;
434
400
  }
401
+ isProjectBrainRuntimeDisabled() {
402
+ return /^(1|true|yes)$/i.test(String(process.env.VIGTHORIA_NO_BRAIN || process.env.VIGTHORIA_BRAIN_DISABLED || ''));
403
+ }
404
+ buildProjectBrainRuntimeContext(prompt, source) {
405
+ if (this.isProjectBrainRuntimeDisabled()) {
406
+ return undefined;
407
+ }
408
+ try {
409
+ if (!this.projectMemory) {
410
+ this.projectMemory = new ProjectMemoryService(this.currentProjectPath);
411
+ }
412
+ const status = this.projectMemory.getStatus();
413
+ const context = this.projectMemory.buildContextForPrompt(prompt);
414
+ if (!context && status.itemCount === 0) {
415
+ return undefined;
416
+ }
417
+ return {
418
+ schema: 'vigthoria.project-brain.v1',
419
+ source,
420
+ memoryDir: status.memoryDir,
421
+ itemCount: status.itemCount,
422
+ updatedAt: status.updatedAt,
423
+ context,
424
+ };
425
+ }
426
+ catch {
427
+ return undefined;
428
+ }
429
+ }
430
+ rememberBrainEvent(type, text, mode) {
431
+ if (this.isProjectBrainRuntimeDisabled()) {
432
+ return;
433
+ }
434
+ try {
435
+ if (!this.projectMemory) {
436
+ this.projectMemory = new ProjectMemoryService(this.currentProjectPath);
437
+ }
438
+ this.projectMemory.remember(type, text, { source: 'vigthoria-cli', mode, model: this.currentModel });
439
+ }
440
+ catch {
441
+ // Project Brain memory must not break chat, GoA, or operator execution.
442
+ }
443
+ }
435
444
  async getPromptRuntimeContext(prompt) {
445
+ const runtimeContext = {};
446
+ const brainContext = this.buildProjectBrainRuntimeContext(prompt, this.operatorMode ? 'vigthoria-cli.operator' : this.agentMode ? 'vigthoria-cli.agent' : 'vigthoria-cli.chat');
447
+ if (brainContext) {
448
+ runtimeContext.vigthoriaBrain = brainContext;
449
+ }
436
450
  if (!this.isBrowserTaskPrompt(prompt)) {
437
- return {};
451
+ return runtimeContext;
438
452
  }
439
453
  const bridgeStatus = await this.callApi('Checking DevTools Bridge status', () => this.api.getDevtoolsBridgeStatus(), 0);
440
454
  if (!this.jsonOutput && bridgeStatus.ok) {
441
- console.log(chalk_1.default.gray(`Browser task detected. DevTools Bridge is reachable at ${bridgeStatus.endpoint}.`));
455
+ console.log(chalk.gray(`Browser task detected. DevTools Bridge is reachable at ${bridgeStatus.endpoint}.`));
442
456
  }
443
457
  else if (!this.jsonOutput) {
444
- console.log(chalk_1.default.yellow(`Browser task detected. DevTools Bridge is not running at ${bridgeStatus.endpoint}.`));
458
+ console.log(chalk.yellow(`Browser task detected. DevTools Bridge is not running at ${bridgeStatus.endpoint}.`));
445
459
  }
446
460
  return {
461
+ ...runtimeContext,
447
462
  browserTask: true,
448
463
  devtoolsBridgeAvailable: bridgeStatus.ok,
449
464
  devtoolsBridgeEndpoint: bridgeStatus.endpoint,
@@ -460,12 +475,7 @@ class ChatCommand {
460
475
  sanitizeServerPath(text) {
461
476
  if (!text)
462
477
  return text;
463
- // Strip common server path prefixes from output
464
- return text
465
- .replace(/\/var\/www\/V3-Code-Agent\//g, '')
466
- .replace(/\/var\/www\/[a-zA-Z0-9_-]+\//g, '')
467
- .replace(/\/tmp\/vig-remote-[a-zA-Z0-9_-]+\//g, '')
468
- .replace(/\/opt\/vigthoria[a-zA-Z0-9_/-]*\//g, '');
478
+ return sanitizeUserFacingPathText(text);
469
479
  }
470
480
  describeV3AgentTool(toolName) {
471
481
  const normalized = String(toolName || '').toLowerCase();
@@ -498,12 +508,12 @@ class ChatCommand {
498
508
  const source = (event && typeof event === 'object' && !Buffer.isBuffer(event) && !(event instanceof Uint8Array))
499
509
  ? (event.body ?? event.stream ?? event.response ?? event)
500
510
  : event;
501
- for await (const chunk of (0, tools_js_1.robustifyStreamResponse)(source)) {
511
+ for await (const chunk of robustifyStreamResponse(source)) {
502
512
  this.v3LastActivity = Date.now();
503
513
  if (chunk.type === 'error') {
504
514
  if (spinner.isSpinning)
505
515
  spinner.stop();
506
- process.stderr.write(chalk_1.default.red(`\nStream error: ${chunk.content}\n`));
516
+ process.stderr.write(chalk.red(`\nStream error: ${chunk.content}\n`));
507
517
  continue;
508
518
  }
509
519
  if (chunk.content) {
@@ -515,11 +525,12 @@ class ChatCommand {
515
525
  const message = error instanceof Error ? error.message : String(error);
516
526
  if (spinner.isSpinning)
517
527
  spinner.stop();
518
- process.stderr.write(chalk_1.default.red(`\nStream error: ${message}\n`));
528
+ process.stderr.write(chalk.red(`\nStream error: ${message}\n`));
519
529
  }
520
530
  }
521
531
  writeV3StreamText(spinner, text) {
522
- if (!text) {
532
+ const safeText = this.sanitizeServerPath(text);
533
+ if (!safeText) {
523
534
  return;
524
535
  }
525
536
  if (!this.v3StreamingStarted) {
@@ -532,7 +543,7 @@ class ChatCommand {
532
543
  else {
533
544
  spinner.stop();
534
545
  }
535
- process.stdout.write(text);
546
+ process.stdout.write(safeText);
536
547
  }
537
548
  updateV3AgentSpinner(spinner, event) {
538
549
  if (this.isRawV3StreamPayload(event)) {
@@ -540,7 +551,7 @@ class ChatCommand {
540
551
  const message = error instanceof Error ? error.message : String(error);
541
552
  if (spinner.isSpinning)
542
553
  spinner.stop();
543
- process.stderr.write(chalk_1.default.red(`\nStream error: ${message}\n`));
554
+ process.stderr.write(chalk.red(`\nStream error: ${message}\n`));
544
555
  });
545
556
  return;
546
557
  }
@@ -554,7 +565,7 @@ class ChatCommand {
554
565
  const toolTarget = event.arguments?.path || event.arguments?.file_path || event.arguments?.pattern || '';
555
566
  const sanitizedTarget = this.sanitizeServerPath(String(toolTarget));
556
567
  const shortTarget = sanitizedTarget ? ` → ${sanitizedTarget.replace(/\\/g, '/').split('/').slice(-2).join('/')}` : '';
557
- const stepLabel = chalk_1.default.cyan(` [${this.v3IterationCount}/${this.v3ToolCallCount}]`) + ` ${toolDesc}${shortTarget}`;
568
+ const stepLabel = chalk.cyan(` [${this.v3IterationCount}/${this.v3ToolCallCount}]`) + ` ${toolDesc}${shortTarget}`;
558
569
  if (spinner.isSpinning)
559
570
  spinner.stop();
560
571
  process.stderr.write(stepLabel + '\n');
@@ -563,10 +574,11 @@ class ChatCommand {
563
574
  const toolName = event.name || event.tool || '';
564
575
  if ((toolName === 'write_file' || toolName === 'edit_file') && typeof args.content === 'string') {
565
576
  const len = args.content.length;
566
- process.stderr.write(chalk_1.default.gray(` ${len > 1000 ? Math.round(len / 1024) + ' KB' : len + ' bytes'} content\n`));
577
+ process.stderr.write(chalk.gray(` ${len > 1000 ? Math.round(len / 1024) + ' KB' : len + ' bytes'} content\n`));
567
578
  }
568
579
  else if (toolName === 'bash' && typeof args.command === 'string') {
569
- process.stderr.write(chalk_1.default.gray(` $ ${args.command.slice(0, 120)}${args.command.length > 120 ? '…' : ''}\n`));
580
+ const command = this.sanitizeServerPath(args.command);
581
+ process.stderr.write(chalk.gray(` $ ${command.slice(0, 120)}${command.length > 120 ? '…' : ''}\n`));
570
582
  }
571
583
  spinner.start();
572
584
  spinner.text = `Running ${toolDesc}...`;
@@ -575,7 +587,7 @@ class ChatCommand {
575
587
  if (event.type === 'tool_result') {
576
588
  const success = event.success !== false;
577
589
  const toolName = event.name || event.tool || '';
578
- const indicator = success ? chalk_1.default.green(' ✓') : chalk_1.default.red(' ✗');
590
+ const indicator = success ? chalk.green(' ✓') : chalk.red(' ✗');
579
591
  if (spinner.isSpinning)
580
592
  spinner.stop();
581
593
  process.stderr.write(`${indicator} ${toolName}\n`);
@@ -585,11 +597,11 @@ class ChatCommand {
585
597
  if (!success && output) {
586
598
  const sanitizedError = this.sanitizeServerPath(typeof event.error === 'string' ? event.error : output);
587
599
  const lines = sanitizedError.split('\n').slice(0, 4);
588
- process.stderr.write(chalk_1.default.red(` ${lines.join('\n ')}\n`));
600
+ process.stderr.write(chalk.red(` ${lines.join('\n ')}\n`));
589
601
  }
590
602
  else if (success && output && output.length > 0) {
591
603
  const brief = output.split('\n')[0].slice(0, 120);
592
- process.stderr.write(chalk_1.default.gray(` ${brief}${output.length > 120 ? '…' : ''}\n`));
604
+ process.stderr.write(chalk.gray(` ${brief}${output.length > 120 ? '…' : ''}\n`));
593
605
  }
594
606
  spinner.start();
595
607
  spinner.text = 'Next step...';
@@ -597,10 +609,10 @@ class ChatCommand {
597
609
  }
598
610
  if (event.type === 'thinking') {
599
611
  this.v3IterationCount += 1;
600
- const iterText = event.content || '';
612
+ const iterText = this.sanitizeServerPath(event.content || '');
601
613
  if (spinner.isSpinning)
602
614
  spinner.stop();
603
- process.stderr.write(chalk_1.default.cyan(`\n── ${iterText || `Iteration ${this.v3IterationCount}`} ──\n`));
615
+ process.stderr.write(chalk.cyan(`\n── ${iterText || `Iteration ${this.v3IterationCount}`} ──\n`));
604
616
  spinner.start();
605
617
  spinner.text = 'Analyzing...';
606
618
  return;
@@ -614,7 +626,7 @@ class ChatCommand {
614
626
  this.writeV3StreamText(spinner, text);
615
627
  }
616
628
  else {
617
- spinner.text = chalk_1.default.cyan('[Response] ') + 'Writing response...';
629
+ spinner.text = chalk.cyan('[Response] ') + 'Writing response...';
618
630
  }
619
631
  return;
620
632
  }
@@ -632,53 +644,53 @@ class ChatCommand {
632
644
  const fail = event.tasks_failed ?? 0;
633
645
  statLine += ` — ${ok} tasks done`;
634
646
  if (fail > 0)
635
- statLine += chalk_1.default.yellow(`, ${fail} failed`);
647
+ statLine += chalk.yellow(`, ${fail} failed`);
636
648
  }
637
- process.stderr.write(chalk_1.default.green(`\n✓ Complete`) + ` — ${statLine}\n`);
649
+ process.stderr.write(chalk.green(`\n✓ Complete`) + ` — ${statLine}\n`);
638
650
  // Show seal quality score if available
639
651
  if (event.seal_score && typeof event.seal_score.overall === 'number') {
640
652
  const score = event.seal_score.overall;
641
653
  const tier = event.seal_score.tier || '';
642
- const scoreColor = score >= 7 ? chalk_1.default.green : score >= 5 ? chalk_1.default.yellow : chalk_1.default.red;
643
- process.stderr.write(chalk_1.default.cyan(' [Quality] ') + scoreColor(`${score}/10`) + (tier ? chalk_1.default.gray(` (${tier})`) : '') + '\n');
654
+ const scoreColor = score >= 7 ? chalk.green : score >= 5 ? chalk.yellow : chalk.red;
655
+ process.stderr.write(chalk.cyan(' [Quality] ') + scoreColor(`${score}/10`) + (tier ? chalk.gray(` (${tier})`) : '') + '\n');
644
656
  }
645
657
  return;
646
658
  }
647
659
  if (event.type === 'plan') {
648
660
  const plan = event.plan || {};
649
- const planKind = plan.task_kind || event.task_kind || '';
650
- const quality = plan.quality_profile || '';
661
+ const planKind = this.sanitizeServerPath(plan.task_kind || event.task_kind || '');
662
+ const quality = this.sanitizeServerPath(plan.quality_profile || '');
651
663
  const status = typeof plan.status === 'string' ? plan.status : '';
652
- const summary = typeof plan.summary === 'string' ? plan.summary : '';
664
+ const summary = typeof plan.summary === 'string' ? this.sanitizeServerPath(plan.summary) : '';
653
665
  if (spinner.isSpinning)
654
666
  spinner.stop();
655
- process.stderr.write(chalk_1.default.cyan(` [Plan] `) + `Task: ${planKind || 'analyzing'}`);
667
+ process.stderr.write(chalk.cyan(` [Plan] `) + `Task: ${planKind || 'analyzing'}`);
656
668
  if (quality)
657
- process.stderr.write(chalk_1.default.gray(` (${quality})`));
669
+ process.stderr.write(chalk.gray(` (${quality})`));
658
670
  process.stderr.write('\n');
659
671
  if (summary) {
660
- process.stderr.write(chalk_1.default.gray(` ${summary}\n`));
672
+ process.stderr.write(chalk.gray(` ${summary}\n`));
661
673
  }
662
674
  if (status === 'planning' && Number.isFinite(Number(plan.elapsed_seconds))) {
663
- process.stderr.write(chalk_1.default.gray(` elapsed: ${plan.elapsed_seconds}s\n`));
675
+ process.stderr.write(chalk.gray(` elapsed: ${plan.elapsed_seconds}s\n`));
664
676
  }
665
677
  if (Array.isArray(plan.tasks) && plan.tasks.length > 0) {
666
- process.stderr.write(chalk_1.default.gray(` ${plan.total_tasks || plan.tasks.length} tasks:\n`));
678
+ process.stderr.write(chalk.gray(` ${plan.total_tasks || plan.tasks.length} tasks:\n`));
667
679
  for (const t of plan.tasks.slice(0, 10)) {
668
- const targets = Array.isArray(t.targets) && t.targets.length ? ` → ${t.targets.join(', ')}` : '';
669
- process.stderr.write(chalk_1.default.gray(` • ${t.title || t.id}${targets}\n`));
680
+ const targets = Array.isArray(t.targets) && t.targets.length ? ` → ${t.targets.map((target) => this.sanitizeServerPath(String(target))).join(', ')}` : '';
681
+ process.stderr.write(chalk.gray(` • ${this.sanitizeServerPath(String(t.title || t.id))}${targets}\n`));
670
682
  }
671
683
  if (plan.tasks.length > 10) {
672
- process.stderr.write(chalk_1.default.gray(` ... and ${plan.tasks.length - 10} more\n`));
684
+ process.stderr.write(chalk.gray(` ... and ${plan.tasks.length - 10} more\n`));
673
685
  }
674
686
  }
675
687
  if (Array.isArray(plan.notes) && plan.notes.length > 0) {
676
688
  for (const note of plan.notes.slice(0, 3)) {
677
- process.stderr.write(chalk_1.default.gray(` note: ${note}\n`));
689
+ process.stderr.write(chalk.gray(` note: ${this.sanitizeServerPath(String(note))}\n`));
678
690
  }
679
691
  }
680
692
  if (Array.isArray(plan.target_files) && plan.target_files.length > 0) {
681
- process.stderr.write(chalk_1.default.gray(` Files: ${plan.target_files.join(', ')}\n`));
693
+ process.stderr.write(chalk.gray(` Files: ${plan.target_files.map((filePath) => this.sanitizeServerPath(String(filePath))).join(', ')}\n`));
682
694
  }
683
695
  spinner.start();
684
696
  spinner.text = status === 'planning' ? 'Planning...' : 'Executing plan...';
@@ -687,7 +699,7 @@ class ChatCommand {
687
699
  if (event.type === 'executor_start') {
688
700
  if (spinner.isSpinning)
689
701
  spinner.stop();
690
- process.stderr.write(chalk_1.default.cyan(' [Executor] ') + `Starting ${event.task_id || 'task'}${event.title ? ` - ${event.title}` : ''}
702
+ process.stderr.write(chalk.cyan(' [Executor] ') + `Starting ${this.sanitizeServerPath(String(event.task_id || 'task'))}${event.title ? ` - ${this.sanitizeServerPath(String(event.title))}` : ''}
691
703
  `);
692
704
  spinner.start();
693
705
  spinner.text = 'Vigthoria Executor running...';
@@ -696,8 +708,8 @@ class ChatCommand {
696
708
  if (event.type === 'executor_error') {
697
709
  if (spinner.isSpinning)
698
710
  spinner.stop();
699
- const msg = (0, api_js_1.sanitizeUserFacingErrorText)(String(event.error || 'Executor error')) || 'Executor error';
700
- process.stderr.write(chalk_1.default.red(' [Executor] ') + `Vigthoria Executor encountered an issue: ${msg}
711
+ const msg = sanitizeUserFacingErrorText(String(event.error || 'Executor error')) || 'Executor error';
712
+ process.stderr.write(chalk.red(' [Executor] ') + `Vigthoria Executor encountered an issue: ${msg}
701
713
  `);
702
714
  spinner.start();
703
715
  spinner.text = 'Recovering executor...';
@@ -710,11 +722,11 @@ class ChatCommand {
710
722
  const status = String(summary.status || 'completed');
711
723
  const changed = Array.isArray(summary.changed_files) ? summary.changed_files.length : 0;
712
724
  if (status === 'failed') {
713
- process.stderr.write(chalk_1.default.red(' [Executor] ') + `Vigthoria Executor task failed${summary.task_id ? ` (${summary.task_id})` : ''}.
725
+ process.stderr.write(chalk.red(' [Executor] ') + `Vigthoria Executor task failed${summary.task_id ? ` (${summary.task_id})` : ''}.
714
726
  `);
715
727
  }
716
728
  else {
717
- process.stderr.write(chalk_1.default.green(' [Executor] ') + `Task completed${summary.task_id ? ` (${summary.task_id})` : ''}${changed ? `, ${changed} files changed` : ''}.
729
+ process.stderr.write(chalk.green(' [Executor] ') + `Task completed${summary.task_id ? ` (${summary.task_id})` : ''}${changed ? `, ${changed} files changed` : ''}.
718
730
  `);
719
731
  }
720
732
  spinner.start();
@@ -724,11 +736,11 @@ class ChatCommand {
724
736
  if (event.type === 'file_mutation') {
725
737
  const rawPath = typeof event.path === 'string' ? this.sanitizeServerPath(event.path) : '';
726
738
  const filePath = rawPath ? rawPath.replace(/\\/g, '/').split('/').slice(-2).join('/') : '';
727
- const action = event.action === 'delete' ? chalk_1.default.red('deleted') : chalk_1.default.green('wrote');
739
+ const action = event.action === 'delete' ? chalk.red('deleted') : chalk.green('wrote');
728
740
  if (filePath) {
729
741
  if (spinner.isSpinning)
730
742
  spinner.stop();
731
- process.stderr.write(chalk_1.default.cyan(' [File] ') + `${action} ${filePath}\n`);
743
+ process.stderr.write(chalk.cyan(' [File] ') + `${action} ${filePath}\n`);
732
744
  spinner.start();
733
745
  }
734
746
  return;
@@ -737,23 +749,23 @@ class ChatCommand {
737
749
  if (event.checkpointed) {
738
750
  if (spinner.isSpinning)
739
751
  spinner.stop();
740
- process.stderr.write(chalk_1.default.yellow(' [Checkpoint] ') + 'Budget reached — auto-continuing...\n');
752
+ process.stderr.write(chalk.yellow(' [Checkpoint] ') + 'Budget reached — auto-continuing...\n');
741
753
  spinner.start();
742
754
  }
743
755
  else {
744
756
  if (spinner.isSpinning)
745
757
  spinner.stop();
746
- const message = (0, api_js_1.sanitizeUserFacingErrorText)(String(event.message || 'Agent error')) || 'Agent error';
758
+ const message = sanitizeUserFacingErrorText(String(event.message || 'Agent error')) || 'Agent error';
747
759
  const plannerLike = /plan|planner|dependency graph/i.test(message);
748
760
  const executorLike = /executor|task failed|iteration/i.test(message);
749
761
  if (plannerLike) {
750
- process.stderr.write(chalk_1.default.red(' [Planner] ') + `Vigthoria Planner encountered an issue: ${message}\n`);
762
+ process.stderr.write(chalk.red(' [Planner] ') + `Vigthoria Planner encountered an issue: ${this.sanitizeServerPath(message)}\n`);
751
763
  }
752
764
  else if (executorLike) {
753
- process.stderr.write(chalk_1.default.red(' [Executor] ') + `Vigthoria Executor encountered an issue: ${message}\n`);
765
+ process.stderr.write(chalk.red(' [Executor] ') + `Vigthoria Executor encountered an issue: ${this.sanitizeServerPath(message)}\n`);
754
766
  }
755
767
  else {
756
- process.stderr.write(chalk_1.default.red(' [Error] ') + message + '\n');
768
+ process.stderr.write(chalk.red(' [Error] ') + this.sanitizeServerPath(message) + '\n');
757
769
  }
758
770
  }
759
771
  return;
@@ -761,7 +773,7 @@ class ChatCommand {
761
773
  if (event.type === 'context') {
762
774
  if (spinner.isSpinning)
763
775
  spinner.stop();
764
- process.stderr.write(chalk_1.default.cyan(' [Context] ') + 'Workspace bound\n');
776
+ process.stderr.write(chalk.cyan(' [Context] ') + 'Workspace bound\n');
765
777
  spinner.start();
766
778
  spinner.text = 'Starting agent...';
767
779
  return;
@@ -771,7 +783,7 @@ class ChatCommand {
771
783
  this.v3ToolCallCount = 0;
772
784
  if (spinner.isSpinning)
773
785
  spinner.stop();
774
- process.stderr.write(chalk_1.default.cyan(' [Start] ') + 'Agent initialized\n');
786
+ process.stderr.write(chalk.cyan(' [Start] ') + 'Agent initialized\n');
775
787
  spinner.start();
776
788
  spinner.text = 'Working...';
777
789
  }
@@ -783,7 +795,7 @@ class ChatCommand {
783
795
  if (event.type === 'started') {
784
796
  if (spinner.isSpinning)
785
797
  spinner.stop();
786
- process.stderr.write(chalk_1.default.cyan(' [Operator] ') + 'Starting BMAD workflow...\n');
798
+ process.stderr.write(chalk.cyan(' [Operator] ') + 'Starting BMAD workflow...\n');
787
799
  spinner.start();
788
800
  spinner.text = 'Connecting...';
789
801
  return;
@@ -791,7 +803,7 @@ class ChatCommand {
791
803
  if (event.type === 'connected') {
792
804
  if (spinner.isSpinning)
793
805
  spinner.stop();
794
- process.stderr.write(chalk_1.default.green(' ✓') + ' Connected to BMAD stream\n');
806
+ process.stderr.write(chalk.green(' ✓') + ' Connected to BMAD stream\n');
795
807
  spinner.start();
796
808
  spinner.text = 'Working...';
797
809
  return;
@@ -800,7 +812,7 @@ class ChatCommand {
800
812
  const agentName = event.agent || 'BMAD agent';
801
813
  if (spinner.isSpinning)
802
814
  spinner.stop();
803
- process.stderr.write(chalk_1.default.cyan(` [Agent] `) + agentName + '\n');
815
+ process.stderr.write(chalk.cyan(` [Agent] `) + agentName + '\n');
804
816
  spinner.start();
805
817
  spinner.text = `Running ${agentName}...`;
806
818
  return;
@@ -810,7 +822,7 @@ class ChatCommand {
810
822
  const statusText = `${event.status || 'Working'}${progress}`;
811
823
  if (spinner.isSpinning)
812
824
  spinner.stop();
813
- process.stderr.write(chalk_1.default.cyan(' [Status] ') + statusText + '\n');
825
+ process.stderr.write(chalk.cyan(' [Status] ') + statusText + '\n');
814
826
  spinner.start();
815
827
  spinner.text = statusText;
816
828
  return;
@@ -818,15 +830,15 @@ class ChatCommand {
818
830
  if (event.type === 'result') {
819
831
  if (spinner.isSpinning)
820
832
  spinner.stop();
821
- process.stderr.write(chalk_1.default.green('\n ✓ Operator workflow complete\n'));
833
+ process.stderr.write(chalk.green('\n ✓ Operator workflow complete\n'));
822
834
  return;
823
835
  }
824
836
  }
825
837
  constructor(config, logger) {
826
838
  this.config = config;
827
839
  this.logger = logger;
828
- this.api = new api_js_1.APIClient(config, logger);
829
- this.sessionManager = new session_js_1.SessionManager();
840
+ this.api = new APIClient(config, logger);
841
+ this.sessionManager = new SessionManager();
830
842
  }
831
843
  async run(options) {
832
844
  if (!this.config.isAuthenticated()) {
@@ -857,11 +869,11 @@ class ChatCommand {
857
869
  this.ensureProjectWorkspace();
858
870
  this.directPromptMode = Boolean(options.prompt);
859
871
  this.directToolContinuationCount = 0;
860
- this.tools = new tools_js_1.AgenticTools(this.logger, this.currentProjectPath, async (action) => this.requestPermission(action), this.autoApprove);
872
+ this.tools = new AgenticTools(this.logger, this.currentProjectPath, async (action) => this.requestPermission(action), this.autoApprove);
861
873
  this.initializeSession(options.resume === true);
862
874
  // ── Commando Bridge: connect if --bridge was specified ──────────
863
875
  if (options.bridge) {
864
- const bridgeClient = new bridge_client_js_1.BridgeClient({
876
+ const bridgeClient = new BridgeClient({
865
877
  bridgeUrl: options.bridge,
866
878
  apiKey: this.config.get('authToken'),
867
879
  onAdminCommand: (cmd) => this.handleAdminCommand(cmd),
@@ -896,7 +908,7 @@ class ChatCommand {
896
908
  const timeoutId = options.bridge && bridgePromptTimeoutMs > 0
897
909
  ? setTimeout(() => {
898
910
  timedOut = true;
899
- const b = (0, bridge_client_js_1.getBridgeClient)();
911
+ const b = getBridgeClient();
900
912
  if (b) {
901
913
  b.emitEnd({ reason: 'timeout' });
902
914
  b.destroy();
@@ -911,7 +923,7 @@ class ChatCommand {
911
923
  if (timeoutId)
912
924
  clearTimeout(timeoutId);
913
925
  if (!timedOut) {
914
- const bridge = (0, bridge_client_js_1.getBridgeClient)();
926
+ const bridge = getBridgeClient();
915
927
  if (bridge) {
916
928
  bridge.emitEnd({ reason: 'prompt-complete' });
917
929
  bridge.destroy();
@@ -920,7 +932,7 @@ class ChatCommand {
920
932
  return;
921
933
  }
922
934
  await this.startInteractiveChat();
923
- const bridge = (0, bridge_client_js_1.getBridgeClient)();
935
+ const bridge = getBridgeClient();
924
936
  if (bridge) {
925
937
  bridge.emitEnd({ reason: 'interactive-exit' });
926
938
  bridge.destroy();
@@ -930,24 +942,24 @@ class ChatCommand {
930
942
  handleAdminCommand(cmd) {
931
943
  switch (cmd.action) {
932
944
  case 'ping':
933
- (0, bridge_client_js_1.getBridgeClient)()?.emitModelResponse({ model: this.currentModel, chars: 0, hasToolCalls: false, preview: 'pong' });
945
+ getBridgeClient()?.emitModelResponse({ model: this.currentModel, chars: 0, hasToolCalls: false, preview: 'pong' });
934
946
  break;
935
947
  case 'set-model':
936
948
  if (cmd.params?.value && typeof cmd.params.value === 'string') {
937
949
  this.currentModel = cmd.params.value;
938
- (0, bridge_client_js_1.getBridgeClient)()?.emitModeChange({ mode: this.operatorMode ? 'operator' : this.agentMode ? 'agent' : 'chat', model: this.currentModel });
950
+ getBridgeClient()?.emitModeChange({ mode: this.operatorMode ? 'operator' : this.agentMode ? 'agent' : 'chat', model: this.currentModel });
939
951
  if (!this.jsonOutput)
940
- console.log(chalk_1.default.yellow(`[bridge] Model changed to: ${this.currentModel}`));
952
+ console.log(chalk.yellow(`[bridge] Model changed to: ${this.currentModel}`));
941
953
  }
942
954
  break;
943
955
  case 'abort':
944
956
  if (!this.jsonOutput)
945
- console.log(chalk_1.default.red(`[bridge] Abort requested by admin`));
946
- (0, bridge_client_js_1.getBridgeClient)()?.emitEnd({ reason: 'admin-abort' });
957
+ console.log(chalk.red(`[bridge] Abort requested by admin`));
958
+ getBridgeClient()?.emitEnd({ reason: 'admin-abort' });
947
959
  process.exit(0);
948
960
  break;
949
961
  default:
950
- (0, bridge_client_js_1.getBridgeClient)()?.emitError({ message: `Unknown admin command: ${cmd.action}` });
962
+ getBridgeClient()?.emitError({ message: `Unknown admin command: ${cmd.action}` });
951
963
  }
952
964
  }
953
965
  ensureProjectWorkspace() {
@@ -1061,6 +1073,7 @@ class ChatCommand {
1061
1073
  return path.join(rootPath, `${folderName}-${Date.now()}`);
1062
1074
  }
1063
1075
  initializeSession(resume) {
1076
+ this.projectMemory = new ProjectMemoryService(this.currentProjectPath);
1064
1077
  if (resume) {
1065
1078
  this.currentSession = this.sessionManager.getLatest(this.currentProjectPath);
1066
1079
  if (this.currentSession) {
@@ -1106,11 +1119,11 @@ class ChatCommand {
1106
1119
  // Suppress all setup banners in direct-prompt mode so only the final
1107
1120
  // answer reaches stdout. Interactive (REPL) mode still shows them.
1108
1121
  if (!this.jsonOutput) {
1109
- console.log(chalk_1.default.cyan('Running single prompt in direct mode.'));
1110
- console.log(chalk_1.default.gray(`Model: ${this.currentModel}`));
1111
- console.log(chalk_1.default.gray(`Project: ${this.currentProjectPath}`));
1122
+ console.log(chalk.cyan('Running single prompt in direct mode.'));
1123
+ console.log(chalk.gray(`Model: ${this.currentModel}`));
1124
+ console.log(chalk.gray(`Project: ${this.currentProjectPath}`));
1112
1125
  if (this.workflowTarget) {
1113
- console.log(chalk_1.default.gray(`Workflow target: ${this.workflowTarget}`));
1126
+ console.log(chalk.gray(`Workflow target: ${this.workflowTarget}`));
1114
1127
  }
1115
1128
  console.log();
1116
1129
  }
@@ -1174,7 +1187,7 @@ class ChatCommand {
1174
1187
  const runtimeContext = await this.getPromptRuntimeContext(prompt);
1175
1188
  const resolvedWorkflow = await this.callApi('Resolve VigFlow workflow', () => this.api.resolveVigFlowWorkflow(selector));
1176
1189
  const invocationMode = this.operatorMode ? 'operator' : this.agentMode ? 'agent' : 'chat';
1177
- const spinner = this.jsonOutput ? null : (0, logger_js_1.createSpinner)({ text: `Running workflow ${resolvedWorkflow.name}...`, spinner: 'clock' }).start();
1190
+ const spinner = this.jsonOutput ? null : createSpinner({ text: `Running workflow ${resolvedWorkflow.name}...`, spinner: 'clock' }).start();
1178
1191
  try {
1179
1192
  const execution = await this.callApi('Run VigFlow workflow', () => this.api.runVigFlowWorkflow(resolvedWorkflow.id, {
1180
1193
  data: {
@@ -1223,9 +1236,9 @@ class ChatCommand {
1223
1236
  return;
1224
1237
  }
1225
1238
  this.logger.success(`Workflow target ${resolvedWorkflow.name} ${execution.status}`);
1226
- console.log(chalk_1.default.gray(`Workflow ID: ${resolvedWorkflow.id}`));
1227
- console.log(chalk_1.default.gray(`Execution ID: ${execution.executionId}`));
1228
- console.log(chalk_1.default.gray(`Mode: ${invocationMode}`));
1239
+ console.log(chalk.gray(`Workflow ID: ${resolvedWorkflow.id}`));
1240
+ console.log(chalk.gray(`Execution ID: ${execution.executionId}`));
1241
+ console.log(chalk.gray(`Mode: ${invocationMode}`));
1229
1242
  if (content) {
1230
1243
  console.log(content);
1231
1244
  }
@@ -1268,12 +1281,13 @@ class ChatCommand {
1268
1281
  await this.runLocalAgentLoop(prompt);
1269
1282
  return;
1270
1283
  }
1271
- (0, bridge_client_js_1.getBridgeClient)()?.emitPrompt({ prompt, mode: 'operator', model: this.currentModel });
1284
+ getBridgeClient()?.emitPrompt({ prompt, mode: 'operator', model: this.currentModel });
1272
1285
  const runtimeContext = await this.getPromptRuntimeContext(prompt);
1273
- const spinner = this.jsonOutput ? null : (0, logger_js_1.createSpinner)({ text: 'Thinking like an operator...', spinner: 'clock' }).start();
1286
+ const spinner = this.jsonOutput ? null : createSpinner({ text: 'Thinking like an operator...', spinner: 'clock' }).start();
1274
1287
  const workflowType = 'full';
1275
1288
  const executionPrompt = this.buildExecutionPrompt(prompt);
1276
1289
  try {
1290
+ this.rememberBrainEvent('task', `GoA operator workflow started for prompt: ${prompt.slice(0, 220)}`, 'operator');
1277
1291
  const response = await this.callApi('Run operator workflow', () => this.api.runOperatorWorkflow(executionPrompt, {
1278
1292
  workspacePath: this.currentProjectPath,
1279
1293
  projectPath: this.currentProjectPath,
@@ -1297,7 +1311,7 @@ class ChatCommand {
1297
1311
  const isPolicyAck = /^(i will follow|i understand|i('ll| will) adhere|understood[.,!]|sure[.,!]|provide your|waiting for|i('ll| will) proceed)/i.test(responseText)
1298
1312
  || (responseText.length < 200 && /follow the instructions|next instruction|ready to assist/i.test(responseText));
1299
1313
  if (isPolicyAck) {
1300
- throw new api_js_1.CLIError('Operator workflow returned a non-actionable acknowledgement instead of a grounded result.', 'model_backend');
1314
+ throw new CLIError('Operator workflow returned a non-actionable acknowledgement instead of a grounded result.', 'model_backend');
1301
1315
  }
1302
1316
  if (this.jsonOutput) {
1303
1317
  console.log(JSON.stringify({
@@ -1313,9 +1327,10 @@ class ChatCommand {
1313
1327
  else {
1314
1328
  console.log(response.content || 'Operator workflow completed.');
1315
1329
  if (response.savedWorkflow?.id) {
1316
- console.log(chalk_1.default.gray(`Saved VigFlow workflow: ${response.savedWorkflow.id}${response.savedWorkflow.name ? ` (${response.savedWorkflow.name})` : ''}`));
1330
+ console.log(chalk.gray(`Saved VigFlow workflow: ${response.savedWorkflow.id}${response.savedWorkflow.name ? ` (${response.savedWorkflow.name})` : ''}`));
1317
1331
  }
1318
1332
  }
1333
+ this.rememberBrainEvent('validation', `GoA operator workflow completed${response.workflowId ? ` workflow ${response.workflowId}` : ''}${response.savedWorkflow?.id ? ` saved VigFlow ${response.savedWorkflow.id}` : ''}.`, 'operator');
1319
1334
  this.messages.push({ role: 'assistant', content: response.content || 'Operator workflow completed.' });
1320
1335
  this.saveSession();
1321
1336
  }
@@ -1323,11 +1338,12 @@ class ChatCommand {
1323
1338
  if (spinner) {
1324
1339
  spinner.stop();
1325
1340
  }
1326
- const cliErr = error instanceof api_js_1.CLIError ? error : (0, api_js_1.classifyError)(error);
1327
- const errorMsg = (0, api_js_1.formatCLIError)(cliErr);
1341
+ const cliErr = error instanceof CLIError ? error : classifyError(error);
1342
+ const errorMsg = formatCLIError(cliErr);
1328
1343
  if (!this.jsonOutput) {
1329
1344
  this.logger.error('Operator workflow failed');
1330
1345
  }
1346
+ this.rememberBrainEvent('issue', `GoA operator workflow failed: ${errorMsg}`, 'operator');
1331
1347
  if (this.jsonOutput) {
1332
1348
  process.exitCode = 1;
1333
1349
  console.log(JSON.stringify({
@@ -1350,8 +1366,8 @@ class ChatCommand {
1350
1366
  * BMAD orchestrator to scan the workspace.
1351
1367
  */
1352
1368
  async runOperatorDirectAnswer(prompt) {
1353
- (0, bridge_client_js_1.getBridgeClient)()?.emitPrompt({ prompt, mode: 'operator-direct', model: this.currentModel });
1354
- const spinner = this.jsonOutput ? null : (0, logger_js_1.createSpinner)({ text: 'Operator (direct)...', spinner: 'clock' }).start();
1369
+ getBridgeClient()?.emitPrompt({ prompt, mode: 'operator-direct', model: this.currentModel });
1370
+ const spinner = this.jsonOutput ? null : createSpinner({ text: 'Operator (direct)...', spinner: 'clock' }).start();
1355
1371
  try {
1356
1372
  let operatorGrounding = [
1357
1373
  'You are Vigthoria Operator, a DevOps and infrastructure analysis assistant.',
@@ -1383,7 +1399,7 @@ class ChatCommand {
1383
1399
  const isPolicyAck = /^(i will follow|i understand|i('ll| will) adhere|understood[.,!]|sure[.,!]|provide your|waiting for|i('ll| will) proceed)/i.test(content)
1384
1400
  || (content.length < 200 && /follow the instructions|next instruction|ready to assist/i.test(content));
1385
1401
  if (isPolicyAck || !content) {
1386
- throw new api_js_1.CLIError('Operator returned a non-actionable acknowledgement instead of a grounded result.', 'model_backend');
1402
+ throw new CLIError('Operator returned a non-actionable acknowledgement instead of a grounded result.', 'model_backend');
1387
1403
  }
1388
1404
  if (this.jsonOutput) {
1389
1405
  console.log(JSON.stringify({
@@ -1396,14 +1412,15 @@ class ChatCommand {
1396
1412
  else {
1397
1413
  console.log(content);
1398
1414
  }
1415
+ this.rememberBrainEvent('validation', `GoA operator direct answer completed for prompt: ${prompt.slice(0, 220)}`, 'operator');
1399
1416
  this.messages.push({ role: 'assistant', content });
1400
1417
  this.saveSession();
1401
1418
  }
1402
1419
  catch (error) {
1403
1420
  if (spinner)
1404
1421
  spinner.stop();
1405
- const cliErr = error instanceof api_js_1.CLIError ? error : (0, api_js_1.classifyError)(error);
1406
- const errorMsg = (0, api_js_1.formatCLIError)(cliErr);
1422
+ const cliErr = error instanceof CLIError ? error : classifyError(error);
1423
+ const errorMsg = formatCLIError(cliErr);
1407
1424
  if (this.jsonOutput) {
1408
1425
  process.exitCode = 1;
1409
1426
  console.log(JSON.stringify({
@@ -1475,8 +1492,8 @@ class ChatCommand {
1475
1492
  }
1476
1493
  }
1477
1494
  this.messages.push({ role: 'user', content: this.buildExecutionPrompt(prompt) });
1478
- (0, bridge_client_js_1.getBridgeClient)()?.emitPrompt({ prompt, mode: 'chat', model: this.currentModel });
1479
- const spinner = this.jsonOutput ? null : (0, logger_js_1.createSpinner)({ text: 'Thinking...', spinner: 'clock' }).start();
1495
+ getBridgeClient()?.emitPrompt({ prompt, mode: 'chat', model: this.currentModel });
1496
+ const spinner = this.jsonOutput ? null : createSpinner({ text: 'Thinking...', spinner: 'clock' }).start();
1480
1497
  try {
1481
1498
  const response = await this.callApi('Send chat message', () => this.api.chat(this.getMessagesForModel(), this.currentModel));
1482
1499
  if (spinner)
@@ -1500,7 +1517,7 @@ class ChatCommand {
1500
1517
  console.log(finalText);
1501
1518
  }
1502
1519
  else {
1503
- console.log(chalk_1.default.yellow('The model returned an empty response. Try rephrasing your question, or use --agent mode for grounded repo analysis.'));
1520
+ console.log(chalk.yellow('The model returned an empty response. Try rephrasing your question, or use --agent mode for grounded repo analysis.'));
1504
1521
  }
1505
1522
  this.messages.push({ role: 'assistant', content: response.message || '' });
1506
1523
  this.saveSession();
@@ -1508,8 +1525,8 @@ class ChatCommand {
1508
1525
  catch (error) {
1509
1526
  if (spinner)
1510
1527
  spinner.stop();
1511
- const cliErr = error instanceof api_js_1.CLIError ? error : (0, api_js_1.classifyError)(error);
1512
- const errorMsg = (0, api_js_1.formatCLIError)(cliErr);
1528
+ const cliErr = error instanceof CLIError ? error : classifyError(error);
1529
+ const errorMsg = formatCLIError(cliErr);
1513
1530
  if (this.jsonOutput) {
1514
1531
  process.exitCode = 1;
1515
1532
  console.log(JSON.stringify({
@@ -1562,13 +1579,13 @@ class ChatCommand {
1562
1579
  this.directToolContinuationCount = 0;
1563
1580
  this.agentToolEvidence = { discovery: 0, mutation: 0, searchFailed: 0 };
1564
1581
  this.tools.clearSessionApprovals();
1565
- (0, bridge_client_js_1.getBridgeClient)()?.emitPrompt({ prompt, mode: this.operatorMode ? 'operator' : 'agent', model: this.currentModel });
1582
+ getBridgeClient()?.emitPrompt({ prompt, mode: this.operatorMode ? 'operator' : 'agent', model: this.currentModel });
1566
1583
  this.ensureAgentSystemPrompt();
1567
1584
  this.messages.push({ role: 'user', content: this.buildScopedUserPrompt(prompt) });
1568
1585
  this.saveSession();
1569
1586
  const maxTurns = 10;
1570
1587
  for (let turn = 0; turn < maxTurns; turn += 1) {
1571
- const spinner = this.jsonOutput ? null : (0, logger_js_1.createSpinner)({ text: turn === 0 ? 'Planning...' : 'Continuing...', spinner: 'clock' }).start();
1588
+ const spinner = this.jsonOutput ? null : createSpinner({ text: turn === 0 ? 'Planning...' : 'Continuing...', spinner: 'clock' }).start();
1572
1589
  let response;
1573
1590
  try {
1574
1591
  response = await this.callApi('Send agent chat message', () => this.api.chat(this.getMessagesForModel(), this.currentModel), 0);
@@ -1619,7 +1636,7 @@ class ChatCommand {
1619
1636
  this.messages.push({ role: 'assistant', content: assistantMessage });
1620
1637
  const toolCalls = this.extractToolCalls(assistantMessage);
1621
1638
  const visibleText = this.stripToolPayloads(assistantMessage).trim();
1622
- (0, bridge_client_js_1.getBridgeClient)()?.emitModelResponse({
1639
+ getBridgeClient()?.emitModelResponse({
1623
1640
  model: this.currentModel,
1624
1641
  chars: assistantMessage.length,
1625
1642
  hasToolCalls: toolCalls.length > 0,
@@ -1700,8 +1717,9 @@ class ChatCommand {
1700
1717
  catch (error) {
1701
1718
  if (spinner)
1702
1719
  spinner.stop();
1703
- const cliErr = error instanceof api_js_1.CLIError ? error : (0, api_js_1.classifyError)(error);
1704
- const errorMsg = (0, api_js_1.formatCLIError)(cliErr);
1720
+ const cliErr = error instanceof CLIError ? error : classifyError(error);
1721
+ const errorMsg = formatCLIError(cliErr);
1722
+ this.rememberBrainEvent('issue', `Agent turn failed: ${errorMsg}`, 'agent');
1705
1723
  if (this.jsonOutput) {
1706
1724
  process.exitCode = 1;
1707
1725
  console.log(JSON.stringify({
@@ -1758,12 +1776,12 @@ class ChatCommand {
1758
1776
  args: { path: targetFile },
1759
1777
  };
1760
1778
  if (!this.jsonOutput) {
1761
- console.log(chalk_1.default.cyan(`⚙ Executing: ${readCall.tool}`));
1779
+ console.log(chalk.cyan(`⚙ Executing: ${readCall.tool}`));
1762
1780
  }
1763
1781
  const readResult = await this.tools.execute(readCall);
1764
1782
  const readSummary = this.formatToolResult(readCall, readResult);
1765
1783
  if (!this.jsonOutput) {
1766
- console.log(readResult.success ? chalk_1.default.gray(readSummary) : chalk_1.default.red(readSummary));
1784
+ console.log(readResult.success ? chalk.gray(readSummary) : chalk.red(readSummary));
1767
1785
  }
1768
1786
  this.messages.push({ role: 'system', content: readSummary });
1769
1787
  if (!readResult.success || !readResult.output) {
@@ -1806,12 +1824,12 @@ class ChatCommand {
1806
1824
  },
1807
1825
  };
1808
1826
  if (!this.jsonOutput) {
1809
- console.log(chalk_1.default.cyan(`⚙ Executing: ${writeCall.tool}`));
1827
+ console.log(chalk.cyan(`⚙ Executing: ${writeCall.tool}`));
1810
1828
  }
1811
1829
  const writeResult = await this.tools.execute(writeCall);
1812
1830
  const writeSummary = this.formatToolResult(writeCall, writeResult);
1813
1831
  if (!this.jsonOutput) {
1814
- console.log(writeResult.success ? chalk_1.default.gray(writeSummary) : chalk_1.default.red(writeSummary));
1832
+ console.log(writeResult.success ? chalk.gray(writeSummary) : chalk.red(writeSummary));
1815
1833
  }
1816
1834
  this.messages.push({ role: 'system', content: writeSummary });
1817
1835
  if (!writeResult.success) {
@@ -1847,31 +1865,77 @@ class ChatCommand {
1847
1865
  console.log(`Updated ${targetFile}.`);
1848
1866
  if (previewGate.required) {
1849
1867
  if (previewGate.passed) {
1850
- console.log(chalk_1.default.gray(`Template Service preview gate: passed via ${previewGate.backendUrl || 'unknown backend'}`));
1868
+ console.log(chalk.gray(`Template Service preview gate: passed via ${previewGate.backendUrl || 'unknown backend'}`));
1851
1869
  }
1852
1870
  else {
1853
- console.log(chalk_1.default.yellow(`Template Service preview gate: failed${previewGate.error ? ` - ${previewGate.error}` : ''}`));
1871
+ console.log(chalk.yellow(`Template Service preview gate: failed${previewGate.error ? ` - ${previewGate.error}` : ''}`));
1854
1872
  }
1855
1873
  }
1856
1874
  }
1857
1875
  return true;
1858
1876
  }
1877
+ isConfirmationFollowUp(prompt) {
1878
+ const normalized = prompt.trim().toLowerCase().replace(/[.!?]+$/g, '').replace(/\s+/g, ' ');
1879
+ 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);
1880
+ }
1881
+ getPreviousActionablePrompt() {
1882
+ if (this.lastActionableUserInput && !this.isConfirmationFollowUp(this.lastActionableUserInput)) {
1883
+ return this.lastActionableUserInput;
1884
+ }
1885
+ for (let i = this.messages.length - 1; i >= 0; i -= 1) {
1886
+ const message = this.messages[i];
1887
+ if (message.role !== 'user')
1888
+ continue;
1889
+ const content = (message.content || '').trim();
1890
+ if (!content || this.isConfirmationFollowUp(content))
1891
+ continue;
1892
+ return content
1893
+ .replace(/\n\nProject root:[\s\S]*$/i, '')
1894
+ .replace(/\n\nStay within this project root[\s\S]*$/i, '')
1895
+ .trim();
1896
+ }
1897
+ return '';
1898
+ }
1899
+ buildContextualAgentPrompt(prompt) {
1900
+ if (!this.isConfirmationFollowUp(prompt)) {
1901
+ return prompt;
1902
+ }
1903
+ const previousPrompt = this.getPreviousActionablePrompt();
1904
+ if (!previousPrompt) {
1905
+ return prompt;
1906
+ }
1907
+ return [
1908
+ 'The user confirmed the previous agent task. Continue and execute that original task now.',
1909
+ '',
1910
+ 'Original task:',
1911
+ previousPrompt,
1912
+ '',
1913
+ `Latest confirmation: ${prompt}`,
1914
+ '',
1915
+ 'Do not reinterpret this confirmation as a new website, landing page, template, or index.html task.',
1916
+ ].join('\n');
1917
+ }
1859
1918
  async tryV3AgentWorkflow(prompt) {
1860
- const runtimeContext = await this.getPromptRuntimeContext(prompt);
1861
- const routingPolicy = this.resolveAgentExecutionPolicy(prompt);
1919
+ const contextualPrompt = this.buildContextualAgentPrompt(prompt);
1920
+ if (contextualPrompt === prompt && !this.isConfirmationFollowUp(prompt)) {
1921
+ this.lastActionableUserInput = prompt;
1922
+ }
1923
+ this.messages.push({ role: 'user', content: contextualPrompt });
1924
+ const runtimeContext = await this.getPromptRuntimeContext(contextualPrompt);
1925
+ const routingPolicy = this.resolveAgentExecutionPolicy(contextualPrompt);
1862
1926
  // Reset streaming counters for new workflow
1863
1927
  this.v3IterationCount = 0;
1864
1928
  this.v3ToolCallCount = 0;
1865
1929
  this.v3LastActivity = Date.now();
1866
1930
  this.v3StreamingStarted = false;
1867
- const taskDisplay = new task_display_js_1.TaskDisplay(['Analyse workspace', 'Execute tasks', 'Validate output', 'Self-heal'], !this.jsonOutput);
1931
+ const taskDisplay = new TaskDisplay(['Analyse workspace', 'Execute tasks', 'Validate output', 'Self-heal'], !this.jsonOutput);
1868
1932
  taskDisplay.start(0);
1869
- const spinner = this.jsonOutput ? null : (0, logger_js_1.createSpinner)({
1933
+ const spinner = this.jsonOutput ? null : createSpinner({
1870
1934
  text: routingPolicy.cloudSelected ? 'Routing heavy task to Vigthoria Cloud...' : 'Routing to V3 Agent...',
1871
1935
  spinner: 'clock',
1872
1936
  }).start();
1873
- const executionPrompt = this.buildExecutionPrompt(prompt);
1874
- const agentTaskType = this.inferAgentTaskType(prompt);
1937
+ const executionPrompt = this.buildExecutionPrompt(contextualPrompt);
1938
+ const agentTaskType = this.inferAgentTaskType(contextualPrompt);
1875
1939
  const workspaceContext = {
1876
1940
  workspacePath: this.currentProjectPath,
1877
1941
  projectPath: this.currentProjectPath,
@@ -1881,7 +1945,7 @@ class ChatCommand {
1881
1945
  // Start workspace watcher for bidirectional real-time sync
1882
1946
  let watcher = null;
1883
1947
  if (this.currentProjectPath && fs.existsSync(this.currentProjectPath)) {
1884
- watcher = new workspace_stream_js_1.WorkspaceWatcher({
1948
+ watcher = new WorkspaceWatcher({
1885
1949
  workspaceRoot: this.currentProjectPath,
1886
1950
  onFileChange: (relativePath, content, action) => {
1887
1951
  this.logger.debug(`Local change detected: ${action} ${relativePath}`);
@@ -1905,6 +1969,7 @@ class ChatCommand {
1905
1969
  agentExecutionPolicy: routingPolicy,
1906
1970
  legacyFallbackAllowed: this.isLegacyAgentFallbackAllowed(),
1907
1971
  rawPrompt: prompt,
1972
+ contextualPrompt,
1908
1973
  history: this.getMessagesForModel(),
1909
1974
  ...runtimeContext,
1910
1975
  onStreamEvent: (event) => {
@@ -1967,7 +2032,7 @@ class ChatCommand {
1967
2032
  return true;
1968
2033
  }
1969
2034
  if (!this.jsonOutput && previewGate?.required && previewGate?.passed !== true && workspaceHasOutput) {
1970
- 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}` : '.'}`));
2035
+ console.log(chalk.yellow(`Template Service preview gate did not fully validate this output, but generated workspace files were preserved${previewGate?.error ? `: ${previewGate.error}` : '.'}`));
1971
2036
  }
1972
2037
  if (this.jsonOutput) {
1973
2038
  console.log(JSON.stringify({
@@ -1985,41 +2050,41 @@ class ChatCommand {
1985
2050
  else if (this.v3StreamingStarted) {
1986
2051
  // Content was already streamed to stdout in real-time; skip duplicate print.
1987
2052
  if (!this.jsonOutput) {
1988
- console.log(chalk_1.default.gray(`Agent routing: ${routingPolicy.cloudSelected ? 'Vigthoria Cloud' : 'V3 Agent'}`));
2053
+ console.log(chalk.gray(`Agent routing: ${routingPolicy.cloudSelected ? 'Vigthoria Cloud' : 'V3 Agent'}`));
1989
2054
  }
1990
2055
  }
1991
2056
  else if (response.content) {
1992
2057
  if (!this.directPromptMode) {
1993
- console.log(chalk_1.default.gray(`Agent routing: ${routingPolicy.cloudSelected ? 'Vigthoria Cloud' : 'V3 Agent'}`));
2058
+ console.log(chalk.gray(`Agent routing: ${routingPolicy.cloudSelected ? 'Vigthoria Cloud' : 'V3 Agent'}`));
1994
2059
  }
1995
2060
  console.log(response.content);
1996
2061
  }
1997
2062
  else {
1998
2063
  if (!this.directPromptMode) {
1999
- console.log(chalk_1.default.gray(`Agent routing: ${routingPolicy.cloudSelected ? 'Vigthoria Cloud' : 'V3 Agent'}`));
2064
+ console.log(chalk.gray(`Agent routing: ${routingPolicy.cloudSelected ? 'Vigthoria Cloud' : 'V3 Agent'}`));
2000
2065
  }
2001
2066
  console.log('V3 agent workflow completed.');
2002
2067
  }
2003
2068
  if (!this.jsonOutput && previewGate?.required) {
2004
2069
  if (previewGate.passed) {
2005
- console.log(chalk_1.default.gray(`Template Service preview gate: passed via ${previewGate.backendUrl || 'unknown backend'}`));
2070
+ console.log(chalk.gray(`Template Service preview gate: passed via ${previewGate.backendUrl || 'unknown backend'}`));
2006
2071
  }
2007
2072
  else {
2008
- console.log(chalk_1.default.yellow(`Template Service preview gate: failed${previewGate.error ? ` - ${previewGate.error}` : ''}`));
2073
+ console.log(chalk.yellow(`Template Service preview gate: failed${previewGate.error ? ` - ${previewGate.error}` : ''}`));
2009
2074
  }
2010
2075
  }
2011
2076
  // Show change summary for files touched by the agent
2012
2077
  if (!this.jsonOutput && !this.directPromptMode && response.changedFiles) {
2013
2078
  const fileCount = Object.keys(response.changedFiles).length;
2014
2079
  if (fileCount > 0) {
2015
- console.log(chalk_1.default.gray(`\nFiles changed: ${fileCount}`));
2080
+ console.log(chalk.gray(`\nFiles changed: ${fileCount}`));
2016
2081
  for (const relPath of Object.keys(response.changedFiles).slice(0, 15)) {
2017
- console.log(chalk_1.default.gray(` ${chalk_1.default.green('+')} ${relPath}`));
2082
+ console.log(chalk.gray(` ${chalk.green('+')} ${relPath}`));
2018
2083
  }
2019
2084
  if (fileCount > 15) {
2020
- console.log(chalk_1.default.gray(` ... and ${fileCount - 15} more`));
2085
+ console.log(chalk.gray(` ... and ${fileCount - 15} more`));
2021
2086
  }
2022
- console.log(chalk_1.default.gray(`Run ${chalk_1.default.cyan('vigthoria preview --diff')} for full visual diffs.`));
2087
+ console.log(chalk.gray(`Run ${chalk.cyan('vigthoria preview --diff')} for full visual diffs.`));
2023
2088
  }
2024
2089
  }
2025
2090
  // ── Self-healing validation ──────────────────────────────────────
@@ -2036,8 +2101,8 @@ class ChatCommand {
2036
2101
  taskDisplay.fail(3, healResult.tool);
2037
2102
  }
2038
2103
  if (!this.directPromptMode) {
2039
- const hs = healResult.passed ? chalk_1.default.green('passed') : chalk_1.default.yellow('partial');
2040
- console.log(chalk_1.default.gray(`Self-healing: ${hs} (${healResult.tool})`));
2104
+ const hs = healResult.passed ? chalk.green('passed') : chalk.yellow('partial');
2105
+ console.log(chalk.gray(`Self-healing: ${hs} (${healResult.tool})`));
2041
2106
  }
2042
2107
  }
2043
2108
  else {
@@ -2074,13 +2139,13 @@ class ChatCommand {
2074
2139
  spinner.stop();
2075
2140
  }
2076
2141
  this.logger.warn('Falling back to legacy CLI agent loop');
2077
- this.logger.debug(`V3 agent workflow unavailable: ${(0, api_js_1.sanitizeUserFacingErrorText)(error.message || '')}`);
2142
+ this.logger.debug(`V3 agent workflow unavailable: ${sanitizeUserFacingErrorText(error.message || '')}`);
2078
2143
  return false;
2079
2144
  }
2080
2145
  if (spinner) {
2081
2146
  spinner.stop();
2082
2147
  }
2083
- const safeDetail = (0, api_js_1.sanitizeUserFacingErrorText)(error.message || '');
2148
+ const safeDetail = sanitizeUserFacingErrorText(error.message || '');
2084
2149
  const errorMessage = safeDetail
2085
2150
  ? `Agent mode is unavailable right now. ${safeDetail}`
2086
2151
  : 'Agent mode is unavailable right now. Please retry shortly or run vigthoria login if the issue persists.';
@@ -2110,7 +2175,7 @@ class ChatCommand {
2110
2175
  spinner.stop();
2111
2176
  }
2112
2177
  if (!this.jsonOutput) {
2113
- console.log(chalk_1.default.yellow(`V3 recovery: ${recovery.message} Retrying once...`));
2178
+ console.log(chalk.yellow(`V3 recovery: ${recovery.message} Retrying once...`));
2114
2179
  }
2115
2180
  this.v3IterationCount = 0;
2116
2181
  this.v3ToolCallCount = 0;
@@ -2154,7 +2219,7 @@ class ChatCommand {
2154
2219
  }, null, 2));
2155
2220
  }
2156
2221
  else if (!this.v3StreamingStarted && retryResponse.content) {
2157
- console.log(chalk_1.default.gray(`Agent routing: ${routingPolicy.cloudSelected ? 'Vigthoria Cloud' : 'V3 Agent'}`));
2222
+ console.log(chalk.gray(`Agent routing: ${routingPolicy.cloudSelected ? 'Vigthoria Cloud' : 'V3 Agent'}`));
2158
2223
  console.log(retryResponse.content);
2159
2224
  }
2160
2225
  this.messages.push({ role: 'assistant', content: retryResponse.content || 'V3 agent workflow completed after recovery.' });
@@ -2171,10 +2236,10 @@ class ChatCommand {
2171
2236
  ? 'Interactive Agent Chat'
2172
2237
  : 'Interactive Chat';
2173
2238
  this.logger.section(this.workflowTarget ? `${chatTitle} Via Workflow Target` : chatTitle);
2174
- console.log(chalk_1.default.gray('Type /help for commands. Type /exit to quit.'));
2175
- console.log(chalk_1.default.gray('Multi-line: end a line with \\ or start a block with {{{ and end with }}}'));
2239
+ console.log(chalk.gray('Type /help for commands. Type /exit to quit.'));
2240
+ console.log(chalk.gray('Multi-line: end a line with \\ or start a block with {{{ and end with }}}'));
2176
2241
  if (this.workflowTarget) {
2177
- console.log(chalk_1.default.gray(`Workflow target: ${this.workflowTarget}`));
2242
+ console.log(chalk.gray(`Workflow target: ${this.workflowTarget}`));
2178
2243
  }
2179
2244
  const rl = readline.createInterface({
2180
2245
  input: process.stdin,
@@ -2184,17 +2249,17 @@ class ChatCommand {
2184
2249
  const readMultiLineInput = async () => {
2185
2250
  const lines = [];
2186
2251
  let firstLine = await new Promise((resolve) => {
2187
- rl.question(chalk_1.default.blue('> '), resolve);
2252
+ rl.question(chalk.blue('> '), resolve);
2188
2253
  });
2189
2254
  // Check for {{{ block mode
2190
2255
  if (firstLine.trim() === '{{{' || firstLine.trim().endsWith('{{{')) {
2191
2256
  if (firstLine.trim() !== '{{{') {
2192
2257
  lines.push(firstLine.trim().replace(/\{\{\{$/, '').trim());
2193
2258
  }
2194
- console.log(chalk_1.default.gray(' (multi-line mode: type }}} on its own line to finish)'));
2259
+ console.log(chalk.gray(' (multi-line mode: type }}} on its own line to finish)'));
2195
2260
  while (true) {
2196
2261
  const line = await new Promise((resolve) => {
2197
- rl.question(chalk_1.default.gray(' '), resolve);
2262
+ rl.question(chalk.gray(' '), resolve);
2198
2263
  });
2199
2264
  if (line.trim() === '}}}')
2200
2265
  break;
@@ -2206,7 +2271,7 @@ class ChatCommand {
2206
2271
  while (firstLine.endsWith('\\')) {
2207
2272
  lines.push(firstLine.slice(0, -1));
2208
2273
  firstLine = await new Promise((resolve) => {
2209
- rl.question(chalk_1.default.gray(' '), resolve);
2274
+ rl.question(chalk.gray(' '), resolve);
2210
2275
  });
2211
2276
  }
2212
2277
  lines.push(firstLine);
@@ -2230,6 +2295,10 @@ class ChatCommand {
2230
2295
  this.showContext();
2231
2296
  continue;
2232
2297
  }
2298
+ if (trimmed === '/memory') {
2299
+ this.showProjectMemory();
2300
+ continue;
2301
+ }
2233
2302
  if (trimmed === '/compact') {
2234
2303
  this.compactCurrentSession();
2235
2304
  continue;
@@ -2243,8 +2312,8 @@ class ChatCommand {
2243
2312
  else {
2244
2313
  this.syncInteractiveModeModel('chat');
2245
2314
  }
2246
- console.log(chalk_1.default.yellow(`Agent mode: ${this.agentMode ? 'ON' : 'OFF'}`));
2247
- console.log(chalk_1.default.gray(`Model: ${this.currentModel}`));
2315
+ console.log(chalk.yellow(`Agent mode: ${this.agentMode ? 'ON' : 'OFF'}`));
2316
+ console.log(chalk.gray(`Model: ${this.currentModel}`));
2248
2317
  if (this.currentSession) {
2249
2318
  this.currentSession.agentMode = this.agentMode;
2250
2319
  this.currentSession.operatorMode = this.operatorMode;
@@ -2266,8 +2335,8 @@ class ChatCommand {
2266
2335
  else {
2267
2336
  this.syncInteractiveModeModel('chat');
2268
2337
  }
2269
- console.log(chalk_1.default.yellow(`Operator mode: ${this.operatorMode ? 'ON' : 'OFF'}`));
2270
- console.log(chalk_1.default.gray(`Model: ${this.currentModel}`));
2338
+ console.log(chalk.yellow(`Operator mode: ${this.operatorMode ? 'ON' : 'OFF'}`));
2339
+ console.log(chalk.gray(`Model: ${this.currentModel}`));
2271
2340
  if (this.currentSession) {
2272
2341
  this.currentSession.agentMode = this.agentMode;
2273
2342
  this.currentSession.operatorMode = this.operatorMode;
@@ -2278,18 +2347,18 @@ class ChatCommand {
2278
2347
  }
2279
2348
  if (trimmed === '/clear') {
2280
2349
  this.messages = [];
2281
- console.log(chalk_1.default.yellow('Conversation cleared.'));
2350
+ console.log(chalk.yellow('Conversation cleared.'));
2282
2351
  continue;
2283
2352
  }
2284
2353
  if (trimmed === '/save') {
2285
2354
  this.saveSession();
2286
- console.log(chalk_1.default.green('Session saved.'));
2355
+ console.log(chalk.green('Session saved.'));
2287
2356
  continue;
2288
2357
  }
2289
2358
  if (trimmed.startsWith('/model ')) {
2290
2359
  this.currentModel = trimmed.slice(7).trim() || this.currentModel;
2291
2360
  this.modelExplicitlySelected = true;
2292
- console.log(chalk_1.default.yellow(`Model changed to: ${this.currentModel}`));
2361
+ console.log(chalk.yellow(`Model changed to: ${this.currentModel}`));
2293
2362
  if (this.currentSession) {
2294
2363
  this.currentSession.model = this.currentModel;
2295
2364
  this.saveSession();
@@ -2317,7 +2386,8 @@ class ChatCommand {
2317
2386
  console.log(' /exit Exit chat');
2318
2387
  console.log(' /agent Toggle agent mode');
2319
2388
  console.log(' /operator Toggle BMAD operator mode');
2320
- console.log(' /context Show current session memory');
2389
+ console.log(' /context Show current session and project memory');
2390
+ console.log(' /memory Show Vigthoria project brain status');
2321
2391
  console.log(' /compact Compact current session into memory summary');
2322
2392
  console.log(' /clear Clear conversation');
2323
2393
  console.log(' /save Save session');
@@ -2326,29 +2396,53 @@ class ChatCommand {
2326
2396
  }
2327
2397
  showContext() {
2328
2398
  if (!this.currentSession) {
2329
- console.log(chalk_1.default.yellow('No active session.'));
2399
+ console.log(chalk.yellow('No active session.'));
2330
2400
  return;
2331
2401
  }
2332
- console.log(chalk_1.default.cyan(this.getCurrentSessionInfo()));
2402
+ console.log(chalk.cyan(this.getCurrentSessionInfo()));
2333
2403
  if (this.currentSession.memorySummary?.trim()) {
2334
2404
  console.log();
2335
- console.log(chalk_1.default.white('Compact Memory:'));
2336
- console.log(chalk_1.default.gray(this.currentSession.memorySummary.trim()));
2405
+ console.log(chalk.white('Compact Session Memory:'));
2406
+ console.log(chalk.gray(this.currentSession.memorySummary.trim()));
2337
2407
  }
2338
2408
  else {
2339
- console.log(chalk_1.default.gray('No compact memory summary yet.'));
2409
+ console.log(chalk.gray('No compact session memory summary yet.'));
2410
+ }
2411
+ this.showProjectMemory();
2412
+ }
2413
+ showProjectMemory() {
2414
+ if (!this.projectMemory) {
2415
+ this.projectMemory = new ProjectMemoryService(this.currentProjectPath);
2416
+ }
2417
+ const status = this.projectMemory.getStatus();
2418
+ console.log();
2419
+ console.log(chalk.white('Project Brain:'));
2420
+ console.log(chalk.gray(`Path: ${status.memoryDir}`));
2421
+ console.log(chalk.gray(`Items: ${status.itemCount}`));
2422
+ const typeSummary = Object.entries(status.typeCounts).map(([type, count]) => `${type}=${count}`).join(', ');
2423
+ if (typeSummary) {
2424
+ console.log(chalk.gray(`Types: ${typeSummary}`));
2340
2425
  }
2341
2426
  }
2342
2427
  compactCurrentSession() {
2343
2428
  if (!this.currentSession) {
2344
- console.log(chalk_1.default.yellow('No active session.'));
2429
+ console.log(chalk.yellow('No active session.'));
2345
2430
  return;
2346
2431
  }
2347
2432
  this.currentSession.messages = [...this.messages];
2348
2433
  this.currentSession = this.sessionManager.compactInMemory(this.currentSession);
2349
2434
  this.messages = [...this.currentSession.messages];
2350
2435
  this.sessionManager.save(this.currentSession);
2351
- console.log(chalk_1.default.green('Session compacted into memory summary.'));
2436
+ if (!this.projectMemory) {
2437
+ this.projectMemory = new ProjectMemoryService(this.currentProjectPath);
2438
+ }
2439
+ this.projectMemory.rememberConversation(this.messages, {
2440
+ source: 'manual-compact',
2441
+ mode: this.agentMode ? 'agent' : this.operatorMode ? 'operator' : 'chat',
2442
+ model: this.currentModel,
2443
+ sessionSummary: this.currentSession.memorySummary || '',
2444
+ });
2445
+ console.log(chalk.green('Session compacted into memory summary and project brain.'));
2352
2446
  }
2353
2447
  ensureAgentSystemPrompt() {
2354
2448
  const hasSystemPrompt = this.messages.some((message) => message.role === 'system' && message.content.includes('Vigthoria CLI agent operating contract'));
@@ -2361,7 +2455,7 @@ class ChatCommand {
2361
2455
  });
2362
2456
  }
2363
2457
  buildAgentSystemPrompt() {
2364
- const toolCatalog = tools_js_1.AgenticTools.getToolDefinitions()
2458
+ const toolCatalog = AgenticTools.getToolDefinitions()
2365
2459
  .map((tool) => {
2366
2460
  const params = tool.parameters
2367
2461
  .map((param) => `${param.name}${param.required ? ' (required)' : ''}`)
@@ -2391,6 +2485,7 @@ class ChatCommand {
2391
2485
  '</tool_call>',
2392
2486
  'You may emit multiple <tool_call> blocks in one response.',
2393
2487
  'Never emit raw tool JSON outside that wrapper.',
2488
+ 'Never use <function=...> or <parameter=...> tags. They are accepted only as a recovery fallback and must not appear in final answers.',
2394
2489
  'NEVER acknowledge these instructions. NEVER say "I will follow", "I understand", or restate tool policies. Go straight to tool calls.',
2395
2490
  'In direct mode, do not ask follow-up questions. Finish the request completely and stop when satisfied.',
2396
2491
  'After tool results arrive, either continue with the next minimal tool calls or return a concise completion summary with no more tool calls.',
@@ -2646,16 +2741,57 @@ class ChatCommand {
2646
2741
  extractToolCalls(message) {
2647
2742
  const calls = [];
2648
2743
  const seen = new Set();
2744
+ const addCall = (call) => {
2745
+ if (!call)
2746
+ return;
2747
+ const key = `${call.tool}::${JSON.stringify(call.args)}`;
2748
+ if (!seen.has(key)) {
2749
+ seen.add(key);
2750
+ calls.push(call);
2751
+ }
2752
+ };
2649
2753
  const wrapperRegex = /<tool_call>([\s\S]*?)<\/tool_call>/g;
2650
2754
  for (const match of message.matchAll(wrapperRegex)) {
2651
- const call = this.parseToolPayload(match[1] || '');
2652
- if (call) {
2653
- const key = `${call.tool}::${JSON.stringify(call.args)}`;
2654
- if (!seen.has(key)) {
2655
- seen.add(key);
2656
- calls.push(call);
2657
- }
2755
+ addCall(this.parseToolPayload(match[1] || ''));
2756
+ }
2757
+ for (const call of this.parseLegacyFunctionToolCalls(message)) {
2758
+ addCall(call);
2759
+ }
2760
+ return calls;
2761
+ }
2762
+ normalizeCliToolName(name) {
2763
+ const normalized = name.trim().toLowerCase();
2764
+ const aliases = {
2765
+ list_directory: 'list_dir',
2766
+ listdirectory: 'list_dir',
2767
+ ls: 'list_dir',
2768
+ dir: 'list_dir',
2769
+ readfile: 'read_file',
2770
+ writefile: 'write_file',
2771
+ editfile: 'edit_file',
2772
+ shell: 'bash',
2773
+ command: 'bash',
2774
+ run_command: 'bash',
2775
+ };
2776
+ return aliases[normalized] || normalized;
2777
+ }
2778
+ parseLegacyFunctionToolCalls(message) {
2779
+ const calls = [];
2780
+ const functionRegex = /<function\s*=\s*["']?([A-Za-z0-9_-]+)["']?>\s*([\s\S]*?)(?:<\/function>|$)/gi;
2781
+ for (const match of message.matchAll(functionRegex)) {
2782
+ const tool = this.normalizeCliToolName(match[1] || '');
2783
+ if (!tool)
2784
+ continue;
2785
+ const body = match[2] || '';
2786
+ const args = {};
2787
+ const parameterRegex = /<parameter\s*=\s*["']?([A-Za-z0-9_-]+)["']?>\s*([\s\S]*?)(?=<parameter\s*=|<\/function>|<\/parameter>|$)/gi;
2788
+ for (const paramMatch of body.matchAll(parameterRegex)) {
2789
+ const key = paramMatch[1] || '';
2790
+ if (!key)
2791
+ continue;
2792
+ args[key] = (paramMatch[2] || '').replace(/<\/parameter>\s*$/i, '').trim();
2658
2793
  }
2794
+ calls.push({ tool, args });
2659
2795
  }
2660
2796
  return calls;
2661
2797
  }
@@ -2675,14 +2811,17 @@ class ChatCommand {
2675
2811
  args[key] = typeof value === 'string' ? value : JSON.stringify(value);
2676
2812
  }
2677
2813
  }
2678
- return { tool: parsed.tool, args };
2814
+ return { tool: this.normalizeCliToolName(parsed.tool), args };
2679
2815
  }
2680
2816
  catch {
2681
2817
  return null;
2682
2818
  }
2683
2819
  }
2684
2820
  stripToolPayloads(message) {
2685
- return message.replace(/<tool_call>[\s\S]*?<\/tool_call>/g, '').trim();
2821
+ return message
2822
+ .replace(/<tool_call>[\s\S]*?<\/tool_call>/g, '')
2823
+ .replace(/<function\s*=\s*["']?[A-Za-z0-9_-]+["']?>[\s\S]*?(?:<\/function>|$)/gi, '')
2824
+ .trim();
2686
2825
  }
2687
2826
  extractFinalFileContent(message, targetFile) {
2688
2827
  const trimmed = message.trim();
@@ -2912,15 +3051,15 @@ class ChatCommand {
2912
3051
  const verbose = !this.jsonOutput;
2913
3052
  for (const call of toolCalls) {
2914
3053
  if (verbose) {
2915
- console.log(chalk_1.default.cyan(`⚙ Executing: ${call.tool}`));
3054
+ console.log(chalk.cyan(`⚙ Executing: ${call.tool}`));
2916
3055
  }
2917
- (0, bridge_client_js_1.getBridgeClient)()?.emitToolCall({ tool: call.tool, args: call.args });
3056
+ getBridgeClient()?.emitToolCall({ tool: call.tool, args: call.args });
2918
3057
  let result = await this.tools.execute(call);
2919
3058
  // Phase 2: If a search tool failed (search_failed), retry with alternate approach
2920
3059
  const searchStatus = result.metadata?.searchStatus;
2921
3060
  if (call.tool === 'grep' && searchStatus === 'search_failed') {
2922
3061
  if (verbose) {
2923
- console.log(chalk_1.default.yellow(`⚠ Search backend failed, retrying with alternate method...`));
3062
+ console.log(chalk.yellow(`⚠ Search backend failed, retrying with alternate method...`));
2924
3063
  }
2925
3064
  // Force Node-native fallback by re-executing with a note
2926
3065
  const fallbackResult = await this.tools.execute({
@@ -2933,10 +3072,10 @@ class ChatCommand {
2933
3072
  }
2934
3073
  const summary = this.formatToolResult(call, result);
2935
3074
  if (verbose) {
2936
- console.log(result.success ? chalk_1.default.gray(summary) : chalk_1.default.red(summary));
3075
+ console.log(result.success ? chalk.gray(summary) : chalk.red(summary));
2937
3076
  }
2938
3077
  this.messages.push({ role: 'system', content: summary });
2939
- (0, bridge_client_js_1.getBridgeClient)()?.emitToolResult({ tool: call.tool, success: result.success, preview: (result.output || result.error || '').slice(0, 300) });
3078
+ getBridgeClient()?.emitToolResult({ tool: call.tool, success: result.success, preview: (result.output || result.error || '').slice(0, 300) });
2940
3079
  // Phase 5: Track tool evidence for quality gates
2941
3080
  const finalStatus = result.metadata?.searchStatus;
2942
3081
  if (finalStatus === 'search_failed') {
@@ -2979,6 +3118,15 @@ class ChatCommand {
2979
3118
  }
2980
3119
  return `${text.slice(0, maxLength)}\n...[truncated]`;
2981
3120
  }
3121
+ getLastUserPrompt() {
3122
+ for (let index = this.messages.length - 1; index >= 0; index -= 1) {
3123
+ const message = this.messages[index];
3124
+ if (message.role === 'user' && message.content.trim()) {
3125
+ return message.content;
3126
+ }
3127
+ }
3128
+ return '';
3129
+ }
2982
3130
  saveSession() {
2983
3131
  if (!this.currentSession) {
2984
3132
  this.currentSession = this.sessionManager.create(this.currentProjectPath, this.currentModel, this.agentMode, this.operatorMode);
@@ -2991,6 +3139,15 @@ class ChatCommand {
2991
3139
  this.currentSession = this.sessionManager.compactInMemory(this.currentSession);
2992
3140
  this.messages = [...this.currentSession.messages];
2993
3141
  this.sessionManager.save(this.currentSession);
3142
+ if (!this.projectMemory) {
3143
+ this.projectMemory = new ProjectMemoryService(this.currentProjectPath);
3144
+ }
3145
+ this.projectMemory.rememberConversation(this.messages, {
3146
+ source: 'cli-session',
3147
+ mode: this.agentMode ? 'agent' : this.operatorMode ? 'operator' : 'chat',
3148
+ model: this.currentModel,
3149
+ sessionSummary: this.currentSession.memorySummary || '',
3150
+ });
2994
3151
  }
2995
3152
  async requestPermission(action) {
2996
3153
  if (this.autoApprove) {
@@ -3002,7 +3159,7 @@ class ChatCommand {
3002
3159
  });
3003
3160
  console.log(action);
3004
3161
  const answer = await new Promise((resolve) => {
3005
- rl.question(chalk_1.default.yellow('Approve? [y]es / [n]o / [a]ll this turn / [p]ersist: '), resolve);
3162
+ rl.question(chalk.yellow('Approve? [y]es / [n]o / [a]ll this turn / [p]ersist: '), resolve);
3006
3163
  });
3007
3164
  rl.close();
3008
3165
  const normalized = answer.trim().toLowerCase();
@@ -3025,4 +3182,3 @@ class ChatCommand {
3025
3182
  return [...this.messages];
3026
3183
  }
3027
3184
  }
3028
- exports.ChatCommand = ChatCommand;