# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [1.44.2] - 2026-11-05

### Added

- **`hula contribute <upstream>`.** One command to start contributing to a repo you don't own: it forks the upstream, clones the **fork** (`origin` = fork, `upstream` = parent), writes a minimal guest config (`contributionMode: true`) without running full `hula init`, adds `.git/info/exclude` entries that keep HubLaunch artifacts off the fork, and guides the GitHub-App install + `hula login` inside the clone. It stops and points at the normal owner setup when you already have push access, falls back to `gh repo fork` if the API fork fails, and is idempotent (reuses an existing fork and clone directory). Accepts `owner/repo`, a repo URL (with or without `.git`), or an issue URL; `--dir <path>` and `-y, --yes` (non-interactive) options. Documented on the website (`/docs#contributing-to-others`, linked from a new header/footer "Contribute" entry) and in the README, `docs/commands.md`, and the contributing-to-others guide.
- **`hula reassess <trackingName>`.** Recomputes a plan's merge confidence from all of its recorded evidence (the sandbox run, `/hula-e2e` harness reports, local fix runs, CI results) and prints the newest assessment with the evidence behind it; `--raw` prints it as one JSON line. Evidence counts toward the score only when its commit SHA matches the PR head, and local results are capped at the moderate tier. `hula info --verify` now prefers this plan-level assessment and lists its evidence sources, falling back to the per-run assessment when no reassess has run yet. A pushed local `fix-commit` is now recorded as local evidence. Against an older server the command reports that reassess is unsupported.
- **Grounding Loop (opt-in).** `hula launch <name> --maxGroundingRounds <n> [--groundUntil <0-100>]`, or `groundingLoop: { groundUntil, maxGroundingRounds }` in `hublaunch.config.js`, keeps grounding the merge assessment with newly executed evidence until it reaches the target confidence. Each round picks the weakest assessment category, writes one runnable check per concrete uncertainty, executes them in the sandbox, and reassesses. Passing agent-written checks count only as weak evidence; a failing check blocks the assessment. The loop stops at the target, on a failed check, when nothing testable remains, or at the round cap. Off by default; with neither flag nor config set, the launch request is unchanged. See `docs/advanced.md`.

### Changed

- **`/hula-plan` clarifying questions are plain-language again.** A single shared plain-language rule (write for a slightly-below-average non-technical reader — describe what the user will see, no file paths/function/library names/acronyms, one idea per question, a "what this means for you" rationale) now governs every place that generates user-facing questions: the remote `plan-initiate`, `planning`, `proceed`, and `plan-generate` instruction templates and the `/hula-plan` and `/hula-followup` skills. The two fixed questions (the assessment go/no-go and the save-goals prompt) read plainly while keeping their exact option strings, letters, and detection prefixes — so the answer classifier and launch gate are unchanged and stored plans keep working. The honest assessment now opens with a 1–2 sentence plain-language verdict summary before the score table, and the dashboard shows it above the table. The draft End-to-End Test Cases review shows each case in plain words (hiding the internal driver tag and criterion id behind a "Show technical details" toggle) and `/hula-followup` now accepts natural-language case edits ("drop the second one…") alongside the typed `strike N:` / `edit N:` / `add:` syntax, still writing the same `{version, cases}` JSON. No runtime contract changed (assessment JSON, end-to-end `{version, cases}` JSON, `hula plan validate-cases`, and the worker checks are all byte-stable).
- **An unparsable config is now a fatal error.** An existing `.hublaunch/hublaunch.config.*` that cannot be loaded (for example CommonJS `module.exports` in an ESM package) now stops with an actionable error instead of silently running with defaults. Defaults apply only when no config file exists. `hublaunch.config.cjs` is now a recognised filename, so the suggested "rename to `.cjs`" fix works. The JSON-on-stdout script readers report the error on stderr.
- **Go/no-go answers are read more reliably.** The remote-planning go/no-go gate now reads a parenthesised option letter (`(B)`, `… (B)`), resolves "as recommended" to the option actually marked as recommended instead of always A, and accepts an unconditional "Other" proceed. Conditional answers ("go ahead if…") and conflicting letters still stop the plan. When `hula plan answer` is rejected, the error lists every accepted answer form, and `/hula-followup` documents the full contract.
- **Remote planning forwards `e2e.envVars`.** `hula plan start`, `hula plan draft-and-launch`, and the default credential-set sync now forward and save the union of top-level `envVars` and `e2e.envVars`. Dashboard and phone launches of projects that declare variables only under `e2e.envVars` now receive them.
- **`hula todo` shows plan and PR context.** Each follow-up lists its plan tracking name and PR number (`my-plan #42`) with a plain-language reason. Whether an item blocks merge is computed live, so it stops blocking as soon as its plan's PR merges or closes, and end-user-step items close automatically when the PR merges.
- **Own-repo PR bodies always disclose AI assistance.** Every PR HubLaunch opens on your own repo now ends with a disclosure paragraph ("made with AI assistance, orchestrated by HubLaunch; a human reviewed and is accountable for it"), including partial-progress runs. The Summary, Key Changes and file stats come from the run's actual diff; the plan's explanation is kept but relabelled "In plain terms (the plan's goal)" so it reads as intent rather than a description of the diff.

### Fixed

- **End-to-end cases gate no longer rejects struck cases.** `hula plan validate-cases` (and the server's identical check at `plan.generated`) only requires an *active* case's `criterion` to name an acceptance criterion. A struck case is out of scope by declaration, so a label such as `"Deferred: cohort funnel"` no longer fails the whole plan; it still needs a `reason` and still cannot stand in for an acceptance criterion it does not match.
- **Planning instructions spell out the criterion rule.** The plan and plan-generate templates now say to cite the AC id (`"AC1"`), never a paraphrase, and to drop a deferred feature's criterion and case together instead of keeping a `"Deferred: …"` placeholder — the two shapes that made every remote plan-generate fail on 2026-11-03.
- **`beforeLaunch` hook values no longer leak into saved credentials.** Launch env vars are snapshotted before the hook runs, so a per-launch value the hook sets (such as a branch `DATABASE_URL`) is still sent with that launch but never saved to your credential set. The "Removed from default" notice no longer reports hook-supplied keys as removed.

## [1.44.1] - 2026-11-03

### Added

- **`hula submit-upstream <trackingName>`.** Opens the upstream pull request for a contribution plan's fork draft PR. It copies the fork PR's current title and body verbatim, rewriting only the issue reference ("Upstream issue #N" becomes `Closes #N`), then moves the plan to awaiting maintainer review. Submitting upstream is always a deliberate action; it is never triggered automatically. The dashboard has a matching **Submit upstream** button. `--project <owner/repo>` overrides the git remote.
- **Fork-aware contribution launches.** A `--contribution` launch against a repo you cannot push to now creates or reuses your fork, enables Actions on it, mutes its notifications, and opens the draft PR there. A contribution plan carries its own stage (`draft_on_fork`, `submitted_upstream`, `awaiting_maintainer`, `changes_requested`, `merged_upstream`, `declined`, `withdrawn`). Upstream outcomes are detected by polling the upstream PR with your token, and your fork's default branch is fast-forwarded on merge or decline. Approve, auto-merge and merge-confidence controls are hidden and rejected server-side for fork PRs.
- **`hula archive <trackingName>`** archives a plan by tracking name, for contribution plans whose fork has no issue number. The issue-number form still works. New `--project <owner/repo>` option scopes the lookup.
- **`hula plan start --base <branch>`** (also `/hula-plan --base`) targets a non-default base branch: the implementation run's feature branch is cut from `<branch>` instead of the repo's default branch. It is persisted on the plan and validated against GitHub at draft-and-launch, which accepts `--base` as an override.
- **`--acknowledge-assessment`** on `hula plan answer` and `hula plan draft-and-launch` proceeds past a `stop` or `review` assessment verdict.
- **`e2e` block in `hublaunch.config.js`.** Declares what a launch needs to run the project's own end-to-end tests in the sandbox: `tools` (installed from the worker's catalogue, e.g. `psql`), `setup` commands, `envVars` (names or `'all'`, same rules as top-level `envVars`), a `ready` command polled until it exits 0, `timeoutMinutes`, `readyTimeoutMinutes`, and `postReport` to post the report as a PR comment. Unknown keys are a config error. Any precondition failure marks the stage "not executed", never a pass.
- **Backlog `--auto-launch`.** `hula backlog add --auto-launch` and `hula script backlog-add --auto-launch` mark a `plan` item to start its own remote planning the moment its last blocker resolves (for example when a blocking PR merges). Opt-in per project, default off. `hula backlog list` and `backlog-list` show a derived `blocked` status with the unresolved blockers and a `blocked` count; blocked items are listed by default since they are waiting work.
- **`hula launch --logs --full <name>`** returns the whole stored run log instead of the tail.
- **`hula info` shows grounded verification evidence:** claim/evidence/verdict rows (with commit and carried-forward run), Tier-1 findings (test integrity, edited-to-pass tests, unresolved references, unplanned files) and end-to-end report cases.
- **`hula todo`** lists your open user follow-ups: things a person must do that the system noticed on its own runs (a missing local env var, a manual end-user check, an uncovered flow), ordered merge-blocking first. `--project owner/repo` scopes to one project; `--done <id>` closes one. The same list ends `hula info` and shows on the dashboard as "User follow-through needed".

### Changed

