import type { Command } from "commander"; import { ShipFlowClient, type RefreshedTokens } from "../client.js"; import { type StoredCredentials } from "../config.js"; import { type OutputFormat } from "../output.js"; export { emit } from "../output.js"; /** The auth half of a ShipFlowClient's options, resolved from the one shared * credential context. A `login` JWT wires transparent refresh so a signed-in * session stays valid across the 30-day window; a legacy `sfk_` API key is sent * as-is. Both go out as Bearer tokens — so `login` signs a user in for EVERY * command, not just the JWT/loop ones. Pure + testable (no process/global reads). */ export type ClientAuth = { jwt: string; refreshToken: string; onRefreshed: (t: RefreshedTokens) => void; } | { apiKey: string; }; export declare function buildClientAuth(auth: { token: string; kind: "jwt" | "apiKey"; }, creds: StoredCredentials | null): ClientAuth; /** A client for the API-key-style commands (repos/workflows/channels/activity/ * stats/trigger). Resolves credentials through the SAME context as the loop's * JWT commands (resolveAuthToken → buildClientAuth), so running `login` is * enough to use everything. Exits 1 when nothing is signed in. */ export declare function getClient(cmd: Command): ShipFlowClient; /** Resolve a JWT-authenticated client + the shared credential context WITHOUT * resolving a project — the project-less core of {@link loadCtx}. For commands * that hit an org-scoped endpoint but don't need the cwd's project (e.g. * `regression status`), or that must run other checks before the project is * resolved (`regression` trigger resolves the ref first). Exits 1 when not * signed in. */ export declare function loadJwtCtx(program: Command): { auth: { token: string; kind: "jwt" | "apiKey"; }; creds: StoredCredentials; client: ShipFlowClient; }; /** Bootstrap a JWT-authenticated client + resolved project for the loop-facing * `pr`/`issue` commands. Signs in from stored credentials (JWT + refresh), * wiring transparent token refresh, and resolves the active project from the * cwd's repo. Exits 1 when not signed in. */ export declare function loadCtx(program: Command): Promise<{ auth: { token: string; kind: "jwt" | "apiKey"; }; creds: StoredCredentials; client: ShipFlowClient; project: import("../project.js").ResolvedProject; }>; /** Context for the GitHub-only `pr` subcommands (issue #447). * * Same shape as {@link loadCtx}, except a ShipFlow API outage DEGRADES instead * of aborting: `project.repoFullName` comes from the git remote — which is * where it was always derived from, five lines before the server was ever * consulted — while `projectId`/`projectName` go null. Commands that only * drive `gh` (post-review, approve, reviews, resolve, packet) then complete, * because nothing they do ever needed the API. * * It is never silent: the one warning line is printed here (stderr, so `--json` * stdout stays parseable) and `degraded` travels to the caller for the machine- * readable `--json` field. Anything project-scoped must check `projectId`. */ export interface GhCtx { auth: { token: string; kind: "jwt" | "apiKey"; }; creds: StoredCredentials; client: ShipFlowClient; project: { repoFullName: string; projectId: string | null; projectName: string | null; }; /** Unavailable dependencies — `[]` when everything resolved. */ degraded: string[]; /** `projectId` is null because the lookup was DELIBERATELY not performed AND * we can affirm no map applies — `--repo` names a repo we KNOW is outside * this checkout, so the cwd project's id and feature map describe something * this command is not touching. * * Strictly narrower than "the lookup was skipped": when the cwd repo is * UNKNOWN the lookup is skipped too, but the target may be this very project, * so the skip is unclassified and this stays `false` — the caller then reports * a ⚠️ skipped gate, not a neutral N/A. Never guess "not applicable" from * ignorance; that is how a gate that did not run reads as one that passed. * * A null `projectId` on its own cannot tell that apart from an outage, and * conflating the two is a false RED: the packet rendered "⚠️ WARNING * shipflow-api feature map unavailable" for a supported cross-repo override * on a fully healthy API (PR #482 review — chatgpt-codex-connector + the loop * reviewer). Only a genuine failure may set `degraded`/a ⚠️ marker; "not * consulted, because irrelevant" is a neutral note. Callers branch on THIS, * never on `degraded.length`. */ projectNotApplicable: boolean; } /** Whether an explicit `--repo` makes the ShipFlow project lookup pointless: it * names a repo other than the cwd's. Case-insensitive, because GitHub repo * names are. A cwd with no github.com remote counts as "other" — with an * explicit target there is nothing left for the remote to contribute. Pure. * * This answers ONE question — "is the lookup worth performing?" — and a null * cwd repo answers it `true`: resolving the cwd's project cannot help a target * the cwd does not name. It deliberately does NOT answer "could any feature map * apply to the target?"; see {@link repoOverrideMakesProjectNotApplicable}. */ export declare function repoOverrideBypassesProject(repoOverride: string | undefined, cwdRepo: string | null): boolean; /** Whether an explicit `--repo` makes the ShipFlow feature map genuinely * INAPPLICABLE — i.e. we can affirm that no map this checkout knows about * could describe the target. That needs a KNOWN cwd repo that differs from it. * * Split from {@link repoOverrideBypassesProject} because one predicate was * answering two different questions (PR #482 round-3 review). "Skip the * pointless lookup" is true for a null cwd repo; "no map could apply" is NOT. * With no cwd repo at all the target may well be this very project, so * `pr packet --repo ` run outside a git checkout * took the bypass and printed the neutral "omitted by design, not by failure" * note for a repo the map DOES cover — the per-feature evidence gate silently * did not run and was reported inapplicable. That is issue #447's own * false-green class, re-armed from the other side. * * So: a KNOWN, different repo → not applicable (a neutral NOTE). An UNKNOWN cwd * repo → unclassified, which routes back to a ⚠️ skipCause. Guessing "not * applicable" from ignorance is how a skipped gate reads as a passed one. Pure. */ export declare function repoOverrideMakesProjectNotApplicable(repoOverride: string | undefined, cwdRepo: string | null): boolean; export declare function loadGhCtx(program: Command, repoOverride?: string): Promise; /** The `--json` degradation field: present ONLY when something degraded, so a * healthy run's JSON stays byte-identical for existing consumers. Same * spread-when-set shape `pr automerge` uses for `unsatisfiable`. */ export declare function degradedField(ctx: { degraded: string[]; }): { degraded?: string[]; }; /** Best-effort `signal`: on failure, warn (prefixing the message with `label` — * e.g. "Merged but ShipFlow signal failed") and continue instead of throwing, * so a transient signal blip never fails the surrounding command. Returns * whether the signal landed. Omit `label` to swallow silently (no warning). * The warn-and-continue semantics are load-bearing for the loop — keep exact. */ export declare function signalBestEffort(ctx: { client: ShipFlowClient; creds: { org: string; }; project: { projectId: string; }; }, refKind: "issues" | "prs", n: number, action: "claim" | "release-claim" | "opened" | "merged", body: Record, label?: string): Promise; /** Resolve the shared context for the API-key-style commands in one call: an * authenticated {@link getClient}, the target {@link getOrg}, and the requested * {@link getFormat}. Preserves the getClient-first order so an unsigned-in * invocation still exits 1 before anything else runs. */ export declare function getApiCtx(cmd: Command): { client: ShipFlowClient; org: string; format: OutputFormat; }; /** Unpack a `` positional + optional `--repo` override into the numeric * issue/PR number and the resolved repo (defaulting to the active project's). * Collapses the `parseInt(numberStr, 10)` + `opts.repo ?? ctx.project.repoFullName` * pair repeated across the `pr`/`issue` subcommands. */ export declare function resolveTarget(ctx: { project: { repoFullName: string; }; }, numberStr: string, opts: { repo?: string; }): { number: number; repo: string; }; export declare function getOrg(cmd: Command): string; export declare function getFormat(cmd: Command): OutputFormat; /** * Reserved exit code for an unexpected/transient CLI failure (an unhandled throw * — a network blip, a `gh`/`git` hiccup, etc.). It's deliberately distinct from * every intentional control-flow code so the unattended loop can tell "retry or * escalate" apart from a real verdict: * 3 = issue taken · 4 = no actionable issues · 5 = automerge not ready * 6 = rebase conflict · 7 = unresolved review threads (approve) * 10 = unexpected/transient failure — retry or escalate (this one). */ export declare const UNEXPECTED_EXIT_CODE = 10; /** * Wrap a loop-gated command action so an unexpected throw becomes a clean * `Error: ` (or `{"error": …}` JSON when `--json`) on exit code 10 — * instead of a raw stack trace on the generic exit 1 that collides with the * deliberate codes above. Deliberate `process.exit(n)` calls inside `fn` * terminate the process directly and are never caught here, so all intentional * exit codes are preserved. */ /** An argument/usage failure (exit 1 per the output contract), as opposed to * an unexpected failure (exit 10). Thrown by e.g. the headless prompt guard: * "you forgot --title" is the caller's mistake, not a crash (PR #491 review). */ export declare class UsageError extends Error { } /** * Resolve a `@me` filter to the authenticated login, failing CLOSED (issue #652). * * `ghCurrentLogin()` swallows every `gh api user` failure and returns `""`, and * an empty login handed to a `--assignee` / `--author` filter DROPS the filter: * "my issues" silently became "every open issue in the repo", exit 0, no stderr. * A 401, an expired token, or a network blip must read as an error, never as a * widened scope — so every `@me` site resolves through here. * * `context` names the command in the message ("issues list --assignee @me"). */ export declare function resolveMeLogin(context: string): string; export declare function runAction(fn: (...args: A) => void | Promise): (...args: A) => Promise; //# sourceMappingURL=helpers.d.ts.map