diff --git a/bin/task-master-init.js b/bin/task-master-init.js deleted file mode 100755 index a4f27ffb..00000000 --- a/bin/task-master-init.js +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env node - -/** - * Claude Task Master Init - * Direct executable for the init command - */ - -import { spawn } from 'child_process'; -import { fileURLToPath } from 'url'; -import { dirname, resolve } from 'path'; - -const __filename = fileURLToPath(import.meta.url); -const __dirname = dirname(__filename); - -// Get the path to the init script -const initScriptPath = resolve(__dirname, '../scripts/init.js'); - -// Pass through all arguments -const args = process.argv.slice(2); - -// Spawn the init script with all arguments -const child = spawn('node', [initScriptPath, ...args], { - stdio: 'inherit', - cwd: process.cwd() -}); - -// Handle exit -child.on('close', (code) => { - process.exit(code); -}); diff --git a/bin/task-master.js b/bin/task-master.js index afa40fc8..ea1c9176 100755 --- a/bin/task-master.js +++ b/bin/task-master.js @@ -225,47 +225,47 @@ function createDevScriptAction(commandName) { }; } -// Special case for the 'init' command which uses a different script -function registerInitCommand(program) { - program - .command('init') - .description('Initialize a new project') - .option('-y, --yes', 'Skip prompts and use default values') - .option('-n, --name ', 'Project name') - .option('-d, --description ', 'Project description') - .option('-v, --version ', 'Project version') - .option('-a, --author ', 'Author name') - .option('--skip-install', 'Skip installing dependencies') - .option('--dry-run', 'Show what would be done without making changes') - .action((options) => { - // Pass through any options to the init script - const args = [ - '--yes', - 'name', - 'description', - 'version', - 'author', - 'skip-install', - 'dry-run' - ] - .filter((opt) => options[opt]) - .map((opt) => { - if (opt === 'yes' || opt === 'skip-install' || opt === 'dry-run') { - return `--${opt}`; - } - return `--${opt}=${options[opt]}`; - }); +// // Special case for the 'init' command which uses a different script +// function registerInitCommand(program) { +// program +// .command('init') +// .description('Initialize a new project') +// .option('-y, --yes', 'Skip prompts and use default values') +// .option('-n, --name ', 'Project name') +// .option('-d, --description ', 'Project description') +// .option('-v, --version ', 'Project version') +// .option('-a, --author ', 'Author name') +// .option('--skip-install', 'Skip installing dependencies') +// .option('--dry-run', 'Show what would be done without making changes') +// .action((options) => { +// // Pass through any options to the init script +// const args = [ +// '--yes', +// 'name', +// 'description', +// 'version', +// 'author', +// 'skip-install', +// 'dry-run' +// ] +// .filter((opt) => options[opt]) +// .map((opt) => { +// if (opt === 'yes' || opt === 'skip-install' || opt === 'dry-run') { +// return `--${opt}`; +// } +// return `--${opt}=${options[opt]}`; +// }); - const child = spawn('node', [initScriptPath, ...args], { - stdio: 'inherit', - cwd: process.cwd() - }); +// const child = spawn('node', [initScriptPath, ...args], { +// stdio: 'inherit', +// cwd: process.cwd() +// }); - child.on('close', (code) => { - process.exit(code); - }); - }); -} +// child.on('close', (code) => { +// process.exit(code); +// }); +// }); +// } // Set up the command-line interface const program = new Command(); @@ -286,8 +286,8 @@ program.on('--help', () => { displayHelp(); }); -// Add special case commands -registerInitCommand(program); +// // Add special case commands +// registerInitCommand(program); program .command('dev') @@ -303,7 +303,7 @@ registerCommands(tempProgram); // For each command in the temp instance, add a modified version to our actual program tempProgram.commands.forEach((cmd) => { - if (['init', 'dev'].includes(cmd.name())) { + if (['dev'].includes(cmd.name())) { // Skip commands we've already defined specially return; } diff --git a/mcp-server/src/tools/initialize-project.js b/mcp-server/src/tools/initialize-project.js index 3f47c615..fdb3c0ff 100644 --- a/mcp-server/src/tools/initialize-project.js +++ b/mcp-server/src/tools/initialize-project.js @@ -1,56 +1,97 @@ import { z } from 'zod'; import { execSync } from 'child_process'; -import { createContentResponse, createErrorResponse } from './utils.js'; // Only need response creators +import { + createContentResponse, + createErrorResponse, + getProjectRootFromSession +} from './utils.js'; export function registerInitializeProjectTool(server) { server.addTool({ - name: 'initialize_project', // snake_case for tool name + name: 'initialize_project', description: - "Initializes a new Task Master project structure in the current working directory by running 'task-master init'.", + "Initializes a new Task Master project structure in the specified project directory by running 'task-master init'. If the project information required for the initialization is not available or provided by the user, prompt if the user wishes to provide them (name, description, author) or skip them. If the user wishes to skip, set the 'yes' flag to true and do not set any other parameters. DO NOT run the initialize_project tool without parameters.", parameters: z.object({ projectName: z .string() .optional() - .describe('The name for the new project.'), + .describe( + 'The name for the new project. If not provided, prompt the user for it.' + ), projectDescription: z .string() .optional() - .describe('A brief description for the project.'), + .describe( + 'A brief description for the project. If not provided, prompt the user for it.' + ), projectVersion: z .string() .optional() - .describe("The initial version for the project (e.g., '0.1.0')."), - authorName: z.string().optional().describe("The author's name."), + .describe( + "The initial version for the project (e.g., '0.1.0'). User input not needed unless user requests to override." + ), + authorName: z + .string() + .optional() + .describe( + "The author's name. User input not needed unless user requests to override." + ), skipInstall: z .boolean() .optional() .default(false) - .describe('Skip installing dependencies automatically.'), + .describe( + 'Skip installing dependencies automatically. Never do this unless you are sure the project is already installed.' + ), addAliases: z .boolean() .optional() .default(false) - .describe('Add shell aliases (tm, taskmaster) to shell config file.'), + .describe( + 'Add shell aliases (tm, taskmaster) to shell config file. User input not needed.' + ), yes: z .boolean() .optional() .default(false) - .describe('Skip prompts and use default values or provided arguments.') - // projectRoot is not needed here as 'init' works on the current directory + .describe( + "Skip prompts and use default values or provided arguments. Use true if you wish to skip details like the project name, etc. If the project information required for the initialization is not available or provided by the user, prompt if the user wishes to provide them (name, description, author) or skip them. If the user wishes to skip, set the 'yes' flag to true and do not set any other parameters." + ), + projectRoot: z + .string() + .optional() + .describe( + 'Optional fallback project root if session data is unavailable. Setting a value is not needed unless you are running the tool from a different directory than the project root.' + ) }), - execute: async (args, { log }) => { - // Destructure context to get log + execute: async (args, { log, session }) => { try { log.info( `Executing initialize_project with args: ${JSON.stringify(args)}` ); - // Construct the command arguments carefully - // Using npx ensures it uses the locally installed version if available, or fetches it - let command = 'npx task-master init'; + let targetDirectory = getProjectRootFromSession(session, log); + if (!targetDirectory) { + if (args.projectRoot) { + targetDirectory = args.projectRoot; + log.warn( + `Using projectRoot argument as fallback: ${targetDirectory}` + ); + } else { + log.error( + 'Could not determine target directory for initialization from session or arguments.' + ); + return createErrorResponse( + 'Failed to determine target directory for initialization.' + ); + } + } + log.info(`Target directory for initialization: ${targetDirectory}`); + + let commandBase = 'npx task-master init'; const cliArgs = []; if (args.projectName) - cliArgs.push(`--name "${args.projectName.replace(/"/g, '\\"')}"`); // Escape quotes + cliArgs.push(`--name "${args.projectName.replace(/"/g, '\\"')}"`); if (args.projectDescription) cliArgs.push( `--description "${args.projectDescription.replace(/"/g, '\\"')}"` @@ -63,36 +104,47 @@ export function registerInitializeProjectTool(server) { cliArgs.push(`--author "${args.authorName.replace(/"/g, '\\"')}"`); if (args.skipInstall) cliArgs.push('--skip-install'); if (args.addAliases) cliArgs.push('--aliases'); - if (args.yes) cliArgs.push('--yes'); - command += ' ' + cliArgs.join(' '); + log.debug( + `Value of args.yes before check: ${args.yes} (Type: ${typeof args.yes})` + ); + if (args.yes === true) { + cliArgs.push('--yes'); + log.info('Added --yes flag to cliArgs.'); + } else { + log.info(`Did NOT add --yes flag. args.yes value: ${args.yes}`); + } - log.info(`Constructed command: ${command}`); + const command = + cliArgs.length > 0 + ? `${commandBase} ${cliArgs.join(' ')}` + : commandBase; + + log.info(`FINAL Constructed command for execSync: ${command}`); - // Execute the command in the current working directory of the server process - // Capture stdout/stderr. Use a reasonable timeout (e.g., 5 minutes) const output = execSync(command, { encoding: 'utf8', stdio: 'pipe', - timeout: 300000 + timeout: 300000, + cwd: targetDirectory }); log.info(`Initialization output:\n${output}`); - // Return a standard success response manually return createContentResponse({ - message: 'Taskmaster successfully initialized for this project.', + message: `Taskmaster successfully initialized in ${targetDirectory}.`, next_step: - 'Now that the project is initialized, the next step is to create the tasks by parsing a PRD. This will create the tasks folder and the initial task files. The parse-prd tool will required a prd.txt file as input in scripts/prd.txt. You can create a prd.txt file by asking the user about their idea, and then using the scripts/example_prd.txt file as a template to genrate a prd.txt file in scripts/. Before creating the PRD for the user, make sure you understand the idea fully and ask questions to eliminate ambiguity. You can then use the parse-prd tool to create the tasks. So: step 1 after initialization is to create a prd.txt file in scripts/prd.txt. Step 2 is to use the parse-prd tool to create the tasks. Do not bother looking for tasks after initialization, just use the parse-prd tool to create the tasks after creating a prd.txt from which to parse the tasks. ' + 'Now that the project is initialized, the next step is to create the tasks by parsing a PRD. This will create the tasks folder and the initial task files. The parse-prd tool will required a prd.txt file as input in scripts/prd.txt. You can create a prd.txt file by asking the user about their idea, and then using the scripts/example_prd.txt file as a template to genrate a prd.txt file in scripts/. Before creating the PRD for the user, make sure you understand the idea fully and ask questions to eliminate ambiguity. You can then use the parse-prd tool to create the tasks. So: step 1 after initialization is to create a prd.txt file in scripts/prd.txt. Step 2 is to use the parse-prd tool to create the tasks. Do not bother looking for tasks after initialization, just use the parse-prd tool to create the tasks after creating a prd.txt from which to parse the tasks. ', + output: output }); } catch (error) { - // Catch errors from execSync or timeouts - const errorMessage = `Project initialization failed: ${error.message}`; + const errorMessage = `Project initialization failed: ${ + error.message || 'Unknown error' + }`; const errorDetails = - error.stderr?.toString() || error.stdout?.toString() || error.message; // Provide stderr/stdout if available + error.stderr?.toString() || error.stdout?.toString() || error.message; log.error(`${errorMessage}\nDetails: ${errorDetails}`); - // Return a standard error response manually return createErrorResponse(errorMessage, { details: errorDetails }); } } diff --git a/package-lock.json b/package-lock.json index 889d9378..0e48d428 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,8 +32,7 @@ "bin": { "task-master": "bin/task-master.js", "task-master-init": "bin/task-master-init.js", - "task-master-mcp": "mcp-server/server.js", - "task-master-mcp-server": "mcp-server/server.js" + "task-master-mcp": "mcp-server/server.js" }, "devDependencies": { "@changesets/changelog-github": "^0.5.1", diff --git a/package.json b/package.json index d3640553..e9fa2e0b 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,6 @@ "type": "module", "bin": { "task-master": "bin/task-master.js", - "task-master-init": "bin/task-master-init.js", "task-master-mcp": "mcp-server/server.js" }, "scripts": { @@ -16,7 +15,7 @@ "test:coverage": "node --experimental-vm-modules node_modules/.bin/jest --coverage", "prepare-package": "node scripts/prepare-package.js", "prepublishOnly": "npm run prepare-package", - "prepare": "chmod +x bin/task-master.js bin/task-master-init.js mcp-server/server.js", + "prepare": "chmod +x bin/task-master.js mcp-server/server.js", "changeset": "changeset", "release": "changeset publish", "inspector": "npx @modelcontextprotocol/inspector node mcp-server/server.js", diff --git a/scripts/init.js b/scripts/init.js index 92d27fcf..90fc97c0 100755 --- a/scripts/init.js +++ b/scripts/init.js @@ -1,5 +1,3 @@ -#!/usr/bin/env node - /** * Task Master * Copyright (c) 2025 Eyal Toledano, Ralph Khreish @@ -27,7 +25,6 @@ import chalk from 'chalk'; import figlet from 'figlet'; import boxen from 'boxen'; import gradient from 'gradient-string'; -import { Command } from 'commander'; // Debug information console.log('Node version:', process.version); @@ -37,42 +34,6 @@ console.log('Script path:', import.meta.url); const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); -// Configure the CLI program -const program = new Command(); -program - .name('task-master-init') - .description('Initialize a new Claude Task Master project') - .version('1.0.0') // Will be replaced by prepare-package script - .option('-y, --yes', 'Skip prompts and use default values') - .option('-n, --name ', 'Project name') - .option('-my_name ', 'Project name (alias for --name)') - .option('-d, --description ', 'Project description') - .option( - '-my_description ', - 'Project description (alias for --description)' - ) - .option('-v, --version ', 'Project version') - .option('-my_version ', 'Project version (alias for --version)') - .option('--my_name ', 'Project name (alias for --name)') - .option('-a, --author ', 'Author name') - .option('--skip-install', 'Skip installing dependencies') - .option('--dry-run', 'Show what would be done without making changes') - .option('--aliases', 'Add shell aliases (tm, taskmaster)') - .parse(process.argv); - -const options = program.opts(); - -// Map custom aliases to standard options -if (options.my_name && !options.name) { - options.name = options.my_name; -} -if (options.my_description && !options.description) { - options.description = options.my_description; -} -if (options.my_version && !options.version) { - options.version = options.my_version; -} - // Define log levels const LOG_LEVELS = { debug: 0, @@ -419,27 +380,35 @@ function copyTemplateFile(templateName, targetPath, replacements = {}) { log('info', `Created file: ${targetPath}`); } -// Main function to initialize a new project -async function initializeProject(options = {}) { - // Display the banner +// Main function to initialize a new project (Now relies solely on passed options) +async function initializeProject(options = {}) { // Receives options as argument displayBanner(); - // If options are provided, use them directly without prompting - if (options.projectName && options.projectDescription) { - const projectName = options.projectName; - const projectDescription = options.projectDescription; - const projectVersion = options.projectVersion || '1.0.0'; - const authorName = options.authorName || ''; + console.log('===== DEBUG: INITIALIZE PROJECT OPTIONS RECEIVED ====='); + console.log('Full options object:', JSON.stringify(options)); + console.log('options.yes:', options.yes); + console.log('options.name:', options.name); + console.log('=================================================='); + + // Determine if we should skip prompts based on the passed options + const skipPrompts = options.yes || (options.name && options.description); + console.log('Skip prompts determined:', skipPrompts); + + if (skipPrompts) { + console.log('SKIPPING PROMPTS - Using defaults or provided values'); + + // Use provided options or defaults + const projectName = options.name || 'task-master-project'; + const projectDescription = options.description || 'A project managed with Task Master AI'; + const projectVersion = options.version || '0.1.0'; // Default from commands.js or here + const authorName = options.author || 'Vibe coder'; // Default if not provided const dryRun = options.dryRun || false; const skipInstall = options.skipInstall || false; - const addAliases = options.addAliases || false; + const addAliases = options.aliases || false; if (dryRun) { log('info', 'DRY RUN MODE: No files will be modified'); - log( - 'info', - `Would initialize project: ${projectName} (${projectVersion})` - ); + log('info', `Would initialize project: ${projectName} (${projectVersion})`); log('info', `Description: ${projectDescription}`); log('info', `Author: ${authorName || 'Not specified'}`); log('info', 'Would create/update necessary project files'); @@ -458,6 +427,7 @@ async function initializeProject(options = {}) { }; } + // Create structure using determined values createProjectStructure( projectName, projectDescription, @@ -466,120 +436,88 @@ async function initializeProject(options = {}) { skipInstall, addAliases ); - return { - projectName, - projectDescription, - projectVersion, - authorName - }; - } + } else { + // Prompting logic (only runs if skipPrompts is false) + log('info', 'Required options not provided, proceeding with prompts.'); + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout + }); - // Otherwise, prompt the user for input - // Create readline interface only when needed - const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout - }); + try { + // Prompt user for input... + const projectName = await promptQuestion(rl, chalk.cyan('Enter project name: ')); + const projectDescription = await promptQuestion(rl, chalk.cyan('Enter project description: ')); + const projectVersionInput = await promptQuestion(rl, chalk.cyan('Enter project version (default: 1.0.0): ')); // Use a default for prompt + const authorName = await promptQuestion(rl, chalk.cyan('Enter your name: ')); + const addAliasesInput = await promptQuestion(rl, chalk.cyan('Add shell aliases for task-master? (Y/n): ')); + const addAliasesPrompted = addAliasesInput.trim().toLowerCase() !== 'n'; + const projectVersion = projectVersionInput.trim() ? projectVersionInput : '1.0.0'; - try { - const projectName = await promptQuestion( - rl, - chalk.cyan('Enter project name: ') - ); - const projectDescription = await promptQuestion( - rl, - chalk.cyan('Enter project description: ') - ); - const projectVersionInput = await promptQuestion( - rl, - chalk.cyan('Enter project version (default: 1.0.0): ') - ); - const authorName = await promptQuestion( - rl, - chalk.cyan('Enter your name: ') - ); + // Confirm settings... + console.log('\nProject settings:'); + console.log(chalk.blue('Name:'), chalk.white(projectName)); + console.log(chalk.blue('Description:'), chalk.white(projectDescription)); + console.log(chalk.blue('Version:'), chalk.white(projectVersion)); + console.log( + chalk.blue('Author:'), + chalk.white(authorName || 'Not specified') + ); + console.log( + chalk.blue('Add shell aliases (so you can use "tm" instead of "task-master"):'), + chalk.white(addAliasesPrompted ? 'Yes' : 'No') + ); - // Ask about shell aliases - const addAliasesInput = await promptQuestion( - rl, - chalk.cyan('Add shell aliases for task-master? (Y/n): ') - ); - const addAliases = addAliasesInput.trim().toLowerCase() !== 'n'; + const confirmInput = await promptQuestion(rl, chalk.yellow('\nDo you want to continue with these settings? (Y/n): ')); + const shouldContinue = confirmInput.trim().toLowerCase() !== 'n'; + rl.close(); - // Set default version if not provided - const projectVersion = projectVersionInput.trim() - ? projectVersionInput - : '1.0.0'; - - // Confirm settings - console.log('\nProject settings:'); - console.log(chalk.blue('Name:'), chalk.white(projectName)); - console.log(chalk.blue('Description:'), chalk.white(projectDescription)); - console.log(chalk.blue('Version:'), chalk.white(projectVersion)); - console.log( - chalk.blue('Author:'), - chalk.white(authorName || 'Not specified') - ); - console.log( - chalk.blue('Add shell aliases:'), - chalk.white(addAliases ? 'Yes' : 'No') - ); - - const confirmInput = await promptQuestion( - rl, - chalk.yellow('\nDo you want to continue with these settings? (Y/n): ') - ); - const shouldContinue = confirmInput.trim().toLowerCase() !== 'n'; - - // Close the readline interface - rl.close(); - - if (!shouldContinue) { - log('info', 'Project initialization cancelled by user'); - return null; - } - - const dryRun = options.dryRun || false; - const skipInstall = options.skipInstall || false; - - if (dryRun) { - log('info', 'DRY RUN MODE: No files will be modified'); - log('info', 'Would create/update necessary project files'); - if (addAliases) { - log('info', 'Would add shell aliases for task-master'); + if (!shouldContinue) { + log('info', 'Project initialization cancelled by user'); + process.exit(0); // Exit if cancelled + return; // Added return for clarity } - if (!skipInstall) { - log('info', 'Would install dependencies'); + + // Still respect dryRun/skipInstall if passed initially even when prompting + const dryRun = options.dryRun || false; + const skipInstall = options.skipInstall || false; + + if (dryRun) { + log('info', 'DRY RUN MODE: No files will be modified'); + log('info', `Would initialize project: ${projectName} (${projectVersion})`); + log('info', `Description: ${projectDescription}`); + log('info', `Author: ${authorName || 'Not specified'}`); + log('info', 'Would create/update necessary project files'); + if (addAliasesPrompted) { + log('info', 'Would add shell aliases for task-master'); + } + if (!skipInstall) { + log('info', 'Would install dependencies'); + } + return { + projectName, + projectDescription, + projectVersion, + authorName, + dryRun: true + }; } - return { + + // Create structure using prompted values, respecting initial options where relevant + createProjectStructure( projectName, projectDescription, projectVersion, authorName, - dryRun: true - }; + skipInstall, // Use value from initial options + addAliasesPrompted // Use value from prompt + ); + + } catch (error) { + rl.close(); + log('error', `Error during prompting: ${error.message}`); // Use log function + process.exit(1); // Exit on error during prompts } - - // Create the project structure - createProjectStructure( - projectName, - projectDescription, - projectVersion, - authorName, - skipInstall, - addAliases - ); - - return { - projectName, - projectDescription, - projectVersion, - authorName - }; - } catch (error) { - // Make sure to close readline on error - rl.close(); - throw error; } } @@ -985,51 +923,5 @@ function setupMCPConfiguration(targetDir, projectName) { log('info', 'MCP server will use the installed task-master-ai package'); } -// Run the initialization if this script is executed directly -// The original check doesn't work with npx and global commands -// if (process.argv[1] === fileURLToPath(import.meta.url)) { -// Instead, we'll always run the initialization if this file is the main module -console.log('Checking if script should run initialization...'); -console.log('import.meta.url:', import.meta.url); -console.log('process.argv:', process.argv); - -// Always run initialization when this file is loaded directly -// This works with both direct node execution and npx/global commands -(async function main() { - try { - console.log('Starting initialization...'); - - // Check if we should use the CLI options or prompt for input - if (options.yes || (options.name && options.description)) { - // When using --yes flag or providing name and description, use CLI options - await initializeProject({ - projectName: options.name || 'task-master-project', - projectDescription: - options.description || - 'A task management system for AI-driven development', - projectVersion: options.version || '1.0.0', - authorName: options.author || '', - dryRun: options.dryRun || false, - skipInstall: options.skipInstall || false, - addAliases: options.aliases || false - }); - } else { - // Otherwise, prompt for input normally - await initializeProject({ - dryRun: options.dryRun || false, - skipInstall: options.skipInstall || false - }); - } - - // Process should exit naturally after completion - console.log('Initialization completed, exiting...'); - process.exit(0); - } catch (error) { - console.error('Failed to initialize project:', error); - log('error', 'Failed to initialize project:', error); - process.exit(1); - } -})(); - -// Export functions for programmatic use -export { initializeProject, createProjectStructure, log }; +// Ensure necessary functions are exported +export { initializeProject, log }; // Only export what's needed by commands.js diff --git a/scripts/modules/commands.js b/scripts/modules/commands.js index 3bc79fd8..9e42e42f 100644 --- a/scripts/modules/commands.js +++ b/scripts/modules/commands.js @@ -10,8 +10,9 @@ import boxen from 'boxen'; import fs from 'fs'; import https from 'https'; import inquirer from 'inquirer'; +import ora from 'ora'; -import { CONFIG, log, readJSON } from './utils.js'; +import { CONFIG, log, readJSON, writeJSON } from './utils.js'; import { parsePRD, updateTasks, @@ -51,6 +52,8 @@ import { stopLoadingIndicator } from './ui.js'; +import { initializeProject } from '../init.js'; + /** * Configure and register CLI commands * @param {Object} program - Commander program instance @@ -1368,44 +1371,6 @@ function registerCommands(programInstance) { ); } - // init command (documentation only, implementation is in init.js) - programInstance - .command('init') - .description('Initialize a new project with Task Master structure') - .option('-n, --name ', 'Project name') - .option('-my_name ', 'Project name (alias for --name)') - .option('--my_name ', 'Project name (alias for --name)') - .option('-d, --description ', 'Project description') - .option( - '-my_description ', - 'Project description (alias for --description)' - ) - .option('-v, --version ', 'Project version') - .option('-my_version ', 'Project version (alias for --version)') - .option('-a, --author ', 'Author name') - .option('-y, --yes', 'Skip prompts and use default values') - .option('--skip-install', 'Skip installing dependencies') - .action(() => { - console.log( - chalk.yellow( - 'The init command must be run as a standalone command: task-master init' - ) - ); - console.log(chalk.cyan('Example usage:')); - console.log( - chalk.white( - ' task-master init -n "My Project" -d "Project description"' - ) - ); - console.log( - chalk.white( - ' task-master init -my_name "My Project" -my_description "Project description"' - ) - ); - console.log(chalk.white(' task-master init -y')); - process.exit(0); - }); - // remove-task command programInstance .command('remove-task') @@ -1552,6 +1517,37 @@ function registerCommands(programInstance) { } }); + // init command (Directly calls the implementation from init.js) + programInstance + .command('init') + .description('Initialize a new project with Task Master structure') + .option('-y, --yes', 'Skip prompts and use default values') + .option('-n, --name ', 'Project name') + .option('-d, --description ', 'Project description') + .option('-v, --version ', 'Project version', '0.1.0') // Set default here + .option('-a, --author ', 'Author name') + .option('--skip-install', 'Skip installing dependencies') + .option('--dry-run', 'Show what would be done without making changes') + .option('--aliases', 'Add shell aliases (tm, taskmaster)') + .action(async (cmdOptions) => { + // cmdOptions contains parsed arguments + try { + console.log('DEBUG: Running init command action in commands.js'); + console.log( + 'DEBUG: Options received by action:', + JSON.stringify(cmdOptions) + ); + // Directly call the initializeProject function, passing the parsed options + await initializeProject(cmdOptions); + // initializeProject handles its own flow, including potential process.exit() + } catch (error) { + console.error( + chalk.red(`Error during initialization: ${error.message}`) + ); + process.exit(1); + } + }); + // Add more commands as needed... return programInstance;