From aee88ffda6d3f2bffef0dd9d15219318956f1e77 Mon Sep 17 00:00:00 2001 From: Joe Danziger Date: Mon, 12 May 2025 19:03:17 -0400 Subject: [PATCH] add interactive rules setup --- .changeset/lemon-deer-hide.md | 4 ++- README.md | 25 +++++++++++++++++- docs/command-reference.md | 19 ++++++++++++++ scripts/init.js | 19 +++----------- scripts/modules/commands.js | 38 +++++++++++++++++++++++++++ scripts/modules/rules-setup.js | 47 ++++++++++++++++++++++++++++++++++ 6 files changed, 134 insertions(+), 18 deletions(-) create mode 100644 scripts/modules/rules-setup.js diff --git a/.changeset/lemon-deer-hide.md b/.changeset/lemon-deer-hide.md index 2241d2db..003adf34 100644 --- a/.changeset/lemon-deer-hide.md +++ b/.changeset/lemon-deer-hide.md @@ -6,8 +6,10 @@ Added flexible brand rules management: - New `init` flag: You can now specify which brands to include rules for at project initialization using `--rules ` or `-r ` (e.g., `task-master init -r cursor,roo`). Only the selected brands' rules and configuration are included. - New commands: `task-master rules add ` and `task-master rules remove ` let you add or remove brand-specific rules and MCP config after initialization, supporting multiple brands at once. +- New command: `task-master rules setup` launches an interactive prompt to select which brand rules to apply to your project. This does **not** re-initialize your project or affect shell aliases; it only manages rules. The list of brands is always up-to-date with available profiles, so you never have to update the CLI when adding a new brand. +- The interactive rules setup flow is also used during `init` if you don't specify brands with `--rules`. - Documentation and tests were updated to reflect these changes. -This enables more flexible, brand-specific project setups and makes rules management much easier. +This enables more flexible, brand-specific project setups and makes rules management much easier. You can update or switch brands at any time after initialization using the interactive setup. - Resolves #338 \ No newline at end of file diff --git a/README.md b/README.md index 0af50cf3..c52f16cd 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ task-master init --rules cursor,windsurf # Parse a PRD and generate tasks task-master parse-prd your-prd.txt -# Add or remove brand rules after initialization +# Add, remove, or set up brand rules after initialization You can add or remove brand rules at any time after project initialization: @@ -117,6 +117,16 @@ task-master rules remove windsurf # Removes the specified brand rule sets and their MCP config from your project. +task-master rules setup + +# Launches an interactive prompt to select which brand rules to apply to your project. + +# This does NOT re-initialize the project or ask about shell aliases. + +# Useful for updating/enforcing rules or switching brands at any time. + +# The list of brands is always up-to-date with available profiles. + ```` - Adding rules creates the brand rules directory (e.g., `.roo/rules`) and copies/initializes the brand's rules. @@ -147,6 +157,19 @@ task-master generate ```` +### Interactive Rules Setup + +You can launch the interactive rules setup at any time with: + +```bash +task-master rules setup +``` + +This command opens a prompt where you can select which brand rules (e.g., Cursor, Roo, Windsurf) you want to apply to your project. The list of brands is always current with the available profiles—no manual updates needed. This does **not** re-initialize your project or ask about shell aliases; it only manages rules. + +- Use this command to update, enforce, or switch brand rules interactively after project creation. +- The same interactive prompt is also used during `init` if you don't specify brands with `--rules`. + ## Documentation For more detailed information, check out the documentation in the `docs` directory: diff --git a/docs/command-reference.md b/docs/command-reference.md index 4da8aa7a..d47bfa89 100644 --- a/docs/command-reference.md +++ b/docs/command-reference.md @@ -232,19 +232,38 @@ task-master rules add # Remove brand rule sets from your project task-master rules remove + +# Launch interactive rules setup to select brands +# (does not re-initialize project or ask about shell aliases) +task-master rules setup ``` - Adding rules creates the brand rules directory (e.g., `.roo/rules`) and copies/initializes the brand's rules. - Removing rules deletes the brand rules directory and associated MCP config. - You can use multiple comma-separated brands in a single command. +- The `setup` action launches an interactive prompt to select which brand rules to apply. The list of brands is always current with the available profiles, and no manual updates are needed. This command does **not** re-initialize your project or affect shell aliases; it only manages rules interactively. **Examples:** ```bash task-master rules add windsurf,roo task-master rules remove windsurf +task-master rules setup ``` +### Interactive Rules Setup + +You can launch the interactive rules setup at any time with: + +```bash +task-master rules setup +``` + +This command opens a prompt where you can select which brand rules (e.g., Cursor, Roo, Windsurf) you want to apply to your project. The list of brands is always current with the available profiles—no manual updates needed. This does **not** re-initialize your project or ask about shell aliases; it only manages rules. + +- Use this command to update, enforce, or switch brand rules interactively after project creation. +- The same interactive prompt is also used during `init` if you don't specify brands with `--rules`. + ## Configure AI Models ```bash diff --git a/scripts/init.js b/scripts/init.js index abf8d510..84c7d69e 100755 --- a/scripts/init.js +++ b/scripts/init.js @@ -25,6 +25,7 @@ import boxen from 'boxen'; import gradient from 'gradient-string'; import { isSilentMode } from './modules/utils.js'; import { convertAllRulesToBrandRules } from './modules/rule-transformer.js'; +import { runInteractiveRulesSetup } from './modules/rules-setup.js'; import { execSync } from 'child_process'; const __filename = fileURLToPath(import.meta.url); @@ -402,22 +403,8 @@ async function initializeProject(options = {}) { return; } - // === Brand Rules Selection (Inquirer) === - console.log( - chalk.cyan( - '\nRules help enforce best practices and conventions for Task Master.' - ) - ); - const brandRulesQuestion = { - type: 'checkbox', - name: 'brandRules', - message: 'Which IDEs would you like rules included for?', - choices: availableBrandRules, - default: ['cursor'], - validate: (input) => input.length > 0 || 'You must select at least one.' - }; - const { brandRules } = await inquirer.prompt([brandRulesQuestion]); - selectedBrandRules = brandRules; + // === Brand Rules Selection (via shared module) === + const selectedBrandRules = await runInteractiveRulesSetup(); const dryRun = options.dryRun || false; diff --git a/scripts/modules/commands.js b/scripts/modules/commands.js index bf95effa..dcff3028 100644 --- a/scripts/modules/commands.js +++ b/scripts/modules/commands.js @@ -80,6 +80,7 @@ import { isValidBrand, getBrandProfile } from './rule-transformer.js'; +import { runInteractiveRulesSetup } from './rules-setup.js'; /** * Runs the interactive setup process for model configuration. @@ -511,6 +512,43 @@ function registerCommands(programInstance) { .action(async (action, brands, options) => { const projectDir = process.cwd(); + /** + * 'task-master rules setup' action: + * + * Launches an interactive prompt to select which brand rules to apply to the current project. + * This does NOT perform project initialization or ask about shell aliases—only rules selection. + * + * Example usage: + * $ task-master rules setup + * + * Useful for updating/enforcing rules after project creation, or switching brands. + * + * The list of brands is always up-to-date with the available profiles. + */ + if (action === 'setup') { + // Run interactive rules setup ONLY (no project init) + const selectedBrandRules = await runInteractiveRulesSetup(); + for (const brand of selectedBrandRules) { + if (!isValidBrand(brand)) { + console.warn( + `Rules profile for brand "${brand}" not found. Valid brands: ${BRAND_NAMES.join(', ')}. Skipping.` + ); + continue; + } + const profile = getBrandProfile(brand); + const addResult = convertAllRulesToBrandRules(projectDir, profile); + if (typeof profile.onAddBrandRules === 'function') { + profile.onAddBrandRules(projectDir); + } + console.log( + chalk.green( + `Summary for ${brand}: ${addResult.success} rules added, ${addResult.failed} failed.` + ) + ); + } + return; + } + if (!brands || brands.length === 0) { console.error( 'Please specify at least one brand (e.g., windsurf, roo).' diff --git a/scripts/modules/rules-setup.js b/scripts/modules/rules-setup.js new file mode 100644 index 00000000..f092b760 --- /dev/null +++ b/scripts/modules/rules-setup.js @@ -0,0 +1,47 @@ +import readline from 'readline'; +import inquirer from 'inquirer'; +import chalk from 'chalk'; +import { BRAND_PROFILES, BRAND_NAMES } from './rule-transformer.js'; + +// Dynamically generate availableBrandRules from BRAND_NAMES and brand profiles +const availableBrandRules = BRAND_NAMES.map((name) => { + const displayName = + BRAND_PROFILES[name]?.brandName || + name.charAt(0).toUpperCase() + name.slice(1); + return { + name: name === 'cursor' ? `${displayName} (default)` : displayName, + value: name + }; +}); + +/** + * Runs the interactive rules setup flow (brand rules selection only) + * @returns {Promise} The selected brand rules + */ +/** + * Launches an interactive prompt for selecting which brand rules to include in your project. + * + * This function dynamically lists all available brands (from BRAND_PROFILES) and presents them as checkboxes. + * The user must select at least one brand (default: cursor). The result is an array of selected brand names. + * + * Used by both project initialization (init) and the CLI 'task-master rules setup' command to ensure DRY, consistent UX. + * + * @returns {Promise} Array of selected brand rule names (e.g., ['cursor', 'windsurf']) + */ +export async function runInteractiveRulesSetup() { + console.log( + chalk.cyan( + '\nRules help enforce best practices and conventions for Task Master.' + ) + ); + const brandRulesQuestion = { + type: 'checkbox', + name: 'brandRules', + message: 'Which IDEs would you like rules included for?', + choices: availableBrandRules, + default: ['cursor'], + validate: (input) => input.length > 0 || 'You must select at least one.' + }; + const { brandRules } = await inquirer.prompt([brandRulesQuestion]); + return brandRules; +}