This report summarizes findings from a semantic function clustering analysis of all non-test Go files in the pkg/ directory. Analysis covered 553 files and approximately 2,742 functions across 18 sub-packages.
Executive Summary
The analysis identified 5 concrete refactoring opportunities with medium-to-high impact, including near-duplicate implementations of utility functions, functions that should use existing shared utilities, a file naming inconsistency, and a highly repetitive engine-specific API pattern in domains.go that could be simplified.
| Category |
Count |
Impact |
| Near-duplicate functions (vs. existing utilities) |
2 |
High |
| Thin wrapper functions (trivial indirection) |
1 |
Medium |
| Semver logic scattered across packages |
2 files |
Medium |
| Repetitive engine-specific function pattern |
15 functions |
Medium |
| File naming inconsistency |
1 file |
Low |
Issue 1: truncateString Duplicates stringutil.Truncate
File: pkg/cli/gateway_logs.go
Existing utility: pkg/stringutil/stringutil.go
The function truncateString (private, in gateway_logs.go) is a near-identical reimplementation of the exported stringutil.Truncate:
View Code Comparison
// pkg/cli/gateway_logs.go:378
func truncateString(s string, maxLen int) string {
if len(s) <= maxLen {
return s
}
if maxLen <= 3 {
return s[:maxLen]
}
return s[:maxLen-3] + "..."
}
// pkg/stringutil/stringutil.go:16
func Truncate(s string, maxLen int) string {
if len(s) <= maxLen {
return s
}
if maxLen <= 3 {
return s[:maxLen]
}
...
}
Recommendation: Replace truncateString in gateway_logs.go with stringutil.Truncate. This eliminates the duplicate and ensures consistent truncation behavior across the codebase.
Issue 2: uniqueStrings Duplicates sliceutil.Deduplicate
File: pkg/cli/codemod_mcp_network.go:423
Existing utility: pkg/sliceutil/sliceutil.go
uniqueStrings is a private function that implements the exact same logic as sliceutil.Deduplicate[string] which already exists in the shared utility package:
View Code Comparison
// pkg/cli/codemod_mcp_network.go:423
func uniqueStrings(input []string) []string {
seen := make(map[string]bool)
var result []string
for _, item := range input {
if !seen[item] {
seen[item] = true
result = append(result, item)
}
}
return result
}
// pkg/sliceutil/sliceutil.go
func Deduplicate[T comparable](slice []T) []T {
seen := make(map[T]bool, len(slice))
result := make([]T, 0, len(slice))
for _, item := range slice {
if !seen[item] {
seen[item] = true
result = append(result, item)
}
}
return result
}
Recommendation: Replace uniqueStrings(input) calls in codemod_mcp_network.go with sliceutil.Deduplicate(input). The generic version is more efficient (pre-allocates the result slice) and avoids duplication.
Issue 3: SortStrings is a Trivial Wrapper of sort.Strings
File: pkg/workflow/strings.go:106
SortStrings is a single-line function that only wraps the standard library sort.Strings:
// pkg/workflow/strings.go:106
func SortStrings(s []string) {
sort.Strings(s)
}
This adds a layer of indirection with no benefit — callers could use sort.Strings directly.
Recommendation: Audit callers of SortStrings within pkg/workflow/ and replace them with direct sort.Strings calls, then remove the wrapper. This reduces the public API surface of the workflow package.
Issue 4: Semver Logic Split Across Two Packages
Files: pkg/cli/semver.go and pkg/workflow/semver.go
Both packages define private semver-related functions using the same golang.org/x/mod/semver library, but with non-overlapping functionality that is closely related:
View Function Inventory
pkg/cli/semver.go:
isSemanticVersionTag(ref string) bool — validates if a ref is a semver tag
parseVersion(v string) *semanticVersion — parses a semver string into struct
parseInt(s string) int — parses an integer from string (note: reimplements strconv.Atoi)
(v *semanticVersion) isPreciseVersion() bool — checks if version has minor+patch
(v *semanticVersion) isNewer(other *semanticVersion) bool — compares two versions
pkg/workflow/semver.go:
compareVersions(v1, v2 string) int — compares two version strings
extractMajorVersion(version string) int — extracts the major version number
isSemverCompatible(pinVersion, requestedVersion string) bool — checks major version compatibility
Both files duplicate the same pattern of normalizing version strings with a v prefix before delegating to golang.org/x/mod/semver.
Additionally, parseInt in pkg/cli/semver.go reimplements the functionality of strconv.Atoi without using it.
Recommendation:
- Consider consolidating into a shared
pkg/semverutil package to avoid future duplication as new engines or update-checking logic is added.
- In
pkg/cli/semver.go, replace the parseInt function with strconv.Atoi from the standard library.
Issue 5: Highly Repetitive Engine-Specific Domain Functions
File: pkg/workflow/domains.go (691 lines, 15 GetXxxAllowedDomains functions)
domains.go contains a highly repetitive family of 15 functions that follow an identical pattern — each calls the same set of internal mergeDomainsWithNetwork* helpers, differing only in which engine's default domain constant is used:
View Repetitive Pattern
// Copilot variants
func GetCopilotAllowedDomains(network *NetworkPermissions) string {
return GetCopilotAllowedDomainsWithSafeInputs(network, false)
}
func GetCopilotAllowedDomainsWithSafeInputs(network *NetworkPermissions, hasSafeInputs bool) string {
return mergeDomainsWithNetwork(CopilotDefaultDomains, network)
}
func GetCopilotAllowedDomainsWithTools(network *NetworkPermissions, tools map[string]any) string {
return mergeDomainsWithNetworkAndTools(CopilotDefaultDomains, network, tools)
}
func GetCopilotAllowedDomainsWithToolsAndRuntimes(network *NetworkPermissions, tools map[string]any, runtimes map[string]any) string {
return mergeDomainsWithNetworkToolsAndRuntimes(CopilotDefaultDomains, network, tools, runtimes)
}
// Claude variants (identical pattern, different constant)
func GetClaudeAllowedDomains(network *NetworkPermissions) string {
return GetClaudeAllowedDomainsWithSafeInputs(network, false)
}
func GetClaudeAllowedDomainsWithSafeInputs(network *NetworkPermissions, hasSafeInputs bool) string {
return mergeDomainsWithNetwork(ClaudeDefaultDomains, network)
}
// ... 9 more functions following the same pattern for Codex and Gemini
Each new engine requires adding 3-4 nearly identical functions. Currently there are variants for Copilot, Claude, Codex, and Gemini. The hasSafeInputs parameter in *WithSafeInputs functions is notably unused (both Copilot and Claude variants ignore it in the function body).
Recommendation: Introduce a parameterized or engine-keyed API:
// Option A: single function with engine parameter
func GetAllowedDomainsForEngine(engine CodingAgentEngine, network *NetworkPermissions, tools map[string]any, runtimes map[string]any) string
// Option B: map-based lookup of default domains by engine
var engineDefaultDomains = map[CodingAgentEngine][]string{
EngineClaudeCode: ClaudeDefaultDomains,
EngineCopilot: CopilotDefaultDomains,
// ...
}
This would reduce 15 functions to 1-3, and make adding new engines trivial.
Issue 6: copilot-agents.go File Naming Inconsistency
File: pkg/cli/copilot-agents.go
This is the only file in the entire pkg/ directory that uses a hyphen in its filename. All other files follow the standard Go convention of using underscores (e.g., copilot_agent.go, copilot_setup.go).
Recommendation: Rename pkg/cli/copilot-agents.go to pkg/cli/copilot_agents.go to match Go naming conventions and the rest of the codebase.
Detailed Function Cluster Analysis
View Full Clustering Summary by Package
pkg/workflow (276 files, primary analysis area)
| Cluster |
File Pattern |
Description |
| Validation |
*_validation.go (20+ files) |
Well-organized, validation per feature |
| Creation |
create_*.go (6 files) |
Each creation action in its own file |
| Safe Outputs |
safe_outputs_*.go (10 files) |
Well-organized feature cluster |
| Engine helpers |
*_engine*.go (7 files) |
Engine-specific logic, well-separated |
| MCP config |
mcp_*.go (15 files) |
MCP configuration, well-organized |
| Runtime |
runtime_*.go (6 files) |
Runtime detection and setup |
| Expression |
expression_*.go (6 files) |
Expression building/parsing/validation |
| Bundler |
bundler*.go (7 files) |
JavaScript bundling |
| WASM stubs |
*_wasm.go (8 files) |
Platform stubs with build tags ✓ |
Outliers identified: SortStrings in strings.go (trivial wrapper)
pkg/cli (187 files)
| Cluster |
File Pattern |
Description |
| Compile |
compile_*.go (10 files) |
Workflow compilation |
| Logs |
logs_*.go (10 files) |
Log fetching and display |
| MCP |
mcp_*.go (15 files) |
MCP server management |
| Codemod |
codemod_*.go (15 files) |
Automated code migrations |
| Add |
add_*.go (6 files) |
Workflow addition |
| Update |
update_*.go (7 files) |
Workflow update |
| Run |
run_*.go (5 files) |
Workflow execution |
Outliers identified: truncateString (should use stringutil), uniqueStrings (should use sliceutil)
pkg/parser (37 files)
content_extractor.go uses a well-designed pattern with a generic extractFrontmatterField helper and thin named wrappers per field. This is a good design, not a concern.
Implementation Checklist
Analysis Metadata
|
|
| Total Go Files Analyzed |
553 |
| Total Functions Cataloged |
~2,742 |
| Packages Analyzed |
18 |
| Outliers Found |
6 issues across 7 files |
| Detection Method |
Naming pattern analysis + code comparison + Serena semantic analysis |
| Analysis Date |
2026-02-20 |
| Workflow Run |
§22233602384 |
Generated by Semantic Function Refactoring
This report summarizes findings from a semantic function clustering analysis of all non-test Go files in the
pkg/directory. Analysis covered 553 files and approximately 2,742 functions across 18 sub-packages.Executive Summary
The analysis identified 5 concrete refactoring opportunities with medium-to-high impact, including near-duplicate implementations of utility functions, functions that should use existing shared utilities, a file naming inconsistency, and a highly repetitive engine-specific API pattern in
domains.gothat could be simplified.Issue 1:
truncateStringDuplicatesstringutil.TruncateFile:
pkg/cli/gateway_logs.goExisting utility:
pkg/stringutil/stringutil.goThe function
truncateString(private, ingateway_logs.go) is a near-identical reimplementation of the exportedstringutil.Truncate:View Code Comparison
Recommendation: Replace
truncateStringingateway_logs.gowithstringutil.Truncate. This eliminates the duplicate and ensures consistent truncation behavior across the codebase.Issue 2:
uniqueStringsDuplicatessliceutil.DeduplicateFile:
pkg/cli/codemod_mcp_network.go:423Existing utility:
pkg/sliceutil/sliceutil.gouniqueStringsis a private function that implements the exact same logic assliceutil.Deduplicate[string]which already exists in the shared utility package:View Code Comparison
Recommendation: Replace
uniqueStrings(input)calls incodemod_mcp_network.gowithsliceutil.Deduplicate(input). The generic version is more efficient (pre-allocates the result slice) and avoids duplication.Issue 3:
SortStringsis a Trivial Wrapper ofsort.StringsFile:
pkg/workflow/strings.go:106SortStringsis a single-line function that only wraps the standard librarysort.Strings:This adds a layer of indirection with no benefit — callers could use
sort.Stringsdirectly.Recommendation: Audit callers of
SortStringswithinpkg/workflow/and replace them with directsort.Stringscalls, then remove the wrapper. This reduces the public API surface of theworkflowpackage.Issue 4: Semver Logic Split Across Two Packages
Files:
pkg/cli/semver.goandpkg/workflow/semver.goBoth packages define private semver-related functions using the same
golang.org/x/mod/semverlibrary, but with non-overlapping functionality that is closely related:View Function Inventory
pkg/cli/semver.go:isSemanticVersionTag(ref string) bool— validates if a ref is a semver tagparseVersion(v string) *semanticVersion— parses a semver string into structparseInt(s string) int— parses an integer from string (note: reimplementsstrconv.Atoi)(v *semanticVersion) isPreciseVersion() bool— checks if version has minor+patch(v *semanticVersion) isNewer(other *semanticVersion) bool— compares two versionspkg/workflow/semver.go:compareVersions(v1, v2 string) int— compares two version stringsextractMajorVersion(version string) int— extracts the major version numberisSemverCompatible(pinVersion, requestedVersion string) bool— checks major version compatibilityBoth files duplicate the same pattern of normalizing version strings with a
vprefix before delegating togolang.org/x/mod/semver.Additionally,
parseIntinpkg/cli/semver.goreimplements the functionality ofstrconv.Atoiwithout using it.Recommendation:
pkg/semverutilpackage to avoid future duplication as new engines or update-checking logic is added.pkg/cli/semver.go, replace theparseIntfunction withstrconv.Atoifrom the standard library.Issue 5: Highly Repetitive Engine-Specific Domain Functions
File:
pkg/workflow/domains.go(691 lines, 15GetXxxAllowedDomainsfunctions)domains.gocontains a highly repetitive family of 15 functions that follow an identical pattern — each calls the same set of internalmergeDomainsWithNetwork*helpers, differing only in which engine's default domain constant is used:View Repetitive Pattern
Each new engine requires adding 3-4 nearly identical functions. Currently there are variants for Copilot, Claude, Codex, and Gemini. The
hasSafeInputsparameter in*WithSafeInputsfunctions is notably unused (both Copilot and Claude variants ignore it in the function body).Recommendation: Introduce a parameterized or engine-keyed API:
This would reduce 15 functions to 1-3, and make adding new engines trivial.
Issue 6:
copilot-agents.goFile Naming InconsistencyFile:
pkg/cli/copilot-agents.goThis is the only file in the entire
pkg/directory that uses a hyphen in its filename. All other files follow the standard Go convention of using underscores (e.g.,copilot_agent.go,copilot_setup.go).Recommendation: Rename
pkg/cli/copilot-agents.gotopkg/cli/copilot_agents.goto match Go naming conventions and the rest of the codebase.Detailed Function Cluster Analysis
View Full Clustering Summary by Package
pkg/workflow(276 files, primary analysis area)*_validation.go(20+ files)create_*.go(6 files)safe_outputs_*.go(10 files)*_engine*.go(7 files)mcp_*.go(15 files)runtime_*.go(6 files)expression_*.go(6 files)bundler*.go(7 files)*_wasm.go(8 files)Outliers identified:
SortStringsinstrings.go(trivial wrapper)pkg/cli(187 files)compile_*.go(10 files)logs_*.go(10 files)mcp_*.go(15 files)codemod_*.go(15 files)add_*.go(6 files)update_*.go(7 files)run_*.go(5 files)Outliers identified:
truncateString(should use stringutil),uniqueStrings(should use sliceutil)pkg/parser(37 files)content_extractor.gouses a well-designed pattern with a genericextractFrontmatterFieldhelper and thin named wrappers per field. This is a good design, not a concern.Implementation Checklist
truncateStringinpkg/cli/gateway_logs.gowithstringutil.TruncateuniqueStringsinpkg/cli/codemod_mcp_network.gowithsliceutil.DeduplicateparseIntinpkg/cli/semver.gowithstrconv.AtoiSortStringsfrompkg/workflow/strings.go; usesort.Stringsdirectly at call sitespkg/cli/semver.goandpkg/workflow/semver.gointo a shared utilitypkg/workflow/domains.goto use a single parameterized function; remove unusedhasSafeInputsparameter from*WithSafeInputsvariantspkg/cli/copilot-agents.go→pkg/cli/copilot_agents.goAnalysis Metadata