diff --git a/pkg/brain/direct.go b/pkg/brain/direct.go index 9b2dc9d..fa13651 100644 --- a/pkg/brain/direct.go +++ b/pkg/brain/direct.go @@ -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) diff --git a/pkg/brain/direct_test.go b/pkg/brain/direct_test.go index b87c08d..ea0f686 100644 --- a/pkg/brain/direct_test.go +++ b/pkg/brain/direct_test.go @@ -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) diff --git a/pkg/brain/provider.go b/pkg/brain/provider.go index 2b64a61..dd8bcd2 100644 --- a/pkg/brain/provider.go +++ b/pkg/brain/provider.go @@ -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 { diff --git a/pkg/brain/tools.go b/pkg/brain/tools.go index ae7f74b..c693b4d 100644 --- a/pkg/brain/tools.go +++ b/pkg/brain/tools.go @@ -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"`