workspace

package
v0.6.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 2, 2026 License: MIT Imports: 19 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var IsZombieWorktreePath = IsZombieWorktree

IsZombieWorktreePath is an alias for IsZombieWorktree for clarity when checking arbitrary paths (not necessarily the current working directory).

Functions

func ExtractWorkspaceFromNotebook

func ExtractWorkspaceFromNotebook(absPath, notebookRoot string, notebook *config.Notebook) string

ExtractWorkspaceFromNotebook extracts the workspace name from a path within a notebook using the notebook's configured templates.

func ExtractWorkspaceNameFromNotebookPath

func ExtractWorkspaceNameFromNotebookPath(absPath, notebookRoot string) string

ExtractWorkspaceNameFromNotebookPath is the public interface for extracting workspace name from a notebook path. It uses config-based lookup when possible.

func FindEcosystemRoot

func FindEcosystemRoot(startDir string) (string, error)

FindEcosystemRoot searches upward from startDir to find a grove.yml containing a 'workspaces' key.

func FindNotebookMarker

func FindNotebookMarker(path string) string

FindNotebookMarker walks up from path looking for notebook.yml marker. This is a fallback for notebooks not defined in config. Returns the directory containing the marker, or empty string if not found.

func FindNotebookRoot

func FindNotebookRoot(path string) string

FindNotebookRoot is kept for backwards compatibility. Prefer FindNotebookRootFromConfig for config-aware lookup.

func FindNotebookRootFromConfig

func FindNotebookRootFromConfig(absPath string, cfg *config.Config) (string, *config.Notebook)

FindNotebookRootFromConfig checks if a path is under any configured notebook's root_dir. Returns the notebook root path and the notebook config if found.

func GenerateWorktreeGoWork

func GenerateWorktreeGoWork(config *GoWorkspaceConfig, requiredModules []string) string

func GetAccessHistoryPath

func GetAccessHistoryPath(configDir string) string

GetAccessHistoryPath returns the path to the access history file

func IsNotebookRepo

func IsNotebookRepo(path string) bool

IsNotebookRepo checks if a given path is a notebook repository by looking for a marker file.

func IsZombieWorktree

func IsZombieWorktree(path string) bool

IsZombieWorktree checks if the given path is inside a deleted git worktree. A worktree is considered "zombie" if it's inside a .grove-worktrees directory but the .git file (which links to the main repo) is missing.

This is used to prevent recreating files (like .grove/rules or logs) in deleted worktrees, which would cause "zombie" directories to reappear after cleanup.

This function uses the same detection logic as zombieAwareWriter in the logging package, ensuring consistent behavior across the grove ecosystem.

func IsZombieWorktreeCwd

func IsZombieWorktreeCwd() bool

IsZombieWorktreeCwd checks if the current working directory is inside a zombie worktree.

func LoadAccessHistoryAsMap

func LoadAccessHistoryAsMap(configDir string) (map[string]time.Time, error)

LoadAccessHistoryAsMap is a convenience function that returns a simple map of path -> lastAccessed time

func Prepare

func Prepare(ctx context.Context, opts PrepareOptions, setupHandlers ...func(worktreePath, gitRoot string) error) (string, error)

Prepare creates or gets a fully configured worktree.

func SetupGoWorkspaceForWorktree

func SetupGoWorkspaceForWorktree(worktreePath string, gitRoot string) error

SetupGoWorkspaceForWorktree checks if the current project uses Go workspaces and if so, creates an appropriate go.work file in the worktree.

func SetupSubmodules

func SetupSubmodules(ctx context.Context, worktreePath, branchName string, repos []string, provider *Provider, setupHandlers ...func(worktreePath, gitRoot string) error) error

SetupSubmodules initializes submodules, creating linked worktrees where possible. It accepts a Provider containing pre-discovered workspaces to avoid redundant filesystem scans.

func UpdateAccessHistory

func UpdateAccessHistory(configDir, workspacePath string) error

UpdateAccessHistory is a convenience function that loads, updates, and saves access history

Types

type AccessHistory

type AccessHistory struct {
	Projects map[string]*ProjectAccess `json:"projects"`
}

AccessHistory manages project access history

func LoadAccessHistory

func LoadAccessHistory(configDir string) (*AccessHistory, error)

LoadAccessHistory loads the access history from disk

