added task no indicator + improved deleteTask code

This commit is contained in:
ShayBC 2025-02-24 23:00:11 +02:00
parent 655930cf5f
commit 01765995ad
7 changed files with 62 additions and 38 deletions

View file

@ -75,6 +75,7 @@ type UserContent = Array<
export class Cline {
readonly taskId: string
private taskNumber: number
// a flag that indicated if this Cline instance is a subtask (on finish return control to parent task)
private isSubTask: boolean = false
// a flag that indicated if this Cline instance is paused (waiting for provider to resume it after subtask completion)
@ -139,6 +140,7 @@ export class Cline {
}
this.taskId = crypto.randomUUID()
this.taskNumber = -1
this.api = buildApiHandler(apiConfiguration)
this.terminalManager = new TerminalManager()
this.urlContentFetcher = new UrlContentFetcher(provider.context)
@ -170,6 +172,16 @@ export class Cline {
this.isSubTask = true
}
// sets the task number (sequencial number of this task from all the subtask ran from this main task stack)
setTaskNumber(taskNumber: number) {
this.taskNumber = taskNumber
}
// gets the task number, the sequencial number of this task from all the subtask ran from this main task stack
getTaskNumber() {
return this.taskNumber
}
// Add method to update diffStrategy
async updateDiffStrategy(experimentalDiffStrategy?: boolean) {
// If not provided, get from current state
@ -276,6 +288,7 @@ export class Cline {
await this.providerRef.deref()?.updateTaskHistory({
id: this.taskId,
number: this.taskNumber,
ts: lastRelevantMessage.ts,
task: taskMessage.text ?? "",
tokensIn: apiMetrics.totalTokensIn,

View file

@ -222,6 +222,7 @@ describe("Cline", () => {
return [
{
id: "123",
number: 0,
ts: Date.now(),
task: "historical task",
tokensIn: 100,

View file

@ -153,6 +153,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
private latestAnnouncementId = "jan-21-2025-custom-modes" // update to some unique identifier when we add a new announcement
configManager: ConfigManager
customModesManager: CustomModesManager
private lastTaskNumber = -1
constructor(
readonly context: vscode.ExtensionContext,
@ -180,6 +181,17 @@ export class ClineProvider implements vscode.WebviewViewProvider {
// The instance is pushed to the top of the stack (LIFO order).
// When the task is completed, the top instance is removed, reactivating the previous task.
addClineToStack(cline: Cline): void {
// if cline.getTaskNumber() is -1, it means it is a new task
if (cline.getTaskNumber() === -1) {
// increase last cline number by 1
this.lastTaskNumber = this.lastTaskNumber + 1
cline.setTaskNumber(this.lastTaskNumber)
}
// if cline.getTaskNumber() > lastTaskNumber, set lastTaskNumber to cline.getTaskNumber()
else if (cline.getTaskNumber() > this.lastTaskNumber) {
this.lastTaskNumber = cline.getTaskNumber()
}
// push the cline instance to the stack
this.clineStack.push(cline)
}
@ -192,6 +204,10 @@ export class ClineProvider implements vscode.WebviewViewProvider {
// make sure no reference kept, once promises end it will be garbage collected
clineToBeRemoved = undefined
}
// if the stack is empty, reset the last task number
if (this.clineStack.length === 0) {
this.lastTaskNumber = -1
}
}
// remove the cline object with the received clineId, and all the cline objects bove it in the stack
@ -520,6 +536,8 @@ export class ClineProvider implements vscode.WebviewViewProvider {
historyItem,
experiments,
)
// get this cline task number id from the history item and set it to newCline
newCline.setTaskNumber(historyItem.number)
this.addClineToStack(newCline)
}
@ -2384,38 +2402,25 @@ export class ClineProvider implements vscode.WebviewViewProvider {
await downloadTask(historyItem.ts, apiConversationHistory)
}
// this function deletes a task from task hidtory, and deletes it's checkpoints and delete the task folder
async deleteTaskWithId(id: string) {
// get the task directory full path
const { taskDirPath } = await this.getTaskWithId(id)
// remove task from stack if it's the current task
if (id === this.getCurrentCline()?.taskId) {
await this.removeClineWithIdFromStack(id)
}
const { taskDirPath, apiConversationHistoryFilePath, uiMessagesFilePath } = await this.getTaskWithId(id)
// delete task from the task history state
await this.deleteTaskFromState(id)
// Delete the task files.
const apiConversationHistoryFileExists = await fileExistsAtPath(apiConversationHistoryFilePath)
if (apiConversationHistoryFileExists) {
await fs.unlink(apiConversationHistoryFilePath)
}
const uiMessagesFileExists = await fileExistsAtPath(uiMessagesFilePath)
if (uiMessagesFileExists) {
await fs.unlink(uiMessagesFilePath)
}
const legacyMessagesFilePath = path.join(taskDirPath, "claude_messages.json")
if (await fileExistsAtPath(legacyMessagesFilePath)) {
await fs.unlink(legacyMessagesFilePath)
}
// check if checkpoints are enabled
const { checkpointsEnabled } = await this.getState()
// get the base directory of the project
const baseDir = vscode.workspace.workspaceFolders?.map((folder) => folder.uri.fsPath).at(0)
// Delete checkpoints branch.
// delete checkpoints branch from project git repo
if (checkpointsEnabled && baseDir) {
const branchSummary = await simpleGit(baseDir)
.branch(["-D", `roo-code-checkpoints-${id}`])
@ -2426,22 +2431,15 @@ export class ClineProvider implements vscode.WebviewViewProvider {
}
}
// Delete checkpoints directory
const checkpointsDir = path.join(taskDirPath, "checkpoints")
if (await fileExistsAtPath(checkpointsDir)) {
try {
await fs.rm(checkpointsDir, { recursive: true, force: true })
console.log(`[deleteTaskWithId${id}] removed checkpoints repo`)
} catch (error) {
console.error(
`[deleteTaskWithId${id}] failed to remove checkpoints repo: ${error instanceof Error ? error.message : String(error)}`,
)
}
// delete the entire task directory including checkpoints and all content
try {
await fs.rm(taskDirPath, { recursive: true, force: true })
console.log(`[deleteTaskWithId${id}] removed task directory`)
} catch (error) {
console.error(
`[deleteTaskWithId${id}] failed to remove task directory: ${error instanceof Error ? error.message : String(error)}`,
)
}
// Succeeds if the dir is empty.
await fs.rmdir(taskDirPath)
}
async deleteTaskFromState(id: string) {

View file

@ -1,5 +1,6 @@
export type HistoryItem = {
id: string
number: number
ts: number
task: string
tokensIn: number

View file

@ -158,7 +158,10 @@ const TaskHeader: React.FC<TaskHeaderProps> = ({
flexGrow: 1,
minWidth: 0, // This allows the div to shrink below its content size
}}>
<span style={{ fontWeight: "bold" }}>Task{!isTaskExpanded && ":"}</span>
<span style={{ fontWeight: "bold" }}>
Task ({currentTaskItem?.number === 0 ? "Main" : currentTaskItem.number})
{!isTaskExpanded && ":"}
</span>
{!isTaskExpanded && (
<span style={{ marginLeft: 4 }}>{highlightMentions(task.text, false)}</span>
)}

View file

@ -120,6 +120,12 @@ const HistoryPreview = ({ showHistoryView }: HistoryPreviewProps) => {
}}>
{formatDate(item.ts)}
</span>
<span
style={{
marginLeft: "auto",
}}>
({item.number === 0 ? "Main" : item.number})
</span>
<button
title="Copy Prompt"
aria-label="Copy Prompt"

View file

@ -23,6 +23,7 @@ jest.mock("react-virtuoso", () => ({
const mockTaskHistory = [
{
id: "1",
number: 0,
task: "Test task 1",
ts: new Date("2022-02-16T00:00:00").getTime(),
tokensIn: 100,
@ -31,6 +32,7 @@ const mockTaskHistory = [
},
{
id: "2",
number: 0,
task: "Test task 2",
ts: new Date("2022-02-17T00:00:00").getTime(),
tokensIn: 200,