Skip to content

Releases: DevSpecAI/devspec-claude-plugin

v0.7.3

Choose a tag to compare

@DevSpecAliPrice DevSpecAliPrice released this 07 Aug 09:53

Memory search returns a card now, so the plugin has to be able to read the body

DevSpec's search_memories (and get_decisions / get_conventions) changed today: they return a card — title, one-line summary, id, state — instead of full memory bodies, because a 15-result search was returning over 600,000 characters. The full text now comes from a new get_memory tool.

  • Five commands could have found a memory and never read it. devspec.brainstorm, devspec.remote, devspec.session-brainstorm, devspec.work and the autopilot skill all allow-list search_memories, and an allowed-tools: list is a hard gate — a tool absent from it cannot be called. get_memory did not exist when those lists were written, so without this release those commands get cards and have no way to reach the body. Nothing errors; the agent just quietly has less to reason with.
  • The instruction it broke was the safety-relevant one. Four of those commands say "search_memories FIRST and supersede_memory the stale match instead of duplicating". Yesterday that judgement was made against full bodies. On cards alone, an agent would overwrite an entry in the team's shared decision record having read only its one-line summary. Each of those now says to get_memory the match and read it in full first — a card is enough to choose WHICH memory you mean, not enough to justify replacing it.
  • The autopilot loop needed more than a tool name. Its memory step is marked MANDATORY — never skip and tells the agent to treat what comes back as hard constraints. A constraint is exactly the thing you cannot safely obey from a summary: the summary states the decision, the body carries the qualifications and exceptions. That step now says to get_memory any card that looks like it binds the work — an unattended loop obeying a constraint without its exceptions is how it confidently does the wrong thing.

Nothing else needs reinstalling for the DevSpec-side change: MCP tool definitions come from the server, so a reconnect picks up the renamed body parameter and the now-required title on record_memory. This release is required only because allowed-tools: lists ship inside the plugin.

Item 93a851b5.

v0.7.2

Choose a tag to compare

@DevSpecAliPrice DevSpecAliPrice released this 04 Aug 09:47

A connected agent no longer wakes itself up once a day for nothing

  • Measured, not theorised. The 24h cap fired on schedule two days running against a healthy owner-anchored stream. Each firing spent a model turn on a wake that carried no owner mail and a re-arm that changed nothing — and the host reported the non-zero exit as a failure on top of that. be0a929a's own intent named this cost before it happened: "in a metered product that is real money spent on zero work."
  • The cap was buying nothing there. A deadline is a zombie backstop, not a policy. An owner-anchored arm already exits the moment the owning agent process goes, which is exactly the contract the poller has always run on with no time cap — it was at 2d+ uptime while this was written. So an anchored --stream arm now has no deadline at all.
  • An arm that cannot anchor still keeps the cap, because for that one the clock is the only remaining guarantee that it cannot outlive its owner. That is the case remote-control-state.mjs already refuses to start a poller in, and it must not be given up here either.
  • The one-shot fallback keeps its cap unconditionally. That asymmetry is deliberate: a one-shot arm is expected to be short-lived, so its 24h rollover is a rare edge case rather than a daily event, and exit-3-at-24h is a contract d655b2a4 established on purpose.

Item be0a929a.

v0.7.1

Choose a tag to compare

@DevSpecAliPrice DevSpecAliPrice released this 04 Aug 09:47

A 24-hour rollover no longer looks like a crash

  • Observed in real use, one day after 0.7.0 shipped. The wake stream hit its 24h cap exactly as designed, emitted its listener_rollover notice and exited 3 (non-terminal, "re-arm me"). The host then reported that as "script failed (exit 3)" — so a routine rollover reads, at a glance, like something broke. This is the same misreading d655b2a4 was filed for, in new clothing: a non-terminal end that looks like an ending, which historically provokes the harmful reflex of re-registering a perfectly live connection.
  • The rollover notice now says so explicitly — that the host may label the exit a failure, that it is not one, and that nothing is lost because re-arming resumes from the same cursor. The exit table says the same, and to trust the event over the host's summary label. The truth goes where the agent actually reads it, rather than relying on it to interpret an exit code correctly.
  • No behaviour change: the cap, the exit code and the cursor are all unchanged.

