Skip to content

Commit 10a223f

Browse files
authored
Terminal profile setting (RooCodeInc#4079)
* feat: Added a configurable default terminal profile setting * chore: format * refactor: migrate terminal profiles to gRPC and remove legacy message handling - rename AvailableTerminalProfilesResponse to TerminalProfiles in proto - remove duplicate TerminalProfile type from terminal_types.ts - update all imports to use TerminalProfile from proto/state - remove legacy availableTerminalProfiles message handling from ExtensionStateContext - clean up ExtensionMessage type by removing unused availableTerminalProfiles - translate Spanish comment to English in TerminalSettingsSection - update server-side getAvailableTerminalProfiles to use new proto type * chore: lint * fix: merge main * fix: resolve errors * chore: notify terminal profile settings * chore: merge main * feat: improve default terminal profile changes * fix: update changes on save
1 parent 0fade12 commit 10a223f

File tree

18 files changed

+491
-27
lines changed

18 files changed

+491
-27
lines changed

.changeset/nice-trees-ring.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"claude-dev": minor
3+
---
4+
5+
Added a configurable default terminal profile setting

proto/state.proto

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ import "common.proto";
77

88
service StateService {
99
rpc getLatestState(EmptyRequest) returns (State);
10+
rpc updateTerminalConnectionTimeout(Int64Request) returns (Int64);
11+
rpc updateTerminalReuseEnabled(BooleanRequest) returns (Empty);
12+
rpc updateDefaultTerminalProfile(StringRequest) returns (TerminalProfileUpdateResponse);
13+
rpc getAvailableTerminalProfiles(EmptyRequest) returns (TerminalProfiles);
1014
rpc subscribeToState(EmptyRequest) returns (stream State);
1115
rpc toggleFavoriteModel(StringRequest) returns (Empty);
1216
rpc resetState(EmptyRequest) returns (Empty);
@@ -19,6 +23,23 @@ message State {
1923
string state_json = 1;
2024
}
2125

26+
message TerminalProfiles {
27+
repeated TerminalProfile profiles = 1;
28+
}
29+
30+
message TerminalProfile {
31+
string id = 1;
32+
string name = 2;
33+
optional string path = 3;
34+
optional string description = 4;
35+
}
36+
37+
message TerminalProfileUpdateResponse {
38+
int32 closed_count = 1;
39+
int32 busy_terminals_count = 2;
40+
bool has_busy_terminals = 3;
41+
}
42+
2243
message TogglePlanActModeRequest {
2344
Metadata metadata = 1;
2445
ChatSettings chat_settings = 2;
@@ -42,10 +63,8 @@ message ChatContent {
4263
repeated string files = 3;
4364
}
4465

45-
// Message for auto approval settings
4666
message AutoApprovalSettingsRequest {
4767
Metadata metadata = 1;
48-
4968
message Actions {
5069
bool read_files = 1;
5170
bool read_files_externally = 2;
@@ -56,7 +75,6 @@ message AutoApprovalSettingsRequest {
5675
bool use_browser = 7;
5776
bool use_mcp = 8;
5877
}
59-
6078
int32 version = 2;
6179
bool enabled = 3;
6280
Actions actions = 4;

src/core/controller/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ export class Controller {
135135
chatSettings,
136136
shellIntegrationTimeout,
137137
terminalReuseEnabled,
138+
defaultTerminalProfile,
138139
enableCheckpointsSetting,
139140
isNewUser,
140141
taskHistory,
@@ -170,6 +171,7 @@ export class Controller {
170171
chatSettings,
171172
shellIntegrationTimeout,
172173
terminalReuseEnabled ?? true,
174+
defaultTerminalProfile ?? "default",
173175
enableCheckpointsSetting ?? true,
174176
task,
175177
images,
@@ -220,6 +222,7 @@ export class Controller {
220222
await this.postStateToWebview()
221223
break
222224
}
225+
223226
case "clearAllTaskHistory": {
224227
const answer = await vscode.window.showWarningMessage(
225228
"What would you like to delete?",
@@ -952,6 +955,7 @@ export class Controller {
952955
globalWorkflowToggles,
953956
shellIntegrationTimeout,
954957
terminalReuseEnabled,
958+
defaultTerminalProfile,
955959
isNewUser,
956960
mcpResponsesCollapsed,
957961
} = await getAllExtensionState(this.context)
@@ -997,6 +1001,7 @@ export class Controller {
9971001
globalWorkflowToggles: globalWorkflowToggles || {},
9981002
shellIntegrationTimeout,
9991003
terminalReuseEnabled,
1004+
defaultTerminalProfile,
10001005
isNewUser,
10011006
mcpResponsesCollapsed,
10021007
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { Controller } from "../index"
2+
import * as proto from "@/shared/proto"
3+
import { getAvailableTerminalProfiles as getTerminalProfilesFromShell } from "../../../utils/shell"
4+
5+
export async function getAvailableTerminalProfiles(
6+
controller: Controller,
7+
request: proto.cline.EmptyRequest,
8+
): Promise<proto.cline.TerminalProfiles> {
9+
const profiles = getTerminalProfilesFromShell()
10+
11+
return proto.cline.TerminalProfiles.create({
12+
profiles: profiles.map((profile) => ({
13+
id: profile.id,
14+
name: profile.name,
15+
path: profile.path || "",
16+
description: profile.description || "",
17+
})),
18+
})
19+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import * as vscode from "vscode"
2+
import { Controller } from "../index"
3+
import * as proto from "@/shared/proto"
4+
import { updateGlobalState } from "../../storage/state"
5+
import { TerminalInfo } from "@/integrations/terminal/TerminalRegistry"
6+
7+
export async function updateDefaultTerminalProfile(
8+
controller: Controller,
9+
request: proto.cline.StringRequest,
10+
): Promise<proto.cline.TerminalProfileUpdateResponse> {
11+
const profileId = request.value
12+
13+
// Update the terminal profile in the state
14+
await updateGlobalState(controller.context, "defaultTerminalProfile", profileId)
15+
16+
let closedCount = 0
17+
let busyTerminals: TerminalInfo[] = []
18+
19+
// Update the terminal manager of the current task if it exists
20+
if (controller.task) {
21+
// Call the updated setDefaultTerminalProfile method that returns closed terminal info
22+
const result = controller.task.terminalManager.setDefaultTerminalProfile(profileId)
23+
closedCount = result.closedCount
24+
busyTerminals = result.busyTerminals
25+
26+
// Show information message if terminals were closed
27+
if (closedCount > 0) {
28+
vscode.window.showInformationMessage(
29+
`Closed ${closedCount} ${closedCount === 1 ? "terminal" : "terminals"} with different profile.`,
30+
)
31+
}
32+
33+
// Show warning if there are busy terminals that couldn't be closed
34+
if (busyTerminals.length > 0) {
35+
vscode.window.showWarningMessage(
36+
`${busyTerminals.length} busy ${busyTerminals.length === 1 ? "terminal has" : "terminals have"} a different profile. ` +
37+
`Close ${busyTerminals.length === 1 ? "it" : "them"} to use the new profile for all commands.`,
38+
)
39+
}
40+
}
41+
42+
// Broadcast state update to all webviews
43+
await controller.postStateToWebview()
44+
45+
return proto.cline.TerminalProfileUpdateResponse.create({
46+
closedCount,
47+
busyTerminalsCount: busyTerminals.length,
48+
hasBusyTerminals: busyTerminals.length > 0,
49+
})
50+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { Controller } from "../index"
2+
import * as proto from "@/shared/proto"
3+
import { updateGlobalState } from "../../storage/state"
4+
5+
export async function updateTerminalConnectionTimeout(
6+
controller: Controller,
7+
request: proto.cline.Int64Request,
8+
): Promise<proto.cline.Int64> {
9+
const timeoutValue = request.value
10+
11+
// Update the terminal connection timeout setting in the state
12+
await updateGlobalState(controller.context, "shellIntegrationTimeout", timeoutValue)
13+
14+
// Broadcast state update to all webviews
15+
await controller.postStateToWebview()
16+
17+
return proto.cline.Int64.create({ value: timeoutValue })
18+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { Controller } from "../index"
2+
import * as proto from "@/shared/proto"
3+
import { updateGlobalState } from "../../storage/state"
4+
5+
export async function updateTerminalReuseEnabled(
6+
controller: Controller,
7+
request: proto.cline.BooleanRequest,
8+
): Promise<proto.cline.Empty> {
9+
const enabled = request.value
10+
11+
// Update the terminal reuse setting in the state
12+
await updateGlobalState(controller.context, "terminalReuseEnabled", enabled)
13+
14+
// Broadcast state update to all webviews
15+
await controller.postStateToWebview()
16+
17+
return proto.cline.Empty.create({})
18+
}

src/core/storage/state-keys.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ export type GlobalStateKey =
6767
| "shellIntegrationTimeout"
6868
| "mcpResponsesCollapsed"
6969
| "terminalReuseEnabled"
70+
| "defaultTerminalProfile"
7071
| "isNewUser"
7172

7273
export type LocalStateKey =

src/core/storage/state.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ export async function getAllExtensionState(context: vscode.ExtensionContext) {
237237
mcpResponsesCollapsedRaw,
238238
globalWorkflowToggles,
239239
terminalReuseEnabled,
240+
defaultTerminalProfile,
240241
] = await Promise.all([
241242
getGlobalState(context, "isNewUser") as Promise<boolean | undefined>,
242243
getSecret(context, "apiKey") as Promise<string | undefined>,
@@ -300,6 +301,7 @@ export async function getAllExtensionState(context: vscode.ExtensionContext) {
300301
getGlobalState(context, "mcpResponsesCollapsed") as Promise<boolean | undefined>,
301302
getGlobalState(context, "globalWorkflowToggles") as Promise<ClineRulesToggles | undefined>,
302303
getGlobalState(context, "terminalReuseEnabled") as Promise<boolean | undefined>,
304+
getGlobalState(context, "defaultTerminalProfile") as Promise<string | undefined>,
303305
])
304306

305307
const localClineRulesToggles = (await getWorkspaceState(context, "localClineRulesToggles")) as ClineRulesToggles
@@ -496,6 +498,7 @@ export async function getAllExtensionState(context: vscode.ExtensionContext) {
496498
enableCheckpointsSetting: enableCheckpointsSetting,
497499
shellIntegrationTimeout: shellIntegrationTimeout || 4000,
498500
terminalReuseEnabled: terminalReuseEnabled ?? true,
501+
defaultTerminalProfile: defaultTerminalProfile ?? "default",
499502
globalWorkflowToggles: globalWorkflowToggles || {},
500503
}
501504
}

src/core/task/index.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ import { isInTestMode } from "../../services/test/TestMode"
113113
import { processFilesIntoText } from "@integrations/misc/extract-text"
114114
import { featureFlagsService } from "@services/posthog/feature-flags/FeatureFlagsService"
115115
import { StreamingJsonReplacer, ChangeLocation } from "@core/assistant-message/diff-json"
116-
import { isClaude4ModelFamily } from "@/utils/model-utils"
116+
import { isClaude4ModelFamily } from "@utils/model-utils"
117117
import { saveClineMessagesAndUpdateHistory } from "./message-state"
118118

119119
export const USE_EXPERIMENTAL_CLAUDE4_FEATURES = false
@@ -141,7 +141,7 @@ export class Task {
141141
readonly taskId: string
142142
private taskIsFavorited?: boolean
143143
api: ApiHandler
144-
private terminalManager: TerminalManager
144+
terminalManager: TerminalManager
145145
private urlContentFetcher: UrlContentFetcher
146146
browserSession: BrowserSession
147147
contextManager: ContextManager
@@ -204,6 +204,7 @@ export class Task {
204204
chatSettings: ChatSettings,
205205
shellIntegrationTimeout: number,
206206
terminalReuseEnabled: boolean,
207+
defaultTerminalProfile: string,
207208
enableCheckpointsSetting: boolean,
208209
task?: string,
209210
images?: string[],
@@ -223,6 +224,7 @@ export class Task {
223224
this.terminalManager = new TerminalManager()
224225
this.terminalManager.setShellIntegrationTimeout(shellIntegrationTimeout)
225226
this.terminalManager.setTerminalReuseEnabled(terminalReuseEnabled ?? true)
227+
this.terminalManager.setDefaultTerminalProfile(defaultTerminalProfile)
226228
this.urlContentFetcher = new UrlContentFetcher(context)
227229
this.browserSession = new BrowserSession(context, browserSettings)
228230
this.contextManager = new ContextManager()

0 commit comments

Comments
 (0)