[agent/codex] Fix ALL AX findings in pkg/brain/direct.go. Read CODEX.md. O... #9

Merged
Virgil merged 5 commits from agent/fix-all-ax-findings-in-pkg-brain-direct into dev 2026-03-22 13:03:20 +00:00
26 changed files with 507 additions and 227 deletions

4
go.mod
View file

@ -3,13 +3,13 @@ module dappco.re/go/agent
go 1.26.0
require (
dappco.re/go/core v0.5.0
dappco.re/go/core v0.6.0
dappco.re/go/core/api v0.2.0
dappco.re/go/core/process v0.3.0
dappco.re/go/core/ws v0.3.0
forge.lthn.ai/core/api v0.1.6
forge.lthn.ai/core/cli v0.3.7
forge.lthn.ai/core/mcp v0.4.4
forge.lthn.ai/core/mcp v0.4.8
github.com/gin-gonic/gin v1.12.0
github.com/gorilla/websocket v1.5.3
github.com/modelcontextprotocol/go-sdk v1.4.1

11
go.sum
View file

@ -1,13 +1,18 @@
dappco.re/go/core v0.5.0 h1:P5DJoaCiK5Q+af5UiTdWqUIW4W4qYKzpgGK50thm21U=
dappco.re/go/core v0.5.0/go.mod h1:f2/tBZ3+3IqDrg2F5F598llv0nmb/4gJVCFzM5geE4A=
dappco.re/go/core v0.6.0 h1:0wmuO/UmCWXxJkxQ6XvVLnqkAuWitbd49PhxjCsplyk=
dappco.re/go/core v0.6.0/go.mod h1:f2/tBZ3+3IqDrg2F5F598llv0nmb/4gJVCFzM5geE4A=
dappco.re/go/core/api v0.2.0 h1:5OcN9nawpp18Jp6dB1OwI2CBfs0Tacb0y0zqxFB6TJ0=
dappco.re/go/core/api v0.2.0/go.mod h1:AtgNAx8lDY+qhVObFdNQOjSUQrHX1BeiDdMuA6RIfzo=
dappco.re/go/core/i18n v0.2.0/go.mod h1:9eSVJXr3OpIGWQvDynfhqcp27xnLMwlYLgsByU+p7ok=
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/process v0.3.0 h1:BPF9R79+8ZWe34qCIy/sZy+P4HwbaO95js2oPJL7IqM=
dappco.re/go/core/process v0.3.0/go.mod h1:qwx8kt6x+J9gn7fu8lavuess72Ye9jPBODqDZQ9K0as=
dappco.re/go/core/scm v0.4.0/go.mod h1:ufb7si6HBkaT6zC8L67kLm8zzBaD1aQoTn4OsVAM1aI=
dappco.re/go/core/store v0.2.0/go.mod h1:QQGJiruayjna3nywbf0N2gcO502q/oEkPoSpBpSKbLM=
dappco.re/go/core/ws v0.3.0 h1:ZxR8y5pfrWvnCHVN7qExXz7fdP5a063uNqyqE0Ab8pQ=
dappco.re/go/core/ws v0.3.0/go.mod h1:aLyXrJnbCOGL0SW9rC1EHAAIS83w3djO374gHIz4Nic=
forge.lthn.ai/core/api v0.1.5 h1:NwZrcOyBjaiz5/cn0n0tnlMUodi8Or6FHMx59C7Kv2o=
@ -42,6 +47,12 @@ forge.lthn.ai/core/mcp v0.4.0 h1:t4HMTI6CpoGB/VmE1aTklSEM8EI4Z/uKWyjGHxa1f4M=
forge.lthn.ai/core/mcp v0.4.0/go.mod h1:eU35WT/8Mc0oJDVWdKaXEtNp27+Hc8KvnTKPf4DAqXE=
forge.lthn.ai/core/mcp v0.4.4 h1:VTCOA1Dj/L7S8JCRg9BfYw7KfowW/Vvrp39bxc0dYyw=
forge.lthn.ai/core/mcp v0.4.4/go.mod h1:eU35WT/8Mc0oJDVWdKaXEtNp27+Hc8KvnTKPf4DAqXE=
forge.lthn.ai/core/mcp v0.4.6 h1:jZY72sfPiCppKU4YyX7Gwy7ynbgVzUto+3S6oAj5Qs4=
forge.lthn.ai/core/mcp v0.4.6/go.mod h1:eU35WT/8Mc0oJDVWdKaXEtNp27+Hc8KvnTKPf4DAqXE=
forge.lthn.ai/core/mcp v0.4.7 h1:Iy/83laUpkaH8W2EoDlVMJbyv60xJ4aMgQe6sOcwL7k=
forge.lthn.ai/core/mcp v0.4.7/go.mod h1:eU35WT/8Mc0oJDVWdKaXEtNp27+Hc8KvnTKPf4DAqXE=
forge.lthn.ai/core/mcp v0.4.8 h1:nd1x3AL8AkUfl0kziltoJUX96Nx1BeFWEbgHmfrkKz8=
forge.lthn.ai/core/mcp v0.4.8/go.mod h1:eU35WT/8Mc0oJDVWdKaXEtNp27+Hc8KvnTKPf4DAqXE=
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=

View file

@ -4,8 +4,6 @@ package agentic
import (
"context"
"os"
"path/filepath"
"syscall"
"time"
@ -101,8 +99,7 @@ func agentCommand(agent, prompt string) (string, []string, error) {
}
return "coderabbit", args, nil
case "local":
home, _ := os.UserHomeDir()
script := core.JoinPath(home, "Code", "core", "agent", "scripts", "local-agent.sh")
script := core.JoinPath(core.Env("DIR_HOME"), "Code", "core", "agent", "scripts", "local-agent.sh")
return "bash", []string{script, prompt}, nil
default:
return "", nil, core.E("agentCommand", "unknown agent: "+agent, nil)
@ -125,7 +122,7 @@ func (s *PrepSubsystem) spawnAgent(agent, prompt, wsDir, srcDir string) (int, st
// Clean up stale BLOCKED.md from previous runs so it doesn't
// prevent this run from completing
os.Remove(core.JoinPath(srcDir, "BLOCKED.md"))
fs.Delete(core.JoinPath(srcDir, "BLOCKED.md"))
proc, err := process.StartWithOptions(context.Background(), process.RunOptions{
Command: command,
@ -191,7 +188,7 @@ func (s *PrepSubsystem) spawnAgent(agent, prompt, wsDir, srcDir string) (int, st
}
// Emit completion event with actual status
emitCompletionEvent(agent, filepath.Base(wsDir), finalStatus)
emitCompletionEvent(agent, core.PathBase(wsDir), finalStatus)
// Notify monitor immediately (push to connected clients)
if s.onComplete != nil {

View file

@ -4,7 +4,7 @@ package agentic
import (
"encoding/json"
"os"
"io"
"time"
core "dappco.re/go/core"
@ -42,10 +42,11 @@ func emitCompletionEvent(agent, workspace, status string) {
}
// Append to events log
f, err := os.OpenFile(eventsFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
r := fs.Append(eventsFile)
if !r.OK {
return
}
defer f.Close()
f.Write(append(data, '\n'))
wc := r.Value.(io.WriteCloser)
defer wc.Close()
wc.Write(append(data, '\n'))
}

View file

@ -6,8 +6,6 @@ import (
"bytes"
"encoding/json"
"net/http"
"os"
"path/filepath"
core "dappco.re/go/core"
)
@ -21,7 +19,7 @@ func (s *PrepSubsystem) ingestFindings(wsDir string) {
}
// Read the log file
logFiles, _ := filepath.Glob(core.JoinPath(wsDir, "agent-*.log"))
logFiles := core.PathGlob(core.JoinPath(wsDir, "agent-*.log"))
if len(logFiles) == 0 {
return
}
@ -92,8 +90,7 @@ func (s *PrepSubsystem) createIssueViaAPI(repo, title, description, issueType, p
}
// Read the agent API key from file
home, _ := os.UserHomeDir()
r := fs.Read(core.JoinPath(home, ".claude", "agent-api.key"))
r := fs.Read(core.JoinPath(core.Env("DIR_HOME"), ".claude", "agent-api.key"))
if !r.OK {
return
}

View file

@ -60,8 +60,7 @@ func (s *PrepSubsystem) mirror(ctx context.Context, _ *mcp.CallToolRequest, inpu
basePath := s.codePath
if basePath == "" {
home, _ := os.UserHomeDir()
basePath = core.JoinPath(home, "Code", "core")
basePath = core.JoinPath(core.Env("DIR_HOME"), "Code", "core")
} else {
basePath = core.JoinPath(basePath, "core")
}
@ -246,17 +245,18 @@ func filesChanged(repoDir, base, head string) int {
// listLocalRepos returns repo names that exist as directories in basePath.
func (s *PrepSubsystem) listLocalRepos(basePath string) []string {
entries, err := os.ReadDir(basePath)
if err != nil {
r := fs.List(basePath)
if !r.OK {
return nil
}
entries := r.Value.([]os.DirEntry)
var repos []string
for _, e := range entries {
if !e.IsDir() {
continue
}
// Must have a .git directory
if _, err := os.Stat(core.JoinPath(basePath, e.Name(), ".git")); err == nil {
if fs.IsDir(core.JoinPath(basePath, e.Name(), ".git")) {
repos = append(repos, e.Name())
}
}

View file

@ -3,7 +3,6 @@
package agentic
import (
"os"
"os/exec"
"strconv"
"unsafe"
@ -45,11 +44,10 @@ func WorkspaceRoot() string {
//
// root := agentic.CoreRoot()
func CoreRoot() string {
if root := os.Getenv("CORE_WORKSPACE"); root != "" {
if root := core.Env("CORE_WORKSPACE"); root != "" {
return root
}
home, _ := os.UserHomeDir()
return core.JoinPath(home, "Code", ".core")
return core.JoinPath(core.Env("DIR_HOME"), "Code", ".core")
}
// PlansRoot returns the root directory for agent plans.
@ -64,11 +62,10 @@ func PlansRoot() string {
//
// name := agentic.AgentName() // "cladius" on Snider's Mac, "charon" elsewhere
func AgentName() string {
if name := os.Getenv("AGENT_NAME"); name != "" {
if name := core.Env("AGENT_NAME"); name != "" {
return name
}
hostname, _ := os.Hostname()
h := core.Lower(hostname)
h := core.Lower(core.Env("HOSTNAME"))
if core.Contains(h, "snider") || core.Contains(h, "studio") || core.Contains(h, "mac") {
return "cladius"
}
@ -102,7 +99,7 @@ func DefaultBranch(repoDir string) string {
//
// org := agentic.GitHubOrg() // "dAppCore"
func GitHubOrg() string {
if org := os.Getenv("GITHUB_ORG"); org != "" {
if org := core.Env("GITHUB_ORG"); org != "" {
return org
}
return "dAppCore"

View file

@ -8,7 +8,6 @@ import (
"encoding/hex"
"encoding/json"
"os"
"path/filepath"
"strings"
"time"
@ -279,7 +278,7 @@ func (s *PrepSubsystem) planDelete(_ context.Context, _ *mcp.CallToolRequest, in
}
path := planPath(PlansRoot(), input.ID)
if _, err := os.Stat(path); err != nil {
if !fs.Exists(path) {
return nil, PlanDeleteOutput{}, core.E("planDelete", "plan not found: "+input.ID, nil)
}
@ -301,10 +300,11 @@ func (s *PrepSubsystem) planList(_ context.Context, _ *mcp.CallToolRequest, inpu
return nil, PlanListOutput{}, core.E("planList", "failed to access plans directory", err)
}
entries, err := os.ReadDir(dir)
if err != nil {
return nil, PlanListOutput{}, core.E("planList", "failed to read plans directory", err)
r := fs.List(dir)
if !r.OK {
return nil, PlanListOutput{}, nil
}
entries := r.Value.([]os.DirEntry)
var plans []Plan
for _, entry := range entries {
@ -340,7 +340,7 @@ func (s *PrepSubsystem) planList(_ context.Context, _ *mcp.CallToolRequest, inpu
func planPath(dir, id string) string {
// Sanitise ID to prevent path traversal
safe := filepath.Base(id)
safe := core.PathBase(id)
if safe == "." || safe == ".." || safe == "" {
safe = "invalid"
}

View file

@ -7,7 +7,6 @@ import (
"context"
"encoding/json"
"net/http"
"os"
"os/exec"
core "dappco.re/go/core"
@ -58,7 +57,7 @@ func (s *PrepSubsystem) createPR(ctx context.Context, _ *mcp.CallToolRequest, in
wsDir := core.JoinPath(WorkspaceRoot(), input.Workspace)
srcDir := core.JoinPath(wsDir, "src")
if _, err := os.Stat(srcDir); err != nil {
if !fs.IsDir(srcDir) {
return nil, CreatePROutput{}, core.E("createPR", "workspace not found: "+input.Workspace, nil)
}

View file

@ -10,9 +10,7 @@ import (
"encoding/json"
goio "io"
"net/http"
"os"
"os/exec"
"path/filepath"
"strings"
"sync"
"time"
@ -56,14 +54,14 @@ var _ coremcp.Subsystem = (*PrepSubsystem)(nil)
// sub.SetCompletionNotifier(monitor)
// sub.RegisterTools(server)
func NewPrep() *PrepSubsystem {
home, _ := os.UserHomeDir()
home := core.Env("DIR_HOME")
forgeToken := os.Getenv("FORGE_TOKEN")
forgeToken := core.Env("FORGE_TOKEN")
if forgeToken == "" {
forgeToken = os.Getenv("GITEA_TOKEN")
forgeToken = core.Env("GITEA_TOKEN")
}
brainKey := os.Getenv("CORE_BRAIN_KEY")
brainKey := core.Env("CORE_BRAIN_KEY")
if brainKey == "" {
if r := fs.Read(core.JoinPath(home, ".claude", "brain.key")); r.OK {
brainKey = core.Trim(r.Value.(string))
@ -89,7 +87,7 @@ func (s *PrepSubsystem) SetCompletionNotifier(n CompletionNotifier) {
}
func envOr(key, fallback string) string {
if v := os.Getenv(key); v != "" {
if v := core.Env(key); v != "" {
return v
}
return fallback
@ -192,7 +190,7 @@ func (s *PrepSubsystem) prepWorkspace(ctx context.Context, _ *mcp.CallToolReques
out := PrepOutput{WorkspaceDir: wsDir}
// Source repo path — sanitise to prevent path traversal
repoName := filepath.Base(input.Repo) // strips ../ and absolute paths
repoName := core.PathBase(input.Repo) // strips ../ and absolute paths
if repoName == "." || repoName == ".." || repoName == "" {
return nil, PrepOutput{}, core.E("prep", "invalid repo name: "+input.Repo, nil)
}
@ -250,12 +248,20 @@ func (s *PrepSubsystem) prepWorkspace(ctx context.Context, _ *mcp.CallToolReques
wsTmpl = "review"
}
promptContent, _ := lib.Prompt(input.Template)
promptContent := ""
if r := lib.Prompt(input.Template); r.OK {
promptContent = r.Value.(string)
}
personaContent := ""
if input.Persona != "" {
personaContent, _ = lib.Persona(input.Persona)
if r := lib.Persona(input.Persona); r.OK {
personaContent = r.Value.(string)
}
}
flowContent := ""
if r := lib.Flow(detectLanguage(repoPath)); r.OK {
flowContent = r.Value.(string)
}
flowContent, _ := lib.Flow(detectLanguage(repoPath))
wsData := &lib.WorkspaceData{
Repo: input.Repo,
@ -319,13 +325,13 @@ func (s *PrepSubsystem) prepWorkspace(ctx context.Context, _ *mcp.CallToolReques
// --- Prompt templates ---
func (s *PrepSubsystem) writePromptTemplate(template, wsDir string) {
prompt, err := lib.Template(template)
if err != nil {
// Fallback to default template
prompt, _ = lib.Template("default")
if prompt == "" {
prompt = "Read TODO.md and complete the task. Work in src/.\n"
}
r := lib.Template(template)
if !r.OK {
r = lib.Template("default")
}
prompt := "Read TODO.md and complete the task. Work in src/.\n"
if r.OK {
prompt = r.Value.(string)
}
fs.Write(core.JoinPath(wsDir, "src", "PROMPT.md"), prompt)
@ -337,12 +343,12 @@ func (s *PrepSubsystem) writePromptTemplate(template, wsDir string) {
// and writes PLAN.md into the workspace src/ directory.
func (s *PrepSubsystem) writePlanFromTemplate(templateSlug string, variables map[string]string, task string, wsDir string) {
// Load template from embedded prompts package
data, err := lib.Template(templateSlug)
if err != nil {
r := lib.Template(templateSlug)
if !r.OK {
return // Template not found, skip silently
}
content := data
content := r.Value.(string)
// Substitute variables ({{variable_name}} → value)
for key, value := range variables {
@ -565,7 +571,7 @@ func (s *PrepSubsystem) findConsumers(repo, wsDir string) int {
}
modData := mr.Value.(string)
if core.Contains(modData, modulePath) && !core.HasPrefix(modData, "module "+modulePath) {
consumers = append(consumers, filepath.Base(dir))
consumers = append(consumers, core.PathBase(dir))
}
}
@ -649,7 +655,7 @@ func detectLanguage(repoPath string) string {
{"Dockerfile", "docker"},
}
for _, c := range checks {
if _, err := os.Stat(core.JoinPath(repoPath, c.file)); err == nil {
if fs.IsFile(core.JoinPath(repoPath, c.file)) {
return c.lang
}
}

View file

@ -119,10 +119,11 @@ func (s *PrepSubsystem) delayForAgent(agent string) time.Duration {
func (s *PrepSubsystem) countRunningByAgent(agent string) int {
wsRoot := WorkspaceRoot()
entries, err := os.ReadDir(wsRoot)
if err != nil {
r := fs.List(wsRoot)
if !r.OK {
return 0
}
entries := r.Value.([]os.DirEntry)
count := 0
for _, entry := range entries {
@ -171,10 +172,11 @@ func (s *PrepSubsystem) drainQueue() {
wsRoot := WorkspaceRoot()
entries, err := os.ReadDir(wsRoot)
if err != nil {
r := fs.List(wsRoot)
if !r.OK {
return
}
entries := r.Value.([]os.DirEntry)
for _, entry := range entries {
if !entry.IsDir() {

View file

@ -6,7 +6,6 @@ import (
"context"
"encoding/json"
"net/http"
"os"
"time"
core "dappco.re/go/core"
@ -180,17 +179,17 @@ func resolveHost(host string) string {
func remoteToken(host string) string {
// Check environment first
envKey := core.Sprintf("AGENT_TOKEN_%s", core.Upper(host))
if token := os.Getenv(envKey); token != "" {
if token := core.Env(envKey); token != "" {
return token
}
// Fallback to shared agent token
if token := os.Getenv("MCP_AUTH_TOKEN"); token != "" {
if token := core.Env("MCP_AUTH_TOKEN"); token != "" {
return token
}
// Try reading from file
home, _ := os.UserHomeDir()
home := core.Env("DIR_HOME")
tokenFiles := []string{
core.Sprintf("%s/.core/tokens/%s.token", home, core.Lower(host)),
core.Sprintf("%s/.core/agent-token", home),

View file

@ -4,7 +4,6 @@ package agentic
import (
"context"
"os"
core "dappco.re/go/core"
"github.com/modelcontextprotocol/go-sdk/mcp"
@ -48,7 +47,7 @@ func (s *PrepSubsystem) resume(ctx context.Context, _ *mcp.CallToolRequest, inpu
srcDir := core.JoinPath(wsDir, "src")
// Verify workspace exists
if _, err := os.Stat(srcDir); err != nil {
if !fs.IsDir(srcDir) {
return nil, ResumeOutput{}, core.E("resume", "workspace not found: "+input.Workspace, nil)
}

View file

@ -5,6 +5,7 @@ package agentic
import (
"context"
"encoding/json"
"io"
"os"
"os/exec"
"regexp"
@ -134,10 +135,11 @@ func (s *PrepSubsystem) reviewQueue(ctx context.Context, _ *mcp.CallToolRequest,
// findReviewCandidates returns repos that are ahead of GitHub main.
func (s *PrepSubsystem) findReviewCandidates(basePath string) []string {
entries, err := os.ReadDir(basePath)
if err != nil {
r := fs.List(basePath)
if !r.OK {
return nil
}
entries := r.Value.([]os.DirEntry)
var candidates []string
for _, e := range entries {
@ -337,8 +339,7 @@ func (s *PrepSubsystem) buildReviewCommand(ctx context.Context, repoDir, reviewe
// storeReviewOutput saves raw review output for training data collection.
func (s *PrepSubsystem) storeReviewOutput(repoDir, repo, reviewer, output string) {
home, _ := os.UserHomeDir()
dataDir := core.JoinPath(home, ".core", "training", "reviews")
dataDir := core.JoinPath(core.Env("DIR_HOME"), ".core", "training", "reviews")
fs.EnsureDir(dataDir)
timestamp := time.Now().Format("2006-01-02T15-04-05")
@ -361,25 +362,25 @@ func (s *PrepSubsystem) storeReviewOutput(repoDir, repo, reviewer, output string
jsonLine, _ := json.Marshal(entry)
jsonlPath := core.JoinPath(dataDir, "reviews.jsonl")
f, err := os.OpenFile(jsonlPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err == nil {
defer f.Close()
f.Write(append(jsonLine, '\n'))
r := fs.Append(jsonlPath)
if !r.OK {
return
}
wc := r.Value.(io.WriteCloser)
defer wc.Close()
wc.Write(append(jsonLine, '\n'))
}
// saveRateLimitState persists rate limit info for cross-run awareness.
func (s *PrepSubsystem) saveRateLimitState(info *RateLimitInfo) {
home, _ := os.UserHomeDir()
path := core.JoinPath(home, ".core", "coderabbit-ratelimit.json")
path := core.JoinPath(core.Env("DIR_HOME"), ".core", "coderabbit-ratelimit.json")
data, _ := json.Marshal(info)
fs.Write(path, string(data))
}
// loadRateLimitState reads persisted rate limit info.
func (s *PrepSubsystem) loadRateLimitState() *RateLimitInfo {
home, _ := os.UserHomeDir()
path := core.JoinPath(home, ".core", "coderabbit-ratelimit.json")
path := core.JoinPath(core.Env("DIR_HOME"), ".core", "coderabbit-ratelimit.json")
r := fs.Read(path)
if !r.OK {
return nil

View file

@ -6,7 +6,6 @@ import (
"context"
"encoding/json"
"net/http"
"strings"
core "dappco.re/go/core"
"github.com/modelcontextprotocol/go-sdk/mcp"
@ -197,7 +196,7 @@ func (s *PrepSubsystem) listRepoIssues(ctx context.Context, org, repo, label str
Title: issue.Title,
Labels: labels,
Assignee: assignee,
URL: strings.Replace(issue.HTMLURL, "https://forge.lthn.ai", s.forgeURL, 1),
URL: core.Replace(issue.HTMLURL, "https://forge.lthn.ai", s.forgeURL),
})
}

View file

@ -113,10 +113,11 @@ func (s *PrepSubsystem) registerStatusTool(server *mcp.Server) {
func (s *PrepSubsystem) status(ctx context.Context, _ *mcp.CallToolRequest, input StatusInput) (*mcp.CallToolResult, StatusOutput, error) {
wsRoot := WorkspaceRoot()
entries, err := os.ReadDir(wsRoot)
if err != nil {
return nil, StatusOutput{}, core.E("status", "no workspaces found", err)
r := fs.List(wsRoot)
if !r.OK {
return nil, StatusOutput{}, core.E("status", "no workspaces found", nil)
}
entries := r.Value.([]os.DirEntry)
var workspaces []WorkspaceInfo

View file

@ -9,7 +9,6 @@ import (
"net/http"
"os"
"os/exec"
"path/filepath"
"time"
core "dappco.re/go/core"
@ -130,7 +129,7 @@ func (s *PrepSubsystem) rebaseBranch(srcDir, branch string) bool {
}
// Force-push the rebased branch to Forge (origin is local clone)
st, _ := readStatus(filepath.Dir(srcDir))
st, _ := readStatus(core.PathDir(srcDir))
org := "core"
repo := ""
if st != nil {

View file

@ -4,7 +4,6 @@ package agentic
import (
"context"
"path/filepath"
"time"
core "dappco.re/go/core"
@ -192,20 +191,17 @@ func (s *PrepSubsystem) watch(ctx context.Context, req *mcp.CallToolRequest, inp
// findActiveWorkspaces returns workspace names that are running or queued.
func (s *PrepSubsystem) findActiveWorkspaces() []string {
wsRoot := WorkspaceRoot()
entries, err := filepath.Glob(core.JoinPath(wsRoot, "*/status.json"))
if err != nil {
return nil
}
entries := core.PathGlob(core.JoinPath(wsRoot, "*/status.json"))
var active []string
for _, entry := range entries {
wsDir := filepath.Dir(entry)
wsDir := core.PathDir(entry)
st, err := readStatus(wsDir)
if err != nil {
continue
}
if st.Status == "running" || st.Status == "queued" {
active = append(active, filepath.Base(wsDir))
active = append(active, core.PathBase(wsDir))
}
}
return active
@ -213,7 +209,7 @@ func (s *PrepSubsystem) findActiveWorkspaces() []string {
// resolveWorkspaceDir converts a workspace name to full path.
func (s *PrepSubsystem) resolveWorkspaceDir(name string) string {
if filepath.IsAbs(name) {
if core.PathIsAbs(name) {
return name
}
return core.JoinPath(WorkspaceRoot(), name)

View file

@ -7,7 +7,6 @@ import (
"context"
"encoding/json"
"net/http"
"os"
"time"
"dappco.re/go/agent/pkg/agentic"
@ -33,16 +32,15 @@ var _ coremcp.Subsystem = (*DirectSubsystem)(nil)
// sub := brain.NewDirect()
// sub.RegisterTools(server)
func NewDirect() *DirectSubsystem {
apiURL := os.Getenv("CORE_BRAIN_URL")
apiURL := core.Env("CORE_BRAIN_URL")
if apiURL == "" {
apiURL = "https://api.lthn.sh"
}
apiKey := os.Getenv("CORE_BRAIN_KEY")
apiKey := core.Env("CORE_BRAIN_KEY")
keyPath := ""
if apiKey == "" {
home, _ := os.UserHomeDir()
keyPath = brainKeyPath(home)
keyPath = brainKeyPath(brainHomeDir())
if keyPath != "" {
if r := fs.Read(keyPath); r.OK {
apiKey = core.Trim(r.Value.(string))
@ -104,6 +102,13 @@ func brainKeyPath(home string) string {
return core.JoinPath(core.TrimSuffix(home, "/"), ".claude", "brain.key")
}
func brainHomeDir() string {
if home := core.Env("CORE_HOME"); home != "" {
return home
}
return core.Env("DIR_HOME")
}
func (s *DirectSubsystem) apiCall(ctx context.Context, method, path string, body any) (map[string]any, error) {
if s.apiKey == "" {
return nil, core.E("brain.apiCall", "no API key (set CORE_BRAIN_KEY or create ~/.claude/brain.key)", nil)

View file

@ -5,12 +5,12 @@ package brain
import (
"context"
"encoding/json"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"net/http"
"net/http/httptest"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// newTestDirect returns a DirectSubsystem wired to the given test server.
@ -59,7 +59,7 @@ func TestNewDirect_Good_KeyFromFile(t *testing.T) {
t.Setenv("CORE_BRAIN_KEY", "")
tmpHome := t.TempDir()
t.Setenv("HOME", tmpHome)
t.Setenv("CORE_HOME", tmpHome)
keyDir := filepath.Join(tmpHome, ".claude")
require.True(t, fs.EnsureDir(keyDir).OK)
require.True(t, fs.Write(filepath.Join(keyDir, "brain.key"), " file-key-456 \n").OK)

View file

@ -4,6 +4,7 @@ package brain
import (
"net/http"
"strconv"
"dappco.re/go/core/api"
"dappco.re/go/core/api/pkg/provider"
@ -14,6 +15,11 @@ import (
// BrainProvider wraps the brain Subsystem as a service provider with REST
// endpoints. It delegates to the same IDE bridge that the MCP tools use.
//
// Usage example:
//
// provider := brain.NewProvider(bridge, hub)
// provider.RegisterRoutes(router.Group("/api/brain"))
type BrainProvider struct {
bridge *ide.Bridge
hub *ws.Hub
@ -294,13 +300,23 @@ func (p *BrainProvider) list(c *gin.Context) {
return
}
limit := 0
if rawLimit := c.Query("limit"); rawLimit != "" {
parsedLimit, err := strconv.Atoi(rawLimit)
if err != nil {
c.JSON(http.StatusBadRequest, api.Fail("invalid_limit", "limit must be an integer"))
return
}
limit = parsedLimit
}
err := p.bridge.Send(ide.BridgeMessage{
Type: "brain_list",
Data: map[string]any{
"project": c.Query("project"),
"type": c.Query("type"),
"agent_id": c.Query("agent_id"),
"limit": c.Query("limit"),
"limit": limit,
},
})
if err != nil {

View file

@ -14,6 +14,13 @@ import (
// -- Input/Output types -------------------------------------------------------
// RememberInput is the input for brain_remember.
//
// Usage example:
//
// input := brain.RememberInput{
// Content: "Use core.Env for system paths.",
// Type: "convention",
// }
type RememberInput struct {
Content string `json:"content"`
Type string `json:"type"`
@ -25,6 +32,13 @@ type RememberInput struct {
}
// RememberOutput is the output for brain_remember.
//
// Usage example:
//
// output := brain.RememberOutput{
// Success: true,
// MemoryID: "mem_123",
// }
type RememberOutput struct {
Success bool `json:"success"`
MemoryID string `json:"memoryId,omitempty"`
@ -32,6 +46,13 @@ type RememberOutput struct {
}
// RecallInput is the input for brain_recall.
//
// Usage example:
//
// input := brain.RecallInput{
// Query: "core.Env conventions",
// TopK: 5,
// }
type RecallInput struct {
Query string `json:"query"`
TopK int `json:"top_k,omitempty"`
@ -39,6 +60,13 @@ type RecallInput struct {
}
// RecallFilter holds optional filter criteria for brain_recall.
//
// Usage example:
//
// filter := brain.RecallFilter{
// Project: "agent",
// Type: "convention",
// }
type RecallFilter struct {
Project string `json:"project,omitempty"`
Type any `json:"type,omitempty"`
@ -47,6 +75,13 @@ type RecallFilter struct {
}
// RecallOutput is the output for brain_recall.
//
// Usage example:
//
// output := brain.RecallOutput{
// Success: true,
// Count: 1,
// }
type RecallOutput struct {
Success bool `json:"success"`
Count int `json:"count"`
@ -54,6 +89,14 @@ type RecallOutput struct {
}
// Memory is a single memory entry returned by recall or list.
//
// Usage example:
//
// memory := brain.Memory{
// ID: "mem_123",
// Type: "convention",
// Content: "Use core.Env for system paths.",
// }
type Memory struct {
ID string `json:"id"`
AgentID string `json:"agent_id"`
@ -69,12 +112,26 @@ type Memory struct {
}
// ForgetInput is the input for brain_forget.
//
// Usage example:
//
// input := brain.ForgetInput{
// ID: "mem_123",
// Reason: "superseded",
// }
type ForgetInput struct {
ID string `json:"id"`
Reason string `json:"reason,omitempty"`
}
// ForgetOutput is the output for brain_forget.
//
// Usage example:
//
// output := brain.ForgetOutput{
// Success: true,
// Forgotten: "mem_123",
// }
type ForgetOutput struct {
Success bool `json:"success"`
Forgotten string `json:"forgotten"`
@ -82,6 +139,13 @@ type ForgetOutput struct {
}
// ListInput is the input for brain_list.
//
// Usage example:
//
// input := brain.ListInput{
// Project: "agent",
// Limit: 20,
// }
type ListInput struct {
Project string `json:"project,omitempty"`
Type string `json:"type,omitempty"`
@ -90,6 +154,13 @@ type ListInput struct {
}
// ListOutput is the output for brain_list.
//
// Usage example:
//
// output := brain.ListOutput{
// Success: true,
// Count: 2,
// }
type ListOutput struct {
Success bool `json:"success"`
Count int `json:"count"`

View file

@ -14,104 +14,134 @@
//
// Usage:
//
// prompt, _ := lib.Prompt("coding")
// task, _ := lib.Task("code/review")
// persona, _ := lib.Persona("secops/developer")
// flow, _ := lib.Flow("go")
// r := lib.Prompt("coding") // r.Value.(string)
// r := lib.Task("code/review") // r.Value.(string)
// r := lib.Persona("secops/dev") // r.Value.(string)
// r := lib.Flow("go") // r.Value.(string)
// lib.ExtractWorkspace("default", "/tmp/ws", data)
package lib
import (
"bytes"
"embed"
"io/fs"
"os"
"path/filepath"
"text/template"
core "dappco.re/go/core"
)
//go:embed prompt/*.md
var promptFS embed.FS
//go:embed all:prompt
var promptFiles embed.FS
//go:embed all:task
var taskFS embed.FS
var taskFiles embed.FS
//go:embed flow/*.md
var flowFS embed.FS
//go:embed all:flow
var flowFiles embed.FS
//go:embed persona
var personaFS embed.FS
//go:embed all:persona
var personaFiles embed.FS
//go:embed all:workspace
var workspaceFS embed.FS
var workspaceFiles embed.FS
var (
promptFS = mustMount(promptFiles, "prompt")
taskFS = mustMount(taskFiles, "task")
flowFS = mustMount(flowFiles, "flow")
personaFS = mustMount(personaFiles, "persona")
workspaceFS = mustMount(workspaceFiles, "workspace")
)
func mustMount(fsys embed.FS, basedir string) *core.Embed {
r := core.Mount(fsys, basedir)
if !r.OK {
panic(r.Value)
}
return r.Value.(*core.Embed)
}
// --- Prompts ---
// Template tries Prompt then Task (backwards compat).
func Template(slug string) (string, error) {
if content, err := Prompt(slug); err == nil {
return content, nil
//
// r := lib.Template("coding")
// if r.OK { content := r.Value.(string) }
func Template(slug string) core.Result {
if r := Prompt(slug); r.OK {
return r
}
return Task(slug)
}
func Prompt(slug string) (string, error) {
data, err := promptFS.ReadFile("prompt/" + slug + ".md")
if err != nil {
return "", err
}
return string(data), nil
// Prompt reads a system prompt by slug.
//
// r := lib.Prompt("coding")
// if r.OK { content := r.Value.(string) }
func Prompt(slug string) core.Result {
return promptFS.ReadString(slug + ".md")
}
func Task(slug string) (string, error) {
// Task reads a structured task plan by slug. Tries .md, .yaml, .yml.
//
// r := lib.Task("code/review")
// if r.OK { content := r.Value.(string) }
func Task(slug string) core.Result {
for _, ext := range []string{".md", ".yaml", ".yml"} {
data, err := taskFS.ReadFile("task/" + slug + ext)
if err == nil {
return string(data), nil
if r := taskFS.ReadString(slug + ext); r.OK {
return r
}
}
return "", fs.ErrNotExist
return core.Result{Value: fs.ErrNotExist}
}
func TaskBundle(slug string) (string, map[string]string, error) {
main, err := Task(slug)
if err != nil {
return "", nil, err
// Bundle holds a task's main content plus companion files.
//
// r := lib.TaskBundle("code/review")
// if r.OK { b := r.Value.(lib.Bundle) }
type Bundle struct {
Main string
Files map[string]string
}
// TaskBundle reads a task and its companion files.
//
// r := lib.TaskBundle("code/review")
// if r.OK { b := r.Value.(lib.Bundle) }
func TaskBundle(slug string) core.Result {
main := Task(slug)
if !main.OK {
return main
}
bundleDir := "task/" + slug
entries, err := fs.ReadDir(taskFS, bundleDir)
if err != nil {
return main, nil, nil
b := Bundle{Main: main.Value.(string), Files: make(map[string]string)}
r := taskFS.ReadDir(slug)
if !r.OK {
return core.Result{Value: b, OK: true}
}
bundle := make(map[string]string)
for _, e := range entries {
for _, e := range r.Value.([]fs.DirEntry) {
if e.IsDir() {
continue
}
data, err := taskFS.ReadFile(bundleDir + "/" + e.Name())
if err == nil {
bundle[e.Name()] = string(data)
if fr := taskFS.ReadString(slug + "/" + e.Name()); fr.OK {
b.Files[e.Name()] = fr.Value.(string)
}
}
return main, bundle, nil
return core.Result{Value: b, OK: true}
}
func Flow(slug string) (string, error) {
data, err := flowFS.ReadFile("flow/" + slug + ".md")
if err != nil {
return "", err
}
return string(data), nil
// Flow reads a build/release workflow by slug.
//
// r := lib.Flow("go")
// if r.OK { content := r.Value.(string) }
func Flow(slug string) core.Result {
return flowFS.ReadString(slug + ".md")
}
func Persona(path string) (string, error) {
data, err := personaFS.ReadFile("persona/" + path + ".md")
if err != nil {
return "", err
}
return string(data), nil
// Persona reads a domain/role persona by path.
//
// r := lib.Persona("secops/developer")
// if r.OK { content := r.Value.(string) }
func Persona(path string) core.Result {
return personaFS.ReadString(path + ".md")
}
// --- Workspace Templates ---
@ -138,67 +168,36 @@ type WorkspaceData struct {
// ExtractWorkspace creates an agent workspace from a template.
// Template names: "default", "security", "review".
func ExtractWorkspace(tmplName, targetDir string, data *WorkspaceData) error {
wsDir := "workspace/" + tmplName
if err := os.MkdirAll(targetDir, 0755); err != nil {
return err
r := workspaceFS.Sub(tmplName)
if !r.OK {
if err, ok := r.Value.(error); ok {
return err
}
return core.E("ExtractWorkspace", "template not found: "+tmplName, nil)
}
return fs.WalkDir(workspaceFS, wsDir, func(path string, d fs.DirEntry, err error) error {
if err != nil {
result := core.Extract(r.Value.(*core.Embed).FS(), targetDir, data)
if !result.OK {
if err, ok := result.Value.(error); ok {
return err
}
// Get relative path from template root
rel, err := filepath.Rel(wsDir, path)
if err != nil || rel == "." {
return nil
}
targetPath := filepath.Join(targetDir, rel)
if d.IsDir() {
return os.MkdirAll(targetPath, 0755)
}
content, err := fs.ReadFile(workspaceFS, path)
if err != nil {
return err
}
// Process .tmpl files through text/template
outputName := filepath.Base(targetPath)
if core.HasSuffix(outputName, ".tmpl") {
outputName = core.TrimSuffix(outputName, ".tmpl")
targetPath = filepath.Join(filepath.Dir(targetPath), outputName)
tmpl, err := template.New(outputName).Parse(string(content))
if err != nil {
return err
}
var buf bytes.Buffer
if err := tmpl.Execute(&buf, data); err != nil {
return err
}
content = buf.Bytes()
}
return os.WriteFile(targetPath, content, 0644)
})
}
return nil
}
// --- List Functions ---
func ListPrompts() []string { return listDir(promptFS, "prompt") }
func ListFlows() []string { return listDir(flowFS, "flow") }
func ListWorkspaces() []string { return listDir(workspaceFS, "workspace") }
func ListPrompts() []string { return listDir(promptFS) }
func ListFlows() []string { return listDir(flowFS) }
func ListWorkspaces() []string { return listDir(workspaceFS) }
func ListTasks() []string {
var slugs []string
fs.WalkDir(taskFS, "task", func(path string, d fs.DirEntry, err error) error {
base := taskFS.BaseDirectory()
fs.WalkDir(taskFS.FS(), base, func(path string, d fs.DirEntry, err error) error {
if err != nil || d.IsDir() {
return nil
}
rel := core.TrimPrefix(path, "task/")
rel := core.TrimPrefix(path, base+"/")
ext := filepath.Ext(rel)
slugs = append(slugs, core.TrimSuffix(rel, ext))
return nil
@ -208,12 +207,13 @@ func ListTasks() []string {
func ListPersonas() []string {
var paths []string
fs.WalkDir(personaFS, "persona", func(path string, d fs.DirEntry, err error) error {
base := personaFS.BaseDirectory()
fs.WalkDir(personaFS.FS(), base, func(path string, d fs.DirEntry, err error) error {
if err != nil || d.IsDir() {
return nil
}
if core.HasSuffix(path, ".md") {
rel := core.TrimPrefix(path, "persona/")
rel := core.TrimPrefix(path, base+"/")
rel = core.TrimSuffix(rel, ".md")
paths = append(paths, rel)
}
@ -222,13 +222,13 @@ func ListPersonas() []string {
return paths
}
func listDir(fsys embed.FS, dir string) []string {
entries, err := fsys.ReadDir(dir)
if err != nil {
func listDir(emb *core.Embed) []string {
r := emb.ReadDir(".")
if !r.OK {
return nil
}
var slugs []string
for _, e := range entries {
for _, e := range r.Value.([]fs.DirEntry) {
name := e.Name()
if e.IsDir() {
slugs = append(slugs, name)

View file

@ -1,11 +1,202 @@
package lib
import (
"io/fs"
"os"
"path/filepath"
"testing"
)
// --- Prompt ---
func TestPrompt_Good(t *testing.T) {
r := Prompt("coding")
if !r.OK {
t.Fatal("Prompt('coding') returned !OK")
}
if r.Value.(string) == "" {
t.Error("Prompt('coding') returned empty string")
}
}
func TestPrompt_Bad(t *testing.T) {
r := Prompt("nonexistent-slug")
if r.OK {
t.Error("Prompt('nonexistent-slug') should return !OK")
}
}
// --- Task ---
func TestTask_Good_Yaml(t *testing.T) {
r := Task("bug-fix")
if !r.OK {
t.Fatal("Task('bug-fix') returned !OK")
}
if r.Value.(string) == "" {
t.Error("Task('bug-fix') returned empty string")
}
}
func TestTask_Good_Md(t *testing.T) {
r := Task("code/review")
if !r.OK {
t.Fatal("Task('code/review') returned !OK")
}
if r.Value.(string) == "" {
t.Error("Task('code/review') returned empty string")
}
}
func TestTask_Bad(t *testing.T) {
r := Task("nonexistent-slug")
if r.OK {
t.Error("Task('nonexistent-slug') should return !OK")
}
if r.Value != fs.ErrNotExist {
t.Error("Task('nonexistent-slug') should return fs.ErrNotExist")
}
}
// --- TaskBundle ---
func TestTaskBundle_Good(t *testing.T) {
r := TaskBundle("code/review")
if !r.OK {
t.Fatal("TaskBundle('code/review') returned !OK")
}
b := r.Value.(Bundle)
if b.Main == "" {
t.Error("Bundle.Main is empty")
}
if len(b.Files) == 0 {
t.Error("Bundle.Files is empty — expected companion files")
}
}
func TestTaskBundle_Bad(t *testing.T) {
r := TaskBundle("nonexistent")
if r.OK {
t.Error("TaskBundle('nonexistent') should return !OK")
}
}
// --- Flow ---
func TestFlow_Good(t *testing.T) {
r := Flow("go")
if !r.OK {
t.Fatal("Flow('go') returned !OK")
}
if r.Value.(string) == "" {
t.Error("Flow('go') returned empty string")
}
}
// --- Persona ---
func TestPersona_Good(t *testing.T) {
// Use first persona from list to avoid hardcoding
personas := ListPersonas()
if len(personas) == 0 {
t.Skip("no personas found")
}
r := Persona(personas[0])
if !r.OK {
t.Fatalf("Persona(%q) returned !OK", personas[0])
}
if r.Value.(string) == "" {
t.Errorf("Persona(%q) returned empty string", personas[0])
}
}
// --- Template ---
func TestTemplate_Good_Prompt(t *testing.T) {
r := Template("coding")
if !r.OK {
t.Fatal("Template('coding') returned !OK")
}
if r.Value.(string) == "" {
t.Error("Template('coding') returned empty string")
}
}
func TestTemplate_Good_TaskFallback(t *testing.T) {
r := Template("bug-fix")
if !r.OK {
t.Fatal("Template('bug-fix') returned !OK — should fall through to Task")
}
}
func TestTemplate_Bad(t *testing.T) {
r := Template("nonexistent-slug")
if r.OK {
t.Error("Template('nonexistent-slug') should return !OK")
}
}
// --- List Functions ---
func TestListPrompts(t *testing.T) {
prompts := ListPrompts()
if len(prompts) == 0 {
t.Error("ListPrompts() returned empty")
}
}
func TestListTasks(t *testing.T) {
tasks := ListTasks()
if len(tasks) == 0 {
t.Fatal("ListTasks() returned empty")
}
// Verify nested paths are included (e.g., "code/review")
found := false
for _, s := range tasks {
if s == "code/review" {
found = true
break
}
}
if !found {
t.Error("ListTasks() missing nested path 'code/review'")
}
}
func TestListPersonas(t *testing.T) {
personas := ListPersonas()
if len(personas) == 0 {
t.Error("ListPersonas() returned empty")
}
// Should have nested paths like "code/go"
hasNested := false
for _, p := range personas {
if len(p) > 0 && filepath.Dir(p) != "." {
hasNested = true
break
}
}
if !hasNested {
t.Error("ListPersonas() has no nested paths")
}
}
func TestListFlows(t *testing.T) {
flows := ListFlows()
if len(flows) == 0 {
t.Error("ListFlows() returned empty")
}
}
func TestListWorkspaces(t *testing.T) {
workspaces := ListWorkspaces()
if len(workspaces) == 0 {
t.Error("ListWorkspaces() returned empty")
}
}
// --- ExtractWorkspace ---
func TestExtractWorkspace_CreatesFiles(t *testing.T) {
dir := t.TempDir()
data := &WorkspaceData{Repo: "test-repo", Task: "test task"}
@ -15,7 +206,6 @@ func TestExtractWorkspace_CreatesFiles(t *testing.T) {
t.Fatalf("ExtractWorkspace failed: %v", err)
}
// Check top-level template files exist
for _, name := range []string{"CODEX.md", "CLAUDE.md", "PROMPT.md", "TODO.md", "CONTEXT.md"} {
path := filepath.Join(dir, name)
if _, err := os.Stat(path); os.IsNotExist(err) {
@ -33,19 +223,16 @@ func TestExtractWorkspace_CreatesSubdirectories(t *testing.T) {
t.Fatalf("ExtractWorkspace failed: %v", err)
}
// Check .core/reference/ directory exists with files
refDir := filepath.Join(dir, ".core", "reference")
if _, err := os.Stat(refDir); os.IsNotExist(err) {
t.Fatalf(".core/reference/ directory not created")
}
// Check AX spec exists
axSpec := filepath.Join(refDir, "RFC-025-AGENT-EXPERIENCE.md")
if _, err := os.Stat(axSpec); os.IsNotExist(err) {
t.Errorf("AX spec not extracted: %s", axSpec)
}
// Check Core source files exist
entries, err := os.ReadDir(refDir)
if err != nil {
t.Fatalf("failed to read reference dir: %v", err)
@ -61,7 +248,6 @@ func TestExtractWorkspace_CreatesSubdirectories(t *testing.T) {
t.Error("no .go files in .core/reference/")
}
// Check docs subdirectory
docsDir := filepath.Join(refDir, "docs")
if _, err := os.Stat(docsDir); os.IsNotExist(err) {
t.Errorf(".core/reference/docs/ not created")
@ -77,7 +263,6 @@ func TestExtractWorkspace_TemplateSubstitution(t *testing.T) {
t.Fatalf("ExtractWorkspace failed: %v", err)
}
// TODO.md should contain the task
content, err := os.ReadFile(filepath.Join(dir, "TODO.md"))
if err != nil {
t.Fatalf("failed to read TODO.md: %v", err)

View file

@ -1,4 +0,0 @@
.idea/
.vscode/
*.log
.core/

View file

@ -0,0 +1,3 @@
go 1.26.0
use .