*/ public function dependencies(): array { return [ ToolDependency::entityExists('plan', 'Plan must exist', ['arg_key' => 'plan_slug']), ]; } public function name(): string { return 'task_update'; } public function description(): string { return 'Update task details (status, notes)'; } public function inputSchema(): array { return [ 'type' => 'object', 'properties' => [ 'plan_slug' => [ 'type' => 'string', 'description' => 'Plan slug identifier', ], 'phase' => [ 'type' => 'string', 'description' => 'Phase identifier (number or name)', ], 'task_index' => [ 'type' => 'integer', 'description' => 'Task index (0-based)', ], 'status' => [ 'type' => 'string', 'description' => 'New status', 'enum' => ['pending', 'in_progress', 'completed', 'blocked', 'skipped'], ], 'notes' => [ 'type' => 'string', 'description' => 'Task notes', ], ], 'required' => ['plan_slug', 'phase', 'task_index'], ]; } public function handle(array $args, array $context = []): array { try { $planSlug = $this->requireString($args, 'plan_slug', 255); $phaseIdentifier = $this->requireString($args, 'phase', 255); $taskIndex = $this->requireInt($args, 'task_index', min: 0, max: 1000); // Validate optional status enum $status = $this->optionalEnum($args, 'status', ['pending', 'in_progress', 'completed', 'blocked', 'skipped']); $notes = $this->optionalString($args, 'notes', null, 5000); } catch (\InvalidArgumentException $e) { return $this->error($e->getMessage()); } $plan = AgentPlan::where('slug', $planSlug)->first(); if (! $plan) { return $this->error("Plan not found: {$planSlug}"); } $phase = $this->findPhase($plan, $phaseIdentifier); if (! $phase) { return $this->error("Phase not found: {$phaseIdentifier}"); } $tasks = $phase->tasks ?? []; if (! isset($tasks[$taskIndex])) { return $this->error("Task not found at index: {$taskIndex}"); } // Normalise task to array format if (is_string($tasks[$taskIndex])) { $tasks[$taskIndex] = ['name' => $tasks[$taskIndex], 'status' => 'pending']; } // Update fields using pre-validated values if ($status !== null) { $tasks[$taskIndex]['status'] = $status; } if ($notes !== null) { $tasks[$taskIndex]['notes'] = $notes; } $phase->update(['tasks' => $tasks]); return $this->success([ 'task' => $tasks[$taskIndex], ]); } /** * Find a phase by order number or name. */ protected function findPhase(AgentPlan $plan, string|int $identifier): ?AgentPhase { if (is_numeric($identifier)) { return $plan->agentPhases()->where('order', (int) $identifier)->first(); } return $plan->agentPhases() ->where(function ($query) use ($identifier) { $query->where('name', $identifier) ->orWhere('order', $identifier); }) ->first(); } }