import type { LinearAdapterConfig } from "./adapters/linear/schema.ts"; import type { ShellAdapterConfig } from "./adapters/shell/schema.ts"; import type { TodoTxtAdapterConfig } from "./adapters/todo-txt/schema.ts"; export { BUILD_SECRET_NAMES } from "./buildSecrets.ts"; export declare const WORKER_ENVIRONMENT_NAMES: readonly ["GROUNDCREW_TASK_ID", "GROUNDCREW_COMPLETE"]; /** * Authoring shape for a manifest-backed (discovered) task source: enable by * kind, with light overrides. The concrete per-source schema is built at * runtime by `manifestAdapter` and validated at `buildSources` time, so the * static type here is intentionally permissive. */ export interface ManifestSourceConfig { kind: string; name?: string; env?: Record; timeouts?: ShellAdapterConfig["timeouts"]; enabled?: boolean; } /** * Discriminated union of all built-in adapter config shapes. Used at * config-load time as the static type for `Config.sources[]` and * `ResolvedConfig.sources[]`. The runtime Zod validation lives in each * adapter's `schema.ts` and runs at `buildSources` time, not here. */ export type SourceConfig = LinearAdapterConfig | ShellAdapterConfig | TodoTxtAdapterConfig | ManifestSourceConfig; export interface HookCommands { prepareWorktree?: string; } /** * Operator-only hooks that run OUTSIDE the sandbox, on the host shell. Mirrors * the `prepareWorktree` phase name of {@link HookCommands} but is a distinct * type: the two families follow opposite rules (host hooks are operator-only * with no `defaults` cascade), and keeping them separate lets a consumer that * takes an `UnsandboxedHookCommands` be self-documenting about isolation and * lets the two shapes diverge without leaking into each other. */ export interface UnsandboxedHookCommands { prepareWorktree?: string; } /** * Reserved agent name. A task labeled `agent-any` resolves at runtime * to the configured agent with the most available session capacity, so * `any` cannot itself be a agent. orchestrator.ts imports this constant * so the reserved name lives in one place. */ export declare const AGENT_ANY = "any"; /** * Which terminal session manager hosts the agent process: * * - `auto`: pick the first available — cmux when installed, else tmux. * - `cmux`: require the cmux binary; fail loudly if missing. * - `tmux`: require the tmux binary; fail loudly if missing. * - `zellij`: require the zellij binary; fail loudly if missing. */ export type WorkspaceKindSetting = "auto" | "cmux" | "tmux" | "zellij"; export declare const WORKSPACE_KIND_SETTINGS: readonly WorkspaceKindSetting[]; /** * Concrete local isolation backend selected for a launch. `safehouse` is * macOS-only (clearance HTTP-egress + sandbox profile); `sdx` is Docker * Sandboxes (`sbx` CLI) on Linux and macOS; `none` is an explicit unsandboxed * escape hatch. */ export type LocalRunner = "safehouse" | "sdx" | "none"; /** * User-facing local runner setting. `auto` resolves at launch time: * macOS picks `safehouse`, Linux picks `sdx`. `none` is never picked implicitly. */ export type LocalRunnerSetting = LocalRunner | "auto"; export declare const LOCAL_RUNNER_SETTINGS: readonly LocalRunnerSetting[]; /** * Network posture for local runners that can choose between allowlisted and * unrestricted egress. Only the safehouse runner consumes this today. */ export type NetworkEgressSetting = "allowlisted" | "open"; export declare const NETWORK_EGRESS_SETTINGS: readonly NetworkEgressSetting[]; /** * Per-agent Docker Sandboxes (sdx) binding. Required at launch when * `local.runner` resolves to `sdx` so groundcrew knows which existing * sbx sandbox to address. */ export interface SandboxDefinition { /** sbx agent name (e.g. "claude", "codex"). */ agent: string; } export interface AgentDefinition { /** * Shell command launched for the agent. Wrapped with Safehouse/clearance * for execution. The rendered prompt is appended as a single quoted * positional argument. `{{worktree}}` is replaced before launch. * * Keep this agent-native (e.g., `claude --permission-mode auto`). * Groundcrew adds the Safehouse wrapper. */ cmd: string; /** * Optional shell snippet run in the launch shell **before** the agent is * exec'd and **outside** Safehouse/sdx. Use to mint short-lived credentials * (e.g. `export SESSION_TOKEN=...`) that the wrapped `cmd` inherits via * the process environment. `{{worktree}}` is replaced before launch. * Failures abort launch (unlike prepareWorktree, which logs and continues). * Not supported for `local.runner` `sdx` in v1. */ preLaunch?: string; /** * Optional list of env var names to forward from the launch shell into * the agent under the safehouse runner. Companion to `preLaunch` — * names exported by `preLaunch` go here so groundcrew appends them to the * Safehouse wrap's `--env-pass=` flag without forcing the user to rewrite * `cmd`. Under `local.runner: "none"`, listed names are cleared immediately * before `preLaunch`, then its exports flow through unchanged to the agent. * An empty array is a uniform no-op in every runner (it forwards zero names, * so the unsupported-runner guards do not fire). A non-empty list is * rejected when `local.runner` resolves to `sdx` in v1, and when `cmd` * already starts with `safehouse` (the user owns env forwarding in that * case). Groundcrew-managed worker environment names are also rejected. * Each name must match `[A-Za-z_][A-Za-z0-9_]*` (POSIX env var name). */ preLaunchEnv?: string[]; color: string; usage?: { codexbar: { provider: string; source?: string; }; }; /** * Docker Sandboxes binding. Required when `local.runner` resolves to * `sdx` — pure additive: omitted agents can still run under `safehouse` * or `none` without surprise. */ sandbox?: SandboxDefinition; /** * Opt-in: shell args appended to `cmd` on `crew resume` so the agent reopens * its previous conversation in the worktree instead of cold-starting. groundcrew * stores no session id — it relies on one conversation per worktree, so the * agent's own "resume latest in this directory" primitive is enough. * * Examples: `"--continue"` (Claude Code), `"resume --last"` (Codex). * * `crew resume --new` ignores this and forces a fresh conversation. */ resumeArgs?: string; } /** * User-facing agent entry shape. Built-in agent names (`claude`, `codex`, * `cursor`, `cursor-grok`, `pi`) accept empty or partial entries because they * merge over built-in presets. * Brand-new agent names must supply enough fields to satisfy `validate()`. * * `usage` accepts an extra `{ disabled: true }` sentinel that strips the * usage block from the merged definition — the only way to opt a shipped * preset out of codexbar gating without removing the agent entirely. */ type UserUsage = AgentDefinition["usage"] | { disabled: true; }; type EnabledUserAgentDefinition = Partial> & { usage?: UserUsage; }; type UserAgentDefinition = EnabledUserAgentDefinition; /** * Loose user-facing shape — what a `config.ts` file declares. * Fields with defaults are optional; only `workspace.*` is required. * * Groundcrew's built-in Linear adapter is implicit and needs no config: * it picks up every Linear issue assigned to the API key's viewer that * carries an `agent-*` label. There is no project or view configuration. * Linear's default "In Progress" / "In Review" status names disambiguate * `started` workflow states; unmatched statuses fall back to `state.type`. */ /** * Scripted provisioning templates for a repository. Both run via `sh -c` in * place of the native git porcelain: `create` replaces `git worktree add`, * `remove` replaces `git worktree remove`. Grouping them in one object makes the * native/scripted split structural — an entry either has `provision` (scripted) * or it doesn't (native) — and removes the need for a both-or-neither check. */ export interface ProvisionScripts { /** Shell template run in place of `git worktree add`. */ create: string; /** Shell template run in place of `git worktree remove`. */ remove: string; } /** * A configured repository. The bare-string form keeps the repo under * `workspace.projectDir`; the object form's optional `projectDirOverride` * overrides that parent directory so repos can live in more than one place. When * a `provision` block is present (a *scripted* entry), groundcrew runs its * templates via `sh -c` in place of `git worktree add`/`remove`; the `name` is * then a logical handle and the physical clone is the template's concern (e.g. * graft's own registry). */ export interface KnownRepository { /** Logical repo name: the token tickets reference and the worktree dir basename. */ name: string; /** Overrides the parent directory the source repo lives under (defaults to `projectDir`). */ projectDirOverride?: string; /** Scripted provisioning templates; presence marks this a scripted entry. Mutually exclusive with `projectDirOverride`. */ provision?: ProvisionScripts; /** * Project subdirectory within the worktree. When set, the agent cwd, the * `prepareWorktree` hook, and the `.groundcrew/config.json` lookup re-root to * `/`. The worktree root itself (identity, sandbox access) * is unchanged. Relative, no `..`. */ workdir?: string; /** * Per-repo operator hooks, reusing the same `HookCommands` shape that * `defaults.hooks` and the in-repo `.groundcrew/config.json` use. Slots * between the repo-committed file (wins) and `defaults.hooks` (fallback) in * the `prepareWorktree` cascade, so an operator can set the hook for a repo * they don't want to (or can't) commit a `.groundcrew/config.json` into. */ hooks?: HookCommands; /** * Operator-only, per-repository hooks run on the HOST shell outside any * sandbox. `unsandboxedHooks.prepareWorktree` runs before the sandboxed * `hooks.prepareWorktree` and the agent. Honored ONLY from `crew.config.ts`; * a `.groundcrew/config.json` that sets `unsandboxedHooks` is a hard config * error (see `repositoryHooks.ts`). There is deliberately no `defaults` * equivalent — host execution is an explicit per-repo grant, never a global * default. Runs with the operator's full host authority against * repo-controlled code (lifecycle scripts, the repo's own `bin/setup`), so * granting it is an explicit trust decision. */ unsandboxedHooks?: UnsandboxedHookCommands; } export interface Config { /** * Additional pluggable task sources beyond the built-in Linear adapter * (which is always implicit). Each entry is a `SourceConfig` discriminated * by `kind`. The most common use is a `kind: "shell"` adapter that wires * an external system (Jira, plan-keeper, etc.) by pointing at command * templates that emit/consume JSON. * * The implicit Linear source can be turned off with the opt-out sentinel * `{ kind: "linear", enabled: false }` — useful for shell-only setups with * no Linear API key, where a failing Linear probe would otherwise take down * the whole queue. * * Per-source Zod validation runs at `buildSources` time — config.ts only * verifies the structural shape (array of objects with a string `kind`). */ sources?: SourceConfig[]; git?: { remote?: string; defaultBranch?: string; /** * Overrides the prefix groundcrew puts in front of the task id when it * names a worktree branch (`-`). Defaults to the OS * account username when unset. Must be a git-ref-safe, slash-free slug. */ branchPrefix?: string; }; workspace: { projectDir: string; /** * Parent directory all per-task worktrees are created under. Defaults * to `projectDir` when unset, so single-directory setups are unchanged. */ worktreeDir?: string; knownRepositories: Array; /** * When true, cmux workspace panels are titled with the task's ticket title * instead of the task id. Identity still keys on the task id, so renaming * is safe. Defaults to false. Only cmux paints a panel title; tmux ignores * it. */ useTaskTitleForPanelName?: boolean; }; defaults?: { hooks?: HookCommands; }; orchestrator?: { maximumInProgress?: number; pollIntervalMilliseconds?: number; sessionLimitPercentage?: number; }; agents?: { default?: string; /** * Explicit enabled agent set. Built-in keys (`claude`, `codex`, `cursor`, * `cursor-grok`, `pi`) merge over their presets, so `{ claude: {} }` enables * Claude with the shipped command/color/usage. Brand-new agent names must * supply enough fields to satisfy `validate()`. */ definitions?: Record; }; prompts?: { /** Inline initial prompt. Mutually exclusive with `promptFile`. */ initial?: string; /** * Path to a UTF-8 file whose contents become the initial prompt. Resolved * relative to the config file's directory; `~` is expanded; absolute paths * are used as-is. Mutually exclusive with `initial`. */ promptFile?: string; }; /** * Terminal session manager that hosts agent processes. Defaults to * `"auto"` — cmux on macOS when installed, else tmux. Set explicitly * to fail loudly when the chosen backend is missing. */ workspaceKind?: WorkspaceKindSetting; /** * Local isolation backend selector. Defaults to `"auto"` (macOS → * safehouse, Linux → sdx). `"none"` is an explicit unsandboxed escape * hatch — never selected implicitly. */ local?: { runner?: LocalRunnerSetting; /** * Network egress posture for local launches. Defaults to `"allowlisted"`. * With the safehouse runner, `"allowlisted"` uses Clearance and `"open"` * keeps the filesystem sandbox while running bare `safehouse` with * unrestricted network egress. `sdx`/`none` ignore this setting. */ networkEgress?: NetworkEgressSetting; /** * Safehouse sandbox tuning. Consumed only by the `safehouse` runner; other * runners ignore it. */ safehouse?: { /** * Optional Safehouse integrations turned on for every agent launched * under the safehouse runner, forwarded verbatim to * `safehouse --enable=` on the agent wrap. Each name layers * the matching optional sandbox profile on top of the deny-by-default * policy — e.g. `agent-browser` for the chrome-devtools MCP server's * Puppeteer/CDP browser, or `browser-native-messaging` for * `claude --chrome`. Names are safehouse feature slugs * (`[a-z0-9][a-z0-9-]*`); unknown names are rejected by safehouse at * launch. Defaults to none. */ enable?: string[]; }; /** * Host directories re-opened read-only inside the safehouse sandbox, for * toolchains the sandbox profile masks but does not re-open. * `~` is expanded. Defaults to tfenv's config root so `terraform`/`tfenv` * work in the sandbox; set your own list to add or replace entries. */ readOnlyDirs?: string[]; }; logging?: { /** * Append-mode log file destination. `log()` and `logEvent()` tee here * in addition to stdout, so a vanished workspace doesn't take the * evidence with it. Defaults to * `${XDG_STATE_HOME:-~/.local/state}/groundcrew/groundcrew.log`. */ file?: string; }; } /** * Strict shape after defaults are applied — what scripts work with. */ export interface ResolvedConfig { /** * Resolved list of additional task sources beyond the built-in Linear * adapter. Defaults to `[]` when the user omits `sources` in their config. * Each entry's per-adapter validation is the responsibility of `buildSources`, * not the config loader. */ sources: SourceConfig[]; git: { remote: string; defaultBranch: string; branchPrefix?: string; }; workspace: { projectDir: string; /** Resolved worktree root; unset means "use projectDir". */ worktreeDir?: string; /** Repository names only — derived; what name-matching consumers read. */ knownRepositories: string[]; /** Normalized full entries carrying any `projectDirOverride`/`provision`. */ repositories: KnownRepository[]; /** name -> resolved parent dir, only for entries that override projectDir. */ repositoryDirs?: Record; /** Present and true only when panels should use the task's ticket title. */ useTaskTitleForPanelName?: boolean; }; defaults: { hooks: HookCommands; }; orchestrator: { maximumInProgress: number; pollIntervalMilliseconds: number; sessionLimitPercentage: number; }; agents: { default: string; definitions: Record; }; prompts: { initial: string; }; /** * Terminal session manager. Always present — defaults to `"auto"`. * `auto` resolves to cmux when installed, else tmux. */ workspaceKind: WorkspaceKindSetting; /** * Local isolation selection. The user-facing `auto` is preserved here * so `localRunner.resolve()` can pick the platform default later — the * resolver is the only place that knows the host capabilities. */ local: { runner: LocalRunnerSetting; /** * Resolved network egress posture. Always present; defaults to * `"allowlisted"`. Only the safehouse runner consumes this today. */ networkEgress: NetworkEgressSetting; /** * Resolved Safehouse tuning. Always present; `enable` defaults to `[]`. * Only the safehouse runner consumes it. */ safehouse: { enable: readonly string[]; }; /** Resolved, `~`-expanded read-only sandbox dirs. Defaults to tfenv's config root. */ readOnlyDirs: string[]; }; logging: { file: string; }; } /** * Parent directory under which a repository's clone lives. The per-repo * `projectDirOverride` wins; otherwise repos sit under `projectDir`. */ export declare function repositoryBaseDir(config: ResolvedConfig, repository: string): string; /** * Parent directory all worktrees are created under, independent of where the * source repositories live. Falls back to `projectDir` when `worktreeDir` is * unset. */ export declare function worktreeBaseDir(config: ResolvedConfig): string; export type ConfigSourceKind = "env" | "project" | "xdg"; export interface ConfigSource { kind: ConfigSourceKind; filepath: string; } export interface LoadedConfig { config: Readonly; source: Readonly; } /** * Single source of truth for "is preLaunchEnv asking us to forward anything?" * * An empty array forwards zero names, so it is a uniform no-op in every * runner. The unsupported-runner guards (sdx, safehouse-prefixed cmd) only * fire when there is actually something to forward — rejecting `[]` only on * those runners would make an empty list accepted under `safehouse`/`none` * but fatal elsewhere, which is a worse asymmetry than what the helper * collapses. Centralized so all four call sites stay in lockstep. */ export declare function hasPreLaunchEnv(definition: Pick): boolean; /** * True when `name` is a built-in preset but not present in the enabled * definitions. Consumers use this to distinguish `agent-codex` when codex is * not enabled from an arbitrary unknown label like `agent-typo`. */ export declare function isBuiltInAgentNotEnabled(config: Pick, name: string): boolean; export declare function loadConfigWithSource(): Promise>; export declare function loadConfig(): Promise>; //# sourceMappingURL=config.d.ts.map