Documentation
¶
Overview ¶
The lib package is the implementation of the core functionality of the raid CLI tool.
Index ¶
- Constants
- Variables
- func AcquireMutationLock() (func(), error)
- func AddProfile(profile Profile) error
- func AddProfiles(profiles []Profile) error
- func CloneRepository(repo Repo) error
- func ContainsEnv(name string) bool
- func ContainsProfile(name string) bool
- func CreateRepoConfigs(repos []RepoDraft)
- func ExecuteCommand(name string, args []string, named map[string]string) error
- func ExecuteEnv(name string) error
- func ExecuteRepoCommand(repoName, cmdName string, args []string, named map[string]string) error
- func ExecuteTask(task Task) error
- func ExecuteTasks(tasks []Task) error
- func ForceLoad() error
- func GetEnv() string
- func InitConfig() error
- func Install(maxThreads int) error
- func InstallRepo(name string) error
- func ListEnvs() []string
- func Load() error
- func LoadEnv() error
- func RecordRecentEnd(command string, runErr error, startedAt time.Time)
- func RecordRecentStart(command string) time.Time
- func RemoveProfile(name string) error
- func ResetContext()
- func Set(key string, value any) error
- func SetCommandOutput(stdout, stderr io.Writer) func()
- func SetEnv(name string) error
- func SetProfile(name string) error
- func ValidateProfile(path string) error
- func ValidateRepo(path string) error
- func ValidateSchema(path string, schemaPath string) error
- func WatchRaidVars(ctx stdctx.Context, onChange func()) error
- func WithMutationLock(fn func() error) error
- func Write() error
- func WriteProfileFile(draft ProfileDraft, path string) error
- type Arg
- type Command
- type Condition
- type Context
- type Env
- type EnvVar
- type Finding
- type Flag
- type OnInstall
- type Output
- type Profile
- type ProfileDraft
- type RecentEntry
- type Repo
- type RepoDraft
- type Severity
- type Task
- type TaskProps
- type TaskType
- type Workspace
- type WorkspaceCommand
- type WorkspaceContext
- type WorkspaceRepo
- type WorkspaceStep
- type WorkspaceTool
Constants ¶
const ( ConfigDirName = ".raid" ConfigFileName = "config.toml" ConfigPathDefault = "~" + sys.Sep + ConfigDirName + sys.Sep + ConfigFileName ConfigPathFlag = "config" ConfigPathFlagShort = "c" ConfigPathFlagDesc = "configuration file path (default is " + ConfigPathDefault + ")" )
const ( RecentStatusCompleted = "completed" RecentStatusInterrupted = "interrupted" )
Recent statuses surfaced via ReadRecent. "running" only ever appears on disk while a command is in flight; ReadRecent rewrites it to RecentStatusInterrupted so consumers always see one of these two terminal states.
const (
RaidConfigFileName = "raid.yaml"
)
Variables ¶
var CfgPath string
var LockPathOverride string
LockPathOverride redirects the mutation lock file. Tests set this so they don't contend with a real raid invocation running on the developer's machine. Empty in production.
var RecentPathOverride string
RecentPathOverride redirects the recent.json log path. Intended only for tests that exercise ExecuteCommand so they do not pollute the developer's real ~/.raid/recent.json.
Functions ¶
func AcquireMutationLock ¶
func AcquireMutationLock() (func(), error)
AcquireMutationLock blocks until this process holds the raid mutation lock, then returns a release function. Callers MUST defer the release.
This is a cross-process exclusive lock backed by flock(2) on POSIX and LockFileEx on Windows. Any raid invocation (CLI or MCP server) that calls AcquireMutationLock while another raid process holds it will wait until released. The kernel releases the lock automatically if the holder exits unexpectedly — no stale-lock recovery code needed.
Read-only operations (raid context, raid_list_*, raid doctor, etc.) don't need this lock; stale reads during a mutation are recoverable. Only paths that write ~/.raid state, .env files in repos, or run task sequences should acquire it.
func AddProfile ¶
AddProfile registers a profile in the config store.
func AddProfiles ¶
AddProfiles registers multiple profiles in the config store.
func CloneRepository ¶
CloneRepository clones a repository to its configured path. Skips if it already exists. Repos with no configured `url` (local-only) are never cloned — the path must already exist on disk; otherwise an error is returned so the user knows the local repo is missing rather than getting a confusing git error.
func ContainsEnv ¶
ContainsEnv reports whether an environment with the given name exists in the active profile.
func ContainsProfile ¶
ContainsProfile reports whether a profile with the given name is registered.
func CreateRepoConfigs ¶
func CreateRepoConfigs(repos []RepoDraft)
CreateRepoConfigs writes a raid.yaml stub into each repository's local directory.
func ExecuteCommand ¶
ExecuteCommand runs the tasks for the named command, applying any output configuration. Positional `args` are exposed as RAID_ARG_1, RAID_ARG_2, ... environment variables. When `named` is non-nil, each entry is also exported as a env var with the key uppercased — this is how cobra-parsed named arguments and flags reach task scripts. All bindings are unset after the command exits.
func ExecuteEnv ¶
ExecuteEnv writes environment variables to each repo's .env file and runs the environment's tasks.
func ExecuteRepoCommand ¶
ExecuteRepoCommand runs a command defined in a specific repository's raid.yaml. See ExecuteCommand for how `args` and `named` are bound to env vars.
func ExecuteTask ¶
func ExecuteTasks ¶
func ForceLoad ¶
func ForceLoad() error
ForceLoad rebuilds the context from the active profile, ignoring any cached state.
func InitConfig ¶
func InitConfig() error
func InstallRepo ¶
InstallRepo clones a single named repository and runs its install tasks. The profile-level install tasks are not run.
func ListEnvs ¶
func ListEnvs() []string
ListEnvs returns the names of all environments in the active profile.
func Load ¶
func Load() error
Load initializes the context from the active profile, using cached results if available.
func LoadEnv ¶
func LoadEnv() error
LoadEnv loads .env files from all repositories in the active profile into the process environment.
func RecordRecentEnd ¶
RecordRecentEnd updates the placeholder entry written by RecordRecentStart with the command's exit code and total duration. If the matching entry has fallen off the cap, no update is recorded.
func RecordRecentStart ¶
RecordRecentStart prepends a placeholder entry for command in the recent log and returns the moment recording began. Pass the same value to RecordRecentEnd once the command has finished. Errors writing the log are silenced — recording history must never break command execution.
The returned time keeps full nanosecond precision so it can be used as an unambiguous key by RecordRecentEnd: two commands started in the same second would alias if we truncated. Display formatters can re-truncate at render time when a coarser granularity is desired.
func RemoveProfile ¶
RemoveProfile removes a registered profile by name.
func ResetContext ¶
func ResetContext()
ResetContext clears the cached load context, forcing the next Load or ForceLoad to rebuild from the current viper configuration.
func SetCommandOutput ¶
SetCommandOutput swaps the writers used by task execution, repository cloning, and environment setup. The returned function restores the previous writers — call via defer to keep state clean even if the caller panics. Not safe for concurrent calls; serialise at the call site if multiple goroutines may run mutating operations.
func SetProfile ¶
SetProfile sets the named profile as the active profile.
func ValidateProfile ¶
ValidateProfile validates the profile file at path against the profile JSON schema.
func ValidateRepo ¶
ValidateRepo validates the repo config file at path against the repo JSON schema.
func ValidateSchema ¶
ValidateSchema validates the file at path against the JSON schema at schemaPath. schemaPath must be an absolute or CWD-relative path to a schema file on disk.
func WatchRaidVars ¶
WatchRaidVars watches the raid vars file (~/.raid/vars) for the lifetime of ctx and invokes onChange whenever the file is created, modified, or replaced. Events are debounced. The watcher is attached to the parent directory so atomic-rename writes (which swap the inode) keep firing — a watch on the file itself would silently go deaf after the first rename.
onChange is the caller's reload hook; lib does not assume what to reload, so the MCP server passes a closure that runs ForceLoad under the cross-process mutation lock.
func WithMutationLock ¶
WithMutationLock acquires the mutation lock, runs fn, and releases the lock. Use this when an entire user-visible operation should be atomic across processes — for example, the env-switch sequence (set + force load + execute) where any gap between steps could let another raid invocation interleave.
func WriteProfileFile ¶
func WriteProfileFile(draft ProfileDraft, path string) error
WriteProfileFile serializes draft as YAML and writes it to path, creating parent directories as needed.
Types ¶
type Arg ¶
type Arg struct {
Name string `json:"name"`
Usage string `json:"usage,omitempty"`
Required bool `json:"required,omitempty"`
}
Arg declares a positional argument for a custom command. The supplied value is bound to the env var named after Name (uppercased) for the duration of the command, so tasks can reference it as `$NAME`. Required args without a matching positional value cause cobra to reject the invocation.
type Command ¶
type Command struct {
Name string `json:"name"`
Usage string `json:"usage"`
Args []Arg `json:"args,omitempty"`
Flags []Flag `json:"flags,omitempty"`
Tasks []Task `json:"tasks"`
Out *Output `json:"out,omitempty"`
}
Command is a named, user-defined CLI command that can be invoked via 'raid <name>'.
func GetCommands ¶
func GetCommands() []Command
GetCommands returns all commands available in the active profile.
func QuietLoad ¶
func QuietLoad() []Command
QuietLoad attempts a best-effort, read-only profile load. It does not create config files, does not emit warnings, and returns nil if the config is absent or loading fails. Intended for info-command paths (--help, --version) where user-command registration is opportunistic and side effects are undesirable.
type Condition ¶
type Condition struct {
Platform string `json:"platform,omitempty"`
Exists string `json:"exists,omitempty"`
Cmd string `json:"cmd,omitempty"`
}
Condition guards a task — all specified fields must be satisfied for the task to run.
type Env ¶
type Env struct {
Name string `json:"name"`
Variables []EnvVar `json:"variables"`
Tasks []Task `json:"tasks"`
}
Env represents a named environment with variables and tasks.
type Finding ¶
type Finding struct {
Severity Severity
Check string
Message string
Suggestion string // shown when non-empty
}
Finding represents the result of a single doctor check.
type Flag ¶
type Flag struct {
Name string `json:"name"`
Short string `json:"short,omitempty"`
Usage string `json:"usage,omitempty"`
Type string `json:"type,omitempty"`
Required bool `json:"required,omitempty"`
Default any `json:"default,omitempty"`
}
Flag declares a long-form (--name) and optional short-form (-x) flag for a custom command. Type is one of "string" (default), "bool", or "int". Required flags are enforced by cobra. Default supplies the value when the flag is omitted; bool flags default to false unless overridden.
type OnInstall ¶
type OnInstall struct {
Tasks []Task `json:"tasks"`
}
OnInstall holds the tasks to run during profile installation.
type Output ¶
type Output struct {
Stdout bool `json:"stdout"`
Stderr bool `json:"stderr"`
File string `json:"file,omitempty"`
}
Output configures how a command's task output is handled. Stdout and Stderr default to true when Out is nil. When Out is set, only streams explicitly set to true are shown.
type Profile ¶
type Profile struct {
Name string `json:"name"`
Path string `json:"path"`
Repositories []Repo `json:"repositories"`
Environments []Env `json:"environments"`
Install OnInstall `json:"install"`
Groups map[string][]Task `json:"task_groups" yaml:"task_groups"`
Commands []Command `json:"commands"`
}
Profile represents a named collection of repositories, environments, and task groups.
func BuildSingleRepoProfile ¶
BuildSingleRepoProfile validates the raid.yaml at path and returns a synthetic profile whose only repository points at the raid.yaml's directory. The profile's Path is the raid.yaml itself, so IsSingleRepo reports true on subsequent loads. The repository's full configuration (commands, environments, install tasks) is merged in by buildRepo later in the load pipeline.
func ExtractProfile ¶
ExtractProfile reads and returns a single named profile from the given file.
func ExtractProfiles ¶
ExtractProfiles reads all profiles from a YAML or JSON file.
func (Profile) IsSingleRepo ¶
IsSingleRepo reports whether the profile is backed by a raid.yaml (repo config) rather than a profile YAML. Detected by the registered path's basename: when `raid profile add ./raid.yaml` records a path pointing at a repo config, raid synthesizes a single-repo profile around it instead of requiring a wrapping profile file. The buildProfile / doctor paths branch on this check to switch schemas.
type ProfileDraft ¶
type ProfileDraft struct {
Name string `yaml:"name"`
Repositories []RepoDraft `yaml:"repositories,omitempty"`
}
ProfileDraft is the minimal structure written to a new profile file.
type RecentEntry ¶
type RecentEntry struct {
Command string `json:"command"`
Status string `json:"status"`
ExitCode int `json:"exitCode"`
StartedAt time.Time `json:"startedAt"`
DurationMs int64 `json:"durationMs,omitempty"`
}
RecentEntry records a single `raid <command>` invocation. The log is intentionally per-command (not per-task) — agents asking "what did the developer just run?" want a high-level history, not every Shell step.
Lifecycle: an entry is first written on command start with Status="running", then updated to Status="completed" once the command exits normally. If the process is killed (SIGINT/SIGTERM/SIGKILL), the running entry survives on disk and ReadRecent reports it as Status="interrupted".
func ReadRecent ¶
func ReadRecent() []RecentEntry
ReadRecent returns the most-recent-first list of recorded command runs. Returns nil if the log file does not exist or cannot be parsed; callers should treat absence as "no history yet".
Any entry still in the on-disk "running" state is rewritten to RecentStatusInterrupted before returning, so callers always see a terminal status. Concurrent in-flight invocations will be misreported, but that's an acceptable trade-off for a tool that is overwhelmingly run sequentially.
type Repo ¶
type Repo struct {
Name string `json:"name"`
Path string `json:"path"`
URL string `json:"url"`
Branch string `json:"branch"`
Environments []Env `json:"environments"`
Install OnInstall `json:"install"`
Commands []Command `json:"commands"`
}
Repo represents a single repository entry in a profile.
func ExtractRepo ¶
ExtractRepo reads and parses the raid.yaml from the given repository directory.
func (Repo) IsLocalOnly ¶
IsLocalOnly reports whether the repo has no configured git remote. Local-only repos skip cloning; install tasks run directly against the existing path. The path must already exist on disk for install to work.
Whitespace-only URLs (e.g. `url: " "` from a stray edit) are treated as local-only — the trimmed value is what would be handed to `git clone`, and an empty string there produces a confusing error rather than a useful one.
type RepoDraft ¶
type RepoDraft struct {
Name string `yaml:"name"`
Path string `yaml:"path"`
URL string `yaml:"url"`
Branch string `yaml:"branch,omitempty"`
}
RepoDraft holds the fields collected for each repository during profile creation.
func CollectRepos ¶
CollectRepos runs an interactive prompt loop to collect repository details from reader.
type Task ¶
type Task struct {
TaskProps `yaml:",inline"`
Type TaskType `json:"type"`
Concurrent bool `json:"concurrent,omitempty"`
Condition *Condition `json:"condition,omitempty"`
// Shell
Cmd string `json:"cmd,omitempty"`
Literal bool `json:"literal,omitempty"`
Shell string `json:"shell,omitempty"`
// Script
Path string `json:"path,omitempty"`
Runner string `json:"runner,omitempty"`
// HTTP
URL string `json:"url,omitempty"`
Dest string `json:"dest,omitempty"`
// Wait
Timeout string `json:"timeout,omitempty"`
// Template
Src string `json:"src,omitempty"`
// Group
Ref string `json:"ref,omitempty"`
Parallel bool `json:"parallel,omitempty"`
// Git
Op string `json:"op,omitempty"`
Branch string `json:"branch,omitempty"`
// Prompt / Confirm / Print
Message string `json:"message,omitempty"`
// Prompt / SetVar
Var string `json:"var,omitempty"`
Default string `json:"default,omitempty"`
// SetVar
Value string `json:"value,omitempty"`
// Print
Color string `json:"color,omitempty"`
// Retry
Attempts int `json:"attempts,omitempty"`
Delay string `json:"delay,omitempty"`
}
Task represents a single unit of work in a task sequence.
type TaskProps ¶
type TaskProps struct {
// Name is an optional human-readable label for the task, surfaced in logs
// and agent-facing output. It does not affect execution.
Name string `json:"name,omitempty" yaml:"name,omitempty"`
}
TaskProps holds properties shared by every task type. It is embedded into Task so the fields below appear at the top level of a task's YAML/JSON representation, and remain accessible via field promotion (e.g. `task.Name`).
type Workspace ¶
type Workspace struct {
Profile string `json:"profile"`
Env string `json:"env,omitempty"`
Repos []WorkspaceRepo `json:"repos"`
Commands []WorkspaceCommand `json:"commands"`
Recent []RecentEntry `json:"recent,omitempty"`
Vars map[string]string `json:"vars,omitempty"`
}
Workspace is the inline workspace state — what would be served via `resources/read` against a `raid://workspace/*` URI by an MCP server.
type WorkspaceCommand ¶
type WorkspaceCommand struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
Steps []WorkspaceStep `json:"steps,omitempty"`
}
WorkspaceCommand exposes a profile command's name and short description. Script bodies are intentionally excluded so the snapshot stays token-efficient and free of secrets. If any of the command's tasks have a `name` field set, they're surfaced in Steps as an outline of what the command does — also without script bodies.
type WorkspaceContext ¶
type WorkspaceContext struct {
Name string `json:"name"`
Title string `json:"title,omitempty"`
Version string `json:"version,omitempty"`
WebsiteUrl string `json:"websiteUrl,omitempty"`
GeneratedAt time.Time `json:"generatedAt"`
Tools []WorkspaceTool `json:"tools,omitempty"`
Workspace Workspace `json:"workspace"`
}
WorkspaceContext is a condensed snapshot of the active workspace, intended for agent / `raid context` consumption. It is derived from the loaded session context plus on-disk git state per repository.
The top-level Name / Version / WebsiteUrl / GeneratedAt fields identify the producer so an agent that picks up the snapshot in isolation can find the project on GitHub for further context and judge freshness. The Workspace sub-object holds the actual workspace state.
Field naming follows MCP (camelCase) so this shape can be lifted directly into the future `raid context serve` MCP server responses with no further translation.
func GetWorkspaceContext ¶
func GetWorkspaceContext() WorkspaceContext
GetWorkspaceContext returns a snapshot of the active workspace. The session context must already be loaded (Load / ForceLoad). If no profile is active the returned WorkspaceContext has empty Workspace.Profile and empty Repos / Commands slices.
type WorkspaceRepo ¶
type WorkspaceRepo struct {
Name string `json:"name"`
Path string `json:"path"`
Cloned bool `json:"cloned"`
Branch string `json:"branch,omitempty"`
Dirty bool `json:"dirty,omitempty"`
}
WorkspaceRepo describes a single repository in the active profile, as it exists on disk right now (not just as configured).
type WorkspaceStep ¶
type WorkspaceStep struct {
Name string `json:"name"`
}
WorkspaceStep describes one named task inside a command's task sequence. Only tasks with a populated TaskProps.Name appear here; unnamed tasks are omitted to keep the snapshot focused on user-meaningful labels.
type WorkspaceTool ¶
type WorkspaceTool struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
}
WorkspaceTool describes a built-in `raid` subcommand the agent can invoke directly (e.g. `raid install`, `raid env`). User-defined commands live in Workspace.Commands and are not duplicated here.