vigthoria-cli 1.10.37 → 1.10.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/agent-session-menu.d.ts +19 -0
- package/dist/commands/agent-session-menu.js +155 -0
- package/dist/commands/auth.js +68 -51
- package/dist/commands/bridge.js +19 -12
- package/dist/commands/cancel.js +22 -15
- package/dist/commands/chat.d.ts +0 -28
- package/dist/commands/chat.js +407 -1254
- package/dist/commands/config.js +73 -33
- package/dist/commands/deploy.js +123 -83
- package/dist/commands/device.js +61 -21
- package/dist/commands/edit.js +39 -32
- package/dist/commands/explain.js +25 -18
- package/dist/commands/generate.js +44 -37
- package/dist/commands/hub.js +102 -95
- package/dist/commands/index.js +46 -41
- package/dist/commands/legion.js +186 -146
- package/dist/commands/review.js +36 -29
- package/dist/commands/security.js +12 -5
- package/dist/commands/wallet.js +35 -28
- package/dist/commands/workflow.js +20 -13
- package/dist/utils/brain-hub-client.js +5 -1
- package/dist/utils/bridge-client.js +52 -11
- package/dist/utils/codebase-indexer.js +41 -4
- package/dist/utils/context-ranker.js +21 -15
- package/dist/utils/files.js +42 -5
- package/dist/utils/logger.js +50 -42
- package/dist/utils/persona.js +8 -3
- package/dist/utils/post-write-validator.js +29 -22
- package/dist/utils/project-memory.js +23 -16
- package/dist/utils/task-display.js +20 -13
- package/dist/utils/workspace-brain-service.js +45 -8
- package/dist/utils/workspace-cache.js +26 -18
- package/dist/utils/workspace-stream.js +63 -21
- package/package.json +3 -6
- package/dist/commands/fork.d.ts +0 -17
- package/dist/commands/fork.js +0 -164
- package/dist/commands/history.d.ts +0 -17
- package/dist/commands/history.js +0 -113
- package/dist/commands/preview.d.ts +0 -55
- package/dist/commands/preview.js +0 -467
- package/dist/commands/replay.d.ts +0 -18
- package/dist/commands/replay.js +0 -156
- package/dist/commands/repo.d.ts +0 -97
- package/dist/commands/repo.js +0 -773
- package/dist/commands/update.d.ts +0 -9
- package/dist/commands/update.js +0 -201
- package/dist/index.d.ts +0 -21
- package/dist/index.js +0 -1823
- package/dist/utils/api.d.ts +0 -572
- package/dist/utils/api.js +0 -6548
- package/dist/utils/cli-state.d.ts +0 -54
- package/dist/utils/cli-state.js +0 -185
- package/dist/utils/config.d.ts +0 -85
- package/dist/utils/config.js +0 -267
- package/dist/utils/session.d.ts +0 -118
- package/dist/utils/session.js +0 -423
- package/dist/utils/tools.d.ts +0 -276
- package/dist/utils/tools.js +0 -3516
package/dist/commands/chat.js
CHANGED
|
@@ -1,25 +1,64 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
|
58
|
+
return 300000;
|
|
20
59
|
}
|
|
21
60
|
const parsed = Number.parseInt(rawValue, 10);
|
|
22
|
-
return Number.isFinite(parsed) && parsed
|
|
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
|
-
|
|
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(
|
|
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:
|
|
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(
|
|
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
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
if (
|
|
498
|
-
|
|
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
|
-
|
|
566
|
-
|
|
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
|
-
//
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
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
|
-
|
|
597
|
-
|
|
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(
|
|
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(
|
|
495
|
+
console.log(chalk_1.default.gray(`Allowed roots: ${displayRoots}`));
|
|
745
496
|
}
|
|
746
|
-
console.log(
|
|
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.
|
|
798
|
-
return
|
|
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 (
|
|
815
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 ||
|
|
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 =
|
|
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(
|
|
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(
|
|
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 ?
|
|
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(
|
|
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
|
|
1307
|
-
|
|
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(
|
|
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 =
|
|
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 +=
|
|
865
|
+
statLine += chalk_1.default.yellow(`, ${fail} failed`);
|
|
1372
866
|
}
|
|
1373
|
-
process.stderr.write(
|
|
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 ?
|
|
1379
|
-
process.stderr.write(
|
|
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(
|
|
890
|
+
process.stderr.write(chalk_1.default.cyan(` [Plan] `) + `Task: ${planKind || 'analyzing'}`);
|
|
1392
891
|
if (quality)
|
|
1393
|
-
process.stderr.write(
|
|
892
|
+
process.stderr.write(chalk_1.default.gray(` (${quality})`));
|
|
1394
893
|
process.stderr.write('\n');
|
|
1395
894
|
if (summary) {
|
|
1396
|
-
process.stderr.write(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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' ?
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
1916
|
-
console.log(
|
|
1917
|
-
console.log(
|
|
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(
|
|
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(
|
|
2033
|
-
console.log(
|
|
2034
|
-
console.log(
|
|
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(
|
|
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(
|
|
2249
|
-
console.log(
|
|
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(
|
|
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
|
|
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 (
|
|
2375
|
-
|
|
2376
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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(
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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 && (
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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 ?
|
|
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(
|
|
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 ?
|
|
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(
|
|
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 ?
|
|
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(
|
|
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(
|
|
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|
|
|
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
|
|
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(
|
|
2972
|
-
console.log(
|
|
2973
|
-
console.log(
|
|
2974
|
-
console.log(
|
|
2975
|
-
console.log(
|
|
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(
|
|
2323
|
+
console.log(chalk_1.default.gray(`Task Complexity: HEAVY`));
|
|
2978
2324
|
}
|
|
2979
|
-
console.log(
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
2417
|
+
// Early health check: fail fast if V3 is not available
|
|
2418
|
+
const healthCheck = await this.api.runV3HealthCheck();
|
|
3096
2419
|
if (!healthCheck.healthy) {
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2544
|
+
watcher?.stop();
|
|
3268
2545
|
return true;
|
|
3269
2546
|
}
|
|
3270
2547
|
if (!this.jsonOutput && previewGate?.required && previewGate?.passed !== true && workspaceHasOutput) {
|
|
3271
|
-
console.log(
|
|
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
|
|
3288
|
-
|
|
3289
|
-
|
|
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 (!
|
|
3302
|
-
console.log(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
2595
|
+
console.log(chalk_1.default.gray(` ${chalk_1.default.green('+')} ${relPath}`));
|
|
3335
2596
|
}
|
|
3336
2597
|
if (fileCount > 15) {
|
|
3337
|
-
console.log(
|
|
2598
|
+
console.log(chalk_1.default.gray(` ... and ${fileCount - 15} more`));
|
|
3338
2599
|
}
|
|
3339
|
-
console.log(
|
|
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 ?
|
|
3378
|
-
console.log(
|
|
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
|
-
|
|
2685
|
+
watcher?.stop();
|
|
3425
2686
|
return true;
|
|
3426
2687
|
}
|
|
3427
2688
|
catch (error) {
|
|
3428
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
3568
|
-
console.log(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
3652
|
-
console.log(
|
|
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(
|
|
3675
|
-
console.log(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
2990
|
+
console.log(chalk_1.default.gray('Agent mode re-enabled for continuation.'));
|
|
3738
2991
|
}
|
|
3739
|
-
await this.runAgentTurn(followUp
|
|
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(
|
|
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(
|
|
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 =
|
|
3807
|
-
const ok =
|
|
3808
|
-
const warn =
|
|
3809
|
-
const bad =
|
|
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} ${
|
|
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} ${
|
|
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} ${
|
|
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(
|
|
3071
|
+
console.log(chalk_1.default.gray(` Tasks completed: ${succ}`));
|
|
3827
3072
|
if (failedList.length > 0) {
|
|
3828
|
-
const more = outcome.failedTaskIds.length > failedList.length ?
|
|
3829
|
-
console.log(
|
|
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 ?
|
|
3833
|
-
console.log(
|
|
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 ?
|
|
3842
|
-
console.log(
|
|
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 ?
|
|
3847
|
-
console.log(
|
|
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 ?
|
|
3852
|
-
console.log(
|
|
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(
|
|
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(
|
|
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(
|
|
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(' ' +
|
|
3864
|
-
console.log(' ' +
|
|
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(' ' +
|
|
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(' ' +
|
|
3112
|
+
console.log(' ' + chalk_1.default.cyan('vigthoria preview --diff') + chalk_1.default.gray(' inspect the file changes'));
|
|
3871
3113
|
}
|
|
3872
|
-
console.log(' ' +
|
|
3873
|
-
console.log(' ' +
|
|
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(
|
|
3938
|
-
console.log(
|
|
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(
|
|
3190
|
+
console.log(chalk_1.default.yellow('No active session.'));
|
|
3952
3191
|
return;
|
|
3953
3192
|
}
|
|
3954
|
-
console.log(
|
|
3193
|
+
console.log(chalk_1.default.cyan(this.getCurrentSessionInfo()));
|
|
3955
3194
|
if (this.currentSession.memorySummary?.trim()) {
|
|
3956
3195
|
console.log();
|
|
3957
|
-
console.log(
|
|
3958
|
-
console.log(
|
|
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(
|
|
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(
|
|
3966
|
-
console.log(
|
|
3967
|
-
console.log(
|
|
3968
|
-
console.log(
|
|
3969
|
-
console.log(
|
|
3970
|
-
console.log(
|
|
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(
|
|
3980
|
-
console.log(
|
|
3981
|
-
console.log(
|
|
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(
|
|
3223
|
+
console.log(chalk_1.default.gray(`Types: ${typeSummary}`));
|
|
3985
3224
|
}
|
|
3986
3225
|
}
|
|
3987
3226
|
compactCurrentSession() {
|
|
3988
3227
|
if (!this.currentSession) {
|
|
3989
|
-
console.log(
|
|
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(
|
|
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
|
|
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
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
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
|
-
|
|
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(
|
|
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 ?
|
|
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(
|
|
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;
|