mirror of
https://github.com/eyaltoledano/claude-task-master.git
synced 2025-06-27 00:29:58 +00:00

- Update to ES modules syntax throughout the codebase - Add fileURLToPath and dirname imports to handle __dirname in ES modules - Upgrade Anthropic SDK from 0.10.0 to 0.39.0 - Fix JSON parsing to handle responses wrapped in Markdown code blocks - Improve documentation with clearer installation instructions - Add important notes about ES modules and SDK requirements - Update environment examples with API key format and model recommendations - Include index.js in package files list - Bump version to 1.0.2
255 lines
7.6 KiB
JavaScript
Executable File
255 lines
7.6 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
|
|
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';
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = dirname(__filename);
|
|
|
|
// Create readline interface for user input
|
|
const rl = readline.createInterface({
|
|
input: process.stdin,
|
|
output: process.stdout
|
|
});
|
|
|
|
// Define log levels and colors
|
|
const LOG_LEVELS = {
|
|
debug: 0,
|
|
info: 1,
|
|
warn: 2,
|
|
error: 3
|
|
};
|
|
|
|
const COLORS = {
|
|
reset: '\x1b[0m',
|
|
bright: '\x1b[1m',
|
|
dim: '\x1b[2m',
|
|
red: '\x1b[31m',
|
|
green: '\x1b[32m',
|
|
yellow: '\x1b[33m',
|
|
blue: '\x1b[34m',
|
|
magenta: '\x1b[35m',
|
|
cyan: '\x1b[36m'
|
|
};
|
|
|
|
// Get log level from environment or default to info
|
|
const LOG_LEVEL = process.env.LOG_LEVEL ? LOG_LEVELS[process.env.LOG_LEVEL.toLowerCase()] : LOG_LEVELS.info;
|
|
|
|
// Logging function
|
|
function log(level, ...args) {
|
|
const levelValue = LOG_LEVELS[level.toLowerCase()];
|
|
|
|
if (levelValue >= LOG_LEVEL) {
|
|
const prefix = {
|
|
debug: `${COLORS.dim}[DEBUG]${COLORS.reset}`,
|
|
info: `${COLORS.blue}[INFO]${COLORS.reset}`,
|
|
warn: `${COLORS.yellow}[WARN]${COLORS.reset}`,
|
|
error: `${COLORS.red}[ERROR]${COLORS.reset}`
|
|
}[level.toLowerCase()];
|
|
|
|
console.log(prefix, ...args);
|
|
}
|
|
|
|
// Write to debug log if DEBUG=true
|
|
if (process.env.DEBUG === 'true') {
|
|
const logMessage = `[${level.toUpperCase()}] ${args.join(' ')}\n`;
|
|
fs.appendFileSync('init-debug.log', logMessage);
|
|
}
|
|
}
|
|
|
|
// Function to create directory if it doesn't exist
|
|
function ensureDirectoryExists(dirPath) {
|
|
if (!fs.existsSync(dirPath)) {
|
|
fs.mkdirSync(dirPath, { recursive: true });
|
|
log('info', `Created directory: ${dirPath}`);
|
|
}
|
|
}
|
|
|
|
// Function to copy a file from the package to the target directory
|
|
function copyTemplateFile(templateName, targetPath, replacements = {}) {
|
|
// Get the template content from the templates directory
|
|
const templatePath = path.join(__dirname, '..', 'templates', templateName);
|
|
let content = fs.readFileSync(templatePath, 'utf8');
|
|
|
|
// Replace placeholders with actual values
|
|
Object.entries(replacements).forEach(([key, value]) => {
|
|
const regex = new RegExp(`\\{\\{${key}\\}\\}`, 'g');
|
|
content = content.replace(regex, value);
|
|
});
|
|
|
|
// Write the content to the target path
|
|
fs.writeFileSync(targetPath, content);
|
|
log('info', `Created file: ${targetPath}`);
|
|
}
|
|
|
|
// Main function to initialize a new project
|
|
async function initializeProject(options = {}) {
|
|
return new Promise((resolve) => {
|
|
// If options are provided, use them directly
|
|
if (options.projectName && options.projectDescription) {
|
|
const projectName = options.projectName;
|
|
const projectDescription = options.projectDescription;
|
|
const projectVersion = options.projectVersion || '1.0.0';
|
|
const authorName = options.authorName || '';
|
|
|
|
createProjectStructure(projectName, projectDescription, projectVersion, authorName);
|
|
resolve({
|
|
projectName,
|
|
projectDescription,
|
|
projectVersion,
|
|
authorName
|
|
});
|
|
} else {
|
|
// Otherwise, prompt the user for input
|
|
rl.question('Enter project name: ', (projectName) => {
|
|
rl.question('Enter project description: ', (projectDescription) => {
|
|
rl.question('Enter project version (default: 1.0.0): ', (projectVersion) => {
|
|
rl.question('Enter your name: ', (authorName) => {
|
|
// Set default version if not provided
|
|
if (!projectVersion.trim()) {
|
|
projectVersion = '1.0.0';
|
|
}
|
|
|
|
// Create the project structure
|
|
createProjectStructure(projectName, projectDescription, projectVersion, authorName);
|
|
|
|
rl.close();
|
|
resolve({
|
|
projectName,
|
|
projectDescription,
|
|
projectVersion,
|
|
authorName
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
// Function to create the project structure
|
|
function createProjectStructure(projectName, projectDescription, projectVersion, authorName) {
|
|
const targetDir = process.cwd();
|
|
log('info', `Initializing project in ${targetDir}`);
|
|
|
|
// Create directories
|
|
ensureDirectoryExists(path.join(targetDir, '.cursor', 'rules'));
|
|
ensureDirectoryExists(path.join(targetDir, 'scripts'));
|
|
ensureDirectoryExists(path.join(targetDir, 'tasks'));
|
|
|
|
// Create package.json
|
|
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",
|
|
"chalk": "^4.1.2",
|
|
"commander": "^11.1.0",
|
|
"dotenv": "^16.3.1"
|
|
}
|
|
};
|
|
|
|
fs.writeFileSync(
|
|
path.join(targetDir, 'package.json'),
|
|
JSON.stringify(packageJson, null, 2)
|
|
);
|
|
log('info', 'Created package.json');
|
|
|
|
// Copy template files with replacements
|
|
const replacements = {
|
|
projectName,
|
|
projectDescription,
|
|
projectVersion,
|
|
authorName,
|
|
year: new Date().getFullYear()
|
|
};
|
|
|
|
// Copy .env.example
|
|
copyTemplateFile('env.example', path.join(targetDir, '.env.example'), replacements);
|
|
|
|
// Copy .gitignore
|
|
copyTemplateFile('gitignore', path.join(targetDir, '.gitignore'));
|
|
|
|
// Copy dev_workflow.mdc
|
|
copyTemplateFile('dev_workflow.mdc', path.join(targetDir, '.cursor', 'rules', 'dev_workflow.mdc'));
|
|
|
|
// 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', path.join(targetDir, 'scripts', 'example_prd.txt'));
|
|
|
|
// Create main README.md
|
|
copyTemplateFile('README.md', path.join(targetDir, 'README.md'), replacements);
|
|
|
|
// Create empty tasks.json
|
|
const tasksJson = {
|
|
meta: {
|
|
name: projectName,
|
|
version: projectVersion,
|
|
description: projectDescription
|
|
},
|
|
tasks: []
|
|
};
|
|
|
|
fs.writeFileSync(
|
|
path.join(targetDir, 'tasks.json'),
|
|
JSON.stringify(tasksJson, null, 2)
|
|
);
|
|
log('info', 'Created tasks.json');
|
|
|
|
// Initialize git repository if git is available
|
|
try {
|
|
if (!fs.existsSync(path.join(targetDir, '.git'))) {
|
|
execSync('git init', { stdio: 'ignore' });
|
|
log('info', 'Initialized git repository');
|
|
}
|
|
} catch (error) {
|
|
log('warn', 'Git not available, skipping repository initialization');
|
|
}
|
|
|
|
log('info', `${COLORS.green}${COLORS.bright}Project initialized successfully!${COLORS.reset}`);
|
|
log('info', '');
|
|
log('info', 'Next steps:');
|
|
log('info', '1. Run `npm install` to install dependencies');
|
|
log('info', '2. Create a .env file with your ANTHROPIC_API_KEY (see .env.example)');
|
|
log('info', '3. Add your PRD to the project');
|
|
log('info', '4. Run `npm run parse-prd -- --input=<your-prd-file.txt>` to generate tasks');
|
|
log('info', '');
|
|
}
|
|
|
|
// Run the initialization if this script is executed directly
|
|
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
(async function main() {
|
|
try {
|
|
await initializeProject();
|
|
} catch (error) {
|
|
log('error', 'Failed to initialize project:', error);
|
|
process.exit(1);
|
|
}
|
|
})();
|
|
}
|
|
|
|
// Export functions for programmatic use
|
|
export {
|
|
initializeProject,
|
|
createProjectStructure,
|
|
log
|
|
};
|