-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbuilder.go
More file actions
113 lines (95 loc) · 3.14 KB
/
Copy pathbuilder.go
File metadata and controls
113 lines (95 loc) · 3.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
package frictionax
import "strings"
// BuildConfig controls the catalog build process.
type BuildConfig struct {
MinHumanCount int
MinAgentCount int
MinTotalCount int
SkipKinds []string
DiffOnly bool
}
// DefaultBuildConfig returns sensible defaults for catalog building.
func DefaultBuildConfig() BuildConfig {
return BuildConfig{
MinHumanCount: 2,
MinAgentCount: 3,
MinTotalCount: 2,
SkipKinds: []string{string(FailureUnknownFlag)},
}
}
// BuildResult contains the output of a catalog build.
type BuildResult struct {
Catalog CatalogData `json:"catalog"`
NewEntries []CommandMapping `json:"new_entries"`
Skipped []SkippedPattern `json:"skipped"`
}
// SkippedPattern records why a pattern was not added to the catalog.
type SkippedPattern struct {
Pattern string `json:"pattern"`
Kind string `json:"kind"`
Reason string `json:"reason"`
}
// Build creates catalog entries from pattern data, deduplicating against an existing catalog.
// It filters patterns by thresholds, skips configured kinds, and produces CommandMapping
// entries with empty Target fields (targets are determined by LLM reasoning in the skill layer).
func Build(patterns []PatternDetail, existing CatalogData, cfg BuildConfig) (*BuildResult, error) {
existingPatterns := make(map[string]bool, len(existing.Commands))
for _, cmd := range existing.Commands {
existingPatterns[strings.ToLower(cmd.Pattern)] = true
}
skipKinds := make(map[string]bool, len(cfg.SkipKinds))
for _, k := range cfg.SkipKinds {
skipKinds[k] = true
}
result := &BuildResult{
Catalog: CatalogData{
Version: existing.Version,
Commands: make([]CommandMapping, len(existing.Commands)),
Tokens: existing.Tokens,
},
NewEntries: []CommandMapping{},
Skipped: []SkippedPattern{},
}
copy(result.Catalog.Commands, existing.Commands)
for _, p := range patterns {
normalized := strings.ToLower(strings.TrimSpace(p.Pattern))
if normalized == "" {
continue
}
if skipKinds[p.Kind] {
result.Skipped = append(result.Skipped, SkippedPattern{
Pattern: p.Pattern, Kind: p.Kind, Reason: "skipped-kind",
})
continue
}
if existingPatterns[normalized] {
result.Skipped = append(result.Skipped, SkippedPattern{
Pattern: p.Pattern, Kind: p.Kind, Reason: "already-in-catalog",
})
continue
}
// pattern qualifies if it meets total threshold AND at least one actor threshold
meetsHuman := cfg.MinHumanCount > 0 && p.HumanCount >= int64(cfg.MinHumanCount)
meetsAgent := cfg.MinAgentCount > 0 && p.AgentCount >= int64(cfg.MinAgentCount)
meetsTotal := p.TotalCount >= int64(cfg.MinTotalCount)
if !meetsTotal || (!meetsHuman && !meetsAgent) {
result.Skipped = append(result.Skipped, SkippedPattern{
Pattern: p.Pattern, Kind: p.Kind, Reason: "below-threshold",
})
continue
}
entry := CommandMapping{
Pattern: p.Pattern,
Count: int(p.TotalCount),
}
result.NewEntries = append(result.NewEntries, entry)
if !cfg.DiffOnly {
result.Catalog.Commands = append(result.Catalog.Commands, entry)
}
existingPatterns[normalized] = true
}
if cfg.DiffOnly {
result.Catalog.Commands = result.NewEntries
}
return result, nil
}