Skip to content

Commit c3c3874

Browse files
author
ShayBC
committed
subtasks alpha version (still in development)
1 parent 79e5f56 commit c3c3874

File tree

5 files changed

+218
-78
lines changed

5 files changed

+218
-78
lines changed

src/activate/registerCommands.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export const registerCommands = (options: RegisterCommandOptions) => {
2020
const getCommandsMap = ({ context, outputChannel, provider }: RegisterCommandOptions) => {
2121
return {
2222
"roo-cline.plusButtonClicked": async () => {
23-
await provider.clearTask()
23+
await provider.removeClineFromStack()
2424
await provider.postStateToWebview()
2525
await provider.postMessageToWebview({ type: "action", action: "chatButtonClicked" })
2626
},

src/core/Cline.ts

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ type UserContent = Array<
7575

7676
export class Cline {
7777
readonly taskId: string
78+
// a flag that indicated if this Cline instance is a subtask (on finish return control to parent task)
79+
private isSubTask: boolean = false
80+
// a flag that indicated if this Cline instance is paused (waiting for provider to resume it after subtask completion)
81+
private isPaused: boolean = false
7882
api: ApiHandler
7983
private terminalManager: TerminalManager
8084
private urlContentFetcher: UrlContentFetcher
@@ -160,6 +164,12 @@ export class Cline {
160164
}
161165
}
162166

167+
// a helper function to set the private member isSubTask to true
168+
// and by that set this Cline instance to be a subtask (on finish return control to parent task)
169+
setSubTask() {
170+
this.isSubTask = true
171+
}
172+
163173
// Add method to update diffStrategy
164174
async updateDiffStrategy(experimentalDiffStrategy?: boolean) {
165175
// If not provided, get from current state
@@ -480,6 +490,43 @@ export class Cline {
480490
])
481491
}
482492

493+
async resumePausedTask() {
494+
// release this Cline instance from paused state
495+
this.isPaused = false
496+
497+
// Clear any existing ask state and simulate a completed ask response
498+
// this.askResponse = "messageResponse";
499+
// this.askResponseText = "Sub Task finished Successfully!\nthere is no need to perform this task again, please continue to the next task.";
500+
// this.askResponseImages = undefined;
501+
// this.lastMessageTs = Date.now();
502+
503+
// This adds the completion message to conversation history
504+
await this.say(
505+
"text",
506+
"Sub Task finished Successfully!\nthere is no need to perform this task again, please continue to the next task.",
507+
)
508+
509+
// this.userMessageContent.push({
510+
// type: "text",
511+
// text: `${"Result:\\n\\nSub Task finished Successfully!\nthere is no need to perform this task again, please continue to the next task."}`,
512+
// })
513+
514+
try {
515+
// Resume parent task
516+
await this.ask("resume_task")
517+
} catch (error) {
518+
if (error.message === "Current ask promise was ignored") {
519+
// ignore the ignored promise, since it was performed by launching a subtask and it probably took more then 1 sec,
520+
// also set the didAlreadyUseTool flag to indicate that the tool was already used, and there is no need to relaunch it
521+
this.didAlreadyUseTool = true
522+
} else {
523+
// Handle error appropriately
524+
console.error("Failed to resume task:", error)
525+
throw error
526+
}
527+
}
528+
}
529+
483530
private async resumeTaskFromHistory() {
484531
const modifiedClineMessages = await this.getSavedClineMessages()
485532

@@ -2553,10 +2600,12 @@ export class Cline {
25532600
const provider = this.providerRef.deref()
25542601
if (provider) {
25552602
await provider.handleModeSwitch(mode)
2556-
await provider.initClineWithTask(message)
2603+
await provider.initClineWithSubTask(message)
25572604
pushToolResult(
25582605
`Successfully created new task in ${targetMode.name} mode with message: ${message}`,
25592606
)
2607+
// pasue the current task and start the new task
2608+
this.isPaused = true
25602609
} else {
25612610
pushToolResult(
25622611
formatResponse.toolError("Failed to create new task: provider not available"),
@@ -2648,6 +2697,10 @@ export class Cline {
26482697
if (lastMessage && lastMessage.ask !== "command") {
26492698
// havent sent a command message yet so first send completion_result then command
26502699
await this.say("completion_result", result, undefined, false)
2700+
if (this.isSubTask) {
2701+
// tell the provider to remove the current subtask and resume the previous task in the stack
2702+
this.providerRef.deref()?.finishSubTask()
2703+
}
26512704
}
26522705

26532706
// complete command message
@@ -2665,6 +2718,10 @@ export class Cline {
26652718
commandResult = execCommandResult
26662719
} else {
26672720
await this.say("completion_result", result, undefined, false)
2721+
if (this.isSubTask) {
2722+
// tell the provider to remove the current subtask and resume the previous task in the stack
2723+
this.providerRef.deref()?.finishSubTask()
2724+
}
26682725
}
26692726

26702727
// we already sent completion_result says, an empty string asks relinquishes control over button and field
@@ -2740,6 +2797,20 @@ export class Cline {
27402797
}
27412798
}
27422799

2800+
// this function checks if this Cline instance is set to pause state and wait for being resumed,
2801+
// this is used when a sub-task is launched and the parent task is waiting for it to finish
2802+
async waitForResume() {
2803+
// wait until isPaused is false
2804+
await new Promise<void>((resolve) => {
2805+
const interval = setInterval(() => {
2806+
if (!this.isPaused) {
2807+
clearInterval(interval)
2808+
resolve()
2809+
}
2810+
}, 1000) // TBD: the 1 sec should be added to the settings, also should add a timeout to prevent infinit wait
2811+
})
2812+
}
2813+
27432814
async recursivelyMakeClineRequests(
27442815
userContent: UserContent,
27452816
includeFileDetails: boolean = false,
@@ -2779,6 +2850,12 @@ export class Cline {
27792850
await this.checkpointSave({ isFirst: true })
27802851
}
27812852

2853+
// in this Cline request loop, we need to check if this cline (Task) instance has been asked to wait
2854+
// for a sub-task (it has launched) to finish before continuing
2855+
if (this.isPaused) {
2856+
await this.waitForResume()
2857+
}
2858+
27822859
// getting verbose details is an expensive operation, it uses globby to top-down build file structure of project which for large projects can take a few seconds
27832860
// for the best UX we show a placeholder api_req_started message with a loading spinner as this happens
27842861
await this.say(

0 commit comments

Comments
 (0)