diff --git a/internal/workspace/create.go b/internal/workspace/create.go index 5501f59..9dae4ad 100644 --- a/internal/workspace/create.go +++ b/internal/workspace/create.go @@ -29,6 +29,12 @@ type CreateOpts struct { // ProjectSeedDir is the absolute path to the project-specific seed directory. // Only consulted when IsProject is true. ProjectSeedDir string + + // SkipCategoryDir, when true, places the workspace directly under Root + // without inserting a Category subdirectory. Used for project mode with + // CTASK_PROJECT_ROOT set and no explicit -c flag. Category is still + // recorded in TaskMeta. + SkipCategoryDir bool } // CreateResult holds the result of workspace creation. @@ -60,7 +66,10 @@ func Create(opts CreateOpts) (*CreateResult, error) { date := now.Format("2006-01-02") id := now.Format("20060102-150405") - categoryDir := filepath.Join(opts.Root, opts.Category) + categoryDir := opts.Root + if !opts.SkipCategoryDir { + categoryDir = filepath.Join(opts.Root, opts.Category) + } if err := os.MkdirAll(categoryDir, 0755); err != nil { return nil, fmt.Errorf("creating category dir: %w", err) } diff --git a/internal/workspace/create_test.go b/internal/workspace/create_test.go index f245546..ea00874 100644 --- a/internal/workspace/create_test.go +++ b/internal/workspace/create_test.go @@ -241,6 +241,51 @@ func TestCreateSeedDoesNotReplaceTaskYAML(t *testing.T) { } } +func TestCreateSkipCategoryDirPlacesUnderRoot(t *testing.T) { + root := t.TempDir() + opts := CreateOpts{ + Root: root, + Title: "billing service", + Category: "projects", // recorded in metadata + Mode: "local", + Agent: "claude", + IsProject: true, + SkipCategoryDir: true, + } + ws, err := Create(opts) + if err != nil { + t.Fatalf("Create: %v", err) + } + parent := filepath.Dir(ws.Path) + if parent != root { + t.Errorf("workspace parent: got %q, want %q", parent, root) + } + if ws.Meta.Category != "projects" { + t.Errorf("Category metadata: got %q, want \"projects\"", ws.Meta.Category) + } +} + +func TestCreateExplicitCategoryAppendsSubdir(t *testing.T) { + root := t.TempDir() + opts := CreateOpts{ + Root: root, + Title: "billing service", + Category: "backend", + Mode: "local", + Agent: "claude", + IsProject: true, + // SkipCategoryDir: false (default) + } + ws, err := Create(opts) + if err != nil { + t.Fatalf("Create: %v", err) + } + parent := filepath.Dir(ws.Path) + if parent != filepath.Join(root, "backend") { + t.Errorf("workspace parent: got %q, want %q", parent, filepath.Join(root, "backend")) + } +} + func TestCreateCollisionSuffix(t *testing.T) { root := t.TempDir()