func (*AccessHistory) GetLastAccessed

func (h *AccessHistory) GetLastAccessed(path string) *time.Time

GetLastAccessed returns the last accessed time for a project

func (*AccessHistory) RecordAccess

func (h *AccessHistory) RecordAccess(path string)

RecordAccess records that a project was accessed

func (*AccessHistory) Save

func (h *AccessHistory) Save(configDir string) error

Save saves the access history to disk

type ContentDirectory

type ContentDirectory struct {
	Path string
	Type string // "notes", "plans", "chats"
}

ContentDirectory represents a directory containing notebook content

type DiscoveredWorkspace

type DiscoveredWorkspace struct {
	Name              string        `json:"name"`
	Path              string        `json:"path"`
	Type              WorkspaceType `json:"type"`
	ParentProjectPath string        `json:"parent_project_path"`
}

DiscoveredWorkspace represents a specific, checked-out instance of a Project.

type DiscoveryResult

type DiscoveryResult struct {
	Projects            []Project   `json:"projects"`
	Ecosystems          []Ecosystem `json:"ecosystems"`
	NonGroveDirectories []string    `json:"non_grove_directories,omitempty"`
}

DiscoveryResult is the comprehensive output of the DiscoveryService.

type DiscoveryService

type DiscoveryService struct {
	// contains filtered or unexported fields
}

DiscoveryService scans the filesystem to find and classify Grove entities.

func NewDiscoveryService

func NewDiscoveryService(logger *logrus.Logger) *DiscoveryService

NewDiscoveryService creates a new discovery service.

func (*DiscoveryService) DiscoverAll

func (s *DiscoveryService) DiscoverAll() (*DiscoveryResult, error)

DiscoverAll scans all configured 'groves' and returns a comprehensive result.

func (*DiscoveryService) WithConfigPath

func (s *DiscoveryService) WithConfigPath(configPath string) *DiscoveryService

WithConfigPath returns a new DiscoveryService with a custom config path for testing. If configPath is set, it will be used instead of HOME directory when loading config.

type Ecosystem

type Ecosystem struct {
	Name string `json:"name"`
	Path string `json:"path"`
	Type string `json:"type"` // "Grove" or "User"
}

Ecosystem represents a top-level meta-repository.

type GoWorkspaceConfig

type GoWorkspaceConfig struct {
	RootGoWorkPath string
	WorkspaceRoot  string
	GoVersion      string
	ModulePaths    []string
}

func FindRootGoWorkspace

func FindRootGoWorkspace(startPath string) (*GoWorkspaceConfig, error)

FindRootGoWorkspace searches for a go.work file by walking up the directory tree.

type NotebookLocator

type NotebookLocator struct {
	// contains filtered or unexported fields
}

NotebookLocator resolves paths for notes, plans, and chats based on configuration. It operates in two modes:

  • Local Mode (default): Plans/chats are stored within the project directory (e.g., ./plans)
  • Centralized Mode (opt-in): Plans/chats are stored in a centralized notebook directory

The mode is determined by whether notebook.root_dir is configured.

func NewNotebookLocator

func NewNotebookLocator(cfg *config.Config) *NotebookLocator

NewNotebookLocator creates a new locator. It gracefully handles a nil config. It now stores the full config to support dynamic notebook resolution based on WorkspaceNode.NotebookName.

func (*NotebookLocator) GetAllContentDirs

func (l *NotebookLocator) GetAllContentDirs(node *WorkspaceNode) ([]ContentDirectory, error)

GetAllContentDirs returns all directories that contain content for a workspace. This includes the notes directory (containing subdirs like current, learn, etc.), the plans directory, and the chats directory.

func (*NotebookLocator) GetChatsDir

func (l *NotebookLocator) GetChatsDir(node *WorkspaceNode) (string, error)

GetChatsDir is analogous to GetPlansDir.

func (*NotebookLocator) GetCompletedDir

func (l *NotebookLocator) GetCompletedDir(node *WorkspaceNode) (string, error)

GetCompletedDir is analogous to GetPlansDir but for completed notes.

func (*NotebookLocator) GetDocgenDir

func (l *NotebookLocator) GetDocgenDir(node *WorkspaceNode) (string, error)

GetDocgenDir returns the absolute path to the root docgen directory for a given workspace node. This is the parent directory containing prompts/, docs/, images/, asciicasts/, videos/, and the docgen.config.yml file.

