Files
bzzz/pkg/slurp/intelligence/pattern_detector.go
anthonyrawlins d96c931a29 Resolve import cycles and migrate to chorus.services module path
This comprehensive refactoring addresses critical architectural issues:

IMPORT CYCLE RESOLUTION:
• pkg/crypto ↔ pkg/slurp/roles: Created pkg/security/access_levels.go
• pkg/ucxl → pkg/dht: Created pkg/storage/interfaces.go
• pkg/slurp/leader → pkg/election → pkg/slurp/storage: Moved types to pkg/election/interfaces.go

MODULE PATH MIGRATION:
• Changed from github.com/anthonyrawlins/bzzz to chorus.services/bzzz
• Updated all import statements across 115+ files
• Maintains compatibility while removing personal GitHub account dependency

TYPE SYSTEM IMPROVEMENTS:
• Resolved duplicate type declarations in crypto package
• Added missing type definitions (RoleStatus, TimeRestrictions, KeyStatus, KeyRotationResult)
• Proper interface segregation to prevent future cycles

ARCHITECTURAL BENEFITS:
• Build now progresses past structural issues to normal dependency resolution
• Cleaner separation of concerns between packages
• Eliminates circular dependencies that prevented compilation
• Establishes foundation for scalable codebase growth

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-17 10:04:25 +10:00

1147 lines
33 KiB
Go

