From b5560276fe3e6ab281dc13a9d3334996b89886a5 Mon Sep 17 00:00:00 2001 From: Joshua Gilman Date: Fri, 2 Jan 2026 14:32:21 -0800 Subject: [PATCH 1/2] Refactor CLI command helpers --- internal/cmd/attach.go | 30 ++++++++-------------------- internal/cmd/helpers.go | 43 ++++++++++++++++++++++++++++++++++++++++ internal/cmd/kill.go | 11 ++++------ internal/cmd/logs.go | 9 ++++----- internal/cmd/ps.go | 21 +++++++++++--------- internal/cmd/recreate.go | 18 ++++------------- internal/cmd/rm.go | 17 ++++------------ internal/cmd/run.go | 11 ++++++---- internal/cmd/stop.go | 18 ++++------------- 9 files changed, 90 insertions(+), 88 deletions(-) create mode 100644 internal/cmd/helpers.go diff --git a/internal/cmd/attach.go b/internal/cmd/attach.go index c091ea4..f9746c3 100644 --- a/internal/cmd/attach.go +++ b/internal/cmd/attach.go @@ -3,7 +3,6 @@ package cmd import ( "errors" "fmt" - "os" "github.com/spf13/cobra" @@ -39,7 +38,10 @@ session continues running.`, } func runAttachCmd(cmd *cobra.Command, args []string) error { - mgr := ManagerFromContext(cmd.Context()) + mgr, err := requireManager(cmd.Context()) + if err != nil { + return err + } switch len(args) { case 0: @@ -66,17 +68,9 @@ func attachGlobalMRU(cmd *cobra.Command, mgr *instance.Manager) error { // attachInstanceMRU attaches to the most recently accessed session for a specific instance. func attachInstanceMRU(cmd *cobra.Command, mgr *instance.Manager, branch string) error { - repoPath, err := os.Getwd() - if err != nil { - return fmt.Errorf("get working directory: %w", err) - } - - inst, err := mgr.GetByBranch(cmd.Context(), repoPath, branch) + inst, err := getInstanceByBranch(cmd.Context(), mgr, branch, "no instance found for branch %q (use 'hjk run' to create one)") if err != nil { - if errors.Is(err, instance.ErrNotFound) { - return fmt.Errorf("no instance found for branch %q (use 'hjk run' to create one)", branch) - } - return fmt.Errorf("get instance: %w", err) + return err } session, err := mgr.GetMRUSession(cmd.Context(), inst.ID) @@ -92,17 +86,9 @@ func attachInstanceMRU(cmd *cobra.Command, mgr *instance.Manager, branch string) // attachExplicitSession attaches to a specific session by name. func attachExplicitSession(cmd *cobra.Command, mgr *instance.Manager, branch, sessionName string) error { - repoPath, err := os.Getwd() + inst, err := getInstanceByBranch(cmd.Context(), mgr, branch, "no instance found for branch %q (use 'hjk run' to create one)") if err != nil { - return fmt.Errorf("get working directory: %w", err) - } - - inst, err := mgr.GetByBranch(cmd.Context(), repoPath, branch) - if err != nil { - if errors.Is(err, instance.ErrNotFound) { - return fmt.Errorf("no instance found for branch %q (use 'hjk run' to create one)", branch) - } - return fmt.Errorf("get instance: %w", err) + return err } // Verify session exists diff --git a/internal/cmd/helpers.go b/internal/cmd/helpers.go new file mode 100644 index 0000000..2e375cb --- /dev/null +++ b/internal/cmd/helpers.go @@ -0,0 +1,43 @@ +package cmd + +import ( + "context" + "errors" + "fmt" + "os" + + "github.com/jmgilman/headjack/internal/instance" +) + +func requireManager(ctx context.Context) (*instance.Manager, error) { + mgr := ManagerFromContext(ctx) + if mgr == nil { + return nil, errors.New("instance manager not initialized") + } + return mgr, nil +} + +func repoPath() (string, error) { + path, err := os.Getwd() + if err != nil { + return "", fmt.Errorf("get working directory: %w", err) + } + return path, nil +} + +func getInstanceByBranch(ctx context.Context, mgr *instance.Manager, branch, notFoundMsg string) (*instance.Instance, error) { + repoPath, err := repoPath() + if err != nil { + return nil, err + } + + inst, err := mgr.GetByBranch(ctx, repoPath, branch) + if err != nil { + if errors.Is(err, instance.ErrNotFound) && notFoundMsg != "" { + return nil, fmt.Errorf(notFoundMsg, branch) + } + return nil, fmt.Errorf("get instance: %w", err) + } + + return inst, nil +} diff --git a/internal/cmd/kill.go b/internal/cmd/kill.go index 60b10e8..10b6263 100644 --- a/internal/cmd/kill.go +++ b/internal/cmd/kill.go @@ -3,7 +3,6 @@ package cmd import ( "errors" "fmt" - "os" "strings" "github.com/spf13/cobra" @@ -32,16 +31,14 @@ func runKillCmd(cmd *cobra.Command, args []string) error { return err } - // Get the instance for this branch - repoPath, err := os.Getwd() + mgr, err := requireManager(cmd.Context()) if err != nil { - return fmt.Errorf("get working directory: %w", err) + return err } - mgr := ManagerFromContext(cmd.Context()) - inst, err := mgr.GetByBranch(cmd.Context(), repoPath, branch) + inst, err := getInstanceByBranch(cmd.Context(), mgr, branch, "") if err != nil { - return fmt.Errorf("get instance: %w", err) + return err } // Kill the session diff --git a/internal/cmd/logs.go b/internal/cmd/logs.go index cec1610..f36e549 100644 --- a/internal/cmd/logs.go +++ b/internal/cmd/logs.go @@ -57,15 +57,14 @@ func runLogsCmd(cmd *cobra.Command, args []string) error { } // Get the instance for this branch - repoPath, err := os.Getwd() + mgr, err := requireManager(cmd.Context()) if err != nil { - return fmt.Errorf("get working directory: %w", err) + return err } - mgr := ManagerFromContext(cmd.Context()) - inst, err := mgr.GetByBranch(cmd.Context(), repoPath, branch) + inst, err := getInstanceByBranch(cmd.Context(), mgr, branch, "") if err != nil { - return fmt.Errorf("get instance: %w", err) + return err } // Get the session to verify it exists and get its ID diff --git a/internal/cmd/ps.go b/internal/cmd/ps.go index 66e8448..06fa13b 100644 --- a/internal/cmd/ps.go +++ b/internal/cmd/ps.go @@ -52,13 +52,18 @@ func listInstances(cmd *cobra.Command) error { return fmt.Errorf("get all flag: %w", err) } + mgr, err := requireManager(cmd.Context()) + if err != nil { + return err + } + filter := instance.ListFilter{} // If not showing all, filter by current repo if !all { - repoPath, wdErr := os.Getwd() - if wdErr != nil { - return fmt.Errorf("get working directory: %w", wdErr) + repoPath, err := repoPath() + if err != nil { + return err } opener := git.NewOpener(exec.New()) @@ -70,7 +75,6 @@ func listInstances(cmd *cobra.Command) error { filter.RepoID = repo.Identifier() } - mgr := ManagerFromContext(cmd.Context()) instances, err := mgr.List(cmd.Context(), filter) if err != nil { return fmt.Errorf("list instances: %w", err) @@ -113,15 +117,14 @@ func listInstances(cmd *cobra.Command) error { } func listSessions(cmd *cobra.Command, branch string) error { - repoPath, err := os.Getwd() + mgr, err := requireManager(cmd.Context()) if err != nil { - return fmt.Errorf("get working directory: %w", err) + return err } - mgr := ManagerFromContext(cmd.Context()) - inst, err := mgr.GetByBranch(cmd.Context(), repoPath, branch) + inst, err := getInstanceByBranch(cmd.Context(), mgr, branch, "") if err != nil { - return fmt.Errorf("get instance: %w", err) + return err } sessions, err := mgr.ListSessions(cmd.Context(), inst.ID) diff --git a/internal/cmd/recreate.go b/internal/cmd/recreate.go index 99d9972..8d57b08 100644 --- a/internal/cmd/recreate.go +++ b/internal/cmd/recreate.go @@ -1,13 +1,9 @@ package cmd import ( - "errors" "fmt" - "os" "github.com/spf13/cobra" - - "github.com/jmgilman/headjack/internal/instance" ) var recreateCmd = &cobra.Command{ @@ -30,20 +26,14 @@ The worktree (and all git-tracked and untracked files) is preserved.`, RunE: func(cmd *cobra.Command, args []string) error { branch := args[0] - // Get current working directory as repo path - repoPath, err := os.Getwd() + mgr, err := requireManager(cmd.Context()) if err != nil { - return fmt.Errorf("get working directory: %w", err) + return err } - // Get instance by branch - mgr := ManagerFromContext(cmd.Context()) - inst, err := mgr.GetByBranch(cmd.Context(), repoPath, branch) + inst, err := getInstanceByBranch(cmd.Context(), mgr, branch, "no instance found for branch %q") if err != nil { - if errors.Is(err, instance.ErrNotFound) { - return fmt.Errorf("no instance found for branch %q", branch) - } - return fmt.Errorf("get instance: %w", err) + return err } // Determine image to use (precedence: flag > config) diff --git a/internal/cmd/rm.go b/internal/cmd/rm.go index ecf011a..fb4b3a0 100644 --- a/internal/cmd/rm.go +++ b/internal/cmd/rm.go @@ -2,14 +2,11 @@ package cmd import ( "bufio" - "errors" "fmt" "os" "strings" "github.com/spf13/cobra" - - "github.com/jmgilman/headjack/internal/instance" ) var rmCmd = &cobra.Command{ @@ -37,20 +34,14 @@ WARNING: This deletes uncommitted work in the worktree.`, return fmt.Errorf("get force flag: %w", err) } - // Get current working directory as repo path - repoPath, err := os.Getwd() + mgr, err := requireManager(cmd.Context()) if err != nil { - return fmt.Errorf("get working directory: %w", err) + return err } - // Get instance by branch - mgr := ManagerFromContext(cmd.Context()) - inst, err := mgr.GetByBranch(cmd.Context(), repoPath, branch) + inst, err := getInstanceByBranch(cmd.Context(), mgr, branch, "no instance found for branch %q") if err != nil { - if errors.Is(err, instance.ErrNotFound) { - return fmt.Errorf("no instance found for branch %q", branch) - } - return fmt.Errorf("get instance: %w", err) + return err } // Confirm removal unless --force diff --git a/internal/cmd/run.go b/internal/cmd/run.go index c675994..77c439b 100644 --- a/internal/cmd/run.go +++ b/internal/cmd/run.go @@ -3,7 +3,6 @@ package cmd import ( "errors" "fmt" - "os" "github.com/spf13/cobra" @@ -173,9 +172,9 @@ func injectAuthToken(agent string, cfg *instance.CreateSessionConfig) error { func runRunCmd(cmd *cobra.Command, args []string) error { branch := args[0] - repoPath, err := os.Getwd() + mgr, err := requireManager(cmd.Context()) if err != nil { - return fmt.Errorf("get working directory: %w", err) + return err } flags, err := parseRunFlags(cmd) @@ -183,7 +182,11 @@ func runRunCmd(cmd *cobra.Command, args []string) error { return err } - mgr := ManagerFromContext(cmd.Context()) + repoPath, err := repoPath() + if err != nil { + return err + } + inst, err := getOrCreateInstance(cmd, mgr, repoPath, branch, flags.image) if err != nil { return err diff --git a/internal/cmd/stop.go b/internal/cmd/stop.go index 4941d07..08fa7db 100644 --- a/internal/cmd/stop.go +++ b/internal/cmd/stop.go @@ -1,13 +1,9 @@ package cmd import ( - "errors" "fmt" - "os" "github.com/spf13/cobra" - - "github.com/jmgilman/headjack/internal/instance" ) var stopCmd = &cobra.Command{ @@ -21,20 +17,14 @@ The worktree is preserved and the instance can be resumed later with 'hjk run'.` RunE: func(cmd *cobra.Command, args []string) error { branch := args[0] - // Get current working directory as repo path - repoPath, err := os.Getwd() + mgr, err := requireManager(cmd.Context()) if err != nil { - return fmt.Errorf("get working directory: %w", err) + return err } - // Get instance by branch - mgr := ManagerFromContext(cmd.Context()) - inst, err := mgr.GetByBranch(cmd.Context(), repoPath, branch) + inst, err := getInstanceByBranch(cmd.Context(), mgr, branch, "no instance found for branch %q") if err != nil { - if errors.Is(err, instance.ErrNotFound) { - return fmt.Errorf("no instance found for branch %q", branch) - } - return fmt.Errorf("get instance: %w", err) + return err } // Stop the instance From 315cf37056949e58e266ddba8272b4b1604f8413 Mon Sep 17 00:00:00 2001 From: Joshua Gilman Date: Fri, 2 Jan 2026 14:36:51 -0800 Subject: [PATCH 2/2] Fix shadowed error in ps command --- internal/cmd/ps.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/cmd/ps.go b/internal/cmd/ps.go index 06fa13b..43f916d 100644 --- a/internal/cmd/ps.go +++ b/internal/cmd/ps.go @@ -61,13 +61,13 @@ func listInstances(cmd *cobra.Command) error { // If not showing all, filter by current repo if !all { - repoPath, err := repoPath() - if err != nil { - return err + repoPathValue, pathErr := repoPath() + if pathErr != nil { + return pathErr } opener := git.NewOpener(exec.New()) - repo, openErr := opener.Open(cmd.Context(), repoPath) + repo, openErr := opener.Open(cmd.Context(), repoPathValue) if openErr != nil { return fmt.Errorf("open repository: %w", openErr) }