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.
@@ -678,7 +678,9 @@ class ChatCommand {
678
678
  return false;
679
679
  }
680
680
  async handleDirectPrompt(prompt) {
681
- if (!this.jsonOutput) {
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 — if the agent tries to conclude on the first
1194
- // turn without any discovery, push it to gather evidence first.
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
- if (turn === 0 && this.agentToolEvidence.discovery === 0 && (this.isDiagnosticPrompt(prompt) || this.directPromptMode || isPolicyAck)) {
1201
- // Remove the useless acknowledgement from history so it
1202
- // doesn't anchor the model in the same pattern.
1203
- if (isPolicyAck) {
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: you responded with text instead of tool calls.',
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
- `Start by running: list_dir on the project root, then read_file on files relevant to: ${prompt}`,
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
- console.log(chalk_1.default.gray(`Agent routing: ${routingPolicy.cloudSelected ? 'Vigthoria Cloud' : 'V3 Agent'} via model ${routingPolicy.selectedModel}`));
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
- console.log(chalk_1.default.gray(`Agent routing: ${routingPolicy.cloudSelected ? 'Vigthoria Cloud' : 'V3 Agent'} via model ${routingPolicy.selectedModel}`));
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 diagnosticMode = this.isDiagnosticPrompt(this.lastActionableUserInput);
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 so far (list_dir, glob, read_file, grep). Use at least 2 before concluding.`);
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 tool call(s) failed. Do not treat failed searches as evidence that something is missing.`);
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
- `Tool results received for direct mode step ${this.directToolContinuationCount + 1}.`,
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
- 'Do not declare success until the exact user question has been answered with tool-backed evidence.',
1991
- 'If a user is asking which file is correct or most recent, keep inspecting until you can justify the answer from actual results.',
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
- const normalized = (visibleText || '').trim();
2157
- if (normalized && !this.isDirectModeFollowUpQuestion(normalized)) {
2158
- return this.sanitizeDirectModeOutput(normalized);
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 normalized || 'Task complete.';
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 multi-line tool-output blocks ──
2175
- // Pattern: "Tool <name> succeeded/FAILED." followed by optional
2176
- // metadata lines (File:, Search status:, Output:) and then a
2177
- // content block until the next double-newline or end-of-string.
2178
- cleaned = cleaned.replace(/Tool (?:read_file|grep|list_dir|glob|bash|write_file) (?:succeeded|FAILED)\.[\s\S]*?(?=\n\n[A-Z_]+:|$)/g, '');
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 (!this.jsonOutput) {
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 (!this.jsonOutput) {
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 (!this.jsonOutput) {
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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vigthoria-cli",
3
- "version": "1.6.46",
3
+ "version": "1.6.49",
4
4
  "description": "Vigthoria Coder CLI - AI-powered terminal coding assistant",
5
5
  "main": "dist/index.js",
6
6
  "files": [