Compare commits

...

2 Commits

Author SHA1 Message Date
Ralph Khreish
879409ac66 feat: improve tm init 2025-04-16 14:51:02 +02:00
Ralph Khreish
d9296614f8 chore: fix weird bug where package.json is not upgrading its version based on current package version 2025-04-16 14:48:20 +02:00
4 changed files with 41 additions and 220 deletions

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`

4
package-lock.json generated
View File

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

View File

@@ -1,6 +1,6 @@
{ {
"name": "task-master-ai", "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.", "description": "A task management system for ambitious AI-driven development that doesn't overwhelm and confuse Cursor.",
"main": "index.js", "main": "index.js",
"type": "module", "type": "module",

View File

@@ -15,7 +15,6 @@
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
import { execSync } from 'child_process';
import readline from 'readline'; import readline from 'readline';
import { fileURLToPath } from 'url'; import { fileURLToPath } from 'url';
import { dirname } from 'path'; import { dirname } from 'path';
@@ -179,9 +178,6 @@ function copyTemplateFile(templateName, targetPath, replacements = {}) {
// Map template names to their actual source paths // Map template names to their actual source paths
switch (templateName) { switch (templateName) {
case 'dev.js':
sourcePath = path.join(__dirname, 'dev.js');
break;
case 'scripts_README.md': case 'scripts_README.md':
sourcePath = path.join(__dirname, '..', 'assets', 'scripts_README.md'); sourcePath = path.join(__dirname, '..', 'assets', 'scripts_README.md');
break; break;
@@ -297,61 +293,8 @@ function copyTemplateFile(templateName, targetPath, replacements = {}) {
return; 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 // 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`); log('info', `${targetPath} already exists`);
// Create a separate README file specifically for this project // Create a separate README file specifically for this project
const taskMasterReadmePath = path.join( const taskMasterReadmePath = path.join(
@@ -361,7 +304,7 @@ function copyTemplateFile(templateName, targetPath, replacements = {}) {
fs.writeFileSync(taskMasterReadmePath, content); fs.writeFileSync(taskMasterReadmePath, content);
log( log(
'success', 'success',
`Created ${taskMasterReadmePath} (preserved original README.md)` `Created ${taskMasterReadmePath} (preserved original README-task-master.md)`
); );
return; return;
} }
@@ -396,6 +339,30 @@ async function initializeProject(options = {}) {
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 || (options.name && options.description);
if (!isSilentMode()) { if (!isSilentMode()) {
@@ -414,7 +381,6 @@ async function initializeProject(options = {}) {
const projectVersion = options.version || '0.1.0'; // Default from commands.js or here const projectVersion = options.version || '0.1.0'; // Default from commands.js or here
const authorName = options.author || 'Vibe coder'; // Default if not provided 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 addAliases = options.aliases || false; const addAliases = options.aliases || false;
if (dryRun) { if (dryRun) {
@@ -429,9 +395,6 @@ async function initializeProject(options = {}) {
if (addAliases) { if (addAliases) {
log('info', 'Would add shell aliases for task-master'); log('info', 'Would add shell aliases for task-master');
} }
if (!skipInstall) {
log('info', 'Would install dependencies');
}
return { return {
projectName, projectName,
projectDescription, projectDescription,
@@ -447,7 +410,6 @@ async function initializeProject(options = {}) {
projectDescription, projectDescription,
projectVersion, projectVersion,
authorName, authorName,
skipInstall,
addAliases addAliases
); );
} else { } else {
@@ -514,9 +476,8 @@ async function initializeProject(options = {}) {
return; // Added return for clarity 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 dryRun = options.dryRun || 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');
@@ -530,9 +491,6 @@ async function initializeProject(options = {}) {
if (addAliasesPrompted) { if (addAliasesPrompted) {
log('info', 'Would add shell aliases for task-master'); log('info', 'Would add shell aliases for task-master');
} }
if (!skipInstall) {
log('info', 'Would install dependencies');
}
return { return {
projectName, projectName,
projectDescription, projectDescription,
@@ -548,7 +506,6 @@ async function initializeProject(options = {}) {
projectDescription, projectDescription,
projectVersion, projectVersion,
authorName, authorName,
skipInstall, // Use value from initial options
addAliasesPrompted // Use value from prompt addAliasesPrompted // Use value from prompt
); );
} catch (error) { } catch (error) {
@@ -574,7 +531,6 @@ function createProjectStructure(
projectDescription, projectDescription,
projectVersion, projectVersion,
authorName, authorName,
skipInstall,
addAliases addAliases
) { ) {
const targetDir = process.cwd(); const targetDir = process.cwd();
@@ -585,105 +541,8 @@ function createProjectStructure(
ensureDirectoryExists(path.join(targetDir, 'scripts')); ensureDirectoryExists(path.join(targetDir, 'scripts'));
ensureDirectoryExists(path.join(targetDir, 'tasks')); 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 // Setup MCP configuration for integration with Cursor
setupMCPConfiguration(targetDir, packageJson.name); setupMCPConfiguration(targetDir, projectName);
// Copy template files with replacements // Copy template files with replacements
const replacements = { const replacements = {
@@ -731,15 +590,6 @@ function createProjectStructure(
// Copy .windsurfrules // Copy .windsurfrules
copyTemplateFile('windsurfrules', path.join(targetDir, '.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 // Copy example_prd.txt
copyTemplateFile( copyTemplateFile(
'example_prd.txt', 'example_prd.txt',
@@ -749,43 +599,13 @@ function createProjectStructure(
// Create main README.md // Create main README.md
copyTemplateFile( copyTemplateFile(
'README-task-master.md', 'README-task-master.md',
path.join(targetDir, 'README.md'), path.join(targetDir, 'README-task-master.md'),
replacements replacements
); );
// Initialize git repository if git is available // Add shell aliases if requested
try { if (addAliases) {
if (!fs.existsSync(path.join(targetDir, '.git'))) { addShellAliases();
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');
} }
// Display success message // Display success message
@@ -807,11 +627,6 @@ function createProjectStructure(
); );
} }
// Add shell aliases if requested
if (addAliases) {
addShellAliases();
}
// Display next steps in a nice box // Display next steps in a nice box
if (!isSilentMode()) { if (!isSilentMode()) {
console.log( console.log(