Files
claude-task-master/packages/tm-core/src/task-master-core.ts
Eyal Toledano fce841490a Tm start (#1200)
Co-authored-by: Max Tuzzolino <maxtuzz@Maxs-MacBook-Pro.local>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Max Tuzzolino <max.tuzsmith@gmail.com>
Co-authored-by: Ralph Khreish <35776126+Crunchyman-ralph@users.noreply.github.com>
2025-09-20 00:08:20 +02:00

316 lines
7.6 KiB
TypeScript

/**
* @fileoverview TaskMasterCore facade - main entry point for tm-core functionality
*/
import { ConfigManager } from './config/config-manager.js';
import {
TaskService,
type TaskListResult as ListTasksResult,
type GetTaskListOptions
} from './services/task-service.js';
import {
TaskExecutionService,
type StartTaskOptions,
type StartTaskResult,
type ConflictCheckResult
} from './services/task-execution-service.js';
import { ERROR_CODES, TaskMasterError } from './errors/task-master-error.js';
import type { IConfiguration } from './interfaces/configuration.interface.js';
import type {
Task,
TaskStatus,
TaskFilter,
StorageType
} from './types/index.js';
import {
ExecutorService,
type ExecutorServiceOptions,
type ExecutionResult,
type ExecutorType
} from './executors/index.js';
/**
* Options for creating TaskMasterCore instance
*/
export interface TaskMasterCoreOptions {
projectPath: string;
configuration?: Partial<IConfiguration>;
}
/**
* Re-export result types from services
*/
export type { TaskListResult as ListTasksResult } from './services/task-service.js';
export type { GetTaskListOptions } from './services/task-service.js';
export type {
StartTaskOptions,
StartTaskResult,
ConflictCheckResult
} from './services/task-execution-service.js';
/**
* TaskMasterCore facade class
* Provides simplified API for all tm-core operations
*/
export class TaskMasterCore {
private configManager: ConfigManager;
private taskService: TaskService;
private taskExecutionService: TaskExecutionService;
private executorService: ExecutorService | null = null;
/**
* Create and initialize a new TaskMasterCore instance
* This is the ONLY way to create a TaskMasterCore
*
* @param options - Configuration options for TaskMasterCore
* @returns Fully initialized TaskMasterCore instance
*/
static async create(options: TaskMasterCoreOptions): Promise<TaskMasterCore> {
const instance = new TaskMasterCore();
await instance.initialize(options);
return instance;
}
/**
* Private constructor - use TaskMasterCore.create() instead
* This ensures the TaskMasterCore is always properly initialized
*/
private constructor() {
// Services will be initialized in the initialize() method
this.configManager = null as any;
this.taskService = null as any;
this.taskExecutionService = null as any;
}
/**
* Initialize by loading services
* Private - only called by the factory method
*/
private async initialize(options: TaskMasterCoreOptions): Promise<void> {
if (!options.projectPath) {
throw new TaskMasterError(
'Project path is required',
ERROR_CODES.MISSING_CONFIGURATION
);
}
try {
// Create config manager using factory method
this.configManager = await ConfigManager.create(options.projectPath);
// Apply configuration overrides if provided
if (options.configuration) {
await this.configManager.updateConfig(options.configuration);
}
// Create task service
this.taskService = new TaskService(this.configManager);
await this.taskService.initialize();
// Create task execution service
this.taskExecutionService = new TaskExecutionService(this.taskService);
} catch (error) {
throw new TaskMasterError(
'Failed to initialize TaskMasterCore',
ERROR_CODES.INTERNAL_ERROR,
{ operation: 'initialize' },
error as Error
);
}
}
/**
* Get list of tasks with optional filtering
* @deprecated Use getTaskList() instead
*/
async listTasks(options?: {
tag?: string;
filter?: TaskFilter;
includeSubtasks?: boolean;
}): Promise<ListTasksResult> {
return this.getTaskList(options);
}
/**
* Get list of tasks with optional filtering
*/
async getTaskList(options?: GetTaskListOptions): Promise<ListTasksResult> {
return this.taskService.getTaskList(options);
}
/**
* Get a specific task by ID
*/
async getTask(taskId: string, tag?: string): Promise<Task | null> {
return this.taskService.getTask(taskId, tag);
}
/**
* Get tasks by status
*/
async getTasksByStatus(
status: TaskStatus | TaskStatus[],
tag?: string
): Promise<Task[]> {
return this.taskService.getTasksByStatus(status, tag);
}
/**
* Get task statistics
*/
async getTaskStats(tag?: string): Promise<{
total: number;
byStatus: Record<TaskStatus, number>;
withSubtasks: number;
blocked: number;
}> {
const stats = await this.taskService.getTaskStats(tag);
// Remove storageType from the return to maintain backward compatibility
const { storageType, ...restStats } = stats;
return restStats;
}
/**
* Get next available task
*/
async getNextTask(tag?: string): Promise<Task | null> {
return this.taskService.getNextTask(tag);
}
/**
* Get current storage type
*/
getStorageType(): StorageType {
return this.taskService.getStorageType();
}
/**
* Get current active tag
*/
getActiveTag(): string {
return this.configManager.getActiveTag();
}
/**
* Set active tag
*/
async setActiveTag(tag: string): Promise<void> {
await this.configManager.setActiveTag(tag);
}
// ==================== Task Execution Methods ====================
/**
* Start working on a task with comprehensive business logic
*/
async startTask(
taskId: string,
options: StartTaskOptions = {}
): Promise<StartTaskResult> {
return this.taskExecutionService.startTask(taskId, options);
}
/**
* Check if a task can be started (no conflicts)
*/
async canStartTask(taskId: string, force = false): Promise<boolean> {
return this.taskExecutionService.canStartTask(taskId, force);
}
/**
* Check for existing in-progress tasks and determine conflicts
*/
async checkInProgressConflicts(
targetTaskId: string
): Promise<ConflictCheckResult> {
return this.taskExecutionService.checkInProgressConflicts(targetTaskId);
}
/**
* Get task with subtask resolution
*/
async getTaskWithSubtask(
taskId: string
): Promise<{ task: Task | null; subtask?: any; subtaskId?: string }> {
return this.taskExecutionService.getTaskWithSubtask(taskId);
}
/**
* Get the next available task to start
*/
async getNextAvailableTask(): Promise<string | null> {
return this.taskExecutionService.getNextAvailableTask();
}
// ==================== Executor Service Methods ====================
/**
* Initialize executor service (lazy initialization)
*/
private getExecutorService(): ExecutorService {
if (!this.executorService) {
const executorOptions: ExecutorServiceOptions = {
projectRoot: this.configManager.getProjectRoot()
};
this.executorService = new ExecutorService(executorOptions);
}
return this.executorService;
}
/**
* Execute a task
*/
async executeTask(
task: Task,
executorType?: ExecutorType
): Promise<ExecutionResult> {
const executor = this.getExecutorService();
return executor.executeTask(task, executorType);
}
/**
* Stop the current task execution
*/
async stopCurrentTask(): Promise<void> {
if (this.executorService) {
await this.executorService.stopCurrentTask();
}
}
/**
* Update task status
*/
async updateTaskStatus(
taskId: string | number,
newStatus: TaskStatus,
tag?: string
): Promise<{
success: boolean;
oldStatus: TaskStatus;
newStatus: TaskStatus;
taskId: string;
}> {
return this.taskService.updateTaskStatus(taskId, newStatus, tag);
}
/**
* Close and cleanup resources
*/
async close(): Promise<void> {
// Stop any running executors
if (this.executorService) {
await this.executorService.stopCurrentTask();
}
// TaskService handles storage cleanup internally
}
}
/**
* Factory function to create TaskMasterCore instance
*/
export async function createTaskMasterCore(
options: TaskMasterCoreOptions
): Promise<TaskMasterCore> {
return TaskMasterCore.create(options);
}