- **Plan filenames use `YYYY-MM-DD-HH-MM-<slug>.md`** (hyphen between hours and minutes). A colon is invalid in Windows paths and broke `actions/checkout` on `windows-latest`. Existing plans are unaffected.
- **Repos that gitignore `.hublaunch/` now work for planning.** The plan is stored on HubLaunch instead of being committed to the branch, and seeded into the sandbox at launch. `hula upload --branch` detects the ignored path, prints an ℹ️ line and treats it as success.
- **Merge confidence is computed once, server-side, from evidence** and is reproducible: the same evidence always yields the same number. Evidence is carried across runs on a PR when the cited files are unchanged.
- **Environment failures are no longer scored as code failures.** The sandbox finds tools on `~/.bun/bin`, pnpm/npm global bins, `~/.local/bin` and `~/.cargo/bin`, stops check/build/regression loops on a diagnosed environment failure instead of retrying, and marks the regression step "not executed" rather than passed. The PR body warns when the repo pins a Node, Python or Bun version (`.nvmrc`, `.node-version`, `engines.node`, `.tool-versions`, `.python-version`, `.bun-version`) different from the one the sandbox ran.
- **Contribution-mode runs get a style survey and a polish pass.** Before implementation a dedicated pass surveys the repo's idioms and injects a `## Conventions` block; afterwards a polish pass tidies only the lines the run added, using the repo's own formatter, and never rewrites moved maintainer code. Both skip rather than fail when over budget.
- **Contribution branch names must not embed the issue or PR number.** `hula launch` refuses such a name before spending a sandbox and suggests one without it. The reference belongs in the PR body.
- **Go/no-go assessment answers are classified server-side** and accept the option letter, a bare `a`/`b`/`c` or `1b`-style reply, the option text, or clearly-mapping prose, failing closed when ambiguous. The `/hula-plan` and `/hula-followup` skills keep the leading letter in `answerText`. Follow-up rounds re-open the plan at stage `QUESTIONS`, and draft-and-launch waits until the understanding is confirmed.

### Fixed

- **`envVars: "all"` no longer fails every launch when `.env` holds a platform-prefixed key.** The CLI now skips names under the server's reserved prefixes (`HULA_`, `RALPH_`, `DAYTONA_`, `ANTHROPIC_`, `CLAUDE_`, `AWS_`, `OPENROUTER_`, `CODEX_`, `GIT_AUTHOR_`, `GIT_COMMITTER_`) instead of sending them and getting HTTP 400 `Reserved environment variable(s) not allowed`. An explicit list that names one is refused with a message naming the prefix rule. Applies to `hula launch`, `hula schedule` and remote planning credentials.
- `hula plan start` no longer loses `--issue` and `--folder` on the way to the server.

### Documentation

- `docs/contributing-to-others.md` now covers the automatic fork, `hula submit-upstream`, the contribution lifecycle stages and archiving by tracking name.

## [1.44.0] - 2026-11-02

### Added

- **Project-level backlog: `hula backlog add | list | launch` and the `/hula-backlog` skill.** Backlog items now belong to the project, so they survive when a planning chat is archived. Each item has a type (`plan`, `local`, `note` or `external`), optional markdown detail (`--detail-file`), and blockers (`--blocked-by kind:ref` with kind `plan`, `pr`, `item` or `issue`). Items move from `parked` to `ready`, `planned` and `done`. A `plan` item whose blockers are all resolved becomes `ready`, but nothing starts on its own. `hula backlog launch <itemId> --yes` starts a remote plan and links it to the item. `hula script backlog-add` accepts the same `--type`, `--detail-file` and `--blocked-by` flags.
- **Stored contribution guidelines.** In contribution mode, HubLaunch reads the target repo's contribution policy and stores it per project: CONTRIBUTING files, the PR template, the code of conduct, the README, agent-instruction files and commit trailers. It refreshes the record when it's empty or older than 90 days. A repo whose policy bans AI contributions stops a contribution-mode launch at preflight. A policy that requires disclosure makes the guest PR say that AI assistance was used. Detected requirements (DCO, CLA, discuss-first, changelog policy, PR template) are checked before the PR opens. You can correct the stored policy on the project's Contribution guidelines card in the dashboard.
- **End-to-End Test Cases in plans.** New plans end with a `## End-to-End Test Cases` section: one JSON block listing what would prove the plan works, with every acceptance criterion mapped to at least one case. Plan-initiate drafts the cases next to the clarifying questions, and plan-generate keeps them unless you strike one. Plans without the section work as before.
- **`hula plan validate-cases [plan] [--file <path>]`** checks a plan's cases against its acceptance criteria and prints a JSON result. It reads the plan from a path or stdin and exits 1 when a criterion has no case, a case has an unknown driver or criterion, or a struck or `not-testable` case has no reason.
- **`hula plan cases <planId> --file <cases.json|plan.md>`** saves your edits to a plan's draft cases, from a JSON case list or a markdown file holding the cases section. `/hula-followup` now shows the draft cases with the Understanding and lets you add, edit or strike them. A struck case stays in the list with its reason.
- `hula upload` warns, without blocking, when the plan's cases fail validation.

## [1.43.1] - 2026-10-31

### Added

- **Issue/plan split — `issuePlanSplit` config key.** Accepts `true | false | "auto"`, default `"auto"`. When the split is on, `hula create` posts a short, human-readable issue (what is wanted and why, taken from the plan's summary and problem statement, with links removed) and keeps the plan itself private. If the repo has an issue template, the draft is rendered into it, and the template's title prefix (e.g. `[Feature] `) is added to the title. Without `--name`, `hula create` shows the draft first and asks you to **Post**, **Edit in editor** or **Cancel**; nothing is posted until you choose Post. Split issues carry no `Fixes #N` line pointing at themselves, and the local plan file is kept when the plan isn't on `origin/main`. `"auto"` splits only when you don't own the target repo or contribution mode is on, so the owner flow is unchanged.
- **Waiting window — `waitForMaintainerDays` config key and `--skip-wait` flag.** When the value is above 0, `hula launch` won't start building from an issue (`--issue`, a PR's linked issue, or the issue `hula create` posted) until that many days have passed since the issue was opened, or until a maintainer (owner, member or collaborator) replies. Until then it exits and says when the window ends. A maintainer's reply is shown to you, shortened, and you confirm before the build starts. It is never passed to the agent. Pass `--skip-wait` for small fixes. Resumes and relaunches aren't held. `0` (the default) means no wait.
- **`linkGuidelines` config key and `--link-guidelines` flag.** Makes the guest PR disclosure cite the Responsible Contribution Guidelines instead of the default product-free line.
- `hula launch --pr` accepts a pull request whose head is on a fork when contribution mode is on. It still rejects one otherwise.

### Changed

- **Guest PR disclosure is now product-free by default.** It reads "This change was made with AI assistance and reviewed by a person before this pull request was opened." It no longer names HubLaunch or includes a link.
- **Contribution-mode pull requests open as drafts,** so you can review them before asking maintainers to look. Owner-flow PRs are unchanged.
- Contribution launches are limited to 5 plans per hour per user on one repository. Relaunching the same plan doesn't count again, and rejected requests don't use a slot.

### Fixed

- **Contribution-mode launches open their pull request again.** Squashing to one commit rewrote the plan branch the CLI had already pushed, so the push was rejected and no PR was opened. The sandbox now replaces that branch safely: only history the run already has, never a branch someone else pushed to.
- **Contribution-mode PRs no longer delete the project's own files** at HubLaunch paths, such as its own `.claude/` settings or `ralph.md`. Those paths are kept exactly as they are on the base branch, and only files the run added are left out. A run that changed nothing but HubLaunch files now stops with a clear message instead of opening an empty PR.

## [1.43.0] - 2026-10-29

### Added

- `hula explain [target] [--update "<text>"] [--json]` — view or edit a plan's
  plain-language explanation ("The problem" / "The fix"). Resolves a tracking
  name, a plan id, or the current git branch. The CLI is pure storage and never
  calls an LLM.
- `/hula-explain` skill — shows a plan's stored explanation, generating and
  persisting one (via `hula explain … --update`) when it is missing.
- **PR-hygiene `defaults` — five "defaults for everyone" behaviours, all on by default.** A new `defaults` block in `hublaunch.config.js` lets you opt any of them out per-behaviour: `styleSurvey` (survey the repo's dominant idioms and write in the majority style, explaining any departure in one PR-body sentence), `scopeLock` (touch only the files the fix needs — no drive-by refactors/dependency bumps/unrelated fixes), `contributingDocs` (read and obey `CONTRIBUTING.md` and any PR template — DCO `git commit -s`, CLA, discuss-first, changelog policy), `environmentFailureOnce` (record a diagnosed environment failure — missing system library, no display, no root — once as "could not run, and why" instead of re-running it up to five times), and `windowsSafeNames` (generate plan/lessons filenames as `YYYY-MM-DD-HH-MM-<slug>.md` with a hyphen instead of `:`). An absent block means all five are on; the block is forwarded on `hula launch` and `hula plan start`. See [docs/advanced.md](docs/advanced.md#pr-hygiene-defaults-defaults).
- **Contribution (guest) mode — `contributionMode` config key + `--contribution` flag.** Makes HubLaunch's output acceptable when it builds a PR on a **fork of someone else's** repo. When engaged, a launch keeps every HubLaunch artifact off the PR branch (nothing under `.hublaunch/`/`.claude/`, no `ralph.md`/`launch.md`/plan file/lessons), squashes to **one commit** in the repo's own message convention (Conventional Commits vs sentence case, auto-detected) with a `Signed-off-by` trailer **iff** `CONTRIBUTING.md` requires a DCO and **no** issue/PR number in the commit or branch, never adapts the repo to the sandbox, and writes a **guest PR body** (plain summary, the issue/PR reference, a Verification section reporting only what actually ran and what could not, and a short HubLaunch+AI disclosure — no "Generated by HubLaunch" banner, no ⚠️ boxes, no plan links). `contributionMode` accepts `true | false | "auto"` and **defaults to `false`** (opt-in this release); `--contribution` forces it on and overrides a `false` config; `"auto"` engages only on a GitHub fork and prints a one-time notice the first time it engages per fork. An off/absent value is omitted from the request body, so legacy launches stay byte-identical. See [docs/contributing-to-others.md](docs/contributing-to-others.md) and [docs/advanced.md](docs/advanced.md#contribution-guest-mode-contributionmode----contribution). (The key's default flips to `"auto"` in a future release.)
- `afterRevert` project hook — the symmetric "rebuild" counterpart of `afterMerge`. It runs after `hula revert --merge` squash-merges the revert PR, so a project can re-provision whatever its `afterMerge` hook tore down when the original PR merged (e.g. recreate a per-launch database branch). It is **best-effort** (a missing file or a failing hook logs but never fails the revert) and fires **only** in the `hula revert --merge` path — a plain `hula revert` with `afterRevert` configured only warns that the hook will not run automatically until the revert PR is merged. The hook receives `{ revertedPrNumber, revertedPrTitle, revertPrNumber, revertPrUrl, reopenedIssueNumber?, branch? }`, and the `hula revert --json` output gains `afterRevertHook` (`"ran" | "failed" | "skipped" | "not-configured"`) and `afterRevertHookWarning`. `hula init` now scaffolds a side-effect-free `.hublaunch/hooks/afterRevert.ts` (a local-only active example plus commented-out Slack and Neon+Vercel re-provision ideas) and wires `afterRevert` into the generated config as a real (uncommented) line, mirroring the `afterMerge` scaffolding; a project that already has `hooks.afterRevert` configured or an existing `afterRevert.ts` file is left untouched.
- **Resume-time status digest for Claude Code chats.** When a chat is resumed, compacted, or forked, a new `SessionStart` branch of `hula session-hook` prints a short digest of what changed server-side for the plans that chat planned or launched (stage, PR/merge, deployment, archived/closed). It makes one request with a hard 1.5s timeout and prints nothing on any failure. `hula init` registers the hook (matcher `resume|compact|fork`) idempotently next to the existing `PreToolUse` hook. A new `hula status [trackingName...]` command and `/hula-status` skill show the same digest on demand.