Also confirms 0.7.0 in the field: that stream ran a full 24 hours as a persistent monitor before its own cap ended it, across an entirely idle period, and the re-arm drained a cursor exactly level with the inbox — nothing skipped, nothing replayed.

Item be0a929a.

v0.7.0

Choose a tag to compare

@DevSpecAliPrice DevSpecAliPrice released this 04 Aug 09:47

The wake channel is armed once per session, not once per turn

  • The failure this fixes. 0.6.5's Stop hook correctly refuses to let an agent end a turn with nothing listening. But the listener it asked for was a background task, and Claude Code reaps background tasks at turn end. So: the agent armed correctly, the Stop hook saw a live listener and passed, the host then reaped it, the reap started a new turn with nothing armed, the Stop hook blocked, the agent re-armed — round and round, one model turn per lap, with no way out. Reproduced five times consecutively. No amount of agent diligence touched it, because the agent was already doing everything right; the failure was in who owned the process.
  • The wake no longer depends on something dying. The listener used to wake the model by exiting, which is exactly what tied its lifetime to the turn. It can now run in --stream mode: it prints one line per owner command and keeps watching. Armed once with a persistent monitor, it lasts the whole session, so there is no re-arm to lose to a reaper — and nothing to forget.
  • The Stop hook was not weakened to achieve this. It still blocks a turn ending with no listener, on exactly the same condition as before. A session-long listener simply satisfies it on every turn from a single arm. What changed is the way out of the block, which used to name the reapable arm.
  • "Live" and "will actually wake up" stop being different promises on this host: the listener's lifetime is now the session's lifetime. 0.6.6's honest Not reading signal stays exactly as it is, for any host that cannot hold a stream.
  • No command is lost in the switch. The inbox cursor is unchanged, and now advances only after a command has been handed over — so an interrupted delivery repeats rather than vanishes.
  • The one-shot behaviour remains available for hosts with no persistent monitor; drop --stream and it exits on the first command as before.

Item be0a929a (related: 8b4ceaa3, d655b2a4).

Maintainers: plugin families are independent implementations now

  • We no longer treat this plugin as canonical and port its scripts outward. That practice produced a repeating cycle — fix one host, port the fix, break a second host, port that fix, introduce a third bug. What is shared is the DevSpec side: the MCP tool contract, the inbox format, the delivery contract, the skills, and the behaviour a connection must exhibit. How a host meets that contract is allowed to differ.
  • devspec-remote-wait.mjs is now HOST_OWNED and is never synced. How you wake a model is the most host-specific thing in the plugin: Claude Code needs a session-scoped stream, Grok Build's monitor already turns every stdout line into an event, and Codex is a bridge with no local waker at all. This also retires the 0.6.5 maintainer warning below — the exit-3 change can no longer be carried into families whose exit tables would misread it.
  • sync-hooks.mjs declares host-owned files on every run, so "owned by policy" can never again look identical to "somebody forgot to list it".
  • Fixed: marketplace.json had drifted to 0.5.1 while plugin.json was 0.6.6, despite being documented as lockstep. Both are 0.7.0.

v0.6.6

Choose a tag to compare

@DevSpecAliPrice DevSpecAliPrice released this 04 Aug 09:47

DevSpec can now tell you an agent has stopped listening

  • "Live" used to mean "the process is up", which is not the same as "work sent here will be read." 0.6.5 stopped an agent going deaf in the first place; this reports the state honestly if it ever happens anyway. The poller now checks whether a wake listener actually holds the pidfile, counts any owner commands sitting unconsumed in the inbox, and sends both on the heartbeat it already makes. DevSpec shows such an agent as Not reading rather than Live, and stops offering it as available to dispatch to.
  • Armed is proved by a live pid, never by a leftover file — a hard-killed listener leaves its pidfile behind, and trusting that would report exactly the lie this is meant to catch.
  • It will not cry wolf. A missing pidfile only counts as evidence once this connection has armed a pidfile-writing wait at least once. Waits armed before that shipped never wrote one, so "no file" from them means "older build", not "deaf" — otherwise every healthy pre-upgrade agent would light up as Not reading at the very moment you were deciding whether to trust the new badge.

