export interface HulaApiConfig { baseUrl: string; apiKey: string; timeout?: number; } export interface ApiTrackedPlan { id: string; trackingName: string; issueNumber: number | null; title: string; trackingStatus: string; githubState: string; priority: string; project: { owner: string; name: string; fullName: string; }; prNumber?: number | null; prUrl?: string | null; /** * Whether the plan's PR was merged. Optional for backward compatibility with * older servers. Used by the resume-digest to render the merge date. */ prMerged?: boolean | null; githubUrl: string; createdAt: string; updatedAt: string; /** ISO timestamp the GitHub issue was closed (a merged PR closes its issue). */ closedAt?: string | null; /** ISO timestamp the plan was archived (HubLaunch-only soft close). */ archivedAt?: string | null; deploymentStatus?: 'pending' | 'success' | 'failed' | null; /** * Server-side owner id. Optional for backward compatibility with older * servers that did not expose this field. */ createdById?: string; /** * Lowest-category merge-confidence score (0-100) for the plan's latest task, * surfaced by `GET /api/v1/plans` (hula-server merge-confidence plan). * Optional/`null` on older servers or when no assessment exists yet. */ mergeConfidence?: number | null; /** * Merge verdict for the plan's latest task (`ready`/`review`/`blocked`/ * `unavailable`). Optional/`null` on older servers. */ mergeVerdict?: string | null; /** * Unified lifecycle stage (one of the ordered set in * `src/types/stage.schema.ts`: `backlog → … → completed → archived`). * Optional for backward compatibility with older servers that do not yet * derive/expose it — the CLI falls back to `deriveStageFallback()`. */ stage?: string; /** * Whether the plan has been archived (soft-closed but retained). Optional for * backward compatibility with older servers; treated as `false` when absent. */ archived?: boolean; } /** * The four merge verdicts the server assigns a PR (hula-server merge-confidence * plan). `unavailable` means no assessment could be produced. */ export type MergeVerdict = 'ready' | 'review' | 'blocked' | 'unavailable'; /** * Structured merge assessment attached to every launched PR by the server * (hula-server merge-confidence part 01). `confidence` is the lowest of the five * category scores; `threshold` is the project policy frozen at launch time. The * CLI only ever reads and prints this — it never recomputes confidence. */ export interface MergeAssessment { version: 1 | 2; confidence: number | null; verdict: MergeVerdict; threshold: number; categories: Record<'planCoverage' | 'executedVerification' | 'changeRisk' | 'testCoverage' | 'docsAndCompatibility', { score: number; reason: string; }> | null; checks: { check: boolean | null; build: boolean | null; regression: boolean | null; e2e?: boolean | null; }; concerns: string[]; endUserSteps: Array<{ step: string; expected: string; }>; model: string | null; generatedAt: string | null; evidence?: Array<{ claim: string; evidence: string; verdict: 'pass' | 'fail' | 'unknown'; commit?: string | null; carriedFromRun?: number | null; }> | null; testIntegrity?: { tampered: boolean; locations: string[]; } | null; baseHead?: { executed: boolean; editedToPass: string[]; } | null; references?: Array<{ kind: string; name: string; location: string; }> | null; unplannedFiles?: string[] | null; e2e?: { executed: boolean; passed: boolean | null; cases: Array<{ name: string; status: string; }>; } | null; } /** * One plan-level evidence record posted to `POST /api/v1/plans/:id/evidence` * (part 02 — latest-evidence merge assessment). The server stamps `actor` and * `timestamp`; the caller supplies the proof it produced. `checks`/`rows`/`e2e` * carry no secrets (only `file:line` refs or `command + exit code`). */ export interface PlanEvidenceInput { source: 'harness-e2e' | 'local' | 'ci' | 'sandbox'; /** Commit SHA the evidence was produced against (gated vs the PR head). */ sha: string; verdict: 'pass' | 'fail' | 'unknown'; checks?: { check?: boolean | null; build?: boolean | null; regression?: boolean | null; e2e?: boolean | null; } | null; rows?: Array<{ claim: string; evidence: string; verdict: string; }> | null; e2e?: { executed: boolean; passed: boolean | null; cases: Array<{ name: string; status: string; }>; } | null; summary?: string | null; } /** Response of `POST /api/v1/plans/:id/evidence`. */ export interface RecordEvidenceResponse { record: PlanEvidenceInput & { actor: string; timestamp: string; }; shaMatched: boolean; assessment: MergeAssessment | null; provenance: string[]; requiredEvidencePresent: boolean; } /** Response of `POST /api/v1/plans/:id/reassess`. */ export interface ReassessResponse { assessment: MergeAssessment | null; provenance: string[]; requiredEvidencePresent: boolean; recordCount: number; } /** * Response shape of `GET /api/v1/ralph-run/:issueName/status`. Only the fields * the CLI reads are typed; every other field passes through via the index * signature. `mergeAssessment` is absent/`null` on older servers. */ export interface RunStatusResponse { status?: string; completedAt?: string | null; clientSessionId?: string | null; mergeAssessment?: MergeAssessment | null; [key: string]: unknown; } export interface PlanStatusResponse { plan: ApiTrackedPlan; copilotStatus: { status: string; isAssigned: boolean; hasPR: boolean; }; lastPolled: string; } export interface TrackPlanRequest { owner: string; repo: string; issueNumber: number; trackingName: string; title: string; priority?: string; planPath?: string; copilotAssigned?: boolean; } /** * One backlog item as returned by the server (`POST /api/v1/plans/backlog`, * companion `hula-project` plan). `id` is the server-side item id the CLI * records locally as `remoteId`. */ export interface ApiBacklogItem { id: string; /** Owning project id (items are project-owned; legacy servers may omit it). */ projectId?: string; /** Plan the item was noticed on (optional). Older servers returned `planId`. */ originPlanId?: string | null; text: string; /** * Item state. The new server returns the state-machine value * (`parked|ready|planned|done|archived`); older servers returned `open|done`. * `normStatus` in `backlog-sync.ts` coerces either into the legacy pair. */ status: string; /** Item kind (`plan|local|note|external`); absent on older servers. */ type?: string; /** Optional markdown detail; absent on older servers. */ detail?: string | null; /** Typed blocker refs; absent on older servers. */ blockedBy?: Array<{ kind: string; ref: string; resolvedAt?: string; }>; /** Auto-launch override (null = inherit project default); absent on older servers. */ autoLaunch?: boolean | null; /** Why an eligible item did not auto-launch; absent on older servers / when it launched cleanly. */ launchBlockedReason?: string | null; /** Whether the item is enabled; absent on older servers. */ enabled?: boolean; createdAt: string; /** Last-updated timestamp; used by `hula backlog list` for the grooming nudge. */ updatedAt?: string; /** * The client session id that originally pushed the item, when the server * records it. Used by `backlog-pull` to label a pulled item's `origin` * (`local` when it matches the current chat's session, `other-session` * when a different session pushed it, `dashboard` when absent). Optional for * backward compatibility with servers that don't expose it. */ clientSessionId?: string | null; } /** One open user follow-up (owner=user) as returned by `GET /api/v1/followups`. */ export interface ApiFollowupItem { id: string; projectId: string; projectFullName: string; text: string; why: string | null; /** `followup|action|prerequisite|local_setup`; absent on older servers. */ kind: string | null; /** True when the item blocks a merge (sorted to the top). */ blockingMerge: boolean; /** Origin-plan tracking name; optional/back-compat (absent on older servers). */ planTrackingName?: string | null; /** Origin-plan PR number; optional/back-compat. */ prNumber?: number | null; /** Origin-plan PR url; optional/back-compat. */ prUrl?: string | null; } /** Response of `GET /api/v1/followups` — the caller's open user follow-ups. */ export interface ListFollowupsResponse { items: ApiFollowupItem[]; } /** * Response of `GET /api/v1/plans/backlog` (hula-server `backlog-sync` plan). The * target `Plan` is resolved server-side by natural key; `items` are its backlog * items. `truncated` is set when the server capped the list. */ export interface ListBacklogItemsResponse { /** Resolved plan when a natural key was supplied; null for a project-only query. */ plan: { id: string; trackingName: string; issueNumber: number | null; } | null; items: ApiBacklogItem[]; truncated?: boolean; } /** * Request body for {@link HulaApiClient.pushBacklogItem}. The target `Plan` is * resolved server-side by natural key (`owner`/`repo` + `trackingName` and/or * `issueNumber`) — the CLI never learns an internal `Plan.id`. */ export interface PushBacklogItemRequest { owner: string; repo: string; trackingName?: string; issueNumber?: number; text: string; clientSessionId?: string | null; /** New project-owned fields (all optional; ignored by older servers). */ type?: string; detail?: string | null; blockedBy?: Array<{ kind: string; ref: string; resolvedAt?: string; }>; /** Auto-launch override: start the item's own plan when its last blocker resolves. */ autoLaunch?: boolean; } /** * One plan entry in a feature-group status response (hula-server PR #505, * `GET /api/v1/groups/:groupId`). */ export interface GroupPlanEntry { id: string; trackingName: string; trackingStatus: string; prNumber: number | null; prUrl: string | null; prMerged: boolean; deploymentStatus: string | null; prConflictStatus: string | null; githubState: string | null; issueNumber: number | null; githubUrl: string | null; updatedAt: string; project: { owner: string; name: string; fullName: string; }; latestTask: { id: string; status: string; startedAt: string | null; completedAt: string | null; } | null; } /** * Combined status for a feature group (hula-server PR #505). */ export interface GroupStatusResponse { groupId: string; total: number; completed: number; plans: GroupPlanEntry[]; } export interface ListPlansResponse { plans: ApiTrackedPlan[]; total: number; page: number; limit: number; totalPages: number; /** * The id of the authenticated user. Optional for backward compatibility * with older servers that did not expose this field. */ currentUserId?: string; } /** * Request body for {@link HulaApiClient.startPlanning} (`POST /api/v1/planning`). * `mode: 'local'` records a plan drafted locally and only needs the `request` * block; `mode: 'remote'` also carries the credential bundle (identical to the * launch request body) so the server's sandbox can clone the repo and run a * planning agent. Remote-only fields are optional here and omitted for local. */ export interface StartPlanningRequest { project: string; trackingName: string; mode: 'local' | 'remote'; request: { text: string; issueNumber?: number; folder?: string; base?: string; }; clientSessionId?: string | null; githubToken?: string; provider?: { type: string; authToken: string; }; envVars?: Record; containerResources?: { cpu: number; memory: number; disk: number; }; sandboxProfile?: { type: string; jdk?: number; }; updateNotificationUrl?: string; updateNotificationNameTag?: string; test?: boolean; handoff?: string; /** * "Defaults for everyone" PR-hygiene block (resolved from the repo config). * Forwarded so the planning sandbox's plan generation honours them (e.g. * Windows-safe filenames). Omitted when every behaviour is on. */ defaults?: { styleSurvey?: boolean; scopeLock?: boolean; contributingDocs?: boolean; environmentFailureOnce?: boolean; windowsSafeNames?: boolean; }; /** * Guest contribution mode, forwarded unresolved (`true | 'auto'`). The server * resolves `'auto'` against fork metadata. Omitted when the inert default * `false` so legacy request bodies stay byte-identical. */ contributionMode?: boolean | 'auto'; } /** * Response from {@link HulaApiClient.startPlanning}. The server upserts the plan * record by natural key and returns its id and current stage. */ export interface StartPlanningResponse { planId: string; trackingName: string; mode: 'local' | 'remote'; stage?: string; dashboardUrl?: string | null; wait?: string; /** Whether guest contribution mode engaged (resolved server-side). */ contributionModeEngaged?: boolean; /** Whether the target repo was detected as a GitHub fork. */ forkDetected?: boolean; } /** One clarifying question on a planning record. */ export interface PlanningQuestion { id: string; order: number; round: number; questionText: string; rationale: string | null; options: (string | { text: string; recommendation?: string; })[] | null; userResponse: string | null; answerText: string | null; /** Who recorded the answer: 'dashboard' | 'cli' | null (legacy). */ answerSource?: string | null; } /** Full planning record returned by {@link HulaApiClient.getPlanning}. */ export interface PlanningRecord { planId: string; trackingName: string; stage: string; planMode: string | null; requestText: string; requestFlags: Record | null; assessment: string | null; context: string | null; questions: PlanningQuestion[]; understanding: string | null; planText: string | null; /** Plain-language explanation ("The problem" / "The fix"); null for legacy plans. */ simpleExplanation: string | null; planPath: string | null; dashboardUrl: string | null; /** The plan's latest task, when it has one. `jobType` is the plaintext * planning job kind (never the raw params blob). */ task?: { id: string; status: string | null; failureStage: string | null; jobType?: string | null; } | null; } /** Lightweight stage poll returned by {@link HulaApiClient.getPlanningStage}. */ export interface PlanningStageResponse { planId: string; stage: string; taskStatus: string | null; questionCount: number | null; updatedAt: string; } /** * Public, hint-only shape of a stored credential set as returned by the server. * Never carries secret values — only hints and metadata — so it is safe to * print. Mirrors the hula-server `credential-sync` plan's public shape. */ export interface ApiCredentialSet { id: string; name: string; /** Masked GitHub token, e.g. `ghp_…k3Q9`, or null when none is stored. */ githubTokenHint: string | null; providers: Array<{ type: string; hint: string; }>; envVars: Array<{ key: string; secret: boolean; }>; /** Slack/webhook URL for launch update notifications. Non-secret; stored and returned verbatim. */ updateNotificationUrl?: string; /** Label prefixed to update notifications. Non-secret. */ updateNotificationNameTag?: string; /** How the set was created, e.g. `cli` or `web`. */ source: string; lastSyncedAt: string | null; updatedAt: string; } /** * Request body for {@link HulaApiClient.upsertCredentialSet} * (`POST /api/v1/credential-sets`). `providerKeys` maps a provider type to its * credential; `envVars` carries the forwarded variables, each marked secret so * the dashboard hides their values. */ export interface UpsertCredentialSetRequest { project: string; name?: string; githubToken: string; providerKeys: Partial>; envVars?: Array<{ key: string; value: string; secret: boolean; }>; /** Slack/webhook URL for launch update notifications. Non-secret; stored and returned verbatim. */ updateNotificationUrl?: string; /** Label prefixed to update notifications. Non-secret. */ updateNotificationNameTag?: string; } /** An open-source program invite as returned by the admin endpoint. */ export interface ApiOssInvite { id: string; /** The invite code (`hula login --invite `). */ code: string; /** Shareable program page: `https://www.hublaunch.site/open-source/`. */ link: string; label: string; githubLogin: string | null; durationDays: number; /** `open` | `claimed` | `revoked`. */ status: string; claimedAt: string | null; expiresAt: string; createdAt: string; } /** Request body for {@link HulaApiClient.createOssInvite}. */ export interface CreateOssInviteRequest { label: string; githubLogin?: string; durationDays?: number; } export declare class HulaApiClient { private client; constructor(config: HulaApiConfig); /** * List all tracked plans for authenticated user */ listPlans(filters?: { status?: string; priority?: string; project?: string; page?: number; limit?: number; /** Filter by a single lifecycle stage (forwarded as `stage`). */ stage?: string; /** * Include/exclude archived plans (forwarded as `archived`). Omit to use the * server default (active list excludes archived/completed). */ archived?: boolean; /** * Also return plans whose GitHub issue is closed (forwarded as * `includeClosed=true`). The server hides them by default; a merged PR * closes its issue, so the Archive view needs them. */ includeClosed?: boolean; /** Server-side sort, e.g. `stage` for earliest-stage-first (forwarded as `sort`). */ sort?: string; /** * Fetch exactly these plans by tracking name (forwarded as the comma-joined * `trackingNames` param). When set, the server bypasses the default * closed/archived exclusion — powers the resume-time status digest. */ trackingNames?: string[]; }): Promise; /** * Track a new plan */ trackPlan(data: TrackPlanRequest): Promise<{ plan: ApiTrackedPlan; }>; /** * Push one chat-scoped backlog item to the server, which resolves the target * `Plan` by natural key (see {@link PushBacklogItemRequest}) and attaches the * item to it. * * Deliberately does NOT call `maybeRethrowAsIncompatibleServer`: a 404 here is * an expected, routine outcome (the `Plan` isn't tracked yet, or the server * predates this brand-new endpoint) — not a version-incompatibility signal. It * surfaces as `NotFoundError` via the shared `handleError` interceptor, and the * calling script treats that as "not yet trackable" and retries later. */ pushBacklogItem(data: PushBacklogItemRequest): Promise<{ item: ApiBacklogItem; }>; /** * List the server-side backlog items for a plan, resolved by natural key * (`owner`/`repo` + `trackingName` and/or `issueNumber`). Calls * `GET /api/v1/plans/backlog` (hula-server `backlog-sync` plan). * * When both `trackingName` and `issueNumber` are given, `trackingName` wins — * the same precedence the server applies. Like {@link pushBacklogItem}, a 404 * (plan not tracked yet, or the endpoint absent on an older server) surfaces as * `NotFoundError` and is treated by the caller as "not yet pullable". */ listBacklogItems(params: { owner: string; repo: string; trackingName?: string; issueNumber?: number; }): Promise; /** * List the caller's open user follow-ups (the `hula todo` backlog). With * `owner`/`repo` the list is scoped to that project; without them it spans * every project the user belongs to. Calls `GET /api/v1/followups`. */ listFollowups(params?: { owner?: string; repo?: string; }): Promise; /** * Mark one of the caller's user follow-ups done (tier-b). Calls * `PATCH /api/v1/followups/:id` with `{ status: 'done' }`. */ markFollowupDone(id: string): Promise<{ ok: boolean; }>; /** * Update a single backlog item's status by its server-side id. Calls * `PATCH /api/v1/plans/backlog/:id` (hula-server `backlog-sync` plan). Used by * `backlog-pull` to push a locally-completed item back to the dashboard. */ updateBacklogItemStatus(id: string, status: 'open' | 'done'): Promise<{ item: ApiBacklogItem; }>; /** * Fetch the editable launch prefill for a `ready` `plan` backlog item: the * prompt (text + detail), the owning project (`owner/repo`), a suggested * tracking name, and the folder. Calls `GET /api/v1/plans/backlog/:id/launch`. */ getBacklogLaunchPrompt(id: string): Promise<{ prompt: string; project: string; trackingName: string; folder: string | null; item: ApiBacklogItem; }>; /** * Link an already-started plan back to the backlog item it was launched from: * sets `launchedPlanId` and moves the item to `planned`. Calls * `POST /api/v1/plans/backlog/:id/launch` with the started plan's id. Keeping * the plan-start itself on the existing planning path avoids duplicating * credential handling here. */ markBacklogLaunched(id: string, body: { planId: string; }): Promise<{ plan: { id: string; trackingName: string; issueNumber: number | null; }; }>; /** * Get detailed status for a specific plan */ getPlanStatus(owner: string, repo: string, issueNumber: number): Promise; /** * Update plan priority */ updatePlanPriority(planId: string, priority: string): Promise<{ plan: ApiTrackedPlan; }>; /** * Archive (or un-archive) a plan — a soft close that **retains** the plan * record (unlike {@link untrackPlan}). On `archived: true` the server also * closes the plan's GitHub issue and moves it to the Archive view; on * `archived: false` it un-archives, and the plan returns to its derived * active stage. * * Uses the same `PATCH /api/v1/plans/:id` shape as * {@link updatePlanPriority}. Against an older server that does not support * the `archived` field the PATCH typically 400s/404s, surfaced to the caller * as an `ApiError`/`NotFoundError` so it can report "server does not support * archive." */ archivePlan(planId: string, archived: boolean): Promise<{ plan: ApiTrackedPlan; }>; /** * Record one plan-level evidence record (part 02). The server validates + * stamps it, appends it to `Plan.evidence`, and auto-reassesses, returning * the stored record plus the recomputed assessment. A 404 (plan absent, or an * older server without this route) surfaces as `NotFoundError` so callers can * report "unsupported against this server" and continue. */ recordEvidence(planId: string, record: PlanEvidenceInput): Promise; /** * Trigger a reassess for a plan (part 02): recompute the authoritative * assessment from the full `Plan.evidence` set and return it. A 404 (plan * absent, or an older server without this route) surfaces as `NotFoundError`. */ reassessPlan(planId: string): Promise; /** * Submit a contribution plan's fork draft PR upstream. Posts the caller's * local GitHub token (the HubLaunch GitHub App is not installed on the * upstream repo, so a user token is required) to * `POST /api/v1/plans/:id/submit-upstream`; the server reads the fork PR, * opens the upstream PR verbatim (rewriting only issue references) and * advances the contribution stage. */ submitUpstream(planId: string, githubToken: string): Promise<{ upstreamPrNumber: number; upstreamPrUrl: string; }>; /** * Untrack a plan */ untrackPlan(planId: string): Promise<{ success: boolean; }>; /** * Fetch the latest Task's logs for a tracked plan (plain text). * Calls GET /api/v1/logs/:planName. * * The URL string `/api/v1/logs/` is preserved across the server * rename; only the underlying resolution semantics changed (the server * now resolves the name to a Plan and reads its latest Task's log). * * When `options.include` is provided it is forwarded as the `include` * query param (`logs` | `lessons` | `all`), selecting the persisted run * log, lessons markdown, or both instead of the default live output tail. * When omitted, no `include` param is sent and the server returns the * live `lastOutput` tail (unchanged default behavior). */ fetchTaskLogs(planName: string, options: { type?: 'log' | 'output'; lines?: number; project: string; include?: 'logs' | 'lessons' | 'all'; }): Promise; /** * Fetch the multi-key info object for a tracked plan (server PR #421). * Calls GET /api/v1/info/:planName. * * @param planName - plan tracking name (branch name) * @param options.include - one or more of 'logs'|'lastLogs'|'diff'|'initial'|'lessons'|'verify' * @param options.project - 'owner/repo' (required by the server) * @param options.lines - trailing-line count for the `lastLogs` key only * @returns object with exactly the requested keys. Text keys are a string or * null; the `verify` key is a {@link MergeAssessment} object or null. */ fetchInfo(planName: string, options: { include: Array<'logs' | 'lastLogs' | 'diff' | 'initial' | 'lessons' | 'verify'>; project: string; lines?: number; }): Promise>; /** * Fetch the latest Task's run status for a tracked plan. * Calls `GET /api/v1/ralph-run/:issueName/status`. * * Surfaces the stored {@link MergeAssessment} (`mergeAssessment`) that * `hula approve` gates on and the launching `clientSessionId`. An older server * omits `mergeAssessment`, which the caller treats as "assessment unavailable". * * @param trackingName - plan tracking name (branch name) * @param project - 'owner/repo' (required by the server) */ getRunStatus(trackingName: string, project: string): Promise; /** * Fetch combined status for a feature group (hula-server PR #505). * Calls GET /api/v1/groups/:groupId. * * The endpoint is scoped by the caller's project membership (not by the API * key's project), so any member repo's key works. A 404 is intentionally * ambiguous server-side — it means either the group does not exist OR the * caller is not a member of any of its projects (the caller cannot * distinguish the two). A 404 can also mean the server predates feature * groups; callers surface that possibility in the user-facing message. * * @param groupId - The opaque group id (validated `^[A-Za-z0-9_-]{1,64}$`). * @returns The group's `total`/`completed` counts and per-plan entries. */ fetchGroupStatus(groupId: string): Promise; /** * Check the authenticated user's subscription plan. * Calls GET /api/v1/user/subscription. */ checkUserTier(): Promise<{ plan: 'free' | 'pro'; subscriptionStatus: string | null; currentPeriodEnd: string | null; }>; /** * Create an open-source program invite (ADMIN only; 403 otherwise). * `POST /api/v1/admin/oss-invites`. */ createOssInvite(req: CreateOssInviteRequest): Promise; /** * List open-source program invites, newest first (ADMIN only). * `GET /api/v1/admin/oss-invites`. */ listOssInvites(): Promise; /** * Start (or upsert) a planning record. `POST /api/v1/planning`. */ startPlanning(req: StartPlanningRequest): Promise; /** * Fetch the full planning record. `GET /api/v1/planning/:planId`. */ getPlanning(planId: string): Promise; /** * Fetch just the planning stage (cheap poll for `hula plan wait`). * `GET /api/v1/planning/:planId?fields=stage`. */ getPlanningStage(planId: string): Promise; /** * Upload the codebase-analysis context document (local mode). * `PUT /api/v1/planning/:planId/context`. * * In local planning mode the CLI (`hula plan context`) splits the plan-initiate * output layout itself before calling this: the `## Assessment` body becomes * `assessment`, the fenced json in `## Questions` becomes `questions`, and the * remaining sections become `context` — the same split the server does in * remote mode, so the server records one `PlanQuestion` row per question. */ putPlanningContext(planId: string, body: { context: string; assessment?: string; questions?: unknown; expectPlanMode?: 'local' | 'remote'; }): Promise<{ planId: string; stage: string; }>; /** * Record a round of answers (and any follow-up questions). * `PUT /api/v1/planning/:planId/answers`. */ putPlanningAnswers(planId: string, body: { answers: Array<{ questionId?: string; order?: number; userResponse: string; answerText: string; }>; newQuestions?: Array<{ questionText: string; rationale?: string; options?: (string | { text: string; recommendation?: string; })[]; }>; expectPlanMode?: 'local' | 'remote'; /** Passthrough acknowledge flag (`hula plan answer --acknowledge-assessment`). */ acknowledgeAssessment?: boolean; }): Promise<{ stage: string; unanswered: number; }>; /** * Store the confirmed understanding. `PUT /api/v1/planning/:planId/understanding`. */ putPlanningUnderstanding(planId: string, body: { understanding: string; expectPlanMode?: 'local' | 'remote'; }): Promise<{ planId: string; stage: string; }>; /** Replace the draft End-to-End Test Cases block in a plan's context. */ putPlanningCases(planId: string, body: { cases: unknown; expectPlanMode?: 'local' | 'remote'; }): Promise<{ stage: string; caseCount: number; }>; /** * Store the generated plan text + its repo-relative path. * `PUT /api/v1/planning/:planId/plan-text`. */ putPlanningPlanText(planId: string, body: { planText: string; planPath: string; expectPlanMode?: 'local' | 'remote'; planStorage?: 'branch' | 'server'; }): Promise<{ planId: string; stage: string; }>; /** * Fetch a plan's stored plain-language explanation. Reads the full planning * record (which already includes `simpleExplanation`) and returns just that * field. Never calls an LLM — pure storage retrieval. * `GET /api/v1/planning/:planId`. */ fetchExplanation(planId: string): Promise; /** * Store (or overwrite) a plan's plain-language explanation. * `PUT /api/v1/planning/:planId/explanation`. Editable at any stage; the * server truncates over-length input to 20,000 chars. */ updateExplanation(planId: string, text: string): Promise<{ simpleExplanation: string; }>; /** * Hand the plan off to the server to draft and launch automatically. * `POST /api/v1/planning/:planId/draft-and-launch`. */ draftAndLaunchPlanning(planId: string, body: Record): Promise<{ taskId: string; stage: string; dashboardUrl: string; }>; /** * Create or update this project's stored credential set. * `POST /api/v1/credential-sets`. Returns the public (hint-only) set plus * whether it was newly created. * * A 404 here means the server predates this endpoint; it surfaces as * `NotFoundError` via the shared interceptor and the credential-sync helper * maps it to `server-unsupported`. Deliberately does NOT call * `maybeRethrowAsIncompatibleServer`: the URL is not the `/api/v1/plans` * collection, so that guard does not apply. */ upsertCredentialSet(req: UpsertCredentialSetRequest): Promise<{ set: ApiCredentialSet; created: boolean; /** * Names (never values) of env vars a full-replace upsert cleared from the * set. Optional: older servers omit it, so callers must treat a missing * value as `[]` (no removal to warn about). */ removedEnvVarKeys?: string[]; }>; /** * List this project's stored credential sets (hints only). * `GET /api/v1/credential-sets?project=`. */ listCredentialSets(project: string): Promise<{ sets: ApiCredentialSet[]; }>; /** * Delete a stored credential set. * `DELETE /api/v1/credential-sets?project=&name=`. A 404 (nothing stored) * surfaces as `NotFoundError` for the caller to treat as a no-op. */ deleteCredentialSet(project: string, name: string): Promise; /** * Detects when a 404 came from the `/api/v1/plans` collection endpoint * and rethrows as an `IncompatibleServerError`. A collection 404 can * only mean the server predates the Plan/Project/Task rename. Per- * resource 404s (e.g. `/api/v1/plans/`) keep `NotFoundError` * semantics and are handled by `handleError()`. */ private maybeRethrowAsIncompatibleServer; /** * Handle API errors with user-friendly messages */ private handleError; } export declare class ApiError extends Error { statusCode?: number | undefined; constructor(message: string, statusCode?: number | undefined); } export declare class AuthenticationError extends ApiError { constructor(message: string); } export declare class AuthorizationError extends ApiError { constructor(message: string); } /** * Raised on an HTTP 402 from a billing-gated endpoint (planning / launch). The * server's JSON body carries a human-readable `error` and an optional * `upgradeUrl` the CLI surfaces so the user can add credits / upgrade. */ export declare class PaymentRequiredError extends ApiError { readonly upgradeUrl?: string; constructor(message: string, upgradeUrl?: string); } export declare class NotFoundError extends ApiError { constructor(message: string); } export declare class ConflictError extends ApiError { constructor(message: string); } export declare class RateLimitError extends ApiError { constructor(message: string); } export declare class ServerError extends ApiError { constructor(message: string); } export declare class TimeoutError extends ApiError { constructor(message: string); } export declare class NetworkError extends ApiError { constructor(message: string); } /** * The connection dropped mid-request (`ECONNRESET`, or a 2xx whose body never * finished arriving). Transient: idempotent reads retry it. */ export declare class ConnectionDroppedError extends NetworkError { constructor(); } /** * Raised when hub-launch detects that the connected hula-server predates * the Plan/Project/Task rename (NoStackApp/hula-server PR #159) — i.e. a * 404 on the `/api/v1/plans` collection endpoint. */ export declare class IncompatibleServerError extends ApiError { constructor(); } //# sourceMappingURL=HulaApiClient.d.ts.map