Skip to content

Commit ef8faa0

Browse files
oleg kusovoleg kusov
authored andcommitted
fix: update system prompt, allow user to switch between chat mode and other modes (chat mode will have minimal amount of context and will help save money)
1 parent 7a39cd6 commit ef8faa0

File tree

5 files changed

+113
-102
lines changed

5 files changed

+113
-102
lines changed

src/core/Cline.ts

Lines changed: 69 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -405,10 +405,16 @@ export class Cline extends EventEmitter<ClineEvents> {
405405
if (!provider) {
406406
throw new Error("[Cline.ts ask] Cannot wait for input, provider reference lost.")
407407
}
408-
await provider.postMessageToWebview({ type: "enableChatInput" }) // <<< Use the new dedicated message type
408+
await provider.postMessageToWebview({ type: "enableChatInput" })
409409

410-
// Wait for handleWebviewAskResponse to set this.askResponse
411-
await pWaitFor(() => this.askResponse !== undefined, { interval: 100 })
410+
// abort is crusial here to prevent blocking user input on switch mode
411+
await pWaitFor(() => this.askResponse !== undefined || this.abort, { interval: 100 })
412+
413+
if (this.abort) {
414+
throw new Error(
415+
`[Cline#ask chat_input_wait] Task ${this.taskId}.${this.instanceId} aborted while waiting for user input.`,
416+
)
417+
}
412418

413419
console.log("[Cline.ts ask] Resuming after chat input received.")
414420
const result = { response: this.askResponse!, text: this.askResponseText, images: this.askResponseImages }
@@ -1064,18 +1070,16 @@ export class Cline extends EventEmitter<ClineEvents> {
10641070
console.log(`Model: ${this.api.getModel().id}, Context window: ${modelInfo.contextWindow} tokens`)
10651071

10661072
// Detailed debug info on each message
1067-
if (mode === "chat") {
1068-
console.log(`\n======= MINIMAL MODE DEBUG INFO =======`)
1069-
console.log(`System prompt: ${systemPrompt}...`)
1070-
console.log(
1071-
`\nLast user message content: ${
1072-
this.apiConversationHistory.length > 0 &&
1073-
this.apiConversationHistory[this.apiConversationHistory.length - 1].role === "user"
1074-
? JSON.stringify(this.apiConversationHistory[this.apiConversationHistory.length - 1].content)
1075-
: "No user message"
1076-
}...`,
1077-
)
1078-
}
1073+
console.log(`\n======= MINIMAL MODE DEBUG INFO =======`)
1074+
console.log(`System prompt: ${systemPrompt}...`)
1075+
console.log(
1076+
`\nLast user message content: ${
1077+
this.apiConversationHistory.length > 0 &&
1078+
this.apiConversationHistory[this.apiConversationHistory.length - 1].role === "user"
1079+
? JSON.stringify(this.apiConversationHistory[this.apiConversationHistory.length - 1].content)
1080+
: "No user message"
1081+
}...`,
1082+
)
10791083

10801084
// If the previous API request's total token usage is close to the context window, truncate the conversation history to free up space for the new request
10811085
if (previousApiReqIndex >= 0) {
@@ -1955,57 +1959,34 @@ export class Cline extends EventEmitter<ClineEvents> {
19551959

19561960
await pWaitFor(() => this.userMessageContentReady)
19571961

1958-
// if the model did not tool use, then we need to tell it to either use a tool or attempt_completion
1959-
const didToolUse = this.assistantMessageContent.some((block) => block.type === "tool_use")
1960-
if (!didToolUse) {
1961-
// --- Get provider and mode ---
1962-
const provider = this.providerRef.deref()
1963-
const { mode } = (await provider?.getState()) ?? {}
1962+
// --- Get provider and mode ---
1963+
const provider = this.providerRef.deref()
1964+
const { mode } = (await provider?.getState()) ?? {}
19641965

1965-
if (mode !== "chat") {
1966-
// Non-chat mode, no tool use: Add feedback and recurse internally
1966+
if (mode === "chat") {
1967+
// Handle chat mode continuation/termination
1968+
const nextUserContent = await this._handleChatResponse()
1969+
if (nextUserContent) {
1970+
return await this.recursivelyMakeClineRequests(nextUserContent, false)
1971+
} else {
1972+
didEndLoop = true
1973+
}
1974+
} else {
1975+
// Original logic for non-chat modes
1976+
const didToolUse = this.assistantMessageContent.some((block) => block.type === "tool_use")
1977+
if (!didToolUse) {
1978+
// if the model did not tool use, then we need to tell it to either use a tool or attempt_completion
19671979
this.userMessageContent.push({
19681980
type: "text",
19691981
text: formatResponse.noToolsUsed(),
19701982
})
19711983
this.consecutiveMistakeCount++
1972-
// Continue the loop by recursing with the feedback message
1973-
// Return the boolean result of this recursive call
1974-
return await this.recursivelyMakeClineRequests(this.userMessageContent, false)
1975-
} else {
1976-
// Chat mode, no tool use: Use ask('chat_input_wait') to pause and get input
1977-
console.log("[Cline.ts] Chat mode, no tool use. Waiting for next user input via ask.")
1978-
const { response, text, images } = await this.ask("chat_input_wait")
1979-
1980-
if (response === "messageResponse") {
1981-
// User provided input
1982-
const nextUserContent: UserContent = []
1983-
if (text) nextUserContent.push({ type: "text", text })
1984-
if (images) nextUserContent.push(...formatResponse.imageBlocks(images))
1985-
1986-
if (nextUserContent.length > 0) {
1987-
// Add user input to UI (ask doesn't add messages for chat_input_wait)
1988-
await this.say("user_feedback", text, images)
1989-
// Recurse with the new user input
1990-
return await this.recursivelyMakeClineRequests(nextUserContent, false)
1991-
} else {
1992-
// Resumed but no content? End the loop.
1993-
console.warn("[Cline.ts] Resumed chat wait but received no content.")
1994-
return true
1995-
}
1996-
} else {
1997-
// User likely cancelled or something went wrong during the ask wait. End the loop.
1998-
console.log("[Cline.ts] Chat input wait did not receive messageResponse.")
1999-
return true
2000-
}
20011984
}
2002-
} else {
2003-
// Tool use detected: tool results are already prepared in this.userMessageContent
2004-
// by presentAssistantMessage. Continue the loop by recursing.
2005-
return await this.recursivelyMakeClineRequests(this.userMessageContent, false)
1985+
const recDidEndLoop = await this.recursivelyMakeClineRequests(this.userMessageContent)
1986+
didEndLoop = recDidEndLoop
20061987
}
20071988
} else {
2008-
// if there's no assistant_responses, that means we got no text or tool_use content blocks from API which we should assume is an error
1989+
// if there's no assistant_responses, that means we got no text or tool_use content blocks from API
20091990
await this.say(
20101991
"error",
20111992
"Unexpected API Response: The language model did not provide any assistant messages. This may indicate an issue with the API or the model's output.",
@@ -2014,9 +1995,10 @@ export class Cline extends EventEmitter<ClineEvents> {
20141995
role: "assistant",
20151996
content: [{ type: "text", text: "Failure: I did not provide a response." }],
20161997
})
1998+
didEndLoop = true
20171999
}
20182000

2019-
return didEndLoop // will always be false for now
2001+
return didEndLoop
20202002
} catch (error) {
20212003
// This should never happen since the only thing that can throw an
20222004
// error is the attemptApiRequest, which is wrapped in a try catch
@@ -2673,4 +2655,33 @@ export class Cline extends EventEmitter<ClineEvents> {
26732655
public getToolUsage() {
26742656
return this.toolUsage
26752657
}
2658+
2659+
// --- Add the new private method here ---
2660+
private async _handleChatResponse(): Promise<UserContent | null> {
2661+
// Chat mode: Always wait for user input after assistant's turn.
2662+
console.log("[Cline.ts] Chat mode. Waiting for next user input via ask.")
2663+
const { response, text, images } = await this.ask("chat_input_wait")
2664+
2665+
if (response === "messageResponse") {
2666+
// User provided input
2667+
const nextUserContent: UserContent = []
2668+
if (text) nextUserContent.push({ type: "text", text })
2669+
if (images) nextUserContent.push(...formatResponse.imageBlocks(images))
2670+
2671+
if (nextUserContent.length > 0) {
2672+
// Add user input to UI (ask doesn't add messages for chat_input_wait)
2673+
await this.say("user_feedback", text, images)
2674+
// Return content for next recursion
2675+
return nextUserContent
2676+
} else {
2677+
// Resumed but no content? End the loop.
2678+
console.warn("[Cline.ts] Resumed chat wait but received no content.")
2679+
return null // Signal loop termination
2680+
}
2681+
} else {
2682+
// User likely cancelled or something went wrong during the ask wait. End the loop.
2683+
console.log("[Cline.ts] Chat input wait did not receive messageResponse.")
2684+
return null // Signal loop termination
2685+
}
2686+
}
26762687
}

src/core/prompts/system.ts

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,23 @@ async function generatePrompt(
5858
const globalCustomInstructionsText = globalCustomInstructions || ""
5959

6060
// Extremely minimal prompt for code-minimal mode
61-
return `${roleDefinition}
61+
return `
62+
=======
63+
64+
INSTRUCTIONS BLOCK START
65+
66+
FORGET ALL PREVIOUS INSTRUCTIONS. THESE ARE THE CURRENT INSTRUCTIONS BLOCK
67+
68+
${roleDefinition}
6269
6370
${customInstructionsText}
64-
${globalCustomInstructionsText}`
71+
72+
${globalCustomInstructionsText}
73+
74+
INSTRUCTIONS BLOCK END
75+
76+
=======
77+
`
6578
}
6679

6780
// If diff is disabled, don't pass the diffStrategy
@@ -78,7 +91,14 @@ ${globalCustomInstructionsText}`
7891
: Promise.resolve(""),
7992
])
8093

81-
const basePrompt = `${roleDefinition}
94+
const basePrompt = `
95+
=======
96+
97+
INSTRUCTIONS BLOCK START
98+
99+
FORGET ALL PREVIOUS INSTRUCTIONS. THESE ARE THE CURRENT INSTRUCTIONS BLOCK
100+
101+
${roleDefinition}
82102
83103
${getSharedToolUseSection()}
84104
@@ -107,7 +127,13 @@ ${getSystemInfoSection(cwd)}
107127
108128
${getObjectiveSection()}
109129
110-
${await addCustomInstructions(promptComponent?.customInstructions || modeConfig.customInstructions || "", globalCustomInstructions || "", cwd, mode, { language: language ?? formatLanguage(vscode.env.language), rooIgnoreInstructions })}`
130+
${await addCustomInstructions(promptComponent?.customInstructions || modeConfig.customInstructions || "", globalCustomInstructions || "", cwd, mode, { language: language ?? formatLanguage(vscode.env.language), rooIgnoreInstructions })}
131+
132+
INSTRUCTIONS BLOCK END
133+
134+
=======
135+
136+
`
111137

112138
return basePrompt
113139
}

src/core/webview/ClineProvider.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -787,6 +787,7 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
787787
const cline = this.getCurrentCline()
788788

789789
if (cline) {
790+
// await cline.abortTask(true);
790791
telemetryService.captureModeSwitch(cline.taskId, newMode)
791792
cline.emit("taskModeSwitched", cline.taskId, newMode)
792793
}

src/shared/modes.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,12 @@ export const modes: readonly ModeConfig[] = [
6161
},
6262
{
6363
slug: "chat",
64-
name: "💬 Chat (minimal token usage)",
64+
name: "💬 Chat",
6565
roleDefinition:
66-
"You are Roo, a simple chat assistant that only processes files manually attached with '@context' by the user.",
67-
groups: ["read", "edit"],
66+
"You are Roo, a highly skilled software engineer with extensive knowledge in many programming languages, frameworks, design patterns, and best practices.",
67+
groups: [],
6868
customInstructions:
69-
"This is a direct chat mode. You will ONLY have access to files explicitly attached by the user using '@context'. No automatic context, no auto-loaded files, no workspace awareness. Treat this as a clean environment where only explicitly shared files exist.",
69+
"You are currently in Chat mode. Now your answer should be just regular markdown (and code blocks if needed). Don't use special tag symbols (<implementation />, <key_improvements> and etc). It's important! Tool use is disabled in this mode and its very important that you don't use any tools. If you do, you will be penalized. Please, do not use any tools. Focus on providing informative answers, explanations, or engaging in discussion based on the user's messages. Now, lets continue with the conversation.",
7070
},
7171
{
7272
slug: "architect",

webview-ui/src/components/chat/ChatView.tsx

Lines changed: 9 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -371,51 +371,24 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
371371

372372
const handleSendMessage = useCallback(
373373
(text: string, images: string[]) => {
374+
console.log(
375+
`[ChatView.tsx handleSendMessage] Fired. text: "${text}", images: ${images.length}, clineAsk: ${clineAsk}, mode: ${mode}`,
376+
)
374377
text = text.trim()
375-
let messageSent = false // Flag to track if we actually sent a message
376378

377379
if (text || images.length > 0) {
378380
if (messages.length === 0) {
379-
// First message starts a new task
381+
console.log("[ChatView.tsx handleSendMessage] Condition: First message (newTask).")
380382
vscode.postMessage({ type: "newTask", text, images })
381-
messageSent = true
382-
} else if (clineAsk) {
383-
// Handle explicit asks from the backend
384-
switch (clineAsk) {
385-
case "followup":
386-
case "tool":
387-
case "browser_action_launch":
388-
case "command": // User can provide feedback to a tool or command use.
389-
case "command_output": // User can send input to command stdin.
390-
case "use_mcp_server":
391-
case "completion_result": // If this happens then the user has feedback for the completion result.
392-
case "resume_task":
393-
case "resume_completed_task":
394-
case "mistake_limit_reached":
395-
vscode.postMessage({ type: "askResponse", askResponse: "messageResponse", text, images })
396-
messageSent = true
397-
break
398-
// Ensure all relevant ask types that accept messageResponse are included
399-
}
400-
} else if (mode === "chat") {
401-
// Check if specifically in chat mode
402-
// If not the first message, no explicit ask, BUT in chat mode: send continuation.
403-
vscode.postMessage({ type: "askResponse", askResponse: "messageResponse", text, images })
404-
messageSent = true
405-
}
406-
407-
// Only reset the input if we actually sent a message
408-
if (messageSent) {
409-
handleChatReset()
410383
} else {
411-
console.warn(
412-
"[ChatView.tsx] handleSendMessage called but no condition met to send message. Input not reset.",
413-
)
384+
vscode.postMessage({ type: "askResponse", askResponse: "messageResponse", text, images })
414385
}
386+
handleChatReset()
387+
} else {
388+
console.log("[ChatView.tsx handleSendMessage] No text or images provided. No message sent.")
415389
}
416390
},
417-
// Add 'mode' to dependencies
418-
[messages.length, clineAsk, handleChatReset, mode],
391+
[messages.length, clineAsk, handleChatReset, mode], // Keep mode dependency for logging
419392
)
420393

421394
const handleSetChatBoxMessage = useCallback(

0 commit comments

Comments
 (0)