From d48a3d3edc3f9b696192e95ee63d9bf0f789575e Mon Sep 17 00:00:00 2001 From: Joe Danziger Date: Fri, 9 May 2025 12:21:51 -0400 Subject: [PATCH] fix formatting --- docs/command-reference.md | 2 + .../direct-functions/initialize-project.js | 4 +- mcp-server/src/core/direct-functions/rules.js | 10 +- mcp-server/src/tools/initialize-project.js | 4 +- mcp-server/src/tools/rules.js | 64 +++++++---- scripts/init.js | 35 +++--- tests/integration/mcp-server/rules.test.js | 101 +++++++++--------- 7 files changed, 129 insertions(+), 91 deletions(-) diff --git a/docs/command-reference.md b/docs/command-reference.md index 4fe7225b..4da8aa7a 100644 --- a/docs/command-reference.md +++ b/docs/command-reference.md @@ -218,6 +218,7 @@ task-master init --rules cursor,windsurf - You can use multiple comma-separated brands in a single command. **Example:** + ```bash task-master init --rules cursor,roo ``` @@ -238,6 +239,7 @@ task-master rules remove - You can use multiple comma-separated brands in a single command. **Examples:** + ```bash task-master rules add windsurf,roo task-master rules remove windsurf diff --git a/mcp-server/src/core/direct-functions/initialize-project.js b/mcp-server/src/core/direct-functions/initialize-project.js index 25ebe947..43e43da2 100644 --- a/mcp-server/src/core/direct-functions/initialize-project.js +++ b/mcp-server/src/core/direct-functions/initialize-project.js @@ -80,9 +80,9 @@ export async function initializeProjectDirect(args, log, context = {}) { // Handle rules option just like CLI if (Array.isArray(args.rules) && args.rules.length > 0) { options.rules = args.rules; - log.info(`Including brand rules: ${args.rules.join(", ")}`); + log.info(`Including brand rules: ${args.rules.join(', ')}`); } else { - options.rules = ["cursor"]; + options.rules = ['cursor']; log.info(`No rules specified, defaulting to: cursor`); } diff --git a/mcp-server/src/core/direct-functions/rules.js b/mcp-server/src/core/direct-functions/rules.js index f2cbb9de..e570f207 100644 --- a/mcp-server/src/core/direct-functions/rules.js +++ b/mcp-server/src/core/direct-functions/rules.js @@ -24,7 +24,12 @@ export async function rulesDirect(args, log, context = {}) { enableSilentMode(); try { const { action, rules, projectRoot, yes } = args; - if (!action || !Array.isArray(rules) || rules.length === 0 || !projectRoot) { + if ( + !action || + !Array.isArray(rules) || + rules.length === 0 || + !projectRoot + ) { return { success: false, error: { @@ -35,7 +40,8 @@ export async function rulesDirect(args, log, context = {}) { } const rulesList = rules.join(','); const yesFlag = yes !== false ? '--yes' : ''; - const cmd = `npx task-master rules ${action} ${rulesList} ${yesFlag}`.trim(); + const cmd = + `npx task-master rules ${action} ${rulesList} ${yesFlag}`.trim(); log.info(`[rulesDirect] Running: ${cmd} in ${projectRoot}`); const output = execSync(cmd, { cwd: projectRoot, encoding: 'utf8' }); log.info(`[rulesDirect] Output: ${output}`); diff --git a/mcp-server/src/tools/initialize-project.js b/mcp-server/src/tools/initialize-project.js index 2542f0de..f4a286d4 100644 --- a/mcp-server/src/tools/initialize-project.js +++ b/mcp-server/src/tools/initialize-project.js @@ -39,7 +39,9 @@ export function registerInitializeProjectTool(server) { rules: z .array(z.string()) .optional() - .describe('List of brand rules to include at initialization (e.g., ["cursor", "roo"]). If omitted, defaults to ["cursor"].') + .describe( + 'List of brand rules to include at initialization (e.g., ["cursor", "roo"]). If omitted, defaults to ["cursor"].' + ) }), execute: withNormalizedProjectRoot(async (args, context) => { const { log } = context; diff --git a/mcp-server/src/tools/rules.js b/mcp-server/src/tools/rules.js index bccbcf29..9d55fd5f 100644 --- a/mcp-server/src/tools/rules.js +++ b/mcp-server/src/tools/rules.js @@ -5,9 +5,9 @@ import { z } from 'zod'; import { - createErrorResponse, - handleApiResult, - withNormalizedProjectRoot + createErrorResponse, + handleApiResult, + withNormalizedProjectRoot } from './utils.js'; import { rulesDirect } from '../core/direct-functions/rules.js'; @@ -16,24 +16,42 @@ import { rulesDirect } from '../core/direct-functions/rules.js'; * @param {Object} server - FastMCP server instance */ export function registerRulesTool(server) { - server.addTool({ - name: 'rules', - description: 'Add or remove brand rules and MCP config from the project (mirrors CLI rules add/remove).', - parameters: z.object({ - action: z.enum(['add', 'remove']).describe('Whether to add or remove brand rules.'), - rules: z.array(z.string()).min(1).describe('List of brand rules to add or remove (e.g., ["roo", "windsurf"]).'), - projectRoot: z.string().describe('The root directory of the project. Must be an absolute path.'), - yes: z.boolean().optional().default(true).describe('Run non-interactively (default: true).') - }), - execute: withNormalizedProjectRoot(async (args, { log, session }) => { - try { - log.info(`[rules tool] Executing action: ${args.action} for rules: ${args.rules.join(', ')} in ${args.projectRoot}`); - const result = await rulesDirect(args, log, { session }); - return handleApiResult(result, log); - } catch (error) { - log.error(`[rules tool] Error: ${error.message}`); - return createErrorResponse(error.message, { details: error.stack }); - } - }) - }); + server.addTool({ + name: 'rules', + description: + 'Add or remove brand rules and MCP config from the project (mirrors CLI rules add/remove).', + parameters: z.object({ + action: z + .enum(['add', 'remove']) + .describe('Whether to add or remove brand rules.'), + rules: z + .array(z.string()) + .min(1) + .describe( + 'List of brand rules to add or remove (e.g., ["roo", "windsurf"]).' + ), + projectRoot: z + .string() + .describe( + 'The root directory of the project. Must be an absolute path.' + ), + yes: z + .boolean() + .optional() + .default(true) + .describe('Run non-interactively (default: true).') + }), + execute: withNormalizedProjectRoot(async (args, { log, session }) => { + try { + log.info( + `[rules tool] Executing action: ${args.action} for rules: ${args.rules.join(', ')} in ${args.projectRoot}` + ); + const result = await rulesDirect(args, log, { session }); + return handleApiResult(result, log); + } catch (error) { + log.error(`[rules tool] Error: ${error.message}`); + return createErrorResponse(error.message, { details: error.stack }); + } + }) + }); } diff --git a/scripts/init.js b/scripts/init.js index ba7a035a..a8086d42 100755 --- a/scripts/init.js +++ b/scripts/init.js @@ -319,9 +319,10 @@ async function initializeProject(options = {}) { // } const skipPrompts = options.yes || (options.name && options.description); -let selectedBrandRules = options.rules && Array.isArray(options.rules) && options.rules.length > 0 - ? options.rules - : ['cursor']; + let selectedBrandRules = + options.rules && Array.isArray(options.rules) && options.rules.length > 0 + ? options.rules + : ['cursor']; // if (!isSilentMode()) { // console.log('Skip prompts determined:', skipPrompts); @@ -383,8 +384,8 @@ let selectedBrandRules = options.rules && Array.isArray(options.rules) && option console.log('\nTask Master Project settings:'); console.log( chalk.blue( - 'Add shell aliases (so you can use "tm" instead of "task-master"):') - , + 'Add shell aliases (so you can use "tm" instead of "task-master"):' + ), chalk.white(addAliasesPrompted ? 'Yes' : 'No') ); @@ -401,10 +402,11 @@ let selectedBrandRules = options.rules && Array.isArray(options.rules) && option return; } - // === Brand Rules Selection (Inquirer) === console.log( - chalk.cyan('\nRules help enforce best practices and conventions for Task Master.') + chalk.cyan( + '\nRules help enforce best practices and conventions for Task Master.' + ) ); const brandRulesQuestion = { type: 'checkbox', @@ -417,7 +419,6 @@ let selectedBrandRules = options.rules && Array.isArray(options.rules) && option const { brandRules } = await inquirer.prompt([brandRulesQuestion]); selectedBrandRules = brandRules; - const dryRun = options.dryRun || false; if (dryRun) { @@ -435,12 +436,12 @@ let selectedBrandRules = options.rules && Array.isArray(options.rules) && option // Create structure using only necessary values createProjectStructure(addAliasesPrompted, dryRun, selectedBrandRules); - for (const rule of selectedBrandRules) { - const profile = ruleProfiles[rule]; - if (profile) { - convertAllRulesToBrandRules(targetDir, profile); - // Ensure MCP config is set up under the correct brand folder - if (rule === 'windsurf' || rule === 'roo') { + for (const rule of selectedBrandRules) { + const profile = ruleProfiles[rule]; + if (profile) { + convertAllRulesToBrandRules(targetDir, profile); + // Ensure MCP config is set up under the correct brand folder + if (rule === 'windsurf' || rule === 'roo') { } } else { log('warn', `Unknown rules profile: ${rule}`); @@ -468,7 +469,11 @@ function promptQuestion(rl, question) { } // Function to create the project structure -function createProjectStructure(addAliases, dryRun, selectedBrandRules = ['cursor']) { +function createProjectStructure( + addAliases, + dryRun, + selectedBrandRules = ['cursor'] +) { const targetDir = process.cwd(); log('info', `Initializing project in ${targetDir}`); diff --git a/tests/integration/mcp-server/rules.test.js b/tests/integration/mcp-server/rules.test.js index 3b286819..7cb3d462 100644 --- a/tests/integration/mcp-server/rules.test.js +++ b/tests/integration/mcp-server/rules.test.js @@ -8,66 +8,71 @@ import path from 'path'; // Mock logger const mockLogger = { - info: jest.fn(), - error: jest.fn(), - warn: jest.fn(), - debug: jest.fn() + info: jest.fn(), + error: jest.fn(), + warn: jest.fn(), + debug: jest.fn() }; // Use a temp directory for testing (simulate a project root) -const tempProjectRoot = path.join(__dirname, '../../fixtures/temp-rules-project'); +const tempProjectRoot = path.join( + __dirname, + '../../fixtures/temp-rules-project' +); beforeAll(() => { - if (!fs.existsSync(tempProjectRoot)) fs.mkdirSync(tempProjectRoot, { recursive: true }); + if (!fs.existsSync(tempProjectRoot)) + fs.mkdirSync(tempProjectRoot, { recursive: true }); }); afterAll(() => { - if (fs.existsSync(tempProjectRoot)) fs.rmSync(tempProjectRoot, { recursive: true, force: true }); + if (fs.existsSync(tempProjectRoot)) + fs.rmSync(tempProjectRoot, { recursive: true, force: true }); }); describe('rulesDirect (integration)', () => { - it('should add brand rules successfully', async () => { - const args = { - action: 'add', - rules: ['roo'], - projectRoot: tempProjectRoot, - yes: true - }; - const result = await rulesDirect(args, mockLogger, {}); - expect(result.success).toBe(true); - expect(result.data.output).toMatch(/add|roo/i); - }); + it('should add brand rules successfully', async () => { + const args = { + action: 'add', + rules: ['roo'], + projectRoot: tempProjectRoot, + yes: true + }; + const result = await rulesDirect(args, mockLogger, {}); + expect(result.success).toBe(true); + expect(result.data.output).toMatch(/add|roo/i); + }); - it('should remove brand rules successfully', async () => { - const args = { - action: 'remove', - rules: ['roo'], - projectRoot: tempProjectRoot, - yes: true - }; - const result = await rulesDirect(args, mockLogger, {}); - expect(result.success).toBe(true); - expect(result.data.output).toMatch(/remove|roo/i); - }); + it('should remove brand rules successfully', async () => { + const args = { + action: 'remove', + rules: ['roo'], + projectRoot: tempProjectRoot, + yes: true + }; + const result = await rulesDirect(args, mockLogger, {}); + expect(result.success).toBe(true); + expect(result.data.output).toMatch(/remove|roo/i); + }); - it('should fail if missing required arguments', async () => { - const args = { - action: 'add', - rules: [], // missing brands - projectRoot: tempProjectRoot - }; - const result = await rulesDirect(args, mockLogger, {}); - expect(result.success).toBe(false); - expect(result.error.code).toBe('MISSING_ARGUMENT'); - }); + it('should fail if missing required arguments', async () => { + const args = { + action: 'add', + rules: [], // missing brands + projectRoot: tempProjectRoot + }; + const result = await rulesDirect(args, mockLogger, {}); + expect(result.success).toBe(false); + expect(result.error.code).toBe('MISSING_ARGUMENT'); + }); - it('should fail if projectRoot is missing', async () => { - const args = { - action: 'add', - rules: ['roo'] - }; - const result = await rulesDirect(args, mockLogger, {}); - expect(result.success).toBe(false); - expect(result.error.code).toBe('MISSING_ARGUMENT'); - }); + it('should fail if projectRoot is missing', async () => { + const args = { + action: 'add', + rules: ['roo'] + }; + const result = await rulesDirect(args, mockLogger, {}); + expect(result.success).toBe(false); + expect(result.error.code).toBe('MISSING_ARGUMENT'); + }); });