erosolar-cli 2.1.177 → 2.1.179

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.
@@ -4,7 +4,6 @@ import { exec } from 'node:child_process';
4
4
  import { promisify } from 'node:util';
5
5
  import { existsSync, readFileSync, statSync, writeFileSync } from 'node:fs';
6
6
  import { join, resolve } from 'node:path';
7
- import { AgentOrchestrator } from '../core/agentOrchestrator.js';
8
7
  import { display } from '../ui/display.js';
9
8
  import { theme } from '../ui/theme.js';
10
9
  import { getTerminalColumns } from '../ui/layout.js';
@@ -13,12 +12,13 @@ import { ensureSecretForProvider, getSecretDefinitionForProvider, getSecretValue
13
12
  import { saveActiveProfilePreference, saveModelPreference, loadToolSettings, saveToolSettings, clearToolSettings, clearActiveProfilePreference, loadSessionPreferences, saveSessionPreferences, loadFeatureFlags, saveFeatureFlags, toggleFeatureFlag, FEATURE_FLAG_INFO, } from '../core/preferences.js';
14
13
  import { getLearningSummary, getRecentLearning, commitLearning, exportAllLearning, getLearningDir, } from '../core/learningPersistence.js';
15
14
  import { buildEnabledToolSet, evaluateToolPermissions, getToolToggleOptions, } from '../capabilities/toolRegistry.js';
15
+ import { FlowOrchestrator } from '../runtime/flowOrchestrator.js';
16
16
  import { detectApiKeyError } from '../core/errors/apiKeyErrors.js';
17
17
  import { detectPromptBlockError, } from '../core/errors/promptBlockErrors.js';
18
18
  import { detectNetworkError } from '../core/errors/networkErrors.js';
19
19
  import { buildWorkspaceContext } from '../workspace.js';
20
20
  import { buildInteractiveSystemPrompt } from './systemPrompt.js';
21
- import { WRITE_TOOLS } from './taskCompletionDetector.js';
21
+ import { getTaskCompletionDetector, resetTaskCompletionDetector, WRITE_TOOLS, } from './taskCompletionDetector.js';
22
22
  import { discoverAllModels, quickCheckProviders, getCachedDiscoveredModels, sortModelsByPriority } from '../core/modelDiscovery.js';
23
23
  import { getModels, getSlashCommands, getProviders } from '../core/agentSchemaLoader.js';
24
24
  import { loadMcpServers } from '../mcp/config.js';
@@ -124,6 +124,7 @@ export class InteractiveShell {
124
124
  uiUpdates;
125
125
  _fileChangeTracker = new FileChangeTracker(); // Reserved for future file tracking features
126
126
  alphaZeroMetrics; // Alpha Zero 2 performance tracking
127
+ flowOrchestrator = new FlowOrchestrator();
127
128
  maxNetworkRetries = 2;
128
129
  statusSubscription = null;
129
130
  followUpQueue = [];
@@ -2275,17 +2276,14 @@ export class InteractiveShell {
2275
2276
  // Check for continuous/infinite loop commands or auto-escalation to completion mode
2276
2277
  const explicitContinuous = this.isContinuousCommand(trimmed);
2277
2278
  const autoContinuous = this.shouldAutoRunToCompletion(trimmed);
2278
- if (explicitContinuous) {
2279
+ if (explicitContinuous || autoContinuous) {
2280
+ if (autoContinuous && !explicitContinuous) {
2281
+ display.showSystemMessage('⚡ Actionable request detected; running continuously until complete (Ctrl+C to stop).');
2282
+ }
2279
2283
  await this.processContinuousRequest(trimmed);
2280
2284
  this.syncRendererInput();
2281
2285
  return;
2282
2286
  }
2283
- if (autoContinuous) {
2284
- display.showSystemMessage('⚡ Actionable request detected; orchestrating until complete (Ctrl+C to stop).');
2285
- await this.processRequest(trimmed, { orchestrate: true });
2286
- this.syncRendererInput();
2287
- return;
2288
- }
2289
2287
  // Direct execution for all inputs, including multi-line pastes
2290
2288
  await this.processRequest(trimmed);
2291
2289
  this.syncRendererInput();
@@ -2360,7 +2358,7 @@ export class InteractiveShell {
2360
2358
  return false;
2361
2359
  }
2362
2360
  const mutatingToolUsed = toolsUsed.some((tool) => WRITE_TOOL_NAMES.has(tool.toLowerCase()));
2363
- const planOnly = this.isPlanOnlyResponse(response);
2361
+ const planOnly = this.flowOrchestrator.isPlanOnlyResponse(response);
2364
2362
  const lowActionDensity = response.split(/\s+/).length < 80;
2365
2363
  // If it's clearly a plan-only response, continue regardless of prior tool usage
2366
2364
  if (planOnly) {
@@ -5834,7 +5832,7 @@ Execute the plan you outlined. Use the available tools (bash, edits, git) to tak
5834
5832
  this.clearInlinePanel();
5835
5833
  this.syncRendererInput();
5836
5834
  }
5837
- async processRequest(request, options) {
5835
+ async processRequest(request) {
5838
5836
  if (this.isProcessing) {
5839
5837
  this.enqueueFollowUpAction({ type: 'request', text: request });
5840
5838
  return;
@@ -5886,23 +5884,10 @@ Execute the plan you outlined. Use the available tools (bash, edits, git) to tak
5886
5884
  this.beginAiRuntime();
5887
5885
  let responseText = '';
5888
5886
  let autoFollowThrough = null;
5889
- let orchestratorResult = null;
5890
- const orchestrate = options?.orchestrate ?? false;
5891
5887
  try {
5892
5888
  // Start streaming - no header needed, the input area already provides context
5893
5889
  this.startStreamingHeartbeat('Streaming response');
5894
- if (orchestrate) {
5895
- const orchestrator = new AgentOrchestrator(agent);
5896
- orchestratorResult = await orchestrator.runToCompletion(request, {
5897
- streaming: true,
5898
- maxPasses: options?.maxPasses ?? 4,
5899
- enforceActions: true,
5900
- });
5901
- responseText = orchestratorResult.finalResponse;
5902
- }
5903
- else {
5904
- responseText = await agent.send(request, true);
5905
- }
5890
+ responseText = await agent.send(request, true);
5906
5891
  this.finishStreamingFormatter(undefined, { refreshPrompt: false, mode: 'complete' });
5907
5892
  await this.awaitPendingCleanup();
5908
5893
  this.captureHistorySnapshot();
@@ -5914,24 +5899,14 @@ Execute the plan you outlined. Use the available tools (bash, edits, git) to tak
5914
5899
  display.showWarning('The provider returned an empty response. Check your API key/provider selection or retry the prompt.');
5915
5900
  }
5916
5901
  // AlphaZero: Extract and track tool calls from response
5917
- const toolsUsed = orchestratorResult
5918
- ? orchestratorResult.passes.flatMap(pass => pass.toolsUsed)
5919
- : this.getExecutedTools(responseText);
5902
+ const toolsUsed = this.getExecutedTools(responseText);
5920
5903
  this.currentToolCalls = toolsUsed.map(name => ({
5921
5904
  name,
5922
5905
  arguments: {},
5923
5906
  success: true, // Assume success if we got here
5924
5907
  duration: 0,
5925
5908
  }));
5926
- if (!orchestrate) {
5927
- autoFollowThrough = this.maybeAutoFollowThrough(request, responseText, toolsUsed);
5928
- }
5929
- if (orchestratorResult && orchestratorResult.exitReason !== 'complete') {
5930
- const exitDetail = orchestratorResult.exitReason === 'max-passes'
5931
- ? 'Reached orchestrator pass limit; showing last response.'
5932
- : 'Received empty replies while orchestrating; showing last response.';
5933
- display.showSystemMessage(`⚠️ ${exitDetail}`);
5934
- }
5909
+ autoFollowThrough = this.maybeAutoFollowThrough(request, responseText, toolsUsed);
5935
5910
  // AlphaZero: Check for failure in response
5936
5911
  const failure = detectFailure(responseText, {
5937
5912
  toolCalls: this.currentToolCalls,
@@ -5963,7 +5938,7 @@ Execute the plan you outlined. Use the available tools (bash, edits, git) to tak
5963
5938
  }
5964
5939
  }
5965
5940
  catch (error) {
5966
- const handled = this.handleProviderError(error, () => this.processRequest(request, options));
5941
+ const handled = this.handleProviderError(error, () => this.processRequest(request));
5967
5942
  if (!handled) {
5968
5943
  // Pass full error object for enhanced formatting with stack trace
5969
5944
  display.showError(error instanceof Error ? error.message : String(error), error);
@@ -6017,7 +5992,7 @@ Execute the plan you outlined. Use the available tools (bash, edits, git) to tak
6017
5992
  * Context is automatically managed - overflow errors trigger auto-recovery.
6018
5993
  */
6019
5994
  async processContinuousRequest(initialRequest) {
6020
- const MAX_PASSES = 100; // Safety limit to prevent truly infinite loops
5995
+ const MAX_ITERATIONS = 100; // Safety limit to prevent truly infinite loops
6021
5996
  if (this.isProcessing) {
6022
5997
  this.enqueueFollowUpAction({ type: 'continuous', text: initialRequest });
6023
5998
  return;
@@ -6026,9 +6001,49 @@ Execute the plan you outlined. Use the available tools (bash, edits, git) to tak
6026
6001
  display.showWarning('Configure an API key via /secrets before sending requests.');
6027
6002
  return;
6028
6003
  }
6004
+ this.inlinePanelScopeActive = false;
6005
+ this.clearInlinePanel();
6006
+ const agent = this.agent;
6007
+ if (!agent) {
6008
+ return;
6009
+ }
6010
+ this.toolsUsedThisRun = [];
6011
+ this.currentToolCalls = [];
6012
+ this.lastUserQuery = initialRequest;
6013
+ this.clearToolUsageMeta();
6014
+ this.isProcessing = true;
6015
+ this.uiUpdates.setMode('processing');
6016
+ this.streamingTokenCount = 0; // Reset token counter for new request
6017
+ this.terminalInput.setStreaming(true);
6018
+ if (this.suppressNextNetworkReset) {
6019
+ this.suppressNextNetworkReset = false;
6020
+ }
6021
+ else {
6022
+ this.resetNetworkRetryState();
6023
+ }
6024
+ const overallStartTime = Date.now();
6025
+ // Clear previous parallel agents and start fresh for continuous mode
6026
+ const parallelManager = getParallelAgentManager();
6027
+ parallelManager.clear();
6028
+ parallelManager.startBatch();
6029
+ // Initialize the task completion detector
6030
+ const completionDetector = getTaskCompletionDetector();
6031
+ completionDetector.reset();
6032
+ // Display user prompt in scrollback (Claude Code style)
6033
+ this.logUserPrompt(initialRequest);
6029
6034
  display.showSystemMessage(`Continuous mode active. Ctrl+C to stop.`);
6030
- const preparedRequest = this.isSelfImprovementRequest(initialRequest)
6031
- ? `${initialRequest}
6035
+ this.uiAdapter.startProcessing('Continuous execution mode');
6036
+ this.setProcessingStatus();
6037
+ this.beginAiRuntime();
6038
+ // No streaming header - just start streaming directly
6039
+ this.startStreamingHeartbeat('Streaming');
6040
+ this.flowOrchestrator.start(initialRequest);
6041
+ let iteration = 0;
6042
+ try {
6043
+ // Enhance initial prompt with git context for self-improvement tasks
6044
+ let currentPrompt = initialRequest;
6045
+ if (this.isSelfImprovementRequest(initialRequest)) {
6046
+ currentPrompt = `${initialRequest}
6032
6047
 
6033
6048
  IMPORTANT: You have full git access. After making improvements:
6034
6049
  1. Use bash to run: git status (see changes)
@@ -6037,9 +6052,124 @@ IMPORTANT: You have full git access. After making improvements:
6037
6052
  4. Use bash to run: git push (when milestone reached)
6038
6053
 
6039
6054
  Commit frequently with descriptive messages. Push when ready.
6040
- When truly finished with ALL tasks, explicitly state "TASK_FULLY_COMPLETE".`
6041
- : initialRequest;
6042
- await this.processRequest(preparedRequest, { orchestrate: true, maxPasses: MAX_PASSES });
6055
+ When truly finished with ALL tasks, explicitly state "TASK_FULLY_COMPLETE".`;
6056
+ }
6057
+ while (iteration < MAX_ITERATIONS) {
6058
+ iteration++;
6059
+ this.toolsUsedThisRun = [];
6060
+ display.showSystemMessage(`\n📍 Iteration ${iteration}/${MAX_ITERATIONS}`);
6061
+ this.updateStatusMessage(`Working on iteration ${iteration}...`);
6062
+ try {
6063
+ // Send the request and capture the response (streaming disabled)
6064
+ display.showThinking('Responding...');
6065
+ this.refreshStatusLine(true);
6066
+ const response = await agent.send(currentPrompt, true);
6067
+ this.finishStreamingFormatter(undefined, { refreshPrompt: false, mode: 'complete' });
6068
+ await this.awaitPendingCleanup();
6069
+ this.captureHistorySnapshot();
6070
+ this.autosaveIfEnabled();
6071
+ // Track metrics
6072
+ const elapsedMs = Date.now() - overallStartTime;
6073
+ this.alphaZeroMetrics.recordMessage(elapsedMs);
6074
+ if (!response?.trim()) {
6075
+ display.showWarning('Model returned an empty response. Retrying this iteration...');
6076
+ currentPrompt = `${initialRequest}
6077
+
6078
+ The previous reply was empty. Resume the task now: take the next action, call the necessary tools, and report progress.`;
6079
+ continue;
6080
+ }
6081
+ // Extract tools used from the response (look for tool call patterns)
6082
+ const toolsUsed = this.getExecutedTools(response);
6083
+ toolsUsed.forEach(tool => completionDetector.recordToolCall(tool, true, true));
6084
+ // Use intelligent completion detection
6085
+ const completionAnalysis = completionDetector.analyzeCompletion(response, toolsUsed);
6086
+ display.showSystemMessage(`📈 Completion confidence: ${(completionAnalysis.confidence * 100).toFixed(0)}%`);
6087
+ const decision = this.flowOrchestrator.decide({
6088
+ iteration,
6089
+ response,
6090
+ toolsUsed,
6091
+ completionAnalysis,
6092
+ verificationConfirmed: this.flowOrchestrator.isVerificationPending()
6093
+ ? completionDetector.isVerificationConfirmed(response)
6094
+ : false,
6095
+ });
6096
+ if (decision.type === 'stop') {
6097
+ display.showSystemMessage(decision.message);
6098
+ break;
6099
+ }
6100
+ if (decision.type === 'stagnation-stop') {
6101
+ display.showWarning(decision.message);
6102
+ break;
6103
+ }
6104
+ if (decision.type === 'execute-plan') {
6105
+ display.showSystemMessage(decision.message);
6106
+ currentPrompt = decision.prompt;
6107
+ await new Promise(resolve => setTimeout(resolve, 500));
6108
+ continue;
6109
+ }
6110
+ if (decision.type === 'verify') {
6111
+ display.showSystemMessage(decision.message);
6112
+ currentPrompt = decision.prompt;
6113
+ await new Promise(resolve => setTimeout(resolve, 500));
6114
+ continue;
6115
+ }
6116
+ if (decision.type === 'continue') {
6117
+ if (decision.message) {
6118
+ display.showSystemMessage(decision.message);
6119
+ }
6120
+ currentPrompt = decision.prompt;
6121
+ }
6122
+ // Small delay between iterations to prevent rate limiting
6123
+ await new Promise(resolve => setTimeout(resolve, 500));
6124
+ }
6125
+ catch (error) {
6126
+ display.stopThinking(false);
6127
+ // Handle context overflow specially - the agent should auto-recover
6128
+ // but if it propagates here, we continue the loop
6129
+ if (this.isContextOverflowError(error)) {
6130
+ display.showSystemMessage(`⚡ Context overflow handled. Continuing with reduced context...`);
6131
+ // The agent.ts should have already handled recovery
6132
+ // Continue to next iteration
6133
+ continue;
6134
+ }
6135
+ // For other errors, check if handled by provider error handler
6136
+ const handled = this.handleProviderError(error, () => this.processContinuousRequest(initialRequest));
6137
+ if (!handled) {
6138
+ display.showError(error instanceof Error ? error.message : String(error), error);
6139
+ break;
6140
+ }
6141
+ }
6142
+ }
6143
+ if (iteration >= MAX_ITERATIONS) {
6144
+ display.showWarning(`\n⚠️ Reached maximum iterations (${MAX_ITERATIONS}). Stopping to prevent infinite loop.`);
6145
+ }
6146
+ }
6147
+ finally {
6148
+ this.finishStreamingFormatter(undefined, { refreshPrompt: false, mode: 'complete' });
6149
+ const totalElapsed = Date.now() - overallStartTime;
6150
+ const minutes = Math.floor(totalElapsed / 60000);
6151
+ const seconds = Math.floor((totalElapsed % 60000) / 1000);
6152
+ display.showSystemMessage(`\n🏁 Continuous execution completed: ${iteration} iterations, ${minutes}m ${seconds}s total`);
6153
+ // Reset completion detector for next task
6154
+ resetTaskCompletionDetector();
6155
+ this.uiUpdates.setMode('processing');
6156
+ this.stopStreamingHeartbeat('complete', { quiet: true });
6157
+ this.endAiRuntime();
6158
+ this.isProcessing = false;
6159
+ this.terminalInput.setStreaming(false);
6160
+ this.uiAdapter.endProcessing('Ready for prompts');
6161
+ this.updateToolUsageMeta(this.uiAdapter.getToolUsageSummary({ plain: true }));
6162
+ this.setIdleStatus();
6163
+ this.updateStatusMessage(null);
6164
+ this.toolsUsedThisRun = [];
6165
+ queueMicrotask(() => this.uiUpdates.setMode('idle'));
6166
+ // CRITICAL: Ensure readline prompt is active for user input
6167
+ // Erosolar-CLI style: New prompt naturally appears at bottom
6168
+ this.ensureReadlineReady();
6169
+ this.scheduleQueueProcessing();
6170
+ this.maybeProcessPromptInbox();
6171
+ this.refreshQueueIndicators();
6172
+ }
6043
6173
  }
6044
6174
  /**
6045
6175
  * Resolve executed tools for the current turn. Prefer the actual tool
@@ -6061,129 +6191,6 @@ When truly finished with ALL tasks, explicitly state "TASK_FULLY_COMPLETE".`
6061
6191
  }
6062
6192
  return this.extractToolsFromResponse(responseText);
6063
6193
  }
6064
- /**
6065
- * Detect plan-only responses that narrate intent without executing actions.
6066
- */
6067
- isPlanOnlyResponse(response) {
6068
- const normalized = response.trim().toLowerCase();
6069
- if (!normalized) {
6070
- return false;
6071
- }
6072
- // If the assistant is clearly declaring completion, don't treat it as plan-only
6073
- const completionGuards = [
6074
- /\bnothing\s+(left|else)\s+(to\s+do|pending)\b/i,
6075
- /\b(already|now)\s+(clean|complete|done)\b/i,
6076
- /\b(no\s+(junk|issues?|changes?)\s+found)\b/i,
6077
- ];
6078
- if (completionGuards.some((pattern) => pattern.test(response))) {
6079
- return false;
6080
- }
6081
- const planIndicators = [
6082
- /\bplan\b/i,
6083
- /\bapproach\b/i,
6084
- /\bsteps?:\b/i,
6085
- /\bstep\s+1\b/i,
6086
- /\bstart by\b/i,
6087
- /\bfirst[, ]/i,
6088
- /\bthen\b/i,
6089
- /\bnext\b/i,
6090
- /\bi['’]?\s*will\b/i,
6091
- /\bi['’]?\s*ll\b/i,
6092
- /\bi['’]?\s*can\b.{0,40}\bthen\b/i,
6093
- /\bi['’]?\s*(?:will|ll)\s+begin\b/i,
6094
- ];
6095
- return planIndicators.some((pattern) => pattern.test(response));
6096
- }
6097
- /**
6098
- * Check if a response contains indicators that work is actually incomplete,
6099
- * even if it also contains TASK_FULLY_COMPLETE marker.
6100
- * This catches contradictory responses where the AI says "done" but also "not integrated yet".
6101
- */
6102
- responseIndicatesIncompleteWork(response) {
6103
- // Patterns that indicate work isn't actually complete
6104
- // Organized by category for maintainability
6105
- const incompletePatterns = [
6106
- // === INTEGRATION/DEPLOYMENT STATE ===
6107
- // "hasn't been integrated/implemented/connected yet"
6108
- /hasn'?t\s+been\s+(integrated|implemented|connected|deployed|added|completed|tested|verified)\s*(yet|still)?/i,
6109
- // "not yet integrated/implemented" or "not integrated"
6110
- /not\s+(yet\s+)?(integrated|implemented|connected|deployed|functional|working|complete|tested|verified)/i,
6111
- // "ready for integration" = NOT integrated
6112
- /ready\s+(for|to\s+be)\s+(integration|integrated|connected|deployed|testing|review)/i,
6113
- // "needs to be integrated"
6114
- /needs?\s+to\s+be\s+(integrated|connected|deployed|added|hooked|wired|tested|reviewed|merged)/i,
6115
- // Passive voice: "was not performed/completed"
6116
- /was\s+not\s+(performed|completed|implemented|deployed|integrated|tested)/i,
6117
- // "the [X] service hasn't been"
6118
- /the\s+\w+\s+(service|module|component|feature)\s+hasn'?t\s+been/i,
6119
- // === PARTIAL/INCOMPLETE STATE ===
6120
- // "still stores/uses/has" (current bad state persists)
6121
- /still\s+(stores?|uses?|has|contains?|needs?|requires?|missing|lacks?|broken)/i,
6122
- // Partial completion: "partially", "mostly", "almost"
6123
- /\b(partially|mostly|almost|nearly|not\s+fully)\s+(complete|done|finished|implemented|working)/i,
6124
- // Explicit partial: "part of", "some of", "half of"
6125
- /\b(only\s+)?(part|some|half|portion)\s+of\s+(the\s+)?(task|work|feature|implementation)/i,
6126
- // === QUALIFIER WORDS (uncertain completion) ===
6127
- // "should be complete", "appears complete", "theoretically"
6128
- /\b(should|might|may|could|appears?\s+to)\s+be\s+(complete|done|working|functional)/i,
6129
- /\btheoretically\s+(complete|done|working|functional)/i,
6130
- // "assuming", "if everything works"
6131
- /\b(assuming|provided|if)\s+(everything|it|this|that)\s+(works?|is\s+correct)/i,
6132
- // === SELF-CONTRADICTION PHRASES ===
6133
- // "done but...", "complete except...", "finished however..."
6134
- /\b(done|complete|finished)\s+(but|except|however|although|though)/i,
6135
- // "however" followed by incomplete indicator
6136
- /however[,\s].{0,50}?(hasn'?t|not\s+yet|still\s+needs?|pending|remains?|missing|broken|failing)/i,
6137
- // "but" followed by negative state
6138
- /\bbut\s+.{0,30}?(not|hasn'?t|won'?t|can'?t|doesn'?t|isn'?t|wasn'?t)/i,
6139
- // === FUTURE TENSE / DEFERRED WORK ===
6140
- // "will need to", "will require"
6141
- /will\s+(need\s+to|require|have\s+to)\s+(integrate|connect|deploy|complete|implement|test|fix)/i,
6142
- // Deferred: "left as", "deferred", "postponed", "out of scope"
6143
- /\b(left\s+as|deferred|postponed|out\s+of\s+scope|for\s+later|in\s+a\s+future)/i,
6144
- // Time-dependent: "after restart", "takes effect after", "once you"
6145
- /\b(after\s+(restart|reboot|redeploy)|takes?\s+effect\s+after|once\s+you)/i,
6146
- // === REMAINING WORK INDICATORS ===
6147
- // "remaining tasks", "outstanding items"
6148
- /\b(remaining|outstanding|pending|leftover)\s+(tasks?|items?|work|issues?|steps?)/i,
6149
- // "X more to do", "still have to"
6150
- /\b(more\s+to\s+do|still\s+have\s+to|yet\s+to\s+be\s+done)/i,
6151
- // Explicit blockers
6152
- /\b(blocker|blocked\s+by|waiting\s+(for|on)|depends?\s+on)/i,
6153
- // === ERROR/FAILURE STATE ===
6154
- // "failing tests", "build errors"
6155
- /\b(failing|broken|erroring)\s+(tests?|builds?|checks?|validations?)/i,
6156
- // "tests? (are )?(still )?failing"
6157
- /\btests?\s+(are\s+)?(still\s+)?failing/i,
6158
- // "errors? to (address|fix)"
6159
- /\b(errors?|warnings?|issues?)\s+to\s+(address|fix|resolve)/i,
6160
- // "doesn't work", "isn't working", "not working"
6161
- /\b(doesn'?t|isn'?t|not)\s+(work|working|functional|functioning)/i,
6162
- // === MANUAL STEPS REQUIRED ===
6163
- // "you'll need to", "manually run", "requires user"
6164
- /\b(you('ll|\s+will)\s+need\s+to|manually\s+(run|configure|set|update)|requires?\s+user)/i,
6165
- // "run this command", "execute the following"
6166
- /\b(run\s+this|execute\s+the\s+following|apply\s+the\s+migration)/i,
6167
- // === TODO/FIXME IN PROSE ===
6168
- // TODO or FIXME mentioned as remaining work (not in code blocks)
6169
- /\b(todo|fixme|hack|xxx):\s/i,
6170
- // "need to add", "should implement"
6171
- /\b(need\s+to|should|must)\s+(add|implement|create|write|build|fix)\b/i,
6172
- // === SCOPE LIMITATIONS ===
6173
- // "didn't have time", "ran out of time"
6174
- /\b(didn'?t|did\s+not)\s+have\s+(time|chance|opportunity)/i,
6175
- // "beyond scope", "outside scope"
6176
- /\b(beyond|outside)\s+(the\s+)?scope/i,
6177
- // "for now" (temporary state)
6178
- /\b(for\s+now|at\s+this\s+point|currently)\s*.{0,20}?(not|without|lacks?|missing)/i,
6179
- ];
6180
- for (const pattern of incompletePatterns) {
6181
- if (pattern.test(response)) {
6182
- return true;
6183
- }
6184
- }
6185
- return false;
6186
- }
6187
6194
  /**
6188
6195
  * Extract tool names from a response by looking for tool call patterns
6189
6196
  */