Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions internal/converter/converter.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ func NewConverter(llmClient *llm.Client, outputDir string) *Converter {

// ConvertResult represents the result of conversion
type ConvertResult struct {
GeneratedFiles []string // List of generated file paths (including code-policy.json)
GeneratedFiles []string // List of generated file paths (including code-policy.json)
CodePolicy *schema.CodePolicy // Generated code policy
Errors map[string]error // Errors per linter
Warnings []string // Conversion warnings
Errors map[string]error // Errors per linter
Warnings []string // Conversion warnings
}

// Convert is the main entry point for converting user policy to linter configs
Expand Down Expand Up @@ -328,8 +328,10 @@ Available linters and NATIVE capabilities:
- CANNOT: Complex business logic, context-aware rules, file naming, advanced async patterns
- prettier: Code formatting ONLY (quotes, semicolons, indentation, line length, trailing commas)
- tsc: TypeScript type checking ONLY (strict modes, noImplicitAny, strictNullChecks, type inference)
- checkstyle: Java style checks (naming, whitespace, imports, line length, complexity)
- pmd: Java code quality (unused code, empty blocks, naming conventions, design issues)
- checkstyle: Java naming and style checks ONLY (ClassName, MethodName, VariableName, ConstantName, ParameterName, LocalVariableName, LineLength, MethodLength,
ParameterNumber, FileLength, Indentation, WhitespaceAround, NeedBraces, LeftCurly, RightCurly, AvoidStarImport,
IllegalImport, UnusedImports, CyclomaticComplexity, NPathComplexity, JavadocMethod, JavadocType, MissingJavadocMethod)
Comment on lines +331 to +333
Copy link

Copilot AI Nov 26, 2025

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:

- checkstyle: Java naming and style checks ONLY 
  (ClassName, MethodName, VariableName, ConstantName, ParameterName, 
   LocalVariableName, LineLength, MethodLength, ParameterNumber, FileLength, 
   Indentation, WhitespaceAround, NeedBraces, LeftCurly, RightCurly, 
   AvoidStarImport, IllegalImport, UnusedImports, CyclomaticComplexity, 
   NPathComplexity, JavadocMethod, JavadocType, MissingJavadocMethod)
Suggested change
- checkstyle: Java naming and style checks ONLY (ClassName, MethodName, VariableName, ConstantName, ParameterName, LocalVariableName, LineLength, MethodLength,
ParameterNumber, FileLength, Indentation, WhitespaceAround, NeedBraces, LeftCurly, RightCurly, AvoidStarImport,
IllegalImport, UnusedImports, CyclomaticComplexity, NPathComplexity, JavadocMethod, JavadocType, MissingJavadocMethod)
- checkstyle: Java naming and style checks ONLY (
ClassName, MethodName, VariableName, ConstantName, ParameterName, LocalVariableName, LineLength, MethodLength,
ParameterNumber, FileLength, Indentation, WhitespaceAround, NeedBraces, LeftCurly, RightCurly, AvoidStarImport,
IllegalImport, UnusedImports, CyclomaticComplexity, NPathComplexity, JavadocMethod, JavadocType, MissingJavadocMethod
)

Copilot uses AI. Check for mistakes.
- pmd: Java code quality (unused code, empty blocks, basic naming, design issues)

STRICT Rules for selection:
1. ONLY select if the linter has a NATIVE rule that can enforce this
Expand Down
60 changes: 42 additions & 18 deletions internal/converter/linters/eslint.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"sort"
"strings"
"sync"

Expand Down Expand Up @@ -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:
{
Expand All @@ -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:

Expand All @@ -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 != "" {
Expand Down Expand Up @@ -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
Copy link

Copilot AI Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using fmt.Printf to output warnings directly to stdout is not ideal for library code. Consider one of these alternatives:

  1. Use a logger interface that can be injected (e.g., log.Printf or a structured logger)
  2. Collect warnings in the result structure and return them
  3. Return warnings through the error return value using a custom error type that includes warnings

This would make the code more testable and allow callers to control how warnings are handled.

Copilot uses AI. Check for mistakes.
}
Comment on lines +230 to +236
Copy link

Copilot AI Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new ESLint rule validation logic lacks test coverage. Consider adding tests to verify:

  1. Valid rules are accepted and processed
  2. Invalid rules are rejected and return empty values
  3. The warning message is logged (or consider returning warnings in a testable way)
  4. Deprecated rules are handled correctly (if any are added to the registry)

Copilot uses AI. Check for mistakes.

// Map user severity to ESLint severity if needed
severity := mapSeverity(rule.Severity)
if severity == "" {
Expand Down
95 changes: 62 additions & 33 deletions internal/converter/linters/prettier_tsc.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand All @@ -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)

Expand All @@ -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
Copy link

Copilot AI Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using fmt.Printf to output warnings directly to stdout is not ideal for library code. Consider one of these alternatives:

  1. Use a logger interface that can be injected (e.g., log.Printf or a structured logger)
  2. Collect warnings in the result structure and return them
  3. Return warnings through the error return value using a custom error type that includes warnings

This would make the code more testable and allow callers to control how warnings are handled.

Copilot uses AI. Check for mistakes.
}

return validConfig, nil
Comment on lines +144 to +155
Copy link

Copilot AI Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new validation logic that filters out invalid Prettier options lacks test coverage. Consider adding tests to verify:

  1. Valid options are kept in the result
  2. Invalid options are filtered out
  3. The warning message is logged (or consider returning warnings in a testable way)
  4. Empty configuration is handled correctly when all options are invalid

Copilot uses AI. Check for mistakes.
}

// TSCLinterConverter converts rules to TypeScript compiler configuration
Expand Down Expand Up @@ -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:

Expand All @@ -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)

Expand All @@ -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
Copy link

Copilot AI Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using fmt.Printf to output warnings directly to stdout is not ideal for library code. Consider one of these alternatives:

  1. Use a logger interface that can be injected (e.g., log.Printf or a structured logger)
  2. Collect warnings in the result structure and return them
  3. Return warnings through the error return value using a custom error type that includes warnings

This would make the code more testable and allow callers to control how warnings are handled.

Copilot uses AI. Check for mistakes.
}

return validConfig, nil
Comment on lines +300 to +311
Copy link

Copilot AI Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new validation logic that filters out invalid TSC options lacks test coverage. Consider adding tests to verify:

  1. Valid options are kept in the result
  2. Invalid options are filtered out
  3. The warning message is logged (or consider returning warnings in a testable way)
  4. Empty configuration is handled correctly when all options are invalid

Copilot uses AI. Check for mistakes.
}
Loading
Loading