Skip to content

Commit 36a3bbf

Browse files
feat(store,cli): add cascade project delete and delete sub-commands for sessions and prompts (#441)
Implements store.DeleteProject (hard/soft, transactional, orphans memory_relations) and three new CLI sub-commands: engram delete session <id>, engram delete prompt <id>, engram delete project <name> [--hard]. Backward-compat: bare engram delete <obs_id> still works. README and ARCHITECTURE CLI reference tables updated. Closes #218 Closes #219
1 parent ab86d8e commit 36a3bbf

6 files changed

Lines changed: 705 additions & 2 deletions

File tree

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,9 @@ Your production engram is fully untouched throughout.
308308
| `engram search <query>` | Search memories |
309309
| `engram save <title> <msg>` | Save a memory |
310310
| `engram delete <obs_id>` | Delete an observation (soft by default; `--hard` removes permanently) |
311+
| `engram delete session <id>` | Delete a session by ID (must have no observations) |
312+
| `engram delete prompt <id>` | Delete a prompt by ID (permanent) |
313+
| `engram delete project <name> [--hard]` | Cascade-delete a project: soft-deletes observations by default (`--hard` removes permanently and also removes sessions) |
311314
| `engram timeline <obs_id>` | Chronological context |
312315
| `engram context [project]` | Recent session context |
313316
| `engram stats` | Memory statistics |

cmd/engram/main.go

Lines changed: 125 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,13 @@ var (
8888
storeSearch = func(s *store.Store, query string, opts store.SearchOptions) ([]store.SearchResult, error) {
8989
return s.Search(query, opts)
9090
}
91-
storeAddObservation = func(s *store.Store, p store.AddObservationParams) (int64, error) { return s.AddObservation(p) }
92-
storeDeleteObservation = func(s *store.Store, id int64, hard bool) error { return s.DeleteObservation(id, hard) }
91+
storeAddObservation = func(s *store.Store, p store.AddObservationParams) (int64, error) { return s.AddObservation(p) }
92+
storeDeleteObservation = func(s *store.Store, id int64, hard bool) error { return s.DeleteObservation(id, hard) }
93+
storeDeleteSession = func(s *store.Store, id string) error { return s.DeleteSession(id) }
94+
storeDeletePrompt = func(s *store.Store, id int64) error { return s.DeletePrompt(id) }
95+
storeDeleteProject = func(s *store.Store, name string, hard bool) (*store.DeleteProjectResult, error) {
96+
return s.DeleteProject(name, hard)
97+
}
9398
storeTimeline = func(s *store.Store, observationID int64, before, after int) (*store.TimelineResult, error) {
9499
return s.Timeline(observationID, before, after)
95100
}
@@ -1056,6 +1061,30 @@ func cmdSave(cfg store.Config) {
10561061
}
10571062

10581063
func cmdDelete(cfg store.Config) {
1064+
if len(os.Args) < 3 {
1065+
fmt.Fprintln(os.Stderr, "usage: engram delete <observation_id> [--hard]")
1066+
fmt.Fprintln(os.Stderr, " engram delete session <id>")
1067+
fmt.Fprintln(os.Stderr, " engram delete prompt <id>")
1068+
fmt.Fprintln(os.Stderr, " engram delete project <name> [--hard]")
1069+
exitFunc(1)
1070+
return
1071+
}
1072+
1073+
sub := os.Args[2]
1074+
switch sub {
1075+
case "session":
1076+
cmdDeleteSession(cfg)
1077+
case "prompt":
1078+
cmdDeletePrompt(cfg)
1079+
case "project":
1080+
cmdDeleteProject(cfg)
1081+
default:
1082+
// Backward-compat: treat the second arg as a numeric observation ID.
1083+
cmdDeleteObservation(cfg)
1084+
}
1085+
}
1086+
1087+
func cmdDeleteObservation(cfg store.Config) {
10591088
if len(os.Args) < 3 {
10601089
fmt.Fprintln(os.Stderr, "usage: engram delete <observation_id> [--hard]")
10611090
exitFunc(1)
@@ -1095,6 +1124,93 @@ func cmdDelete(cfg store.Config) {
10951124
fmt.Printf("Observation #%d %s\n", id, kind)
10961125
}
10971126

1127+
func cmdDeleteSession(cfg store.Config) {
1128+
if len(os.Args) < 4 {
1129+
fmt.Fprintln(os.Stderr, "usage: engram delete session <id>")
1130+
exitFunc(1)
1131+
return
1132+
}
1133+
1134+
id := os.Args[3]
1135+
1136+
s, err := storeNew(cfg)
1137+
if err != nil {
1138+
fatal(err)
1139+
return
1140+
}
1141+
defer s.Close()
1142+
1143+
if err := storeDeleteSession(s, id); err != nil {
1144+
fatal(err)
1145+
return
1146+
}
1147+
fmt.Printf("Session %q deleted\n", id)
1148+
}
1149+
1150+
func cmdDeletePrompt(cfg store.Config) {
1151+
if len(os.Args) < 4 {
1152+
fmt.Fprintln(os.Stderr, "usage: engram delete prompt <id>")
1153+
exitFunc(1)
1154+
return
1155+
}
1156+
1157+
id, err := strconv.ParseInt(os.Args[3], 10, 64)
1158+
if err != nil {
1159+
fmt.Fprintf(os.Stderr, "error: invalid prompt id %q\n", os.Args[3])
1160+
exitFunc(1)
1161+
return
1162+
}
1163+
1164+
s, err := storeNew(cfg)
1165+
if err != nil {
1166+
fatal(err)
1167+
return
1168+
}
1169+
defer s.Close()
1170+
1171+
if err := storeDeletePrompt(s, id); err != nil {
1172+
fatal(err)
1173+
return
1174+
}
1175+
fmt.Printf("Prompt #%d deleted\n", id)
1176+
}
1177+
1178+
func cmdDeleteProject(cfg store.Config) {
1179+
if len(os.Args) < 4 {
1180+
fmt.Fprintln(os.Stderr, "usage: engram delete project <name> [--hard]")
1181+
exitFunc(1)
1182+
return
1183+
}
1184+
1185+
name := os.Args[3]
1186+
hard := false
1187+
for i := 4; i < len(os.Args); i++ {
1188+
if os.Args[i] == "--hard" {
1189+
hard = true
1190+
}
1191+
}
1192+
1193+
s, err := storeNew(cfg)
1194+
if err != nil {
1195+
fatal(err)
1196+
return
1197+
}
1198+
defer s.Close()
1199+
1200+
result, err := storeDeleteProject(s, name, hard)
1201+
if err != nil {
1202+
fatal(err)
1203+
return
1204+
}
1205+
1206+
kind := "soft-deleted"
1207+
if hard {
1208+
kind = "hard-deleted"
1209+
}
1210+
fmt.Printf("Project %q %s: %d observation(s), %d prompt(s), %d session(s)\n",
1211+
result.Project, kind, result.ObservationsDeleted, result.PromptsDeleted, result.SessionsDeleted)
1212+
}
1213+
10981214
func cmdTimeline(cfg store.Config) {
10991215
if len(os.Args) < 3 {
11001216
fmt.Fprintln(os.Stderr, "usage: engram timeline <observation_id> [--before N] [--after N]")
@@ -2330,6 +2446,13 @@ Commands:
23302446
search <query> Search memories [--type TYPE] [--project PROJECT] [--scope SCOPE] [--limit N]
23312447
save <title> <msg> Save a memory [--type TYPE] [--project PROJECT] [--scope SCOPE]
23322448
delete <obs_id> Delete an observation [--hard] (soft-delete by default; --hard removes permanently)
2449+
delete session <id>
2450+
Delete a session by ID (session must have no observations)
2451+
delete prompt <id>
2452+
Delete a prompt by ID (permanent)
2453+
delete project <name> [--hard]
2454+
Cascade-delete a project: soft-deletes observations (or hard if --hard),
2455+
removes prompts; with --hard also removes sessions
23332456
timeline <obs_id> Show chronological context around an observation [--before N] [--after N]
23342457
conflicts <sub> Inspect and manage memory conflict relations
23352458
list [--project P] [--status S] [--since RFC3339] [--limit N]

0 commit comments

Comments
 (0)