Releases: quantizor/directa
Release list
v2.0.0
v1.5.3
v1.5.2
v1.5.1
Patch Changes
-
#26
13e293b- Grok Build sessions receive a live server snapshot.devctl hook install --harness grokwires PreToolUse (the event Grok delivers into context, after the first tool of a turn) and UserPromptSubmit (the per-turn gate) alongside the existing home rule, and removes a leftover SessionStart or Stop registration of the same command. Re-run install to upgrade a SessionStart-only hook;devctl doctorreports the old install as missing. -
#26
13e293b- A noisy dev server no longer drives the daemon's memory into the tens of gigabytes. -
#26
13e293b-devctl uninstallnow also removes Start at Login and the leftover background item in System Settings, so the app cannot launch itself after you remove it. A DMG-installed app is moved to the Trash; Homebrew's copy is left for brew to remove.--purgealso deletes preferences and caches.
v1.5.0
Minor Changes
- #25
be1705a- Servers in a linked git worktree keep the project's declared host instead of getting an ephemeralworktree-<label>.<preferred>.localhostorigin. Every*.localhostname resolves to loopback, so the label never disambiguated a bind, while the third-level subdomain broke any auth config an app pins to one origin (an OAuth callback, a cookie domain, a CORS allow list, a trusted-origins check). Sibling worktrees of one repo are still told apart: the shared committed port auto-rebinds as before, and the worktree name surfaces as a display value (worktreeandmainProjecton status JSON, aworktree: <label>line indevctl statushuman output and inconfig check, and a banner line in session context). The menu bar app and Spotlight show a worktree server under its project family (myproj · review), so searching for the project name still finds it.
Patch Changes
-
#25
557bc3c- Antigravity sessions now rediscover running servers the way Claude Code and Cursor already do.devctl hook install --harness antigravity(also offered at first run and in Settings when~/.geminiis present) wires a PreInvocation hook so a new session sees livedevctlstatus instead of spawning duplicates.devctl hook uninstall --harness antigravityremoves it. -
ef2adc7-devctl doctornow catches a second devctl copy shadowing your Homebrew install, the failure wherebrew upgradechanges one copy while a baredevctl(or the background daemon) keeps running an older one. It reports when a manual~/.local/bininstall coexists with the Homebrew cask (whichever~/.local/binor brew's bin comes first on your PATH is what actually runs), and when an/Applications/devctl.appthat Homebrew did not place is still present. Each finding names the exact cleanup command. Like doctor's other environment findings, it only reports, and only fires when a Homebrew install is present, so a plainmake installis never flagged. -
#25
69232b8- Grok Build is a supported agent harness.devctl hook install --harness grok(also offered at first run and in Settings when~/.grokis present) wires a session hook and a short home rule that tells Grok to rundevctl contextbefore touching a server, so a new session does not spawn duplicates.devctl hook uninstall --harness grokremoves both. -
#25
b170109- A server whose start command waits on an interactive credential prompt (a secrets CLI unlock, a biometric approval) can no longer crash-loop invisibly. When runs repeatedly exit on their own, nonzero, after tens of seconds, without ever passing a healthcheck, status surfacesblockedOn: "interactive-auth"with the remedy in human status,devctl why, and the session-context block: start the server once in a terminal to surface the prompt, thendevctl ensure. The classification is a heuristic in devctl's own words and clears the first time a run dies differently or a healthcheck passes. -
#25
3f8a7c2- Servers under a discarded checkout now stop on their own within about a minute of the checkout disappearing, instead of waiting for a reboot or a machine-widedevctl status --all. The daemon stats every registered project every 30 seconds and prunes a path only after it is missing on two consecutive sweeps, so one flaky stat (a network mount blip, a slow Finder move) never tears down a live project. -
#25
8409259- OpenCode is a supported agent harness.devctl hook install --harness opencode(also offered at first run and in Settings when~/.config/opencodeis present) writes a managed~/.config/opencode/devctl.mdthat tells OpenCode to rundevctl contextbefore touching a server, wired through theinstructionsarray of the winning global config file, so a new session discovers the servers without spawning duplicates. OpenCode has no session-start hook to inject live context into, so the standing instruction is the integration; instruction files already referenced by a losingopencode.jsonstay active after the entry lands inopencode.jsonc.devctl hook uninstall --harness opencoderemoves both halves. -
#25
3f8a7c2---projectnow refuses anything that is not an existing directory (exit 2,usage). Passing a project's NAME instead of its path used to resolve to a nonexistent relative directory and answer an empty scoped view:devctl down --project myprojreported success against a phantom project while the real server kept running.
v1.4.1
Patch Changes
397a4b0- The Homebrew install now tells you what to do next. A freshbrew install --cask quantizor/tap/devctlused to finish with a post-install message that mentioned only uninstall, so you were left with an app that had not started and no hint that the background agent only comes up once you open the app for the first time. The message now names the menu bar app and its agent, the one step to start it (open -a devctl), the one-time macOS confirmation you will see on that first launch, and how to enable your coding-agent session hooks.
v1.4.0
Minor Changes
-
#12
f5831ac-devctl config initwrites a devservers.json from the servers the daemon already knows, which is the way back from losing one. The file is routinely gitignored per machine and nothing could regenerate it, so a lost or destroyed config meant retyping every server by hand. What it writes is deliberately portable: an effective or rebound port, a worktree-derived host, a materialized url, an absolute icon path, and the port and host variables devctl injects are all dropped, so a file recovered inside a linked worktree is still correct on someone else's machine.--dry-runshows the content without touching disk, and an existing file is refused unless--force.devctl register --writeappends one server to the file, leaving every other entry alone.devctl config checknow reports the host a start would actually use when it differs from the declared one. A linked worktree gets an ephemeral host, so any origin the app itself pins (an auth callback URL, a CORS allow list, an API key referrer restriction) was already wrong with nothing saying so until the app broke.devctl stopon a server that declares a resource points atdevctl lock, which gets exclusive access without a bounce, andServerStatuscarries the server'slocksso the same fact reaches--jsonconsumers. The session context block namesdevctl lockas the way to exclusive access, and its invitation to report devctl friction now says to describe the problem generically rather than naming the project, since that backlog lives outside it. -
#18
0069434- devctl installs from Homebrew:brew install --cask quantizor/tap/devctl.brew upgradekeeps it current, and when a newer version ships the menu bar popover shows a quiet notice with a one-click Upgrade button that runs the upgrade in Terminal (a Homebrew install) or links to the release notes (a direct download). A direct DMG download still works exactly as before.A new Settings window, opened from the gear at the bottom of the popover, is the way back to anything you skipped at first run: install or remove the Claude Code and Cursor session hooks per harness, toggle Start at login, and turn the update check on or off. devctl still only edits a harness's settings when you click; it never changes them on its own.
Removing devctl is now a single command.
devctl uninstallunregisters the background agent, removes the agent hooks, and removes the CLI, keeping your data unless you pass--purge; running servers keep going. The Settings window offers the same as a button.devctl doctorreports a harness whose hook is missing or points at a path that no longer exists, and names the command to fix it. -
#13
5f24f46-devctl lockcan now tell you when a command changed the state it was guarding while a server still held that state open. Alocksentry may name where the resource lives on disk ({"name": "d1", "path": ".wrangler/state/v3/d1"}, alongside the plain"d1"form, which keeps working unchanged). With a path declared,lockfingerprints that state before and after the command. Under--no-pausewith a declaring server still running, a change is aresource-mutatedfailure naming the servers to stop and the command to re-run, because the running server holds the old state open and can write its cached pages back over what the command wrote. Under the default paused mode the same change is just a note.This closes a silent data loss: a migration run under
--no-pausethat wiped and rebuilt a local database reported success while the seeded rows were gone, and nothing in the output distinguished that from a clean run. The check is deliberately modest about its limits, and the contract states them: it flags the risk window rather than the damage, it cannot see state outside the declared path or divergence that never reaches disk, and above 8 MiB it samples a file rather than hashing it whole. -
#13
5f24f46-devctl lockno longer passes its own options to the guarded command.devctl lock d1 --timeout 300 -- cmdranenv --timeout 300 -- cmdand died withenv: illegal option -- t, because the resource name ended option parsing and everything after it was captured as the command. The command is now taken from after--verbatim, so a nested--, a dash option, and an empty string all survive, while a missing terminator or an unknown option is rejected instead of quietly passed through.A contended
devctl locksays who holds the resource instead of blocking silently for up to five minutes. It names the holder's pid, how long that run has been going, and which servers it paused or left running, then repeats a still-waiting line while it waits. Silence there reads as a hung gate, and the reflex it invites is killing the run that holds the lock, which is the one making progress.--acquire-timeout 0now makes exactly one attempt and fails immediately, which it could not do before: the wait loop never ran its body at a zero budget and failed with a message naming no holder. All oflock's own output moved to stderr, so stdout carries only the guarded command's. -
#11
ae6a118- Port conflicts now announce themselves at the first surface that can see them.whynames the server and project holding a stopped server's port instead of answering only "not running (stopped)", and the machine-wide status sweep carries the same annotation, so the menu bar app anddoctorsee it too.doctorgains aport-collisionfinding for two unrelated projects that declare one port, which the host-keyed signature table could never report because the hostnames differ while the bind does not; sibling worktrees stay excluded since they rebind by design. A server whose healthcheck is answered by another supervised server now fails withportConflict.state: "foreign"rather than reporting healthy while serving nothing, and a port held by this server and a stranger at once is reported as"shared". A listener outside the process tree that cannot be attributed to a managed server is annotated but left running, because a container-backed or daemonizing server keeps its socket in a process devctl never parented. An unavailablelsofcan never fail a healthy server, since an empty result is not treated as evidence.config checkwarns when a healthcheck declarestype: httpwith nourl, which silently falls back to a TCP probe: a mistyped key otherwise yields a server reporting healthy while its HTTP layer was never checked.doctoralso stops calling a listener unmanaged when another supervised server is the one holding the port.devctl daemon status --jsonnow reportsreachable. Every other command's hint points at this one, and it previously answered{"launchd": "state = running"}with exit 0 when nothing was listening, so a script or agent following the hint was told things were fine.launchd: runningonly means a job is loaded, never that the socket accepts. -
#10
b7bc910- Lock resume waits until claimed ports are free before re-ensuring, so a pause no longer races a dirty bind. Composite servers declare a port span or named subports so sibling worktrees rebind a whole claim block (relative offsets move, absolute ports stay singleton), anddevctl whykeeps the refusal lines from the last run across ensure retries instead of going blank on exit 0. -
#15
aa209df-devctl restart <name>is a real command. Agents were writingdevctl stop X && devctl ensure Xby hand, and several assumed the verb already existed. That pair has two problems this fixes: another session'sensurecan land between the two commands, and a refusal (a held resource, a paused server, a config that no longer parses) arrives only after the server is already down, leaving it down. A restart now refuses before it stops anything, and keeps the server's resume-on-boot intent, which a manual stop clears.A server can also list the config files it reads at boot but does not reload on its own, and devctl restarts it when one changes. Without that, a long-lived supervised server keeps running the old config, so a correct fix looks like it did nothing and a test harness keeps checking stale behavior. A server whose framework already reloads its own config declares nothing and behaves exactly as before. A config a server writes during its own startup will not bounce it, one save touching several files is a single restart, and a server that rewrites its own watched file has its watch suspended with a log line naming the culprit rather than restarting forever.
-
#16
22d5743- A daemon that is coming back up no longer looks like one that is gone. While devctld restores supervised servers at boot it kept its socket closed, so every client gotdaemon-unreachable, which is the same answer a daemon that was never started gives. An agent polling acro...
v1.3.0
Minor Changes
- #8
e503e93- Ship a DMG installer that places the app in Applications and registers the Login Items agent, keep resource locks across a daemon crash so paused servers resume, and rebind the helper after ad-hoc upgrades so the daemon returns in seconds instead of stalling on a codesign throttle.
All notable changes to this project are documented here. Releases are tagged
vX.Y.Z on GitHub (same scheme Changesets uses for this root package).
v1.2.0
Minor Changes
- #4
40d3e97- Add Cursor sessionStart harness (devctl hook install --harness cursor) so Agent sessions get live<devctl-servers>context. - #6
d5ebe80- Open servers and run lifecycle verbs viadevctl://URLs (menu bar app +devctl link/devctl x-url), with unified logging under subsystemdev.quantizor.devctl.
Patch Changes
- #6
46ab7b1- Warn inconfig checkwhen a host or url uses barelocalhost/127.0.0.1instead of a<slug>.localhostorigin. - #6
4695414- Afterhook install, print a one-bullet CLAUDE.md / AGENTS.md discovery tip for the project (paste-only; never auto-edits those files). - #6
67d83e7- Restore config-defined servers after reboot anddaemon installupgrades (merged config+registry recover; install re-ensures like restart). - #6
631a50f- Spotlight entries use<project> · <head>titles with adevctl · <url>subtitle for clearer discovery.