Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 132 additions & 12 deletions src/core/prompts/__tests__/__snapshots__/system.test.ts.snap

Large diffs are not rendered by default.

11 changes: 10 additions & 1 deletion src/core/prompts/tools/new-task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,31 @@ import { ToolArgs } from "./types"

export function getNewTaskDescription(_args: ToolArgs): string {
return `## new_task
Description: This will let you create a new task instance in the chosen mode using your provided message.
Description: This will let you create a new task instance in the chosen mode using your provided message and attached files.

Parameters:
- mode: (required) The slug of the mode to start the new task in (e.g., "code", "debug", "architect").
- message: (required) The initial user message or instructions for this new task.
- files: (optional) A list of relevant files to include in the new task. Use a parent <files> tag containing one or more <file> tags, each with a relative workspace path, optionally followed by \`:startLine:endLine\` to specify a range (e.g., \`<file>path/to/file.ts:10:50</file>\`) if needed).

Usage:
<new_task>
<mode>your-mode-slug-here</mode>
<message>Your initial instructions here</message>
<files>
<file>path/without/range.js</file>
<file>path/with/range.py:25:100</file>
</files>
</new_task>

Example:
<new_task>
<mode>code</mode>
<message>Implement a new feature for the application.</message>
<files>
<file>src/somefile.ts</file>
<file>src/anotherfile.ts:10:50</file>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please see #2886 and work with @samhvw8 so that are line range formats do not diverge; multiple range formats would probably confuse the model across tools.

Gemini and Claude have both been shown empirically tested that the following format used in #2886 works very well for the model. this is a real life example:

<read_file>
<args>
  <file>
    <path>src/core/config/__tests__/ContextProxy.test.ts</path>
    <line_range>130-140</line_range> <!-- Around line 134 -->
    <line_range>160-170</line_range> <!-- Around line 166 -->
  </file>
  <file>
    <path>src/exports/api.ts</path>
    <line_range>60-75</line_range> <!-- Around line 67 -->
  </file>
  <file>
    <path>src/exports/types.ts</path> <!-- To see RooCodeSettings definition -->
  </file>
  <file>
    <path>src/schemas/index.ts</path>
    <line_range>150-160</line_range> <!-- To see historyItemSchema definition -->
  </file>
</args>
</read_file>

standardizing XML representation will simplify it tool class refactors in the future

</files>
</new_task>
`
}
68 changes: 60 additions & 8 deletions src/core/task/Task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import { ClineApiReqCancelReason, ClineApiReqInfo } from "../../shared/Extension
import { getApiMetrics } from "../../shared/getApiMetrics"
import { ClineAskResponse } from "../../shared/WebviewMessage"
import { defaultModeSlug } from "../../shared/modes"
import { DiffStrategy } from "../../shared/tools"
import { DiffStrategy, AttachedFileSpec } from "../../shared/tools"

// services
import { UrlContentFetcher } from "../../services/browser/UrlContentFetcher"
Expand Down Expand Up @@ -80,6 +80,7 @@ import { processUserContentMentions } from "../mentions/processUserContentMentio
import { ApiMessage } from "../task-persistence/apiMessages"
import { getMessagesSinceLastSummary, summarizeConversation } from "../condense"
import { maybeRemoveImageBlocks } from "../../api/transform/image-cleaning"
import { processFileForReading, formatProcessedFileResultToString } from "../../shared/fileReadUtils"

export type ClineEvents = {
message: [{ action: "created" | "updated"; message: ClineMessage }]
Expand All @@ -104,6 +105,7 @@ export type TaskOptions = {
consecutiveMistakeLimit?: number
task?: string
images?: string[]
attachedFiles?: AttachedFileSpec[]
historyItem?: HistoryItem
experiments?: Record<string, boolean>
startTask?: boolean
Expand All @@ -121,6 +123,7 @@ export class Task extends EventEmitter<ClineEvents> {
readonly parentTask: Task | undefined = undefined
readonly taskNumber: number
readonly workspacePath: string
public readonly attachedFiles: AttachedFileSpec[] = []

providerRef: WeakRef<ClineProvider>
private readonly globalStoragePath: string
Expand Down Expand Up @@ -197,6 +200,7 @@ export class Task extends EventEmitter<ClineEvents> {
consecutiveMistakeLimit = 3,
task,
images,
attachedFiles,
historyItem,
startTask = true,
rootTask,
Expand Down Expand Up @@ -241,6 +245,7 @@ export class Task extends EventEmitter<ClineEvents> {
this.rootTask = rootTask
this.parentTask = parentTask
this.taskNumber = taskNumber
this.attachedFiles = attachedFiles || []

if (historyItem) {
telemetryService.captureTaskRestarted(this.taskId)
Expand Down Expand Up @@ -656,6 +661,42 @@ export class Task extends EventEmitter<ClineEvents> {

// Start / Abort / Resume

/**
* Formats the content of attached files for inclusion in the task prompt
* @param workspaceRoot The root directory of the workspace
* @param maxReadFileLine Maximum number of lines to read from each file
* @returns Formatted string containing all attached files content
*/
private async _formatAttachedFilesContent(workspaceRoot: string, maxReadFileLine: number): Promise<string> {
const attachedFilesStrings: string[] = []

for (const fileSpec of this.attachedFiles) {
// Handle both string and AttachedFileSpec types
const relativePath = typeof fileSpec === "string" ? fileSpec : fileSpec.path
if (!relativePath) continue // Skip empty paths

const requestedStartLine = typeof fileSpec === "string" ? undefined : fileSpec.startLine
const requestedEndLine = typeof fileSpec === "string" ? undefined : fileSpec.endLine
const absolutePath = path.join(workspaceRoot, relativePath)

// Process the file using shared utilities
const result = await processFileForReading(
absolutePath,
relativePath,
maxReadFileLine,
requestedStartLine,
requestedEndLine,
this.rooIgnoreController,
)

// Format the result to string
const fileString = formatProcessedFileResultToString(result)
attachedFilesStrings.push(fileString)
}

return attachedFilesStrings.join("\n")
}

private async startTask(task?: string, images?: string[]): Promise<void> {
// `conversationHistory` (for API) and `clineMessages` (for webview)
// need to be in sync.
Expand All @@ -667,20 +708,31 @@ export class Task extends EventEmitter<ClineEvents> {
this.apiConversationHistory = []
await this.providerRef.deref()?.postStateToWebview()

const providerState = await this.providerRef.deref()?.getState()
const workspaceRoot = this.cwd
const maxReadFileLine = providerState?.maxReadFileLine ?? 500 // Default to 500

await this.say("text", task, images)
this.isInitialized = true

let imageBlocks: Anthropic.ImageBlockParam[] = formatResponse.imageBlocks(images)

console.log(`[subtasks] task ${this.taskId}.${this.instanceId} starting`)

await this.initiateTaskLoop([
{
type: "text",
text: `<task>\n${task}\n</task>`,
},
...imageBlocks,
])
const taskBlock = `<task>\n${task ?? ""}\n</task>`

const attachedFilesContent = await this._formatAttachedFilesContent(workspaceRoot, maxReadFileLine)

const attachedFilesBlock =
this.attachedFiles.length > 0 ? `<attached-files>\n${attachedFilesContent}</attached-files>` : ""

const blocks: Anthropic.ContentBlockParam[] = [{ type: "text", text: taskBlock }, ...imageBlocks]

if (attachedFilesBlock) {
blocks.push({ type: "text", text: attachedFilesBlock })
}

await this.initiateTaskLoop(blocks)
}

public async resumePausedTask(lastMessage: string) {
Expand Down
Loading
Loading