diff --git a/.gitignore b/.gitignore index 6d203d1..2365340 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,7 @@ .core/ docker/.env ui/node_modules +# Compiled binaries +core-agent +mcp +*.exe diff --git a/core-agent b/core-agent deleted file mode 100755 index 507cea5..0000000 Binary files a/core-agent and /dev/null differ diff --git a/mcp b/mcp deleted file mode 100755 index 45e154d..0000000 Binary files a/mcp and /dev/null differ diff --git a/pkg/agentic/events.go b/pkg/agentic/events.go index ac8d329..57c2273 100644 --- a/pkg/agentic/events.go +++ b/pkg/agentic/events.go @@ -4,7 +4,6 @@ package agentic import ( "encoding/json" - "os" "path/filepath" "time" @@ -24,12 +23,7 @@ type CompletionEvent struct { // emitCompletionEvent appends a completion event to the events log. // The plugin's hook watches this file to notify the orchestrating agent. func emitCompletionEvent(agent, workspace string) { - home, err := os.UserHomeDir() - if err != nil { - return - } - - eventsFile := filepath.Join(home, "Code", "host-uk", "core", ".core", "workspace", "events.jsonl") + eventsFile := filepath.Join(WorkspaceRoot(), "events.jsonl") event := CompletionEvent{ Type: "agent_completed", diff --git a/pkg/agentic/paths.go b/pkg/agentic/paths.go new file mode 100644 index 0000000..4047c9e --- /dev/null +++ b/pkg/agentic/paths.go @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: EUPL-1.2 + +package agentic + +import ( + "os" + "path/filepath" +) + +// WorkspaceRoot returns the root directory for agent workspaces. +// Checks CORE_WORKSPACE env var first, falls back to ~/Code/.core/workspace. +func WorkspaceRoot() string { + return filepath.Join(CoreRoot(), "workspace") +} + +// CoreRoot returns the root directory for core ecosystem files. +// Checks CORE_WORKSPACE env var first, falls back to ~/Code/.core. +func CoreRoot() string { + if root := os.Getenv("CORE_WORKSPACE"); root != "" { + return root + } + home, _ := os.UserHomeDir() + return filepath.Join(home, "Code", ".core") +} + +// PlansRoot returns the root directory for agent plans. +func PlansRoot() string { + return filepath.Join(CoreRoot(), "plans") +} diff --git a/pkg/agentic/plan.go b/pkg/agentic/plan.go index cf4cf4e..4155768 100644 --- a/pkg/agentic/plan.go +++ b/pkg/agentic/plan.go @@ -313,8 +313,7 @@ func (s *PrepSubsystem) planList(_ context.Context, _ *mcp.CallToolRequest, inpu // --- Helpers --- func (s *PrepSubsystem) plansDir() string { - home, _ := os.UserHomeDir() - return filepath.Join(home, "Code", "host-uk", "core", ".core", "plans") + return PlansRoot() } func planPath(dir, id string) string { diff --git a/pkg/agentic/pr.go b/pkg/agentic/pr.go index e200de6..d4f8d9d 100644 --- a/pkg/agentic/pr.go +++ b/pkg/agentic/pr.go @@ -54,8 +54,7 @@ func (s *PrepSubsystem) createPR(ctx context.Context, _ *mcp.CallToolRequest, in return nil, CreatePROutput{}, coreerr.E("createPR", "no Forge token configured", nil) } - home, _ := os.UserHomeDir() - wsDir := filepath.Join(home, "Code", "host-uk", "core", ".core", "workspace", input.Workspace) + wsDir := filepath.Join(WorkspaceRoot(), input.Workspace) srcDir := filepath.Join(wsDir, "src") if _, err := os.Stat(srcDir); err != nil { diff --git a/pkg/agentic/prep.go b/pkg/agentic/prep.go index d9cac80..2e747a0 100644 --- a/pkg/agentic/prep.go +++ b/pkg/agentic/prep.go @@ -58,13 +58,13 @@ func NewPrep() *PrepSubsystem { } return &PrepSubsystem{ - forgeURL: envOr("FORGE_URL", "https://forge.lthn.ai"), - forgeToken: forgeToken, - brainURL: envOr("CORE_BRAIN_URL", "https://api.lthn.sh"), - brainKey: brainKey, - specsPath: envOr("SPECS_PATH", filepath.Join(home, "Code", "host-uk", "specs")), - codePath: envOr("CODE_PATH", filepath.Join(home, "Code")), - client: &http.Client{Timeout: 30 * time.Second}, + forgeURL: envOr("FORGE_URL", "https://forge.lthn.ai"), + forgeToken: forgeToken, + brainURL: envOr("CORE_BRAIN_URL", "https://api.lthn.sh"), + brainKey: brainKey, + specsPath: envOr("SPECS_PATH", filepath.Join(home, "Code", "specs")), + codePath: envOr("CODE_PATH", filepath.Join(home, "Code")), + client: &http.Client{Timeout: 30 * time.Second}, } } @@ -152,8 +152,7 @@ func (s *PrepSubsystem) prepWorkspace(ctx context.Context, _ *mcp.CallToolReques } // Workspace root: .core/workspace/{repo}-{timestamp}/ - home, _ := os.UserHomeDir() - wsRoot := filepath.Join(home, "Code", "host-uk", "core", ".core", "workspace") + wsRoot := WorkspaceRoot() wsName := fmt.Sprintf("%s-%d", input.Repo, time.Now().Unix()) wsDir := filepath.Join(wsRoot, wsName) diff --git a/pkg/agentic/queue.go b/pkg/agentic/queue.go index c3cb4d7..a5d4547 100644 --- a/pkg/agentic/queue.go +++ b/pkg/agentic/queue.go @@ -104,8 +104,7 @@ func (s *PrepSubsystem) delayForAgent(agent string) time.Duration { // countRunningByAgent counts running workspaces for a specific agent type. func (s *PrepSubsystem) countRunningByAgent(agent string) int { - home, _ := os.UserHomeDir() - wsRoot := filepath.Join(home, "Code", "host-uk", "core", ".core", "workspace") + wsRoot := WorkspaceRoot() entries, err := os.ReadDir(wsRoot) if err != nil { @@ -163,8 +162,7 @@ func (s *PrepSubsystem) canDispatch() bool { // drainQueue finds the oldest queued workspace and spawns it if a slot is available. // Applies rate-based delay between spawns. func (s *PrepSubsystem) drainQueue() { - home, _ := os.UserHomeDir() - wsRoot := filepath.Join(home, "Code", "host-uk", "core", ".core", "workspace") + wsRoot := WorkspaceRoot() entries, err := os.ReadDir(wsRoot) if err != nil { diff --git a/pkg/agentic/resume.go b/pkg/agentic/resume.go index fa0f8cd..471474f 100644 --- a/pkg/agentic/resume.go +++ b/pkg/agentic/resume.go @@ -43,8 +43,7 @@ func (s *PrepSubsystem) resume(ctx context.Context, _ *mcp.CallToolRequest, inpu return nil, ResumeOutput{}, coreerr.E("resume", "workspace is required", nil) } - home, _ := os.UserHomeDir() - wsDir := filepath.Join(home, "Code", "host-uk", "core", ".core", "workspace", input.Workspace) + wsDir := filepath.Join(WorkspaceRoot(), input.Workspace) srcDir := filepath.Join(wsDir, "src") // Verify workspace exists diff --git a/pkg/agentic/status.go b/pkg/agentic/status.go index 9e396f3..dbe87ce 100644 --- a/pkg/agentic/status.go +++ b/pkg/agentic/status.go @@ -97,8 +97,7 @@ func (s *PrepSubsystem) registerStatusTool(server *mcp.Server) { } func (s *PrepSubsystem) status(ctx context.Context, _ *mcp.CallToolRequest, input StatusInput) (*mcp.CallToolResult, StatusOutput, error) { - home, _ := os.UserHomeDir() - wsRoot := filepath.Join(home, "Code", "host-uk", "core", ".core", "workspace") + wsRoot := WorkspaceRoot() entries, err := os.ReadDir(wsRoot) if err != nil { diff --git a/pkg/agentic/watch.go b/pkg/agentic/watch.go index b9a7a52..eb34faa 100644 --- a/pkg/agentic/watch.go +++ b/pkg/agentic/watch.go @@ -5,7 +5,6 @@ package agentic import ( "context" "fmt" - "os" "path/filepath" "time" @@ -196,6 +195,5 @@ func (s *PrepSubsystem) resolveWorkspaceDir(name string) string { // workspaceRoot returns the root directory for agent workspaces. func (s *PrepSubsystem) workspaceRoot() string { - home, _ := os.UserHomeDir() - return filepath.Join(home, "Code", "host-uk", "core", ".core", "workspace") + return WorkspaceRoot() } diff --git a/pkg/monitor/monitor.go b/pkg/monitor/monitor.go index c69337b..4e3089d 100644 --- a/pkg/monitor/monitor.go +++ b/pkg/monitor/monitor.go @@ -366,8 +366,11 @@ func (m *Subsystem) agentStatusResource(ctx context.Context, req *mcp.ReadResour } func workspaceRoot() string { + if root := os.Getenv("CORE_WORKSPACE"); root != "" { + return filepath.Join(root, "workspace") + } home, _ := os.UserHomeDir() - return filepath.Join(home, "Code", "host-uk", "core", ".core", "workspace") + return filepath.Join(home, "Code", ".core", "workspace") } func agentName() string {