From 3d5754089eb2325c0056176dd5d2373e8445af83 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 12 Feb 2026 03:08:06 +0900 Subject: [PATCH] feat(task): add team_name routing to task_get tool - Add optional team_name parameter to task_get - Route to team-namespaced storage when team_name provided - Preserve existing behavior when team_name absent - Add tests for both team and regular task retrieval - Part of Task 12 (2/4 files complete) --- src/tools/task/task-get.test.ts | 52 +++++++++++++++++++++++++++++++++ src/tools/task/task-get.ts | 17 +++++++---- src/tools/task/types.ts | 1 + 3 files changed, 64 insertions(+), 6 deletions(-) diff --git a/src/tools/task/task-get.test.ts b/src/tools/task/task-get.test.ts index 640b8bc0..f9893e00 100644 --- a/src/tools/task/task-get.test.ts +++ b/src/tools/task/task-get.test.ts @@ -3,6 +3,7 @@ import { existsSync, rmSync, mkdirSync, writeFileSync } from "fs" import { join } from "path" import type { TaskObject } from "./types" import { createTaskGetTool } from "./task-get" +import { writeTeamTask } from "../agent-teams/team-task-store" const TEST_STORAGE = ".test-task-get-tool" const TEST_DIR = join(process.cwd(), TEST_STORAGE) @@ -10,6 +11,7 @@ const TEST_CONFIG = { sisyphus: { tasks: { storage_path: TEST_STORAGE, + claude_code_compat: true, }, }, } @@ -218,5 +220,55 @@ describe("task_get tool", () => { expect(result.task.owner).toBeUndefined() expect(result.task.metadata).toBeUndefined() }) + + test("retrieves task from team namespace when team_name provided", async () => { + //#given + const teamName = "test-team" + const taskId = "T-team-task-404" + const taskData: TaskObject = { + id: taskId, + subject: "Team task", + description: "Team description", + status: "pending", + blocks: [], + blockedBy: [], + threadID: TEST_SESSION_ID, + } + writeTeamTask(teamName, taskId, taskData) + + //#when + const resultStr = await tool.execute({ id: taskId, team_name: teamName }, TEST_CONTEXT) + const result = JSON.parse(resultStr) + + //#then + expect(result).toHaveProperty("task") + expect(result.task).not.toBeNull() + expect(result.task.id).toBe(taskId) + expect(result.task.subject).toBe("Team task") + }) + + test("retrieves task from regular storage when no team_name", async () => { + //#given + const taskId = "T-regular-task-505" + const taskData: TaskObject = { + id: taskId, + subject: "Regular task", + description: "Regular", + status: "pending", + blocks: [], + blockedBy: [], + threadID: TEST_SESSION_ID, + } + const taskFile = join(TEST_DIR, `${taskId}.json`) + writeFileSync(taskFile, JSON.stringify(taskData, null, 2)) + + //#when + const resultStr = await tool.execute({ id: taskId }, TEST_CONTEXT) + const result = JSON.parse(resultStr) + + //#then + expect(result.task).not.toBeNull() + expect(result.task.subject).toBe("Regular task") + }) }) }) diff --git a/src/tools/task/task-get.ts b/src/tools/task/task-get.ts index 94ac9d89..95253488 100644 --- a/src/tools/task/task-get.ts +++ b/src/tools/task/task-get.ts @@ -4,6 +4,7 @@ import type { OhMyOpenCodeConfig } from "../../config/schema" import type { TaskGetInput } from "./types" import { TaskGetInputSchema, TaskObjectSchema } from "./types" import { getTaskDir, readJsonSafe } from "../../features/claude-tasks/storage" +import { readTeamTask } from "../agent-teams/team-task-store" const TASK_ID_PATTERN = /^T-[A-Za-z0-9-]+$/ @@ -21,6 +22,7 @@ Returns the full task object including all fields: id, subject, description, sta Returns null if the task does not exist or the file is invalid.`, args: { id: tool.schema.string().describe("Task ID to retrieve (format: T-{uuid})"), + team_name: tool.schema.string().optional().describe("Optional: team name for team-namespaced tasks"), }, execute: async (args: Record): Promise => { try { @@ -31,12 +33,15 @@ Returns null if the task does not exist or the file is invalid.`, return JSON.stringify({ error: "invalid_task_id" }) } - const taskDir = getTaskDir(config) - const taskPath = join(taskDir, `${taskId}.json`) - - const task = readJsonSafe(taskPath, TaskObjectSchema) - - return JSON.stringify({ task: task ?? null }) + if (validatedArgs.team_name) { + const task = readTeamTask(validatedArgs.team_name, taskId) + return JSON.stringify({ task: task ?? null }) + } else { + const taskDir = getTaskDir(config) + const taskPath = join(taskDir, `${taskId}.json`) + const task = readJsonSafe(taskPath, TaskObjectSchema) + return JSON.stringify({ task: task ?? null }) + } } catch (error) { if (error instanceof Error && error.message.includes("validation")) { return JSON.stringify({ error: "invalid_arguments" }) diff --git a/src/tools/task/types.ts b/src/tools/task/types.ts index 0762e4f6..33194697 100644 --- a/src/tools/task/types.ts +++ b/src/tools/task/types.ts @@ -51,6 +51,7 @@ export type TaskListInput = z.infer export const TaskGetInputSchema = z.object({ id: z.string(), + team_name: z.string().optional(), }) export type TaskGetInput = z.infer