In Local Mode, it returns the docgen directory within the project's .notebook directory. In Centralized Mode, it uses the configured root_dir and path templates.

Callers can use this to construct paths to subdirectories:

  • filepath.Join(docgenDir, "prompts") - prompt files
  • filepath.Join(docgenDir, "docs") - generated documentation output
  • filepath.Join(docgenDir, "docgen.config.yml") - configuration file
  • filepath.Join(docgenDir, "images") - image assets
  • filepath.Join(docgenDir, "asciicasts") - asciicast recordings
  • filepath.Join(docgenDir, "videos") - video files

func (*NotebookLocator) GetDocgenPromptsDir

func (l *NotebookLocator) GetDocgenPromptsDir(node *WorkspaceNode) (string, error)

GetDocgenPromptsDir returns the absolute path to the docgen prompts directory for a given workspace node. This is a convenience method equivalent to filepath.Join(GetDocgenDir(node), "prompts").

func (*NotebookLocator) GetGroupDir

func (l *NotebookLocator) GetGroupDir(node *WorkspaceNode, groupName string) (string, error)

GetGroupDir resolves the absolute path for any group directory (e.g., "inbox", "plans", "plans/my-feature"). This is the centralized method for resolving all note-related directory paths.

func (*NotebookLocator) GetInProgressDir

func (l *NotebookLocator) GetInProgressDir(node *WorkspaceNode) (string, error)

GetInProgressDir is analogous to GetPlansDir but for in_progress notes.

func (*NotebookLocator) GetNotesDir

func (l *NotebookLocator) GetNotesDir(node *WorkspaceNode, noteType string) (string, error)

GetNotesDir returns the absolute path to the notes directory for a given workspace node and note type. In Local Mode, it returns the notes directory within the project (e.g., ./notes/{noteType}). In Centralized Mode, it uses the configured root_dir and path templates.

func (*NotebookLocator) GetPlansDir

func (l *NotebookLocator) GetPlansDir(node *WorkspaceNode) (string, error)

GetPlansDir returns the absolute path to the plans directory for a given workspace node. In Local Mode, it returns the plans directory within the project (using GetGroupingKey to handle worktrees). In Centralized Mode, it uses the configured root_dir and path templates.

func (*NotebookLocator) GetRecipesDir

func (l *NotebookLocator) GetRecipesDir(node *WorkspaceNode) (string, error)

GetRecipesDir is analogous to GetPlansDir but for recipes.

func (*NotebookLocator) GetSkillsDir

func (l *NotebookLocator) GetSkillsDir(node *WorkspaceNode) (string, error)

GetSkillsDir returns the absolute path to the skills directory for a given workspace node. Skills are stored alongside plans and chats in the notebook structure.

func (*NotebookLocator) GetTemplatesDir

func (l *NotebookLocator) GetTemplatesDir(node *WorkspaceNode) (string, error)

GetTemplatesDir is analogous to GetPlansDir but for templates.

func (*NotebookLocator) ScanForAllChats

func (l *NotebookLocator) ScanForAllChats(provider *Provider) ([]ScannedDir, error)

ScanForAllChats discovers all chat directories across all known workspaces. It returns a list of ScannedDir structs, linking each directory to its owner. This method properly handles both Local Mode and Centralized Mode.

func (*NotebookLocator) ScanForAllNotes

func (l *NotebookLocator) ScanForAllNotes(provider *Provider) ([]ScannedDir, error)

ScanForAllNotes discovers all notes directories across all known workspaces. It returns a list of ScannedDir structs, linking each directory to its owner.

func (*NotebookLocator) ScanForAllPlans

func (l *NotebookLocator) ScanForAllPlans(provider *Provider) ([]ScannedDir, error)

ScanForAllPlans discovers all plan directories across all known workspaces. It returns a list of ScannedDir structs, linking each directory to its owner. This method properly handles both Local Mode and Centralized Mode.

type PrepareOptions

type PrepareOptions struct {
	GitRoot      string
	WorktreeName string
	BranchName   string
	PlanName     string   // Optional, for state management in grove-flow
	Repos        []string // For ecosystem worktrees
}

PrepareOptions holds configuration for preparing a workspace.

type Project

