Updates to enter interactive mode from the extension settings instead of parsing the prompt

This commit is contained in:
a8trejo 2024-12-10 19:37:49 -08:00
parent 4d53bd2104
commit f049fb4577
9 changed files with 118 additions and 32 deletions

2
.gitignore vendored
View file

@ -11,4 +11,4 @@ roo-cline-*.vsix
# Prompts
prompts
.clinerules

View file

@ -98,6 +98,8 @@ export class Cline {
apiConfiguration: ApiConfiguration,
customInstructions?: string,
diffEnabled?: boolean,
isInteractiveMode?: boolean,
browserPort?: string,
task?: string,
images?: string[],
historyItem?: HistoryItem,
@ -108,6 +110,8 @@ export class Cline {
this.urlContentFetcher = new UrlContentFetcher(provider.context)
this.browserSession = new BrowserSession(provider.context)
this.diffViewProvider = new DiffViewProvider(cwd)
this.isInteractiveMode = isInteractiveMode ?? false
this.browserPort = browserPort ?? "7333"
this.customInstructions = customInstructions
if (diffEnabled && this.api.getModel().id) {
this.diffStrategy = getDiffStrategy(this.api.getModel().id)
@ -633,48 +637,29 @@ export class Cline {
if (responseImages && responseImages.length > 0) {
newUserContent.push(...formatResponse.imageBlocks(responseImages))
}
const wasInteractiveBrowser = (lastRelevantMessageIndex > 0) ? modifiedClineMessages[lastRelevantMessageIndex - 1].text?.includes("interactive mode") : false
const state = await this.providerRef.deref()?.getState()
const wasInteractiveBrowser = state?.isInteractiveMode
let hadBrowserPort = '';
if ( wasInteractiveBrowser ) {
const match = modifiedClineMessages[lastRelevantMessageIndex - 1].text?.match(/\(browserPort\s*=\s*(\d+)\)/)
if (match) {
hadBrowserPort = match[1] ?? this.browserPort;
this.providerRef.deref()?.outputChannel.appendLine(`resumeTaskFromHistory :: browserPort :: ${hadBrowserPort}`)
}
hadBrowserPort = state?.browserPort;
this.providerRef.deref()?.outputChannel.appendLine(`resumeTaskFromHistory :: browserPort :: ${hadBrowserPort}`)
}
await this.overwriteApiConversationHistory(modifiedApiConversationHistory)
await this.initiateTaskLoop(newUserContent, wasInteractiveBrowser, hadBrowserPort)
}
private async initiateTaskLoop(userContent: UserContent, wasInteractiveBrowser: boolean = false, hadBrowserPort: string = ''): Promise<void> {
// Check if any text block contains "interactive mode"
const hasInteractiveMode = userContent.some((block) => {
if (block.type === "text" && typeof block.text === "string") {
return (block.type === "text" &&
typeof block.text === "string" &&
block.text.toLowerCase().includes("interactive mode"))
} else {
return false
}
}
) || wasInteractiveBrowser;
const state = await this.providerRef.deref()?.getState()
const hasInteractiveMode = state?.isInteractiveMode ?? wasInteractiveBrowser
this.providerRef.deref()?.outputChannel.appendLine(`initiateTaskLoop :: hasInteractiveMode :: ${hasInteractiveMode}`)
// Set interactive mode flag if found in text
if (hasInteractiveMode) {
this.isInteractiveMode = true;
// Parse browserPort if specified in text blocks
userContent.forEach((block) => {
if (block.type === "text" && typeof block.text === "string") {
const match = block.text.match(/\(browserPort\s*=\s*(\d+)\)/)
if (match) {
this.browserPort = match[1] ?? hadBrowserPort;
this.providerRef.deref()?.outputChannel.appendLine(`initiateTaskLoop :: browserPort :: ${this.browserPort}`)
}
}
})
this.browserPort = state?.browserPort ?? hadBrowserPort
this.providerRef.deref()?.outputChannel.appendLine(`initiateTaskLoop :: browserPort :: ${this.browserPort}`)
}
let nextUserContent = userContent;
@ -1863,12 +1848,25 @@ export class Cline {
userContent: UserContent,
includeFileDetails: boolean = false,
isInteractiveMode: boolean = false,
browserPort: string = '7333'
browserPort?: string
): Promise<boolean> {
if (this.presentAssistantMessageHasPendingUpdates) {
this.presentAssistantMessage()
}
const state = await this.providerRef.deref()?.getState()
// Use optional chaining and provide defaults
this.isInteractiveMode = isInteractiveMode ?? state?.isInteractiveMode ?? false
this.browserPort = browserPort ?? state?.browserPort ?? "7333"
// Store interactive mode state
if (state?.isInteractiveMode !== undefined) {
this.isInteractiveMode = state.isInteractiveMode
}
if (this.abort) {
throw new Error("Cline instance aborted")
}
// Store interactive mode state
this.isInteractiveMode = isInteractiveMode;

View file

@ -237,6 +237,8 @@ describe('Cline', () => {
mockApiConfig,
'custom instructions',
false,
true, // isInteractiveMode
'7333', // browserPort
'test task'
);

View file

@ -65,6 +65,8 @@ type GlobalStateKey =
| "allowedCommands"
| "soundEnabled"
| "diffEnabled"
| "isInteractiveMode"
| "browserPort"
export const GlobalFileNames = {
apiConversationHistory: "api_conversation_history.json",
@ -203,13 +205,17 @@ export class ClineProvider implements vscode.WebviewViewProvider {
apiConfiguration,
customInstructions,
diffEnabled,
isInteractiveMode,
browserPort,
} = await this.getState()
this.cline = new Cline(
this,
apiConfiguration,
customInstructions,
diffEnabled,
isInteractiveMode,
browserPort,
task,
images
)
@ -221,6 +227,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
apiConfiguration,
customInstructions,
diffEnabled,
isInteractiveMode,
browserPort,
} = await this.getState()
this.cline = new Cline(
@ -228,6 +236,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
apiConfiguration,
customInstructions,
diffEnabled,
isInteractiveMode,
browserPort,
undefined,
undefined,
historyItem,
@ -547,6 +557,14 @@ export class ClineProvider implements vscode.WebviewViewProvider {
await this.updateGlobalState("diffEnabled", diffEnabled)
await this.postStateToWebview()
break
case "isInteractiveMode":
await this.updateGlobalState("isInteractiveMode", message.bool ?? false)
await this.postStateToWebview()
break
case "browserPort":
await this.updateGlobalState("browserPort", message.text ?? "7333")
await this.postStateToWebview()
break
}
},
null,
@ -855,6 +873,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
soundEnabled,
diffEnabled,
taskHistory,
isInteractiveMode,
browserPort,
} = await this.getState()
const allowedCommands = vscode.workspace
@ -878,6 +898,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
diffEnabled: diffEnabled ?? false,
shouldShowAnnouncement: lastShownAnnouncementId !== this.latestAnnouncementId,
allowedCommands,
isInteractiveMode: isInteractiveMode ?? false,
browserPort: browserPort ?? "7333",
}
}
@ -969,6 +991,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
allowedCommands,
soundEnabled,
diffEnabled,
isInteractiveMode,
browserPort,
] = await Promise.all([
this.getGlobalState("apiProvider") as Promise<ApiProvider | undefined>,
this.getGlobalState("apiModelId") as Promise<string | undefined>,
@ -1005,6 +1029,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
this.getGlobalState("allowedCommands") as Promise<string[] | undefined>,
this.getGlobalState("soundEnabled") as Promise<boolean | undefined>,
this.getGlobalState("diffEnabled") as Promise<boolean | undefined>,
this.getGlobalState("isInteractiveMode") as Promise<boolean | undefined>,
this.getGlobalState("browserPort") as Promise<string | undefined>
])
let apiProvider: ApiProvider
@ -1059,6 +1085,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
allowedCommands,
soundEnabled,
diffEnabled,
isInteractiveMode: isInteractiveMode ?? false,
browserPort: browserPort ?? "7333"
}
}

