export declare function isSafeRelPath(p: string): boolean;
export type ProjectConfig = Record<string, string>;
export interface Component {
    name: string;
    repo: string;
    relativePath?: string;
    worktreeSetupCommand?: string;
    commands?: Record<string, string>;
    config?: Record<string, string>;
}
export type CommandStepStructural = {
    name: string;
    type: "command";
    component: string;
    command: string;
    phase?: number;
    expect?: "success" | "failure";
    timeout?: number;
    optional?: boolean;
    label?: string;
    optionalLabel?: string;
    description?: string;
};
export type CommandStepComponentRun = {
    name: string;
    type: "command";
    component: string;
    run: string;
    phase?: number;
    expect?: "success" | "failure";
    timeout?: number;
    optional?: boolean;
    label?: string;
    optionalLabel?: string;
    description?: string;
};
export type CommandStepFreeform = {
    name: string;
    type: "command";
    run: string;
    phase?: number;
    expect?: "success" | "failure";
    timeout?: number;
    optional?: boolean;
    label?: string;
    optionalLabel?: string;
    description?: string;
};
export type CommandStep = CommandStepStructural | CommandStepComponentRun | CommandStepFreeform;
export type LlmReviewStep = {
    name: string;
    type: "llm-review";
    prompt: string;
    role?: string;
    phase?: number;
    expect?: "success" | "failure";
    timeout?: number;
    optional?: boolean;
    label?: string;
    optionalLabel?: string;
    description?: string;
};
export type AgentQaStep = {
    name: string;
    type: "agent-qa";
    prompt: string;
    role?: string;
    component?: string;
    phase?: number;
    timeout?: number;
    optional?: boolean;
    label?: string;
    optionalLabel?: string;
    description?: string;
};
export type HumanSignoffStep = {
    name: string;
    type: "human-signoff";
    prompt: string;
    label: string;
    phase?: number;
    optional?: boolean;
    optionalLabel?: string;
    description?: string;
};
export type InlineVerifyStep = CommandStep | LlmReviewStep | AgentQaStep | HumanSignoffStep;
export interface InlineWorkflowGate {
    id: string;
    name: string;
    dependsOn?: string[];
    content?: boolean;
    injectDownstream?: boolean;
    optional?: boolean;
    manual?: boolean;
    metadata?: Record<string, string>;
    verify?: InlineVerifyStep[];
}
export interface InlineWorkflowDef {
    id: string;
    name: string;
    description?: string;
    hidden?: boolean;
    gates: InlineWorkflowGate[];
}
export interface ConfigDirectoryEntry {
    path: string;
    types: string[];
}
export interface SandboxTokenEntry {
    key: string;
    enabled: boolean;
    /** Only used at API ingress (PUT redaction merge). Not persisted to disk. */
    value?: string;
}
/**
 * Project config store persisted to .bobbit/config/project.yaml.
 *
 * Two coexisting shapes:
 *   1. Legacy flat string map (`build_command`, `test_command`, …) — preserved
 *      for back-compat. Reads continue to work after migration.
 *   2. Structured fields (`components: []`, `workflows: {}`, plus the five
 *      Native-YAML fields above) — emitted as native YAML on save.
 *
 * The store keeps a back-compat read surface for the migrated fields:
 * `get("config_directories")` etc. return the JSON-stringified form
 * computed on demand from the typed side-tables. Internal callers should
 * prefer the typed accessors (`getConfigDirectories()`, …).
 *
 * Auto-saves on every set/remove. Handles missing file gracefully.
 */
export declare class ProjectConfigStore {
    private data;
    /** Structured side-table — components[] and workflows{} from the same yaml file. */
    private components;
    private workflows;
    private configDirectories;
    private sandboxTokens;
    /** Track whether each migrated field was explicitly present on disk. */
    private present;
    /** Set when load() found legacy (string-encoded) shapes — triggers next save() to rewrite native. */
    private dirty;
    private readonly configFile;
    constructor(configDir: string);
    /** True iff the loaded file contained a legacy JSON-string or numeric-string
     *  shape for any of the migrated fields. Cleared by save(). */
    isDirty(): boolean;
    private load;
    private loadMigrated;
    private save;
    /** Compute the back-compat flat-string view including JSON-stringified migrated values.
     *  Sandbox token values are included so legacy callers can still read them; save()
     *  always strips values from the on-disk YAML. */
    private flatLegacyView;
    get(key: string): string | undefined;
    set(key: string, value: string): void;
    private setMigratedFromString;
    private removeMigrated;
    remove(key: string): void;
    getAll(): ProjectConfig;
    /** Returns a copy of the built-in defaults. */
    getDefaults(): Record<string, string>;
    /** Returns all fields with defaults applied for any missing values.
     *  Re-reads from disk to pick up changes made by external processes (e.g. setup wizard agent).
     */
    getWithDefaults(): Record<string, string>;
    getConfigDirectories(): ConfigDirectoryEntry[];
    setConfigDirectories(dirs: ConfigDirectoryEntry[]): void;
    getSandboxTokens(): SandboxTokenEntry[];
    setSandboxTokens(tokens: SandboxTokenEntry[]): void;
    /** Returns a defensive clone of the named component's `config` map (or {} if missing/unknown). */
    getComponentConfig(name: string): Record<string, string>;
    /** Reads `components[name].config.qa_max_duration_minutes`, parses with Number(), falls back to 10. */
    getQaMaxDurationMinutes(componentName: string): number;
    /** True iff any component has a non-empty `config.qa_start_command`. */
    isQaConfiguredOnAnyComponent(): boolean;
    /** Returns all components declared in project.yaml, in declared order. */
    getComponents(): Component[];
    /** Lookup a component by name. */
    getComponent(name: string): Component | undefined;
    /** Group components by their `repo` value. */
    componentsByRepo(): Map<string, Component[]>;
    /** Distinct repo names ("." for single-repo). */
    repoNames(): string[];
    /** True iff any component has `repo !== "."`. */
    isMultiRepo(): boolean;
    /** True iff the component has no `commands` map (or it's empty). */
    isDataOnly(c: Component): boolean;
    /** Replace the components[] array. Persists immediately. */
    setComponents(components: Component[]): void;
    /** Returns the inline workflows map (or undefined). */
    getWorkflows(): Record<string, InlineWorkflowDef> | undefined;
    /** Replace the workflows{} map. Persists immediately. */
    setWorkflows(workflows: Record<string, InlineWorkflowDef> | undefined): void;
    /** Reload from disk — used by the migration to pick up out-of-band writes. */
    reload(): void;
}
//# sourceMappingURL=project-config-store.d.ts.map