From 7d134f9d0c9e3841e2fba4e824fea3719f2c3591 Mon Sep 17 00:00:00 2001 From: Snider Date: Sun, 8 Feb 2026 21:55:10 +0000 Subject: [PATCH] fix: resolve API signature mismatches after IO migration merge Reconcile callers with actual function signatures after merging IO migration branches. Some functions gained io.Medium params (repos.*), others kept their original signatures (release.*, cache.*, container.*). - Add io.Local to repos.LoadRegistry/FindRegistry/ScanDirectory callers - Remove extra io.Local from release.ConfigExists/LoadConfig/WriteConfig callers - Fix cache.New call (remove nil Medium arg) - Add missing IsCPPProject to build discovery - Add missing fields to mcp.Service struct (subsystems, logger, etc.) - Add DefaultTCPAddr constant to mcp transport - Fix node.go interface check (coreio.Medium, not coreio.Node) - Fix container.linuxkit LoadState/EnsureLogsDir arg counts - Fix vm templates to use package-level functions - Remove unused Medium field from DaemonOptions Co-Authored-By: Virgil --- internal/cmd/ci/cmd_init.go | 5 ++--- internal/cmd/ci/cmd_publish.go | 3 +-- internal/cmd/daemon/cmd.go | 2 -- internal/cmd/dev/cmd_apply.go | 6 +++--- internal/cmd/dev/cmd_file_sync.go | 4 ++-- internal/cmd/docs/cmd_scan.go | 8 ++++---- internal/cmd/pkgcmd/cmd_search.go | 2 +- internal/cmd/setup/cmd_registry.go | 2 +- internal/cmd/vm/cmd_templates.go | 10 ++++------ pkg/agentic/config.go | 2 +- pkg/agentic/context.go | 2 +- pkg/build/buildcmd/cmd_release.go | 5 ++--- pkg/build/discovery.go | 5 +++++ pkg/container/linuxkit.go | 4 ++-- pkg/io/node/node.go | 2 +- pkg/mcp/mcp.go | 21 ++++++++++++++++---- pkg/mcp/transport_tcp.go | 3 +++ pkg/release/config_test.go | 31 +++++++++++++++--------------- 18 files changed, 65 insertions(+), 52 deletions(-) diff --git a/internal/cmd/ci/cmd_init.go b/internal/cmd/ci/cmd_init.go index aa7d022c..59e4958c 100644 --- a/internal/cmd/ci/cmd_init.go +++ b/internal/cmd/ci/cmd_init.go @@ -5,7 +5,6 @@ import ( "github.com/host-uk/core/pkg/cli" "github.com/host-uk/core/pkg/i18n" - "github.com/host-uk/core/pkg/io" "github.com/host-uk/core/pkg/release" ) @@ -18,14 +17,14 @@ func runCIReleaseInit() error { cli.Print("%s %s\n\n", releaseDimStyle.Render(i18n.Label("init")), i18n.T("cmd.ci.init.initializing")) // Check if already initialized - if release.ConfigExists(io.Local, cwd) { + if release.ConfigExists(cwd) { cli.Text(i18n.T("cmd.ci.init.already_initialized")) return nil } // Create release config cfg := release.DefaultConfig() - if err := release.WriteConfig(io.Local, cfg, cwd); err != nil { + if err := release.WriteConfig(cfg, cwd); err != nil { return cli.Err("%s: %w", i18n.T("i18n.fail.create", "config"), err) } diff --git a/internal/cmd/ci/cmd_publish.go b/internal/cmd/ci/cmd_publish.go index 4dc73c2e..23b0c4ef 100644 --- a/internal/cmd/ci/cmd_publish.go +++ b/internal/cmd/ci/cmd_publish.go @@ -7,7 +7,6 @@ import ( "github.com/host-uk/core/pkg/cli" "github.com/host-uk/core/pkg/i18n" - "github.com/host-uk/core/pkg/io" "github.com/host-uk/core/pkg/release" ) @@ -23,7 +22,7 @@ func runCIPublish(dryRun bool, version string, draft, prerelease bool) error { } // Load configuration - cfg, err := release.LoadConfig(io.Local, projectDir) + cfg, err := release.LoadConfig(projectDir) if err != nil { return cli.WrapVerb(err, "load", "config") } diff --git a/internal/cmd/daemon/cmd.go b/internal/cmd/daemon/cmd.go index 1a1ec4aa..0afd8fab 100644 --- a/internal/cmd/daemon/cmd.go +++ b/internal/cmd/daemon/cmd.go @@ -8,7 +8,6 @@ import ( "path/filepath" "github.com/host-uk/core/pkg/cli" - "github.com/host-uk/core/pkg/io" "github.com/host-uk/core/pkg/log" "github.com/host-uk/core/pkg/mcp" ) @@ -118,7 +117,6 @@ func runDaemon(cfg Config) error { // Create daemon with health checks daemon := cli.NewDaemon(cli.DaemonOptions{ - Medium: io.Local, PIDFile: cfg.PIDFile, HealthAddr: cfg.HealthAddr, ShutdownTimeout: 30, diff --git a/internal/cmd/dev/cmd_apply.go b/internal/cmd/dev/cmd_apply.go index 21bd1b0f..4385718f 100644 --- a/internal/cmd/dev/cmd_apply.go +++ b/internal/cmd/dev/cmd_apply.go @@ -15,7 +15,7 @@ import ( "strings" "github.com/host-uk/core/pkg/cli" - "github.com/host-uk/core/pkg/errors" + errors "github.com/host-uk/core/pkg/framework/core" "github.com/host-uk/core/pkg/git" "github.com/host-uk/core/pkg/i18n" "github.com/host-uk/core/pkg/io" @@ -225,12 +225,12 @@ func runApply() error { // getApplyTargetRepos gets repos to apply command to func getApplyTargetRepos() ([]*repos.Repo, error) { // Load registry - registryPath, err := repos.FindRegistry() + registryPath, err := repos.FindRegistry(io.Local) if err != nil { return nil, errors.E("dev.apply", "failed to find registry", err) } - registry, err := repos.LoadRegistry(registryPath) + registry, err := repos.LoadRegistry(io.Local, registryPath) if err != nil { return nil, errors.E("dev.apply", "failed to load registry", err) } diff --git a/internal/cmd/dev/cmd_file_sync.go b/internal/cmd/dev/cmd_file_sync.go index 9eb44fbc..89b603c0 100644 --- a/internal/cmd/dev/cmd_file_sync.go +++ b/internal/cmd/dev/cmd_file_sync.go @@ -207,12 +207,12 @@ func runFileSync(source string) error { // resolveTargetRepos resolves the --to pattern to actual repos func resolveTargetRepos(pattern string) ([]*repos.Repo, error) { // Load registry - registryPath, err := repos.FindRegistry() + registryPath, err := repos.FindRegistry(coreio.Local) if err != nil { return nil, log.E("dev.sync", "failed to find registry", err) } - registry, err := repos.LoadRegistry(registryPath) + registry, err := repos.LoadRegistry(coreio.Local, registryPath) if err != nil { return nil, log.E("dev.sync", "failed to load registry", err) } diff --git a/internal/cmd/docs/cmd_scan.go b/internal/cmd/docs/cmd_scan.go index d88ad278..08eb2eab 100644 --- a/internal/cmd/docs/cmd_scan.go +++ b/internal/cmd/docs/cmd_scan.go @@ -30,22 +30,22 @@ func loadRegistry(registryPath string) (*repos.Registry, string, error) { var registryDir string if registryPath != "" { - reg, err = repos.LoadRegistry(registryPath) + reg, err = repos.LoadRegistry(io.Local, registryPath) if err != nil { return nil, "", cli.Wrap(err, i18n.T("i18n.fail.load", "registry")) } registryDir = filepath.Dir(registryPath) } else { - registryPath, err = repos.FindRegistry() + registryPath, err = repos.FindRegistry(io.Local) if err == nil { - reg, err = repos.LoadRegistry(registryPath) + reg, err = repos.LoadRegistry(io.Local, registryPath) if err != nil { return nil, "", cli.Wrap(err, i18n.T("i18n.fail.load", "registry")) } registryDir = filepath.Dir(registryPath) } else { cwd, _ := os.Getwd() - reg, err = repos.ScanDirectory(cwd) + reg, err = repos.ScanDirectory(io.Local, cwd) if err != nil { return nil, "", cli.Wrap(err, i18n.T("i18n.fail.scan", "directory")) } diff --git a/internal/cmd/pkgcmd/cmd_search.go b/internal/cmd/pkgcmd/cmd_search.go index 4b573b81..778ce358 100644 --- a/internal/cmd/pkgcmd/cmd_search.go +++ b/internal/cmd/pkgcmd/cmd_search.go @@ -74,7 +74,7 @@ func runPkgSearch(org, pattern, repoType string, limit int, refresh bool) error cacheDir = filepath.Join(filepath.Dir(regPath), ".core", "cache") } - c, err := cache.New(nil, cacheDir, 0) + c, err := cache.New(cacheDir, 0) if err != nil { c = nil } diff --git a/internal/cmd/setup/cmd_registry.go b/internal/cmd/setup/cmd_registry.go index a06e10ef..9f3b8b04 100644 --- a/internal/cmd/setup/cmd_registry.go +++ b/internal/cmd/setup/cmd_registry.go @@ -22,7 +22,7 @@ import ( // runRegistrySetup loads a registry from path and runs setup. func runRegistrySetup(ctx context.Context, registryPath, only string, dryRun, all, runBuild bool) error { - reg, err := repos.LoadRegistry(registryPath) + reg, err := repos.LoadRegistry(coreio.Local, registryPath) if err != nil { return fmt.Errorf("failed to load registry: %w", err) } diff --git a/internal/cmd/vm/cmd_templates.go b/internal/cmd/vm/cmd_templates.go index c03253e5..aad7f048 100644 --- a/internal/cmd/vm/cmd_templates.go +++ b/internal/cmd/vm/cmd_templates.go @@ -16,8 +16,6 @@ import ( "github.com/spf13/cobra" ) -var templateManager = container.NewTemplateManager(io.Local) - // addVMTemplatesCommand adds the 'templates' command under vm. func addVMTemplatesCommand(parent *cobra.Command) { templatesCmd := &cobra.Command{ @@ -71,7 +69,7 @@ func addTemplatesVarsCommand(parent *cobra.Command) { } func listTemplates() error { - templates := templateManager.ListTemplates() + templates := container.ListTemplates() if len(templates) == 0 { fmt.Println(i18n.T("cmd.vm.templates.no_templates")) @@ -102,7 +100,7 @@ func listTemplates() error { } func showTemplate(name string) error { - content, err := templateManager.GetTemplate(name) + content, err := container.GetTemplate(name) if err != nil { return err } @@ -114,7 +112,7 @@ func showTemplate(name string) error { } func showTemplateVars(name string) error { - content, err := templateManager.GetTemplate(name) + content, err := container.GetTemplate(name) if err != nil { return err } @@ -151,7 +149,7 @@ func showTemplateVars(name string) error { // RunFromTemplate builds and runs a LinuxKit image from a template. func RunFromTemplate(templateName string, vars map[string]string, runOpts container.RunOptions) error { // Apply template with variables - content, err := templateManager.ApplyTemplate(templateName, vars) + content, err := container.ApplyTemplate(templateName, vars) if err != nil { return fmt.Errorf(i18n.T("common.error.failed", map[string]any{"Action": "apply template"})+": %w", err) } diff --git a/pkg/agentic/config.go b/pkg/agentic/config.go index a0b2f992..c621b086 100644 --- a/pkg/agentic/config.go +++ b/pkg/agentic/config.go @@ -5,7 +5,7 @@ import ( "path/filepath" "strings" - "github.com/host-uk/core/pkg/errors" + errors "github.com/host-uk/core/pkg/framework/core" "github.com/host-uk/core/pkg/io" "gopkg.in/yaml.v3" ) diff --git a/pkg/agentic/context.go b/pkg/agentic/context.go index 7db8e517..2f808a92 100644 --- a/pkg/agentic/context.go +++ b/pkg/agentic/context.go @@ -9,7 +9,7 @@ import ( "regexp" "strings" - "github.com/host-uk/core/pkg/errors" + errors "github.com/host-uk/core/pkg/framework/core" "github.com/host-uk/core/pkg/io" ) diff --git a/pkg/build/buildcmd/cmd_release.go b/pkg/build/buildcmd/cmd_release.go index e08be39b..330c96b3 100644 --- a/pkg/build/buildcmd/cmd_release.go +++ b/pkg/build/buildcmd/cmd_release.go @@ -9,7 +9,6 @@ import ( "github.com/host-uk/core/pkg/cli" "github.com/host-uk/core/pkg/framework/core" "github.com/host-uk/core/pkg/i18n" - "github.com/host-uk/core/pkg/io" "github.com/host-uk/core/pkg/release" ) @@ -51,7 +50,7 @@ func runRelease(ctx context.Context, dryRun bool, version string, draft, prerele } // Check for release config - if !release.ConfigExists(io.Local, projectDir) { + if !release.ConfigExists(projectDir) { cli.Print("%s %s\n", buildErrorStyle.Render(i18n.Label("error")), i18n.T("cmd.build.release.error.no_config"), @@ -61,7 +60,7 @@ func runRelease(ctx context.Context, dryRun bool, version string, draft, prerele } // Load configuration - cfg, err := release.LoadConfig(io.Local, projectDir) + cfg, err := release.LoadConfig(projectDir) if err != nil { return core.E("release", "load config", err) } diff --git a/pkg/build/discovery.go b/pkg/build/discovery.go index ea4ee121..9a2bc844 100644 --- a/pkg/build/discovery.go +++ b/pkg/build/discovery.go @@ -83,6 +83,11 @@ func IsPHPProject(fs io.Medium, dir string) bool { return fileExists(fs, filepath.Join(dir, markerComposer)) } +// IsCPPProject checks if the directory contains a C++ project (CMakeLists.txt). +func IsCPPProject(fs io.Medium, dir string) bool { + return fileExists(fs, filepath.Join(dir, "CMakeLists.txt")) +} + // fileExists checks if a file exists and is not a directory. func fileExists(fs io.Medium, path string) bool { return fs.IsFile(path) diff --git a/pkg/container/linuxkit.go b/pkg/container/linuxkit.go index 1906edb2..e771b33c 100644 --- a/pkg/container/linuxkit.go +++ b/pkg/container/linuxkit.go @@ -27,7 +27,7 @@ func NewLinuxKitManager(m io.Medium) (*LinuxKitManager, error) { return nil, fmt.Errorf("failed to determine state path: %w", err) } - state, err := LoadState(m, statePath) + state, err := LoadState(statePath) if err != nil { return nil, fmt.Errorf("failed to load state: %w", err) } @@ -90,7 +90,7 @@ func (m *LinuxKitManager) Run(ctx context.Context, image string, opts RunOptions } // Ensure logs directory exists - if err := EnsureLogsDir(m.medium); err != nil { + if err := EnsureLogsDir(); err != nil { return nil, fmt.Errorf("failed to create logs directory: %w", err) } diff --git a/pkg/io/node/node.go b/pkg/io/node/node.go index e509327a..3d768692 100644 --- a/pkg/io/node/node.go +++ b/pkg/io/node/node.go @@ -25,7 +25,7 @@ type Node struct { } // compile-time interface check -var _ coreio.Node = (*Node)(nil) +var _ coreio.Medium = (*Node)(nil) // New creates a new, empty Node. func New() *Node { diff --git a/pkg/mcp/mcp.go b/pkg/mcp/mcp.go index 9f07dbc8..79cdbff2 100644 --- a/pkg/mcp/mcp.go +++ b/pkg/mcp/mcp.go @@ -5,20 +5,30 @@ package mcp import ( "context" "fmt" + "net/http" "os" "path/filepath" "strings" "github.com/host-uk/core/pkg/io" + "github.com/host-uk/core/pkg/log" + "github.com/host-uk/core/pkg/process" + "github.com/host-uk/core/pkg/ws" "github.com/modelcontextprotocol/go-sdk/mcp" ) // Service provides a lightweight MCP server with file operations only. // For full GUI features, use the core-gui package. type Service struct { - server *mcp.Server - workspaceRoot string // Root directory for file operations (empty = unrestricted) - medium io.Medium // Filesystem medium for sandboxed operations + server *mcp.Server + workspaceRoot string // Root directory for file operations (empty = unrestricted) + medium io.Medium // Filesystem medium for sandboxed operations + subsystems []Subsystem // Additional subsystems registered via WithSubsystem + logger *log.Logger // Logger for tool execution auditing + processService *process.Service // Process management service (optional) + wsHub *ws.Hub // WebSocket hub for real-time streaming (optional) + wsServer *http.Server // WebSocket HTTP server (optional) + wsAddr string // WebSocket server address } // Option configures a Service. @@ -61,7 +71,10 @@ func New(opts ...Option) (*Service, error) { } server := mcp.NewServer(impl, nil) - s := &Service{server: server} + s := &Service{ + server: server, + logger: log.Default(), + } // Default to current working directory with sandboxed medium cwd, err := os.Getwd() diff --git a/pkg/mcp/transport_tcp.go b/pkg/mcp/transport_tcp.go index 0e6e0f7e..ba37bfb8 100644 --- a/pkg/mcp/transport_tcp.go +++ b/pkg/mcp/transport_tcp.go @@ -12,6 +12,9 @@ import ( "github.com/modelcontextprotocol/go-sdk/mcp" ) +// DefaultTCPAddr is the default address for the MCP TCP server. +const DefaultTCPAddr = "127.0.0.1:9100" + // maxMCPMessageSize is the maximum size for MCP JSON-RPC messages (10 MB). const maxMCPMessageSize = 10 * 1024 * 1024 diff --git a/pkg/release/config_test.go b/pkg/release/config_test.go index 59d47e8f..44f65c0f 100644 --- a/pkg/release/config_test.go +++ b/pkg/release/config_test.go @@ -5,7 +5,6 @@ import ( "path/filepath" "testing" - "github.com/host-uk/core/pkg/io" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -54,7 +53,7 @@ changelog: ` dir := setupConfigTestDir(t, content) - cfg, err := LoadConfig(io.Local, dir) + cfg, err := LoadConfig(dir) require.NoError(t, err) require.NotNil(t, cfg) @@ -77,7 +76,7 @@ changelog: t.Run("returns defaults when config file missing", func(t *testing.T) { dir := t.TempDir() - cfg, err := LoadConfig(io.Local, dir) + cfg, err := LoadConfig(dir) require.NoError(t, err) require.NotNil(t, cfg) @@ -97,7 +96,7 @@ project: ` dir := setupConfigTestDir(t, content) - cfg, err := LoadConfig(io.Local, dir) + cfg, err := LoadConfig(dir) require.NoError(t, err) require.NotNil(t, cfg) @@ -114,7 +113,7 @@ project: t.Run("sets project directory on load", func(t *testing.T) { dir := setupConfigTestDir(t, "version: 1") - cfg, err := LoadConfig(io.Local, dir) + cfg, err := LoadConfig(dir) require.NoError(t, err) assert.Equal(t, dir, cfg.projectDir) }) @@ -129,7 +128,7 @@ project: ` dir := setupConfigTestDir(t, content) - cfg, err := LoadConfig(io.Local, dir) + cfg, err := LoadConfig(dir) assert.Error(t, err) assert.Nil(t, cfg) assert.Contains(t, err.Error(), "failed to parse config file") @@ -146,7 +145,7 @@ project: err = os.Mkdir(configPath, 0755) require.NoError(t, err) - cfg, err := LoadConfig(io.Local, dir) + cfg, err := LoadConfig(dir) assert.Error(t, err) assert.Nil(t, cfg) assert.Contains(t, err.Error(), "failed to read config file") @@ -205,17 +204,17 @@ func TestConfigPath_Good(t *testing.T) { func TestConfigExists_Good(t *testing.T) { t.Run("returns true when config exists", func(t *testing.T) { dir := setupConfigTestDir(t, "version: 1") - assert.True(t, ConfigExists(io.Local, dir)) + assert.True(t, ConfigExists(dir)) }) t.Run("returns false when config missing", func(t *testing.T) { dir := t.TempDir() - assert.False(t, ConfigExists(io.Local, dir)) + assert.False(t, ConfigExists(dir)) }) t.Run("returns false when .core dir missing", func(t *testing.T) { dir := t.TempDir() - assert.False(t, ConfigExists(io.Local, dir)) + assert.False(t, ConfigExists(dir)) }) } @@ -227,14 +226,14 @@ func TestWriteConfig_Good(t *testing.T) { cfg.Project.Name = "testapp" cfg.Project.Repository = "owner/testapp" - err := WriteConfig(io.Local, cfg, dir) + err := WriteConfig(cfg, dir) require.NoError(t, err) // Verify file exists - assert.True(t, ConfigExists(io.Local, dir)) + assert.True(t, ConfigExists(dir)) // Reload and verify - loaded, err := LoadConfig(io.Local, dir) + loaded, err := LoadConfig(dir) require.NoError(t, err) assert.Equal(t, "testapp", loaded.Project.Name) assert.Equal(t, "owner/testapp", loaded.Project.Repository) @@ -244,7 +243,7 @@ func TestWriteConfig_Good(t *testing.T) { dir := t.TempDir() cfg := DefaultConfig() - err := WriteConfig(io.Local, cfg, dir) + err := WriteConfig(cfg, dir) require.NoError(t, err) // Check directory was created @@ -321,7 +320,7 @@ func TestWriteConfig_Bad(t *testing.T) { defer func() { _ = os.Chmod(coreDir, 0755) }() cfg := DefaultConfig() - err = WriteConfig(io.Local, cfg, dir) + err = WriteConfig(cfg, dir) assert.Error(t, err) assert.Contains(t, err.Error(), "failed to write config file") }) @@ -332,7 +331,7 @@ func TestWriteConfig_Bad(t *testing.T) { } // Use a path that doesn't exist and can't be created cfg := DefaultConfig() - err := WriteConfig(io.Local, cfg, "/nonexistent/path/that/cannot/be/created") + err := WriteConfig(cfg, "/nonexistent/path/that/cannot/be/created") assert.Error(t, err) }) }