vigthoria-cli 1.6.46 → 1.6.49
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/chat.js +70 -52
- package/dist/index.js +3 -2
- package/package.json +1 -1
package/dist/commands/chat.js
CHANGED
|
@@ -678,7 +678,9 @@ class ChatCommand {
|
|
|
678
678
|
return false;
|
|
679
679
|
}
|
|
680
680
|
async handleDirectPrompt(prompt) {
|
|
681
|
-
|
|
681
|
+
// Suppress all setup banners in direct-prompt mode so only the final
|
|
682
|
+
// answer reaches stdout. Interactive (REPL) mode still shows them.
|
|
683
|
+
if (!this.jsonOutput && !this.directPromptMode) {
|
|
682
684
|
console.log(chalk_1.default.cyan('Running single prompt in direct mode.'));
|
|
683
685
|
console.log(chalk_1.default.gray(`Model: ${this.currentModel}`));
|
|
684
686
|
console.log(chalk_1.default.gray(`Project: ${this.currentProjectPath}`));
|
|
@@ -1190,26 +1192,37 @@ class ChatCommand {
|
|
|
1190
1192
|
preview: visibleText.slice(0, 300),
|
|
1191
1193
|
});
|
|
1192
1194
|
if (toolCalls.length === 0) {
|
|
1193
|
-
// Phase 5: Quality gate —
|
|
1194
|
-
//
|
|
1195
|
-
// Applies to diagnostic prompts AND any direct-prompt agent call where
|
|
1196
|
-
// the model failed to invoke tools (prevents truncated output).
|
|
1197
|
-
// Also catches policy-acknowledgement responses ("I will follow…",
|
|
1198
|
-
// "I understand the instructions…") which are never useful.
|
|
1195
|
+
// Phase 5: Quality gate — reject non-answers and push the model
|
|
1196
|
+
// to gather real evidence.
|
|
1199
1197
|
const isPolicyAck = /^(i will follow|i understand|i('ll| will) adhere|understood[.,!]|sure[.,!]|i('ll| will) use the tools|i('ll| will) proceed|let me know|provide your|waiting for)/i.test(visibleText.trim());
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1198
|
+
// Detect if the response is effectively empty or just a follow-up
|
|
1199
|
+
// question after we strip tool output echoes.
|
|
1200
|
+
const sanitized = this.sanitizeDirectModeOutput(visibleText.trim());
|
|
1201
|
+
const isFollowUp = this.isDirectModeFollowUpQuestion(sanitized);
|
|
1202
|
+
const isEmptyAfterSanitize = !sanitized || sanitized.length < 10;
|
|
1203
|
+
// Detect resignation: model gives up saying files/things were "not found"
|
|
1204
|
+
// without having tried list_dir to discover the correct path.
|
|
1205
|
+
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;
|
|
1206
|
+
// Gate 1: First turn with no discovery at all
|
|
1207
|
+
const gate1 = turn === 0 && this.agentToolEvidence.discovery === 0 && (this.isDiagnosticPrompt(prompt) || this.directPromptMode || isPolicyAck);
|
|
1208
|
+
// Gate 2: Any turn where the response is just a follow-up question,
|
|
1209
|
+
// tool-failure echoes, or premature resignation (the model gave up
|
|
1210
|
+
// instead of retrying with list_dir to find the correct paths)
|
|
1211
|
+
const gate2 = this.directPromptMode && turn < 6 && (isPolicyAck || isFollowUp || isEmptyAfterSanitize || isResignation);
|
|
1212
|
+
if (gate1 || gate2) {
|
|
1213
|
+
// Remove the useless response from history
|
|
1214
|
+
if (isPolicyAck || isFollowUp || isEmptyAfterSanitize || isResignation) {
|
|
1204
1215
|
this.messages.pop();
|
|
1205
1216
|
}
|
|
1217
|
+
const hint = isResignation
|
|
1218
|
+
? 'Files were not found at the guessed paths. Use list_dir to discover the correct directory structure, then read_file with the correct paths.'
|
|
1219
|
+
: `Start by running: list_dir on the project root, then read_file on files relevant to: ${prompt}`;
|
|
1206
1220
|
this.messages.push({
|
|
1207
1221
|
role: 'system',
|
|
1208
1222
|
content: [
|
|
1209
|
-
'Quality gate:
|
|
1210
|
-
'Your response was rejected. Do NOT acknowledge instructions or describe your plan.',
|
|
1223
|
+
'Quality gate: your response was rejected because it did not answer the user\'s question.',
|
|
1211
1224
|
'You MUST use tools to gather concrete evidence IMMEDIATELY.',
|
|
1212
|
-
|
|
1225
|
+
hint,
|
|
1213
1226
|
'Respond ONLY with <tool_call> blocks. No other text.',
|
|
1214
1227
|
].join('\n'),
|
|
1215
1228
|
});
|
|
@@ -1303,12 +1316,12 @@ class ChatCommand {
|
|
|
1303
1316
|
tool: 'read_file',
|
|
1304
1317
|
args: { path: targetFile },
|
|
1305
1318
|
};
|
|
1306
|
-
if (!this.jsonOutput) {
|
|
1319
|
+
if (!this.jsonOutput && !this.directPromptMode) {
|
|
1307
1320
|
console.log(chalk_1.default.cyan(`⚙ Executing: ${readCall.tool}`));
|
|
1308
1321
|
}
|
|
1309
1322
|
const readResult = await this.tools.execute(readCall);
|
|
1310
1323
|
const readSummary = this.formatToolResult(readCall, readResult);
|
|
1311
|
-
if (!this.jsonOutput) {
|
|
1324
|
+
if (!this.jsonOutput && !this.directPromptMode) {
|
|
1312
1325
|
console.log(readResult.success ? chalk_1.default.gray(readSummary) : chalk_1.default.red(readSummary));
|
|
1313
1326
|
}
|
|
1314
1327
|
this.messages.push({ role: 'system', content: readSummary });
|
|
@@ -1351,12 +1364,12 @@ class ChatCommand {
|
|
|
1351
1364
|
content: rewrittenContent,
|
|
1352
1365
|
},
|
|
1353
1366
|
};
|
|
1354
|
-
if (!this.jsonOutput) {
|
|
1367
|
+
if (!this.jsonOutput && !this.directPromptMode) {
|
|
1355
1368
|
console.log(chalk_1.default.cyan(`⚙ Executing: ${writeCall.tool}`));
|
|
1356
1369
|
}
|
|
1357
1370
|
const writeResult = await this.tools.execute(writeCall);
|
|
1358
1371
|
const writeSummary = this.formatToolResult(writeCall, writeResult);
|
|
1359
|
-
if (!this.jsonOutput) {
|
|
1372
|
+
if (!this.jsonOutput && !this.directPromptMode) {
|
|
1360
1373
|
console.log(writeResult.success ? chalk_1.default.gray(writeSummary) : chalk_1.default.red(writeSummary));
|
|
1361
1374
|
}
|
|
1362
1375
|
this.messages.push({ role: 'system', content: writeSummary });
|
|
@@ -1503,14 +1516,18 @@ class ChatCommand {
|
|
|
1503
1516
|
}, null, 2));
|
|
1504
1517
|
}
|
|
1505
1518
|
else if (response.content) {
|
|
1506
|
-
|
|
1519
|
+
if (!this.directPromptMode) {
|
|
1520
|
+
console.log(chalk_1.default.gray(`Agent routing: ${routingPolicy.cloudSelected ? 'Vigthoria Cloud' : 'V3 Agent'} via model ${routingPolicy.selectedModel}`));
|
|
1521
|
+
}
|
|
1507
1522
|
console.log(response.content);
|
|
1508
1523
|
}
|
|
1509
1524
|
else {
|
|
1510
|
-
|
|
1525
|
+
if (!this.directPromptMode) {
|
|
1526
|
+
console.log(chalk_1.default.gray(`Agent routing: ${routingPolicy.cloudSelected ? 'Vigthoria Cloud' : 'V3 Agent'} via model ${routingPolicy.selectedModel}`));
|
|
1527
|
+
}
|
|
1511
1528
|
console.log('V3 agent workflow completed.');
|
|
1512
1529
|
}
|
|
1513
|
-
if (!this.jsonOutput && previewGate?.required) {
|
|
1530
|
+
if (!this.jsonOutput && !this.directPromptMode && previewGate?.required) {
|
|
1514
1531
|
if (previewGate.passed) {
|
|
1515
1532
|
console.log(chalk_1.default.gray(`Template Service preview gate: passed via ${previewGate.backendUrl || 'unknown backend'}`));
|
|
1516
1533
|
}
|
|
@@ -1967,35 +1984,26 @@ class ChatCommand {
|
|
|
1967
1984
|
return protectedPatterns.some((pattern) => pattern.test(prompt));
|
|
1968
1985
|
}
|
|
1969
1986
|
buildContinuationPrompt() {
|
|
1970
|
-
const
|
|
1971
|
-
const { discovery, mutation, searchFailed } = this.agentToolEvidence;
|
|
1987
|
+
const { discovery, searchFailed } = this.agentToolEvidence;
|
|
1972
1988
|
const evidenceLines = [];
|
|
1973
1989
|
if (discovery < 2) {
|
|
1974
|
-
evidenceLines.push(`Quality gate: only ${discovery} discovery tool(s) used
|
|
1990
|
+
evidenceLines.push(`Quality gate: only ${discovery} discovery tool(s) used. Use at least 2 before concluding.`);
|
|
1975
1991
|
}
|
|
1976
1992
|
if (searchFailed > 0) {
|
|
1977
|
-
evidenceLines.push(`Warning: ${searchFailed} search
|
|
1993
|
+
evidenceLines.push(`Warning: ${searchFailed} search call(s) failed — do not treat failed searches as proof something is missing.`);
|
|
1978
1994
|
}
|
|
1979
1995
|
// Cross-file overlap: extract Key* identifiers per file from tool results
|
|
1980
1996
|
const crossFileEvidence = this.computeCrossFileKeyEvidence();
|
|
1981
1997
|
if (crossFileEvidence) {
|
|
1982
1998
|
evidenceLines.push(crossFileEvidence);
|
|
1983
1999
|
}
|
|
2000
|
+
// Keep the continuation prompt SHORT — the system prompt already has the
|
|
2001
|
+
// full grounding rules. Repeating them here causes the model to echo them.
|
|
1984
2002
|
return [
|
|
1985
|
-
`
|
|
1986
|
-
`Original user request: ${this.lastActionableUserInput}`,
|
|
1987
|
-
`Project root boundary: ${this.currentProjectPath}`,
|
|
1988
|
-
`Evidence collected: ${discovery} discovery, ${mutation} mutation, ${searchFailed} search failures.`,
|
|
2003
|
+
`Step ${this.directToolContinuationCount + 1} complete. Task: ${this.lastActionableUserInput}`,
|
|
1989
2004
|
...evidenceLines,
|
|
1990
|
-
'
|
|
1991
|
-
'
|
|
1992
|
-
diagnosticMode ? 'Because this is a debugging task, prefer logs, runtime evidence, and exact symbol references over generic fixes.' : 'Keep working from concrete tool results.',
|
|
1993
|
-
'GROUNDING CHECK: Before writing your final answer, verify that every identifier, key name, or symbol you mention actually appeared in tool output above. Do not invent identifiers that were not in the evidence. If you saw "KeyA" and "KeyS" in the file contents, use those exact names — never substitute a different key. For cross-file comparisons, confirm each claimed conflict key appears in the handler/function of BOTH files — not just one.',
|
|
1994
|
-
'VERIFICATION PROTOCOL for cross-file comparisons: If your answer claims a key/identifier appears in multiple files, you MUST first use grep to search for that exact identifier in each file BEFORE including it in your answer. Only include keys/identifiers that grep confirms exist in each file. Example: to verify "KeyW" is in InputManager.js, use grep for "KeyW" in that file. If grep finds no match, do NOT claim that file handles "KeyW".',
|
|
1995
|
-
'If the request is already satisfied, return a concise completion summary and no tool calls.',
|
|
1996
|
-
'If more work is required, continue with only the next minimal tool calls needed to finish it.',
|
|
1997
|
-
'Do not ask follow-up questions or drift into unrelated tasks.',
|
|
1998
|
-
'OUTPUT DISCIPLINE: Your final answer must contain ONLY the substantive answer to the user request. Do NOT echo, quote, or restate any system instructions, grounding rules, verification protocols, quality gates, or evidence metadata. Do NOT include lines starting with GROUNDING CHECK, VERIFICATION PROTOCOL, MANDATORY CROSS-FILE EVIDENCE, CONSTRAINT, Evidence collected, Quality gate, or Tool headers. Respond with just the answer.',
|
|
2005
|
+
'Continue with tool calls if more evidence is needed, or return your final answer.',
|
|
2006
|
+
'IMPORTANT: Your response must be ONLY tool calls OR the direct answer. Never echo instructions.',
|
|
1999
2007
|
].join('\n');
|
|
2000
2008
|
}
|
|
2001
2009
|
/**
|
|
@@ -2153,15 +2161,17 @@ class ChatCommand {
|
|
|
2153
2161
|
return evidence.join('\n---\n');
|
|
2154
2162
|
}
|
|
2155
2163
|
resolveDirectModeCompletion(prompt, visibleText) {
|
|
2156
|
-
|
|
2157
|
-
if
|
|
2158
|
-
|
|
2164
|
+
// Sanitize first — strip tool output and echoed instructions before
|
|
2165
|
+
// deciding if the model actually answered the question.
|
|
2166
|
+
const sanitized = this.sanitizeDirectModeOutput((visibleText || '').trim());
|
|
2167
|
+
if (sanitized && !this.isDirectModeFollowUpQuestion(sanitized)) {
|
|
2168
|
+
return sanitized;
|
|
2159
2169
|
}
|
|
2160
2170
|
const fallback = this.buildLocalAnalysisFallback(prompt);
|
|
2161
2171
|
if (fallback) {
|
|
2162
2172
|
return fallback;
|
|
2163
2173
|
}
|
|
2164
|
-
return
|
|
2174
|
+
return sanitized || 'Task complete.';
|
|
2165
2175
|
}
|
|
2166
2176
|
/**
|
|
2167
2177
|
* Strip system-prompt echoes, tool execution headers, grounding-rule
|
|
@@ -2171,13 +2181,11 @@ class ChatCommand {
|
|
|
2171
2181
|
*/
|
|
2172
2182
|
sanitizeDirectModeOutput(text) {
|
|
2173
2183
|
let cleaned = text;
|
|
2174
|
-
// ── Phase 1: Strip entire
|
|
2175
|
-
//
|
|
2176
|
-
//
|
|
2177
|
-
//
|
|
2178
|
-
cleaned = cleaned.replace(/Tool (?:read_file|grep|list_dir|glob|bash|write_file) (?:succeeded|FAILED)\.[\s\S]*?(?=\n\n
|
|
2179
|
-
// Fallback: simpler block pattern for any remaining tool headers
|
|
2180
|
-
cleaned = cleaned.replace(/Tool (?:read_file|grep|list_dir|glob|bash|write_file) (?:succeeded|FAILED)\.[\s\S]*?(?:\n\n|\s*$)/g, '');
|
|
2184
|
+
// ── Phase 1: Strip entire tool-output blocks ──
|
|
2185
|
+
// Matches "Tool <name> succeeded/FAILED." through the next blank line,
|
|
2186
|
+
// next tool header, or end-of-string. The DOTALL-like [\s\S]*? is
|
|
2187
|
+
// terminated by whichever boundary comes first.
|
|
2188
|
+
cleaned = cleaned.replace(/Tool (?:read_file|grep|list_dir|glob|bash|write_file|edit_file|ssh_exec) (?:succeeded|FAILED)\.[\s\S]*?(?=\nTool |\n\n|$)/g, '');
|
|
2181
2189
|
// ── Phase 2: Strip echoed system-prompt / grounding lines ──
|
|
2182
2190
|
const contaminationPatterns = [
|
|
2183
2191
|
/^\[Agent recovered from backend failure[^\]]*\]\s*/m,
|
|
@@ -2192,11 +2200,15 @@ class ChatCommand {
|
|
|
2192
2200
|
/^Evidence collected:[^\n]*/m,
|
|
2193
2201
|
/^Warning: \d+ search tool[^\n]*/m,
|
|
2194
2202
|
/^Tool results received for direct mode[^\n]*/m,
|
|
2203
|
+
/^Step \d+ complete\. Task:[^\n]*/m,
|
|
2195
2204
|
/^Original user request:[^\n]*/m,
|
|
2196
2205
|
/^Project root boundary:[^\n]*/m,
|
|
2197
2206
|
/^Do not declare success[^\n]*/m,
|
|
2198
2207
|
/^Keep working from concrete[^\n]*/m,
|
|
2208
|
+
/^Continue with tool calls if more[^\n]*/m,
|
|
2209
|
+
/^IMPORTANT: Your response must be ONLY[^\n]*/m,
|
|
2199
2210
|
/^Because this is a debugging[^\n]*/m,
|
|
2211
|
+
/^If a user is asking which file[^\n]*/m,
|
|
2200
2212
|
/^If the request is already[^\n]*/m,
|
|
2201
2213
|
/^If more work is required[^\n]*/m,
|
|
2202
2214
|
/^Do not ask follow-up[^\n]*/m,
|
|
@@ -2205,6 +2217,9 @@ class ChatCommand {
|
|
|
2205
2217
|
/^EVIDENCE-GROUNDING RULE:[^\n]*/m,
|
|
2206
2218
|
/^CROSS-FILE RULE:[^\n]*/m,
|
|
2207
2219
|
/^OUTPUT DISCIPLINE:[^\n]*/m,
|
|
2220
|
+
/^Vigthoria CLI agent operating contract\.[^\n]*/m,
|
|
2221
|
+
/^You are operating inside the project root:[^\n]*/m,
|
|
2222
|
+
/^CRITICAL: Begin working on the user's task[^\n]*/m,
|
|
2208
2223
|
/^File: \S+\s*$/m,
|
|
2209
2224
|
/^Search status: \S+\s*$/m,
|
|
2210
2225
|
/^Output:\s*$/m,
|
|
@@ -2324,8 +2339,11 @@ class ChatCommand {
|
|
|
2324
2339
|
if (!this.tools) {
|
|
2325
2340
|
throw new Error('Agent tools are not initialized.');
|
|
2326
2341
|
}
|
|
2342
|
+
// In direct-prompt mode (--prompt), suppress verbose tool output so
|
|
2343
|
+
// only the final answer prints. In interactive mode, show full detail.
|
|
2344
|
+
const verbose = !this.jsonOutput && !this.directPromptMode;
|
|
2327
2345
|
for (const call of toolCalls) {
|
|
2328
|
-
if (
|
|
2346
|
+
if (verbose) {
|
|
2329
2347
|
console.log(chalk_1.default.cyan(`⚙ Executing: ${call.tool}`));
|
|
2330
2348
|
}
|
|
2331
2349
|
(0, bridge_client_js_1.getBridgeClient)()?.emitToolCall({ tool: call.tool, args: call.args });
|
|
@@ -2333,7 +2351,7 @@ class ChatCommand {
|
|
|
2333
2351
|
// Phase 2: If a search tool failed (search_failed), retry with alternate approach
|
|
2334
2352
|
const searchStatus = result.metadata?.searchStatus;
|
|
2335
2353
|
if (call.tool === 'grep' && searchStatus === 'search_failed') {
|
|
2336
|
-
if (
|
|
2354
|
+
if (verbose) {
|
|
2337
2355
|
console.log(chalk_1.default.yellow(`⚠ Search backend failed, retrying with alternate method...`));
|
|
2338
2356
|
}
|
|
2339
2357
|
// Force Node-native fallback by re-executing with a note
|
|
@@ -2346,7 +2364,7 @@ class ChatCommand {
|
|
|
2346
2364
|
}
|
|
2347
2365
|
}
|
|
2348
2366
|
const summary = this.formatToolResult(call, result);
|
|
2349
|
-
if (
|
|
2367
|
+
if (verbose) {
|
|
2350
2368
|
console.log(result.success ? chalk_1.default.gray(summary) : chalk_1.default.red(summary));
|
|
2351
2369
|
}
|
|
2352
2370
|
this.messages.push({ role: 'system', content: summary });
|
package/dist/index.js
CHANGED
|
@@ -155,6 +155,7 @@ async function main() {
|
|
|
155
155
|
const invokedBinaryName = getInvokedBinaryName();
|
|
156
156
|
const firstArg = process.argv[2];
|
|
157
157
|
const jsonOutputRequested = process.argv.includes('--json');
|
|
158
|
+
const directPromptRequested = process.argv.includes('--prompt') || process.argv.includes('-P');
|
|
158
159
|
if (invokedBinaryName === 'vigthoria-chat') {
|
|
159
160
|
const knownCommands = new Set([
|
|
160
161
|
'chat', 'chat-resume', 'agent', 'edit', 'generate', 'explain', 'fix', 'review',
|
|
@@ -172,7 +173,7 @@ async function main() {
|
|
|
172
173
|
// Calculate padding for centering
|
|
173
174
|
const titlePad = Math.floor((boxWidth - 4 - titleText.length) / 2);
|
|
174
175
|
const versionPad = Math.floor((boxWidth - 4 - versionText.length) / 2);
|
|
175
|
-
if (process.env.VIGTHORIA_NO_BANNER !== '1' && !jsonOutputRequested) {
|
|
176
|
+
if (process.env.VIGTHORIA_NO_BANNER !== '1' && !jsonOutputRequested && !directPromptRequested) {
|
|
176
177
|
console.log(chalk_1.default.cyan(logger_js_1.CH.dTl + logger_js_1.CH.dH.repeat(boxWidth) + logger_js_1.CH.dTr));
|
|
177
178
|
console.log(chalk_1.default.cyan(logger_js_1.CH.dV + ' '.repeat(boxWidth) + logger_js_1.CH.dV));
|
|
178
179
|
console.log(chalk_1.default.cyan(logger_js_1.CH.dV) + ' '.repeat(titlePad) + chalk_1.default.bold.white('VIGTHORIA CLI') + chalk_1.default.cyan(' - AI-Powered Coding Assistant') + ' '.repeat(boxWidth - titlePad - titleText.length) + chalk_1.default.cyan(logger_js_1.CH.dV));
|
|
@@ -182,7 +183,7 @@ async function main() {
|
|
|
182
183
|
console.log();
|
|
183
184
|
}
|
|
184
185
|
// Check for updates in background (don't wait)
|
|
185
|
-
if (process.env.VIGTHORIA_NO_UPDATE_CHECK !== '1' && !jsonOutputRequested) {
|
|
186
|
+
if (process.env.VIGTHORIA_NO_UPDATE_CHECK !== '1' && !jsonOutputRequested && !directPromptRequested) {
|
|
186
187
|
checkForUpdatesQuietly();
|
|
187
188
|
}
|
|
188
189
|
program
|