From ca4d93ee6afe181f2807c84dd6590e2f1f4d6e11 Mon Sep 17 00:00:00 2001 From: Ralph Khreish <35776126+Crunchyman-ralph@users.noreply.github.com> Date: Wed, 6 Aug 2025 11:10:29 +0300 Subject: [PATCH] chore: prepare prd for tm-core creation phase 1 (#1045) --- .taskmaster/docs/tm-core-phase-1.txt | 343 +++++++++++++++++++++++++++ 1 file changed, 343 insertions(+) create mode 100644 .taskmaster/docs/tm-core-phase-1.txt diff --git a/.taskmaster/docs/tm-core-phase-1.txt b/.taskmaster/docs/tm-core-phase-1.txt new file mode 100644 index 00000000..f64a23fa --- /dev/null +++ b/.taskmaster/docs/tm-core-phase-1.txt @@ -0,0 +1,343 @@ +# Product Requirements Document: tm-core Package - Parse PRD Feature + +## Project Overview +Create a TypeScript package named `tm-core` at `packages/tm-core` that implements parse-prd functionality using class-based architecture similar to the existing AI providers pattern. + +## Design Patterns & Architecture + +### Patterns to Apply +1. **Factory Pattern**: Use for `ProviderFactory` to create AI provider instances +2. **Strategy Pattern**: Use for `IAIProvider` implementations and `IStorage` implementations +3. **Facade Pattern**: Use for `TaskMasterCore` as the main API entry point +4. **Template Method Pattern**: Use for `BaseProvider` abstract class +5. **Dependency Injection**: Use throughout for testability (pass dependencies via constructor) +6. **Repository Pattern**: Use for `FileStorage` to abstract data persistence + +### Naming Conventions +- **Files**: kebab-case (e.g., `task-parser.ts`, `file-storage.ts`) +- **Classes**: PascalCase (e.g., `TaskParser`, `FileStorage`) +- **Interfaces**: PascalCase with 'I' prefix (e.g., `IStorage`, `IAIProvider`) +- **Methods**: camelCase (e.g., `parsePRD`, `loadTasks`) +- **Constants**: UPPER_SNAKE_CASE (e.g., `DEFAULT_MODEL`) +- **Type aliases**: PascalCase (e.g., `TaskStatus`, `ParseOptions`) + +## Exact Folder Structure Required +``` +packages/tm-core/ +├── src/ +│ ├── index.ts +│ ├── types/ +│ │ └── index.ts +│ ├── interfaces/ +│ │ ├── index.ts # Barrel export +│ │ ├── storage.interface.ts +│ │ ├── ai-provider.interface.ts +│ │ └── configuration.interface.ts +│ ├── tasks/ +│ │ ├── index.ts # Barrel export +│ │ └── task-parser.ts +│ ├── ai/ +│ │ ├── index.ts # Barrel export +│ │ ├── base-provider.ts +│ │ ├── provider-factory.ts +│ │ ├── prompt-builder.ts +│ │ └── providers/ +│ │ ├── index.ts # Barrel export +│ │ ├── anthropic-provider.ts +│ │ ├── openai-provider.ts +│ │ └── google-provider.ts +│ ├── storage/ +│ │ ├── index.ts # Barrel export +│ │ └── file-storage.ts +│ ├── config/ +│ │ ├── index.ts # Barrel export +│ │ └── config-manager.ts +│ ├── utils/ +│ │ ├── index.ts # Barrel export +│ │ └── id-generator.ts +│ └── errors/ +│ ├── index.ts # Barrel export +│ └── task-master-error.ts +├── tests/ +│ ├── task-parser.test.ts +│ ├── integration/ +│ │ └── parse-prd.test.ts +│ └── mocks/ +│ └── mock-provider.ts +├── package.json +├── tsconfig.json +├── tsup.config.js +└── jest.config.js +``` + +## Specific Implementation Requirements + +### 1. Create types/index.ts +Define these exact TypeScript interfaces: +- `Task` interface with fields: id, title, description, status, priority, complexity, dependencies, subtasks, metadata, createdAt, updatedAt, source +- `Subtask` interface with fields: id, title, description, completed +- `TaskMetadata` interface with fields: parsedFrom, aiProvider, version, tags (optional) +- Type literals: `TaskStatus` = 'pending' | 'in-progress' | 'completed' | 'blocked' +- Type literals: `TaskPriority` = 'low' | 'medium' | 'high' | 'critical' +- Type literals: `TaskComplexity` = 'simple' | 'moderate' | 'complex' +- `ParseOptions` interface with fields: dryRun (optional), additionalContext (optional), tag (optional), maxTasks (optional) + +### 2. Create interfaces/storage.interface.ts +Define `IStorage` interface with these exact methods: +- `loadTasks(tag?: string): Promise` +- `saveTasks(tasks: Task[], tag?: string): Promise` +- `appendTasks(tasks: Task[], tag?: string): Promise` +- `updateTask(id: string, task: Partial, tag?: string): Promise` +- `deleteTask(id: string, tag?: string): Promise` +- `exists(tag?: string): Promise` + +### 3. Create interfaces/ai-provider.interface.ts +Define `IAIProvider` interface with these exact methods: +- `generateCompletion(prompt: string, options?: AIOptions): Promise` +- `calculateTokens(text: string): number` +- `getName(): string` +- `getModel(): string` + +Define `AIOptions` interface with fields: temperature (optional), maxTokens (optional), systemPrompt (optional) + +### 4. Create interfaces/configuration.interface.ts +Define `IConfiguration` interface with fields: +- `projectPath: string` +- `aiProvider: string` +- `apiKey?: string` +- `aiOptions?: AIOptions` +- `mainModel?: string` +- `researchModel?: string` +- `fallbackModel?: string` +- `tasksPath?: string` +- `enableTags?: boolean` + +### 5. Create tasks/task-parser.ts +Create class `TaskParser` with: +- Constructor accepting `aiProvider: IAIProvider` and `config: IConfiguration` +- Private property `promptBuilder: PromptBuilder` +- Public method `parsePRD(prdPath: string, options: ParseOptions = {}): Promise` +- Private method `readPRD(prdPath: string): Promise` +- Private method `extractTasks(aiResponse: string): Partial[]` +- Private method `enrichTasks(rawTasks: Partial[], prdPath: string): Task[]` +- Apply **Dependency Injection** pattern via constructor + +### 6. Create ai/base-provider.ts +Copy existing base-provider.js and convert to TypeScript abstract class: +- Abstract class `BaseProvider` implementing `IAIProvider` +- Protected properties: `apiKey: string`, `model: string` +- Constructor accepting `apiKey: string` and `options: { model?: string }` +- Abstract methods matching IAIProvider interface +- Abstract method `getDefaultModel(): string` +- Apply **Template Method** pattern for common provider logic + +### 7. Create ai/provider-factory.ts +Create class `ProviderFactory` with: +- Static method `create(config: { provider: string; apiKey?: string; model?: string }): Promise` +- Switch statement for providers: 'anthropic', 'openai', 'google' +- Dynamic imports for each provider +- Throw error for unknown providers +- Apply **Factory** pattern for creating provider instances + +Example implementation structure: +```typescript +switch (provider.toLowerCase()) { + case 'anthropic': + const { AnthropicProvider } = await import('./providers/anthropic-provider.js'); + return new AnthropicProvider(apiKey, { model }); +} +``` + +### 8. Create ai/providers/anthropic-provider.ts +Create class `AnthropicProvider` extending `BaseProvider`: +- Import Anthropic SDK: `import { Anthropic } from '@anthropic-ai/sdk'` +- Private property `client: Anthropic` +- Implement all abstract methods from BaseProvider +- Default model: 'claude-3-sonnet-20240229' +- Handle API errors and wrap with meaningful messages + +### 9. Create ai/providers/openai-provider.ts (placeholder) +Create class `OpenAIProvider` extending `BaseProvider`: +- Import OpenAI SDK when implemented +- For now, throw error: "OpenAI provider not yet implemented" + +### 10. Create ai/providers/google-provider.ts (placeholder) +Create class `GoogleProvider` extending `BaseProvider`: +- Import Google Generative AI SDK when implemented +- For now, throw error: "Google provider not yet implemented" + +### 11. Create ai/prompt-builder.ts +Create class `PromptBuilder` with: +- Method `buildParsePrompt(prdContent: string, options: ParseOptions = {}): string` +- Method `buildExpandPrompt(task: string, context?: string): string` +- Use template literals for prompt construction +- Include specific JSON format instructions in prompts + +### 9. Create storage/file-storage.ts +Create class `FileStorage` implementing `IStorage`: +- Private property `basePath: string` set to `{projectPath}/.taskmaster` +- Constructor accepting `projectPath: string` +- Private method `getTasksPath(tag?: string): string` returning correct path based on tag +- Private method `ensureDirectory(dir: string): Promise` +- Implement all IStorage methods +- Handle ENOENT errors by returning empty arrays +- Use JSON format with structure: `{ tasks: Task[], metadata: { version: string, lastModified: string } }` +- Apply **Repository** pattern for data access abstraction + +### 10. Create config/config-manager.ts +Create class `ConfigManager`: +- Private property `config: IConfiguration` +- Constructor accepting `options: Partial` +- Use Zod for validation with schema matching IConfiguration +- Method `get(key: K): IConfiguration[K]` +- Method `getAll(): IConfiguration` +- Method `validate(): boolean` +- Default values: projectPath = process.cwd(), aiProvider = 'anthropic', enableTags = true + +### 11. Create utils/id-generator.ts +Export functions: +- `generateTaskId(index: number = 0): string` returning format `task_{timestamp}_{index}_{random}` +- `generateSubtaskId(parentId: string, index: number = 0): string` returning format `{parentId}_sub_{index}_{random}` + +### 16. Create src/index.ts +Create main class `TaskMasterCore`: +- Private properties: `config: ConfigManager`, `storage: IStorage`, `aiProvider?: IAIProvider`, `parser?: TaskParser` +- Constructor accepting `options: Partial` +- Method `initialize(): Promise` for lazy loading +- Method `parsePRD(prdPath: string, options: ParseOptions = {}): Promise` +- Method `getTasks(tag?: string): Promise` +- Apply **Facade** pattern to provide simple API over complex subsystems + +Export: +- Class `TaskMasterCore` +- Function `createTaskMaster(options: Partial): TaskMasterCore` +- All types from './types' +- All interfaces from './interfaces/*' + +Import statements should use kebab-case: +```typescript +import { TaskParser } from './tasks/task-parser'; +import { FileStorage } from './storage/file-storage'; +import { ConfigManager } from './config/config-manager'; +import { ProviderFactory } from './ai/provider-factory'; +``` + +### 17. Configure package.json +Create package.json with: +- name: "@task-master/core" +- version: "0.1.0" +- type: "module" +- main: "./dist/index.js" +- module: "./dist/index.mjs" +- types: "./dist/index.d.ts" +- exports map for proper ESM/CJS support +- scripts: build (tsup), dev (tsup --watch), test (jest), typecheck (tsc --noEmit) +- dependencies: zod@^3.23.8 +- peerDependencies: @anthropic-ai/sdk, openai, @google/generative-ai +- devDependencies: typescript, tsup, jest, ts-jest, @types/node, @types/jest + +### 18. Configure TypeScript +Create tsconfig.json with: +- target: "ES2022" +- module: "ESNext" +- strict: true (with all strict flags enabled) +- declaration: true +- outDir: "./dist" +- rootDir: "./src" + +### 19. Configure tsup +Create tsup.config.js with: +- entry: ['src/index.ts'] +- format: ['cjs', 'esm'] +- dts: true +- sourcemap: true +- clean: true +- external: AI provider SDKs + +### 20. Configure Jest +Create jest.config.js with: +- preset: 'ts-jest' +- testEnvironment: 'node' +- Coverage threshold: 80% for all metrics + +## Build Process +1. Use tsup to compile TypeScript to both CommonJS and ESM +2. Generate .d.ts files for TypeScript consumers +3. Output to dist/ directory +4. Ensure tree-shaking works properly + +## Testing Requirements +- Create unit tests for TaskParser in tests/task-parser.test.ts +- Create MockProvider class in tests/mocks/mock-provider.ts for testing without API calls +- Test error scenarios (file not found, invalid JSON, etc.) +- Create integration test in tests/integration/parse-prd.test.ts +- Follow kebab-case naming for all test files + +## Success Criteria +- TypeScript compilation with zero errors +- No use of 'any' type +- All interfaces properly exported +- Compatible with existing tasks.json format +- Feature flag support via USE_TM_CORE environment variable + +## Import/Export Conventions +- Use named exports for all classes and interfaces +- Use barrel exports (index.ts) in each directory +- Import types/interfaces with type-only imports: `import type { Task } from '../types'` +- Group imports in order: Node built-ins, external packages, internal packages, relative imports +- Use .js extension in import paths for ESM compatibility + +## Error Handling Patterns +- Create custom error classes in `src/errors/` directory +- All public methods should catch and wrap errors with context +- Use error codes for different error types (e.g., 'FILE_NOT_FOUND', 'PARSE_ERROR') +- Never expose internal implementation details in error messages +- Log errors to console.error only in development mode + +## Barrel Exports Content + +### interfaces/index.ts +```typescript +export type { IStorage } from './storage.interface'; +export type { IAIProvider, AIOptions } from './ai-provider.interface'; +export type { IConfiguration } from './configuration.interface'; +``` + +### tasks/index.ts +```typescript +export { TaskParser } from './task-parser'; +``` + +### ai/index.ts +```typescript +export { BaseProvider } from './base-provider'; +export { ProviderFactory } from './provider-factory'; +export { PromptBuilder } from './prompt-builder'; +``` + +### ai/providers/index.ts +```typescript +export { AnthropicProvider } from './anthropic-provider'; +export { OpenAIProvider } from './openai-provider'; +export { GoogleProvider } from './google-provider'; +``` + +### storage/index.ts +```typescript +export { FileStorage } from './file-storage'; +``` + +### config/index.ts +```typescript +export { ConfigManager } from './config-manager'; +``` + +### utils/index.ts +```typescript +export { generateTaskId, generateSubtaskId } from './id-generator'; +``` + +### errors/index.ts +```typescript +export { TaskMasterError } from './task-master-error'; +``` \ No newline at end of file