Documentation
¶
Index ¶
- Constants
- Variables
- func AcquireInstanceLock(configDir string, timeout time.Duration) (*flock.Flock, error)
- func DefaultLauncherPresetsPath() (string, error)
- func EnsureWorkspaceMeta()
- func ExpandEnvVars(m map[string]string) map[string]string
- func GetAliasesByGroup(cfg *Config) map[string][]AliasConfig
- func GetAvailablePrograms() []string
- func GetClaudeCommand() (string, error)
- func GetClaudeDir() (string, error)
- func GetConfigDir() (string, error)
- func GetConfigDirForDir(dir string) (string, error)
- func GetPreferredWorkspaceFile(baseDir string) string
- func ImportSessionEnabled() bool
- func IsIsolatedInstance() bool
- func IsNamedInstance() bool
- func IsTestMode() bool
- func ResolveClaudeHistoryDir(homeDir string, isIsolated bool) (string, error)
- func ResolveGlobalStreamHubDefault(cfg *Config, requested bool) (bool, error)
- func SaveConfig(config *Config) error
- func SaveDiscoveryConfig(config *DiscoveryConfig) error
- func SaveState(state *State) error
- func SetPreferredWorkspace(baseDir, configDir string) error
- type AliasConfig
- type AppState
- type BrowserPassthroughCDPConfig
- type BrowserPassthroughConfig
- type CallbackConfig
- type CapacityConfig
- type ClaudeConfigManager
- func (m *ClaudeConfigManager) GetConfig(filename string) (*ConfigFile, error)
- func (m *ClaudeConfigManager) ListConfigs() ([]ConfigFile, error)
- func (m *ClaudeConfigManager) UpdateConfig(filename string, content string) error
- func (m *ClaudeConfigManager) UpdateConfigWithValidation(filename string, content string) error
- func (m *ClaudeConfigManager) ValidateJSON(filename string, content string) error
- type CommandExecutor
- type Config
- func (c *Config) AnalyticsMaxAgeDaysOrDefault() int
- func (c *Config) AnalyticsMaxRowsOrDefault() int
- func (c *Config) AutoSpawnReadyItemsOrDefault() bool
- func (c *Config) BacklogAttachmentDirOrDefault() (string, error)
- func (c *Config) GetAvailablePrograms() []string
- func (c *Config) GetClaudeCommand() (string, error)
- func (c *Config) GetFeatureFlag(name string) bool
- func (c *Config) GetGitHubEnterpriseHosts() []GitHubEnterpriseHost
- func (c *Config) GetKeyCategoryForKey(key string) string
- func (c *Config) GetOrCreateClaimantHostID() (string, error)
- func (c *Config) GetOrCreateEncryptionKey() ([]byte, error)
- func (c *Config) GetStreamHubSessionOverride(sessionName string) (forceHub bool, ok bool)
- func (c *Config) HeadlessFailureCaptureDirOrDefault() (string, error)
- func (c *Config) HibernationCheckpointDirOrDefault() (string, error)
- func (c *Config) MaxAutoReworkIterationsOrDefault() int
- func (c *Config) MaxConcurrentBacklogWorkItemsOrDefault() int
- func (c *Config) NewProjectBaseDirOrDefault() (string, error)
- func (c *Config) OSCPayloadsAreRedacted() bool
- func (c *Config) OneOffBaseDirOrDefault() (string, error)
- func (c *Config) PromptCacheDirOrDefault() (string, error)
- func (c *Config) RecordRollbackRehearsalCompleted() error
- func (c *Config) RemoteByName(name string) (*RemoteConfig, bool)
- func (c *Config) RemoveKeyCategory(key string)
- func (c *Config) SetFeatureFlag(name string, value bool) error
- func (c *Config) SetKeyCategory(key, category string)
- func (c *Config) SetStreamHubSessionOverride(sessionName string, forceHub *bool) error
- func (c *Config) SlackSigningSecretOverride() string
- func (c *Config) SlackWebhookURLOverride() string
- func (c *Config) TriageArtifactDirOrDefault() (string, error)
- type ConfigFile
- type DirectoryRule
- type DiscoveryConfig
- func (c *DiscoveryConfig) CanAttachToExternal() bool
- func (c *DiscoveryConfig) IsExternalDiscoveryEnabled() bool
- func (c *DiscoveryConfig) IsManagedDiscoveryEnabled() bool
- func (c *DiscoveryConfig) ShouldConfirmOperation(isExternal bool) bool
- func (c *DiscoveryConfig) ShouldShowExternalInstances() bool
- type DiscoveryMode
- type GitHubEnterpriseHost
- type HibernationConfig
- type LauncherPreset
- type LauncherPresetsFile
- type NotificationPrefs
- type ProfileDefaults
- type ProviderPriority
- type QuotaConfig
- type RemoteConfig
- type ResolvedDefaults
- type SessionDefaults
- type SessionRetentionConfig
- type SessionType
- type SlackConfig
- type StaleSessionConfig
- type State
- func (s *State) Close() error
- func (s *State) GetCategoryExpanded(category string) bool
- func (s *State) GetHelpScreensSeen() uint32
- func (s *State) GetSearchState() (bool, string)
- func (s *State) GetSelectedIndex() int
- func (s *State) GetUIState() UIState
- func (s *State) RefreshState() error
- func (s *State) SetCategoryExpanded(category string, expanded bool) error
- func (s *State) SetHelpScreensSeen(seen uint32) error
- func (s *State) SetHidePaused(hidePaused bool) error
- func (s *State) SetSearchMode(searchMode bool, query string) error
- func (s *State) SetSelectedIndex(index int) error
- type StateManager
- type TmuxExecGateConfig
- type TransitionMode
- type UIState
- type UIStateAccess
- type WorkspaceMeta
Constants ¶
const ( // InstanceLockFileName is the exclusive, process-lifetime lock file used // to prevent two stapler-squad server processes from running against the // same config/DB directory at once. InstanceLockFileName = "instance.lock" // DefaultInstanceLockTimeout bounds how long a starting process waits for // a prior process to release the instance lock before giving up. Matches // scripts/install-service.sh's wait_for_port_release budget so a normal // service restart doesn't spuriously fail here. DefaultInstanceLockTimeout = 10 * time.Second )
const ( StateFileName = "state.json" InstancesFileName = "instances.json" )
const ( // DefaultLockTimeout is the default timeout for acquiring locks DefaultLockTimeout = 5 * time.Second // LockFileName is the name of the lock file LockFileName = "state.lock" )
const (
ConfigFileName = "config.json"
)
const DiscoveryConfigFileName = "discovery.json"
Variables ¶
var ( ErrConfigNotFound = fmt.Errorf("config file not found") ErrInvalidConfig = fmt.Errorf("invalid config file") ErrInvalidJSON = fmt.Errorf("invalid JSON") )
Common errors for Claude config operations
var ErrAliasNotFound = errors.New("alias not found")
ErrAliasNotFound is returned by ResolveAlias when no alias matches the given name.
var ErrRollbackRehearsalNotCompleted = errors.New("config: cannot enable the global stream-hub default: rollback rehearsal (RollbackRehearsalCompletedAt) has not been completed — see Story 3.3.2")
ErrRollbackRehearsalNotCompleted is returned by ResolveGlobalStreamHubDefault when the caller requests the global STAPLER_SQUAD_USE_STREAM_HUB default resolve to true but RollbackRehearsalCompletedAt is unset — Story 3.3.2's rollback rehearsal must be executed and recorded first (pre-mortem P1 #4).
Functions ¶
func AcquireInstanceLock ¶ added in v1.42.0
AcquireInstanceLock takes an exclusive lock on instance.lock in configDir, retrying until timeout elapses. Unlike the PID/port-based liveness checks documented in .claude/rules/service-restart-orphan-process.md, the OS releases a flock the moment the holding process's file descriptors close — including on an unclean exit or reparenting to PID 1 — so a prior process that launchd/systemd has lost track of still can't hold this lock forever.
Returns the acquired *flock.Flock; the caller must keep it alive for the life of the process and Unlock() it on shutdown.
func DefaultLauncherPresetsPath ¶ added in v1.42.0
DefaultLauncherPresetsPath returns the resolved path to launcher-presets.json, honoring the same instance-isolation rules as the rest of config/ (GetConfigDir).
func EnsureWorkspaceMeta ¶
func EnsureWorkspaceMeta()
EnsureWorkspaceMeta writes workspace metadata for the current configuration directory. Should be called once at server startup. Skips test mode directories.
func ExpandEnvVars ¶ added in v1.35.0
ExpandEnvVars expands ${VAR_NAME} tokens in map values using os.LookupEnv. If any referenced env var is not set (vs. set to ""), the key is omitted from the result and a warning is logged.
func GetAliasesByGroup ¶ added in v1.35.0
func GetAliasesByGroup(cfg *Config) map[string][]AliasConfig
GetAliasesByGroup groups all aliases by their Group field. Aliases without a Group are stored under the empty-string key "".
func GetAvailablePrograms ¶
func GetAvailablePrograms() []string
GetAvailablePrograms is a package-level convenience wrapper using the default executor. Callers that need a custom executor should use NewConfigWithExecutor(exec).GetAvailablePrograms().
func GetClaudeCommand ¶
GetClaudeCommand is a package-level convenience wrapper using the default executor. Callers that need a custom executor should use NewConfigWithExecutor(exec).GetClaudeCommand().
func GetClaudeDir ¶
GetClaudeDir returns the path to the ~/.claude directory
func GetConfigDir ¶
GetConfigDir returns the path to the application's configuration directory with hierarchical isolation for safe multi-instance and test execution.
Priority hierarchy:
- Test directory override via STAPLER_SQUAD_TEST_DIR (for --test-mode flag)
- Explicit instance ID via STAPLER_SQUAD_INSTANCE environment variable
- Test mode auto-detection (automatic isolation for tests/benchmarks)
- Preferred workspace from preference file (explicit switch via SwitchDatabase RPC)
- Per-directory workspace isolation, opt-in via STAPLER_SQUAD_WORKSPACE_MODE=true
- Global shared state (default)
func GetConfigDirForDir ¶ added in v1.35.0
GetConfigDirForDir returns the path to the application's configuration directory using the provided directory for workspace-based isolation.
func GetPreferredWorkspaceFile ¶
GetPreferredWorkspaceFile returns the path to the preferred workspace preference file.
func ImportSessionEnabled ¶ added in v1.42.0
func ImportSessionEnabled() bool
ImportSessionEnabled reports whether the import-external-session feature (Phase 1: ssq-mux single-session import) is enabled. Unlike GetFeatureFlag, this is a plain environment variable rather than a persisted config flag — the feature involves signaling live, unmanaged processes (SIGSTOP/SIGCONT) outside Stapler Squad's own supervision, so it defaults to a deliberate, explicit opt-in per deployment/session rather than a UI-toggleable persisted setting. Re-read on every call (not cached) so it can be flipped without a server restart, matching the re-read behavior of GetFeatureFlag.
func IsIsolatedInstance ¶ added in v1.39.0
func IsIsolatedInstance() bool
IsIsolatedInstance reports whether this process's config/DB state is isolated from the shared default (~/.stapler-squad) directory by ANY known mechanism: a `go test` binary (IsTestMode), an explicit named instance (IsNamedInstance), or a STAPLER_SQUAD_TEST_DIR override (GetConfigDirForDir priority 1 — used by --test-mode harnesses like tests/demo/helpers.go's StartDemoServer). Isolated DB state does NOT imply an isolated tmux socket under any of these mechanisms — see IsNamedInstance's doc comment for the confirmed incident that motivated this check. Call sites that could otherwise touch shared, non-isolated resources (like the default tmux socket in ReconcileOrphanedTmuxSessions) must skip when this is true. STAPLER_SQUAD_TEST_DIR was the still-missing case: a demo/test-mode harness process gets a fully isolated DB via GetConfigDirForDir but, before this check existed, its startup orphan sweep still targeted the shared default tmux socket — killing every real production session it didn't recognize.
func IsNamedInstance ¶ added in v1.39.0
func IsNamedInstance() bool
IsNamedInstance reports whether this process is running as an explicitly named, non-default instance (STAPLER_SQUAD_INSTANCE set to anything other than "" or "shared" — see GetConfigDirForDir's priority hierarchy above). A named instance gets its own isolated DB/config directory but does NOT get its own tmux socket — it shares the default tmux server with every other instance on the machine, including the real production one. IsTestMode() alone doesn't catch this: this repo's own E2E harness (tests/e2e, per CLAUDE.md: "STAPLER_SQUAD_INSTANCE=e2e-local ./stapler-squad --tmux-keep-server") runs the real production binary, not a `go test` binary, so IsTestMode() returns false for it even though it has exactly the same "small, isolated instance list vs. the shared tmux socket" hazard a `go test` binary does. Confirmed live: an e2e-local run's orphan sweep killed 5 unrelated production tmux sessions it had never heard of, including the interactive session this very fix was written in.
func IsTestMode ¶ added in v1.35.0
func IsTestMode() bool
IsTestMode detects if the application is running in test/benchmark mode
func ResolveClaudeHistoryDir ¶ added in v1.47.0
ResolveClaudeHistoryDir returns the directory holding Claude Code's JSONL session-history files, which two independent consumers walk and watch: server's TokenStore/ArtifactExtractor (BuildRuntimeDeps) and session's HistoryLinker fsnotify watcher.
In production (isIsolated=false) that is the real ~/.claude/projects. An isolated test/demo instance must not touch it: on a dev machine with a long Claude Code history that tree is thousands of real JSONL files, some large, which overflows the consumers' bounded parse/scan queues and made the walk+parse take 100s-1500s+ in CI, well past any reasonable test timeout. Under isolation this returns (and creates) a per-instance directory inside the isolated config dir instead — it starts empty, so both consumers stay fully functional against test fixtures without ever reading real session data. Same hazard class as IsIsolatedInstance's other documented case (the shared tmux socket); here the isolated resource is the config dir.
isIsolated is a parameter rather than an internal IsIsolatedInstance() call so both branches are directly unit-testable — every test exercising this is itself a go test binary, where IsIsolatedInstance() is unconditionally true.
func ResolveGlobalStreamHubDefault ¶ added in v1.47.0
ResolveGlobalStreamHubDefault applies Story 3.3.1/3.3.2's mechanical rollback-rehearsal gate to a raw requested value for the *global* STAPLER_SQUAD_USE_STREAM_HUB default (e.g. read from that environment variable). Requesting false is always permitted — the gate only blocks turning the risky path *on*. Requesting true is refused with ErrRollbackRehearsalNotCompleted, not a silent fallback to false, unless cfg.RollbackRehearsalCompletedAt is a recorded, non-zero timestamp. This gate does not apply to the per-session override path (StreamHubSessionOverrides / streamhub.SetSessionOverrideLookup), which callers resolve independently and which remains available even when this function returns an error.
func SaveConfig ¶
SaveConfig exports the saveConfig function for use by other packages.
func SaveDiscoveryConfig ¶
func SaveDiscoveryConfig(config *DiscoveryConfig) error
SaveDiscoveryConfig saves the discovery configuration to disk
func SetPreferredWorkspace ¶
SetPreferredWorkspace atomically writes the preferred workspace config dir path. Pass configDir="" to clear the preference.
Types ¶
type AliasConfig ¶ added in v1.35.0
type AliasConfig struct {
// Name is the unique alias identifier (e.g. "myproj"). Must match ^[\w-]+$.
Name string `json:"name"`
// Group is an optional display group for palette organization.
Group string `json:"group,omitempty"`
// Path is the working directory for the session (supports ~/... expansion).
Path string `json:"path,omitempty"`
// Description is a human-readable summary shown in the palette.
Description string `json:"description,omitempty"`
// Profile is the named profile to apply when resolving defaults.
Profile string `json:"profile,omitempty"`
// Program overrides the default program (e.g. "aider").
Program string `json:"program,omitempty"`
// AutoYes auto-approves all prompts for this alias.
AutoYes bool `json:"auto_yes,omitempty"`
// Tags are pre-applied to sessions created from this alias.
Tags []string `json:"tags,omitempty"`
// EnvVars are environment variables set for sessions from this alias.
EnvVars map[string]string `json:"env_vars,omitempty"`
// CLIFlags are CLI flags appended to the program command for this alias.
// At session creation, invocation-time extraFlags are appended after these.
CLIFlags string `json:"cli_flags,omitempty"`
// SessionType overrides the session creation mode for this alias.
// SessionTypeDefault (empty) means use the default (directory session).
SessionType SessionType `json:"session_type,omitempty"`
// NamePrefix is prepended to the user-supplied session label when naming sessions.
// For example, prefix "ssq-" + label "my-feature" → session name "ssq-my-feature".
NamePrefix string `json:"name_prefix,omitempty"`
}
AliasConfig defines a named session preset invoked via @name in the omnibar. Name must match ^[\w-]+$ (letters, digits, hyphens, underscores only).
func FindAlias ¶ added in v1.35.0
func FindAlias(cfg *Config, name string) *AliasConfig
FindAlias returns the AliasConfig with the given name (case-insensitive), or nil if not found.
type AppState ¶
type AppState interface {
// GetHelpScreensSeen returns the bitmask of seen help screens
GetHelpScreensSeen() uint32
// SetHelpScreensSeen updates the bitmask of seen help screens
SetHelpScreensSeen(seen uint32) error
}
AppState handles application-level state
type BrowserPassthroughCDPConfig ¶ added in v1.35.0
type BrowserPassthroughCDPConfig struct {
// ScreencastQuality is the JPEG compression quality (1–100).
// Default: 70.
ScreencastQuality int `json:"screencast_quality,omitempty"`
// ScreencastMaxWidth is the maximum frame width in pixels.
// Default: 1280.
ScreencastMaxWidth int `json:"screencast_max_width,omitempty"`
// ScreencastMaxHeight is the maximum frame height in pixels.
// Default: 800.
ScreencastMaxHeight int `json:"screencast_max_height,omitempty"`
// ScreencastMaxFPS is the target frame-rate cap (frames per second).
// Default: 15 (one frame delivered every ~67 ms via everyNthFrame heuristic).
ScreencastMaxFPS int `json:"screencast_max_fps,omitempty"`
}
BrowserPassthroughCDPConfig holds tunable parameters for the Chrome DevTools Protocol screencast stream. All fields default to zero (use CDPConfigOrDefault to apply canonical defaults).
func (*BrowserPassthroughCDPConfig) CDPConfigOrDefault ¶ added in v1.35.0
func (c *BrowserPassthroughCDPConfig) CDPConfigOrDefault() BrowserPassthroughCDPConfig
CDPConfigOrDefault returns a BrowserPassthroughCDPConfig with any zero-value fields replaced by the canonical defaults. This allows a partial JSON config (e.g. only ScreencastQuality set) to inherit the remaining defaults.
type BrowserPassthroughConfig ¶ added in v1.35.0
type BrowserPassthroughConfig struct {
// Enabled controls whether VNC is started for new sessions.
// When nil (absent from config), VNC is enabled when required binaries are present.
// Set to false to unconditionally disable VNC for all sessions.
Enabled *bool `json:"enabled,omitempty"`
// DisplayBase is the first X11 display number to allocate (e.g. 100 for :100).
// Default: 100.
DisplayBase int `json:"display_base,omitempty"`
// DisplayRangeMax is the number of display numbers to search above DisplayBase.
// Default: 100 (searches :100–:199).
DisplayRangeMax int `json:"display_range_max,omitempty"`
// Resolution is the Xvfb screen resolution string (WxHxDepth).
// Default: "1280x800x24".
Resolution string `json:"resolution,omitempty"`
// CDP holds tunable parameters for the CDP screencast stream.
// Absent (zero) values are filled in by CDPConfigOrDefault().
CDP BrowserPassthroughCDPConfig `json:"cdp,omitempty"`
}
BrowserPassthroughConfig controls the per-session virtual display (Xvfb + x11vnc) feature.
func (*BrowserPassthroughConfig) IsEnabled ¶ added in v1.35.0
func (c *BrowserPassthroughConfig) IsEnabled() bool
IsEnabled returns false unless the user has explicitly set enabled=true. When Enabled is nil (absent from config), browser passthrough is disabled.
type CallbackConfig ¶ added in v1.43.0
type CallbackConfig struct {
// OnSessionCompleteURL receives a POST when a backlog item transitions to
// BacklogStatusDone. Empty string means disabled.
OnSessionCompleteURL string `json:"on_session_complete_url,omitempty"`
// OnSessionStaleURL receives a POST the first time a work session is
// detected stale (StuckReasonStaleWork). Empty string means disabled.
OnSessionStaleURL string `json:"on_session_stale_url,omitempty"`
// OnQueueItemCreatedURL receives a POST when an item is added to the
// review queue. Empty string means disabled.
OnQueueItemCreatedURL string `json:"on_queue_item_created_url,omitempty"`
}
CallbackConfig holds the global singleton outbound-callback URLs fired by server/services.CallbackDispatcher on the three lifecycle events FR7 covers. Never echoed back in plaintext by any RPC (see sessionv1.CallbackConfigProto, which reports booleans only) — same masked-boolean-not-value shape as the (unimplemented) project_plans/slack-review-notifications design, applied fresh here since that project has no shipped code to reuse.
type CapacityConfig ¶ added in v1.35.0
type CapacityConfig struct {
// TransitionMode controls auto vs manual transition. Default: "manual".
TransitionMode TransitionMode `json:"transition_mode,omitempty"`
// ContextWindowWarnPct is the context usage percentage to trigger a warning. Default: 0.75.
ContextWindowWarnPct float64 `json:"context_window_warn_pct,omitempty"`
// ContextWindowAutoPct is the context usage percentage to trigger auto-transition (in auto mode). Default: 0.90.
ContextWindowAutoPct float64 `json:"context_window_auto_pct,omitempty"`
// RateLimitWarnRemaining triggers a warning when remaining requests fall below this. Default: 10.
RateLimitWarnRemaining int `json:"rate_limit_warn_remaining,omitempty"`
// CostBudgetUSD is the accumulated USD cost limit. 0 means no limit. Default: 0.
CostBudgetUSD float64 `json:"cost_budget_usd,omitempty"`
// PollIntervalSeconds controls limit API querying frequency. Default: 60.
PollIntervalSeconds int `json:"poll_interval_seconds,omitempty"`
// ProviderPriority lists fallback providers in order of preference.
ProviderPriority []ProviderPriority `json:"provider_priority,omitempty"`
}
CapacityConfig holds configuration for the provider capacity monitoring and transition feature.
func (CapacityConfig) CapacityConfigOrDefault ¶ added in v1.35.0
func (c CapacityConfig) CapacityConfigOrDefault() CapacityConfig
CapacityConfigOrDefault returns a CapacityConfig with standard defaults applied to zero fields.
type ClaudeConfigManager ¶
type ClaudeConfigManager struct {
// contains filtered or unexported fields
}
ClaudeConfigManager manages access to Claude configuration files located in the ~/.claude directory
func NewClaudeConfigManager ¶
func NewClaudeConfigManager() (*ClaudeConfigManager, error)
NewClaudeConfigManager creates a new ClaudeConfigManager instance with the ~/.claude directory resolved
func (*ClaudeConfigManager) GetConfig ¶
func (m *ClaudeConfigManager) GetConfig(filename string) (*ConfigFile, error)
GetConfig reads a specific Claude configuration file by name Common file names include "CLAUDE.md", "settings.json", "agents.md"
func (*ClaudeConfigManager) ListConfigs ¶
func (m *ClaudeConfigManager) ListConfigs() ([]ConfigFile, error)
ListConfigs returns all configuration files in the ~/.claude directory
func (*ClaudeConfigManager) UpdateConfig ¶
func (m *ClaudeConfigManager) UpdateConfig(filename string, content string) error
UpdateConfig updates a Claude configuration file atomically with backup It creates a .bak file before writing, and uses a temporary file for atomicity. JSON files are validated before writing to prevent corrupt settings files.
func (*ClaudeConfigManager) UpdateConfigWithValidation ¶
func (m *ClaudeConfigManager) UpdateConfigWithValidation(filename string, content string) error
UpdateConfigWithValidation updates a config file with JSON validation This is a convenience method that combines validation and update
func (*ClaudeConfigManager) ValidateJSON ¶
func (m *ClaudeConfigManager) ValidateJSON(filename string, content string) error
ValidateJSON validates a JSON configuration file against a schema Returns nil if valid, error with details if invalid
type CommandExecutor ¶
type CommandExecutor interface {
Command(name string, args ...string) *exec.Cmd
Output(cmd *exec.Cmd) ([]byte, error)
LookPath(file string) (string, error)
}
CommandExecutor defines the interface for executing external commands
type Config ¶
type Config struct {
// ListenAddress is the address the HTTP server listens on.
// Default: "localhost:8543". Set to "0.0.0.0:8543" for remote access.
ListenAddress string `json:"listen_address"`
// PasskeyRPID is the WebAuthn Relying Party ID (effective domain, no scheme/port).
// Example: "192.168.1.42" or "myhost.local". Must match the hostname clients use.
// Required when remote access is enabled.
PasskeyRPID string `json:"passkey_rp_id"`
// PasskeyEnabled controls whether passkey authentication is enforced.
// Automatically set to true when non-localhost listen address is used.
PasskeyEnabled bool `json:"passkey_enabled"`
// DefaultProgram is the default program to run in new instances
DefaultProgram string `json:"default_program"`
// AutoYes is a flag to automatically accept all prompts.
AutoYes bool `json:"auto_yes"`
// DaemonPollInterval is the interval (ms) at which the daemon polls sessions for autoyes mode.
DaemonPollInterval int `json:"daemon_poll_interval"`
// BranchPrefix is the prefix used for git branches created by the application.
BranchPrefix string `json:"branch_prefix"`
// DetectNewSessions is a flag to enable detection of new sessions from other windows
DetectNewSessions bool `json:"detect_new_sessions"`
// SessionDetectionInterval is the interval (ms) at which the daemon checks for new sessions
SessionDetectionInterval int `json:"session_detection_interval"`
// StateRefreshInterval is the interval (ms) at which the state is refreshed from disk
StateRefreshInterval int `json:"state_refresh_interval"`
// LogsEnabled is a flag to enable logging to files
LogsEnabled bool `json:"logs_enabled"`
// LogsDir is the directory where logs are stored (defaults to ~/.stapler-squad/logs)
LogsDir string `json:"logs_dir"`
// LogMaxSize is the maximum size of a log file in megabytes before it gets rotated
LogMaxSize int `json:"log_max_size"`
// LogMaxFiles is the maximum number of rotated log files to keep (not including the current log file)
LogMaxFiles int `json:"log_max_files"`
// LogMaxAge is the maximum number of days to keep rotated log files
LogMaxAge int `json:"log_max_age"`
// LogCompress is a flag to enable compression of rotated log files
LogCompress bool `json:"log_compress"`
// UseSessionLogs is a flag to enable per-session log files
UseSessionLogs bool `json:"use_session_logs"`
// TmuxSessionPrefix allows customizing the tmux session prefix for process isolation
TmuxSessionPrefix string `json:"tmux_session_prefix"`
// PerformBackgroundHealthChecks enables non-blocking health checks for session maintenance
PerformBackgroundHealthChecks bool `json:"perform_background_health_checks"`
// KeyCategories defines custom category mappings for key bindings in help system
KeyCategories map[string]string `json:"key_categories"`
// VCSPreference controls which version control system to prefer when both are available
// Options: "auto" (prefer JJ if available), "jj" (always use JJ), "git" (always use Git)
VCSPreference string `json:"vcs_preference"`
// AvailablePrograms is a list of detected CLI programs
AvailablePrograms []string `json:"available_programs"`
// ConfigVersion tracks the schema version for future migrations (1 = session_defaults added)
ConfigVersion int `json:"config_version,omitempty"`
// SessionDefaults holds named profiles, directory rules, and global defaults for new sessions.
SessionDefaults SessionDefaults `json:"session_defaults,omitempty"`
// Notifications holds the user's notification delivery preferences.
Notifications NotificationPrefs `json:"notifications,omitempty"`
// Remotes is a named list of SSH-reachable remote hosts sessions can be
// created against (ssh-remote-workspaces feature). Holds connection
// coordinates only — no SSH key material; see RemoteConfig's doc
// comment. Looked up by name via RemoteByName.
Remotes []RemoteConfig `json:"remotes,omitempty"`
// OneOffBaseDir is the base directory where one-off session directories are created.
// Default: "~/oneoff". Tilde is expanded at runtime. Created automatically on first use.
OneOffBaseDir string `json:"one_off_base_dir,omitempty"`
// PyroscopeServerAddress is the Pyroscope server URL for continuous profiling.
// Empty string (the default) disables continuous profiling.
// Example: "http://localhost:4040"
PyroscopeServerAddress string `json:"pyroscope_server_address,omitempty"`
// NewProjectBaseDir is the base directory where new project directories are created.
// Default: "~/Projects". Tilde is expanded at runtime. Created on first use.
// Zero-value (empty string) is backwards-compatible — existing configs load without change.
NewProjectBaseDir string `json:"new_project_base_dir,omitempty"`
// MachineEncryptionKey is a base64-encoded 32-byte AES-256-GCM key for local data encryption.
// Generated on first run and persisted here. Used to encrypt sensitive token data in ItemSource configs.
MachineEncryptionKey string `json:"machine_encryption_key,omitempty"`
// ClaimantHostID is a randomly generated identifier for THIS physical process/config
// directory, generated on first use and persisted here. Recorded on backlog ItemSession
// rows to show which host/process claimed or attached a session (see
// GetOrCreateClaimantHostID). Stable across restarts of this same process/config dir;
// distinct across different hosts and across different STAPLER_SQUAD_INSTANCE-namespaced
// config dirs on the same machine, since each gets its own config.json. Unrelated to
// STAPLER_SQUAD_INSTANCE (which only namespaces config/state directories on a single
// machine) and unrelated to session/contexts.go's CloudContext.InstanceID (a cloud
// provider's instance identifier, not populated for local/dev sessions).
ClaimantHostID string `json:"claimant_host_id,omitempty"`
// MaxAutoReworkIterations caps how many automated work sessions the backlog auto-reopen
// loop will spawn for a single item before leaving it for manual review. 0 = use the
// default (20). Individual items can also override this via
// BacklogItemData.ReworkCapOverride (0 = unlimited for that item, >0 = that item's own
// cap) — see effectiveReworkCap in server/services/backlog_service_triage.go.
MaxAutoReworkIterations int `json:"max_auto_rework_iterations,omitempty"`
// MaxConcurrentBacklogWorkItems caps how many distinct backlog items may be
// "in_progress" at the same time. 0 = use the default (2). Values above
// maxConcurrentBacklogWorkItemsHardCeiling are clamped to the ceiling.
MaxConcurrentBacklogWorkItems int `json:"max_concurrent_backlog_work_items,omitempty"`
// AutoSpawnReadyItems controls whether "ready" backlog items (post-triage, plan
// approved or SkipPlanning) automatically claim a free WIP slot and spawn a work
// session — in priority order (P1 first) — the moment one is free, without a
// human clicking "Spawn Session". A *bool, not bool: the zero value of bool
// can't represent "unset" the way 0 does for the int settings above, and this
// setting's default is true (unlike SkipReviewGate/AutoCreatePR's per-item
// false-by-default opt-ins), so nil must mean "use the default", not "disabled".
// Pass explicit false to require manual spawning instead.
AutoSpawnReadyItems *bool `json:"auto_spawn_ready_items,omitempty"`
// AnalyticsMaxRows is the maximum number of analytics events to retain in the database.
// When exceeded, the oldest rows are deleted. 0 means no row-count limit.
// Default: 100_000.
AnalyticsMaxRows int `json:"analytics_max_rows,omitempty"`
// AnalyticsMaxAgeDays is the maximum age in days of analytics events to retain.
// Events older than this are deleted. 0 means no age limit.
// Default: 90.
AnalyticsMaxAgeDays int `json:"analytics_max_age_days,omitempty"`
// BrowserPassthrough configures the per-session Xvfb + x11vnc virtual display feature.
BrowserPassthrough BrowserPassthroughConfig `json:"browser_passthrough,omitempty"`
// FeatureFlags stores the enabled/disabled state of named runtime feature flags.
// Keys are machine names (e.g. "backlog"); values are booleans.
// Absent key == disabled (false is the safe default for all flags).
FeatureFlags map[string]bool `json:"feature_flags,omitempty"`
// Hibernation holds configuration for the session hibernation feature.
Hibernation HibernationConfig `json:"hibernation,omitempty"`
// Capacity holds configuration for the provider capacity monitoring and transition feature.
Capacity CapacityConfig `json:"capacity,omitempty"`
// Quota holds configuration for the account-wide session-quota gate that
// pauses/resumes backlog automation based on inferred quota headroom.
Quota QuotaConfig `json:"quota,omitempty"`
// TmuxExecGate bounds concurrent tmux subprocess execution across all processes.
TmuxExecGate TmuxExecGateConfig `json:"tmux_exec_gate,omitempty"`
// SessionRetention holds configuration for the automatic session-retention cleanup sweep.
SessionRetention SessionRetentionConfig `json:"session_retention,omitempty"`
// StaleSession holds configuration for stale-session detection (inactivity threshold
// and notify-on-stale toggle).
StaleSession StaleSessionConfig `json:"stale_session,omitempty"`
// Callbacks holds the global singleton outbound-callback URLs (webhook-triggers
// Phase 5, FR7) fired by CallbackDispatcher on session-complete/session-stale/
// queue-item-created lifecycle events.
Callbacks CallbackConfig `json:"callbacks,omitempty"`
// Slack holds configuration for the Slack review-queue notification
// feature. Secret fields are ciphertext only — see ADR-001.
Slack SlackConfig `json:"slack,omitempty"`
// EscapeAnalyticsCaptureLevel controls the verbosity of escape sequence capture.
// Valid values: "full" (store raw bytes + hash), "summary" (type/length only), "off" (disabled).
// Default: "summary".
EscapeAnalyticsCaptureLevel string `json:"escapeAnalyticsCaptureLevel,omitempty"`
// EscapeAnalyticsSamplingRate is the fraction of sessions to capture, in [0.0, 1.0].
// 1.0 captures all sessions; 0.0 captures none.
// A nil pointer means "unset" and defaults to 1.0 at load time.
// Using a pointer allows 0.0 (capture nothing) to be distinguished from the zero value.
// Default: 1.0.
EscapeAnalyticsSamplingRate *float64 `json:"escapeAnalyticsSamplingRate,omitempty"`
// EscapeAnalyticsMaxRowsPerSession is the maximum number of escape event rows stored per session.
// Default: 10000.
EscapeAnalyticsMaxRowsPerSession int `json:"escapeAnalyticsMaxRowsPerSession,omitempty"`
// EscapeAnalyticsDisableOSCRedaction disables OSC payload redaction when true.
// By default (false), OSC payloads (clipboard, window title, CWD) are redacted for security.
// Set to true only if you explicitly need to capture raw OSC payload content.
EscapeAnalyticsDisableOSCRedaction bool `json:"escapeAnalyticsDisableOSCRedaction,omitempty"`
// EscapeAnalyticsRetentionDays is the number of days to retain escape event rows.
// Default: 7.
EscapeAnalyticsRetentionDays int `json:"escapeAnalyticsRetentionDays,omitempty"`
// AnthropicAPIKey is the API key for the Anthropic AI API.
// Used by the AI rule generation feature (GenerateSuggestedRule RPC).
// Set via config.json or the ANTHROPIC_API_KEY environment variable.
// Do not log this value.
AnthropicAPIKey string `json:"anthropicApiKey,omitempty"`
// ProcessManagerBackend selects the process manager implementation.
// Valid values: "tmux" (default), "native" (Phase 2).
// Empty string is backwards-compatible and defaults to "tmux".
ProcessManagerBackend string `json:"process_manager_backend,omitempty"`
// GitHubEnterpriseHosts registers GitHub Enterprise Server instances (beyond
// github.com) with their own OAuth App client IDs, enabling device-flow login,
// PR polling, and link detection against those hosts. Empty means github.com only.
GitHubEnterpriseHosts []GitHubEnterpriseHost `json:"github_enterprise_hosts,omitempty"`
// StreamHubSessionOverrides forces the terminal-multi-connection-streaming
// project's PathHubOwned resolution for specific named tmux sessions,
// regardless of the global STAPLER_SQUAD_USE_STREAM_HUB default — the
// per-session canary mechanism (Story 3.3.1). Keys are tmux session
// names; an absent key means "no override, use the global default".
// Consulted via streamhub.SetSessionOverrideLookup, wired at process
// startup in server/services so package session/streamhub never imports
// package config directly.
StreamHubSessionOverrides map[string]bool `json:"stream_hub_session_overrides,omitempty"`
// RollbackRehearsalCompletedAt records when Story 3.3.2's rollback
// rehearsal (flip STAPLER_SQUAD_USE_STREAM_HUB's per-session override on
// for a disposable session, use it briefly, remove the override, confirm
// a clean reconnect under the legacy path) was last completed
// successfully. nil means "never completed". ResolveGlobalStreamHubDefault
// refuses to let the *global* default resolve to true until this is set
// (pre-mortem P1 #4's mechanical gate) — the per-session override above
// is unaffected by this gate. Set via RecordRollbackRehearsalCompleted.
RollbackRehearsalCompletedAt *time.Time `json:"rollback_rehearsal_completed_at,omitempty"`
// contains filtered or unexported fields
}
Config represents the application configuration
func LoadConfig ¶
func LoadConfig() *Config
func LoadConfigFromPath ¶ added in v1.16.0
LoadConfigFromPath loads and parses a config file from an explicit path. Returns the config and any error encountered.
func NewConfig ¶ added in v1.35.0
func NewConfig() *Config
NewConfig creates a Config with the default timeout executor.
func NewConfigWithExecutor ¶ added in v1.35.0
func NewConfigWithExecutor(exec CommandExecutor) *Config
NewConfigWithExecutor creates a Config with an explicit command executor. Pass nil to use the default timeout executor.
func (*Config) AnalyticsMaxAgeDaysOrDefault ¶ added in v1.35.0
AnalyticsMaxAgeDaysOrDefault returns the configured max analytics age in days, or 90 if not set (zero value).
func (*Config) AnalyticsMaxRowsOrDefault ¶ added in v1.35.0
AnalyticsMaxRowsOrDefault returns the configured max analytics rows, or 100_000 if not set (zero value).
func (*Config) AutoSpawnReadyItemsOrDefault ¶ added in v1.41.0
AutoSpawnReadyItemsOrDefault reports whether "ready" items should be automatically dequeued and spawned — in priority order, respecting the WIP cap — the moment a slot frees up, without a human manually clicking "Spawn Session". Defaults to true (nil or c == nil); pass explicit false to require manual spawning instead.
func (*Config) BacklogAttachmentDirOrDefault ¶ added in v1.39.0
BacklogAttachmentDirOrDefault returns the resolved backlog attachment directory. Uploaded images referenced from backlog item descriptions are stored here, durably (unlike the 24h temp paste dir) since they're linked from persisted markdown text. Always defaults to "~/.stapler-squad/backlog-attachments".
func (*Config) GetAvailablePrograms ¶ added in v1.35.0
GetAvailablePrograms returns a list of all detected CLI programs.
func (*Config) GetClaudeCommand ¶ added in v1.35.0
GetClaudeCommand attempts to find the "claude" command in the user's shell It checks in the following order: 1. Shell alias resolution (proxy-claude, then claude) 2. PATH lookup
If both fail, it returns an error.
func (*Config) GetFeatureFlag ¶ added in v1.35.0
GetFeatureFlag returns the persisted enabled state of the named feature flag. Absent key returns false — all feature flags default to disabled. Currently recognized flags:
"backlog" — enables the Backlog tab and backlog lifecycle controller.
func (*Config) GetGitHubEnterpriseHosts ¶ added in v1.41.0
func (c *Config) GetGitHubEnterpriseHosts() []GitHubEnterpriseHost
GetGitHubEnterpriseHosts returns the configured GHES hosts, or nil if c is nil.
func (*Config) GetKeyCategoryForKey ¶
GetKeyCategoryForKey returns the category for a specific key, or empty string if not found
func (*Config) GetOrCreateClaimantHostID ¶ added in v1.43.0
GetOrCreateClaimantHostID returns this process/config directory's stable ClaimantHostID, generating and persisting a new random UUID on first call. See the ClaimantHostID field doc comment for what this identifier is (and is not) used for.
func (*Config) GetOrCreateEncryptionKey ¶ added in v1.35.0
GetOrCreateEncryptionKey returns the 32-byte AES-256-GCM key for local data encryption. Generates and persists a new key on first call. Non-fatal errors during save are logged.
func (*Config) GetStreamHubSessionOverride ¶ added in v1.47.0
GetStreamHubSessionOverride reports whether sessionName has a per-session StreamHubSessionOverrides entry recorded, and if so, what it forces. Mirrors GetFeatureFlag's nil-safe shape: a nil Config or nil map reports (false, false) — no override.
func (*Config) HeadlessFailureCaptureDirOrDefault ¶ added in v1.42.0
HeadlessFailureCaptureDirOrDefault returns the resolved directory for durable headless (triage/review claude -p) failure captures — see session.WriteHeadlessFailureCapture. Always defaults to "~/.stapler-squad/headless-failures".
func (*Config) HibernationCheckpointDirOrDefault ¶ added in v1.35.0
HibernationCheckpointDirOrDefault returns the resolved hibernation checkpoint directory. If CheckpointDir is empty, it returns "~/.stapler-squad/checkpoints" with ~ expanded. The directory is NOT created here — the checkpoint writer creates it on first use.
func (*Config) MaxAutoReworkIterationsOrDefault ¶ added in v1.39.0
MaxAutoReworkIterationsOrDefault returns the configured rework-cap ceiling, or 20 if not set (zero value) or c is nil (BacklogService's cfg is nil in some test setups). Raised from 3 to 20: 3 was tripping routinely on real, ultimately-fixable items (e.g. a multi-round diff/review-harness flake, or a straightforward merge conflict) well before the work was actually stuck, forcing manual "Reopen for Revision" clicks for otherwise-recoverable items. Genuinely stuck items still get caught — just later — and per-item overrides (BacklogItemData.ReworkCapOverride) exist for cases that need to go further still.
func (*Config) MaxConcurrentBacklogWorkItemsOrDefault ¶ added in v1.41.0
MaxConcurrentBacklogWorkItemsOrDefault returns the configured backlog work-item concurrency cap, clamped to [1, maxConcurrentBacklogWorkItemsHardCeiling]. Falls back to the default (2) if unset (<=0) or c is nil.
func (*Config) NewProjectBaseDirOrDefault ¶ added in v1.35.0
NewProjectBaseDirOrDefault returns the resolved new-project base directory. If NewProjectBaseDir is empty, it defaults to "~/Projects" with ~ expanded.
func (*Config) OSCPayloadsAreRedacted ¶ added in v1.35.0
OSCPayloadsAreRedacted returns true when OSC payload redaction is enabled (the default). Redaction prevents PII (clipboard contents, window titles, CWD paths) from being stored in escape event records. Set EscapeAnalyticsDisableOSCRedaction=true in config to opt out.
func (*Config) OneOffBaseDirOrDefault ¶ added in v1.21.0
OneOffBaseDirOrDefault returns the resolved one-off base directory. If OneOffBaseDir is empty, it returns "~/oneoff" with ~ expanded to the current user's home directory. The directory is NOT created here — call namegen.GenerateAndCreate to create it on first use.
func (*Config) PromptCacheDirOrDefault ¶ added in v1.44.0
PromptCacheDirOrDefault returns the resolved directory for temp-file-backed session launch prompts (see Instance.promptArg). Always defaults to "~/.stapler-squad/prompt-cache".
func (*Config) RecordRollbackRehearsalCompleted ¶ added in v1.47.0
RecordRollbackRehearsalCompleted persists the current time as RollbackRehearsalCompletedAt and saves the config — Story 3.3.2's Task 3.3.2c, intended to be called exactly once, after manually verifying a rollback rehearsal (flip on via the per-session override, use briefly, remove the override, confirm clean legacy reconnect) passed against a real disposable session. Unblocks ResolveGlobalStreamHubDefault from refusing to enable the global default.
func (*Config) RemoteByName ¶ added in v1.47.0
func (c *Config) RemoteByName(name string) (*RemoteConfig, bool)
RemoteByName looks up a configured remote by its exact Name. Returns (nil, false) if c is nil or no remote with that name is registered. Consumed by session creation (Phase 4) and Settings UI validation (Phase 6).
func (*Config) RemoveKeyCategory ¶
RemoveKeyCategory removes the category mapping for a specific key
func (*Config) SetFeatureFlag ¶ added in v1.35.0
SetFeatureFlag sets the named feature flag and persists the config to disk.
func (*Config) SetKeyCategory ¶
SetKeyCategory updates the category for a specific key
func (*Config) SetStreamHubSessionOverride ¶ added in v1.47.0
SetStreamHubSessionOverride sets or clears sessionName's per-session PathHubOwned override and persists the config to disk — Story 3.3.1's canary mechanism. forceHub follows this file's existing *bool convention for a tri-state field (see AutoSpawnReadyItems): nil removes any override for sessionName (falling back to the global default), a non-nil false explicitly pins the session to the legacy path regardless of the global default, and a non-nil true forces PathHubOwned.
func (*Config) SlackSigningSecretOverride ¶ added in v1.44.0
SlackSigningSecretOverride returns the SLACK_SIGNING_SECRET environment variable value captured at load time, or "" if it was unset. See SlackWebhookURLOverride for why this getter exists.
func (*Config) SlackWebhookURLOverride ¶ added in v1.44.0
SlackWebhookURLOverride returns the SLACK_WEBHOOK_URL environment variable value captured at load time, or "" if it was unset. Exported because server/services (which resolves the effective Slack webhook URL per ADR-001: env override first, else decrypt the stored ciphertext) cannot read the unexported slackWebhookURLOverride field directly.
func (*Config) TriageArtifactDirOrDefault ¶ added in v1.37.0
TriageArtifactDirOrDefault returns the resolved triage artifact directory. Triage workers write their planning files here instead of into the item's repo. Always defaults to "~/.stapler-squad/triage-artifacts".
type ConfigFile ¶
type ConfigFile struct {
// Name is the filename (e.g., "CLAUDE.md", "settings.json", "agents.md")
Name string
// Path is the absolute path to the file
Path string
// Content is the file contents
Content string
// ModTime is the last modification timestamp
ModTime time.Time
}
ConfigFile represents a single Claude configuration file
type DirectoryRule ¶ added in v1.12.0
type DirectoryRule struct {
// Path is the absolute path prefix to match (longest match wins).
Path string `json:"path"`
// Profile is the optional named profile to apply when this rule matches.
Profile string `json:"profile,omitempty"`
// Overrides are field-level overrides applied after the profile (if any).
Overrides ProfileDefaults `json:"overrides,omitempty"`
}
DirectoryRule associates a working-directory path prefix with profile defaults.
type DiscoveryConfig ¶
type DiscoveryConfig struct {
// Mode determines which types of instances to discover
Mode DiscoveryMode `json:"mode"`
// AllowExternalAttach controls whether users can attach to external instances
AllowExternalAttach bool `json:"allow_external_attach"`
// ConfirmExternalOperations requires confirmation before operations on external instances
ConfirmExternalOperations bool `json:"confirm_external_operations"`
// SocketPaths defines custom socket paths for discovery (optional)
// Empty means use system defaults (/tmp/tmux-*/default)
SocketPaths []string `json:"socket_paths"`
// ExcludedSocketPaths defines socket paths to skip during discovery
ExcludedSocketPaths []string `json:"excluded_socket_paths"`
// DiscoverInterval is the interval (ms) at which external instances are scanned
DiscoverInterval int `json:"discover_interval"`
// AutoRefreshExternal enables automatic refresh of external instance metadata
AutoRefreshExternal bool `json:"auto_refresh_external"`
}
DiscoveryConfig controls instance discovery behavior and safety settings
func DefaultDiscoveryConfig ¶
func DefaultDiscoveryConfig() *DiscoveryConfig
DefaultDiscoveryConfig returns the default discovery configuration By default, only managed instances are shown for safety
func LoadDiscoveryConfig ¶
func LoadDiscoveryConfig() *DiscoveryConfig
LoadDiscoveryConfig loads the discovery configuration from disk
func (*DiscoveryConfig) CanAttachToExternal ¶
func (c *DiscoveryConfig) CanAttachToExternal() bool
CanAttachToExternal returns true if attaching to external instances is allowed
func (*DiscoveryConfig) IsExternalDiscoveryEnabled ¶
func (c *DiscoveryConfig) IsExternalDiscoveryEnabled() bool
IsExternalDiscoveryEnabled returns true if external instance discovery is enabled
func (*DiscoveryConfig) IsManagedDiscoveryEnabled ¶
func (c *DiscoveryConfig) IsManagedDiscoveryEnabled() bool
IsManagedDiscoveryEnabled returns true if managed instance discovery is enabled
func (*DiscoveryConfig) ShouldConfirmOperation ¶
func (c *DiscoveryConfig) ShouldConfirmOperation(isExternal bool) bool
ShouldConfirmOperation returns true if the operation requires user confirmation
func (*DiscoveryConfig) ShouldShowExternalInstances ¶
func (c *DiscoveryConfig) ShouldShowExternalInstances() bool
ShouldShowExternalInstances returns true if external instances should be displayed
type DiscoveryMode ¶
type DiscoveryMode string
DiscoveryMode defines how the application discovers Claude instances
const ( // DiscoveryManagedOnly discovers only instances created by stapler-squad DiscoveryManagedOnly DiscoveryMode = "managed-only" // DiscoveryExternalOnly discovers only external Claude instances DiscoveryExternalOnly DiscoveryMode = "external-only" // DiscoveryAll discovers both managed and external instances DiscoveryAll DiscoveryMode = "all" )
type GitHubEnterpriseHost ¶ added in v1.41.0
type GitHubEnterpriseHost struct {
// Host is the bare hostname (no scheme, no trailing slash), e.g. "github.example.com".
Host string `json:"host"`
// ClientID is the OAuth App client ID registered on that GHES instance.
ClientID string `json:"client_id"`
}
GitHubEnterpriseHost registers a GitHub Enterprise Server instance's OAuth App client ID so device-flow login can target that host in addition to github.com.
type HibernationConfig ¶ added in v1.35.0
type HibernationConfig struct {
// Enabled controls whether hibernation is active. Default: true.
Enabled bool `json:"enabled"`
// IdleTimeoutMinutes is the number of minutes a session must be idle before
// the sweeper automatically hibernates it. Default: 20.
IdleTimeoutMinutes int `json:"idle_timeout_minutes"`
// ResourcePressureThreshold is the memory usage percentage at which the
// sweeper begins hibernating idle sessions. Default: 85.
ResourcePressureThreshold int `json:"resource_pressure_threshold_pct"`
// CheckpointDir is the directory where hibernation checkpoint data is stored.
// Default: "~/.stapler-squad/checkpoints". Tilde is expanded at runtime.
CheckpointDir string `json:"checkpoint_dir"`
// RetentionDays is the number of days to retain stale checkpoint data.
// Default: 30.
RetentionDays int `json:"retention_days"`
}
HibernationConfig holds configuration for the session hibernation feature.
type LauncherPreset ¶ added in v1.42.0
type LauncherPreset struct {
ID string `json:"id"`
Label string `json:"label"`
Argv []string `json:"argv"`
Program string `json:"program,omitempty"`
DefaultPath string `json:"default_path,omitempty"`
}
LauncherPreset is one hand-authored entry in launcher-presets.json: a named, argv-based launch shortcut (e.g. a specific agent + flags, or a remote-exec ssh command). argv is never shell-split — argv[0] maps to Program, argv[1:] maps to session.Instance.ExtraArgs, both shell-quoted independently at launch time (see buildLaunchCommand in session/instance_tmux.go).
type LauncherPresetsFile ¶ added in v1.42.0
type LauncherPresetsFile struct {
Version int `json:"version"`
Presets []LauncherPreset `json:"presets"`
}
LauncherPresetsFile is the top-level document shape of launcher-presets.json.
func LoadLauncherPresets ¶ added in v1.42.0
func LoadLauncherPresets(path string) (*LauncherPresetsFile, error)
LoadLauncherPresets reads and validates launcher-presets.json at path.
A missing file is reported via an os.IsNotExist-satisfying error, distinguishable from a validation failure — callers treat "not exist" as "zero presets, no error to surface" and any other error as a loud, whole-file rejection to surface as load_error.
type NotificationPrefs ¶ added in v1.16.0
type NotificationPrefs struct {
// PushEnabled controls whether web push notifications are sent.
// Default is false (opt-in).
PushEnabled bool `json:"push_enabled"`
}
NotificationPrefs holds the user's notification delivery preferences.
type ProfileDefaults ¶ added in v1.12.0
type ProfileDefaults struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
Program string `json:"program,omitempty"`
AutoYes bool `json:"auto_yes,omitempty"`
Tags []string `json:"tags,omitempty"`
EnvVars map[string]string `json:"env_vars,omitempty"`
CLIFlags string `json:"cli_flags,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
ProfileDefaults holds the configurable fields for a named profile.
type ProviderPriority ¶ added in v1.35.0
ProviderPriority defines a prioritized CLI and model target for transitions.
type QuotaConfig ¶ added in v1.42.0
type QuotaConfig struct {
// Enabled gates the entire feature. When false, the gate is a no-op and
// BacklogController's toggle behaves exactly as it does today. Default: false.
Enabled bool `json:"enabled,omitempty"`
// PauseBelowHeadroomPct is the soft/proactive threshold: backlog is paused
// once estimated headroom drops below this percentage. Default: 20.0.
PauseBelowHeadroomPct float64 `json:"pause_below_headroom_pct,omitempty"`
// ResumeMarginPct is added to PauseBelowHeadroomPct to form the resume
// threshold, avoiding flapping right at the pause line. Default: 15.0.
ResumeMarginPct float64 `json:"resume_margin_pct,omitempty"`
// ConsecutiveTicksToPause is how many consecutive below-threshold reconcile
// ticks are required before the soft signal pauses backlog. Default: 2.
ConsecutiveTicksToPause int `json:"consecutive_ticks_to_pause,omitempty"`
// ConsecutiveTicksToResume is how many consecutive above-threshold reconcile
// ticks are required before the soft signal resumes backlog. Default: 3.
ConsecutiveTicksToResume int `json:"consecutive_ticks_to_resume,omitempty"`
// AssumedWindowTokenBudget is the operator-supplied assumed token budget for
// the trailing 5h window. Anthropic publishes no real budget, so this must be
// calibrated manually; 0 (the default) disables the soft/percentage signal
// entirely, leaving only the hard/reactive rate-limit override active.
AssumedWindowTokenBudget int64 `json:"assumed_window_token_budget,omitempty"`
// RateLimitWindowMinutes is how long a detected rate-limit event keeps the
// hard/reactive override active. Default: 30.
RateLimitWindowMinutes int `json:"rate_limit_window_minutes,omitempty"`
// ManualOverrideGraceMinutes is how long after a detected manual override the
// notification cooldown is bypassed for the next auto-transition. Default: 10.
ManualOverrideGraceMinutes int `json:"manual_override_grace_minutes,omitempty"`
// ForegroundThrottleDelaySeconds is how long the foreground-session dispatch
// throttle stays active after the most recently observed foreground activity.
// Default: 300.
ForegroundThrottleDelaySeconds int `json:"foreground_throttle_delay_seconds,omitempty"`
}
QuotaConfig holds configuration for the account-wide Claude Code session-quota gate that pauses/resumes backlog automation (see BacklogController) based on an inferred quota-headroom signal, plus a foreground-session dispatch throttle.
func (QuotaConfig) QuotaConfigOrDefault ¶ added in v1.42.0
func (c QuotaConfig) QuotaConfigOrDefault() QuotaConfig
QuotaConfigOrDefault returns a QuotaConfig with standard defaults applied to zero fields.
type RemoteConfig ¶ added in v1.47.0
type RemoteConfig struct {
// Name is the unique, user-chosen identifier for this remote (e.g.
// "prod-box"), referenced by session-creation flows. Not a hostname and
// not required to resolve as one.
Name string `json:"name"`
// Host is the SSH-reachable hostname or address (e.g. "prod.example.com"
// or "10.0.0.5"). Not a full "user@host" string — see User for the
// login name — and not a URL (no scheme, no path, no port suffix; use a
// standard SSH config Host block for non-default ports).
Host string `json:"host"`
// User is the SSH login username on the remote host. Not a local
// username and not validated against the remote's actual user list at
// save time.
User string `json:"user"`
// BasePath is the absolute filesystem path on the remote host under
// which session worktrees/directories are created (e.g.
// "/srv/workspaces"). Not a local path and not created automatically by
// saving this config — it must already exist (or be creatable) on the
// remote.
BasePath string `json:"base_path"`
// IdentityRef is an opaque, non-secret pointer to the SSH identity
// (private key + optional passphrase) that authenticates to this
// remote, resolved at connection time via sshremote.KeyStore against the
// OS keychain. It is NOT a filesystem path to a key file, NOT the key's
// raw bytes, and NOT the passphrase itself — no key material is ever
// stored in config.json. An empty value means no identity has been
// registered yet for this remote.
IdentityRef string `json:"identity_ref"`
}
RemoteConfig registers one SSH-reachable remote host that sessions can be created against (ssh-remote-workspaces feature). It holds only connection coordinates and a pointer to credential material — never the credential material itself. See sshremote.KeyStore (Phase 3.2) for where the actual SSH private key/passphrase bytes live (OS keychain), and Config.RemoteByName for the lookup helper consumed by session creation (Phase 4) and Settings UI validation (Phase 6).
type ResolvedDefaults ¶ added in v1.12.0
type ResolvedDefaults struct {
Program string
AutoYes bool
Tags []string
EnvVars map[string]string
CLIFlags string
// Path is the working directory path from an alias (empty for non-alias resolution).
Path string
// Branch is the git branch hint from alias invocation (e.g. from @alias:branch).
Branch string
// SessionLabel is the session label from alias invocation (text between alias and --)
SessionLabel string
// Source tracking — which layers contributed to this result.
UsedGlobal bool
UsedDirectory bool
UsedProfile bool
MatchedDirectory string
}
ResolvedDefaults is the merged result of all applicable default layers for a new session.
func ResolveAlias ¶ added in v1.35.0
func ResolveAlias(cfg *Config, aliasName, branch, label, extraFlags string) (ResolvedDefaults, error)
ResolveAlias resolves an alias by name and returns merged session defaults. Resolution order: global → directory → profile → alias inline fields. The alias's CLIFlags replace each prior layer (same as mergeProfileInto semantics). extraFlags are appended to the final CLIFlags as an explicit invocation-time step.
Path, Branch, and SessionLabel are promoted into the returned ResolvedDefaults so callers do not need to access the raw AliasConfig.
func ResolveDefaults ¶ added in v1.12.0
func ResolveDefaults(cfg *Config, workingDir, profileName string) ResolvedDefaults
ResolveDefaults merges the three layers of session defaults (global → directory → profile) for the given working directory and optional profile name.
Precedence (lowest → highest):
- cfg.DefaultProgram (legacy fallback)
- cfg.SessionDefaults global fields
- DirectoryRule.Overrides for the longest-matching path prefix
- Named profile (profileName argument)
Merge semantics:
- Scalar fields (Program, CLIFlags): non-empty source value overwrites target
- AutoYes: true in any layer sets it true
- Tags: union across all layers (duplicates removed)
- EnvVars: higher-layer key overwrites lower-layer key
type SessionDefaults ¶ added in v1.12.0
type SessionDefaults struct {
// Program is the default AI program (e.g., "claude", "aider").
Program string `json:"program,omitempty"`
// AutoYes auto-approves prompts in new sessions.
AutoYes bool `json:"auto_yes,omitempty"`
// Tags are pre-applied to every new session.
Tags []string `json:"tags,omitempty"`
// EnvVars are environment variables passed to new sessions.
EnvVars map[string]string `json:"env_vars,omitempty"`
// CLIFlags are additional CLI flags for the program.
CLIFlags string `json:"cli_flags,omitempty"`
// Profiles maps profile name → profile configuration.
Profiles map[string]ProfileDefaults `json:"profiles,omitempty"`
// DirectoryRules are path-based rules matched against the session's working directory.
DirectoryRules []DirectoryRule `json:"directory_rules,omitempty"`
// Aliases are named session presets invoked via @name in the omnibar.
Aliases []AliasConfig `json:"aliases,omitempty"`
}
SessionDefaults is the top-level container for all session default configuration.
type SessionRetentionConfig ¶ added in v1.41.0
type SessionRetentionConfig struct {
// Enabled controls whether the retention sweep runs. A pointer so a config
// saved before this field existed (nil) can be distinguished from an explicit
// `false` — nil defaults to enabled, matching AutoSpawnReadyItems's pattern.
Enabled *bool `json:"enabled,omitempty"`
// RetentionDays is how many days after a session is archived before the sweep
// is eligible to delete it (still subject to safety checks). Default: 14.
RetentionDays int `json:"retention_days,omitempty"`
}
SessionRetentionConfig holds configuration for the automatic session-retention cleanup sweep, which deletes archived sessions past a retention window once they pass safety checks (clean worktree, no open PR).
func (SessionRetentionConfig) EnabledOrDefault ¶ added in v1.41.0
func (c SessionRetentionConfig) EnabledOrDefault() bool
EnabledOrDefault returns whether the sweep is enabled, defaulting to true when unset.
func (SessionRetentionConfig) RetentionDaysOrDefault ¶ added in v1.41.0
func (c SessionRetentionConfig) RetentionDaysOrDefault() int
RetentionDaysOrDefault returns RetentionDays, falling back to defaultSessionRetentionDays when unset (<=0).
type SessionType ¶ added in v1.35.0
type SessionType string
SessionType is the session creation mode (directory, new_worktree, existing_worktree, etc.). Defined here so both the config layer and the session layer share the same type without a circular import — session already imports config.
const ( // SessionTypeDefault uses the default behavior (directory session). SessionTypeDefault SessionType = "" // SessionTypeDirectory creates a simple directory session without a worktree. SessionTypeDirectory SessionType = "directory" // SessionTypeNewWorktree creates a new git worktree for the session. SessionTypeNewWorktree SessionType = "new_worktree" // SessionTypeExistingWorktree reuses an existing git worktree. SessionTypeExistingWorktree SessionType = "existing_worktree" // SessionTypeNewProject creates a new directory with a git repo. SessionTypeNewProject SessionType = "new_project" // SessionTypeOneOff creates a temporary directory under OneOffBaseDir with a generated name. SessionTypeOneOff SessionType = "one_off" )
func (SessionType) IsValid ¶ added in v1.35.0
func (st SessionType) IsValid() bool
IsValid reports whether st is a recognized session type.
type SlackConfig ¶ added in v1.44.0
type SlackConfig struct {
// WebhookURLEncrypted is the AES-256-GCM-encrypted Slack Incoming Webhook
// URL, or empty if not configured. Never store or log the plaintext value.
WebhookURLEncrypted string `json:"webhook_url_encrypted,omitempty"`
// SigningSecretEncrypted is the AES-256-GCM-encrypted Slack app signing
// secret (Phase 2, used to verify interactive-button callbacks), or empty
// if not configured. Never store or log the plaintext value.
SigningSecretEncrypted string `json:"signing_secret_encrypted,omitempty"`
// NotifyOnQueueItem controls whether a Slack message is sent when an item
// enters the review queue. Default: false (opt-in).
NotifyOnQueueItem bool `json:"notify_on_queue_item,omitempty"`
// QueueDepthThreshold is the review-queue depth at which a digest
// notification is sent (edge-triggered: one digest per burst). 0 disables
// depth-based notifications.
QueueDepthThreshold int `json:"queue_depth_threshold,omitempty"`
// ApprovalEnabled controls whether outbound Slack messages include
// interactive allow/deny buttons (Phase 2) and whether the interactive
// callback route is registered. Default: false.
ApprovalEnabled bool `json:"approval_enabled,omitempty"`
// DashboardBaseURL is the base URL used to build "view in dashboard" links
// in Slack messages. Empty string means links are omitted.
DashboardBaseURL string `json:"dashboard_base_url,omitempty"`
}
SlackConfig holds configuration for the Slack review-queue notification feature (Phase 1: notify-only; Phase 2: interactive approval buttons).
WebhookURLEncrypted and SigningSecretEncrypted store ciphertext only, per ADR-001 (project_plans/slack-review-notifications/decisions/ADR-001-slack-secret-storage-encryption.md): both values are encrypted at rest with Config.GetOrCreateEncryptionKey() + session.EncryptToken/DecryptToken, the same primitive already used for backlog ItemSource tokens. The config package cannot decrypt them itself (it would need to import session, which already imports config); decryption happens in server/services, which imports both.
type StaleSessionConfig ¶ added in v1.44.0
type StaleSessionConfig struct {
// ThresholdMinutes is how many minutes of inactivity before a session is
// considered stale. Default: 30.
ThresholdMinutes int `json:"threshold_minutes,omitempty"`
// NotifyEnabled controls whether a notification is sent when a session goes
// stale. A pointer so a config saved before this field existed (nil) can be
// distinguished from an explicit `false` — nil defaults to enabled, matching
// SessionRetentionConfig.Enabled's pattern.
NotifyEnabled *bool `json:"notify_enabled,omitempty"`
}
StaleSessionConfig holds configuration for stale-session detection: how long a session may go without activity before it's flagged stale, and whether that triggers a notification.
func (StaleSessionConfig) NotifyEnabledOrDefault ¶ added in v1.44.0
func (c StaleSessionConfig) NotifyEnabledOrDefault() bool
NotifyEnabledOrDefault returns whether stale-session notifications are enabled, defaulting to true when unset.
func (StaleSessionConfig) ThresholdMinutesOrDefault ¶ added in v1.44.0
func (c StaleSessionConfig) ThresholdMinutesOrDefault() int
ThresholdMinutesOrDefault returns ThresholdMinutes, falling back to defaultStaleSessionThresholdMinutes when unset (<=0).
type State ¶
type State struct {
// HelpScreensSeen is a bitmask tracking which help screens have been shown
HelpScreensSeen uint32 `json:"help_screens_seen"`
// UI stores the UI preferences and state
UI UIState `json:"ui"`
// contains filtered or unexported fields
}
State represents the application state that persists between sessions
func LoadState ¶
func LoadState() *State
LoadState loads the state from disk with locking. If it cannot be done, we return the default state.
func NewTestState ¶
NewTestState creates a test state with isolated storage in the given directory This prevents tests from loading or interfering with production data
func (*State) GetCategoryExpanded ¶
GetCategoryExpanded returns whether a category is expanded (defaults to true for new categories)
func (*State) GetHelpScreensSeen ¶
GetHelpScreensSeen returns the bitmask of seen help screens
func (*State) GetSearchState ¶
GetSearchState returns the current search mode and query
func (*State) GetSelectedIndex ¶
GetSelectedIndex returns the last selected session index
func (*State) GetUIState ¶
GetUIState returns a copy of the current UI state
func (*State) RefreshState ¶
RefreshState reloads state from disk with locking
func (*State) SetCategoryExpanded ¶
SetCategoryExpanded updates the expanded state for a category
func (*State) SetHelpScreensSeen ¶
SetHelpScreensSeen updates the bitmask of seen help screens
func (*State) SetHidePaused ¶
SetHidePaused updates the hide paused filter state
func (*State) SetSearchMode ¶
SetSearchMode updates the search mode state
func (*State) SetSelectedIndex ¶
SetSelectedIndex updates the selected session index
type StateManager ¶
type StateManager interface {
AppState
UIStateAccess
// RefreshState reloads state from disk to detect changes made by other processes
RefreshState() error
// Close releases any resources held by the state manager
Close() error
}
StateManager combines app state and UI state management
type TmuxExecGateConfig ¶ added in v1.37.0
type TmuxExecGateConfig struct {
// Slots is the number of concurrent tmux subprocess execution slots.
// Zero or unset means "use the default" — see SlotsOrDefault. Default: 8.
Slots int `json:"slots"`
// ResyncFastLaneSlots is the number of concurrent tmux subprocess execution
// slots reserved for terminal-resync traffic when the
// "terminal:resync-exec-gate-fast-lane" feature flag is on, so resync calls
// don't contend with other tmux exec traffic for the shared Slots pool.
// Zero or unset means "use the default" — see ResyncFastLaneSlotsOrDefault.
// Default: 4.
ResyncFastLaneSlots int `json:"resyncFastLaneSlots"`
// InputFastLaneSlots is the number of concurrent tmux subprocess execution
// slots reserved for keystroke input traffic (the legacy per-keystroke
// send-keys path used when STAPLER_SQUAD_USE_CONTROL_MODE=false), so a
// poller flooding the shared Slots pool with capture-pane calls never
// makes user keystrokes queue behind it. Zero or unset means "use the
// default" — see InputFastLaneSlotsOrDefault. Default: 4.
InputFastLaneSlots int `json:"inputFastLaneSlots"`
}
TmuxExecGateConfig bounds how many tmux subprocesses may run concurrently against one tmux server, across every process on the machine (the main daemon and every --mcp process) — tmux's server is single-threaded, so unbounded concurrent subprocess spawns degrade it for everyone.
func (TmuxExecGateConfig) InputFastLaneSlotsOrDefault ¶ added in v1.47.0
func (c TmuxExecGateConfig) InputFastLaneSlotsOrDefault() int
InputFastLaneSlotsOrDefault returns InputFastLaneSlots, falling back to defaultInputFastLaneSlots when unset (covers both a fresh zero-value struct and a config.json saved before this field existed, which unmarshals the same way).
func (TmuxExecGateConfig) ResyncFastLaneSlotsOrDefault ¶ added in v1.44.0
func (c TmuxExecGateConfig) ResyncFastLaneSlotsOrDefault() int
ResyncFastLaneSlotsOrDefault returns ResyncFastLaneSlots, falling back to defaultResyncFastLaneSlots when unset (covers both a fresh zero-value struct and a config.json saved before this field existed, which unmarshals the same way).
func (TmuxExecGateConfig) SlotsOrDefault ¶ added in v1.37.0
func (c TmuxExecGateConfig) SlotsOrDefault() int
SlotsOrDefault returns Slots, falling back to defaultTmuxExecGateSlots when unset (covers both a fresh zero-value struct and a config.json saved before this field existed, which unmarshals the same way).
type TransitionMode ¶ added in v1.35.0
type TransitionMode string
TransitionMode controls how the system responds when capacity thresholds are crossed.
const ( // TransitionModeManual displays a suggestion banner; the user must click to switch. TransitionModeManual TransitionMode = "manual" // TransitionModeAuto automatically transitions sessions without user interaction. TransitionModeAuto TransitionMode = "auto" // TransitionModeNotify shows a warning notification without offering transition UI. TransitionModeNotify TransitionMode = "notify" )
type UIState ¶
type UIState struct {
// HidePaused controls whether paused sessions are filtered out
HidePaused bool `json:"hide_paused"`
// CategoryExpanded maps category names to their expanded state
CategoryExpanded map[string]bool `json:"category_expanded"`
// SearchMode tracks if search mode was active
SearchMode bool `json:"search_mode"`
// SearchQuery holds the last search query
SearchQuery string `json:"search_query"`
// SelectedIdx tracks the last selected session index
SelectedIdx int `json:"selected_idx"`
}
UIState represents UI preferences that persist between sessions
type UIStateAccess ¶
type UIStateAccess interface {
// GetUIState returns a copy of the current UI state
GetUIState() UIState
// SetHidePaused updates the hide paused filter state
SetHidePaused(hidePaused bool) error
// SetCategoryExpanded updates the expanded state for a category
SetCategoryExpanded(category string, expanded bool) error
// GetCategoryExpanded returns whether a category is expanded
GetCategoryExpanded(category string) bool
// SetSearchMode updates the search mode state
SetSearchMode(searchMode bool, query string) error
// GetSearchState returns the current search mode and query
GetSearchState() (bool, string)
// SetSelectedIndex updates the selected session index
SetSelectedIndex(index int) error
// GetSelectedIndex returns the last selected session index
GetSelectedIndex() int
}
UIStateAccess provides methods for accessing and modifying UI state
type WorkspaceMeta ¶
type WorkspaceMeta struct {
WorkspaceID string `json:"workspace_id"` // dir name (hash or instance name)
Type string `json:"type"` // "workspace", "instance", "shared"
CWD string `json:"cwd"`
Name string `json:"name"` // last path component of CWD, or "Default"
ConfigDir string `json:"config_dir"` // absolute path to this workspace dir
LastUsed time.Time `json:"last_used"`
}
WorkspaceMeta stores display information about a workspace/database. Written to each workspace directory at startup to enable workspace discovery.
func ListAvailableWorkspaces ¶
func ListAvailableWorkspaces(baseDir string) ([]WorkspaceMeta, error)
ListAvailableWorkspaces discovers all known workspaces by scanning workspace and instance subdirs. Skips test directories. Returns an empty slice (not an error) if none are found.
func ReadWorkspaceMeta ¶
func ReadWorkspaceMeta(configDir string) (WorkspaceMeta, error)
ReadWorkspaceMeta reads workspace metadata from the given config directory.