View file

@ -23,6 +23,7 @@ export class BrowserSession {
async launchBrowser(interactive: boolean = false, port?: string) {
console.log("launch browser called")
this.isInteractive = interactive
this.browserPort = port ?? this.browserPort
// Set browserPort if provided, otherwise use default
if (port) {

View file

@ -43,6 +43,8 @@ export interface ExtensionState {
allowedCommands?: string[]
soundEnabled?: boolean
diffEnabled?: boolean
isInteractiveMode?: boolean
browserPort?: string
}
export interface ClineMessage {
@ -99,6 +101,8 @@ export interface ClineSayTool {
content?: string
regex?: string
filePattern?: string
isInteractiveMode?: boolean
browserPort?: string
}
// must keep in sync with system prompt

View file

@ -32,6 +32,8 @@ export interface WebviewMessage {
| "playSound"
| "soundEnabled"
| "diffEnabled"
| "isInteractiveMode"
| "browserPort"
text?: string
askResponse?: ClineAskResponse
apiConfiguration?: ApiConfiguration

View file

@ -32,6 +32,10 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
openRouterModels,
setAllowedCommands,
allowedCommands,
isInteractiveMode,
setInteractiveBrowserMode,
browserPort,
setBrowserPort,
} = useExtensionState()
const [apiErrorMessage, setApiErrorMessage] = useState<string | undefined>(undefined)
const [modelIdErrorMessage, setModelIdErrorMessage] = useState<string | undefined>(undefined)
@ -53,6 +57,8 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
vscode.postMessage({ type: "allowedCommands", commands: allowedCommands ?? [] })
vscode.postMessage({ type: "soundEnabled", bool: soundEnabled })
vscode.postMessage({ type: "diffEnabled", bool: diffEnabled })
vscode.postMessage({ type: "isInteractiveMode", bool: isInteractiveMode })
vscode.postMessage({ type: "browserPort", text: browserPort })
onDone()
}
}
@ -323,6 +329,45 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
</div>
</div>
<div style={{ marginBottom: 5 }}>
<h4 style={{ fontWeight: 500, marginBottom: 10 }}>Browser Settings</h4>
<div style={{ marginBottom: 5 }}>
<VSCodeCheckbox
checked={isInteractiveMode}
onChange={(e: any) => setInteractiveBrowserMode(e.target.checked)}
>
<span style={{ fontWeight: "500" }}>Interactive Browser Mode</span>
</VSCodeCheckbox>
<p style={{
fontSize: "12px",
marginTop: "5px",
color: "var(--vscode-descriptionForeground)",
}}>
When enabled, connects to an existing Chrome instance instead of launching a new browser.
</p>
</div>
{isInteractiveMode && (
<div style={{ marginBottom: 5 }}>
<VSCodeTextField
value={browserPort}
onInput={(e: any) => setBrowserPort(e.target.value)}
placeholder="Browser debugging port (default: 7333)"
>
<span style={{ fontWeight: "500" }}>Browser Port</span>
</VSCodeTextField>
<p style={{
fontSize: "12px",
marginTop: "5px",
color: "var(--vscode-descriptionForeground)",
}}>
Port number for Chrome's remote debugging. Launch Chrome with --remote-debugging-port=PORT
</p>
</div>
)}
</div>
{IS_DEV && (
<>
<div style={{ marginTop: "10px", marginBottom: "4px" }}>Debug</div>

View file

@ -27,6 +27,8 @@ export interface ExtensionStateContextType extends ExtensionState {
setAllowedCommands: (value: string[]) => void
setSoundEnabled: (value: boolean) => void
setDiffEnabled: (value: boolean) => void
setInteractiveBrowserMode: (value: boolean) => void
setBrowserPort: (value: string) => void
}
const ExtensionStateContext = createContext<ExtensionStateContextType | undefined>(undefined)
@ -40,6 +42,8 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
allowedCommands: [],
soundEnabled: false,
diffEnabled: false,
isInteractiveMode: false,
browserPort: "7333",
})
const [didHydrateState, setDidHydrateState] = useState(false)
const [showWelcome, setShowWelcome] = useState(false)
@ -130,6 +134,8 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
setAllowedCommands: (value) => setState((prevState) => ({ ...prevState, allowedCommands: value })),
setSoundEnabled: (value) => setState((prevState) => ({ ...prevState, soundEnabled: value })),
setDiffEnabled: (value) => setState((prevState) => ({ ...prevState, diffEnabled: value })),
setInteractiveBrowserMode: (value) => setState((prevState) => ({ ...prevState, isInteractiveMode: value })),
setBrowserPort: (value) => setState((prevState) => ({ ...prevState, browserPort: value })),
}
return <ExtensionStateContext.Provider value={contextValue}>{children}</ExtensionStateContext.Provider>