fix merge conflicts to prep for merge with branch next
- Enhance E2E testing and LLM analysis report and: - Add --analyze-log flag to run_e2e.sh to re-run LLM analysis on existing logs. - Add test:e2e and analyze-log scripts to package.json for easier execution. - Correct display errors and dependency validation output: - Update chalk usage in add-task.js to use bracket notation (chalk[color]) compatible with v5, resolving 'chalk.keyword is not a function' error. - Modify fix-dependencies command output to show red failure box with issue count instead of green success box when validation fails. - Refactor interactive model setup: - Verify inclusion of 'No change' option during interactive model setup flow (task-master models --setup). - Update model definitions: - Add max_tokens field for gpt-4o in supported-models.json. - Remove unused scripts: - Delete prepare-package.js and rule-transformer.test.js. Release candidate
This commit is contained in:
363
scripts/init.js
363
scripts/init.js
@@ -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';
|
||||
@@ -24,6 +23,8 @@ import figlet from 'figlet';
|
||||
import boxen from 'boxen';
|
||||
import gradient from 'gradient-string';
|
||||
import { isSilentMode } from './modules/utils.js';
|
||||
import { convertAllCursorRulesToRooRules } from './modules/rule-transformer.js';
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
@@ -179,9 +180,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;
|
||||
@@ -227,6 +225,27 @@ function copyTemplateFile(templateName, targetPath, replacements = {}) {
|
||||
case 'windsurfrules':
|
||||
sourcePath = path.join(__dirname, '..', 'assets', '.windsurfrules');
|
||||
break;
|
||||
case '.roomodes':
|
||||
sourcePath = path.join(__dirname, '..', 'assets', 'roocode', '.roomodes');
|
||||
break;
|
||||
case 'architect-rules':
|
||||
case 'ask-rules':
|
||||
case 'boomerang-rules':
|
||||
case 'code-rules':
|
||||
case 'debug-rules':
|
||||
case 'test-rules':
|
||||
// Extract the mode name from the template name (e.g., 'architect' from 'architect-rules')
|
||||
const mode = templateName.split('-')[0];
|
||||
sourcePath = path.join(
|
||||
__dirname,
|
||||
'..',
|
||||
'assets',
|
||||
'roocode',
|
||||
'.roo',
|
||||
`rules-${mode}`,
|
||||
templateName
|
||||
);
|
||||
break;
|
||||
default:
|
||||
// For other files like env.example, gitignore, etc. that don't have direct equivalents
|
||||
sourcePath = path.join(__dirname, '..', 'assets', templateName);
|
||||
@@ -297,61 +316,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 +327,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;
|
||||
}
|
||||
@@ -389,7 +355,6 @@ async function initializeProject(options = {}) {
|
||||
console.log('===== DEBUG: INITIALIZE PROJECT OPTIONS RECEIVED =====');
|
||||
console.log('Full options object:', JSON.stringify(options));
|
||||
console.log('options.yes:', options.yes);
|
||||
console.log('options.name:', options.name);
|
||||
console.log('==================================================');
|
||||
}
|
||||
|
||||
@@ -411,43 +376,21 @@ async function initializeProject(options = {}) {
|
||||
const projectVersion = options.version || '0.1.0';
|
||||
const authorName = options.author || 'Vibe coder';
|
||||
const dryRun = options.dryRun || false;
|
||||
const skipInstall = options.skipInstall || false;
|
||||
const addAliases = options.aliases || false;
|
||||
|
||||
if (dryRun) {
|
||||
log('info', 'DRY RUN MODE: No files will be modified');
|
||||
log(
|
||||
'info',
|
||||
`Would initialize project: ${projectName} (${projectVersion})`
|
||||
);
|
||||
log('info', `Description: ${projectDescription}`);
|
||||
log('info', `Author: ${authorName || 'Not specified'}`);
|
||||
log('info', 'Would initialize Task Master project');
|
||||
log('info', 'Would create/update necessary project files');
|
||||
if (addAliases) {
|
||||
log('info', 'Would add shell aliases for task-master');
|
||||
}
|
||||
if (!skipInstall) {
|
||||
log('info', 'Would install dependencies');
|
||||
}
|
||||
return {
|
||||
projectName,
|
||||
projectDescription,
|
||||
projectVersion,
|
||||
authorName,
|
||||
dryRun: true
|
||||
};
|
||||
}
|
||||
|
||||
// Call createProjectStructure (no need for isInteractive flag)
|
||||
createProjectStructure(
|
||||
projectName,
|
||||
projectDescription,
|
||||
projectVersion,
|
||||
authorName,
|
||||
skipInstall,
|
||||
addAliases,
|
||||
dryRun // Pass dryRun
|
||||
);
|
||||
createProjectStructure(addAliases, dryRun);
|
||||
} else {
|
||||
// Interactive logic
|
||||
log('info', 'Required options not provided, proceeding with prompts.');
|
||||
@@ -457,41 +400,17 @@ async function initializeProject(options = {}) {
|
||||
});
|
||||
|
||||
try {
|
||||
// Prompt user for input...
|
||||
const projectName = await promptQuestion(
|
||||
rl,
|
||||
chalk.cyan('Enter project name: ')
|
||||
);
|
||||
const projectDescription = await promptQuestion(
|
||||
rl,
|
||||
chalk.cyan('Enter project description: ')
|
||||
);
|
||||
const projectVersionInput = await promptQuestion(
|
||||
rl,
|
||||
chalk.cyan('Enter project version (default: 1.0.0): ')
|
||||
);
|
||||
const authorName = await promptQuestion(
|
||||
rl,
|
||||
chalk.cyan('Enter your name: ')
|
||||
);
|
||||
// Only prompt for shell aliases
|
||||
const addAliasesInput = await promptQuestion(
|
||||
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 projectVersion = projectVersionInput.trim()
|
||||
? projectVersionInput
|
||||
: '1.0.0';
|
||||
|
||||
// Confirm settings...
|
||||
console.log('\nProject settings:');
|
||||
console.log(chalk.blue('Name:'), chalk.white(projectName));
|
||||
console.log(chalk.blue('Description:'), chalk.white(projectDescription));
|
||||
console.log(chalk.blue('Version:'), chalk.white(projectVersion));
|
||||
console.log(
|
||||
chalk.blue('Author:'),
|
||||
chalk.white(authorName || 'Not specified')
|
||||
);
|
||||
console.log('\nTask Master Project settings:');
|
||||
console.log(
|
||||
chalk.blue(
|
||||
'Add shell aliases (so you can use "tm" instead of "task-master"):'
|
||||
@@ -513,42 +432,21 @@ async function initializeProject(options = {}) {
|
||||
}
|
||||
|
||||
const dryRun = options.dryRun || false;
|
||||
const skipInstall = options.skipInstall || false;
|
||||
|
||||
if (dryRun) {
|
||||
log('info', 'DRY RUN MODE: No files will be modified');
|
||||
log(
|
||||
'info',
|
||||
`Would initialize project: ${projectName} (${projectVersion})`
|
||||
);
|
||||
log('info', `Description: ${projectDescription}`);
|
||||
log('info', `Author: ${authorName || 'Not specified'}`);
|
||||
log('info', 'Would initialize Task Master project');
|
||||
log('info', 'Would create/update necessary project files');
|
||||
if (addAliasesPrompted) {
|
||||
log('info', 'Would add shell aliases for task-master');
|
||||
}
|
||||
if (!skipInstall) {
|
||||
log('info', 'Would install dependencies');
|
||||
}
|
||||
return {
|
||||
projectName,
|
||||
projectDescription,
|
||||
projectVersion,
|
||||
authorName,
|
||||
dryRun: true
|
||||
};
|
||||
}
|
||||
|
||||
// Call createProjectStructure (no need for isInteractive flag)
|
||||
createProjectStructure(
|
||||
projectName,
|
||||
projectDescription,
|
||||
projectVersion,
|
||||
authorName,
|
||||
skipInstall,
|
||||
addAliasesPrompted,
|
||||
dryRun // Pass dryRun
|
||||
);
|
||||
// Create structure using only necessary values
|
||||
createProjectStructure(addAliasesPrompted, dryRun);
|
||||
} catch (error) {
|
||||
rl.close();
|
||||
log('error', `Error during initialization process: ${error.message}`);
|
||||
@@ -567,138 +465,35 @@ function promptQuestion(rl, question) {
|
||||
}
|
||||
|
||||
// Function to create the project structure
|
||||
function createProjectStructure(
|
||||
projectName,
|
||||
projectDescription,
|
||||
projectVersion,
|
||||
authorName,
|
||||
skipInstall,
|
||||
addAliases,
|
||||
dryRun
|
||||
) {
|
||||
function createProjectStructure(addAliases, dryRun) {
|
||||
const targetDir = process.cwd();
|
||||
log('info', `Initializing project in ${targetDir}`);
|
||||
|
||||
// Create directories
|
||||
ensureDirectoryExists(path.join(targetDir, '.cursor', 'rules'));
|
||||
|
||||
// Create Roo directories
|
||||
ensureDirectoryExists(path.join(targetDir, '.roo'));
|
||||
ensureDirectoryExists(path.join(targetDir, '.roo', 'rules'));
|
||||
for (const mode of [
|
||||
'architect',
|
||||
'ask',
|
||||
'boomerang',
|
||||
'code',
|
||||
'debug',
|
||||
'test'
|
||||
]) {
|
||||
ensureDirectoryExists(path.join(targetDir, '.roo', `rules-${mode}`));
|
||||
}
|
||||
|
||||
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: {
|
||||
'@ai-sdk/anthropic': '^1.2.10',
|
||||
'@ai-sdk/azure': '^1.3.17',
|
||||
'@ai-sdk/google': '^1.2.13',
|
||||
'@ai-sdk/mistral': '^1.2.7',
|
||||
'@ai-sdk/openai': '^1.3.20',
|
||||
'@ai-sdk/perplexity': '^1.1.7',
|
||||
'@ai-sdk/xai': '^1.2.15',
|
||||
'@openrouter/ai-sdk-provider': '^0.4.5',
|
||||
'ollama-ai-provider': '^1.2.0',
|
||||
ai: '^4.3.10',
|
||||
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 if it existed)'
|
||||
);
|
||||
}
|
||||
} 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);
|
||||
|
||||
// Copy template files with replacements
|
||||
const replacements = {
|
||||
projectName,
|
||||
projectDescription,
|
||||
projectVersion,
|
||||
authorName,
|
||||
year: new Date().getFullYear()
|
||||
};
|
||||
|
||||
@@ -745,17 +540,24 @@ function createProjectStructure(
|
||||
path.join(targetDir, '.cursor', 'rules', 'self_improve.mdc')
|
||||
);
|
||||
|
||||
// Generate Roo rules from Cursor rules
|
||||
log('info', 'Generating Roo rules from Cursor rules...');
|
||||
convertAllCursorRulesToRooRules(targetDir);
|
||||
|
||||
// Copy .windsurfrules
|
||||
copyTemplateFile('windsurfrules', path.join(targetDir, '.windsurfrules'));
|
||||
|
||||
// Copy scripts/dev.js
|
||||
copyTemplateFile('dev.js', path.join(targetDir, 'scripts', 'dev.js'));
|
||||
// Copy .roomodes for Roo Code integration
|
||||
copyTemplateFile('.roomodes', path.join(targetDir, '.roomodes'));
|
||||
|
||||
// Copy scripts/README.md
|
||||
copyTemplateFile(
|
||||
'scripts_README.md',
|
||||
path.join(targetDir, 'scripts', 'README.md')
|
||||
);
|
||||
// Copy Roo rule files for each mode
|
||||
const rooModes = ['architect', 'ask', 'boomerang', 'code', 'debug', 'test'];
|
||||
for (const mode of rooModes) {
|
||||
copyTemplateFile(
|
||||
`${mode}-rules`,
|
||||
path.join(targetDir, '.roo', `rules-${mode}`, `${mode}-rules`)
|
||||
);
|
||||
}
|
||||
|
||||
// Copy example_prd.txt
|
||||
copyTemplateFile(
|
||||
@@ -766,7 +568,7 @@ function createProjectStructure(
|
||||
// Create main README.md
|
||||
copyTemplateFile(
|
||||
'README-task-master.md',
|
||||
path.join(targetDir, 'README.md'),
|
||||
path.join(targetDir, 'README-task-master.md'),
|
||||
replacements
|
||||
);
|
||||
|
||||
@@ -804,24 +606,6 @@ function createProjectStructure(
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
if (!skipInstall) {
|
||||
// Use the determined options
|
||||
execSync('npm install', npmInstallOptions);
|
||||
log('success', 'Dependencies installed successfully!');
|
||||
} else {
|
||||
log('info', 'Dependencies installation skipped');
|
||||
}
|
||||
} catch (error) {
|
||||
log('error', 'Failed to install dependencies:', error.message);
|
||||
// Add more detail if silent, as the user won't see npm's error directly
|
||||
if (isSilentMode()) {
|
||||
log('error', 'Check npm logs or run "npm install" manually for details.');
|
||||
} else {
|
||||
log('error', 'Please run npm install manually');
|
||||
}
|
||||
}
|
||||
|
||||
// === Add Model Configuration Step ===
|
||||
if (!isSilentMode() && !dryRun) {
|
||||
console.log(
|
||||
@@ -876,11 +660,6 @@ function createProjectStructure(
|
||||
);
|
||||
}
|
||||
|
||||
// Add shell aliases if requested
|
||||
if (addAliases) {
|
||||
addShellAliases();
|
||||
}
|
||||
|
||||
// Display next steps in a nice box
|
||||
if (!isSilentMode()) {
|
||||
console.log(
|
||||
@@ -969,7 +748,7 @@ function createProjectStructure(
|
||||
}
|
||||
|
||||
// Function to setup MCP configuration for Cursor integration
|
||||
function setupMCPConfiguration(targetDir, projectName) {
|
||||
function setupMCPConfiguration(targetDir) {
|
||||
const mcpDirPath = path.join(targetDir, '.cursor');
|
||||
const mcpJsonPath = path.join(mcpDirPath, 'mcp.json');
|
||||
|
||||
@@ -988,9 +767,9 @@ function setupMCPConfiguration(targetDir, projectName) {
|
||||
PERPLEXITY_API_KEY: 'YOUR_PERPLEXITY_API_KEY',
|
||||
MODEL: 'claude-3-7-sonnet-20250219',
|
||||
PERPLEXITY_MODEL: 'sonar-pro',
|
||||
MAX_TOKENS: 64000,
|
||||
TEMPERATURE: 0.2,
|
||||
DEFAULT_SUBTASKS: 5,
|
||||
MAX_TOKENS: '64000',
|
||||
TEMPERATURE: '0.2',
|
||||
DEFAULT_SUBTASKS: '5',
|
||||
DEFAULT_PRIORITY: 'medium'
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user