From 363ffec9ec38bf84fd2476d28c6906965cda9822 Mon Sep 17 00:00:00 2001 From: Tai Groot Date: Thu, 4 Jun 2026 12:00:07 -0700 Subject: [PATCH] feat: discover skills from git root in monorepos (#3078) --- internal/config/load.go | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/internal/config/load.go b/internal/config/load.go index 09c15262e18b80bf2c0b5d2aaf9a3b53b747b524..2f0946e7bc45ca2e3dcc17c79cba1dc30106d975 100644 --- a/internal/config/load.go +++ b/internal/config/load.go @@ -1046,15 +1046,37 @@ func GlobalSkillsDirs() []string { return paths } +// projectSkillSubdirs lists the conventional subdirectories where +// project-level skills are discovered. Shared across working-dir and +// git-root lookups to prevent drift when a new convention is added. +var projectSkillSubdirs = []string{ + ".agents/skills", + ".crush/skills", + ".claude/skills", + ".cursor/skills", +} + // ProjectSkillsDir returns the default project directories for which Crush -// will look for skills. +// will look for skills. In addition to the working directory, it also +// checks the git working tree root so that monorepo-level skills are +// discovered when the user is inside a subdirectory. +// Working-directory paths come first so local skills take precedence +// over monorepo-level ones. func ProjectSkillsDir(workingDir string) []string { - return []string{ - filepath.Join(workingDir, ".agents/skills"), - filepath.Join(workingDir, ".crush/skills"), - filepath.Join(workingDir, ".claude/skills"), - filepath.Join(workingDir, ".cursor/skills"), + dirs := make([]string, 0, len(projectSkillSubdirs)*2) + for _, sub := range projectSkillSubdirs { + dirs = append(dirs, filepath.Join(workingDir, sub)) + } + + // When the working directory is inside a git repository, also look at + // the repository root so monorepo-level .agents/skills are found. + if root := worktreeRoot(workingDir); root != "" && root != workingDir { + for _, sub := range projectSkillSubdirs { + dirs = append(dirs, filepath.Join(root, sub)) + } } + + return dirs } func isAppleTerminal() bool { return os.Getenv("TERM_PROGRAM") == "Apple_Terminal" }