From 99d9179522dc66797ec7e3f428d72b46a9557f09 Mon Sep 17 00:00:00 2001 From: Ralph Khreish <35776126+Crunchyman-ralph@users.noreply.github.com> Date: Wed, 19 Nov 2025 18:03:34 +0100 Subject: [PATCH] feat: deprecate generate command (#1425) --- .changeset/rich-kings-fold.md | 5 + .taskmaster/CLAUDE.md | 6 - README.md | 3 - docs/command-reference.md | 7 - docs/tutorial.md | 11 - .../direct-functions/generate-task-files.js | 102 ------ mcp-server/src/core/task-master-core.js | 3 - mcp-server/src/tools/generate.js | 95 ------ mcp-server/src/tools/tool-registry.js | 3 - .../commands/generate-tasks.md | 121 ------- scripts/modules/commands.js | 64 ---- scripts/modules/dependency-manager.js | 2 - scripts/modules/task-manager.js | 2 - scripts/modules/task-manager/add-task.js | 1 - .../task-manager/generate-task-files.js | 202 ------------ scripts/modules/task-manager/remove-task.js | 1 - .../modules/task-manager/set-task-status.js | 1 - .../task-manager/update-subtask-by-id.js | 1 - scripts/modules/task-manager/update-tasks.js | 1 - tests/helpers/tool-counts.js | 8 +- tests/integration/cli/move-cross-tag.test.js | 58 +--- .../mcp-server/direct-functions.test.js | 2 - .../move-task-cross-tag.integration.test.js | 9 +- tests/unit/dependency-manager.test.js | 8 - tests/unit/parse-prd.test.js | 8 - .../modules/commands/move-cross-tag.test.js | 34 +- .../fix-dependencies-command.test.js | 7 - .../modules/task-manager/add-subtask.test.js | 6 - .../modules/task-manager/add-task.test.js | 13 - .../task-manager/clear-subtasks.test.js | 15 - .../modules/task-manager/expand-task.test.js | 14 - .../task-manager/generate-task-files.test.js | 306 ------------------ .../task-manager/move-task-cross-tag.test.js | 7 - .../modules/task-manager/move-task.test.js | 12 - .../modules/task-manager/parse-prd.test.js | 14 - .../modules/task-manager/remove-task.test.js | 12 - .../task-manager/set-task-status.test.js | 13 - .../task-manager/update-subtask-by-id.test.js | 7 - .../task-manager/update-task-by-id.test.js | 7 - .../modules/task-manager/update-tasks.test.js | 7 - 40 files changed, 15 insertions(+), 1183 deletions(-) create mode 100644 .changeset/rich-kings-fold.md delete mode 100644 mcp-server/src/core/direct-functions/generate-task-files.js delete mode 100644 mcp-server/src/tools/generate.js delete mode 100644 packages/claude-code-plugin/commands/generate-tasks.md delete mode 100644 scripts/modules/task-manager/generate-task-files.js delete mode 100644 tests/unit/scripts/modules/task-manager/generate-task-files.test.js diff --git a/.changeset/rich-kings-fold.md b/.changeset/rich-kings-fold.md new file mode 100644 index 00000000..fa9d0828 --- /dev/null +++ b/.changeset/rich-kings-fold.md @@ -0,0 +1,5 @@ +--- +"task-master-ai": minor +--- + +Deprecated generate command diff --git a/.taskmaster/CLAUDE.md b/.taskmaster/CLAUDE.md index f063eecf..55adb6ca 100644 --- a/.taskmaster/CLAUDE.md +++ b/.taskmaster/CLAUDE.md @@ -32,7 +32,6 @@ task-master expand --all --research # Expand all eligible tasks task-master add-dependency --id= --depends-on= # Add task dependency task-master move --from= --to= # Reorganize task hierarchy task-master validate-dependencies # Check for dependency issues -task-master generate # Update task markdown files (usually auto-called) ``` ## Key Files & Project Structure @@ -361,9 +360,6 @@ task-master models --set-fallback gpt-4o-mini ### Task File Sync Issues ```bash -# Regenerate task files from tasks.json -task-master generate - # Fix dependency issues task-master fix-dependencies ``` @@ -389,8 +385,6 @@ These commands make AI calls and may take up to a minute: - Never manually edit `tasks.json` - use commands instead - Never manually edit `.taskmaster/config.json` - use `task-master models` -- Task markdown files in `tasks/` are auto-generated -- Run `task-master generate` after manual changes to tasks.json ### Claude Code Session Management diff --git a/README.md b/README.md index e07f8934..487b30df 100644 --- a/README.md +++ b/README.md @@ -277,9 +277,6 @@ task-master move --from=5 --from-tag=backlog --to-tag=in-progress task-master move --from=5,6,7 --from-tag=backlog --to-tag=done --with-dependencies task-master move --from=5 --from-tag=backlog --to-tag=in-progress --ignore-dependencies -# Generate task files -task-master generate - # Add rules after initialization task-master rules add windsurf,roo,vscode ``` diff --git a/docs/command-reference.md b/docs/command-reference.md index f51d7f59..4345ca26 100644 --- a/docs/command-reference.md +++ b/docs/command-reference.md @@ -103,13 +103,6 @@ task-master update-subtask --id= --prompt="" --resea Unlike the `update-task` command which replaces task information, the `update-subtask` command _appends_ new information to the existing subtask details, marking it with a timestamp. This is useful for iteratively enhancing subtasks while preserving the original content. -## Generate Task Files - -```bash -# Generate individual task files from tasks.json -task-master generate -``` - ## Set Task Status ```bash diff --git a/docs/tutorial.md b/docs/tutorial.md index 8c81fb43..19ed803b 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -107,9 +107,6 @@ task-master list # Show the next task to work on task-master next - -# Generate task files -task-master generate ``` ## Setting up Cursor AI Integration @@ -178,14 +175,6 @@ Next, ask the agent to generate individual task files: Please generate individual task files from tasks.json ``` -The agent will execute: - -```bash -task-master generate -``` - -This creates individual task files in the `tasks/` directory (e.g., `task_001.txt`, `task_002.txt`), making it easier to reference specific tasks. - ## AI-Driven Development Workflow The Cursor agent is pre-configured (via the rules file) to follow this workflow: diff --git a/mcp-server/src/core/direct-functions/generate-task-files.js b/mcp-server/src/core/direct-functions/generate-task-files.js deleted file mode 100644 index f42a602a..00000000 --- a/mcp-server/src/core/direct-functions/generate-task-files.js +++ /dev/null @@ -1,102 +0,0 @@ -/** - * generate-task-files.js - * Direct function implementation for generating task files from tasks.json - */ - -import { generateTaskFiles } from '../../../../scripts/modules/task-manager.js'; -import { - enableSilentMode, - disableSilentMode -} from '../../../../scripts/modules/utils.js'; - -/** - * Direct function wrapper for generateTaskFiles with error handling. - * - * @param {Object} args - Command arguments containing tasksJsonPath and outputDir. - * @param {string} args.tasksJsonPath - Path to the tasks.json file. - * @param {string} args.outputDir - Path to the output directory. - * @param {string} args.projectRoot - Project root path (for MCP/env fallback) - * @param {string} args.tag - Tag for the task (optional) - * @param {Object} log - Logger object. - * @returns {Promise} - Result object with success status and data/error information. - */ -export async function generateTaskFilesDirect(args, log) { - // Destructure expected args - const { tasksJsonPath, outputDir, projectRoot, tag } = args; - try { - log.info(`Generating task files with args: ${JSON.stringify(args)}`); - - // Check if paths were provided - if (!tasksJsonPath) { - const errorMessage = 'tasksJsonPath is required but was not provided.'; - log.error(errorMessage); - return { - success: false, - error: { code: 'MISSING_ARGUMENT', message: errorMessage } - }; - } - if (!outputDir) { - const errorMessage = 'outputDir is required but was not provided.'; - log.error(errorMessage); - return { - success: false, - error: { code: 'MISSING_ARGUMENT', message: errorMessage } - }; - } - - // Use the provided paths - const tasksPath = tasksJsonPath; - const resolvedOutputDir = outputDir; - - log.info(`Generating task files from ${tasksPath} to ${resolvedOutputDir}`); - - // Execute core generateTaskFiles function in a separate try/catch - try { - // Enable silent mode to prevent logs from being written to stdout - enableSilentMode(); - - // Pass projectRoot and tag so the core respects context - generateTaskFiles(tasksPath, resolvedOutputDir, { - projectRoot, - tag, - mcpLog: log - }); - - // Restore normal logging after task generation - disableSilentMode(); - } catch (genError) { - // Make sure to restore normal logging even if there's an error - disableSilentMode(); - - log.error(`Error in generateTaskFiles: ${genError.message}`); - return { - success: false, - error: { code: 'GENERATE_FILES_ERROR', message: genError.message } - }; - } - - // Return success with file paths - return { - success: true, - data: { - message: `Successfully generated task files`, - tasksPath: tasksPath, - outputDir: resolvedOutputDir, - taskFiles: - 'Individual task files have been generated in the output directory' - } - }; - } catch (error) { - // Make sure to restore normal logging if an outer error occurs - disableSilentMode(); - - log.error(`Error generating task files: ${error.message}`); - return { - success: false, - error: { - code: 'GENERATE_TASKS_ERROR', - message: error.message || 'Unknown error generating task files' - } - }; - } -} diff --git a/mcp-server/src/core/task-master-core.js b/mcp-server/src/core/task-master-core.js index bfb9dead..e8977d26 100644 --- a/mcp-server/src/core/task-master-core.js +++ b/mcp-server/src/core/task-master-core.js @@ -10,7 +10,6 @@ import { parsePRDDirect } from './direct-functions/parse-prd.js'; import { updateTasksDirect } from './direct-functions/update-tasks.js'; import { updateTaskByIdDirect } from './direct-functions/update-task-by-id.js'; import { updateSubtaskByIdDirect } from './direct-functions/update-subtask-by-id.js'; -import { generateTaskFilesDirect } from './direct-functions/generate-task-files.js'; import { setTaskStatusDirect } from './direct-functions/set-task-status.js'; import { nextTaskDirect } from './direct-functions/next-task.js'; import { expandTaskDirect } from './direct-functions/expand-task.js'; @@ -50,7 +49,6 @@ export const directFunctions = new Map([ ['updateTasksDirect', updateTasksDirect], ['updateTaskByIdDirect', updateTaskByIdDirect], ['updateSubtaskByIdDirect', updateSubtaskByIdDirect], - ['generateTaskFilesDirect', generateTaskFilesDirect], ['setTaskStatusDirect', setTaskStatusDirect], ['nextTaskDirect', nextTaskDirect], ['expandTaskDirect', expandTaskDirect], @@ -88,7 +86,6 @@ export { updateTasksDirect, updateTaskByIdDirect, updateSubtaskByIdDirect, - generateTaskFilesDirect, setTaskStatusDirect, nextTaskDirect, expandTaskDirect, diff --git a/mcp-server/src/tools/generate.js b/mcp-server/src/tools/generate.js deleted file mode 100644 index aab34355..00000000 --- a/mcp-server/src/tools/generate.js +++ /dev/null @@ -1,95 +0,0 @@ -/** - * tools/generate.js - * Tool to generate individual task files from tasks.json - */ - -import { z } from 'zod'; -import { - handleApiResult, - createErrorResponse, - withNormalizedProjectRoot -} from './utils.js'; -import { generateTaskFilesDirect } from '../core/task-master-core.js'; -import { findTasksPath } from '../core/utils/path-utils.js'; -import { resolveTag } from '../../../scripts/modules/utils.js'; -import path from 'path'; - -/** - * Register the generate tool with the MCP server - * @param {Object} server - FastMCP server instance - */ -export function registerGenerateTool(server) { - server.addTool({ - name: 'generate', - description: - 'Generates individual task files in tasks/ directory based on tasks.json', - parameters: z.object({ - file: z.string().optional().describe('Absolute path to the tasks file'), - output: z - .string() - .optional() - .describe('Output directory (default: same directory as tasks file)'), - projectRoot: z - .string() - .describe('The directory of the project. Must be an absolute path.'), - tag: z.string().optional().describe('Tag context to operate on') - }), - execute: withNormalizedProjectRoot(async (args, { log, session }) => { - try { - log.info(`Generating task files with args: ${JSON.stringify(args)}`); - - const resolvedTag = resolveTag({ - projectRoot: args.projectRoot, - tag: args.tag - }); - // Use args.projectRoot directly (guaranteed by withNormalizedProjectRoot) - let tasksJsonPath; - try { - tasksJsonPath = findTasksPath( - { projectRoot: args.projectRoot, file: args.file }, - log - ); - } catch (error) { - log.error(`Error finding tasks.json: ${error.message}`); - return createErrorResponse( - `Failed to find tasks.json: ${error.message}` - ); - } - - const outputDir = args.output - ? path.resolve(args.projectRoot, args.output) - : path.dirname(tasksJsonPath); - - const result = await generateTaskFilesDirect( - { - tasksJsonPath: tasksJsonPath, - outputDir: outputDir, - projectRoot: args.projectRoot, - tag: resolvedTag - }, - log, - { session } - ); - - if (result.success) { - log.info(`Successfully generated task files: ${result.data.message}`); - } else { - log.error( - `Failed to generate task files: ${result.error?.message || 'Unknown error'}` - ); - } - - return handleApiResult( - result, - log, - 'Error generating task files', - undefined, - args.projectRoot - ); - } catch (error) { - log.error(`Error in generate tool: ${error.message}`); - return createErrorResponse(error.message); - } - }) - }); -} diff --git a/mcp-server/src/tools/tool-registry.js b/mcp-server/src/tools/tool-registry.js index 90acd5b6..971be22d 100644 --- a/mcp-server/src/tools/tool-registry.js +++ b/mcp-server/src/tools/tool-registry.js @@ -8,7 +8,6 @@ import { registerParsePRDTool } from './parse-prd.js'; import { registerUpdateTool } from './update.js'; import { registerUpdateTaskTool } from './update-task.js'; import { registerUpdateSubtaskTool } from './update-subtask.js'; -import { registerGenerateTool } from './generate.js'; import { registerNextTaskTool } from './next-task.js'; import { registerExpandTaskTool } from './expand-task.js'; import { registerAddTaskTool } from './add-task.js'; @@ -72,7 +71,6 @@ export const toolRegistry = { next_task: registerNextTaskTool, complexity_report: registerComplexityReportTool, set_task_status: registerSetTaskStatusTool, - generate: registerGenerateTool, add_task: registerAddTaskTool, add_subtask: registerAddSubtaskTool, update: registerUpdateTool, @@ -128,7 +126,6 @@ export const standardTools = [ 'expand_all', 'add_subtask', 'remove_task', - 'generate', 'add_task', 'complexity_report' ]; diff --git a/packages/claude-code-plugin/commands/generate-tasks.md b/packages/claude-code-plugin/commands/generate-tasks.md deleted file mode 100644 index 01140d75..00000000 --- a/packages/claude-code-plugin/commands/generate-tasks.md +++ /dev/null @@ -1,121 +0,0 @@ -Generate individual task files from tasks.json. - -## Task File Generation - -Creates separate markdown files for each task, perfect for AI agents or documentation. - -## Execution - -```bash -task-master generate -``` - -## What It Creates - -For each task, generates a file like `task_001.txt`: - -``` -Task ID: 1 -Title: Implement user authentication -Status: pending -Priority: high -Dependencies: [] -Created: 2024-01-15 -Complexity: 7 - -## Description -Create a secure user authentication system with login, logout, and session management. - -## Details -- Use JWT tokens for session management -- Implement secure password hashing -- Add remember me functionality -- Include password reset flow - -## Test Strategy -- Unit tests for auth functions -- Integration tests for login flow -- Security testing for vulnerabilities -- Performance tests for concurrent logins - -## Subtasks -1.1 Setup authentication framework (pending) -1.2 Create login endpoints (pending) -1.3 Implement session management (pending) -1.4 Add password reset (pending) -``` - -## File Organization - -Creates structure: -``` -.taskmaster/ -└── tasks/ - ├── task_001.txt - ├── task_002.txt - ├── task_003.txt - └── ... -``` - -## Smart Features - -1. **Consistent Formatting** - - Standardized structure - - Clear sections - - AI-readable format - - Markdown compatible - -2. **Contextual Information** - - Full task details - - Related task references - - Progress indicators - - Implementation notes - -3. **Incremental Updates** - - Only regenerate changed tasks - - Preserve custom additions - - Track generation timestamp - - Version control friendly - -## Use Cases - -- **AI Context**: Provide task context to AI assistants -- **Documentation**: Standalone task documentation -- **Archival**: Task history preservation -- **Sharing**: Send specific tasks to team members -- **Review**: Easier task review process - -## Generation Options - -Based on arguments: -- Filter by status -- Include/exclude completed -- Custom templates -- Different formats - -## Post-Generation - -``` -Task File Generation Complete -━━━━━━━━━━━━━━━━━━━━━━━━━━ -Generated: 45 task files -Location: .taskmaster/tasks/ -Total size: 156 KB - -New files: 5 -Updated files: 12 -Unchanged: 28 - -Ready for: -- AI agent consumption -- Version control -- Team distribution -``` - -## Integration Benefits - -- Git-trackable task history -- Easy task sharing -- AI tool compatibility -- Offline task access -- Backup redundancy \ No newline at end of file diff --git a/scripts/modules/commands.js b/scripts/modules/commands.js index d9937986..71e87a5e 100644 --- a/scripts/modules/commands.js +++ b/scripts/modules/commands.js @@ -25,7 +25,6 @@ import { import { parsePRD, updateTasks, - generateTaskFiles, expandTask, expandAllTasks, clearSubtasks, @@ -1000,42 +999,6 @@ function registerCommands(programInstance) { } }); - // generate command - programInstance - .command('generate') - .description('Generate task files from tasks.json') - .option( - '-f, --file ', - 'Path to the tasks file', - TASKMASTER_TASKS_FILE - ) - .option( - '-o, --output ', - 'Output directory', - path.dirname(TASKMASTER_TASKS_FILE) - ) - .option('--tag ', 'Specify tag context for task operations') - .action(async (options) => { - // Initialize TaskMaster - const taskMaster = initTaskMaster({ - tasksPath: options.file || true, - tag: options.tag - }); - - const outputDir = options.output; - const tag = taskMaster.getCurrentTag(); - - console.log( - chalk.blue(`Generating task files from: ${taskMaster.getTasksPath()}`) - ); - console.log(chalk.blue(`Output directory: ${outputDir}`)); - - await generateTaskFiles(taskMaster.getTasksPath(), outputDir, { - projectRoot: taskMaster.getProjectRoot(), - tag - }); - }); - // ======================================== // Register All Commands from @tm/cli // ======================================== @@ -3340,33 +3303,6 @@ Examples: console.log('\n' + chalk.yellow.bold('Next Steps:')); result.tips.forEach((t) => console.log(chalk.white(` • ${t}`))); } - - // Check if source tag still contains tasks before regenerating files - const tasksData = readJSON( - taskMaster.getTasksPath(), - taskMaster.getProjectRoot(), - sourceTag - ); - const sourceTagHasTasks = - tasksData && - Array.isArray(tasksData.tasks) && - tasksData.tasks.length > 0; - - // Generate task files for the affected tags - await generateTaskFiles( - taskMaster.getTasksPath(), - path.dirname(taskMaster.getTasksPath()), - { tag: toTag, projectRoot: taskMaster.getProjectRoot() } - ); - - // Only regenerate source tag files if it still contains tasks - if (sourceTagHasTasks) { - await generateTaskFiles( - taskMaster.getTasksPath(), - path.dirname(taskMaster.getTasksPath()), - { tag: sourceTag, projectRoot: taskMaster.getProjectRoot() } - ); - } } // Helper function to handle within-tag move logic diff --git a/scripts/modules/dependency-manager.js b/scripts/modules/dependency-manager.js index e7c618a2..08927413 100644 --- a/scripts/modules/dependency-manager.js +++ b/scripts/modules/dependency-manager.js @@ -20,8 +20,6 @@ import { import { displayBanner } from './ui.js'; -import generateTaskFiles from './task-manager/generate-task-files.js'; - /** * Structured error class for dependency operations */ diff --git a/scripts/modules/task-manager.js b/scripts/modules/task-manager.js index 32a38ebf..a6729007 100644 --- a/scripts/modules/task-manager.js +++ b/scripts/modules/task-manager.js @@ -7,7 +7,6 @@ import { findTaskById } from './utils.js'; import parsePRD from './task-manager/parse-prd/index.js'; import updateTasks from './task-manager/update-tasks.js'; import updateTaskById from './task-manager/update-task-by-id.js'; -import generateTaskFiles from './task-manager/generate-task-files.js'; import setTaskStatus from './task-manager/set-task-status.js'; import updateSingleTaskStatus from './task-manager/update-single-task-status.js'; import listTasks from './task-manager/list-tasks.js'; @@ -40,7 +39,6 @@ export { updateTasks, updateTaskById, updateSubtaskById, - generateTaskFiles, setTaskStatus, updateSingleTaskStatus, listTasks, diff --git a/scripts/modules/task-manager/add-task.js b/scripts/modules/task-manager/add-task.js index 115bb8b8..0f837df7 100644 --- a/scripts/modules/task-manager/add-task.js +++ b/scripts/modules/task-manager/add-task.js @@ -27,7 +27,6 @@ import { generateObjectService } from '../ai-services-unified.js'; import { getDefaultPriority, hasCodebaseAnalysis } from '../config-manager.js'; import { getPromptManager } from '../prompt-manager.js'; import ContextGatherer from '../utils/contextGatherer.js'; -import generateTaskFiles from './generate-task-files.js'; import { COMMAND_SCHEMAS } from '../../../src/schemas/registry.js'; import { TASK_PRIORITY_OPTIONS, diff --git a/scripts/modules/task-manager/generate-task-files.js b/scripts/modules/task-manager/generate-task-files.js deleted file mode 100644 index 581e9ec7..00000000 --- a/scripts/modules/task-manager/generate-task-files.js +++ /dev/null @@ -1,202 +0,0 @@ -import path from 'path'; -import fs from 'fs'; -import chalk from 'chalk'; - -import { log, readJSON } from '../utils.js'; -import { formatDependenciesWithStatus } from '../ui.js'; -import { validateAndFixDependencies } from '../dependency-manager.js'; -import { getDebugFlag } from '../config-manager.js'; - -/** - * Generate individual task files from tasks.json - * @param {string} tasksPath - Path to the tasks.json file - * @param {string} outputDir - Output directory for task files - * @param {Object} options - Additional options (mcpLog for MCP mode, projectRoot, tag) - * @param {string} [options.projectRoot] - Project root path - * @param {string} [options.tag] - Tag for the task - * @param {Object} [options.mcpLog] - MCP logger object - * @returns {Object|undefined} Result object in MCP mode, undefined in CLI mode - */ -function generateTaskFiles(tasksPath, outputDir, options = {}) { - try { - const isMcpMode = !!options?.mcpLog; - const { projectRoot, tag } = options; - - // 1. Read the raw data structure, ensuring we have all tags. - // We call readJSON without a specific tag to get the resolved default view, - // which correctly contains the full structure in `_rawTaggedData`. - const resolvedData = readJSON(tasksPath, projectRoot, tag); - if (!resolvedData) { - throw new Error(`Could not read or parse tasks file: ${tasksPath}`); - } - // Prioritize the _rawTaggedData if it exists, otherwise use the data as is. - const rawData = resolvedData._rawTaggedData || resolvedData; - - // 2. Determine the target tag we need to generate files for. - const tagData = rawData[tag]; - - if (!tagData || !tagData.tasks) { - throw new Error(`Tag '${tag}' not found or has no tasks in the data.`); - } - const tasksForGeneration = tagData.tasks; - - // Create the output directory if it doesn't exist - if (!fs.existsSync(outputDir)) { - fs.mkdirSync(outputDir, { recursive: true }); - } - - log( - 'info', - `Preparing to regenerate ${tasksForGeneration.length} task files for tag '${tag}'` - ); - - // 3. Validate dependencies using the FULL, raw data structure to prevent data loss. - validateAndFixDependencies( - rawData, // Pass the entire object with all tags - tasksPath, - projectRoot, - tag // Provide the current tag context for the operation - ); - - const allTasksInTag = tagData.tasks; - const validTaskIds = allTasksInTag.map((task) => task.id); - - // Cleanup orphaned task files - log('info', 'Checking for orphaned task files to clean up...'); - try { - const files = fs.readdirSync(outputDir); - // Tag-aware file patterns: master -> task_001.txt, other tags -> task_001_tagname.txt - const masterFilePattern = /^task_(\d+)\.txt$/; - const taggedFilePattern = new RegExp(`^task_(\\d+)_${tag}\\.txt$`); - - const orphanedFiles = files.filter((file) => { - let match = null; - let fileTaskId = null; - - // Check if file belongs to current tag - if (tag === 'master') { - match = file.match(masterFilePattern); - if (match) { - fileTaskId = parseInt(match[1], 10); - // Only clean up master files when processing master tag - return !validTaskIds.includes(fileTaskId); - } - } else { - match = file.match(taggedFilePattern); - if (match) { - fileTaskId = parseInt(match[1], 10); - // Only clean up files for the current tag - return !validTaskIds.includes(fileTaskId); - } - } - return false; - }); - - if (orphanedFiles.length > 0) { - log( - 'info', - `Found ${orphanedFiles.length} orphaned task files to remove for tag '${tag}'` - ); - orphanedFiles.forEach((file) => { - const filePath = path.join(outputDir, file); - fs.unlinkSync(filePath); - }); - } else { - log('info', 'No orphaned task files found.'); - } - } catch (err) { - log('warn', `Error cleaning up orphaned task files: ${err.message}`); - } - - // Generate task files for the target tag - log('info', `Generating individual task files for tag '${tag}'...`); - tasksForGeneration.forEach((task) => { - // Tag-aware file naming: master -> task_001.txt, other tags -> task_001_tagname.txt - const taskFileName = - tag === 'master' - ? `task_${task.id.toString().padStart(3, '0')}.txt` - : `task_${task.id.toString().padStart(3, '0')}_${tag}.txt`; - - const taskPath = path.join(outputDir, taskFileName); - - let content = `# Task ID: ${task.id}\n`; - content += `# Title: ${task.title}\n`; - content += `# Status: ${task.status || 'pending'}\n`; - - if (task.dependencies && task.dependencies.length > 0) { - content += `# Dependencies: ${formatDependenciesWithStatus(task.dependencies, allTasksInTag, false)}\n`; - } else { - content += '# Dependencies: None\n'; - } - - content += `# Priority: ${task.priority || 'medium'}\n`; - content += `# Description: ${task.description || ''}\n`; - content += '# Details:\n'; - content += (task.details || '') - .split('\n') - .map((line) => line) - .join('\n'); - content += '\n\n'; - content += '# Test Strategy:\n'; - content += (task.testStrategy || '') - .split('\n') - .map((line) => line) - .join('\n'); - content += '\n'; - - if (task.subtasks && task.subtasks.length > 0) { - content += '\n# Subtasks:\n'; - task.subtasks.forEach((subtask) => { - content += `## ${subtask.id}. ${subtask.title} [${subtask.status || 'pending'}]\n`; - if (subtask.dependencies && subtask.dependencies.length > 0) { - const subtaskDeps = subtask.dependencies - .map((depId) => - typeof depId === 'number' - ? `${task.id}.${depId}` - : depId.toString() - ) - .join(', '); - content += `### Dependencies: ${subtaskDeps}\n`; - } else { - content += '### Dependencies: None\n'; - } - content += `### Description: ${subtask.description || ''}\n`; - content += '### Details:\n'; - content += (subtask.details || '') - .split('\n') - .map((line) => line) - .join('\n'); - content += '\n\n'; - }); - } - - fs.writeFileSync(taskPath, content); - }); - - log( - 'success', - `All ${tasksForGeneration.length} tasks for tag '${tag}' have been generated into '${outputDir}'.` - ); - - if (isMcpMode) { - return { - success: true, - count: tasksForGeneration.length, - directory: outputDir - }; - } - } catch (error) { - log('error', `Error generating task files: ${error.message}`); - if (!options?.mcpLog) { - console.error(chalk.red(`Error generating task files: ${error.message}`)); - if (getDebugFlag()) { - console.error(error); - } - process.exit(1); - } else { - throw error; - } - } -} - -export default generateTaskFiles; diff --git a/scripts/modules/task-manager/remove-task.js b/scripts/modules/task-manager/remove-task.js index e44f6f0a..2472a58c 100644 --- a/scripts/modules/task-manager/remove-task.js +++ b/scripts/modules/task-manager/remove-task.js @@ -1,7 +1,6 @@ import path from 'path'; import * as fs from 'fs'; import { readJSON, writeJSON, log, findTaskById } from '../utils.js'; -import generateTaskFiles from './generate-task-files.js'; import taskExists from './task-exists.js'; /** diff --git a/scripts/modules/task-manager/set-task-status.js b/scripts/modules/task-manager/set-task-status.js index 18c18ced..af28f37a 100644 --- a/scripts/modules/task-manager/set-task-status.js +++ b/scripts/modules/task-manager/set-task-status.js @@ -13,7 +13,6 @@ import { displayBanner } from '../ui.js'; import { validateTaskDependencies } from '../dependency-manager.js'; import { getDebugFlag } from '../config-manager.js'; import updateSingleTaskStatus from './update-single-task-status.js'; -import generateTaskFiles from './generate-task-files.js'; import { isValidTaskStatus, TASK_STATUS_OPTIONS diff --git a/scripts/modules/task-manager/update-subtask-by-id.js b/scripts/modules/task-manager/update-subtask-by-id.js index 94f9c0ea..35d4347d 100644 --- a/scripts/modules/task-manager/update-subtask-by-id.js +++ b/scripts/modules/task-manager/update-subtask-by-id.js @@ -22,7 +22,6 @@ import { import { generateTextService } from '../ai-services-unified.js'; import { getDebugFlag, hasCodebaseAnalysis } from '../config-manager.js'; import { getPromptManager } from '../prompt-manager.js'; -import generateTaskFiles from './generate-task-files.js'; import { ContextGatherer } from '../utils/contextGatherer.js'; import { FuzzyTaskSearch } from '../utils/fuzzyTaskSearch.js'; import { tryUpdateViaRemote } from '@tm/bridge'; diff --git a/scripts/modules/task-manager/update-tasks.js b/scripts/modules/task-manager/update-tasks.js index 2895fc51..89d5307e 100644 --- a/scripts/modules/task-manager/update-tasks.js +++ b/scripts/modules/task-manager/update-tasks.js @@ -20,7 +20,6 @@ import { import { getDebugFlag, hasCodebaseAnalysis } from '../config-manager.js'; import { getPromptManager } from '../prompt-manager.js'; -import generateTaskFiles from './generate-task-files.js'; import { generateObjectService } from '../ai-services-unified.js'; import { COMMAND_SCHEMAS } from '../../../src/schemas/registry.js'; import { getModelConfiguration } from './models.js'; diff --git a/tests/helpers/tool-counts.js b/tests/helpers/tool-counts.js index 5435923c..b9f34c6c 100644 --- a/tests/helpers/tool-counts.js +++ b/tests/helpers/tool-counts.js @@ -4,8 +4,8 @@ */ import { - getToolCounts, - getToolCategories + getToolCategories, + getToolCounts } from '../../mcp-server/src/tools/tool-registry.js'; /** @@ -14,8 +14,8 @@ import { */ export const EXPECTED_TOOL_COUNTS = { core: 7, - standard: 15, - total: 44 + standard: 14, + total: 43 }; /** diff --git a/tests/integration/cli/move-cross-tag.test.js b/tests/integration/cli/move-cross-tag.test.js index 699d4223..27ddf275 100644 --- a/tests/integration/cli/move-cross-tag.test.js +++ b/tests/integration/cli/move-cross-tag.test.js @@ -1,11 +1,10 @@ -import { jest } from '@jest/globals'; import fs from 'fs'; import path from 'path'; +import { jest } from '@jest/globals'; // --- Define mock functions --- const mockMoveTasksBetweenTags = jest.fn(); const mockMoveTask = jest.fn(); -const mockGenerateTaskFiles = jest.fn(); const mockLog = jest.fn(); // --- Setup mocks using unstable_mockModule --- @@ -17,13 +16,6 @@ jest.unstable_mockModule( }) ); -jest.unstable_mockModule( - '../../../scripts/modules/task-manager/generate-task-files.js', - () => ({ - default: mockGenerateTaskFiles - }) -); - jest.unstable_mockModule('../../../scripts/modules/utils.js', () => ({ log: mockLog, readJSON: jest.fn(), @@ -58,7 +50,7 @@ jest.unstable_mockModule('chalk', () => ({ })); // --- Import modules (AFTER mock setup) --- -let moveTaskModule, generateTaskFilesModule, utilsModule, chalk; +let moveTaskModule, utilsModule, chalk; describe('Cross-Tag Move CLI Integration', () => { // Setup dynamic imports before tests run @@ -66,9 +58,6 @@ describe('Cross-Tag Move CLI Integration', () => { moveTaskModule = await import( '../../../scripts/modules/task-manager/move-task.js' ); - generateTaskFilesModule = await import( - '../../../scripts/modules/task-manager/generate-task-files.js' - ); utilsModule = await import('../../../scripts/modules/utils.js'); chalk = (await import('chalk')).default; }); @@ -176,18 +165,6 @@ describe('Cross-Tag Move CLI Integration', () => { console.log('Next Steps:'); result.tips.forEach((t) => console.log(` • ${t}`)); } - - // Generate task files for both tags - await generateTaskFilesModule.default( - tasksPath, - path.dirname(tasksPath), - { tag: sourceTag } - ); - await generateTaskFilesModule.default( - tasksPath, - path.dirname(tasksPath), - { tag: toTag } - ); } catch (error) { console.error(chalk.red(`Error: ${error.message}`)); // Print ID collision guidance similar to CLI help block @@ -271,7 +248,6 @@ describe('Cross-Tag Move CLI Integration', () => { it('should move task without dependencies successfully', async () => { // Mock successful cross-tag move mockMoveTasksBetweenTags.mockResolvedValue(undefined); - mockGenerateTaskFiles.mockResolvedValue(undefined); const options = { from: '2', @@ -324,7 +300,6 @@ describe('Cross-Tag Move CLI Integration', () => { it('should move task with dependencies when --with-dependencies is used', async () => { // Mock successful cross-tag move with dependencies mockMoveTasksBetweenTags.mockResolvedValue(undefined); - mockGenerateTaskFiles.mockResolvedValue(undefined); const options = { from: '1', @@ -350,7 +325,6 @@ describe('Cross-Tag Move CLI Integration', () => { it('should break dependencies when --ignore-dependencies is used', async () => { // Mock successful cross-tag move with dependency breaking mockMoveTasksBetweenTags.mockResolvedValue(undefined); - mockGenerateTaskFiles.mockResolvedValue(undefined); const options = { from: '1', @@ -376,7 +350,6 @@ describe('Cross-Tag Move CLI Integration', () => { it('should create target tag if it does not exist', async () => { // Mock successful cross-tag move to new tag mockMoveTasksBetweenTags.mockResolvedValue(undefined); - mockGenerateTaskFiles.mockResolvedValue(undefined); const options = { from: '2', @@ -567,24 +540,11 @@ describe('Cross-Tag Move CLI Integration', () => { ignoreDependencies: false } ); - - // Verify that generateTaskFiles was called for both tags - expect(generateTaskFilesModule.default).toHaveBeenCalledWith( - expect.stringContaining('.taskmaster/tasks/tasks.json'), - expect.stringContaining('.taskmaster/tasks'), - { tag: 'master' } - ); - expect(generateTaskFilesModule.default).toHaveBeenCalledWith( - expect.stringContaining('.taskmaster/tasks/tasks.json'), - expect.stringContaining('.taskmaster/tasks'), - { tag: 'in-progress' } - ); }); it('should move multiple tasks with comma-separated IDs successfully', async () => { // Mock successful cross-tag move for multiple tasks mockMoveTasksBetweenTags.mockResolvedValue(undefined); - mockGenerateTaskFiles.mockResolvedValue(undefined); const options = { from: '1,2,3', @@ -604,19 +564,6 @@ describe('Cross-Tag Move CLI Integration', () => { ignoreDependencies: undefined } ); - - // Verify task files are generated for both tags - expect(mockGenerateTaskFiles).toHaveBeenCalledTimes(2); - expect(mockGenerateTaskFiles).toHaveBeenCalledWith( - expect.stringContaining('tasks.json'), - expect.stringContaining('.taskmaster/tasks'), - { tag: 'backlog' } - ); - expect(mockGenerateTaskFiles).toHaveBeenCalledWith( - expect.stringContaining('tasks.json'), - expect.stringContaining('.taskmaster/tasks'), - { tag: 'in-progress' } - ); }); // Note: --force flag is no longer supported for cross-tag moves @@ -710,7 +657,6 @@ describe('Cross-Tag Move CLI Integration', () => { it('should handle whitespace in comma-separated task IDs', async () => { // Mock successful cross-tag move with whitespace mockMoveTasksBetweenTags.mockResolvedValue(undefined); - mockGenerateTaskFiles.mockResolvedValue(undefined); const options = { from: ' 1 , 2 , 3 ', // Whitespace around IDs and commas diff --git a/tests/integration/mcp-server/direct-functions.test.js b/tests/integration/mcp-server/direct-functions.test.js index 8d1e60a5..66eca26e 100644 --- a/tests/integration/mcp-server/direct-functions.test.js +++ b/tests/integration/mcp-server/direct-functions.test.js @@ -111,7 +111,6 @@ const mockExpandTask = jest } ); -const mockGenerateTaskFiles = jest.fn().mockResolvedValue(true); const mockFindTaskById = jest.fn(); const mockTaskExists = jest.fn().mockReturnValue(true); @@ -155,7 +154,6 @@ jest.mock('../../../scripts/modules/ai-services-unified.js', () => ({ // Mock task-manager.js to avoid real operations jest.mock('../../../scripts/modules/task-manager.js', () => ({ expandTask: mockExpandTask, - generateTaskFiles: mockGenerateTaskFiles, findTaskById: mockFindTaskById, taskExists: mockTaskExists })); diff --git a/tests/integration/move-task-cross-tag.integration.test.js b/tests/integration/move-task-cross-tag.integration.test.js index 9a689787..16c00e9a 100644 --- a/tests/integration/move-task-cross-tag.integration.test.js +++ b/tests/integration/move-task-cross-tag.integration.test.js @@ -1,7 +1,7 @@ -import { jest } from '@jest/globals'; import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; +import { jest } from '@jest/globals'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -106,13 +106,6 @@ jest.unstable_mockModule('../../scripts/modules/dependency-manager.js', () => ({ }) })); -jest.unstable_mockModule( - '../../scripts/modules/task-manager/generate-task-files.js', - () => ({ - default: jest.fn().mockResolvedValue() - }) -); - // Import the modules we'll be testing after mocking const { moveTasksBetweenTags } = await import( '../../scripts/modules/task-manager/move-task.js' diff --git a/tests/unit/dependency-manager.test.js b/tests/unit/dependency-manager.test.js index deda7cf4..8af633a4 100644 --- a/tests/unit/dependency-manager.test.js +++ b/tests/unit/dependency-manager.test.js @@ -71,10 +71,6 @@ jest.mock('../../scripts/modules/ui.js', () => ({ displayBanner: jest.fn() })); -jest.mock('../../scripts/modules/task-manager.js', () => ({ - generateTaskFiles: jest.fn() -})); - // Use a temporary path for test files - Jest will clean up the temp directory const TEST_TASKS_PATH = '/tmp/jest-test-tasks.json'; @@ -991,10 +987,6 @@ describe('Dependency Manager Module', () => { await jest.unstable_mockModule('../../scripts/modules/ui.js', () => ({ displayBanner: jest.fn() })); - await jest.unstable_mockModule( - '../../scripts/modules/task-manager/generate-task-files.js', - () => ({ default: jest.fn() }) - ); // Set up test data that matches the issue report // Clone fixture data before each test to prevent mutation issues mockReadJSON.mockImplementation(() => diff --git a/tests/unit/parse-prd.test.js b/tests/unit/parse-prd.test.js index 2a71f937..f6240aa1 100644 --- a/tests/unit/parse-prd.test.js +++ b/tests/unit/parse-prd.test.js @@ -121,14 +121,6 @@ jest.unstable_mockModule('../../scripts/modules/ui.js', () => ({ displayAiUsageSummary: jest.fn() })); -// Mock task generation to prevent file operations -jest.unstable_mockModule( - '../../scripts/modules/task-manager/generate-task-files.js', - () => ({ - default: jest.fn() - }) -); - // Mock stream parser jest.unstable_mockModule('../../src/utils/stream-parser.js', () => { // Define mock StreamingError class diff --git a/tests/unit/scripts/modules/commands/move-cross-tag.test.js b/tests/unit/scripts/modules/commands/move-cross-tag.test.js index 28eaca44..ea2d18ad 100644 --- a/tests/unit/scripts/modules/commands/move-cross-tag.test.js +++ b/tests/unit/scripts/modules/commands/move-cross-tag.test.js @@ -12,7 +12,6 @@ const mockConfig = { // Core functionality mocks (always needed) core: { moveTasksBetweenTags: true, - generateTaskFiles: true, readJSON: true, initTaskMaster: true, findProjectRoot: true @@ -48,9 +47,6 @@ function createMockFactory(config = mockConfig) { if (config.core?.moveTasksBetweenTags) { mocks.moveTasksBetweenTags = createMock('moveTasksBetweenTags'); } - if (config.core?.generateTaskFiles) { - mocks.generateTaskFiles = createMock('generateTaskFiles'); - } if (config.core?.readJSON) { mocks.readJSON = createMock('readJSON'); } @@ -80,14 +76,9 @@ function setupMocks(config = mockConfig) { ); } - if ( - config.core?.generateTaskFiles || - config.core?.readJSON || - config.core?.findProjectRoot - ) { + if (config.core?.readJSON || config.core?.findProjectRoot) { jest.mock('../../../../../scripts/modules/utils.js', () => ({ findProjectRoot: mocks.findProjectRoot, - generateTaskFiles: mocks.generateTaskFiles, readJSON: mocks.readJSON, // Minimal set of utils that might be used log: jest.fn(), @@ -172,20 +163,6 @@ async function handleCrossTagMove(moveContext, options) { const sourceTagHasTasks = tasksData && Array.isArray(tasksData.tasks) && tasksData.tasks.length > 0; - // Generate task files for the affected tags - await mocks.generateTaskFiles(taskMaster.getTasksPath(), 'tasks', { - tag: toTag, - projectRoot: taskMaster.getProjectRoot() - }); - - // Only regenerate source tag files if it still contains tasks - if (sourceTagHasTasks) { - await mocks.generateTaskFiles(taskMaster.getTasksPath(), 'tasks', { - tag: sourceTag, - projectRoot: taskMaster.getProjectRoot() - }); - } - return result; } @@ -216,7 +193,6 @@ describe('CLI Move Command Cross-Tag Functionality', () => { mocks.initTaskMaster.mockReturnValue(mockTaskMaster); mocks.findProjectRoot.mockReturnValue('/test/project'); - mocks.generateTaskFiles.mockResolvedValue(); mocks.readJSON.mockReturnValue({ tasks: [ { id: 1, title: 'Test Task 1' }, @@ -482,14 +458,12 @@ describe('CLI Move Command Cross-Tag Functionality', () => { const minimalConfig = { core: { moveTasksBetweenTags: true, - generateTaskFiles: true, readJSON: true } }; const minimalMocks = createMockFactory(minimalConfig); expect(minimalMocks.moveTasksBetweenTags).toBeDefined(); - expect(minimalMocks.generateTaskFiles).toBeDefined(); expect(minimalMocks.readJSON).toBeDefined(); }); @@ -498,15 +472,13 @@ describe('CLI Move Command Cross-Tag Functionality', () => { const selectiveConfig = { core: { moveTasksBetweenTags: true, - generateTaskFiles: false, // Disabled - readJSON: true + readJSON: false // Disabled } }; const selectiveMocks = createMockFactory(selectiveConfig); expect(selectiveMocks.moveTasksBetweenTags).toBeDefined(); - expect(selectiveMocks.generateTaskFiles).toBeUndefined(); - expect(selectiveMocks.readJSON).toBeDefined(); + expect(selectiveMocks.readJSON).toBeUndefined(); }); }); }); diff --git a/tests/unit/scripts/modules/dependency-manager/fix-dependencies-command.test.js b/tests/unit/scripts/modules/dependency-manager/fix-dependencies-command.test.js index ee0fbf4d..e2c72df9 100644 --- a/tests/unit/scripts/modules/dependency-manager/fix-dependencies-command.test.js +++ b/tests/unit/scripts/modules/dependency-manager/fix-dependencies-command.test.js @@ -44,13 +44,6 @@ jest.unstable_mockModule('../../../../../scripts/modules/ui.js', () => ({ })); // Mock task-manager.js -jest.unstable_mockModule( - '../../../../../scripts/modules/task-manager.js', - () => ({ - generateTaskFiles: jest.fn() - }) -); - // Mock external libraries jest.unstable_mockModule('chalk', () => ({ default: { diff --git a/tests/unit/scripts/modules/task-manager/add-subtask.test.js b/tests/unit/scripts/modules/task-manager/add-subtask.test.js index 6fc87a94..9554120d 100644 --- a/tests/unit/scripts/modules/task-manager/add-subtask.test.js +++ b/tests/unit/scripts/modules/task-manager/add-subtask.test.js @@ -23,12 +23,6 @@ jest.unstable_mockModule( '../../../../../scripts/modules/task-manager.js', () => mockTaskManager ); -jest.unstable_mockModule( - '../../../../../scripts/modules/task-manager/generate-task-files.js', - () => ({ - default: mockGenerateTaskFiles - }) -); const addSubtask = ( await import('../../../../../scripts/modules/task-manager/add-subtask.js') diff --git a/tests/unit/scripts/modules/task-manager/add-task.test.js b/tests/unit/scripts/modules/task-manager/add-task.test.js index a6a19dac..b2f0eb1b 100644 --- a/tests/unit/scripts/modules/task-manager/add-task.test.js +++ b/tests/unit/scripts/modules/task-manager/add-task.test.js @@ -118,13 +118,6 @@ jest.unstable_mockModule( }) ); -jest.unstable_mockModule( - '../../../../../scripts/modules/task-manager/generate-task-files.js', - () => ({ - default: jest.fn().mockResolvedValue() - }) -); - jest.unstable_mockModule( '../../../../../scripts/modules/prompt-manager.js', () => ({ @@ -173,12 +166,6 @@ const { generateObjectService } = await import( '../../../../../scripts/modules/ai-services-unified.js' ); -const generateTaskFiles = ( - await import( - '../../../../../scripts/modules/task-manager/generate-task-files.js' - ) -).default; - // Import the module under test const { default: addTask } = await import( '../../../../../scripts/modules/task-manager/add-task.js' diff --git a/tests/unit/scripts/modules/task-manager/clear-subtasks.test.js b/tests/unit/scripts/modules/task-manager/clear-subtasks.test.js index c8c6b9b1..0ef3aef0 100644 --- a/tests/unit/scripts/modules/task-manager/clear-subtasks.test.js +++ b/tests/unit/scripts/modules/task-manager/clear-subtasks.test.js @@ -24,13 +24,6 @@ jest.unstable_mockModule('../../../../../scripts/modules/ui.js', () => ({ displayBanner: jest.fn() })); -jest.unstable_mockModule( - '../../../../../scripts/modules/task-manager/generate-task-files.js', - () => ({ - default: jest.fn().mockResolvedValue() - }) -); - // Mock external UI libraries jest.unstable_mockModule('chalk', () => ({ default: { @@ -68,11 +61,6 @@ const mockExit = jest.spyOn(process, 'exit').mockImplementation((code) => { // Import the mocked modules const { readJSON, writeJSON, log, findTaskById, ensureTagMetadata } = await import('../../../../../scripts/modules/utils.js'); -const generateTaskFiles = ( - await import( - '../../../../../scripts/modules/task-manager/generate-task-files.js' - ) -).default; // Import the module under test const { default: clearSubtasks } = await import( @@ -116,7 +104,6 @@ describe('clearSubtasks', () => { }; }); writeJSON.mockResolvedValue(); - generateTaskFiles.mockResolvedValue(); log.mockImplementation(() => {}); }); @@ -191,7 +178,6 @@ describe('clearSubtasks', () => { expect(readJSON).toHaveBeenCalledWith(tasksPath, undefined, 'master'); // Should not write the file if no changes were made expect(writeJSON).not.toHaveBeenCalled(); - expect(generateTaskFiles).not.toHaveBeenCalled(); }); test('should handle non-existent task IDs gracefully', () => { @@ -208,7 +194,6 @@ describe('clearSubtasks', () => { expect(log).toHaveBeenCalledWith('error', 'Task 99 not found'); // Should not write the file if no changes were made expect(writeJSON).not.toHaveBeenCalled(); - expect(generateTaskFiles).not.toHaveBeenCalled(); }); test('should handle multiple task IDs including both valid and non-existent IDs', () => { diff --git a/tests/unit/scripts/modules/task-manager/expand-task.test.js b/tests/unit/scripts/modules/task-manager/expand-task.test.js index a43bf42b..4f6b08c9 100644 --- a/tests/unit/scripts/modules/task-manager/expand-task.test.js +++ b/tests/unit/scripts/modules/task-manager/expand-task.test.js @@ -153,13 +153,6 @@ jest.unstable_mockModule( }) ); -jest.unstable_mockModule( - '../../../../../scripts/modules/task-manager/generate-task-files.js', - () => ({ - default: jest.fn().mockResolvedValue() - }) -); - jest.unstable_mockModule( '../../../../../scripts/modules/prompt-manager.js', () => ({ @@ -246,12 +239,6 @@ const { generateObjectService } = await import( '../../../../../scripts/modules/ai-services-unified.js' ); -const generateTaskFiles = ( - await import( - '../../../../../scripts/modules/task-manager/generate-task-files.js' - ) -).default; - const { getDefaultSubtasks } = await import( '../../../../../scripts/modules/config-manager.js' ); @@ -365,7 +352,6 @@ describe('expandTask', () => { findProjectRoot.mockReturnValue('/mock/project/root'); writeJSON.mockResolvedValue(); - generateTaskFiles.mockResolvedValue(); log.mockImplementation(() => {}); // Mock console.log to avoid output during tests diff --git a/tests/unit/scripts/modules/task-manager/generate-task-files.test.js b/tests/unit/scripts/modules/task-manager/generate-task-files.test.js deleted file mode 100644 index d5e47b26..00000000 --- a/tests/unit/scripts/modules/task-manager/generate-task-files.test.js +++ /dev/null @@ -1,306 +0,0 @@ -/** - * Tests for the generate-task-files.js module - */ -import { jest } from '@jest/globals'; - -// Mock the dependencies before importing the module under test -jest.unstable_mockModule('fs', () => ({ - default: { - existsSync: jest.fn(), - mkdirSync: jest.fn(), - readdirSync: jest.fn(), - unlinkSync: jest.fn(), - writeFileSync: jest.fn() - }, - existsSync: jest.fn(), - mkdirSync: jest.fn(), - readdirSync: jest.fn(), - unlinkSync: jest.fn(), - writeFileSync: jest.fn() -})); - -jest.unstable_mockModule('path', () => ({ - default: { - join: jest.fn((...args) => args.join('/')), - dirname: jest.fn((p) => p.split('/').slice(0, -1).join('/')) - }, - join: jest.fn((...args) => args.join('/')), - dirname: jest.fn((p) => p.split('/').slice(0, -1).join('/')) -})); - -jest.unstable_mockModule('../../../../../scripts/modules/utils.js', () => ({ - readJSON: jest.fn(), - writeJSON: jest.fn(), - log: jest.fn(), - CONFIG: { - model: 'mock-claude-model', - maxTokens: 4000, - temperature: 0.7, - debug: false - }, - sanitizePrompt: jest.fn((prompt) => prompt), - truncate: jest.fn((text) => text), - isSilentMode: jest.fn(() => false), - findTaskById: jest.fn((tasks, id) => - tasks.find((t) => t.id === parseInt(id)) - ), - findProjectRoot: jest.fn(() => '/mock/project/root'), - resolveEnvVariable: jest.fn((varName) => `mock_${varName}`), - ensureTagMetadata: jest.fn() -})); - -jest.unstable_mockModule('../../../../../scripts/modules/ui.js', () => ({ - formatDependenciesWithStatus: jest.fn(), - displayBanner: jest.fn(), - displayTaskList: jest.fn(), - startLoadingIndicator: jest.fn(() => ({ stop: jest.fn() })), - stopLoadingIndicator: jest.fn(), - createProgressBar: jest.fn(() => ' MOCK_PROGRESS_BAR '), - getStatusWithColor: jest.fn((status) => status), - getComplexityWithColor: jest.fn((score) => `Score: ${score}`) -})); - -jest.unstable_mockModule( - '../../../../../scripts/modules/dependency-manager.js', - () => ({ - validateAndFixDependencies: jest.fn(), - validateTaskDependencies: jest.fn() - }) -); - -jest.unstable_mockModule( - '../../../../../scripts/modules/config-manager.js', - () => ({ - getDebugFlag: jest.fn(() => false), - getProjectName: jest.fn(() => 'Test Project') - }) -); - -// Import the mocked modules -const { readJSON, writeJSON, log, findProjectRoot, ensureTagMetadata } = - await import('../../../../../scripts/modules/utils.js'); -const { formatDependenciesWithStatus } = await import( - '../../../../../scripts/modules/ui.js' -); -const { validateAndFixDependencies } = await import( - '../../../../../scripts/modules/dependency-manager.js' -); - -const fs = (await import('fs')).default; -const path = (await import('path')).default; - -// Import the module under test -const { default: generateTaskFiles } = await import( - '../../../../../scripts/modules/task-manager/generate-task-files.js' -); - -describe('generateTaskFiles', () => { - // Sample task data for testing - updated to tagged format - const sampleTasksData = { - master: { - tasks: [ - { - id: 1, - title: 'Task 1', - description: 'First task description', - status: 'pending', - dependencies: [], - priority: 'high', - details: 'Detailed information for task 1', - testStrategy: 'Test strategy for task 1' - }, - { - id: 2, - title: 'Task 2', - description: 'Second task description', - status: 'pending', - dependencies: [1], - priority: 'medium', - details: 'Detailed information for task 2', - testStrategy: 'Test strategy for task 2' - }, - { - id: 3, - title: 'Task with Subtasks', - description: 'Task with subtasks description', - status: 'pending', - dependencies: [1, 2], - priority: 'high', - details: 'Detailed information for task 3', - testStrategy: 'Test strategy for task 3', - subtasks: [ - { - id: 1, - title: 'Subtask 1', - description: 'First subtask', - status: 'pending', - dependencies: [], - details: 'Details for subtask 1' - }, - { - id: 2, - title: 'Subtask 2', - description: 'Second subtask', - status: 'pending', - dependencies: [1], - details: 'Details for subtask 2' - } - ] - } - ], - metadata: { - projectName: 'Test Project', - created: '2024-01-01T00:00:00.000Z', - updated: '2024-01-01T00:00:00.000Z' - } - } - }; - - beforeEach(() => { - jest.clearAllMocks(); - // Mock readJSON to return the full tagged structure - readJSON.mockImplementation((tasksPath, projectRoot, tag) => { - if (tag && sampleTasksData[tag]) { - return { - ...sampleTasksData[tag], - tag, - _rawTaggedData: sampleTasksData - }; - } - // Default to master if no tag or tag not found - return { - ...sampleTasksData.master, - tag: 'master', - _rawTaggedData: sampleTasksData - }; - }); - }); - - test('should generate task files from tasks.json - working test', async () => { - // Set up mocks for this specific test - fs.existsSync.mockReturnValue(true); - - // Call the function - const tasksPath = 'tasks/tasks.json'; - const outputDir = 'tasks'; - - await generateTaskFiles(tasksPath, outputDir, { - tag: 'master', - mcpLog: { info: jest.fn() } - }); - - // Verify the data was read with new signature, defaulting to master - expect(readJSON).toHaveBeenCalledWith(tasksPath, undefined, 'master'); - - // Verify dependencies were validated with the raw tagged data - expect(validateAndFixDependencies).toHaveBeenCalledWith( - sampleTasksData, - tasksPath, - undefined, - 'master' - ); - - // Verify files were written for each task in the master tag - expect(fs.writeFileSync).toHaveBeenCalledTimes(3); - - // Verify specific file paths - expect(fs.writeFileSync).toHaveBeenCalledWith( - 'tasks/task_001.txt', - expect.any(String) - ); - expect(fs.writeFileSync).toHaveBeenCalledWith( - 'tasks/task_002.txt', - expect.any(String) - ); - expect(fs.writeFileSync).toHaveBeenCalledWith( - 'tasks/task_003.txt', - expect.any(String) - ); - }); - - test('should format dependencies with status indicators', async () => { - // Set up mocks - fs.existsSync.mockReturnValue(true); - formatDependenciesWithStatus.mockReturnValue( - '✅ Task 1 (done), ⏱️ Task 2 (pending)' - ); - - // Call the function - await generateTaskFiles('tasks/tasks.json', 'tasks', { - tag: 'master', - mcpLog: { info: jest.fn() } - }); - - // Verify formatDependenciesWithStatus was called for tasks with dependencies - // It will be called multiple times, once for each task that has dependencies. - expect(formatDependenciesWithStatus).toHaveBeenCalled(); - }); - - test('should handle tasks with no subtasks', async () => { - // Create data with tasks that have no subtasks - updated to tagged format - const tasksWithoutSubtasks = { - master: { - tasks: [ - { - id: 1, - title: 'Simple Task', - description: 'A simple task without subtasks', - status: 'pending', - dependencies: [], - priority: 'medium', - details: 'Simple task details', - testStrategy: 'Simple test strategy' - } - ], - metadata: { - projectName: 'Test Project', - created: '2024-01-01T00:00:00.000Z', - updated: '2024-01-01T00:00:00.000Z' - } - } - }; - - // Update the mock for this specific test case - readJSON.mockImplementation((tasksPath, projectRoot, tag) => { - return { - ...tasksWithoutSubtasks.master, - tag: 'master', - _rawTaggedData: tasksWithoutSubtasks - }; - }); - - fs.existsSync.mockReturnValue(true); - - // Call the function - await generateTaskFiles('tasks/tasks.json', 'tasks', { - tag: 'master', - mcpLog: { info: jest.fn() } - }); - - // Verify the file was written - expect(fs.writeFileSync).toHaveBeenCalledTimes(1); - expect(fs.writeFileSync).toHaveBeenCalledWith( - 'tasks/task_001.txt', - expect.any(String) - ); - }); - - test('should validate dependencies before generating files', async () => { - // Set up mocks - fs.existsSync.mockReturnValue(true); - - // Call the function - await generateTaskFiles('tasks/tasks.json', 'tasks', { - tag: 'master', - mcpLog: { info: jest.fn() } - }); - - // Verify validateAndFixDependencies was called with the raw tagged data - expect(validateAndFixDependencies).toHaveBeenCalledWith( - sampleTasksData, - 'tasks/tasks.json', - undefined, - 'master' - ); - }); -}); diff --git a/tests/unit/scripts/modules/task-manager/move-task-cross-tag.test.js b/tests/unit/scripts/modules/task-manager/move-task-cross-tag.test.js index 6126abb6..168cd5b7 100644 --- a/tests/unit/scripts/modules/task-manager/move-task-cross-tag.test.js +++ b/tests/unit/scripts/modules/task-manager/move-task-cross-tag.test.js @@ -42,13 +42,6 @@ jest.unstable_mockModule('../../../../../scripts/modules/utils.js', () => ({ }) })); -jest.unstable_mockModule( - '../../../../../scripts/modules/task-manager/generate-task-files.js', - () => ({ - default: jest.fn().mockResolvedValue() - }) -); - jest.unstable_mockModule( '../../../../../scripts/modules/task-manager.js', () => ({ diff --git a/tests/unit/scripts/modules/task-manager/move-task.test.js b/tests/unit/scripts/modules/task-manager/move-task.test.js index aadcd6f5..793203be 100644 --- a/tests/unit/scripts/modules/task-manager/move-task.test.js +++ b/tests/unit/scripts/modules/task-manager/move-task.test.js @@ -10,13 +10,6 @@ jest.unstable_mockModule('../../../../../scripts/modules/utils.js', () => ({ traverseDependencies: jest.fn(() => []) })); -jest.unstable_mockModule( - '../../../../../scripts/modules/task-manager/generate-task-files.js', - () => ({ - default: jest.fn().mockResolvedValue() - }) -); - jest.unstable_mockModule( '../../../../../scripts/modules/task-manager/is-task-dependent.js', () => ({ @@ -36,11 +29,6 @@ jest.unstable_mockModule( const { readJSON, writeJSON, log } = await import( '../../../../../scripts/modules/utils.js' ); -const generateTaskFiles = ( - await import( - '../../../../../scripts/modules/task-manager/generate-task-files.js' - ) -).default; const { default: moveTask } = await import( '../../../../../scripts/modules/task-manager/move-task.js' diff --git a/tests/unit/scripts/modules/task-manager/parse-prd.test.js b/tests/unit/scripts/modules/task-manager/parse-prd.test.js index 17d64e7e..9ec4742c 100644 --- a/tests/unit/scripts/modules/task-manager/parse-prd.test.js +++ b/tests/unit/scripts/modules/task-manager/parse-prd.test.js @@ -192,13 +192,6 @@ jest.unstable_mockModule( }) ); -jest.unstable_mockModule( - '../../../../../scripts/modules/task-manager/generate-task-files.js', - () => ({ - default: jest.fn().mockResolvedValue() - }) -); - jest.unstable_mockModule( '../../../../../scripts/modules/task-manager/models.js', () => ({ @@ -353,13 +346,6 @@ const { displayParsePrdStart, displayParsePrdSummary } = await import( '../../../../../src/ui/parse-prd.js' ); -// Note: getDefaultNumTasks validation happens at CLI/MCP level, not in the main parse-prd module -const generateTaskFiles = ( - await import( - '../../../../../scripts/modules/task-manager/generate-task-files.js' - ) -).default; - const fs = await import('fs'); const path = await import('path'); diff --git a/tests/unit/scripts/modules/task-manager/remove-task.test.js b/tests/unit/scripts/modules/task-manager/remove-task.test.js index 3b716195..4aaa4f45 100644 --- a/tests/unit/scripts/modules/task-manager/remove-task.test.js +++ b/tests/unit/scripts/modules/task-manager/remove-task.test.js @@ -16,13 +16,6 @@ jest.unstable_mockModule('../../../../../scripts/modules/utils.js', () => ({ isSilentMode: jest.fn(() => false) })); -jest.unstable_mockModule( - '../../../../../scripts/modules/task-manager/generate-task-files.js', - () => ({ - default: jest.fn().mockResolvedValue() - }) -); - // fs is used for file deletion side-effects – stub the methods we touch jest.unstable_mockModule('fs', () => ({ existsSync: jest.fn(() => true), @@ -35,11 +28,6 @@ jest.unstable_mockModule('fs', () => ({ const { readJSON, writeJSON, log } = await import( '../../../../../scripts/modules/utils.js' ); -const generateTaskFiles = ( - await import( - '../../../../../scripts/modules/task-manager/generate-task-files.js' - ) -).default; const fs = await import('fs'); // Import module under test (AFTER mocks in place) diff --git a/tests/unit/scripts/modules/task-manager/set-task-status.test.js b/tests/unit/scripts/modules/task-manager/set-task-status.test.js index 72e75b95..7628b97c 100644 --- a/tests/unit/scripts/modules/task-manager/set-task-status.test.js +++ b/tests/unit/scripts/modules/task-manager/set-task-status.test.js @@ -24,13 +24,6 @@ jest.unstable_mockModule('../../../../../scripts/modules/utils.js', () => ({ getCurrentTag: jest.fn(() => 'master') })); -jest.unstable_mockModule( - '../../../../../scripts/modules/task-manager/generate-task-files.js', - () => ({ - default: jest.fn().mockResolvedValue() - }) -); - jest.unstable_mockModule('../../../../../scripts/modules/ui.js', () => ({ formatDependenciesWithStatus: jest.fn(), displayBanner: jest.fn(), @@ -87,12 +80,6 @@ const { readJSON, writeJSON, log, findTaskById } = await import( '../../../../../scripts/modules/utils.js' ); -const generateTaskFiles = ( - await import( - '../../../../../scripts/modules/task-manager/generate-task-files.js' - ) -).default; - const updateSingleTaskStatus = ( await import( '../../../../../scripts/modules/task-manager/update-single-task-status.js' diff --git a/tests/unit/scripts/modules/task-manager/update-subtask-by-id.test.js b/tests/unit/scripts/modules/task-manager/update-subtask-by-id.test.js index fa11ef34..693f326d 100644 --- a/tests/unit/scripts/modules/task-manager/update-subtask-by-id.test.js +++ b/tests/unit/scripts/modules/task-manager/update-subtask-by-id.test.js @@ -44,13 +44,6 @@ jest.unstable_mockModule('../../../../../scripts/modules/ui.js', () => ({ displayContextAnalysis: jest.fn() })); -jest.unstable_mockModule( - '../../../../../scripts/modules/task-manager/generate-task-files.js', - () => ({ - default: jest.fn().mockResolvedValue() - }) -); - jest.unstable_mockModule( '../../../../../scripts/modules/ai-services-unified.js', () => ({ diff --git a/tests/unit/scripts/modules/task-manager/update-task-by-id.test.js b/tests/unit/scripts/modules/task-manager/update-task-by-id.test.js index 1499e6e1..a40c714d 100644 --- a/tests/unit/scripts/modules/task-manager/update-task-by-id.test.js +++ b/tests/unit/scripts/modules/task-manager/update-task-by-id.test.js @@ -42,13 +42,6 @@ jest.unstable_mockModule('../../../../../scripts/modules/ui.js', () => ({ displayContextAnalysis: jest.fn() })); -jest.unstable_mockModule( - '../../../../../scripts/modules/task-manager/generate-task-files.js', - () => ({ - default: jest.fn().mockResolvedValue() - }) -); - jest.unstable_mockModule( '../../../../../scripts/modules/ai-services-unified.js', () => ({ diff --git a/tests/unit/scripts/modules/task-manager/update-tasks.test.js b/tests/unit/scripts/modules/task-manager/update-tasks.test.js index 1cc241ec..fdbe1f18 100644 --- a/tests/unit/scripts/modules/task-manager/update-tasks.test.js +++ b/tests/unit/scripts/modules/task-manager/update-tasks.test.js @@ -55,13 +55,6 @@ jest.unstable_mockModule( }) ); -jest.unstable_mockModule( - '../../../../../scripts/modules/task-manager/generate-task-files.js', - () => ({ - default: jest.fn().mockResolvedValue() - }) -); - jest.unstable_mockModule( '../../../../../scripts/modules/prompt-manager.js', () => ({