 d96c931a29
			
		
	
	d96c931a29
	
	
	
		
			
			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>
		
			
				
	
	
		
			1037 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			1037 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package intelligence
 | |
| 
 | |
| import (
 | |
| 	"crypto/md5"
 | |
| 	"crypto/rand"
 | |
| 	"encoding/hex"
 | |
| 	"encoding/json"
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"math"
 | |
| 	"os"
 | |
| 	"path/filepath"
 | |
| 	"regexp"
 | |
| 	"sort"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 	"time"
 | |
| 
 | |
| 	slurpContext "chorus.services/bzzz/pkg/slurp/context"
 | |
| )
 | |
| 
 | |
| // Utility functions and helper types for the intelligence engine
 | |
| 
 | |
| // ContentAnalysisUtils provides utilities for content analysis
 | |
| type ContentAnalysisUtils struct{}
 | |
| 
 | |
| // NewContentAnalysisUtils creates new content analysis utilities
 | |
| func NewContentAnalysisUtils() *ContentAnalysisUtils {
 | |
| 	return &ContentAnalysisUtils{}
 | |
| }
 | |
| 
 | |
| // ExtractIdentifiers extracts identifiers from code content
 | |
| func (cau *ContentAnalysisUtils) ExtractIdentifiers(content, language string) (functions, classes, variables []string) {
 | |
| 	switch strings.ToLower(language) {
 | |
| 	case "go":
 | |
| 		return cau.extractGoIdentifiers(content)
 | |
| 	case "javascript", "typescript":
 | |
| 		return cau.extractJSIdentifiers(content)
 | |
| 	case "python":
 | |
| 		return cau.extractPythonIdentifiers(content)
 | |
| 	case "java":
 | |
| 		return cau.extractJavaIdentifiers(content)
 | |
| 	case "rust":
 | |
| 		return cau.extractRustIdentifiers(content)
 | |
| 	default:
 | |
| 		return cau.extractGenericIdentifiers(content)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (cau *ContentAnalysisUtils) extractGoIdentifiers(content string) (functions, classes, variables []string) {
 | |
| 	// Go function pattern: func FunctionName
 | |
| 	funcPattern := regexp.MustCompile(`func\s+(\w+)\s*\(`)
 | |
| 	funcMatches := funcPattern.FindAllStringSubmatch(content, -1)
 | |
| 	for _, match := range funcMatches {
 | |
| 		if len(match) > 1 {
 | |
| 			functions = append(functions, match[1])
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Go type/struct pattern: type TypeName struct
 | |
| 	typePattern := regexp.MustCompile(`type\s+(\w+)\s+struct`)
 | |
| 	typeMatches := typePattern.FindAllStringSubmatch(content, -1)
 | |
| 	for _, match := range typeMatches {
 | |
| 		if len(match) > 1 {
 | |
| 			classes = append(classes, match[1])
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Go variable pattern: var varName or varName :=
 | |
| 	varPattern := regexp.MustCompile(`(?:var\s+(\w+)|(\w+)\s*:=)`)
 | |
| 	varMatches := varPattern.FindAllStringSubmatch(content, -1)
 | |
| 	for _, match := range varMatches {
 | |
| 		if len(match) > 1 && match[1] != "" {
 | |
| 			variables = append(variables, match[1])
 | |
| 		} else if len(match) > 2 && match[2] != "" {
 | |
| 			variables = append(variables, match[2])
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return removeDuplicates(functions), removeDuplicates(classes), removeDuplicates(variables)
 | |
| }
 | |
| 
 | |
| func (cau *ContentAnalysisUtils) extractJSIdentifiers(content string) (functions, classes, variables []string) {
 | |
| 	// JavaScript function patterns
 | |
| 	funcPatterns := []*regexp.Regexp{
 | |
| 		regexp.MustCompile(`function\s+(\w+)\s*\(`),
 | |
| 		regexp.MustCompile(`(\w+)\s*:\s*function\s*\(`),
 | |
| 		regexp.MustCompile(`const\s+(\w+)\s*=\s*\(`),
 | |
| 		regexp.MustCompile(`(?:let|var)\s+(\w+)\s*=\s*\(`),
 | |
| 	}
 | |
| 
 | |
| 	for _, pattern := range funcPatterns {
 | |
| 		matches := pattern.FindAllStringSubmatch(content, -1)
 | |
| 		for _, match := range matches {
 | |
| 			if len(match) > 1 {
 | |
| 				functions = append(functions, match[1])
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// JavaScript class pattern
 | |
| 	classPattern := regexp.MustCompile(`class\s+(\w+)`)
 | |
| 	classMatches := classPattern.FindAllStringSubmatch(content, -1)
 | |
| 	for _, match := range classMatches {
 | |
| 		if len(match) > 1 {
 | |
| 			classes = append(classes, match[1])
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// JavaScript variable patterns
 | |
| 	varPatterns := []*regexp.Regexp{
 | |
| 		regexp.MustCompile(`(?:const|let|var)\s+(\w+)`),
 | |
| 	}
 | |
| 
 | |
| 	for _, pattern := range varPatterns {
 | |
| 		matches := pattern.FindAllStringSubmatch(content, -1)
 | |
| 		for _, match := range matches {
 | |
| 			if len(match) > 1 {
 | |
| 				variables = append(variables, match[1])
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return removeDuplicates(functions), removeDuplicates(classes), removeDuplicates(variables)
 | |
| }
 | |
| 
 | |
| func (cau *ContentAnalysisUtils) extractPythonIdentifiers(content string) (functions, classes, variables []string) {
 | |
| 	// Python function pattern
 | |
| 	funcPattern := regexp.MustCompile(`def\s+(\w+)\s*\(`)
 | |
| 	funcMatches := funcPattern.FindAllStringSubmatch(content, -1)
 | |
| 	for _, match := range funcMatches {
 | |
| 		if len(match) > 1 {
 | |
| 			functions = append(functions, match[1])
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Python class pattern
 | |
| 	classPattern := regexp.MustCompile(`class\s+(\w+)`)
 | |
| 	classMatches := classPattern.FindAllStringSubmatch(content, -1)
 | |
| 	for _, match := range classMatches {
 | |
| 		if len(match) > 1 {
 | |
| 			classes = append(classes, match[1])
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Python variable pattern (simple assignment)
 | |
| 	varPattern := regexp.MustCompile(`^(\w+)\s*=`)
 | |
| 	lines := strings.Split(content, "\n")
 | |
| 	for _, line := range lines {
 | |
| 		line = strings.TrimSpace(line)
 | |
| 		if matches := varPattern.FindStringSubmatch(line); matches != nil && len(matches) > 1 {
 | |
| 			variables = append(variables, matches[1])
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return removeDuplicates(functions), removeDuplicates(classes), removeDuplicates(variables)
 | |
| }
 | |
| 
 | |
| func (cau *ContentAnalysisUtils) extractJavaIdentifiers(content string) (functions, classes, variables []string) {
 | |
| 	// Java method pattern
 | |
| 	methodPattern := regexp.MustCompile(`(?:public|private|protected)?\s*(?:static)?\s*\w+\s+(\w+)\s*\(`)
 | |
| 	methodMatches := methodPattern.FindAllStringSubmatch(content, -1)
 | |
| 	for _, match := range methodMatches {
 | |
| 		if len(match) > 1 {
 | |
| 			functions = append(functions, match[1])
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Java class pattern
 | |
| 	classPattern := regexp.MustCompile(`(?:public|private)?\s*class\s+(\w+)`)
 | |
| 	classMatches := classPattern.FindAllStringSubmatch(content, -1)
 | |
| 	for _, match := range classMatches {
 | |
| 		if len(match) > 1 {
 | |
| 			classes = append(classes, match[1])
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Java field/variable pattern
 | |
| 	varPattern := regexp.MustCompile(`(?:private|public|protected)?\s*\w+\s+(\w+)\s*[=;]`)
 | |
| 	varMatches := varPattern.FindAllStringSubmatch(content, -1)
 | |
| 	for _, match := range varMatches {
 | |
| 		if len(match) > 1 {
 | |
| 			variables = append(variables, match[1])
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return removeDuplicates(functions), removeDuplicates(classes), removeDuplicates(variables)
 | |
| }
 | |
| 
 | |
| func (cau *ContentAnalysisUtils) extractRustIdentifiers(content string) (functions, classes, variables []string) {
 | |
| 	// Rust function pattern
 | |
| 	funcPattern := regexp.MustCompile(`fn\s+(\w+)\s*\(`)
 | |
| 	funcMatches := funcPattern.FindAllStringSubmatch(content, -1)
 | |
| 	for _, match := range funcMatches {
 | |
| 		if len(match) > 1 {
 | |
| 			functions = append(functions, match[1])
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Rust struct pattern
 | |
| 	structPattern := regexp.MustCompile(`struct\s+(\w+)`)
 | |
| 	structMatches := structPattern.FindAllStringSubmatch(content, -1)
 | |
| 	for _, match := range structMatches {
 | |
| 		if len(match) > 1 {
 | |
| 			classes = append(classes, match[1])
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Rust variable pattern
 | |
| 	varPattern := regexp.MustCompile(`let\s+(?:mut\s+)?(\w+)`)
 | |
| 	varMatches := varPattern.FindAllStringSubmatch(content, -1)
 | |
| 	for _, match := range varMatches {
 | |
| 		if len(match) > 1 {
 | |
| 			variables = append(variables, match[1])
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return removeDuplicates(functions), removeDuplicates(classes), removeDuplicates(variables)
 | |
| }
 | |
| 
 | |
| func (cau *ContentAnalysisUtils) extractGenericIdentifiers(content string) (functions, classes, variables []string) {
 | |
| 	// Generic patterns for unknown languages
 | |
| 	words := regexp.MustCompile(`\b[a-zA-Z_]\w*\b`).FindAllString(content, -1)
 | |
| 	return removeDuplicates(words), []string{}, []string{}
 | |
| }
 | |
| 
 | |
| // CalculateComplexity calculates code complexity based on various metrics
 | |
| func (cau *ContentAnalysisUtils) CalculateComplexity(content, language string) float64 {
 | |
| 	complexity := 0.0
 | |
| 	
 | |
| 	// Lines of code (basic metric)
 | |
| 	lines := strings.Split(content, "\n")
 | |
| 	nonEmptyLines := 0
 | |
| 	for _, line := range lines {
 | |
| 		if strings.TrimSpace(line) != "" && !strings.HasPrefix(strings.TrimSpace(line), "//") {
 | |
| 			nonEmptyLines++
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	// Base complexity from lines of code
 | |
| 	complexity += float64(nonEmptyLines) * 0.1
 | |
| 	
 | |
| 	// Control flow complexity (if, for, while, switch, etc.)
 | |
| 	controlFlowPatterns := []*regexp.Regexp{
 | |
| 		regexp.MustCompile(`\b(?:if|for|while|switch|case)\b`),
 | |
| 		regexp.MustCompile(`\b(?:try|catch|finally)\b`),
 | |
| 		regexp.MustCompile(`\?\s*.*\s*:`), // ternary operator
 | |
| 	}
 | |
| 	
 | |
| 	for _, pattern := range controlFlowPatterns {
 | |
| 		matches := pattern.FindAllString(content, -1)
 | |
| 		complexity += float64(len(matches)) * 0.5
 | |
| 	}
 | |
| 	
 | |
| 	// Function complexity
 | |
| 	functions, _, _ := cau.ExtractIdentifiers(content, language)
 | |
| 	complexity += float64(len(functions)) * 0.3
 | |
| 	
 | |
| 	// Nesting level (simple approximation)
 | |
| 	maxNesting := 0
 | |
| 	currentNesting := 0
 | |
| 	for _, line := range lines {
 | |
| 		trimmed := strings.TrimSpace(line)
 | |
| 		openBraces := strings.Count(trimmed, "{")
 | |
| 		closeBraces := strings.Count(trimmed, "}")
 | |
| 		currentNesting += openBraces - closeBraces
 | |
| 		if currentNesting > maxNesting {
 | |
| 			maxNesting = currentNesting
 | |
| 		}
 | |
| 	}
 | |
| 	complexity += float64(maxNesting) * 0.2
 | |
| 	
 | |
| 	// Normalize to 0-10 scale
 | |
| 	return math.Min(10.0, complexity/10.0)
 | |
| }
 | |
| 
 | |
| // DetectTechnologies detects technologies used in the content
 | |
| func (cau *ContentAnalysisUtils) DetectTechnologies(content, filename string) []string {
 | |
| 	technologies := []string{}
 | |
| 	lowerContent := strings.ToLower(content)
 | |
| 	ext := strings.ToLower(filepath.Ext(filename))
 | |
| 	
 | |
| 	// Language detection
 | |
| 	languageMap := map[string][]string{
 | |
| 		".go":   {"go", "golang"},
 | |
| 		".py":   {"python"},
 | |
| 		".js":   {"javascript", "node.js"},
 | |
| 		".jsx":  {"javascript", "react", "jsx"},
 | |
| 		".ts":   {"typescript"},
 | |
| 		".tsx":  {"typescript", "react", "jsx"},
 | |
| 		".java": {"java"},
 | |
| 		".kt":   {"kotlin"},
 | |
| 		".rs":   {"rust"},
 | |
| 		".cpp":  {"c++"},
 | |
| 		".c":    {"c"},
 | |
| 		".cs":   {"c#", ".net"},
 | |
| 		".php":  {"php"},
 | |
| 		".rb":   {"ruby"},
 | |
| 		".swift": {"swift"},
 | |
| 		".scala": {"scala"},
 | |
| 		".clj":  {"clojure"},
 | |
| 		".hs":   {"haskell"},
 | |
| 		".ml":   {"ocaml"},
 | |
| 	}
 | |
| 	
 | |
| 	if langs, exists := languageMap[ext]; exists {
 | |
| 		technologies = append(technologies, langs...)
 | |
| 	}
 | |
| 	
 | |
| 	// Framework and library detection
 | |
| 	frameworkPatterns := map[string][]string{
 | |
| 		"react":          {"import.*react", "from [\"']react[\"']", "<.*/>", "jsx"},
 | |
| 		"vue":            {"import.*vue", "from [\"']vue[\"']", "<template>", "vue"},
 | |
| 		"angular":        {"import.*@angular", "from [\"']@angular", "ngmodule", "component"},
 | |
| 		"express":        {"import.*express", "require.*express", "app.get", "app.post"},
 | |
| 		"django":         {"from django", "import django", "django.db", "models.model"},
 | |
| 		"flask":          {"from flask", "import flask", "@app.route", "flask.request"},
 | |
| 		"spring":         {"@springboot", "@controller", "@service", "@repository"},
 | |
| 		"hibernate":      {"@entity", "@table", "@column", "hibernate"},
 | |
| 		"jquery":         {"$\\(", "jquery"},
 | |
| 		"bootstrap":      {"bootstrap", "btn-", "col-", "row"},
 | |
| 		"docker":         {"dockerfile", "docker-compose", "from.*:", "run.*"},
 | |
| 		"kubernetes":     {"apiversion:", "kind:", "metadata:", "spec:"},
 | |
| 		"terraform":      {"\\.tf$", "resource \"", "provider \"", "terraform"},
 | |
| 		"ansible":        {"\\.yml$", "hosts:", "tasks:", "playbook"},
 | |
| 		"jenkins":        {"jenkinsfile", "pipeline", "stage", "steps"},
 | |
| 		"git":            {"\\.git", "git add", "git commit", "git push"},
 | |
| 		"mysql":          {"mysql", "select.*from", "insert into", "create table"},
 | |
| 		"postgresql":     {"postgresql", "postgres", "psql"},
 | |
| 		"mongodb":        {"mongodb", "mongo", "find\\(", "insert\\("},
 | |
| 		"redis":          {"redis", "set.*", "get.*", "rpush"},
 | |
| 		"elasticsearch":  {"elasticsearch", "elastic", "query.*", "search.*"},
 | |
| 		"graphql":        {"graphql", "query.*{", "mutation.*{", "subscription.*{"},
 | |
| 		"grpc":           {"grpc", "proto", "service.*rpc", "\\.proto$"},
 | |
| 		"websocket":      {"websocket", "ws://", "wss://", "socket.io"},
 | |
| 		"jwt":            {"jwt", "jsonwebtoken", "bearer.*token"},
 | |
| 		"oauth":          {"oauth", "oauth2", "client_id", "client_secret"},
 | |
| 		"ssl":            {"ssl", "tls", "https", "certificate"},
 | |
| 		"encryption":     {"encrypt", "decrypt", "bcrypt", "sha256"},
 | |
| 	}
 | |
| 	
 | |
| 	for tech, patterns := range frameworkPatterns {
 | |
| 		for _, pattern := range patterns {
 | |
| 			if matched, _ := regexp.MatchString(pattern, lowerContent); matched {
 | |
| 				technologies = append(technologies, tech)
 | |
| 				break
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	return removeDuplicates(technologies)
 | |
| }
 | |
| 
 | |
| // ScoreUtils provides utilities for scoring and metrics
 | |
| type ScoreUtils struct{}
 | |
| 
 | |
| // NewScoreUtils creates new score utilities
 | |
| func NewScoreUtils() *ScoreUtils {
 | |
| 	return &ScoreUtils{}
 | |
| }
 | |
| 
 | |
| // NormalizeScore normalizes a score to a 0-1 range
 | |
| func (su *ScoreUtils) NormalizeScore(score, min, max float64) float64 {
 | |
| 	if max == min {
 | |
| 		return 0.5 // Default middle value when range is zero
 | |
| 	}
 | |
| 	return math.Max(0, math.Min(1, (score-min)/(max-min)))
 | |
| }
 | |
| 
 | |
| // CalculateWeightedScore calculates weighted score from multiple scores
 | |
| func (su *ScoreUtils) CalculateWeightedScore(scores map[string]float64, weights map[string]float64) float64 {
 | |
| 	totalWeight := 0.0
 | |
| 	weightedSum := 0.0
 | |
| 	
 | |
| 	for dimension, score := range scores {
 | |
| 		weight := weights[dimension]
 | |
| 		if weight == 0 {
 | |
| 			weight = 1.0 // Default weight
 | |
| 		}
 | |
| 		weightedSum += score * weight
 | |
| 		totalWeight += weight
 | |
| 	}
 | |
| 	
 | |
| 	if totalWeight == 0 {
 | |
| 		return 0.0
 | |
| 	}
 | |
| 	
 | |
| 	return weightedSum / totalWeight
 | |
| }
 | |
| 
 | |
| // CalculatePercentile calculates percentile from a slice of values
 | |
| func (su *ScoreUtils) CalculatePercentile(values []float64, percentile int) float64 {
 | |
| 	if len(values) == 0 {
 | |
| 		return 0.0
 | |
| 	}
 | |
| 	
 | |
| 	sorted := make([]float64, len(values))
 | |
| 	copy(sorted, values)
 | |
| 	sort.Float64s(sorted)
 | |
| 	
 | |
| 	if percentile <= 0 {
 | |
| 		return sorted[0]
 | |
| 	}
 | |
| 	if percentile >= 100 {
 | |
| 		return sorted[len(sorted)-1]
 | |
| 	}
 | |
| 	
 | |
| 	index := float64(percentile) / 100.0 * float64(len(sorted)-1)
 | |
| 	lower := int(math.Floor(index))
 | |
| 	upper := int(math.Ceil(index))
 | |
| 	
 | |
| 	if lower == upper {
 | |
| 		return sorted[lower]
 | |
| 	}
 | |
| 	
 | |
| 	// Linear interpolation
 | |
| 	lowerValue := sorted[lower]
 | |
| 	upperValue := sorted[upper]
 | |
| 	weight := index - float64(lower)
 | |
| 	
 | |
| 	return lowerValue + weight*(upperValue-lowerValue)
 | |
| }
 | |
| 
 | |
| // CalculateStandardDeviation calculates standard deviation
 | |
| func (su *ScoreUtils) CalculateStandardDeviation(values []float64) float64 {
 | |
| 	if len(values) <= 1 {
 | |
| 		return 0.0
 | |
| 	}
 | |
| 	
 | |
| 	// Calculate mean
 | |
| 	sum := 0.0
 | |
| 	for _, value := range values {
 | |
| 		sum += value
 | |
| 	}
 | |
| 	mean := sum / float64(len(values))
 | |
| 	
 | |
| 	// Calculate variance
 | |
| 	variance := 0.0
 | |
| 	for _, value := range values {
 | |
| 		diff := value - mean
 | |
| 		variance += diff * diff
 | |
| 	}
 | |
| 	variance /= float64(len(values) - 1)
 | |
| 	
 | |
| 	return math.Sqrt(variance)
 | |
| }
 | |
| 
 | |
| // FileUtils provides file system utilities
 | |
| type FileUtils struct{}
 | |
| 
 | |
| // NewFileUtils creates new file utilities
 | |
| func NewFileUtils() *FileUtils {
 | |
| 	return &FileUtils{}
 | |
| }
 | |
| 
 | |
| // GetFileSize returns file size in bytes
 | |
| func (fu *FileUtils) GetFileSize(filePath string) (int64, error) {
 | |
| 	info, err := os.Stat(filePath)
 | |
| 	if err != nil {
 | |
| 		return 0, err
 | |
| 	}
 | |
| 	return info.Size(), nil
 | |
| }
 | |
| 
 | |
| // GetFileModTime returns file modification time
 | |
| func (fu *FileUtils) GetFileModTime(filePath string) (time.Time, error) {
 | |
| 	info, err := os.Stat(filePath)
 | |
| 	if err != nil {
 | |
| 		return time.Time{}, err
 | |
| 	}
 | |
| 	return info.ModTime(), nil
 | |
| }
 | |
| 
 | |
| // IsTextFile determines if a file is a text file
 | |
| func (fu *FileUtils) IsTextFile(filePath string) bool {
 | |
| 	ext := strings.ToLower(filepath.Ext(filePath))
 | |
| 	textExtensions := map[string]bool{
 | |
| 		".txt": true, ".md": true, ".go": true, ".py": true, ".js": true,
 | |
| 		".jsx": true, ".ts": true, ".tsx": true, ".java": true, ".cpp": true,
 | |
| 		".c": true, ".cs": true, ".php": true, ".rb": true, ".rs": true,
 | |
| 		".swift": true, ".kt": true, ".scala": true, ".clj": true, ".hs": true,
 | |
| 		".ml": true, ".json": true, ".xml": true, ".yaml": true, ".yml": true,
 | |
| 		".toml": true, ".ini": true, ".cfg": true, ".conf": true, ".html": true,
 | |
| 		".css": true, ".scss": true, ".sass": true, ".less": true, ".sql": true,
 | |
| 		".sh": true, ".bat": true, ".ps1": true, ".dockerfile": true,
 | |
| 	}
 | |
| 	return textExtensions[ext]
 | |
| }
 | |
| 
 | |
| // WalkDirectory recursively walks a directory
 | |
| func (fu *FileUtils) WalkDirectory(rootPath string, handler func(path string, info os.FileInfo) error) error {
 | |
| 	return filepath.Walk(rootPath, func(path string, info os.FileInfo, err error) error {
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		return handler(path, info)
 | |
| 	})
 | |
| }
 | |
| 
 | |
| // StringUtils provides string manipulation utilities
 | |
| type StringUtils struct{}
 | |
| 
 | |
| // NewStringUtils creates new string utilities
 | |
| func NewStringUtils() *StringUtils {
 | |
| 	return &StringUtils{}
 | |
| }
 | |
| 
 | |
| // Similarity calculates string similarity using Jaccard index
 | |
| func (su *StringUtils) Similarity(s1, s2 string) float64 {
 | |
| 	if s1 == s2 {
 | |
| 		return 1.0
 | |
| 	}
 | |
| 	
 | |
| 	words1 := strings.Fields(strings.ToLower(s1))
 | |
| 	words2 := strings.Fields(strings.ToLower(s2))
 | |
| 	
 | |
| 	if len(words1) == 0 && len(words2) == 0 {
 | |
| 		return 1.0
 | |
| 	}
 | |
| 	
 | |
| 	if len(words1) == 0 || len(words2) == 0 {
 | |
| 		return 0.0
 | |
| 	}
 | |
| 	
 | |
| 	set1 := make(map[string]bool)
 | |
| 	set2 := make(map[string]bool)
 | |
| 	
 | |
| 	for _, word := range words1 {
 | |
| 		set1[word] = true
 | |
| 	}
 | |
| 	for _, word := range words2 {
 | |
| 		set2[word] = true
 | |
| 	}
 | |
| 	
 | |
| 	intersection := 0
 | |
| 	for word := range set1 {
 | |
| 		if set2[word] {
 | |
| 			intersection++
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	union := len(set1) + len(set2) - intersection
 | |
| 	
 | |
| 	if union == 0 {
 | |
| 		return 1.0
 | |
| 	}
 | |
| 	
 | |
| 	return float64(intersection) / float64(union)
 | |
| }
 | |
| 
 | |
| // ExtractKeywords extracts keywords from text
 | |
| func (su *StringUtils) ExtractKeywords(text string, minLength int) []string {
 | |
| 	// Common stop words
 | |
| 	stopWords := map[string]bool{
 | |
| 		"a": true, "an": true, "and": true, "are": true, "as": true, "at": true,
 | |
| 		"be": true, "by": true, "for": true, "from": true, "has": true, "he": true,
 | |
| 		"in": true, "is": true, "it": true, "its": true, "of": true, "on": true,
 | |
| 		"that": true, "the": true, "to": true, "was": true, "will": true, "with": true,
 | |
| 		"this": true, "these": true, "they": true, "them": true, "their": true,
 | |
| 		"have": true, "had": true, "been": true, "were": true, "said": true,
 | |
| 		"what": true, "when": true, "where": true, "who": true, "which": true, "why": true,
 | |
| 		"how": true, "all": true, "any": true, "both": true, "each": true, "few": true,
 | |
| 		"more": true, "most": true, "other": true, "some": true, "such": true,
 | |
| 		"no": true, "nor": true, "not": true, "only": true, "own": true, "same": true,
 | |
| 		"so": true, "than": true, "too": true, "very": true, "can": true, "could": true,
 | |
| 		"should": true, "would": true, "use": true, "used": true, "using": true,
 | |
| 	}
 | |
| 	
 | |
| 	// Extract words
 | |
| 	wordRegex := regexp.MustCompile(`\b[a-zA-Z]+\b`)
 | |
| 	words := wordRegex.FindAllString(strings.ToLower(text), -1)
 | |
| 	
 | |
| 	keywords := []string{}
 | |
| 	wordFreq := make(map[string]int)
 | |
| 	
 | |
| 	for _, word := range words {
 | |
| 		if len(word) >= minLength && !stopWords[word] {
 | |
| 			wordFreq[word]++
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	// Sort by frequency and return top keywords
 | |
| 	type wordCount struct {
 | |
| 		word  string
 | |
| 		count int
 | |
| 	}
 | |
| 	
 | |
| 	var sortedWords []wordCount
 | |
| 	for word, count := range wordFreq {
 | |
| 		sortedWords = append(sortedWords, wordCount{word, count})
 | |
| 	}
 | |
| 	
 | |
| 	sort.Slice(sortedWords, func(i, j int) bool {
 | |
| 		return sortedWords[i].count > sortedWords[j].count
 | |
| 	})
 | |
| 	
 | |
| 	maxKeywords := 20
 | |
| 	for i, wc := range sortedWords {
 | |
| 		if i >= maxKeywords {
 | |
| 			break
 | |
| 		}
 | |
| 		keywords = append(keywords, wc.word)
 | |
| 	}
 | |
| 	
 | |
| 	return keywords
 | |
| }
 | |
| 
 | |
| // TruncateText truncates text to specified length with ellipsis
 | |
| func (su *StringUtils) TruncateText(text string, maxLength int) string {
 | |
| 	if len(text) <= maxLength {
 | |
| 		return text
 | |
| 	}
 | |
| 	if maxLength <= 3 {
 | |
| 		return text[:maxLength]
 | |
| 	}
 | |
| 	return text[:maxLength-3] + "..."
 | |
| }
 | |
| 
 | |
| // HashUtils provides hashing utilities
 | |
| type HashUtils struct{}
 | |
| 
 | |
| // NewHashUtils creates new hash utilities
 | |
| func NewHashUtils() *HashUtils {
 | |
| 	return &HashUtils{}
 | |
| }
 | |
| 
 | |
| // MD5Hash calculates MD5 hash of string
 | |
| func (hu *HashUtils) MD5Hash(text string) string {
 | |
| 	hasher := md5.New()
 | |
| 	hasher.Write([]byte(text))
 | |
| 	return hex.EncodeToString(hasher.Sum(nil))
 | |
| }
 | |
| 
 | |
| // GenerateID generates a random ID
 | |
| func (hu *HashUtils) GenerateID(prefix string) string {
 | |
| 	bytes := make([]byte, 8)
 | |
| 	rand.Read(bytes)
 | |
| 	return fmt.Sprintf("%s_%x_%d", prefix, bytes, time.Now().Unix())
 | |
| }
 | |
| 
 | |
| // TimeUtils provides time-related utilities
 | |
| type TimeUtils struct{}
 | |
| 
 | |
| // NewTimeUtils creates new time utilities
 | |
| func NewTimeUtils() *TimeUtils {
 | |
| 	return &TimeUtils{}
 | |
| }
 | |
| 
 | |
| // FormatDuration formats duration in human-readable format
 | |
| func (tu *TimeUtils) FormatDuration(duration time.Duration) string {
 | |
| 	if duration < time.Microsecond {
 | |
| 		return fmt.Sprintf("%dns", duration.Nanoseconds())
 | |
| 	}
 | |
| 	if duration < time.Millisecond {
 | |
| 		return fmt.Sprintf("%.1fμs", float64(duration.Nanoseconds())/1000.0)
 | |
| 	}
 | |
| 	if duration < time.Second {
 | |
| 		return fmt.Sprintf("%.1fms", float64(duration.Nanoseconds())/1000000.0)
 | |
| 	}
 | |
| 	if duration < time.Minute {
 | |
| 		return fmt.Sprintf("%.1fs", duration.Seconds())
 | |
| 	}
 | |
| 	if duration < time.Hour {
 | |
| 		return fmt.Sprintf("%.1fm", duration.Minutes())
 | |
| 	}
 | |
| 	return fmt.Sprintf("%.1fh", duration.Hours())
 | |
| }
 | |
| 
 | |
| // IsWithinTimeRange checks if time is within range
 | |
| func (tu *TimeUtils) IsWithinTimeRange(t, start, end time.Time) bool {
 | |
| 	return (t.Equal(start) || t.After(start)) && (t.Equal(end) || t.Before(end))
 | |
| }
 | |
| 
 | |
| // JSONUtils provides JSON utilities
 | |
| type JSONUtils struct{}
 | |
| 
 | |
| // NewJSONUtils creates new JSON utilities
 | |
| func NewJSONUtils() *JSONUtils {
 | |
| 	return &JSONUtils{}
 | |
| }
 | |
| 
 | |
| // PrettyPrint formats JSON with indentation
 | |
| func (ju *JSONUtils) PrettyPrint(data interface{}) (string, error) {
 | |
| 	bytes, err := json.MarshalIndent(data, "", "  ")
 | |
| 	if err != nil {
 | |
| 		return "", err
 | |
| 	}
 | |
| 	return string(bytes), nil
 | |
| }
 | |
| 
 | |
| // DeepCopy creates a deep copy of a JSON-serializable object
 | |
| func (ju *JSONUtils) DeepCopy(src, dst interface{}) error {
 | |
| 	bytes, err := json.Marshal(src)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	return json.Unmarshal(bytes, dst)
 | |
| }
 | |
| 
 | |
| // ValidationUtils provides validation utilities
 | |
| type ValidationUtils struct{}
 | |
| 
 | |
| // NewValidationUtils creates new validation utilities
 | |
| func NewValidationUtils() *ValidationUtils {
 | |
| 	return &ValidationUtils{}
 | |
| }
 | |
| 
 | |
| // IsValidEmail validates email address
 | |
| func (vu *ValidationUtils) IsValidEmail(email string) bool {
 | |
| 	emailRegex := regexp.MustCompile(`^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$`)
 | |
| 	return emailRegex.MatchString(email)
 | |
| }
 | |
| 
 | |
| // IsValidURL validates URL
 | |
| func (vu *ValidationUtils) IsValidURL(url string) bool {
 | |
| 	urlRegex := regexp.MustCompile(`^https?://[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,}(/.*)?$`)
 | |
| 	return urlRegex.MatchString(url)
 | |
| }
 | |
| 
 | |
| // IsValidPath validates file path
 | |
| func (vu *ValidationUtils) IsValidPath(path string) bool {
 | |
| 	if path == "" {
 | |
| 		return false
 | |
| 	}
 | |
| 	// Check for invalid characters
 | |
| 	invalidChars := []string{"<", ">", ":", "\"", "|", "?", "*"}
 | |
| 	for _, char := range invalidChars {
 | |
| 		if strings.Contains(path, char) {
 | |
| 			return false
 | |
| 		}
 | |
| 	}
 | |
| 	return true
 | |
| }
 | |
| 
 | |
| // Context utilities
 | |
| 
 | |
| // CloneContextNode creates a deep copy of a context node
 | |
| func CloneContextNode(node *slurpContext.ContextNode) *slurpContext.ContextNode {
 | |
| 	if node == nil {
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	clone := &slurpContext.ContextNode{
 | |
| 		Path:                  node.Path,
 | |
| 		Summary:               node.Summary,
 | |
| 		Purpose:               node.Purpose,
 | |
| 		Technologies:          make([]string, len(node.Technologies)),
 | |
| 		Tags:                  make([]string, len(node.Tags)),
 | |
| 		Insights:              make([]string, len(node.Insights)),
 | |
| 		CreatedAt:             node.CreatedAt,
 | |
| 		UpdatedAt:             node.UpdatedAt,
 | |
| 		ContextSpecificity:    node.ContextSpecificity,
 | |
| 		RAGConfidence:         node.RAGConfidence,
 | |
| 		ProcessedForRole:      node.ProcessedForRole,
 | |
| 	}
 | |
| 
 | |
| 	copy(clone.Technologies, node.Technologies)
 | |
| 	copy(clone.Tags, node.Tags)
 | |
| 	copy(clone.Insights, node.Insights)
 | |
| 
 | |
| 	if node.RoleSpecificInsights != nil {
 | |
| 		clone.RoleSpecificInsights = make([]*RoleSpecificInsight, len(node.RoleSpecificInsights))
 | |
| 		copy(clone.RoleSpecificInsights, node.RoleSpecificInsights)
 | |
| 	}
 | |
| 
 | |
| 	if node.Metadata != nil {
 | |
| 		clone.Metadata = make(map[string]interface{})
 | |
| 		for k, v := range node.Metadata {
 | |
| 			clone.Metadata[k] = v
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return clone
 | |
| }
 | |
| 
 | |
| // MergeContextNodes merges multiple context nodes into one
 | |
| func MergeContextNodes(nodes ...*slurpContext.ContextNode) *slurpContext.ContextNode {
 | |
| 	if len(nodes) == 0 {
 | |
| 		return nil
 | |
| 	}
 | |
| 	if len(nodes) == 1 {
 | |
| 		return CloneContextNode(nodes[0])
 | |
| 	}
 | |
| 
 | |
| 	merged := CloneContextNode(nodes[0])
 | |
| 	
 | |
| 	for i := 1; i < len(nodes); i++ {
 | |
| 		node := nodes[i]
 | |
| 		if node == nil {
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		// Merge technologies
 | |
| 		merged.Technologies = mergeStringSlices(merged.Technologies, node.Technologies)
 | |
| 		
 | |
| 		// Merge tags
 | |
| 		merged.Tags = mergeStringSlices(merged.Tags, node.Tags)
 | |
| 		
 | |
| 		// Merge insights
 | |
| 		merged.Insights = mergeStringSlices(merged.Insights, node.Insights)
 | |
| 		
 | |
| 		// Use most recent timestamps
 | |
| 		if node.CreatedAt.Before(merged.CreatedAt) {
 | |
| 			merged.CreatedAt = node.CreatedAt
 | |
| 		}
 | |
| 		if node.UpdatedAt.After(merged.UpdatedAt) {
 | |
| 			merged.UpdatedAt = node.UpdatedAt
 | |
| 		}
 | |
| 		
 | |
| 		// Average context specificity
 | |
| 		merged.ContextSpecificity = (merged.ContextSpecificity + node.ContextSpecificity) / 2
 | |
| 		
 | |
| 		// Average RAG confidence
 | |
| 		merged.RAGConfidence = (merged.RAGConfidence + node.RAGConfidence) / 2
 | |
| 		
 | |
| 		// Merge metadata
 | |
| 		if node.Metadata != nil {
 | |
| 			if merged.Metadata == nil {
 | |
| 				merged.Metadata = make(map[string]interface{})
 | |
| 			}
 | |
| 			for k, v := range node.Metadata {
 | |
| 				merged.Metadata[k] = v
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return merged
 | |
| }
 | |
| 
 | |
| // Helper functions
 | |
| 
 | |
| func removeDuplicates(slice []string) []string {
 | |
| 	seen := make(map[string]bool)
 | |
| 	result := []string{}
 | |
| 	for _, item := range slice {
 | |
| 		if !seen[item] {
 | |
| 			seen[item] = true
 | |
| 			result = append(result, item)
 | |
| 		}
 | |
| 	}
 | |
| 	return result
 | |
| }
 | |
| 
 | |
| func mergeStringSlices(slice1, slice2 []string) []string {
 | |
| 	merged := make([]string, len(slice1))
 | |
| 	copy(merged, slice1)
 | |
| 	
 | |
| 	for _, item := range slice2 {
 | |
| 		found := false
 | |
| 		for _, existing := range merged {
 | |
| 			if existing == item {
 | |
| 				found = true
 | |
| 				break
 | |
| 			}
 | |
| 		}
 | |
| 		if !found {
 | |
| 			merged = append(merged, item)
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	return merged
 | |
| }
 | |
| 
 | |
| // MathUtils provides mathematical utilities
 | |
| type MathUtils struct{}
 | |
| 
 | |
| // NewMathUtils creates new math utilities
 | |
| func NewMathUtils() *MathUtils {
 | |
| 	return &MathUtils{}
 | |
| }
 | |
| 
 | |
| // Clamp clamps value between min and max
 | |
| func (mu *MathUtils) Clamp(value, min, max float64) float64 {
 | |
| 	return math.Max(min, math.Min(max, value))
 | |
| }
 | |
| 
 | |
| // Lerp performs linear interpolation between a and b
 | |
| func (mu *MathUtils) Lerp(a, b, t float64) float64 {
 | |
| 	return a + t*(b-a)
 | |
| }
 | |
| 
 | |
| // RoundToDecimalPlaces rounds to specified decimal places
 | |
| func (mu *MathUtils) RoundToDecimalPlaces(value float64, places int) float64 {
 | |
| 	multiplier := math.Pow(10, float64(places))
 | |
| 	return math.Round(value*multiplier) / multiplier
 | |
| }
 | |
| 
 | |
| // SafeDivide performs division with zero-check
 | |
| func (mu *MathUtils) SafeDivide(numerator, denominator float64) float64 {
 | |
| 	if math.Abs(denominator) < 1e-10 {
 | |
| 		return 0.0
 | |
| 	}
 | |
| 	return numerator / denominator
 | |
| }
 | |
| 
 | |
| // InRange checks if value is in range (inclusive)
 | |
| func (mu *MathUtils) InRange(value, min, max float64) bool {
 | |
| 	return value >= min && value <= max
 | |
| }
 | |
| 
 | |
| // ParseUtils provides parsing utilities
 | |
| type ParseUtils struct{}
 | |
| 
 | |
| // NewParseUtils creates new parse utilities
 | |
| func NewParseUtils() *ParseUtils {
 | |
| 	return &ParseUtils{}
 | |
| }
 | |
| 
 | |
| // ParseFloat safely parses float with default
 | |
| func (pu *ParseUtils) ParseFloat(s string, defaultValue float64) float64 {
 | |
| 	if value, err := strconv.ParseFloat(s, 64); err == nil {
 | |
| 		return value
 | |
| 	}
 | |
| 	return defaultValue
 | |
| }
 | |
| 
 | |
| // ParseInt safely parses int with default
 | |
| func (pu *ParseUtils) ParseInt(s string, defaultValue int) int {
 | |
| 	if value, err := strconv.Atoi(s); err == nil {
 | |
| 		return value
 | |
| 	}
 | |
| 	return defaultValue
 | |
| }
 | |
| 
 | |
| // ParseBool safely parses bool with default
 | |
| func (pu *ParseUtils) ParseBool(s string, defaultValue bool) bool {
 | |
| 	if value, err := strconv.ParseBool(s); err == nil {
 | |
| 		return value
 | |
| 	}
 | |
| 	return defaultValue
 | |
| }
 | |
| 
 | |
| // ParseDuration safely parses duration with default
 | |
| func (pu *ParseUtils) ParseDuration(s string, defaultValue time.Duration) time.Duration {
 | |
| 	if value, err := time.ParseDuration(s); err == nil {
 | |
| 		return value
 | |
| 	}
 | |
| 	return defaultValue
 | |
| }
 | |
| 
 | |
| // ParseTime safely parses time with default
 | |
| func (pu *ParseUtils) ParseTime(s, layout string, defaultValue time.Time) time.Time {
 | |
| 	if value, err := time.Parse(layout, s); err == nil {
 | |
| 		return value
 | |
| 	}
 | |
| 	return defaultValue
 | |
| }
 | |
| 
 | |
| // ConversionUtils provides type conversion utilities
 | |
| type ConversionUtils struct{}
 | |
| 
 | |
| // NewConversionUtils creates new conversion utilities
 | |
| func NewConversionUtils() *ConversionUtils {
 | |
| 	return &ConversionUtils{}
 | |
| }
 | |
| 
 | |
| // ToString converts various types to string
 | |
| func (cu *ConversionUtils) ToString(value interface{}) string {
 | |
| 	switch v := value.(type) {
 | |
| 	case string:
 | |
| 		return v
 | |
| 	case int, int8, int16, int32, int64:
 | |
| 		return fmt.Sprintf("%d", v)
 | |
| 	case uint, uint8, uint16, uint32, uint64:
 | |
| 		return fmt.Sprintf("%d", v)
 | |
| 	case float32, float64:
 | |
| 		return fmt.Sprintf("%g", v)
 | |
| 	case bool:
 | |
| 		return fmt.Sprintf("%t", v)
 | |
| 	case time.Time:
 | |
| 		return v.Format(time.RFC3339)
 | |
| 	case time.Duration:
 | |
| 		return v.String()
 | |
| 	default:
 | |
| 		return fmt.Sprintf("%v", v)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // ToStringSlice converts interface{} to []string
 | |
| func (cu *ConversionUtils) ToStringSlice(value interface{}) []string {
 | |
| 	switch v := value.(type) {
 | |
| 	case []string:
 | |
| 		return v
 | |
| 	case []interface{}:
 | |
| 		result := make([]string, len(v))
 | |
| 		for i, item := range v {
 | |
| 			result[i] = cu.ToString(item)
 | |
| 		}
 | |
| 		return result
 | |
| 	case string:
 | |
| 		return []string{v}
 | |
| 	default:
 | |
| 		return []string{cu.ToString(v)}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // ByteUtils provides byte manipulation utilities
 | |
| type ByteUtils struct{}
 | |
| 
 | |
| // NewByteUtils creates new byte utilities
 | |
| func NewByteUtils() *ByteUtils {
 | |
| 	return &ByteUtils{}
 | |
| }
 | |
| 
 | |
| // FormatBytes formats bytes in human-readable format
 | |
| func (bu *ByteUtils) FormatBytes(bytes int64) string {
 | |
| 	const unit = 1024
 | |
| 	if bytes < unit {
 | |
| 		return fmt.Sprintf("%d B", bytes)
 | |
| 	}
 | |
| 	div, exp := int64(unit), 0
 | |
| 	for n := bytes / unit; n >= unit; n /= unit {
 | |
| 		div *= unit
 | |
| 		exp++
 | |
| 	}
 | |
| 	return fmt.Sprintf("%.1f %cB", float64(bytes)/float64(div), "KMGTPE"[exp])
 | |
| }
 | |
| 
 | |
| // ReadFileWithLimit reads file with size limit
 | |
| func (bu *ByteUtils) ReadFileWithLimit(filename string, maxSize int64) ([]byte, error) {
 | |
| 	file, err := os.Open(filename)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	defer file.Close()
 | |
| 
 | |
| 	stat, err := file.Stat()
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	if stat.Size() > maxSize {
 | |
| 		return nil, fmt.Errorf("file too large: %d bytes (limit: %d)", stat.Size(), maxSize)
 | |
| 	}
 | |
| 
 | |
| 	return io.ReadAll(file)
 | |
| } |