diff --git a/src/core/prompts/instructions/__tests__/generate-rules.test.ts b/src/core/prompts/instructions/__tests__/generate-rules.test.ts new file mode 100644 index 0000000000..a04e91a8c0 --- /dev/null +++ b/src/core/prompts/instructions/__tests__/generate-rules.test.ts @@ -0,0 +1,93 @@ +import { describe, it, expect } from "vitest" +import { + generateRulesInstructions, + ruleTypeDefinitions, + RulesGenerationOptions, + RuleInstruction, +} from "../generate-rules" + +describe("generateRulesInstructions", () => { + it("should generate instructions with all options enabled", () => { + const ruleInstructions: RuleInstruction[] = [ruleTypeDefinitions.general, ruleTypeDefinitions.code] + + const options: RulesGenerationOptions = { + selectedRuleTypes: ["general", "code"], + addToGitignore: true, + alwaysAllowWriteProtected: true, + includeCustomRules: true, + customRulesText: "Always use TypeScript", + } + + const result = generateRulesInstructions(ruleInstructions, options) + + expect(result).toContain("Analyze this codebase and generate comprehensive rules") + expect(result).toContain("coding-standards.md") + expect(result).toContain("implementation-rules.md") + expect(result).toContain("The directory has already been created for you") + expect(result).toContain("Add the generated files to .gitignore") + expect(result).toContain("Always use TypeScript") + }) + + it("should generate instructions with minimal options", () => { + const ruleInstructions: RuleInstruction[] = [ruleTypeDefinitions.general] + + const options: RulesGenerationOptions = { + selectedRuleTypes: ["general"], + addToGitignore: false, + alwaysAllowWriteProtected: false, + includeCustomRules: false, + customRulesText: "", + } + + const result = generateRulesInstructions(ruleInstructions, options) + + expect(result).toContain("Analyze this codebase and generate comprehensive rules") + expect(result).toContain("coding-standards.md") + expect(result).toContain("Create the necessary directories if they don't exist") + expect(result).not.toContain("Add the generated files to .gitignore") + expect(result).not.toContain("Additional rules from User") + }) + + it("should handle all rule types", () => { + const allRuleTypes = Object.keys(ruleTypeDefinitions) + const ruleInstructions: RuleInstruction[] = allRuleTypes.map( + (type) => ruleTypeDefinitions[type as keyof typeof ruleTypeDefinitions], + ) + + const options: RulesGenerationOptions = { + selectedRuleTypes: allRuleTypes, + addToGitignore: false, + alwaysAllowWriteProtected: false, + includeCustomRules: false, + customRulesText: "", + } + + const result = generateRulesInstructions(ruleInstructions, options) + + expect(result).toContain("coding-standards.md") + expect(result).toContain("implementation-rules.md") + expect(result).toContain("architecture-rules.md") + expect(result).toContain("debugging-rules.md") + expect(result).toContain("documentation-rules.md") + }) +}) + +describe("ruleTypeDefinitions", () => { + it("should have all expected rule types", () => { + expect(ruleTypeDefinitions).toHaveProperty("general") + expect(ruleTypeDefinitions).toHaveProperty("code") + expect(ruleTypeDefinitions).toHaveProperty("architect") + expect(ruleTypeDefinitions).toHaveProperty("debug") + expect(ruleTypeDefinitions).toHaveProperty("docs-extractor") + }) + + it("should have proper structure for each rule type", () => { + Object.values(ruleTypeDefinitions).forEach((rule) => { + expect(rule).toHaveProperty("path") + expect(rule).toHaveProperty("focus") + expect(rule).toHaveProperty("analysisSteps") + expect(Array.isArray(rule.analysisSteps)).toBe(true) + expect(rule.analysisSteps.length).toBeGreaterThan(0) + }) + }) +}) diff --git a/src/core/prompts/instructions/generate-rules.ts b/src/core/prompts/instructions/generate-rules.ts new file mode 100644 index 0000000000..5deb504e56 --- /dev/null +++ b/src/core/prompts/instructions/generate-rules.ts @@ -0,0 +1,127 @@ +export interface RuleInstruction { + path: string + focus: string + analysisSteps: string[] +} + +export interface RulesGenerationOptions { + selectedRuleTypes: string[] + addToGitignore: boolean + alwaysAllowWriteProtected: boolean + includeCustomRules: boolean + customRulesText: string +} + +export function generateRulesInstructions( + ruleInstructions: RuleInstruction[], + options: RulesGenerationOptions, +): string { + const { addToGitignore, alwaysAllowWriteProtected, includeCustomRules, customRulesText } = options + + return `Analyze this codebase and generate comprehensive rules for AI agents working in this repository. + +Your task is to: + +1. **Analyze the project structure** by: +${ruleInstructions.map((rule) => ` - For ${rule.path.split("/").pop()}: ${rule.analysisSteps.join("; ")}`).join("\n")} + +2. **Look for existing rule files** that might provide guidance: + - Check for CLAUDE.md, .cursorrules, .cursor/rules, or .github/copilot-instructions.md + - If found, incorporate and improve upon their content + +3. **Generate and save the following rule files**: +${ruleInstructions + .map( + (rule, index) => ` + ${index + 1}. **${rule.path}** + - Focus: ${rule.focus}${alwaysAllowWriteProtected ? "\n - The directory has already been created for you" : "\n - Create the necessary directories if they don't exist"} + - Always overwrite the existing file if it exists + - Use the \`write_to_file\` tool to save the content${alwaysAllowWriteProtected ? "\n - Note: Auto-approval for protected file writes is enabled, so you can write to .roo directories without manual approval" : "\n - Note: You will need to approve the creation of protected directories and files"}`, + ) + .join("\n")} + +4. **Make the rules actionable and specific** by including: + - Build/lint/test commands (especially for running single tests) + - Code style guidelines including imports, formatting, types, naming conventions + - Error handling patterns specific to this project + - Project-specific conventions and best practices + - File organization patterns + +5. **Keep rules concise** - aim for 20 lines per file, focusing on the most important guidelines + +6. **Open the generated files** in the editor for review after creation + +${ + addToGitignore + ? `7. **Add the generated files to .gitignore**: + - After generating all rule files, add entries to .gitignore to prevent them from being committed + - Add each generated file path to .gitignore (e.g., .roo/rules/coding-standards.md) + - If .gitignore doesn't exist, create it + - If the entries already exist in .gitignore, don't duplicate them` + : "" +} + +${ + includeCustomRules && customRulesText + ? `\n**Additional rules from User to add to the rules file:**\n${customRulesText}` + : "" +}` +} + +export const ruleTypeDefinitions = { + general: { + path: ".roo/rules/coding-standards.md", + focus: "General coding standards that apply to all modes, including naming conventions, file organization, and general best practices", + analysisSteps: [ + "Examine the project structure and file organization patterns", + "Identify naming conventions for files, functions, variables, and classes", + "Look for general coding patterns and conventions used throughout the codebase", + "Check for any existing documentation or README files that describe project standards", + ], + }, + code: { + path: ".roo/rules-code/implementation-rules.md", + focus: "Specific rules for code implementation, focusing on syntax patterns, code structure, error handling, testing approaches, and detailed implementation guidelines", + analysisSteps: [ + "Analyze package.json or equivalent files to identify dependencies and build tools", + "Check for linting and formatting tools (ESLint, Prettier, etc.) and their configurations", + "Examine test files to understand testing patterns and frameworks used", + "Look for error handling patterns and logging strategies", + "Identify code style preferences and import/export patterns", + "Check for TypeScript usage and type definition patterns if applicable", + ], + }, + architect: { + path: ".roo/rules-architect/architecture-rules.md", + focus: "High-level system design rules, focusing on file layout, module organization, architectural patterns, and system-wide design principles", + analysisSteps: [ + "Analyze the overall directory structure and module organization", + "Identify architectural patterns (MVC, microservices, monorepo, etc.)", + "Look for separation of concerns and layering patterns", + "Check for API design patterns and service boundaries", + "Examine how different parts of the system communicate", + ], + }, + debug: { + path: ".roo/rules-debug/debugging-rules.md", + focus: "Debugging workflow rules, including error investigation approaches, logging strategies, troubleshooting patterns, and debugging best practices", + analysisSteps: [ + "Identify logging frameworks and patterns used in the codebase", + "Look for error handling and exception patterns", + "Check for debugging tools or scripts in the project", + "Analyze test structure for debugging approaches", + "Look for monitoring or observability patterns", + ], + }, + "docs-extractor": { + path: ".roo/rules-docs-extractor/documentation-rules.md", + focus: "Documentation extraction and formatting rules, including documentation style guides, API documentation patterns, and content organization", + analysisSteps: [ + "Check for existing documentation files and their formats", + "Analyze code comments and documentation patterns", + "Look for API documentation tools or generators", + "Identify documentation structure and organization patterns", + "Check for examples or tutorials in the codebase", + ], + }, +} diff --git a/src/services/rules/rulesGenerator.ts b/src/services/rules/rulesGenerator.ts index bdcc82acbf..599c925f2e 100644 --- a/src/services/rules/rulesGenerator.ts +++ b/src/services/rules/rulesGenerator.ts @@ -1,5 +1,11 @@ import * as fs from "fs/promises" import * as path from "path" +import { + generateRulesInstructions, + ruleTypeDefinitions, + RulesGenerationOptions, + RuleInstruction, +} from "../../core/prompts/instructions/generate-rules" /** * Creates a comprehensive task message for rules generation that can be used with initClineWithTask @@ -32,132 +38,20 @@ export async function createRulesGenerationTaskMessage( } // Create rule-specific instructions based on selected types - interface RuleInstruction { - path: string - focus: string - analysisSteps: string[] - } - const ruleInstructions: RuleInstruction[] = selectedRuleTypes .map((type) => { - switch (type) { - case "general": - return { - path: ".roo/rules/coding-standards.md", - focus: "General coding standards that apply to all modes, including naming conventions, file organization, and general best practices", - analysisSteps: [ - "Examine the project structure and file organization patterns", - "Identify naming conventions for files, functions, variables, and classes", - "Look for general coding patterns and conventions used throughout the codebase", - "Check for any existing documentation or README files that describe project standards", - ], - } - case "code": - return { - path: ".roo/rules-code/implementation-rules.md", - focus: "Specific rules for code implementation, focusing on syntax patterns, code structure, error handling, testing approaches, and detailed implementation guidelines", - analysisSteps: [ - "Analyze package.json or equivalent files to identify dependencies and build tools", - "Check for linting and formatting tools (ESLint, Prettier, etc.) and their configurations", - "Examine test files to understand testing patterns and frameworks used", - "Look for error handling patterns and logging strategies", - "Identify code style preferences and import/export patterns", - "Check for TypeScript usage and type definition patterns if applicable", - ], - } - case "architect": - return { - path: ".roo/rules-architect/architecture-rules.md", - focus: "High-level system design rules, focusing on file layout, module organization, architectural patterns, and system-wide design principles", - analysisSteps: [ - "Analyze the overall directory structure and module organization", - "Identify architectural patterns (MVC, microservices, monorepo, etc.)", - "Look for separation of concerns and layering patterns", - "Check for API design patterns and service boundaries", - "Examine how different parts of the system communicate", - ], - } - case "debug": - return { - path: ".roo/rules-debug/debugging-rules.md", - focus: "Debugging workflow rules, including error investigation approaches, logging strategies, troubleshooting patterns, and debugging best practices", - analysisSteps: [ - "Identify logging frameworks and patterns used in the codebase", - "Look for error handling and exception patterns", - "Check for debugging tools or scripts in the project", - "Analyze test structure for debugging approaches", - "Look for monitoring or observability patterns", - ], - } - case "docs-extractor": - return { - path: ".roo/rules-docs-extractor/documentation-rules.md", - focus: "Documentation extraction and formatting rules, including documentation style guides, API documentation patterns, and content organization", - analysisSteps: [ - "Check for existing documentation files and their formats", - "Analyze code comments and documentation patterns", - "Look for API documentation tools or generators", - "Identify documentation structure and organization patterns", - "Check for examples or tutorials in the codebase", - ], - } - default: - return null - } + const definition = ruleTypeDefinitions[type as keyof typeof ruleTypeDefinitions] + return definition || null }) .filter((rule): rule is RuleInstruction => rule !== null) - // Create a comprehensive message for the rules generation task - const taskMessage = `Analyze this codebase and generate comprehensive rules for AI agents working in this repository. + const options: RulesGenerationOptions = { + selectedRuleTypes, + addToGitignore, + alwaysAllowWriteProtected, + includeCustomRules, + customRulesText, + } -Your task is to: - -1. **Analyze the project structure** by: -${ruleInstructions.map((rule) => ` - For ${rule.path.split("/").pop()}: ${rule.analysisSteps.join("; ")}`).join("\n")} - -2. **Look for existing rule files** that might provide guidance: - - Check for CLAUDE.md, .cursorrules, .cursor/rules, or .github/copilot-instructions.md - - If found, incorporate and improve upon their content - -3. **Generate and save the following rule files**: -${ruleInstructions - .map( - (rule, index) => ` - ${index + 1}. **${rule.path}** - - Focus: ${rule.focus}${alwaysAllowWriteProtected ? "\n - The directory has already been created for you" : "\n - Create the necessary directories if they don't exist"} - - Always overwrite the existing file if it exists - - Use the \`write_to_file\` tool to save the content${alwaysAllowWriteProtected ? "\n - Note: Auto-approval for protected file writes is enabled, so you can write to .roo directories without manual approval" : "\n - Note: You will need to approve the creation of protected directories and files"}`, - ) - .join("\n")} - -4. **Make the rules actionable and specific** by including: - - Build/lint/test commands (especially for running single tests) - - Code style guidelines including imports, formatting, types, naming conventions - - Error handling patterns specific to this project - - Project-specific conventions and best practices - - File organization patterns - -5. **Keep rules concise** - aim for 20 lines per file, focusing on the most important guidelines - -6. **Open the generated files** in the editor for review after creation - -Use the \`safeWriteJson\` utility from \`src/utils/safeWriteJson.ts\` for any JSON file operations to ensure atomic writes. - -${ - addToGitignore - ? `7. **Add the generated files to .gitignore**: - - After generating all rule files, add entries to .gitignore to prevent them from being committed - - Add each generated file path to .gitignore (e.g., .roo/rules/coding-standards.md) - - If .gitignore doesn't exist, create it - - If the entries already exist in .gitignore, don't duplicate them` - : "" -} - -${ - includeCustomRules && customRulesText - ? `\n**Additional rules from User to add to the rules file:**\n${customRulesText}` - : "" -}` - - return taskMessage + return generateRulesInstructions(ruleInstructions, options) } diff --git a/webview-ui/src/components/settings/RulesSettings.tsx b/webview-ui/src/components/settings/RulesSettings.tsx index 2b65b51389..e8308157a1 100644 --- a/webview-ui/src/components/settings/RulesSettings.tsx +++ b/webview-ui/src/components/settings/RulesSettings.tsx @@ -178,7 +178,7 @@ export const RulesSettings = ({ className, hasUnsavedChanges, ...props }: RulesS
{/* Magic Rules Generation subsection */} -
+
@@ -188,9 +188,9 @@ export const RulesSettings = ({ className, hasUnsavedChanges, ...props }: RulesS {t("settings:rules.magicGeneration.description")}
-
+
{/* Recommendation box */} -
+
{t("settings:rules.autoApproveRecommendation")} @@ -233,7 +233,7 @@ export const RulesSettings = ({ className, hasUnsavedChanges, ...props }: RulesS {/* Small repository warning */} {sourceFileCount !== null && sourceFileCount > 0 && sourceFileCount < 20 && ( -
+
{t("settings:rules.smallRepoWarning", { count: sourceFileCount })} @@ -241,7 +241,7 @@ export const RulesSettings = ({ className, hasUnsavedChanges, ...props }: RulesS
)} {hasExistingFiles && ( -
+
{t("settings:rules.overwriteWarning")}
@@ -254,7 +254,7 @@ export const RulesSettings = ({ className, hasUnsavedChanges, ...props }: RulesS
)} -
+
-
-
-
-