diff --git a/go.mod b/go.mod index 14b7515..fbb528e 100644 --- a/go.mod +++ b/go.mod @@ -3,26 +3,21 @@ module dappco.re/go/core/ide go 1.26.0 require ( + dappco.re/go/core/scm v0.4.0 forge.lthn.ai/core/api v0.1.5 forge.lthn.ai/core/config v0.1.8 forge.lthn.ai/core/go v0.3.3 - forge.lthn.ai/core/go-process v0.2.9 - forge.lthn.ai/core/go-scm v0.3.6 - forge.lthn.ai/core/go-ws v0.2.5 - forge.lthn.ai/core/gui v0.1.5 - forge.lthn.ai/core/mcp v0.3.4 + forge.lthn.ai/core/go-process v0.2.7 + forge.lthn.ai/core/go-ws v0.2.3 + forge.lthn.ai/core/gui v0.1.3 + forge.lthn.ai/core/mcp v0.3.2 + github.com/stretchr/testify v1.11.1 github.com/wailsapp/wails/v3 v3.0.0-alpha.74 ) require ( -<<<<<<< HEAD + forge.lthn.ai/core/go-log v0.0.4 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect -======= - dappco.re/go/core v0.4.7 // indirect - dappco.re/go/core/io v0.1.7 // indirect - dappco.re/go/core/log v0.0.4 // indirect - dappco.re/go/core/process v0.2.9 // indirect ->>>>>>> ff717ef516c6d2261f84b727c409dd050c8e63a6 github.com/dustin/go-humanize v1.0.1 // indirect github.com/ncruces/go-strftime v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect @@ -34,12 +29,13 @@ require ( ) require ( + dappco.re/go/core/io v0.2.0 // indirect + dappco.re/go/core/log v0.1.0 dario.cat/mergo v1.0.2 // indirect - forge.lthn.ai/core/go-ai v0.1.12 // indirect + forge.lthn.ai/core/go-ai v0.1.11 // indirect forge.lthn.ai/core/go-io v0.1.7 // indirect - forge.lthn.ai/core/go-log v0.0.4 // indirect forge.lthn.ai/core/go-rag v0.1.11 // indirect - forge.lthn.ai/core/go-webview v0.1.7 // indirect + forge.lthn.ai/core/go-webview v0.1.5 // indirect github.com/99designs/gqlgen v0.17.88 // indirect github.com/KyleBanks/depth v1.2.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect @@ -183,4 +179,3 @@ require ( gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) - diff --git a/go.sum b/go.sum index eb546c0..29ea80f 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,9 @@ +dappco.re/go/core/io v0.2.0 h1:zuudgIiTsQQ5ipVt97saWdGLROovbEB/zdVyy9/l+I4= +dappco.re/go/core/io v0.2.0/go.mod h1:1QnQV6X9LNgFKfm8SkOtR9LLaj3bDcsOIeJOOyjbL5E= +dappco.re/go/core/log v0.1.0 h1:pa71Vq2TD2aoEUQWFKwNcaJ3GBY8HbaNGqtE688Unyc= +dappco.re/go/core/log v0.1.0/go.mod h1:Nkqb8gsXhZAO8VLpx7B8i1iAmohhzqA20b9Zr8VUcJs= +dappco.re/go/core/scm v0.4.0 h1:Fpi+AcDicezZ82HH6s2yuk6RjBXT0w/kynKHecNpmgc= +dappco.re/go/core/scm v0.4.0/go.mod h1:ufb7si6HBkaT6zC8L67kLm8zzBaD1aQoTn4OsVAM1aI= dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= forge.lthn.ai/core/api v0.1.5 h1:NwZrcOyBjaiz5/cn0n0tnlMUodi8Or6FHMx59C7Kv2o= @@ -6,24 +12,24 @@ forge.lthn.ai/core/config v0.1.8 h1:xP2hys7T94QGVF/OTh84/Zr5Dm/dL/0vzjht8zi+LOg= forge.lthn.ai/core/config v0.1.8/go.mod h1:8epZrkwoCt+5ayrqdinOUU/+w6UoxOyv9ZrdgVOgYfQ= forge.lthn.ai/core/go v0.3.3 h1:kYYZ2nRYy0/Be3cyuLJspRjLqTMxpckVyhb/7Sw2gd0= forge.lthn.ai/core/go v0.3.3/go.mod h1:Cp4ac25pghvO2iqOu59t1GyngTKVOzKB5/VPdhRi9CQ= -forge.lthn.ai/core/go-ai v0.1.12 h1:OHt0bUABlyhvgxZxyMwueRoh8rS3YKWGFY6++zCAwC8= -forge.lthn.ai/core/go-ai v0.1.12/go.mod h1:5Pc9lszxgkO7Aj2Z3dtq4L9Xk9l/VNN+Baj1t///OCM= +forge.lthn.ai/core/go-ai v0.1.11 h1:EJ3XIVg7NcLSPoOCX8I1YGso+uxtVVujafRyShXPAEA= +forge.lthn.ai/core/go-ai v0.1.11/go.mod h1:5Pc9lszxgkO7Aj2Z3dtq4L9Xk9l/VNN+Baj1t///OCM= forge.lthn.ai/core/go-io v0.1.7 h1:Tdb6sqh+zz1lsGJaNX9RFWM6MJ/RhSAyxfulLXrJsbk= forge.lthn.ai/core/go-io v0.1.7/go.mod h1:8lRLFk4Dnp5cR/Cyzh9WclD5566TbpdRgwcH7UZLWn4= forge.lthn.ai/core/go-log v0.0.4 h1:KTuCEPgFmuM8KJfnyQ8vPOU1Jg654W74h8IJvfQMfv0= forge.lthn.ai/core/go-log v0.0.4/go.mod h1:r14MXKOD3LF/sI8XUJQhRk/SZHBE7jAFVuCfgkXoZPw= -forge.lthn.ai/core/go-process v0.2.9 h1:Wql+5TUF+lfU2oJ9I+S764MkTqJhBsuyMM0v1zsfZC4= -forge.lthn.ai/core/go-process v0.2.9/go.mod h1:NIzZOF5IVYYCjHkcNIGcg1mZH+bzGoie4SlZUDYOKIM= +forge.lthn.ai/core/go-process v0.2.7 h1:yl7jOxzDqWpJd/ZvJ/Ff6bHgPFLA1ZYU5UDcsz3AzLM= +forge.lthn.ai/core/go-process v0.2.7/go.mod h1:I6x11UNaZbU3k0FWUaSlPRTE4YZk/lWIjiODm/8Jr9c= forge.lthn.ai/core/go-rag v0.1.11 h1:KXTOtnOdrx8YKmvnj0EOi2EI/+cKjE8w2PpJCQIrSd8= forge.lthn.ai/core/go-rag v0.1.11/go.mod h1:vIlOKVD1SdqqjkJ2XQyXPuKPtiajz/STPLCaDpqOzk8= -forge.lthn.ai/core/go-scm v0.3.6 h1:LFNx8Fs82mrpxro/MPUM6tMiD4DqPmdu83UknXztQjc= -forge.lthn.ai/core/go-scm v0.3.6/go.mod h1:IWFIYDfRH0mtRdqY5zV06l/RkmkPpBM6FcbKWhg1Qa8= -forge.lthn.ai/core/go-webview v0.1.7 h1:9+aEHeAvNcPX8Zwr+UGu0/T+menRm5T1YOmqZ9dawDc= -forge.lthn.ai/core/go-webview v0.1.7/go.mod h1:5n1tECD1wBV/uFZRY9ZjfPFO5TYZrlaR3mQFwvO2nek= -forge.lthn.ai/core/go-ws v0.2.5 h1:ZIV7Yrv01R/xpJUogA5vrfP9yB9li1w7EV3eZFMt8h0= -forge.lthn.ai/core/go-ws v0.2.5/go.mod h1:C3riJyLLcV6QhLvYlq3P/XkGTsN598qQeGBoLdoHBU4= -forge.lthn.ai/core/gui v0.1.5 h1:qelZQQ/6zKvZEKKJ/x9EodjIeFxUW+Z1c7t242U7E3A= -forge.lthn.ai/core/gui v0.1.5/go.mod h1:4lB4gdMbLvNBDrxHkIc+Tmb4KURiKSCDQb555HrPkhc= +forge.lthn.ai/core/go-webview v0.1.5 h1:tr6HJvDLfrF6GoDo0aT/kIdKtZCV9Qky6xI0TI4vEH8= +forge.lthn.ai/core/go-webview v0.1.5/go.mod h1:5n1tECD1wBV/uFZRY9ZjfPFO5TYZrlaR3mQFwvO2nek= +forge.lthn.ai/core/go-ws v0.2.3 h1:qTeMtJQjtTdTwfPvtbOBdch2Dmbde+Aso8Ow1qvg/bk= +forge.lthn.ai/core/go-ws v0.2.3/go.mod h1:C3riJyLLcV6QhLvYlq3P/XkGTsN598qQeGBoLdoHBU4= +forge.lthn.ai/core/gui v0.1.3 h1:73OcHQ3zWC21OxofWG/Jf6kV3TffZB+N3e2zdSDzS0g= +forge.lthn.ai/core/gui v0.1.3/go.mod h1:oW9C/opgYU0j3gIaZTbUIU6GNZo4A1NKsTB9W0I6REI= +forge.lthn.ai/core/mcp v0.3.2 h1:+wtlQolyUJpwaWzWQFBhX5gSfrqLapw4EhLUa3AqR5U= +forge.lthn.ai/core/mcp v0.3.2/go.mod h1:ZKcV57TPUFiLSbOam1nBHlQJo5rlskiJKFV/uJ9Gkco= github.com/99designs/gqlgen v0.17.88 h1:neMQDgehMwT1vYIOx/w5ZYPUU/iMNAJzRO44I5Intoc= github.com/99designs/gqlgen v0.17.88/go.mod h1:qeqYFEgOeSKqWedOjogPizimp2iu4E23bdPvl4jTYic= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= diff --git a/main.go b/main.go index ee8560a..55470d6 100644 --- a/main.go +++ b/main.go @@ -11,6 +11,7 @@ import ( "runtime" "syscall" + "dappco.re/go/core/ide/icons" "forge.lthn.ai/core/api" "forge.lthn.ai/core/api/pkg/provider" "forge.lthn.ai/core/config" @@ -20,7 +21,6 @@ import ( "forge.lthn.ai/core/go/pkg/core" "forge.lthn.ai/core/gui/pkg/display" guiMCP "forge.lthn.ai/core/gui/pkg/mcp" - "forge.lthn.ai/core/ide/icons" "forge.lthn.ai/core/mcp/pkg/mcp" "forge.lthn.ai/core/mcp/pkg/mcp/agentic" "forge.lthn.ai/core/mcp/pkg/mcp/brain" @@ -41,10 +41,7 @@ func main() { } // ── Configuration ────────────────────────────────────────── - cfg, err := config.New() - if err != nil { - log.Printf("config load failed, using defaults: %v", err) - } + cfg, _ := config.New() cwd, err := os.Getwd() if err != nil { @@ -74,15 +71,12 @@ func main() { if addr := os.Getenv("CORE_API_ADDR"); addr != "" { apiAddr = addr } - engine, err := api.New( + engine, _ := api.New( api.WithAddr(apiAddr), api.WithCORS("*"), api.WithWSHandler(http.Handler(hub.Handler())), api.WithSwagger("Core IDE", "Service Provider API", "0.1.0"), ) - if err != nil { - log.Fatalf("failed to create API engine: %v", err) - } reg.MountAll(engine) // ── Runtime Provider Manager ────────────────────────────── @@ -93,49 +87,55 @@ func main() { engine.Register(NewProvidersAPI(reg, rm)) // ── Core framework ───────────────────────────────────────── - c, err := core.New() + c, err := core.New( + core.WithName("ws", func(c *core.Core) (any, error) { + return hub, nil + }), + core.WithService(display.Register(nil)), // nil platform until Wails starts + core.WithName("mcp", func(c *core.Core) (any, error) { + return mcp.New( + mcp.WithWorkspaceRoot(cwd), + mcp.WithWSHub(hub), + mcp.WithSubsystem(brain.NewDirect()), + mcp.WithSubsystem(agentic.NewPrep()), + mcp.WithSubsystem(guiMCP.New(c)), + ) + }), + ) if err != nil { log.Fatalf("failed to create core: %v", err) } - if err := c.RegisterService("ws", hub); err != nil { - log.Fatalf("failed to register ws service: %v", err) - } - - displaySvcAny, err := display.Register(nil)(c) + // Retrieve the MCP service for transport control + mcpSvc, err := core.ServiceFor[*mcp.Service](c, "mcp") if err != nil { - log.Fatalf("failed to create display service: %v", err) - } - if err := c.RegisterService("display", displaySvcAny); err != nil { - log.Fatalf("failed to register display service: %v", err) - } - if ds, ok := displaySvcAny.(*display.Service); ok { - c.RegisterAction(ds.HandleIPCEvents) - } - - mcpSvc, err := mcp.New(mcp.Options{ - WorkspaceRoot: cwd, - WSHub: hub, - Subsystems: []mcp.Subsystem{ - brain.NewDirect(), - agentic.NewPrep(), - guiMCP.New(c), - }, - }) - if err != nil { - log.Fatalf("failed to create MCP service: %v", err) - } - if err := c.RegisterService("mcp", mcpSvc); err != nil { - log.Fatalf("failed to register MCP service: %v", err) + log.Fatalf("failed to get MCP service: %v", err) } // ── Mode selection ───────────────────────────────────────── if mcpOnly { - ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) + // stdio mode — Claude Code connects via --mcp flag + ctx, cancel := signal.NotifyContext(context.Background(), + syscall.SIGINT, syscall.SIGTERM) defer cancel() - startCore(ctx, c, bridge, hub, rm) - go serveAPI(ctx, engine, apiAddr) + if err := c.ServiceStartup(ctx, nil); err != nil { + log.Fatalf("core startup failed: %v", err) + } + bridge.Start(ctx) + go hub.Run(ctx) + + // Start runtime providers + if err := rm.StartAll(ctx); err != nil { + log.Printf("runtime provider error: %v", err) + } + + // Start API server in background for provider endpoints + go func() { + if err := engine.Serve(ctx); err != nil { + log.Printf("API server error: %v", err) + } + }() if err := mcpSvc.ServeStdio(ctx); err != nil { log.Printf("MCP stdio error: %v", err) @@ -148,11 +148,29 @@ func main() { } if !guiEnabled(cfg) { - ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) + // No GUI — run Core with MCP transport in background + ctx, cancel := signal.NotifyContext(context.Background(), + syscall.SIGINT, syscall.SIGTERM) defer cancel() - startCore(ctx, c, bridge, hub, rm) - go serveAPI(ctx, engine, apiAddr) + if err := c.ServiceStartup(ctx, nil); err != nil { + log.Fatalf("core startup failed: %v", err) + } + bridge.Start(ctx) + go hub.Run(ctx) + + // Start runtime providers + if err := rm.StartAll(ctx); err != nil { + log.Printf("runtime provider error: %v", err) + } + + // Start API server + go func() { + log.Printf("API server listening on %s", apiAddr) + if err := engine.Serve(ctx); err != nil { + log.Printf("API server error: %v", err) + } + }() go func() { if err := mcpSvc.Run(ctx); err != nil { @@ -174,8 +192,6 @@ func main() { log.Fatal(err) } - guiCtx, guiCancel := context.WithCancel(context.Background()) - app := application.New(application.Options{ Name: "Core IDE", Description: "Host UK Core IDE - Development Environment", @@ -189,12 +205,10 @@ func main() { ActivationPolicy: application.ActivationPolicyAccessory, }, OnShutdown: func() { - guiCancel() rm.StopAll() ctx := context.Background() _ = mcpSvc.Shutdown(ctx) bridge.Shutdown() - _ = c.ServiceShutdown(ctx) }, }) @@ -256,16 +270,24 @@ func main() { // Start MCP transport, runtime providers, and API server alongside Wails go func() { - bridge.Start(guiCtx) - go hub.Run(guiCtx) + ctx := context.Background() + bridge.Start(ctx) + go hub.Run(ctx) - if err := rm.StartAll(guiCtx); err != nil { + // Start runtime providers + if err := rm.StartAll(ctx); err != nil { log.Printf("runtime provider error: %v", err) } - go serveAPI(guiCtx, engine, apiAddr) + // Start API server + go func() { + log.Printf("API server listening on %s", apiAddr) + if err := engine.Serve(ctx); err != nil { + log.Printf("API server error: %v", err) + } + }() - if err := mcpSvc.Run(guiCtx); err != nil { + if err := mcpSvc.Run(ctx); err != nil { log.Printf("MCP error: %v", err) } }() @@ -277,26 +299,6 @@ func main() { } } -func startCore(ctx context.Context, c *core.Core, bridge *ide.Bridge, hub *ws.Hub, rm *RuntimeManager) { - if err := c.ServiceStartup(ctx, nil); err != nil { - log.Fatalf("core startup failed: %v", err) - } - bridge.Start(ctx) - go hub.Run(ctx) - if err := rm.StartAll(ctx); err != nil { - log.Printf("runtime provider error: %v", err) - } -} - -func serveAPI(ctx context.Context, engine *api.Engine, addr string) { - if addr != "" { - log.Printf("API server listening on %s", addr) - } - if err := engine.Serve(ctx); err != nil { - log.Printf("API server error: %v", err) - } -} - // guiEnabled checks whether the GUI should start. // Returns false if config says gui.enabled: false, or if no display is available. func guiEnabled(cfg *config.Config) bool { diff --git a/providers_test.go b/providers_test.go index 36ab5c4..86b5e34 100644 --- a/providers_test.go +++ b/providers_test.go @@ -6,8 +6,8 @@ import ( "net/http/httptest" "testing" + "dappco.re/go/core/scm/manifest" "forge.lthn.ai/core/api/pkg/provider" - "forge.lthn.ai/core/go-scm/manifest" "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/runtime.go b/runtime.go index 36358a4..f01db98 100644 --- a/runtime.go +++ b/runtime.go @@ -13,11 +13,11 @@ import ( "sync" "time" + coreerr "dappco.re/go/core/log" + "dappco.re/go/core/scm/manifest" + "dappco.re/go/core/scm/marketplace" "forge.lthn.ai/core/api" "forge.lthn.ai/core/api/pkg/provider" - coreerr "forge.lthn.ai/core/go-log" - "forge.lthn.ai/core/go-scm/manifest" - "forge.lthn.ai/core/go-scm/marketplace" "github.com/gin-gonic/gin" ) @@ -230,7 +230,11 @@ func findFreePort() (int, error) { return 0, err } defer l.Close() - return l.Addr().(*net.TCPAddr).Port, nil + tcpAddr, ok := l.Addr().(*net.TCPAddr) + if !ok { + return 0, coreerr.E("runtime.findFreePort", "unexpected address type", nil) + } + return tcpAddr.Port, nil } // waitForHealth polls a health URL until it returns 200 or the timeout expires. diff --git a/runtime_test.go b/runtime_test.go index ae596eb..6b36427 100644 --- a/runtime_test.go +++ b/runtime_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - "forge.lthn.ai/core/go-scm/manifest" + "dappco.re/go/core/scm/manifest" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" )