Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,9 @@ Your production engram is fully untouched throughout.
| `engram search <query>` | Search memories |
| `engram save <title> <msg>` | Save a memory |
| `engram delete <obs_id>` | Delete an observation (soft by default; `--hard` removes permanently) |
| `engram delete session <id>` | Delete a session by ID (must have no observations) |
| `engram delete prompt <id>` | Delete a prompt by ID (permanent) |
| `engram delete project <name> [--hard]` | Cascade-delete a project: soft-deletes observations by default (`--hard` removes permanently and also removes sessions) |
| `engram timeline <obs_id>` | Chronological context |
| `engram context [project]` | Recent session context |
| `engram stats` | Memory statistics |
Expand Down
127 changes: 125 additions & 2 deletions cmd/engram/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,13 @@ var (
storeSearch = func(s *store.Store, query string, opts store.SearchOptions) ([]store.SearchResult, error) {
return s.Search(query, opts)
}
storeAddObservation = func(s *store.Store, p store.AddObservationParams) (int64, error) { return s.AddObservation(p) }
storeDeleteObservation = func(s *store.Store, id int64, hard bool) error { return s.DeleteObservation(id, hard) }
storeAddObservation = func(s *store.Store, p store.AddObservationParams) (int64, error) { return s.AddObservation(p) }
storeDeleteObservation = func(s *store.Store, id int64, hard bool) error { return s.DeleteObservation(id, hard) }
storeDeleteSession = func(s *store.Store, id string) error { return s.DeleteSession(id) }
storeDeletePrompt = func(s *store.Store, id int64) error { return s.DeletePrompt(id) }
storeDeleteProject = func(s *store.Store, name string, hard bool) (*store.DeleteProjectResult, error) {
return s.DeleteProject(name, hard)
}
storeTimeline = func(s *store.Store, observationID int64, before, after int) (*store.TimelineResult, error) {
return s.Timeline(observationID, before, after)
}
Expand Down Expand Up @@ -1056,6 +1061,30 @@ func cmdSave(cfg store.Config) {
}

func cmdDelete(cfg store.Config) {
if len(os.Args) < 3 {
fmt.Fprintln(os.Stderr, "usage: engram delete <observation_id> [--hard]")
fmt.Fprintln(os.Stderr, " engram delete session <id>")
fmt.Fprintln(os.Stderr, " engram delete prompt <id>")
fmt.Fprintln(os.Stderr, " engram delete project <name> [--hard]")
exitFunc(1)
return
}

sub := os.Args[2]
switch sub {
case "session":
cmdDeleteSession(cfg)
case "prompt":
cmdDeletePrompt(cfg)
case "project":
cmdDeleteProject(cfg)
default:
// Backward-compat: treat the second arg as a numeric observation ID.
cmdDeleteObservation(cfg)
}
}

func cmdDeleteObservation(cfg store.Config) {
if len(os.Args) < 3 {
fmt.Fprintln(os.Stderr, "usage: engram delete <observation_id> [--hard]")
exitFunc(1)
Expand Down Expand Up @@ -1095,6 +1124,93 @@ func cmdDelete(cfg store.Config) {
fmt.Printf("Observation #%d %s\n", id, kind)
}

func cmdDeleteSession(cfg store.Config) {
if len(os.Args) < 4 {
fmt.Fprintln(os.Stderr, "usage: engram delete session <id>")
exitFunc(1)
return
}

id := os.Args[3]

s, err := storeNew(cfg)
if err != nil {
fatal(err)
return
}
defer s.Close()

if err := storeDeleteSession(s, id); err != nil {
fatal(err)
return
}
fmt.Printf("Session %q deleted\n", id)
}

func cmdDeletePrompt(cfg store.Config) {
if len(os.Args) < 4 {
fmt.Fprintln(os.Stderr, "usage: engram delete prompt <id>")
exitFunc(1)
return
}

id, err := strconv.ParseInt(os.Args[3], 10, 64)
if err != nil {
fmt.Fprintf(os.Stderr, "error: invalid prompt id %q\n", os.Args[3])
exitFunc(1)
return
}

s, err := storeNew(cfg)
if err != nil {
fatal(err)
return
}
defer s.Close()

if err := storeDeletePrompt(s, id); err != nil {
fatal(err)
return
}
fmt.Printf("Prompt #%d deleted\n", id)
}

func cmdDeleteProject(cfg store.Config) {
if len(os.Args) < 4 {
fmt.Fprintln(os.Stderr, "usage: engram delete project <name> [--hard]")
exitFunc(1)
return
}

name := os.Args[3]
hard := false
for i := 4; i < len(os.Args); i++ {
if os.Args[i] == "--hard" {
hard = true
}
}

s, err := storeNew(cfg)
if err != nil {
fatal(err)
return
}
defer s.Close()

result, err := storeDeleteProject(s, name, hard)
if err != nil {
fatal(err)
return
}

kind := "soft-deleted"
if hard {
kind = "hard-deleted"
}
fmt.Printf("Project %q %s: %d observation(s), %d prompt(s), %d session(s)\n",
result.Project, kind, result.ObservationsDeleted, result.PromptsDeleted, result.SessionsDeleted)
}

func cmdTimeline(cfg store.Config) {
if len(os.Args) < 3 {
fmt.Fprintln(os.Stderr, "usage: engram timeline <observation_id> [--before N] [--after N]")
Expand Down Expand Up @@ -2330,6 +2446,13 @@ Commands:
search <query> Search memories [--type TYPE] [--project PROJECT] [--scope SCOPE] [--limit N]
save <title> <msg> Save a memory [--type TYPE] [--project PROJECT] [--scope SCOPE]
delete <obs_id> Delete an observation [--hard] (soft-delete by default; --hard removes permanently)
delete session <id>
Delete a session by ID (session must have no observations)
delete prompt <id>
Delete a prompt by ID (permanent)
delete project <name> [--hard]
Cascade-delete a project: soft-deletes observations (or hard if --hard),
removes prompts; with --hard also removes sessions
timeline <obs_id> Show chronological context around an observation [--before N] [--after N]
conflicts <sub> Inspect and manage memory conflict relations
list [--project P] [--status S] [--since RFC3339] [--limit N]
Expand Down
Loading
Loading