v0.5.0
looptroop@0.5.0 release:notes
node scripts/print-release-notes.ts 0.5.0
- Restructured the server into an embeddable runtime that performs no work when imported, so tooling can load it without starting a server, opening the database, or installing signal handlers.
- Added a production build that bundles the server and ships only compiled output, cutting a global installation from roughly three hundred packages to sixteen.
- Made the production server serve the interface itself, so a single command opens a working application on one address with no separate web server to configure.
- Moved the public website and published documentation into a dedicated repository, leaving the application checkout and development stack focused on LoopTroop itself.
- Repaired documentation and README media references after the website and documentation moved to their standalone repository, restoring the walkthrough GIF without duplicating the asset.
- Replaced the native
better-sqlite3database driver with Node's built-innode:sqlite, removing the last compiled dependency from the runtime and eliminating the most common cause of failed installations. - Introduced schema versioning for both the application and per-project databases, so a database written by a newer LoopTroop is refused with a clear message instead of being silently and destructively downgraded.
- Established the first CI workflow to enforce lint, typecheck, test, build, and clean-tree checks on every push and pull request. The required lanes all run the exact toolchain the package declares — Node 24.15.0 and npm 11.12.1 — on Linux, macOS and Windows, because the floor is a promise on all three. Non-blocking lanes track the newest Node 24 patch and Node 26 so upstream breakage arrives as news rather than as a failed required check.
- Downgraded
@types/nodefrom 26 to 24 to prevent accidental use of Node 26-only APIs while targeting Node 24 LTS as the minimum supported runtime. - Declared the runtime Node floor (
engines.node), pinned the package manager, and added.nvmrcso CI can enforce the minimum without testing only the latest patch. - Fixed a latent development-server bug where
@codemirror/mergewould fail to load in the diff editor because Vite's dependency optimizer was configured withnoDiscovery: truebut the package was missing from the pre-declared list. - Removed direct email and Twitter contact links from the web landing page footer.
- Pointed all in-app documentation links at the independently hosted documentation site and removed the local docs server from the app development stack.
- Made
npm run devverify-only, so starting the development environment no longer silently updates dependencies, rewrites the lockfile, or upgrades the OpenCode CLI; those updates are now explicit opt-in commands. - Made
package.jsonthe single source of truth for the application version, enforced in CI, so the version shown in the app interface can no longer drift from the released version; the website independently reads the latest stable GitHub Release. - Wired the root changelog to release notes: publishing a release now derives its notes from
CHANGELOG.mdinstead of being written by hand, so the two can no longer disagree. - Moved routine dependency updates onto a predictable weekly schedule with a maturity delay, so updates are reviewed in batches and security fixes still arrive quickly.
- Added governance documents (
SECURITY.mdand generated third-party notices) and wired the notices into CI, so a missing licence or stale notices file fails the build. The notices cover both the packages installed at runtime and the interface libraries compiled into the shipped bundle, which no dependency scan can see on its own. - Added a
looptroopcommand that runs LoopTroop as a background service:setup,start,stop,restart,status,open,logs,doctorandclean.startdetaches from the terminal and keeps running after the shell closes, so the application is no longer tied to a terminal window left open. - Limited each configuration directory to one running daemon, held by a lock that records which process took it rather than only when it last checked in. Two daemons sharing one set of databases and worktrees is the failure this prevents, and a suspended laptop no longer looks like an abandoned lock.
- Separated browser sessions from script access. A browser is signed in through a single-use code carried in the URL fragment, which is never sent in a request line and so cannot reach an access log, and exchanges it for a session cookie that scripts cannot read. Automation uses a separate bearer token. There is no way to authenticate by query string.
- Restricted accepted requests to this machine and to this daemon's own address, and made the session cookie usable only on requests the browser itself vouches for as same-origin. Another page served from a different port on the same loopback interface cannot drive this daemon with the cookie the browser would otherwise attach for it, since cookies carry no port scope of their own.
- Made the daemon manage the OpenCode server it depends on: it adopts one already running on the configured address or starts its own, records which of those happened, and stops only servers it started.
- Added a one-a-day update check that names the upgrade command for the way this copy was actually installed, and prints nothing when the check cannot reach the registry.
- Made the shipped package verifiable as a package: CI packs it, installs it globally on Linux, macOS and Windows, and drives a whole session from an empty directory — including one run from an installation the kernel makes read-only, which is what a global install on a shared machine is.
- Added a signed-out screen, so a tab whose 12-hour session has ended says so and names the command that signs in again, instead of rendering an interface whose every request is refused.
- Made releasing a version an automated, resumable operation rather than a sequence someone has to perform correctly by hand. A release is cut by merging a pull request that raises the version; the workflow then builds the package once and publishes those exact bytes to every channel. Because publishing is several irreversible steps in a row, the workflow first reads what already exists — the tag, the GitHub Release, the registry — and resolves a state from it, so a run interrupted halfway resumes from where it stopped instead of starting over or refusing to continue. Three situations deliberately stop it dead rather than being repaired: a tag that names a commit other than the one being released, a version already on npm under different bytes, and a version whose bytes are right but whose install tag points elsewhere. None can be corrected by retrying, and all three mean something happened outside the workflow. A run only calls a release finished once it has compared the bytes on the registry against the bytes it built; having every channel in place is not by itself proof, so a run that cannot make that comparison reports it as unverified rather than as done.
- Published the CLI to npm as
looptroop, installable withnpm install -g looptroop, with the package's authenticity established by npm's trusted publishing rather than by a long-lived token stored in the repository. - Made raising the version refuse to proceed when the changelog entries it would publish are not publishable, using the same reader that renders the release notes rather than a looser check of its own, so a release cannot get as far as a pull request and then fail at the point of publishing.
- Made the container image a release channel rather than a Dockerfile nobody ran. It is now built on every push for both architectures, pinned to its base image by digest, labelled with the version and the commit it came from, and driven from inside a running container by a smoke test that proves the daemon starts as an unprivileged user, keeps its API closed without credentials, and shuts down cleanly when asked.
Container image
docker pull looptroopai/looptroop:0.5.0
docker pull ghcr.io/looptroop-ai/looptroop:0.5.0
Both registries serve the same linux/amd64 + linux/arm64 index,
sha256:3c5fe464d23857cff85de154110566819b3b2a0c1db04c73b86f07ac2dedb089.
The image ships without OpenCode, so a bare docker run exits at
startup by design — see Run it in a container
for how to point it at a server and reach the interface.