/** * publish-pr — foundation for `cleo docs publish-pr`. * * This file ships the pieces of the publish-pr flow that have no * git/gh side-effects: * - slug validation (mirror of the docs domain validator) * - branch naming (`docs/`) * - temp worktree path generation * - structured error envelope shape (LAFS-compatible) * - reusable subprocess runner indirection so later commits can swap in * real `git`/`gh` calls or test stubs without touching the surface area * * Subsequent commits layer the publish flow on top: * - T9718 — new-doc publish flow with YAML frontmatter * - T9717 — atomic update of an existing PR's body via force-push + edit * - T9719 — structured error envelopes (E_NO_GH_AUTH, E_DETACHED_HEAD, …) * * @task T9716 (T9644 / Epic T9630 / Saga T9625) */ /** * Snapshot of every built-in doc-kind name from the canonical * {@link DocKindRegistry} (T9788). * * Built-in-only — project-level extensions registered through * `.cleo/docs-config.json` are NOT included here. Use * {@link knownDocTypesForProject} when extensions matter (e.g. inside * the publish-pr flow where the project root is known). * * Kept as a {@link Set} for O(1) `has()` checks. */ export declare const KNOWN_DOC_TYPES: ReadonlySet; /** * Project-aware variant of {@link KNOWN_DOC_TYPES}. * * Loads the per-project registry (built-ins + `.cleo/docs-config.json` * extensions) and returns the union as a {@link Set}. Used by the * publish-pr flow so project-defined kinds (e.g. `incident`) land in the * correct `publishDir` rather than the `docs/note` fallback. * * Safe against malformed configs — falls back to built-ins on load failure. * * @param projectRoot - Absolute repo-root path. * @task T9788 */ export declare function knownDocTypesForProject(projectRoot: string): ReadonlySet; /** * Validate a slug string against {@link SLUG_PATTERN}. * * @returns `{ ok: true, slug }` on success; `{ ok: false, reason }` otherwise. */ export declare function validatePublishSlug(raw: unknown): { ok: true; slug: string; } | { ok: false; reason: string; }; /** * Map a doc type to its publish subdir. * * As of T9788 the lookup is delegated to {@link DocKindRegistry} so the * subdir follows whatever each kind declares (e.g. `changeset → .changeset`, * `release-note → docs/release`, `llm-readme → .`). Unknown types fall back * to `docs/note`, preserving prior behavior. * * When `projectRoot` is provided the lookup includes project-level * extensions registered through `.cleo/docs-config.json`; otherwise it * uses the built-in-only registry. * * @param type - Doc kind id (`adr`, `spec`, `changeset`, …). * @param projectRoot - Optional repo-root path for project-level extensions. * @task T9788 */ export declare function publishDirForType(type: string | null | undefined, projectRoot?: string): string; /** Compute the canonical branch name for a slug. */ export declare function branchForSlug(slug: string): string; /** * Build the publish-pr temp-worktree directory under `os.tmpdir()`. * * Each invocation produces a unique path with a random 8-hex suffix so * concurrent publishes for the same slug never collide. */ export declare function tempWorktreeDirForSlug(slug: string): string; /** * Structured LAFS error returned by the publish-pr flow. * * Mirrors the `{success:false, error}` shape that `cliError` serialises so * the CLI surface can forward the envelope without translation. */ export interface PublishPrError { readonly codeName: string; readonly message: string; readonly fix?: string; readonly alternatives?: readonly string[]; readonly details?: Record; } /** Construct a {@link PublishPrError} with all optional fields normalised. */ export declare function publishPrError(codeName: string, message: string, fix?: string, alternatives?: readonly string[], details?: Record): PublishPrError; /** Extract the stderr (or message) of a thrown execFile error. */ export declare function execMsg(e: unknown): string; /** * Inject hooks let tests swap out `git` and `gh` invocations without forking * subprocesses. Real callers leave both undefined. */ export interface PublishPrRunners { readonly git?: (args: readonly string[], cwd: string) => Promise<{ stdout: string; stderr: string; }>; readonly gh?: (args: readonly string[], cwd: string) => Promise<{ stdout: string; stderr: string; }>; } /** Default subprocess runner — shells out via `execFile`. */ export declare function defaultRun(bin: 'git' | 'gh', args: readonly string[], cwd: string): Promise<{ stdout: string; stderr: string; }>; /** Resolve a runner for `bin`, preferring the caller-supplied override. */ export declare function pickRunner(bin: 'git' | 'gh', runners: PublishPrRunners | undefined): (args: readonly string[], cwd: string) => Promise<{ stdout: string; stderr: string; }>; /** * Outcome of {@link provisionWorktree}. * * - `ok: true` — the worktree was created and is ready to commit into. * `remoteHasBranch` indicates whether `origin/` already existed * so the caller can decide between "open new PR" vs "update existing". * - `ok: false` — provisioning aborted; `error` carries a LAFS-compatible * envelope (e.g. `E_BRANCH_COLLISION`, `E_WORKTREE_ADD_FAILED`). */ export type ProvisionResult = { ok: true; remoteHasBranch: boolean; } | { ok: false; error: PublishPrError; }; /** * Provision a fresh temp worktree on branch `docs/`. * * Branch handling: * - When `origin/` exists, the worktree tracks it. * - Otherwise the branch is cut from `origin/`. * - A local-only `docs/` branch that doesn't match the remote * aborts with `E_BRANCH_COLLISION` so we never force-push over an * unrelated branch. * * The caller is responsible for {@link teardownPublishPrWorktree}-ing the * dir in a `finally` block — this function never auto-cleans on success. * * Named with the `publishPr` prefix to avoid clashing with the SDK-level * worktree dispatcher. */ export declare function provisionPublishPrWorktree(opts: { projectRoot: string; worktreeDir: string; branch: string; base: string; runGit: (args: readonly string[], cwd: string) => Promise<{ stdout: string; stderr: string; }>; }): Promise; /** * Tear a publish-pr temp worktree down. Best-effort — failures are * swallowed so the surrounding `finally` never masks the underlying error. * * Named with the `publishPr` prefix to avoid clashing with the SDK-level * `teardownWorktree` exported by `sentient/worktree-dispatch.ts`. */ export declare function teardownPublishPrWorktree(opts: { projectRoot: string; worktreeDir: string; runGit: (args: readonly string[], cwd: string) => Promise<{ stdout: string; stderr: string; }>; }): Promise; /** * Input options for {@link publishDocsAsPr}. * * @task T9718 / T9644 */ export interface PublishPrOptions { /** * The slug-or-id of the doc to publish. Resolution mirrors `cleo docs * fetch`: slug → attachment id → sha256. */ readonly slugOrId: string; /** Optional PR title override. Default: `"docs(): publish "`. */ readonly title?: string; /** Optional PR body override. Default: a short auto-generated body. */ readonly body?: string; /** Base branch for the PR. Default: `"main"`. */ readonly base?: string; /** Project root override (mostly for tests). Defaults to `getProjectRoot()`. */ readonly projectRoot?: string; /** * Optional slug override when `slugOrId` is itself a non-slug identifier * (e.g. attachment id or sha256). Validated against {@link validatePublishSlug}. */ readonly slug?: string; /** * Optional type override. When the stored attachment carries no `type` * column the caller can pin one — bypassing the `docs/note/` default. */ readonly type?: string; /** Test-only runner overrides for `git` and `gh`. */ readonly runners?: PublishPrRunners; } /** * Success payload returned by {@link publishDocsAsPr}. * * @task T9718 / T9717 */ export interface PublishPrSuccess { readonly action: 'new' | 'updated'; readonly prUrl: string; readonly branch: string; readonly commitSha: string; /** The PR head sha BEFORE the update (only set when `action === 'updated'`). */ readonly priorSha?: string; /** The published file path inside the worktree, project-root-relative. */ readonly filePath: string; /** The slug under which the doc was published. */ readonly slug: string; /** The doc type recorded in frontmatter (e.g. `spec`, `adr`, `note`). */ readonly type: string; /** Lowercase hex sha256 of the stored blob bytes (pre-frontmatter). */ readonly blobSha: string; } /** Result envelope returned by {@link publishDocsAsPr}. */ export type PublishPrResult = { readonly success: true; readonly data: PublishPrSuccess; } | { readonly success: false; readonly error: PublishPrError; }; /** * Strip an existing leading `---\n…\n---\n` block from `text` so we never * double-stack frontmatter when the stored doc already carries one. * Idempotent on docs without frontmatter. * * @internal */ export declare function stripExistingFrontmatter(text: string): string; /** Build the YAML frontmatter block prepended to published markdown. */ export declare function buildPublishFrontmatter(opts: { slug: string; type: string; blobSha: string; createdAt: string; }): string; /** Default PR body emitted when the caller doesn't supply one. */ export declare function defaultPublishPrBody(opts: { slug: string; type: string; blobSha: string; }): string; /** * Parse the PR URL out of `gh pr create`'s stdout. * * `gh` prints the URL on its own line; we pick the first line that looks * like a github.com pull URL. * * @internal */ export declare function parseGhPrUrl(stdout: string): string; /** * Publish the doc identified by `slugOrId` to a new PR. * * Flow (T9718): * 1. Resolve the doc → bytes + (optional) stored slug/type. * 2. Validate the resolved slug; pick the publish dir from the doc type. * 3. Pre-flight `gh auth status` so authentication failures surface * before any side effects. * 4. Provision a temp worktree on `docs/`. * 5. Write `docs//.md` with YAML frontmatter, commit, push. * 6. Open the PR via `gh pr create`. * 7. Tear the worktree down in `finally`. * * This implementation only handles the "open new PR" path. Update-PR * detection (force-push existing branch + `gh pr edit`) lands in T9717. * * @task T9718 / T9644 */ export declare function publishDocsAsPr(opts: PublishPrOptions): Promise; //# sourceMappingURL=publish-pr.d.ts.map