Documentation
¶
Index ¶
- Constants
- Variables
- func AcquireLock(worktreePath, command, agentName string) error
- func BranchExistsLocally(dir, branch string) (bool, error)
- func ClearLockTaskID(worktreePath string) error
- func DetectIntegrationBranch(worktrees []WorktreeInfo) string
- func EnsureBdDaemonRunning(timeout time.Duration) (bool, error)
- func EnsureEpicPR(worktreePath, epicID string) error
- func EnsureWorktreeBranch(worktreePath, targetBranch, fallbackRef string) error
- func Execute() error
- func FilterEnv(env []string) []string
- func FilteredEnv() []string
- func GenerateConflictResolutionPrompt(sourceBranch, targetBranch string, conflicts []string) string
- func GenerateFleetPlanningPrompt(agentName, taskID string, workspace *WorkspaceConfig) string
- func GenerateFleetTaskPrompt(agentName, taskID string, workspace *WorkspaceConfig, backendName string) string
- func GenerateLeadPrompt() string
- func GeneratePlanningPrompt(agentName string, workspace *WorkspaceConfig, parentID string) string
- func GenerateTaskPrompt(agentName string, workspace *WorkspaceConfig, parentID string, ...) string
- func GetBackendName() string
- func GetBeadsDir() string
- func GetConfigDir() string
- func GetConfigPath() string
- func GetConflictedFiles(dir string) ([]string, error)
- func GetCurrentBranch(path string) (string, error)
- func GetDefaultBranch() string
- func GetDefaultBranchForWorktrees(worktrees []WorktreeInfo) string
- func GetGitBranches() ([]string, error)
- func GetLockStatus(worktreePath string) string
- func GetScriptDir() (string, error)
- func GetSignalFilePath(worktreePath string) string
- func GetWorkspaceDir(name string) string
- func GetWorktreeName(path string) string
- func GetWorktreesDir() string
- func GitCheckout(dir, branch string) error
- func GitCheckoutDetached(dir, ref string) error
- func GitCheckoutNewFromRef(dir, branch, startPoint string) error
- func GitClean(dir string) error
- func GitCleanDryRun(dir string) (string, error)
- func GitCreateBranchFromHead(dir, name string) error
- func GitDeleteBranch(dir, name string, force bool) error
- func GitFetch(dir string) error
- func GitFetchRemote(dir, remote string) error
- func GitMerge(dir, branch, message string) error
- func GitMergeOrigin(dir, branch, message string) error
- func GitMergeRemote(dir, remote, branch, message string) error
- func GitPull(dir, branch string) error
- func GitPullRemote(dir, remote, branch string) error
- func GitPush(dir, branch string) error
- func GitPushForce(dir, branch string) error
- func GitPushRefspec(dir, remote, localRef, remoteRef string) error
- func GitPushRemote(dir, remote, branch string) error
- func GitReset(dir, ref string) error
- func GitStash(dir string) (bool, error)
- func GitStashPop(dir string) error
- func HasAnyAvailableTasks(parentID string) (bool, error)
- func HasAvailableImplementationTasks(parentID string) (bool, error)
- func HasAvailablePlanningTasks(parentID string) (bool, error)
- func HasCommitsBetween(dir, target, source string) (bool, error)
- func HasCommitsBetweenRemote(dir, remote, target, source string) (bool, error)
- func HasDesign(issue BdIssue) bool
- func HasNeedsRevision(issue BdIssue) bool
- func HasUnclosedBlockers(deps []Dependency, unclosedIDs map[string]bool) bool
- func HasUnmergedFiles(dir string) (bool, error)
- func InvokeAgent(workDir, prompt, agentName string) error
- func InvokeAgentForConflicts(workDir, sourceBranch, targetBranch string, conflicts []string) error
- func InvokeAgentNonInteractive(workDir, prompt, agentName string, shutdown <-chan struct{}) error
- func IsAvailableForAny(issue BdIssue, unclosedIDs map[string]bool) bool
- func IsAvailableForImplementation(issue BdIssue, unclosedIDs map[string]bool) bool
- func IsAvailableForPlanning(issue BdIssue, unclosedIDs map[string]bool) bool
- func IsCleanWorkingTree(dir string) (bool, error)
- func IsEpic(issue BdIssue) bool
- func IsOpen(issue BdIssue) bool
- func IsRefCheckedOutInWorktree(dir, branch string) (bool, string, error)
- func IsWorkableTask(issue BdIssue) bool
- func IsWorkspaceMode() bool
- func ListBackends() []string
- func LoadPromptTemplate(path string, data PromptData) (string, error)
- func NeedsPlan(issue BdIssue) bool
- func ReadyToImplement(issue BdIssue) bool
- func RecoverWorktree(worktreePath, agentName string, exitCode int) error
- func RegisterBackend(b Backend)
- func ReleaseLock(worktreePath string) error
- func RemoteBranchExists(dir, remote, branch string) (bool, error)
- func ResetBeadsDirCache()
- func ResolveAndSetBackend() error
- func ResolveBackendName() string
- func ResolveDaemonStatePath(projectDir string) string
- func ResolveLockDir(path string) string
- func ResolveWorktreePath(name string) (string, error)
- func ResolveWorktreesDir() (string, error)
- func RunAutoModeLoop(opts AutoModeOptions, shutdown chan struct{})
- func RunAutoModeTmux(opts AutoModeOptions, shutdown chan struct{})
- func RunGitCommand(dir string, args ...string) (string, error)
- func RunGitCommandWithOutput(dir string, args ...string) error
- func SaveConfig(cfg *LoomConfig) error
- func SetBackend(name string) error
- func SetupSignalHandler() chan struct{}
- func UpdateLockState(worktreePath, state string) error
- func UpdateLockTask(worktreePath, taskID, taskTitle string) error
- func ValidBackendNames() string
- func ValidateRemoteName(name string) error
- type AgentEntry
- type AgentProcess
- type AgentStatus
- type AgentsResponse
- type AutoModeOptions
- type AutoModeState
- type Backend
- type BdIssue
- type BdStats
- type ClaudeBackend
- type CodexBackend
- type CommandResult
- type CommitDetail
- type ContentBlock
- type Daemon
- type DaemonAgentInfo
- type DaemonAgentState
- type DaemonAgentStateEntry
- type DaemonAgentStatus
- type DaemonConfig
- type DaemonSettings
- type DaemonState
- type Dependency
- type EpicAssigner
- type EpicInfo
- type EventMessage
- type FileChange
- type HealthResponse
- type LockInfo
- type LoomConfig
- type MonitorData
- type MonitorStats
- type OpenCodeBackend
- type PaneState
- type ProjectFile
- type PromptData
- type RepoConfig
- type ResolvedTarget
- type Resolver
- func (r *Resolver) DiscoverWorktrees() ([]WorktreeInfo, error)
- func (r *Resolver) GetDefaultBranch() string
- func (r *Resolver) GetWorktreesDir() string
- func (r *Resolver) Mode() ResolverMode
- func (r *Resolver) ResolveWorkspaceByName(name string) (string, bool)
- func (r *Resolver) ResolveWorktreePath(name string) (string, error)
- func (r *Resolver) SetWorkspace(name string) error
- func (r *Resolver) WorkspaceName() string
- func (r *Resolver) WorkspaceNames() []string
- type ResolverMode
- type RestartPolicy
- type RoleConfig
- type StatsResponse
- type StatusResponse
- type StreamEvent
- type SupervisedAgentStatus
- type SyncInfo
- type SyncResponse
- type TaskInfo
- type TaskSummary
- type TasksResponse
- type WorkspaceConfig
- type WorkspaceDetail
- type WorkspaceInfo
- type WorkspacesResponse
- type WorktreeInfo
- type WorktreeSyncDetail
Constants ¶
const ( StateActive = "active" // Claude is executing (planning/working) StateIdle = "idle" // Polling for tasks, no work available )
Lock states for auto mode tracking
const LockFileName = ".agent.lock"
LockFileName is the name of the lock file in each worktree
const NeedsRevisionLabel = "needs-revision"
NeedsRevisionLabel is added when a plan review is rejected. Issues with this label are treated as needing re-planning even if they have a design.
Variables ¶
var ( Version = "dev" Build = "unknown" )
Version information (set at build time via ldflags)
var IsTmuxAvailable = func() bool { return exec.Command("tmux", "-V").Run() == nil }
IsTmuxAvailable checks if tmux is installed and available. It is a variable so tests can override it.
Functions ¶
func AcquireLock ¶
AcquireLock attempts to acquire an agent lock for the worktree Returns an error if an agent is already running Uses atomic file creation (O_EXCL) to prevent race conditions
func BranchExistsLocally ¶
BranchExistsLocally checks if a branch exists as a local ref.
func ClearLockTaskID ¶
ClearLockTaskID resets the TaskID, TaskTitle, and TaskStartedAt in the lock file. Called before each agent session in auto mode so we can detect whether the new session claims a task (used for no-progress detection).
func DetectIntegrationBranch ¶
func DetectIntegrationBranch(worktrees []WorktreeInfo) string
DetectIntegrationBranch analyzes worktree branches to find a common integration branch that is closer than main. Returns empty string if no such branch is found.
func EnsureBdDaemonRunning ¶
EnsureBdDaemonRunning checks if the bd daemon is running and starts it if not. Returns (true, nil) if we started the daemon, (false, nil) if it was already running, or (false, err) if the daemon could not be started or did not become ready in time.
func EnsureEpicPR ¶
EnsureEpicPR checks if a PR exists for the epic branch and creates one if needed. This is non-fatal — errors are returned but should not block agent restarts.
func EnsureWorktreeBranch ¶
EnsureWorktreeBranch switches the worktree to the target branch. If already on the target branch, it's a no-op. If the working tree is dirty, a WIP commit is created before switching. The fallbackRef (e.g. "origin/main") is used when creating a brand-new branch.
func FilterEnv ¶
FilterEnv filters the given environment variable slice through the allowlist. Only variables whose names match an exact allowlist entry or an allowed prefix are included. Malformed entries (missing '=') are excluded.
func FilteredEnv ¶
func FilteredEnv() []string
FilteredEnv returns os.Environ() filtered through the allowlist. Use this instead of os.Environ() when setting up subprocess environments.
func GenerateConflictResolutionPrompt ¶
GenerateConflictResolutionPrompt creates the prompt for merge conflict resolution
func GenerateFleetPlanningPrompt ¶
func GenerateFleetPlanningPrompt(agentName, taskID string, workspace *WorkspaceConfig) string
GenerateFleetPlanningPrompt creates the prompt for a fleet planning agent with a pre-assigned task. Fleet workers receive their task from the Fleet API and skip task selection/claiming.
func GenerateFleetTaskPrompt ¶
func GenerateFleetTaskPrompt(agentName, taskID string, workspace *WorkspaceConfig, backendName string) string
GenerateFleetTaskPrompt creates the prompt for a fleet implementation agent with a pre-assigned task. Fleet workers receive their task from the Fleet API and skip task selection/claiming.
func GenerateLeadPrompt ¶
func GenerateLeadPrompt() string
GenerateLeadPrompt creates the prompt for the interactive lead/manager mode
func GeneratePlanningPrompt ¶
func GeneratePlanningPrompt(agentName string, workspace *WorkspaceConfig, parentID string) string
GeneratePlanningPrompt creates the prompt for the planning agent. If workspace is non-nil, workspace context is injected into the prompt. If parentID is non-empty, the prompt scopes task discovery to that epic. SYNC: The jq filters below must match taskfilter.go NeedsPlan() criteria:
planning: design empty OR has "needs-revision" label
func GenerateTaskPrompt ¶
func GenerateTaskPrompt(agentName string, workspace *WorkspaceConfig, parentID string, backendName string) string
GenerateTaskPrompt creates the prompt for the implementation agent. If workspace is non-nil, workspace context is injected into the prompt. If parentID is non-empty, the prompt scopes task discovery to that epic. SYNC: The jq filters below must match taskfilter.go ReadyToImplement() criteria:
implementation: design non-empty AND no "needs-revision" label
func GetBackendName ¶
func GetBackendName() string
GetBackendName returns the name of the currently active backend.
func GetBeadsDir ¶
func GetBeadsDir() string
GetBeadsDir returns the directory where .beads/ lives. In workspace mode, this is the workspace root path (shared across repos). In legacy mode, this returns "." (current directory). The result is cached for the lifetime of the process.
func GetConfigDir ¶
func GetConfigDir() string
GetConfigDir returns the loom config directory path. Respects LOOM_CONFIG_DIR env var, otherwise defaults to ~/.loom.
func GetConfigPath ¶
func GetConfigPath() string
GetConfigPath returns the full path to the loom config file.
func GetConflictedFiles ¶
GetConflictedFiles returns a list of files with merge conflicts
func GetCurrentBranch ¶
GetCurrentBranch returns the current branch for a git directory
func GetDefaultBranch ¶
func GetDefaultBranch() string
GetDefaultBranch returns the default integration branch. Resolution order: LOOM_DEFAULT_BRANCH env var > auto-detected from worktree topology > "main" This is a convenience wrapper that discovers worktrees automatically. When worktrees are already available, use GetDefaultBranchForWorktrees instead.
func GetDefaultBranchForWorktrees ¶
func GetDefaultBranchForWorktrees(worktrees []WorktreeInfo) string
GetDefaultBranchForWorktrees returns the default integration branch using pre-discovered worktrees to avoid redundant filesystem/git operations.
func GetGitBranches ¶
GetGitBranches returns all local and remote branch names
func GetLockStatus ¶
GetLockStatus returns a human-readable status for a worktree's lock Uses explicit state words: planning, working, done, review, idle
func GetScriptDir ¶
GetScriptDir returns the directory where loom is run from
func GetSignalFilePath ¶
GetSignalFilePath returns the path to the signal file for a given worktree. The signal file is stored in a temporary directory to avoid being deleted by git clean operations.
func GetWorkspaceDir ¶
GetWorkspaceDir returns the directory path for a named workspace.
func GetWorktreeName ¶
GetWorktreeName extracts the worktree name from a path
func GetWorktreesDir ¶
func GetWorktreesDir() string
GetWorktreesDir returns the worktrees directory path Priority: --worktrees flag > LOOM_WORKTREES_DIR env var > default "worktrees"
func GitCheckoutDetached ¶
GitCheckoutDetached checks out a ref in detached HEAD mode
func GitCheckoutNewFromRef ¶
GitCheckoutNewFromRef creates a new local branch at the given starting point.
func GitCleanDryRun ¶
GitCleanDryRun returns the list of untracked files that would be removed by git clean
func GitCreateBranchFromHead ¶
GitCreateBranchFromHead creates a new branch at the current HEAD and switches to it
func GitDeleteBranch ¶
GitDeleteBranch deletes a local branch. Use force=true for -D (force delete).
func GitFetchRemote ¶
GitFetchRemote fetches from the specified remote
func GitMergeOrigin ¶
GitMergeOrigin attempts to merge origin/branch
func GitMergeRemote ¶
GitMergeRemote attempts to merge remote/branch
func GitPullRemote ¶
GitPullRemote pulls from the specified remote for the given branch
func GitPushRefspec ¶
GitPushRefspec pushes a local ref to a different remote ref using a refspec
func GitPushRemote ¶
GitPushRemote pushes to the specified remote
func GitStash ¶
GitStash stashes local changes. Returns true if changes were actually stashed, false if nothing was stashed (e.g. only untracked files, or clean tree).
func HasAnyAvailableTasks ¶
HasAnyAvailableTasks checks if there are any ready tasks regardless of design status. Used by custom roles with task_filter=any.
func HasAvailableImplementationTasks ¶
HasAvailableImplementationTasks checks if there are tasks ready for implementation (ready tasks WITH an approved design, excluding tasks with needs-revision label and epics)
func HasAvailablePlanningTasks ¶
HasAvailablePlanningTasks checks if there are tasks that need planning (ready tasks without a design OR with needs-revision label, excluding epics)
func HasCommitsBetween ¶
HasCommitsBetween checks if source has commits not in target
func HasCommitsBetweenRemote ¶
HasCommitsBetweenRemote checks if source has commits not in target using a specific remote
func HasNeedsRevision ¶
HasNeedsRevision returns true if the issue has the needs-revision label.
func HasUnclosedBlockers ¶
func HasUnclosedBlockers(deps []Dependency, unclosedIDs map[string]bool) bool
HasUnclosedBlockers returns true if any blocking dependency is still unclosed. unclosedIDs is a set of issue IDs that have NOT been closed yet. A blocker is only considered resolved when its issue is closed.
func HasUnmergedFiles ¶
HasUnmergedFiles checks if there are unmerged files in the working tree
func InvokeAgent ¶
InvokeAgent dispatches an interactive invocation to the active backend.
func InvokeAgentForConflicts ¶
InvokeAgentForConflicts runs the active backend to resolve merge conflicts.
func InvokeAgentNonInteractive ¶
InvokeAgentNonInteractive dispatches a non-interactive invocation to the active backend.
func IsAvailableForAny ¶
IsAvailableForAny returns true if the issue can be picked up by any agent regardless of design status: workable and no unclosed blockers.
func IsAvailableForImplementation ¶
IsAvailableForImplementation returns true if the issue should be picked up by an implementation agent: workable, no unclosed blockers, and has an approved design.
func IsAvailableForPlanning ¶
IsAvailableForPlanning returns true if the issue should be picked up by a planning agent: workable, no unclosed blockers, and needs a plan.
func IsCleanWorkingTree ¶
IsCleanWorkingTree checks if the working tree is clean
func IsRefCheckedOutInWorktree ¶
IsRefCheckedOutInWorktree checks if a branch is checked out in any worktree. Returns (isCheckedOut, worktreePath, error).
func IsWorkableTask ¶
IsWorkableTask returns true if the issue can be picked up by an agent: status is open and not an epic.
func IsWorkspaceMode ¶
func IsWorkspaceMode() bool
IsWorkspaceMode returns true if a config file exists with at least one workspace defined.
func ListBackends ¶
func ListBackends() []string
ListBackends returns a sorted list of registered backend names.
func LoadPromptTemplate ¶
func LoadPromptTemplate(path string, data PromptData) (string, error)
LoadPromptTemplate reads a prompt template file and executes it with the given data. Returns the rendered prompt string.
func NeedsPlan ¶
NeedsPlan returns true if the issue needs planning: either no design, or has the needs-revision label (plan was rejected). SYNC: Must match issueCategory.ts getOpenStatus()
func ReadyToImplement ¶
ReadyToImplement returns true if the issue has an approved design ready for implementation (has design AND no needs-revision label). SYNC: Must match issueCategory.ts getOpenStatus() returning 'ready'
func RecoverWorktree ¶
RecoverWorktree provides a programmatic, non-interactive recovery path for daemon use. It wraps the existing recovery helpers with force=true and analyze=false semantics: force-release any stale lock, kill running processes, reset orphaned tasks to open, and clean untracked files without prompting.
exitCode informs recovery behavior: on clean exit (0) tasks that are still in_progress are likely mid-completion (e.g. the agent signaled done but the status update hasn't landed yet), so we log but still run the status-aware resetTask which preserves review/closed tasks. On non-zero exit the task is more likely genuinely orphaned.
func RegisterBackend ¶
func RegisterBackend(b Backend)
RegisterBackend adds a backend to the registry by its Name(). Panics if b is nil (programming error).
func ReleaseLock ¶
ReleaseLock releases the agent lock for the worktree
func RemoteBranchExists ¶
RemoteBranchExists checks if a branch exists on the specified remote.
func ResetBeadsDirCache ¶
func ResetBeadsDirCache()
ResetBeadsDirCache clears the cached beads directory value. For testing only.
func ResolveAndSetBackend ¶
func ResolveAndSetBackend() error
ResolveAndSetBackend resolves the backend name from the precedence chain and sets it as the active backend. Returns an error if the resolved name is not a registered backend.
func ResolveBackendName ¶
func ResolveBackendName() string
ResolveBackendName returns the backend name using the precedence chain: --backend flag > LOOM_BACKEND env > project-local loom.yaml > global config > default ("claude").
func ResolveDaemonStatePath ¶
ResolveDaemonStatePath returns the path to daemon-agents.json for the given project directory. It loads the daemon config to determine the PID file location, then returns the state file path adjacent to the PID file. On config load error, falls back to <projectDir>/.loom/daemon-agents.json.
func ResolveLockDir ¶
ResolveLockDir determines the correct directory for the lock file. If the path is inside a workspace (matches a workspace path or repo path), returns the workspace root so all repos share one lock. Otherwise returns the path unchanged (legacy mode).
func ResolveWorktreePath ¶
ResolveWorktreePath converts a worktree name to its full path Accepts:
- A worktree name (e.g., "falcon") -> ./worktrees/falcon
- An absolute path (e.g., /path/to/worktree) -> as-is
- Empty string -> current directory
func ResolveWorktreesDir ¶
ResolveWorktreesDir returns the absolute path to the worktrees directory If the configured path is absolute, use it directly; otherwise join with scriptDir
func RunAutoModeLoop ¶
func RunAutoModeLoop(opts AutoModeOptions, shutdown chan struct{})
RunAutoModeLoop runs the auto mode loop for either plan or task agents
func RunAutoModeTmux ¶
func RunAutoModeTmux(opts AutoModeOptions, shutdown chan struct{})
RunAutoModeTmux runs auto mode with tmux session management and live streaming
func RunGitCommand ¶
RunGitCommand executes a git command in the specified directory
func RunGitCommandWithOutput ¶
RunGitCommandWithOutput executes a git command and streams output to stdout/stderr
func SaveConfig ¶
func SaveConfig(cfg *LoomConfig) error
SaveConfig writes the loom config to the config file. Creates the config directory if it doesn't exist.
func SetBackend ¶
SetBackend validates and switches the active backend.
func SetupSignalHandler ¶
func SetupSignalHandler() chan struct{}
SetupSignalHandler sets up graceful shutdown on SIGINT/SIGTERM Returns a channel that will be CLOSED when shutdown is requested (closed channel pattern allows multiple goroutines to detect shutdown)
func UpdateLockState ¶
UpdateLockState updates the lock file with current execution state Used by auto mode to distinguish idle (polling) from active (executing Claude)
func UpdateLockTask ¶
UpdateLockTask updates the lock file with task information This is called by Claude after picking a task to work on
func ValidBackendNames ¶
func ValidBackendNames() string
ValidBackendNames returns a formatted string of valid backend names for help text.
func ValidateRemoteName ¶
ValidateRemoteName checks if a remote name is safe for use in git commands. Empty is allowed (resolveRemote defaults it to "origin").
Types ¶
type AgentEntry ¶
type AgentEntry struct {
Worktree string `yaml:"worktree"`
Role string `yaml:"role"`
Auto bool `yaml:"auto,omitempty"`
Backend string `yaml:"backend,omitempty"`
}
AgentEntry defines a single agent assignment.
type AgentProcess ¶
type AgentProcess struct {
// contains filtered or unexported fields
}
AgentProcess tracks a single supervised agent subprocess.
type AgentStatus ¶
type AgentStatus struct {
Name string `json:"name"`
Branch string `json:"branch"`
Status string `json:"status"` // "ready", "3 changes", "running (plan, 5m ago)"
Ahead int `json:"ahead"` // commits ahead of integration branch
Behind int `json:"behind"` // commits behind integration branch
Role string `json:"role,omitempty"` // role from daemon config (e.g., "plan", "task")
Workspace string `json:"workspace"` // workspace name (empty in legacy mode)
DaemonManaged bool `json:"daemon_managed,omitempty"` // true if under daemon supervision
Commits []CommitDetail `json:"commits,omitempty"` // recent commits ahead of integration branch
Changes []FileChange `json:"changes,omitempty"` // uncommitted file changes
}
AgentStatus represents a single agent/worktree status
func CollectAgentStatusOnly ¶
func CollectAgentStatusOnly() []AgentStatus
CollectAgentStatusOnly returns just agent status without task context. Exported for use by the HTTP server.
type AgentsResponse ¶
type AgentsResponse struct {
Workspace WorkspaceInfo `json:"workspace"` // workspace mode info
Agents []AgentStatus `json:"agents"` // flat list (existing)
ByWorkspace map[string][]AgentStatus `json:"by_workspace,omitempty"` // grouped by workspace
Timestamp time.Time `json:"timestamp"`
}
AgentsResponse wraps the agents list with optional workspace grouping.
type AutoModeOptions ¶
type AutoModeOptions struct {
Interval int // Polling interval in seconds when no tasks available
MaxTasks int // Maximum tasks to process before exiting (0 = unlimited)
IdleTimeout int // Exit after N minutes with no available tasks (0 = no timeout)
AgentType string // "plan" or "task"
AgentName string
WorktreePath string
ParentID string // Epic ID to scope task discovery to (empty = all tasks)
CustomPromptGen func(string, *WorkspaceConfig) string // Custom prompt generator (overrides AgentType selection)
CustomTaskCheck func() (bool, error) // Custom task availability check (overrides AgentType selection)
BackoffBase time.Duration // Base backoff duration for no-progress retries (default 30s)
}
AutoModeOptions holds configuration for auto mode
type AutoModeState ¶
type AutoModeState struct {
TasksCompleted int
ConsecutiveErrors int
ConsecutiveNoProgress int // sessions that completed without claiming a task
LastTaskTime time.Time
IdleStartTime time.Time
ShouldExit bool
ExitReason string
}
AutoModeState tracks the current state of auto mode execution
type Backend ¶
type Backend interface {
Name() string
InvokeInteractive(workDir, prompt, agentName string) error
InvokeNonInteractive(workDir, prompt, agentName string, shutdown <-chan struct{}) error
}
Backend is the interface that all AI coding agent backends must implement.
Name returns a unique identifier for the backend (e.g. "claude", "codex"). InvokeInteractive starts a live, interactive agent session in the terminal. InvokeNonInteractive runs a headless agent session that can be canceled via the shutdown channel.
type BdIssue ¶
type BdIssue struct {
ID string `json:"id"`
Title string `json:"title"`
Status string `json:"status"`
Priority int `json:"priority"`
IssueType string `json:"issue_type"`
Design string `json:"design"`
Assignee string `json:"assignee"`
Labels []string `json:"labels"`
Dependencies []Dependency `json:"dependencies"`
}
BdIssue represents an issue from bd list --json
func GetAnyAvailableTasks ¶
GetAnyAvailableTasks returns any ready tasks regardless of design status. Used by custom roles with task_filter=any. When parentID is non-empty, only tasks under that epic are returned.
func GetAvailableImplementationTasks ¶
GetAvailableImplementationTasks returns tasks ready for implementation (ready tasks WITH an approved design, excluding tasks with needs-revision label and epics) When parentID is non-empty, only tasks under that epic are returned.
func GetAvailablePlanningTasks ¶
GetAvailablePlanningTasks returns tasks that need planning (ready tasks without a design OR with needs-revision label, excluding epics) When parentID is non-empty, only tasks under that epic are returned.
type BdStats ¶
type BdStats struct {
Summary struct {
TotalIssues int `json:"total_issues"`
OpenIssues int `json:"open_issues"`
ClosedIssues int `json:"closed_issues"`
InProgressIssues int `json:"in_progress_issues"`
BlockedIssues int `json:"blocked_issues"`
DeferredIssues int `json:"deferred_issues"`
TombstoneIssues int `json:"tombstone_issues"`
PinnedIssues int `json:"pinned_issues"`
} `json:"summary"`
}
BdStats represents output from bd stats --json
type ClaudeBackend ¶
type ClaudeBackend struct{}
ClaudeBackend implements the Backend interface for the Claude CLI.
func (*ClaudeBackend) InvokeInteractive ¶
func (c *ClaudeBackend) InvokeInteractive(workDir, prompt, agentName string) error
func (*ClaudeBackend) InvokeNonInteractive ¶
func (c *ClaudeBackend) InvokeNonInteractive(workDir, prompt, agentName string, shutdown <-chan struct{}) error
func (*ClaudeBackend) Name ¶
func (c *ClaudeBackend) Name() string
type CodexBackend ¶
type CodexBackend struct{}
CodexBackend implements the Backend interface for the OpenAI Codex CLI.
func (*CodexBackend) InvokeInteractive ¶
func (c *CodexBackend) InvokeInteractive(workDir, prompt, agentName string) error
func (*CodexBackend) InvokeNonInteractive ¶
func (c *CodexBackend) InvokeNonInteractive(workDir, prompt, agentName string, shutdown <-chan struct{}) error
func (*CodexBackend) Name ¶
func (c *CodexBackend) Name() string
type CommandResult ¶
CommandResult represents the output of a command execution
type CommitDetail ¶
type CommitDetail struct {
Hash string `json:"hash"`
Message string `json:"message"`
URL string `json:"url,omitempty"` // GitHub commit URL if remote available
}
CommitDetail represents a single commit with hash, message, and optional GitHub URL.
type ContentBlock ¶
type Daemon ¶
type Daemon struct {
// contains filtered or unexported fields
}
Daemon coordinates multiple supervised agents.
func NewDaemon ¶
func NewDaemon(config *DaemonConfig, projectDir string) (*Daemon, error)
NewDaemon creates a daemon from the loaded config.
func (*Daemon) AgentCount ¶
AgentCount returns the number of configured agents.
func (*Daemon) Agents ¶
func (d *Daemon) Agents() []SupervisedAgentStatus
Agents returns a snapshot of all agent statuses for inspection. The returned SupervisedAgentStatus structs are safe to use without synchronization.
type DaemonAgentInfo ¶
DaemonAgentInfo carries daemon supervision metadata for a worktree.
type DaemonAgentState ¶
type DaemonAgentState struct {
PID int `json:"pid"`
Agents []DaemonAgentStateEntry `json:"agents"`
}
DaemonAgentState represents the daemon-agents.json file format. This matches the DaemonState written by daemon_cmd.go.
type DaemonAgentStateEntry ¶
type DaemonAgentStateEntry struct {
Worktree string `json:"worktree"`
Status string `json:"status"`
Role string `json:"role"`
}
DaemonAgentStateEntry represents a single agent in daemon-agents.json
type DaemonAgentStatus ¶
type DaemonAgentStatus struct {
Worktree string `json:"worktree"`
Role string `json:"role"`
PID int `json:"pid"`
Status string `json:"status"` // "running", "starting", "stopped", "failed"
TaskID string `json:"task_id,omitempty"`
EpicID string `json:"epic_id,omitempty"`
RestartCount int `json:"restart_count"`
LastStart time.Time `json:"last_start,omitempty"`
LastExit time.Time `json:"last_exit,omitempty"`
LastExitCode int `json:"last_exit_code,omitempty"`
}
DaemonAgentStatus represents the status of a single supervised agent
type DaemonConfig ¶
type DaemonConfig struct {
Backend string
Daemon DaemonSettings
Roles map[string]RoleConfig
Agents []AgentEntry
}
DaemonConfig is the merged, resolved configuration used by callers.
func LoadDaemonConfig ¶
func LoadDaemonConfig(projectDir string) (*DaemonConfig, error)
LoadDaemonConfig merges global (~/.loom/config.yaml) and local (loom.yaml) config. Local values override global. Returns defaults if neither file exists.
func (*DaemonConfig) ResolveRole ¶
func (dc *DaemonConfig) ResolveRole(name string) (RoleConfig, bool)
ResolveRole looks up a role by name in the merged config. Returns the RoleConfig and true if found, zero value and false if not.
type DaemonSettings ¶
type DaemonSettings struct {
PIDFile string `yaml:"pid_file,omitempty"`
LogDir string `yaml:"log_dir,omitempty"`
RestartPolicy RestartPolicy `yaml:"restart_policy,omitempty"`
MaxAgents *int `yaml:"max_agents,omitempty"`
}
DaemonSettings holds daemon-specific config fields.
type DaemonState ¶
type DaemonState struct {
PID int `json:"pid"`
StartedAt time.Time `json:"started_at"`
Agents []DaemonAgentStatus `json:"agents"`
}
DaemonState represents the complete daemon state in daemon-agents.json
type Dependency ¶
type Dependency struct {
IssueID string `json:"issue_id"`
DependsOnID string `json:"depends_on_id"`
Type string `json:"type"` // "parent-child" or "blocks"
CreatedAt string `json:"created_at"`
CreatedBy string `json:"created_by"`
}
Dependency represents a dependency relationship from bd ready --json
type EpicAssigner ¶
type EpicAssigner struct {
// contains filtered or unexported fields
}
EpicAssigner manages epic-to-worktree assignments for the daemon. It queries open epics and assigns each available worktree to the highest-priority epic with ready tasks.
func NewEpicAssigner ¶
func NewEpicAssigner() *EpicAssigner
NewEpicAssigner creates a new EpicAssigner.
func (*EpicAssigner) AssignWorktree ¶
func (ea *EpicAssigner) AssignWorktree(worktreeName string) (string, error)
AssignWorktree queries open epics, skips already-assigned ones, and returns the highest-priority unassigned epic with ready tasks. Returns empty string if no epics are available (agent falls back to non-epic mode).
func (*EpicAssigner) Assignments ¶
func (ea *EpicAssigner) Assignments() map[string]string
Assignments returns a copy of the full assignment map (for status/logging).
func (*EpicAssigner) GetAssignment ¶
func (ea *EpicAssigner) GetAssignment(worktreeName string) string
GetAssignment returns the current epic ID for a worktree, or empty string.
func (*EpicAssigner) ReleaseWorktree ¶
func (ea *EpicAssigner) ReleaseWorktree(worktreeName string)
ReleaseWorktree removes the assignment for a worktree, making its epic available for reassignment.
type EventMessage ¶
type EventMessage struct {
Content []ContentBlock `json:"content,omitempty"`
}
type FileChange ¶
type FileChange struct {
Status string `json:"status"` // "M", "A", "D", "??", "R"
Path string `json:"path"`
}
FileChange represents a single file change from git status.
type HealthResponse ¶
HealthResponse is the health check response.
type LockInfo ¶
type LockInfo struct {
PID int `json:"pid"`
Command string `json:"command"`
StartedAt time.Time `json:"started_at"`
AgentName string `json:"agent_name"`
TaskID string `json:"task_id,omitempty"`
TaskTitle string `json:"task_title,omitempty"`
TaskStartedAt time.Time `json:"task_started_at,omitempty"` // Per-task timing (reset when new task claimed)
State string `json:"state,omitempty"` // Execution state (active/idle) for auto mode
Workspace string `json:"workspace,omitempty"` // Workspace name when in workspace mode
}
LockInfo holds information about a running agent
func CheckLock ¶
CheckLock checks if a lock exists and if the process is still running Returns the lock info, whether the process is running, and any error
func ReadLockFile ¶
ReadLockFile reads and parses the lock file without modifying it
type LoomConfig ¶
type LoomConfig struct {
DefaultWorkspace string `yaml:"default_workspace,omitempty"`
Backend string `yaml:"backend,omitempty"`
Workspaces map[string]WorkspaceConfig `yaml:"workspaces"`
Daemon *DaemonSettings `yaml:"daemon,omitempty"`
}
LoomConfig is the top-level configuration from ~/.loom/config.yaml
func LoadConfig ¶
func LoadConfig() (*LoomConfig, error)
LoadConfig reads and parses the loom config file. Returns (nil, nil) if the config file does not exist. Returns (nil, error) on read or parse errors.
type MonitorData ¶
type MonitorData struct {
Timestamp time.Time
Agents []AgentStatus
Tasks TaskSummary
NeedsPlanningTasks []TaskInfo // Ready tasks without design (top 5)
ReadyToImplement []TaskInfo // Ready tasks with design (top 5)
ReviewTasks []TaskInfo // top 5 need review tasks
InProgressTasks []TaskInfo // all in_progress tasks
BacklogTasks []TaskInfo // backlog tasks (top 20)
AgentTasks map[string]TaskInfo // agent name -> current task (from assignee)
TaskConflicts map[string][]string // TaskID -> agent names (if multiple agents claim same task)
SyncStatus SyncInfo
Stats MonitorStats
}
MonitorData holds all dashboard information
func CollectMonitorData ¶
func CollectMonitorData() *MonitorData
CollectMonitorData gathers all dashboard data. Exported for use by the HTTP server.
type MonitorStats ¶
type MonitorStats struct {
Open int `json:"open"`
Closed int `json:"closed"`
Total int `json:"total"`
Completion float64 `json:"completion"`
Remaining int `json:"remaining"`
InProgress int `json:"in_progress"`
Review int `json:"review"`
Blocked int `json:"blocked"`
}
MonitorStats holds overall statistics
type OpenCodeBackend ¶
type OpenCodeBackend struct{}
OpenCodeBackend implements the Backend interface for the OpenCode CLI.
func (*OpenCodeBackend) InvokeInteractive ¶
func (o *OpenCodeBackend) InvokeInteractive(workDir, prompt, agentName string) error
func (*OpenCodeBackend) InvokeNonInteractive ¶
func (o *OpenCodeBackend) InvokeNonInteractive(workDir, prompt, agentName string, shutdown <-chan struct{}) error
func (*OpenCodeBackend) Name ¶
func (o *OpenCodeBackend) Name() string
type ProjectFile ¶
type ProjectFile struct {
Backend string `yaml:"backend,omitempty"`
Daemon *DaemonSettings `yaml:"daemon,omitempty"`
Roles map[string]RoleConfig `yaml:"roles,omitempty"`
Agents []AgentEntry `yaml:"agents,omitempty"`
}
ProjectFile represents the project-local loom.yaml.
func LoadProjectFile ¶
func LoadProjectFile(dir string) (*ProjectFile, error)
LoadProjectFile reads and parses the project-local loom.yaml from dir. Returns (nil, nil) if the file does not exist.
type PromptData ¶
PromptData is the template context for custom prompt files.
type RepoConfig ¶
type RepoConfig struct {
Name string `yaml:"name" json:"name"` // Display name / identifier
Path string `yaml:"path" json:"path"` // Path to the repo (absolute or relative to workspace)
DefaultBranch string `yaml:"default_branch,omitempty" json:"default_branch,omitempty"` // Override default branch (defaults to "main")
Remote string `yaml:"remote,omitempty" json:"remote,omitempty"` // Git remote name (defaults to "origin")
}
RepoConfig defines a single repository within a workspace
type ResolvedTarget ¶
type ResolvedTarget struct {
WorkDir string // directory where Claude should run
AgentName string // agent name for locks and prompts
}
ResolvedTarget holds the result of workspace-aware argument resolution.
func ResolveAgentTarget ¶
func ResolveAgentTarget(name string) (ResolvedTarget, error)
ResolveAgentTarget resolves a CLI argument (workspace name, repo name, or worktree name) into the working directory and agent name. In workspace mode, Claude always runs from the workspace root so bd commands use the shared .beads/ directory.
type Resolver ¶
type Resolver struct {
// contains filtered or unexported fields
}
Resolver abstracts worktree/repo discovery behind legacy and workspace modes.
func NewResolver ¶
NewResolver creates a Resolver, selecting workspace mode if a config with workspaces exists, otherwise falling back to legacy mode.
func (*Resolver) DiscoverWorktrees ¶
func (r *Resolver) DiscoverWorktrees() ([]WorktreeInfo, error)
DiscoverWorktrees returns discovered worktrees using the resolver's mode.
func (*Resolver) GetDefaultBranch ¶
GetDefaultBranch returns the default integration branch using the resolver's mode. Resolution order: LOOM_DEFAULT_BRANCH env var > mode-specific logic > "main"
func (*Resolver) GetWorktreesDir ¶
GetWorktreesDir returns the worktrees directory path using the resolver's mode. In workspace mode, returns the active workspace's path.
func (*Resolver) Mode ¶
func (r *Resolver) Mode() ResolverMode
Mode returns the resolver's current mode.
func (*Resolver) ResolveWorkspaceByName ¶
ResolveWorkspaceByName checks if name matches a workspace name and returns the workspace root path. Returns (path, true) if found, ("", false) if not.
func (*Resolver) ResolveWorktreePath ¶
ResolveWorktreePath converts a worktree name to its full path using the resolver's mode.
func (*Resolver) SetWorkspace ¶
SetWorkspace switches the active workspace. Returns an error if the workspace name is not found in the config.
func (*Resolver) WorkspaceName ¶
WorkspaceName returns the active workspace name (empty in legacy mode).
func (*Resolver) WorkspaceNames ¶
WorkspaceNames returns the names of all configured workspaces. Returns nil in legacy mode.
type ResolverMode ¶
type ResolverMode int
ResolverMode indicates how the Resolver discovers worktrees
const ( ModeLegacy ResolverMode = iota // scan ./worktrees/ directory ModeWorkspace // read from ~/.loom/config.yaml )
type RestartPolicy ¶
type RestartPolicy struct {
MaxRetries *int `yaml:"max_retries,omitempty"`
BackoffInitial *int `yaml:"backoff_initial,omitempty"` // seconds
BackoffMax *int `yaml:"backoff_max,omitempty"` // seconds
OutputTimeout *int `yaml:"output_timeout,omitempty"` // seconds; kill agent after this long with no output (0 = disabled)
}
RestartPolicy defines how the daemon restarts failed agents.
type RoleConfig ¶
type RoleConfig struct {
Description string `yaml:"description,omitempty"`
PromptFile string `yaml:"prompt_file,omitempty"`
Model string `yaml:"model,omitempty"`
TaskFilter string `yaml:"task_filter,omitempty"`
}
RoleConfig defines an agent role (built-in like "plan"/"task", or custom).
type StatsResponse ¶
type StatsResponse struct {
Stats MonitorStats `json:"stats"`
Timestamp time.Time `json:"timestamp"`
}
StatsResponse wraps statistics.
type StatusResponse ¶
type StatusResponse struct {
Workspace WorkspaceInfo `json:"workspace"`
Agents []AgentStatus `json:"agents"`
Tasks TaskSummary `json:"tasks"`
InProgressList []TaskInfo `json:"in_progress_list"`
AgentTasks map[string]TaskInfo `json:"agent_tasks"`
Stats MonitorStats `json:"stats"`
Sync SyncInfo `json:"sync"`
Timestamp time.Time `json:"timestamp"`
}
StatusResponse is the full status (like monitor dashboard).
type StreamEvent ¶
type StreamEvent struct {
Type string `json:"type"`
Message *EventMessage `json:"message,omitempty"`
}
StreamEvent represents a Claude stream-json event
type SupervisedAgentStatus ¶
type SupervisedAgentStatus struct {
Worktree string
Role string
WorktreePath string
PID int
RestartCount int
LastStart time.Time
LastExit time.Time
LastExitCode int
AssignedEpicID string
}
SupervisedAgentStatus is a snapshot of a supervised agent's state for external inspection. This type is safe to copy and does not contain a mutex.
type SyncInfo ¶
type SyncInfo struct {
DBSynced bool `json:"db_synced"`
DBLastSync string `json:"db_last_sync"`
DBError string `json:"db_error,omitempty"`
GitNeedsPush int `json:"git_needs_push"`
GitNeedsPull int `json:"git_needs_pull"`
GitPushDetails []WorktreeSyncDetail `json:"git_push_details,omitempty"`
GitPullDetails []WorktreeSyncDetail `json:"git_pull_details,omitempty"`
}
SyncInfo holds sync status information
type SyncResponse ¶
SyncResponse wraps sync status.
type TaskInfo ¶
type TaskInfo struct {
ID string `json:"id"`
Title string `json:"title"`
Priority int `json:"priority"`
Status string `json:"status"` // "in_progress", "closed", "open"
}
TaskInfo represents a task with basic info
type TaskSummary ¶
type TaskSummary struct {
NeedsPlanning int `json:"needs_planning"` // Ready tasks without design
ReadyToImplement int `json:"ready_to_implement"` // Ready tasks with approved design
InProgress int `json:"in_progress"`
NeedReview int `json:"need_review"`
Backlog int `json:"backlog"`
}
TaskSummary holds task counts by category
type TasksResponse ¶
type TasksResponse struct {
Summary TaskSummary `json:"summary"`
NeedsPlanning []TaskInfo `json:"needs_planning"`
ReadyToImplement []TaskInfo `json:"ready_to_implement"`
NeedsReview []TaskInfo `json:"needs_review"`
InProgress []TaskInfo `json:"in_progress"`
Backlog []TaskInfo `json:"backlog"`
Timestamp time.Time `json:"timestamp"`
}
TasksResponse wraps task information.
type WorkspaceConfig ¶
type WorkspaceConfig struct {
Path string `yaml:"path" json:"path"` // Directory path for this workspace
Repos []RepoConfig `yaml:"repos" json:"repos"` // Repositories in this workspace
}
WorkspaceConfig defines a named workspace containing multiple repos
func ResolveActiveWorkspace ¶
func ResolveActiveWorkspace() (*WorkspaceConfig, error)
ResolveActiveWorkspace loads the config and returns the active workspace config. Returns (nil, nil) if not in workspace mode (no config or no workspaces defined). Uses DefaultWorkspace if set, otherwise uses the first workspace in the map.
type WorkspaceDetail ¶
type WorkspaceDetail struct {
Path string `json:"path"` // workspace root path
Repos []string `json:"repos"` // repo names in this workspace
}
WorkspaceDetail contains details about a single workspace.
type WorkspaceInfo ¶
type WorkspaceInfo struct {
Mode string `json:"mode"` // "workspace" or "legacy"
Name string `json:"name,omitempty"` // workspace name (workspace mode only)
Workspaces []string `json:"workspaces,omitempty"` // all workspace names (workspace mode only)
}
WorkspaceInfo represents workspace metadata for API responses.
type WorkspacesResponse ¶
type WorkspacesResponse struct {
Mode string `json:"mode"` // "workspace" or "legacy"
Default string `json:"default"` // default workspace name
Workspaces map[string]WorkspaceDetail `json:"workspaces"` // workspace details
Timestamp time.Time `json:"timestamp"`
}
WorkspacesResponse lists all configured workspaces.
type WorktreeInfo ¶
type WorktreeInfo struct {
Name string
Path string
Branch string
Workspace string // workspace name (empty in legacy mode)
Repo *RepoConfig // source repo config (nil in legacy mode)
}
WorktreeInfo holds information about a discovered worktree
func DiscoverWorktrees ¶
func DiscoverWorktrees() ([]WorktreeInfo, error)
DiscoverWorktrees finds all worktrees in the worktrees directory
type WorktreeSyncDetail ¶
WorktreeSyncDetail holds per-worktree sync detail (commits ahead or behind).
Source Files
¶
- agent_cmd.go
- automode.go
- automode_poller.go
- automode_tmux.go
- backend.go
- backend_claude.go
- backend_codex.go
- backend_opencode.go
- claim.go
- complete.go
- completions.go
- config.go
- config_cmd.go
- daemon.go
- daemon_branch.go
- daemon_cmd.go
- daemon_config.go
- daemon_ensure.go
- daemon_epic.go
- daemon_exhaust.go
- daemon_pr.go
- envfilter.go
- exec.go
- git.go
- init.go
- lead.go
- list.go
- lock.go
- logrouter_cmd.go
- monitor.go
- monitor_display.go
- plan.go
- pr.go
- prompts.go
- pull.go
- push.go
- recover.go
- reset.go
- root.go
- serve.go
- serve_cache.go
- signal_dir_unix.go
- sync.go
- task.go
- taskfilter.go
- workspace_cmd.go
- worktree.go