/** * Per-project configuration: a `farketari.ts` at the repository root of * the project being worked on. Three layers configure a run, with clear * precedence: CLI flag > farketari.ts (project) > farketari/ * template.json (template) > built-ins. * * The boundary to keep sharp: farketari.ts holds facts about HOW * FARKETARI WORKS ON THIS PROJECT (its template, discipline, models, * budgets, runners). Facts about the app itself (languages, modules, * headers) live in the project's own global configuration and never * here. * * The file is plain TypeScript loaded with Node's native type stripping * (Node >= 22.18), so it must stick to erasable syntax — type * annotations and type-only imports are fine, enums and namespaces are * not. `import type { FarketariConfig } from "farketari"` erases at * runtime, so the config file types itself even before farketari is * installed as a devDependency. */ import type { CommandCatalog } from "./commands.js"; import type { ModelPolicy, ModelRoster, WorkKind } from "./model-roster.js"; import type { PromptCatalog } from "./prompts.js"; import type { RepoLayout } from "./repo-layout.js"; import type { ClientPlatform, IterationBudgets, TddModeOverrides } from "./types.js"; /** The name of the config file, at the project's repository root. */ export declare const PROJECT_CONFIG_FILENAME = "farketari.ts"; /** Every field optional — an empty config is valid and means "all defaults". */ export interface FarketariConfig { /** * Where the example project this project follows can be found — makes * --example optional on every command. Either: * * - a GitHub directory URL, which is fetched into a local cache on * first use: * "https://github.com/redjolr/farketari/tree/main/stacks/nextjs-fastify/v1/example" * - a filesystem path, relative to this config file's directory unless * absolute ("." for an example that is its own example). * * The URL form is the normal one: the stacks live in farketari's * REPOSITORY and not in its npm package, so a project that installed * farketari from npm has no example on disk. Naming a branch pins it at * first fetch — see example-source.ts for refreshing and pinning. */ exampleProjectSource?: string; /** The project's name, used in reports and escalations. */ projectName?: string; /** The project's default mode; the --tdd flag still overrides per run. */ tddMode?: boolean; /** * Standing exceptions to tddMode, per participant ("mobile-app", * "backend") or per single test level ("mobile-app-acceptance", * "use-case-unit"); a level's own key wins over its participant's. * E.g. { "mobile-app": false, "mobile-app-acceptance": false }. */ tddModeOverrides?: TddModeOverrides; /** Partial overrides of the iteration budgets. */ budgets?: Partial; models?: { /** Which model backend serves each tier (partial: override one tier). */ roster?: Partial; /** Which tier serves each work kind (partial: override one kind). */ policy?: Partial; }; /** The template's platforms this project actually builds (narrowing only). */ clients?: ClientPlatform[]; /** Project conventions injected into EVERY run, before any --instructions. */ runInstructions?: string[]; /** Project-local prompt overrides: work kind -> file, relative to the config. */ prompts?: Partial>; /** Deep-merged over the template's layout — for the rare renamed directory. */ layout?: DeepPartial>; /** * Deep-merged over the template's command catalog. This is also where * the remote-runner command group ("NOT CONFIGURED" in the templates) * gets its per-project implementations. */ commands?: DeepPartial; /** Where run journals and checkpoints live; default ".farketari". */ journalDir?: string; trunk?: { /** The branch slices are committed and pushed to; default "main". */ branch?: string; /** Prefix for every slice commit message. */ commitPrefix?: string; /** How long to wait for a slice pipeline before stop-and-flag. */ pipelineTimeoutMinutes?: number; }; escalation?: { /** Where stop-and-flag reports go besides the console. */ webhookUrl?: string; }; } /** Typed identity helper so farketari.ts is compiler-checked. */ export declare function defineConfig(config: FarketariConfig): FarketariConfig; export type DeepPartial = { [K in keyof T]?: T[K] extends string | number | boolean | readonly unknown[] ? T[K] : DeepPartial; }; /** * The project's non-merging settings, carried in the deps for the roles * that consume them (journal, trunk, escalation, orienter). */ export interface ProjectSettings { projectName?: string; clients?: ClientPlatform[]; runInstructions: string[]; journalDir: string; trunk: { branch: string; commitPrefix?: string; pipelineTimeoutMinutes?: number; }; escalation: { webhookUrl?: string; }; } /** A config loaded, validated, and with every path resolved absolute. */ export interface LoadedProjectConfig { /** Absolute path of the farketari.ts that was loaded. */ configPath: string; config: FarketariConfig & { /** * exampleProjectSource resolved to an absolute path, when configured. * Everything downstream of loading takes THIS, never the raw source. */ examplePath?: string; /** Overridden prompt CONTENTS (files already read), not file paths. */ prompts?: Partial; }; settings: ProjectSettings; } /** * Find and load the project's farketari.ts, walking UP from startDir to * the filesystem root (the config belongs at the repo root, but commands * run from anywhere inside it). Returns null when no config exists; * throws with every problem found when one exists but is invalid. */ export declare function loadProjectConfig(startDir: string): Promise; /** Deep-merge an override into a base; arrays and scalars replace wholesale. */ export declare function deepMerge(base: T, override: DeepPartial | undefined): T;