refactor(init): Fix init command execution and argument handling

Centralizes init command logic within the main CLI structure. The action handler in commands.js now directly calls initializeProject from the init.js module, resolving issues with argument parsing (like -y) and removing the need for the separate bin/task-master-init.js executable. Updates package.json and bin/task-master.js accordingly.
This commit is contained in:
Eyal Toledano
2025-04-10 22:32:08 -04:00
parent 6403e96ef9
commit a86e9affc5
7 changed files with 257 additions and 349 deletions

View File

@@ -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);
});

View File

@@ -225,47 +225,47 @@ function createDevScriptAction(commandName) {
}; };
} }
// Special case for the 'init' command which uses a different script // // Special case for the 'init' command which uses a different script
function registerInitCommand(program) { // function registerInitCommand(program) {
program // program
.command('init') // .command('init')
.description('Initialize a new project') // .description('Initialize a new project')
.option('-y, --yes', 'Skip prompts and use default values') // .option('-y, --yes', 'Skip prompts and use default values')
.option('-n, --name <name>', 'Project name') // .option('-n, --name <name>', 'Project name')
.option('-d, --description <description>', 'Project description') // .option('-d, --description <description>', 'Project description')
.option('-v, --version <version>', 'Project version') // .option('-v, --version <version>', 'Project version')
.option('-a, --author <author>', 'Author name') // .option('-a, --author <author>', 'Author name')
.option('--skip-install', 'Skip installing dependencies') // .option('--skip-install', 'Skip installing dependencies')
.option('--dry-run', 'Show what would be done without making changes') // .option('--dry-run', 'Show what would be done without making changes')
.action((options) => { // .action((options) => {
// Pass through any options to the init script // // Pass through any options to the init script
const args = [ // const args = [
'--yes', // '--yes',
'name', // 'name',
'description', // 'description',
'version', // 'version',
'author', // 'author',
'skip-install', // 'skip-install',
'dry-run' // 'dry-run'
] // ]
.filter((opt) => options[opt]) // .filter((opt) => options[opt])
.map((opt) => { // .map((opt) => {
if (opt === 'yes' || opt === 'skip-install' || opt === 'dry-run') { // if (opt === 'yes' || opt === 'skip-install' || opt === 'dry-run') {
return `--${opt}`; // return `--${opt}`;
} // }
return `--${opt}=${options[opt]}`; // return `--${opt}=${options[opt]}`;
}); // });
const child = spawn('node', [initScriptPath, ...args], { // const child = spawn('node', [initScriptPath, ...args], {
stdio: 'inherit', // stdio: 'inherit',
cwd: process.cwd() // cwd: process.cwd()
}); // });
child.on('close', (code) => { // child.on('close', (code) => {
process.exit(code); // process.exit(code);
}); // });
}); // });
} // }
// Set up the command-line interface // Set up the command-line interface
const program = new Command(); const program = new Command();
@@ -286,8 +286,8 @@ program.on('--help', () => {
displayHelp(); displayHelp();
}); });
// Add special case commands // // Add special case commands
registerInitCommand(program); // registerInitCommand(program);
program program
.command('dev') .command('dev')
@@ -303,7 +303,7 @@ registerCommands(tempProgram);
// For each command in the temp instance, add a modified version to our actual program // For each command in the temp instance, add a modified version to our actual program
tempProgram.commands.forEach((cmd) => { tempProgram.commands.forEach((cmd) => {
if (['init', 'dev'].includes(cmd.name())) { if (['dev'].includes(cmd.name())) {
// Skip commands we've already defined specially // Skip commands we've already defined specially
return; return;
} }

View File

@@ -1,56 +1,97 @@
import { z } from 'zod'; import { z } from 'zod';
import { execSync } from 'child_process'; 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) { export function registerInitializeProjectTool(server) {
server.addTool({ server.addTool({
name: 'initialize_project', // snake_case for tool name name: 'initialize_project',
description: 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({ parameters: z.object({
projectName: z projectName: z
.string() .string()
.optional() .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 projectDescription: z
.string() .string()
.optional() .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 projectVersion: z
.string() .string()
.optional() .optional()
.describe("The initial version for the project (e.g., '0.1.0')."), .describe(
authorName: z.string().optional().describe("The author's name."), "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 skipInstall: z
.boolean() .boolean()
.optional() .optional()
.default(false) .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 addAliases: z
.boolean() .boolean()
.optional() .optional()
.default(false) .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 yes: z
.boolean() .boolean()
.optional() .optional()
.default(false) .default(false)
.describe('Skip prompts and use default values or provided arguments.') .describe(
// projectRoot is not needed here as 'init' works on the current directory "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 }) => { execute: async (args, { log, session }) => {
// Destructure context to get log
try { try {
log.info( log.info(
`Executing initialize_project with args: ${JSON.stringify(args)}` `Executing initialize_project with args: ${JSON.stringify(args)}`
); );
// Construct the command arguments carefully let targetDirectory = getProjectRootFromSession(session, log);
// Using npx ensures it uses the locally installed version if available, or fetches it if (!targetDirectory) {
let command = 'npx task-master init'; 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 = []; const cliArgs = [];
if (args.projectName) if (args.projectName)
cliArgs.push(`--name "${args.projectName.replace(/"/g, '\\"')}"`); // Escape quotes cliArgs.push(`--name "${args.projectName.replace(/"/g, '\\"')}"`);
if (args.projectDescription) if (args.projectDescription)
cliArgs.push( cliArgs.push(
`--description "${args.projectDescription.replace(/"/g, '\\"')}"` `--description "${args.projectDescription.replace(/"/g, '\\"')}"`
@@ -63,36 +104,47 @@ export function registerInitializeProjectTool(server) {
cliArgs.push(`--author "${args.authorName.replace(/"/g, '\\"')}"`); cliArgs.push(`--author "${args.authorName.replace(/"/g, '\\"')}"`);
if (args.skipInstall) cliArgs.push('--skip-install'); if (args.skipInstall) cliArgs.push('--skip-install');
if (args.addAliases) cliArgs.push('--aliases'); 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, { const output = execSync(command, {
encoding: 'utf8', encoding: 'utf8',
stdio: 'pipe', stdio: 'pipe',
timeout: 300000 timeout: 300000,
cwd: targetDirectory
}); });
log.info(`Initialization output:\n${output}`); log.info(`Initialization output:\n${output}`);
// Return a standard success response manually
return createContentResponse({ return createContentResponse({
message: 'Taskmaster successfully initialized for this project.', message: `Taskmaster successfully initialized in ${targetDirectory}.`,
next_step: 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 (error) {
// Catch errors from execSync or timeouts const errorMessage = `Project initialization failed: ${
const errorMessage = `Project initialization failed: ${error.message}`; error.message || 'Unknown error'
}`;
const errorDetails = 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}`); log.error(`${errorMessage}\nDetails: ${errorDetails}`);
// Return a standard error response manually
return createErrorResponse(errorMessage, { details: errorDetails }); return createErrorResponse(errorMessage, { details: errorDetails });
} }
} }

3
package-lock.json generated
View File

@@ -32,8 +32,7 @@
"bin": { "bin": {
"task-master": "bin/task-master.js", "task-master": "bin/task-master.js",
"task-master-init": "bin/task-master-init.js", "task-master-init": "bin/task-master-init.js",
"task-master-mcp": "mcp-server/server.js", "task-master-mcp": "mcp-server/server.js"
"task-master-mcp-server": "mcp-server/server.js"
}, },
"devDependencies": { "devDependencies": {
"@changesets/changelog-github": "^0.5.1", "@changesets/changelog-github": "^0.5.1",

View File

@@ -6,7 +6,6 @@
"type": "module", "type": "module",
"bin": { "bin": {
"task-master": "bin/task-master.js", "task-master": "bin/task-master.js",
"task-master-init": "bin/task-master-init.js",
"task-master-mcp": "mcp-server/server.js" "task-master-mcp": "mcp-server/server.js"
}, },
"scripts": { "scripts": {
@@ -16,7 +15,7 @@
"test:coverage": "node --experimental-vm-modules node_modules/.bin/jest --coverage", "test:coverage": "node --experimental-vm-modules node_modules/.bin/jest --coverage",
"prepare-package": "node scripts/prepare-package.js", "prepare-package": "node scripts/prepare-package.js",
"prepublishOnly": "npm run prepare-package", "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", "changeset": "changeset",
"release": "changeset publish", "release": "changeset publish",
"inspector": "npx @modelcontextprotocol/inspector node mcp-server/server.js", "inspector": "npx @modelcontextprotocol/inspector node mcp-server/server.js",

View File

@@ -1,5 +1,3 @@
#!/usr/bin/env node
/** /**
* Task Master * Task Master
* Copyright (c) 2025 Eyal Toledano, Ralph Khreish * Copyright (c) 2025 Eyal Toledano, Ralph Khreish
@@ -27,7 +25,6 @@ import chalk from 'chalk';
import figlet from 'figlet'; import figlet from 'figlet';
import boxen from 'boxen'; import boxen from 'boxen';
import gradient from 'gradient-string'; import gradient from 'gradient-string';
import { Command } from 'commander';
// Debug information // Debug information
console.log('Node version:', process.version); console.log('Node version:', process.version);
@@ -37,42 +34,6 @@ console.log('Script path:', import.meta.url);
const __filename = fileURLToPath(import.meta.url); const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename); 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 <name>', 'Project name')
.option('-my_name <name>', 'Project name (alias for --name)')
.option('-d, --description <description>', 'Project description')
.option(
'-my_description <description>',
'Project description (alias for --description)'
)
.option('-v, --version <version>', 'Project version')
.option('-my_version <version>', 'Project version (alias for --version)')
.option('--my_name <name>', 'Project name (alias for --name)')
.option('-a, --author <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 // Define log levels
const LOG_LEVELS = { const LOG_LEVELS = {
debug: 0, debug: 0,
@@ -419,27 +380,35 @@ function copyTemplateFile(templateName, targetPath, replacements = {}) {
log('info', `Created file: ${targetPath}`); log('info', `Created file: ${targetPath}`);
} }
// Main function to initialize a new project // Main function to initialize a new project (Now relies solely on passed options)
async function initializeProject(options = {}) { async function initializeProject(options = {}) { // Receives options as argument
// Display the banner
displayBanner(); displayBanner();
// If options are provided, use them directly without prompting console.log('===== DEBUG: INITIALIZE PROJECT OPTIONS RECEIVED =====');
if (options.projectName && options.projectDescription) { console.log('Full options object:', JSON.stringify(options));
const projectName = options.projectName; console.log('options.yes:', options.yes);
const projectDescription = options.projectDescription; console.log('options.name:', options.name);
const projectVersion = options.projectVersion || '1.0.0'; console.log('==================================================');
const authorName = options.authorName || '';
// 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 dryRun = options.dryRun || false;
const skipInstall = options.skipInstall || false; const skipInstall = options.skipInstall || false;
const addAliases = options.addAliases || false; const addAliases = options.aliases || false;
if (dryRun) { if (dryRun) {
log('info', 'DRY RUN MODE: No files will be modified'); log('info', 'DRY RUN MODE: No files will be modified');
log( log('info', `Would initialize project: ${projectName} (${projectVersion})`);
'info',
`Would initialize project: ${projectName} (${projectVersion})`
);
log('info', `Description: ${projectDescription}`); log('info', `Description: ${projectDescription}`);
log('info', `Author: ${authorName || 'Not specified'}`); log('info', `Author: ${authorName || 'Not specified'}`);
log('info', 'Would create/update necessary project files'); log('info', 'Would create/update necessary project files');
@@ -458,6 +427,7 @@ async function initializeProject(options = {}) {
}; };
} }
// Create structure using determined values
createProjectStructure( createProjectStructure(
projectName, projectName,
projectDescription, projectDescription,
@@ -466,52 +436,25 @@ async function initializeProject(options = {}) {
skipInstall, skipInstall,
addAliases addAliases
); );
return { } else {
projectName, // Prompting logic (only runs if skipPrompts is false)
projectDescription, log('info', 'Required options not provided, proceeding with prompts.');
projectVersion,
authorName
};
}
// Otherwise, prompt the user for input
// Create readline interface only when needed
const rl = readline.createInterface({ const rl = readline.createInterface({
input: process.stdin, input: process.stdin,
output: process.stdout output: process.stdout
}); });
try { try {
const projectName = await promptQuestion( // Prompt user for input...
rl, const projectName = await promptQuestion(rl, chalk.cyan('Enter project name: '));
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 projectDescription = await promptQuestion( const authorName = await promptQuestion(rl, chalk.cyan('Enter your name: '));
rl, const addAliasesInput = await promptQuestion(rl, chalk.cyan('Add shell aliases for task-master? (Y/n): '));
chalk.cyan('Enter project description: ') const addAliasesPrompted = addAliasesInput.trim().toLowerCase() !== 'n';
); const projectVersion = projectVersionInput.trim() ? projectVersionInput : '1.0.0';
const projectVersionInput = await promptQuestion(
rl,
chalk.cyan('Enter project version (default: 1.0.0): ')
);
const authorName = await promptQuestion(
rl,
chalk.cyan('Enter your name: ')
);
// Ask about shell aliases // Confirm settings...
const addAliasesInput = await promptQuestion(
rl,
chalk.cyan('Add shell aliases for task-master? (Y/n): ')
);
const addAliases = addAliasesInput.trim().toLowerCase() !== 'n';
// Set default version if not provided
const projectVersion = projectVersionInput.trim()
? projectVersionInput
: '1.0.0';
// Confirm settings
console.log('\nProject settings:'); console.log('\nProject settings:');
console.log(chalk.blue('Name:'), chalk.white(projectName)); console.log(chalk.blue('Name:'), chalk.white(projectName));
console.log(chalk.blue('Description:'), chalk.white(projectDescription)); console.log(chalk.blue('Description:'), chalk.white(projectDescription));
@@ -521,31 +464,31 @@ async function initializeProject(options = {}) {
chalk.white(authorName || 'Not specified') chalk.white(authorName || 'Not specified')
); );
console.log( console.log(
chalk.blue('Add shell aliases:'), chalk.blue('Add shell aliases (so you can use "tm" instead of "task-master"):'),
chalk.white(addAliases ? 'Yes' : 'No') chalk.white(addAliasesPrompted ? 'Yes' : 'No')
); );
const confirmInput = await promptQuestion( const confirmInput = await promptQuestion(rl, chalk.yellow('\nDo you want to continue with these settings? (Y/n): '));
rl,
chalk.yellow('\nDo you want to continue with these settings? (Y/n): ')
);
const shouldContinue = confirmInput.trim().toLowerCase() !== 'n'; const shouldContinue = confirmInput.trim().toLowerCase() !== 'n';
// Close the readline interface
rl.close(); rl.close();
if (!shouldContinue) { if (!shouldContinue) {
log('info', 'Project initialization cancelled by user'); log('info', 'Project initialization cancelled by user');
return null; process.exit(0); // Exit if cancelled
return; // Added return for clarity
} }
// Still respect dryRun/skipInstall if passed initially even when prompting
const dryRun = options.dryRun || false; const dryRun = options.dryRun || false;
const skipInstall = options.skipInstall || false; const skipInstall = options.skipInstall || false;
if (dryRun) { if (dryRun) {
log('info', 'DRY RUN MODE: No files will be modified'); 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'); log('info', 'Would create/update necessary project files');
if (addAliases) { if (addAliasesPrompted) {
log('info', 'Would add shell aliases for task-master'); log('info', 'Would add shell aliases for task-master');
} }
if (!skipInstall) { if (!skipInstall) {
@@ -560,26 +503,21 @@ async function initializeProject(options = {}) {
}; };
} }
// Create the project structure // Create structure using prompted values, respecting initial options where relevant
createProjectStructure( createProjectStructure(
projectName, projectName,
projectDescription, projectDescription,
projectVersion, projectVersion,
authorName, authorName,
skipInstall, skipInstall, // Use value from initial options
addAliases addAliasesPrompted // Use value from prompt
); );
return {
projectName,
projectDescription,
projectVersion,
authorName
};
} catch (error) { } catch (error) {
// Make sure to close readline on error
rl.close(); rl.close();
throw error; log('error', `Error during prompting: ${error.message}`); // Use log function
process.exit(1); // Exit on error during prompts
}
} }
} }
@@ -985,51 +923,5 @@ function setupMCPConfiguration(targetDir, projectName) {
log('info', 'MCP server will use the installed task-master-ai package'); log('info', 'MCP server will use the installed task-master-ai package');
} }
// Run the initialization if this script is executed directly // Ensure necessary functions are exported
// The original check doesn't work with npx and global commands export { initializeProject, log }; // Only export what's needed by commands.js
// 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 };

View File

@@ -10,8 +10,9 @@ import boxen from 'boxen';
import fs from 'fs'; import fs from 'fs';
import https from 'https'; import https from 'https';
import inquirer from 'inquirer'; import inquirer from 'inquirer';
import ora from 'ora';
import { CONFIG, log, readJSON } from './utils.js'; import { CONFIG, log, readJSON, writeJSON } from './utils.js';
import { import {
parsePRD, parsePRD,
updateTasks, updateTasks,
@@ -51,6 +52,8 @@ import {
stopLoadingIndicator stopLoadingIndicator
} from './ui.js'; } from './ui.js';
import { initializeProject } from '../init.js';
/** /**
* Configure and register CLI commands * Configure and register CLI commands
* @param {Object} program - Commander program instance * @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 <name>', 'Project name')
.option('-my_name <name>', 'Project name (alias for --name)')
.option('--my_name <name>', 'Project name (alias for --name)')
.option('-d, --description <description>', 'Project description')
.option(
'-my_description <description>',
'Project description (alias for --description)'
)
.option('-v, --version <version>', 'Project version')
.option('-my_version <version>', 'Project version (alias for --version)')
.option('-a, --author <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 // remove-task command
programInstance programInstance
.command('remove-task') .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 <name>', 'Project name')
.option('-d, --description <description>', 'Project description')
.option('-v, --version <version>', 'Project version', '0.1.0') // Set default here
.option('-a, --author <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... // Add more commands as needed...
return programInstance; return programInstance;