workspaceId = $this->resolveWorkspaceId();
if ($this->workspaceId === null) {
$this->error('Workspace context required. Use --workspace=ID or ensure user is authenticated.');
return 1;
}
$action = $this->argument('action');
return match ($action) {
'list', 'ls' => $this->listTasks(),
'add', 'new' => $this->addTask(),
'done', 'complete' => $this->completeTask(),
'start', 'wip' => $this->startTask(),
'remove', 'rm', 'delete' => $this->removeTask(),
'show' => $this->showTask(),
default => $this->showHelp(),
};
}
/**
* Resolve workspace ID from option or authenticated user.
*/
protected function resolveWorkspaceId(): ?int
{
// Explicit workspace option takes precedence
if ($workspaceOption = $this->option('workspace')) {
return (int) $workspaceOption;
}
// Fall back to authenticated user's default workspace
$user = auth()->user();
if ($user && method_exists($user, 'defaultHostWorkspace')) {
$workspace = $user->defaultHostWorkspace();
return $workspace?->id;
}
return null;
}
protected function listTasks(): int
{
$query = Task::forWorkspace($this->workspaceId);
$status = $this->option('status');
if ($status && $status !== 'all') {
$query->where('status', $status);
} elseif (! $status) {
$query->active(); // Default: show active only
}
if ($category = $this->option('category')) {
$query->where('category', $category);
}
$tasks = $query->orderByRaw("FIELD(priority, 'urgent', 'high', 'normal', 'low')")
->orderByRaw("FIELD(status, 'in_progress', 'pending', 'done')")
->limit($this->option('limit'))
->get();
if ($tasks->isEmpty()) {
$this->info('No tasks found.');
return 0;
}
$this->newLine();
foreach ($tasks as $task) {
$line = sprintf(
' %s %s #%d %s',
$task->status_badge,
$task->priority_badge,
$task->id,
$task->title
);
if ($task->category) {
$line .= " [{$task->category}]";
}
if ($task->file_ref) {
$ref = basename($task->file_ref);
if ($task->line_ref) {
$ref .= ":{$task->line_ref}";
}
$line .= " ($ref)";
}
$this->line($line);
}
$this->newLine();
$pending = Task::forWorkspace($this->workspaceId)->pending()->count();
$inProgress = Task::forWorkspace($this->workspaceId)->inProgress()->count();
$this->comment(" {$pending} pending, {$inProgress} in progress");
return 0;
}
protected function addTask(): int
{
$title = $this->option('title');
if (! $title) {
$title = $this->ask('Task title');
}
if (! $title) {
$this->error('Title is required');
return 1;
}
$task = Task::create([
'workspace_id' => $this->workspaceId,
'title' => $title,
'description' => $this->option('desc'),
'priority' => $this->option('priority'),
'category' => $this->option('category'),
'file_ref' => $this->option('file'),
'line_ref' => $this->option('line'),
'status' => 'pending',
]);
$this->info("Created task #{$task->id}: {$task->title}");
return 0;
}
protected function completeTask(): int
{
$task = $this->findTask('complete');
if (! $task) {
return 1;
}
$task->update(['status' => 'done']);
$this->info("Completed: {$task->title}");
return 0;
}
protected function startTask(): int
{
$task = $this->findTask('start');
if (! $task) {
return 1;
}
$task->update(['status' => 'in_progress']);
$this->info("Started: {$task->title}");
return 0;
}
protected function removeTask(): int
{
$task = $this->findTask('remove');
if (! $task) {
return 1;
}
$title = $task->title;
$task->delete();
$this->info("Removed: {$title}");
return 0;
}
protected function showTask(): int
{
$task = $this->findTask('show');
if (! $task) {
return 1;
}
$this->newLine();
$this->line(" #{$task->id} {$task->title}");
$this->line(" Status: {$task->status}");
$this->line(" Priority: {$task->priority}");
if ($task->category) {
$this->line(" Category: {$task->category}");
}
if ($task->description) {
$this->newLine();
$this->line(" {$task->description}");
}
if ($task->file_ref) {
$this->newLine();
$ref = $task->file_ref;
if ($task->line_ref) {
$ref .= ":{$task->line_ref}";
}
$this->comment(" File: {$ref}");
}
$this->newLine();
$this->comment(" Created: {$task->created_at->diffForHumans()}");
return 0;
}
/**
* Find a task by ID, scoped to the current workspace.
*/
protected function findTask(string $action): ?Task
{
$id = $this->option('id');
if (! $id) {
$id = $this->ask("Task ID to {$action}");
}
if (! $id) {
$this->error('Task ID is required');
return null;
}
// Always scope by workspace to prevent data leakage
$task = Task::forWorkspace($this->workspaceId)->where('id', $id)->first();
if (! $task) {
$this->error("Task #{$id} not found");
return null;
}
return $task;
}
protected function showHelp(): int
{
$this->newLine();
$this->line(' Task Manager');
$this->newLine();
$this->line(' Usage:');
$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 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');
$this->line(' php artisan task remove --id=1 Remove a task');
$this->newLine();
$this->line(' Options:');
$this->line(' --workspace=ID Workspace ID (required if not authenticated)');
$this->line(' --priority=urgent|high|normal|low');
$this->line(' --category=feature|bug|task|docs');
$this->line(' --file=path/to/file.php --line=42');
$this->line(' --status=pending|in_progress|done|all');
$this->newLine();
return 0;
}
}