// SPDX-License-Identifier: EUPL-1.2 package agentic import ( "context" iofs "io/fs" "sort" core "dappco.re/go/core" ) const brainSeedMemoryDefaultAgent = "virgil" const brainSeedMemoryDefaultPath = "~/.claude/projects/*/memory/" type BrainSeedMemoryInput struct { WorkspaceID int AgentID string Path string DryRun bool } type BrainSeedMemoryOutput struct { Success bool `json:"success"` WorkspaceID int `json:"workspace_id,omitempty"` AgentID string `json:"agent_id,omitempty"` Path string `json:"path,omitempty"` Files int `json:"files,omitempty"` Imported int `json:"imported,omitempty"` Skipped int `json:"skipped,omitempty"` DryRun bool `json:"dry_run,omitempty"` } type brainSeedMemorySection struct { Heading string Content string } // result := c.Command("brain/seed-memory").Run(ctx, core.NewOptions( // // core.Option{Key: "workspace", Value: "1"}, // core.Option{Key: "path", Value: "/Users/snider/.claude/projects/*/memory/"}, // // )) func (s *PrepSubsystem) cmdBrainSeedMemory(options core.Options) core.Result { return s.cmdBrainSeedMemoryLike(options, "brain seed-memory", "agentic.cmdBrainSeedMemory") } // result := c.Command("brain/ingest").Run(ctx, core.NewOptions( // // core.Option{Key: "workspace", Value: "1"}, // core.Option{Key: "path", Value: "/Users/snider/.claude/projects/*/memory/"}, // // )) func (s *PrepSubsystem) cmdBrainIngest(options core.Options) core.Result { return s.cmdBrainSeedMemoryLikeMode(options, "brain ingest", "agentic.cmdBrainIngest", false) } func (s *PrepSubsystem) cmdBrainSeedMemoryLike(options core.Options, commandName string, errorLabel string) core.Result { return s.cmdBrainSeedMemoryLikeMode(options, commandName, errorLabel, true) } func (s *PrepSubsystem) cmdBrainSeedMemoryLikeMode(options core.Options, commandName string, errorLabel string, memoryFilesOnly bool) core.Result { input := BrainSeedMemoryInput{ WorkspaceID: parseIntString(optionStringValue(options, "workspace", "workspace_id", "workspace-id", "_arg")), AgentID: optionStringValue(options, "agent", "agent_id", "agent-id"), Path: optionStringValue(options, "path"), DryRun: optionBoolValue(options, "dry-run"), } if input.WorkspaceID == 0 { core.Print(nil, "usage: core-agent %s --workspace=1 [--agent=virgil] [--path=~/.claude/projects/*/memory/] [--dry-run]", commandName) return core.Result{Value: core.E(errorLabel, "workspace is required", nil), OK: false} } if input.AgentID == "" { input.AgentID = brainSeedMemoryDefaultAgent } if input.Path == "" { input.Path = brainSeedMemoryDefaultPath } result := s.brainSeedMemory(s.commandContext(), input, memoryFilesOnly) if !result.OK { err := commandResultError(errorLabel, result) core.Print(nil, "error: %v", err) return core.Result{Value: err, OK: false} } output, ok := result.Value.(BrainSeedMemoryOutput) if !ok { err := core.E(errorLabel, "invalid brain seed memory output", nil) core.Print(nil, "error: %v", err) return core.Result{Value: err, OK: false} } if output.Files == 0 { core.Print(nil, "No markdown memory files found in: %s", output.Path) return core.Result{Value: output, OK: true} } prefix := "" if output.DryRun { prefix = "[DRY RUN] " } core.Print(nil, "%sImported %d memories, skipped %d.", prefix, output.Imported, output.Skipped) return core.Result{Value: output, OK: true} } func (s *PrepSubsystem) brainSeedMemory(ctx context.Context, input BrainSeedMemoryInput, memoryFilesOnly bool) core.Result { if s.brainKey == "" { return core.Result{Value: core.E("agentic.brainSeedMemory", "no brain API key configured", nil), OK: false} } scanPath := brainSeedMemoryScanPath(input.Path) files := brainSeedMemoryFiles(scanPath, memoryFilesOnly) output := BrainSeedMemoryOutput{ Success: true, WorkspaceID: input.WorkspaceID, AgentID: input.AgentID, Path: scanPath, Files: len(files), DryRun: input.DryRun, } if len(files) == 0 { return core.Result{Value: output, OK: true} } for _, path := range files { readResult := fs.Read(path) if !readResult.OK { output.Skipped++ continue } sections := brainSeedMemorySections(readResult.Value.(string)) if len(sections) == 0 { output.Skipped++ core.Print(nil, " Skipped %s (no sections found)", core.PathBase(path)) continue } project := brainSeedMemoryProject(path) filename := core.TrimSuffix(core.PathBase(path), ".md") for _, section := range sections { memoryType := brainSeedMemoryType(section.Heading, section.Content) if input.DryRun { core.Print(nil, " [DRY RUN] %s :: %s (%s) — %d chars", core.PathBase(path), section.Heading, memoryType, core.RuneCount(section.Content)) output.Imported++ continue } body := map[string]any{ "workspace_id": input.WorkspaceID, "agent_id": input.AgentID, "type": memoryType, "content": core.Concat(section.Heading, "\n\n", section.Content), "tags": brainSeedMemoryTags(filename), "project": project, "confidence": 0.7, } if result := HTTPPost(ctx, core.Concat(s.brainURL, "/v1/brain/remember"), core.JSONMarshalString(body), s.brainKey, "Bearer"); !result.OK { output.Skipped++ core.Print(nil, " Failed to import %s :: %s", core.PathBase(path), section.Heading) continue } output.Imported++ } } return core.Result{Value: output, OK: true} } func brainSeedMemoryScanPath(path string) string { trimmed := brainSeedMemoryExpandHome(core.Trim(path)) if trimmed == "" { return brainSeedMemoryExpandHome(brainSeedMemoryDefaultPath) } if fs.IsFile(trimmed) { return trimmed } return trimmed } func brainSeedMemoryExpandHome(path string) string { if core.HasPrefix(path, "~/") { return core.Concat(HomeDir(), core.TrimPrefix(path, "~")) } return path } func brainSeedMemoryFiles(scanPath string, memoryFilesOnly bool) []string { if scanPath == "" { return nil } var files []string seen := map[string]struct{}{} add := func(path string) { if path == "" { return } if _, ok := seen[path]; ok { return } seen[path] = struct{}{} files = append(files, path) } var walk func(string) walk = func(dir string) { if fs.IsFile(dir) { if brainSeedMemoryFile(dir, memoryFilesOnly) { add(dir) } return } if !fs.IsDir(dir) { return } r := fs.List(dir) if !r.OK { return } entries, ok := r.Value.([]iofs.DirEntry) if !ok { return } for _, entry := range entries { next := core.JoinPath(dir, entry.Name()) if entry.IsDir() { walk(next) continue } if brainSeedMemoryFile(next, memoryFilesOnly) { add(next) } } } if fs.IsFile(scanPath) { if brainSeedMemoryFile(scanPath, memoryFilesOnly) { add(scanPath) } sort.Strings(files) return files } if brainSeedMemoryHasGlobMeta(scanPath) { for _, path := range core.PathGlob(scanPath) { if fs.IsFile(path) { if brainSeedMemoryFile(path, memoryFilesOnly) { add(path) } continue } walk(path) } } else { walk(scanPath) } sort.Strings(files) return files } func brainSeedMemoryHasGlobMeta(path string) bool { return core.Contains(path, "*") || core.Contains(path, "?") || core.Contains(path, "[") } func brainSeedMemoryFile(path string, memoryFilesOnly bool) bool { if memoryFilesOnly { return core.PathBase(path) == "MEMORY.md" } return core.Lower(core.PathExt(path)) == ".md" } func brainSeedMemorySections(content string) []brainSeedMemorySection { lines := core.Split(content, "\n") var sections []brainSeedMemorySection currentHeading := "" var currentContent []string flush := func() { if currentHeading == "" || len(currentContent) == 0 { return } sectionContent := core.Trim(core.Join("\n", currentContent...)) if sectionContent == "" { return } sections = append(sections, brainSeedMemorySection{ Heading: currentHeading, Content: sectionContent, }) } for _, line := range lines { if heading, ok := brainSeedMemoryHeading(line); ok { flush() currentHeading = heading currentContent = currentContent[:0] continue } if currentHeading == "" { continue } currentContent = append(currentContent, line) } flush() return sections } func brainSeedMemoryHeading(line string) (string, bool) { trimmed := core.Trim(line) if trimmed == "" || !core.HasPrefix(trimmed, "#") { return "", false } hashes := 0 for _, r := range trimmed { if r != '#' { break } hashes++ } if hashes < 1 || hashes > 3 || len(trimmed) <= hashes || trimmed[hashes] != ' ' { return "", false } heading := core.Trim(trimmed[hashes:]) if heading == "" { return "", false } return heading, true } func brainSeedMemoryType(heading, content string) string { lower := core.Lower(core.Concat(heading, " ", content)) for _, candidate := range []struct { memoryType string keywords []string }{ {memoryType: "architecture", keywords: []string{"architecture", "stack", "infrastructure", "layer", "service mesh"}}, {memoryType: "convention", keywords: []string{"convention", "standard", "naming", "pattern", "rule", "coding"}}, {memoryType: "decision", keywords: []string{"decision", "chose", "strategy", "approach", "domain"}}, {memoryType: "bug", keywords: []string{"bug", "fix", "broken", "error", "issue", "lesson"}}, {memoryType: "plan", keywords: []string{"plan", "todo", "roadmap", "milestone", "phase"}}, {memoryType: "research", keywords: []string{"research", "finding", "discovery", "analysis", "rfc"}}, } { for _, keyword := range candidate.keywords { if core.Contains(lower, keyword) { return candidate.memoryType } } } return "observation" } func brainSeedMemoryTags(filename string) []string { if filename == "" { return []string{"memory-import"} } tags := []string{} if core.Lower(filename) != "memory" { tag := core.Replace(core.Replace(filename, "-", " "), "_", " ") if tag != "" { tags = append(tags, tag) } } tags = append(tags, "memory-import") return tags } func brainSeedMemoryProject(path string) string { normalised := core.Replace(path, "\\", "/") segments := core.Split(normalised, "/") for i := 1; i < len(segments); i++ { if segments[i] != "memory" { continue } projectSegment := segments[i-1] if projectSegment == "" { continue } chunks := core.Split(projectSegment, "-") for j := len(chunks) - 1; j >= 0; j-- { if chunks[j] != "" { return chunks[j] } } } return "" }