import type { IntakeApprovalMode } from "./issue-order.js"; export type { IntakeApprovalMode }; /** The accepted intake-approval modes (#448), in one place so the resolver's * allowlist and the `IntakeApprovalMode` union cannot drift apart. */ export declare const INTAKE_APPROVAL_MODES: readonly ["code-org", "reporter", "off"]; /** The active profile name, or "" for the default (unprofiled) store. Selected * by the global `--profile` flag (which exports SHIPFLOW_PROFILE) or the env * var directly. */ export declare function activeProfile(): string; /** The effective config directory. Each profile is an isolated * credentials/projects/config store under `/profiles/`, so one * machine can stay signed into several tenants at once. The default (no * profile) keeps using `` so existing single-tenant setups are unchanged. */ export declare function configDir(): string; /** Names of the profiles that have a config dir under `/profiles`. The * default (unprofiled) store is not a profile and is reported separately. */ export declare function listProfiles(): string[]; /** Reads a profile's stored credentials without switching the active profile — * for listing tenants across profiles. Pass "" for the default store. */ export declare function credentialsForProfile(name: string): StoredCredentials | null; export interface StoredConfig { apiUrl?: string; defaultOrg?: string; apiKey?: string; autoIssue?: boolean; liveReload?: boolean; requireCi?: boolean; mergePolicy?: MergePolicy; maxFixAttempts?: number; wipLimit?: number; stalePrHours?: number; bugHunt?: boolean; bugHuntCap?: number; requireReview?: boolean; gitName?: string; gitEmail?: string; signoffOwner?: string; conflictSweep?: boolean; /** Intake gate (#448): who may release `needs-reporter-approval`. * `code-org` (default) = the issue waits for someone with repo write access, * which GitHub itself enforces — an outside account cannot remove a label. * `reporter` additionally accepts the FILER's own confirmation, which is only * meaningful for chat-filed issues where ShipFlow knows the exact reporter * identity (bug_triage.go: "Only the original reporter decides"). * `off` disables the gate entirely — new arming stops AND issues already * carrying the label become claimable again (the mode is threaded into * `isActionableForPickup`), which is the only REPO-WIDE way back from a * mass-arming event: no code path removes the label automatically (tracked * in #473). Per issue, a maintainer removing the label is the release, and * it sticks — arming is a one-time event (`decideIntakeGate`). * * Stored as a free string because config files are hand-edited; the resolver * narrows it to `IntakeApprovalMode` and falls back to `code-org`. */ intakeApproval?: string; loopWorkerModel?: string; intentGate?: IntentGateMode; pickupScope?: PickupScope; cliDriftPollSeconds?: number; appSlug?: string; } /** How far the loop may drive a PR toward merge without a human. */ export type MergePolicy = "manual" | "auto-on-green" | "auto-timeout"; /** The whole merge-policy vocabulary — exported (issue #669) so every surface * that accepts a policy validates against THIS list instead of re-typing it: * `resolveMergePolicy()` below, and the `--policy` guards on `pr ready` / * `pr automerge`, which also render their `--policy` help text from it. */ export declare const MERGE_POLICIES: MergePolicy[]; /** Which issues the loop's `issue next` may claim (issue #600). */ export type PickupScope = "assigned" | "all"; export declare const PICKUP_SCOPES: PickupScope[]; /** Which body signals park a PR on the reporter (issue #471). */ export type IntentGateMode = "strict" | "trusted"; export declare const INTENT_GATE_MODES: IntentGateMode[]; export interface StoredCredentials { jwt: string; refreshToken: string; tenantId: string; org: string; expiresAt: number; } export interface ProjectCacheEntry { projectId: string; projectName: string; org: string; tenantId: string; } type ProjectCache = Record; export declare const loadConfig: () => StoredConfig; export declare const saveConfig: (c: StoredConfig) => void; export declare const clearConfig: () => void; export declare const loadCredentials: () => StoredCredentials | null; export declare const saveCredentials: (c: StoredCredentials) => void; export declare const clearCredentials: () => void; /** Auth options that wire transparent token refresh into a ShipFlowClient: * the stored refresh token, plus a callback that persists rotated tokens back * to credentials.json. Spread into `new ShipFlowClient({..., ...refreshOpts(creds)})`. */ export declare function refreshOpts(creds: StoredCredentials): { refreshToken: string; onRefreshed: (t: { token: string; refreshToken: string; expiresAt: number; }) => void; }; export declare const loadProjectCache: () => ProjectCache; export declare const saveProjectCache: (c: ProjectCache) => void; /** Stable key for a repo path (resolves symlinks / paths to the git work tree). */ export declare function projectCacheKeyForRepoPath(absRepoRoot: string): string; /** Parse a human boolean ("true"/"1"/"on"/"yes"). */ export declare function parseBool(v: string | undefined): boolean; /** Whether to auto-create an issue for off-issue feature work (vs. asking). * Env SHIPFLOW_AUTO_ISSUE wins over the stored config; defaults to false. */ export declare function resolveAutoIssue(): boolean; /** Effective live-reload preference: env > stored config > undefined (undecided, * so the SessionStart hook asks once). */ export declare function resolveLiveReload(): boolean | undefined; /** Parse a non-negative integer; fall back when absent/invalid. */ export declare function parseIntOr(v: string | number | undefined, fallback: number): number; /** Boolean spellings `config set` accepts, true then false. */ export declare const BOOL_TRUE_WORDS: string[]; export declare const BOOL_FALSE_WORDS: string[]; /** Parse a boolean typed at `config set`, or throw naming every accepted * spelling. Accepts the same truthy words as `parseBool`; the difference is * that a value matching neither list is an error, not a `false`. */ export declare function parseBoolStrict(key: string, v: string): boolean; /** Parse a non-negative whole number typed at `config set`, or throw. Digits * only: this rejects `abc` (which `parseIntOr` turns into the default), `1e3` * and `3.7` (which `parseInt` truncates to `1` and `3`), and `-1`. */ export declare function parseIntStrict(key: string, v: string): number; /** Require CI green before a PR is "advanced" (env > config > true). */ export declare function resolveRequireCi(): boolean; /** Effective merge policy (env > config > "manual"). Invalid values fall back. */ export declare function resolveMergePolicy(): MergePolicy; /** Max CI-fix attempts on one PR before escalating (env > config > 3). */ export declare function resolveMaxFixAttempts(): number; /** Max concurrent open PRs before the loop stops admitting work (env > config > 10). */ export declare function resolveWipLimit(): number; /** Hours before a parked (green, unreviewed) PR is "stale" (env > config > 48). */ export declare function resolveStalePrHours(): number; /** Run the empty-queue bug sweep + auto-file issues (env > config > true). */ export declare function resolveBugHunt(): boolean; /** Max new issues the bug sweep may file per loop run (env > config > 5). */ export declare function resolveBugHuntCap(): number; /** Route every issue + PR through the reviewer first (env > config > true). */ export declare function resolveRequireReview(): boolean; /** Pickup scope (env > config > **"assigned"**, issue #600): `assigned` — the * loop claims only issues assigned to the account running it (assignment IS * the queueing gesture); `all` — the pre-#600 repo-wide pickup. Unknown values * fall back to `assigned` (the narrow direction). */ export declare function resolvePickupScope(): PickupScope; /** Intent-gate mode (env > config > **"strict"**). Unknown values fall back * to strict — the gate must never fail OPEN on a typo. */ export declare function resolveIntentGateMode(): IntentGateMode; /** Repo-wide conflict sweep over OTHER authors' PRs (env > config > **false**). * Defaults OFF: the sweep hands the loop a third-party branch to check out and * run, so it must be chosen, never inherited from a CLI/plugin upgrade. */ export declare function resolveConflictSweep(): boolean; /** Effective intake-approval mode. Env wins, then stored config, else `code-org` * — the safe default: an unset knob must not disable a control. * * Only `off` and `code-org` change GitHub-intake behavior today. `reporter` is * accepted (so the knob is forward-compatible) but the reporter-side clearing * path is chat-only and has no GitHub-intake implementation yet (tracked in * #473), so today it gates exactly like `code-org`. Kept in the allowlist so * the knob is forward-compatible rather than rejecting a value it will later * honor. */ export declare function resolveIntakeApproval(): IntakeApprovalMode; /** Sign-off owner the loop names on escalations (env > config > undefined — * the escalate command then falls back to the issue author). A leading @ is * tolerated and stripped so both "gemscng" and "@gemscng" work. */ export declare function resolveSignoffOwner(): string | undefined; /** Model for loop WORKER dispatches (env > config > undefined — the loop then * keeps the host's default per dispatch). Freeform trimmed string; no * allowlist because valid model names are host-specific. Blank values fall * through, so `SHIPFLOW_LOOP_WORKER_MODEL=""` doesn't mask the stored config. */ export declare function resolveLoopWorkerModel(): string | undefined; /** Seconds to poll npm for a just-published CLI version before continuing * degraded (env > config > 180). Covers the measured ~62s publish lag with * headroom; the loop NEVER halts on this timer — it warns loudly and runs on. */ export declare function resolveCliDriftPollSeconds(): number; /** Where the effective trusted slug came from. Carried with the value so the * reader's fail-stuck diagnostic can say WHY it expected what it expected — * the difference between a diagnosable stuck gate and an inexplicable one. */ export type AuditAuthorSlugSource = "env SHIPFLOW_APP_SLUG" | "env GITHUB_APP_SLUG" | "config app-slug" | "contract default"; export interface ResolvedAuditAuthorSlug { /** Normalised (trimmed, lower-cased, `[bot]` stripped) and never empty. */ slug: string; source: AuditAuthorSlugSource; /** Set when a configured value was REFUSED as malformed and the contract * default was used instead — so the operator sees their typo, not silence. */ rejected?: string; } /** Is `v` usable as an App slug, once normalised? Exported for `config set`, * which refuses a bad value at the point the operator can still fix it. */ export declare function isValidAppSlug(v: string | undefined): boolean; /** * The ONE bot identity trusted to author an intent-gate clearance record, for * THIS deployment: `SHIPFLOW_APP_SLUG` > `GITHUB_APP_SLUG` (the server's own * var, so a machine configured for the server needs nothing extra) > stored * `app-slug` > the contract default. * * `GITHUB_APP_SLUG` is read second, not first, so an operator can pin the CLI * independently on a host whose environment already names a *different* App. */ export declare function resolveIntentGateAuditAuthorSlug(): ResolvedAuditAuthorSlug; export declare function resolveApiUrl(flagUrl?: string): string; /** JWT first, API key fallback. */ export declare function resolveAuthToken(): { token: string; kind: "jwt" | "apiKey"; } | null; /** Backward-compat: existing call sites that imported resolveApiKey still work. */ export declare function resolveApiKey(): string | undefined; //# sourceMappingURL=config.d.ts.map