Skip to content

Commit b2edfa0

Browse files
committed
Copy slash commands into worktrees on creation
When mobs_dir is external, git worktree add doesn't include untracked .claude/commands/ files (they're globally gitignored). Copy mob-*.md and codemob-*.md from the main repo into each new worktree so agents have access to slash commands.
1 parent d0b0d55 commit b2edfa0

File tree

3 files changed

+55
-0
lines changed

3 files changed

+55
-0
lines changed

cmd/root.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,8 @@ func createMob(root string, cfg *mob.Config, name, agent string) (string, error)
289289
return "", err
290290
}
291291

292+
mob.CopySlashCommands(root, worktreePath)
293+
292294
cfg.Mobs = append(cfg.Mobs, mob.Mob{
293295
Name: name,
294296
Branch: branch,

internal/mob/init.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,27 @@ func setupClaudeCommands(repoRoot string, multipleAgents bool) {
590590
}
591591
}
592592

593+
func CopySlashCommands(srcRoot, destRoot string) {
594+
srcDir := filepath.Join(srcRoot, ".claude", "commands")
595+
destDir := filepath.Join(destRoot, ".claude", "commands")
596+
597+
for _, prefix := range []string{"mob-", "codemob-"} {
598+
matches, _ := filepath.Glob(filepath.Join(srcDir, prefix+"*.md"))
599+
if len(matches) == 0 {
600+
continue
601+
}
602+
os.MkdirAll(destDir, 0755)
603+
for _, src := range matches {
604+
data, err := os.ReadFile(src)
605+
if err != nil {
606+
continue
607+
}
608+
dest := filepath.Join(destDir, filepath.Base(src))
609+
os.WriteFile(dest, data, 0644)
610+
}
611+
}
612+
}
613+
593614
func setupCodexPrompts(multipleAgents bool) {
594615
promptsDir := filepath.Join(os.Getenv("HOME"), ".codex", "prompts")
595616
os.MkdirAll(promptsDir, 0755)

internal/mob/integration_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1452,3 +1452,35 @@ func TestPurgeExternalMobs(t *testing.T) {
14521452
t.Errorf("expected 0 mobs after purge, got %d", len(mobs))
14531453
}
14541454
}
1455+
1456+
func TestSlashCommandsCopiedToExternalWorktree(t *testing.T) {
1457+
bin := buildCore(t)
1458+
_, repoPath := setupTestRepo(t)
1459+
initRepoWithMobsDir(t, bin, repoPath, "2")
1460+
1461+
// given -> slash commands exist in the main repo's .claude/commands/
1462+
srcCommands := filepath.Join(repoPath, ".claude", "commands")
1463+
expectedFiles := []string{
1464+
"mob-list.md", "mob-new.md", "mob-switch.md", "mob-remove.md", "mob-drop.md",
1465+
"codemob-list.md", "codemob-new.md", "codemob-switch.md", "codemob-remove.md", "codemob-drop.md",
1466+
}
1467+
for _, name := range expectedFiles {
1468+
if _, err := os.Stat(filepath.Join(srcCommands, name)); err != nil {
1469+
t.Fatalf("precondition: slash command %s missing from main repo: %v", name, err)
1470+
}
1471+
}
1472+
1473+
// when -> create a mob (worktree will be external)
1474+
runCore(t, bin, repoPath, "new", "cmd-test", "--no-launch")
1475+
1476+
repoName := filepath.Base(repoPath)
1477+
worktreePath := filepath.Join(filepath.Dir(repoPath), ".codemob", repoName, "mobs", "cmd-test")
1478+
1479+
// then -> slash commands should exist in the worktree's .claude/commands/
1480+
destCommands := filepath.Join(worktreePath, ".claude", "commands")
1481+
for _, name := range expectedFiles {
1482+
if _, err := os.Stat(filepath.Join(destCommands, name)); err != nil {
1483+
t.Errorf("slash command %s not copied to worktree: %v", name, err)
1484+
}
1485+
}
1486+
}

0 commit comments

Comments
 (0)