### Changed

- **Adopting the assessment's safer alternative can now lift the launch gate.** The assessment additionally scores the plan as it would look with its `### Safer alternative` applied; answering the go/no-go question with `B)` adopts that alternative, and when its scores clear the gate the launch proceeds without a "Proceed anyway" override. Any other go/no-go answer withdraws the adoption.
- **Generated plan filenames are now Windows-safe by default** (`YYYY-MM-DD-HH-MM-<slug>.md`, a hyphen between hour and minute). The previous `HH:MM` colon form is an invalid path character on Windows and broke `actions/checkout` on `windows-latest`. Already-committed colon-named plans still resolve (both forms are parsed); nothing is renamed. Set `defaults.windowsSafeNames: false` to keep the colon form. Because the PR-hygiene `defaults` ship **on**, existing configs get this and the other hygiene behaviours without opting in — these are hygiene fixes, not opt-in features.

## [1.42.0] - 2026-10-27

### Added

- Install `hula` without Node: `curl -fsSL https://hublaunch.site/install.sh | sh` downloads a standalone binary for your platform from the latest stable release of [NoStackApp/hub-launch](https://github.com/NoStackApp/hub-launch/releases) and verifies it against the release's `SHA256SUMS` (a mismatch aborts and removes the download). It installs to `~/.hublaunch/bin` (override with `HULA_INSTALL_DIR`; never uses sudo) and also installs the global Claude Code slash commands. `npm install -g hub-launch` remains available for Node users. The binaries are currently unsigned, so macOS or Windows may ask you to allow them once.
- `hula update` on a curl-installed binary now updates the binary itself. It downloads the matching binary from the latest stable release, verifies it against `SHA256SUMS`, replaces itself atomically and refreshes the slash commands; it exits cleanly when already up to date. `--manager` forces the npm/pnpm path. A standalone binary's update notice now reads those releases instead of npm.
- `hula install-claude-commands` installs or refreshes the global Claude Code slash commands (`~/.claude/commands/hula-*.md`). This is the step the npm package's postinstall performs, available for installs that have no postinstall.
- `hula credentials sync` and the automatic credential sync now name the env vars they stored (names only, up to 8, then `+N more`) or say `no env vars`. They also warn when a sync forwards none: such a sync clears previously stored env vars, and dashboard/phone launches and the sandbox `beforeLaunch` hook only see forwarded variables. The warning lists which stored env vars the sync removed.

### Changed

- Dashboard and phone launches, and retries of CLI-started plans, use your current `default` credential set. Run `hula credentials sync` after changing `envVars` or rotating a token.
- A missing `@playwright/test` now prints install guidance instead of a raw module-not-found error (browser launcher and preview).

### Fixed

- HubLaunch runs no longer commit the sandbox runtime files `.hublaunch/.current-stage` and `.hublaunch/.metrics-cpu` into feature branches. `hula init` adds them to `.gitignore` and, when they are already tracked, prints the `git rm --cached …` command to untrack them.

### Documentation

- The README and docs lead with the curl install, including a download-review-run variant, and list npm/pnpm as the Node alternative. Node.js ≥ 18 is now listed as required only for the npm install.

## [1.41.0] - 2026-10-22

### Added

- Backlog sync is now two-way and visible from any machine. `/hula-approve` pulls the plan's backlog from the dashboard, merges it into the chat's file, pushes back anything completed locally, and prints the open items in a **suggested execution order** — the smallest independent item first, with blocked items listed separately (`blocked by <n>`) and an origin tag for items that came from the dashboard or another session. New `hula script backlog-pull` (push + pull + merge + status push-back) and `hula script backlog-list` (print the backlog as JSON from the local file, the server, or both). `/hula-help backlog [<trackingName>]` shows this chat's — or any plan's — backlog in the same order, including on a fresh checkout with no local file, and `/hula-followup` prints the same block before its questions.
- Plans started in a local `/hula-plan` chat are now fully usable from the HubLaunch dashboard. `hula plan context`, `answer`, `understanding` and `plan-text` accept `--local`, which `/hula-plan` now passes on every sync: the write is rejected once the plan has been continued on HubLaunch, and `answer`/`understanding`/`plan-text` refuse to overwrite an answer already given on the dashboard and report any `dashboardAnswers` in their JSON so the chat can pick them up. Questions synced from `/hula-plan` can mark a recommended option (shown as ⭐ on the dashboard), and `/hula-followup` now resumes local-chat plans as well as remote ones, asking only the questions still unanswered.

### Fixed

- `hula view` and interactive mode now show the lifecycle stage the server derives. The CLI dropped the server's `stage` and `archived` fields and guessed a stage from the tracking status, so failed plans read as `ready`, completed plans stayed in the active list, and `--stage completed` matched nothing. The Archive view (`hula view --archived`, the interactive Archive toggle) also only searched the active list; it now fetches archived plans and plans whose issue is closed (a merged PR closes its issue), so completed and archived plans actually appear there. The stage table now mirrors the server's 14 stages, so `failed` (❌), `backlogBlocked` (📌 open backlog: PR merged, backlog items still open) and `failedMerge` (🚨 deploy failed) sort and render properly instead of as `⚪` raw names.
- A plan-list request whose connection dropped after the server had answered failed with the meaningless `API error: 200`. It is now reported as a dropped connection, and the plan list (used by `hula view`, interactive mode and the Archive view) retries it up to twice.
- Backlog items silently never reached the server in repos configured with `hulaApiKey` but no `hulaProjectUrl` — the normal state after `hula init`. The backlog scripts now default the server URL exactly as every command does, so sync works whenever an API key is present, and a skipped sync says why on stderr and in its JSON (`skipped: <reason>`).
- A server 500 is no longer reported as "temporarily unavailable". The CLI now says the server hit an internal error, and includes the server's error message (when it gives one) and the request id, so failures can be traced.

## [1.40.0] - 2026-10-21

### Added

- The remote planning agent now marks a clearly best answer on clarifying questions. The `plan-initiate` instruction template describes the `{ "text": "...", "recommendation": "recommended" | "strongly" }` option shape and when to use it (at most one option per question, only when a better answer genuinely exists, never on the two fixed-format questions); the dashboard renders those options as `⭐ (recommended)` / `⭐ (strongly recommended)`, and the stored answer stays the clean option text. The server's follow-up policy for later question rounds carries the same instruction. (#788)

### Fixed

- `hula launch --test` is accepted again. The option had been commented out while the server, `hula script launch-run --test`, the `hula-launch` skill and `docs/commands.md` still supported it. It swaps the real Claude CLI for the sandbox's baked-in mock, so a full queue → sandbox → PR → webhook run completes in minutes at no LLM cost (used by hula-server's `/hula-e2e` smoke suite).

## [1.39.0] - 2026-10-20

### Added

