/** * Faithful markdown → typed-spec adoption — the deterministic half of `init` * auto-adopt (research/install-enforcement-dx.md). * * Turns an existing instruction file (CLAUDE.md / AGENTS.md) into a `instructionFile()` * spec source that compiles back to ~the same file, so adopting a rich, * hand-tuned instruction file is SAFE: every heading becomes a prose section * (verbatim), no rule is invented, nothing is dropped. The contract to the user * is "review the diff" — for a well-headed file that diff is small (whitespace + * the canonical `# ` h1). The agentic path (the `adopt-spec` skill) * handles irregular prose better; this is the zero-model floor. * * WHY IT ALWAYS COMPILES: the compiler only rejects `#`/`##` headers INSIDE a * section body (sections render as `##`), so we split the file on every `#`/`##` * heading — each becomes its own section, and `###`+ subheadings ride along * inside the body untouched. Reserved lowercase keys * (`commands`/`keyFiles`/`rules`) are never produced (`safeKey`). `guidance()` * vs `enforce()` is deliberately NOT guessed here — that cross-referencing is * `strengthen`'s separate, later job; adoption is lossless transcription. */ import { type SkillSpec, type AgentSpec } from "./spec.js"; export type AdoptTier = "structured" | "raw"; export interface AdoptResult { /** Generated `.spec.ts` source (compiles back to ~the original file). */ source: string; /** * Backticked paths that RESOLVED at adoption time and were emitted as verified * `file()` refs. Empty when no `exists` predicate was supplied (the faithful, * extract-nothing behaviour) or when nothing resolved. */ adoptedRefs?: readonly string[]; /** * `structured` = a clean `##`-headed file mapped 1:1 to sections (the diff is * just the canonical h1 + whitespace). `raw` = a heading-less or * intro-bearing file we wrapped under a synthesized `Overview` section — * content is preserved verbatim, but the diff adds a heading, so "review the * diff" matters more. */ tier: AdoptTier; /** Number of named sections produced (excluding the auto-rendered h1). */ sectionCount: number; } /** * The intermediate adoption result: the `instructionFile()` spec FIELDS (before * rendering to source). Exposed so the renderer and the round-trip tests share * one parse — the test can feed `sections` straight into `compileClaude` and * assert the file is reproduced, without evaluating generated TS source. */ export interface AdoptedSpec { target: string; /** Heading → verbatim section body, in document order. */ sections: Record; /** Set only when a faithful section exceeds the compiler's 200-line guard. */ maxSectionLines?: number; tier: AdoptTier; } /** * Render a section as a template, turning every backticked path that RESOLVES * TODAY into a verified `${file("…")}` reference. * * WHY THIS EXISTS. Adoption used to transcribe faithfully and extract NOTHING — * `rules: {}`, every reference left as inert prose — on the stated grounds that * cross-referencing is `strengthen`'s later job. An adopter measured what that * trade actually costs (2026-08-28, a 51-skill monorepo): running * `vigiles init --target=CLAUDE.md` turned the file into one opaque template with * zero refs extracted, so "the price is paid immediately — the file becomes a * build artifact, hand edits are blocked by a hook, every backtick is escaped — * while the benefit is deferred until a human rewrites every reference by hand." * The checker was never the problem: he hand-wrote two `file()` calls and compile * correctly reported `[stale-file]`, exit 1. The ADOPTION PATH did not populate it, * so nobody reached the value and the second step never happened. * * WHY RESOLVE-NOW IS THE RIGHT FILTER, and not a heuristic. The undecidable * question is "is this OUR path or a path inside the third-party repo this * document DESCRIBES?" — the same wall that made `doc-refs` default to off after * scoring 0 true positives, and that the adopter hit independently (1560 * path-shaped strings in his corpus, 907 unresolvable, single-digit true * positives after filtering; almost all the rest were paths in repos his skills * merely describe). Asking instead "does it resolve HERE, right now?" sidesteps * it: a described repo's path does not exist locally, so it is never emitted. * * The consequences are the ones adoption needs: * - ZERO new failures at adoption time — only already-green refs are emitted, so * `compile` cannot start red on a file that was fine a second earlier; * - value from the FIRST compile rather than after a manual pass — the ref goes * red exactly when the file moves, which is the entire point of marking it; * - a false emit is benign (an extra verified ref), while a false SKIP costs * only what the old behaviour already cost. * * Fence-awareness goes through the shared `fencedLineFlags` oracle, never a * private toggle: a path inside a fenced block is example code, and hand-rolled * fence state is the exact defect that oracle exists to make unrepresentable. */ export declare function refInterpolatedTemplate(content: string, exists: (p: string) => boolean): { template: string; refs: string[]; }; /** * Parse an instruction file's markdown into the faithful `instructionFile()` spec FIELDS. * The shared core of {@link adoptMarkdown} and the round-trip tests. * * @param markdown the file's current content (an existing integrity header, if * any, is stripped — we adopt the body) * @param target the bare target filename (`"CLAUDE.md"` / `"AGENTS.md"`), * which the compiler renders as the h1 */ export declare function adoptToSpec(markdown: string, target: string): AdoptedSpec; /** * Convert an instruction file's markdown into a faithful `instructionFile()` spec source * (the deliverable `init` writes). */ export declare function adoptMarkdown(markdown: string, target: string, opts?: { readonly exists?: (p: string) => boolean; }): AdoptResult; export interface AdoptSurfaceResult { /** Generated `.spec.ts` source. */ source: string; kind: "skill" | "agent"; /** * The parsed spec object the source builds — exposed so a round-trip test can * feed it straight to `compileSkill`/`compileAgent` without evaluating the * generated TS (the same split as `adoptToSpec`/`renderSpecSource`). */ spec: SkillSpec | AgentSpec; /** * Frontmatter keys present in the source that the typed spec has no field for * — emitted as a `// NOTE:` comment in the source so nothing is lost silently. */ unmappedKeys: string[]; } /** * Adopt an existing SKILL.md into an `experimental_skill()` spec. The body is carried verbatim * (skills are freeform markdown — `##` headings stay in the body), so a clean * skill round-trips below the integrity header. * * @param markdown the SKILL.md content * @param dirName the skill's directory name — the CC fallback for `name` when * frontmatter omits it */ export declare function adoptSkill(markdown: string, dirName: string): AdoptSurfaceResult; export declare function adoptAgent(markdown: string, fileBase: string): AdoptSurfaceResult; //# sourceMappingURL=adopt.d.ts.map