type Project struct {
	Name                string                `json:"name"`
	Path                string                `json:"path"`
	Type                string                `json:"type"`
	ModulePath          string                `json:"module_path,omitempty"`
	ParentEcosystemPath string                `json:"parent_ecosystem_path,omitempty"`
	Workspaces          []DiscoveredWorkspace `json:"workspaces"`

	// Cloned repository-specific fields (populated by discovery for cx repo managed repos)
	Version       string `json:"version,omitempty"`
	Commit        string `json:"commit,omitempty"`
	AuditStatus   string `json:"audit_status,omitempty"`
	ReportPath    string `json:"report_path,omitempty"`
	RepoURL       string `json:"repo_url,omitempty"`
	RepoShorthand string `json:"repo_shorthand,omitempty"`
}

Project represents a single software repository.

type ProjectAccess

type ProjectAccess struct {
	Path         string    `json:"path"`
	LastAccessed time.Time `json:"last_accessed"`
	AccessCount  int       `json:"access_count"`
}

ProjectAccess tracks when a project was last accessed

type Provider

type Provider struct {
	// contains filtered or unexported fields
}

Provider acts as a read-only, in-memory store for a snapshot of discovered workspaces. It provides fast lookups and access to the workspace hierarchy.

The caching strategy is explicit and consumer-controlled. The Provider itself does not perform discovery; it is initialized with the results of a DiscoveryService.DiscoverAll() call. Short-lived applications should create a Provider once at startup. Long-running services are responsible for deciding when to refresh the data by performing a new discovery and creating a new Provider instance.

func NewProvider

func NewProvider(result *DiscoveryResult) *Provider

NewProvider creates a new workspace provider from a discovery result. It transforms the raw discovery data into the final WorkspaceNode representation and builds internal indexes for fast lookups.

func (*Provider) All

func (p *Provider) All() []*WorkspaceNode

All returns a slice of all discovered WorkspaceNodes.

func (*Provider) Ecosystems

func (p *Provider) Ecosystems() []*WorkspaceNode

Ecosystems returns all nodes that are ecosystem roots.

func (*Provider) FindByName

func (p *Provider) FindByName(name string) *WorkspaceNode

FindByName returns the first workspace node that matches the given name. It returns nil if no matching workspace is found.

func (*Provider) FindByPath

func (p *Provider) FindByPath(path string) *WorkspaceNode

FindByPath returns the WorkspaceNode for a given absolute path. It performs a fast lookup using an internal map.

func (*Provider) FindByWorktree

func (p *Provider) FindByWorktree(baseProjectNode *WorkspaceNode, worktreeName string) *WorkspaceNode

FindByWorktree finds a workspace node for a worktree within an ecosystem. It is used to resolve a job's `worktree` field to the correct workspace node. - baseProjectNode: The base project node (can be ecosystem root or subproject). - worktreeName: The name of the worktree.

This handles two cases: 1. Ecosystem worktrees: /ecosystem/.grove-worktrees/<name>/<subproject> 2. Subproject worktrees: /ecosystem/<subproject>/.grove-worktrees/<name>

func (*Provider) LocalWorkspaces deprecated

func (p *Provider) LocalWorkspaces() map[string]string

LocalWorkspaces returns a map of workspace names to their paths, suitable for use in submodule setup operations.

WARNING: This method may silently overwrite entries if multiple workspaces have the same name (e.g., "grove-core" in different ecosystems). For ecosystem-aware operations, use LocalWorkspacesInEcosystem instead.

Deprecated: Use LocalWorkspacesInEcosystem for more reliable lookups that avoid name collision issues.

func (*Provider) LocalWorkspacesInEcosystem

func (p *Provider) LocalWorkspacesInEcosystem(ecosystemPath string) map[string]string

LocalWorkspacesInEcosystem returns a map of workspace names to their paths, filtered to only include workspaces within the specified ecosystem. This avoids name collisions between workspaces in different ecosystems.

type ScannedDir

type ScannedDir struct {
	Path  string
	Owner *WorkspaceNode
}

ScannedDir represents a directory found by the locator, linking it to the WorkspaceNode that owns it.

type WorkspaceInfo

type WorkspaceInfo struct {
	Name      string         `json:"name"`
	Path      string         `json:"path"`
	Worktrees []WorktreeInfo `json:"worktrees"`
}

WorkspaceInfo represents a workspace from grove ws list --json

type WorkspaceKind