- `hula revert <target>` and the `/hula-revert` skill: the safe inverse of `hula approve` for a problem that surfaces after a merge. `<target>` is a merged branch name, a PR number (`42`, `#42`, or a PR URL), or a commit SHA. It resolves the merged PR, reverts it on a new `revert-<branch>` in an isolated worktree (your working tree is never touched), opens a `Revert "<title>"` PR, reopens the issue the merge closed, and recreates the deleted feature branch. It never resets or force-pushes. Flags: `--merge` (squash-merge the revert PR now), `--dry-run`, `--pr <n>` (disambiguate a reused branch), `--continue` (resume after resolving conflicts), `--json`.
- `hula archive [issueNumber]` (`--unarchive` to reverse): a soft close that keeps the plan record. The server moves the plan to the `archived` lifecycle stage and closes its GitHub issue, but the plan stays visible in the Archive view. Unlike `hula close`, it does not remove the plan from tracking. Against an older server the command reports a clear "server does not support archiving" error.
- Every plan now has a single **lifecycle stage** on one ordered axis from creation to retirement (`backlog → QUESTIONS → UNDERSTOOD → GENERATE_PLAN → LAUNCHED → initialWaiting → waiting → ready → completed → archived`). `hula view` and the interactive list sort earliest-stage-first with priority as the within-stage tiebreaker; `completed` (all PRs merged, backlog done, deploy succeeded) and `archived` plans are hidden from the active list. The canonical stage comes from hula-server; the CLI derives a best-effort stage from local tracking status when talking to an older server.
- `mergePolicy` config key (`hublaunch.config.js`): `{ minConfidence, autoMerge }` declares the merge-confidence gate for the repo. `minConfidence` (integer 0–100, server default 90) is the lowest-category score a PR must reach to be `ready`; `autoMerge` (default false) opts into server-side auto-merge of `ready` PRs. The value is forwarded to the server on every `hula launch` and wins over the dashboard setting field by field. See [Merge policy](docs/advanced.md#merge-policy). Omitting it keeps the previous behavior.
- `hula info <name> --verify` prints the PR's merge assessment — confidence, verdict, the five category scores, concerns, and recommended end-user test steps — then the full JSON. `--raw` prints a single compact JSON line for scripts; it is never opened in an editor. Composes with the other info keys (K ≥ 2 → combined JSON).
- `hula view` now prints a `Confidence: 87 · review` line per plan when the server provides a merge confidence (`Confidence: — (unavailable)` for an unavailable verdict; nothing on servers that don't produce one).
- `hula approve --force`. Before merging, `hula approve` reads the PR's stored merge assessment and, when the verdict is not `ready` (or no assessment exists), prints the concerns and end-user steps and asks to confirm (interactive) or exits with code 3 (non-interactive). `--force` skips the prompt / overrides the gate. The threshold used is the assessment's own frozen value, never a local recomputation. PR-ready notifications now also carry the `mergeAssessment` object.

### Changed

- A plan whose planning job is still in flight (raw stage `INITIATED` / `GATHER_INFO`) is now shown as `analyzing` 🔍 (rank 0) instead of the misleading `backlog`. `backlog` (rank 1) is kept for dormant/legacy post-launch plans. The lifecycle is now 11 stages; `hula view` sorts `analyzing` first.
- `/hula-approve` prompts before merging a low-confidence PR. Both merge scripts (`approve-local`, `approve-remote`) take `--force` and, on a below-threshold gate (exit 3), emit `{"status":"error","code":"below_threshold","message":…,"assessment":{…}}` so the skill can show the concerns and end-user steps and ask the user before re-running with `--force`.

### Fixed

- `hula launch --show <name>` printed `No job found` for every task: the CLI read a legacy `jobStatus` / `jobPid` / … payload, but `GET /api/v1/ralph-run/:issueName/status` returns the task row (`status`, `pid`, `startedAt`, `completedAt`, `exitCode`, `workerHost`, `startedBy`, `planPath`, `heartbeatStale`, `mergeAssessment`). `--show` now reads that shape (the legacy one is still accepted), flags a stale heartbeat, and prints the stored merge confidence/verdict when the server has one.
- `hula view` is registered again. It is documented and maintained (stage ordering, confidence line), but had been dropped from the command list, so `hula script fix-setup` (used by `/hula-fix`) always failed with "Issue #N is not tracked. Run: hula track N" — and `hula track` is not a command either. `fix-setup` now reports a failing `hula view` as its own error (exit 2) instead of "not tracked", and its hints point at `hula launch --issue <n>` rather than the removed `hula track` / `hula checkout` commands (same change in the `/hula-fix` skill table and the bash twin script).
- `hula plan context` now splits a plan-initiate-layout document itself, sending the `## Assessment` body as the assessment, the fenced json in `## Questions` as the clarifying questions (`question` → `questionText`), and the remaining sections as the context — the same split the server does in remote mode. In local planning mode the questions were never uploaded, so every later `hula plan answer <planId> --file …` failed with `Resource not found`. Explicit `--assessment-file` / `--questions` still win, and a malformed questions block exits 1 with `Invalid questions JSON in ## Questions`.
- API errors from HubLaunch now show the server's own text. The planning routes return `{ error }`, which the client ignored in favour of a generic message (`Resource not found`, `Resource already exists`, …); every status branch now prefers `error`, then `message`, then the generic fallback.
- `hula script fix-setup` and `hula script verify-gather` resolved the wrong PR for an issue: both used `gh pr list --search "linked:<n>"`, but `linked:<number>` is not a valid GitHub search qualifier, so the list was unfiltered and the scripts picked the newest open PR instead of the one that references the issue (a fix session could be created on an unrelated branch). PR resolution is now deterministic — it uses GitHub's own linked-PR data (`gh issue view … --json closedByPullRequests`) and falls back to scanning open-PR bodies for a closing keyword (`Fixes`/`Closes`/`Resolves #N`). `fix-setup` only accepts an open PR (it must check the branch out), fails loudly with `No PR found for issue #N …` when nothing matches, and now verifies the specific issue number is tracked.
- `hula init` no longer tells you to write `.hublaunch/goals.md` when the project already has one. The "Next steps" tip is printed only when the file is missing, or, with different wording, when it exists but is empty.

## [1.37.0] - 2026-10-16

### Added

- `hula login` reuses the GitHub login already stored on the machine. Logging into a second project no longer opens the browser or asks for a device code: when a valid GitHub token is saved (for example after logging into another repo), login prints `🔑 Using your saved GitHub login` and skips straight to the HubLaunch exchange, so `hula init` in another project finishes in one step. If the stored token is expired or rejected by HubLaunch, login falls back to the browser device flow automatically. Pass `hula login --fresh` to force the browser flow, for example to switch GitHub accounts.
- Credentials are synced at the end of `hula login`. When the project has `saveCredentials: true` and a provider key is already configured, a successful login (including the one chained from `hula init`) immediately saves the project's `default` credential set to HubLaunch, so the dashboard and the phone PWA can plan right away instead of waiting for the first `hula launch`. If no provider key is configured yet, login says the credentials will be saved on the first launch instead. The sync is best-effort and never fails the login.

### Changed

- The GitHub token refresh logic now lives in a shared `github-token-refresh` utility used by both `hula login` and the GitHub CLI helpers, so a stale stored token is refreshed the same way everywhere.
- The bundled `.agents/skills` are refreshed: new `hula-assess` and `hula-followup` skills, `hula-plan` gains the remote-by-default planning mode with `--local` / `--remote` flags, and `hula-help` documents `hula update`, `saveCredentials` and `.hublaunch/goals.md`. The repo also ships a committed `.hublaunch/goals.md` describing the project's goals and non-goals.

## [1.36.0] - 2026-10-15

### Added

- Stored credential sets now carry your update-notification settings. When `saveCredentials` is on, `hula launch` (and `hula credentials sync`) include the effective `updateNotificationUrl` and `updateNotificationNameTag` (CLI flag → config → env) in the `default` credential set saved to HubLaunch, so a launch started from the dashboard or the phone PWA fires the same Slack/webhook notification a CLI launch does. Both values are stored non-secret and shown verbatim by `hula credentials list` in two new columns, `NOTIFY URL` and `NOTIFY TAG` (`—` when unset). The fields are omitted when unset and read defensively, so against an older hula-server that ignores them the sync still reports `synced` and the columns show `—`.

### Fixed

- A failed credential sync is now printed as a red error line on stderr. `hula credentials sync`, `hula init web`, and the automatic sync after `hula launch` previously printed "🔐 Credentials not saved: …" through the normal info logger, so an expired API key or an unreachable server blended into the surrounding output. A successful sync is unchanged, and `hula plan` keeps writing the plain line to stderr for the AI harness.

## [1.35.0] - 2026-10-14

### Added

- CLI self-update. `hula update` upgrades `hub-launch` with whichever package manager installed it (pnpm or npm, auto-detected; override with `--manager pnpm|npm`), always running the install from your home directory so a repo that pins a different `packageManager` can't redirect the global bin dir. It then guarantees the global Claude Code slash commands are refreshed — if pnpm's build-approval gate blocked the package's postinstall, it runs it directly and reminds you to `pnpm approve-builds -g` so future updates refresh automatically — and prints the new version. `hula update --check` reports whether an update exists (exit 0 up to date, 2 available) without installing.
- Update notice. Interactive, non-fast-path commands now print a one-line `⬆️  hub-launch <latest> is available …` notice at the end when a newer version is on npm, checked at most once per day (cached in `~/.hublaunch/update-check.json`). The check is skipped entirely on non-TTY output and on the JSON/instruction fast-path commands (`script`, `instructions`, `plan`, `session-hook`), so parsed output is never touched. Set `HULA_NO_UPDATE_CHECK=1` to disable it (e.g. in CI).
- Opt-in automatic updates. `hula login` and `hula init` ask once whether to keep the CLI up to date automatically; with it on, the update runs **after** your command finishes (never mid-command) and tells you what it did. Toggle any time with `hula update --auto on|off`. Stored per machine in `~/.hublaunch/settings.json`.

## [1.34.0] - 2026-10-14

### Added

- Credential sync for web + phone planning. Opt in once (`saveCredentials: true`, asked by `hula init` and `hula login`, default yes) and every `hula launch` and `hula plan start|draft-and-launch` also saves the exact credential bundle it sends — your GitHub token, provider key and forwarded env vars — to HubLaunch as this project's `default` credential set, so the dashboard's New Plan wizard and the phone PWA can launch without uploading a `.env` or typing a token. `hula credentials sync` pushes without launching (always re-sending the current `gh auth token`, so a rotated GitHub token never goes stale) and `hula credentials list` prints a hint-only table of what the server holds. Turn it on or off any time with `hula init web` / `hula init web disable` (disabling also deletes the stored set from HubLaunch). Secrets are never printed — only hints (`ghp_…k3Q9`). The sync is best-effort: it runs only after a launch succeeds, never blocks or fails the command, and prints one short line. **Requires hula-server with the `/api/v1/credential-sets` endpoints; against an older server every path reports `server-unsupported` and nothing else changes.**

### Changed

- Remote planning now runs the honest assessment in the sandbox and `/hula-followup` handles the stop and goals questions. The `plan-initiate` pass runs `hula instructions assess` (the same rubric as local `/hula-plan`), pasting its output under `## Assessment`; when a category scores below 5 the first clarifying question is proceed / adopt the safer alternative / stop, and when `.hublaunch/goals.md` is absent the last question offers to save the inferred goals. `/hula-followup` titles the block by its overall score, stops cleanly on "Stop planning" (the plan stays resumable), and writes `.hublaunch/goals.md` in your checkout on approval with a commit reminder.

## [1.33.0] - 2026-10-13

### Added

- Remote planning for `/hula-plan`. In Claude Code, `/hula-plan` now defaults to running its exhaustive repo analysis and clarifying-question derivation on the hula-server (for half a plan's credits): the session starts the plan, drops a one-line notice, and ends its turn, resuming automatically once the server has questions (or when the server finds none, reporting that it is drafting and launching on its own). After you confirm the understanding, you choose between planning locally (with approval before launch) or having HubLaunch draft and launch the plan automatically. A new `/hula-followup <planId>` skill resumes an in-flight remote plan from any checkout of the repo — answering the questions, storing the confirmed understanding, and running the local-or-remote choice — so a dead session loses nothing. `--local` forces today's all-local flow (also the default outside Claude Code and whenever `--pr` is used); `--remote` forces remote. Local planning now also backs up its context, questions, answers, understanding, and plan text to the dashboard on a best-effort basis. Implemented by a new `hula plan <start|wait|followup|answer|understanding|context|plan-text|draft-and-launch>` command group and two new sandbox instruction docs (`hula instructions plan-initiate`, `plan-generate`). **Remote planning requires hula-server with the `/api/v1/planning` endpoints; the server's sandbox image installs this package and requires ≥ 1.33.0 (the first release with `hula instructions plan-initiate`).**
- Pre-releases: `scripts/release.sh rc` (or an explicit `x.y.z-rc.N`, also via `/hula-release`) cuts a GitHub pre-release from any branch without touching `CHANGELOG.md`, and `publish-to-npm.yml` publishes it under the npm `next` dist-tag so `npm install -g hub-launch` keeps resolving the last stable release. The workflow now refuses a version/release-kind mismatch (an `-rc.N` version on a normal release, or a stable version on a pre-release).
- `/hula-assess <description | --issue n | --pr n>` and `hula instructions assess` — an honest 1–5 assessment of whether a requested change is a good idea for the project (not whether it can be built), scoring code integrity, project goals, security, compatibility, and maintenance burden. The overall verdict is the lowest category score; anything below 5 is explained with `file:line` evidence and paired with a concrete "Safer alternative". `/hula-plan` (local) now runs the same assessment inline, before its clarifying questions — a score below 5 makes the first question proceed / adopt the safer alternative / stop. Project goals are read from a new committed file `.hublaunch/goals.md`; when it is absent, goals are inferred from the README and docs and the skill offers to write the file. Read-only and chat-only — nothing is posted to GitHub.

### Fixed

- `hula launch` could hang until its timeout ("Request timed out") with the request never reaching the server. The launch POST reused an idle keep-alive connection from the subscription check a few seconds earlier, which the server side was closing at the same moment. Launch and kill requests now open a fresh connection.

## [1.32.0] - 2026-10-09

### Fixed

- `hula launch` waits up to 120s for the server to accept a launch (was 30s, shorter than the server's own limit, so a slow or cold server dropped launches). A timeout now says the launch may still have been queued and how to check.
- `--pr <n>` on an issue number now says "#n is an issue, not a pull request. Use --issue n instead." rather than "not found", and `--issue` / `--pr` lookups no longer print the raw `gh api` failure before the clean error.
- Billing refusals from `hula launch` no longer offer a free trial to accounts that can't take one. An exhausted credit balance (the server's 402 with `buyCreditsUrl`) now says "Buy more credits"; a free trial is offered only when the server reports the account never subscribed; an unknown status gets neutral wording. The closing line no longer claims Pro allows unlimited launches.

## [1.31.0] - 2026-10-08

### Added

- `hula launch --issue <ref>`, `--pr <ref>` and `--base <branch>` — launch directly from an existing GitHub issue or pull request. `--issue` takes on an open issue (branch `issue-<n>`, plan generated from the issue text, PR linked with `Closes #N`); `--pr` continues an open same-repo pull request on its own head branch (fork PRs are rejected); `--base` cuts the work branch from, and opens the PR into, a non-default branch. Both `--issue` and `--pr` accept a number, `#number`, or a full GitHub URL, and the plan file becomes optional (generated from the GitHub text when omitted). Requires a hula-server with launch-from-existing support; an older server ignores the new `issueNumber` / `prNumber` / `headBranch` / `baseBranch` request fields, so the launch still runs but the linkage is dropped.
- `/hula-plan --issue <n>` and `/hula-plan --pr <n>` — plan from an existing issue or same-repo pull request. The skill reads the issue or PR (read-only), drafts and validates the plan from it, defaults the name to `issue-<n>` / `pr-<n>`, and emits a `<!-- hula-source -->` comment so the Launch Offer and `/hula-launch` pass `--issue` / `--pr` to the launch. `hula script launch-run` now forwards `--issue`, `--pr` and `--base` to `hula launch` and reports `prNumber` in its JSON.
- Per-repo `sandboxProfile` in `.hublaunch` config (`{ type: base|android, jdk?: 17|21 }`) to select the Android sandbox image (JDK 17/21, Android SDK, fastlane). `hula init` asks "What does this project build?" once — only when no `sandboxProfile` is set — preselecting `android` when a Gradle/Android project is detected, and warns when an Android profile pins `containerResources` below 4 vCPU / 8 GiB. `hula launch`, `hula schedule` and `hula error-watcher --create` send the configured profile, and `--sandbox-profile base|android|android:17|android:21` overrides it per task. The `base` profile is omitted from request bodies, so existing payloads are unchanged.
- `hula login --invite <code>` — apply an open-source program invite at login. When the server grants the program, the CLI reports the Pro period and its end date; a code that wasn't applied (already used, expired, or the account already has Pro) produces a warning instead of a silent login.
- Chat-scoped backlog: `/hula-plan`, `/hula-fix` and `/hula-approve` now keep a per-chat list of out-of-scope items that surface mid-conversation, stored in `.hublaunch/.backlogs/<key>.json` via the new `hula script backlog-init` and `backlog-add` scripts. The session hook records the real Claude Code session id on each backlog file for provenance. When `hulaApiKey` / `hulaProjectUrl` are configured, items are pushed to hula-project (best-effort, non-blocking — the local file stays the source of truth), and the new `hula script backlog-resync` retries unsynced items. `/hula-approve` ends by listing the chat's open backlog items with a suggested next action for each.

### Fixed

- `hula launch`: the "Track it" link now points at the server the task was launched on, instead of always linking to the production dashboard — preview and self-hosted launches now open the right place.

## [1.30.0] - 2026-09-18

### Fixed

- `hula schedule` now forwards `config.envVars` (an array of names, or `"all"`) from the project's local `.env` into the container, reaching parity with `hula launch`. Previously `schedule` never read `config.envVars`, so one-off execute-action runs and recurring cron `Schedule`s always ran with an empty environment — silently, with no error — leaving custom action skills without the secrets (Vercel tokens, test-login credentials, Slack webhooks) they declared. Values are read from `.env` at request time, validated against the reserved blocklist, and never printed (only a count is logged). Because the server persists `envVars` into a schedule's `execParams`, every future cron tick and `--run-now` dispatch picks it up automatically. Existing schedules can't be updated in place — cancel and recreate with `--schedule` to change their env vars (`hula schedule --cancel-schedule` now points this out).

## [1.29.0] - 2026-09-18

### Added

- `/hula-research` Agent Skill — research a codebase (or a specific topic within it) and get a digestible, code-grounded R&D report. Scaffolded by `hula init` and installed globally on `hula` install, the same way as the other Agent Skills.
- `--tracking-name` flag for `hula schedule`, giving scheduled runs a human-readable name (e.g. in Slack notifications) instead of an auto-generated `execute-action-<timestamp>` label.

## [1.28.0] - 2026-09-08

### Added

- The feature branch for a plan is now created at plan time (`/hula-plan` runs `hula upload <planPath> --branch <issueName>`) instead of late in the sandbox run, so pre-launch infrastructure like a `beforeLaunch` hook that provisions branch-scoped resources (e.g. a Vercel Preview env var scoped to a per-branch database) can reference a git branch that already exists. `hula launch` now runs `ensurePlanBranch` as a safety net before the `beforeLaunch` hook — creating the branch if it wasn't (rename, legacy plan) and refreshing the plan on it — and the launch request carries `branchExists: true` so hula-server checks out the existing branch instead of creating one. Plans are no longer pushed to `origin/main`; they reach `main` only when the PR merges. Requires a matching hula-server version.

## [1.27.0] - 2026-09-04

### Changed

- `hula init` now scaffolds working `.hublaunch/hooks/beforeLaunch.ts` and `afterMerge.ts` files (a local-only active example plus commented-out Slack and Neon+Vercel database-branch ideas) instead of only a README, and wires both into the generated config as real (uncommented) lines by default — superseding `1.26.0`'s commented-out-placeholder behavior. A project that already has `hooks.beforeLaunch`/`hooks.afterMerge` configured is unaffected; a project with neither configured will now run these (side-effect-free) hooks automatically after upgrading and re-running `hula init`. Note that if a `beforeLaunch.ts`/`afterMerge.ts` file already exists at the default path (even one hand-written before this change), init leaves it in place but still wires the config key to that path when the key was never configured, so a previously-inert file at that path becomes active.

### Fixed

- Pinned `core-js` via `overrides`/`pnpm.overrides` to `^3.23.3` (the first non-deprecated release per npm's own deprecation notice) to prevent the `deprecated subdependencies found: core-js@2.6.12` warning some users saw on `pnpm i -g hub-launch`.

## [1.26.0] - 2026-09-03

### Added

- `beforeLaunch` and `afterMerge` project hooks. `beforeLaunch` runs before `hula launch` sends its request, letting a project hook provision per-launch resources (e.g. an isolated database branch) and inject the resulting environment variables into the launch — a hook failure aborts the launch rather than proceeding against the wrong resources. `afterMerge` runs after `hula approve` successfully merges a PR, for best-effort cleanup of whatever `beforeLaunch` provisioned. `hula init` now scaffolds commented-out placeholders for both in generated configs.

### Changed

- Removed the `deploymentStartup` hook and its Clerk/Auth0 preview-auto-login scaffold — unrelated to the new lifecycle hooks and superseded by `hula preview`'s built-in auto-login fallback.

### Fixed

- Hook execution no longer runs through a shell on POSIX systems, so shell metacharacters in a hook's context (e.g. backticks in a PR title) can no longer be reinterpreted instead of passed through as literal text.

## [1.25.0] - 2026-08-23

### Added

- New `hula error-watcher` command to manage production **Error Watchers** — inbound error webhooks that deduplicate reported errors and auto-launch a fix (PR, plan, or feedback run). Seven modes: `--create`, `--list`, `--show`, `--events`, `--update`, `--delete`, and `--print-setup`. `--create` and `--print-setup` print the steps to mint a project ingest key, a paste-ready `.env` block, and a hard-coded signed-request snippet that signs the exact `` `${timestamp}.${body}` `` payload the server verifies. Credentials resolve flags → `.hublaunch/hublaunch.config.js` → env, identical to `hula schedule`, and are never written to any file. `--events` shows each error's disposition (`deduped`, `skipped_environment`, `skipped_budget`, `skipped_credits`, `skipped_open_pr`, `launched`, `failed`) so "why was there no PR?" is answerable from the terminal; with no watcher id it covers every watcher on the project. Requires a hula-server with Error Watcher support; older servers return a clear "not supported yet" message. See [docs/error-watcher.md](./docs/error-watcher.md).
- `--instructions`, `--error-env KEY=VALUE`, and `--secret <literal>` flags on `--create`/`--update` for the three watcher fields that shape what the fix agent sees: trusted guidance rendered into the agent's brief, environment variables injected into the fix sandbox, and extra literal values redacted from every payload. Each replaces the stored value wholesale, with matching `--clear-instructions` / `--clear-error-env` / `--clear-secrets` flags. Values are write-only — the server never echoes them back, and the CLI never prints them.
- Optional `errorWebhookUrl` config key to override the Error Watcher ingest URL for self-hosted servers whose webhook host differs from `hulaProjectUrl`.

### Changed

- **Error Watcher ingest now authenticates on a project ingest key.** Matching the server, the credential a reporting app presents moved from the per-watcher `hew_` token to a project-level `hik_` key signing with its own `his_` secret. The generated `.env` block emits `HULA_INGEST_KEY` / `HULA_INGEST_SECRET` (was `HULA_ERROR_WATCHER_TOKEN` / `HULA_ERROR_WATCHER_SECRET`) and the snippet sends `X-Hula-Api-Key` (was `X-Hula-Watcher`). The watcher now holds configuration only; `--create` prints no credential of its own and instead directs you to **Projects → gear icon → Error reporting API keys**, the only place a key can be minted. **Any reporter wired up against an earlier build of this command must be updated or it will 401.**
- The printed snippet now sends `errorName` and `errorCode` alongside `key`. The server's dedupe fingerprint is built from those three identity fields and nothing else, so a report carrying none of them shares a single bucket with every other such report — one fix per watcher per dedupe window.
- `--events` with no watcher id lists the project's watchers and queries each one, merging the results newest-first. It previously requested a project-wide events route that does not exist on the server and always returned 404.
- `--update` now rejects `--container-cpu`, `--container-memory`, `--container-disk`, `--update-notification-url`, and `--update-notification-name-tag` up front. The update endpoint ignores those fields, so sending them reported success while changing nothing; they remain settable at `--create`.
- `--delete` no longer claims reports will start returning 401. Deleting a watcher revokes no credential — ingest keys keep authenticating, and the webhook answers 409 once the project has no enabled watcher left.
- `--show` displays the watcher's `instructions` and no longer prints the vestigial watcher token prefix.
- A `409` from the ingest webhook now maps to a specific message naming the cause ("no enabled error watcher is configured for this project") and the commands that fix it, instead of falling through to a generic status line.

### Removed

- `hula error-watcher --rotate`. It rotated the watcher's signing secret, which the ingest webhook no longer verifies, and its response handling did not match the server's shape. The signing secret that matters belongs to the project ingest key and is rotated from the dashboard settings page, which has no REST route for the CLI to call.

## [1.24.0] - 2026-08-18

### Fixed

- Entitlement is now verified with the server on every launch instead of being cached indefinitely in local config. A cancelled subscription, an expired free trial, or exhausted PR credits no longer slips past the local gate only to fail later against the server. `usageTier` is now treated as a refreshable cache of the last server answer, consulted only when the server is unreachable.
- Access-denied messages now reflect the account's actual subscription status — never subscribed (offers the free trial), cancelled, payment failed, or the server's status verbatim — instead of always claiming the user is on the free tier.
- Billing and upgrade links point at the configured server's own dashboard rather than a hardcoded hublaunch.site, so preview and self-hosted deployments send users to their own billing page.
- Chained hula skills (`/hula-plan` → confirm → launch) now execute inline explicitly, fixing the "blocked by disable-model-invocation" break under newer Claude Code versions that enforce that flag on model-initiated Skill-tool calls.

### Changed

- Prefer the server's own 402 response message and its `upgradeUrl` / `buyCreditsUrl` over anything the CLI infers, in both the single-repo and `--folder` launch paths.

## [1.23.0] - 2026-08-13

### Added

- Multi-provider LLM support: choose `claude` (default), `openai`, or `openrouter` per launch. Select a provider with `--provider <type>` (or `config.provider.type`) and supply its credential with `--provider-key <key>` (or `config.provider.apiKey`, or the `PROVIDER_AUTH_TOKEN` env var). `hula init` now prompts for the provider first, then its credential. Client-side live validation runs against the selected provider's API before launch.
- `claude` now accepts Anthropic API keys (`sk-ant-api03-…`) in addition to subscription OAuth tokens (`sk-ant-oat01-…`).

### Changed

- **BREAKING:** the `anthropicApiKey` config key, the `--anthropic-key` flag, and the `ANTHROPIC_API_KEY` env fallback have been replaced by the `provider` config block, `--provider`/`--provider-key` flags, and the `PROVIDER_AUTH_TOKEN` env var. Request bodies now send `provider: { type, authToken }` instead of `anthropicApiKey`. A leftover `anthropicApiKey` in config produces an explicit migration error. Requires hula-server with matching multi-provider support.

### Documentation

- Improved multi-provider documentation and discovery.

### Fixed

- Build errors fixed.

## [1.22.0] - 2026-08-12

### Added

- Multi-repo feature groups: client-side `--folder` fan-out enables launching and checking status across a group of repositories from a single parent folder (group launch, group status).

### Fixed

- `--folder` group launch now works when invoked from the `/hula-launch` Agent Skill in non-TTY environments (#344).

## [1.21.0] - 2026-08-06

### Added

- Single-command onboarding: `hula init` now chains directly into login, with Anthropic token entry deferred until actually needed.
- When login fails with `repo_not_found`, guidance is shown for installing the GitHub App.

### Changed

- Generated config no longer emits a default `containerResources` block.
- Obsolete Daytona keys are scrubbed from existing config on re-init.

## [1.20.0] - 2026-08-04

### Added

- Added a one-plan-one-PR scope check to plan validation.
- Made `/hula-plan` output clearer with a summary, folded detail section, optional diagram, and pre-save checklist.

### Changed

- Renamed the `hula-merge` Agent Skill to `hula-approve`, updating the CLI command, wrapper scripts, and docs.

### Security

- Removed client-side Daytona API key handling.

### Documentation

- Updated the plan/approve-first README, notifications guide, and skill descriptions.

## [1.19.0] - 2026-07-29

### Added

- Added `--autoLaunch` flag to `/hula-plan`.

### Documentation

- Documented the `envVars: "all"` forwarding option everywhere it's surfaced to users (generated config, hooks README).

## [1.18.0] - 2026-07-28

### Added

- Verify reports now auto-post to the PR by default; use `--no-post` to restore the old confirm-before-posting behavior.
- Added a `hula-help` Agent Skill for interactive onboarding and reference guidance.
- Moved `hula-verify`'s tmp writes from OS `/tmp` to a repo-local, gitignored `.hublaunch/tmp/` directory (#325).

## [1.17.0] - 2026-07-23

### Added

- `hula launch` now supports per-pipeline-step model and iteration-cap overrides via a `steps` block in `.hublaunch/hublaunch.config.js` (hula-server PR #442), plus a `--skip-regression` flag to force-skip regression tests for a single launch. Conflicting `bugfix.model`/`mergeConflict.model` values, or `regression.skip` combined with the legacy `--regression` flag, are rejected locally before any network call.

## [1.16.0] - 2026-07-17

### Fixed

- Plan upload to `origin/main` now survives a concurrent push to `main`. When the remote branch advances between the temporary worktree's commit and its push, the upload automatically fetches, rebases the plan commit onto the new head, and retries (up to 3 attempts with exponential backoff) instead of failing with `cannot lock ref`. Genuine content conflicts still fail fast with a clear message, and non-race errors (auth, network) are no longer retried. This fixes transient `hula launch`/`hula upload` failures on active repositories (#318).

## [1.15.0] - 2026-07-15

### Added

- `hula info <trackingName>` is now the single command for surfacing plan facts. It supports `--logs`, `--lastLogs`, `--diff`, `--initial`, `--lessons`, and `--clientSessionId` (plus `--lines` and `--raw`). Content keys are fetched from the server's new `GET /api/v1/info/:planName` endpoint (hula-server PR #421); `--clientSessionId` continues to use the status endpoint. A single content key opens in your editor (or stdout with `--raw`), a single `--clientSessionId` prints the id plain, and two or more keys print one merged JSON object.

### Removed

- **Breaking:** removed the `hula logs` command and the `/hula-log` chat skill. Use `hula info` / `/hula-info` instead (`hula info <name> --lastLogs` replaces the old default live-tail behavior; `--logs`/`--lessons` map directly).

## [1.14.0] - 2026-07-15

### Added

- Capture and send `clientSessionId` at launch for session tracking.
- New `hula info` command to display client/session details.

### Changed

- Groundwork for the free-tier upgrade funnel in the hula launch flow.

## [1.13.0] - 2026-07-14

### Changed

- `hula init` now prompts for **only** the two required API keys (Anthropic OAuth token and Daytona API key). Every other setting is written to `.hublaunch/hublaunch.config.js` with a sensible default (documented inline) for the user to edit manually, instead of being asked one field at a time. Re-running `hula init` preserves all previously-set values, including non-prompted fields such as `daytonaTier` and `envVars`.
- The generated config no longer includes commented-out placeholder sections for `teamMembers`, `commandAliases`, `priorityLevels`, `issueSortOrder`, `services`, and `hooks`. These sections are written only when they hold a real value (or are preserved from an existing config on re-init); an unset optional setting is simply omitted, keeping the file compact.
- The generated config no longer writes `trackingFile`, `createIssuesPreference`, `planChoices`, `pollingProvider`, `pollIntervalSeconds`, or `browserHeadless`. Each falls back to its documented default at load time, so behavior is unchanged; setting any of them manually in the config file still works and is preserved across re-runs.
- Reworded the `hula init` intro and "Next steps" output to reflect the minimal prompt and point users to the config file for further customization.

### Fixed

- `hula init` now creates (and logs) the plans directory at the config's actual `planPath` on re-init, instead of always using the default `.hublaunch/plans`.

### Removed

- Removed the `daytonaTier`, `editorCommand`, `planPath`, `trackingFile`, `createIssuesPreference`, `planChoices`, `skipUntrackedPrompt`, `teamMembers`, `authProvider`, `configureVsCodeAutoApproval`, and `envVars` prompts from `hula init`. These are now configured by editing the generated config file. (VS Code auto-approval is still applied by default; auth provider defaults to `none`.)

## [1.12.0] - 2026-07-13

### Changed

- `hula init` now preserves **all** existing config values on re-run, including custom/arbitrary keys and `hulaProject`/`hulaProjectUrl`, emitting any keys not in the documented template under a `// Preserved custom settings` block.
- `hula init` loads an existing config via native dynamic `import()` only; the lossy regex config parser has been removed. A syntactically broken config now fails fast with guidance to fix the file or run `hula init --force` to regenerate a fresh config (custom edits will be lost). `hula init --force` now correctly skips parsing the existing file.

### Removed

- Removed the `overrideStartupScript` prompt from `hula init`. An existing `.hublaunch/hooks/deploymentStartupScript.ts` is never overwritten (created only when missing).

## [1.11.1] - 2026-07-09

### Added

- Offer to launch automatically after plan validation completes, so you can go straight from a validated plan into a run without a separate step.

## [1.11.0] - 2026-07-08

### Added

- Client support for the unified launch/cancel `ralph-run` API, with `--kill` to stop an active run and `--kill-and-relaunch` to restart it. (#287)

## [1.10.0] - 2026-07-07

### Added

- `hula logs` now supports `--logs`, `--lessons`, and `--all` flags for client-side artifact retrieval.
- `hula launch` CLI and the `hula-launch` skill template gained a `--test` flag.
- Integrated `updateNotificationNameTag` on the hub-launch client.

### Changed

- Renamed `hula execute` to `hula schedule` across the CLI, skill, wrapper scripts, and docs.

### Fixed

- `/hula-merge` now correctly updates the local main branch and reports status properly.

## [1.9.0] - 2026-06-26

### Added

- Environment Variables to Interactive Init Prompts: Users can now pass environment variables directly through interactive initialization prompts for flexible runtime configuration.

## [1.8.1] - 2026-06-25

### Added

- Consolidate Hula Workflow to Single `/hula-launch` Path for a more streamlined execution flow.
- Pass Environment Variables from Hub-Launch to Hula Server to enable dynamic configuration at runtime.
- Fix JSON Parsing Mismatch in hula-fix Workflow to handle edge cases in workflow state management.

### Fixed

- Write compact JSON in hula-fix session files to ensure correct parsing and prevent data corruption.

## [1.8.0] - 2026-06-24

### Added

- Updated `LaunchJobResponse` type and display logic to reflect the new server response shape.

## [1.7.0] - 2026-06-23

### Fixed

- **`/hula-launch` relaunch now checks the remote for a plan file before failing.** When relaunching a project, the CLI previously failed immediately if the local plan file was missing. It now checks the remote repository first, so a relaunch succeeds on a fresh clone or after the local `.hublaunch/` directory has been cleaned — without requiring a manual `git pull`.

## [1.6.0] - 2026-06-23

### Changed

- chore: upload plan via hula

## [1.5.0] - 2026-06-22

### Fixed

- **`hula execute` now sends Slack notifications.** The `/hula-execute` skill was not triggering Slack notifications on job completion. The `execute` command now correctly invokes the notification path, giving parity with `hula launch`. No config changes required — notifications fire automatically when `SLACK_WEBHOOK_URL` is set or a webhook URL is configured.

- **SSH remote URLs with custom host aliases are now parsed correctly.** `GitService` previously matched only `git@github.com:owner/repo.git`; the regex now accepts any SSH alias (`git@github-work:owner/repo.git`, `git@gh-personal:owner/repo.git`, etc.). This unblocks users who use `~/.ssh/config` host aliases for multi-account GitHub setups.

### Changed

- **Generic AI-agent language in plan instructions.** The planning and confirmation instruction templates (`planning-instructions.md`, `proceed-instructions.md`) and the `hula-plan` / `hula-confirm` skills no longer describe the execution target as "GitHub Copilot" or reference the deprecated `/hula-upload` command. End-of-plan completion messaging now uses generic "AI-assisted implementation" / "AI implementation agent" wording that covers both pro mode (Claude at hula-server) and free mode (GitHub Copilot agent), and directs users to run `/hula-create` — which syncs the plan to `origin/main` automatically — as the next step. Functional GitHub Copilot references (bot assignment in `hula-create`, Copilot-authored PRs in `hula-merge`) are unchanged. **Existing projects must re-run `hula init` to pick up the updated `.hublaunch/planning-instructions.md` and `.hublaunch/proceed-instructions.md`** (use the override flags or delete the files first, since these are not overwritten if they already exist); skill files are always refreshed by `hula init`.

- **`release.sh` now builds before bumping the version.** A failed TypeScript compile aborts the release before the tag, commit, or npm publish — so a broken build can never produce a published package with a new version number. Local `dist/` is also refreshed so linked binaries reflect the released source immediately.

## [1.4.1] - 2026-06-21

### Added

- **Re-init reminder after CLI upgrades.** `hula init` now stamps the generating CLI version into the project's `hublaunch.config.*` as a new optional top-level `version` field. On every subsequent command (except `init`, `help`, `--help`/`-h`, and `--version`/`-V`), the CLI compares that recorded version against the installed one and prints a one-line warning to re-run `hula init` when they differ — so upgraded skills, instruction templates, and config scaffolding don't silently go stale. The check is synchronous, cheap, and crash-proof (it can never break a command). Existing projects initialized before this release have no `version` field, so they will see the reminder until they re-run `hula init` once. No `postinstall` script is involved.

## [1.4.0] - 2026-06-21

### Added

- The `/hula-execute` skill can now **author an execute action from a plain-language description** and **manage runs and schedules conversationally**. Describe a task with no `--built-in`/`--action-path` (e.g. `/hula-execute remove unreachable code in src/ every night and open a PR`) and the skill asks clarifying questions, confirms a name, writes a free-form action file to `.hublaunch/skills/<YYYY-MM-DD-HH:MM-slug>.md`, publishes it to `origin/main` **before** the run, then runs or schedules it. Management is now conversational too: `list`, `show <runId>`, `run now <scheduleId>`, `cancel <scheduleId>` (offers to delete the related action file, guarded so it refuses while another active schedule still references it), and `update <scheduleId> …` (either edit + re-publish the action file, or cancel + recreate the schedule with a new cron on the same action).
- `hula execute --publish-skill <path>` and `--delete-skill <path>` — commit or remove a `.hublaunch/skills/` action file on `origin/main` through a temporary detached worktree, so the hula-project server can read it from the default branch at run time without ever touching your current branch. Both reject absolute paths, path traversal, and any path outside `.hublaunch/skills/`. These back the skill's authoring/cleanup steps; you normally invoke them through the skill rather than directly.
- `hula init` now installs `.hublaunch/skill-creation-instructions.md` (read by the `/hula-execute` skill at runtime, parallel to how `/hula-plan` reads its planning instructions) and ensures the `.hublaunch/skills/` action-file directory exists (seeded with a `.gitkeep`). The new `hula-execute-manage.sh` wrapper is added to the bundled scripts.

### Changed

- Extracted `FilePublishService` (`src/services/git/FilePublishService.ts`), which encapsulates the temporary-worktree commit/push/delete flow that was previously inlined in `hula upload`. Both plan upload and the `/hula-execute` skill-publish path now share this single tested implementation (DRY), including the git-push refspec injection guard.

### Documentation

- Documented the new `/hula-execute` authoring and conversational management flows, and the `--publish-skill` / `--delete-skill` flags, in `README.md` and `docs/commands.md`.

## [1.3.0] - 2026-06-19

### Added

- `hula merge` now automatically fast-forwards your **local** root `main` to match `origin/main` after a successful merge — no manual `git pull` needed. It is cwd-independent (works even when `hula merge` runs from inside a worktree, via the new `WorktreeService.getMainWorktree()`), fast-forward only, and fully guarded: the update is skipped with a warning (never an error, since the PR is already merged remotely) when the project root isn't on the default branch, has uncommitted changes, or can't fast-forward. When it succeeds, the merge summary reports `✓ Local main updated to latest origin`.
- `WorktreeService.getMainWorktree()` — returns the project root (main) worktree's path and checked-out branch by reading the first entry of `git worktree list --porcelain`, so it stays correct even when called from inside a linked worktree.

### Documentation

- Documented the post-merge local `main` fast-forward behavior in `README.md` and `docs/commands.md`.
- Expanded the `hula execute` reference: marked each flag as required/optional, added cron schedule examples, and noted that the `/hula-execute` agent skill can drive the command from plain language (translating schedule phrasing into cron and confirming before running).

## [1.2.3] - 2026-06-16

### Fixed

- `hula logs` no longer fails with "Server URL and API key are required" when `.hublaunch/hublaunch.config.js` has a `hulaApiKey` but no `hulaProjectUrl` (and `HULA_PROJECT_URL` is unset). It now falls back to the default `HULA_PROJECT_URL` (`https://www.hublaunch.site`), matching `launch`, `execute`, and `login`. The same fallback was applied to the job-status and job-log paths in `hula launch`.
- `hula login` now writes `hulaProjectUrl` to the config even when an API key is already present. Previously the URL was only saved on first login, so re-running `hula login` on a config that already had `hulaApiKey` but no URL never repaired it — leaving commands like `hula logs` unable to resolve the server.

### Internal

- `publish-to-npm` workflow now posts a Slack notification on release publish success and failure (`notify-success` / `notify-failure` jobs). Self-guarded: skips on forks and no-ops when the optional `SLACK_RELEASE_WEBHOOK_URL` secret is unset.

## [1.2.2] - 2026-06-16

### Fixed

- `hula execute` now attaches the Anthropic OAuth token to the request, matching `hula launch`. Previously the token was never sent, so sandbox runs failed because the container had no `CLAUDE_CODE_OAUTH_TOKEN`. The token is resolved from `--anthropic-key`, then `config.anthropicApiKey` in `.hublaunch/hublaunch.config.js`, then the `ANTHROPIC_API_KEY` environment variable, and must be an OAuth token (`sk-ant-oat…`) — standard API keys (`sk-ant-api03-…`) are rejected with a clear error.

### Added

- `hula execute --anthropic-key <key>` flag for supplying the Anthropic OAuth token directly.

### Changed

- Extracted a shared `resolveEphemeralCredentials()` resolver (`src/utils/ephemeral-credentials.ts`) used by both `launch` and `execute`, making credential precedence and validation a single source of truth (DRY). Resolution is now pure and side-effect free; each command owns how it presents errors.

### Documentation

- Documented the Anthropic OAuth token and Daytona API key requirements for `hula execute` in `README.md` and `docs/commands.md`, including the new `--anthropic-key` and `--daytona-key` options.

## [1.2.1] - 2026-06-12

### Fixed

- `hula execute` now attaches the GitHub token to the request (parity with `hula launch`), resolved from `hula login` credentials or the `GITHUB_TOKEN` environment variable — the server hard-requires it on `POST /api/v1/execute`.
- `hula execute` defaults the server URL to `https://www.hublaunch.site` instead of erroring out when no URL is configured; still overridable via `--url` or `HULA_PROJECT_URL`.
- `--outcome-type` now defaults to `pr` when omitted and is validated against `pr`, `plan`, and `feedback`, rejecting invalid values with a clear error.

### Documentation

- Added a `hula execute` section to `README.md` and documented the new server URL, GitHub token, and `--outcome-type` defaults in `docs/commands.md`.

## [1.2.0] - 2026-06-11

### Added

- `GitService.showFile(ref, path)` — retrieves any file from git history by ref and path, with injection guards.
- `upload.ts` fallback: if the plan file no longer exists on disk, automatically recovers it from `git show main:<path>` so uploads never silently fail after a branch clean-up.
- `launch.ts` resilience: treats working-tree-only plans as needing upload, so the launch flow works even when the plan hasn't been committed yet.

### Improved

- `/hula-launch` skill rewritten to a single-step branch + plan extraction, removing a redundant `Read` call and multi-turn reasoning overhead.
- `hula-launch-run.sh`: `json_escape` now uses `jq` with a `sed` fallback, producing valid JSON for multi-line git error messages and removing a pre-existing double-escape bug.

## [1.1.0] - 2026-06-08

### Fixed

- Register `create` command in CLI entry point so it is accessible from the command line.

## [1.0.3] - 2026-06-05

### Fixed

- Bug fixes and stability improvements for the official release.

## [1.0.2] - 2026-06-02

### Fixed

- `hula --version` now reports the correct version from `package.json` instead of a hardcoded value.

## [1.0.1] - 2026-06-02

### Fixed

- npm package now only ships compiled `dist/` output; TypeScript source is no longer included in the tarball.

## [1.0.0] - 2026-06-02

### Changed — BREAKING

- **Slash commands renamed**: `/hula.plan` → `/hula-plan`, `/hula.fix` → `/hula-fix`,
  `/hula.create` → `/hula-create`, `/hula.verify` → `/hula-verify`,
  `/hula.merge` → `/hula-merge`, `/hula.launch` → `/hula-launch`,
  `/hula.upload` → `/hula-upload`, `/hula.log` → `/hula-log`,
  `/hula.confirm` → `/hula-confirm`. The Agent Skills spec (which both
  GitHub Copilot and Claude Code natively support) forbids dots in skill
  names. After running `hula init`, the new names are available; the
  legacy `.github/prompts/*.prompt.md` files are no longer generated and
  can be deleted manually if you have them from a prior install.

### Added

- **Agent Skills support** — `hula init` now generates skill files in the
  vendor-neutral standard path `.agents/skills/<name>/SKILL.md` and creates
  a `.claude/skills` symlink pointing to `.agents/skills` so that the same
  files are picked up by **both GitHub Copilot (VS Code) and Claude Code**
  (and 30+ other Agent Skills-compatible tools). Spec:
  https://agentskills.io/specification. The `.claude/skills` symlink
  should be committed so all team members get Claude Code support.

### Removed

- `initializePromptFiles()` and the 9 `src/templates/vscode-*.prompt.md`
  source templates. Their replacements live at
  `src/templates/skills/<name>/SKILL.md`. The old `.github/prompts/`
  directory is no longer generated by `hula init`.

### Fixed

- `hula upload` command reference corrected to `hula chore upload` in
  templates (`src/templates/scripts/hula-launch-run.sh`,
  `src/templates/vscode-upload-prompt.md`,
  `src/templates/vscode-create-prompt.md`) and documentation
  (`docs/vscode-workflow.md`). Without this fix, the auto-upload step
  inside `/hula.launch` failed with `error: unknown command 'upload'`
  because `upload` is registered under the `chore` subcommand group.
  Users who have already run `hula init` must re-run `hula init`
  (with override confirmation) to update their `.github/` files.

### Added

- `hula execute` command — drives the new hula-server Execute Pipeline
  (`/api/v1/execute`, introduced in hula-server PR #225). Supports
  built-in actions (`--built-in harden`) and custom action files
  (`--action-path`), one-off runs and recurring cron schedules
  (`--schedule "0 3 * * *"`), plus management subflags
  `--show`, `--list`, `--list-schedules`, `--cancel-schedule`,
  `--run-now`. Replaces the now-removed `/api/v1/harden-run` endpoint.
- `hula launch` now supports `--update-notification-url <url>` (and the
  `updateNotificationUrl` config field / `HULA_UPDATE_NOTIFICATION_URL`
  env var) to opt into per-task webhook notifications introduced in
  NoStackApp/hula-server PR #183. The server POSTs a single
  notification on every terminal task transition.

### Changed

- Utility/maintenance commands are now grouped under `hula chore
<subcommand>` for a cleaner top-level CLI. Affected subcommands:
  `view`, `track`, `clean`, `close`, `checkout`, `setPriority`,
  `diff`, `watch`, `respond`, `preview`, `logs`, `upload`,
  `worktree`, `project`. Top-level commands remaining: `login`,
  `init`, `create`, `merge`, `launch`, `execute`, `ralph`
  (deprecated alias for `launch`).

### Changed (BREAKING)

- **Plan / Project / Task rename to match hula-project server.**
  Aligns the CLI with hula-project PR #159, which renamed the
  `Repository` model to `Project` and the `Issue` model to `Plan`
  (jobs are now `Tasks` server-side).
  - Server endpoints: `/api/v1/issues*` → `/api/v1/plans*`. Any older
    server is now refused at startup with an `IncompatibleServerError`
    that points operators at this version.
  - Local config: `hulaRepository` → `hulaProject`. The CLI now refuses
    to start if the legacy key is present in `.hublaunch/hublaunch.config.js`.
  - Local tracking file default: `.hublaunch/trackedIssues.json` →
    `.hublaunch/trackedPlans.json`. The CLI auto-renames the legacy
    file the first time it loads config and rewrites the contents to
    schema `version: '3.0'` (renaming `repoOwner`/`repoName` →
    `projectOwner`/`projectName` and dropping per-task `job*` fields).
  - Per-task job-status UI in `hublaunch view` and `hublaunch interactive`
    has been removed. The standalone `/api/v1/ralph-run/<name>/status`
    endpoint and `hublaunch launch --show <name>` continue to work.

  **Upgrade steps for users:**
  1. Update the hula-project server to a build that includes PR #159
     (any release ≥ 2026.04.28 will do).
  2. Edit `.hublaunch/hublaunch.config.js`: rename `hulaRepository:` to
     `hulaProject:` (same value).
  3. Run any `hublaunch` command. The first run automatically renames
     `trackedIssues.json` → `trackedPlans.json` and migrates its contents.

### Fixed

- Fixed `hublaunch clean` and `hublaunch close` failing with HTTP 403
  "You do not have access to this resource" for closed issues tracked by
  other users on the same hula-project server. The server now allows any
  authenticated user to untrack a row whose `githubState === 'closed'`,
  with an audit log entry recorded for non-owner deletes. The CLI handles
  403 responses from older servers gracefully — `close` no longer reports
  the close as failed when only the tracking removal step was denied, and
  `clean` no longer prompts users to retry permanently-denied entries.
- Fixed connection reset errors in long-running background processes (#36)
  - Added retry logic with exponential backoff to `getRepoInfo()` function
  - Added in-memory caching for repository information (5-minute TTL)
  - Enhanced error classification to detect connection reset errors (`ECONNRESET`, `connection reset`, `read tcp`)
  - Improved error messages for network issues during background polling

### Improved

- Background polling now recovers gracefully from network interruptions
- Reduced error message spam during transient network failures
- Added intelligent caching to reduce API calls and survive brief network outages
