From 45c994136f7bea863a850a6be3995f81b4b229be Mon Sep 17 00:00:00 2001 From: Lukasz Raczylo Date: Sat, 23 May 2026 02:41:06 +0100 Subject: [PATCH] feat(canonical): introduce core domain types --- go.sum | 5 ++ internal/canonical/types.go | 89 ++++++++++++++++++++++++++++++++ internal/canonical/types_test.go | 28 ++++++++++ 3 files changed, 122 insertions(+) create mode 100644 internal/canonical/types.go create mode 100644 internal/canonical/types_test.go diff --git a/go.sum b/go.sum index 90aef4c..1e256ac 100644 --- a/go.sum +++ b/go.sum @@ -19,6 +19,8 @@ github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0/go.mod github.com/charmbracelet/x/term v0.2.0 h1:cNB9Ot9q8I711MyZ7myUR5HFWL/lc3OpU8jZ4hwm0x0= github.com/charmbracelet/x/term v0.2.0/go.mod h1:GVxgxAbjUrmpvIINHIQnJJKpMlHiZ4cktEQCN6GWyF0= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= @@ -50,6 +52,8 @@ github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a h1:2MaM6YC3mGu54 github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a/go.mod h1:hxSnBBYLK21Vtq/PHd0S2FYCxBXzBua8ov5s1RobyRQ= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= @@ -73,4 +77,5 @@ golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/canonical/types.go b/internal/canonical/types.go new file mode 100644 index 0000000..e3dd18a --- /dev/null +++ b/internal/canonical/types.go @@ -0,0 +1,89 @@ +// Package canonical defines the source-of-truth domain types for harness-sync. +package canonical + +type Config struct { + Paths Paths `yaml:"paths,omitempty"` + ActiveProfile string `yaml:"active_profile"` + EnabledHarnesses []string `yaml:"enabled_harnesses"` +} + +type Paths struct { + Skills string `yaml:"skills,omitempty"` + Agents string `yaml:"agents,omitempty"` + Instructions string `yaml:"instructions,omitempty"` +} + +type Profile struct { + Name string `yaml:"name"` + Gateway Gateway `yaml:"gateway"` + Upstreams []Upstream `yaml:"upstreams,omitempty"` + Models []Model `yaml:"models"` +} + +type Gateway struct { + URL string `yaml:"url"` + Token string `yaml:"token"` + DefaultModel string `yaml:"default_model"` +} + +type Upstream struct { + Name string `yaml:"name"` + APIKey string `yaml:"api_key,omitempty"` + BaseURL string `yaml:"base_url,omitempty"` +} + +type Model struct { + ID string `yaml:"id"` + Alias string `yaml:"alias,omitempty"` +} + +type MCPRegistry struct { + Servers []MCPServer `yaml:"servers"` +} + +type MCPServer struct { + Env map[string]string `yaml:"env,omitempty"` + Name string `yaml:"name"` + Command string `yaml:"command,omitempty"` + URL string `yaml:"url,omitempty"` + Transport string `yaml:"transport,omitempty"` + Args []string `yaml:"args,omitempty"` +} + +type Skill struct { + Name string + Description string + Body string + Path string +} + +type Agent struct { + Name string + Description string + Body string + Path string +} + +type Instructions struct { + PerHarness map[string]string + Global string +} + +type Bundle struct { + Config Config + Instructions Instructions + Root string + Profile Profile + Skills []Skill + Agents []Agent + MCP MCPRegistry +} + +func (p *Profile) LookupModel(alias string) (Model, bool) { + for _, m := range p.Models { + if m.Alias == alias || m.ID == alias { + return m, true + } + } + return Model{}, false +} diff --git a/internal/canonical/types_test.go b/internal/canonical/types_test.go new file mode 100644 index 0000000..8feeff0 --- /dev/null +++ b/internal/canonical/types_test.go @@ -0,0 +1,28 @@ +package canonical + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestProfileActiveModel(t *testing.T) { + p := &Profile{ + Name: "home", + Gateway: Gateway{ + URL: "https://gw", + Token: "dummy", + DefaultModel: "sonnet", + }, + Models: []Model{ + {ID: "claude-sonnet-4-6", Alias: "sonnet"}, + {ID: "claude-opus-4-7", Alias: "opus"}, + }, + } + m, ok := p.LookupModel("sonnet") + assert.True(t, ok) + assert.Equal(t, "claude-sonnet-4-6", m.ID) + + _, ok = p.LookupModel("missing") + assert.False(t, ok) +}