feat(agentic): add task update and toggle console actions
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
c27c531805
commit
47bc3dc0fa
2 changed files with 214 additions and 2 deletions
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
|
||||
// SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Core\Mod\Agentic\Console\Commands;
|
||||
|
|
@ -10,8 +12,8 @@ use Illuminate\Console\Command;
|
|||
class TaskCommand extends Command
|
||||
{
|
||||
protected $signature = 'task
|
||||
{action=list : Action: list, add, done, start, remove, show}
|
||||
{--id= : Task ID for done/start/remove/show}
|
||||
{action=list : Action: list, add, update, toggle, done, start, remove, show}
|
||||
{--id= : Task ID for update/toggle/done/start/remove/show}
|
||||
{--title= : Task title for add}
|
||||
{--desc= : Task description}
|
||||
{--priority=normal : Priority: low, normal, high, urgent}
|
||||
|
|
@ -42,6 +44,8 @@ class TaskCommand extends Command
|
|||
return match ($action) {
|
||||
'list', 'ls' => $this->listTasks(),
|
||||
'add', 'new' => $this->addTask(),
|
||||
'update', 'edit' => $this->updateTask(),
|
||||
'toggle', 'flip' => $this->toggleTask(),
|
||||
'done', 'complete' => $this->completeTask(),
|
||||
'start', 'wip' => $this->startTask(),
|
||||
'remove', 'rm', 'delete' => $this->removeTask(),
|
||||
|
|
@ -161,6 +165,98 @@ class TaskCommand extends Command
|
|||
return 0;
|
||||
}
|
||||
|
||||
protected function updateTask(): int
|
||||
{
|
||||
$task = $this->findTask('update');
|
||||
|
||||
if (! $task) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
$updates = [];
|
||||
|
||||
$title = $this->option('title');
|
||||
if ($title !== null) {
|
||||
$title = trim((string) $title);
|
||||
if ($title === '') {
|
||||
$this->error('Title cannot be empty');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$updates['title'] = $title;
|
||||
}
|
||||
|
||||
$description = $this->option('desc');
|
||||
if ($description !== null) {
|
||||
$updates['description'] = (string) $description;
|
||||
}
|
||||
|
||||
$priority = $this->option('priority');
|
||||
if ($this->input->hasParameterOption('--priority')) {
|
||||
$allowed = ['low', 'normal', 'high', 'urgent'];
|
||||
if (! in_array($priority, $allowed, true)) {
|
||||
$this->error('Priority must be one of: low, normal, high, urgent');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$updates['priority'] = $priority;
|
||||
}
|
||||
|
||||
$category = $this->option('category');
|
||||
if ($category !== null) {
|
||||
$updates['category'] = (string) $category;
|
||||
}
|
||||
|
||||
$file = $this->option('file');
|
||||
if ($file !== null) {
|
||||
$updates['file_ref'] = (string) $file;
|
||||
}
|
||||
|
||||
$line = $this->option('line');
|
||||
if ($line !== null) {
|
||||
if (! is_numeric($line)) {
|
||||
$this->error('Line must be a number');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$updates['line_ref'] = (int) $line;
|
||||
}
|
||||
|
||||
if ($updates === []) {
|
||||
$this->error('Provide at least one field to update: --title, --desc, --priority, --category, --file, or --line');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$task->update($updates);
|
||||
|
||||
$fresh = $task->fresh();
|
||||
$this->info("Updated: {$fresh->title}");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected function toggleTask(): int
|
||||
{
|
||||
$task = $this->findTask('toggle');
|
||||
|
||||
if (! $task) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
$currentStatus = $task->status;
|
||||
$newStatus = $currentStatus === 'done' ? 'pending' : 'done';
|
||||
$task->update(['status' => $newStatus]);
|
||||
|
||||
$fresh = $task->fresh();
|
||||
$this->info("Toggled: {$fresh->title} {$currentStatus} → {$fresh->status}");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected function completeTask(): int
|
||||
{
|
||||
$task = $this->findTask('complete');
|
||||
|
|
@ -278,6 +374,8 @@ class TaskCommand extends Command
|
|||
$this->line(' <comment>Usage:</comment>');
|
||||
$this->line(' php artisan task list List active tasks');
|
||||
$this->line(' php artisan task add --title="Fix bug" Add a task');
|
||||
$this->line(' php artisan task update --id=1 --desc="..." Update task details');
|
||||
$this->line(' php artisan task toggle --id=1 Toggle task completion');
|
||||
$this->line(' php artisan task start --id=1 Start working on task');
|
||||
$this->line(' php artisan task done --id=1 Complete a task');
|
||||
$this->line(' php artisan task show --id=1 Show task details');
|
||||
|
|
|
|||
114
php/tests/Feature/TaskCommandTest.php
Normal file
114
php/tests/Feature/TaskCommandTest.php
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
<?php
|
||||
|
||||
// SPDX-License-Identifier: EUPL-1.2
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Core\Mod\Agentic\Tests\Feature;
|
||||
|
||||
use Core\Mod\Agentic\Models\Task;
|
||||
use Core\Tenant\Models\Workspace;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class TaskCommandTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
private Workspace $workspace;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->workspace = Workspace::factory()->create();
|
||||
}
|
||||
|
||||
public function test_it_updates_task_details(): void
|
||||
{
|
||||
$task = Task::create([
|
||||
'workspace_id' => $this->workspace->id,
|
||||
'title' => 'Original title',
|
||||
'description' => 'Original description',
|
||||
'priority' => 'normal',
|
||||
'category' => 'task',
|
||||
'file_ref' => 'app/Original.php',
|
||||
'line_ref' => 12,
|
||||
'status' => 'pending',
|
||||
]);
|
||||
|
||||
$this->artisan('task', [
|
||||
'action' => 'update',
|
||||
'--workspace' => $this->workspace->id,
|
||||
'--id' => $task->id,
|
||||
'--title' => 'Updated title',
|
||||
'--desc' => 'Updated description',
|
||||
'--priority' => 'urgent',
|
||||
'--category' => 'docs',
|
||||
'--file' => 'app/Updated.php',
|
||||
'--line' => 88,
|
||||
])
|
||||
->expectsOutputToContain('Updated: Updated title')
|
||||
->assertSuccessful();
|
||||
|
||||
$this->assertDatabaseHas('tasks', [
|
||||
'id' => $task->id,
|
||||
'workspace_id' => $this->workspace->id,
|
||||
'title' => 'Updated title',
|
||||
'description' => 'Updated description',
|
||||
'priority' => 'urgent',
|
||||
'category' => 'docs',
|
||||
'file_ref' => 'app/Updated.php',
|
||||
'line_ref' => 88,
|
||||
'status' => 'pending',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_it_toggles_task_completion(): void
|
||||
{
|
||||
$task = Task::create([
|
||||
'workspace_id' => $this->workspace->id,
|
||||
'title' => 'Toggle me',
|
||||
'status' => 'pending',
|
||||
]);
|
||||
|
||||
$this->artisan('task', [
|
||||
'action' => 'toggle',
|
||||
'--workspace' => $this->workspace->id,
|
||||
'--id' => $task->id,
|
||||
])
|
||||
->expectsOutputToContain('Toggled: Toggle me pending → done')
|
||||
->assertSuccessful();
|
||||
|
||||
$this->assertSame('done', $task->fresh()->status);
|
||||
|
||||
$this->artisan('task', [
|
||||
'action' => 'toggle',
|
||||
'--workspace' => $this->workspace->id,
|
||||
'--id' => $task->id,
|
||||
])
|
||||
->expectsOutputToContain('Toggled: Toggle me done → pending')
|
||||
->assertSuccessful();
|
||||
|
||||
$this->assertSame('pending', $task->fresh()->status);
|
||||
}
|
||||
|
||||
public function test_it_rejects_update_without_any_changes(): void
|
||||
{
|
||||
$task = Task::create([
|
||||
'workspace_id' => $this->workspace->id,
|
||||
'title' => 'No change',
|
||||
'status' => 'pending',
|
||||
]);
|
||||
|
||||
$this->artisan('task', [
|
||||
'action' => 'update',
|
||||
'--workspace' => $this->workspace->id,
|
||||
'--id' => $task->id,
|
||||
])
|
||||
->expectsOutputToContain('Provide at least one field to update')
|
||||
->assertFailed();
|
||||
|
||||
$this->assertSame('No change', $task->fresh()->title);
|
||||
$this->assertSame('pending', $task->fresh()->status);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue