# Latchkey CLI changelog

> What each release of the Latchkey CLI added, changed and fixed, newest first, from 0.3.0 on.

Source: https://latchkey.dev/documentation/latchkey-cli-changelog

## Summary

- Every Latchkey CLI release from 0.3.0 on, newest first, with what it added, changed and fixed.
- `latchkey version` prints the version you have, and `npm install -g @latchkeydev/cli` upgrades to the newest.

Each entry lists what a release of the Latchkey CLI (`@latchkeydev/cli` and the standalone `latchkey` binaries) changed for you and your coding agents, dated the day it was published, in UTC. [Run one-off jobs from the CLI](https://latchkey.dev/documentation/latchkey-cli) describes the commands as they work today.

## 0.17.0

Released 2026-11-13.

### Added

- `latchkey workflows rerun <run-id> --repository <owner/name>` runs the jobs of a Latchkey CI run that did not pass again, with every job that needs one of them, as a new attempt of the same run at the same commit, and prints the attempt and the run's page. Every other job that passed is not run or billed again, and the command does not wait for the attempt. A job bound to an environment that needs an approval waits for one, unless it is held in a trial run or a pipeline on trial, or its environment does not accept the run, and the command names those environments on stderr, as far as Latchkey can still read them from an earlier attempt. With `--output json` it prints one object with `run_id`, `attempt`, `allocated`, `run_url` and `waits_for_approval`, and `code` `RERUN_NEEDS_APPROVAL` with its sentence in `message` when that list names any. A refusal prints its sentence on stderr and exits 1. It needs a key with `runs:control`.
- `latchkey workflows cancel <run-id> --repository <owner/name>` cancels every job of a Latchkey CI run's current attempt that has not finished, through the one cancel path `latchkey cancel` uses, and the jobs it had not created yet are never created. It prints the attempt and the run's page, and with `--output json` one object with `run_id`, `attempt`, `cancel_requested`, `not_created` and `run_url`. A refusal prints its sentence on stderr and exits 1. It needs a key with `runs:control`.
- `latchkey run` and `latchkey preflight` now say on stderr when the batch a job is started under, by `--batch` or `LATCHKEY_BATCH`, has no budget, with the command that sets one (`latchkey budget set <batch> <dollars>`), and the job still starts. Nothing is said when Latchkey could not read the budget or the batch's spend, since the batch may still have a budget, and `latchkey preflight --batch-budget` never says it. With `--output json`, `latchkey run` prints no line: the `created` event's new `batch_budget_status` reads `set`, `none` or `unread`, and is null when the job has no batch.

### Changed

- The bundled skill now covers `latchkey workflows rerun` and `latchkey workflows cancel`, and the MCP tools `rerun_run` and `cancel_run`, where it said no `latchkey` command takes a run id and no key can cancel a whole run.
- The bundled skill now says a `heals` entry below `stage` 3 with `category` `flaky_test` is self-heal's flaky-test re-run, which ran the failed step again unchanged, so its `outcome` `fixed` means the re-run passed, not that anything was fixed.

### Fixed

- `latchkey workflows --help` and the bundled skill now say a job that `latchkey workflows run` starts and that is bound to an environment that needs an approval waits for one, unless it is held in a trial run or a pipeline on trial, or its environment does not accept the run, where they made a trial run the only exception.

## 0.16.0

Released 2026-11-11.

### Added

- `latchkey preflight --workflow <file> --repository <owner/name>` runs the jobs of one workflow file, as Latchkey compiles it, on your working tree as it is now, before you commit or push it. The repository is packed whole, from its top level, by the rules of `latchkey run`, and the change is measured against the merge base of `HEAD` and the default branch `origin/HEAD` names, as this clone last fetched it. Before any runner starts, each job is judged: it runs as written, it runs its dry run, it is skipped by its own condition or for a job it needs, it fails before it starts, or it is not run, with a reason, such as a job that reads a secret, since a preflight is handed none. A preflight paints no check, opens no pull request, starts no self-heal and saves no cache entry. It exits 0 only when no job failed, none was left not run, and a job ran, and 195 only when it left a job not run, or ran no job, and every job it ran passed or was skipped by its own condition or for a job it needs; `latchkey preflight --help` lists the rest. With `--output json` it prints one object once its run has ended. When git cannot say what the change is, it is refused with `CLI_GIT_UNREADABLE` and nothing is sent. It needs a key with `jobs:run`, and a repository connected to Latchkey CI.

### Changed

- `latchkey wait` can now print that self-heal `judged step 3's failure flaky and re-ran the step unchanged`, then `it passed`, or that it `re-ran step 3 unchanged in case its failure was flaky`, then `the re-run did not make the step pass`, where it said self-heal fixed the step or could not fix it, and the `Self-heal` row of `latchkey status` can say the same.
- The bundled skill now covers `latchkey preflight`, including the run it answers, which `latchkey workflows list` never lists, and says a job's own check, such as `Latchkey / ci.yml / test`, can show as queued or in progress while its job waits or runs, where it said Latchkey writes no pending check.
- A key that starts with `lk_dev_` is from Latchkey's dev environment and is no longer sent to the prod API (`api.latchkey.dev` or `latchkey.dev`): the command stops before anything is sent and exits 1 with `CLI_API_KEY_WRONG_ENVIRONMENT`. A key that starts with neither `lk_live_` nor `lk_dev_` still only warns.

### Fixed

- When `latchkey run` reads the files git tracks, it now sets git's filesystem monitor empty, not `false`: a git before 2.36 runs `false` as a program found on `PATH`, where a relative entry can find one in the repository being packed. A directory named `git` on `PATH` no longer stops that read, and on Windows every variable whose name starts with `GIT_` or `LATCHKEY_`, in any case, is kept out of git's environment.
- The README and the bundled skill now give the compressed size ceiling as 200 MiB, the bound the CLI has always held, where they said 200 MB.
- The bundled skill now says a job held in its workflow file's `concurrency:` group (`JOB_HELD_IN_GROUP`) can end without starting if a newer run joins the group first, where it said it ends without starting: the newer run cancelled before Latchkey ends the held job leaves it waiting.
- An API key from `--token`, `LATCHKEY_TOKEN` or the saved config is now trimmed of the spaces, tabs and line breaks around it before it is checked and sent, and `latchkey login --token` saves it trimmed, or stops with `Empty API key.` and exits 2 when the key is only whitespace, where it exited 1. A key ending in a line break, such as a `LATCHKEY_TOKEN` set with a trailing newline, no longer stops the command with `invalid authorization header`, and an `lk_live_` key with whitespace before it no longer draws the warning that it does not start with `lk_live_`.

## 0.15.0

Released 2026-11-09.

### Added

- `latchkey workflows list --repository <owner/name>`, with `--commit <sha>` or `--pr <number>`, lists the Latchkey CI runs of a commit, named by its full id, or of a pull request over the last 90 days, newest first. With `--output json` it prints one object with `repository`, `runs` and `truncated`, each run the object the MCP tool `get_run_status` answers. With more than 50 runs it exits 1 with `LIST_TRUNCATED`, unless `--allow-truncated` takes the newest 50. It needs a key with `jobs:read`.
- `latchkey wait --commit <sha>` and `latchkey wait --pr <number>`, with `--repository <owner/name>`, wait on Latchkey CI runs instead of jobs: the runs at a commit, named by its full id, or a pull request's runs at its newest commit. When the runs end it prints a table of them and exits 0 if each one succeeded or was skipped, 1 otherwise, and with `--output json` it prints one `run_ended` event per run, then a `summary` event. It exits 194 with `RUN_NOT_EXPECTED` when the repository is not connected to Latchkey CI, or when every run it follows ended with no job because no workflow file matched, and 197 with `RUN_WAIT_LIMIT_REACHED` when its last look before it gave up met Latchkey's limit on the waits it holds for a workspace at once.

### Changed

- Before a command sends its key, it prints `Latchkey API: <host>` on stderr, naming the host the key goes to, once a process and in json mode too, so a key pointed at the wrong environment shows before anything answers. On `latchkey run` and `latchkey watch`, `--quiet` leaves it off.
- The bundled skill now covers finding the runs of a commit or a pull request with `latchkey workflows list` and `latchkey wait --commit` or `--pr`, and with the MCP tools `list_runs` and `wait_run`.
- `latchkey watch --help` now gives the exception the bundled skill already gave: on GitHub Actions, the one re-run of a failing test that Latchkey allows while the workflow has an open flaky-test finding is not escalated when Latchkey could read the failing tests from the test runner's report and the re-run failed the same way, so the run surfaces once, under its one id.

## 0.14.0

Released 2026-11-06.

### Changed

- `latchkey migrate plan` now prints a pipeline already on trial as `On trial`, where it printed `Stays`, and the help no longer says `latchkey migrate apply` needs the repository in advisory mode: it needs the repository connected to Latchkey CI. Latchkey opens such an apply for any version of the CLI. The bundled skill's refusals of `migrate apply` now name a plan that moves a pipeline already on trial, in place of a repository not in advisory mode.
- `latchkey watch --help` and the bundled skill now say `latchkey watch` polls the failures self-heal recorded but did not fix and hands off the first new failure of each job (of each run on Latchkey on GH), and that a failure whose step self-heal refused to read carries no root cause or failing file. The prompt it hands your agent now says self-heal recorded the failure but did not fix it, and that `get_failure_bundle` returns what Latchkey kept of it. Only the words changed: `watch` picks the failures it hands off by the same rule as 0.13.0, from the list Latchkey returns to any version of the CLI.

## 0.13.0

Released 2026-11-04.

### Added

- `latchkey list --state active` lists only the jobs still on their way: every queued, provisioning or running job, up to 100, newest first, however many newer jobs have finished. `--batch` narrows it, and `--before` and `--limit` are refused, since it is read whole rather than a page at a time. With `--output json` it prints `jobs` and `truncated`, true when more than 100 were on their way, and in text a line on stderr says so. A Latchkey API that cannot read the jobs still on their way would answer with jobs in every state, so it is refused with `CLI_LIST_FILTER_UNSUPPORTED` and nothing is listed. `--state all`, the default, lists every job as before.

### Changed

- The bundled skill now covers reading what is still on its way with `latchkey list --state active`, and its `CLI_LIST_FILTER_UNSUPPORTED` row names `--state active`.

## 0.12.0

Released 2026-11-04.

### Changed

- When `latchkey run` refuses an empty directory, it now says what to do: run from your project's directory, or pass `--no-context` for a command that needs no files. The bundled skill's `CLI_CONTEXT_EMPTY` row names the flag too.
- With `--output json`, `latchkey list`, `latchkey batch show` and `latchkey status` always hold `run_id`, the Latchkey CI run a job belongs to, and `served_by`, how it got its runner (`warm`, `parked` or `cold_start`), each `null` when Latchkey sends none. A CLI from 0.4.0 on already prints both when Latchkey sends them, and the text output has not changed.
- The bundled skill now says to split a suite through the package's own `test` script (`npm test -- --shard=2/4`), so every shard runs the package's `pretest` steps as CI does. It also covers moving to Latchkey CI: a run reads only the secrets set in Latchkey, never a GitHub secret of the same name, so an owner or admin carries them over before the switch pull request merges, and once it merges, a moved pipeline is read from the commit's Latchkey checks, `get_run_status` or `latchkey list`, because GitHub Actions no longer runs it.
- The bundled skill now names `merge_group` among the events a workflow file runs on: each group a merge queue builds, matched on the branch the group targets.

## 0.11.0

Released 2026-11-03.

### Changed

- `latchkey migrate plan` now says a moving job bound to an environment waits for an owner or admin to approve it once its pipeline runs live, where it used to say in a live run. In a repository on Latchkey CI, a pipeline runs on trial, even inside a live run, while the GitHub Actions file it replaces is still at the commit.
- The bundled skill now says that `failure_explanation` names the first step that failed, and that step's exit code when it has one, on a workflow-file job that failed with `failure_reason` `null`, and that output the CLI cannot write never changes its exit code. It also covers pipelines on trial inside a live run: what `advisory:` does in their jobs, the `failure_explanation` starting `Held on trial:` of a held job, `list_trial_verdicts` naming in `workflow_path` the pipeline on trial each entry compares, and `get_run_status` saying in `trial` why some of a run's pipelines ran on trial.

### Fixed

- A full disk under a redirected stdout or stderr no longer makes the CLI exit 1, which reads as a failed job: when either stream cannot be written, the CLI exits with the code it would have given. It drops the rest of its stdout and, unless the reader went away (`| head`), says so once on stderr, and it drops what it cannot write to stderr without a word. A log it could not write is still on the job, and `latchkey logs <id>` reads it again.

## 0.10.0

Released 2026-11-02.

### Added

- `latchkey workflows run <workflow> --repository <owner/name>` starts a run of a Latchkey CI workflow file that declares `on: workflow_dispatch`, at the repository's default branch or at the branch or tag `--ref` names, with the file's inputs given as `--input <name=value>`. It prints a link to the run's page without waiting for the run, and running it again starts a second run, billed as its own. The key needs the `workflows:run` scope. An input is recorded on the run and shown on its page, and a job's log does not hide it, so never put a token or password in one.
- `latchkey budget set <batch> <dollars> --max-concurrent <n>` limits how many of a batch's jobs run at once, a whole number from 1 to 1000. The rest wait in the queue and start as the batch has room. An API key can set a batch's first limit or lower it, never raise or remove it, and `latchkey budget show` prints the limit.

### Changed

- `latchkey status` shows a Self-heal row for each self-heal attempt on the job that fixed a failure or could not, and `latchkey run` and `latchkey wait` print each as a line after the job's outcome. When self-heal's agent reported a fix but the job ended without passing, the line says the fix was reported, not made. With `--output json`, `run`'s `complete` event and `wait`'s `ended` and `summary` events carry the job's `heals` list as `status` prints it.
- The bundled skill now covers starting a workflow file with `latchkey workflows run` or the `run_workflow` MCP tool, pacing a burst of jobs with its batch's limit, why a batch job can wait behind Latchkey CI jobs near the workspace's limit, reading heal records and trial verdicts over MCP (`list_heals`, `get_heal` and `list_trial_verdicts`), splitting a test suite across matrix legs, and what self-heal reads from an uploaded working tree.
- The npm package's homepage is now the Latchkey CLI documentation page, and its README links this changelog.

## 0.9.0

Released 2026-10-28.

### Added

- `latchkey oidc request-token --audience <audience>` gets a short-lived identity token for your cloud from inside a job of a Latchkey CI run. It prints the token, or with `--out <file>` writes it to a file only its owner can read. Like `latchkey cache`, it talks to the job's runner and needs no API key.
- `latchkey validate <file>` checks a workflow file the way Latchkey compiles it for a run, without starting anything. A refused file prints its first refusal with its line; a file that compiles prints its jobs and the most one run of the file can cost at list rates. It exits 0 when the file compiles and 1 when it is refused.

### Changed

- The standalone binaries now embed Node 24, so the macOS binaries need macOS 13.5 or newer. On older macOS, install with npm.
- `latchkey environments list` adds an Approval column saying whether an environment's jobs wait for an owner or admin to approve them.
- `latchkey migrate plan` says when a moving job bound to an environment will wait for an approval in a live run, and, for a pipeline that stays on GitHub Actions but runs after one that moves, whether it stops running or runs less often once the switch pull request is merged.
- A batch named `.` or `..` alone is refused by every command that takes a batch name (exit 2).

### Fixed

- An `--env` variable named `__proto__` is now sent with the job instead of being dropped.

## 0.8.0

Released 2026-10-27.

### Changed

- A batch name can no longer hold `/` or `%`. `latchkey run`, `latchkey list` and `latchkey wait` now refuse one, including one set in `LATCHKEY_BATCH`, before reaching Latchkey (exit 2), as `latchkey budget` and `latchkey batch show` already did.

## 0.7.0

Released 2026-10-26.

### Changed

- `latchkey migrate plan` marks a step whose dry-run command the plan refused: its "Needs a person" line gives the reason, and the item stays open until you answer with a command the workflow file can run.
- The answers file for `latchkey migrate plan --answers <file>` can give a `dry_run` answer with a `dry_run_command`, which the plan writes as a trial-run step in place of the answered one.
- The bundled skill now covers trial runs (`advisory:` on jobs and steps), `concurrency:` groups in workflow files, and the `latchkey/artifact-upload` and `latchkey/artifact-download` steps.

## 0.6.0

Released 2026-10-24.

### Added

- `latchkey wait` waits until the jobs you name, or every job of a batch with `--batch <name>`, have ended, printing each outcome as it ends and then a table of state, exit code, run time, cost and batch. It exits 0 only when every job exited 0, and Ctrl-C stops the wait, never a job.
- `latchkey batch show <name>` shows one batch: its budget, what its jobs have cost at list rates, its jobs by state and why its queued jobs wait. `latchkey budget show <batch>` prints the budget and spend alone.
- `latchkey environments list --repository <owner/name>` lists the branches each environment of a repository accepts.
- `latchkey list` takes `--batch <name>` for one batch's jobs and `--before <value>` to page further back, shows a COST column, and `latchkey status` shows a finished job's cost.
- When a newer CLI is out, a command printing text says so on stderr, at most once a day and never in CI. Set `LATCHKEY_NO_UPDATE_NOTICE` to turn it off.

### Changed

- Inside a git work tree, `latchkey run` packs every file git tracks, even one a `.gitignore` pattern matches, as a fresh clone would have it. The credential deny list, `--exclude` and `.latchkeyignore` still apply, and `--no-git-index` packs by the `.gitignore` files alone.
- Every refusal carries a code. With `--output json`, a refused command prints one object with its code, and the bundled skill lists every code.
- `latchkey run` refuses a leading `bash -c` or `sh -c` (exit 2), since that shell would run only the first word of its script. Pass the script as one quoted argument instead.

## 0.5.0

Released 2026-10-24.

### Added

- `latchkey artifact upload` and `latchkey artifact download` pass files between the jobs of one Latchkey CI run. A failed upload or download exits 1, so the step fails, and an upload leaves out dot-files such as `.env` unless you pass `--include-hidden-files`.
- `latchkey migrate plan <owner/name>` reads a repository's GitHub Actions workflow files and prints its plan for moving to Latchkey CI, with each item that needs your answer. `latchkey migrate apply` opens the one pull request that adds the Latchkey CI workflow files, and `latchkey migrate status` shows where it stands.
- `latchkey steps` lists the Latchkey steps a workflow file can name with `uses: latchkey/<name>@<version>`, with their inputs and defaults.

### Changed

- A job whose run an owner or admin rejected for its environment exits 130 from `latchkey run` and `latchkey logs --follow`, as a stop rather than a failure to fix.

## 0.4.1

Released 2026-10-19.

### Fixed

- The help text, README and bundled skill now match how 0.4.0 behaves: the exit codes list `skipped` (exit 1), logs stay readable for 7 days from when the job started, and `latchkey cache` works only inside a Latchkey CI job or a `latchkey run` job.
- `latchkey budget set` help says an API key can set a batch's first budget up to the workspace's spend limit, and that an owner or admin raises a budget on the Billing page.

## 0.4.0

Released 2026-10-19.

### Added

- `latchkey run --context <dir>` packs another directory instead of the current one, and `--exclude <pattern>` holds paths back without editing a file. No `--exclude` pattern can ship a file the credential deny list blocks.
- `latchkey run --budget <dollars>` stops a job once it has cost that much (exit 3), and `--batch <name>` or `LATCHKEY_BATCH` puts jobs in a batch.
- `latchkey budget set <batch> <dollars>` caps a batch. A `latchkey run` refused at its batch's budget exits 193 and creates no job.
- `latchkey cache restore` and `latchkey cache save` restore and save a cache entry from inside a job. No cache condition fails the job.
- `latchkey secrets set <name>` sets a secret a workflow step can read, for the workspace or one repository. The value comes from a pipe or a hidden prompt, never the command line.

### Changed

- `latchkey run` prints a link to each job's page in the dashboard, and `latchkey watch` links a failure to its Latchkey run page.
- `latchkey status` shows more when Latchkey sends it: running and queued jobs against the workspace's limit, the jobs ahead of a queued job and why it waits, the cost so far, its batch, key and budget, when it last wrote output, and what ended it. `latchkey list` adds BATCH and KEY columns.
- `--timeout` accepts up to 3 hours, and a job's logs stay readable for 7 days from when it started.
- `latchkey run --detach` exits with the job's mapped code when the submit already ended the job, a `skipped` job exits 1, and every `--output json` event carries `at`.

### Fixed

- Text from Latchkey that reaches your terminal is stripped of control, text-direction and invisible format characters, and a failure reason a job reported itself is shown as the job's own words.

## 0.3.0

Released 2026-08-15.

### Added

- `latchkey list` lists your workspace's recent jobs, newest first, including the ones still running, so you can recover a job id that scrolled away.

### Changed

- `latchkey watch` also reports `latchkey run` jobs that self-heal recorded but did not fix, and every failure it prints carries its root cause and failing file when one was recorded.

### Fixed

- When the log tail of `latchkey run` or `latchkey logs --follow` gives up after repeated network errors, the CLI checks the job once more and exits with its own code if it finished, where it used to exit 1.
- The log tail catches up faster on a backlog of colored or non-ASCII output.

---

Latchkey runs CI/CD that repairs its own failures. Agent entry points: https://latchkey.dev/agent.txt, https://latchkey.dev/openapi.json, https://latchkey.dev/llms.txt
