feat(v0.4.1): add config.SearchRoots for multi-root workspace lookup

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-22 17:51:37 -04:00
parent 02dcdcc215
commit 42efcc261a
2 changed files with 82 additions and 0 deletions
+29
View File
@@ -59,6 +59,35 @@ func ResolveProjectRoot() string {
return expandPath(v)
}
// SearchRoots returns the deduplicated list of workspace roots that all query
// and listing operations must consult. Always includes CTASK_ROOT; also
// includes CTASK_PROJECT_ROOT when set and different from CTASK_ROOT.
func SearchRoots() []string {
taskRoot := ResolveRoot()
roots := []string{taskRoot}
projRoot := ResolveProjectRoot()
if projRoot == "" {
return roots
}
if samePath(taskRoot, projRoot) {
return roots
}
return append(roots, projRoot)
}
// samePath reports whether two absolute paths refer to the same directory
// on the current platform. Uses case-insensitive compare on Windows.
func samePath(a, b string) bool {
ac := filepath.Clean(a)
bc := filepath.Clean(b)
if runtime.GOOS == "windows" {
return strings.EqualFold(ac, bc)
}
return ac == bc
}
// EnvVars returns the environment variables to export into child sessions.
// taskType must be "task" or "project"; an empty value defaults to "task".
func EnvVars(slug, mode, root, workspace, category, taskType string) map[string]string {
+53
View File
@@ -0,0 +1,53 @@
package config
import (
"os"
"path/filepath"
"testing"
)
func TestSearchRootsUnsetProjectRoot(t *testing.T) {
os.Unsetenv("CTASK_PROJECT_ROOT")
os.Setenv("CTASK_ROOT", t.TempDir())
defer os.Unsetenv("CTASK_ROOT")
roots := SearchRoots()
if len(roots) != 1 {
t.Fatalf("expected 1 root, got %d: %v", len(roots), roots)
}
if roots[0] != ResolveRoot() {
t.Errorf("root mismatch: got %q, want %q", roots[0], ResolveRoot())
}
}
func TestSearchRootsDistinctProjectRoot(t *testing.T) {
taskRoot := t.TempDir()
projRoot := t.TempDir()
os.Setenv("CTASK_ROOT", taskRoot)
os.Setenv("CTASK_PROJECT_ROOT", projRoot)
defer os.Unsetenv("CTASK_ROOT")
defer os.Unsetenv("CTASK_PROJECT_ROOT")
roots := SearchRoots()
if len(roots) != 2 {
t.Fatalf("expected 2 roots, got %d: %v", len(roots), roots)
}
absTask, _ := filepath.Abs(taskRoot)
absProj, _ := filepath.Abs(projRoot)
if roots[0] != absTask || roots[1] != absProj {
t.Errorf("roots: got %v, want [%q, %q]", roots, absTask, absProj)
}
}
func TestSearchRootsSameRootDedupes(t *testing.T) {
d := t.TempDir()
os.Setenv("CTASK_ROOT", d)
os.Setenv("CTASK_PROJECT_ROOT", d)
defer os.Unsetenv("CTASK_ROOT")
defer os.Unsetenv("CTASK_PROJECT_ROOT")
roots := SearchRoots()
if len(roots) != 1 {
t.Fatalf("expected 1 root (dedup), got %d: %v", len(roots), roots)
}
}