diff --git a/pkg/agentic/paths.go b/pkg/agentic/paths.go index 4047c9e..c058376 100644 --- a/pkg/agentic/paths.go +++ b/pkg/agentic/paths.go @@ -4,6 +4,7 @@ package agentic import ( "os" + "strings" "path/filepath" ) @@ -27,3 +28,17 @@ func CoreRoot() string { func PlansRoot() string { return filepath.Join(CoreRoot(), "plans") } + +// AgentName returns the name of this agent based on hostname. +// Checks AGENT_NAME env var first. +func AgentName() string { + if name := os.Getenv("AGENT_NAME"); name != "" { + return name + } + hostname, _ := os.Hostname() + h := strings.ToLower(hostname) + if strings.Contains(h, "snider") || strings.Contains(h, "studio") || strings.Contains(h, "mac") { + return "cladius" + } + return "charon" +} diff --git a/pkg/agentic/remote.go b/pkg/agentic/remote.go index 4a0db97..8126476 100644 --- a/pkg/agentic/remote.go +++ b/pkg/agentic/remote.go @@ -11,6 +11,7 @@ import ( "strings" "time" + coreio "forge.lthn.ai/core/go-io" coreerr "forge.lthn.ai/core/go-log" "github.com/modelcontextprotocol/go-sdk/mcp" ) @@ -193,8 +194,8 @@ func remoteToken(host string) string { fmt.Sprintf("%s/.core/agent-token", home), } for _, f := range tokenFiles { - if data, err := os.ReadFile(f); err == nil { - return strings.TrimSpace(string(data)) + if data, err := coreio.Local.Read(f); err == nil { + return strings.TrimSpace(data) } } diff --git a/pkg/agentic/review_queue.go b/pkg/agentic/review_queue.go index 53fde04..06e7009 100644 --- a/pkg/agentic/review_queue.go +++ b/pkg/agentic/review_queue.go @@ -242,12 +242,12 @@ func (s *PrepSubsystem) pushAndMerge(ctx context.Context, repoDir, repo string) } // Mark PR ready if draft - readyCmd := exec.CommandContext(ctx, "gh", "pr", "ready", "1") + readyCmd := exec.CommandContext(ctx, "gh", "pr", "ready", "--repo", "dAppCore/"+repo) readyCmd.Dir = repoDir readyCmd.Run() // Ignore error — might already be ready // Try to merge - mergeCmd := exec.CommandContext(ctx, "gh", "pr", "merge", "1", "--merge", "--delete-branch") + mergeCmd := exec.CommandContext(ctx, "gh", "pr", "merge", "--merge", "--delete-branch") mergeCmd.Dir = repoDir if out, err := mergeCmd.CombinedOutput(); err != nil { return coreerr.E("pushAndMerge", "merge failed: "+string(out), err) diff --git a/pkg/agentic/verify.go b/pkg/agentic/verify.go index 53250e6..ddb68df 100644 --- a/pkg/agentic/verify.go +++ b/pkg/agentic/verify.go @@ -341,6 +341,5 @@ func extractPRNumber(prURL string) int { // fileExists checks if a file exists. func fileExists(path string) bool { - data, err := coreio.Local.Read(path) - return err == nil && data != "" + return coreio.Local.IsFile(path) } diff --git a/pkg/brain/direct.go b/pkg/brain/direct.go index d7598eb..16140f8 100644 --- a/pkg/brain/direct.go +++ b/pkg/brain/direct.go @@ -14,22 +14,15 @@ import ( "strings" "time" + "forge.lthn.ai/core/agent/pkg/agentic" coreio "forge.lthn.ai/core/go-io" coreerr "forge.lthn.ai/core/go-log" "github.com/modelcontextprotocol/go-sdk/mcp" ) -// agentName returns the identity of this agent from env or hostname. +// agentName returns the identity of this agent. func agentName() string { - if name := os.Getenv("AGENT_NAME"); name != "" { - return name - } - hostname, _ := os.Hostname() - h := strings.ToLower(hostname) - if strings.Contains(h, "snider") || strings.Contains(h, "studio") || strings.Contains(h, "mac") { - return "cladius" - } - return "charon" + return agentic.AgentName() } // DirectSubsystem implements mcp.Subsystem for OpenBrain via direct HTTP calls. diff --git a/pkg/brain/provider.go b/pkg/brain/provider.go index 3dec757..1a02cb1 100644 --- a/pkg/brain/provider.go +++ b/pkg/brain/provider.go @@ -1,4 +1,4 @@ -// SPDX-Licence-Identifier: EUPL-1.2 +// SPDX-License-Identifier: EUPL-1.2 package brain diff --git a/pkg/monitor/monitor.go b/pkg/monitor/monitor.go index d4ced1f..8083df4 100644 --- a/pkg/monitor/monitor.go +++ b/pkg/monitor/monitor.go @@ -20,6 +20,9 @@ import ( "sync" "time" + coreio "forge.lthn.ai/core/go-io" + "forge.lthn.ai/core/agent/pkg/agentic" + coreerr "forge.lthn.ai/core/go-log" "github.com/modelcontextprotocol/go-sdk/mcp" ) @@ -169,7 +172,7 @@ func (m *Subsystem) check(ctx context.Context) { // checkCompletions scans workspace for newly completed agents. func (m *Subsystem) checkCompletions() string { - wsRoot := workspaceRoot() + wsRoot := agentic.WorkspaceRoot() entries, err := filepath.Glob(filepath.Join(wsRoot, "*/status.json")) if err != nil { return "" @@ -181,7 +184,7 @@ func (m *Subsystem) checkCompletions() string { var recentlyCompleted []string for _, entry := range entries { - data, err := os.ReadFile(entry) + data, err := coreio.Local.Read(entry) if err != nil { continue } @@ -190,7 +193,7 @@ func (m *Subsystem) checkCompletions() string { Repo string `json:"repo"` Agent string `json:"agent"` } - if json.Unmarshal(data, &st) != nil { + if json.Unmarshal([]byte(data), &st) != nil { continue } @@ -229,15 +232,15 @@ func (m *Subsystem) checkCompletions() string { func (m *Subsystem) checkInbox() string { home, _ := os.UserHomeDir() keyFile := filepath.Join(home, ".claude", "brain.key") - apiKey, err := os.ReadFile(keyFile) + apiKeyStr, err := coreio.Local.Read(keyFile) if err != nil { return "" } // Call the API to check inbox cmd := exec.Command("curl", "-sf", - "-H", "Authorization: Bearer "+strings.TrimSpace(string(apiKey)), - "https://api.lthn.sh/v1/messages/inbox?agent="+agentName(), + "-H", "Authorization: Bearer "+strings.TrimSpace(apiKeyStr), + "https://api.lthn.sh/v1/messages/inbox?agent="+agentic.AgentName(), ) out, err := cmd.Output() if err != nil { @@ -292,7 +295,7 @@ func (m *Subsystem) checkInbox() string { if latestSubject != "" { notify += fmt.Sprintf(" — \"%s\"", latestSubject) } - os.WriteFile("/tmp/claude-inbox-notify", []byte(notify), 0644) + coreio.Local.Write("/tmp/claude-inbox-notify", notify) return fmt.Sprintf("%d unread message(s) in inbox", unread) } @@ -315,7 +318,7 @@ func (m *Subsystem) notify(ctx context.Context, message string) { // agentStatusResource returns current workspace status as a JSON resource. func (m *Subsystem) agentStatusResource(ctx context.Context, req *mcp.ReadResourceRequest) (*mcp.ReadResourceResult, error) { - wsRoot := workspaceRoot() + wsRoot := agentic.WorkspaceRoot() entries, err := filepath.Glob(filepath.Join(wsRoot, "*/status.json")) if err != nil { return nil, coreerr.E("monitor.agentStatus", "failed to scan workspaces", err) @@ -331,7 +334,7 @@ func (m *Subsystem) agentStatusResource(ctx context.Context, req *mcp.ReadResour var workspaces []wsInfo for _, entry := range entries { - data, err := os.ReadFile(entry) + data, err := coreio.Local.Read(entry) if err != nil { continue } @@ -341,7 +344,7 @@ func (m *Subsystem) agentStatusResource(ctx context.Context, req *mcp.ReadResour Agent string `json:"agent"` PRURL string `json:"pr_url"` } - if json.Unmarshal(data, &st) != nil { + if json.Unmarshal([]byte(data), &st) != nil { continue } workspaces = append(workspaces, wsInfo{ @@ -365,22 +368,3 @@ func (m *Subsystem) agentStatusResource(ctx context.Context, req *mcp.ReadResour }, nil } -func workspaceRoot() string { - if root := os.Getenv("CORE_WORKSPACE"); root != "" { - return filepath.Join(root, "workspace") - } - home, _ := os.UserHomeDir() - return filepath.Join(home, "Code", ".core", "workspace") -} - -func agentName() string { - if name := os.Getenv("AGENT_NAME"); name != "" { - return name - } - hostname, _ := os.Hostname() - h := strings.ToLower(hostname) - if strings.Contains(h, "snider") || strings.Contains(h, "studio") || strings.Contains(h, "mac") { - return "cladius" - } - return "charon" -} diff --git a/pkg/monitor/sync.go b/pkg/monitor/sync.go index 181824d..846ff45 100644 --- a/pkg/monitor/sync.go +++ b/pkg/monitor/sync.go @@ -11,6 +11,9 @@ import ( "path/filepath" "strings" "time" + + "forge.lthn.ai/core/agent/pkg/agentic" + coreio "forge.lthn.ai/core/go-io" ) // CheckinResponse is what the API returns for an agent checkin. @@ -36,7 +39,7 @@ func (m *Subsystem) syncRepos() string { apiURL = "https://api.lthn.sh" } - agentName := agentName() + agentName := agentic.AgentName() url := fmt.Sprintf("%s/v1/agent/checkin?agent=%s&since=%d", apiURL, agentName, m.lastSyncTimestamp) @@ -49,8 +52,8 @@ func (m *Subsystem) syncRepos() string { brainKey := os.Getenv("CORE_BRAIN_KEY") if brainKey == "" { home, _ := os.UserHomeDir() - if data, err := os.ReadFile(filepath.Join(home, ".claude", "brain.key")); err == nil { - brainKey = strings.TrimSpace(string(data)) + if data, err := coreio.Local.Read(filepath.Join(home, ".claude", "brain.key")); err == nil { + brainKey = strings.TrimSpace(data) } } if brainKey != "" {