vigthoria-cli 1.6.48 → 1.6.50

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.
@@ -1192,26 +1192,44 @@ class ChatCommand {
1192
1192
  preview: visibleText.slice(0, 300),
1193
1193
  });
1194
1194
  if (toolCalls.length === 0) {
1195
- // Phase 5: Quality gate — if the agent tries to conclude on the first
1196
- // turn without any discovery, push it to gather evidence first.
1197
- // Applies to diagnostic prompts AND any direct-prompt agent call where
1198
- // the model failed to invoke tools (prevents truncated output).
1199
- // Also catches policy-acknowledgement responses ("I will follow…",
1200
- // "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.
1201
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());
1202
- if (turn === 0 && this.agentToolEvidence.discovery === 0 && (this.isDiagnosticPrompt(prompt) || this.directPromptMode || isPolicyAck)) {
1203
- // Remove the useless acknowledgement from history so it
1204
- // doesn't anchor the model in the same pattern.
1205
- 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
+ // Gate 3: Model outputs code blocks as text instead of using write_file.
1213
+ // If the response contains ``` code fences but no write_file was called,
1214
+ // reject and instruct the model to use write_file.
1215
+ const hasCodeBlocks = (sanitized.match(/```/g) || []).length >= 2;
1216
+ const gate3 = hasCodeBlocks && this.agentToolEvidence.mutation === 0 && turn < 6;
1217
+ if (gate1 || gate2 || gate3) {
1218
+ // Remove the useless response from history
1219
+ if (isPolicyAck || isFollowUp || isEmptyAfterSanitize || isResignation || gate3) {
1206
1220
  this.messages.pop();
1207
1221
  }
1222
+ const hint = gate3
1223
+ ? 'You output code as text instead of writing files. Use write_file to create each file on disk. Do NOT output code in markdown fences — call write_file for every file.'
1224
+ : isResignation
1225
+ ? 'Files were not found at the guessed paths. Use list_dir to discover the correct directory structure, then read_file with the correct paths.'
1226
+ : `Start by running: list_dir on the project root, then read_file on files relevant to: ${prompt}`;
1208
1227
  this.messages.push({
1209
1228
  role: 'system',
1210
1229
  content: [
1211
- 'Quality gate: you responded with text instead of tool calls.',
1212
- 'Your response was rejected. Do NOT acknowledge instructions or describe your plan.',
1230
+ 'Quality gate: your response was rejected because it did not answer the user\'s question.',
1213
1231
  'You MUST use tools to gather concrete evidence IMMEDIATELY.',
1214
- `Start by running: list_dir on the project root, then read_file on files relevant to: ${prompt}`,
1232
+ hint,
1215
1233
  'Respond ONLY with <tool_call> blocks. No other text.',
1216
1234
  ].join('\n'),
1217
1235
  });
@@ -1796,6 +1814,7 @@ class ChatCommand {
1796
1814
  'Vigthoria CLI agent operating contract.',
1797
1815
  `You are operating inside the project root: ${this.currentProjectPath}`,
1798
1816
  'CRITICAL: Begin working on the user\'s task IMMEDIATELY. Your very first response MUST contain <tool_call> blocks to gather evidence. Do NOT acknowledge instructions, restate the task, describe your plan, or discuss tool policies. Act, do not talk.',
1817
+ 'FILE CREATION RULE: When the user asks you to build, create, or generate files, you MUST use the write_file tool to actually create each file on disk. NEVER output code snippets as markdown text — always write them to files using write_file. If the task requires a new project, create a new directory first, then write all files into it.',
1799
1818
  'Stay inside that project unless the user explicitly asks otherwise.',
1800
1819
  'Read files before editing or rewriting them.',
1801
1820
  'When the user asks to inspect a folder or find the right file, verify with tools before concluding.',
@@ -1973,35 +1992,26 @@ class ChatCommand {
1973
1992
  return protectedPatterns.some((pattern) => pattern.test(prompt));
1974
1993
  }
1975
1994
  buildContinuationPrompt() {
1976
- const diagnosticMode = this.isDiagnosticPrompt(this.lastActionableUserInput);
1977
- const { discovery, mutation, searchFailed } = this.agentToolEvidence;
1995
+ const { discovery, searchFailed } = this.agentToolEvidence;
1978
1996
  const evidenceLines = [];
1979
1997
  if (discovery < 2) {
1980
- evidenceLines.push(`Quality gate: only ${discovery} discovery tool(s) used so far (list_dir, glob, read_file, grep). Use at least 2 before concluding.`);
1998
+ evidenceLines.push(`Quality gate: only ${discovery} discovery tool(s) used. Use at least 2 before concluding.`);
1981
1999
  }
1982
2000
  if (searchFailed > 0) {
1983
- evidenceLines.push(`Warning: ${searchFailed} search tool call(s) failed. Do not treat failed searches as evidence that something is missing.`);
2001
+ evidenceLines.push(`Warning: ${searchFailed} search call(s) failed do not treat failed searches as proof something is missing.`);
1984
2002
  }
1985
2003
  // Cross-file overlap: extract Key* identifiers per file from tool results
1986
2004
  const crossFileEvidence = this.computeCrossFileKeyEvidence();
1987
2005
  if (crossFileEvidence) {
1988
2006
  evidenceLines.push(crossFileEvidence);
1989
2007
  }
2008
+ // Keep the continuation prompt SHORT — the system prompt already has the
2009
+ // full grounding rules. Repeating them here causes the model to echo them.
1990
2010
  return [
1991
- `Tool results received for direct mode step ${this.directToolContinuationCount + 1}.`,
1992
- `Original user request: ${this.lastActionableUserInput}`,
1993
- `Project root boundary: ${this.currentProjectPath}`,
1994
- `Evidence collected: ${discovery} discovery, ${mutation} mutation, ${searchFailed} search failures.`,
2011
+ `Step ${this.directToolContinuationCount + 1} complete. Task: ${this.lastActionableUserInput}`,
1995
2012
  ...evidenceLines,
1996
- 'Do not declare success until the exact user question has been answered with tool-backed evidence.',
1997
- 'If a user is asking which file is correct or most recent, keep inspecting until you can justify the answer from actual results.',
1998
- diagnosticMode ? 'Because this is a debugging task, prefer logs, runtime evidence, and exact symbol references over generic fixes.' : 'Keep working from concrete tool results.',
1999
- '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.',
2000
- '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".',
2001
- 'If the request is already satisfied, return a concise completion summary and no tool calls.',
2002
- 'If more work is required, continue with only the next minimal tool calls needed to finish it.',
2003
- 'Do not ask follow-up questions or drift into unrelated tasks.',
2004
- '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.',
2013
+ 'Continue with tool calls if more evidence is needed, or return your final answer.',
2014
+ 'IMPORTANT: Your response must be ONLY tool calls OR the direct answer. Never echo instructions.',
2005
2015
  ].join('\n');
2006
2016
  }
2007
2017
  /**
@@ -2159,15 +2169,17 @@ class ChatCommand {
2159
2169
  return evidence.join('\n---\n');
2160
2170
  }
2161
2171
  resolveDirectModeCompletion(prompt, visibleText) {
2162
- const normalized = (visibleText || '').trim();
2163
- if (normalized && !this.isDirectModeFollowUpQuestion(normalized)) {
2164
- return this.sanitizeDirectModeOutput(normalized);
2172
+ // Sanitize first strip tool output and echoed instructions before
2173
+ // deciding if the model actually answered the question.
2174
+ const sanitized = this.sanitizeDirectModeOutput((visibleText || '').trim());
2175
+ if (sanitized && !this.isDirectModeFollowUpQuestion(sanitized)) {
2176
+ return sanitized;
2165
2177
  }
2166
2178
  const fallback = this.buildLocalAnalysisFallback(prompt);
2167
2179
  if (fallback) {
2168
2180
  return fallback;
2169
2181
  }
2170
- return normalized || 'Task complete.';
2182
+ return sanitized || 'Task complete.';
2171
2183
  }
2172
2184
  /**
2173
2185
  * Strip system-prompt echoes, tool execution headers, grounding-rule
@@ -2177,13 +2189,11 @@ class ChatCommand {
2177
2189
  */
2178
2190
  sanitizeDirectModeOutput(text) {
2179
2191
  let cleaned = text;
2180
- // ── Phase 1: Strip entire multi-line tool-output blocks ──
2181
- // Pattern: "Tool <name> succeeded/FAILED." followed by optional
2182
- // metadata lines (File:, Search status:, Output:) and then a
2183
- // content block until the next double-newline or end-of-string.
2184
- cleaned = cleaned.replace(/Tool (?:read_file|grep|list_dir|glob|bash|write_file) (?:succeeded|FAILED)\.[\s\S]*?(?=\n\n[A-Z_]+:|$)/g, '');
2185
- // Fallback: simpler block pattern for any remaining tool headers
2186
- cleaned = cleaned.replace(/Tool (?:read_file|grep|list_dir|glob|bash|write_file) (?:succeeded|FAILED)\.[\s\S]*?(?:\n\n|\s*$)/g, '');
2192
+ // ── Phase 1: Strip entire tool-output blocks ──
2193
+ // Matches "Tool <name> succeeded/FAILED." through the next blank line,
2194
+ // next tool header, or end-of-string. The DOTALL-like [\s\S]*? is
2195
+ // terminated by whichever boundary comes first.
2196
+ 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, '');
2187
2197
  // ── Phase 2: Strip echoed system-prompt / grounding lines ──
2188
2198
  const contaminationPatterns = [
2189
2199
  /^\[Agent recovered from backend failure[^\]]*\]\s*/m,
@@ -2198,11 +2208,15 @@ class ChatCommand {
2198
2208
  /^Evidence collected:[^\n]*/m,
2199
2209
  /^Warning: \d+ search tool[^\n]*/m,
2200
2210
  /^Tool results received for direct mode[^\n]*/m,
2211
+ /^Step \d+ complete\. Task:[^\n]*/m,
2201
2212
  /^Original user request:[^\n]*/m,
2202
2213
  /^Project root boundary:[^\n]*/m,
2203
2214
  /^Do not declare success[^\n]*/m,
2204
2215
  /^Keep working from concrete[^\n]*/m,
2216
+ /^Continue with tool calls if more[^\n]*/m,
2217
+ /^IMPORTANT: Your response must be ONLY[^\n]*/m,
2205
2218
  /^Because this is a debugging[^\n]*/m,
2219
+ /^If a user is asking which file[^\n]*/m,
2206
2220
  /^If the request is already[^\n]*/m,
2207
2221
  /^If more work is required[^\n]*/m,
2208
2222
  /^Do not ask follow-up[^\n]*/m,
@@ -2211,6 +2225,9 @@ class ChatCommand {
2211
2225
  /^EVIDENCE-GROUNDING RULE:[^\n]*/m,
2212
2226
  /^CROSS-FILE RULE:[^\n]*/m,
2213
2227
  /^OUTPUT DISCIPLINE:[^\n]*/m,
2228
+ /^Vigthoria CLI agent operating contract\.[^\n]*/m,
2229
+ /^You are operating inside the project root:[^\n]*/m,
2230
+ /^CRITICAL: Begin working on the user's task[^\n]*/m,
2214
2231
  /^File: \S+\s*$/m,
2215
2232
  /^Search status: \S+\s*$/m,
2216
2233
  /^Output:\s*$/m,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vigthoria-cli",
3
- "version": "1.6.48",
3
+ "version": "1.6.50",
4
4
  "description": "Vigthoria Coder CLI - AI-powered terminal coding assistant",
5
5
  "main": "dist/index.js",
6
6
  "files": [
@@ -86,4 +86,4 @@
86
86
  "engines": {
87
87
  "node": ">=18.0.0"
88
88
  }
89
- }
89
+ }