Complete BZZZ functionality port to CHORUS
🎭 CHORUS now contains full BZZZ functionality adapted for containers Core systems ported: - P2P networking (libp2p with DHT and PubSub) - Task coordination (COOEE protocol) - HMMM collaborative reasoning - SHHH encryption and security - SLURP admin election system - UCXL content addressing - UCXI server integration - Hypercore logging system - Health monitoring and graceful shutdown - License validation with KACHING Container adaptations: - Environment variable configuration (no YAML files) - Container-optimized logging to stdout/stderr - Auto-generated agent IDs for container deployments - Docker-first architecture All proven BZZZ P2P protocols, AI integration, and collaboration features are now available in containerized form. Next: Build and test container deployment. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
871
pkg/slurp/intelligence/file_analyzer.go
Normal file
871
pkg/slurp/intelligence/file_analyzer.go
Normal file
@@ -0,0 +1,871 @@
|
||||
package intelligence
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DefaultFileAnalyzer provides comprehensive file analysis capabilities
|
||||
type DefaultFileAnalyzer struct {
|
||||
config *EngineConfig
|
||||
languageDetector *LanguageDetector
|
||||
structureAnalyzer *CodeStructureAnalyzer
|
||||
metadataExtractor *MetadataExtractor
|
||||
}
|
||||
|
||||
// LanguageDetector detects programming languages from file content and extensions
|
||||
type LanguageDetector struct {
|
||||
extensionMap map[string]string
|
||||
signatureRegexs map[string][]*regexp.Regexp
|
||||
}
|
||||
|
||||
// CodeStructureAnalyzer analyzes code structure and patterns
|
||||
type CodeStructureAnalyzer struct {
|
||||
languagePatterns map[string]*LanguagePatterns
|
||||
}
|
||||
|
||||
// LanguagePatterns contains regex patterns for different language constructs
|
||||
type LanguagePatterns struct {
|
||||
Functions []*regexp.Regexp
|
||||
Classes []*regexp.Regexp
|
||||
Variables []*regexp.Regexp
|
||||
Imports []*regexp.Regexp
|
||||
Comments []*regexp.Regexp
|
||||
TODOs []*regexp.Regexp
|
||||
}
|
||||
|
||||
// MetadataExtractor extracts file system metadata
|
||||
type MetadataExtractor struct {
|
||||
mimeTypes map[string]string
|
||||
}
|
||||
|
||||
// NewDefaultFileAnalyzer creates a new file analyzer with comprehensive language support
|
||||
func NewDefaultFileAnalyzer(config *EngineConfig) *DefaultFileAnalyzer {
|
||||
return &DefaultFileAnalyzer{
|
||||
config: config,
|
||||
languageDetector: NewLanguageDetector(),
|
||||
structureAnalyzer: NewCodeStructureAnalyzer(),
|
||||
metadataExtractor: NewMetadataExtractor(),
|
||||
}
|
||||
}
|
||||
|
||||
// NewLanguageDetector creates a language detector with extensive language support
|
||||
func NewLanguageDetector() *LanguageDetector {
|
||||
detector := &LanguageDetector{
|
||||
extensionMap: make(map[string]string),
|
||||
signatureRegexs: make(map[string][]*regexp.Regexp),
|
||||
}
|
||||
|
||||
// Map file extensions to languages
|
||||
extensions := map[string]string{
|
||||
".go": "go",
|
||||
".py": "python",
|
||||
".js": "javascript",
|
||||
".jsx": "javascript",
|
||||
".ts": "typescript",
|
||||
".tsx": "typescript",
|
||||
".java": "java",
|
||||
".c": "c",
|
||||
".cpp": "cpp",
|
||||
".cc": "cpp",
|
||||
".cxx": "cpp",
|
||||
".h": "c",
|
||||
".hpp": "cpp",
|
||||
".cs": "csharp",
|
||||
".php": "php",
|
||||
".rb": "ruby",
|
||||
".rs": "rust",
|
||||
".kt": "kotlin",
|
||||
".swift": "swift",
|
||||
".m": "objective-c",
|
||||
".mm": "objective-c",
|
||||
".scala": "scala",
|
||||
".clj": "clojure",
|
||||
".hs": "haskell",
|
||||
".ex": "elixir",
|
||||
".exs": "elixir",
|
||||
".erl": "erlang",
|
||||
".lua": "lua",
|
||||
".pl": "perl",
|
||||
".r": "r",
|
||||
".sh": "shell",
|
||||
".bash": "shell",
|
||||
".zsh": "shell",
|
||||
".fish": "shell",
|
||||
".sql": "sql",
|
||||
".html": "html",
|
||||
".htm": "html",
|
||||
".css": "css",
|
||||
".scss": "scss",
|
||||
".sass": "sass",
|
||||
".less": "less",
|
||||
".xml": "xml",
|
||||
".json": "json",
|
||||
".yaml": "yaml",
|
||||
".yml": "yaml",
|
||||
".toml": "toml",
|
||||
".ini": "ini",
|
||||
".cfg": "ini",
|
||||
".conf": "config",
|
||||
".md": "markdown",
|
||||
".rst": "rst",
|
||||
".tex": "latex",
|
||||
".proto": "protobuf",
|
||||
".tf": "terraform",
|
||||
".hcl": "hcl",
|
||||
".dockerfile": "dockerfile",
|
||||
".dockerignore": "dockerignore",
|
||||
".gitignore": "gitignore",
|
||||
".vim": "vim",
|
||||
".emacs": "emacs",
|
||||
}
|
||||
|
||||
for ext, lang := range extensions {
|
||||
detector.extensionMap[ext] = lang
|
||||
}
|
||||
|
||||
// Language signature patterns
|
||||
signatures := map[string][]string{
|
||||
"go": {
|
||||
`^package\s+\w+`,
|
||||
`^import\s*\(`,
|
||||
`func\s+\w+\s*\(`,
|
||||
},
|
||||
"python": {
|
||||
`^#!/usr/bin/env python`,
|
||||
`^#!/usr/bin/python`,
|
||||
`^import\s+\w+`,
|
||||
`^from\s+\w+\s+import`,
|
||||
`^def\s+\w+\s*\(`,
|
||||
`^class\s+\w+`,
|
||||
},
|
||||
"javascript": {
|
||||
`^#!/usr/bin/env node`,
|
||||
`function\s+\w+\s*\(`,
|
||||
`const\s+\w+\s*=`,
|
||||
`let\s+\w+\s*=`,
|
||||
`var\s+\w+\s*=`,
|
||||
`require\s*\(`,
|
||||
`import\s+.*from`,
|
||||
},
|
||||
"typescript": {
|
||||
`interface\s+\w+`,
|
||||
`type\s+\w+\s*=`,
|
||||
`class\s+\w+`,
|
||||
`import\s+.*from.*\.ts`,
|
||||
},
|
||||
"java": {
|
||||
`^package\s+[\w\.]+;`,
|
||||
`^import\s+[\w\.]+;`,
|
||||
`public\s+class\s+\w+`,
|
||||
`public\s+static\s+void\s+main`,
|
||||
},
|
||||
"rust": {
|
||||
`^use\s+\w+`,
|
||||
`fn\s+\w+\s*\(`,
|
||||
`struct\s+\w+`,
|
||||
`impl\s+\w+`,
|
||||
`extern\s+crate`,
|
||||
},
|
||||
"cpp": {
|
||||
`^#include\s*<.*>`,
|
||||
`^#include\s*".*"`,
|
||||
`using\s+namespace`,
|
||||
`class\s+\w+`,
|
||||
`template\s*<`,
|
||||
},
|
||||
}
|
||||
|
||||
for lang, patterns := range signatures {
|
||||
regexes := make([]*regexp.Regexp, len(patterns))
|
||||
for i, pattern := range patterns {
|
||||
regexes[i] = regexp.MustCompile(pattern)
|
||||
}
|
||||
detector.signatureRegexs[lang] = regexes
|
||||
}
|
||||
|
||||
return detector
|
||||
}
|
||||
|
||||
// NewCodeStructureAnalyzer creates a code structure analyzer
|
||||
func NewCodeStructureAnalyzer() *CodeStructureAnalyzer {
|
||||
analyzer := &CodeStructureAnalyzer{
|
||||
languagePatterns: make(map[string]*LanguagePatterns),
|
||||
}
|
||||
|
||||
// Define patterns for different languages
|
||||
patterns := map[string]*LanguagePatterns{
|
||||
"go": {
|
||||
Functions: []*regexp.Regexp{
|
||||
regexp.MustCompile(`func\s+(\w+)\s*\(`),
|
||||
regexp.MustCompile(`func\s+\(\w+\s+\*?\w+\)\s+(\w+)\s*\(`),
|
||||
},
|
||||
Classes: []*regexp.Regexp{
|
||||
regexp.MustCompile(`type\s+(\w+)\s+struct`),
|
||||
regexp.MustCompile(`type\s+(\w+)\s+interface`),
|
||||
},
|
||||
Variables: []*regexp.Regexp{
|
||||
regexp.MustCompile(`var\s+(\w+)`),
|
||||
regexp.MustCompile(`(\w+)\s*:=`),
|
||||
},
|
||||
Imports: []*regexp.Regexp{
|
||||
regexp.MustCompile(`import\s+"([^"]+)"`),
|
||||
regexp.MustCompile(`import\s+\w+\s+"([^"]+)"`),
|
||||
},
|
||||
Comments: []*regexp.Regexp{
|
||||
regexp.MustCompile(`//\s*(.*)`),
|
||||
regexp.MustCompile(`/\*([^*]|\*(?!/))*\*/`),
|
||||
},
|
||||
TODOs: []*regexp.Regexp{
|
||||
regexp.MustCompile(`//\s*TODO:?\s*(.*)`),
|
||||
regexp.MustCompile(`//\s*FIXME:?\s*(.*)`),
|
||||
regexp.MustCompile(`//\s*HACK:?\s*(.*)`),
|
||||
},
|
||||
},
|
||||
"python": {
|
||||
Functions: []*regexp.Regexp{
|
||||
regexp.MustCompile(`def\s+(\w+)\s*\(`),
|
||||
regexp.MustCompile(`async\s+def\s+(\w+)\s*\(`),
|
||||
},
|
||||
Classes: []*regexp.Regexp{
|
||||
regexp.MustCompile(`class\s+(\w+)\s*[\(:]`),
|
||||
},
|
||||
Variables: []*regexp.Regexp{
|
||||
regexp.MustCompile(`(\w+)\s*=`),
|
||||
},
|
||||
Imports: []*regexp.Regexp{
|
||||
regexp.MustCompile(`import\s+(\w+)`),
|
||||
regexp.MustCompile(`from\s+(\w+)\s+import`),
|
||||
},
|
||||
Comments: []*regexp.Regexp{
|
||||
regexp.MustCompile(`#\s*(.*)`),
|
||||
regexp.MustCompile(`"""([^"]|"(?!""))*"""`),
|
||||
regexp.MustCompile(`'''([^']|'(?!''))*'''`),
|
||||
},
|
||||
TODOs: []*regexp.Regexp{
|
||||
regexp.MustCompile(`#\s*TODO:?\s*(.*)`),
|
||||
regexp.MustCompile(`#\s*FIXME:?\s*(.*)`),
|
||||
},
|
||||
},
|
||||
"javascript": {
|
||||
Functions: []*regexp.Regexp{
|
||||
regexp.MustCompile(`function\s+(\w+)\s*\(`),
|
||||
regexp.MustCompile(`(\w+)\s*:\s*function\s*\(`),
|
||||
regexp.MustCompile(`const\s+(\w+)\s*=\s*\([^)]*\)\s*=>`),
|
||||
regexp.MustCompile(`(\w+)\s*=\s*\([^)]*\)\s*=>`),
|
||||
},
|
||||
Classes: []*regexp.Regexp{
|
||||
regexp.MustCompile(`class\s+(\w+)`),
|
||||
},
|
||||
Variables: []*regexp.Regexp{
|
||||
regexp.MustCompile(`var\s+(\w+)`),
|
||||
regexp.MustCompile(`let\s+(\w+)`),
|
||||
regexp.MustCompile(`const\s+(\w+)`),
|
||||
},
|
||||
Imports: []*regexp.Regexp{
|
||||
regexp.MustCompile(`import\s+.*from\s+['"]([^'"]+)['"]`),
|
||||
regexp.MustCompile(`require\s*\(\s*['"]([^'"]+)['"]`),
|
||||
},
|
||||
Comments: []*regexp.Regexp{
|
||||
regexp.MustCompile(`//\s*(.*)`),
|
||||
regexp.MustCompile(`/\*([^*]|\*(?!/))*\*/`),
|
||||
},
|
||||
TODOs: []*regexp.Regexp{
|
||||
regexp.MustCompile(`//\s*TODO:?\s*(.*)`),
|
||||
regexp.MustCompile(`//\s*FIXME:?\s*(.*)`),
|
||||
},
|
||||
},
|
||||
"java": {
|
||||
Functions: []*regexp.Regexp{
|
||||
regexp.MustCompile(`(?:public|private|protected|static|\s)*\w+\s+(\w+)\s*\(`),
|
||||
},
|
||||
Classes: []*regexp.Regexp{
|
||||
regexp.MustCompile(`(?:public|private|protected|\s)*class\s+(\w+)`),
|
||||
regexp.MustCompile(`(?:public|private|protected|\s)*interface\s+(\w+)`),
|
||||
},
|
||||
Variables: []*regexp.Regexp{
|
||||
regexp.MustCompile(`(?:public|private|protected|static|final|\s)*\w+\s+(\w+)\s*[=;]`),
|
||||
},
|
||||
Imports: []*regexp.Regexp{
|
||||
regexp.MustCompile(`import\s+([\w\.]+);`),
|
||||
},
|
||||
Comments: []*regexp.Regexp{
|
||||
regexp.MustCompile(`//\s*(.*)`),
|
||||
regexp.MustCompile(`/\*([^*]|\*(?!/))*\*/`),
|
||||
},
|
||||
TODOs: []*regexp.Regexp{
|
||||
regexp.MustCompile(`//\s*TODO:?\s*(.*)`),
|
||||
regexp.MustCompile(`//\s*FIXME:?\s*(.*)`),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for lang, pattern := range patterns {
|
||||
analyzer.languagePatterns[lang] = pattern
|
||||
}
|
||||
|
||||
return analyzer
|
||||
}
|
||||
|
||||
// NewMetadataExtractor creates a metadata extractor
|
||||
func NewMetadataExtractor() *MetadataExtractor {
|
||||
return &MetadataExtractor{
|
||||
mimeTypes: map[string]string{
|
||||
".txt": "text/plain",
|
||||
".md": "text/markdown",
|
||||
".json": "application/json",
|
||||
".xml": "application/xml",
|
||||
".html": "text/html",
|
||||
".css": "text/css",
|
||||
".js": "application/javascript",
|
||||
".pdf": "application/pdf",
|
||||
".png": "image/png",
|
||||
".jpg": "image/jpeg",
|
||||
".gif": "image/gif",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// AnalyzeContent performs comprehensive analysis of file content
|
||||
func (fa *DefaultFileAnalyzer) AnalyzeContent(ctx context.Context, filePath string, content []byte) (*FileAnalysis, error) {
|
||||
analysis := &FileAnalysis{
|
||||
FilePath: filePath,
|
||||
Size: int64(len(content)),
|
||||
LineCount: countLines(content),
|
||||
Dependencies: []string{},
|
||||
Exports: []string{},
|
||||
Imports: []string{},
|
||||
Functions: []string{},
|
||||
Classes: []string{},
|
||||
Variables: []string{},
|
||||
Comments: []string{},
|
||||
TODOs: []string{},
|
||||
Metadata: make(map[string]interface{}),
|
||||
AnalyzedAt: time.Now(),
|
||||
}
|
||||
|
||||
// Detect language
|
||||
language, confidence, err := fa.DetectLanguage(ctx, filePath, content)
|
||||
if err != nil {
|
||||
language = "unknown"
|
||||
confidence = 0.0
|
||||
}
|
||||
analysis.Language = language
|
||||
analysis.LanguageConf = confidence
|
||||
|
||||
// Extract metadata
|
||||
metadata, err := fa.ExtractMetadata(ctx, filePath)
|
||||
if err == nil {
|
||||
analysis.FileType = metadata.Extension
|
||||
analysis.Metadata["mime_type"] = metadata.MimeType
|
||||
analysis.Metadata["permissions"] = metadata.Permissions
|
||||
analysis.Metadata["mod_time"] = metadata.ModTime
|
||||
}
|
||||
|
||||
// Analyze structure if it's a known programming language
|
||||
if patterns, exists := fa.structureAnalyzer.languagePatterns[language]; exists {
|
||||
fa.analyzeCodeStructure(analysis, content, patterns)
|
||||
}
|
||||
|
||||
// Calculate complexity
|
||||
analysis.Complexity = fa.calculateComplexity(analysis)
|
||||
|
||||
return analysis, nil
|
||||
}
|
||||
|
||||
// DetectLanguage detects programming language from content and file extension
|
||||
func (fa *DefaultFileAnalyzer) DetectLanguage(ctx context.Context, filePath string, content []byte) (string, float64, error) {
|
||||
ext := strings.ToLower(filepath.Ext(filePath))
|
||||
|
||||
// First try extension-based detection
|
||||
if lang, exists := fa.languageDetector.extensionMap[ext]; exists {
|
||||
confidence := 0.8 // High confidence for extension-based detection
|
||||
|
||||
// Verify with content signatures
|
||||
if signatures, hasSignatures := fa.languageDetector.signatureRegexs[lang]; hasSignatures {
|
||||
matches := 0
|
||||
for _, regex := range signatures {
|
||||
if regex.Match(content) {
|
||||
matches++
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust confidence based on signature matches
|
||||
if matches > 0 {
|
||||
confidence = 0.9 + float64(matches)/float64(len(signatures))*0.1
|
||||
} else {
|
||||
confidence = 0.6 // Lower confidence if no signatures match
|
||||
}
|
||||
}
|
||||
|
||||
return lang, confidence, nil
|
||||
}
|
||||
|
||||
// Fall back to content-based detection
|
||||
bestLang := "unknown"
|
||||
bestScore := 0
|
||||
|
||||
for lang, signatures := range fa.languageDetector.signatureRegexs {
|
||||
score := 0
|
||||
for _, regex := range signatures {
|
||||
if regex.Match(content) {
|
||||
score++
|
||||
}
|
||||
}
|
||||
|
||||
if score > bestScore {
|
||||
bestScore = score
|
||||
bestLang = lang
|
||||
}
|
||||
}
|
||||
|
||||
confidence := float64(bestScore) / 5.0 // Normalize to 0-1 range
|
||||
if confidence > 1.0 {
|
||||
confidence = 1.0
|
||||
}
|
||||
|
||||
return bestLang, confidence, nil
|
||||
}
|
||||
|
||||
// ExtractMetadata extracts file system metadata
|
||||
func (fa *DefaultFileAnalyzer) ExtractMetadata(ctx context.Context, filePath string) (*FileMetadata, error) {
|
||||
info, err := os.Stat(filePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get file info: %w", err)
|
||||
}
|
||||
|
||||
ext := filepath.Ext(filePath)
|
||||
mimeType := fa.metadataExtractor.mimeTypes[strings.ToLower(ext)]
|
||||
if mimeType == "" {
|
||||
mimeType = "application/octet-stream"
|
||||
}
|
||||
|
||||
metadata := &FileMetadata{
|
||||
Path: filePath,
|
||||
Size: info.Size(),
|
||||
ModTime: info.ModTime(),
|
||||
Mode: uint32(info.Mode()),
|
||||
IsDir: info.IsDir(),
|
||||
Extension: ext,
|
||||
MimeType: mimeType,
|
||||
Permissions: info.Mode().String(),
|
||||
}
|
||||
|
||||
return metadata, nil
|
||||
}
|
||||
|
||||
// AnalyzeStructure analyzes code structure and organization
|
||||
func (fa *DefaultFileAnalyzer) AnalyzeStructure(ctx context.Context, filePath string, content []byte) (*StructureAnalysis, error) {
|
||||
analysis := &StructureAnalysis{
|
||||
Architecture: "unknown",
|
||||
Patterns: []string{},
|
||||
Components: []*Component{},
|
||||
Relationships: []*Relationship{},
|
||||
Complexity: &ComplexityMetrics{},
|
||||
QualityMetrics: &QualityMetrics{},
|
||||
TestCoverage: 0.0,
|
||||
Documentation: &DocMetrics{},
|
||||
AnalyzedAt: time.Now(),
|
||||
}
|
||||
|
||||
// Detect language
|
||||
language, _, err := fa.DetectLanguage(ctx, filePath, content)
|
||||
if err != nil {
|
||||
return analysis, fmt.Errorf("failed to detect language: %w", err)
|
||||
}
|
||||
|
||||
// Analyze based on language patterns
|
||||
if patterns, exists := fa.structureAnalyzer.languagePatterns[language]; exists {
|
||||
fa.analyzeArchitecturalPatterns(analysis, content, patterns, language)
|
||||
}
|
||||
|
||||
return analysis, nil
|
||||
}
|
||||
|
||||
// IdentifyPurpose identifies the primary purpose of the file
|
||||
func (fa *DefaultFileAnalyzer) IdentifyPurpose(ctx context.Context, analysis *FileAnalysis) (string, float64, error) {
|
||||
purpose := "General purpose file"
|
||||
confidence := 0.5
|
||||
|
||||
// Purpose identification based on file patterns
|
||||
filename := filepath.Base(analysis.FilePath)
|
||||
filenameUpper := strings.ToUpper(filename)
|
||||
|
||||
// Configuration files
|
||||
if strings.Contains(filenameUpper, "CONFIG") ||
|
||||
strings.Contains(filenameUpper, "CONF") ||
|
||||
analysis.FileType == ".ini" || analysis.FileType == ".toml" {
|
||||
purpose = "Configuration management"
|
||||
confidence = 0.9
|
||||
return purpose, confidence, nil
|
||||
}
|
||||
|
||||
// Test files
|
||||
if strings.Contains(filenameUpper, "TEST") ||
|
||||
strings.Contains(filenameUpper, "SPEC") ||
|
||||
strings.HasSuffix(filenameUpper, "_TEST.GO") ||
|
||||
strings.HasSuffix(filenameUpper, "_TEST.PY") {
|
||||
purpose = "Testing and quality assurance"
|
||||
confidence = 0.9
|
||||
return purpose, confidence, nil
|
||||
}
|
||||
|
||||
// Documentation files
|
||||
if analysis.FileType == ".md" || analysis.FileType == ".rst" ||
|
||||
strings.Contains(filenameUpper, "README") ||
|
||||
strings.Contains(filenameUpper, "DOC") {
|
||||
purpose = "Documentation and guidance"
|
||||
confidence = 0.9
|
||||
return purpose, confidence, nil
|
||||
}
|
||||
|
||||
// API files
|
||||
if strings.Contains(filenameUpper, "API") ||
|
||||
strings.Contains(filenameUpper, "ROUTER") ||
|
||||
strings.Contains(filenameUpper, "HANDLER") {
|
||||
purpose = "API endpoint management"
|
||||
confidence = 0.8
|
||||
return purpose, confidence, nil
|
||||
}
|
||||
|
||||
// Database files
|
||||
if strings.Contains(filenameUpper, "DB") ||
|
||||
strings.Contains(filenameUpper, "DATABASE") ||
|
||||
strings.Contains(filenameUpper, "MODEL") ||
|
||||
strings.Contains(filenameUpper, "SCHEMA") {
|
||||
purpose = "Data storage and management"
|
||||
confidence = 0.8
|
||||
return purpose, confidence, nil
|
||||
}
|
||||
|
||||
// UI/Frontend files
|
||||
if analysis.Language == "javascript" || analysis.Language == "typescript" ||
|
||||
strings.Contains(filenameUpper, "COMPONENT") ||
|
||||
strings.Contains(filenameUpper, "VIEW") ||
|
||||
strings.Contains(filenameUpper, "UI") {
|
||||
purpose = "User interface component"
|
||||
confidence = 0.7
|
||||
return purpose, confidence, nil
|
||||
}
|
||||
|
||||
// Service/Business logic
|
||||
if strings.Contains(filenameUpper, "SERVICE") ||
|
||||
strings.Contains(filenameUpper, "BUSINESS") ||
|
||||
strings.Contains(filenameUpper, "LOGIC") {
|
||||
purpose = "Business logic implementation"
|
||||
confidence = 0.7
|
||||
return purpose, confidence, nil
|
||||
}
|
||||
|
||||
// Utility files
|
||||
if strings.Contains(filenameUpper, "UTIL") ||
|
||||
strings.Contains(filenameUpper, "HELPER") ||
|
||||
strings.Contains(filenameUpper, "COMMON") {
|
||||
purpose = "Utility and helper functions"
|
||||
confidence = 0.7
|
||||
return purpose, confidence, nil
|
||||
}
|
||||
|
||||
// Based on functions and structure
|
||||
if len(analysis.Functions) > 5 {
|
||||
purpose = "Multi-function module"
|
||||
confidence = 0.6
|
||||
} else if len(analysis.Classes) > 0 {
|
||||
purpose = "Class-based component"
|
||||
confidence = 0.6
|
||||
} else if len(analysis.Functions) > 0 {
|
||||
purpose = "Functional implementation"
|
||||
confidence = 0.6
|
||||
}
|
||||
|
||||
return purpose, confidence, nil
|
||||
}
|
||||
|
||||
// GenerateSummary generates a concise summary of file content
|
||||
func (fa *DefaultFileAnalyzer) GenerateSummary(ctx context.Context, analysis *FileAnalysis) (string, error) {
|
||||
summary := strings.Builder{}
|
||||
|
||||
// Language and type
|
||||
if analysis.Language != "unknown" {
|
||||
summary.WriteString(fmt.Sprintf("%s", strings.Title(analysis.Language)))
|
||||
} else {
|
||||
summary.WriteString("File")
|
||||
}
|
||||
|
||||
// Size information
|
||||
if analysis.Size > 0 {
|
||||
summary.WriteString(fmt.Sprintf(" (%s)", formatFileSize(analysis.Size)))
|
||||
}
|
||||
|
||||
// Content summary
|
||||
if len(analysis.Functions) > 0 {
|
||||
summary.WriteString(fmt.Sprintf(" with %d function(s)", len(analysis.Functions)))
|
||||
}
|
||||
if len(analysis.Classes) > 0 {
|
||||
summary.WriteString(fmt.Sprintf(" and %d class(es)", len(analysis.Classes)))
|
||||
}
|
||||
if len(analysis.Dependencies) > 0 {
|
||||
summary.WriteString(fmt.Sprintf(", imports %d dependencies", len(analysis.Dependencies)))
|
||||
}
|
||||
|
||||
// Complexity note
|
||||
if analysis.Complexity > 10 {
|
||||
summary.WriteString(" (high complexity)")
|
||||
} else if analysis.Complexity > 5 {
|
||||
summary.WriteString(" (medium complexity)")
|
||||
}
|
||||
|
||||
return summary.String(), nil
|
||||
}
|
||||
|
||||
// ExtractTechnologies identifies technologies used in the file
|
||||
func (fa *DefaultFileAnalyzer) ExtractTechnologies(ctx context.Context, analysis *FileAnalysis) ([]string, error) {
|
||||
technologies := []string{}
|
||||
|
||||
// Add primary language
|
||||
if analysis.Language != "unknown" && analysis.Language != "" {
|
||||
technologies = append(technologies, analysis.Language)
|
||||
}
|
||||
|
||||
// Extract from imports/dependencies
|
||||
for _, dep := range analysis.Imports {
|
||||
if tech := fa.mapImportToTechnology(dep, analysis.Language); tech != "" {
|
||||
technologies = append(technologies, tech)
|
||||
}
|
||||
}
|
||||
|
||||
// Extract from file patterns
|
||||
filename := strings.ToLower(filepath.Base(analysis.FilePath))
|
||||
|
||||
// Framework detection
|
||||
frameworks := map[string]string{
|
||||
"react": "React",
|
||||
"vue": "Vue.js",
|
||||
"angular": "Angular",
|
||||
"express": "Express.js",
|
||||
"django": "Django",
|
||||
"flask": "Flask",
|
||||
"spring": "Spring",
|
||||
"gin": "Gin",
|
||||
"echo": "Echo",
|
||||
"fastapi": "FastAPI",
|
||||
"bootstrap": "Bootstrap",
|
||||
"tailwind": "Tailwind CSS",
|
||||
"material": "Material UI",
|
||||
"antd": "Ant Design",
|
||||
}
|
||||
|
||||
for pattern, tech := range frameworks {
|
||||
if strings.Contains(filename, pattern) {
|
||||
technologies = append(technologies, tech)
|
||||
}
|
||||
}
|
||||
|
||||
// Database detection from file content or names
|
||||
if strings.Contains(filename, "sql") || strings.Contains(filename, "db") {
|
||||
technologies = append(technologies, "SQL")
|
||||
}
|
||||
if strings.Contains(filename, "mongo") {
|
||||
technologies = append(technologies, "MongoDB")
|
||||
}
|
||||
if strings.Contains(filename, "redis") {
|
||||
technologies = append(technologies, "Redis")
|
||||
}
|
||||
|
||||
// Remove duplicates
|
||||
seen := make(map[string]bool)
|
||||
uniqueTech := []string{}
|
||||
for _, tech := range technologies {
|
||||
if !seen[tech] {
|
||||
seen[tech] = true
|
||||
uniqueTech = append(uniqueTech, tech)
|
||||
}
|
||||
}
|
||||
|
||||
return uniqueTech, nil
|
||||
}
|
||||
|
||||
// Helper methods
|
||||
|
||||
func countLines(content []byte) int {
|
||||
return bytes.Count(content, []byte("\n")) + 1
|
||||
}
|
||||
|
||||
func formatFileSize(size int64) string {
|
||||
const unit = 1024
|
||||
if size < unit {
|
||||
return fmt.Sprintf("%d B", size)
|
||||
}
|
||||
div, exp := int64(unit), 0
|
||||
for n := size / unit; n >= unit; n /= unit {
|
||||
div *= unit
|
||||
exp++
|
||||
}
|
||||
return fmt.Sprintf("%.1f %cB", float64(size)/float64(div), "KMGTPE"[exp])
|
||||
}
|
||||
|
||||
func (fa *DefaultFileAnalyzer) analyzeCodeStructure(analysis *FileAnalysis, content []byte, patterns *LanguagePatterns) {
|
||||
contentStr := string(content)
|
||||
|
||||
// Extract functions
|
||||
for _, regex := range patterns.Functions {
|
||||
matches := regex.FindAllStringSubmatch(contentStr, -1)
|
||||
for _, match := range matches {
|
||||
if len(match) > 1 {
|
||||
analysis.Functions = append(analysis.Functions, match[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extract classes
|
||||
for _, regex := range patterns.Classes {
|
||||
matches := regex.FindAllStringSubmatch(contentStr, -1)
|
||||
for _, match := range matches {
|
||||
if len(match) > 1 {
|
||||
analysis.Classes = append(analysis.Classes, match[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extract variables
|
||||
for _, regex := range patterns.Variables {
|
||||
matches := regex.FindAllStringSubmatch(contentStr, -1)
|
||||
for _, match := range matches {
|
||||
if len(match) > 1 {
|
||||
analysis.Variables = append(analysis.Variables, match[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extract imports
|
||||
for _, regex := range patterns.Imports {
|
||||
matches := regex.FindAllStringSubmatch(contentStr, -1)
|
||||
for _, match := range matches {
|
||||
if len(match) > 1 {
|
||||
analysis.Imports = append(analysis.Imports, match[1])
|
||||
analysis.Dependencies = append(analysis.Dependencies, match[1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extract comments
|
||||
for _, regex := range patterns.Comments {
|
||||
matches := regex.FindAllString(contentStr, -1)
|
||||
for _, match := range matches {
|
||||
if len(strings.TrimSpace(match)) > 2 {
|
||||
analysis.Comments = append(analysis.Comments, strings.TrimSpace(match))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extract TODOs
|
||||
for _, regex := range patterns.TODOs {
|
||||
matches := regex.FindAllStringSubmatch(contentStr, -1)
|
||||
for _, match := range matches {
|
||||
if len(match) > 1 {
|
||||
analysis.TODOs = append(analysis.TODOs, strings.TrimSpace(match[1]))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (fa *DefaultFileAnalyzer) calculateComplexity(analysis *FileAnalysis) float64 {
|
||||
complexity := 0.0
|
||||
|
||||
// Base complexity from structure
|
||||
complexity += float64(len(analysis.Functions)) * 1.5
|
||||
complexity += float64(len(analysis.Classes)) * 2.0
|
||||
complexity += float64(len(analysis.Variables)) * 0.5
|
||||
complexity += float64(len(analysis.Dependencies)) * 1.0
|
||||
|
||||
// Line count factor
|
||||
if analysis.LineCount > 500 {
|
||||
complexity += 5.0
|
||||
} else if analysis.LineCount > 200 {
|
||||
complexity += 2.0
|
||||
} else if analysis.LineCount > 100 {
|
||||
complexity += 1.0
|
||||
}
|
||||
|
||||
return complexity
|
||||
}
|
||||
|
||||
func (fa *DefaultFileAnalyzer) analyzeArchitecturalPatterns(analysis *StructureAnalysis, content []byte, patterns *LanguagePatterns, language string) {
|
||||
contentStr := string(content)
|
||||
|
||||
// Detect common architectural patterns
|
||||
if strings.Contains(contentStr, "interface") && language == "go" {
|
||||
analysis.Patterns = append(analysis.Patterns, "Interface Segregation")
|
||||
}
|
||||
if strings.Contains(contentStr, "Factory") {
|
||||
analysis.Patterns = append(analysis.Patterns, "Factory Pattern")
|
||||
}
|
||||
if strings.Contains(contentStr, "Singleton") {
|
||||
analysis.Patterns = append(analysis.Patterns, "Singleton Pattern")
|
||||
}
|
||||
if strings.Contains(contentStr, "Observer") {
|
||||
analysis.Patterns = append(analysis.Patterns, "Observer Pattern")
|
||||
}
|
||||
|
||||
// Architectural style detection
|
||||
if strings.Contains(contentStr, "http.") || strings.Contains(contentStr, "router") {
|
||||
analysis.Architecture = "REST API"
|
||||
} else if strings.Contains(contentStr, "graphql") {
|
||||
analysis.Architecture = "GraphQL"
|
||||
} else if strings.Contains(contentStr, "grpc") || strings.Contains(contentStr, "proto") {
|
||||
analysis.Architecture = "gRPC"
|
||||
} else if len(patterns.Functions) > 0 && len(patterns.Classes) == 0 {
|
||||
analysis.Architecture = "Functional"
|
||||
} else if len(patterns.Classes) > 0 {
|
||||
analysis.Architecture = "Object-Oriented"
|
||||
}
|
||||
}
|
||||
|
||||
func (fa *DefaultFileAnalyzer) mapImportToTechnology(importPath, language string) string {
|
||||
// Technology mapping based on common imports
|
||||
techMap := map[string]string{
|
||||
// Go
|
||||
"gin-gonic/gin": "Gin",
|
||||
"labstack/echo": "Echo",
|
||||
"gorilla/mux": "Gorilla Mux",
|
||||
"gorm.io/gorm": "GORM",
|
||||
"github.com/redis": "Redis",
|
||||
"go.mongodb.org": "MongoDB",
|
||||
|
||||
// Python
|
||||
"django": "Django",
|
||||
"flask": "Flask",
|
||||
"fastapi": "FastAPI",
|
||||
"requests": "HTTP Client",
|
||||
"sqlalchemy": "SQLAlchemy",
|
||||
"pandas": "Pandas",
|
||||
"numpy": "NumPy",
|
||||
"tensorflow": "TensorFlow",
|
||||
"torch": "PyTorch",
|
||||
|
||||
// JavaScript/TypeScript
|
||||
"react": "React",
|
||||
"vue": "Vue.js",
|
||||
"angular": "Angular",
|
||||
"express": "Express.js",
|
||||
"axios": "Axios",
|
||||
"lodash": "Lodash",
|
||||
"moment": "Moment.js",
|
||||
"socket.io": "Socket.IO",
|
||||
}
|
||||
|
||||
for pattern, tech := range techMap {
|
||||
if strings.Contains(strings.ToLower(importPath), pattern) {
|
||||
return tech
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
Reference in New Issue
Block a user