From b4ff6781b3eea88a5ea7ad87eb75017668bc9beb Mon Sep 17 00:00:00 2001 From: rictus Date: Thu, 1 Jan 2026 19:18:23 -0800 Subject: [PATCH] feat(connection): Add LocalConnection implementation (gt-f9x.8) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement LocalConnection that provides local file operations, command execution, and tmux integration: - File ops: ReadFile, WriteFile, MkdirAll, Remove, RemoveAll, Stat, Glob, Exists - Command exec: Exec, ExecDir, ExecEnv - Tmux: NewSession, KillSession, SendKeys, CapturePane, HasSession, ListSessions Uses proper error wrapping with NotFoundError and PermissionError types. Integrates with existing internal/tmux package for session management. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- internal/connection/local.go | 189 +++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 internal/connection/local.go diff --git a/internal/connection/local.go b/internal/connection/local.go new file mode 100644 index 00000000..05aeba94 --- /dev/null +++ b/internal/connection/local.go @@ -0,0 +1,189 @@ +package connection + +import ( + "io/fs" + "os" + "os/exec" + "path/filepath" + + "github.com/steveyegge/gastown/internal/tmux" +) + +// LocalConnection implements Connection for local file and command operations. +type LocalConnection struct { + tmux *tmux.Tmux +} + +// NewLocalConnection creates a new local connection. +func NewLocalConnection() *LocalConnection { + return &LocalConnection{ + tmux: tmux.NewTmux(), + } +} + +// Name returns "local" for local connections. +func (c *LocalConnection) Name() string { + return "local" +} + +// IsLocal returns true for local connections. +func (c *LocalConnection) IsLocal() bool { + return true +} + +// ReadFile reads the named file. +func (c *LocalConnection) ReadFile(path string) ([]byte, error) { + data, err := os.ReadFile(path) + if err != nil { + if os.IsNotExist(err) { + return nil, &NotFoundError{Path: path} + } + if os.IsPermission(err) { + return nil, &PermissionError{Path: path, Op: "read"} + } + return nil, err + } + return data, nil +} + +// WriteFile writes data to the named file. +func (c *LocalConnection) WriteFile(path string, data []byte, perm fs.FileMode) error { + err := os.WriteFile(path, data, perm) + if err != nil { + if os.IsPermission(err) { + return &PermissionError{Path: path, Op: "write"} + } + return err + } + return nil +} + +// MkdirAll creates a directory and all parent directories. +func (c *LocalConnection) MkdirAll(path string, perm fs.FileMode) error { + err := os.MkdirAll(path, perm) + if err != nil { + if os.IsPermission(err) { + return &PermissionError{Path: path, Op: "mkdir"} + } + return err + } + return nil +} + +// Remove removes the named file or empty directory. +func (c *LocalConnection) Remove(path string) error { + err := os.Remove(path) + if err != nil { + if os.IsNotExist(err) { + return nil // Already gone + } + if os.IsPermission(err) { + return &PermissionError{Path: path, Op: "remove"} + } + return err + } + return nil +} + +// RemoveAll removes the named file or directory and any children. +func (c *LocalConnection) RemoveAll(path string) error { + err := os.RemoveAll(path) + if err != nil { + if os.IsPermission(err) { + return &PermissionError{Path: path, Op: "remove"} + } + return err + } + return nil +} + +// Stat returns file info for the named file. +func (c *LocalConnection) Stat(path string) (FileInfo, error) { + fi, err := os.Stat(path) + if err != nil { + if os.IsNotExist(err) { + return nil, &NotFoundError{Path: path} + } + if os.IsPermission(err) { + return nil, &PermissionError{Path: path, Op: "stat"} + } + return nil, err + } + return FromOSFileInfo(fi), nil +} + +// Glob returns the names of all files matching the pattern. +func (c *LocalConnection) Glob(pattern string) ([]string, error) { + matches, err := filepath.Glob(pattern) + if err != nil { + return nil, err + } + return matches, nil +} + +// Exists returns true if the path exists. +func (c *LocalConnection) Exists(path string) (bool, error) { + _, err := os.Stat(path) + if err != nil { + if os.IsNotExist(err) { + return false, nil + } + return false, err + } + return true, nil +} + +// Exec runs a command and returns its combined output. +func (c *LocalConnection) Exec(cmd string, args ...string) ([]byte, error) { + return exec.Command(cmd, args...).CombinedOutput() +} + +// ExecDir runs a command in the specified directory. +func (c *LocalConnection) ExecDir(dir, cmd string, args ...string) ([]byte, error) { + command := exec.Command(cmd, args...) + command.Dir = dir + return command.CombinedOutput() +} + +// ExecEnv runs a command with additional environment variables. +func (c *LocalConnection) ExecEnv(env map[string]string, cmd string, args ...string) ([]byte, error) { + command := exec.Command(cmd, args...) + command.Env = os.Environ() + for k, v := range env { + command.Env = append(command.Env, k+"="+v) + } + return command.CombinedOutput() +} + +// TmuxNewSession creates a new tmux session. +func (c *LocalConnection) TmuxNewSession(name, dir string) error { + return c.tmux.NewSession(name, dir) +} + +// TmuxKillSession terminates a tmux session. +func (c *LocalConnection) TmuxKillSession(name string) error { + return c.tmux.KillSession(name) +} + +// TmuxSendKeys sends keys to a tmux session. +func (c *LocalConnection) TmuxSendKeys(session, keys string) error { + return c.tmux.SendKeys(session, keys) +} + +// TmuxCapturePane captures the last N lines from a tmux pane. +func (c *LocalConnection) TmuxCapturePane(session string, lines int) (string, error) { + return c.tmux.CapturePane(session, lines) +} + +// TmuxHasSession returns true if the session exists. +func (c *LocalConnection) TmuxHasSession(name string) (bool, error) { + return c.tmux.HasSession(name) +} + +// TmuxListSessions returns all tmux session names. +func (c *LocalConnection) TmuxListSessions() ([]string, error) { + return c.tmux.ListSessions() +} + +// Verify LocalConnection implements Connection. +var _ Connection = (*LocalConnection)(nil)