chore: align slurp config and scaffolding

This commit is contained in:
anthonyrawlins
2025-09-27 21:03:12 +10:00
parent acc4361463
commit 4a77862289
47 changed files with 5133 additions and 4274 deletions

View File

@@ -227,7 +227,7 @@ func (cau *ContentAnalysisUtils) extractGenericIdentifiers(content string) (func
// 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
@@ -236,26 +236,26 @@ func (cau *ContentAnalysisUtils) CalculateComplexity(content, language string) f
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
@@ -269,7 +269,7 @@ func (cau *ContentAnalysisUtils) CalculateComplexity(content, language string) f
}
}
complexity += float64(maxNesting) * 0.2
// Normalize to 0-10 scale
return math.Min(10.0, complexity/10.0)
}
@@ -279,66 +279,66 @@ func (cau *ContentAnalysisUtils) DetectTechnologies(content, filename 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"},
".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"},
".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"},
"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 {
@@ -347,7 +347,7 @@ func (cau *ContentAnalysisUtils) DetectTechnologies(content, filename string) []
}
}
}
return removeDuplicates(technologies)
}
@@ -371,7 +371,7 @@ func (su *ScoreUtils) NormalizeScore(score, min, max float64) float64 {
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 {
@@ -380,11 +380,11 @@ func (su *ScoreUtils) CalculateWeightedScore(scores map[string]float64, weights
weightedSum += score * weight
totalWeight += weight
}
if totalWeight == 0 {
return 0.0
}
return weightedSum / totalWeight
}
@@ -393,31 +393,31 @@ func (su *ScoreUtils) CalculatePercentile(values []float64, percentile int) floa
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)
}
@@ -426,14 +426,14 @@ 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 {
@@ -441,7 +441,7 @@ func (su *ScoreUtils) CalculateStandardDeviation(values []float64) float64 {
variance += diff * diff
}
variance /= float64(len(values) - 1)
return math.Sqrt(variance)
}
@@ -510,41 +510,41 @@ 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)
}
@@ -565,35 +565,35 @@ func (su *StringUtils) ExtractKeywords(text string, minLength int) []string {
"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 {
@@ -601,7 +601,7 @@ func (su *StringUtils) ExtractKeywords(text string, minLength int) []string {
}
keywords = append(keywords, wc.word)
}
return keywords
}
@@ -741,30 +741,58 @@ func CloneContextNode(node *slurpContext.ContextNode) *slurpContext.ContextNode
}
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,
Path: node.Path,
UCXLAddress: node.UCXLAddress,
Summary: node.Summary,
Purpose: node.Purpose,
Technologies: make([]string, len(node.Technologies)),
Tags: make([]string, len(node.Tags)),
Insights: make([]string, len(node.Insights)),
OverridesParent: node.OverridesParent,
ContextSpecificity: node.ContextSpecificity,
AppliesToChildren: node.AppliesToChildren,
AppliesTo: node.AppliesTo,
GeneratedAt: node.GeneratedAt,
UpdatedAt: node.UpdatedAt,
CreatedBy: node.CreatedBy,
WhoUpdated: node.WhoUpdated,
RAGConfidence: node.RAGConfidence,
EncryptedFor: make([]string, len(node.EncryptedFor)),
AccessLevel: node.AccessLevel,
}
copy(clone.Technologies, node.Technologies)
copy(clone.Tags, node.Tags)
copy(clone.Insights, node.Insights)
copy(clone.EncryptedFor, node.EncryptedFor)
if node.RoleSpecificInsights != nil {
clone.RoleSpecificInsights = make([]*RoleSpecificInsight, len(node.RoleSpecificInsights))
copy(clone.RoleSpecificInsights, node.RoleSpecificInsights)
if node.Parent != nil {
parent := *node.Parent
clone.Parent = &parent
}
if len(node.Children) > 0 {
clone.Children = make([]string, len(node.Children))
copy(clone.Children, node.Children)
}
if node.Language != nil {
language := *node.Language
clone.Language = &language
}
if node.Size != nil {
sz := *node.Size
clone.Size = &sz
}
if node.LastModified != nil {
lm := *node.LastModified
clone.LastModified = &lm
}
if node.ContentHash != nil {
hash := *node.ContentHash
clone.ContentHash = &hash
}
if node.Metadata != nil {
clone.Metadata = make(map[string]interface{})
clone.Metadata = make(map[string]interface{}, len(node.Metadata))
for k, v := range node.Metadata {
clone.Metadata[k] = v
}
@@ -783,7 +811,7 @@ func MergeContextNodes(nodes ...*slurpContext.ContextNode) *slurpContext.Context
}
merged := CloneContextNode(nodes[0])
for i := 1; i < len(nodes); i++ {
node := nodes[i]
if node == nil {
@@ -792,27 +820,29 @@ func MergeContextNodes(nodes ...*slurpContext.ContextNode) *slurpContext.Context
// 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
// Use most relevant timestamps
if merged.GeneratedAt.IsZero() {
merged.GeneratedAt = node.GeneratedAt
} else if !node.GeneratedAt.IsZero() && node.GeneratedAt.Before(merged.GeneratedAt) {
merged.GeneratedAt = node.GeneratedAt
}
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 {
@@ -844,7 +874,7 @@ func removeDuplicates(slice []string) []string {
func mergeStringSlices(slice1, slice2 []string) []string {
merged := make([]string, len(slice1))
copy(merged, slice1)
for _, item := range slice2 {
found := false
for _, existing := range merged {
@@ -857,7 +887,7 @@ func mergeStringSlices(slice1, slice2 []string) []string {
merged = append(merged, item)
}
}
return merged
}
@@ -1034,4 +1064,4 @@ func (bu *ByteUtils) ReadFileWithLimit(filename string, maxSize int64) ([]byte,
}
return io.ReadAll(file)
}
}