import { getAgentsDir, getSolosquadConfigDir } from "./paths.js"; export interface Product { name: string; slug: string; github_org?: string; } export interface BriefingConfig { time: string; enabled?: boolean; } export interface WeeklyCronConfig { day: string; time: string; enabled?: boolean; } export interface GoalConfig { /** v0.4.0 — autonomous engine defaults. */ default_hours?: number; default_budget_usd?: number; dedicated_session_prefix?: string; } /** * Chief session configuration. v1.2.10 renamed the TS type `PmConfig` → * `ChiefConfig` to match the Chief rebrand. The `workspace.yaml` property key * stays `pm` (see {@link WorkspaceYaml.pm}) — it's a persisted contract across * every existing workspace, so renaming the key is deferred to a dedicated * migration (docs/prd/v1.2.10-consolidation-cleanup.md §A.3). */ export interface ChiefConfig { /** Cap per claude --print call. Workspace default. */ max_budget_usd?: number; /** Hard timeout per Chief invocation. */ invoke_timeout_seconds?: number; /** Real-time partial reply streaming (--include-partial-messages). */ include_partial_messages?: boolean; /** Cross-call prompt-cache friendliness (--exclude-dynamic-system-prompt-sections). */ exclude_dynamic_system_prompt_sections?: boolean; /** Per-session in-process mutex queue depth (chief-runner). */ mutex_queue_depth?: number; /** v0.3.0+: daily compaction-cron trigger time (HH:MM). */ compaction_time?: string; /** v1.3.0 Part A: git push approval-gate policy. */ git?: ChiefGitConfig; } /** * v1.3.0 Part A — git push approval-gate policy. Lives under `pm.git` in * workspace.yaml (the `pm` key is the persisted Chief-config contract, kept for * back-compat per v1.2.10 §4.2). Read at spawn time by the dev-confirm hook * (via env) and the bridge. */ export interface ChiefGitConfig { /** * Branches that may NEVER be pushed to directly — the hook blocks these * before the approval flow even starts (fail-closed, regardless of hook * error policy). Default `["main", "master", "develop"]`. */ protected_branches?: string[]; /** * When true, only feature branches reach the approval card; protected * branches are auto-blocked. Default true. (When false the protected list is * still honored, but the gate is otherwise advisory — reserved for future * relaxation; the hook treats it as true today.) */ require_feature_branch?: boolean; /** Minutes the approval card waits before timing out (= block). Default 30. */ approval_timeout_minutes?: number; } export interface WorkspaceYaml { version: string; display_name: string; persona?: string; /** IANA timezone (e.g. "Asia/Seoul"). v0.2.4+. Defaults applied at load time. */ timezone?: string; /** v0.2.4+: user-facing daily briefs. */ briefings?: { morning?: BriefingConfig; evening?: BriefingConfig; }; /** v0.2.4+: background crons that feed into the briefs. */ background_routines?: { signal_scan?: BriefingConfig; experiment_check?: BriefingConfig; weekly_review?: WeeklyCronConfig; }; /** * v0.3.0+: Chief session configuration. Property key kept as `pm` for * back-compat with existing workspace.yaml files (v1.2.10 §4.2/§7). */ pm?: ChiefConfig; /** v0.4.0+: autonomous goal engine configuration. */ goal?: GoalConfig; /** v0.5.0+: 3-tier skill loader ordering. */ skill_loader?: SkillLoaderConfig; /** v0.5.0+: author-loop budget envelope. */ author?: AuthorConfig; /** v0.6.0+: spawn-assembler token cap (§2.2 P1 #4). */ spawn?: SpawnConfig; /** v0.6.0+: FTS5 cold archive retention + compression (§4.7). */ archive?: ArchiveConfig; /** v0.6.0+: fs.watch external-edit reload policy (§10.5). */ fs_watch?: FsWatchConfig; /** v0.6.0+: migration budget cap (§2.2 P0 #2). */ migration?: MigrationBudgetConfig; /** v0.8.2+: workspace-wide dev_capability master toggle + bash denylist. */ dev_capability?: DevCapabilityConfig; /** v1.2.0+: messenger-wide policies (owner-only gate, install mode, thread budget). */ messenger?: MessengerWorkspaceConfig; created_at: string; last_migrated_to?: string; } /** * v0.8 §3.6 (broadcast fields) + v1.2 §13.3 (discord/slack subsections) — * Workspace-level messenger policies. Per-org / per-guild binding still * lives at `//config.yaml` (v0.2.2+); this section is for * policies that apply across every adapter instance. */ export interface MessengerWorkspaceConfig { /** v0.8 §3.6 — broadcast channel opt-in (single designated owner bot). */ broadcast_enabled?: boolean; broadcast_owner_handle?: string | null; broadcast_channel?: string; /** v1.2 §4.5 / §3 / §9.2 — Discord-specific policies. */ discord?: DiscordWorkspaceConfig; /** v1.2.x — Slack-specific policies (mirror of discord). */ slack?: SlackWorkspaceConfig; } export interface DiscordWorkspaceConfig { /** * v1.2 §4.5 — When `true` (fresh install default), Chief only processes * messages whose `author.id === user.yaml.messenger_user_id`. v1.0.x * upgrades land with `false` (preserves v1.0.2 channel-ACL-only behavior). */ owner_only?: boolean; /** * v1.2 §3 — `oauth_invite` (default fresh install) auto-synthesizes the * invite URL via `solosquad discord invite-url`. `byo_manual` skips URL * synthesis — user pastes their own. Migration defaults existing users * to `byo_manual` (their current flow). */ install_mode?: "oauth_invite" | "byo_manual"; /** * v1.2 §9.2 — Token budget per workflow thread. Once exceeded, Chief * prompts the user to start a fresh thread with a summary link back. */ thread_token_budget?: number; } export interface SlackWorkspaceConfig { owner_only?: boolean; } export declare const DEFAULT_DISCORD_WORKSPACE_CONFIG: Required>; /** * Resolve Discord workspace config — fresh-install defaults from * DEFAULT_DISCORD_WORKSPACE_CONFIG. Migrations write explicit values for * upgraded workspaces so the resolver never has to guess based on workspace * age. */ export declare function loadDiscordWorkspaceConfig(workspace?: string): Required>; /** * v0.8.2 §3.3 — workspace-level master toggle for engineering dev actions. * * - `enabled` (default `true`): when `false`, every SKILL is forced into * read-only mode regardless of its frontmatter `dev_capability: true`. Used * for sandbox / client-confidential repos / emergency-stop. * - `require_push_confirmation` (default `true`, always `true` — the schema * accepts `false` but the loader rejects it): every `git push` / `gh pr * merge` / `gh pr close` waits for a user confirmation event before the * bash invocation runs. * - `bash_denylist`: workspace-strict denylist. SKILLs cannot override it — * merged on top of any per-SKILL `dev_permissions.bash.denied`. */ export interface DevCapabilityConfig { enabled?: boolean; require_push_confirmation?: boolean; bash_denylist?: string[]; } export declare const DEFAULT_DEV_CAPABILITY_DENYLIST: readonly string[]; export declare const DEFAULT_DEV_CAPABILITY_CONFIG: Required; /** * Resolve dev_capability config — applies v0.8.2 defaults when workspace.yaml * is absent or lacks a `dev_capability` section. `require_push_confirmation` * is normalized to `true` (false is rejected per §3.3 박제 정책 — always true). */ export declare function loadDevCapabilityConfig(workspace?: string): Required; /** Pure resolver — exposed for spawn-assembler hot-path (avoid re-reading yaml). */ export declare function resolveDevCapabilityConfig(partial: DevCapabilityConfig | undefined): Required; /** v1.2.9 §E — read the dev-capability master toggle (default ON). */ export declare function isDevCapabilityEnabled(workspace?: string): boolean; /** v1.3.0 Part A — protected branches the push gate never lets through. */ export declare const DEFAULT_PROTECTED_BRANCHES: readonly string[]; export declare const DEFAULT_CHIEF_GIT_CONFIG: Required; /** * v1.3.0 Part A — resolve the git push approval-gate policy from * `workspace.yaml.pm.git`, applying defaults when absent. Used by the * dev-confirm bridge + hook (the hook receives the resolved values via spawn * env, so it never re-reads yaml on its hot path). */ export declare function loadChiefGitConfig(workspace?: string): Required; /** Pure resolver — exposed so callers can resolve without re-reading yaml. */ export declare function resolveChiefGitConfig(partial: ChiefGitConfig | undefined): Required; /** * v1.2.9 §E — flip the dev-capability master toggle in workspace.yaml and * persist it. Returns the PREVIOUS value (so callers can report "already on"). * Backs the `/grant` (enabled=true) and `/revoke` (enabled=false) commands. */ export declare function setDevCapabilityEnabled(enabled: boolean, workspace?: string): boolean; /** * v0.6 §10.5 — fs.watch reload policy. The watcher itself lives in v0.6 S6.A * (`src/bot/fs-watcher.ts` / `reload-policy.ts`); the migration only ensures * the workspace.yaml exposes the defaults so the watcher boots without * extra prompts on first run. */ export interface FsWatchConfig { mode?: "auto" | "prompt" | "manual"; git_only?: boolean; } export declare const DEFAULT_FS_WATCH_CONFIG: Required; /** * Resolve fs-watch config — falls back to v0.6 defaults when workspace.yaml * is absent or lacks an `fs_watch` section. The reload-policy module reads * this to decide auto/prompt/manual behavior on each fs-watcher event. */ export declare function loadFsWatchConfig(workspace?: string): Required; /** * v0.6 §2.2 P0 #2 — migration budget cap. `budget_usd` is the hard ceiling * for *one* `solosquad migrate --apply` invocation. The 0.5.0→0.6.0 step is * the first migration to honor it; LLM fallback for ledger redestination * checks `recordAuthorCost`-style cumulative spend against this cap and * stops rather than ballooning past it. */ export interface MigrationBudgetConfig { budget_usd?: number; } export declare const DEFAULT_MIGRATION_BUDGET_USD = 5; /** * v0.6 §2.2 P1 #4 — 8-layer spawn context cap. * * When the assembled context approaches the model token limit, the assembler * drops lower-priority layers in the order documented in * `src/bot/spawn-assembler.ts`. Default 80,000 tokens — leaves headroom * inside Claude Sonnet/Opus 200k context for the actual conversation. */ export interface SpawnConfig { max_context_tokens?: number; } export declare const DEFAULT_SPAWN_MAX_CONTEXT_TOKENS = 80000; export interface SkillLoaderConfig { /** Tier ordering — higher index = higher priority. v0.5 default: [org, user, bundle]. */ tiers: ("org" | "user" | "bundle")[]; } export interface AuthorConfig { budget?: { daily_usd?: number; weekly_usd?: number; per_call_usd?: number; }; /** What to do when a cap is hit. v0.5 default: "pause". */ on_cap_action?: "pause" | "warn" | "block"; } export interface ArchiveConfig { /** * v0.6 §4.7 — rows older than this in archive.sqlite are deleted by the * nightly retention pass. Default 365. */ retention_days?: number; /** * v0.6 §4.7 — when true, the retention pass writes * `archive-.zst` snapshots before DELETE; default false. */ compress_before_delete?: boolean; } export declare const DEFAULT_ARCHIVE_CONFIG: Required; /** * Resolve archive config — falls back to the v0.6 defaults when * workspace.yaml is absent or lacks an `archive` section. */ export declare function loadArchiveConfig(workspace?: string): Required; /** v0.2.4 defaults — used both at init and as fallbacks when fields are missing. */ export declare const DEFAULT_WORKSPACE_SETTINGS: { readonly timezone: "Asia/Seoul"; readonly briefings: { readonly morning: { readonly time: "08:00"; readonly enabled: true; }; readonly evening: { readonly time: "18:00"; readonly enabled: true; }; }; /** * @deprecated v0.8.5 — `signal-scan`, `experiment-check`, `weekly-review` * crons were removed from the live scheduler. This constant is preserved * solely so the historical `0.2.1-to-0.2.4.ts` migration script (immutable * per AGENTS.md) continues to compile and reproduce the schema state it was * written for. `init.ts` no longer writes these defaults, and * `applyWorkspaceDefaults` no longer injects them at load time. */ readonly background_routines: { readonly signal_scan: { readonly time: "12:00"; readonly enabled: true; }; readonly experiment_check: { readonly time: "16:00"; readonly enabled: true; }; readonly weekly_review: { readonly day: "sunday"; readonly time: "20:00"; readonly enabled: true; }; }; /** v0.3.0 (PM mode) defaults; compaction_time added in v0.3.0. */ readonly pm: { readonly max_budget_usd: 5; readonly invoke_timeout_seconds: 300; readonly include_partial_messages: true; readonly exclude_dynamic_system_prompt_sections: true; readonly mutex_queue_depth: 4; readonly compaction_time: "23:00"; }; }; /** Merge a partial WorkspaceYaml with defaults for v0.2.4+ fields. */ export declare function applyWorkspaceDefaults(ws: WorkspaceYaml): WorkspaceYaml; export interface OrgProduct { name: string; slug: string; description?: string; repos?: string[]; } export interface OrgYaml { name: string; slug: string; provider: "github" | "gitlab" | "gitea" | "local"; remote_url?: string | null; homepage?: string | null; products?: OrgProduct[]; description?: string; /** * v1.2 §4.1 — org-level Chief display name (e.g. "Hermes", "Atlas"). * One Chief per org; init/add-org prompts for it and recommends the same * string for the Discord Developer Portal Bot name. Missing → runtime * falls back to the literal "Chief". */ chief_name?: string; created_at: string; } export interface RepoYaml { slug: string; name: string; /** * @deprecated v1.0.1 — repo `role` field is no longer prompted at * registration or read for routing. Existing yamls keep the value for * backward compat; new yamls default to "main" silently. Multi-repo * intent resolution is now handled by (a) `@` mention syntax in * user messages (`src/bot/mention-parser.ts`), (b) PM clarifying * question when ambiguous, (c) workflow stage `target_repo` for * explicit declaration. Hard removal scheduled for v2.0 per * `docs/policy/schema-stability.md` schema read-window policy. */ role: "main" | "frontend" | "backend" | "data" | "infra" | "docs" | "unknown"; language?: string; linked_org: string; remote_url?: string | null; products?: string[]; notes?: string; registered_at: string; /** * v0.9.1 — path-reference mode (workspace ↔ repo 관계 재설계). * When set, the workspace's `//repositories//` tree * does not exist (or is empty) — the actual repo lives at this absolute * path on disk. The agent's spawn cwd resolves to this path via * `src/util/paths.ts:resolveRepoCwd`. * * Backward-compat: when omitted, behavior falls back to the legacy * `//repositories//` tree (move/copy modes from v0.8.x). * * Documented: docs/plan/v0.9.1-workspace-repo-relationship.md §7 (path-reference * is the v0.9+ default; legacy tree stays permanently supported). */ path?: string; } export declare function loadEnv(dir?: string): Record; export declare function saveEnv(updates: Record, dir?: string): void; export declare function loadWorkspaceYaml(workspace?: string): WorkspaceYaml | null; export declare function saveWorkspaceYaml(doc: WorkspaceYaml, workspace?: string): void; export declare function loadOrgYaml(orgDir: string): OrgYaml | null; export declare function saveOrgYaml(orgDir: string, doc: OrgYaml): void; /** List all organization directories inside a workspace. */ export declare function listOrganizations(workspace?: string): { slug: string; path: string; yaml: OrgYaml; }[]; export declare function loadRepoYaml(repoDir: string): RepoYaml | null; export declare function saveRepoYaml(repoDir: string, doc: RepoYaml): void; export declare function loadProducts(dir?: string): Product[]; export declare function saveProducts(products: Product[], dir?: string): void; /** Messenger channel config (per-org on v0.2.2+, per-product on v0.1.x). */ export declare function loadMessengerConfig(orgOrProductDir: string, platform: string): Record; /** Guard: MESSENGER must be a single platform in v0.2.2+. */ export declare function normalizeMessenger(raw: string | undefined): string; /** Read silently — helps callers that still expect the old agentsDir path. */ export { getAgentsDir, getSolosquadConfigDir };