Просмотр исходного кода

Merge pull request #1139 from RooVetGit/cte/cline-options

Add a ClineOptions type
Chris Estreich 10 месяцев назад
Родитель
Сommit
91ca35ddc8

+ 35 - 22
src/core/Cline.ts

@@ -73,6 +73,20 @@ type UserContent = Array<
 	Anthropic.TextBlockParam | Anthropic.ImageBlockParam | Anthropic.ToolUseBlockParam | Anthropic.ToolResultBlockParam
 >
 
+export type ClineOptions = {
+	provider: ClineProvider
+	apiConfiguration: ApiConfiguration
+	customInstructions?: string
+	enableDiff?: boolean
+	enableCheckpoints?: boolean
+	fuzzyMatchThreshold?: number
+	task?: string
+	images?: string[]
+	historyItem?: HistoryItem
+	experiments?: Record<string, boolean>
+	startTask?: boolean
+}
+
 export class Cline {
 	readonly taskId: string
 	api: ApiHandler
@@ -118,19 +132,19 @@ export class Cline {
 	private didAlreadyUseTool = false
 	private didCompleteReadingStream = false
 
-	constructor(
-		provider: ClineProvider,
-		apiConfiguration: ApiConfiguration,
-		customInstructions?: string,
-		enableDiff?: boolean,
-		enableCheckpoints?: boolean,
-		fuzzyMatchThreshold?: number,
-		task?: string | undefined,
-		images?: string[] | undefined,
-		historyItem?: HistoryItem | undefined,
-		experiments?: Record<string, boolean>,
+	constructor({
+		provider,
+		apiConfiguration,
+		customInstructions,
+		enableDiff,
+		enableCheckpoints,
+		fuzzyMatchThreshold,
+		task,
+		images,
+		historyItem,
+		experiments,
 		startTask = true,
-	) {
+	}: ClineOptions) {
 		if (startTask && !task && !images && !historyItem) {
 			throw new Error("Either historyItem or task/images must be provided")
 		}
@@ -165,21 +179,20 @@ export class Cline {
 		}
 	}
 
-	static create(...args: ConstructorParameters<typeof Cline>): [Cline, Promise<void>] {
-		args[10] = false // startTask
-		const instance = new Cline(...args)
-
-		let task
+	static create(options: ClineOptions): [Cline, Promise<void>] {
+		const instance = new Cline({ ...options, startTask: false })
+		const { images, task, historyItem } = options
+		let promise
 
-		if (args[6] || args[7]) {
-			task = instance.startTask(args[6], args[7])
-		} else if (args[8]) {
-			task = instance.resumeTaskFromHistory()
+		if (images || task) {
+			promise = instance.startTask(task, images)
+		} else if (historyItem) {
+			promise = instance.resumeTaskFromHistory()
 		} else {
 			throw new Error("Either historyItem or task/images must be provided")
 		}
 
-		return [instance, task]
+		return [instance, promise]
 	}
 
 	// Add method to update diffStrategy

+ 67 - 108
src/core/__tests__/Cline.test.ts

@@ -327,15 +327,13 @@ describe("Cline", () => {
 
 	describe("constructor", () => {
 		it("should respect provided settings", async () => {
-			const [cline, task] = Cline.create(
-				mockProvider,
-				mockApiConfig,
-				"custom instructions",
-				false,
-				false,
-				0.95, // 95% threshold
-				"test task",
-			)
+			const [cline, task] = Cline.create({
+				provider: mockProvider,
+				apiConfiguration: mockApiConfig,
+				customInstructions: "custom instructions",
+				fuzzyMatchThreshold: 0.95,
+				task: "test task",
+			})
 
 			expect(cline.customInstructions).toBe("custom instructions")
 			expect(cline.diffEnabled).toBe(false)
@@ -345,15 +343,14 @@ describe("Cline", () => {
 		})
 
 		it("should use default fuzzy match threshold when not provided", async () => {
-			const [cline, task] = await Cline.create(
-				mockProvider,
-				mockApiConfig,
-				"custom instructions",
-				true,
-				false,
-				undefined,
-				"test task",
-			)
+			const [cline, task] = await Cline.create({
+				provider: mockProvider,
+				apiConfiguration: mockApiConfig,
+				customInstructions: "custom instructions",
+				enableDiff: true,
+				fuzzyMatchThreshold: 0.95,
+				task: "test task",
+			})
 
 			expect(cline.diffEnabled).toBe(true)
 			// The diff strategy should be created with default threshold (1.0)
@@ -366,15 +363,14 @@ describe("Cline", () => {
 		it("should use provided fuzzy match threshold", async () => {
 			const getDiffStrategySpy = jest.spyOn(require("../diff/DiffStrategy"), "getDiffStrategy")
 
-			const [cline, task] = Cline.create(
-				mockProvider,
-				mockApiConfig,
-				"custom instructions",
-				true,
-				false,
-				0.9, // 90% threshold
-				"test task",
-			)
+			const [cline, task] = await Cline.create({
+				provider: mockProvider,
+				apiConfiguration: mockApiConfig,
+				customInstructions: "custom instructions",
+				enableDiff: true,
+				fuzzyMatchThreshold: 0.9,
+				task: "test task",
+			})
 
 			expect(cline.diffEnabled).toBe(true)
 			expect(cline.diffStrategy).toBeDefined()
@@ -389,15 +385,13 @@ describe("Cline", () => {
 		it("should pass default threshold to diff strategy when not provided", async () => {
 			const getDiffStrategySpy = jest.spyOn(require("../diff/DiffStrategy"), "getDiffStrategy")
 
-			const [cline, task] = Cline.create(
-				mockProvider,
-				mockApiConfig,
-				"custom instructions",
-				true,
-				false,
-				undefined,
-				"test task",
-			)
+			const [cline, task] = Cline.create({
+				provider: mockProvider,
+				apiConfiguration: mockApiConfig,
+				customInstructions: "custom instructions",
+				enableDiff: true,
+				task: "test task",
+			})
 
 			expect(cline.diffEnabled).toBe(true)
 			expect(cline.diffStrategy).toBeDefined()
@@ -411,15 +405,7 @@ describe("Cline", () => {
 
 		it("should require either task or historyItem", () => {
 			expect(() => {
-				new Cline(
-					mockProvider,
-					mockApiConfig,
-					undefined, // customInstructions
-					false, // diffEnabled
-					false, // checkpointsEnabled
-					undefined, // fuzzyMatchThreshold
-					undefined, // task
-				)
+				new Cline({ provider: mockProvider, apiConfiguration: mockApiConfig })
 			}).toThrow("Either historyItem or task/images must be provided")
 		})
 	})
@@ -469,15 +455,11 @@ describe("Cline", () => {
 		})
 
 		it("should include timezone information in environment details", async () => {
-			const [cline, task] = Cline.create(
-				mockProvider,
-				mockApiConfig,
-				undefined,
-				false,
-				false,
-				undefined,
-				"test task",
-			)
+			const [cline, task] = Cline.create({
+				provider: mockProvider,
+				apiConfiguration: mockApiConfig,
+				task: "test task",
+			})
 
 			const details = await cline["getEnvironmentDetails"](false)
 
@@ -493,15 +475,12 @@ describe("Cline", () => {
 
 		describe("API conversation handling", () => {
 			it("should clean conversation history before sending to API", async () => {
-				const [cline, task] = Cline.create(
-					mockProvider,
-					mockApiConfig,
-					undefined,
-					false,
-					false,
-					undefined,
-					"test task",
-				)
+				const [cline, task] = Cline.create({
+					provider: mockProvider,
+					apiConfiguration: mockApiConfig,
+					task: "test task",
+				})
+
 				cline.abandoned = true
 				await task
 
@@ -611,15 +590,11 @@ describe("Cline", () => {
 				]
 
 				// Test with model that supports images
-				const [clineWithImages, taskWithImages] = Cline.create(
-					mockProvider,
-					configWithImages,
-					undefined,
-					false,
-					false,
-					undefined,
-					"test task",
-				)
+				const [clineWithImages, taskWithImages] = Cline.create({
+					provider: mockProvider,
+					apiConfiguration: configWithImages,
+					task: "test task",
+				})
 
 				// Mock the model info to indicate image support
 				jest.spyOn(clineWithImages.api, "getModel").mockReturnValue({
@@ -638,15 +613,11 @@ describe("Cline", () => {
 				clineWithImages.apiConversationHistory = conversationHistory
 
 				// Test with model that doesn't support images
-				const [clineWithoutImages, taskWithoutImages] = Cline.create(
-					mockProvider,
-					configWithoutImages,
-					undefined,
-					false,
-					false,
-					undefined,
-					"test task",
-				)
+				const [clineWithoutImages, taskWithoutImages] = Cline.create({
+					provider: mockProvider,
+					apiConfiguration: configWithoutImages,
+					task: "test task",
+				})
 
 				// Mock the model info to indicate no image support
 				jest.spyOn(clineWithoutImages.api, "getModel").mockReturnValue({
@@ -742,15 +713,11 @@ describe("Cline", () => {
 			})
 
 			it.skip("should handle API retry with countdown", async () => {
-				const [cline, task] = Cline.create(
-					mockProvider,
-					mockApiConfig,
-					undefined,
-					false,
-					false,
-					undefined,
-					"test task",
-				)
+				const [cline, task] = Cline.create({
+					provider: mockProvider,
+					apiConfiguration: mockApiConfig,
+					task: "test task",
+				})
 
 				// Mock delay to track countdown timing
 				const mockDelay = jest.fn().mockResolvedValue(undefined)
@@ -870,15 +837,11 @@ describe("Cline", () => {
 			})
 
 			it.skip("should not apply retry delay twice", async () => {
-				const [cline, task] = Cline.create(
-					mockProvider,
-					mockApiConfig,
-					undefined,
-					false,
-					false,
-					undefined,
-					"test task",
-				)
+				const [cline, task] = Cline.create({
+					provider: mockProvider,
+					apiConfiguration: mockApiConfig,
+					task: "test task",
+				})
 
 				// Mock delay to track countdown timing
 				const mockDelay = jest.fn().mockResolvedValue(undefined)
@@ -998,15 +961,11 @@ describe("Cline", () => {
 
 			describe("loadContext", () => {
 				it("should process mentions in task and feedback tags", async () => {
-					const [cline, task] = Cline.create(
-						mockProvider,
-						mockApiConfig,
-						undefined,
-						false,
-						false,
-						undefined,
-						"test task",
-					)
+					const [cline, task] = Cline.create({
+						provider: mockProvider,
+						apiConfiguration: mockApiConfig,
+						task: "test task",
+					})
 
 					// Mock parseMentions to track calls
 					const mockParseMentions = jest.fn().mockImplementation((text) => `processed: ${text}`)

+ 12 - 15
src/core/webview/ClineProvider.ts

@@ -413,18 +413,17 @@ export class ClineProvider implements vscode.WebviewViewProvider {
 		const modePrompt = customModePrompts?.[mode] as PromptComponent
 		const effectiveInstructions = [globalInstructions, modePrompt?.customInstructions].filter(Boolean).join("\n\n")
 
-		this.cline = new Cline(
-			this,
+		this.cline = new Cline({
+			provider: this,
 			apiConfiguration,
-			effectiveInstructions,
-			diffEnabled,
-			checkpointsEnabled,
+			customInstructions: effectiveInstructions,
+			enableDiff: diffEnabled,
+			enableCheckpoints: checkpointsEnabled,
 			fuzzyMatchThreshold,
 			task,
 			images,
-			undefined,
 			experiments,
-		)
+		})
 	}
 
 	public async initClineWithHistoryItem(historyItem: HistoryItem) {
@@ -444,18 +443,16 @@ export class ClineProvider implements vscode.WebviewViewProvider {
 		const modePrompt = customModePrompts?.[mode] as PromptComponent
 		const effectiveInstructions = [globalInstructions, modePrompt?.customInstructions].filter(Boolean).join("\n\n")
 
-		this.cline = new Cline(
-			this,
+		this.cline = new Cline({
+			provider: this,
 			apiConfiguration,
-			effectiveInstructions,
-			diffEnabled,
-			checkpointsEnabled,
+			customInstructions: effectiveInstructions,
+			enableDiff: diffEnabled,
+			enableCheckpoints: checkpointsEnabled,
 			fuzzyMatchThreshold,
-			undefined,
-			undefined,
 			historyItem,
 			experiments,
-		)
+		})
 	}
 
 	public async postMessageToWebview(message: ExtensionMessage) {

+ 10 - 11
src/core/webview/__tests__/ClineProvider.test.ts

@@ -690,19 +690,18 @@ describe("ClineProvider", () => {
 		await provider.initClineWithTask("Test task")
 
 		// Verify Cline was initialized with mode-specific instructions
-		expect(Cline).toHaveBeenCalledWith(
+		expect(Cline).toHaveBeenCalledWith({
 			provider,
-			mockApiConfig,
-			modeCustomInstructions,
-			true,
-			false,
-			1.0,
-			"Test task",
-			undefined,
-			undefined,
-			experimentDefault,
-		)
+			apiConfiguration: mockApiConfig,
+			customInstructions: modeCustomInstructions,
+			enableDiff: true,
+			enableCheckpoints: false,
+			fuzzyMatchThreshold: 1.0,
+			task: "Test task",
+			experiments: experimentDefault,
+		})
 	})
+
 	test("handles mode-specific custom instructions updates", async () => {
 		await provider.resolveWebviewView(mockWebviewView)
 		const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]