76 lines
2.1 KiB
Go
76 lines
2.1 KiB
Go
// Package memory persists Claude's own freeform notes between runs — its
|
|
// running thesis on positions, why past calls were made, and anything else
|
|
// it wants available as context for future decisions. It is intentionally
|
|
// just a flat text file: Claude rewrites it each run (Load hands back the
|
|
// previous content, Save overwrites with the new content extracted from the
|
|
// response), so summarization and pruning are Claude's job, not Go's.
|
|
package memory
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"gitea.konchin.com/service/stonks/internal/config"
|
|
)
|
|
|
|
const fileName = "memory.md"
|
|
|
|
// Load reads back the memory file from cfg.Dir. Returns "" with no error if
|
|
// it doesn't exist yet (e.g. first-ever run).
|
|
func Load(cfg config.PortfolioConfig) (string, error) {
|
|
dir, err := resolveDir(cfg.Dir)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
data, err := os.ReadFile(filepath.Join(dir, fileName))
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return "", nil
|
|
}
|
|
return "", fmt.Errorf("memory: read: %w", err)
|
|
}
|
|
return string(data), nil
|
|
}
|
|
|
|
// Save overwrites the memory file under cfg.Dir with content. A blank
|
|
// content is a no-op — it leaves whatever was there alone, rather than
|
|
// wiping out prior notes if Claude's response happened to omit the section.
|
|
func Save(cfg config.PortfolioConfig, content string) error {
|
|
content = strings.TrimSpace(content)
|
|
if content == "" {
|
|
return nil
|
|
}
|
|
|
|
dir, err := resolveDir(cfg.Dir)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := os.MkdirAll(dir, 0o755); err != nil {
|
|
return fmt.Errorf("memory: create %q: %w", dir, err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(dir, fileName), []byte(content+"\n"), 0o644); err != nil {
|
|
return fmt.Errorf("memory: write: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func resolveDir(dir string) (string, error) {
|
|
if dir == "" {
|
|
dir = "~/.local/state/stonks"
|
|
}
|
|
if dir != "~" && !strings.HasPrefix(dir, "~/") {
|
|
return dir, nil
|
|
}
|
|
home, err := os.UserHomeDir()
|
|
if err != nil {
|
|
return "", fmt.Errorf("memory: resolve home directory: %w", err)
|
|
}
|
|
if dir == "~" {
|
|
return home, nil
|
|
}
|
|
return filepath.Join(home, dir[2:]), nil
|
|
}
|