From f104c6aa5ea585c94079c05c813229d70eed03b3 Mon Sep 17 00:00:00 2001 From: Levi Neely Date: Wed, 11 Feb 2026 11:35:08 +0100 Subject: [PATCH] rethink save/load --- internal/backends/kirocli.go | 43 +----------------------------------- internal/p9/server.go | 20 ++--------------- internal/sandbox/wrapper.go | 37 ++++++++++++++++++++++--------- main.go | 28 +---------------------- 4 files changed, 30 insertions(+), 98 deletions(-) diff --git a/internal/backends/kirocli.go b/internal/backends/kirocli.go index 1632f72..17cf621 100644 --- a/internal/backends/kirocli.go +++ b/internal/backends/kirocli.go @@ -4,7 +4,6 @@ package backends import ( "anvillm/internal/backend" "anvillm/internal/backend/tmux" - "anvillm/internal/debug" "context" "fmt" "os" @@ -301,7 +300,7 @@ func stripANSI(s string) string { // SessionsDir returns the path to the sessions storage directory func SessionsDir() string { home, _ := os.UserHomeDir() - dir := filepath.Join(home, ".config", "acme-q", "sessions") + dir := filepath.Join(home, ".config", "anvillm", "sessions") os.MkdirAll(dir, 0755) return dir } @@ -395,48 +394,8 @@ func contains(slice []string, item string) bool { return false } -// SavePath returns the save file path for a session ID -func SavePath(id string) string { - return filepath.Join(SessionsDir(), id+".json") -} - // SendCtl is a helper to send a control command to kiro-cli func SendCtl(sess backend.Session, cmd string) error { _, err := sess.Send(context.Background(), cmd) return err } - -// Save saves a session using kiro-cli's /chat save command -func Save(sess backend.Session, id string) error { - return SendCtl(sess, "/chat save "+SavePath(id)) -} - -// Load loads a session using kiro-cli's /chat load command -func Load(sess backend.Session, path string) error { - return SendCtl(sess, "/chat load "+path) -} - -// SaveWithSmartFilename saves with a generated filename -func SaveWithSmartFilename(sess backend.Session, id string, bodyText string) (string, error) { - tempPath := SavePath(id) - if err := SendCtl(sess, "/chat save "+tempPath); err != nil { - return "", err - } - - smartPath, err := GenerateSmartFilename(tempPath, bodyText) - if err != nil { - debug.Log("[session %s] Smart filename failed: %v, keeping %s", id, err, tempPath) - return tempPath, nil - } - - if smartPath != tempPath { - if err := os.Rename(tempPath, smartPath); err == nil { - return smartPath, nil - } else { - debug.Log("[session %s] Rename failed: %v", id, err) - return tempPath, nil - } - } - - return smartPath, nil -} diff --git a/internal/p9/server.go b/internal/p9/server.go index c762d82..2eca38c 100644 --- a/internal/p9/server.go +++ b/internal/p9/server.go @@ -4,7 +4,6 @@ package p9 import ( "anvillm/internal/backend" "anvillm/internal/backend/tmux" - "anvillm/internal/backends" "anvillm/internal/session" "context" "errors" @@ -29,7 +28,7 @@ agent/ ctl (write) "new " creates session, returns id list (read) list sessions: "id state pid cwd" {session-id}/ - ctl (write) "save [path]", "load path", "kill" + ctl (write) "stop", "kill" in (write) send prompt, blocks until response out (read) response from last prompt err (read) stderr/debug output @@ -322,7 +321,7 @@ func (s *Server) write(cs *connState, fc *plan9.Fcall) *plan9.Fcall { } args := strings.Fields(input) if len(args) == 0 { - return errFcall(fc, "usage: stop | save [path] | load path | kill") + return errFcall(fc, "usage: stop | kill") } switch args[0] { case "stop": @@ -330,21 +329,6 @@ func (s *Server) write(cs *connState, fc *plan9.Fcall) *plan9.Fcall { if err := sess.Stop(ctx); err != nil { return errFcall(fc, err.Error()) } - case "save": - path := backends.SavePath(sess.ID()) - if len(args) > 1 { - path = args[1] - } - if err := backends.SendCtl(sess, "/chat save "+path); err != nil { - return errFcall(fc, err.Error()) - } - case "load": - if len(args) < 2 { - return errFcall(fc, "load requires path") - } - if err := backends.Load(sess, args[1]); err != nil { - return errFcall(fc, err.Error()) - } case "kill": sess.Close() s.mgr.Remove(sess.ID()) diff --git a/internal/sandbox/wrapper.go b/internal/sandbox/wrapper.go index f08cb77..8c0d5c6 100644 --- a/internal/sandbox/wrapper.go +++ b/internal/sandbox/wrapper.go @@ -3,9 +3,16 @@ package sandbox import ( "fmt" "os" + "sort" "strings" ) +// pathEntry holds a path with its permission type +type pathEntry struct { + path string + flag string // --ro, --rox, --rw, --rwx +} + // WrapCommand wraps a command with landrun based on the configuration. // Returns the wrapped command args, or the original command if landrun is unavailable. func WrapCommand(cfg *Config, originalCmd []string, cwd string) []string { @@ -44,38 +51,46 @@ func WrapCommand(cfg *Config, originalCmd []string, cwd string) []string { args = append(args, "--add-exec") } - // Filesystem permissions - RO (read-only, no execute) + // Collect all filesystem paths + var entries []pathEntry for _, path := range cfg.Filesystem.RO { expanded := expandPath(path, cwd, home, tmpdir) if pathExists(expanded) { - args = append(args, "--ro", expanded) + entries = append(entries, pathEntry{expanded, "--ro"}) } } - - // Filesystem permissions - ROX (read-only with execute) for _, path := range cfg.Filesystem.ROX { expanded := expandPath(path, cwd, home, tmpdir) if pathExists(expanded) { - args = append(args, "--rox", expanded) + entries = append(entries, pathEntry{expanded, "--rox"}) } } - - // Filesystem permissions - RW (read-write, no execute) for _, path := range cfg.Filesystem.RW { expanded := expandPath(path, cwd, home, tmpdir) if pathExists(expanded) { - args = append(args, "--rw", expanded) + entries = append(entries, pathEntry{expanded, "--rw"}) } } - - // Filesystem permissions - RWX (read-write with execute) for _, path := range cfg.Filesystem.RWX { expanded := expandPath(path, cwd, home, tmpdir) if pathExists(expanded) { - args = append(args, "--rwx", expanded) + entries = append(entries, pathEntry{expanded, "--rwx"}) } } + // Sort paths so parents come before children (shorter paths first, then alphabetically) + sort.Slice(entries, func(i, j int) bool { + if len(entries[i].path) != len(entries[j].path) { + return len(entries[i].path) < len(entries[j].path) + } + return entries[i].path < entries[j].path + }) + + // Add sorted paths to args + for _, e := range entries { + args = append(args, e.flag, e.path) + } + // Network permissions if cfg.Network.Unrestricted { args = append(args, "--unrestricted-network") diff --git a/main.go b/main.go index 721e1df..a04c403 100644 --- a/main.go +++ b/main.go @@ -297,7 +297,7 @@ func openChatWindow(sess backend.Session) (*acme.Win, error) { return nil, err } w.Name(name) - w.Write("tag", []byte("Send Stop Attach Alias Save Sessions ")) + w.Write("tag", []byte("Send Stop Attach Alias Sessions ")) w.Fprintf("body", "# Session %s\n# cwd: %s\n\nUSER:\n", sess.ID(), meta.Cwd) w.Ctl("clean") @@ -372,32 +372,6 @@ func handleChatWindow(w *acme.Win, sess backend.Session) { newName := filepath.Join(meta.Cwd, fmt.Sprintf("+Chat.%s", arg)) w.Name(newName) fmt.Printf("Alias set to %s\n", arg) - case "Save": - // Grab first part of window content for filename hints - var bodyText string - if body, err := w.ReadAll("body"); err == nil && len(body) > 0 { - if len(body) > 500 { - body = body[:500] - } - bodyText = string(body) - } - go func() { - var savePath string - var err error - if arg != "" { - // Save to explicit path - savePath = arg - err = backends.SendCtl(sess, "/chat save "+savePath) - } else { - // Save with smart filename - savePath, err = backends.SaveWithSmartFilename(sess, sess.ID(), bodyText) - } - if err != nil { - fmt.Fprintf(os.Stderr, "Error saving: %v\n", err) - return - } - fmt.Printf("Saved to %s\n", savePath) - }() case "Sessions": openSessionsWindow(w, sess) default: