-
Notifications
You must be signed in to change notification settings - Fork 4
feat: upgrade converter with validation #30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,7 @@ import ( | |
| "context" | ||
| "encoding/json" | ||
| "fmt" | ||
| "sort" | ||
| "strings" | ||
| "sync" | ||
|
|
||
|
|
@@ -126,7 +127,15 @@ func (c *ESLintLinterConverter) ConvertRules(ctx context.Context, rules []schema | |
|
|
||
| // convertSingleRule converts a single user rule to ESLint rule using LLM | ||
| func (c *ESLintLinterConverter) convertSingleRule(ctx context.Context, rule schema.UserRule, llmClient *llm.Client) (string, interface{}, error) { | ||
| systemPrompt := `You are an ESLint configuration expert. Convert natural language coding rules to ESLint rule configurations. | ||
| // Build list of valid ESLint rules for the prompt | ||
| validRules := GetESLintRuleNames() | ||
| sort.Strings(validRules) | ||
| validRulesStr := strings.Join(validRules, ", ") | ||
|
|
||
| systemPrompt := fmt.Sprintf(`You are an ESLint configuration expert. Convert natural language coding rules to ESLint rule configurations. | ||
|
|
||
| IMPORTANT: You MUST ONLY use rules from this exact list of valid ESLint rules: | ||
| %s | ||
|
|
||
| Return ONLY a JSON object (no markdown fences) with this structure: | ||
| { | ||
|
|
@@ -135,22 +144,11 @@ Return ONLY a JSON object (no markdown fences) with this structure: | |
| "options": {...} | ||
| } | ||
|
|
||
| Common ESLint rules: | ||
| - Naming: camelcase, id-match, id-length, new-cap | ||
| - Console: no-console, no-debugger, no-alert | ||
| - Code Quality: no-unused-vars, no-undef, eqeqeq, prefer-const, no-var | ||
| - Complexity: complexity, max-depth, max-nested-callbacks, max-lines-per-function | ||
| - Length: max-len, max-lines, max-params, max-statements | ||
| - Style: indent, quotes, semi, comma-dangle, brace-style | ||
| - Imports: no-restricted-imports | ||
| - Security: no-eval, no-implied-eval, no-new-func | ||
|
|
||
| If the rule cannot be expressed in ESLint, return: | ||
| { | ||
| "rule_name": "", | ||
| "severity": "off", | ||
| "options": null | ||
| } | ||
| CRITICAL RULES: | ||
| 1. ONLY use rule names from the list above - do NOT invent or guess rule names | ||
| 2. If no rule from the list can enforce this requirement, return rule_name as empty string "" | ||
| 3. Do NOT suggest plugin rules (e.g., @typescript-eslint/*, eslint-plugin-*) | ||
| 4. When in doubt, return empty rule_name - it's better to skip than use wrong rule | ||
|
|
||
| Examples: | ||
|
|
||
|
|
@@ -176,7 +174,25 @@ Output: | |
| "rule_name": "camelcase", | ||
| "severity": "error", | ||
| "options": {"properties": "always"} | ||
| }` | ||
| } | ||
|
|
||
| Input: "File names must be kebab-case" | ||
| Output: | ||
| { | ||
| "rule_name": "", | ||
| "severity": "off", | ||
| "options": null | ||
| } | ||
| (Reason: No native ESLint rule for file naming) | ||
|
|
||
| Input: "No hardcoded API keys" | ||
| Output: | ||
| { | ||
| "rule_name": "", | ||
| "severity": "off", | ||
| "options": null | ||
| } | ||
| (Reason: Requires plugin or semantic analysis)`, validRulesStr) | ||
|
|
||
| userPrompt := fmt.Sprintf("Convert this rule to ESLint configuration:\n\n%s", rule.Say) | ||
| if rule.Severity != "" { | ||
|
|
@@ -211,6 +227,14 @@ Output: | |
| return "", nil, nil | ||
| } | ||
|
|
||
| // VALIDATION: Check if the rule actually exists in our registry | ||
| validation := ValidateESLintRule(result.RuleName, result.Options) | ||
| if !validation.Valid { | ||
| // Rule doesn't exist - skip it (will be handled by llm-validator) | ||
| fmt.Printf("⚠️ Invalid ESLint rule '%s': %s\n", result.RuleName, validation.Message) | ||
| return "", nil, nil | ||
|
Comment on lines
+234
to
+235
|
||
| } | ||
|
Comment on lines
+230
to
+236
|
||
|
|
||
| // Map user severity to ESLint severity if needed | ||
| severity := mapSeverity(rule.Severity) | ||
| if severity == "" { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -72,22 +72,22 @@ func (c *PrettierLinterConverter) ConvertRules(ctx context.Context, rules []sche | |
|
|
||
| // convertSingleRule converts a single user rule to Prettier config using LLM | ||
| func (c *PrettierLinterConverter) convertSingleRule(ctx context.Context, rule schema.UserRule, llmClient *llm.Client) (map[string]interface{}, error) { | ||
| systemPrompt := `You are a Prettier configuration expert. Convert natural language formatting rules to Prettier configuration options. | ||
| // Build list of valid Prettier options for the prompt | ||
| validOptions := GetPrettierOptionNames() | ||
| validOptionsStr := strings.Join(validOptions, ", ") | ||
|
|
||
| Return ONLY a JSON object (no markdown fences) with Prettier options. | ||
| systemPrompt := fmt.Sprintf(`You are a Prettier configuration expert. Convert natural language formatting rules to Prettier configuration options. | ||
|
|
||
| IMPORTANT: You MUST ONLY use options from this exact list of valid Prettier options: | ||
| %s | ||
|
|
||
| Available Prettier options: | ||
| - semi: true/false (use semicolons) | ||
| - singleQuote: true/false (use single quotes) | ||
| - tabWidth: number (spaces per indentation level) | ||
| - useTabs: true/false (use tabs instead of spaces) | ||
| - trailingComma: "none"/"es5"/"all" (trailing commas) | ||
| - printWidth: number (line length) | ||
| - arrowParens: "always"/"avoid" (arrow function parentheses) | ||
| - bracketSpacing: true/false (spaces in object literals) | ||
| - endOfLine: "lf"/"crlf"/"auto" | ||
| Return ONLY a JSON object (no markdown fences) with Prettier options. | ||
| If the rule cannot be expressed with Prettier options, return empty object: {} | ||
|
|
||
| If the rule is not about formatting, return empty object: {} | ||
| CRITICAL RULES: | ||
| 1. ONLY use option names from the list above | ||
| 2. Do NOT invent new options | ||
| 3. If no option can enforce this rule, return {} | ||
|
|
||
| Examples: | ||
|
|
||
|
|
@@ -114,7 +114,12 @@ Input: "Maximum line length is 120 characters" | |
| Output: | ||
| { | ||
| "printWidth": 120 | ||
| }` | ||
| } | ||
|
|
||
| Input: "Sort imports alphabetically" | ||
| Output: | ||
| {} | ||
| (Reason: No native Prettier option for this)`, validOptionsStr) | ||
|
|
||
| userPrompt := fmt.Sprintf("Convert this rule to Prettier configuration:\n\n%s", rule.Say) | ||
|
|
||
|
|
@@ -136,7 +141,18 @@ Output: | |
| return nil, fmt.Errorf("failed to parse LLM response: %w", err) | ||
| } | ||
|
|
||
| return config, nil | ||
| // VALIDATION: Filter out invalid options | ||
| validConfig := make(map[string]interface{}) | ||
| for key, value := range config { | ||
| validation := ValidatePrettierOption(key, value) | ||
| if validation.Valid { | ||
| validConfig[key] = value | ||
| } else { | ||
| fmt.Printf("⚠️ Invalid Prettier option '%s': %s\n", key, validation.Message) | ||
| } | ||
|
Comment on lines
+151
to
+152
|
||
| } | ||
|
|
||
| return validConfig, nil | ||
|
Comment on lines
+144
to
+155
|
||
| } | ||
|
|
||
| // TSCLinterConverter converts rules to TypeScript compiler configuration | ||
|
|
@@ -212,25 +228,22 @@ func (c *TSCLinterConverter) ConvertRules(ctx context.Context, rules []schema.Us | |
|
|
||
| // convertSingleRule converts a single user rule to TypeScript compiler option using LLM | ||
| func (c *TSCLinterConverter) convertSingleRule(ctx context.Context, rule schema.UserRule, llmClient *llm.Client) (map[string]interface{}, error) { | ||
| systemPrompt := `You are a TypeScript compiler configuration expert. Convert natural language type-checking rules to tsconfig.json compiler options. | ||
| // Build list of valid TSC options for the prompt | ||
| validOptions := GetTSCOptionNames() | ||
| validOptionsStr := strings.Join(validOptions, ", ") | ||
|
|
||
| systemPrompt := fmt.Sprintf(`You are a TypeScript compiler configuration expert. Convert natural language type-checking rules to tsconfig.json compiler options. | ||
|
|
||
| IMPORTANT: You MUST ONLY use options from this exact list of valid TypeScript compiler options: | ||
| %s | ||
|
|
||
| Return ONLY a JSON object (no markdown fences) with TypeScript compiler options. | ||
| If the rule cannot be expressed with TypeScript compiler options, return empty object: {} | ||
|
|
||
| Available TypeScript compiler options: | ||
| - strict: true/false (enable all strict checks) | ||
| - noImplicitAny: true/false (error on implicit any) | ||
| - strictNullChecks: true/false (strict null checking) | ||
| - strictFunctionTypes: true/false (strict function types) | ||
| - strictBindCallApply: true/false (strict bind/call/apply) | ||
| - noUnusedLocals: true/false (error on unused locals) | ||
| - noUnusedParameters: true/false (error on unused parameters) | ||
| - noImplicitReturns: true/false (error on implicit returns) | ||
| - noFallthroughCasesInSwitch: true/false (error on fallthrough) | ||
| - noUncheckedIndexedAccess: true/false (undefined in index signatures) | ||
| - allowUnreachableCode: true/false (allow unreachable code) | ||
| - allowUnusedLabels: true/false (allow unused labels) | ||
|
|
||
| If the rule is not about TypeScript type-checking, return empty object: {} | ||
| CRITICAL RULES: | ||
| 1. ONLY use option names from the list above | ||
| 2. Do NOT invent new options | ||
| 3. If no option can enforce this rule, return {} | ||
|
|
||
| Examples: | ||
|
|
||
|
|
@@ -257,7 +270,12 @@ Input: "Enable all strict type checks" | |
| Output: | ||
| { | ||
| "strict": true | ||
| }` | ||
| } | ||
|
|
||
| Input: "Functions must have return type annotations" | ||
| Output: | ||
| {} | ||
| (Reason: No native TSC option for this - requires plugin)`, validOptionsStr) | ||
|
|
||
| userPrompt := fmt.Sprintf("Convert this rule to TypeScript compiler configuration:\n\n%s", rule.Say) | ||
|
|
||
|
|
@@ -279,5 +297,16 @@ Output: | |
| return nil, fmt.Errorf("failed to parse LLM response: %w", err) | ||
| } | ||
|
|
||
| return config, nil | ||
| // VALIDATION: Filter out invalid options | ||
| validConfig := make(map[string]interface{}) | ||
| for key, value := range config { | ||
| validation := ValidateTSCOption(key, value) | ||
| if validation.Valid { | ||
| validConfig[key] = value | ||
| } else { | ||
| fmt.Printf("⚠️ Invalid TSC option '%s': %s\n", key, validation.Message) | ||
| } | ||
|
Comment on lines
+307
to
+308
|
||
| } | ||
|
|
||
| return validConfig, nil | ||
|
Comment on lines
+300
to
+311
|
||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] These lines are very long (over 160 characters) which may affect readability and could exceed style guide limits. Consider breaking the checkstyle capabilities list into multiple lines for better readability: