Added confirmation for task overwrite if tasks.json exists.

Slight refactor moving numTasks and outputPath to top with the other variables.  Eliminates duplication, and keeps us from having to check path twice.
Resolves #65
This commit is contained in:
Joe Danziger
2025-03-30 18:30:00 -04:00
parent 4eed269378
commit 7086a77625
2 changed files with 55 additions and 7 deletions

View File

@@ -38,7 +38,8 @@ import {
displayNextTask, displayNextTask,
displayTaskById, displayTaskById,
displayComplexityReport, displayComplexityReport,
getStatusWithColor getStatusWithColor,
confirmTaskOverwrite
} from './ui.js'; } from './ui.js';
/** /**
@@ -59,17 +60,34 @@ function registerCommands(programInstance) {
.option('-i, --input <file>', 'Path to the PRD file (alternative to positional argument)') .option('-i, --input <file>', 'Path to the PRD file (alternative to positional argument)')
.option('-o, --output <file>', 'Output file path', 'tasks/tasks.json') .option('-o, --output <file>', 'Output file path', 'tasks/tasks.json')
.option('-n, --num-tasks <number>', 'Number of tasks to generate', '10') .option('-n, --num-tasks <number>', 'Number of tasks to generate', '10')
.option('-f, --force', 'Skip confirmation when overwriting existing tasks')
.action(async (file, options) => { .action(async (file, options) => {
// Use input option if file argument not provided // Use input option if file argument not provided
const inputFile = file || options.input; const inputFile = file || options.input;
const defaultPrdPath = 'scripts/prd.txt'; const defaultPrdPath = 'scripts/prd.txt';
const numTasks = parseInt(options.numTasks, 10);
const outputPath = options.output;
const force = options.force || false;
// Helper function to check if tasks.json exists and confirm overwrite
async function confirmOverwriteIfNeeded() {
if (fs.existsSync(outputPath) && !force) {
const shouldContinue = await confirmTaskOverwrite(outputPath);
if (!shouldContinue) {
console.log(chalk.yellow('Operation cancelled by user.'));
return false;
}
}
return true;
}
// If no input file specified, check for default PRD location // If no input file specified, check for default PRD location
if (!inputFile) { if (!inputFile) {
if (fs.existsSync(defaultPrdPath)) { if (fs.existsSync(defaultPrdPath)) {
console.log(chalk.blue(`Using default PRD file: ${defaultPrdPath}`)); console.log(chalk.blue(`Using default PRD file: ${defaultPrdPath}`));
const numTasks = parseInt(options.numTasks, 10);
const outputPath = options.output; // Check for existing tasks.json before proceeding
if (!await confirmOverwriteIfNeeded()) return;
console.log(chalk.blue(`Generating ${numTasks} tasks...`)); console.log(chalk.blue(`Generating ${numTasks} tasks...`));
await parsePRD(defaultPrdPath, outputPath, numTasks); await parsePRD(defaultPrdPath, outputPath, numTasks);
@@ -84,10 +102,12 @@ function registerCommands(programInstance) {
chalk.cyan('Options:') + '\n' + chalk.cyan('Options:') + '\n' +
' -i, --input <file> Path to the PRD file (alternative to positional argument)\n' + ' -i, --input <file> Path to the PRD file (alternative to positional argument)\n' +
' -o, --output <file> Output file path (default: "tasks/tasks.json")\n' + ' -o, --output <file> Output file path (default: "tasks/tasks.json")\n' +
' -n, --num-tasks <number> Number of tasks to generate (default: 10)\n\n' + ' -n, --num-tasks <number> Number of tasks to generate (default: 10)\n' +
' -f, --force Skip confirmation when overwriting existing tasks\n\n' +
chalk.cyan('Example:') + '\n' + chalk.cyan('Example:') + '\n' +
' task-master parse-prd requirements.txt --num-tasks 15\n' + ' task-master parse-prd requirements.txt --num-tasks 15\n' +
' task-master parse-prd --input=requirements.txt\n\n' + ' task-master parse-prd --input=requirements.txt\n' +
' task-master parse-prd --force\n\n' +
chalk.yellow('Note: This command will:') + '\n' + chalk.yellow('Note: This command will:') + '\n' +
' 1. Look for a PRD file at scripts/prd.txt by default\n' + ' 1. Look for a PRD file at scripts/prd.txt by default\n' +
' 2. Use the file specified by --input or positional argument if provided\n' + ' 2. Use the file specified by --input or positional argument if provided\n' +
@@ -97,8 +117,8 @@ function registerCommands(programInstance) {
return; return;
} }
const numTasks = parseInt(options.numTasks, 10); // Check for existing tasks.json before proceeding with specified input file
const outputPath = options.output; if (!await confirmOverwriteIfNeeded()) return;
console.log(chalk.blue(`Parsing PRD file: ${inputFile}`)); console.log(chalk.blue(`Parsing PRD file: ${inputFile}`));
console.log(chalk.blue(`Generating ${numTasks} tasks...`)); console.log(chalk.blue(`Generating ${numTasks} tasks...`));

View File

@@ -1052,6 +1052,33 @@ async function displayComplexityReport(reportPath) {
)); ));
} }
/**
* Confirm overwriting existing tasks.json file
* @param {string} tasksPath - Path to the tasks.json file
* @returns {Promise<boolean>} - Promise resolving to true if user confirms, false otherwise
*/
async function confirmTaskOverwrite(tasksPath) {
console.log(boxen(
chalk.yellow('It looks like you\'ve already generated tasks for this project.\n') +
chalk.yellow('Executing this command will overwrite any existing tasks.'),
{ padding: 1, borderColor: 'yellow', borderStyle: 'round', margin: { top: 1 } }
));
// Use dynamic import to get the readline module
const readline = await import('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
const answer = await new Promise(resolve => {
rl.question(chalk.cyan('Are you sure you wish to continue? (y/N): '), resolve);
});
rl.close();
return answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes';
}
// Export UI functions // Export UI functions
export { export {
displayBanner, displayBanner,
@@ -1065,4 +1092,5 @@ export {
displayNextTask, displayNextTask,
displayTaskById, displayTaskById,
displayComplexityReport, displayComplexityReport,
confirmTaskOverwrite
}; };