Spaces:
Running
Running
Commit
Β·
12673bf
1
Parent(s):
7a07363
planning tool
Browse files- src/lib/server/context.md +8 -7
- src/lib/server/langgraph-agent.ts +69 -12
- src/lib/server/task-tracker.ts +136 -0
- src/lib/server/tools.ts +7 -4
src/lib/server/context.md
CHANGED
|
@@ -5,19 +5,20 @@ WebSocket server with LangGraph agent for AI-assisted game development.
|
|
| 5 |
## Key Components
|
| 6 |
|
| 7 |
- **api.ts** - WebSocket message routing with abort handling
|
| 8 |
-
- **langgraph-agent.ts** - LangGraph agent with
|
| 9 |
-
- **tools.ts** - Editor manipulation:
|
|
|
|
| 10 |
- **console-buffer.ts** - Console message storage
|
| 11 |
- **documentation.ts** - VibeGame documentation loader
|
| 12 |
|
| 13 |
## Architecture
|
| 14 |
|
| 15 |
-
LangGraph state machine with
|
| 16 |
|
| 17 |
-
-
|
| 18 |
-
-
|
| 19 |
-
-
|
| 20 |
-
- AbortController for canceling
|
| 21 |
|
| 22 |
## Message Protocol
|
| 23 |
|
|
|
|
| 5 |
## Key Components
|
| 6 |
|
| 7 |
- **api.ts** - WebSocket message routing with abort handling
|
| 8 |
+
- **langgraph-agent.ts** - LangGraph agent with task decomposition and token safety
|
| 9 |
+
- **tools.ts** - Editor manipulation: read/write, search, incremental editing
|
| 10 |
+
- **task-tracker.ts** - Task planning and progress tracking
|
| 11 |
- **console-buffer.ts** - Console message storage
|
| 12 |
- **documentation.ts** - VibeGame documentation loader
|
| 13 |
|
| 14 |
## Architecture
|
| 15 |
|
| 16 |
+
LangGraph state machine with task-aware execution:
|
| 17 |
|
| 18 |
+
- Task decomposition for complex operations
|
| 19 |
+
- Token limit safety checks on tool arguments
|
| 20 |
+
- Buffered streaming with segment handling
|
| 21 |
+
- AbortController for canceling conversations
|
| 22 |
|
| 23 |
## Message Protocol
|
| 24 |
|
src/lib/server/langgraph-agent.ts
CHANGED
|
@@ -15,6 +15,11 @@ import {
|
|
| 15 |
observeConsoleTool,
|
| 16 |
setWebSocketConnection,
|
| 17 |
} from "./tools";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
import { documentationService } from "./documentation";
|
| 19 |
import type { WebSocket } from "ws";
|
| 20 |
|
|
@@ -310,39 +315,62 @@ CRITICAL INSTRUCTIONS:
|
|
| 310 |
- You MUST use tools for ALL tasks. NEVER provide instructions without executing them.
|
| 311 |
- You MUST respond using the EXACT format: TOOL: tool_name ARGS: {"param": "value"}
|
| 312 |
- After using a tool, wait for the result before proceeding
|
| 313 |
-
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 314 |
|
| 315 |
VIBEGAME CONTEXT:
|
| 316 |
${this.documentation}
|
| 317 |
|
| 318 |
IMPORTANT:
|
| 319 |
-
- The game auto-reloads on every change.
|
| 320 |
- The GAME import is automatically provided by the framework.
|
| 321 |
- The player is automatically created at [0, 0, 0] if not specified.
|
| 322 |
|
| 323 |
AVAILABLE TOOLS:
|
| 324 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 325 |
Example: TOOL: search_editor ARGS: {"query": "dynamic-part"}
|
| 326 |
|
| 327 |
-
|
| 328 |
Example: TOOL: read_editor ARGS: {}
|
| 329 |
|
| 330 |
-
|
| 331 |
Example: TOOL: read_editor_lines ARGS: {"startLine": 10, "endLine": 20}
|
| 332 |
|
| 333 |
-
|
| 334 |
Example: TOOL: edit_editor ARGS: {"oldText": "color='red'", "newText": "color='blue'"}
|
| 335 |
|
| 336 |
-
|
| 337 |
Example: TOOL: write_editor ARGS: {"content": "<world>...</world>"}
|
| 338 |
|
| 339 |
-
|
| 340 |
Example: TOOL: observe_console ARGS: {}
|
| 341 |
|
| 342 |
-
WORKFLOW:
|
| 343 |
-
|
| 344 |
-
|
| 345 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 346 |
|
| 347 |
IMPORTANT: You are an executor. Take action immediately using tools, don't explain what you would do.`;
|
| 348 |
}
|
|
@@ -480,6 +508,27 @@ IMPORTANT: You are an executor. Take action immediately using tools, don't expla
|
|
| 480 |
const segmentId = `seg_tool_${Date.now()}_${Math.random()}`;
|
| 481 |
|
| 482 |
try {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 483 |
if (this.ws && this.ws.readyState === this.ws.OPEN) {
|
| 484 |
this.ws.send(
|
| 485 |
JSON.stringify({
|
|
@@ -547,6 +596,14 @@ IMPORTANT: You are an executor. Take action immediately using tools, don't expla
|
|
| 547 |
}
|
| 548 |
} else if (call.name === "observe_console") {
|
| 549 |
result = await observeConsoleTool.func("");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 550 |
} else {
|
| 551 |
result = `Unknown tool: ${call.name}`;
|
| 552 |
}
|
|
|
|
| 15 |
observeConsoleTool,
|
| 16 |
setWebSocketConnection,
|
| 17 |
} from "./tools";
|
| 18 |
+
import {
|
| 19 |
+
planTasksTool,
|
| 20 |
+
updateTaskTool,
|
| 21 |
+
viewTasksTool,
|
| 22 |
+
} from "./task-tracker";
|
| 23 |
import { documentationService } from "./documentation";
|
| 24 |
import type { WebSocket } from "ws";
|
| 25 |
|
|
|
|
| 315 |
- You MUST use tools for ALL tasks. NEVER provide instructions without executing them.
|
| 316 |
- You MUST respond using the EXACT format: TOOL: tool_name ARGS: {"param": "value"}
|
| 317 |
- After using a tool, wait for the result before proceeding
|
| 318 |
+
- For complex tasks, use plan_tasks FIRST to break down the work
|
| 319 |
+
- Keep tool arguments concise - prefer multiple small edits over one large edit
|
| 320 |
+
|
| 321 |
+
TASK DECOMPOSITION RULES:
|
| 322 |
+
- For any task requiring 3+ changes, use plan_tasks FIRST
|
| 323 |
+
- Break large code changes into smaller, focused edits
|
| 324 |
+
- Each edit_editor call should modify ONE logical section (max ~20 lines)
|
| 325 |
+
- Mark tasks as in_progress when starting, completed when done
|
| 326 |
|
| 327 |
VIBEGAME CONTEXT:
|
| 328 |
${this.documentation}
|
| 329 |
|
| 330 |
IMPORTANT:
|
| 331 |
+
- The game auto-reloads on every change.
|
| 332 |
- The GAME import is automatically provided by the framework.
|
| 333 |
- The player is automatically created at [0, 0, 0] if not specified.
|
| 334 |
|
| 335 |
AVAILABLE TOOLS:
|
| 336 |
+
|
| 337 |
+
TASK MANAGEMENT:
|
| 338 |
+
1. plan_tasks - Break complex work into steps (USE FIRST for multi-step tasks!)
|
| 339 |
+
Example: TOOL: plan_tasks ARGS: {"tasks": ["Find the player object", "Add jump ability", "Test the changes"]}
|
| 340 |
+
|
| 341 |
+
2. update_task - Mark task progress
|
| 342 |
+
Example: TOOL: update_task ARGS: {"taskId": 1, "status": "in_progress"}
|
| 343 |
+
|
| 344 |
+
3. view_tasks - See current task list
|
| 345 |
+
Example: TOOL: view_tasks ARGS: {}
|
| 346 |
+
|
| 347 |
+
EDITOR TOOLS:
|
| 348 |
+
4. search_editor - Find text/patterns in code
|
| 349 |
Example: TOOL: search_editor ARGS: {"query": "dynamic-part"}
|
| 350 |
|
| 351 |
+
5. read_editor - Read entire editor content
|
| 352 |
Example: TOOL: read_editor ARGS: {}
|
| 353 |
|
| 354 |
+
6. read_editor_lines - Read specific lines (use after search_editor)
|
| 355 |
Example: TOOL: read_editor_lines ARGS: {"startLine": 10, "endLine": 20}
|
| 356 |
|
| 357 |
+
7. edit_editor - Replace specific text (KEEP EDITS SMALL - max ~20 lines per call)
|
| 358 |
Example: TOOL: edit_editor ARGS: {"oldText": "color='red'", "newText": "color='blue'"}
|
| 359 |
|
| 360 |
+
8. write_editor - Replace entire content (ONLY for new files or complete rewrites)
|
| 361 |
Example: TOOL: write_editor ARGS: {"content": "<world>...</world>"}
|
| 362 |
|
| 363 |
+
9. observe_console - Check console for errors
|
| 364 |
Example: TOOL: observe_console ARGS: {}
|
| 365 |
|
| 366 |
+
WORKFLOW EXAMPLE:
|
| 367 |
+
User: "Add jumping to the player"
|
| 368 |
+
1. TOOL: plan_tasks ARGS: {"tasks": ["Search for player code", "Add jump component", "Add jump controls", "Test jumping"]}
|
| 369 |
+
2. TOOL: update_task ARGS: {"taskId": 1, "status": "in_progress"}
|
| 370 |
+
3. TOOL: search_editor ARGS: {"query": "player"}
|
| 371 |
+
4. TOOL: edit_editor ARGS: {"oldText": "...", "newText": "..."}
|
| 372 |
+
5. TOOL: update_task ARGS: {"taskId": 1, "status": "completed"}
|
| 373 |
+
(continue with remaining tasks...)
|
| 374 |
|
| 375 |
IMPORTANT: You are an executor. Take action immediately using tools, don't explain what you would do.`;
|
| 376 |
}
|
|
|
|
| 508 |
const segmentId = `seg_tool_${Date.now()}_${Math.random()}`;
|
| 509 |
|
| 510 |
try {
|
| 511 |
+
const argString = JSON.stringify(call.args);
|
| 512 |
+
const estimatedTokens = argString.length / 4;
|
| 513 |
+
|
| 514 |
+
if (estimatedTokens > 1000 && (call.name === "edit_editor" || call.name === "write_editor")) {
|
| 515 |
+
console.warn(`Warning: Tool ${call.name} arguments are large (${estimatedTokens} estimated tokens)`);
|
| 516 |
+
|
| 517 |
+
if (call.name === "edit_editor" && call.args.oldText) {
|
| 518 |
+
const oldText = call.args.oldText as string;
|
| 519 |
+
|
| 520 |
+
if (oldText.split('\n').length > 20) {
|
| 521 |
+
results.push(
|
| 522 |
+
new ToolMessage({
|
| 523 |
+
content: `Error: The edit is too large (${oldText.split('\n').length} lines). Please break this into smaller edits of max 20 lines each. Use plan_tasks to organize multiple edits.`,
|
| 524 |
+
tool_call_id: segmentId,
|
| 525 |
+
name: call.name,
|
| 526 |
+
})
|
| 527 |
+
);
|
| 528 |
+
continue;
|
| 529 |
+
}
|
| 530 |
+
}
|
| 531 |
+
}
|
| 532 |
if (this.ws && this.ws.readyState === this.ws.OPEN) {
|
| 533 |
this.ws.send(
|
| 534 |
JSON.stringify({
|
|
|
|
| 596 |
}
|
| 597 |
} else if (call.name === "observe_console") {
|
| 598 |
result = await observeConsoleTool.func("");
|
| 599 |
+
} else if (call.name === "plan_tasks") {
|
| 600 |
+
result = await planTasksTool.func(call.args as { tasks: string[] });
|
| 601 |
+
} else if (call.name === "update_task") {
|
| 602 |
+
result = await updateTaskTool.func(
|
| 603 |
+
call.args as { taskId: number; status: "pending" | "in_progress" | "completed" }
|
| 604 |
+
);
|
| 605 |
+
} else if (call.name === "view_tasks") {
|
| 606 |
+
result = await viewTasksTool.func({});
|
| 607 |
} else {
|
| 608 |
result = `Unknown tool: ${call.name}`;
|
| 609 |
}
|
src/lib/server/task-tracker.ts
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { DynamicStructuredTool } from "@langchain/core/tools";
|
| 2 |
+
import { z } from "zod";
|
| 3 |
+
|
| 4 |
+
interface Task {
|
| 5 |
+
id: number;
|
| 6 |
+
description: string;
|
| 7 |
+
status: "pending" | "in_progress" | "completed";
|
| 8 |
+
createdAt: Date;
|
| 9 |
+
completedAt?: Date;
|
| 10 |
+
}
|
| 11 |
+
|
| 12 |
+
class TaskTracker {
|
| 13 |
+
private tasks: Task[] = [];
|
| 14 |
+
private nextId = 1;
|
| 15 |
+
|
| 16 |
+
addTask(description: string): Task {
|
| 17 |
+
const task: Task = {
|
| 18 |
+
id: this.nextId++,
|
| 19 |
+
description,
|
| 20 |
+
status: "pending",
|
| 21 |
+
createdAt: new Date(),
|
| 22 |
+
};
|
| 23 |
+
this.tasks.push(task);
|
| 24 |
+
return task;
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
updateTaskStatus(id: number, status: Task["status"]): Task | null {
|
| 28 |
+
const task = this.tasks.find((t) => t.id === id);
|
| 29 |
+
if (task) {
|
| 30 |
+
task.status = status;
|
| 31 |
+
if (status === "completed") {
|
| 32 |
+
task.completedAt = new Date();
|
| 33 |
+
}
|
| 34 |
+
return task;
|
| 35 |
+
}
|
| 36 |
+
return null;
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
getTasks(): Task[] {
|
| 40 |
+
return [...this.tasks];
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
getActiveTasks(): Task[] {
|
| 44 |
+
return this.tasks.filter((t) => t.status !== "completed");
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
clear(): void {
|
| 48 |
+
this.tasks = [];
|
| 49 |
+
this.nextId = 1;
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
formatTaskList(): string {
|
| 53 |
+
if (this.tasks.length === 0) {
|
| 54 |
+
return "No tasks in the list.";
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
const statusEmoji = {
|
| 58 |
+
pending: "β³",
|
| 59 |
+
in_progress: "π",
|
| 60 |
+
completed: "β
",
|
| 61 |
+
};
|
| 62 |
+
|
| 63 |
+
return this.tasks
|
| 64 |
+
.map(
|
| 65 |
+
(t) =>
|
| 66 |
+
`${statusEmoji[t.status]} [${t.id}] ${t.description} (${t.status})`,
|
| 67 |
+
)
|
| 68 |
+
.join("\n");
|
| 69 |
+
}
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
const taskTracker = new TaskTracker();
|
| 73 |
+
|
| 74 |
+
export const planTasksTool = new DynamicStructuredTool({
|
| 75 |
+
name: "plan_tasks",
|
| 76 |
+
description:
|
| 77 |
+
"Plan and break down a complex task into smaller steps. Use this BEFORE starting any multi-step work to organize your approach.",
|
| 78 |
+
schema: z.object({
|
| 79 |
+
tasks: z
|
| 80 |
+
.array(z.string())
|
| 81 |
+
.min(1)
|
| 82 |
+
.describe(
|
| 83 |
+
"List of task descriptions in order of execution. Keep each task focused and achievable with a single tool call.",
|
| 84 |
+
),
|
| 85 |
+
}),
|
| 86 |
+
func: async (input: { tasks: string[] }) => {
|
| 87 |
+
taskTracker.clear();
|
| 88 |
+
const createdTasks = input.tasks.map((desc) => taskTracker.addTask(desc));
|
| 89 |
+
|
| 90 |
+
return `Task plan created with ${createdTasks.length} tasks:\n${taskTracker.formatTaskList()}\n\nStart with task 1 and mark it as in_progress when you begin.`;
|
| 91 |
+
},
|
| 92 |
+
});
|
| 93 |
+
|
| 94 |
+
export const updateTaskTool = new DynamicStructuredTool({
|
| 95 |
+
name: "update_task",
|
| 96 |
+
description:
|
| 97 |
+
"Update the status of a task. Mark as 'in_progress' when starting, 'completed' when done.",
|
| 98 |
+
schema: z.object({
|
| 99 |
+
taskId: z.number().min(1).describe("The task ID to update"),
|
| 100 |
+
status: z
|
| 101 |
+
.enum(["pending", "in_progress", "completed"])
|
| 102 |
+
.describe("The new status for the task"),
|
| 103 |
+
}),
|
| 104 |
+
func: async (input: { taskId: number; status: Task["status"] }) => {
|
| 105 |
+
const task = taskTracker.updateTaskStatus(input.taskId, input.status);
|
| 106 |
+
|
| 107 |
+
if (!task) {
|
| 108 |
+
return `Error: Task ${input.taskId} not found.`;
|
| 109 |
+
}
|
| 110 |
+
|
| 111 |
+
const activeTasks = taskTracker.getActiveTasks();
|
| 112 |
+
const nextTask = activeTasks.find((t) => t.status === "pending");
|
| 113 |
+
|
| 114 |
+
let response = `Task ${task.id} marked as ${task.status}: "${task.description}"`;
|
| 115 |
+
|
| 116 |
+
if (input.status === "completed" && nextTask) {
|
| 117 |
+
response += `\n\nNext task: [${nextTask.id}] ${nextTask.description}`;
|
| 118 |
+
} else if (input.status === "completed" && activeTasks.length === 0) {
|
| 119 |
+
response += "\n\nAll tasks completed! π";
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
response += `\n\nCurrent task list:\n${taskTracker.formatTaskList()}`;
|
| 123 |
+
return response;
|
| 124 |
+
},
|
| 125 |
+
});
|
| 126 |
+
|
| 127 |
+
export const viewTasksTool = new DynamicStructuredTool({
|
| 128 |
+
name: "view_tasks",
|
| 129 |
+
description: "View the current task list and their statuses.",
|
| 130 |
+
schema: z.object({}),
|
| 131 |
+
func: async () => {
|
| 132 |
+
return taskTracker.formatTaskList();
|
| 133 |
+
},
|
| 134 |
+
});
|
| 135 |
+
|
| 136 |
+
export const taskTrackerTools = [planTasksTool, updateTaskTool, viewTasksTool];
|
src/lib/server/tools.ts
CHANGED
|
@@ -92,9 +92,9 @@ export const readEditorLinesTool = new DynamicStructuredTool({
|
|
| 92 |
export const editEditorTool = new DynamicStructuredTool({
|
| 93 |
name: "edit_editor",
|
| 94 |
description:
|
| 95 |
-
"Replace specific text in the editor - use for targeted changes
|
| 96 |
schema: z.object({
|
| 97 |
-
oldText: z.string().describe("The exact text to find and replace"),
|
| 98 |
newText: z.string().describe("The text to replace it with"),
|
| 99 |
}),
|
| 100 |
func: async (input: { oldText: string; newText: string }) => {
|
|
@@ -154,9 +154,9 @@ export const editEditorTool = new DynamicStructuredTool({
|
|
| 154 |
export const writeEditorTool = new DynamicStructuredTool({
|
| 155 |
name: "write_editor",
|
| 156 |
description:
|
| 157 |
-
"Replace entire editor content - use for creating new files or complete rewrites",
|
| 158 |
schema: z.object({
|
| 159 |
-
content: z.string().describe("The code content to write to the editor"),
|
| 160 |
}),
|
| 161 |
func: async (input: { content: string }) => {
|
| 162 |
currentEditorContent = input.content;
|
|
@@ -320,6 +320,8 @@ export const observeConsoleTool = new DynamicTool({
|
|
| 320 |
},
|
| 321 |
});
|
| 322 |
|
|
|
|
|
|
|
| 323 |
export const tools = [
|
| 324 |
readEditorTool,
|
| 325 |
readEditorLinesTool,
|
|
@@ -327,4 +329,5 @@ export const tools = [
|
|
| 327 |
editEditorTool,
|
| 328 |
writeEditorTool,
|
| 329 |
observeConsoleTool,
|
|
|
|
| 330 |
];
|
|
|
|
| 92 |
export const editEditorTool = new DynamicStructuredTool({
|
| 93 |
name: "edit_editor",
|
| 94 |
description:
|
| 95 |
+
"Replace specific text in the editor - use for SMALL, targeted changes (max ~20 lines). For large changes, use multiple edit_editor calls with plan_tasks",
|
| 96 |
schema: z.object({
|
| 97 |
+
oldText: z.string().describe("The exact text to find and replace (keep small - max ~20 lines)"),
|
| 98 |
newText: z.string().describe("The text to replace it with"),
|
| 99 |
}),
|
| 100 |
func: async (input: { oldText: string; newText: string }) => {
|
|
|
|
| 154 |
export const writeEditorTool = new DynamicStructuredTool({
|
| 155 |
name: "write_editor",
|
| 156 |
description:
|
| 157 |
+
"Replace entire editor content - use ONLY for creating new files or complete rewrites. For modifications, use edit_editor with plan_tasks instead",
|
| 158 |
schema: z.object({
|
| 159 |
+
content: z.string().describe("The complete code content to write to the editor"),
|
| 160 |
}),
|
| 161 |
func: async (input: { content: string }) => {
|
| 162 |
currentEditorContent = input.content;
|
|
|
|
| 320 |
},
|
| 321 |
});
|
| 322 |
|
| 323 |
+
import { taskTrackerTools } from "./task-tracker";
|
| 324 |
+
|
| 325 |
export const tools = [
|
| 326 |
readEditorTool,
|
| 327 |
readEditorLinesTool,
|
|
|
|
| 329 |
editEditorTool,
|
| 330 |
writeEditorTool,
|
| 331 |
observeConsoleTool,
|
| 332 |
+
...taskTrackerTools,
|
| 333 |
];
|