vigthoria-cli 1.6.5 → 1.6.9
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/README.md +9 -1
- package/dist/commands/auth.d.ts +0 -1
- package/dist/commands/auth.js +57 -6
- package/dist/commands/bridge.d.ts +7 -0
- package/dist/commands/bridge.js +31 -0
- package/dist/commands/chat.d.ts +27 -1
- package/dist/commands/chat.js +508 -14
- package/dist/commands/config.d.ts +0 -1
- package/dist/commands/config.js +10 -3
- package/dist/commands/deploy.d.ts +0 -1
- package/dist/commands/deploy.js +0 -1
- package/dist/commands/edit.d.ts +0 -1
- package/dist/commands/edit.js +0 -1
- package/dist/commands/explain.d.ts +0 -1
- package/dist/commands/explain.js +0 -1
- package/dist/commands/generate.d.ts +0 -1
- package/dist/commands/generate.js +0 -1
- package/dist/commands/hub.d.ts +0 -1
- package/dist/commands/hub.js +0 -1
- package/dist/commands/repo.d.ts +0 -1
- package/dist/commands/repo.js +0 -1
- package/dist/commands/review.d.ts +0 -1
- package/dist/commands/review.js +0 -1
- package/dist/commands/workflow.d.ts +31 -0
- package/dist/commands/workflow.js +140 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +135 -11
- package/dist/utils/api.d.ts +210 -1
- package/dist/utils/api.js +1789 -47
- package/dist/utils/config.d.ts +14 -7
- package/dist/utils/config.js +22 -11
- package/dist/utils/files.d.ts +0 -1
- package/dist/utils/files.js +0 -1
- package/dist/utils/logger.d.ts +0 -1
- package/dist/utils/logger.js +0 -1
- package/dist/utils/session.d.ts +14 -2
- package/dist/utils/session.js +105 -4
- package/dist/utils/tools.d.ts +0 -1
- package/dist/utils/tools.js +0 -1
- package/package.json +23 -4
- package/dist/commands/auth.d.ts.map +0 -1
- package/dist/commands/auth.js.map +0 -1
- package/dist/commands/chat.d.ts.map +0 -1
- package/dist/commands/chat.js.map +0 -1
- package/dist/commands/config.d.ts.map +0 -1
- package/dist/commands/config.js.map +0 -1
- package/dist/commands/deploy.d.ts.map +0 -1
- package/dist/commands/deploy.js.map +0 -1
- package/dist/commands/edit.d.ts.map +0 -1
- package/dist/commands/edit.js.map +0 -1
- package/dist/commands/explain.d.ts.map +0 -1
- package/dist/commands/explain.js.map +0 -1
- package/dist/commands/generate.d.ts.map +0 -1
- package/dist/commands/generate.js.map +0 -1
- package/dist/commands/hub.d.ts.map +0 -1
- package/dist/commands/hub.js.map +0 -1
- package/dist/commands/repo.d.ts.map +0 -1
- package/dist/commands/repo.js.map +0 -1
- package/dist/commands/review.d.ts.map +0 -1
- package/dist/commands/review.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/utils/api.d.ts.map +0 -1
- package/dist/utils/api.js.map +0 -1
- package/dist/utils/config.d.ts.map +0 -1
- package/dist/utils/config.js.map +0 -1
- package/dist/utils/files.d.ts.map +0 -1
- package/dist/utils/files.js.map +0 -1
- package/dist/utils/logger.d.ts.map +0 -1
- package/dist/utils/logger.js.map +0 -1
- package/dist/utils/session.d.ts.map +0 -1
- package/dist/utils/session.js.map +0 -1
- package/dist/utils/tools.d.ts.map +0 -1
- package/dist/utils/tools.js.map +0 -1
package/dist/commands/chat.js
CHANGED
|
@@ -39,11 +39,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
39
39
|
exports.ChatCommand = void 0;
|
|
40
40
|
const chalk_1 = __importDefault(require("chalk"));
|
|
41
41
|
const ora_1 = __importDefault(require("ora"));
|
|
42
|
+
const fs = __importStar(require("fs"));
|
|
42
43
|
const path = __importStar(require("path"));
|
|
43
44
|
const readline = __importStar(require("readline"));
|
|
44
45
|
const api_js_1 = require("../utils/api.js");
|
|
45
46
|
const tools_js_1 = require("../utils/tools.js");
|
|
46
47
|
const session_js_1 = require("../utils/session.js");
|
|
48
|
+
const DEFAULT_V3_AGENT_TIMEOUT_MS = (() => {
|
|
49
|
+
const rawValue = process.env.VIGTHORIA_AGENT_TIMEOUT_MS || process.env.V3_AGENT_TIMEOUT_MS || '1200000';
|
|
50
|
+
const parsed = Number.parseInt(rawValue, 10);
|
|
51
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : 1200000;
|
|
52
|
+
})();
|
|
47
53
|
class ChatCommand {
|
|
48
54
|
config;
|
|
49
55
|
logger;
|
|
@@ -59,6 +65,141 @@ class ChatCommand {
|
|
|
59
65
|
directToolContinuationCount = 0;
|
|
60
66
|
currentModel = 'code';
|
|
61
67
|
autoApprove = false;
|
|
68
|
+
operatorMode = false;
|
|
69
|
+
workflowTarget = null;
|
|
70
|
+
savePlanToVigFlow = false;
|
|
71
|
+
jsonOutput = false;
|
|
72
|
+
hasOperatorAccess() {
|
|
73
|
+
return this.config.hasOperatorAccess();
|
|
74
|
+
}
|
|
75
|
+
operatorAccessMessage() {
|
|
76
|
+
const currentPlan = this.config.get('subscription').plan || 'free';
|
|
77
|
+
return `Operator mode requires Enterprise or admin access. Current plan: ${currentPlan}.`;
|
|
78
|
+
}
|
|
79
|
+
getMessagesForModel() {
|
|
80
|
+
const memoryContext = this.sessionManager.buildMemoryContext(this.currentSession);
|
|
81
|
+
const messages = [...this.messages];
|
|
82
|
+
if (!memoryContext) {
|
|
83
|
+
return messages;
|
|
84
|
+
}
|
|
85
|
+
const alreadyInjected = messages.some((message) => message.role === 'system' && message.content.includes('Session memory summary from earlier conversation turns.'));
|
|
86
|
+
if (alreadyInjected) {
|
|
87
|
+
return messages;
|
|
88
|
+
}
|
|
89
|
+
const insertionIndex = messages.findIndex((message) => message.role !== 'system');
|
|
90
|
+
const memoryMessage = {
|
|
91
|
+
role: 'system',
|
|
92
|
+
content: memoryContext,
|
|
93
|
+
};
|
|
94
|
+
if (insertionIndex === -1) {
|
|
95
|
+
messages.push(memoryMessage);
|
|
96
|
+
return messages;
|
|
97
|
+
}
|
|
98
|
+
messages.splice(insertionIndex, 0, memoryMessage);
|
|
99
|
+
return messages;
|
|
100
|
+
}
|
|
101
|
+
isDiagnosticPrompt(prompt) {
|
|
102
|
+
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);
|
|
103
|
+
}
|
|
104
|
+
isBrowserTaskPrompt(prompt) {
|
|
105
|
+
return /(browser|chrome|devtools|console|dom|network tab|network request|frontend runtime|client-side|client side|rendering|page load|websocket|ui bug|inspect element)/i.test(prompt);
|
|
106
|
+
}
|
|
107
|
+
inferAgentTaskType(prompt) {
|
|
108
|
+
return this.isDiagnosticPrompt(prompt) ? 'debugging' : 'implementation';
|
|
109
|
+
}
|
|
110
|
+
buildTaskShapingInstructions(prompt) {
|
|
111
|
+
const instructions = [];
|
|
112
|
+
if (this.isDiagnosticPrompt(prompt)) {
|
|
113
|
+
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.');
|
|
114
|
+
}
|
|
115
|
+
if (this.isBrowserTaskPrompt(prompt)) {
|
|
116
|
+
instructions.push('Browser-debug mode is active.', 'Prefer concrete browser evidence such as console errors, network failures, DOM state, and websocket behavior.', 'If a DevTools Bridge is available, use it as the primary browser observability path.');
|
|
117
|
+
}
|
|
118
|
+
if (instructions.length === 0) {
|
|
119
|
+
return '';
|
|
120
|
+
}
|
|
121
|
+
return instructions.join('\n');
|
|
122
|
+
}
|
|
123
|
+
buildExecutionPrompt(prompt) {
|
|
124
|
+
const shaping = this.buildTaskShapingInstructions(prompt);
|
|
125
|
+
return shaping ? `${prompt}\n\n${shaping}` : prompt;
|
|
126
|
+
}
|
|
127
|
+
async getPromptRuntimeContext(prompt) {
|
|
128
|
+
if (!this.isBrowserTaskPrompt(prompt)) {
|
|
129
|
+
return {};
|
|
130
|
+
}
|
|
131
|
+
const bridgeStatus = await this.api.getDevtoolsBridgeStatus();
|
|
132
|
+
if (!this.jsonOutput && bridgeStatus.ok) {
|
|
133
|
+
console.log(chalk_1.default.gray(`Browser task detected. DevTools Bridge is reachable at ${bridgeStatus.endpoint}.`));
|
|
134
|
+
}
|
|
135
|
+
else if (!this.jsonOutput) {
|
|
136
|
+
console.log(chalk_1.default.yellow(`Browser task detected. DevTools Bridge is not running at ${bridgeStatus.endpoint}.`));
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
browserTask: true,
|
|
140
|
+
devtoolsBridgeAvailable: bridgeStatus.ok,
|
|
141
|
+
devtoolsBridgeEndpoint: bridgeStatus.endpoint,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
describeV3AgentTool(toolName) {
|
|
145
|
+
const normalized = String(toolName || '').toLowerCase();
|
|
146
|
+
if (/read|grep|search|list|find|glob/.test(normalized)) {
|
|
147
|
+
return 'Planning... gathering project context';
|
|
148
|
+
}
|
|
149
|
+
if (/write|edit|patch|create|delete|replace|rename/.test(normalized)) {
|
|
150
|
+
return 'Applying changes...';
|
|
151
|
+
}
|
|
152
|
+
if (/test|lint|build|run|exec|terminal/.test(normalized)) {
|
|
153
|
+
return 'Validating changes...';
|
|
154
|
+
}
|
|
155
|
+
return 'Working...';
|
|
156
|
+
}
|
|
157
|
+
updateV3AgentSpinner(spinner, event) {
|
|
158
|
+
if (!event || typeof event !== 'object') {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (event.type === 'tool_call') {
|
|
162
|
+
spinner.text = this.describeV3AgentTool(event.tool || event.name || event.tool_name);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
if (event.type === 'message') {
|
|
166
|
+
spinner.text = 'Writing response...';
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
if (event.type === 'complete') {
|
|
170
|
+
spinner.text = 'Finishing...';
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
if (event.type === 'plan' || event.type === 'analysis' || event.type === 'thinking') {
|
|
174
|
+
spinner.text = 'Planning...';
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
updateOperatorSpinner(spinner, event) {
|
|
178
|
+
if (!event || typeof event !== 'object') {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if (event.type === 'started') {
|
|
182
|
+
spinner.text = 'Starting BMAD workflow...';
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
if (event.type === 'connected') {
|
|
186
|
+
spinner.text = 'Connected to BMAD stream...';
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
if (event.type === 'agent') {
|
|
190
|
+
spinner.text = `Running ${event.agent || 'BMAD agent'}...`;
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
if (event.type === 'status') {
|
|
194
|
+
const progress = Number.isFinite(Number(event.progress)) ? ` (${event.progress}%)` : '';
|
|
195
|
+
spinner.text = `${event.status || 'Working'}${progress}`;
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
if (event.type === 'result') {
|
|
199
|
+
spinner.text = 'Finishing operator workflow...';
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
62
203
|
constructor(config, logger) {
|
|
63
204
|
this.config = config;
|
|
64
205
|
this.logger = logger;
|
|
@@ -71,49 +212,246 @@ class ChatCommand {
|
|
|
71
212
|
return;
|
|
72
213
|
}
|
|
73
214
|
this.agentMode = options.agent === true;
|
|
215
|
+
this.operatorMode = options.operator === true;
|
|
216
|
+
this.workflowTarget = typeof options.workflow === 'string' && options.workflow.trim()
|
|
217
|
+
? options.workflow.trim()
|
|
218
|
+
: null;
|
|
219
|
+
this.savePlanToVigFlow = options.savePlan === true;
|
|
220
|
+
this.jsonOutput = options.json === true;
|
|
74
221
|
this.autoApprove = options.autoApprove === true;
|
|
75
222
|
this.currentModel = options.model || 'code';
|
|
76
223
|
this.currentProjectPath = path.resolve(options.project || process.cwd());
|
|
224
|
+
if (this.jsonOutput && !options.prompt) {
|
|
225
|
+
throw new Error('--json is only supported together with --prompt.');
|
|
226
|
+
}
|
|
227
|
+
this.ensureProjectWorkspace();
|
|
77
228
|
this.directPromptMode = Boolean(options.prompt);
|
|
78
229
|
this.directToolContinuationCount = 0;
|
|
79
230
|
this.tools = new tools_js_1.AgenticTools(this.logger, this.currentProjectPath, async (action) => this.requestPermission(action), this.autoApprove);
|
|
80
231
|
this.initializeSession(options.resume === true);
|
|
232
|
+
if (this.operatorMode && !this.hasOperatorAccess()) {
|
|
233
|
+
if (this.currentSession) {
|
|
234
|
+
this.currentSession.operatorMode = false;
|
|
235
|
+
this.saveSession();
|
|
236
|
+
}
|
|
237
|
+
this.operatorMode = false;
|
|
238
|
+
this.logger.error(this.operatorAccessMessage());
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
81
241
|
if (options.prompt) {
|
|
82
242
|
await this.handleDirectPrompt(options.prompt);
|
|
83
243
|
return;
|
|
84
244
|
}
|
|
85
245
|
await this.startInteractiveChat();
|
|
86
246
|
}
|
|
247
|
+
ensureProjectWorkspace() {
|
|
248
|
+
if (fs.existsSync(this.currentProjectPath)) {
|
|
249
|
+
if (!fs.statSync(this.currentProjectPath).isDirectory()) {
|
|
250
|
+
throw new Error(`Project path is not a directory: ${this.currentProjectPath}`);
|
|
251
|
+
}
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
fs.mkdirSync(this.currentProjectPath, { recursive: true });
|
|
255
|
+
}
|
|
87
256
|
initializeSession(resume) {
|
|
88
257
|
if (resume) {
|
|
89
258
|
this.currentSession = this.sessionManager.getLatest(this.currentProjectPath);
|
|
90
259
|
if (this.currentSession) {
|
|
91
260
|
this.messages = [...this.currentSession.messages];
|
|
92
261
|
this.agentMode = this.currentSession.agentMode || this.agentMode;
|
|
262
|
+
this.operatorMode = this.currentSession.operatorMode || this.operatorMode;
|
|
93
263
|
this.currentModel = this.currentSession.model || this.currentModel;
|
|
94
264
|
return;
|
|
95
265
|
}
|
|
96
266
|
}
|
|
97
|
-
this.currentSession = this.sessionManager.create(this.currentProjectPath, this.currentModel, this.agentMode);
|
|
267
|
+
this.currentSession = this.sessionManager.create(this.currentProjectPath, this.currentModel, this.agentMode, this.operatorMode);
|
|
98
268
|
this.messages = [...this.currentSession.messages];
|
|
99
269
|
}
|
|
100
270
|
async handleDirectPrompt(prompt) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
271
|
+
if (!this.jsonOutput) {
|
|
272
|
+
console.log(chalk_1.default.cyan('Running single prompt in direct mode.'));
|
|
273
|
+
console.log(chalk_1.default.gray(`Model: ${this.currentModel}`));
|
|
274
|
+
console.log(chalk_1.default.gray(`Project: ${this.currentProjectPath}`));
|
|
275
|
+
if (this.workflowTarget) {
|
|
276
|
+
console.log(chalk_1.default.gray(`Workflow target: ${this.workflowTarget}`));
|
|
277
|
+
}
|
|
278
|
+
console.log();
|
|
279
|
+
}
|
|
280
|
+
if (this.workflowTarget) {
|
|
281
|
+
await this.runWorkflowTargetPrompt(prompt);
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
105
284
|
if (this.agentMode) {
|
|
106
285
|
await this.runAgentTurn(prompt);
|
|
107
286
|
return;
|
|
108
287
|
}
|
|
288
|
+
if (this.operatorMode) {
|
|
289
|
+
if (!this.hasOperatorAccess()) {
|
|
290
|
+
this.logger.error(this.operatorAccessMessage());
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
await this.runOperatorTurn(prompt);
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
109
296
|
await this.runSimplePrompt(prompt);
|
|
110
297
|
}
|
|
298
|
+
formatWorkflowTargetResult(result) {
|
|
299
|
+
if (typeof result === 'string') {
|
|
300
|
+
return result.trim();
|
|
301
|
+
}
|
|
302
|
+
if (result == null) {
|
|
303
|
+
return '';
|
|
304
|
+
}
|
|
305
|
+
if (typeof result !== 'object') {
|
|
306
|
+
return String(result);
|
|
307
|
+
}
|
|
308
|
+
const entries = Object.entries(result)
|
|
309
|
+
.filter(([, value]) => typeof value === 'string' && String(value).trim());
|
|
310
|
+
if (entries.length === 1) {
|
|
311
|
+
return String(entries[0][1]).trim();
|
|
312
|
+
}
|
|
313
|
+
if (entries.length > 1) {
|
|
314
|
+
return entries
|
|
315
|
+
.map(([key, value]) => `${key}:\n${String(value).trim()}`)
|
|
316
|
+
.join('\n\n');
|
|
317
|
+
}
|
|
318
|
+
const serialized = JSON.stringify(result, null, 2);
|
|
319
|
+
return serialized === '{}' ? '' : serialized;
|
|
320
|
+
}
|
|
321
|
+
async runWorkflowTargetPrompt(prompt) {
|
|
322
|
+
const selector = this.workflowTarget;
|
|
323
|
+
if (!selector) {
|
|
324
|
+
throw new Error('Workflow target is not configured.');
|
|
325
|
+
}
|
|
326
|
+
this.lastActionableUserInput = prompt;
|
|
327
|
+
const executionPrompt = this.buildExecutionPrompt(prompt);
|
|
328
|
+
this.messages.push({ role: 'user', content: executionPrompt });
|
|
329
|
+
const runtimeContext = await this.getPromptRuntimeContext(prompt);
|
|
330
|
+
const resolvedWorkflow = await this.api.resolveVigFlowWorkflow(selector);
|
|
331
|
+
const invocationMode = this.operatorMode ? 'operator' : this.agentMode ? 'agent' : 'chat';
|
|
332
|
+
const spinner = this.jsonOutput ? null : (0, ora_1.default)({ text: `Running workflow ${resolvedWorkflow.name}...`, spinner: 'clock' }).start();
|
|
333
|
+
try {
|
|
334
|
+
const execution = await this.api.runVigFlowWorkflow(resolvedWorkflow.id, {
|
|
335
|
+
data: {
|
|
336
|
+
request: executionPrompt,
|
|
337
|
+
prompt: executionPrompt,
|
|
338
|
+
rawPrompt: prompt,
|
|
339
|
+
workspacePath: this.currentProjectPath,
|
|
340
|
+
projectPath: this.currentProjectPath,
|
|
341
|
+
targetPath: this.currentProjectPath,
|
|
342
|
+
model: this.currentModel,
|
|
343
|
+
history: this.getMessagesForModel(),
|
|
344
|
+
executionSurface: invocationMode,
|
|
345
|
+
clientSurface: 'cli',
|
|
346
|
+
workflowTarget: {
|
|
347
|
+
id: resolvedWorkflow.id,
|
|
348
|
+
name: resolvedWorkflow.name,
|
|
349
|
+
selector: resolvedWorkflow.selector,
|
|
350
|
+
matchedBy: resolvedWorkflow.matchedBy,
|
|
351
|
+
},
|
|
352
|
+
...runtimeContext,
|
|
353
|
+
},
|
|
354
|
+
executionOptions: {
|
|
355
|
+
targetType: 'vigflow-workflow',
|
|
356
|
+
targetSelector: resolvedWorkflow.selector,
|
|
357
|
+
targetMatchedBy: resolvedWorkflow.matchedBy,
|
|
358
|
+
executionSurface: invocationMode,
|
|
359
|
+
clientSurface: 'cli',
|
|
360
|
+
model: this.currentModel,
|
|
361
|
+
},
|
|
362
|
+
});
|
|
363
|
+
const content = this.formatWorkflowTargetResult(execution.result);
|
|
364
|
+
const assistantText = content || `Workflow ${resolvedWorkflow.name} completed with status ${execution.status}.`;
|
|
365
|
+
this.messages.push({ role: 'assistant', content: assistantText });
|
|
366
|
+
this.saveSession();
|
|
367
|
+
if (spinner) {
|
|
368
|
+
spinner.stop();
|
|
369
|
+
}
|
|
370
|
+
if (this.jsonOutput) {
|
|
371
|
+
console.log(JSON.stringify({
|
|
372
|
+
success: true,
|
|
373
|
+
workflow: resolvedWorkflow,
|
|
374
|
+
mode: invocationMode,
|
|
375
|
+
execution,
|
|
376
|
+
content,
|
|
377
|
+
}, null, 2));
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
this.logger.success(`Workflow target ${resolvedWorkflow.name} ${execution.status}`);
|
|
381
|
+
console.log(chalk_1.default.gray(`Workflow ID: ${resolvedWorkflow.id}`));
|
|
382
|
+
console.log(chalk_1.default.gray(`Execution ID: ${execution.executionId}`));
|
|
383
|
+
console.log(chalk_1.default.gray(`Mode: ${invocationMode}`));
|
|
384
|
+
if (content) {
|
|
385
|
+
console.log(content);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
catch (error) {
|
|
389
|
+
if (spinner) {
|
|
390
|
+
spinner.fail('Workflow target execution failed');
|
|
391
|
+
}
|
|
392
|
+
throw error;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
async runOperatorTurn(prompt) {
|
|
396
|
+
if (!this.hasOperatorAccess()) {
|
|
397
|
+
this.logger.error(this.operatorAccessMessage());
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
const runtimeContext = await this.getPromptRuntimeContext(prompt);
|
|
401
|
+
const spinner = this.jsonOutput ? null : (0, ora_1.default)({ text: 'Thinking like an operator...', spinner: 'clock' }).start();
|
|
402
|
+
const executionPrompt = this.buildExecutionPrompt(prompt);
|
|
403
|
+
const workflowType = this.isDiagnosticPrompt(prompt) ? 'analysis_only' : 'full_autonomy';
|
|
404
|
+
try {
|
|
405
|
+
const response = await this.api.runOperatorWorkflow(executionPrompt, {
|
|
406
|
+
workspacePath: this.currentProjectPath,
|
|
407
|
+
projectPath: this.currentProjectPath,
|
|
408
|
+
targetPath: this.currentProjectPath,
|
|
409
|
+
rawPrompt: prompt,
|
|
410
|
+
executionSurface: 'cli',
|
|
411
|
+
clientSurface: 'cli',
|
|
412
|
+
history: this.getMessagesForModel(),
|
|
413
|
+
workflowType,
|
|
414
|
+
model: this.currentModel,
|
|
415
|
+
savePlanToVigFlow: this.savePlanToVigFlow,
|
|
416
|
+
...runtimeContext,
|
|
417
|
+
onStreamEvent: spinner ? (event) => this.updateOperatorSpinner(spinner, event) : undefined,
|
|
418
|
+
});
|
|
419
|
+
if (spinner) {
|
|
420
|
+
spinner.stop();
|
|
421
|
+
}
|
|
422
|
+
if (this.jsonOutput) {
|
|
423
|
+
console.log(JSON.stringify({
|
|
424
|
+
success: true,
|
|
425
|
+
mode: 'operator',
|
|
426
|
+
workflowId: response.workflowId,
|
|
427
|
+
savedWorkflow: response.savedWorkflow || null,
|
|
428
|
+
contextId: response.contextId || null,
|
|
429
|
+
content: response.content || 'Operator workflow completed.',
|
|
430
|
+
metadata: response.metadata || {},
|
|
431
|
+
}, null, 2));
|
|
432
|
+
}
|
|
433
|
+
else {
|
|
434
|
+
console.log(response.content || 'Operator workflow completed.');
|
|
435
|
+
if (response.savedWorkflow?.id) {
|
|
436
|
+
console.log(chalk_1.default.gray(`Saved VigFlow workflow: ${response.savedWorkflow.id}${response.savedWorkflow.name ? ` (${response.savedWorkflow.name})` : ''}`));
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
this.messages.push({ role: 'assistant', content: response.content || 'Operator workflow completed.' });
|
|
440
|
+
this.saveSession();
|
|
441
|
+
}
|
|
442
|
+
catch (error) {
|
|
443
|
+
if (spinner) {
|
|
444
|
+
spinner.fail('Operator workflow failed');
|
|
445
|
+
}
|
|
446
|
+
this.logger.error(error.message);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
111
449
|
async runSimplePrompt(prompt) {
|
|
112
450
|
this.lastActionableUserInput = prompt;
|
|
113
|
-
this.messages.push({ role: 'user', content: prompt });
|
|
451
|
+
this.messages.push({ role: 'user', content: this.buildExecutionPrompt(prompt) });
|
|
114
452
|
const spinner = (0, ora_1.default)({ text: 'Thinking...', spinner: 'clock' }).start();
|
|
115
453
|
try {
|
|
116
|
-
const response = await this.api.chat(this.
|
|
454
|
+
const response = await this.api.chat(this.getMessagesForModel(), this.currentModel);
|
|
117
455
|
spinner.stop();
|
|
118
456
|
const finalText = response.message.trim();
|
|
119
457
|
if (finalText) {
|
|
@@ -136,6 +474,11 @@ class ChatCommand {
|
|
|
136
474
|
this.saveSession();
|
|
137
475
|
return;
|
|
138
476
|
}
|
|
477
|
+
const handledByV3Workflow = await this.tryV3AgentWorkflow(prompt);
|
|
478
|
+
if (handledByV3Workflow) {
|
|
479
|
+
this.saveSession();
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
139
482
|
this.lastActionableUserInput = prompt;
|
|
140
483
|
this.directToolContinuationCount = 0;
|
|
141
484
|
this.tools.clearSessionApprovals();
|
|
@@ -146,7 +489,7 @@ class ChatCommand {
|
|
|
146
489
|
for (let turn = 0; turn < maxTurns; turn += 1) {
|
|
147
490
|
const spinner = (0, ora_1.default)({ text: turn === 0 ? 'Planning...' : 'Continuing...', spinner: 'clock' }).start();
|
|
148
491
|
try {
|
|
149
|
-
const response = await this.api.chat(this.
|
|
492
|
+
const response = await this.api.chat(this.getMessagesForModel(), this.currentModel);
|
|
150
493
|
spinner.stop();
|
|
151
494
|
const assistantMessage = response.message || '';
|
|
152
495
|
this.messages.push({ role: 'assistant', content: assistantMessage });
|
|
@@ -238,9 +581,87 @@ class ChatCommand {
|
|
|
238
581
|
console.log(`Updated ${targetFile}.`);
|
|
239
582
|
return true;
|
|
240
583
|
}
|
|
584
|
+
async tryV3AgentWorkflow(prompt) {
|
|
585
|
+
const runtimeContext = await this.getPromptRuntimeContext(prompt);
|
|
586
|
+
const spinner = this.jsonOutput ? null : (0, ora_1.default)({ text: 'Thinking...', spinner: 'clock' }).start();
|
|
587
|
+
const executionPrompt = this.buildExecutionPrompt(prompt);
|
|
588
|
+
const agentTaskType = this.inferAgentTaskType(prompt);
|
|
589
|
+
try {
|
|
590
|
+
const response = await this.api.runV3AgentWorkflow(executionPrompt, {
|
|
591
|
+
workspace: { path: this.currentProjectPath },
|
|
592
|
+
workspacePath: this.currentProjectPath,
|
|
593
|
+
projectPath: this.currentProjectPath,
|
|
594
|
+
targetPath: this.currentProjectPath,
|
|
595
|
+
agentTaskType,
|
|
596
|
+
executionSurface: 'cli',
|
|
597
|
+
clientSurface: 'cli',
|
|
598
|
+
localMachineCapable: true,
|
|
599
|
+
agentTimeoutMs: DEFAULT_V3_AGENT_TIMEOUT_MS,
|
|
600
|
+
agentIdleTimeoutMs: 90000,
|
|
601
|
+
rawPrompt: prompt,
|
|
602
|
+
history: this.getMessagesForModel(),
|
|
603
|
+
...runtimeContext,
|
|
604
|
+
onStreamEvent: spinner ? (event) => this.updateV3AgentSpinner(spinner, event) : undefined,
|
|
605
|
+
});
|
|
606
|
+
if (spinner) {
|
|
607
|
+
spinner.stop();
|
|
608
|
+
}
|
|
609
|
+
const previewGate = (response.metadata?.previewGate || null);
|
|
610
|
+
const success = previewGate?.required === true ? previewGate?.passed === true : true;
|
|
611
|
+
if (!success) {
|
|
612
|
+
if (spinner) {
|
|
613
|
+
spinner.warn('Falling back to legacy CLI agent loop');
|
|
614
|
+
}
|
|
615
|
+
this.logger.debug(`V3 agent workflow returned an incomplete result: ${previewGate?.error || 'workspace changes were not fully validated'}`);
|
|
616
|
+
return false;
|
|
617
|
+
}
|
|
618
|
+
if (this.jsonOutput) {
|
|
619
|
+
console.log(JSON.stringify({
|
|
620
|
+
success,
|
|
621
|
+
mode: 'agent',
|
|
622
|
+
taskId: response.taskId || null,
|
|
623
|
+
contextId: response.contextId || null,
|
|
624
|
+
partial: response.partial === true,
|
|
625
|
+
content: response.content || 'V3 agent workflow completed.',
|
|
626
|
+
metadata: response.metadata || {},
|
|
627
|
+
}, null, 2));
|
|
628
|
+
}
|
|
629
|
+
else if (response.content) {
|
|
630
|
+
console.log(response.content);
|
|
631
|
+
}
|
|
632
|
+
else {
|
|
633
|
+
console.log('V3 agent workflow completed.');
|
|
634
|
+
}
|
|
635
|
+
if (!this.jsonOutput && previewGate?.required) {
|
|
636
|
+
if (previewGate.passed) {
|
|
637
|
+
console.log(chalk_1.default.gray(`Template Service preview gate: passed via ${previewGate.backendUrl || 'unknown backend'}`));
|
|
638
|
+
}
|
|
639
|
+
else {
|
|
640
|
+
console.log(chalk_1.default.yellow(`Template Service preview gate: failed${previewGate.error ? ` - ${previewGate.error}` : ''}`));
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
this.messages.push({ role: 'assistant', content: response.content || 'V3 agent workflow completed.' });
|
|
644
|
+
return true;
|
|
645
|
+
}
|
|
646
|
+
catch (error) {
|
|
647
|
+
if (spinner) {
|
|
648
|
+
spinner.warn('Falling back to legacy CLI agent loop');
|
|
649
|
+
}
|
|
650
|
+
this.logger.debug(`V3 agent workflow unavailable: ${error.message}`);
|
|
651
|
+
return false;
|
|
652
|
+
}
|
|
653
|
+
}
|
|
241
654
|
async startInteractiveChat() {
|
|
242
|
-
|
|
655
|
+
const chatTitle = this.operatorMode
|
|
656
|
+
? 'Interactive Operator Chat'
|
|
657
|
+
: this.agentMode
|
|
658
|
+
? 'Interactive Agent Chat'
|
|
659
|
+
: 'Interactive Chat';
|
|
660
|
+
this.logger.section(this.workflowTarget ? `${chatTitle} Via Workflow Target` : chatTitle);
|
|
243
661
|
console.log(chalk_1.default.gray('Type /help for commands. Type /exit to quit.'));
|
|
662
|
+
if (this.workflowTarget) {
|
|
663
|
+
console.log(chalk_1.default.gray(`Workflow target: ${this.workflowTarget}`));
|
|
664
|
+
}
|
|
244
665
|
const rl = readline.createInterface({
|
|
245
666
|
input: process.stdin,
|
|
246
667
|
output: process.stdout,
|
|
@@ -261,11 +682,40 @@ class ChatCommand {
|
|
|
261
682
|
this.showHelp();
|
|
262
683
|
continue;
|
|
263
684
|
}
|
|
685
|
+
if (trimmed === '/context') {
|
|
686
|
+
this.showContext();
|
|
687
|
+
continue;
|
|
688
|
+
}
|
|
689
|
+
if (trimmed === '/compact') {
|
|
690
|
+
this.compactCurrentSession();
|
|
691
|
+
continue;
|
|
692
|
+
}
|
|
264
693
|
if (trimmed === '/agent') {
|
|
265
694
|
this.agentMode = !this.agentMode;
|
|
695
|
+
if (this.agentMode) {
|
|
696
|
+
this.operatorMode = false;
|
|
697
|
+
}
|
|
266
698
|
console.log(chalk_1.default.yellow(`Agent mode: ${this.agentMode ? 'ON' : 'OFF'}`));
|
|
267
699
|
if (this.currentSession) {
|
|
268
700
|
this.currentSession.agentMode = this.agentMode;
|
|
701
|
+
this.currentSession.operatorMode = this.operatorMode;
|
|
702
|
+
this.saveSession();
|
|
703
|
+
}
|
|
704
|
+
continue;
|
|
705
|
+
}
|
|
706
|
+
if (trimmed === '/operator') {
|
|
707
|
+
if (!this.operatorMode && !this.hasOperatorAccess()) {
|
|
708
|
+
this.logger.error(this.operatorAccessMessage());
|
|
709
|
+
continue;
|
|
710
|
+
}
|
|
711
|
+
this.operatorMode = !this.operatorMode;
|
|
712
|
+
if (this.operatorMode) {
|
|
713
|
+
this.agentMode = false;
|
|
714
|
+
}
|
|
715
|
+
console.log(chalk_1.default.yellow(`Operator mode: ${this.operatorMode ? 'ON' : 'OFF'}`));
|
|
716
|
+
if (this.currentSession) {
|
|
717
|
+
this.currentSession.agentMode = this.agentMode;
|
|
718
|
+
this.currentSession.operatorMode = this.operatorMode;
|
|
269
719
|
this.saveSession();
|
|
270
720
|
}
|
|
271
721
|
continue;
|
|
@@ -289,7 +739,13 @@ class ChatCommand {
|
|
|
289
739
|
}
|
|
290
740
|
continue;
|
|
291
741
|
}
|
|
292
|
-
if (this.
|
|
742
|
+
if (this.workflowTarget) {
|
|
743
|
+
await this.runWorkflowTargetPrompt(trimmed);
|
|
744
|
+
}
|
|
745
|
+
else if (this.operatorMode) {
|
|
746
|
+
await this.runOperatorTurn(trimmed);
|
|
747
|
+
}
|
|
748
|
+
else if (this.agentMode) {
|
|
293
749
|
await this.runAgentTurn(trimmed);
|
|
294
750
|
}
|
|
295
751
|
else {
|
|
@@ -303,11 +759,40 @@ class ChatCommand {
|
|
|
303
759
|
console.log(' /help Show this help');
|
|
304
760
|
console.log(' /exit Exit chat');
|
|
305
761
|
console.log(' /agent Toggle agent mode');
|
|
762
|
+
console.log(' /operator Toggle BMAD operator mode');
|
|
763
|
+
console.log(' /context Show current session memory');
|
|
764
|
+
console.log(' /compact Compact current session into memory summary');
|
|
306
765
|
console.log(' /clear Clear conversation');
|
|
307
766
|
console.log(' /save Save session');
|
|
308
767
|
console.log(' /model <name> Change model');
|
|
309
768
|
console.log('');
|
|
310
769
|
}
|
|
770
|
+
showContext() {
|
|
771
|
+
if (!this.currentSession) {
|
|
772
|
+
console.log(chalk_1.default.yellow('No active session.'));
|
|
773
|
+
return;
|
|
774
|
+
}
|
|
775
|
+
console.log(chalk_1.default.cyan(this.getCurrentSessionInfo()));
|
|
776
|
+
if (this.currentSession.memorySummary?.trim()) {
|
|
777
|
+
console.log();
|
|
778
|
+
console.log(chalk_1.default.white('Compact Memory:'));
|
|
779
|
+
console.log(chalk_1.default.gray(this.currentSession.memorySummary.trim()));
|
|
780
|
+
}
|
|
781
|
+
else {
|
|
782
|
+
console.log(chalk_1.default.gray('No compact memory summary yet.'));
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
compactCurrentSession() {
|
|
786
|
+
if (!this.currentSession) {
|
|
787
|
+
console.log(chalk_1.default.yellow('No active session.'));
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
this.currentSession.messages = [...this.messages];
|
|
791
|
+
this.currentSession = this.sessionManager.compactInMemory(this.currentSession);
|
|
792
|
+
this.messages = [...this.currentSession.messages];
|
|
793
|
+
this.sessionManager.save(this.currentSession);
|
|
794
|
+
console.log(chalk_1.default.green('Session compacted into memory summary.'));
|
|
795
|
+
}
|
|
311
796
|
ensureAgentSystemPrompt() {
|
|
312
797
|
const hasSystemPrompt = this.messages.some((message) => message.role === 'system' && message.content.includes('Vigthoria CLI agent operating contract'));
|
|
313
798
|
if (hasSystemPrompt) {
|
|
@@ -333,6 +818,10 @@ class ChatCommand {
|
|
|
333
818
|
'Stay inside that project unless the user explicitly asks otherwise.',
|
|
334
819
|
'Read files before editing or rewriting them.',
|
|
335
820
|
'When the user asks to inspect a folder or find the right file, verify with tools before concluding.',
|
|
821
|
+
'For debugging or startup issues, inspect logs and concrete runtime evidence before proposing fixes.',
|
|
822
|
+
'Do not claim a file, asset, symbol, YAML entry, or definition is missing until you verify it with tools.',
|
|
823
|
+
'If earlier output claimed something is missing, re-check the exact file and symbol before repeating that claim.',
|
|
824
|
+
'When diagnosing failures, prefer evidence-backed conclusions over broad repo scans.',
|
|
336
825
|
'If the user corrects you, treat that as evidence that your previous assumption was wrong and re-check with tools.',
|
|
337
826
|
'Do not stop after a partial answer when more direct inspection is possible.',
|
|
338
827
|
'When you need a tool, emit only one or more tool wrappers in this exact format:',
|
|
@@ -349,7 +838,7 @@ class ChatCommand {
|
|
|
349
838
|
}
|
|
350
839
|
buildScopedUserPrompt(prompt) {
|
|
351
840
|
return [
|
|
352
|
-
prompt,
|
|
841
|
+
this.buildExecutionPrompt(prompt),
|
|
353
842
|
'',
|
|
354
843
|
`Project root: ${this.currentProjectPath}`,
|
|
355
844
|
'Stay within this project root unless the user explicitly expands scope.',
|
|
@@ -371,12 +860,14 @@ class ChatCommand {
|
|
|
371
860
|
return null;
|
|
372
861
|
}
|
|
373
862
|
buildContinuationPrompt() {
|
|
863
|
+
const diagnosticMode = this.isDiagnosticPrompt(this.lastActionableUserInput);
|
|
374
864
|
return [
|
|
375
865
|
`Tool results received for direct mode step ${this.directToolContinuationCount + 1}.`,
|
|
376
866
|
`Original user request: ${this.lastActionableUserInput}`,
|
|
377
867
|
`Project root boundary: ${this.currentProjectPath}`,
|
|
378
868
|
'Do not declare success until the exact user question has been answered with tool-backed evidence.',
|
|
379
869
|
'If a user is asking which file is correct or most recent, keep inspecting until you can justify the answer from actual results.',
|
|
870
|
+
diagnosticMode ? 'Because this is a debugging task, prefer logs, runtime evidence, and exact symbol references over generic fixes.' : 'Keep working from concrete tool results.',
|
|
380
871
|
'If the request is already satisfied, return a concise completion summary and no tool calls.',
|
|
381
872
|
'If more work is required, continue with only the next minimal tool calls needed to finish it.',
|
|
382
873
|
'Do not ask follow-up questions or drift into unrelated tasks.',
|
|
@@ -472,12 +963,15 @@ class ChatCommand {
|
|
|
472
963
|
}
|
|
473
964
|
saveSession() {
|
|
474
965
|
if (!this.currentSession) {
|
|
475
|
-
this.currentSession = this.sessionManager.create(this.currentProjectPath, this.currentModel, this.agentMode);
|
|
966
|
+
this.currentSession = this.sessionManager.create(this.currentProjectPath, this.currentModel, this.agentMode, this.operatorMode);
|
|
476
967
|
}
|
|
477
968
|
this.currentSession.project = this.currentProjectPath;
|
|
478
969
|
this.currentSession.model = this.currentModel;
|
|
479
970
|
this.currentSession.agentMode = this.agentMode;
|
|
971
|
+
this.currentSession.operatorMode = this.operatorMode;
|
|
480
972
|
this.currentSession.messages = [...this.messages];
|
|
973
|
+
this.currentSession = this.sessionManager.compactInMemory(this.currentSession);
|
|
974
|
+
this.messages = [...this.currentSession.messages];
|
|
481
975
|
this.sessionManager.save(this.currentSession);
|
|
482
976
|
}
|
|
483
977
|
async requestPermission(action) {
|
|
@@ -503,11 +997,11 @@ class ChatCommand {
|
|
|
503
997
|
if (!this.currentSession) {
|
|
504
998
|
return 'No active session';
|
|
505
999
|
}
|
|
506
|
-
|
|
1000
|
+
const compacted = this.currentSession.memorySummary ? ' with compact memory' : '';
|
|
1001
|
+
return `Session: ${this.currentSession.name} (${this.currentSession.messages.length} messages${compacted})`;
|
|
507
1002
|
}
|
|
508
1003
|
getChatHistory() {
|
|
509
1004
|
return [...this.messages];
|
|
510
1005
|
}
|
|
511
1006
|
}
|
|
512
1007
|
exports.ChatCommand = ChatCommand;
|
|
513
|
-
//# sourceMappingURL=chat.js.map
|