Skip to content

Releases: quantizor/directa

v2.0.0

Choose a tag to compare

@github-actions github-actions released this 04 Sep 19:24
6d591aa

Major Changes

  • #32 45caa88 - The product is now called directa. The CLI is directa, the daemon is ddirecta, the menu bar app is directa.app, and deep links use directa://. devservers.json is unchanged.

Patch Changes

  • #32 45caa88 - The menu bar app has a standard About panel (directa > About directa) showing the version, copyright, and MIT License.

v1.5.3

Choose a tag to compare

@quantizor quantizor released this 03 Sep 00:37
5e49bbc

Patch Changes

  • #30 34c9331 - The menu bar icon no longer quits itself as idle after macOS reclaims memory. The extra has no windows AppKit counts as "in use", so a memory-pressure pass was allowed to terminate it, and Start at Login only brings it back at the next login.

v1.5.2

Choose a tag to compare

@github-actions github-actions released this 02 Sep 19:54
721acd6

Patch Changes

  • #28 3e19c23 - A memory-hungry dev server no longer takes the background daemon down with it. Under the menu bar agent, each server now runs as its own launchd job, so macOS can reclaim that server's memory without killing every other server the daemon is supervising.

v1.5.1

Choose a tag to compare

@github-actions github-actions released this 02 Sep 13:33
e1d3d87

Patch Changes

  • #26 13e293b - Grok Build sessions receive a live server snapshot. devctl hook install --harness grok wires 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 doctor reports 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 uninstall now 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. --purge also deletes preferences and caches.

v1.5.0

Choose a tag to compare

@github-actions github-actions released this 28 Aug 21:49
1348c8f

Minor Changes

  • #25 be1705a - Servers in a linked git worktree keep the project's declared host instead of getting an ephemeral worktree-<label>.<preferred>.localhost origin. Every *.localhost name 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 (worktree and mainProject on status JSON, a worktree: <label> line in devctl status human output and in config 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 ~/.gemini is present) wires a PreInvocation hook so a new session sees live devctl status instead of spawning duplicates. devctl hook uninstall --harness antigravity removes it.

  • ef2adc7 - devctl doctor now catches a second devctl copy shadowing your Homebrew install, the failure where brew upgrade changes one copy while a bare devctl (or the background daemon) keeps running an older one. It reports when a manual ~/.local/bin install coexists with the Homebrew cask (whichever ~/.local/bin or brew's bin comes first on your PATH is what actually runs), and when an /Applications/devctl.app that 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 plain make install is 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 ~/.grok is present) wires a session hook and a short home rule that tells Grok to run devctl context before touching a server, so a new session does not spawn duplicates. devctl hook uninstall --harness grok removes 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 surfaces blockedOn: "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, then devctl 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-wide devctl 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/opencode is present) writes a managed ~/.config/opencode/devctl.md that tells OpenCode to run devctl context before touching a server, wired through the instructions array 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 losing opencode.json stay active after the entry lands in opencode.jsonc. devctl hook uninstall --harness opencode removes both halves.

  • #25 3f8a7c2 - --project now 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 myproj reported success against a phantom project while the real server kept running.

v1.4.1

Choose a tag to compare

@github-actions github-actions released this 10 Aug 04:19
e18ea0a

Patch Changes

  • 397a4b0 - The Homebrew install now tells you what to do next. A fresh brew install --cask quantizor/tap/devctl used 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

Choose a tag to compare

@github-actions github-actions released this 10 Aug 03:52
77f8983

Minor Changes

  • #12 f5831ac - devctl config init writes 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-run shows the content without touching disk, and an existing file is refused unless --force. devctl register --write appends one server to the file, leaving every other entry alone.

    devctl config check now 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 stop on a server that declares a resource points at devctl lock, which gets exclusive access without a bounce, and ServerStatus carries the server's locks so the same fact reaches --json consumers. The session context block names devctl lock as 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 upgrade keeps 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 uninstall unregisters 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 doctor reports 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 lock can now tell you when a command changed the state it was guarding while a server still held that state open. A locks entry 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, lock fingerprints that state before and after the command. Under --no-pause with a declaring server still running, a change is a resource-mutated failure 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-pause that 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 lock no longer passes its own options to the guarded command. devctl lock d1 --timeout 300 -- cmd ran env --timeout 300 -- cmd and died with env: 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 lock says 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 0 now 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 of lock'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. why names 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 and doctor see it too. doctor gains a port-collision finding 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 with portConflict.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 unavailable lsof can never fail a healthy server, since an empty result is not treated as evidence.

    config check warns when a healthcheck declares type: http with no url, which silently falls back to a TCP probe: a mistyped key otherwise yields a server reporting healthy while its HTTP layer was never checked. doctor also stops calling a listener unmanaged when another supervised server is the one holding the port.

    devctl daemon status --json now reports reachable. 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: running only 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), and devctl why keeps 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 writing devctl stop X && devctl ensure X by hand, and several assumed the verb already existed. That pair has two problems this fixes: another session's ensure can 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 got daemon-unreachable, which is the same answer a daemon that was never started gives. An agent polling acro...

Read more

v1.3.0

Choose a tag to compare

@github-actions github-actions released this 25 Jul 04:17
935a2a2

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

Choose a tag to compare

@github-actions github-actions released this 24 Jul 18:01

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 via devctl:// URLs (menu bar app + devctl link / devctl x-url), with unified logging under subsystem dev.quantizor.devctl.

Patch Changes

  • #6 46ab7b1 - Warn in config check when a host or url uses bare localhost / 127.0.0.1 instead of a <slug>.localhost origin.
  • #6 4695414 - After hook 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 and daemon install upgrades (merged config+registry recover; install re-ensures like restart).
  • #6 631a50f - Spotlight entries use <project> · <head> titles with a devctl · <url> subtitle for clearer discovery.

v1.1.0

Choose a tag to compare

@quantizor quantizor released this 23 Jul 06:31
  • Fixed a health check issue with some dev servers that primarily communicate over IPv6.
  • Many design improvements.
  • Keyboard navigation.
  • Sorting & Filtering.