merged
Some checks failed
Security Scan / security (push) Successful in 9s
Test / test (push) Failing after 2m0s
Security Scan / security (pull_request) Successful in 10s
Test / test (pull_request) Failing after 16m42s

This commit is contained in:
Snider 2026-03-24 10:48:31 +00:00
commit 0669feb69b
6 changed files with 114 additions and 107 deletions

27
go.mod
View file

@ -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
)

30
go.sum
View file

@ -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=

148
main.go
View file

@ -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 {

View file

@ -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"

View file

@ -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.

View file

@ -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"
)