Spaces:
Sleeping
Sleeping
| // PicoClaw - Ultra-lightweight personal AI agent | |
| // Inspired by and based on nanobot: https://github.com/HKUDS/nanobot | |
| // License: MIT | |
| // | |
| // Copyright (c) 2026 PicoClaw contributors | |
| package agent | |
| import ( | |
| "fmt" | |
| "os" | |
| "path/filepath" | |
| "time" | |
| ) | |
| // MemoryStore manages persistent memory for the agent. | |
| // - Long-term memory: memory/MEMORY.md | |
| // - Daily notes: memory/YYYYMM/YYYYMMDD.md | |
| type MemoryStore struct { | |
| workspace string | |
| memoryDir string | |
| memoryFile string | |
| } | |
| // NewMemoryStore creates a new MemoryStore with the given workspace path. | |
| // It ensures the memory directory exists. | |
| func NewMemoryStore(workspace string) *MemoryStore { | |
| memoryDir := filepath.Join(workspace, "memory") | |
| memoryFile := filepath.Join(memoryDir, "MEMORY.md") | |
| // Ensure memory directory exists | |
| os.MkdirAll(memoryDir, 0755) | |
| return &MemoryStore{ | |
| workspace: workspace, | |
| memoryDir: memoryDir, | |
| memoryFile: memoryFile, | |
| } | |
| } | |
| // getTodayFile returns the path to today's daily note file (memory/YYYYMM/YYYYMMDD.md). | |
| func (ms *MemoryStore) getTodayFile() string { | |
| today := time.Now().Format("20060102") // YYYYMMDD | |
| monthDir := today[:6] // YYYYMM | |
| filePath := filepath.Join(ms.memoryDir, monthDir, today+".md") | |
| return filePath | |
| } | |
| // ReadLongTerm reads the long-term memory (MEMORY.md). | |
| // Returns empty string if the file doesn't exist. | |
| func (ms *MemoryStore) ReadLongTerm() string { | |
| if data, err := os.ReadFile(ms.memoryFile); err == nil { | |
| return string(data) | |
| } | |
| return "" | |
| } | |
| // WriteLongTerm writes content to the long-term memory file (MEMORY.md). | |
| func (ms *MemoryStore) WriteLongTerm(content string) error { | |
| return os.WriteFile(ms.memoryFile, []byte(content), 0644) | |
| } | |
| // ReadToday reads today's daily note. | |
| // Returns empty string if the file doesn't exist. | |
| func (ms *MemoryStore) ReadToday() string { | |
| todayFile := ms.getTodayFile() | |
| if data, err := os.ReadFile(todayFile); err == nil { | |
| return string(data) | |
| } | |
| return "" | |
| } | |
| // AppendToday appends content to today's daily note. | |
| // If the file doesn't exist, it creates a new file with a date header. | |
| func (ms *MemoryStore) AppendToday(content string) error { | |
| todayFile := ms.getTodayFile() | |
| // Ensure month directory exists | |
| monthDir := filepath.Dir(todayFile) | |
| os.MkdirAll(monthDir, 0755) | |
| var existingContent string | |
| if data, err := os.ReadFile(todayFile); err == nil { | |
| existingContent = string(data) | |
| } | |
| var newContent string | |
| if existingContent == "" { | |
| // Add header for new day | |
| header := fmt.Sprintf("# %s\n\n", time.Now().Format("2006-01-02")) | |
| newContent = header + content | |
| } else { | |
| // Append to existing content | |
| newContent = existingContent + "\n" + content | |
| } | |
| return os.WriteFile(todayFile, []byte(newContent), 0644) | |
| } | |
| // GetRecentDailyNotes returns daily notes from the last N days. | |
| // Contents are joined with "---" separator. | |
| func (ms *MemoryStore) GetRecentDailyNotes(days int) string { | |
| var notes []string | |
| for i := 0; i < days; i++ { | |
| date := time.Now().AddDate(0, 0, -i) | |
| dateStr := date.Format("20060102") // YYYYMMDD | |
| monthDir := dateStr[:6] // YYYYMM | |
| filePath := filepath.Join(ms.memoryDir, monthDir, dateStr+".md") | |
| if data, err := os.ReadFile(filePath); err == nil { | |
| notes = append(notes, string(data)) | |
| } | |
| } | |
| if len(notes) == 0 { | |
| return "" | |
| } | |
| // Join with separator | |
| var result string | |
| for i, note := range notes { | |
| if i > 0 { | |
| result += "\n\n---\n\n" | |
| } | |
| result += note | |
| } | |
| return result | |
| } | |
| // GetMemoryContext returns formatted memory context for the agent prompt. | |
| // Includes long-term memory and recent daily notes. | |
| func (ms *MemoryStore) GetMemoryContext() string { | |
| var parts []string | |
| // Long-term memory | |
| longTerm := ms.ReadLongTerm() | |
| if longTerm != "" { | |
| parts = append(parts, "## Long-term Memory\n\n"+longTerm) | |
| } | |
| // Recent daily notes (last 3 days) | |
| recentNotes := ms.GetRecentDailyNotes(3) | |
| if recentNotes != "" { | |
| parts = append(parts, "## Recent Daily Notes\n\n"+recentNotes) | |
| } | |
| if len(parts) == 0 { | |
| return "" | |
| } | |
| // Join parts with separator | |
| var result string | |
| for i, part := range parts { | |
| if i > 0 { | |
| result += "\n\n---\n\n" | |
| } | |
| result += part | |
| } | |
| return fmt.Sprintf("# Memory\n\n%s", result) | |
| } | |