feat(extension): complete VS Code extension with kanban board interface (#997)

---------
Co-authored-by: DavidMaliglowka <13022280+DavidMaliglowka@users.noreply.github.com>
Co-authored-by: Ralph Khreish <35776126+Crunchyman-ralph@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
This commit is contained in:
DavidMaliglowka
2025-08-01 07:04:22 -05:00
committed by GitHub
parent 60c03c548d
commit 64302dc191
101 changed files with 20608 additions and 181 deletions

View File

@@ -0,0 +1,168 @@
/**
* Task Repository - Simplified version
* Handles data access with caching
*/
import { EventEmitter } from '../utils/event-emitter';
import type { ExtensionLogger } from '../utils/logger';
import type { TaskMasterApi, TaskMasterTask } from '../utils/task-master-api';
// Use the TaskMasterTask type directly to ensure compatibility
export type Task = TaskMasterTask;
export class TaskRepository extends EventEmitter {
private cache: Task[] | null = null;
private cacheTimestamp = 0;
private readonly CACHE_DURATION = 30000; // 30 seconds
constructor(
private api: TaskMasterApi,
private logger: ExtensionLogger
) {
super();
}
async getAll(options?: {
tag?: string;
withSubtasks?: boolean;
}): Promise<Task[]> {
// If a tag is specified, always fetch fresh data
const shouldUseCache =
!options?.tag &&
this.cache &&
Date.now() - this.cacheTimestamp < this.CACHE_DURATION;
if (shouldUseCache) {
return this.cache || [];
}
try {
const result = await this.api.getTasks({
withSubtasks: options?.withSubtasks ?? true,
tag: options?.tag
});
if (result.success && result.data) {
this.cache = result.data;
this.cacheTimestamp = Date.now();
this.emit('tasks:updated', result.data);
return result.data;
}
throw new Error(result.error || 'Failed to fetch tasks');
} catch (error) {
this.logger.error('Failed to get tasks', error);
throw error;
}
}
async getById(taskId: string): Promise<Task | null> {
// First check cache
if (this.cache) {
// Handle both main tasks and subtasks
for (const task of this.cache) {
if (task.id === taskId) {
return task;
}
// Check subtasks
if (task.subtasks) {
for (const subtask of task.subtasks) {
if (
subtask.id.toString() === taskId ||
`${task.id}.${subtask.id}` === taskId
) {
return {
...subtask,
id: subtask.id.toString(),
description: subtask.description || '',
status: (subtask.status ||
'pending') as TaskMasterTask['status'],
priority: 'medium' as const,
dependencies:
subtask.dependencies?.map((d) => d.toString()) || []
};
}
}
}
}
}
// If not in cache, fetch all and search
const tasks = await this.getAll();
for (const task of tasks) {
if (task.id === taskId) {
return task;
}
// Check subtasks
if (task.subtasks) {
for (const subtask of task.subtasks) {
if (
subtask.id.toString() === taskId ||
`${task.id}.${subtask.id}` === taskId
) {
return {
...subtask,
id: subtask.id.toString(),
description: subtask.description || '',
status: (subtask.status || 'pending') as TaskMasterTask['status'],
priority: 'medium' as const,
dependencies: subtask.dependencies?.map((d) => d.toString()) || []
};
}
}
}
}
return null;
}
async updateStatus(taskId: string, status: Task['status']): Promise<void> {
try {
const result = await this.api.updateTaskStatus(taskId, status);
if (!result.success) {
throw new Error(result.error || 'Failed to update status');
}
// Invalidate cache
this.cache = null;
// Fetch updated tasks
await this.getAll();
} catch (error) {
this.logger.error('Failed to update task status', error);
throw error;
}
}
async updateContent(taskId: string, updates: any): Promise<void> {
try {
const result = await this.api.updateTask(taskId, updates, {
append: false,
research: false
});
if (!result.success) {
throw new Error(result.error || 'Failed to update task');
}
// Invalidate cache
this.cache = null;
// Fetch updated tasks
await this.getAll();
} catch (error) {
this.logger.error('Failed to update task content', error);
throw error;
}
}
async refresh(): Promise<void> {
this.cache = null;
await this.getAll();
}
isConnected(): boolean {
return this.api.getConnectionStatus().isConnected;
}
}