mirror of
https://github.com/eyaltoledano/claude-task-master.git
synced 2026-01-30 06:12:05 +00:00
feat: deprecate generate command
This commit is contained in:
5
.changeset/rich-kings-fold.md
Normal file
5
.changeset/rich-kings-fold.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"task-master-ai": minor
|
||||
---
|
||||
|
||||
Deprecated generate command
|
||||
@@ -32,7 +32,6 @@ task-master expand --all --research # Expand all eligible tasks
|
||||
task-master add-dependency --id=<id> --depends-on=<id> # Add task dependency
|
||||
task-master move --from=<id> --to=<id> # 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
|
||||
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -103,13 +103,6 @@ task-master update-subtask --id=<parentId.subtaskId> --prompt="<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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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<Object>} - 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'
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
@@ -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'
|
||||
];
|
||||
|
||||
@@ -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
|
||||
@@ -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 <file>',
|
||||
'Path to the tasks file',
|
||||
TASKMASTER_TASKS_FILE
|
||||
)
|
||||
.option(
|
||||
'-o, --output <dir>',
|
||||
'Output directory',
|
||||
path.dirname(TASKMASTER_TASKS_FILE)
|
||||
)
|
||||
.option('--tag <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
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
@@ -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';
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}));
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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(() =>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -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',
|
||||
() => ({
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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',
|
||||
() => ({
|
||||
|
||||
@@ -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',
|
||||
() => ({
|
||||
|
||||
@@ -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',
|
||||
() => ({
|
||||
|
||||
Reference in New Issue
Block a user