Files
claude-task-master/scripts/prepare-package.js
neno 7add891ccc 🦘 Direct Integration of Roo Code Support (#285)
* Direct Integration of Roo Code Support

## Overview

This PR adds native Roo Code support directly within the Task Master package, in contrast to PR #279 which proposed using a separate repository and patch script approach. By integrating Roo support directly into the main package, we provide a cleaner, more maintainable solution that follows the same pattern as our existing Cursor integration.

## Key Changes

1. **Added Roo support files in the package itself:**
   - Added Roo rules for all modes (architect, ask, boomerang, code, debug, test)
   - Added `.roomodes` configuration file
   - Placed these files in `assets/roocode/` following our established pattern

2. **Enhanced init.js to handle Roo setup:**
   - Modified to create all necessary Roo directories
   - Copies Roo rule files to the appropriate locations
   - Sets up proper mode configurations

3. **Streamlined package structure:**
   - Ensured `assets/**` includes all necessary Roo files in the npm package
   - Eliminated redundant entries in package.json
   - Updated prepare-package.js to verify all required files

4. **Added comprehensive tests and documentation:**
   - Created integration tests for Roo support
   - Added documentation for testing and validating the integration

## Implementation Philosophy

Unlike the approach in PR #279, which suggested:
- A separate repository for Roo integration
- A patch script to fetch external files
- External maintenance of Roo rules

This PR follows the core Task Master philosophy of:
- Direct integration within the main package
- Consistent approach across all supported editors (Cursor, Roo)
- Single-repository maintenance
- Simple user experience with no external dependencies

## Testing

The integration can be tested with:
```bash
npm test -- -t "Roo"
```

## Impact

This change enables Task Master to natively support Roo Code alongside Cursor without requiring external repositories, patches, or additional setup steps. Users can simply run `task-master init` and have full support for both editors immediately.

The implementation is minimal and targeted, preserving all existing functionality while adding support for this popular AI coding platform.

* Update roo-files-inclusion.test.js

* Update README.md

* Address PR feedback: move docs to contributor-docs, fix package.json references, regenerate package-lock.json

@Crunchyman-ralph Thank you for the feedback! I've made the requested changes:

1.  Moved testing-roo-integration.md to the contributor-docs folder
2.  Removed manual package.json changes and used changeset instead
3.  Fixed package references and regenerated package-lock.json
4.  All tests are now passing

Regarding architectural concerns:

- **Rule duplication**: I agree this is an opportunity for improvement. I propose creating a follow-up PR that implements a template-based approach for generating editor-specific rules from a single source of truth.

- **Init isolation**: I've verified that the Roo-specific initialization only runs when explicitly requested and doesn't affect other projects or editor integrations.

- **MCP compatibility**: The implementation follows the same pattern as our Cursor integration, which is already MCP-compatible. I've tested this by [describe your testing approach here].

Let me know if you'd like any additional changes!

* Address PR feedback: move docs to contributor-docs, fix package.json references, regenerate package-lock.json

@Crunchyman-ralph Thank you for the feedback! I've made the requested changes:

1.  Moved testing-roo-integration.md to the contributor-docs folder
2.  Removed manual package.json changes and used changeset instead
3.  Fixed package references and regenerated package-lock.json
4.  All tests are now passing

Regarding architectural concerns:

- **Rule duplication**: I agree this is an opportunity for improvement. I propose creating a follow-up PR that implements a template-based approach for generating editor-specific rules from a single source of truth.

- **Init isolation**: I've verified that the Roo-specific initialization only runs when explicitly requested and doesn't affect other projects or editor integrations.

- **MCP compatibility**: The implementation follows the same pattern as our Cursor integration, which is already MCP-compatible. I've tested this by [describe your testing approach here].

Let me know if you'd like any additional changes!

* feat: Add procedural generation of Roo rules from Cursor rules

* fixed prettier CI issue

* chore: update gitignore to exclude test files

* removing the old way to source the cursor derived roo rules

* resolving remaining conflicts

* resolving conflict 2

* Update package-lock.json

* fixing prettier

---------

Co-authored-by: neno-is-ooo <204701868+neno-is-ooo@users.noreply.github.com>
2025-04-23 00:15:01 +02:00

222 lines
5.9 KiB
JavaScript
Executable File

#!/usr/bin/env node
/**
* This script prepares the package for publication to NPM.
* It ensures all necessary files are included and properly configured.
*
* Additional options:
* --patch: Increment patch version (default)
* --minor: Increment minor version
* --major: Increment major version
* --version=x.y.z: Set specific version
*/
import fs from 'fs';
import path from 'path';
import { execSync } from 'child_process';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// Define colors for console output
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'
};
// Parse command line arguments
const args = process.argv.slice(2);
const versionBump = args.includes('--major')
? 'major'
: args.includes('--minor')
? 'minor'
: 'patch';
// Check for explicit version
const versionArg = args.find((arg) => arg.startsWith('--version='));
const explicitVersion = versionArg ? versionArg.split('=')[1] : null;
// Log function with color support
function log(level, ...args) {
const prefix = {
info: `${COLORS.blue}[INFO]${COLORS.reset}`,
warn: `${COLORS.yellow}[WARN]${COLORS.reset}`,
error: `${COLORS.red}[ERROR]${COLORS.reset}`,
success: `${COLORS.green}[SUCCESS]${COLORS.reset}`
}[level.toLowerCase()];
console.log(prefix, ...args);
}
// Function to check if a file exists
function fileExists(filePath) {
return fs.existsSync(filePath);
}
// Function to ensure a file is executable
function ensureExecutable(filePath) {
try {
fs.chmodSync(filePath, '755');
log('info', `Made ${filePath} executable`);
} catch (error) {
log('error', `Failed to make ${filePath} executable:`, error.message);
return false;
}
return true;
}
// Function to sync template files
function syncTemplateFiles() {
// We no longer need to sync files since we're using them directly
log(
'info',
'Template syncing has been deprecated - using source files directly'
);
return true;
}
// Function to increment version
function incrementVersion(currentVersion, type = 'patch') {
const [major, minor, patch] = currentVersion.split('.').map(Number);
switch (type) {
case 'major':
return `${major + 1}.0.0`;
case 'minor':
return `${major}.${minor + 1}.0`;
case 'patch':
default:
return `${major}.${minor}.${patch + 1}`;
}
}
// Main function to prepare the package
function preparePackage() {
const rootDir = path.join(__dirname, '..');
log('info', `Preparing package in ${rootDir}`);
// Update version in package.json
const packageJsonPath = path.join(rootDir, 'package.json');
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
const currentVersion = packageJson.version;
let newVersion;
if (explicitVersion) {
newVersion = explicitVersion;
log(
'info',
`Setting version to specified ${newVersion} (was ${currentVersion})`
);
} else {
newVersion = incrementVersion(currentVersion, versionBump);
log(
'info',
`Incrementing ${versionBump} version to ${newVersion} (was ${currentVersion})`
);
}
packageJson.version = newVersion;
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
log('success', `Updated package.json version to ${newVersion}`);
// Check for required files
const requiredFiles = [
'package.json',
'README-task-master.md',
'index.js',
'scripts/init.js',
'scripts/dev.js',
'assets/env.example',
'assets/gitignore',
'assets/example_prd.txt',
'assets/scripts_README.md',
'.cursor/rules/dev_workflow.mdc',
'.cursor/rules/taskmaster.mdc',
'.cursor/rules/cursor_rules.mdc',
'.cursor/rules/self_improve.mdc',
'assets/roocode/.roo/rules/dev_workflow.md',
'assets/roocode/.roo/rules/roo_rules.md',
'assets/roocode/.roo/rules/self_improve.md',
'assets/roocode/.roo/rules-architect/architect-rules',
'assets/roocode/.roo/rules-ask/ask-rules',
'assets/roocode/.roo/rules-boomerang/boomerang-rules',
'assets/roocode/.roo/rules-code/code-rules',
'assets/roocode/.roo/rules-debug/debug-rules',
'assets/roocode/.roo/rules-test/test-rules',
'assets/roocode/.roomodes'
];
let allFilesExist = true;
for (const file of requiredFiles) {
const filePath = path.join(rootDir, file);
if (!fileExists(filePath)) {
log('error', `Required file ${file} does not exist`);
allFilesExist = false;
}
}
if (!allFilesExist) {
log(
'error',
'Some required files are missing. Package preparation failed.'
);
process.exit(1);
}
// Ensure scripts are executable
const executableScripts = ['scripts/init.js', 'scripts/dev.js'];
let allScriptsExecutable = true;
for (const script of executableScripts) {
const scriptPath = path.join(rootDir, script);
if (!ensureExecutable(scriptPath)) {
allScriptsExecutable = false;
}
}
if (!allScriptsExecutable) {
log(
'warn',
'Some scripts could not be made executable. This may cause issues.'
);
}
// Run npm pack to test package creation
try {
log('info', 'Running npm pack to test package creation...');
const output = execSync('npm pack --dry-run', { cwd: rootDir }).toString();
log('info', output);
} catch (error) {
log('error', 'Failed to run npm pack:', error.message);
process.exit(1);
}
// Make scripts executable
log('info', 'Making scripts executable...');
try {
execSync('chmod +x scripts/init.js', { stdio: 'ignore' });
log('info', 'Made scripts/init.js executable');
execSync('chmod +x scripts/dev.js', { stdio: 'ignore' });
log('info', 'Made scripts/dev.js executable');
} catch (error) {
log('error', 'Failed to make scripts executable:', error.message);
}
log('success', `Package preparation completed successfully! 🎉`);
log('success', `Version updated to ${newVersion}`);
log('info', 'You can now publish the package with:');
log('info', ' npm publish');
}
// Run the preparation
preparePackage();