type WorkspaceKind string

WorkspaceKind provides an unambiguous classification for a discovered workspace entity.

const (

	// KindStandaloneProject: A standard project with a grove.yml, not within an Ecosystem.
	// Diagram:
	// /path/to/my-project/  (grove.yml, .git/)
	KindStandaloneProject WorkspaceKind = "StandaloneProject"

	// KindStandaloneProjectWorktree: A git worktree of a StandaloneProject.
	// Diagram:
	// /path/to/my-project/
	//   ├─ .git/
	//   └─ .grove-worktrees/
	//        └─ feature-branch/ (grove.yml, .git file) <-- This
	KindStandaloneProjectWorktree WorkspaceKind = "StandaloneProjectWorktree"

	// KindEcosystemRoot: The main repository of an ecosystem (has grove.yml with a 'workspaces' key).
	// Diagram:
	// /path/to/my-ecosystem/ (grove.yml with 'workspaces', .git/) <-- This
	KindEcosystemRoot WorkspaceKind = "EcosystemRoot"

	// KindEcosystemWorktree: A git worktree of an EcosystemRoot. It also functions as an ecosystem.
	// Diagram:
	// /path/to/my-ecosystem/
	//   ├─ .git/
	//   └─ .grove-worktrees/
	//        └─ eco-feature/ (grove.yml with 'workspaces', .git file) <-- This
	KindEcosystemWorktree WorkspaceKind = "EcosystemWorktree"

	// KindEcosystemSubProject: A project (e.g., submodule) located directly inside an EcosystemRoot.
	// Diagram:
	// /path/to/my-ecosystem/ (EcosystemRoot)
	//   └─ sub-project/ (grove.yml, .git/) <-- This
	KindEcosystemSubProject WorkspaceKind = "EcosystemSubProject"

	// KindEcosystemSubProjectWorktree: A git worktree of an EcosystemSubProject.
	// Diagram:
	// /path/to/my-ecosystem/ (EcosystemRoot)
	//   └─ sub-project/
	//        ├─ .git/
	//        └─ .grove-worktrees/
	//             └─ sub-feature/ (grove.yml, .git file) <-- This
	KindEcosystemSubProjectWorktree WorkspaceKind = "EcosystemSubProjectWorktree"

	// KindEcosystemWorktreeSubProject: A project located inside an EcosystemWorktree.
	// This occurs when a submodule is initialized with `git submodule update` instead of as a linked worktree.
	// Diagram:
	// /path/to/my-ecosystem/.grove-worktrees/eco-feature/ (EcosystemWorktree)
	//   └─ sub-project/ (grove.yml, .git/) <-- This
	KindEcosystemWorktreeSubProject WorkspaceKind = "EcosystemWorktreeSubProject"

	// KindEcosystemWorktreeSubProjectWorktree: A git worktree of an EcosystemWorktreeSubProject.
	// This is the preferred "linked development" state for a sub-project in an ecosystem worktree.
	// Diagram:
	// /path/to/my-ecosystem/.grove-worktrees/eco-feature/ (EcosystemWorktree)
	//   └─ sub-project/ (grove.yml, .git file) <-- This
	KindEcosystemWorktreeSubProjectWorktree WorkspaceKind = "EcosystemWorktreeSubProjectWorktree"

	// KindNonGroveRepo: A directory with a .git folder but no grove.yml.
	// Diagram:
	// /path/to/other-repo/ (.git/ only, no grove.yml) <-- This
	KindNonGroveRepo WorkspaceKind = "NonGroveRepo"
)

type WorkspaceNode

