import type { Ground } from "./ground.js"; import { type Profile } from "./profiles.js"; /** * What the home agent says the first time a cockpit opens. * * The greeting itself is written by the model — what this module produces is * the BRIEF it writes from: the ground `readGround` recognised, the profiles * this cockpit actually holds, the single offer that fits, and the copy rules. * * Why a brief rather than the finished sentence. The introduction has to * describe the roles that exist HERE, including ones the user minted after this * file was written, so it cannot be prose in a prompt: hardcoding the list * reproduces the drift `CLAUDE.md` documents half a dozen times — a description * that outlives the thing it describes. Reading the vault means a profile added * later appears with nobody editing any text, and a role this cockpit does not * have can never be promised. * * Why the facts are handed over rather than looked up. The agent could read the * directory itself, but then the first message costs a fistful of tool calls * and can disagree with what shadok saw. `ground.ts` already answered the * question deterministically, for free — so the brief states the answer and * forbids contradicting it. Reading the project properly is what the greeting * OFFERS; doing it before offering would spend the user's quota on a question * nobody asked. * * The register is the whole design. A greeting that misreads the project costs * trust at the exact moment there is none in reserve, so only the register * built on evidence is allowed to be specific, and the one built on nothing * offers the single thing that is true everywhere (`UNIVERSAL_OFFER`) instead of * inventing a stack. */ export type GreetingRegister = "specific" | "informed" | "honest"; export interface GreetingOffer { /** The one thing offered, phrased as the outcome the person gets. */ offer: string; /** The evidence it rests on — always a marker that was actually seen. */ because: string; } /** * The offer that needs no detection at all. It works in a Rails app, an Xcode * project, a folder of marketing copy and a repository cloned five minutes ago; * it delivers something in two minutes; and it is already the lead's stated * KNOW job, so it promises nothing new. That is what makes it the honest * register's answer: the alternative there is guessing. */ export declare const UNIVERSAL_OFFER: GreetingOffer; /** * The one offer that fits what was found, or null when nothing does. * * Deliberately short, and deliberately not a table of one entry per stack: an * offer earns its place on a shadok MECHANISM that the ground makes applicable, * never on the stack being nameable. A `Gemfile` on its own tells us what the * project builds with and nothing at all about what would help — that is * precisely the case the informed register exists for. */ export declare function fittingOffer(g: Ground): GreetingOffer | null; /** * Which register the greeting speaks in. * * `empty` and `unrecognised` are kept apart by `ground.ts` and collapse here on * purpose: they are different facts about the directory, but they lead to the * same claim — we cannot say what this is. What must never collapse is either of * them with `informed`, which names real evidence. */ export declare function greetingRegister(g: Ground): GreetingRegister; /** One role this cockpit can put to work, as the brief hands it over. */ export interface ProfileBrief { name: string; /** The role in the profile's own words — what the outcome line is derived from. */ role: string; } /** * Roles this cockpit can be asked for, from the vault it really has. * * Two exclusions, and neither is cosmetic. A MANAGED profile (`Shadok-Tweak`) * has its prompt rewritten from the build at every boot and has its own CTA — * `isManagedProfile` (public/profile-card.js) exists precisely to keep it out of * lists where one PICKS a role, and a greeting enumerating roles is such a list. * And the lead itself is the one SPEAKING: offering to delegate to the agent * writing the sentence reads as a bug, and its own job is the offer, not a * breadth line. * * The list is duplicated from `MANAGED_PROFILES` rather than imported, because * `src` cannot import from `public` (tsc `rootDir`). `test/greeting.test.ts` * imports both and asserts they agree, so the copy cannot drift in silence. */ export declare function greetingProfiles(stored: readonly Profile[]): ProfileBrief[]; /** The facts `readGround` established, as the brief states them. */ export declare function groundFacts(g: Ground, dirName: string): string[]; export interface GreetingBriefInput { ground: Ground; /** The profiles this cockpit holds, straight from the vault. */ profiles: readonly Profile[]; /** The launch directory's own name — what the person calls this place. */ dirName: string; } /** * The hidden prompt that produces the greeting. * * It is delivered like a cron fire or a parent notification (marked, so it * drives a turn without ever showing as a chat bubble), which is what makes the * greeting arrive unprompted rather than as an answer to something the user can * see themselves not having typed. */ export declare function homeGreetingBrief(input: GreetingBriefInput): string;