Items 8b4ceaa3, d655b2a4.

v0.6.5

Choose a tag to compare

@DevSpecAliPrice DevSpecAliPrice released this 04 Aug 09:47

A remote agent can no longer go silently deaf while telling you it is fine

  • The failure this fixes. You send your agent a command from your phone. Nothing comes back. The Agents page still says Live and available, so you reasonably conclude the connection dropped and reconnect. Nothing had dropped: the poller was healthy and had delivered every message correctly, but the one-shot listener that turns a delivered message into an actual agent wake-up had not been re-armed, so nobody read them. Delivery depended on the agent remembering a bookkeeping step at the end of every turn, and there was no backstop when it forgot.
  • Why it kept coming back. The original design had one process that both heartbeated and woke the agent, so losing the waker also lost the heartbeat and the chip went Disconnected — loud and impossible to misread. Moving liveness onto a keeper-managed poller fixed "Live drops while the agent works" and left the wake channel with no keeper at all, so the thing still reporting Live was no longer the thing that wakes you.
  • Stop now refuses to end a turn deaf. While armed, the wait owns a per-connection pidfile, so the Stop hook can prove whether anything is actually listening — and if a turn is about to end with no listener, or with commands sitting unread, it blocks and hands the agent the re-arm command. The guarantee moved from "the agent remembers" to "the hook enforces". Liveness is proved by a live pid, never by a leftover file, so a hard-killed listener cannot masquerade as a healthy one.
  • A blocked stop keeps you "working". The turn genuinely has not ended, so the indicator stays on rather than reporting a finish that has not happened.
  • A reaped or aged-out listener no longer reads as a dead connection. The wait used to exit 1 both for "a human ended this" and for "my arm aged out" — and the documented response to exit 1 is stop, so an agent following the instructions correctly tore down a perfectly live connection on a 24-hour rollover. Non-terminal cases now exit 3, meaning "re-arm me, nothing is wrong", and exit 1 is strictly "a human or the server ended this".
  • The arming instructions now say not to pass a timeout, since supplying a plausible-looking one was a way for an agent to manufacture this failure itself.

Items 8b4ceaa3, d655b2a4.

Note for maintainers: devspec-remote-wait.mjs is in the sync's UNIVERSAL list, so the next sync-hooks run would carry exit 3 to the other plugin families — whose exit tables still document only 0/1/2 and would misread it. Propagate the docs with the code. The mirror-turn.mjs change relies on Claude Code's Stop-hook decision control and is not portable as-is.

Resolved in 0.7.0: the wake channel is HOST_OWNED and no longer synced anywhere, so this hazard is gone rather than merely noted. This warning is the reason the tier was wrong — it describes a file that could not safely be shared, sitting in the list of files that are shared.

v0.6.4

Choose a tag to compare

@DevSpecAliPrice DevSpecAliPrice released this 04 Aug 09:47

Every plugin's test suite now actually protects it

  • A shared file's tests travel with it. The sync lists named implementations and their tests side by side, by hand, and the pairing had rotted: resolve-mcp-auth.mjs was synced while its test was not, so the Cursor plugin held an older test asserting an export the shared implementation no longer had — its suite could not even load, on main. devspec-remote-poll.test.mjs and devspec-remote-wait.test.mjs were absent from the lists too, so Antigravity had no copy of either. Tests are now derived from the implementation entry, so the pairing cannot rot again.
  • The failure used to be invisible. A file in neither list counted as neither drift nor plugin-owned, so --check reported everything in sync while a downstream suite was red. It now reports a missing downstream test as drift, and warns about a canonical test that pairs with no synced implementation.
  • Owning an implementation now owns its test. A plugin that keeps its own resolve-mcp-auth.mjs because its host stores the token elsewhere keeps the test that asserts that, rather than having the canonical one written over it.
  • Result across the family: Cursor red → 151 passing, Antigravity 62 → 151, Grok Build 69 → 151 (its three owned tests untouched), Codex 38 → 68 (its own bridge poller test preserved).
  • scripts/sync-hooks.mjs no longer runs a real sync when imported, and its pairing rules have their own tests.

