/** * Shared .faf assembly pipeline. The full slot-filling flow used to build a * FRESH .faf for a directory: * detect → interrogate → slotignore inactive categories → Turbo-Cat → Relentless * * Used by BOTH `faf auto` (new-file path) and `faf git` (cloned repo) so they * can't drift — `faf git` previously ran detectStack alone (~33% vs ~75%). */ /** Build a fresh .faf for `dir` using the full slot-filling pipeline. */ export declare function assembleFreshFaf(dir: string): Record; /** * Update an EXISTING .faf for `dir`. Existing values win; interrogated → detected * → Turbo-Cat (formats) → Relentless (6 W's) fill only the remaining empties. * This is exactly the chain `faf auto` runs on an existing file — exported so * consumers (faf-mcp's faf_auto) compose it instead of re-deriving it and * drifting (they used to merge assembleFreshFaf's slotignore'd output over the * existing file, losing interrogated facts such as a docker-compose Redis). * * Shape: `existing` must be a mapping (an empty document, null, reads as `{}`); * a scalar or a list throws rather than being spread into character keys. A * non-null scalar `project:` (older writers stored `project: `) is lifted * to `{ name: String(value) }`, so the name is kept and the rest can be filled. * * Words typed into a slot — a typed none (`None` / `N/A` / `not applicable`) * or any other placeholder word (`unknown`, `"null"`), any case — are an * empty slot: they score 0 until filled. * - In a tech slot (every slot but the 6Ws) a repo fact fills it: "if it's * a fact, fill the slot". With no fact the words stay exactly as the file * has them, comment included; faf never writes `''` over them. * - In a tech slot the file's app-type (`project.type`) leaves out, the * app-type's decision is the fact: with no repo fact, the slot becomes * `slotignored` in place of the words, an empty value or a * placeholder. A real value there is kept. `slotignored` comes only from * the app-type — in a slot the app-type uses, it is never written, even * when detection reads the repo as another type; such a slot gets the * repo's fact for it instead (`runtime: Go` from go.mod), or keeps what * it had. A `project.type` faf does not know decides nothing, and neither * does the `library` detection falls back to when the repo has no * classifying signal. * - In a 6W (`human_context.*`) the words are the person's: auto never * replaces them (`faf go` asks). * A slot that says `slotignored` under either of its names (`stack.db` for * `stack.database`) gets no detected value under either. A slot the file * names only by its Mk4 name (`stack.db`) keeps that name: faf adds no * on-wire twin (`stack.database`), and a repo fact fills the name the file * uses. * * The result is marked as a fill: writeFaf leaves a node with an anchor that * an alias reads as written (`frontend: &x None` with `ui_library: *x`) — * filling it would change every alias — and reports it like a kept alias. */ export declare function updateExistingFaf(dir: string, existing: Record): Record; /** Fill empty slots in `target` with values from `source`. `target` wins * when its slot is non-empty. Empty here is '', null, undefined, an empty * list or mapping — this is what lets interrogated/detected values overwrite * the empty-string defaults that detectStack writes to human_context. * * Typed words — a typed none (`None`, `N/A`, `not applicable`) or any other * placeholder word (`unknown`, `"null"`), any case — are an empty slot, but * only a fact replaces them: in a tech slot (every slot but the 6Ws, under * either of its names) a source value that is real content — not empty, not * a placeholder, not `slotignored` — fills it. Anything else leaves the * words exactly as they are: no fact, a 6W (`human_context.*`, the * person's), or a place that is not a slot. * * A `_meta` the target already carries (the user's own) is never filled * over: faf's runtime `_meta` from `source` is merged only into a target * without one. */ export declare function fillEmpties(target: Record, source: Record): Record;