From 24e9206da0d5d3f2f7819ed94fa0c9b459fc9f9b Mon Sep 17 00:00:00 2001 From: Joe Danziger Date: Wed, 2 Jul 2025 01:05:30 -0400 Subject: [PATCH] Fix `rules` command to use reliable project root detection like other commands (#908) * update/fix projectRoot call for consistency * internal naming consistency * add changeset --- .changeset/wicked-rats-hide.md | 5 +++++ scripts/modules/commands.js | 20 ++++++++++++-------- src/utils/create-mcp-config.js | 10 +++++----- src/utils/rule-transformer.js | 28 ++++++++++++++-------------- 4 files changed, 36 insertions(+), 27 deletions(-) create mode 100644 .changeset/wicked-rats-hide.md diff --git a/.changeset/wicked-rats-hide.md b/.changeset/wicked-rats-hide.md new file mode 100644 index 00000000..bc95d520 --- /dev/null +++ b/.changeset/wicked-rats-hide.md @@ -0,0 +1,5 @@ +--- +"task-master-ai": patch +--- + +Fix rules command to use reliable project root detection like other commands diff --git a/scripts/modules/commands.js b/scripts/modules/commands.js index 23e6d1bb..24b65795 100644 --- a/scripts/modules/commands.js +++ b/scripts/modules/commands.js @@ -3810,7 +3810,11 @@ Examples: $ task-master rules --${RULES_SETUP_ACTION} # Interactive setup to select rule profiles` ) .action(async (action, profiles, options) => { - const projectDir = process.cwd(); + const projectRoot = findProjectRoot(); + if (!projectRoot) { + console.error(chalk.red('Error: Could not find project root.')); + process.exit(1); + } /** * 'task-master rules --setup' action: @@ -3857,7 +3861,7 @@ Examples: const profileConfig = getRulesProfile(profile); const addResult = convertAllRulesToProfileRules( - projectDir, + projectRoot, profileConfig ); @@ -3903,8 +3907,8 @@ Examples: let confirmed = true; if (!options.force) { // Check if this removal would leave no profiles remaining - if (wouldRemovalLeaveNoProfiles(projectDir, expandedProfiles)) { - const installedProfiles = getInstalledProfiles(projectDir); + if (wouldRemovalLeaveNoProfiles(projectRoot, expandedProfiles)) { + const installedProfiles = getInstalledProfiles(projectRoot); confirmed = await confirmRemoveAllRemainingProfiles( expandedProfiles, installedProfiles @@ -3934,12 +3938,12 @@ Examples: if (action === RULES_ACTIONS.ADD) { console.log(chalk.blue(`Adding rules for profile: ${profile}...`)); const addResult = convertAllRulesToProfileRules( - projectDir, + projectRoot, profileConfig ); if (typeof profileConfig.onAddRulesProfile === 'function') { - const assetsDir = path.join(process.cwd(), 'assets'); - profileConfig.onAddRulesProfile(projectDir, assetsDir); + const assetsDir = path.join(projectRoot, 'assets'); + profileConfig.onAddRulesProfile(projectRoot, assetsDir); } console.log( chalk.blue(`Completed adding rules for profile: ${profile}`) @@ -3955,7 +3959,7 @@ Examples: console.log(chalk.green(generateProfileSummary(profile, addResult))); } else if (action === RULES_ACTIONS.REMOVE) { console.log(chalk.blue(`Removing rules for profile: ${profile}...`)); - const result = removeProfileRules(projectDir, profileConfig); + const result = removeProfileRules(projectRoot, profileConfig); removalResults.push(result); console.log( chalk.green(generateProfileRemovalSummary(profile, result)) diff --git a/src/utils/create-mcp-config.js b/src/utils/create-mcp-config.js index c630067f..d9db18e3 100644 --- a/src/utils/create-mcp-config.js +++ b/src/utils/create-mcp-config.js @@ -25,7 +25,7 @@ function formatJSONWithTabs(obj) { } // Structure matches project conventions (see scripts/init.js) -export function setupMCPConfiguration(projectDir, mcpConfigPath) { +export function setupMCPConfiguration(projectRoot, mcpConfigPath) { // Handle null mcpConfigPath (e.g., for Claude/Codex profiles) if (!mcpConfigPath) { log( @@ -36,7 +36,7 @@ export function setupMCPConfiguration(projectDir, mcpConfigPath) { } // Build the full path to the MCP config file - const mcpPath = path.join(projectDir, mcpConfigPath); + const mcpPath = path.join(projectRoot, mcpConfigPath); const configDir = path.dirname(mcpPath); log('info', `Setting up MCP configuration at ${mcpPath}...`); @@ -140,11 +140,11 @@ export function setupMCPConfiguration(projectDir, mcpConfigPath) { /** * Remove Task Master MCP server configuration from an existing mcp.json file * Only removes Task Master entries, preserving other MCP servers - * @param {string} projectDir - Target project directory + * @param {string} projectRoot - Target project directory * @param {string} mcpConfigPath - Relative path to MCP config file (e.g., '.cursor/mcp.json') * @returns {Object} Result object with success status and details */ -export function removeTaskMasterMCPConfiguration(projectDir, mcpConfigPath) { +export function removeTaskMasterMCPConfiguration(projectRoot, mcpConfigPath) { // Handle null mcpConfigPath (e.g., for Claude/Codex profiles) if (!mcpConfigPath) { return { @@ -156,7 +156,7 @@ export function removeTaskMasterMCPConfiguration(projectDir, mcpConfigPath) { }; } - const mcpPath = path.join(projectDir, mcpConfigPath); + const mcpPath = path.join(projectRoot, mcpConfigPath); let result = { success: false, diff --git a/src/utils/rule-transformer.js b/src/utils/rule-transformer.js index d41a5505..2922fa2a 100644 --- a/src/utils/rule-transformer.js +++ b/src/utils/rule-transformer.js @@ -198,7 +198,7 @@ export function convertRuleToProfileRule(sourcePath, targetPath, profile) { /** * Convert all Cursor rules to profile rules for a specific profile */ -export function convertAllRulesToProfileRules(projectDir, profile) { +export function convertAllRulesToProfileRules(projectRoot, profile) { // Handle simple profiles (Claude, Codex) that just copy files to root const isSimpleProfile = Object.keys(profile.fileMap).length === 0; if (isSimpleProfile) { @@ -208,7 +208,7 @@ export function convertAllRulesToProfileRules(projectDir, profile) { const assetsDir = path.join(__dirname, '..', '..', 'assets'); if (typeof profile.onPostConvertRulesProfile === 'function') { - profile.onPostConvertRulesProfile(projectDir, assetsDir); + profile.onPostConvertRulesProfile(projectRoot, assetsDir); } return { success: 1, failed: 0 }; } @@ -216,7 +216,7 @@ export function convertAllRulesToProfileRules(projectDir, profile) { const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const sourceDir = path.join(__dirname, '..', '..', 'assets', 'rules'); - const targetDir = path.join(projectDir, profile.rulesDir); + const targetDir = path.join(projectRoot, profile.rulesDir); // Ensure target directory exists if (!fs.existsSync(targetDir)) { @@ -225,7 +225,7 @@ export function convertAllRulesToProfileRules(projectDir, profile) { // Setup MCP configuration if enabled if (profile.mcpConfig !== false) { - setupMCPConfiguration(projectDir, profile.mcpConfigPath); + setupMCPConfiguration(projectRoot, profile.mcpConfigPath); } let success = 0; @@ -286,7 +286,7 @@ export function convertAllRulesToProfileRules(projectDir, profile) { // Call post-processing hook if defined (e.g., for Roo's rules-*mode* folders) if (typeof profile.onPostConvertRulesProfile === 'function') { const assetsDir = path.join(__dirname, '..', '..', 'assets'); - profile.onPostConvertRulesProfile(projectDir, assetsDir); + profile.onPostConvertRulesProfile(projectRoot, assetsDir); } return { success, failed }; @@ -294,13 +294,13 @@ export function convertAllRulesToProfileRules(projectDir, profile) { /** * Remove only Task Master specific files from a profile, leaving other existing rules intact - * @param {string} projectDir - Target project directory + * @param {string} projectRoot - Target project directory * @param {Object} profile - Profile configuration * @returns {Object} Result object */ -export function removeProfileRules(projectDir, profile) { - const targetDir = path.join(projectDir, profile.rulesDir); - const profileDir = path.join(projectDir, profile.profileDir); +export function removeProfileRules(projectRoot, profile) { + const targetDir = path.join(projectRoot, profile.rulesDir); + const profileDir = path.join(projectRoot, profile.profileDir); const result = { profileName: profile.profileName, @@ -320,12 +320,12 @@ export function removeProfileRules(projectDir, profile) { if (isSimpleProfile) { // For simple profiles, just call their removal hook and return if (typeof profile.onRemoveRulesProfile === 'function') { - profile.onRemoveRulesProfile(projectDir); + profile.onRemoveRulesProfile(projectRoot); } result.success = true; log( 'debug', - `[Rule Transformer] Successfully removed ${profile.profileName} files from ${projectDir}` + `[Rule Transformer] Successfully removed ${profile.profileName} files from ${projectRoot}` ); return result; } @@ -418,7 +418,7 @@ export function removeProfileRules(projectDir, profile) { // 2. Handle MCP configuration - only remove Task Master, preserve other servers if (profile.mcpConfig !== false) { result.mcpResult = removeTaskMasterMCPConfiguration( - projectDir, + projectRoot, profile.mcpConfigPath ); if (result.mcpResult.hasOtherServers) { @@ -432,7 +432,7 @@ export function removeProfileRules(projectDir, profile) { // 3. Call removal hook if defined (e.g., Roo's custom cleanup) if (typeof profile.onRemoveRulesProfile === 'function') { - profile.onRemoveRulesProfile(projectDir); + profile.onRemoveRulesProfile(projectRoot); } // 4. Only remove profile directory if: @@ -490,7 +490,7 @@ export function removeProfileRules(projectDir, profile) { result.success = true; log( 'debug', - `[Rule Transformer] Successfully removed ${profile.profileName} Task Master files from ${projectDir}` + `[Rule Transformer] Successfully removed ${profile.profileName} Task Master files from ${projectRoot}` ); } catch (error) { result.error = error.message;