Compare commits

..

6 Commits

12 changed files with 73 additions and 227 deletions

View File

@@ -0,0 +1,6 @@
---
'task-master-ai': patch
---
- Fixes shebang issue not allowing task-master to run on certain windows operating systems
- Resolves #241 #211 #184 #193

View File

@@ -0,0 +1,5 @@
---
'task-master-ai': patch
---
Updates the parameter descriptions for update, update-task and update-subtask to ensure the MCP server correctly reaches for the right update command based on what is being updated -- all tasks, one task, or a subtask.

View File

@@ -0,0 +1,6 @@
---
'task-master-ai': patch
---
- Fix `task-master init` polluting codebase with new packages inside `package.json` and modifying project `README`
- Now only initializes with cursor rules, windsurf rules, mcp.json, scripts/example_prd.txt, .gitignore modifications, and `README-task-master.md`

View File

@@ -20,7 +20,13 @@ A task management system for AI-driven development with Claude, designed to work
MCP (Model Control Protocol) provides the easiest way to get started with Task Master directly in your editor.
1. **Add the MCP config to your editor** (Cursor recommended, but it works with other text editors):
1. **Install the package**
```bash
npm i -g task-master-ai
```
2. **Add the MCP config to your editor** (Cursor recommended, but it works with other text editors):
```json
{

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env node --trace-deprecation
#!/usr/bin/env node
/**
* Task Master

View File

@@ -10,7 +10,13 @@ There are two ways to set up Task Master: using MCP (recommended) or via npm ins
MCP (Model Control Protocol) provides the easiest way to get started with Task Master directly in your editor.
1. **Add the MCP config to your editor** (Cursor recommended, but it works with other text editors):
1. **Install the package**
```bash
npm i -g task-master-ai
```
2. **Add the MCP config to your editor** (Cursor recommended, but it works with other text editors):
```json
{

View File

@@ -20,12 +20,12 @@ export function registerUpdateSubtaskTool(server) {
server.addTool({
name: 'update_subtask',
description:
'Appends additional information to a specific subtask without replacing existing content',
'Appends timestamped information to a specific subtask without replacing existing content',
parameters: z.object({
id: z
.string()
.describe(
'ID of the subtask to update in format "parentId.subtaskId" (e.g., "5.2")'
'ID of the subtask to update in format "parentId.subtaskId" (e.g., "5.2"). Parent ID is the ID of the task that contains the subtask.'
),
prompt: z.string().describe('Information to add to the subtask'),
research: z

View File

@@ -24,7 +24,9 @@ export function registerUpdateTaskTool(server) {
parameters: z.object({
id: z
.string()
.describe("ID of the task or subtask (e.g., '15', '15.2') to update"),
.describe(
"ID of the task (e.g., '15') to update. Subtasks are supported using the update-subtask tool."
),
prompt: z
.string()
.describe('New information or context to incorporate into the task'),

View File

@@ -20,7 +20,7 @@ export function registerUpdateTool(server) {
server.addTool({
name: 'update',
description:
"Update multiple upcoming tasks (with ID >= 'from' ID) based on new context or changes provided in the prompt. Use 'update_task' instead for a single specific task.",
"Update multiple upcoming tasks (with ID >= 'from' ID) based on new context or changes provided in the prompt. Use 'update_task' instead for a single specific task or 'update_subtask' for subtasks.",
parameters: z.object({
from: z
.string()

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "task-master-ai",
"version": "0.10.1",
"version": "0.11.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "task-master-ai",
"version": "0.10.1",
"version": "0.11.1",
"license": "MIT WITH Commons-Clause",
"dependencies": {
"@anthropic-ai/sdk": "^0.39.0",

View File

@@ -1,6 +1,6 @@
{
"name": "task-master-ai",
"version": "0.11.0",
"version": "0.11.1",
"description": "A task management system for ambitious AI-driven development that doesn't overwhelm and confuse Cursor.",
"main": "index.js",
"type": "module",

View File

@@ -15,7 +15,6 @@
import fs from 'fs';
import path from 'path';
import { execSync } from 'child_process';
import readline from 'readline';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
@@ -179,9 +178,6 @@ function copyTemplateFile(templateName, targetPath, replacements = {}) {
// Map template names to their actual source paths
switch (templateName) {
case 'dev.js':
sourcePath = path.join(__dirname, 'dev.js');
break;
case 'scripts_README.md':
sourcePath = path.join(__dirname, '..', 'assets', 'scripts_README.md');
break;
@@ -297,61 +293,8 @@ function copyTemplateFile(templateName, targetPath, replacements = {}) {
return;
}
// Handle package.json - merge dependencies
if (filename === 'package.json') {
log('info', `${targetPath} already exists, merging dependencies...`);
try {
const existingPackageJson = JSON.parse(
fs.readFileSync(targetPath, 'utf8')
);
const newPackageJson = JSON.parse(content);
// Merge dependencies, preferring existing versions in case of conflicts
existingPackageJson.dependencies = {
...newPackageJson.dependencies,
...existingPackageJson.dependencies
};
// Add our scripts if they don't already exist
existingPackageJson.scripts = {
...existingPackageJson.scripts,
...Object.fromEntries(
Object.entries(newPackageJson.scripts).filter(
([key]) => !existingPackageJson.scripts[key]
)
)
};
// Preserve existing type if present
if (!existingPackageJson.type && newPackageJson.type) {
existingPackageJson.type = newPackageJson.type;
}
fs.writeFileSync(
targetPath,
JSON.stringify(existingPackageJson, null, 2)
);
log(
'success',
`Updated ${targetPath} with required dependencies and scripts`
);
} catch (error) {
log('error', `Failed to merge package.json: ${error.message}`);
// Fallback to writing a backup of the existing file and creating a new one
const backupPath = `${targetPath}.backup-${Date.now()}`;
fs.copyFileSync(targetPath, backupPath);
log('info', `Created backup of existing package.json at ${backupPath}`);
fs.writeFileSync(targetPath, content);
log(
'warn',
`Replaced ${targetPath} with new content (due to JSON parsing error)`
);
}
return;
}
// Handle README.md - offer to preserve or create a different file
if (filename === 'README.md') {
if (filename === 'README-task-master.md') {
log('info', `${targetPath} already exists`);
// Create a separate README file specifically for this project
const taskMasterReadmePath = path.join(
@@ -361,7 +304,7 @@ function copyTemplateFile(templateName, targetPath, replacements = {}) {
fs.writeFileSync(taskMasterReadmePath, content);
log(
'success',
`Created ${taskMasterReadmePath} (preserved original README.md)`
`Created ${taskMasterReadmePath} (preserved original README-task-master.md)`
);
return;
}
@@ -396,6 +339,30 @@ async function initializeProject(options = {}) {
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
const skipPrompts = options.yes || (options.name && options.description);
if (!isSilentMode()) {
@@ -414,7 +381,6 @@ async function initializeProject(options = {}) {
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.aliases || false;
if (dryRun) {
@@ -429,9 +395,6 @@ async function initializeProject(options = {}) {
if (addAliases) {
log('info', 'Would add shell aliases for task-master');
}
if (!skipInstall) {
log('info', 'Would install dependencies');
}
return {
projectName,
projectDescription,
@@ -447,7 +410,6 @@ async function initializeProject(options = {}) {
projectDescription,
projectVersion,
authorName,
skipInstall,
addAliases
);
} else {
@@ -514,9 +476,8 @@ async function initializeProject(options = {}) {
return; // Added return for clarity
}
// Still respect dryRun/skipInstall if passed initially even when prompting
// Still respect dryRun 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');
@@ -530,9 +491,6 @@ async function initializeProject(options = {}) {
if (addAliasesPrompted) {
log('info', 'Would add shell aliases for task-master');
}
if (!skipInstall) {
log('info', 'Would install dependencies');
}
return {
projectName,
projectDescription,
@@ -548,7 +506,6 @@ async function initializeProject(options = {}) {
projectDescription,
projectVersion,
authorName,
skipInstall, // Use value from initial options
addAliasesPrompted // Use value from prompt
);
} catch (error) {
@@ -574,7 +531,6 @@ function createProjectStructure(
projectDescription,
projectVersion,
authorName,
skipInstall,
addAliases
) {
const targetDir = process.cwd();
@@ -585,105 +541,8 @@ function createProjectStructure(
ensureDirectoryExists(path.join(targetDir, 'scripts'));
ensureDirectoryExists(path.join(targetDir, 'tasks'));
// Define our package.json content
const packageJson = {
name: projectName.toLowerCase().replace(/\s+/g, '-'),
version: projectVersion,
description: projectDescription,
author: authorName,
type: 'module',
scripts: {
dev: 'node scripts/dev.js',
list: 'node scripts/dev.js list',
generate: 'node scripts/dev.js generate',
'parse-prd': 'node scripts/dev.js parse-prd'
},
dependencies: {
'@anthropic-ai/sdk': '^0.39.0',
boxen: '^8.0.1',
chalk: '^4.1.2',
commander: '^11.1.0',
'cli-table3': '^0.6.5',
cors: '^2.8.5',
dotenv: '^16.3.1',
express: '^4.21.2',
fastmcp: '^1.20.5',
figlet: '^1.8.0',
'fuse.js': '^7.0.0',
'gradient-string': '^3.0.0',
helmet: '^8.1.0',
inquirer: '^12.5.0',
jsonwebtoken: '^9.0.2',
'lru-cache': '^10.2.0',
openai: '^4.89.0',
ora: '^8.2.0'
}
};
// Check if package.json exists and merge if it does
const packageJsonPath = path.join(targetDir, 'package.json');
if (fs.existsSync(packageJsonPath)) {
log('info', 'package.json already exists, merging content...');
try {
const existingPackageJson = JSON.parse(
fs.readFileSync(packageJsonPath, 'utf8')
);
// Preserve existing fields but add our required ones
const mergedPackageJson = {
...existingPackageJson,
scripts: {
...existingPackageJson.scripts,
...Object.fromEntries(
Object.entries(packageJson.scripts).filter(
([key]) =>
!existingPackageJson.scripts ||
!existingPackageJson.scripts[key]
)
)
},
dependencies: {
...(existingPackageJson.dependencies || {}),
...Object.fromEntries(
Object.entries(packageJson.dependencies).filter(
([key]) =>
!existingPackageJson.dependencies ||
!existingPackageJson.dependencies[key]
)
)
}
};
// Ensure type is set if not already present
if (!mergedPackageJson.type && packageJson.type) {
mergedPackageJson.type = packageJson.type;
}
fs.writeFileSync(
packageJsonPath,
JSON.stringify(mergedPackageJson, null, 2)
);
log('success', 'Updated package.json with required fields');
} catch (error) {
log('error', `Failed to merge package.json: ${error.message}`);
// Create a backup before potentially modifying
const backupPath = `${packageJsonPath}.backup-${Date.now()}`;
fs.copyFileSync(packageJsonPath, backupPath);
log('info', `Created backup of existing package.json at ${backupPath}`);
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
log(
'warn',
'Created new package.json (backup of original file was created)'
);
}
} else {
// If package.json doesn't exist, create it
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
log('success', 'Created package.json');
}
// Setup MCP configuration for integration with Cursor
setupMCPConfiguration(targetDir, packageJson.name);
setupMCPConfiguration(targetDir, projectName);
// Copy template files with replacements
const replacements = {
@@ -731,15 +590,6 @@ function createProjectStructure(
// Copy .windsurfrules
copyTemplateFile('windsurfrules', path.join(targetDir, '.windsurfrules'));
// Copy scripts/dev.js
copyTemplateFile('dev.js', path.join(targetDir, 'scripts', 'dev.js'));
// Copy scripts/README.md
copyTemplateFile(
'scripts_README.md',
path.join(targetDir, 'scripts', 'README.md')
);
// Copy example_prd.txt
copyTemplateFile(
'example_prd.txt',
@@ -749,43 +599,13 @@ function createProjectStructure(
// Create main README.md
copyTemplateFile(
'README-task-master.md',
path.join(targetDir, 'README.md'),
path.join(targetDir, 'README-task-master.md'),
replacements
);
// Initialize git repository if git is available
try {
if (!fs.existsSync(path.join(targetDir, '.git'))) {
log('info', 'Initializing git repository...');
execSync('git init', { stdio: 'ignore' });
log('success', 'Git repository initialized');
}
} catch (error) {
log('warn', 'Git not available, skipping repository initialization');
}
// Run npm install automatically
if (!isSilentMode()) {
console.log(
boxen(chalk.cyan('Installing dependencies...'), {
padding: 0.5,
margin: 0.5,
borderStyle: 'round',
borderColor: 'blue'
})
);
}
try {
if (!skipInstall) {
execSync('npm install', { stdio: 'inherit', cwd: targetDir });
log('success', 'Dependencies installed successfully!');
} else {
log('info', 'Dependencies installation skipped');
}
} catch (error) {
log('error', 'Failed to install dependencies:', error.message);
log('error', 'Please run npm install manually');
// Add shell aliases if requested
if (addAliases) {
addShellAliases();
}
// Display success message
@@ -807,11 +627,6 @@ function createProjectStructure(
);
}
// Add shell aliases if requested
if (addAliases) {
addShellAliases();
}
// Display next steps in a nice box
if (!isSilentMode()) {
console.log(