Skip to content

Commit f049fb4

Browse files
committed
Updates to enter interactive mode from the extension settings instead of parsing the prompt
1 parent 4d53bd2 commit f049fb4

File tree

9 files changed

+118
-32
lines changed

9 files changed

+118
-32
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ roo-cline-*.vsix
1111

1212
# Prompts
1313
prompts
14-
14+
.clinerules

src/core/Cline.ts

Lines changed: 28 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ export class Cline {
9898
apiConfiguration: ApiConfiguration,
9999
customInstructions?: string,
100100
diffEnabled?: boolean,
101+
isInteractiveMode?: boolean,
102+
browserPort?: string,
101103
task?: string,
102104
images?: string[],
103105
historyItem?: HistoryItem,
@@ -108,6 +110,8 @@ export class Cline {
108110
this.urlContentFetcher = new UrlContentFetcher(provider.context)
109111
this.browserSession = new BrowserSession(provider.context)
110112
this.diffViewProvider = new DiffViewProvider(cwd)
113+
this.isInteractiveMode = isInteractiveMode ?? false
114+
this.browserPort = browserPort ?? "7333"
111115
this.customInstructions = customInstructions
112116
if (diffEnabled && this.api.getModel().id) {
113117
this.diffStrategy = getDiffStrategy(this.api.getModel().id)
@@ -633,48 +637,29 @@ export class Cline {
633637
if (responseImages && responseImages.length > 0) {
634638
newUserContent.push(...formatResponse.imageBlocks(responseImages))
635639
}
636-
const wasInteractiveBrowser = (lastRelevantMessageIndex > 0) ? modifiedClineMessages[lastRelevantMessageIndex - 1].text?.includes("interactive mode") : false
640+
const state = await this.providerRef.deref()?.getState()
641+
const wasInteractiveBrowser = state?.isInteractiveMode
637642
let hadBrowserPort = '';
638643
if ( wasInteractiveBrowser ) {
639-
const match = modifiedClineMessages[lastRelevantMessageIndex - 1].text?.match(/\(browserPort\s*=\s*(\d+)\)/)
640-
if (match) {
641-
hadBrowserPort = match[1] ?? this.browserPort;
642-
this.providerRef.deref()?.outputChannel.appendLine(`resumeTaskFromHistory :: browserPort :: ${hadBrowserPort}`)
643-
}
644+
hadBrowserPort = state?.browserPort;
645+
this.providerRef.deref()?.outputChannel.appendLine(`resumeTaskFromHistory :: browserPort :: ${hadBrowserPort}`)
644646
}
645647
await this.overwriteApiConversationHistory(modifiedApiConversationHistory)
646648
await this.initiateTaskLoop(newUserContent, wasInteractiveBrowser, hadBrowserPort)
647649
}
648650

649651
private async initiateTaskLoop(userContent: UserContent, wasInteractiveBrowser: boolean = false, hadBrowserPort: string = ''): Promise<void> {
650-
// Check if any text block contains "interactive mode"
651-
const hasInteractiveMode = userContent.some((block) => {
652-
if (block.type === "text" && typeof block.text === "string") {
653-
return (block.type === "text" &&
654-
typeof block.text === "string" &&
655-
block.text.toLowerCase().includes("interactive mode"))
656-
} else {
657-
return false
658-
}
659-
}
660-
) || wasInteractiveBrowser;
652+
const state = await this.providerRef.deref()?.getState()
653+
const hasInteractiveMode = state?.isInteractiveMode ?? wasInteractiveBrowser
654+
661655

662656
this.providerRef.deref()?.outputChannel.appendLine(`initiateTaskLoop :: hasInteractiveMode :: ${hasInteractiveMode}`)
663657

664658
// Set interactive mode flag if found in text
665659
if (hasInteractiveMode) {
666660
this.isInteractiveMode = true;
667-
668-
// Parse browserPort if specified in text blocks
669-
userContent.forEach((block) => {
670-
if (block.type === "text" && typeof block.text === "string") {
671-
const match = block.text.match(/\(browserPort\s*=\s*(\d+)\)/)
672-
if (match) {
673-
this.browserPort = match[1] ?? hadBrowserPort;
674-
this.providerRef.deref()?.outputChannel.appendLine(`initiateTaskLoop :: browserPort :: ${this.browserPort}`)
675-
}
676-
}
677-
})
661+
this.browserPort = state?.browserPort ?? hadBrowserPort
662+
this.providerRef.deref()?.outputChannel.appendLine(`initiateTaskLoop :: browserPort :: ${this.browserPort}`)
678663
}
679664

680665
let nextUserContent = userContent;
@@ -1863,12 +1848,25 @@ export class Cline {
18631848
userContent: UserContent,
18641849
includeFileDetails: boolean = false,
18651850
isInteractiveMode: boolean = false,
1866-
browserPort: string = '7333'
1851+
browserPort?: string
18671852
): Promise<boolean> {
1853+
if (this.presentAssistantMessageHasPendingUpdates) {
1854+
this.presentAssistantMessage()
1855+
}
1856+
1857+
const state = await this.providerRef.deref()?.getState()
1858+
// Use optional chaining and provide defaults
1859+
this.isInteractiveMode = isInteractiveMode ?? state?.isInteractiveMode ?? false
1860+
this.browserPort = browserPort ?? state?.browserPort ?? "7333"
1861+
1862+
// Store interactive mode state
1863+
if (state?.isInteractiveMode !== undefined) {
1864+
this.isInteractiveMode = state.isInteractiveMode
1865+
}
1866+
18681867
if (this.abort) {
18691868
throw new Error("Cline instance aborted")
18701869
}
1871-
18721870
// Store interactive mode state
18731871
this.isInteractiveMode = isInteractiveMode;
18741872

src/core/__tests__/Cline.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,8 @@ describe('Cline', () => {
237237
mockApiConfig,
238238
'custom instructions',
239239
false,
240+
true, // isInteractiveMode
241+
'7333', // browserPort
240242
'test task'
241243
);
242244

src/core/webview/ClineProvider.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ type GlobalStateKey =
6565
| "allowedCommands"
6666
| "soundEnabled"
6767
| "diffEnabled"
68+
| "isInteractiveMode"
69+
| "browserPort"
6870

6971
export const GlobalFileNames = {
7072
apiConversationHistory: "api_conversation_history.json",
@@ -203,13 +205,17 @@ export class ClineProvider implements vscode.WebviewViewProvider {
203205
apiConfiguration,
204206
customInstructions,
205207
diffEnabled,
208+
isInteractiveMode,
209+
browserPort,
206210
} = await this.getState()
207-
211+
208212
this.cline = new Cline(
209213
this,
210214
apiConfiguration,
211215
customInstructions,
212216
diffEnabled,
217+
isInteractiveMode,
218+
browserPort,
213219
task,
214220
images
215221
)
@@ -221,13 +227,17 @@ export class ClineProvider implements vscode.WebviewViewProvider {
221227
apiConfiguration,
222228
customInstructions,
223229
diffEnabled,
230+
isInteractiveMode,
231+
browserPort,
224232
} = await this.getState()
225233

226234
this.cline = new Cline(
227235
this,
228236
apiConfiguration,
229237
customInstructions,
230238
diffEnabled,
239+
isInteractiveMode,
240+
browserPort,
231241
undefined,
232242
undefined,
233243
historyItem,
@@ -547,6 +557,14 @@ export class ClineProvider implements vscode.WebviewViewProvider {
547557
await this.updateGlobalState("diffEnabled", diffEnabled)
548558
await this.postStateToWebview()
549559
break
560+
case "isInteractiveMode":
561+
await this.updateGlobalState("isInteractiveMode", message.bool ?? false)
562+
await this.postStateToWebview()
563+
break
564+
case "browserPort":
565+
await this.updateGlobalState("browserPort", message.text ?? "7333")
566+
await this.postStateToWebview()
567+
break
550568
}
551569
},
552570
null,
@@ -855,6 +873,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
855873
soundEnabled,
856874
diffEnabled,
857875
taskHistory,
876+
isInteractiveMode,
877+
browserPort,
858878
} = await this.getState()
859879

860880
const allowedCommands = vscode.workspace
@@ -878,6 +898,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
878898
diffEnabled: diffEnabled ?? false,
879899
shouldShowAnnouncement: lastShownAnnouncementId !== this.latestAnnouncementId,
880900
allowedCommands,
901+
isInteractiveMode: isInteractiveMode ?? false,
902+
browserPort: browserPort ?? "7333",
881903
}
882904
}
883905

@@ -969,6 +991,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
969991
allowedCommands,
970992
soundEnabled,
971993
diffEnabled,
994+
isInteractiveMode,
995+
browserPort,
972996
] = await Promise.all([
973997
this.getGlobalState("apiProvider") as Promise<ApiProvider | undefined>,
974998
this.getGlobalState("apiModelId") as Promise<string | undefined>,
@@ -1005,6 +1029,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
10051029
this.getGlobalState("allowedCommands") as Promise<string[] | undefined>,
10061030
this.getGlobalState("soundEnabled") as Promise<boolean | undefined>,
10071031
this.getGlobalState("diffEnabled") as Promise<boolean | undefined>,
1032+
this.getGlobalState("isInteractiveMode") as Promise<boolean | undefined>,
1033+
this.getGlobalState("browserPort") as Promise<string | undefined>
10081034
])
10091035

10101036
let apiProvider: ApiProvider
@@ -1059,6 +1085,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
10591085
allowedCommands,
10601086
soundEnabled,
10611087
diffEnabled,
1088+
isInteractiveMode: isInteractiveMode ?? false,
1089+
browserPort: browserPort ?? "7333"
10621090
}
10631091
}
10641092

src/services/browser/BrowserSession.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export class BrowserSession {
2323
async launchBrowser(interactive: boolean = false, port?: string) {
2424
console.log("launch browser called")
2525
this.isInteractive = interactive
26+
this.browserPort = port ?? this.browserPort
2627

2728
// Set browserPort if provided, otherwise use default
2829
if (port) {

src/shared/ExtensionMessage.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ export interface ExtensionState {
4343
allowedCommands?: string[]
4444
soundEnabled?: boolean
4545
diffEnabled?: boolean
46+
isInteractiveMode?: boolean
47+
browserPort?: string
4648
}
4749

4850
export interface ClineMessage {
@@ -99,6 +101,8 @@ export interface ClineSayTool {
99101
content?: string
100102
regex?: string
101103
filePattern?: string
104+
isInteractiveMode?: boolean
105+
browserPort?: string
102106
}
103107

104108
// must keep in sync with system prompt

src/shared/WebviewMessage.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ export interface WebviewMessage {
3232
| "playSound"
3333
| "soundEnabled"
3434
| "diffEnabled"
35+
| "isInteractiveMode"
36+
| "browserPort"
3537
text?: string
3638
askResponse?: ClineAskResponse
3739
apiConfiguration?: ApiConfiguration

webview-ui/src/components/settings/SettingsView.tsx

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
3232
openRouterModels,
3333
setAllowedCommands,
3434
allowedCommands,
35+
isInteractiveMode,
36+
setInteractiveBrowserMode,
37+
browserPort,
38+
setBrowserPort,
3539
} = useExtensionState()
3640
const [apiErrorMessage, setApiErrorMessage] = useState<string | undefined>(undefined)
3741
const [modelIdErrorMessage, setModelIdErrorMessage] = useState<string | undefined>(undefined)
@@ -53,6 +57,8 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
5357
vscode.postMessage({ type: "allowedCommands", commands: allowedCommands ?? [] })
5458
vscode.postMessage({ type: "soundEnabled", bool: soundEnabled })
5559
vscode.postMessage({ type: "diffEnabled", bool: diffEnabled })
60+
vscode.postMessage({ type: "isInteractiveMode", bool: isInteractiveMode })
61+
vscode.postMessage({ type: "browserPort", text: browserPort })
5662
onDone()
5763
}
5864
}
@@ -323,6 +329,45 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
323329
</div>
324330
</div>
325331

332+
<div style={{ marginBottom: 5 }}>
333+
<h4 style={{ fontWeight: 500, marginBottom: 10 }}>Browser Settings</h4>
334+
335+
<div style={{ marginBottom: 5 }}>
336+
<VSCodeCheckbox
337+
checked={isInteractiveMode}
338+
onChange={(e: any) => setInteractiveBrowserMode(e.target.checked)}
339+
>
340+
<span style={{ fontWeight: "500" }}>Interactive Browser Mode</span>
341+
</VSCodeCheckbox>
342+
<p style={{
343+
fontSize: "12px",
344+
marginTop: "5px",
345+
color: "var(--vscode-descriptionForeground)",
346+
}}>
347+
When enabled, connects to an existing Chrome instance instead of launching a new browser.
348+
</p>
349+
</div>
350+
351+
{isInteractiveMode && (
352+
<div style={{ marginBottom: 5 }}>
353+
<VSCodeTextField
354+
value={browserPort}
355+
onInput={(e: any) => setBrowserPort(e.target.value)}
356+
placeholder="Browser debugging port (default: 7333)"
357+
>
358+
<span style={{ fontWeight: "500" }}>Browser Port</span>
359+
</VSCodeTextField>
360+
<p style={{
361+
fontSize: "12px",
362+
marginTop: "5px",
363+
color: "var(--vscode-descriptionForeground)",
364+
}}>
365+
Port number for Chrome's remote debugging. Launch Chrome with --remote-debugging-port=PORT
366+
</p>
367+
</div>
368+
)}
369+
</div>
370+
326371
{IS_DEV && (
327372
<>
328373
<div style={{ marginTop: "10px", marginBottom: "4px" }}>Debug</div>

webview-ui/src/context/ExtensionStateContext.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ export interface ExtensionStateContextType extends ExtensionState {
2727
setAllowedCommands: (value: string[]) => void
2828
setSoundEnabled: (value: boolean) => void
2929
setDiffEnabled: (value: boolean) => void
30+
setInteractiveBrowserMode: (value: boolean) => void
31+
setBrowserPort: (value: string) => void
3032
}
3133

3234
const ExtensionStateContext = createContext<ExtensionStateContextType | undefined>(undefined)
@@ -40,6 +42,8 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
4042
allowedCommands: [],
4143
soundEnabled: false,
4244
diffEnabled: false,
45+
isInteractiveMode: false,
46+
browserPort: "7333",
4347
})
4448
const [didHydrateState, setDidHydrateState] = useState(false)
4549
const [showWelcome, setShowWelcome] = useState(false)
@@ -130,6 +134,8 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
130134
setAllowedCommands: (value) => setState((prevState) => ({ ...prevState, allowedCommands: value })),
131135
setSoundEnabled: (value) => setState((prevState) => ({ ...prevState, soundEnabled: value })),
132136
setDiffEnabled: (value) => setState((prevState) => ({ ...prevState, diffEnabled: value })),
137+
setInteractiveBrowserMode: (value) => setState((prevState) => ({ ...prevState, isInteractiveMode: value })),
138+
setBrowserPort: (value) => setState((prevState) => ({ ...prevState, browserPort: value })),
133139
}
134140

135141
return <ExtensionStateContext.Provider value={contextValue}>{children}</ExtensionStateContext.Provider>

0 commit comments

Comments
 (0)