Compare commits
2 Commits
fix/140-de
...
ralph/impr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fbe859f886 | ||
|
|
45cc0cf527 |
@@ -1,5 +0,0 @@
|
|||||||
---
|
|
||||||
'task-master-ai': patch
|
|
||||||
---
|
|
||||||
|
|
||||||
Fix remove-task command to handle multiple comma-separated task IDs
|
|
||||||
5
.changeset/witty-jokes-roll.md
Normal file
5
.changeset/witty-jokes-roll.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'task-master-ai': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Remove the need for project name, description, and version. Since we no longer create a package.json for you
|
||||||
@@ -10,7 +10,7 @@ import os from 'os'; // Import os module for home directory check
|
|||||||
/**
|
/**
|
||||||
* Direct function wrapper for initializing a project.
|
* Direct function wrapper for initializing a project.
|
||||||
* Derives target directory from session, sets CWD, and calls core init logic.
|
* Derives target directory from session, sets CWD, and calls core init logic.
|
||||||
* @param {object} args - Arguments containing project details and options (projectName, projectDescription, yes, etc.)
|
* @param {object} args - Arguments containing initialization options (addAliases, skipInstall, yes, projectRoot)
|
||||||
* @param {object} log - The FastMCP logger instance.
|
* @param {object} log - The FastMCP logger instance.
|
||||||
* @param {object} context - The context object, must contain { session }.
|
* @param {object} context - The context object, must contain { session }.
|
||||||
* @returns {Promise<{success: boolean, data?: any, error?: {code: string, message: string}}>} - Standard result object.
|
* @returns {Promise<{success: boolean, data?: any, error?: {code: string, message: string}}>} - Standard result object.
|
||||||
@@ -92,12 +92,8 @@ export async function initializeProjectDirect(args, log, context = {}) {
|
|||||||
try {
|
try {
|
||||||
// Always force yes: true when called via MCP to avoid interactive prompts
|
// Always force yes: true when called via MCP to avoid interactive prompts
|
||||||
const options = {
|
const options = {
|
||||||
name: args.projectName,
|
|
||||||
description: args.projectDescription,
|
|
||||||
version: args.projectVersion,
|
|
||||||
author: args.authorName,
|
|
||||||
skipInstall: args.skipInstall,
|
|
||||||
aliases: args.addAliases,
|
aliases: args.addAliases,
|
||||||
|
skipInstall: args.skipInstall,
|
||||||
yes: true // Force yes mode
|
yes: true // Force yes mode
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,23 +3,18 @@
|
|||||||
* Direct function implementation for removing a task
|
* Direct function implementation for removing a task
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import { removeTask } from '../../../../scripts/modules/task-manager.js';
|
||||||
removeTask,
|
|
||||||
taskExists
|
|
||||||
} from '../../../../scripts/modules/task-manager.js';
|
|
||||||
import {
|
import {
|
||||||
enableSilentMode,
|
enableSilentMode,
|
||||||
disableSilentMode,
|
disableSilentMode
|
||||||
readJSON
|
|
||||||
} from '../../../../scripts/modules/utils.js';
|
} from '../../../../scripts/modules/utils.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Direct function wrapper for removeTask with error handling.
|
* Direct function wrapper for removeTask with error handling.
|
||||||
* Supports removing multiple tasks at once with comma-separated IDs.
|
|
||||||
*
|
*
|
||||||
* @param {Object} args - Command arguments
|
* @param {Object} args - Command arguments
|
||||||
* @param {string} args.tasksJsonPath - Explicit path to the tasks.json file.
|
* @param {string} args.tasksJsonPath - Explicit path to the tasks.json file.
|
||||||
* @param {string} args.id - The ID(s) of the task(s) or subtask(s) to remove (comma-separated for multiple).
|
* @param {string} args.id - The ID of the task or subtask to remove.
|
||||||
* @param {Object} log - Logger object
|
* @param {Object} log - Logger object
|
||||||
* @returns {Promise<Object>} - Remove task result { success: boolean, data?: any, error?: { code: string, message: string }, fromCache: false }
|
* @returns {Promise<Object>} - Remove task result { success: boolean, data?: any, error?: { code: string, message: string }, fromCache: false }
|
||||||
*/
|
*/
|
||||||
@@ -41,7 +36,8 @@ export async function removeTaskDirect(args, log) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Validate task ID parameter
|
// Validate task ID parameter
|
||||||
if (!id) {
|
const taskId = id;
|
||||||
|
if (!taskId) {
|
||||||
log.error('Task ID is required');
|
log.error('Task ID is required');
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
@@ -53,103 +49,46 @@ export async function removeTaskDirect(args, log) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Split task IDs if comma-separated
|
// Skip confirmation in the direct function since it's handled by the client
|
||||||
const taskIdArray = id.split(',').map((taskId) => taskId.trim());
|
log.info(`Removing task with ID: ${taskId} from ${tasksJsonPath}`);
|
||||||
|
|
||||||
log.info(
|
|
||||||
`Removing ${taskIdArray.length} task(s) with ID(s): ${taskIdArray.join(', ')} from ${tasksJsonPath}`
|
|
||||||
);
|
|
||||||
|
|
||||||
// Validate all task IDs exist before proceeding
|
|
||||||
const data = readJSON(tasksJsonPath);
|
|
||||||
if (!data || !data.tasks) {
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
error: {
|
|
||||||
code: 'INVALID_TASKS_FILE',
|
|
||||||
message: `No valid tasks found in ${tasksJsonPath}`
|
|
||||||
},
|
|
||||||
fromCache: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const invalidTasks = taskIdArray.filter(
|
|
||||||
(taskId) => !taskExists(data.tasks, taskId)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (invalidTasks.length > 0) {
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
error: {
|
|
||||||
code: 'INVALID_TASK_ID',
|
|
||||||
message: `The following tasks were not found: ${invalidTasks.join(', ')}`
|
|
||||||
},
|
|
||||||
fromCache: false
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove tasks one by one
|
|
||||||
const results = [];
|
|
||||||
|
|
||||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
|
||||||
enableSilentMode();
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (const taskId of taskIdArray) {
|
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||||
try {
|
enableSilentMode();
|
||||||
const result = await removeTask(tasksJsonPath, taskId);
|
|
||||||
results.push({
|
// Call the core removeTask function using the provided path
|
||||||
taskId,
|
const result = await removeTask(tasksJsonPath, taskId);
|
||||||
success: true,
|
|
||||||
message: result.message,
|
|
||||||
removedTask: result.removedTask
|
|
||||||
});
|
|
||||||
log.info(`Successfully removed task: ${taskId}`);
|
|
||||||
} catch (error) {
|
|
||||||
results.push({
|
|
||||||
taskId,
|
|
||||||
success: false,
|
|
||||||
error: error.message
|
|
||||||
});
|
|
||||||
log.error(`Error removing task ${taskId}: ${error.message}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
// Restore normal logging
|
// Restore normal logging
|
||||||
disableSilentMode();
|
disableSilentMode();
|
||||||
}
|
|
||||||
|
|
||||||
// Check if all tasks were successfully removed
|
log.info(`Successfully removed task: ${taskId}`);
|
||||||
const successfulRemovals = results.filter((r) => r.success);
|
|
||||||
const failedRemovals = results.filter((r) => !r.success);
|
|
||||||
|
|
||||||
if (successfulRemovals.length === 0) {
|
// Return the result
|
||||||
// All removals failed
|
return {
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
message: result.message,
|
||||||
|
taskId: taskId,
|
||||||
|
tasksPath: tasksJsonPath,
|
||||||
|
removedTask: result.removedTask
|
||||||
|
},
|
||||||
|
fromCache: false
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
// Make sure to restore normal logging even if there's an error
|
||||||
|
disableSilentMode();
|
||||||
|
|
||||||
|
log.error(`Error removing task: ${error.message}`);
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: {
|
error: {
|
||||||
code: 'REMOVE_TASK_ERROR',
|
code: error.code || 'REMOVE_TASK_ERROR',
|
||||||
message: 'Failed to remove any tasks',
|
message: error.message || 'Failed to remove task'
|
||||||
details: failedRemovals
|
|
||||||
.map((r) => `${r.taskId}: ${r.error}`)
|
|
||||||
.join('; ')
|
|
||||||
},
|
},
|
||||||
fromCache: false
|
fromCache: false
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// At least some tasks were removed successfully
|
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
data: {
|
|
||||||
totalTasks: taskIdArray.length,
|
|
||||||
successful: successfulRemovals.length,
|
|
||||||
failed: failedRemovals.length,
|
|
||||||
results: results,
|
|
||||||
tasksPath: tasksJsonPath
|
|
||||||
},
|
|
||||||
fromCache: false
|
|
||||||
};
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Ensure silent mode is disabled even if an outer error occurs
|
// Ensure silent mode is disabled even if an outer error occurs
|
||||||
disableSilentMode();
|
disableSilentMode();
|
||||||
|
|||||||
@@ -10,32 +10,8 @@ export function registerInitializeProjectTool(server) {
|
|||||||
server.addTool({
|
server.addTool({
|
||||||
name: 'initialize_project',
|
name: 'initialize_project',
|
||||||
description:
|
description:
|
||||||
"Initializes a new Task Master project structure by calling the core initialization logic. Derives target directory from client session. If project details (name, description, author) are not provided, prompts the user or skips if 'yes' flag is true. DO NOT run without parameters.",
|
'Initializes a new Task Master project structure by calling the core initialization logic. Creates necessary folders and configuration files for Task Master in the current directory.',
|
||||||
parameters: z.object({
|
parameters: z.object({
|
||||||
projectName: z
|
|
||||||
.string()
|
|
||||||
.optional()
|
|
||||||
.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. If not provided, prompt the user for it.'
|
|
||||||
),
|
|
||||||
projectVersion: z
|
|
||||||
.string()
|
|
||||||
.optional()
|
|
||||||
.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
|
skipInstall: z
|
||||||
.boolean()
|
.boolean()
|
||||||
.optional()
|
.optional()
|
||||||
@@ -47,15 +23,13 @@ export function registerInitializeProjectTool(server) {
|
|||||||
.boolean()
|
.boolean()
|
||||||
.optional()
|
.optional()
|
||||||
.default(false)
|
.default(false)
|
||||||
.describe(
|
.describe('Add shell aliases (tm, taskmaster) to shell config file.'),
|
||||||
'Add shell aliases (tm, taskmaster) to shell config file. User input not needed.'
|
|
||||||
),
|
|
||||||
yes: z
|
yes: z
|
||||||
.boolean()
|
.boolean()
|
||||||
.optional()
|
.optional()
|
||||||
.default(false)
|
.default(true)
|
||||||
.describe(
|
.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."
|
'Skip prompts and use default values. Always set to true for MCP tools.'
|
||||||
),
|
),
|
||||||
projectRoot: z
|
projectRoot: z
|
||||||
.string()
|
.string()
|
||||||
|
|||||||
@@ -23,9 +23,7 @@ export function registerRemoveTaskTool(server) {
|
|||||||
parameters: z.object({
|
parameters: z.object({
|
||||||
id: z
|
id: z
|
||||||
.string()
|
.string()
|
||||||
.describe(
|
.describe("ID of the task or subtask to remove (e.g., '5' or '5.2')"),
|
||||||
"ID(s) of the task(s) or subtask(s) to remove (e.g., '5' or '5.2' or '5,6,7')"
|
|
||||||
),
|
|
||||||
file: z.string().optional().describe('Absolute path to the tasks file'),
|
file: z.string().optional().describe('Absolute path to the tasks file'),
|
||||||
projectRoot: z
|
projectRoot: z
|
||||||
.string()
|
.string()
|
||||||
@@ -37,7 +35,7 @@ export function registerRemoveTaskTool(server) {
|
|||||||
}),
|
}),
|
||||||
execute: async (args, { log, session }) => {
|
execute: async (args, { log, session }) => {
|
||||||
try {
|
try {
|
||||||
log.info(`Removing task(s) with ID(s): ${args.id}`);
|
log.info(`Removing task with ID: ${args.id}`);
|
||||||
|
|
||||||
// Get project root from args or session
|
// Get project root from args or session
|
||||||
const rootFolder =
|
const rootFolder =
|
||||||
|
|||||||
126
scripts/init.js
126
scripts/init.js
@@ -335,36 +335,11 @@ async function initializeProject(options = {}) {
|
|||||||
console.log('===== DEBUG: INITIALIZE PROJECT OPTIONS RECEIVED =====');
|
console.log('===== DEBUG: INITIALIZE PROJECT OPTIONS RECEIVED =====');
|
||||||
console.log('Full options object:', JSON.stringify(options));
|
console.log('Full options object:', JSON.stringify(options));
|
||||||
console.log('options.yes:', options.yes);
|
console.log('options.yes:', options.yes);
|
||||||
console.log('options.name:', options.name);
|
|
||||||
console.log('==================================================');
|
console.log('==================================================');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to get project name from package.json if not provided
|
|
||||||
if (!options.name) {
|
|
||||||
const packageJsonPath = path.join(process.cwd(), 'package.json');
|
|
||||||
try {
|
|
||||||
if (fs.existsSync(packageJsonPath)) {
|
|
||||||
const packageJson = JSON.parse(
|
|
||||||
fs.readFileSync(packageJsonPath, 'utf8')
|
|
||||||
);
|
|
||||||
if (packageJson.name) {
|
|
||||||
log(
|
|
||||||
'info',
|
|
||||||
`Found project name '${packageJson.name}' in package.json`
|
|
||||||
);
|
|
||||||
options.name = packageJson.name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
log(
|
|
||||||
'debug',
|
|
||||||
`Could not read project name from package.json: ${error.message}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine if we should skip prompts based on the passed options
|
// Determine if we should skip prompts based on the passed options
|
||||||
const skipPrompts = options.yes || (options.name && options.description);
|
const skipPrompts = options.yes;
|
||||||
if (!isSilentMode()) {
|
if (!isSilentMode()) {
|
||||||
console.log('Skip prompts determined:', skipPrompts);
|
console.log('Skip prompts determined:', skipPrompts);
|
||||||
}
|
}
|
||||||
@@ -374,44 +349,24 @@ async function initializeProject(options = {}) {
|
|||||||
console.log('SKIPPING PROMPTS - Using defaults or provided values');
|
console.log('SKIPPING PROMPTS - Using defaults or provided values');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use provided options or defaults
|
// We no longer need these variables
|
||||||
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 addAliases = options.aliases || 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 Task Master project');
|
||||||
'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 (addAliases) {
|
||||||
log('info', 'Would add shell aliases for task-master');
|
log('info', 'Would add shell aliases for task-master');
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
projectName,
|
|
||||||
projectDescription,
|
|
||||||
projectVersion,
|
|
||||||
authorName,
|
|
||||||
dryRun: true
|
dryRun: true
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create structure using determined values
|
// Create structure using only necessary values
|
||||||
createProjectStructure(
|
createProjectStructure(addAliases);
|
||||||
projectName,
|
|
||||||
projectDescription,
|
|
||||||
projectVersion,
|
|
||||||
authorName,
|
|
||||||
addAliases
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
// Prompting logic (only runs if skipPrompts is false)
|
// Prompting logic (only runs if skipPrompts is false)
|
||||||
log('info', 'Required options not provided, proceeding with prompts.');
|
log('info', 'Required options not provided, proceeding with prompts.');
|
||||||
@@ -421,41 +376,17 @@ async function initializeProject(options = {}) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Prompt user for input...
|
// Only prompt for shell aliases
|
||||||
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(
|
const addAliasesInput = await promptQuestion(
|
||||||
rl,
|
rl,
|
||||||
chalk.cyan('Add shell aliases for task-master? (Y/n): ')
|
chalk.cyan(
|
||||||
|
'Add shell aliases for task-master? This lets you type "tm" instead of "task-master" (Y/n): '
|
||||||
|
)
|
||||||
);
|
);
|
||||||
const addAliasesPrompted = addAliasesInput.trim().toLowerCase() !== 'n';
|
const addAliasesPrompted = addAliasesInput.trim().toLowerCase() !== 'n';
|
||||||
const projectVersion = projectVersionInput.trim()
|
|
||||||
? projectVersionInput
|
|
||||||
: '1.0.0';
|
|
||||||
|
|
||||||
// Confirm settings...
|
// Confirm settings...
|
||||||
console.log('\nProject settings:');
|
console.log('\nTask Master Project 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(
|
console.log(
|
||||||
chalk.blue(
|
chalk.blue(
|
||||||
'Add shell aliases (so you can use "tm" instead of "task-master"):'
|
'Add shell aliases (so you can use "tm" instead of "task-master"):'
|
||||||
@@ -481,33 +412,18 @@ async function initializeProject(options = {}) {
|
|||||||
|
|
||||||
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 Task Master project');
|
||||||
'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 (addAliasesPrompted) {
|
if (addAliasesPrompted) {
|
||||||
log('info', 'Would add shell aliases for task-master');
|
log('info', 'Would add shell aliases for task-master');
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
projectName,
|
|
||||||
projectDescription,
|
|
||||||
projectVersion,
|
|
||||||
authorName,
|
|
||||||
dryRun: true
|
dryRun: true
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create structure using prompted values, respecting initial options where relevant
|
// Create structure using only necessary values
|
||||||
createProjectStructure(
|
createProjectStructure(addAliasesPrompted);
|
||||||
projectName,
|
|
||||||
projectDescription,
|
|
||||||
projectVersion,
|
|
||||||
authorName,
|
|
||||||
addAliasesPrompted // Use value from prompt
|
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
rl.close();
|
rl.close();
|
||||||
log('error', `Error during prompting: ${error.message}`); // Use log function
|
log('error', `Error during prompting: ${error.message}`); // Use log function
|
||||||
@@ -526,13 +442,7 @@ function promptQuestion(rl, question) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Function to create the project structure
|
// Function to create the project structure
|
||||||
function createProjectStructure(
|
function createProjectStructure(addAliases) {
|
||||||
projectName,
|
|
||||||
projectDescription,
|
|
||||||
projectVersion,
|
|
||||||
authorName,
|
|
||||||
addAliases
|
|
||||||
) {
|
|
||||||
const targetDir = process.cwd();
|
const targetDir = process.cwd();
|
||||||
log('info', `Initializing project in ${targetDir}`);
|
log('info', `Initializing project in ${targetDir}`);
|
||||||
|
|
||||||
@@ -542,14 +452,10 @@ function createProjectStructure(
|
|||||||
ensureDirectoryExists(path.join(targetDir, 'tasks'));
|
ensureDirectoryExists(path.join(targetDir, 'tasks'));
|
||||||
|
|
||||||
// Setup MCP configuration for integration with Cursor
|
// Setup MCP configuration for integration with Cursor
|
||||||
setupMCPConfiguration(targetDir, projectName);
|
setupMCPConfiguration(targetDir);
|
||||||
|
|
||||||
// Copy template files with replacements
|
// Copy template files with replacements
|
||||||
const replacements = {
|
const replacements = {
|
||||||
projectName,
|
|
||||||
projectDescription,
|
|
||||||
projectVersion,
|
|
||||||
authorName,
|
|
||||||
year: new Date().getFullYear()
|
year: new Date().getFullYear()
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -695,7 +601,7 @@ function createProjectStructure(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Function to setup MCP configuration for Cursor integration
|
// Function to setup MCP configuration for Cursor integration
|
||||||
function setupMCPConfiguration(targetDir, projectName) {
|
function setupMCPConfiguration(targetDir) {
|
||||||
const mcpDirPath = path.join(targetDir, '.cursor');
|
const mcpDirPath = path.join(targetDir, '.cursor');
|
||||||
const mcpJsonPath = path.join(mcpDirPath, 'mcp.json');
|
const mcpJsonPath = path.join(mcpDirPath, 'mcp.json');
|
||||||
|
|
||||||
|
|||||||
@@ -1374,18 +1374,18 @@ function registerCommands(programInstance) {
|
|||||||
// remove-task command
|
// remove-task command
|
||||||
programInstance
|
programInstance
|
||||||
.command('remove-task')
|
.command('remove-task')
|
||||||
.description('Remove one or more tasks or subtasks permanently')
|
.description('Remove a task or subtask permanently')
|
||||||
.option(
|
.option(
|
||||||
'-i, --id <id>',
|
'-i, --id <id>',
|
||||||
'ID(s) of the task(s) or subtask(s) to remove (e.g., "5" or "5.2" or "5,6,7")'
|
'ID of the task or subtask to remove (e.g., "5" or "5.2")'
|
||||||
)
|
)
|
||||||
.option('-f, --file <file>', 'Path to the tasks file', 'tasks/tasks.json')
|
.option('-f, --file <file>', 'Path to the tasks file', 'tasks/tasks.json')
|
||||||
.option('-y, --yes', 'Skip confirmation prompt', false)
|
.option('-y, --yes', 'Skip confirmation prompt', false)
|
||||||
.action(async (options) => {
|
.action(async (options) => {
|
||||||
const tasksPath = options.file;
|
const tasksPath = options.file;
|
||||||
const taskIds = options.id;
|
const taskId = options.id;
|
||||||
|
|
||||||
if (!taskIds) {
|
if (!taskId) {
|
||||||
console.error(chalk.red('Error: Task ID is required'));
|
console.error(chalk.red('Error: Task ID is required'));
|
||||||
console.error(
|
console.error(
|
||||||
chalk.yellow('Usage: task-master remove-task --id=<taskId>')
|
chalk.yellow('Usage: task-master remove-task --id=<taskId>')
|
||||||
@@ -1394,7 +1394,7 @@ function registerCommands(programInstance) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Check if the tasks file exists and is valid
|
// Check if the task exists
|
||||||
const data = readJSON(tasksPath);
|
const data = readJSON(tasksPath);
|
||||||
if (!data || !data.tasks) {
|
if (!data || !data.tasks) {
|
||||||
console.error(
|
console.error(
|
||||||
@@ -1403,89 +1403,75 @@ function registerCommands(programInstance) {
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Split task IDs if comma-separated
|
if (!taskExists(data.tasks, taskId)) {
|
||||||
const taskIdArray = taskIds.split(',').map((id) => id.trim());
|
console.error(chalk.red(`Error: Task with ID ${taskId} not found`));
|
||||||
|
|
||||||
// Validate all task IDs exist before proceeding
|
|
||||||
const invalidTasks = taskIdArray.filter(
|
|
||||||
(id) => !taskExists(data.tasks, id)
|
|
||||||
);
|
|
||||||
if (invalidTasks.length > 0) {
|
|
||||||
console.error(
|
|
||||||
chalk.red(
|
|
||||||
`Error: The following tasks were not found: ${invalidTasks.join(', ')}`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load task for display
|
||||||
|
const task = findTaskById(data.tasks, taskId);
|
||||||
|
|
||||||
// Skip confirmation if --yes flag is provided
|
// Skip confirmation if --yes flag is provided
|
||||||
if (!options.yes) {
|
if (!options.yes) {
|
||||||
// Display tasks to be removed
|
// Display task information
|
||||||
console.log();
|
console.log();
|
||||||
console.log(
|
console.log(
|
||||||
chalk.red.bold(
|
chalk.red.bold(
|
||||||
'⚠️ WARNING: This will permanently delete the following tasks:'
|
'⚠️ WARNING: This will permanently delete the following task:'
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
console.log();
|
console.log();
|
||||||
|
|
||||||
for (const taskId of taskIdArray) {
|
if (typeof taskId === 'string' && taskId.includes('.')) {
|
||||||
const task = findTaskById(data.tasks, taskId);
|
// It's a subtask
|
||||||
|
const [parentId, subtaskId] = taskId.split('.');
|
||||||
|
console.log(chalk.white.bold(`Subtask ${taskId}: ${task.title}`));
|
||||||
|
console.log(
|
||||||
|
chalk.gray(
|
||||||
|
`Parent Task: ${task.parentTask.id} - ${task.parentTask.title}`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// It's a main task
|
||||||
|
console.log(chalk.white.bold(`Task ${taskId}: ${task.title}`));
|
||||||
|
|
||||||
if (typeof taskId === 'string' && taskId.includes('.')) {
|
// Show if it has subtasks
|
||||||
// It's a subtask
|
if (task.subtasks && task.subtasks.length > 0) {
|
||||||
const [parentId, subtaskId] = taskId.split('.');
|
|
||||||
console.log(chalk.white.bold(`Subtask ${taskId}: ${task.title}`));
|
|
||||||
console.log(
|
console.log(
|
||||||
chalk.gray(
|
chalk.yellow(
|
||||||
`Parent Task: ${task.parentTask.id} - ${task.parentTask.title}`
|
`⚠️ This task has ${task.subtasks.length} subtasks that will also be deleted!`
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
// It's a main task
|
|
||||||
console.log(chalk.white.bold(`Task ${taskId}: ${task.title}`));
|
|
||||||
|
|
||||||
// Show if it has subtasks
|
|
||||||
if (task.subtasks && task.subtasks.length > 0) {
|
|
||||||
console.log(
|
|
||||||
chalk.yellow(
|
|
||||||
`⚠️ This task has ${task.subtasks.length} subtasks that will also be deleted!`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show if other tasks depend on it
|
|
||||||
const dependentTasks = data.tasks.filter(
|
|
||||||
(t) =>
|
|
||||||
t.dependencies &&
|
|
||||||
t.dependencies.includes(parseInt(taskId, 10))
|
|
||||||
);
|
|
||||||
|
|
||||||
if (dependentTasks.length > 0) {
|
|
||||||
console.log(
|
|
||||||
chalk.yellow(
|
|
||||||
`⚠️ Warning: ${dependentTasks.length} other tasks depend on this task!`
|
|
||||||
)
|
|
||||||
);
|
|
||||||
console.log(
|
|
||||||
chalk.yellow('These dependencies will be removed:')
|
|
||||||
);
|
|
||||||
dependentTasks.forEach((t) => {
|
|
||||||
console.log(chalk.yellow(` - Task ${t.id}: ${t.title}`));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
console.log();
|
|
||||||
|
// Show if other tasks depend on it
|
||||||
|
const dependentTasks = data.tasks.filter(
|
||||||
|
(t) =>
|
||||||
|
t.dependencies && t.dependencies.includes(parseInt(taskId, 10))
|
||||||
|
);
|
||||||
|
|
||||||
|
if (dependentTasks.length > 0) {
|
||||||
|
console.log(
|
||||||
|
chalk.yellow(
|
||||||
|
`⚠️ Warning: ${dependentTasks.length} other tasks depend on this task!`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
console.log(chalk.yellow('These dependencies will be removed:'));
|
||||||
|
dependentTasks.forEach((t) => {
|
||||||
|
console.log(chalk.yellow(` - Task ${t.id}: ${t.title}`));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log();
|
||||||
|
|
||||||
// Prompt for confirmation
|
// Prompt for confirmation
|
||||||
const { confirm } = await inquirer.prompt([
|
const { confirm } = await inquirer.prompt([
|
||||||
{
|
{
|
||||||
type: 'confirm',
|
type: 'confirm',
|
||||||
name: 'confirm',
|
name: 'confirm',
|
||||||
message: chalk.red.bold(
|
message: chalk.red.bold(
|
||||||
`Are you sure you want to permanently delete ${taskIdArray.length > 1 ? 'these tasks' : 'this task'}?`
|
'Are you sure you want to permanently delete this task?'
|
||||||
),
|
),
|
||||||
default: false
|
default: false
|
||||||
}
|
}
|
||||||
@@ -1497,72 +1483,31 @@ function registerCommands(programInstance) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const indicator = startLoadingIndicator('Removing tasks...');
|
const indicator = startLoadingIndicator('Removing task...');
|
||||||
|
|
||||||
// Remove each task
|
// Remove the task
|
||||||
const results = [];
|
const result = await removeTask(tasksPath, taskId);
|
||||||
for (const taskId of taskIdArray) {
|
|
||||||
try {
|
|
||||||
const result = await removeTask(tasksPath, taskId);
|
|
||||||
results.push({ taskId, success: true, ...result });
|
|
||||||
} catch (error) {
|
|
||||||
results.push({ taskId, success: false, error: error.message });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stopLoadingIndicator(indicator);
|
stopLoadingIndicator(indicator);
|
||||||
|
|
||||||
// Display results
|
// Display success message with appropriate color based on task or subtask
|
||||||
const successfulRemovals = results.filter((r) => r.success);
|
if (typeof taskId === 'string' && taskId.includes('.')) {
|
||||||
const failedRemovals = results.filter((r) => !r.success);
|
// It was a subtask
|
||||||
|
|
||||||
if (successfulRemovals.length > 0) {
|
|
||||||
console.log(
|
console.log(
|
||||||
boxen(
|
boxen(
|
||||||
chalk.green(
|
chalk.green(`Subtask ${taskId} has been successfully removed`),
|
||||||
`Successfully removed ${successfulRemovals.length} task${successfulRemovals.length > 1 ? 's' : ''}`
|
{ padding: 1, borderColor: 'green', borderStyle: 'round' }
|
||||||
) +
|
|
||||||
'\n\n' +
|
|
||||||
successfulRemovals
|
|
||||||
.map((r) =>
|
|
||||||
chalk.white(
|
|
||||||
`✓ ${r.taskId.includes('.') ? 'Subtask' : 'Task'} ${r.taskId}`
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.join('\n'),
|
|
||||||
{
|
|
||||||
padding: 1,
|
|
||||||
borderColor: 'green',
|
|
||||||
borderStyle: 'round',
|
|
||||||
margin: { top: 1 }
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
} else {
|
||||||
|
// It was a main task
|
||||||
if (failedRemovals.length > 0) {
|
|
||||||
console.log(
|
console.log(
|
||||||
boxen(
|
boxen(chalk.green(`Task ${taskId} has been successfully removed`), {
|
||||||
chalk.red(
|
padding: 1,
|
||||||
`Failed to remove ${failedRemovals.length} task${failedRemovals.length > 1 ? 's' : ''}`
|
borderColor: 'green',
|
||||||
) +
|
borderStyle: 'round'
|
||||||
'\n\n' +
|
})
|
||||||
failedRemovals
|
|
||||||
.map((r) => chalk.white(`✗ ${r.taskId}: ${r.error}`))
|
|
||||||
.join('\n'),
|
|
||||||
{
|
|
||||||
padding: 1,
|
|
||||||
borderColor: 'red',
|
|
||||||
borderStyle: 'round',
|
|
||||||
margin: { top: 1 }
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Exit with error if any removals failed
|
|
||||||
if (successfulRemovals.length === 0) {
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(
|
console.error(
|
||||||
|
|||||||
2636
tasks/tasks.json.bak
2636
tasks/tasks.json.bak
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user