diff --git a/src/tools/index.ts b/src/tools/index.ts index ca4e212..518b26a 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -6,6 +6,7 @@ import { createMessageTools } from './messages.js'; import { createProjectTools } from './projects.js'; import { createMemberTools } from './members.js'; import { createFileTools } from './files.js'; +import { taskLinkTools } from './tasks.js'; /** * Create all Team Board tracker tools for the agent. @@ -19,6 +20,7 @@ export function createTrackerTools(ctx: ToolContext): ToolDefinition[] { ...createProjectTools(ctx), ...createMemberTools(ctx), ...createFileTools(ctx), + ...taskLinkTools(ctx), ]; } diff --git a/src/tools/tasks.ts b/src/tools/tasks.ts index c9eb0f0..5827be8 100644 --- a/src/tools/tasks.ts +++ b/src/tools/tasks.ts @@ -127,3 +127,58 @@ export function createTaskTools(ctx: ToolContext): ToolDefinition[] { }, ]; } + +// --- Task Links --- + +const CreateTaskLinkParams = Type.Object({ + task_id: Type.String({ description: 'Source task ID (UUID)' }), + target_id: Type.String({ description: 'Target task ID (UUID)' }), + link_type: Type.String({ description: 'Link type: blocks | depends_on | relates_to' }), +}); + +const ListTaskLinksParams = Type.Object({ + task_id: Type.String({ description: 'Task ID (UUID)' }), +}); + +const DeleteTaskLinkParams = Type.Object({ + task_id: Type.String({ description: 'Task ID (UUID)' }), + link_id: Type.String({ description: 'Link ID (UUID)' }), +}); + +export function taskLinkTools(ctx: ToolContext): ToolDefinition[] { + return [ + { + name: 'create_task_link', + label: 'Create Task Link', + description: 'Create a dependency/link between tasks. Types: blocks (source blocks target), depends_on (source depends on target), relates_to (informational).', + parameters: CreateTaskLinkParams, + async execute(_id: string, params: any) { + const { task_id, target_id, link_type } = params as { task_id: string; target_id: string; link_type: string }; + const resp = await ctx.trackerClient.request('POST', `/api/v1/tasks/${task_id}/links`, { target_id, link_type }); + return ok(JSON.stringify(resp)); + }, + }, + { + name: 'list_task_links', + label: 'List Task Links', + description: 'List all dependencies/links for a task. Shows both outgoing (this task blocks/depends_on) and incoming (blocked_by/required_by) links.', + parameters: ListTaskLinksParams, + async execute(_id: string, params: any) { + const { task_id } = params as { task_id: string }; + const resp = await ctx.trackerClient.request('GET', `/api/v1/tasks/${task_id}/links`); + return ok(JSON.stringify(resp)); + }, + }, + { + name: 'delete_task_link', + label: 'Delete Task Link', + description: 'Remove a dependency/link between tasks.', + parameters: DeleteTaskLinkParams, + async execute(_id: string, params: any) { + const { task_id, link_id } = params as { task_id: string; link_id: string }; + const resp = await ctx.trackerClient.request('DELETE', `/api/v1/tasks/${task_id}/links/${link_id}`); + return ok(JSON.stringify(resp)); + }, + }, + ]; +} diff --git a/src/tracker/client.ts b/src/tracker/client.ts index 169b972..b70e8cf 100644 --- a/src/tracker/client.ts +++ b/src/tracker/client.ts @@ -17,7 +17,7 @@ export class TrackerClient { private token: string, ) {} - private async request(method: string, path: string, body?: unknown): Promise { + async request(method: string, path: string, body?: unknown): Promise { const url = `${this.baseUrl}${path}`; this.log.info(' REST %s %s', method, path); if (body) this.log.info(' body: %s', JSON.stringify(body).slice(0, 300));