Skip to content

Commit d7cc7c4

Browse files
authored
feat: add category management(add, edit, remove) (#44)
* feat: add category subcommands (list/add/edit/remove) - Refactor sym category to parent command with subcommands - Add sym category list (moved from sym category) - Add sym category add <name> <description> - Add sym category edit <name> --name/--description - Add sym category remove <name> (blocked if rules reference it) * feat: add MCP tools for category management - add_category: add new category with name and description - edit_category: edit category name/description, updates rule refs - remove_category: remove category (blocked if rules reference it) * test: add MCP category management tests - TestAddCategory: success, duplicate, empty name/description - TestEditCategory: description update, rename with rule refs, conflicts - TestRemoveCategory: unused category, rules blocking, not found * feat: add dashboard API endpoints for category management - GET /api/categories: list all categories - POST /api/categories: add new category - PUT /api/categories/{name}: edit category - DELETE /api/categories/{name}: remove category (blocked if used) * feat: add category management UI to dashboard * fix: move category UI to Global Settings and fix input sizing * fix: preserve categories when applying template * feat: auto-add missing categories when applying template * feat: include category description in LLM routing prompt * fix: correct jsonschema tag format for MCP category tools * feat: add batch operations for category management - MCP tools: batch-only mode (categories[], edits[], names[]) - CLI commands: single + batch mode with --file flag - Partial failure handling with success/fail reporting * feat: simplify template category handling - Add category array to template files (node, python, go) - Overwrite categories when applying template (remove merge logic) - Fix LLM provider message in convert dialog * refactor: improve directory change handling in tests and clean up unused code
1 parent eeaf88c commit d7cc7c4

10 files changed

Lines changed: 1747 additions & 20 deletions

File tree

internal/cmd/category.go

Lines changed: 422 additions & 5 deletions
Large diffs are not rendered by default.

internal/converter/converter.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,12 @@ func (c *Converter) routeRulesWithLLM(ctx context.Context, userPolicy *schema.Us
301301
}
302302
sem := make(chan struct{}, maxConcurrent)
303303

304+
// Build category name -> description map
305+
categoryMap := make(map[string]string)
306+
for _, cat := range userPolicy.Category {
307+
categoryMap[cat.Name] = cat.Description
308+
}
309+
304310
// Process rules in parallel with concurrency limit
305311
for _, rule := range userPolicy.Rules {
306312
// Get languages for this rule
@@ -322,7 +328,7 @@ func (c *Converter) routeRulesWithLLM(ctx context.Context, userPolicy *schema.Us
322328
}
323329

324330
wg.Add(1)
325-
go func(r schema.UserRule, linters []string) {
331+
go func(r schema.UserRule, linters []string, catMap map[string]string) {
326332
defer wg.Done()
327333

328334
// Acquire semaphore with context check
@@ -334,7 +340,7 @@ func (c *Converter) routeRulesWithLLM(ctx context.Context, userPolicy *schema.Us
334340
defer func() { <-sem }()
335341

336342
// Ask LLM which linters are appropriate for this rule
337-
selectedLinters := c.selectLintersForRule(ctx, r, linters)
343+
selectedLinters := c.selectLintersForRule(ctx, r, linters, catMap)
338344

339345
// Send result with context check to prevent deadlock
340346
if len(selectedLinters) == 0 {
@@ -351,7 +357,7 @@ func (c *Converter) routeRulesWithLLM(ctx context.Context, userPolicy *schema.Us
351357
return
352358
}
353359
}
354-
}(rule, availableLinters)
360+
}(rule, availableLinters, categoryMap)
355361
}
356362

357363
// Close results channel after all goroutines complete
@@ -398,7 +404,7 @@ func (c *Converter) getAvailableLinters(languages []string) []string {
398404
}
399405

400406
// selectLintersForRule uses LLM to determine which linters are appropriate for a rule
401-
func (c *Converter) selectLintersForRule(ctx context.Context, rule schema.UserRule, availableLinters []string) []string {
407+
func (c *Converter) selectLintersForRule(ctx context.Context, rule schema.UserRule, availableLinters []string, categoryMap map[string]string) []string {
402408
// Build linter descriptions dynamically from registry
403409
linterDescriptions := c.buildLinterDescriptions(availableLinters)
404410

@@ -465,7 +471,11 @@ Input: "Imports from large packages must be specific"
465471
Output: []
466472
Reason: Requires knowing which packages are "large"`, linterDescriptions, routingHints, availableLinters)
467473

468-
userPrompt := fmt.Sprintf("Rule: %s\nCategory: %s", rule.Say, rule.Category)
474+
categoryInfo := rule.Category
475+
if desc, ok := categoryMap[rule.Category]; ok && desc != "" {
476+
categoryInfo = fmt.Sprintf("%s (%s)", rule.Category, desc)
477+
}
478+
userPrompt := fmt.Sprintf("Rule: %s\nCategory: %s", rule.Say, categoryInfo)
469479

470480
// Call LLM
471481
prompt := systemPrompt + "\n\n" + userPrompt

0 commit comments

Comments
 (0)