Skip to content

Commit 542ec2b

Browse files
migrate and remove custom instructions (RooCodeInc#4158)
1 parent 9733ef7 commit 542ec2b

File tree

17 files changed

+61
-176
lines changed

17 files changed

+61
-176
lines changed

proto/state.proto

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -69,15 +69,14 @@ message AutoApprovalSettingsRequest {
6969
message UpdateSettingsRequest {
7070
Metadata metadata = 1;
7171
optional ApiConfiguration api_configuration = 2;
72-
optional string custom_instructions_setting = 3;
73-
optional string telemetry_setting = 4;
74-
optional bool plan_act_separate_models_setting = 5;
75-
optional bool enable_checkpoints_setting = 6;
76-
optional bool mcp_marketplace_enabled = 7;
77-
optional ChatSettings chat_settings = 8;
78-
optional int64 shell_integration_timeout = 9;
79-
optional bool terminal_reuse_enabled = 10;
80-
optional bool mcp_responses_collapsed = 11;
72+
optional string telemetry_setting = 3;
73+
optional bool plan_act_separate_models_setting = 4;
74+
optional bool enable_checkpoints_setting = 5;
75+
optional bool mcp_marketplace_enabled = 6;
76+
optional ChatSettings chat_settings = 7;
77+
optional int64 shell_integration_timeout = 8;
78+
optional bool terminal_reuse_enabled = 9;
79+
optional bool mcp_responses_collapsed = 10;
8180
}
8281

8382
// Complete API Configuration message

src/core/controller/index.ts

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,6 @@ export class Controller {
130130
await this.clearTask() // ensures that an existing task doesn't exist before starting a new one, although this shouldn't be possible since user must clear task before starting a new one
131131
const {
132132
apiConfiguration,
133-
customInstructions,
134133
autoApprovalSettings,
135134
browserSettings,
136135
chatSettings,
@@ -172,7 +171,6 @@ export class Controller {
172171
shellIntegrationTimeout,
173172
terminalReuseEnabled ?? true,
174173
enableCheckpointsSetting ?? true,
175-
customInstructions,
176174
task,
177175
images,
178176
files,
@@ -460,14 +458,6 @@ export class Controller {
460458
}
461459
}
462460

463-
async updateCustomInstructions(instructions?: string) {
464-
// User may be clearing the field
465-
await updateGlobalState(this.context, "customInstructions", instructions || undefined)
466-
if (this.task) {
467-
this.task.customInstructions = instructions || undefined
468-
}
469-
}
470-
471461
// Account
472462

473463
async fetchUserCreditsData() {
@@ -949,7 +939,6 @@ export class Controller {
949939
const {
950940
apiConfiguration,
951941
lastShownAnnouncementId,
952-
customInstructions,
953942
taskHistory,
954943
autoApprovalSettings,
955944
browserSettings,
@@ -981,7 +970,6 @@ export class Controller {
981970
return {
982971
version: this.context.extension?.packageJSON?.version ?? "",
983972
apiConfiguration,
984-
customInstructions,
985973
uriScheme: vscode.env.uriScheme,
986974
currentTaskItem: this.task?.taskId ? (taskHistory || []).find((item) => item.id === this.task?.taskId) : undefined,
987975
checkpointTrackerErrorMessage: this.task?.checkpointTrackerErrorMessage,

src/core/controller/state/updateSettings.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,6 @@ export async function updateSettings(controller: Controller, request: UpdateSett
2525
}
2626
}
2727

28-
// Update custom instructions
29-
if (request.customInstructionsSetting !== undefined) {
30-
await controller.updateCustomInstructions(request.customInstructionsSetting)
31-
}
32-
3328
// Update telemetry setting
3429
if (request.telemetrySetting) {
3530
await controller.updateTelemetrySetting(request.telemetrySetting as TelemetrySetting)

src/core/prompts/model_prompts/claude4.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -663,7 +663,6 @@ You accomplish a given task iteratively, breaking it down into clear steps and w
663663
}
664664

665665
export function addUserInstructions(
666-
settingsCustomInstructions?: string,
667666
globalClineRulesFileInstructions?: string,
668667
localClineRulesFileInstructions?: string,
669668
localCursorRulesFileInstructions?: string,
@@ -676,9 +675,6 @@ export function addUserInstructions(
676675
if (preferredLanguageInstructions) {
677676
customInstructions += preferredLanguageInstructions + "\n\n"
678677
}
679-
if (settingsCustomInstructions) {
680-
customInstructions += settingsCustomInstructions + "\n\n"
681-
}
682678
if (globalClineRulesFileInstructions) {
683679
customInstructions += globalClineRulesFileInstructions + "\n\n"
684680
}

src/core/prompts/system.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -651,7 +651,6 @@ You accomplish a given task iteratively, breaking it down into clear steps and w
651651

652652

653653
export function addUserInstructions(
654-
settingsCustomInstructions?: string,
655654
globalClineRulesFileInstructions?: string,
656655
localClineRulesFileInstructions?: string,
657656
localCursorRulesFileInstructions?: string,
@@ -664,9 +663,6 @@ export function addUserInstructions(
664663
if (preferredLanguageInstructions) {
665664
customInstructions += preferredLanguageInstructions + "\n\n"
666665
}
667-
if (settingsCustomInstructions) {
668-
customInstructions += settingsCustomInstructions + "\n\n"
669-
}
670666
if (globalClineRulesFileInstructions) {
671667
customInstructions += globalClineRulesFileInstructions + "\n\n"
672668
}

src/core/storage/state-keys.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ export type GlobalStateKey =
3333
| "vertexProjectId"
3434
| "vertexRegion"
3535
| "lastShownAnnouncementId"
36-
| "customInstructions"
3736
| "taskHistory"
3837
| "openAiBaseUrl"
3938
| "openAiModelId"

src/core/storage/state.ts

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ import { ChatSettings } from "@shared/ChatSettings"
1111
import { TelemetrySetting } from "@shared/TelemetrySetting"
1212
import { UserInfo } from "@shared/UserInfo"
1313
import { ClineRulesToggles } from "@shared/cline-rules"
14+
import { ensureRulesDirectoryExists } from "./disk"
15+
import fs from "fs/promises"
16+
import path from "path"
1417
/*
1518
Storage
1619
https://dev.to/kompotkot/how-to-use-secretstorage-in-your-vscode-extensions-2hco
@@ -125,6 +128,51 @@ async function migrateEnableCheckpointsSetting(enableCheckpointsSettingRaw: bool
125128
return enableCheckpointsSettingRaw ?? true
126129
}
127130

131+
export async function migrateCustomInstructionsToGlobalRules(context: vscode.ExtensionContext) {
132+
try {
133+
const customInstructions = (await context.globalState.get("customInstructions")) as string | undefined
134+
135+
if (customInstructions?.trim()) {
136+
console.log("Migrating custom instructions to global Cline rules...")
137+
138+
// Create global .clinerules directory if it doesn't exist
139+
const globalRulesDir = await ensureRulesDirectoryExists()
140+
141+
// Use a fixed filename for custom instructions
142+
const migrationFileName = "custom_instructions.md"
143+
const migrationFilePath = path.join(globalRulesDir, migrationFileName)
144+
145+
try {
146+
// Check if file already exists to determine if we should append
147+
let existingContent = ""
148+
try {
149+
existingContent = await fs.readFile(migrationFilePath, "utf8")
150+
} catch (readError) {
151+
// File doesn't exist, which is fine
152+
}
153+
154+
// Append or create the file with custom instructions
155+
const contentToWrite = existingContent
156+
? `${existingContent}\n\n---\n\n${customInstructions.trim()}`
157+
: customInstructions.trim()
158+
159+
await fs.writeFile(migrationFilePath, contentToWrite)
160+
console.log(`Successfully ${existingContent ? "appended to" : "created"} migration file: ${migrationFilePath}`)
161+
} catch (fileError) {
162+
console.error("Failed to write migration file:", fileError)
163+
return
164+
}
165+
166+
// Remove customInstructions from global state only after successful file creation
167+
await context.globalState.update("customInstructions", undefined)
168+
console.log("Successfully migrated custom instructions to global Cline rules")
169+
}
170+
} catch (error) {
171+
console.error("Failed to migrate custom instructions to global rules:", error)
172+
// Continue execution - migration failure shouldn't break extension startup
173+
}
174+
}
175+
128176
export async function getAllExtensionState(context: vscode.ExtensionContext) {
129177
const [
130178
isNewUser,
@@ -161,7 +209,6 @@ export async function getAllExtensionState(context: vscode.ExtensionContext) {
161209
azureApiVersion,
162210
openRouterProviderSorting,
163211
lastShownAnnouncementId,
164-
customInstructions,
165212
taskHistory,
166213
autoApprovalSettings,
167214
browserSettings,
@@ -225,7 +272,6 @@ export async function getAllExtensionState(context: vscode.ExtensionContext) {
225272
getGlobalState(context, "azureApiVersion") as Promise<string | undefined>,
226273
getGlobalState(context, "openRouterProviderSorting") as Promise<string | undefined>,
227274
getGlobalState(context, "lastShownAnnouncementId") as Promise<string | undefined>,
228-
getGlobalState(context, "customInstructions") as Promise<string | undefined>,
229275
getGlobalState(context, "taskHistory") as Promise<HistoryItem[] | undefined>,
230276
getGlobalState(context, "autoApprovalSettings") as Promise<AutoApprovalSettings | undefined>,
231277
getGlobalState(context, "browserSettings") as Promise<BrowserSettings | undefined>,
@@ -425,7 +471,6 @@ export async function getAllExtensionState(context: vscode.ExtensionContext) {
425471
},
426472
isNewUser: isNewUser ?? true,
427473
lastShownAnnouncementId,
428-
customInstructions,
429474
taskHistory,
430475
autoApprovalSettings: autoApprovalSettings || DEFAULT_AUTO_APPROVAL_SETTINGS, // default value can be 0 or empty string
431476
globalClineRulesToggles: globalClineRulesToggles || {},

src/core/task/index.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,6 @@ export class Task {
145145
browserSession: BrowserSession
146146
contextManager: ContextManager
147147
private didEditFile: boolean = false
148-
customInstructions?: string
149148
autoApprovalSettings: AutoApprovalSettings
150149
browserSettings: BrowserSettings
151150
chatSettings: ChatSettings
@@ -205,7 +204,6 @@ export class Task {
205204
shellIntegrationTimeout: number,
206205
terminalReuseEnabled: boolean,
207206
enableCheckpointsSetting: boolean,
208-
customInstructions?: string,
209207
task?: string,
210208
images?: string[],
211209
files?: string[],
@@ -228,7 +226,6 @@ export class Task {
228226
this.browserSession = new BrowserSession(context, browserSettings)
229227
this.contextManager = new ContextManager()
230228
this.diffViewProvider = new DiffViewProvider(cwd)
231-
this.customInstructions = customInstructions
232229
this.autoApprovalSettings = autoApprovalSettings
233230
this.browserSettings = browserSettings
234231
this.chatSettings = chatSettings
@@ -1646,7 +1643,6 @@ export class Task {
16461643
const isClaude4Model = isClaude4ModelFamily(this.api)
16471644
let systemPrompt = await SYSTEM_PROMPT(cwd, supportsBrowserUse, this.mcpHub, this.browserSettings, isClaude4Model)
16481645

1649-
let settingsCustomInstructions = this.customInstructions?.trim()
16501646
await this.migratePreferredLanguageToolSetting()
16511647
const preferredLanguage = getLanguageKey(this.chatSettings.preferredLanguage as LanguageDisplay)
16521648
const preferredLanguageInstructions =
@@ -1674,7 +1670,6 @@ export class Task {
16741670
}
16751671

16761672
if (
1677-
settingsCustomInstructions ||
16781673
globalClineRulesFileInstructions ||
16791674
localClineRulesFileInstructions ||
16801675
localCursorRulesFileInstructions ||
@@ -1685,7 +1680,6 @@ export class Task {
16851680
) {
16861681
// altering the system prompt mid-task will break the prompt cache, but in the grand scheme this will not change often so it's better to not pollute user messages with it the way we have to with <potentially relevant details>
16871682
const userInstructions = addUserInstructions(
1688-
settingsCustomInstructions,
16891683
globalClineRulesFileInstructions,
16901684
localClineRulesFileInstructions,
16911685
localCursorRulesFileInstructions,
@@ -1694,6 +1688,7 @@ export class Task {
16941688
clineIgnoreInstructions,
16951689
preferredLanguageInstructions,
16961690
)
1691+
console.log("[INSTRUCTIONS] User instructions:", userInstructions)
16971692
systemPrompt += userInstructions
16981693
}
16991694
const contextManagementMetadata = await this.contextManager.getNewContextMessagesAndMetadata(

src/exports/README.md

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,6 @@ The Cline extension exposes an API that can be used by other extensions. To use
1818
if (cline) {
1919
// Now you can use the API
2020

21-
// Set custom instructions
22-
await cline.setCustomInstructions("Talk like a pirate")
23-
24-
// Get custom instructions
25-
const instructions = await cline.getCustomInstructions()
26-
console.log("Current custom instructions:", instructions)
27-
2821
// Start a new task with an initial message
2922
await cline.startNewTask("Hello, Cline! Let's make a new project...")
3023

src/exports/__tests__/cline-api.test.ts

Lines changed: 0 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -65,59 +65,6 @@ describe("ClineAPI Core Functionality", () => {
6565
sandbox.restore()
6666
})
6767

68-
describe("setCustomInstructions", () => {
69-
it("should update custom instructions in controller", async () => {
70-
const testInstructions = "Test custom instructions"
71-
72-
await api.setCustomInstructions(testInstructions)
73-
74-
// Verify controller method was called
75-
sinon.assert.calledOnce(mockController.updateCustomInstructions)
76-
sinon.assert.calledWith(mockController.updateCustomInstructions, testInstructions)
77-
78-
// Verify output channel was updated
79-
sinon.assert.calledWith(mockOutputChannel.appendLine, "Custom instructions set")
80-
})
81-
82-
it("should handle empty instructions", async () => {
83-
await api.setCustomInstructions("")
84-
85-
sinon.assert.calledWith(mockController.updateCustomInstructions, "")
86-
sinon.assert.calledWith(mockOutputChannel.appendLine, "Custom instructions set")
87-
})
88-
89-
it("should handle very long instructions", async () => {
90-
const longInstructions = "a".repeat(10000)
91-
92-
await api.setCustomInstructions(longInstructions)
93-
94-
sinon.assert.calledWith(mockController.updateCustomInstructions, longInstructions)
95-
})
96-
})
97-
98-
describe("getCustomInstructions", () => {
99-
it("should retrieve custom instructions from state", async () => {
100-
const testInstructions = "Retrieved instructions"
101-
// The real implementation uses getGlobalState from the state module
102-
getGlobalStateStub.resolves(testInstructions)
103-
104-
const result = await api.getCustomInstructions()
105-
106-
result!.should.equal(testInstructions)
107-
sinon.assert.calledWith(getGlobalStateStub, mockController.context, "customInstructions")
108-
})
109-
110-
it("should return undefined when no instructions set", async () => {
111-
// The real implementation uses getGlobalState from the state module
112-
getGlobalStateStub.resolves(undefined)
113-
114-
const result = await api.getCustomInstructions()
115-
116-
should.not.exist(result)
117-
sinon.assert.calledWith(getGlobalStateStub, mockController.context, "customInstructions")
118-
})
119-
})
120-
12168
describe("startNewTask", () => {
12269
it("should clear existing task and start new one with description", async () => {
12370
const taskDescription = "Create a test function"
@@ -260,17 +207,6 @@ describe("ClineAPI Core Functionality", () => {
260207
})
261208

262209
describe("Error Handling", () => {
263-
it("should handle errors in setCustomInstructions", async () => {
264-
mockController.updateCustomInstructions.rejects(new Error("Update failed"))
265-
266-
try {
267-
await api.setCustomInstructions("test")
268-
should.fail("", "", "Should have thrown an error", "")
269-
} catch (error: any) {
270-
error.message.should.equal("Update failed")
271-
}
272-
})
273-
274210
it("should handle errors in task initialization", async () => {
275211
mockController.initTask.rejects(new Error("Init failed"))
276212

0 commit comments

Comments
 (0)