v0.6.3

Choose a tag to compare

@DevSpecAliPrice DevSpecAliPrice released this 04 Aug 09:47

Remote control — the "working" dots no longer die thirty seconds into a five-minute turn

  • Your agent stays "working" for the whole turn. Arming the wait for the next command used to be treated as "the agent is idle", so it cleared the turn marker the poller had just written. Because agents are told to re-arm the instant they wake — precisely so owner mail arriving mid-turn is not dropped — an agent switched its own indicator off seconds into every turn and then worked on with the driver's UI showing nothing.
  • Worse than a cosmetic bug: with the marker gone, the poller's next tick emitted report_complete, so the activity state machine recorded the turn as finished while it was still running.
  • Turn end is now owned by the Stop hook alone (mirror-turn.mjs stop), which every plugin in this family registers. A first arm (--from-end) still clears the marker — that is the connect/reconnect case the original clear was written for, where a seed delivery can leave a turn nobody will ever wake for.
  • Whether the dots appeared at all used to depend on when in its turn an agent happened to re-arm, which is why this read as intermittent rather than broken.

v0.6.2

Choose a tag to compare

@DevSpecAliPrice DevSpecAliPrice released this 04 Aug 09:47

Remote control — screenshots you send from your phone now actually arrive

  • Attachments on an owner command are saved as real files and handed to the model as a path, instead of being dumped into the turn as base64. Previously the wake payload carried the server's raw content and its dataUrl — the same bytes twice. A 500KB screenshot measured at 1.37MB of stdout, roughly 341,000 tokens of base64 that the model still could not see as an image. It is now 589 bytes and a path the host can open, with the decoded file written to ~/.devspec/remote-control/connections/<id>.attachments/.
  • Small text attachments stay inline (under 2KB) — a file path for a 30-byte note helps nobody.
  • Nothing is dropped silently. If an attachment cannot be written to disk, the descriptor says so and points at get_session_transcript, rather than quietly omitting it.
  • Filenames are sanitised, so an attachment called ../../etc/passwd lands as passwd inside the attachment directory and nowhere else.

v0.6.1

Choose a tag to compare

@DevSpecAliPrice DevSpecAliPrice released this 04 Aug 09:47

Remote control — a dropped agent now says why it dropped

  • owner_gone is no longer reported as local_stop. When the poller detects that its host process has died it tears itself down — but it stamped that with the same local_stop value the server gets when you deliberately run /devspec.remote-stop. On staging that made 117 connection ends indistinguishable, so "why do agents disconnect mid-conversation?" could not be answered from the data at all. The two are now separate reasons, and the Agents page says "The agent process exited" rather than "Local agent disconnected".
  • A relaunch still resumes your agent. owner_gone is a recoverable end, so re-running /devspec.remote after Claude Code exits reconnects the same connection instead of registering a fresh one with a new codename. (Had it been added as a new reason without this, every restart would have silently become a new agent.)
  • Requires DevSpec staging with owner_gone in the heartbeat_connection enum. An end reason the server does not recognise is discarded rather than rejected, which would skip the sticky end and leave a zombie "Live" chip — so the server change ships first.

Internal

  • The seed/advisory split is now covered by tests rather than only by a comment: splitRoomWindow asserts that a cold launch filters the command half only and never the advisory half, which is the invariant that makes a reconnecting agent arrive oriented.