UNPKG

framework

Version:

The (AI) Framework: turnkey, zero-config AI orchestration that wraps a coding-agent CLI (Claude Code) as a black box and takes you from an idea to a running app. Vite for AI.

225 lines 13.5 kB
import { type CliRunner } from '../cli-exec.js'; import { type Cached } from './cache.js'; import type { AutoMergeOutcome } from '../events.js'; /** * The `gh` CLI, in one place: the two JSON reads the dashboard makes and the runner its write * actions use. * * There were four separate `gh` adapters across three modules. Three were reads that each * hand-rolled `execFile` + `JSON.parse` + a swallowed failure and each spelled the 8s timeout * again, and two of those differed only in whether a branch positional was passed; the fourth was * a generic runner for the write actions, which rejects with stderr and waits longer. */ /** Runs `gh`, resolving stdout and rejecting with the CLI's own stderr on failure. */ export type GhRunner = CliRunner; /** * A {@link GhRunner} for the write actions (push, open a PR). Longer timeout than a read: these * talk to the network and to git, and the user is waiting on a button they pressed. */ export declare function nodeGhRunner(): GhRunner; /** * The GitHub token an Actions run authenticates with (#1352). * * `GH_TOKEN` / `GITHUB_TOKEN` win, because CI sets them and must beat whatever `gh` happens to be * logged in as on the runner. With neither set, fall back to the `gh` CLI's own credential — the * same one every PR this framework opens is already authenticated by. A machine that can open a PR * could always have run an Actions session too; it just had no way to say so, and the agent failed * with the credential sitting one `gh auth token` away. * * Undefined when there is no token to be had (gh missing, logged out, or refusing to hand it over), * which the caller turns into the agent's stated reason for not starting. Deliberately quiet about * *why* gh declined: the caller's message names both ways to fix it, and a keyring prompt's stderr * is not something to put in front of someone who simply has not set GH_TOKEN. */ export declare function githubToken(cwd: string, env?: Record<string, string | undefined>, gh?: GhRunner): Promise<string | undefined>; /** A forgiving `gh --json` read: resolves `empty` when gh is missing/unauthed, or its output is not JSON. */ export declare function ghJson<T>(args: string[], cwd: string, empty: T, gh?: GhRunner): Promise<T>; /** The PR opened for a branch, when there is one. */ export interface LinkedPr { number: number; url: string; /** OPEN / MERGED / CLOSED (as gh reports it). */ state: string; title: string; /** ISO creation time, when the read included it: what tells one agent's PR from a predecessor's. */ createdAt?: string; /** * The head commit the PR covers, when the read included it (#1512): what tells "the branch's * PR already landed everything" from "the session kept working after its PR merged". */ headRefOid?: string; } /** * A best-effort PR lookup, for a named branch or for the checkout's current branch. One type for * both: the named-branch form is the general one, and "the current branch" is just omitting it. */ export type PrLookup = (cwd: string, branch?: string) => Promise<LinkedPr | undefined>; /** * The branch-addressed form, for a caller that always names one (#799): a finished session's * worktree may be gone, so "the current branch" would silently be the project's, not the * session's. Narrower than {@link PrLookup} on purpose, so that invariant is in the type. */ export type BranchPrLookup = (cwd: string, branch: string) => Promise<LinkedPr | undefined>; /** * The PR for `branch`, or for whatever branch `cwd` is on when none is named. Resolves undefined * when gh is missing/unauthed or there is no PR. * * The named-branch form is what a finished session needs (#799): its worktree may be gone, so the * checkout's current branch is the project's, not the session's. The fields are copied out rather * than passed through, so a future `--json` addition cannot leak into what callers store. * * Every optional field {@link LinkedPr} declares must be both asked for and copied out here, or it * is silently absent for every caller of this path. `createdAt` was neither, and the CI watch reads * it to decide whether a check-less PR has outlived the window a check suite takes to attach — with * the field missing, that age is unknowable and such a PR could never be merged at all. */ export declare function ghPrView(cwd: string, branch?: string, gh?: GhRunner): Promise<LinkedPr | undefined>; /** * The cached form of {@link ghPrView} (#1028), and what the dashboard's panels use. * * A PR lookup costs about 600ms where the git reads beside it cost ten, and the answer changes * about as often as someone opens a PR. Cached per checkout and branch, shared between the * worktree bar and the handoff summary, and refreshed behind whoever asks. `pending` says the * answer is not known yet rather than that there is no PR — the difference matters to a caller * deciding whether to offer "Open PR". */ export declare function cachedPrView(cwd: string, branch?: string): Promise<Cached<LinkedPr | undefined>>; /** Forget a branch's PR, after an action that changes whether it has one. */ export declare function forgetPr(cwd: string, branch?: string): void; /** * Every PR a branch name has ever had, newest first (#1251). * * `gh pr view <branch>` answers with the newest PR for that head *in any state*, so a session * whose prompt pins its branch name (`the-framework/triage-quick`) inherits a predecessor's * merged PR as its own. The list form keeps the whole history so {@link pickAgentPr} can decide * which entry, if any, belongs to the agent asking. Resolves `[]` when gh is missing/unauthed — * indistinguishable from "no PRs", which is what every caller would do with a failure anyway. */ export declare function ghPrsForBranch(cwd: string, branch: string): Promise<LinkedPr[]>; /** * {@link ghPrsForBranch} for a caller about to *open* a PR (#1601): a listing that fails throws * instead of reading as "no PRs", because "none" and "could not tell" must not look alike there — * the difference is a second draft PR on a branch that already has one. */ export declare function ghPrsForBranchOrThrow(cwd: string, branch: string): Promise<LinkedPr[]>; /** * How {@link ghMergePr} answers GitHub refusing to arm auto-merge. `merge-now` (the default, and * everything before #1418) merges directly: right where a human just said "land it". `watch` * merges directly only when the PR's checks have already passed, and otherwise answers `watched` * — the daemon's CI watch merges it on green. That is the auto path's mode, because the direct * fallback there is precisely the lands-before-CI hazard (#1406): a repo without GitHub * auto-merge saw every armed PR merged seconds after opening, before its first check ran. */ export interface MergePrOptions { whenUnarmed?: 'merge-now' | 'watch'; } /** * Merge a PR the handoff just opened (#1216): GitHub auto-merge first, so the PR lands when its * checks pass rather than before them; where the repo does not allow auto-merge, merged directly * or handed to the daemon's CI watch, per {@link MergePrOptions.whenUnarmed} (#1418). Squash in * all forms — a session's branch is working history, not a story worth preserving. * * Never throws: the caller reports the outcome alongside the handoff's, and a merge that could * not happen must not turn a successful handoff into a failed one. */ export declare function ghMergePr(cwd: string, number: number, gh?: GhRunner, opts?: MergePrOptions): Promise<AutoMergeOutcome>; /** Where a PR's CI stands (#1418), summarised to the one question the merge path asks. */ export interface PrCiStatus { /** * `passing`: every check has concluded and none failed — the PR may land. `failing`: at least * one concluded check failed, whatever the rest are doing — red now, and more green later will * not unsay it. `pending`: something is still running and nothing has failed yet. `none`: no * checks reported — either the repo has no CI, or the suite has not attached yet (they take a * few seconds after a push), which is why callers treat it with a grace period rather than as * green. Also the answer when `gh` itself could not say: acting on an unreadable status must * never merge anything. */ checks: 'passing' | 'failing' | 'pending' | 'none'; /** The names of the failed checks, for the CI-fix agent's prompt. */ failed: string[]; /** The PR's head commit, so a fix attempt can be recorded against the state it saw. */ headSha?: string; /** The PR's head branch, where a CI fix must land. Rides this read because it is the same `gh` call. */ branch?: string; } /** * A PR's combined check state (#1418): GitHub Actions check runs and classic commit statuses, * both of which `statusCheckRollup` carries. * * Skipped and neutral conclusions count as passing, matching how GitHub's own merge box treats * them; everything else that concluded non-successfully counts as failed — a cancelled or * timed-out check is not evidence the work is good, and the merge this feeds exists to stop * unverified work landing (#1406). */ export declare function ghPrCiStatus(cwd: string, number: number, gh?: GhRunner): Promise<PrCiStatus>; /** Whether the repo lets PRs use GitHub auto-merge (#1417); `known: false` when `gh` could not say. */ export interface RepoAutoMerge { known: boolean; allowed: boolean; } /** * Whether this repo allows GitHub auto-merge (#1417). * * An armed merge on a repo that does not (the {@link DIRECT_MERGE_FALLBACK} half of #1216) is * handed to the daemon's CI watch (#1418): merge on green, but only while the daemon runs — so * the launcher notes the local fallback and names the server-side Allow auto-merge setup. * `known: false` (gh missing, unauthenticated, not a GitHub repo) is "could not say", which * renders nothing: no crying wolf, same stance as the trust (#1318) read. * * The probe is the REST endpoint, not `gh repo view --json autoMergeAllowed`: `repo view` has no * such field (any gh version), so that spelling always errored into "could not say". REST omits * `allow_auto_merge` for viewers without push access — absent lands in the same "could not say". */ export declare function ghRepoAutoMerge(cwd: string, gh?: GhRunner): Promise<RepoAutoMerge>; /** The cached form of {@link ghRepoAutoMerge} (#1028): the launcher polls, the setting barely changes. */ export declare function cachedRepoAutoMerge(cwd: string): Promise<Cached<RepoAutoMerge>>; /** The cached form of {@link ghPrsForBranch}, shared through the same read-through cache (#1028). */ export declare function cachedPrsForBranch(cwd: string, branch: string): Promise<Cached<LinkedPr[]>>; /** Forget a branch's PR history, after an action that changes it (opening one). */ export declare function forgetBranchPrs(cwd: string, branch: string): void; /** * The PR that belongs to an agent, out of every PR its branch name has had (#1251/#1255). * * An OPEN PR always counts: GitHub allows one open PR per head branch, so whatever is open on the * run's branch is where its pushed commits land. A closed one counts only when it was created * after the agent started (`since`, the agent's `startedAt`) — the oldest such entry, which is the one * this agent's handoff opened. Anything older is a previous agent's PR wearing the same branch name, * which is exactly what showed a merged two-day-old PR as a fresh session's own. Without `since` * only an open PR is trusted. * * `order` exists for the one caller asking a different question (#1512). `'first'` answers * identity — which PR did *this agent* open, so a later agent's must not be the answer. `'latest'` * answers the handoff decision — which PR last saw the branch, so "did the session keep working * past it" is readable off that PR's `headRefOid`; there the oldest entry would call work that a * second PR already landed unlanded. */ export declare function pickAgentPr(prs: LinkedPr[], since?: string, order?: 'first' | 'latest'): LinkedPr | undefined; /** An open PR on the interventions queue (#632). */ export interface OpenPr { number: number; title: string; url: string; /** * Draft PRs are generally left off the queue: a draft is not asking for review. * * The exception is a draft the framework opened for itself (#1102), which {@link headRefName} * is what tells apart. */ isDraft: boolean; /** The branch the PR is from, so a session's own PR can be recognised as ours (#1102). */ headRefName?: string; createdAt?: string; } /** * A checkout's open PRs. Unlike the other reads here it *rejects* when gh could not answer — no * remote, not authenticated, GitHub unreachable — instead of resolving `[]`. * * "No PRs are open" and "I could not look" are different answers, and its caller keeps a baseline * of what it has already announced (#1623): taking the second for the first makes the next * successful read announce every already-open PR as new. The caller decides what a failure costs; * it cannot decide what it never hears about. */ export declare function ghPrList(cwd: string, gh?: GhRunner): Promise<OpenPr[]>; /** Lists a checkout's open PRs; rejects when there is no remote / gh is unavailable. */ export type PrLister = (cwd: string) => Promise<OpenPr[]>; //# sourceMappingURL=gh.d.ts.map