Skip to content

Commit 3859d11

Browse files
Tasks Galore! (RooCodeInc#2256)
* test script to create tasks * changeset * fix type error * make is_dev checks consistent --------- Co-authored-by: Saoud Rizwan <[email protected]>
1 parent 19c4439 commit 3859d11

File tree

4 files changed

+315
-0
lines changed

4 files changed

+315
-0
lines changed

.changeset/nasty-pigs-camp.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"claude-dev": minor
3+
---
4+
5+
Added a script to create test tasks in dev mode

package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,12 @@
9696
"title": "Open In New Tab",
9797
"category": "Cline"
9898
},
99+
{
100+
"command": "cline.dev.createTestTasks",
101+
"title": "Create Test Tasks",
102+
"category": "Cline",
103+
"when": "cline.isDevMode"
104+
},
99105
{
100106
"command": "cline.openDocumentation",
101107
"title": "Documentation",

src/dev/commands/tasks.ts

Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
import * as vscode from "vscode"
2+
import * as fs from "fs/promises"
3+
import * as path from "path"
4+
import { ClineProvider } from "../../core/webview/ClineProvider"
5+
import { HistoryItem } from "../../shared/HistoryItem"
6+
import { ClineMessage } from "../../shared/ExtensionMessage"
7+
8+
/**
9+
* Registers development-only commands for task manipulation.
10+
* These are only activated in development mode.
11+
*/
12+
export function registerTaskCommands(context: vscode.ExtensionContext, provider: ClineProvider): vscode.Disposable[] {
13+
return [
14+
vscode.commands.registerCommand("cline.dev.createTestTasks", async () => {
15+
const count = await vscode.window.showInputBox({
16+
title: "Test Tasks",
17+
prompt: "How many test tasks to create?",
18+
value: "10",
19+
})
20+
21+
if (!count) {
22+
return
23+
}
24+
25+
const tasksCount = parseInt(count)
26+
const globalStoragePath = context.globalStorageUri.fsPath
27+
const tasksDir = path.join(globalStoragePath, "tasks")
28+
29+
vscode.window.withProgress(
30+
{
31+
location: vscode.ProgressLocation.Notification,
32+
title: `Creating ${tasksCount} test tasks...`,
33+
cancellable: false,
34+
},
35+
async (progress) => {
36+
for (let i = 0; i < tasksCount; i++) {
37+
// Generate a timestamp to ensure unique IDs
38+
const timestamp = Date.now() + i
39+
const taskId = `${timestamp}`
40+
const taskDir = path.join(tasksDir, taskId)
41+
42+
await fs.mkdir(taskDir, { recursive: true })
43+
44+
// Generate a task prompt
45+
const taskName = getRandomTaskName(i)
46+
47+
// Create realistic message sequence
48+
const messages = createRealisticMessageSequence(timestamp, taskName, i)
49+
50+
// Create API conversation history file
51+
await fs.writeFile(
52+
path.join(taskDir, "api_conversation_history.json"),
53+
JSON.stringify(
54+
[
55+
{
56+
role: "user",
57+
content: [{ type: "text", text: `<task>\n${taskName}\n</task>` }],
58+
},
59+
{
60+
role: "assistant",
61+
content: [
62+
{
63+
type: "text",
64+
text: `I'll help you ${taskName.toLowerCase()}. Let me break this down into steps.`,
65+
},
66+
],
67+
},
68+
],
69+
null,
70+
2,
71+
),
72+
)
73+
74+
// Create UI messages file with realistic message sequence
75+
await fs.writeFile(path.join(taskDir, "ui_messages.json"), JSON.stringify(messages, null, 2))
76+
77+
// Create history item to be shown in the HistoryView
78+
const historyItem: HistoryItem = {
79+
id: taskId,
80+
ts: timestamp,
81+
task: taskName,
82+
tokensIn: Math.floor(100 + Math.random() * 900), // Random token count from 100-1000
83+
tokensOut: Math.floor(200 + Math.random() * 1800), // Random token count from 200-2000
84+
cacheWrites: i % 3 === 0 ? Math.floor(50 + Math.random() * 150) : undefined, // Only add cache writes to every 3rd task
85+
cacheReads: i % 3 === 0 ? Math.floor(20 + Math.random() * 80) : undefined, // Only add cache reads to every 3rd task
86+
totalCost: Number((0.0001 + Math.random() * 0.01).toFixed(5)), // Random cost from $0.0001 to $0.0101
87+
size: 1024 * 1024, // 1MB
88+
}
89+
90+
// Update task history in global state
91+
await provider.updateTaskHistory(historyItem)
92+
93+
progress.report({ increment: 100 / tasksCount })
94+
}
95+
96+
// Update the UI to show the new tasks
97+
await provider.postStateToWebview()
98+
99+
vscode.window.showInformationMessage(`Created ${tasksCount} test tasks`)
100+
},
101+
)
102+
}),
103+
]
104+
}
105+
106+
/**
107+
* Creates a realistic sequence of messages that would occur in a typical task
108+
*/
109+
function createRealisticMessageSequence(baseTimestamp: number, taskPrompt: string, taskIndex: number): ClineMessage[] {
110+
// Use an incrementing timestamp to ensure messages appear in sequence
111+
let timestamp = baseTimestamp
112+
const getNextTimestamp = () => {
113+
timestamp += 1000 // Add 1 second between messages
114+
return timestamp
115+
}
116+
117+
// Variables to make different test tasks look unique
118+
const fileName = getRandomFileName(taskIndex)
119+
const commitHash = `commit${taskIndex}${Math.floor(Math.random() * 1000000).toString(16)}`
120+
121+
// Create a realistic message sequence
122+
const messages: ClineMessage[] = [
123+
// Initial task message - uses "say" with "text" which is the format used in Cline.ts
124+
{
125+
ts: baseTimestamp,
126+
type: "say",
127+
say: "text",
128+
text: taskPrompt,
129+
},
130+
131+
// API request started
132+
{
133+
ts: getNextTimestamp(),
134+
type: "say",
135+
say: "api_req_started",
136+
text: JSON.stringify({
137+
request: `<task>\n${taskPrompt}\n</task>`,
138+
tokensIn: Math.floor(100 + Math.random() * 200),
139+
tokensOut: Math.floor(300 + Math.random() * 500),
140+
}),
141+
},
142+
143+
// Reasoning message
144+
{
145+
ts: getNextTimestamp(),
146+
type: "say",
147+
say: "reasoning",
148+
text: `I'll approach this task by breaking it down into manageable steps. First, I'll analyze the requirements, then create a plan, and finally implement the solution systematically.`,
149+
},
150+
151+
// Text response
152+
{
153+
ts: getNextTimestamp(),
154+
type: "say",
155+
say: "text",
156+
text: `I'll help you with this task. Let me start by creating the necessary files and implementing the core functionality.`,
157+
},
158+
]
159+
160+
// Add task-specific messages based on index modulo to create variety
161+
const messageType = taskIndex % 5
162+
163+
if (messageType === 0 || messageType === 2) {
164+
// Tool use - file operations
165+
messages.push({
166+
ts: getNextTimestamp(),
167+
type: "say",
168+
say: "tool",
169+
text: JSON.stringify({
170+
tool: "newFileCreated",
171+
path: fileName,
172+
content: `// Sample code for ${taskPrompt}`,
173+
}),
174+
})
175+
}
176+
177+
if (messageType === 1 || messageType === 3) {
178+
// Command execution
179+
messages.push(
180+
{
181+
ts: getNextTimestamp(),
182+
type: "ask",
183+
ask: "command",
184+
text: `ls -la`,
185+
},
186+
{
187+
ts: getNextTimestamp(),
188+
type: "say",
189+
say: "command_output",
190+
text: `total 24\ndrwxr-xr-x 3 user staff 96 Mar 10 12:34 .\ndrwxr-xr-x 8 user staff 256 Mar 10 12:30 ..\n-rw-r--r-- 1 user staff 158 Mar 10 12:34 ${fileName}`,
191+
},
192+
)
193+
}
194+
195+
if (messageType === 2 || messageType === 4) {
196+
// Browser actions
197+
messages.push(
198+
{
199+
ts: getNextTimestamp(),
200+
type: "ask",
201+
ask: "browser_action_launch",
202+
text: `https://example.com`,
203+
},
204+
{
205+
ts: getNextTimestamp(),
206+
type: "say",
207+
say: "browser_action_result",
208+
text: JSON.stringify({
209+
logs: "Page loaded successfully",
210+
screenshot:
211+
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==",
212+
}),
213+
},
214+
{
215+
ts: getNextTimestamp(),
216+
type: "say",
217+
say: "browser_action",
218+
text: JSON.stringify({
219+
action: "close",
220+
}),
221+
},
222+
)
223+
}
224+
225+
// Add checkpoint
226+
messages.push({
227+
ts: getNextTimestamp(),
228+
type: "say",
229+
say: "checkpoint_created",
230+
lastCheckpointHash: commitHash,
231+
})
232+
233+
// Add completion result (all tasks end with this)
234+
messages.push({
235+
ts: getNextTimestamp(),
236+
type: "say",
237+
say: "completion_result",
238+
text: `I've completed the task to ${taskPrompt.toLowerCase()}. The implementation includes all the required functionality and meets the specifications. ${"x".repeat(1024 * 1024)}`, // 1MB file
239+
lastCheckpointHash: commitHash,
240+
})
241+
242+
return messages
243+
}
244+
245+
/**
246+
* Returns a random task name for test data
247+
*/
248+
function getRandomTaskName(index: number): string {
249+
const tasks = [
250+
"Create a simple todo application",
251+
"Build a weather forecast widget",
252+
"Implement a markdown parser",
253+
"Design a responsive landing page",
254+
"Develop a currency converter",
255+
"Create a file upload component",
256+
"Build a data visualization dashboard",
257+
"Implement a search functionality",
258+
"Create a user authentication system",
259+
"Design a dark mode toggle",
260+
"Build a countdown timer",
261+
"Create a drag and drop interface",
262+
"Implement form validation",
263+
"Design a multi-step wizard",
264+
"Create a notification system",
265+
]
266+
267+
return tasks[index % tasks.length] + ` (Test ${index + 1})`
268+
}
269+
270+
/**
271+
* Returns a random file name for test data
272+
*/
273+
function getRandomFileName(index: number): string {
274+
const files = [
275+
"index.html",
276+
"styles.css",
277+
"script.js",
278+
"app.jsx",
279+
"main.ts",
280+
"utils.py",
281+
"config.json",
282+
"server.js",
283+
"data.csv",
284+
"README.md",
285+
]
286+
287+
return files[index % files.length]
288+
}

src/extension.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ export function activate(context: vscode.ExtensionContext) {
3232

3333
const sidebarProvider = new ClineProvider(context, outputChannel)
3434

35+
vscode.commands.executeCommand("setContext", "cline.isDevMode", IS_DEV && IS_DEV === "true")
36+
3537
context.subscriptions.push(
3638
vscode.window.registerWebviewViewProvider(ClineProvider.sideBarId, sidebarProvider, {
3739
webviewOptions: { retainContextWhenHidden: true },
@@ -193,6 +195,20 @@ export function activate(context: vscode.ExtensionContext) {
193195
}
194196
context.subscriptions.push(vscode.window.registerUriHandler({ handleUri }))
195197

198+
// Register size testing commands in development mode
199+
if (IS_DEV && IS_DEV === "true") {
200+
// Use dynamic import to avoid loading the module in production
201+
import("./dev/commands/tasks")
202+
.then((module) => {
203+
const devTaskCommands = module.registerTaskCommands(context, sidebarProvider)
204+
context.subscriptions.push(...devTaskCommands)
205+
Logger.log("Cline dev task commands registered")
206+
})
207+
.catch((error) => {
208+
Logger.log("Failed to register dev task commands: " + error)
209+
})
210+
}
211+
196212
context.subscriptions.push(
197213
vscode.commands.registerCommand("cline.addToChat", async (range?: vscode.Range, diagnostics?: vscode.Diagnostic[]) => {
198214
const editor = vscode.window.activeTextEditor

0 commit comments

Comments
 (0)