type WorkspaceNode struct {
	Name string        `json:"name"`
	Path string        `json:"path"`
	Kind WorkspaceKind `json:"kind"` // The single source of truth for the entity's type.

	// ParentProjectPath is the path to the repository that manages this worktree.
	// It is set ONLY for kinds that are worktrees (e.g., StandaloneProjectWorktree,
	// EcosystemWorktree, EcosystemSubProjectWorktree, EcosystemWorktreeSubProjectWorktree).
	ParentProjectPath string `json:"parent_project_path,omitempty"`

	// ParentEcosystemPath is the path to the immediate parent that provides ecosystem context.
	// This could be an EcosystemRoot or an EcosystemWorktree.
	// It is set for ALL kinds that exist within an ecosystem context.
	ParentEcosystemPath string `json:"parent_ecosystem_path,omitempty"`

	// RootEcosystemPath is the path to the top-level EcosystemRoot for this node.
	// This allows quick grouping by the ultimate parent ecosystem and facilitates
	// traversing to the root of the hierarchy. It is set for all nodes within an ecosystem.
	RootEcosystemPath string `json:"root_ecosystem_path,omitempty"`

	// NotebookName is the name of the notebook configuration (from notebooks.definitions)
	// that this workspace should use. This is resolved during discovery based on which
	// grove the workspace belongs to.
	NotebookName string `json:"notebook_name,omitempty"`

	// Presentation fields for TUI rendering (pre-calculated for performance)
	TreePrefix string `json:"-"` // Pre-calculated tree indentation and connectors (e.g., "  ├─ ")
	Depth      int    `json:"-"` // Cached depth in the hierarchy

	// Cloned repository-specific fields (populated by discovery)
	Version       string `json:"version,omitempty"`
	Commit        string `json:"commit,omitempty"`
	AuditStatus   string `json:"audit_status,omitempty"`
	ReportPath    string `json:"report_path,omitempty"`
	RepoURL       string `json:"repo_url,omitempty"`
	RepoShorthand string `json:"repo_shorthand,omitempty"`
}

WorkspaceNode is the enriched display model for workspace entities. It represents a flattened, view-friendly node suitable for UIs, with explicit parent-child relationships that form a hierarchical tree structure.

func BuildWorkspaceTree

func BuildWorkspaceTree(nodes []*WorkspaceNode) []*WorkspaceNode

BuildWorkspaceTree takes a flat slice of WorkspaceNodes and pre-calculates presentation data for TUI rendering. It organizes nodes hierarchically and populates the TreePrefix and Depth fields.

The TreePrefix field contains the indentation and tree connectors (e.g., " ├─ ", " └─ ") making it trivial for views to render the tree structure without complex logic.

func GetProjectByPath

func GetProjectByPath(path string) (*WorkspaceNode, error)

GetProjectByPath finds a workspace by path using an efficient upward traversal. It starts from the given path and walks up the directory tree looking for workspace markers. Once a project root is found, it generates the WorkspaceNode for that project and returns the most specific node that contains the original path.

This approach is significantly faster than a full discovery scan (typically <10ms vs 100-500ms) and uses the same centralized classification logic to ensure consistency.

func GetProjectFromNotebookPath

func GetProjectFromNotebookPath(path string) (*WorkspaceNode, string, error)

GetProjectFromNotebookPath finds the project associated with a notebook path. Given a path inside a notebook (e.g., /notebooks/nb/workspaces/zooboo2/plans/...), it extracts the workspace name and finds the corresponding project.

Returns:

  • *WorkspaceNode: the project node if found (nil if not found)
  • string: the notebook root path (empty if not in a notebook)
  • error: only for unexpected errors

func GetProjects

func GetProjects(logger *logrus.Logger) ([]*WorkspaceNode, error)

GetProjects performs discovery and transformation in a single call, returning a flat list of WorkspaceNodes ready for consumption with pre-calculated tree prefixes for rendering.

func TransformToWorkspaceNodes

func TransformToWorkspaceNodes(result *DiscoveryResult, cfg *config.Config) []*WorkspaceNode

TransformToWorkspaceNodes converts a hierarchical DiscoveryResult into a flat list of WorkspaceNode items suitable for display in UIs.

func (*WorkspaceNode) GetDirectChildren

func (w *WorkspaceNode) GetDirectChildren(nodes []*WorkspaceNode) []*WorkspaceNode

GetDirectChildren returns all nodes from the given list that are direct children of this node. This properly handles both ecosystem children and worktree children.

func (*WorkspaceNode) GetGroupingKey

func (w *WorkspaceNode) GetGroupingKey() string

GetGroupingKey returns the path that should be used for grouping related items together. For project worktrees, this returns the parent project path so worktrees are grouped with their parent. For all other nodes (including ecosystem children), this returns the node's own path.

This is useful for filtering and sorting logic that needs to group worktrees with their parent repos while treating ecosystem children as independent entities.

Example:

grove-ecosystem/grove-tmux (EcosystemSubProject) -> returns "grove-tmux"
my-project/.grove-worktrees/feature (StandaloneProjectWorktree) -> returns "my-project"

