From fb44c58a23732d423fb574f221e2e81a44449336 Mon Sep 17 00:00:00 2001 From: Ralph Khreish <35776126+Crunchyman-ralph@users.noreply.github.com> Date: Tue, 26 Aug 2025 21:10:28 +0200 Subject: [PATCH] feat: implement tm list with new refactored structure --- .vscode/settings.json | 36 ++-- apps/cli/src/commands/list.command.ts | 6 +- apps/cli/src/utils/ui.ts | 2 +- package.json | 5 +- packages/tm-core/package.json | 31 +--- .../tm-core/src/config/config-manager.spec.ts | 166 ++++++++++-------- packages/tm-core/src/config/config-manager.ts | 2 +- packages/tm-core/src/entities/task.entity.ts | 27 ++- packages/tm-core/src/index.ts | 26 ++- packages/tm-core/src/services/task-service.ts | 18 +- packages/tm-core/src/storage/file-storage.ts | 30 +++- packages/tm-core/src/task-master-core.ts | 89 +++++----- tsup.config.ts | 1 + 13 files changed, 228 insertions(+), 211 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 714021b1..904bf717 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,15 +1,27 @@ { - "json.schemas": [ - { - "fileMatch": ["src/prompts/*.json"], - "url": "./src/prompts/schemas/prompt-template.schema.json" - } - ], - "files.associations": { - "src/prompts/*.json": "json" - }, + "json.schemas": [ + { + "fileMatch": ["src/prompts/*.json"], + "url": "./src/prompts/schemas/prompt-template.schema.json" + } + ], + "files.associations": { + "src/prompts/*.json": "json" + }, - "json.format.enable": true, - "json.validate.enable": true, - "typescript.tsdk": "node_modules/typescript/lib" + "json.format.enable": true, + "json.validate.enable": true, + "typescript.tsdk": "node_modules/typescript/lib", + "[typescript]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[typescriptreact]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[javascript]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[json]": { + "editor.defaultFormatter": "biomejs.biome" + } } diff --git a/apps/cli/src/commands/list.command.ts b/apps/cli/src/commands/list.command.ts index bdc5e044..65b61d6d 100644 --- a/apps/cli/src/commands/list.command.ts +++ b/apps/cli/src/commands/list.command.ts @@ -24,7 +24,7 @@ export interface ListCommandOptions { status?: string; tag?: string; withSubtasks?: boolean; - format?: string; + format?: OutputFormat; silent?: boolean; project?: string; } @@ -73,6 +73,7 @@ export class ListTasksCommand extends Command { * Execute the list command */ private async executeCommand(options: ListCommandOptions): Promise { + console.log('executeCommand', options); try { // Validate options if (!this.validateOptions(options)) { @@ -138,8 +139,7 @@ export class ListTasksCommand extends Command { */ private async initializeCore(projectRoot: string): Promise { if (!this.tmCore) { - this.tmCore = createTaskMasterCore(projectRoot); - await this.tmCore.initialize(); + this.tmCore = await createTaskMasterCore({ projectPath: projectRoot }); } } diff --git a/apps/cli/src/utils/ui.ts b/apps/cli/src/utils/ui.ts index 66fab733..fe1e5897 100644 --- a/apps/cli/src/utils/ui.ts +++ b/apps/cli/src/utils/ui.ts @@ -266,7 +266,7 @@ export function createTaskTable( if (showSubtasks && task.subtasks && task.subtasks.length > 0) { task.subtasks.forEach((subtask) => { const subRow: string[] = [ - chalk.gray(` └─ ${task.id}.${subtask.id}`), + chalk.gray(` └─ ${subtask.id}`), chalk.gray(truncate(subtask.title, 36)), getStatusWithColor(subtask.status), chalk.gray(subtask.priority || 'medium') diff --git a/package.json b/package.json index d768edea..6fd8987e 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,10 @@ "workspaces": ["apps/*", "packages/*", "."], "scripts": { "build": "npm run build:packages && tsup", - "dev": "tsup --watch --onSuccess 'echo Build complete'", + "dev": "npm run build:packages && (npm run dev:packages & tsup --watch --onSuccess 'echo Build complete')", + "dev:packages": "(cd packages/tm-core && npm run dev) & (cd apps/cli && npm run dev) & wait", + "dev:core": "cd packages/tm-core && npm run dev", + "dev:cli": "cd apps/cli && npm run dev", "build:packages": "npm run build:core && npm run build:cli", "build:core": "cd packages/tm-core && npm run build", "build:cli": "cd apps/cli && npm run build", diff --git a/packages/tm-core/package.json b/packages/tm-core/package.json index 28bf1091..e1f6aceb 100644 --- a/packages/tm-core/package.json +++ b/packages/tm-core/package.json @@ -8,39 +8,10 @@ "types": "./dist/index.d.ts", "exports": { ".": { + "development": "./src/index.ts", "types": "./dist/index.d.ts", "import": "./dist/index.js", "require": "./dist/index.cjs" - }, - "./types": { - "types": "./dist/types/index.d.ts", - "import": "./dist/types/index.js", - "require": "./dist/types/index.cjs" - }, - "./providers": { - "types": "./dist/providers/index.d.ts", - "import": "./dist/providers/index.js", - "require": "./dist/providers/index.cjs" - }, - "./storage": { - "types": "./dist/storage/index.d.ts", - "import": "./dist/storage/index.js", - "require": "./dist/storage/index.cjs" - }, - "./parser": { - "types": "./dist/parser/index.d.ts", - "import": "./dist/parser/index.js", - "require": "./dist/parser/index.cjs" - }, - "./utils": { - "types": "./dist/utils/index.d.ts", - "import": "./dist/utils/index.js", - "require": "./dist/utils/index.cjs" - }, - "./errors": { - "types": "./dist/errors/index.d.ts", - "import": "./dist/errors/index.js", - "require": "./dist/errors/index.cjs" } }, "scripts": { diff --git a/packages/tm-core/src/config/config-manager.spec.ts b/packages/tm-core/src/config/config-manager.spec.ts index 9c9be78d..95ef17d6 100644 --- a/packages/tm-core/src/config/config-manager.spec.ts +++ b/packages/tm-core/src/config/config-manager.spec.ts @@ -25,65 +25,80 @@ describe('ConfigManager', () => { beforeEach(async () => { vi.clearAllMocks(); - + // Clear environment variables - Object.keys(process.env).forEach(key => { + Object.keys(process.env).forEach((key) => { if (key.startsWith('TASKMASTER_')) { delete process.env[key]; } }); // Setup default mock behaviors - vi.mocked(ConfigLoader).mockImplementation(() => ({ - getDefaultConfig: vi.fn().mockReturnValue({ - models: { main: 'default-model', fallback: 'fallback-model' }, - storage: { type: 'file' }, - version: '1.0.0' - }), - loadLocalConfig: vi.fn().mockResolvedValue(null), - loadGlobalConfig: vi.fn().mockResolvedValue(null), - hasLocalConfig: vi.fn().mockResolvedValue(false), - hasGlobalConfig: vi.fn().mockResolvedValue(false) - } as any)); + vi.mocked(ConfigLoader).mockImplementation( + () => + ({ + getDefaultConfig: vi.fn().mockReturnValue({ + models: { main: 'default-model', fallback: 'fallback-model' }, + storage: { type: 'file' }, + version: '1.0.0' + }), + loadLocalConfig: vi.fn().mockResolvedValue(null), + loadGlobalConfig: vi.fn().mockResolvedValue(null), + hasLocalConfig: vi.fn().mockResolvedValue(false), + hasGlobalConfig: vi.fn().mockResolvedValue(false) + }) as any + ); - vi.mocked(ConfigMerger).mockImplementation(() => ({ - addSource: vi.fn(), - clearSources: vi.fn(), - merge: vi.fn().mockReturnValue({ - models: { main: 'merged-model', fallback: 'fallback-model' }, - storage: { type: 'file' } - }), - getSources: vi.fn().mockReturnValue([]), - hasSource: vi.fn().mockReturnValue(false), - removeSource: vi.fn().mockReturnValue(false) - } as any)); + vi.mocked(ConfigMerger).mockImplementation( + () => + ({ + addSource: vi.fn(), + clearSources: vi.fn(), + merge: vi.fn().mockReturnValue({ + models: { main: 'merged-model', fallback: 'fallback-model' }, + storage: { type: 'file' } + }), + getSources: vi.fn().mockReturnValue([]), + hasSource: vi.fn().mockReturnValue(false), + removeSource: vi.fn().mockReturnValue(false) + }) as any + ); - vi.mocked(RuntimeStateManager).mockImplementation(() => ({ - loadState: vi.fn().mockResolvedValue({ activeTag: 'master' }), - saveState: vi.fn().mockResolvedValue(undefined), - getActiveTag: vi.fn().mockReturnValue('master'), - setActiveTag: vi.fn().mockResolvedValue(undefined), - getState: vi.fn().mockReturnValue({ activeTag: 'master' }), - updateMetadata: vi.fn().mockResolvedValue(undefined), - clearState: vi.fn().mockResolvedValue(undefined) - } as any)); + vi.mocked(RuntimeStateManager).mockImplementation( + () => + ({ + loadState: vi.fn().mockResolvedValue({ activeTag: 'master' }), + saveState: vi.fn().mockResolvedValue(undefined), + getActiveTag: vi.fn().mockReturnValue('master'), + setActiveTag: vi.fn().mockResolvedValue(undefined), + getState: vi.fn().mockReturnValue({ activeTag: 'master' }), + updateMetadata: vi.fn().mockResolvedValue(undefined), + clearState: vi.fn().mockResolvedValue(undefined) + }) as any + ); - vi.mocked(ConfigPersistence).mockImplementation(() => ({ - saveConfig: vi.fn().mockResolvedValue(undefined), - configExists: vi.fn().mockResolvedValue(false), - deleteConfig: vi.fn().mockResolvedValue(undefined), - getBackups: vi.fn().mockResolvedValue([]), - restoreFromBackup: vi.fn().mockResolvedValue(undefined) - } as any)); + vi.mocked(ConfigPersistence).mockImplementation( + () => + ({ + saveConfig: vi.fn().mockResolvedValue(undefined), + configExists: vi.fn().mockResolvedValue(false), + deleteConfig: vi.fn().mockResolvedValue(undefined), + getBackups: vi.fn().mockResolvedValue([]), + restoreFromBackup: vi.fn().mockResolvedValue(undefined) + }) as any + ); - vi.mocked(EnvironmentConfigProvider).mockImplementation(() => ({ - loadConfig: vi.fn().mockReturnValue({}), - getRuntimeState: vi.fn().mockReturnValue({}), - hasEnvVar: vi.fn().mockReturnValue(false), - getAllTaskmasterEnvVars: vi.fn().mockReturnValue({}), - addMapping: vi.fn(), - getMappings: vi.fn().mockReturnValue([]) - } as any)); + vi.mocked(EnvironmentConfigProvider).mockImplementation( + () => + ({ + loadConfig: vi.fn().mockReturnValue({}), + getRuntimeState: vi.fn().mockReturnValue({}), + hasEnvVar: vi.fn().mockReturnValue(false), + getAllTaskmasterEnvVars: vi.fn().mockReturnValue({}), + addMapping: vi.fn(), + getMappings: vi.fn().mockReturnValue([]) + }) as any + ); // Since constructor is private, we need to use the factory method // But for testing, we'll create a test instance using create() @@ -167,20 +182,23 @@ describe('ConfigManager', () => { it('should return API storage configuration when configured', async () => { // Create a new instance with API storage config - vi.mocked(ConfigMerger).mockImplementationOnce(() => ({ - addSource: vi.fn(), - clearSources: vi.fn(), - merge: vi.fn().mockReturnValue({ - storage: { - type: 'api', - apiEndpoint: 'https://api.example.com', - apiAccessToken: 'token123' - } - }), - getSources: vi.fn().mockReturnValue([]), - hasSource: vi.fn().mockReturnValue(false), - removeSource: vi.fn().mockReturnValue(false) - } as any)); + vi.mocked(ConfigMerger).mockImplementationOnce( + () => + ({ + addSource: vi.fn(), + clearSources: vi.fn(), + merge: vi.fn().mockReturnValue({ + storage: { + type: 'api', + apiEndpoint: 'https://api.example.com', + apiAccessToken: 'token123' + } + }), + getSources: vi.fn().mockReturnValue([]), + hasSource: vi.fn().mockReturnValue(false), + removeSource: vi.fn().mockReturnValue(false) + }) as any + ); const apiManager = await ConfigManager.create(testProjectRoot); @@ -271,7 +289,9 @@ describe('ConfigManager', () => { // Manager is already initialized in the main beforeEach it('should update configuration and save', async () => { - const updates = { models: { main: 'new-model', fallback: 'fallback-model' } }; + const updates = { + models: { main: 'new-model', fallback: 'fallback-model' } + }; await manager.updateConfig(updates); const persistence = (manager as any).persistence; @@ -302,10 +322,10 @@ describe('ConfigManager', () => { await manager.saveConfig(); const persistence = (manager as any).persistence; - expect(persistence.saveConfig).toHaveBeenCalledWith( - expect.any(Object), - { createBackup: true, atomic: true } - ); + expect(persistence.saveConfig).toHaveBeenCalledWith(expect.any(Object), { + createBackup: true, + atomic: true + }); }); }); @@ -347,9 +367,11 @@ describe('ConfigManager', () => { const unsubscribe = manager.watch(callback); - expect(warnSpy).toHaveBeenCalledWith('Configuration watching not yet implemented'); + expect(warnSpy).toHaveBeenCalledWith( + 'Configuration watching not yet implemented' + ); expect(unsubscribe).toBeInstanceOf(Function); - + // Calling unsubscribe should not throw expect(() => unsubscribe()).not.toThrow(); @@ -364,7 +386,9 @@ describe('ConfigManager', () => { loader.loadLocalConfig.mockRejectedValue(new Error('File error')); // Creating a new manager should not throw even if service fails - await expect(ConfigManager.create(testProjectRoot)).resolves.not.toThrow(); + await expect( + ConfigManager.create(testProjectRoot) + ).resolves.not.toThrow(); }); }); -}); \ No newline at end of file +}); diff --git a/packages/tm-core/src/config/config-manager.ts b/packages/tm-core/src/config/config-manager.ts index 1749b4ce..a0105ec6 100644 --- a/packages/tm-core/src/config/config-manager.ts +++ b/packages/tm-core/src/config/config-manager.ts @@ -41,7 +41,7 @@ export class ConfigManager { /** * Create and initialize a new ConfigManager instance * This is the ONLY way to create a ConfigManager - * + * * @param projectRoot - The root directory of the project * @returns Fully initialized ConfigManager instance */ diff --git a/packages/tm-core/src/entities/task.entity.ts b/packages/tm-core/src/entities/task.entity.ts index 89fcfa41..724ab6b9 100644 --- a/packages/tm-core/src/entities/task.entity.ts +++ b/packages/tm-core/src/entities/task.entity.ts @@ -34,18 +34,25 @@ export class TaskEntity implements Task { assignee?: string; complexity?: Task['complexity']; - constructor(data: Task) { + constructor(data: Task | (Omit & { id: number | string })) { this.validate(data); - this.id = data.id; + // Always convert ID to string + this.id = String(data.id); this.title = data.title; this.description = data.description; this.status = data.status; this.priority = data.priority; - this.dependencies = data.dependencies || []; + // Ensure dependency IDs are also strings + this.dependencies = (data.dependencies || []).map((dep) => String(dep)); this.details = data.details; this.testStrategy = data.testStrategy; - this.subtasks = data.subtasks || []; + // Normalize subtask IDs to strings + this.subtasks = (data.subtasks || []).map((subtask) => ({ + ...subtask, + id: Number(subtask.id), // Keep subtask IDs as numbers per interface + parentId: String(subtask.parentId) + })); // Optional properties this.createdAt = data.createdAt; @@ -60,10 +67,16 @@ export class TaskEntity implements Task { /** * Validate task data */ - private validate(data: Partial): void { - if (!data.id || typeof data.id !== 'string') { + private validate( + data: Partial | Partial & { id: number | string }> + ): void { + if ( + data.id === undefined || + data.id === null || + (typeof data.id !== 'string' && typeof data.id !== 'number') + ) { throw new TaskMasterError( - 'Task ID is required and must be a string', + 'Task ID is required and must be a string or number', ERROR_CODES.VALIDATION_ERROR ); } diff --git a/packages/tm-core/src/index.ts b/packages/tm-core/src/index.ts index 2883131a..6a83c683 100644 --- a/packages/tm-core/src/index.ts +++ b/packages/tm-core/src/index.ts @@ -9,19 +9,19 @@ export { createTaskMasterCore, type TaskMasterCoreOptions, type ListTasksResult -} from './task-master-core.js'; +} from './task-master-core'; // Re-export types -export type * from './types/index'; +export type * from './types'; // Re-export interfaces (types only to avoid conflicts) -export type * from './interfaces/index'; +export type * from './interfaces'; // Re-export constants -export * from './constants/index'; +export * from './constants'; // Re-export providers -export * from './providers/index'; +export * from './providers'; // Re-export storage (selectively to avoid conflicts) export { @@ -29,21 +29,17 @@ export { ApiStorage, StorageFactory, type ApiStorageConfig -} from './storage/index'; -export { PlaceholderStorage, type StorageAdapter } from './storage/index'; +} from './storage'; +export { PlaceholderStorage, type StorageAdapter } from './storage'; // Re-export parser -export * from './parser/index'; +export * from './parser'; // Re-export utilities -export * from './utils/index'; +export * from './utils'; // Re-export errors -export * from './errors/index'; +export * from './errors'; // Re-export entities -export { TaskEntity } from './entities/task.entity.js'; - -// Package metadata -export const version = '1.0.0'; -export const name = '@task-master/tm-core'; +export { TaskEntity } from './entities/task.entity'; diff --git a/packages/tm-core/src/services/task-service.ts b/packages/tm-core/src/services/task-service.ts index df523859..a6a71859 100644 --- a/packages/tm-core/src/services/task-service.ts +++ b/packages/tm-core/src/services/task-service.ts @@ -60,9 +60,6 @@ export class TaskService { async initialize(): Promise { if (this.initialized) return; - // Ensure config manager is initialized - await this.configManager.initialize(); - // Create storage based on configuration const storageConfig = this.configManager.getStorageConfig(); const projectRoot = this.configManager.getProjectRoot(); @@ -83,7 +80,7 @@ export class TaskService { * This is the main method that retrieves tasks from storage and applies filters */ async getTaskList(options: GetTaskListOptions = {}): Promise { - await this.ensureInitialized(); + console.log('getTaskList', options); // Determine which tag to use const activeTag = this.configManager.getActiveTag(); @@ -113,13 +110,15 @@ export class TaskService { })); } + console.log('tasks', tasks); + return { tasks, total: rawTasks.length, filtered: filteredEntities.length, tag: options.tag, // Only include tag if explicitly provided storageType: this.configManager.getStorageConfig().type - } as TaskListResult; + }; } catch (error) { throw new TaskMasterError( 'Failed to get task list', @@ -336,15 +335,6 @@ export class TaskService { }); } - /** - * Ensure service is initialized - */ - private async ensureInitialized(): Promise { - if (!this.initialized) { - await this.initialize(); - } - } - /** * Get current storage type */ diff --git a/packages/tm-core/src/storage/file-storage.ts b/packages/tm-core/src/storage/file-storage.ts index a6fbcf29..2f8ceed2 100644 --- a/packages/tm-core/src/storage/file-storage.ts +++ b/packages/tm-core/src/storage/file-storage.ts @@ -127,6 +127,18 @@ export class FileStorage implements IStorage { // Ensure directory exists await this.ensureDirectoryExists(); + // Normalize task IDs to strings (force string IDs everywhere) + const normalizedTasks = tasks.map(task => ({ + ...task, + id: String(task.id), // Force ID to string + dependencies: task.dependencies?.map(dep => String(dep)) || [], + subtasks: task.subtasks?.map(subtask => ({ + ...subtask, + id: String(subtask.id), + parentId: String(subtask.parentId) + })) || [] + })); + // Check if we need to use legacy format let dataToWrite: any; @@ -140,12 +152,12 @@ export class FileStorage implements IStorage { ) { dataToWrite = { [resolvedTag]: { - tasks, + tasks: normalizedTasks, metadata: { version: '1.0.0', lastModified: new Date().toISOString(), - taskCount: tasks.length, - completedCount: tasks.filter((t) => t.status === 'done').length, + taskCount: normalizedTasks.length, + completedCount: normalizedTasks.filter((t) => t.status === 'done').length, tags: [resolvedTag] } } @@ -153,12 +165,12 @@ export class FileStorage implements IStorage { } else { // Use standard format for new files dataToWrite = { - tasks, + tasks: normalizedTasks, metadata: { version: '1.0.0', lastModified: new Date().toISOString(), - taskCount: tasks.length, - completedCount: tasks.filter((t) => t.status === 'done').length, + taskCount: normalizedTasks.length, + completedCount: normalizedTasks.filter((t) => t.status === 'done').length, tags: tag ? [tag] : [] } }; @@ -166,12 +178,12 @@ export class FileStorage implements IStorage { } catch (error: any) { // File doesn't exist, use standard format dataToWrite = { - tasks, + tasks: normalizedTasks, metadata: { version: '1.0.0', lastModified: new Date().toISOString(), - taskCount: tasks.length, - completedCount: tasks.filter((t) => t.status === 'done').length, + taskCount: normalizedTasks.length, + completedCount: normalizedTasks.filter((t) => t.status === 'done').length, tags: tag ? [tag] : [] } }; diff --git a/packages/tm-core/src/task-master-core.ts b/packages/tm-core/src/task-master-core.ts index 2a07061d..38e89b5f 100644 --- a/packages/tm-core/src/task-master-core.ts +++ b/packages/tm-core/src/task-master-core.ts @@ -33,9 +33,35 @@ export type { GetTaskListOptions } from './services/task-service.js'; export class TaskMasterCore { private configManager: ConfigManager; private taskService: TaskService; - private initialized = false; - constructor(options: TaskMasterCoreOptions) { + /** + * 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 { + 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; + } + + /** + * Initialize by loading services + * Private - only called by the factory method + */ + private async initialize(options: TaskMasterCoreOptions): Promise { if (!options.projectPath) { throw new TaskMasterError( 'Project path is required', @@ -43,28 +69,18 @@ export class TaskMasterCore { ); } - // Create config manager - this.configManager = new ConfigManager(options.projectPath); - - // Create task service - this.taskService = new TaskService(this.configManager); - - // Apply any provided configuration - if (options.configuration) { - // This will be applied after initialization - } - } - - /** - * Initialize the TaskMasterCore instance - */ - async initialize(): Promise { - if (this.initialized) return; - try { - await this.configManager.initialize(); + // 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(); - this.initialized = true; } catch (error) { throw new TaskMasterError( 'Failed to initialize TaskMasterCore', @@ -75,15 +91,6 @@ export class TaskMasterCore { } } - /** - * Ensure the instance is initialized - */ - private async ensureInitialized(): Promise { - if (!this.initialized) { - await this.initialize(); - } - } - /** * Get list of tasks with optional filtering * @deprecated Use getTaskList() instead @@ -100,7 +107,6 @@ export class TaskMasterCore { * Get list of tasks with optional filtering */ async getTaskList(options?: GetTaskListOptions): Promise { - await this.ensureInitialized(); return this.taskService.getTaskList(options); } @@ -108,7 +114,6 @@ export class TaskMasterCore { * Get a specific task by ID */ async getTask(taskId: string, tag?: string): Promise { - await this.ensureInitialized(); return this.taskService.getTask(taskId, tag); } @@ -119,7 +124,6 @@ export class TaskMasterCore { status: TaskStatus | TaskStatus[], tag?: string ): Promise { - await this.ensureInitialized(); return this.taskService.getTasksByStatus(status, tag); } @@ -132,7 +136,6 @@ export class TaskMasterCore { withSubtasks: number; blocked: number; }> { - await this.ensureInitialized(); const stats = await this.taskService.getTaskStats(tag); // Remove storageType from the return to maintain backward compatibility const { storageType, ...restStats } = stats; @@ -143,7 +146,6 @@ export class TaskMasterCore { * Get next available task */ async getNextTask(tag?: string): Promise { - await this.ensureInitialized(); return this.taskService.getNextTask(tag); } @@ -173,21 +175,14 @@ export class TaskMasterCore { */ async close(): Promise { // TaskService handles storage cleanup internally - this.initialized = false; } } /** * Factory function to create TaskMasterCore instance */ -export function createTaskMasterCore( - projectPath: string, - options?: { - configuration?: Partial; - } -): TaskMasterCore { - return new TaskMasterCore({ - projectPath, - configuration: options?.configuration - }); +export async function createTaskMasterCore( + options: TaskMasterCoreOptions +): Promise { + return TaskMasterCore.create(options); } diff --git a/tsup.config.ts b/tsup.config.ts index fbc21754..83f2ab59 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -11,6 +11,7 @@ export default defineConfig({ sourcemap: true, clean: true, shims: true, + dts: true, bundle: true, // Bundle everything into one file outDir: 'dist', // Handle TypeScript imports transparently