From 0dbc55eca16d5795c6abef4d258f96cb489f16b2 Mon Sep 17 00:00:00 2001 From: Bartek Lipinski Date: Tue, 7 Apr 2026 22:55:35 +0200 Subject: [PATCH] Add --cd flag to codemob new for cd-only mob creation Allow creating a mob without launching an agent session, landing directly in the worktree directory instead. The Go binary prints the worktree path as the last stdout line; the shell function intercepts --cd, captures the path, and cd's into it. Closes #27 --- cmd/root.go | 7 +++++++ codemob-shell.sh | 15 +++++++++++++ internal/mob/integration_test.go | 36 ++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+) diff --git a/cmd/root.go b/cmd/root.go index a86eb18..040bec9 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -401,11 +401,14 @@ func cmdNew(args []string) error { name := "" agent := cfg.DefaultAgent noLaunch := false + cdOnly := false for i := 0; i < len(args); i++ { switch { case args[i] == "--no-launch": noLaunch = true + case args[i] == "--cd": + cdOnly = true case args[i] == "--agent": if i+1 >= len(args) { return fmt.Errorf("--agent requires a value (e.g., --agent codex)") @@ -433,6 +436,10 @@ func cmdNew(args []string) error { return err } + if cdOnly { + fmt.Println(worktreePath) + return nil + } if !noLaunch { return launchAgent(root, agent, worktreePath, false) } diff --git a/codemob-shell.sh b/codemob-shell.sh index 5b938b8..17d47d6 100644 --- a/codemob-shell.sh +++ b/codemob-shell.sh @@ -19,6 +19,21 @@ codemob() { fi cd "$dir" ;; + new) + local has_cd=false + for arg in "$@"; do + [ "$arg" = "--cd" ] && has_cd=true + done + if $has_cd; then + local out dir + out="$(command codemob "$@")" || return $? + dir="$(echo "$out" | tail -1)" + echo "$out" | sed '$d' + cd "$dir" + else + command codemob "$@" + fi + ;; *) command codemob "$@" ;; esac } diff --git a/internal/mob/integration_test.go b/internal/mob/integration_test.go index 0dee765..2cb5730 100644 --- a/internal/mob/integration_test.go +++ b/internal/mob/integration_test.go @@ -1731,3 +1731,39 @@ func TestSlashCommandsCopiedToExternalWorktree(t *testing.T) { } } } + +func TestNewCdPrintsWorktreePath(t *testing.T) { + bin := buildCore(t) + _, repoPath := setupTestRepo(t) + initRepo(t, bin, repoPath) + + // when + out := runCore(t, bin, repoPath, "new", "cd-test", "--cd") + + // then -> last line of output should be the worktree path + lines := strings.Split(strings.TrimSpace(out), "\n") + lastLine := lines[len(lines)-1] + worktreePath := filepath.Join(repoPath, ".codemob", "mobs", "cd-test") + if clean, err := filepath.EvalSymlinks(worktreePath); err == nil { + worktreePath = clean + } + if lastLine != worktreePath { + t.Errorf("expected last line to be worktree path %q, got %q", worktreePath, lastLine) + } + + // then -> worktree should exist on disk + if _, err := os.Stat(worktreePath); err != nil { + t.Errorf("worktree not created: %v", err) + } + + // then -> config should have the mob + cfg := readConfig(t, repoPath) + mobs := cfg["mobs"].([]interface{}) + if len(mobs) != 1 { + t.Fatalf("expected 1 mob, got %d", len(mobs)) + } + mob := mobs[0].(map[string]interface{}) + if mob["name"] != "cd-test" { + t.Errorf("expected mob name=cd-test, got %v", mob["name"]) + } +}