Skip to content

Commit b82a81c

Browse files
committed
Add a ClineOptions type
1 parent 9dbfeef commit b82a81c

File tree

4 files changed

+124
-156
lines changed

4 files changed

+124
-156
lines changed

src/core/Cline.ts

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,20 @@ type UserContent = Array<
7373
Anthropic.TextBlockParam | Anthropic.ImageBlockParam | Anthropic.ToolUseBlockParam | Anthropic.ToolResultBlockParam
7474
>
7575

76+
export type ClineOptions = {
77+
provider: ClineProvider
78+
apiConfiguration: ApiConfiguration
79+
customInstructions?: string
80+
enableDiff?: boolean
81+
enableCheckpoints?: boolean
82+
fuzzyMatchThreshold?: number
83+
task?: string
84+
images?: string[]
85+
historyItem?: HistoryItem
86+
experiments?: Record<string, boolean>
87+
startTask?: boolean
88+
}
89+
7690
export class Cline {
7791
readonly taskId: string
7892
api: ApiHandler
@@ -118,19 +132,19 @@ export class Cline {
118132
private didAlreadyUseTool = false
119133
private didCompleteReadingStream = false
120134

121-
constructor(
122-
provider: ClineProvider,
123-
apiConfiguration: ApiConfiguration,
124-
customInstructions?: string,
125-
enableDiff?: boolean,
126-
enableCheckpoints?: boolean,
127-
fuzzyMatchThreshold?: number,
128-
task?: string | undefined,
129-
images?: string[] | undefined,
130-
historyItem?: HistoryItem | undefined,
131-
experiments?: Record<string, boolean>,
135+
constructor({
136+
provider,
137+
apiConfiguration,
138+
customInstructions,
139+
enableDiff,
140+
enableCheckpoints,
141+
fuzzyMatchThreshold,
142+
task,
143+
images,
144+
historyItem,
145+
experiments,
132146
startTask = true,
133-
) {
147+
}: ClineOptions) {
134148
if (startTask && !task && !images && !historyItem) {
135149
throw new Error("Either historyItem or task/images must be provided")
136150
}
@@ -165,21 +179,20 @@ export class Cline {
165179
}
166180
}
167181

168-
static create(...args: ConstructorParameters<typeof Cline>): [Cline, Promise<void>] {
169-
args[10] = false // startTask
170-
const instance = new Cline(...args)
171-
172-
let task
182+
static create(options: ClineOptions): [Cline, Promise<void>] {
183+
const instance = new Cline({ ...options, startTask: false })
184+
const { images, task, historyItem } = options
185+
let promise
173186

174-
if (args[6] || args[7]) {
175-
task = instance.startTask(args[6], args[7])
176-
} else if (args[8]) {
177-
task = instance.resumeTaskFromHistory()
187+
if (images || task) {
188+
promise = instance.startTask(task, images)
189+
} else if (historyItem) {
190+
promise = instance.resumeTaskFromHistory()
178191
} else {
179192
throw new Error("Either historyItem or task/images must be provided")
180193
}
181194

182-
return [instance, task]
195+
return [instance, promise]
183196
}
184197

185198
// Add method to update diffStrategy

src/core/__tests__/Cline.test.ts

Lines changed: 67 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -327,15 +327,13 @@ describe("Cline", () => {
327327

328328
describe("constructor", () => {
329329
it("should respect provided settings", async () => {
330-
const [cline, task] = Cline.create(
331-
mockProvider,
332-
mockApiConfig,
333-
"custom instructions",
334-
false,
335-
false,
336-
0.95, // 95% threshold
337-
"test task",
338-
)
330+
const [cline, task] = Cline.create({
331+
provider: mockProvider,
332+
apiConfiguration: mockApiConfig,
333+
customInstructions: "custom instructions",
334+
fuzzyMatchThreshold: 0.95,
335+
task: "test task",
336+
})
339337

340338
expect(cline.customInstructions).toBe("custom instructions")
341339
expect(cline.diffEnabled).toBe(false)
@@ -345,15 +343,14 @@ describe("Cline", () => {
345343
})
346344

347345
it("should use default fuzzy match threshold when not provided", async () => {
348-
const [cline, task] = await Cline.create(
349-
mockProvider,
350-
mockApiConfig,
351-
"custom instructions",
352-
true,
353-
false,
354-
undefined,
355-
"test task",
356-
)
346+
const [cline, task] = await Cline.create({
347+
provider: mockProvider,
348+
apiConfiguration: mockApiConfig,
349+
customInstructions: "custom instructions",
350+
enableDiff: true,
351+
fuzzyMatchThreshold: 0.95,
352+
task: "test task",
353+
})
357354

358355
expect(cline.diffEnabled).toBe(true)
359356
// The diff strategy should be created with default threshold (1.0)
@@ -366,15 +363,14 @@ describe("Cline", () => {
366363
it("should use provided fuzzy match threshold", async () => {
367364
const getDiffStrategySpy = jest.spyOn(require("../diff/DiffStrategy"), "getDiffStrategy")
368365

369-
const [cline, task] = Cline.create(
370-
mockProvider,
371-
mockApiConfig,
372-
"custom instructions",
373-
true,
374-
false,
375-
0.9, // 90% threshold
376-
"test task",
377-
)
366+
const [cline, task] = await Cline.create({
367+
provider: mockProvider,
368+
apiConfiguration: mockApiConfig,
369+
customInstructions: "custom instructions",
370+
enableDiff: true,
371+
fuzzyMatchThreshold: 0.9,
372+
task: "test task",
373+
})
378374

379375
expect(cline.diffEnabled).toBe(true)
380376
expect(cline.diffStrategy).toBeDefined()
@@ -389,15 +385,13 @@ describe("Cline", () => {
389385
it("should pass default threshold to diff strategy when not provided", async () => {
390386
const getDiffStrategySpy = jest.spyOn(require("../diff/DiffStrategy"), "getDiffStrategy")
391387

392-
const [cline, task] = Cline.create(
393-
mockProvider,
394-
mockApiConfig,
395-
"custom instructions",
396-
true,
397-
false,
398-
undefined,
399-
"test task",
400-
)
388+
const [cline, task] = Cline.create({
389+
provider: mockProvider,
390+
apiConfiguration: mockApiConfig,
391+
customInstructions: "custom instructions",
392+
enableDiff: true,
393+
task: "test task",
394+
})
401395

402396
expect(cline.diffEnabled).toBe(true)
403397
expect(cline.diffStrategy).toBeDefined()
@@ -411,15 +405,7 @@ describe("Cline", () => {
411405

412406
it("should require either task or historyItem", () => {
413407
expect(() => {
414-
new Cline(
415-
mockProvider,
416-
mockApiConfig,
417-
undefined, // customInstructions
418-
false, // diffEnabled
419-
false, // checkpointsEnabled
420-
undefined, // fuzzyMatchThreshold
421-
undefined, // task
422-
)
408+
new Cline({ provider: mockProvider, apiConfiguration: mockApiConfig })
423409
}).toThrow("Either historyItem or task/images must be provided")
424410
})
425411
})
@@ -469,15 +455,11 @@ describe("Cline", () => {
469455
})
470456

471457
it("should include timezone information in environment details", async () => {
472-
const [cline, task] = Cline.create(
473-
mockProvider,
474-
mockApiConfig,
475-
undefined,
476-
false,
477-
false,
478-
undefined,
479-
"test task",
480-
)
458+
const [cline, task] = Cline.create({
459+
provider: mockProvider,
460+
apiConfiguration: mockApiConfig,
461+
task: "test task",
462+
})
481463

482464
const details = await cline["getEnvironmentDetails"](false)
483465

@@ -493,15 +475,12 @@ describe("Cline", () => {
493475

494476
describe("API conversation handling", () => {
495477
it("should clean conversation history before sending to API", async () => {
496-
const [cline, task] = Cline.create(
497-
mockProvider,
498-
mockApiConfig,
499-
undefined,
500-
false,
501-
false,
502-
undefined,
503-
"test task",
504-
)
478+
const [cline, task] = Cline.create({
479+
provider: mockProvider,
480+
apiConfiguration: mockApiConfig,
481+
task: "test task",
482+
})
483+
505484
cline.abandoned = true
506485
await task
507486

@@ -611,15 +590,11 @@ describe("Cline", () => {
611590
]
612591

613592
// Test with model that supports images
614-
const [clineWithImages, taskWithImages] = Cline.create(
615-
mockProvider,
616-
configWithImages,
617-
undefined,
618-
false,
619-
false,
620-
undefined,
621-
"test task",
622-
)
593+
const [clineWithImages, taskWithImages] = Cline.create({
594+
provider: mockProvider,
595+
apiConfiguration: configWithImages,
596+
task: "test task",
597+
})
623598

624599
// Mock the model info to indicate image support
625600
jest.spyOn(clineWithImages.api, "getModel").mockReturnValue({
@@ -638,15 +613,11 @@ describe("Cline", () => {
638613
clineWithImages.apiConversationHistory = conversationHistory
639614

640615
// Test with model that doesn't support images
641-
const [clineWithoutImages, taskWithoutImages] = Cline.create(
642-
mockProvider,
643-
configWithoutImages,
644-
undefined,
645-
false,
646-
false,
647-
undefined,
648-
"test task",
649-
)
616+
const [clineWithoutImages, taskWithoutImages] = Cline.create({
617+
provider: mockProvider,
618+
apiConfiguration: configWithoutImages,
619+
task: "test task",
620+
})
650621

651622
// Mock the model info to indicate no image support
652623
jest.spyOn(clineWithoutImages.api, "getModel").mockReturnValue({
@@ -742,15 +713,11 @@ describe("Cline", () => {
742713
})
743714

744715
it.skip("should handle API retry with countdown", async () => {
745-
const [cline, task] = Cline.create(
746-
mockProvider,
747-
mockApiConfig,
748-
undefined,
749-
false,
750-
false,
751-
undefined,
752-
"test task",
753-
)
716+
const [cline, task] = Cline.create({
717+
provider: mockProvider,
718+
apiConfiguration: mockApiConfig,
719+
task: "test task",
720+
})
754721

755722
// Mock delay to track countdown timing
756723
const mockDelay = jest.fn().mockResolvedValue(undefined)
@@ -870,15 +837,11 @@ describe("Cline", () => {
870837
})
871838

872839
it.skip("should not apply retry delay twice", async () => {
873-
const [cline, task] = Cline.create(
874-
mockProvider,
875-
mockApiConfig,
876-
undefined,
877-
false,
878-
false,
879-
undefined,
880-
"test task",
881-
)
840+
const [cline, task] = Cline.create({
841+
provider: mockProvider,
842+
apiConfiguration: mockApiConfig,
843+
task: "test task",
844+
})
882845

883846
// Mock delay to track countdown timing
884847
const mockDelay = jest.fn().mockResolvedValue(undefined)
@@ -998,15 +961,11 @@ describe("Cline", () => {
998961

999962
describe("loadContext", () => {
1000963
it("should process mentions in task and feedback tags", async () => {
1001-
const [cline, task] = Cline.create(
1002-
mockProvider,
1003-
mockApiConfig,
1004-
undefined,
1005-
false,
1006-
false,
1007-
undefined,
1008-
"test task",
1009-
)
964+
const [cline, task] = Cline.create({
965+
provider: mockProvider,
966+
apiConfiguration: mockApiConfig,
967+
task: "test task",
968+
})
1010969

1011970
// Mock parseMentions to track calls
1012971
const mockParseMentions = jest.fn().mockImplementation((text) => `processed: ${text}`)

src/core/webview/ClineProvider.ts

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -413,18 +413,17 @@ export class ClineProvider implements vscode.WebviewViewProvider {
413413
const modePrompt = customModePrompts?.[mode] as PromptComponent
414414
const effectiveInstructions = [globalInstructions, modePrompt?.customInstructions].filter(Boolean).join("\n\n")
415415

416-
this.cline = new Cline(
417-
this,
416+
this.cline = new Cline({
417+
provider: this,
418418
apiConfiguration,
419-
effectiveInstructions,
420-
diffEnabled,
421-
checkpointsEnabled,
419+
customInstructions: effectiveInstructions,
420+
enableDiff: diffEnabled,
421+
enableCheckpoints: checkpointsEnabled,
422422
fuzzyMatchThreshold,
423423
task,
424424
images,
425-
undefined,
426425
experiments,
427-
)
426+
})
428427
}
429428

430429
public async initClineWithHistoryItem(historyItem: HistoryItem) {
@@ -444,18 +443,16 @@ export class ClineProvider implements vscode.WebviewViewProvider {
444443
const modePrompt = customModePrompts?.[mode] as PromptComponent
445444
const effectiveInstructions = [globalInstructions, modePrompt?.customInstructions].filter(Boolean).join("\n\n")
446445

447-
this.cline = new Cline(
448-
this,
446+
this.cline = new Cline({
447+
provider: this,
449448
apiConfiguration,
450-
effectiveInstructions,
451-
diffEnabled,
452-
checkpointsEnabled,
449+
customInstructions: effectiveInstructions,
450+
enableDiff: diffEnabled,
451+
enableCheckpoints: checkpointsEnabled,
453452
fuzzyMatchThreshold,
454-
undefined,
455-
undefined,
456453
historyItem,
457454
experiments,
458-
)
455+
})
459456
}
460457

461458
public async postMessageToWebview(message: ExtensionMessage) {

0 commit comments

Comments
 (0)