func (*WorkspaceNode) GetHierarchicalParent

func (w *WorkspaceNode) GetHierarchicalParent() string

GetHierarchicalParent returns the logical parent path for hierarchical display. This considers both ParentProjectPath (for worktrees) and ParentEcosystemPath (for sub-projects).

func (*WorkspaceNode) GetWorktreeName

func (w *WorkspaceNode) GetWorktreeName() string

GetWorktreeName returns the worktree name if this is a worktree node, otherwise empty string. For ecosystem worktrees, it extracts the name from the ParentEcosystemPath. For standalone project worktrees, it extracts from ParentProjectPath.

func (*WorkspaceNode) Identifier

func (p *WorkspaceNode) Identifier() string

Identifier generates a unique, sanitized identifier for a project, suitable for use as a tmux session name or other unique identifier. It creates namespaced identifiers for projects within ecosystem worktrees.

func (*WorkspaceNode) IsChildOf

func (w *WorkspaceNode) IsChildOf(parentPath string) bool

IsChildOf returns true if this node is a direct hierarchical child of the given parent path. This handles both worktree children (via ParentProjectPath) and ecosystem children (via ParentEcosystemPath).

func (*WorkspaceNode) IsEcosystem

func (w *WorkspaceNode) IsEcosystem() bool

IsEcosystem returns true if this node represents an ecosystem (root or worktree)

func (*WorkspaceNode) IsEcosystemChild

func (w *WorkspaceNode) IsEcosystemChild() bool

IsEcosystemChild returns true if this is a child within an ecosystem hierarchy (not a worktree child). Useful for distinguishing ecosystem subprojects from worktrees.

func (*WorkspaceNode) IsProjectWorktreeChild

func (w *WorkspaceNode) IsProjectWorktreeChild() bool

IsProjectWorktreeChild returns true if this is a worktree child of a project (not an ecosystem child). Useful for grouping worktrees under their parent projects.

func (*WorkspaceNode) IsWorktree

func (w *WorkspaceNode) IsWorktree() bool

IsWorktree returns true if this node represents a worktree. Note: This includes EcosystemWorktree, which is BOTH a worktree AND a container/ecosystem. For filtering logic that needs to distinguish "leaf worktrees" from containers, use IsProjectWorktreeChild().

Example hierarchy:

grove-ecosystem/ (EcosystemRoot)
  ├─ grove-tmux/ (EcosystemSubProject) - IsWorktree()=false, IsEcosystemChild()=true
  └─ .grove-worktrees/
      └─ my-branch/ (EcosystemWorktree) - IsWorktree()=true, IsEcosystem()=true
          └─ grove-hooks/ (EcosystemWorktreeSubProject) - IsWorktree()=false, IsEcosystemChild()=true

func (*WorkspaceNode) Validate

func (w *WorkspaceNode) Validate() error

Validate checks the internal consistency of this WorkspaceNode and returns an error if the node's fields don't match the expected invariants for its Kind.

This is useful for catching bugs where node relationships are incorrectly set up.

type WorkspaceTree

type WorkspaceTree struct {
	Node     *WorkspaceNode   `json:"node"`
	Children []*WorkspaceTree `json:"children"`
}

WorkspaceTree represents a node in the hierarchical workspace tree. It's designed for consumers that need to render or traverse the full hierarchy.

func BuildTree

func BuildTree(nodes []*WorkspaceNode) []*WorkspaceTree

BuildTree constructs a hierarchical tree from a flat slice of WorkspaceNodes. This is the recommended way for UIs to consume the workspace hierarchy.

func GetWorkspaceTree

func GetWorkspaceTree(logger *logrus.Logger) ([]*WorkspaceTree, error)

GetWorkspaceTree performs discovery and returns a fully formed workspace hierarchy. This is the recommended function for UIs that need to render a tree.

type WorkspaceType

type WorkspaceType string

WorkspaceType defines whether a workspace is the main checkout or a git worktree.

const (
	WorkspaceTypePrimary  WorkspaceType = "Primary"
	WorkspaceTypeWorktree WorkspaceType = "Worktree"
)

type WorktreeInfo

type WorktreeInfo struct {
	Path   string `json:"path"`
	Branch string `json:"branch"`
	IsMain bool   `json:"is_main"`
}

WorktreeInfo represents a worktree within a workspace

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL