summaryrefslogtreecommitdiffhomepage
path: root/packages/core/src/tools
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-05-20 15:04:26 +0900
committerAdam Malczewski <[email protected]>2026-05-20 15:04:26 +0900
commitadc8bd185b54935e7a31aae04da3175b7989927a (patch)
tree031fa53009a4708ce0cc15c0e3dcb2f2a73cf2d9 /packages/core/src/tools
parent52af4ca33b1f83b8f863a6267d447944f55e729d (diff)
downloaddispatch-adc8bd185b54935e7a31aae04da3175b7989927a.tar.gz
dispatch-adc8bd185b54935e7a31aae04da3175b7989927a.zip
feat: phase 3 — config, skills, model groups, task list, and sidebar UI
- Config system: TOML-based dispatch.toml with hot-reload via chokidar - Model/key resolution: tag-based model selection, key fallback chains - Skills system: directory loader with TOML frontmatter, agent mappings - Task list tool: add/update/list/get operations with WebSocket events - API routes: GET /config, /skills, /skills/:name, /models, /models/resolve - Frontend: sidebar with model status, task list, config viewer, skills browser, permission log - Sliding sidebar animation using CSS transitions (not Svelte transitions)
Diffstat (limited to 'packages/core/src/tools')
-rw-r--r--packages/core/src/tools/task-list.ts148
1 files changed, 148 insertions, 0 deletions
diff --git a/packages/core/src/tools/task-list.ts b/packages/core/src/tools/task-list.ts
new file mode 100644
index 0000000..0bacdb4
--- /dev/null
+++ b/packages/core/src/tools/task-list.ts
@@ -0,0 +1,148 @@
+import { z } from "zod";
+import type { TaskItem, TaskStatus, ToolDefinition } from "../types/index.js";
+
+export class TaskList {
+ private tasks: TaskItem[] = [];
+ private counter = 0;
+ private listeners: Array<(tasks: TaskItem[]) => void> = [];
+
+ private notify(): void {
+ const snapshot = this.getTasks();
+ for (const listener of this.listeners) {
+ listener(snapshot);
+ }
+ }
+
+ getTasks(): TaskItem[] {
+ return [...this.tasks];
+ }
+
+ getTask(id: string): TaskItem | undefined {
+ return this.tasks.find((t) => t.id === id);
+ }
+
+ addTask(title: string, description: string): TaskItem {
+ this.counter++;
+ const task: TaskItem = {
+ id: `task-${this.counter}`,
+ title,
+ description,
+ status: "pending",
+ };
+ this.tasks.push(task);
+ this.notify();
+ return task;
+ }
+
+ updateTask(id: string, status: TaskStatus): TaskItem | undefined {
+ const task = this.tasks.find((t) => t.id === id);
+ if (!task) return undefined;
+ task.status = status;
+ this.notify();
+ return { ...task };
+ }
+
+ removeTask(id: string): boolean {
+ const index = this.tasks.findIndex((t) => t.id === id);
+ if (index === -1) return false;
+ this.tasks.splice(index, 1);
+ this.notify();
+ return true;
+ }
+
+ onChange(callback: (tasks: TaskItem[]) => void): () => void {
+ this.listeners.push(callback);
+ return () => {
+ this.listeners = this.listeners.filter((l) => l !== callback);
+ };
+ }
+}
+
+export function createTaskListTool(taskList: TaskList): ToolDefinition {
+ return {
+ name: "task_list",
+ description:
+ "Manages a task list for tracking work items. The agent can add tasks, update their status, list all tasks, or get details on a specific task.",
+ parameters: z.object({
+ action: z
+ .enum(["add", "update", "list", "get", "remove"])
+ .describe("The action to perform"),
+ title: z.string().optional().describe("Task title (required for 'add')"),
+ description: z
+ .string()
+ .optional()
+ .describe("Task description (for 'add', defaults to empty)"),
+ task_id: z
+ .string()
+ .optional()
+ .describe("Task ID (required for 'update', 'get', 'remove')"),
+ status: z
+ .enum(["pending", "in_progress", "done", "blocked"])
+ .optional()
+ .describe("New status (required for 'update')"),
+ }),
+ execute: async (args: Record<string, unknown>): Promise<string> => {
+ const action = args.action as string;
+
+ if (action === "add") {
+ const title = args.title as string | undefined;
+ if (!title) {
+ return "Error: 'title' is required for the 'add' action.";
+ }
+ const description = (args.description as string | undefined) ?? "";
+ const task = taskList.addTask(title, description);
+ return JSON.stringify(task);
+ }
+
+ if (action === "update") {
+ const task_id = args.task_id as string | undefined;
+ const status = args.status as TaskStatus | undefined;
+ if (!task_id) {
+ return "Error: 'task_id' is required for the 'update' action.";
+ }
+ if (!status) {
+ return "Error: 'status' is required for the 'update' action.";
+ }
+ const updated = taskList.updateTask(task_id, status);
+ if (!updated) {
+ return `Error: Task with ID '${task_id}' not found.`;
+ }
+ return JSON.stringify(updated);
+ }
+
+ if (action === "get") {
+ const task_id = args.task_id as string | undefined;
+ if (!task_id) {
+ return "Error: 'task_id' is required for the 'get' action.";
+ }
+ const task = taskList.getTask(task_id);
+ if (!task) {
+ return `Error: Task with ID '${task_id}' not found.`;
+ }
+ return JSON.stringify(task);
+ }
+
+ if (action === "list") {
+ const tasks = taskList.getTasks();
+ if (tasks.length === 0) {
+ return "No tasks.";
+ }
+ return JSON.stringify(tasks);
+ }
+
+ if (action === "remove") {
+ const task_id = args.task_id as string | undefined;
+ if (!task_id) {
+ return "Error: 'task_id' is required for the 'remove' action.";
+ }
+ const removed = taskList.removeTask(task_id);
+ if (!removed) {
+ return `Error: Task with ID '${task_id}' not found.`;
+ }
+ return `Task '${task_id}' removed successfully.`;
+ }
+
+ return `Error: Unknown action '${action}'.`;
+ },
+ };
+}