package intelligence
import (
"context"
"fmt"
"path/filepath"
"regexp"
"sort"
"strings"
"time"
slurpContext "chorus.services/bzzz/pkg/slurp/context"
)
// DefaultPatternDetector provides comprehensive pattern detection capabilities
type DefaultPatternDetector struct {
config *EngineConfig
codeAnalyzer *CodePatternAnalyzer
namingAnalyzer *NamingPatternAnalyzer
orgAnalyzer *OrganizationalPatternAnalyzer
designAnalyzer *DesignPatternAnalyzer
}
// CodePatternAnalyzer detects code-level patterns and anti-patterns
type CodePatternAnalyzer struct {
languagePatterns map[string]*LanguageCodePatterns
}
// LanguageCodePatterns contains patterns specific to a programming language
type LanguageCodePatterns struct {
DesignPatterns []*DesignPatternMatcher
AntiPatterns []*AntiPatternMatcher
ArchPatterns []*ArchitecturalPatternMatcher
BestPractices []*BestPracticeMatcher
}
// DesignPatternMatcher detects specific design patterns
type DesignPatternMatcher struct {
PatternName string
Description string
Signatures []*regexp.Regexp
StructuralCues []string
Confidence func(matches int, totalLines int) float64
}
// AntiPatternMatcher detects anti-patterns and code smells
type AntiPatternMatcher struct {
PatternName string
Description string
Signatures []*regexp.Regexp
Severity string // critical, high, medium, low
Recommendation string
}
// ArchitecturalPatternMatcher detects architectural patterns
type ArchitecturalPatternMatcher struct {
PatternName string
Description string
FilePatterns []*regexp.Regexp
DirectoryHints []string
Dependencies []string
}
// BestPracticeMatcher detects adherence to best practices
type BestPracticeMatcher struct {
PracticeName string
Description string
Indicators []*regexp.Regexp
Violations []*regexp.Regexp
Impact string
}
// NamingPatternAnalyzer analyzes naming conventions and patterns
type NamingPatternAnalyzer struct {
conventionRules map[string]*NamingConventionRule
}
// NamingConventionRule defines a naming convention rule
type NamingConventionRule struct {
Language string
Scope string // function, variable, class, file, etc.
Pattern *regexp.Regexp
Description string
Examples []string
Violations []*regexp.Regexp
}
// OrganizationalPatternAnalyzer detects organizational and structural patterns
type OrganizationalPatternAnalyzer struct {
structuralPatterns []*StructuralPatternMatcher
}
// StructuralPatternMatcher detects structural organization patterns
type StructuralPatternMatcher struct {
PatternName string
Description string
DirectoryPatterns []*regexp.Regexp
FilePatterns []*regexp.Regexp
RequiredFiles []string
OptionalFiles []string
Depth int
Characteristics []string
}
// DesignPatternAnalyzer detects software design patterns
type DesignPatternAnalyzer struct {
patternLibrary map[string]*DesignPatternDefinition
}
// DesignPatternDefinition defines a comprehensive design pattern
type DesignPatternDefinition struct {
Name string
Category string // creational, structural, behavioral
Intent string
Applicability []string
Structure *PatternStructure
Participants []string
Collaborations []string
Consequences []string
Implementation *PatternImplementation
}
// PatternStructure defines the structural elements of a pattern
type PatternStructure struct {
Classes []string
Interfaces []string
Relationships []string
KeyComponents []string
}
// PatternImplementation contains implementation-specific details
type PatternImplementation struct {
Languages []string
CodeSignatures []*regexp.Regexp
FileStructure []string
Dependencies []string
}
// NewDefaultPatternDetector creates a comprehensive pattern detector
func NewDefaultPatternDetector(config *EngineConfig) *DefaultPatternDetector {
return &DefaultPatternDetector{
config: config,
codeAnalyzer: NewCodePatternAnalyzer(),
namingAnalyzer: NewNamingPatternAnalyzer(),
orgAnalyzer: NewOrganizationalPatternAnalyzer(),
designAnalyzer: NewDesignPatternAnalyzer(),
}
}
// NewCodePatternAnalyzer creates a code pattern analyzer
func NewCodePatternAnalyzer() *CodePatternAnalyzer {
analyzer := &CodePatternAnalyzer{
languagePatterns: make(map[string]*LanguageCodePatterns),
}
// Initialize Go patterns
goPatterns := &LanguageCodePatterns{
DesignPatterns: []*DesignPatternMatcher{
{
PatternName: "Singleton",
Description: "Ensures a class has only one instance",
Signatures: []*regexp.Regexp{
regexp.MustCompile(`var\s+instance\s+\*\w+`),
regexp.MustCompile(`sync\.Once`),
regexp.MustCompile(`func\s+GetInstance\s*\(\s*\)\s*\*\w+`),
},
StructuralCues: []string{"sync.Once", "private constructor", "static instance"},
Confidence: func(matches, totalLines int) float64 {
return float64(matches) / 3.0
},
},
{
PatternName: "Factory",
Description: "Creates objects without specifying exact class",
Signatures: []*regexp.Regexp{
regexp.MustCompile(`func\s+New\w+\s*\(`),
regexp.MustCompile(`func\s+Create\w+\s*\(`),
regexp.MustCompile(`func\s+Make\w+\s*\(`),
},
StructuralCues: []string{"factory method", "object creation"},
},
{
PatternName: "Builder",
Description: "Constructs complex objects step by step",
Signatures: []*regexp.Regexp{
regexp.MustCompile(`func\s+\(\w+\s+\*\w+\)\s+With\w+\(`),
regexp.MustCompile(`func\s+\(\w+\s+\*\w+\)\s+Set\w+\(`),
regexp.MustCompile(`func\s+\(\w+\s+\*\w+\)\s+Build\s*\(\s*\)`),
},
StructuralCues: []string{"fluent interface", "method chaining", "build method"},
},
{
PatternName: "Observer",
Description: "Notifies multiple objects about state changes",
Signatures: []*regexp.Regexp{
regexp.MustCompile(`type\s+\w*Observer\w*\s+interface`),
regexp.MustCompile(`func\s+\w*Subscribe\w*\s*\(`),
regexp.MustCompile(`func\s+\w*Notify\w*\s*\(`),
},
StructuralCues: []string{"observer interface", "subscription", "notification"},
},
},
AntiPatterns: []*AntiPatternMatcher{
{
PatternName: "God Object",
Description: "Class that does too much",
Signatures: []*regexp.Regexp{regexp.MustCompile(`func\s+\(\w+\s+\*\w+\)\s+\w+`)},
Severity: "high",
Recommendation: "Split responsibilities into smaller, focused types",
},
{
PatternName: "Magic Numbers",
Description: "Unexplained numeric literals",
Signatures: []*regexp.Regexp{regexp.MustCompile(`\b\d{2,}\b`)},
Severity: "medium",
Recommendation: "Replace with named constants",
},
},
ArchPatterns: []*ArchitecturalPatternMatcher{
{
PatternName: "Repository Pattern",
Description: "Encapsulates data access logic",
FilePatterns: []*regexp.Regexp{regexp.MustCompile(`.*repository.*\.go$`)},
DirectoryHints: []string{"repository", "repo", "storage"},
Dependencies: []string{"database", "storage"},
},
},
BestPractices: []*BestPracticeMatcher{
{
PracticeName: "Error Handling",
Description: "Proper error handling patterns",
Indicators: []*regexp.Regexp{
regexp.MustCompile(`if\s+err\s*!=\s*nil`),
regexp.MustCompile(`return.*,\s*err`),
},
Violations: []*regexp.Regexp{
regexp.MustCompile(`_\s*,\s*_\s*:=`),
},
Impact: "high",
},
},
}
// Initialize JavaScript/TypeScript patterns
jsPatterns := &LanguageCodePatterns{
DesignPatterns: []*DesignPatternMatcher{
{
PatternName: "Module Pattern",
Description: "Encapsulates functionality in modules",
Signatures: []*regexp.Regexp{
regexp.MustCompile(`export\s+default`),
regexp.MustCompile(`module\.exports\s*=`),
regexp.MustCompile(`export\s+\{.*\}`),
},
},
{
PatternName: "Singleton",
Description: "Single instance pattern in JavaScript",
Signatures: []*regexp.Regexp{
regexp.MustCompile(`class\s+\w+\s*\{[\s\S]*static\s+instance`),
regexp.MustCompile(`getInstance\s*\(\s*\)`),
},
},
{
PatternName: "Observer",
Description: "Event-driven programming pattern",
Signatures: []*regexp.Regexp{
regexp.MustCompile(`addEventListener\s*\(`),
regexp.MustCompile(`on\s*\(`),
regexp.MustCompile(`subscribe\s*\(`),
},
},
},
AntiPatterns: []*AntiPatternMatcher{
{
PatternName: "Callback Hell",
Description: "Deeply nested callbacks",
Signatures: []*regexp.Regexp{regexp.MustCompile(`function\s*\([^)]*\)\s*\{[\s\S]*function\s*\([^)]*\)\s*\{[\s\S]*function`)},
Severity: "high",
Recommendation: "Use Promises or async/await",
},
},
}
// Initialize Python patterns
pythonPatterns := &LanguageCodePatterns{
DesignPatterns: []*DesignPatternMatcher{
{
PatternName: "Decorator Pattern",
Description: "Adds behavior to objects dynamically",
Signatures: []*regexp.Regexp{
regexp.MustCompile(`@\w+`),
regexp.MustCompile(`def\s+\w+\s*\([^)]*\)\s*->\s*callable`),
},
},
{
PatternName: "Context Manager",
Description: "Resource management pattern",
Signatures: []*regexp.Regexp{
regexp.MustCompile(`def\s+__enter__\s*\(`),
regexp.MustCompile(`def\s+__exit__\s*\(`),
regexp.MustCompile(`with\s+\w+`),
},
},
},
}
analyzer.languagePatterns["go"] = goPatterns
analyzer.languagePatterns["javascript"] = jsPatterns
analyzer.languagePatterns["typescript"] = jsPatterns
analyzer.languagePatterns["python"] = pythonPatterns
return analyzer
}
// NewNamingPatternAnalyzer creates a naming pattern analyzer
func NewNamingPatternAnalyzer() *NamingPatternAnalyzer {
analyzer := &NamingPatternAnalyzer{
conventionRules: make(map[string]*NamingConventionRule),
}
// Go naming conventions
goRules := []*NamingConventionRule{
{
Language: "go",
Scope: "function",
Pattern: regexp.MustCompile(`^[A-Z][a-zA-Z0-9]*$`),
Description: "Exported functions use PascalCase",
Examples: []string{"GetUser", "ProcessData"},
},
{
Language: "go",
Scope: "variable",
Pattern: regexp.MustCompile(`^[a-z][a-zA-Z0-9]*$`),
Description: "Variables use camelCase",
Examples: []string{"userName", "totalCount"},
},
{
Language: "go",
Scope: "constant",
Pattern: regexp.MustCompile(`^[A-Z][A-Z0-9_]*$`),
Description: "Constants use SCREAMING_SNAKE_CASE",
Examples: []string{"MAX_SIZE", "DEFAULT_TIMEOUT"},
},
}
// JavaScript/TypeScript naming conventions
jsRules := []*NamingConventionRule{
{
Language: "javascript",
Scope: "function",
Pattern: regexp.MustCompile(`^[a-z][a-zA-Z0-9]*$`),
Description: "Functions use camelCase",
Examples: []string{"getUserData", "processResults"},
},
{
Language: "javascript",
Scope: "class",
Pattern: regexp.MustCompile(`^[A-Z][a-zA-Z0-9]*$`),
Description: "Classes use PascalCase",
Examples: []string{"UserManager", "DataProcessor"},
},
}
// Python naming conventions
pythonRules := []*NamingConventionRule{
{
Language: "python",
Scope: "function",
Pattern: regexp.MustCompile(`^[a-z][a-z0-9_]*$`),
Description: "Functions use snake_case",
Examples: []string{"get_user_data", "process_results"},
},
{
Language: "python",
Scope: "class",
Pattern: regexp.MustCompile(`^[A-Z][a-zA-Z0-9]*$`),
Description: "Classes use PascalCase",
Examples: []string{"UserManager", "DataProcessor"},
},
}
// Register all rules
for _, rule := range append(append(goRules, jsRules...), pythonRules...) {
key := fmt.Sprintf("%s_%s", rule.Language, rule.Scope)
analyzer.conventionRules[key] = rule
}
return analyzer
}
// NewOrganizationalPatternAnalyzer creates an organizational pattern analyzer
func NewOrganizationalPatternAnalyzer() *OrganizationalPatternAnalyzer {
analyzer := &OrganizationalPatternAnalyzer{
structuralPatterns: []*StructuralPatternMatcher{},
}
// Define common structural patterns
patterns := []*StructuralPatternMatcher{
{
PatternName: "Hexagonal Architecture",
Description: "Ports and adapters architecture",
DirectoryPatterns: []*regexp.Regexp{
regexp.MustCompile(`.*/(domain|core)/.*`),
regexp.MustCompile(`.*/adapters?/.*`),
regexp.MustCompile(`.*/ports?/.*`),
},
RequiredFiles: []string{"domain", "adapters"},
Characteristics: []string{"dependency_inversion", "testable", "framework_independent"},
},
{
PatternName: "Clean Architecture",
Description: "Uncle Bob's clean architecture",
DirectoryPatterns: []*regexp.Regexp{
regexp.MustCompile(`.*/entities/.*`),
regexp.MustCompile(`.*/usecases/.*`),
regexp.MustCompile(`.*/adapters/.*`),
regexp.MustCompile(`.*/frameworks?/.*`),
},
RequiredFiles: []string{"entities", "usecases"},
Characteristics: []string{"dependency_rule", "testable", "ui_independent"},
},
{
PatternName: "Microservices",
Description: "Service-oriented architecture",
DirectoryPatterns: []*regexp.Regexp{
regexp.MustCompile(`.*/services?/.*`),
regexp.MustCompile(`.*/api-gateway/.*`),
},
RequiredFiles: []string{"services"},
Characteristics: []string{"distributed", "autonomous", "scalable"},
},
{
PatternName: "Monorepo",
Description: "Multiple projects in single repository",
DirectoryPatterns: []*regexp.Regexp{
regexp.MustCompile(`.*/packages?/.*`),
regexp.MustCompile(`.*/apps?/.*`),
regexp.MustCompile(`.*/libs?/.*`),
},
RequiredFiles: []string{"packages", "apps"},
Characteristics: []string{"shared_dependencies", "atomic_commits", "unified_tooling"},
},
}
analyzer.structuralPatterns = patterns
return analyzer
}
// NewDesignPatternAnalyzer creates a design pattern analyzer
func NewDesignPatternAnalyzer() *DesignPatternAnalyzer {
analyzer := &DesignPatternAnalyzer{
patternLibrary: make(map[string]*DesignPatternDefinition),
}
// Define comprehensive design patterns
patterns := []*DesignPatternDefinition{
{
Name: "Singleton",
Category: "creational",
Intent: "Ensure a class has only one instance and provide global point of access",
Applicability: []string{
"exactly one instance needed",
"instance must be accessible from well-known access point",
"sole instance should be extensible by subclassing",
},
Structure: &PatternStructure{
Classes: []string{"Singleton"},
Interfaces: []string{},
Relationships: []string{"self-reference"},
KeyComponents: []string{"private constructor", "static instance", "getInstance method"},
},
Implementation: &PatternImplementation{
Languages: []string{"go", "java", "javascript", "python"},
CodeSignatures: []*regexp.Regexp{
regexp.MustCompile(`getInstance|GetInstance`),
regexp.MustCompile(`static.*instance|var.*instance`),
},
},
},
{
Name: "Factory Method",
Category: "creational",
Intent: "Create objects without specifying their concrete classes",
Structure: &PatternStructure{
Classes: []string{"Creator", "ConcreteCreator", "Product", "ConcreteProduct"},
Interfaces: []string{"Product"},
Relationships: []string{"creator uses product"},
KeyComponents: []string{"factory method", "product hierarchy"},
},
Implementation: &PatternImplementation{
Languages: []string{"go", "java", "javascript", "python"},
CodeSignatures: []*regexp.Regexp{
regexp.MustCompile(`New\w+|Create\w+|Make\w+`),
regexp.MustCompile(`factory|Factory`),
},
},
},
{
Name: "Observer",
Category: "behavioral",
Intent: "Define a one-to-many dependency between objects",
Structure: &PatternStructure{
Classes: []string{"Subject", "Observer", "ConcreteSubject", "ConcreteObserver"},
Interfaces: []string{"Observer", "Subject"},
Relationships: []string{"subject notifies observers"},
KeyComponents: []string{"subscribe", "unsubscribe", "notify"},
},
Implementation: &PatternImplementation{
Languages: []string{"go", "java", "javascript", "python"},
CodeSignatures: []*regexp.Regexp{
regexp.MustCompile(`Subscribe|Unsubscribe|Notify`),
regexp.MustCompile(`Observer|Subject`),
regexp.MustCompile(`addEventListener|on\(`),
},
},
},
}
for _, pattern := range patterns {
analyzer.patternLibrary[pattern.Name] = pattern
}
return analyzer
}
// DetectCodePatterns identifies code patterns and architectural styles
func (pd *DefaultPatternDetector) DetectCodePatterns(ctx context.Context, filePath string, content []byte) ([]*CodePattern, error) {
patterns := []*CodePattern{}
// Detect language
language := pd.detectLanguageFromPath(filePath)
if language == "" {
return patterns, nil
}
// Get language-specific patterns
langPatterns, exists := pd.codeAnalyzer.languagePatterns[language]
if !exists {
return patterns, nil
}
contentStr := string(content)
// Detect design patterns
for _, designPattern := range langPatterns.DesignPatterns {
if pattern := pd.analyzeDesignPattern(contentStr, designPattern, language); pattern != nil {
patterns = append(patterns, pattern)
}
}
// Detect architectural patterns
for _, archPattern := range langPatterns.ArchPatterns {
if pattern := pd.analyzeArchitecturalPattern(filePath, contentStr, archPattern, language); pattern != nil {
patterns = append(patterns, pattern)
}
}
// Detect anti-patterns
for _, antiPattern := range langPatterns.AntiPatterns {
if pattern := pd.analyzeAntiPattern(contentStr, antiPattern, language); pattern != nil {
patterns = append(patterns, pattern)
}
}
return patterns, nil
}
// DetectNamingPatterns identifies naming conventions and patterns
func (pd *DefaultPatternDetector) DetectNamingPatterns(ctx context.Context, contexts []*slurpContext.ContextNode) ([]*NamingPattern, error) {
patterns := []*NamingPattern{}
// Group contexts by language
langGroups := make(map[string][]*slurpContext.ContextNode)
for _, context := range contexts {
if analysis, ok := context.Metadata["analysis"].(*FileAnalysis); ok {
lang := analysis.Language
if lang != "" {
langGroups[lang] = append(langGroups[lang], context)
}
}
}
// Analyze naming patterns for each language
for language, langContexts := range langGroups {
langPatterns := pd.analyzeLanguageNamingPatterns(language, langContexts)
patterns = append(patterns, langPatterns...)
}
return patterns, nil
}
// DetectOrganizationalPatterns identifies organizational patterns
func (pd *DefaultPatternDetector) DetectOrganizationalPatterns(ctx context.Context, rootPath string) ([]*OrganizationalPattern, error) {
patterns := []*OrganizationalPattern{}
for _, matcher := range pd.orgAnalyzer.structuralPatterns {
if pattern := pd.analyzeStructuralPattern(rootPath, matcher); pattern != nil {
patterns = append(patterns, pattern)
}
}
return patterns, nil
}
// MatchPatterns matches context against known patterns
func (pd *DefaultPatternDetector) MatchPatterns(ctx context.Context, node *slurpContext.ContextNode, patterns []*Pattern) ([]*PatternMatch, error) {
matches := []*PatternMatch{}
for _, pattern := range patterns {
if match := pd.calculatePatternMatch(node, pattern); match != nil {
matches = append(matches, match)
}
}
// Sort by match score
sort.Slice(matches, func(i, j int) bool {
return matches[i].MatchScore > matches[j].MatchScore
})
return matches, nil
}
// LearnPatterns learns new patterns from context examples
func (pd *DefaultPatternDetector) LearnPatterns(ctx context.Context, examples []*slurpContext.ContextNode) ([]*Pattern, error) {
patterns := []*Pattern{}
// Group examples by similarity
groups := pd.groupSimilarContexts(examples)
// Extract patterns from each group
for groupID, group := range groups {
if len(group) >= 2 { // Need at least 2 examples to form a pattern
pattern := pd.extractPatternFromGroup(groupID, group)
if pattern != nil {
patterns = append(patterns, pattern)
}
}
}
return patterns, nil
}
// Helper methods
func (pd *DefaultPatternDetector) detectLanguageFromPath(filePath string) string {
ext := strings.ToLower(filepath.Ext(filePath))
langMap := map[string]string{
".go": "go",
".py": "python",
".js": "javascript",
".jsx": "javascript",
".ts": "typescript",
".tsx": "typescript",
".java": "java",
".c": "c",
".cpp": "cpp",
".cs": "csharp",
".php": "php",
".rb": "ruby",
".rs": "rust",
}
return langMap[ext]
}
func (pd *DefaultPatternDetector) analyzeDesignPattern(content string, matcher *DesignPatternMatcher, language string) *CodePattern {
matches := 0
matchedSignatures := []string{}
for _, signature := range matcher.Signatures {
if signature.MatchString(content) {
matches++
matchedSignatures = append(matchedSignatures, signature.String())
}
}
if matches == 0 {
return nil
}
confidence := 0.0
if matcher.Confidence != nil {
lines := strings.Count(content, "\n") + 1
confidence = matcher.Confidence(matches, lines)
} else {
confidence = float64(matches) / float64(len(matcher.Signatures))
}
if confidence < 0.3 {
return nil
}
return &CodePattern{
Pattern: Pattern{
ID: fmt.Sprintf("%s_%s", language, strings.ToLower(matcher.PatternName)),
Name: matcher.PatternName,
Type: "design_pattern",
Description: matcher.Description,
Confidence: confidence,
Examples: matchedSignatures,
DetectedAt: time.Now(),
},
Language: language,
Complexity: pd.calculatePatternComplexity(matcher.PatternName),
Usage: &UsagePattern{
Frequency: pd.determinePatternFrequency(matches),
Context: matcher.StructuralCues,
},
}
}
func (pd *DefaultPatternDetector) analyzeArchitecturalPattern(filePath, content string, matcher *ArchitecturalPatternMatcher, language string) *CodePattern {
// Check file path patterns
pathMatches := false
for _, pattern := range matcher.FilePatterns {
if pattern.MatchString(filePath) {
pathMatches = true
break
}
}
if !pathMatches {
return nil
}
// Check for dependencies if specified
hasRequiredDeps := len(matcher.Dependencies) == 0
if len(matcher.Dependencies) > 0 {
for _, dep := range matcher.Dependencies {
if strings.Contains(strings.ToLower(content), strings.ToLower(dep)) {
hasRequiredDeps = true
break
}
}
}
if !hasRequiredDeps {
return nil
}
return &CodePattern{
Pattern: Pattern{
ID: fmt.Sprintf("%s_%s_arch", language, strings.ToLower(matcher.PatternName)),
Name: matcher.PatternName,
Type: "architectural_pattern",
Description: matcher.Description,
Confidence: 0.8,
Examples: []string{filepath.Base(filePath)},
DetectedAt: time.Now(),
},
Language: language,
Complexity: 0.7,
Usage: &UsagePattern{
Frequency: "common",
Context: matcher.DirectoryHints,
},
}
}
func (pd *DefaultPatternDetector) analyzeAntiPattern(content string, matcher *AntiPatternMatcher, language string) *CodePattern {
matches := 0
for _, signature := range matcher.Signatures {
matches += len(signature.FindAllString(content, -1))
}
if matches == 0 {
return nil
}
severity := 0.5
switch matcher.Severity {
case "critical":
severity = 1.0
case "high":
severity = 0.8
case "medium":
severity = 0.6
case "low":
severity = 0.4
}
return &CodePattern{
Pattern: Pattern{
ID: fmt.Sprintf("%s_%s_anti", language, strings.ToLower(matcher.PatternName)),
Name: matcher.PatternName,
Type: "anti_pattern",
Description: matcher.Description,
Confidence: severity,
Frequency: matches,
Drawbacks: []string{matcher.Recommendation},
DetectedAt: time.Now(),
},
Language: language,
Complexity: severity,
}
}
func (pd *DefaultPatternDetector) analyzeLanguageNamingPatterns(language string, contexts []*slurpContext.ContextNode) []*NamingPattern {
patterns := []*NamingPattern{}
// Collect all identifiers from contexts
identifiers := pd.collectIdentifiers(contexts)
// Analyze patterns for different scopes
scopes := []string{"function", "variable", "class", "file"}
for _, scope := range scopes {
if pattern := pd.analyzeNamingPatternForScope(language, scope, identifiers[scope]); pattern != nil {
patterns = append(patterns, pattern)
}
}
return patterns
}
func (pd *DefaultPatternDetector) collectIdentifiers(contexts []*slurpContext.ContextNode) map[string][]string {
identifiers := make(map[string][]string)
for _, context := range contexts {
if analysis, ok := context.Metadata["analysis"].(*FileAnalysis); ok {
identifiers["function"] = append(identifiers["function"], analysis.Functions...)
identifiers["variable"] = append(identifiers["variable"], analysis.Variables...)
identifiers["class"] = append(identifiers["class"], analysis.Classes...)
identifiers["file"] = append(identifiers["file"], filepath.Base(context.Path))
}
}
return identifiers
}
func (pd *DefaultPatternDetector) analyzeNamingPatternForScope(language, scope string, identifiers []string) *NamingPattern {
if len(identifiers) < 2 {
return nil
}
// Detect dominant convention
conventions := map[string]int{
"camelCase": 0,
"PascalCase": 0,
"snake_case": 0,
"kebab-case": 0,
}
for _, identifier := range identifiers {
if matched, _ := regexp.MatchString(`^[a-z][a-zA-Z0-9]*$`, identifier); matched {
conventions["camelCase"]++
} else if matched, _ := regexp.MatchString(`^[A-Z][a-zA-Z0-9]*$`, identifier); matched {
conventions["PascalCase"]++
} else if matched, _ := regexp.MatchString(`^[a-z][a-z0-9_]*$`, identifier); matched {
conventions["snake_case"]++
} else if matched, _ := regexp.MatchString(`^[a-z][a-z0-9-]*$`, identifier); matched {
conventions["kebab-case"]++
}
}
// Find dominant convention
maxCount := 0
dominantConvention := "mixed"
for convention, count := range conventions {
if count > maxCount {
maxCount = count
dominantConvention = convention
}
}
confidence := float64(maxCount) / float64(len(identifiers))
if confidence < 0.5 {
return nil
}
return &NamingPattern{
Pattern: Pattern{
ID: fmt.Sprintf("%s_%s_naming", language, scope),
Name: fmt.Sprintf("%s %s Naming", strings.Title(language), strings.Title(scope)),
Type: "naming_convention",
Description: fmt.Sprintf("Naming convention for %s %ss", language, scope),
Confidence: confidence,
Examples: identifiers[:min(5, len(identifiers))],
DetectedAt: time.Now(),
},
Convention: dominantConvention,
Scope: scope,
CaseStyle: dominantConvention,
}
}
func (pd *DefaultPatternDetector) analyzeStructuralPattern(rootPath string, matcher *StructuralPatternMatcher) *OrganizationalPattern {
// Check if pattern directory structure exists
matchCount := 0
totalRequired := len(matcher.RequiredFiles)
for _, required := range matcher.RequiredFiles {
checkPath := filepath.Join(rootPath, required)
if pd.pathExists(checkPath) {
matchCount++
}
}
if matchCount < totalRequired {
return nil
}
confidence := float64(matchCount) / float64(totalRequired)
return &OrganizationalPattern{
Pattern: Pattern{
ID: strings.ToLower(strings.ReplaceAll(matcher.PatternName, " ", "_")),
Name: matcher.PatternName,
Type: "organizational",
Description: matcher.Description,
Confidence: confidence,
Examples: matcher.RequiredFiles,
Benefits: matcher.Characteristics,
DetectedAt: time.Now(),
},
Structure: "hierarchical",
Depth: matcher.Depth,
FanOut: len(matcher.RequiredFiles),
Modularity: confidence,
Scalability: pd.assessScalability(matcher.Characteristics),
}
}
func (pd *DefaultPatternDetector) calculatePatternMatch(node *slurpContext.ContextNode, pattern *Pattern) *PatternMatch {
score := 0.0
matchedFields := []string{}
// Check summary match
if pd.textContainsKeywords(node.Summary, pattern.Examples) {
score += 0.3
matchedFields = append(matchedFields, "summary")
}
// Check purpose match
if pd.textContainsKeywords(node.Purpose, pattern.Examples) {
score += 0.3
matchedFields = append(matchedFields, "purpose")
}
// Check technology match
for _, tech := range node.Technologies {
if pd.containsIgnoreCase(pattern.Examples, tech) {
score += 0.2
matchedFields = append(matchedFields, "technologies")
break
}
}
// Check tag match
for _, tag := range node.Tags {
if pd.containsIgnoreCase(pattern.Examples, tag) {
score += 0.2
matchedFields = append(matchedFields, "tags")
break
}
}
if score < 0.3 {
return nil
}
return &PatternMatch{
PatternID: pattern.ID,
MatchScore: score,
Confidence: pattern.Confidence * score,
MatchedFields: matchedFields,
Explanation: fmt.Sprintf("Pattern %s matched with score %.2f", pattern.Name, score),
Suggestions: pd.generatePatternSuggestions(pattern),
}
}
func (pd *DefaultPatternDetector) groupSimilarContexts(contexts []*slurpContext.ContextNode) map[string][]*slurpContext.ContextNode {
groups := make(map[string][]*slurpContext.ContextNode)
for _, context := range contexts {
// Simple grouping by primary technology
groupKey := "unknown"
if len(context.Technologies) > 0 {
groupKey = context.Technologies[0]
}
groups[groupKey] = append(groups[groupKey], context)
}
return groups
}
func (pd *DefaultPatternDetector) extractPatternFromGroup(groupID string, group []*slurpContext.ContextNode) *Pattern {
// Find common characteristics
commonTechs := pd.findCommonTechnologies(group)
commonTags := pd.findCommonTags(group)
if len(commonTechs) == 0 && len(commonTags) == 0 {
return nil
}
return &Pattern{
ID: fmt.Sprintf("learned_%s_%d", groupID, time.Now().Unix()),
Name: fmt.Sprintf("Learned %s Pattern", strings.Title(groupID)),
Type: "learned",
Description: fmt.Sprintf("Pattern extracted from %d similar contexts", len(group)),
Confidence: pd.calculateLearningConfidence(group),
Examples: append(commonTechs, commonTags...),
DetectedAt: time.Now(),
}
}
// Additional helper methods
func (pd *DefaultPatternDetector) calculatePatternComplexity(patternName string) float64 {
complexityMap := map[string]float64{
"Singleton": 0.3,
"Factory": 0.5,
"Builder": 0.7,
"Observer": 0.6,
"Strategy": 0.5,
"Command": 0.6,
"Decorator": 0.8,
"Composite": 0.9,
"Abstract Factory": 0.9,
"Prototype": 0.4,
}
if complexity, exists := complexityMap[patternName]; exists {
return complexity
}
return 0.5 // Default complexity
}
func (pd *DefaultPatternDetector) determinePatternFrequency(matches int) string {
if matches > 5 {
return "frequent"
} else if matches > 2 {
return "common"
} else {
return "rare"
}
}
func (pd *DefaultPatternDetector) pathExists(path string) bool {
_, err := filepath.Abs(path)
return err == nil
}
func (pd *DefaultPatternDetector) assessScalability(characteristics []string) string {
for _, char := range characteristics {
if strings.Contains(char, "scalable") {
return "excellent"
}
}
return "good"
}
func (pd *DefaultPatternDetector) textContainsKeywords(text string, keywords []string) bool {
lowerText := strings.ToLower(text)
for _, keyword := range keywords {
if strings.Contains(lowerText, strings.ToLower(keyword)) {
return true
}
}
return false
}
func (pd *DefaultPatternDetector) containsIgnoreCase(slice []string, item string) bool {
lowerItem := strings.ToLower(item)
for _, s := range slice {
if strings.ToLower(s) == lowerItem {
return true
}
}
return false
}
func (pd *DefaultPatternDetector) generatePatternSuggestions(pattern *Pattern) []string {
suggestions := []string{}
switch pattern.Type {
case "design_pattern":
suggestions = append(suggestions, "Consider documenting the pattern usage")
suggestions = append(suggestions, "Ensure pattern implementation follows best practices")
case "anti_pattern":
suggestions = append(suggestions, "Refactor to eliminate anti-pattern")
suggestions = append(suggestions, "Consider alternative design approaches")
case "architectural_pattern":
suggestions = append(suggestions, "Document architectural decisions")
suggestions = append(suggestions, "Ensure pattern consistency across project")
}
return suggestions
}
func (pd *DefaultPatternDetector) findCommonTechnologies(contexts []*slurpContext.ContextNode) []string {
techCount := make(map[string]int)
for _, context := range contexts {
for _, tech := range context.Technologies {
techCount[tech]++
}
}
common := []string{}
threshold := len(contexts) / 2 // At least half should have the technology
for tech, count := range techCount {
if count >= threshold {
common = append(common, tech)
}
}
return common
}
func (pd *DefaultPatternDetector) findCommonTags(contexts []*slurpContext.ContextNode) []string {
tagCount := make(map[string]int)
for _, context := range contexts {
for _, tag := range context.Tags {
tagCount[tag]++
}
}
common := []string{}
threshold := len(contexts) / 2
for tag, count := range tagCount {
if count >= threshold {
common = append(common, tag)
}
}
return common
}
func (pd *DefaultPatternDetector) calculateLearningConfidence(group []*slurpContext.ContextNode) float64 {
// Simple confidence based on group size and consistency
baseConfidence := 0.5
groupBonus := float64(len(group)) * 0.1
if groupBonus > 0.3 {
groupBonus = 0.3
}
return baseConfidence + groupBonus
}
func min(a, b int) int {
if a < b {
return a
}
return b
}