pushary 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1943 -0
- package/LICENSE +21 -0
- package/README.md +330 -0
- package/data/SKILL.md +591 -0
- package/data/cowork/SKILL.md +77 -0
- package/data/cursor-plugin/.cursor-plugin/plugin.json +25 -0
- package/data/cursor-plugin/CHANGELOG.md +70 -0
- package/data/cursor-plugin/CONTRIBUTING.md +40 -0
- package/data/cursor-plugin/LICENSE +21 -0
- package/data/cursor-plugin/README.md +113 -0
- package/data/cursor-plugin/SECURITY.md +33 -0
- package/data/cursor-plugin/assets/logo.png +0 -0
- package/data/cursor-plugin/commands/notify-when-done.md +14 -0
- package/data/cursor-plugin/commands/pushary-test.md +13 -0
- package/data/cursor-plugin/hooks/hooks.json +75 -0
- package/data/cursor-plugin/mcp.json +11 -0
- package/data/cursor-plugin/rules/pushary.mdc +40 -0
- package/data/cursor-plugin/scripts/pushary-gate.mjs +724 -0
- package/data/cursor-plugin/scripts/pushary-gate.test.mjs +137 -0
- package/data/cursor-plugin/scripts/redaction.mjs +51 -0
- package/data/cursor-plugin/skills/pushary/SKILL.md +584 -0
- package/data/vscode-plugin/.claude-plugin/plugin.json +28 -0
- package/data/vscode-plugin/.mcp.json +11 -0
- package/data/vscode-plugin/CHANGELOG.md +49 -0
- package/data/vscode-plugin/CONTRIBUTING.md +40 -0
- package/data/vscode-plugin/LICENSE +21 -0
- package/data/vscode-plugin/README.md +128 -0
- package/data/vscode-plugin/SECURITY.md +56 -0
- package/data/vscode-plugin/assets/logo.png +0 -0
- package/data/vscode-plugin/commands/notify-when-done.md +14 -0
- package/data/vscode-plugin/commands/pushary-test.md +13 -0
- package/data/vscode-plugin/hooks/hooks.json +60 -0
- package/data/vscode-plugin/scripts/pushary-gate.mjs +762 -0
- package/data/vscode-plugin/scripts/redaction.mjs +51 -0
- package/data/vscode-plugin/skills/pushary/SKILL.md +584 -0
- package/dist/bin/pushary-bell-hook.d.ts +1 -0
- package/dist/bin/pushary-bell-hook.js +46 -0
- package/dist/bin/pushary-bell.d.ts +1 -0
- package/dist/bin/pushary-bell.js +146 -0
- package/dist/bin/pushary-claude.d.ts +1 -0
- package/dist/bin/pushary-claude.js +1916 -0
- package/dist/bin/pushary-clean.d.ts +1 -0
- package/dist/bin/pushary-clean.js +1300 -0
- package/dist/bin/pushary-codex-bridge.d.ts +1 -0
- package/dist/bin/pushary-codex-bridge.js +599 -0
- package/dist/bin/pushary-codex-hook.d.ts +1 -0
- package/dist/bin/pushary-codex-hook.js +886 -0
- package/dist/bin/pushary-codex.d.ts +1 -0
- package/dist/bin/pushary-codex.js +125 -0
- package/dist/bin/pushary-connect.d.ts +1 -0
- package/dist/bin/pushary-connect.js +100 -0
- package/dist/bin/pushary-cowork.d.ts +1 -0
- package/dist/bin/pushary-cowork.js +36 -0
- package/dist/bin/pushary-daemon-supervisor.d.ts +1 -0
- package/dist/bin/pushary-daemon-supervisor.js +27 -0
- package/dist/bin/pushary-daemon.d.ts +1 -0
- package/dist/bin/pushary-daemon.js +878 -0
- package/dist/bin/pushary-disconnect.d.ts +1 -0
- package/dist/bin/pushary-disconnect.js +264 -0
- package/dist/bin/pushary-doctor.d.ts +1 -0
- package/dist/bin/pushary-doctor.js +1433 -0
- package/dist/bin/pushary-elicitation-hook.d.ts +1 -0
- package/dist/bin/pushary-elicitation-hook.js +149 -0
- package/dist/bin/pushary-gemini-bridge.d.ts +1 -0
- package/dist/bin/pushary-gemini-bridge.js +339 -0
- package/dist/bin/pushary-gemini-hook.d.ts +1 -0
- package/dist/bin/pushary-gemini-hook.js +381 -0
- package/dist/bin/pushary-hook.d.ts +1 -0
- package/dist/bin/pushary-hook.js +95 -0
- package/dist/bin/pushary-login.d.ts +1 -0
- package/dist/bin/pushary-login.js +198 -0
- package/dist/bin/pushary-logout.d.ts +1 -0
- package/dist/bin/pushary-logout.js +147 -0
- package/dist/bin/pushary-mcp.d.ts +1 -0
- package/dist/bin/pushary-mcp.js +114 -0
- package/dist/bin/pushary-mode.d.ts +1 -0
- package/dist/bin/pushary-mode.js +130 -0
- package/dist/bin/pushary-notification-hook.d.ts +1 -0
- package/dist/bin/pushary-notification-hook.js +56 -0
- package/dist/bin/pushary-opencode-hook.d.ts +1 -0
- package/dist/bin/pushary-opencode-hook.js +338 -0
- package/dist/bin/pushary-permission-denied-hook.d.ts +1 -0
- package/dist/bin/pushary-permission-denied-hook.js +60 -0
- package/dist/bin/pushary-permission-hook.d.ts +1 -0
- package/dist/bin/pushary-permission-hook.js +65 -0
- package/dist/bin/pushary-post-hook.d.ts +1 -0
- package/dist/bin/pushary-post-hook.js +70 -0
- package/dist/bin/pushary-prompt-hook.d.ts +1 -0
- package/dist/bin/pushary-prompt-hook.js +61 -0
- package/dist/bin/pushary-session-end-hook.d.ts +1 -0
- package/dist/bin/pushary-session-end-hook.js +59 -0
- package/dist/bin/pushary-session-start-hook.d.ts +1 -0
- package/dist/bin/pushary-session-start-hook.js +72 -0
- package/dist/bin/pushary-setup.d.ts +1 -0
- package/dist/bin/pushary-setup.js +2660 -0
- package/dist/bin/pushary-stats.d.ts +1 -0
- package/dist/bin/pushary-stats.js +44 -0
- package/dist/bin/pushary-status.d.ts +1 -0
- package/dist/bin/pushary-status.js +262 -0
- package/dist/bin/pushary-stop-hook.d.ts +1 -0
- package/dist/bin/pushary-stop-hook.js +63 -0
- package/dist/bin/pushary-stopfailure-hook.d.ts +1 -0
- package/dist/bin/pushary-stopfailure-hook.js +56 -0
- package/dist/bin/pushary-suggestions.d.ts +1 -0
- package/dist/bin/pushary-suggestions.js +100 -0
- package/dist/bin/pushary-transcript-register.d.ts +1 -0
- package/dist/bin/pushary-transcript-register.js +40 -0
- package/dist/bin/pushary-transcripts.d.ts +1 -0
- package/dist/bin/pushary-transcripts.js +77 -0
- package/dist/bin/pushary-upgrade.d.ts +1 -0
- package/dist/bin/pushary-upgrade.js +599 -0
- package/dist/bin/pushary-wait.d.ts +1 -0
- package/dist/bin/pushary-wait.js +122 -0
- package/dist/bin/pushary.d.ts +1 -0
- package/dist/bin/pushary.js +74 -0
- package/dist/chunk-2ABTSGFT.js +173 -0
- package/dist/chunk-2OY3ZZ5N.js +124 -0
- package/dist/chunk-3EGEA4KH.js +44 -0
- package/dist/chunk-3EVPNIBE.js +185 -0
- package/dist/chunk-5OP5MQI7.js +45 -0
- package/dist/chunk-6J2JC2RT.js +149 -0
- package/dist/chunk-7QLSKOSU.js +19 -0
- package/dist/chunk-7Z7Q4GGS.js +88 -0
- package/dist/chunk-A2A2YTMG.js +835 -0
- package/dist/chunk-AHV4LNB5.js +377 -0
- package/dist/chunk-AXPCESYO.js +197 -0
- package/dist/chunk-C2WCPBF7.js +86 -0
- package/dist/chunk-CJVVKOSR.js +73 -0
- package/dist/chunk-CULUZTWQ.js +2117 -0
- package/dist/chunk-CVQYGMSR.js +142 -0
- package/dist/chunk-CVWG2N3Y.js +16 -0
- package/dist/chunk-D2FX3EPW.js +15 -0
- package/dist/chunk-DB3ONZB4.js +44 -0
- package/dist/chunk-DE4Y6TCC.js +160 -0
- package/dist/chunk-E4FJTUH4.js +249 -0
- package/dist/chunk-EPCXUP3P.js +41 -0
- package/dist/chunk-EX3GAPA4.js +44 -0
- package/dist/chunk-EYK3GSRH.js +143 -0
- package/dist/chunk-GMXKITVA.js +70 -0
- package/dist/chunk-GPEGOGRJ.js +49 -0
- package/dist/chunk-GU3EJCVV.js +43 -0
- package/dist/chunk-HQ6G3B5R.js +59 -0
- package/dist/chunk-HZVVNGEW.js +22 -0
- package/dist/chunk-IPVKH2ST.js +231 -0
- package/dist/chunk-J5TIV3J6.js +238 -0
- package/dist/chunk-JU6W3XAU.js +64 -0
- package/dist/chunk-JYG4B33G.js +227 -0
- package/dist/chunk-JZTHGAPA.js +503 -0
- package/dist/chunk-KGDWDSNL.js +102 -0
- package/dist/chunk-KVQBWI5T.js +99 -0
- package/dist/chunk-KZERVKTD.js +16 -0
- package/dist/chunk-LXQ5FBBD.js +47 -0
- package/dist/chunk-M5ICYGHT.js +138 -0
- package/dist/chunk-M5JDZ45W.js +943 -0
- package/dist/chunk-MDEPU45E.js +1226 -0
- package/dist/chunk-MPWXGONV.js +157 -0
- package/dist/chunk-N6IOSZOE.js +185 -0
- package/dist/chunk-NGDD6TXY.js +192 -0
- package/dist/chunk-NQTHFF4W.js +2830 -0
- package/dist/chunk-OKXS7WDZ.js +68 -0
- package/dist/chunk-OLQRRFBX.js +21 -0
- package/dist/chunk-P7WQEVWA.js +307 -0
- package/dist/chunk-POFBE6ZY.js +12 -0
- package/dist/chunk-PQ4K74WL.js +30 -0
- package/dist/chunk-PUZZELE6.js +37 -0
- package/dist/chunk-Q7E4I6UA.js +767 -0
- package/dist/chunk-QCGNGEGE.js +15 -0
- package/dist/chunk-R7437YI3.js +151 -0
- package/dist/chunk-RHR6NARM.js +305 -0
- package/dist/chunk-SAXBC4AZ.js +60 -0
- package/dist/chunk-SGZJ5LYH.js +76 -0
- package/dist/chunk-SIFVFP6T.js +70 -0
- package/dist/chunk-SLFABHW3.js +150 -0
- package/dist/chunk-T7E75EKA.js +67 -0
- package/dist/chunk-TY4JT7W3.js +38 -0
- package/dist/chunk-U4TMAANR.js +111 -0
- package/dist/chunk-UI36QSBI.js +177 -0
- package/dist/chunk-VQKRG3AC.js +339 -0
- package/dist/chunk-WE2J62NC.js +1131 -0
- package/dist/chunk-XHKBHWLX.js +14 -0
- package/dist/chunk-ZSZSSJCV.js +275 -0
- package/dist/chunk-ZVSKMPBZ.js +135 -0
- package/dist/reapply-CW3MA66V.js +34 -0
- package/dist/src/index.d.ts +204 -0
- package/dist/src/index.js +64 -0
- package/package.json +108 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,1943 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 1.8.0
|
|
4
|
+
|
|
5
|
+
- **An approval you give after auto mode blocks a call is no longer lost when the agent rewords the call.** Your approval covers only the exact call that was blocked. Claude Code tells the agent only that it may retry, and an agent that sent a changed command was blocked again, so the approval you gave from your phone or the Mac notch did nothing. Pushary now refuses the first changed call and tells the agent to send the blocked call again unchanged. If the agent ends its turn without sending it, Pushary tells it to send it.
|
|
6
|
+
- Claude Code no longer tells your phone and the Mac notch that it is waiting for you while its background tasks are still running. Claude Code reports itself idle after a minute at the prompt even when it will continue by itself once those tasks finish. A question from the agent still reaches you as before.
|
|
7
|
+
- A prompt longer than 500 characters, or a shell command longer than about 490, no longer stops your agent at its next phone approval. The hook sent the whole text and the server refused it, so Claude Code halted with "Pushary could not create a verifiable approval" and Codex and OpenCode denied the call. The question, intent, action, blocker and agent name are now cut to what the card can show, and a cut line ends in "…". The server now does the same for every older CLI, so this fix does not wait for you to update. (#1604)
|
|
8
|
+
- A line cut to fit, including a long diff, never ends on half an emoji. The server stores these lines as JSON, and Postgres refuses a half character, so the approval failed to save.
|
|
9
|
+
- `pushary doctor`, `pushary upgrade`, `pushary clean` and automatic updates recognise a global install under either package name, `@pushary/agent-hooks` or `pushary`. A machine keeps the name it already has, and setup on a new machine installs the name it was run from. Nothing changes for an existing install.
|
|
10
|
+
- `pushary doctor` flags a machine with both names installed globally, because uninstalling either one removes every Pushary command.
|
|
11
|
+
- `pushary upgrade` no longer installs over an automatic update that is still running. It says another upgrade is running, with its process id, and stops.
|
|
12
|
+
|
|
13
|
+
## 1.7.3
|
|
14
|
+
|
|
15
|
+
- **Approval text never hides part of a command.** The value after a credential name is now hidden only up to the first space, quote or piece of shell syntax. 1.7.2 could still hide a command: `eval TOKEN="x; rm -rf ~"` read `eval TOKEN="[redacted]"`, `password= reboot` read `password= [redacted]`, and `rm -rf {password=x,/}` hid the `,/` that also deletes `/`. A private key block is hidden only when it holds nothing but the key. Tokens, keys and quoted values without spaces are hidden as before. A quoted value with spaces now shows what follows its first word, as in `--password='[redacted] pass'`. Logs and error reports still hide the whole value.
|
|
16
|
+
|
|
17
|
+
## 1.7.2
|
|
18
|
+
|
|
19
|
+
- **Approval text shows the whole command again.** A credential name that closes a quoted string, as in `grep -rl "password=" . | xargs rm -f`, made 1.7.1 redact everything after it, so the approval read `grep -rl "password="[redacted]"` and hid the `rm`. The value after a credential name is still redacted, and the rest of the command is shown.
|
|
20
|
+
- **Hermes works again after an uninstall.** When setup found Pushary's own Hermes settings already in `~/.hermes/config.yaml`, left by an earlier install, its record of your config included them, and `pushary clean` put them back: approvals stayed pointed at the plugin clean had just uninstalled, and Hermes' `clarify` tool stayed off, so Hermes could not ask you anything in the terminal. Clean now recognises Pushary's own values in that record, including records older versions wrote, and removes `transport: pushary`, the `pushary` plugin entry, and the `clarify` entry that came with Pushary's transport, while restoring everything else you set. Running clean again, or after the Mac app has disconnected Hermes, no longer fails once Hermes' defaults fill the removed settings back in.
|
|
21
|
+
- `pushary clean` says what it did to Hermes: "Pushary settings removed" only when it changed `config.yaml`, "no Pushary settings" when there was nothing of ours, and it skips the plugin uninstall when the plugin is not installed. `--dry-run` previews the exact change without saving it.
|
|
22
|
+
- `pushary doctor` checks Hermes when Pushary set it up: that the plugin is installed in Hermes' Python and enabled, and whether approvals and questions go through Pushary. A plugin the Mac app left installed but disabled is not reported as a failure.
|
|
23
|
+
- Setup on a machine without npm now says so and asks you to install Node.js, instead of printing `/bin/sh: npm: command not found` and suggesting an `npx` retry that needs npm too. Pushary still needs npm; bun, pnpm and yarn installs are not supported yet.
|
|
24
|
+
- `pushary clean` tells an npm it cannot find, a package that is not installed and a failed uninstall apart, instead of calling each of them "not installed". A failed uninstall no longer ends in "Clean complete".
|
|
25
|
+
- `pushary clean --dry-run` lists only the Mac keychain items and preference domains that exist, and the fx setup record only when there is one. It used to list every candidate it would try. A keychain that cannot be read, for example while locked, still counts as possibly holding Pushary's items, so clean tries them and reports a failure instead of calling the keychain clean.
|
|
26
|
+
- `pushary clean` honours `NO_COLOR`.
|
|
27
|
+
- Setup's agent picker shows only the names you picked once you confirm, instead of repeating every agent's description, and the transcript notice is indented like the lines around it.
|
|
28
|
+
- A message you send from your phone or the Mac notch while Claude Code is running a subagent now waits for Claude Code itself. It used to be handed to the subagent's next tool call, where the main conversation never saw it.
|
|
29
|
+
- When your Pushary workspace has no active subscription, Pushary now steps aside and your agent's own approval prompt decides, as it does when Pushary is not installed. Claude Code, Codex, Gemini CLI and OpenCode used to refuse gated tool calls instead, telling you to retry or that the approval was cancelled, and Claude Code questions, plan approvals and MCP forms stopped the same way. A kill switch and your deny rules still apply.
|
|
30
|
+
- Your agent's terminal says once per session why Pushary stepped aside, with the link to subscribe.
|
|
31
|
+
- A Claude Code or Gemini CLI session started from your phone has no prompt on your machine, so it still refuses the call, now saying the workspace has no active subscription instead of asking you to retry.
|
|
32
|
+
- Pairing your phone during setup now says when your workspace has no active subscription, and to subscribe in the Pushary app or at pushary.com before running setup again. It used to say only that pairing failed.
|
|
33
|
+
|
|
34
|
+
## 1.7.1
|
|
35
|
+
|
|
36
|
+
- **fx asks through Pushary.** Setup now adds `"ask_user_question": "deny"` as the last rule in `~/.fx/settings.json`, which removes fx's own terminal question tool from what its agent can call, so every question goes through `mcp_pushary_ask_user`: to the Mac notch at the Mac, to the phone away from it. fx cannot forward its own prompt, so a question asked there used to wait unseen once you walked away. A rule you already set for `ask_user_question` is kept, and setup names it. The instructions in `~/.fx/AGENTS.md` now tell fx's agent to load `mcp_pushary_ask_user` with `mcp_select_tool` by exact name and to ask there even while you are at the terminal, and to ask in its reply only when Pushary hands the question back or cannot be reached.
|
|
37
|
+
- fx's full-access mode (`yolo`, `full-access` or `full access`, from settings, a workspace, or `FX_PERMISSION_MODE`) ignores permission rules, so the terminal question tool stays on there; setup and `pushary doctor` say so instead of claiming questions are routed, and the instructions still steer the agent to Pushary. fx workspaces with their own permission rules replace the global ones, and setup and doctor name how many keep the question tool on. Setup reads the rules the way fx does (globs, last match wins, per-target maps) and leaves a `settings.json` fx would reject, such as one with an unknown action or permission mode, untouched and says fx ignores it.
|
|
38
|
+
- `pushary clean` removes the question rule only when setup recorded adding it and it is still `deny`, so a rule of your own survives.
|
|
39
|
+
- `pushary clean` now removes Pushary entries from every event in `~/.cursor/hooks.json`, not only the events the permission gate uses. The Mac app registers its bridge on session, compaction and stop events too, and those eight entries were left behind under "Clean complete". Other tools' hooks are untouched.
|
|
40
|
+
- `pushary clean` now removes the Pushary block from OpenCode's `~/.config/opencode/AGENTS.md`, as it already did for Codex and Gemini. Setup wrote it and clean left it.
|
|
41
|
+
- `pushary clean` removes temp files that an interrupted config write left next to an agent's config (`.<file>.pushary-<id>` and `.<pid>-<time>.pushary-tmp`), only next to the agent configs clean rewrites (your home folder, Claude Code, Codex, Gemini, Cursor, OpenCode, fx and VS Code settings), never through a symlink, and only once they are over a minute old.
|
|
42
|
+
- Setup's summary no longer says "(others completed successfully)" or "Other agents will continue" when the failed agent was the only one; it names the agents that were configured.
|
|
43
|
+
- Setup's progress lines no longer leave the tail of the previous frame behind ("with Codexex", "tap the notificationon"): setup uses the shared spinner, which clears the line, keeps each frame to one terminal row, and does not animate into a log when colour is forced on.
|
|
44
|
+
|
|
45
|
+
## 1.7.0
|
|
46
|
+
|
|
47
|
+
- **fx.** `npx @pushary/agent-hooks@latest setup --agents fx`, or ticking fx in the picker, connects Vercel Labs' fx coding agent. Setup adds a `pushary` server to `~/.fx/mcp.json` that runs the new `pushary-mcp` command, a local bridge that signs in with the key setup saved in `~/.pushary`. No key is written to fx's files, and a rotated key is used without running setup again. fx has no hooks other tools can register, so Pushary cannot answer fx's own approval prompts; fx's agent asks through Pushary's tools when it chooses to, guided by a managed block setup writes to `~/.fx/AGENTS.md`.
|
|
48
|
+
- Setup adds `"mcp_pushary_*": "allow"` to the `permission` rules in `~/.fx/settings.json`, after your own rules, so fx runs Pushary's tools without reviewing or prompting for each call, the same as the other agents' auto-allowed Pushary tools. A rule you already set for `mcp_pushary_*` is kept. fx workspaces that set their own permission rules replace the global ones, and setup names how many there are.
|
|
49
|
+
- Setup leaves `~/.fx/mcp.json` and `~/.fx/settings.json` untouched when fx itself would reject or refuse to edit them (comments, a trailing comma, a repeated key, or servers under a key fx ignores) and prints the command that retries only fx. It writes both files as compact JSON, the way fx does, and leaves a file alone rather than grow it past the size fx reads. It skips fx on a machine with no `~/.fx`, and on Windows, which fx does not support. fx is ticked automatically only when fx's own files are there, since other tools use `~/.fx` too.
|
|
50
|
+
- `pushary doctor` checks the fx entry and that its bridge is installed, and warns when Pushary's tools are not auto-allowed. `pushary clean` removes only the Pushary server, the allow rule it added, and the instructions block.
|
|
51
|
+
- `pushary setup --help` lists every agent id setup accepts.
|
|
52
|
+
|
|
53
|
+
## 1.6.4
|
|
54
|
+
|
|
55
|
+
- Requests to Pushary now say which client made them: the CLI version, the platform, a per-process run id, and the machine id this machine already has. They go only to Pushary's own address, never to npm or any other host, and never include file paths, keys or content. Support can then find a problem from your account instead of asking for terminal output.
|
|
56
|
+
- When setup, a hook or the bridge cannot reach Pushary, cannot claim a blocked action, or stands aside for an agent it does not recognise, it now tells Pushary with a short code such as `mode_fetch_failed`. Each code is sent at most once per clock minute per machine, not per hook, even when several hooks fail at the same moment, so an outage does not add a request to every tool call. `~/.pushary/client-reports/` holds one small claim file per code per minute. A report gives up after one second. Set `PUSHARY_DISABLE_CLIENT_REPORTS=1` to send none.
|
|
57
|
+
|
|
58
|
+
## 1.6.3
|
|
59
|
+
|
|
60
|
+
- When setup cannot configure one of the agents you picked, the final summary now names that agent, says why, and prints the one command that retries only it, such as `npx @pushary/agent-hooks@latest setup --agents cursor`. Before, the reason was printed once in the middle of the run and the summary only said "Setup finished with 1 problem".
|
|
61
|
+
- A config file setup refuses to rewrite, such as a `~/.cursor/hooks.json` or `~/.claude/settings.json` with comments or a trailing comma, is still left untouched and backed up next to the original. Fix or remove it, then run the retry command; it reuses your saved key and does not pair your phone again.
|
|
62
|
+
- `setup --json` adds `agentFailures`: one entry per agent that failed, with its reason.
|
|
63
|
+
- Setup's report to your own Pushary workspace now says which agent failed and why (the agent and a reason code only, never a file path or file contents), which check decided the exit code, and the CLI version. Support can then see what went wrong without asking you to paste terminal output.
|
|
64
|
+
|
|
65
|
+
## 1.6.2
|
|
66
|
+
|
|
67
|
+
- "Allow Bash for this session" now still asks before rsync deletes files: `--del`, `--delete-delay`, `--delete-missing-args`, `--remove-sent-files`, and a long option shortened to a unique prefix such as `--delete-dela`. It also still asks for `git checkout -f`, `git checkout <ref> -- <path>`, `git switch` with `-f`, `--force` or `--discard-changes`, branch resets (`git checkout -B`, `git switch -C`, `git branch -f`, `-D`, `-M`, `-C`), and `redis-cli flushall` or `flushdb` in any letter case, wherever the flag or word appears. Before, the grant ran these without asking. `git checkout HEAD file.ts`, which overwrites a file without a `--`, still runs under the grant.
|
|
68
|
+
- The same commands now ask even when your own Claude Code allow rules cover them, such as `Bash(git:*)` allowing `git checkout origin/main -- package-lock.json`. Pushary still leaves every other command those rules allow to Claude Code.
|
|
69
|
+
|
|
70
|
+
## 1.6.1
|
|
71
|
+
|
|
72
|
+
- Pushary-launched Codex sessions can answer supported questions from inside MCP tools through Pushary. Invalid answers and cancelled requests never become approval. This does not connect existing Codex Desktop Computer Use permission dialogs.
|
|
73
|
+
- Claude forms with rules Pushary cannot preserve now stay in the native client instead of silently losing those rules.
|
|
74
|
+
|
|
75
|
+
## 1.6.0
|
|
76
|
+
|
|
77
|
+
- Pushary no longer asks about a Claude Code command your own Claude Code allow rules already cover when only a Pushary default would have asked. Risky commands still ask: destructive, network and data-loss commands, and anything whose effect cannot be read from its text, such as `bash -c`, inline interpreter code, `xargs` or a `#` comment. If Claude Code prompts anyway, the approval still reaches your phone.
|
|
78
|
+
- Your Claude Code rules are read from your managed, user and local settings. A repository's committed `.claude/settings.json` can only add asks and denies, so a cloned repository cannot switch Pushary off.
|
|
79
|
+
- After three approvals of Bash in a row in one session, the phone and the Mac offer "Allow Bash for this session". Risky commands still ask, and a command the grant holds back is delivered exactly as it was before the grant.
|
|
80
|
+
- "Approve for this session" no longer saves a rule for a risky command, or for a wrapper such as `bash -c` or `git -C` that would cover every command after it.
|
|
81
|
+
- A preset you picked in Policies now applies on sites that have an Approvals mode set, which is nearly all of them. The Approvals mode still decides where an approval goes.
|
|
82
|
+
- A call your team routes to an approver keeps that approver, whatever your own Claude Code settings allow.
|
|
83
|
+
- An automatic update whose agent configuration refresh fails now stays pending and retries, instead of reporting success.
|
|
84
|
+
- A newer release now replaces a pending upgrade whose refresh keeps failing, so updates never stop. `pushary upgrade` prints what failed instead of a stack trace.
|
|
85
|
+
- Setup, upgrade and doctor no longer run a Codex binary that sits inside an app bundle when `codex` on your PATH points there directly or through a symlink.
|
|
86
|
+
|
|
87
|
+
## 1.5.1
|
|
88
|
+
|
|
89
|
+
- Setup no longer stops at a Codex version. It writes the Pushary hooks, asks Codex to trust them, and checks Codex's answer. It used to trust the hooks itself only up to Codex 0.155.1, and on anything newer told you to open Codex and trust them by hand.
|
|
90
|
+
- Pushary asks Codex only when you run setup, upgrade or doctor yourself at a terminal, only through the `codex` command on your PATH, and only if Codex has been opened on this machine before. It never starts the Codex inside the ChatGPT app, and never while a hook is running.
|
|
91
|
+
- If Codex does not accept the trust, setup says so and tells you to open Codex, run `/hooks`, and choose Trust for the Pushary hooks. When Codex is not asked, setup says why in one line and keeps the trust entries it wrote itself, as it did before.
|
|
92
|
+
- `pushary upgrade` asks Codex the same way after it refreshes the Codex hooks. The daily automatic update refreshes the hooks and writes the same trust entries but does not start Codex, so an install left untrusted by the old version limit is fixed on its next update either way. Machines older than 1.5.0 do not update themselves: run `npx @pushary/agent-hooks@latest upgrade` once.
|
|
93
|
+
- `pushary doctor` shows what Codex itself reports for each Pushary hook: trusted, untrusted, modified, not listed, or turned off. When Codex is not asked or cannot be asked, doctor says so and uses its own check instead.
|
|
94
|
+
- Pause automatic updates on Linux to preserve phone-started sessions during daemon replacement.
|
|
95
|
+
- Retry incomplete configuration and daemon refreshes after an automatic installation, including when the installed version is already current.
|
|
96
|
+
|
|
97
|
+
## 1.5.0
|
|
98
|
+
|
|
99
|
+
- Pushary now updates itself in the background. When an agent session starts, it checks at most once a day for a newer version within the same major version, installs it into the global npm folder your hooks already run from, and refreshes each agent's config the way `pushary upgrade` does. The session never waits for it. A new major version is shown in `pushary doctor` and left for you to install.
|
|
100
|
+
- Turn it off with `pushary upgrade --auto=off` (and back on with `--auto=on`), or set `PUSHARY_DISABLE_AUTOUPDATE=1` in the environment your agents start from.
|
|
101
|
+
- It does not run on Windows yet, in CI, or for a copy run through `npx`, a plugin, or the Mac app. It never uses `sudo`: if npm cannot write to the global folder, the attempt is recorded as failed and nothing changes.
|
|
102
|
+
- This only reaches machines on 1.5.0 or later. To get there, run `npx @pushary/agent-hooks@latest upgrade` once.
|
|
103
|
+
- `pushary doctor` shows whether automatic updates are on and the result of the last check, and `setup` and `doctor` tell you once when Pushary has updated itself.
|
|
104
|
+
- `pushary doctor` no longer calls a build newer than the npm release out of date.
|
|
105
|
+
- The commands the CLI prints for you to copy now say `npx @pushary/agent-hooks@latest`. Without `@latest`, npx runs whatever older version is installed globally.
|
|
106
|
+
- A failed npm install now reports npm's reason, such as a permission error, instead of a line like `syscall mkdir`.
|
|
107
|
+
|
|
108
|
+
## 1.4.2
|
|
109
|
+
|
|
110
|
+
- Setup trusts the Pushary hooks for you on Codex up to 0.155.1. It used to stop at 0.147.0, so on a newer Codex it wrote the hooks without trusting them, Codex never ran them, and setup still said it was done.
|
|
111
|
+
- On a Codex newer than the last version Pushary checked, setup says so in one line and tells you to open Codex and choose Trust for the Pushary hooks.
|
|
112
|
+
- The phone QR stops waiting 10 seconds before the pairing code expires, so the code is cancelled cleanly on a timeout.
|
|
113
|
+
- After a minute at the QR with no scan, setup offers the browser sign-in: Ctrl-C, then `npx @pushary/agent-hooks setup --connect browser`.
|
|
114
|
+
- A kill switch or standing deny on Codex now appears in your decision history, as it already did for Claude Code. If that record fails to send, it is sent again on the next matching denial.
|
|
115
|
+
- `clean --everything` also removes the sign-in session the Mac app now keeps in the keychain.
|
|
116
|
+
|
|
117
|
+
## 1.4.1
|
|
118
|
+
|
|
119
|
+
- `clean` masks credentials in shell lines it cannot rewrite, including output you might send to support.
|
|
120
|
+
- `clean --everything` stops before changing configuration if the Mac app refuses to quit. Keychain access failures are reported as incomplete cleanup; missing items alone count as absent, and permission prompts can finish.
|
|
121
|
+
- Cleanup passes discovered preference domains, app paths and keychain services as literal command arguments. A foreign skill named `pushary` is preserved unless the skills.sh lock identifies it as ours.
|
|
122
|
+
|
|
123
|
+
## 1.4.0
|
|
124
|
+
|
|
125
|
+
- `pushary clean --everything` removes what the Mac app set up as well as what the CLI did. It quits the app, then removes its keychain items, preferences and caches, the editor extension it installs in Cursor and VS Code, and the skill's registration in `~/.agents`. A plain `clean` still leaves an installed Mac app alone, and now says so instead of printing "Clean complete." over a key it kept.
|
|
126
|
+
- `clean` tells you to run `unset PUSHARY_API_KEY` when your terminal still exports the key. Without it, `setup` in the same terminal signed you straight back in as the account you had just removed.
|
|
127
|
+
- `clean` no longer leaves your key behind in its own backup of your shell profile, in earlier copies of the Cursor plugin, or in the line `pushary logout` comments out.
|
|
128
|
+
- A line in your shell profile that still sets a real key, in a form `clean` will not rewrite, is reported as left behind rather than skipped.
|
|
129
|
+
- `clean`, `doctor` and `setup` name business@pushary.com and the Discord when they leave something broken.
|
|
130
|
+
- A plain `clean` after you delete the Mac app removes what the app left behind. It used to take the app's leftover locator file as proof the app was still installed, and kept every hook pointing at it. In Cursor those hooks deny whatever reaches them once there is no app to answer.
|
|
131
|
+
- `clean` removes the Pushary entry and its key from Claude Code's own backups of `~/.claude.json`, not only from the live file. Restoring one of those backups used to put a working key straight back. A backup from before setup keeps whatever you had there.
|
|
132
|
+
- A file edit no rule was written for goes straight to the agent's own prompt instead of waiting for your phone first, in Claude Code's default mode and when Codex is about to ask. Over 35 days these were asked 224 times and answered from a phone twice. A rule you wrote, a preset or mode you picked, and a scope contract still reach your phone, and runs that bypass permissions are unchanged, because there is no prompt to fall back to.
|
|
133
|
+
- A kill switch or a standing deny enforced on this machine now appears in your decision history, as it already did through the Mac app. Only what the machine allowed was recorded before.
|
|
134
|
+
- Qwen Code, CodeBuddy and Qoder keep the answer you give on a permission prompt. Devin's patch and MCP calls reach the gate that was installed for them. A hook naming an agent Pushary does not recognise with `--source` stands aside, as it already did with `--agent`, rather than acting as Claude Code.
|
|
135
|
+
- `doctor` reads hook commands written in quotes or with `--source`, and tells a malformed MCP registration apart from a configured server.
|
|
136
|
+
- A hook payload that is not the shape it claims to be is refused before it can change anything.
|
|
137
|
+
|
|
138
|
+
## 1.3.1
|
|
139
|
+
|
|
140
|
+
- Keep human-review policies effective inside shell chains, and refuse automatic approval of quoted command substitutions.
|
|
141
|
+
- Check kill switches and standing denials before rejecting unreadable tool targets. Check every file in a patch; ask the agent to split a batch that crosses scope.
|
|
142
|
+
- Preserve symlinked agent configuration files during atomic updates.
|
|
143
|
+
- The installed skill recognizes file-edit capabilities across agents, including Codex patches.
|
|
144
|
+
- `pushary doctor` warns when hooks are registered for an agent this version has no profile for. Every one of them stands aside, so nothing on that agent is gated or recorded until you upgrade.
|
|
145
|
+
- A hook that stands aside because it cannot resolve `--agent` says so in the documented `[degraded]` shape, so a stand-aside is countable instead of invisible.
|
|
146
|
+
|
|
147
|
+
## 1.2.0
|
|
148
|
+
|
|
149
|
+
Nothing to run. These are the gate's own rules, tightened where they were guessing.
|
|
150
|
+
|
|
151
|
+
- A shell command Pushary cannot read is no longer judged on the loosest rule that fits. A tool whose command arrives under a key we do not recognise used to fall through to a bare `Bash` rule, which is always the permissive one; a path in the same position already refused to be judged. Both now say they have no opinion and let the agent ask you. An agent that names its command differently can declare the key instead of losing the gate.
|
|
152
|
+
- An `--agent` Pushary does not recognise stops the hook rather than turning it into Claude Code. A typo in a registered command used to fetch Claude's policy and file the session under Claude's name. Every hook now stands aside and says why, because acting as the wrong agent is worse than not acting.
|
|
153
|
+
- `pushary doctor` calls your hooks intact only when all of them are. It used to accept any single surviving hook as proof, so a config wiped down to one entry read as healthy.
|
|
154
|
+
- Setup no longer counts its own folder as proof you have an agent installed. The directory it writes the config into was being read back as evidence the vendor was there, so ticking an agent once made it look installed forever. Only the agent's own binary counts now.
|
|
155
|
+
- The slowest call we make to Stripe carries its own deadline. Every Stripe call was bounded at five seconds, which is right for the checkout path and too tight for an invoice preview.
|
|
156
|
+
|
|
157
|
+
## 1.1.0
|
|
158
|
+
|
|
159
|
+
Nothing to run if you are already set up. `pushary setup` writes the new skill;
|
|
160
|
+
so does opening the Mac app, which carries its own copy.
|
|
161
|
+
|
|
162
|
+
- A scope boundary now works for a run that changes no files. `propose_scope` takes `promises`: boundaries that are not paths, such as who you will not email, what you will not spend, and which systems you will not open. They are shown to the approver as "Promised, not checked", recorded in the ledger, and never enforced, because the gate judges a file path and these have none. Saying so is the point: the tool used to return `ratified: true` for a marketing or support run whose contract matched nothing.
|
|
163
|
+
- The tool now reports what it will actually check. `enforces` lists what the contract *contains* that can be checked, and an empty array means nothing in it is. `hookSeen: false` means no hook has ever reported that session id, so the contract is stored under a key the gate will never read. Both were previously invisible: a contract that enforced nothing looked exactly like one that enforced everything.
|
|
164
|
+
- Four glob rules that matched nothing are repaired when they are proposed. `customers`, `docs/`, `**/.env*` and `**/*.test.ts` each validated clean and matched no file. The last two shipped as our own documented examples, so anyone who copied them to protect a root `.env` protected nothing. The missing pattern is added at proposal time rather than by loosening the matcher, which would have widened every contract already live.
|
|
165
|
+
- The approval card is budgeted from the restrictive half outwards. The allow list is the only part that grows, so cutting one joined string at the end dropped the off-limits line, the definition of done and the promises first: exactly the half a person needs in order to say no. Agent-supplied text is flattened to one line, so a promise can no longer forge a row the server wrote.
|
|
166
|
+
- The editor gates carry the scope path again. The Cursor and VS Code gates contained no reference to it, so approving a breach never widened the contract and every further file in the same area asked again. An over-length value is dropped rather than sent, because `ask_user` caps it and a rejected call fails closed.
|
|
167
|
+
- A scope breach says so on the card. A breach judged by the server reached the person as a plain "Allow Edit foo.ts?", with nothing saying a boundary they personally ratified had been crossed.
|
|
168
|
+
- `pushary doctor` names a tool that can take your hooks away. cc-switch replaces `~/.claude/settings.json` wholesale when you switch provider, so Pushary stops firing with no error and nothing to see. Doctor says so while the hooks are still there, and says the mechanism rather than blaming it once they are gone. It diagnoses only: it never rewrites a config because somebody else's software is installed.
|
|
169
|
+
- The installed skill goes to 0.10.0. It breaks a task into steps before asking anything, settles every fork a file or a command can answer, and asks the rest as one numbered round. A question in the terminal is free and a push is not, so the whole round goes to the terminal when you are there and only the blocking one crosses to your phone. Setup now branches on the machine instead of assuming one path.
|
|
170
|
+
|
|
171
|
+
## 1.0.0
|
|
172
|
+
|
|
173
|
+
Run `pushary setup` and tick the agents you use. Five more are on the list: Qwen Code, CodeBuddy, Qoder, Droid and Devin CLI. Nothing changes for an agent you already had wired.
|
|
174
|
+
|
|
175
|
+
- Gate five more agents on their own hooks, not on good intentions. Qwen Code, CodeBuddy, Qoder, Droid and Devin CLI each register the same events Claude Code does, in the file and the spelling that agent uses, and each returns a verdict that agent obeys. Shell commands, writes and edits go through your policy and your phone the same way they always have. Twelve agents are now enforced rather than cooperative.
|
|
176
|
+
- Devin answers in its own dialect. It reads `{"decision":"approve"|"block"}` where the others read Claude's `permissionDecision`, so the verdict is written the way each agent reads it. A stop from your phone carries across both: it ends the turn rather than declining one tool call and letting the next one through.
|
|
177
|
+
- Two tools are deliberately not claimed on Devin. A question answered on your phone comes back as rewritten input, and Devin's reply shape has nowhere to put it, so asking there would tell the agent "approved" and hand it the question you already answered. Pushary stands aside instead, and Devin asks you itself.
|
|
178
|
+
- Read the file out of a patch before judging it. Droid edits by sending a patch rather than a path, and a rule written for a file cannot match a patch body. A single-file patch is now resolved to the file it touches, so `Edit(**/.env)` denies it. A patch over several files is not judged at all and goes back to the agent to ask, because one answer cannot cover several files honestly.
|
|
179
|
+
- Never judge a call whose target cannot be read. If a tool names its file under a key Pushary does not recognise, the only rule that could match is the one written for the tool alone, and that is always the looser one. Pushary now says it has no opinion and lets the agent ask you, rather than approving on a rule that was never meant for that file.
|
|
180
|
+
- Leave a config alone when it is not the shape Pushary knows. These are five other vendors' files. Where a hook list is something other than a list, setup now names the key, changes nothing and reports the agent as skipped, instead of replacing what it found. It already refused to rewrite a file that would not parse; this is the same care for a file that parses and is simply shaped differently.
|
|
181
|
+
- Keep the directory a shell command names. An approval card that said `rm -rf *` without saying where it ran was describing a different command than the one about to happen.
|
|
182
|
+
|
|
183
|
+
These five rest on each vendor's documentation and our own round trip; no vendor binary was installed to confirm them. If setup reports one as skipped, that is the check above doing its job, and the message names the key it did not recognise.
|
|
184
|
+
|
|
185
|
+
## 0.99.0
|
|
186
|
+
|
|
187
|
+
Nothing to run. This release is the part that runs itself: the repairs below happen on the next session start, on the machine that already has them wrong.
|
|
188
|
+
|
|
189
|
+
- Switch on a hold that an older release registered without its proof. `PermissionDenied` and Codex's `Stop` hold only when their registration carries `--hold-budget`, and a registration written before 0.97.0 is present and looks correct, so nothing noticed it had stopped holding. Until now the repair was `pushary upgrade`, run by hand. Claude Code's registration is now checked on every session start whether or not Claude Code itself has changed, and Codex's is checked on its own session start, which had no repair of any kind before this. An auto-mode block or an auto-reviewer denial can be answered again without anyone being told to run a command.
|
|
190
|
+
- Hooks the macOS app wrote are still left to the app, on both agents. It repairs its own, and rewriting them here would point them at a binary the app does not install.
|
|
191
|
+
- Read a hold budget that is not this release's as no proof, which is what the Mac app has always done. `pushary doctor` says a hook "carries no current hold budget" rather than that it predates one, because now it can mean either.
|
|
192
|
+
- Never replace an absolute path in a hook command with a bare name. When the bin directory cannot be found, which is what a GUI-launched agent or an nvm switch does to `npm prefix -g`, the rewrite is refused and the registration you already have is left alone. A hold that does not hold still works less well than it should; a hook command that no longer resolves does nothing at all, and the rewrite touches every event at once. `pushary upgrade` is a person at a prompt and is unchanged.
|
|
193
|
+
- Write Codex's `config.toml` atomically, carrying its permissions over. It is now written from a session-start hook, and two sessions starting at once must not leave it half written. An atomic write replaces the file rather than its bytes, so the mode is copied across by hand: this file holds your API key, Codex creates it private to you, and it stays that way.
|
|
194
|
+
|
|
195
|
+
## 0.98.0
|
|
196
|
+
|
|
197
|
+
Run `pushary setup` so the instruction block in Codex's AGENTS.md, GEMINI.md and OpenCode's AGENTS.md carries the new line. `pushary upgrade` rewrites hooks and trust, not instruction files.
|
|
198
|
+
|
|
199
|
+
- Say the Codex rescue in your own voice. When you allow a command the auto-reviewer denied, the turn now resumes with "I approved this command in Pushary after the auto-reviewer denied it", because Codex hands that text to the model as your own turn. The old third-person wording read as a tool claiming someone else's permission and the model refused to run the command again.
|
|
200
|
+
- In the instructions Pushary writes, say that rerunning the exact command you approved after a denial is the approved path and not a bypass of an enforced gate. It sits beside the line that says never to bypass one, which is what the old wording collided with. Tied to the hook result rather than to "when Pushary reports", so a page or a diff reciting the sentence beside a command is not a rule telling the model to run it.
|
|
201
|
+
- Register the Cursor gate where it already sits, like every other writer, instead of taking it out and adding a fresh one on the end.
|
|
202
|
+
- Drop the Codex trust key of a duplicate of ours when a rewrite collapses it. Otherwise the tool that moves up into that index inherits a hash it cannot match, and Codex reads its hook as Modified and silently stops running it.
|
|
203
|
+
- `pushary doctor` no longer says another tool's hook "runs before ours" or "runs after ours". No agent runs the hooks on an event in a defined order, so the line names who else holds the event and for how long, and claims nothing about which goes first.
|
|
204
|
+
|
|
205
|
+
## 0.97.0
|
|
206
|
+
|
|
207
|
+
Requires the Codex auto-reviewer handling in `POST /api/agent/gate`, Codex bodies on `POST /api/agent/blocked-action/claim`, the `blocked-action/v2` Claude identity and `sessionCwd` on the gate request before rollout. Run `pushary upgrade` so Claude Code's `PermissionDenied` and Codex's `Stop` are registered with the hold budget, Codex gives `Stop` the full hook budget, and Codex trusts it again.
|
|
208
|
+
|
|
209
|
+
- When Codex runs with `--approve-for-me`, stop asking you before its auto-reviewer. `PreToolUse` and `PermissionRequest` have no opinion under the reviewer, after the kill switch and a standing deny, so nothing the reviewer allows reaches your phone.
|
|
210
|
+
- When the auto-reviewer denies a shell command, hold the turn end and ask once whether to allow that exact command. A yes continues the turn, Codex runs the same command again, and the retry skips the reviewer once. A no, or no answer, lets the denial stand. Denials of `apply_patch` and MCP calls are not asked about yet. The request names the session's directory as well as the command's, so the card can say where the command runs.
|
|
211
|
+
- Hold a blocked call only when the hook can prove its host waits that long: the `--hold-budget=<seconds>` argument its registration now carries, or the budget the Mac app hands down in `PUSHARY_HOLD_BUDGET_SECONDS`. The hold never waits past that budget less its reserves. Without proof, which is an install registered before this release, the Claude plugin's older entry, an update that skipped `--finish-upgrade` or a Mac app built before it, a Claude block sends the "Auto mode blocked <tool>" notification instead, and a Codex turn end reads no denial, so it is still asked about once the hook is upgraded.
|
|
212
|
+
- Build the `blocked-action/v2` identity for a Claude call, which includes its working directory, so the retry's hint and claim match the server's grant. A Claude call with no working directory is not asked about. Keep the retry hint for the grant's two minutes rather than ten.
|
|
213
|
+
- `pushary doctor` warns when a `PermissionDenied` or Codex `Stop` registration carries no hold budget.
|
|
214
|
+
|
|
215
|
+
## 0.96.0
|
|
216
|
+
|
|
217
|
+
Requires the auto-mode block handling in `POST /api/agent/gate` and the `POST /api/agent/blocked-action/claim` route before rollout. Run `pushary upgrade` so Claude Code gives the `PermissionDenied` hook the full approval window.
|
|
218
|
+
|
|
219
|
+
- When Claude Code's auto mode blocks a call, hold the agent and ask once whether to allow that exact call. A yes retries it and the retry runs. A no, or no answer, lets the block stand.
|
|
220
|
+
- Stop sending a separate "Auto-mode blocked" notification and remove `PUSHARY_AUTOMODE_RETRY`. The server now decides whether a block is asked about, so the kill switch, a standing deny and your approval mode all apply.
|
|
221
|
+
|
|
222
|
+
## 0.95.8
|
|
223
|
+
|
|
224
|
+
- Stop `clean` leaving a key it installed in Claude Code, Codex and Gemini when the Mac app is installed. An entry is kept only when its key is the one the Mac app holds; when the app has never signed in, the key setup recorded is removed.
|
|
225
|
+
- Keep `~/.pushary/bridge-auth` when the Mac app is installed. `clean` used to delete the app's bridge key.
|
|
226
|
+
|
|
227
|
+
## 0.95.7
|
|
228
|
+
|
|
229
|
+
- Let Gemini's own prompt decide in When I'm out when Pushary cannot create the approval question, instead of refusing the call. Every time still refuses, and says Pushary could not create a verifiable approval.
|
|
230
|
+
- Refuse approvals an unattended Gemini bridge cannot show locally with a specific reason (no local input, no phone answer in time, handled on the machine, no device connected), instead of passing them to Gemini's unexplained headless denial.
|
|
231
|
+
- Withdraw the phone question before a Codex bridge gives up on a Terminal or Updates question, so an answer that arrives during the withdrawal still counts.
|
|
232
|
+
- Describe what happens when you don't answer in `pushary doctor` and `pushary wait` in the current mode words: When I'm out asks your phone when you are away or Pushary cannot tell, then hands back to the agent's own prompt.
|
|
233
|
+
|
|
234
|
+
## 0.95.6
|
|
235
|
+
|
|
236
|
+
- Separate synthetic answer tests from real hook activation and report the actual answering surface.
|
|
237
|
+
- Cancel known pending diagnostic questions on interrupted or unanswered exits without fabricating approval.
|
|
238
|
+
- Add Claude Code activation evidence for a unique scratch-file approval, correlated with the observed tool result.
|
|
239
|
+
- Report managed-control readiness separately from hook configuration and phone delivery, refreshing credentials, service state and remote proof after interactive waits.
|
|
240
|
+
|
|
241
|
+
## 0.95.5
|
|
242
|
+
|
|
243
|
+
- Preserve the full approval window while allowing the final poll time to return; keep late denials and cancellation authoritative.
|
|
244
|
+
- Bundle corrected VS Code read classification and Cursor file-gate fallback, including native shell/MCP prompts when a gate script is missing.
|
|
245
|
+
- Share Claude hook ownership with the server to avoid duplicate decisions while retaining native-mode handoff.
|
|
246
|
+
- Keep supported-agent setup guidance and policy scope aligned with the shared agent manifest.
|
|
247
|
+
|
|
248
|
+
## 0.95.4
|
|
249
|
+
|
|
250
|
+
- Keep inactive/offline login results truthful, with one JSON result and no duplicate key minting to recheck readiness.
|
|
251
|
+
- Preserve authentication blocks until readiness is verified; recover the verified saved account while respecting explicit control off.
|
|
252
|
+
- Keep setup and CLI help accurate about approval delivery modes.
|
|
253
|
+
|
|
254
|
+
## 0.95.3
|
|
255
|
+
|
|
256
|
+
- Verify account readiness after phone pairing before activating approvals or starting managed services.
|
|
257
|
+
- Preserve useful configuration while offline, with consistent unverified output and exit codes instead of a successful activation claim.
|
|
258
|
+
- Reuse the saved key when setup is rerun after payment or connectivity recovers; preserve explicit phone-start opt-outs.
|
|
259
|
+
|
|
260
|
+
## 0.95.2
|
|
261
|
+
|
|
262
|
+
- Add `pushary cowork` and connector-only setup aliases for Claude Desktop, Chat and Cowork, without acquiring a CLI key or installing local hooks.
|
|
263
|
+
- Bundle the canonical Cowork skill and expose setup steps, standing instructions and a real user-answer test prompt.
|
|
264
|
+
- Explain cooperative connector limits and keep native permission hooks and managed session control separate.
|
|
265
|
+
|
|
266
|
+
## 0.95.0
|
|
267
|
+
|
|
268
|
+
Requires the additive session-control and encrypted transcript-preview server routes before rollout.
|
|
269
|
+
|
|
270
|
+
- Capture opted-in direct Claude terminal transcripts through the daemon, including Mac-owned hooks when this CLI is installed. Normalize explicit Codex rollout paths.
|
|
271
|
+
- Keep native terminals open for ordinary phone messages; require explicit phone takeover and resume the verified native session identity.
|
|
272
|
+
- Wait for native process exit before SDK control, preserve queued instructions across mode changes, and acknowledge only accepted commands.
|
|
273
|
+
- Stream redacted, encrypted SDK text previews using the existing acknowledged session key; completed transcript records remain authoritative.
|
|
274
|
+
|
|
275
|
+
## 0.94.0
|
|
276
|
+
|
|
277
|
+
- Align Node compatibility with setup dependencies; unsupported runtimes receive an upgrade message before a command loads.
|
|
278
|
+
|
|
279
|
+
## 0.93.1
|
|
280
|
+
|
|
281
|
+
- Attach stable usage report IDs and preserve the report timestamp across retries so the server counts a replay once.
|
|
282
|
+
- Report restricted agent-key scope accurately and fail closed on unknown identity scopes.
|
|
283
|
+
- Requires the additive server accounting and key-capability changes from #1303 before rollout.
|
|
284
|
+
|
|
285
|
+
## 0.93.0
|
|
286
|
+
|
|
287
|
+
Requires Node.js 20.3.0 or newer for shared cancellation signals.
|
|
288
|
+
|
|
289
|
+
- Register the expanded Claude and Codex hook coverage from the September 4 changes; preserve child-agent identity during compaction.
|
|
290
|
+
- Share one deadline across mode lookup, question reconciliation, event reporting, and command acknowledgement. Retain unresolved question markers for retry.
|
|
291
|
+
- Deliver queued phone instructions on UserPromptSubmit and keep observational hooks from consuming them.
|
|
292
|
+
- Redact credentials in notification activity, phone pushes, and unrecognized failure messages.
|
|
293
|
+
- Diagnose modified Codex hook definitions against their installed trust hashes.
|
|
294
|
+
|
|
295
|
+
## 0.92.1
|
|
296
|
+
|
|
297
|
+
### A rejected key is an answer, not an empty poll
|
|
298
|
+
|
|
299
|
+
`pollAgent` returned `{ command: null, mode: null }` for every non-OK response,
|
|
300
|
+
so a 401 was indistinguishable from a healthy poll with nothing queued. That
|
|
301
|
+
landed on the success branch of `startCommandPoller`, which resets `errorStreak`
|
|
302
|
+
to zero, so the backoff never engaged and the wrapper polled a dead key at the
|
|
303
|
+
idle cadence forever. Because `mode` was null, `onModeState` never fired, and
|
|
304
|
+
`onModeState` is the only place `dualMode` sets `killed` and stops the active
|
|
305
|
+
leg. A wrapper whose key the server rejects therefore reported `kill=false` to
|
|
306
|
+
itself indefinitely: remote stop could not reach it, phone messages never
|
|
307
|
+
landed, and nothing was printed.
|
|
308
|
+
|
|
309
|
+
`PollResult` now carries `rejected`, set only for 401 and 403, which is the same
|
|
310
|
+
line `fetchModeState` already drew between an answer and a blip. A rejected poll
|
|
311
|
+
takes the error path, so the existing exponential backoff applies, and the
|
|
312
|
+
wrapper prints once that remote stop and phone messages are not active. A 500 or
|
|
313
|
+
a dropped connection is still a blip and still retried at the normal cadence.
|
|
314
|
+
|
|
315
|
+
A rejected response is also no longer trusted for its contents: neither the
|
|
316
|
+
command nor the mode state it carries is applied.
|
|
317
|
+
|
|
318
|
+
## 0.92.0
|
|
319
|
+
|
|
320
|
+
### A Claude Code upgrade registers the events it just gained
|
|
321
|
+
|
|
322
|
+
`CLAUDE_HOOK_EVENTS` gates each hook on the oldest Claude Code that understands
|
|
323
|
+
it, and the supported set was filtered by the version detected when the hooks
|
|
324
|
+
were written. Nothing re-ran that filter except `pushary upgrade`, so an event
|
|
325
|
+
your Claude Code gained reached you when Pushary next shipped rather than when
|
|
326
|
+
Claude did: the hook sat in the table, supported by your CLI, and unregistered.
|
|
327
|
+
|
|
328
|
+
SessionStart now asks the question, because that is the moment an upgrade becomes
|
|
329
|
+
observable. The answer costs about 5 ms of local reads and writes nothing unless
|
|
330
|
+
the event set actually moved. A machine wired against an older Claude picks up
|
|
331
|
+
the newer events on its next session.
|
|
332
|
+
|
|
333
|
+
It leaves three things alone. Hooks the macOS app wrote are never rewritten: the
|
|
334
|
+
app owns that wiring and the CLI's writer would replace its locator with the npm
|
|
335
|
+
bin. A patch bump that adds no events refreshes only the receipt, so settings.json
|
|
336
|
+
is not rewritten every time Claude Code ships. A machine with nothing wired is
|
|
337
|
+
left to `setup`.
|
|
338
|
+
|
|
339
|
+
The settings write is now atomic — temp file, fsync, rename — because several
|
|
340
|
+
sessions can start in the same moment and two plain writes interleaving is a torn
|
|
341
|
+
settings.json.
|
|
342
|
+
|
|
343
|
+
## 0.91.0
|
|
344
|
+
|
|
345
|
+
### Setup adopts the key the Mac app is already signed in with
|
|
346
|
+
|
|
347
|
+
The Mac app authenticates through Clerk and writes its API key into every agent
|
|
348
|
+
config it wires, but never into `~/.pushary/config.json`. Setup's key ladder did
|
|
349
|
+
not look there, so a Mac the app had already connected still fell through to the
|
|
350
|
+
pairing QR and minted a second key for an account that was already live.
|
|
351
|
+
|
|
352
|
+
The ladder gains one rung between the environment and pairing: a key the Mac app
|
|
353
|
+
wrote, probed against the server exactly like the stored and environment rungs
|
|
354
|
+
are, and skipped whenever `--key` was passed or `--connect app` was typed. Both
|
|
355
|
+
of those still mean what they meant.
|
|
356
|
+
|
|
357
|
+
## 0.90.2
|
|
358
|
+
|
|
359
|
+
### The Mac app and CLI coexist without a hook takeover
|
|
360
|
+
|
|
361
|
+
Setup keeps live Mac app hooks in place without prompting, while verifying that
|
|
362
|
+
the app and mobile setup belong to the same Pushary tenant. Distinct keys for the
|
|
363
|
+
same tenant coexist; a different or unverifiable account is reported without
|
|
364
|
+
silently replacing the app's wiring. Stale app hooks are repaired by the CLI.
|
|
365
|
+
|
|
366
|
+
An explicit `--take-over-hooks` remains reversible. Clean restores the app's
|
|
367
|
+
hooks, MCP credentials, and Codex trust from a retry-safe receipt before removing
|
|
368
|
+
the CLI, and dry runs no longer claim that restoration already happened.
|
|
369
|
+
|
|
370
|
+
## 0.90.1
|
|
371
|
+
|
|
372
|
+
### Daemon logs distinguish health from failure
|
|
373
|
+
|
|
374
|
+
Successful daemon startup and phone-requested launches now go to the normal log,
|
|
375
|
+
leaving the error log for failures that need attention.
|
|
376
|
+
|
|
377
|
+
### Command-store tests never touch live agent state
|
|
378
|
+
|
|
379
|
+
The pending command store accepts an explicit directory override. Tests use a
|
|
380
|
+
fresh temporary store, so parallel and sandboxed runs cannot read, overwrite, or
|
|
381
|
+
prune commands belonging to a real agent session.
|
|
382
|
+
|
|
383
|
+
## 0.90.0
|
|
384
|
+
|
|
385
|
+
### A hook that needs an answer is allowed to wait for one
|
|
386
|
+
|
|
387
|
+
Claude Code hooks are now registered at the 600 second budget Claude itself
|
|
388
|
+
allows, so a policy that waits two minutes for a phone approval is no longer cut
|
|
389
|
+
off at 120 seconds and turned into a denial the user never saw. Anything a hook
|
|
390
|
+
does that is not a decision keeps its short timeout.
|
|
391
|
+
|
|
392
|
+
A phone card is now withdrawn when the tool it guarded completes or the agent's
|
|
393
|
+
own client denies it, so an approval cannot arrive for something that already
|
|
394
|
+
finished.
|
|
395
|
+
|
|
396
|
+
### The prompts that never reached the phone
|
|
397
|
+
|
|
398
|
+
Codex permission requests are no longer filtered by a shell matcher, so a browser
|
|
399
|
+
use request, a managed network request and an MCP tool request all reach the
|
|
400
|
+
phone the same way a shell escalation does. Codex in bypass mode now decides
|
|
401
|
+
before the tool runs rather than after it, which is what push-first has always
|
|
402
|
+
meant everywhere else.
|
|
403
|
+
|
|
404
|
+
Gemini reports its turn boundary through AfterAgent and its teardown through
|
|
405
|
+
SessionEnd, so a finished Gemini turn is a finished turn rather than a session
|
|
406
|
+
that stayed open.
|
|
407
|
+
|
|
408
|
+
A finished subagent now ends its own session instead of ending its parent's, and
|
|
409
|
+
appears under the session that started it. Context compaction is reported as
|
|
410
|
+
itself rather than as a tool that ran.
|
|
411
|
+
|
|
412
|
+
Pushary's own tools are never gated by Pushary, on the server exactly as in the
|
|
413
|
+
hook, so asking the user a question can no longer wait on approval to ask it.
|
|
414
|
+
|
|
415
|
+
### Sessions end, and a daemon that cannot log in says so
|
|
416
|
+
|
|
417
|
+
A session that goes quiet is closed rather than left standing forever, and the
|
|
418
|
+
daemon reaps the sessions it started. `pushary daemon status` lists what it is
|
|
419
|
+
holding, how old each one is, and how much memory it is using.
|
|
420
|
+
|
|
421
|
+
A daemon whose key is rejected now records that and stops, instead of restarting
|
|
422
|
+
into the same rejection until something notices. `pushary doctor` says the daemon
|
|
423
|
+
needs a login, and logging in clears it.
|
|
424
|
+
|
|
425
|
+
### Setup and doctor tell the truth about this machine
|
|
426
|
+
|
|
427
|
+
`pushary doctor` names the other tools holding the same agent hooks and how long
|
|
428
|
+
each of them waits, checks that the bridge the Mac app installed can be found,
|
|
429
|
+
and reports which Node the hooks will actually run under.
|
|
430
|
+
|
|
431
|
+
`pushary setup` gained `--verify none` for an unattended install, prints what it
|
|
432
|
+
would also do during a dry run, offers a way out of pairing, and can take over
|
|
433
|
+
hooks another tool is holding with `--take-over-hooks`. `pushary mode` now sets a
|
|
434
|
+
site's mode permanently unless told how long to keep it.
|
|
435
|
+
|
|
436
|
+
### Machine identity and clean
|
|
437
|
+
|
|
438
|
+
`pushary clean` leaves the machine identity shared with the Mac app intact, keeps
|
|
439
|
+
one entry rather than two, and treats the identity file as the CLI's to own and
|
|
440
|
+
the app's to read. Transcript sync seals its recovery ledger and admits a gap
|
|
441
|
+
rather than syncing bookkeeping around it.
|
|
442
|
+
|
|
443
|
+
## 0.89.13
|
|
444
|
+
|
|
445
|
+
### Synced transcripts survive large turns and restarts safely
|
|
446
|
+
|
|
447
|
+
Oversized file reads and diffs are shortened before encryption instead of being
|
|
448
|
+
rejected by the server, with the removed UTF-8 byte count recorded in the turn.
|
|
449
|
+
Stable HMAC identifiers prevent transcript contents leaking through record ids,
|
|
450
|
+
and a resumed session reuses its persisted encryption key rather than conflicting
|
|
451
|
+
with the key already registered for that session.
|
|
452
|
+
|
|
453
|
+
Transcript recipients are now pinned on first use and later key changes stop sync
|
|
454
|
+
until the user explicitly runs `pushary transcripts trust --rotate`. First use
|
|
455
|
+
also fails closed when that pin cannot be stored, so no session key is wrapped to
|
|
456
|
+
an unrecorded recipient. New sessions can independently wrap their key for the
|
|
457
|
+
owner's phone and an optional audited compliance-recovery recipient; installations
|
|
458
|
+
without one remain compatible.
|
|
459
|
+
|
|
460
|
+
## 0.89.12
|
|
461
|
+
|
|
462
|
+
### Phone control installs as a native service, without a VM
|
|
463
|
+
|
|
464
|
+
Fresh setup enables phone-start when a supported provider and the operating
|
|
465
|
+
system's own per-user service manager are available: launchd on macOS, systemd
|
|
466
|
+
on Linux, and Task Scheduler on Windows. Existing installs keep their stored
|
|
467
|
+
choice. Service replacement is transactional, verifies the new process before
|
|
468
|
+
committing, and restores the previous running or stopped state on failure.
|
|
469
|
+
|
|
470
|
+
Claude Code, Codex and Gemini sessions started from the phone now use the same
|
|
471
|
+
supervised control path. Setup checks both the local heartbeat and the server's
|
|
472
|
+
view of the machine before calling control ready.
|
|
473
|
+
|
|
474
|
+
### Fresh and partial installs repair themselves
|
|
475
|
+
|
|
476
|
+
Setup now verifies every global command, its local JavaScript imports, and the
|
|
477
|
+
Cursor and VS Code runtime assets before reusing an installed version. A
|
|
478
|
+
same-version package missing any of them is reinstalled instead of failing the
|
|
479
|
+
same setup forever. Fresh installs also cover Codex on Windows and recover from
|
|
480
|
+
partially configured providers without disturbing unrelated settings.
|
|
481
|
+
|
|
482
|
+
### The CLI and macOS app keep their own wiring
|
|
483
|
+
|
|
484
|
+
Cursor setup writes one gate per event even when the other installer ran first.
|
|
485
|
+
`pushary clean` removes the CLI gate while preserving the app's bridge, and
|
|
486
|
+
`pushary doctor` validates the MCP entry from the installer that actually wired
|
|
487
|
+
Cursor. This keeps mixed CLI/app machines working and avoids false repair
|
|
488
|
+
failures on app-only installs.
|
|
489
|
+
|
|
490
|
+
### Transcript control works in every shell
|
|
491
|
+
|
|
492
|
+
Setup discloses encrypted transcript sync and its audited compliance-recovery
|
|
493
|
+
path before enabling it for a new install; older installs remain off until setup
|
|
494
|
+
runs again. `pushary setup --transcripts on|off` now provides the same persistent
|
|
495
|
+
choice in POSIX shells, PowerShell and Command Prompt. The existing
|
|
496
|
+
`PUSHARY_TRANSCRIPTS` environment override remains compatible.
|
|
497
|
+
|
|
498
|
+
## 0.89.11
|
|
499
|
+
|
|
500
|
+
### Phone-spawned Codex and Gemini sessions record their conversation again
|
|
501
|
+
|
|
502
|
+
Both bridges relay their turns by default and stop only when told to. The
|
|
503
|
+
launchers told them to stop on every run: the value they passed was an equality
|
|
504
|
+
test against an environment variable nobody sets, which is false whenever it is
|
|
505
|
+
absent and never the "unset" the bridge was looking for. The relay was therefore
|
|
506
|
+
never built, and a session started from the phone had nothing to show but its
|
|
507
|
+
task title. The opt-out is now passed only when it is asked for.
|
|
508
|
+
|
|
509
|
+
### An agent standing by stops reading as working
|
|
510
|
+
|
|
511
|
+
`pushary claude` and the Gemini bridge reported that a session had started, that
|
|
512
|
+
it was still alive, and that it had ended, with nothing in between. Their
|
|
513
|
+
heartbeat kept resetting the clock that would otherwise have aged the session out
|
|
514
|
+
of "active", so the phone showed a working agent for one that had been waiting at
|
|
515
|
+
its prompt since its last reply. That is exactly when someone is looking at the
|
|
516
|
+
screen deciding whether to send it something. Both now report the turn boundary
|
|
517
|
+
they already detect, as the Codex bridge always has.
|
|
518
|
+
|
|
519
|
+
### Taking a session over from your phone says so, and is ready when you ask
|
|
520
|
+
|
|
521
|
+
An instruction from the phone hands the terminal to Pushary so it can drive an
|
|
522
|
+
agent that would otherwise sit idle. That has not changed. The handover is now
|
|
523
|
+
announced before the session is signalled, instead of the terminal appearing to
|
|
524
|
+
lose its agent for no visible reason. The Agent SDK is resolved when the session
|
|
525
|
+
starts rather than during the handover, so the wait is gone, and a machine that
|
|
526
|
+
cannot reach npm reports the instruction as undelivered instead of accepting it
|
|
527
|
+
and then dropping it. An instruction that is dropped now says it was.
|
|
528
|
+
|
|
529
|
+
## 0.89.10
|
|
530
|
+
|
|
531
|
+
### Phone control delivery is crash-safe
|
|
532
|
+
|
|
533
|
+
Phone instructions now use per-command receipts across polling and relay
|
|
534
|
+
delivery. A process restart acknowledges the recorded outcome instead of
|
|
535
|
+
silently losing or repeating an instruction, multiple queued commands no longer
|
|
536
|
+
overwrite each other, and expired receipts are pruned when new commands arrive.
|
|
537
|
+
|
|
538
|
+
### Setup and clean reverse only Pushary-owned changes
|
|
539
|
+
|
|
540
|
+
Setup records the Claude and Hermes configuration it replaces. Clean restores
|
|
541
|
+
that prior shape without deleting later user edits, and retains the receipt when
|
|
542
|
+
service removal, Hermes rollback, or plugin uninstall is incomplete so a retry
|
|
543
|
+
can finish safely. Installations created by older releases keep their compatible
|
|
544
|
+
best-effort cleanup path.
|
|
545
|
+
|
|
546
|
+
### Native permission modes keep their intended owner
|
|
547
|
+
|
|
548
|
+
Codex now reads the permission mode it already reports, and Claude
|
|
549
|
+
`PermissionRequest` remains answerable from Pushary after the agent itself asks
|
|
550
|
+
for a human. Kill switches and standing denies still take precedence.
|
|
551
|
+
|
|
552
|
+
## 0.89.9
|
|
553
|
+
|
|
554
|
+
### Setup proves managed control reaches the server
|
|
555
|
+
|
|
556
|
+
The background daemon now records successful heartbeats separately from
|
|
557
|
+
successful control-queue responses. Setup and upgrade wait for both before
|
|
558
|
+
reporting managed control ready, and doctor reports either missing proof.
|
|
559
|
+
|
|
560
|
+
An authenticated, once-per-minute challenge round trip gives the server the
|
|
561
|
+
same readiness evidence without launching an agent or changing queued work.
|
|
562
|
+
Machine status exposes that observation additively so current clients keep
|
|
563
|
+
their existing fields while newer CLIs can verify capabilities end to end.
|
|
564
|
+
|
|
565
|
+
## 0.89.8
|
|
566
|
+
|
|
567
|
+
### Daemon upgrades keep a recoverable service
|
|
568
|
+
|
|
569
|
+
Background-service replacement now records the previous definition and native
|
|
570
|
+
running state in an owner-only transaction receipt before changing launchd,
|
|
571
|
+
systemd or Task Scheduler. A failed or interrupted replacement restores that
|
|
572
|
+
service before another upgrade is attempted, and verifies whether it should be
|
|
573
|
+
running or stopped.
|
|
574
|
+
|
|
575
|
+
Native probe errors are not treated as proof that a service disappeared, and
|
|
576
|
+
rollback preserves an administratively disabled service instead of enabling it.
|
|
577
|
+
|
|
578
|
+
`pushary clean` now proves the background service was removed before changing
|
|
579
|
+
agent configuration or local state. If removal cannot be proven, clean stops
|
|
580
|
+
with the existing installation intact so it can be repaired and retried.
|
|
581
|
+
|
|
582
|
+
## 0.89.7
|
|
583
|
+
|
|
584
|
+
### clean no longer uninstalls the macOS app along with the CLI
|
|
585
|
+
|
|
586
|
+
`clean` removed `~/.pushary` whole. That directory is not only the CLI's: the Mac
|
|
587
|
+
app roots its entire runtime there, so the locator every hook execs, the socket
|
|
588
|
+
the app listens on, its gate state and its spool all went with it. It also
|
|
589
|
+
stripped the app's hook entries out of the agent configs. The app went on
|
|
590
|
+
running and went on reporting itself connected, while nothing reached it.
|
|
591
|
+
|
|
592
|
+
It now removes what the CLI owns and leaves `bin`, `run`, `spool` and `plugins`.
|
|
593
|
+
Hook entries are attributed by the command they name, so the app keeps its own.
|
|
594
|
+
`disconnect` is unchanged and still disconnects an agent whichever product
|
|
595
|
+
connected it.
|
|
596
|
+
|
|
597
|
+
The `mcpServers.pushary` entry and the VS Code plugin directory cannot be
|
|
598
|
+
attributed to either product, so they are kept when the app is installed and
|
|
599
|
+
removed when it is not. A CLI-only clean still leaves no API key on disk.
|
|
600
|
+
|
|
601
|
+
### clean handles OpenCode
|
|
602
|
+
|
|
603
|
+
setup installs an OpenCode plugin and an MCP entry and `clean` walked past both,
|
|
604
|
+
so a command that says it removes all Pushary configuration left the sixth agent
|
|
605
|
+
wired. A `pushary.js` somebody else wrote, or one the Mac app wrote, is left
|
|
606
|
+
alone.
|
|
607
|
+
|
|
608
|
+
## 0.89.3
|
|
609
|
+
|
|
610
|
+
### Doctor recognizes hooks installed by the macOS app
|
|
611
|
+
|
|
612
|
+
Claude Code, Codex and Gemini hooks that point at the shared native
|
|
613
|
+
`pushary-bridge` now pass the same presence, executable and timeout checks as
|
|
614
|
+
their npm hook binaries. Doctor no longer tells a Mac-managed install to repair
|
|
615
|
+
hooks that are already installed and running.
|
|
616
|
+
|
|
617
|
+
## 0.89.2
|
|
618
|
+
|
|
619
|
+
### Multi-question prompts keep their real answer shape
|
|
620
|
+
|
|
621
|
+
Claude still sends each question separately so older clients can answer it, but
|
|
622
|
+
each card now carries its canonical metadata too. Current notch, phone, web and
|
|
623
|
+
Slack surfaces keep descriptions, write-in answers and every multi-select value
|
|
624
|
+
instead of collapsing a multi-select to one top-level option.
|
|
625
|
+
|
|
626
|
+
## 0.89.0
|
|
627
|
+
|
|
628
|
+
### Hermes approvals go to the phone, and setup wires it
|
|
629
|
+
|
|
630
|
+
`setup --agents hermes` now selects Pushary as the Hermes approval transport,
|
|
631
|
+
with `transport_fallback: builtin`. Hermes owns the timeout (300s) and the
|
|
632
|
+
once/session/always/deny choices, and persists the last two, so a repeated
|
|
633
|
+
approval can become a standing rule instead of being asked forever. The
|
|
634
|
+
fallback is what makes selecting it safe: a missing key, an unreachable
|
|
635
|
+
Pushary, or no connected device falls back to the terminal prompt rather than
|
|
636
|
+
blocking work.
|
|
637
|
+
|
|
638
|
+
The note explaining why setup writes `~/.hermes/config.yaml` directly rather
|
|
639
|
+
than running `hermes plugins enable` was half wrong. Current Hermes does
|
|
640
|
+
resolve pip-installed plugins in both `list` and `enable`; the reason to write
|
|
641
|
+
the file is the rule against executing an agent's binary, and nothing else.
|
|
642
|
+
|
|
643
|
+
### `pushary upgrade` named a package that does not exist
|
|
644
|
+
|
|
645
|
+
It printed `pip install --upgrade pushary-hermes`. That package is not on PyPI.
|
|
646
|
+
The real one is `hermes-plugin-pushary`, and the upgrade path for it is setup,
|
|
647
|
+
which installs into the interpreter Hermes actually runs in. Now points there,
|
|
648
|
+
like every other agent in that list.
|
|
649
|
+
|
|
650
|
+
## 0.88.1
|
|
651
|
+
|
|
652
|
+
### One install, whichever surface writes it
|
|
653
|
+
|
|
654
|
+
The generated hook spec now describes the whole install, not only the hooks: the
|
|
655
|
+
MCP entry for each agent, the managed block in AGENTS.md or GEMINI.md, and the
|
|
656
|
+
skill. The CLI writes exactly what it wrote before. The Mac app reads the same
|
|
657
|
+
spec, so an install from the app and an install from `pushary setup` leave the
|
|
658
|
+
same files behind, and Repair can put back a tool entry as well as a hook.
|
|
659
|
+
|
|
660
|
+
`Elicitation` is no longer CLI-only in the spec: a typed answer can now come back
|
|
661
|
+
from a settlement, so the Mac app writes that hook too.
|
|
662
|
+
|
|
663
|
+
### One predicate for Ctrl-C at a prompt
|
|
664
|
+
|
|
665
|
+
Setup carried its own test for an inquirer cancellation and matched a different
|
|
666
|
+
string from the one in `cli/exit.ts`. Both agreed on inquirer's current wording
|
|
667
|
+
and would have disagreed the day it changed, printing "Setup failed" over a
|
|
668
|
+
deliberate Ctrl-C. One predicate now, matching the union of what both matched.
|
|
669
|
+
|
|
670
|
+
## 0.87.1
|
|
671
|
+
|
|
672
|
+
### Unanswered approvals hand off once and fail closed
|
|
673
|
+
|
|
674
|
+
The installed agent instructions now distinguish a live timeout from cancelled,
|
|
675
|
+
missing and unavailable questions. They poll once, cancel before moving the
|
|
676
|
+
decision into the current client, reconcile the cancellation race once, and stop
|
|
677
|
+
when Pushary cannot safely fence the question state.
|
|
678
|
+
|
|
679
|
+
## 0.86.0
|
|
680
|
+
|
|
681
|
+
### Approving a plan from your phone
|
|
682
|
+
|
|
683
|
+
Exiting plan mode blocked the terminal and pushed nothing, so an away user's agent
|
|
684
|
+
sat waiting on the one approval that decides everything after it. `ExitPlanMode` was
|
|
685
|
+
never in the hook's tool matcher, and `plan` is a mode Pushary steps aside for
|
|
686
|
+
entirely, so matching it alone would not have helped either.
|
|
687
|
+
|
|
688
|
+
The plan approval now reaches your phone before Pushary steps aside, and it carries
|
|
689
|
+
the plan itself, redacted and capped like any diff, so you are approving something
|
|
690
|
+
you can read rather than a bare tool name. Two options rather than the terminal's
|
|
691
|
+
three: a hook cannot set the permission mode the session lands in, so offering that
|
|
692
|
+
choice would have quietly dropped half of what you picked.
|
|
693
|
+
|
|
694
|
+
Falls back to the terminal exactly as before whenever the phone cannot answer.
|
|
695
|
+
|
|
696
|
+
## 0.85.2
|
|
697
|
+
|
|
698
|
+
### One agent-protocol renderer for CLI and native approvals
|
|
699
|
+
|
|
700
|
+
Claude Code, Codex and Gemini hook output now comes from the shared contracts
|
|
701
|
+
renderer also used by the native Mac gate. This keeps the CLI wire bytes unchanged
|
|
702
|
+
while preventing the native bridge and published hooks from drifting into different
|
|
703
|
+
allow or deny shapes.
|
|
704
|
+
|
|
705
|
+
## 0.85.1
|
|
706
|
+
|
|
707
|
+
### A rule you wrote is now honoured in auto mode
|
|
708
|
+
|
|
709
|
+
A policy set to deny a tool outright was computed and then discarded in the three
|
|
710
|
+
Claude Code permission modes Pushary steps aside for: `auto`, `plan` and `dontAsk`.
|
|
711
|
+
`auto` is the default on Pro, Max and Team, so this was most sessions. The native
|
|
712
|
+
classifier has never heard of your rule, so it would approve what you had forbidden.
|
|
713
|
+
|
|
714
|
+
The defer exists to stop Pushary double-prompting on top of Claude Code's own
|
|
715
|
+
prompt. A deny prompts nobody, so that reasoning never covered it. A standing deny
|
|
716
|
+
now sits with the kill switch, ahead of the defer, and applies in every mode.
|
|
717
|
+
|
|
718
|
+
Approvals are unchanged: `auto` still defers them, and a tool the classifier
|
|
719
|
+
decides to ask about is still reclaimed through `PermissionRequest` and pushed to
|
|
720
|
+
your phone. This only closes the case where a rule of yours was silently dropped.
|
|
721
|
+
|
|
722
|
+
The Codex, Gemini and SDK wrapper paths already behaved this way. This brings the
|
|
723
|
+
Claude Code path in line with them.
|
|
724
|
+
|
|
725
|
+
## 0.85.0
|
|
726
|
+
|
|
727
|
+
### A hook command that cannot outlive what it points at
|
|
728
|
+
|
|
729
|
+
Every hook command written into `~/.claude/settings.json`, `~/.codex/hooks.json`
|
|
730
|
+
and `~/.gemini/settings.json`, the free bell included, is now wrapped so that a missing binary, a removed
|
|
731
|
+
`node`, a changed npm prefix or a moved home directory exits 0 and the agent reads
|
|
732
|
+
"no opinion". Before, any of those turned every tool call into a failing hook.
|
|
733
|
+
The guard is tested on every shell that can be `/bin/sh`, including dash, and with
|
|
734
|
+
`node` off PATH, which used to exit 127 and print on every tool call.
|
|
735
|
+
|
|
736
|
+
Codex hashes the exact command string to trust a hook, so `pushary upgrade` rewrites
|
|
737
|
+
the trust entry when it re-applies the hooks.
|
|
738
|
+
|
|
739
|
+
### Hooks registered for the CLI you have
|
|
740
|
+
|
|
741
|
+
Claude Code events are registered from a table with a version floor per event.
|
|
742
|
+
`SubagentStart`, `SubagentStop`, `PreCompact`, `PostCompact` and `TeammateIdle`
|
|
743
|
+
are added on a CLI that understands them, and `pushary upgrade` re-registers
|
|
744
|
+
against the CLI you have now. Disconnect removes every event in the table; a
|
|
745
|
+
hand-typed list used to leave the five observability events behind.
|
|
746
|
+
|
|
747
|
+
### `pushary disconnect <agent>`
|
|
748
|
+
|
|
749
|
+
Removes the MCP server, the hooks, the skill and, for Codex, the trust entries and
|
|
750
|
+
the API key from `config.toml`, for one agent, leaving the others alone. The bin
|
|
751
|
+
map entry was written in a shape the build silently dropped, and the command read
|
|
752
|
+
the dispatcher's own token as the agent name; both are fixed before this, its
|
|
753
|
+
first release, and a test now holds every bin entry to the one shape tsup builds.
|
|
754
|
+
|
|
755
|
+
### A kill switch per hook path
|
|
756
|
+
|
|
757
|
+
`~/.pushary/admission-rules.json` (`{"version":1,"disabled":["claude:PostToolUse"]}`)
|
|
758
|
+
silences one event on one agent, or one agent entirely, without a release. Every
|
|
759
|
+
hook binary reads its input through this check, from the same file the macOS
|
|
760
|
+
helper reads, so one file governs both transports. Deny-only: nothing in the file
|
|
761
|
+
can enable a hook.
|
|
762
|
+
|
|
763
|
+
### `PostToolUse` no longer pays two round trips per tool call
|
|
764
|
+
|
|
765
|
+
The telemetry hooks reuse the mode state the gate fetched for the same session
|
|
766
|
+
within the last 60 seconds. The gate itself still reads it live, so a remote stop
|
|
767
|
+
and a scope contract are never served stale where they decide anything.
|
|
768
|
+
|
|
769
|
+
## 0.84.1
|
|
770
|
+
|
|
771
|
+
### Remote starts survive lost acknowledgements without double-launching
|
|
772
|
+
|
|
773
|
+
A phone-started agent now claims exclusive process ownership before the provider
|
|
774
|
+
starts. If the daemon restarts or the launch acknowledgement is lost, retrying the
|
|
775
|
+
same request reconnects to the existing process instead of starting a second one.
|
|
776
|
+
|
|
777
|
+
### Repository approval scopes reach Codex and Gemini
|
|
778
|
+
|
|
779
|
+
Codex and Gemini sessions now carry their repository identity through approval
|
|
780
|
+
requests, so repository-scoped rules stay inside the repository that created them.
|
|
781
|
+
|
|
782
|
+
### Encrypted transcripts retry safely
|
|
783
|
+
|
|
784
|
+
Transcript writers now retry while a session key is temporarily unavailable and
|
|
785
|
+
stop on a real key conflict, preventing undecryptable transcript entries from being
|
|
786
|
+
accepted or silently dropped.
|
|
787
|
+
|
|
788
|
+
## 0.84.0
|
|
789
|
+
|
|
790
|
+
### The phone can start an agent on a machine that is not running one
|
|
791
|
+
|
|
792
|
+
The phone could reach a session that already existed. Starting one meant being at
|
|
793
|
+
the keyboard. `pushary setup` and `pushary claude` now bring up a small background
|
|
794
|
+
process, `pushary-daemon`, which watches for a launch requested from the phone and
|
|
795
|
+
starts a real session with the prompt you sent.
|
|
796
|
+
|
|
797
|
+
It is a poller over a durable queue, not an open socket. Idle, it asks the server
|
|
798
|
+
for work every 30 seconds and sends one heartbeat a minute, and it carries a
|
|
799
|
+
prompt, never any code. One request is in flight at a time, each bounded by its
|
|
800
|
+
own timeout, with a spawn rate cap so a queue backlog cannot fill a machine with
|
|
801
|
+
agents. Exactly one daemon runs per machine: a second one asks the first to hand
|
|
802
|
+
over rather than racing it, and it declines to take over from a daemon that is
|
|
803
|
+
still healthy.
|
|
804
|
+
|
|
805
|
+
This is a background process that was not running before. `pushary doctor` reports
|
|
806
|
+
whether it is up, and `pushary daemon` in a terminal shows you what it is doing.
|
|
807
|
+
|
|
808
|
+
A launch is now acknowledged rather than assumed. The daemon claims a request,
|
|
809
|
+
confirms the session it started, and the phone shows what actually happened,
|
|
810
|
+
including the failure when a launch does not come up.
|
|
811
|
+
|
|
812
|
+
### Always allowing a tool no longer has to mean everywhere
|
|
813
|
+
|
|
814
|
+
"Approve and always allow" wrote one kind of rule: this tool, this whole
|
|
815
|
+
workspace, until you delete it. That is the right scope for `Read` and far too
|
|
816
|
+
wide for a push.
|
|
817
|
+
|
|
818
|
+
The approval now offers the scope alongside it. Workspace is the previous
|
|
819
|
+
behaviour. Repository binds the rule to the repository the ask came from, so a
|
|
820
|
+
rule written in one checkout stops governing another. Session binds it to the run
|
|
821
|
+
you are watching and goes away with it.
|
|
822
|
+
|
|
823
|
+
An older Pushary never sees the narrower rules. A CLI that does not ask for them
|
|
824
|
+
is sent workspace-wide rules only, so a rule you scoped to one session cannot be
|
|
825
|
+
quietly applied everywhere by something released before the idea existed.
|
|
826
|
+
|
|
827
|
+
### AskUserQuestion reaches the phone with its descriptions
|
|
828
|
+
|
|
829
|
+
A Claude Code question carries a header, a description per option, and sometimes
|
|
830
|
+
permission to pick more than one. The phone received the bare labels. It now
|
|
831
|
+
receives the question the way the agent wrote it.
|
|
832
|
+
|
|
833
|
+
One question goes out as one card carrying the whole shape. Every surface that
|
|
834
|
+
does not know that shape, the web decision page and Slack among them, still sees
|
|
835
|
+
an ordinary choice list and answers with a label, which the server reads back onto
|
|
836
|
+
the question. Two or more questions go out one at a time, which is what every
|
|
837
|
+
surface has always been able to answer: a bare label cannot say which of several
|
|
838
|
+
questions it belongs to, and a half-filled answer is worse for the agent than
|
|
839
|
+
being asked again in the terminal.
|
|
840
|
+
|
|
841
|
+
### Codex and Gemini sessions started from the phone stay live
|
|
842
|
+
|
|
843
|
+
A phone-started Codex or Gemini run now goes through a bridge that survives the
|
|
844
|
+
connection dropping and resumes the same session instead of opening a new one.
|
|
845
|
+
The machine advertises only what it can actually do, so a Gemini CLI too old for
|
|
846
|
+
the bridge is not offered as one, and the phone stops showing controls that would
|
|
847
|
+
fail.
|
|
848
|
+
|
|
849
|
+
Stopping is acknowledged the same way. A stop request names the session and the
|
|
850
|
+
process it belongs to, and the machine proves it owns that process before acting
|
|
851
|
+
on it, so a recycled pid cannot be mistaken for the agent you asked to stop.
|
|
852
|
+
|
|
853
|
+
|
|
854
|
+
## 0.83.0 to 0.83.2
|
|
855
|
+
|
|
856
|
+
Three releases went out without an entry here. In order: `ask_user` became
|
|
857
|
+
idempotent, so a create that gets retried after a timeout resolves to the question
|
|
858
|
+
already asked instead of buzzing a phone twice. The pairing QR stopped encoding a
|
|
859
|
+
code only a browser could resolve. And the gate's two shared pieces, the verdict a
|
|
860
|
+
policy produces and the "most specific rule wins" precedence, moved into the
|
|
861
|
+
package the CLI and the server both read, with no change to what either decides.
|
|
862
|
+
|
|
863
|
+
## 0.82.0
|
|
864
|
+
|
|
865
|
+
### Setup reads properly when an agent is the one running it
|
|
866
|
+
|
|
867
|
+
An agent that runs `setup` relays its output to you, and two things in
|
|
868
|
+
that output were written for a terminal that was not there.
|
|
869
|
+
|
|
870
|
+
Colour was unconditional. Piped into an agent's transcript, ten raw
|
|
871
|
+
escape sequences arrived as literal text around the parts you needed to
|
|
872
|
+
read. Colour now follows the same rule the rest of the CLI already used,
|
|
873
|
+
so it is on at a terminal and off everywhere else.
|
|
874
|
+
|
|
875
|
+
The app download was unreachable until you gave up. "No app?" was
|
|
876
|
+
answered with the command for approving in a browser, which sends you
|
|
877
|
+
away from the phone at the moment you were being asked to install it,
|
|
878
|
+
and the download link appeared only after the fifteen minute wait
|
|
879
|
+
expired. The header now names https://pushary.com/download first and
|
|
880
|
+
says the wait continues while you install.
|
|
881
|
+
|
|
882
|
+
### The pairing QR is smaller
|
|
883
|
+
|
|
884
|
+
The QR encodes a link, and the link's length decides the QR's size. It
|
|
885
|
+
carried the pairing id and the public key, 114 characters, which drew 22
|
|
886
|
+
rows of blocks. Against a server that offers one, the CLI now uses a
|
|
887
|
+
short link instead: 33 characters, 16 rows, and small enough to survive
|
|
888
|
+
being relayed through something that truncates.
|
|
889
|
+
|
|
890
|
+
Nothing depends on it. Against an older server, or if the short link
|
|
891
|
+
cannot be issued, setup uses the long one exactly as before, and the
|
|
892
|
+
`pushary://` link is never shortened at all.
|
|
893
|
+
|
|
894
|
+
|
|
895
|
+
## 0.81.0
|
|
896
|
+
|
|
897
|
+
### The idle ping now honours a Terminal mode set in the dashboard
|
|
898
|
+
|
|
899
|
+
"Your agent is waiting" pushes checked your delivery mode by reading the
|
|
900
|
+
temporary override only. Choosing Terminal in the dashboard or the app
|
|
901
|
+
does not write an override, it writes a standing rule, so the check never
|
|
902
|
+
fired and the ping buzzed a phone whose owner had asked not to be reached.
|
|
903
|
+
|
|
904
|
+
The ping now declares itself a task update, which puts it behind the same
|
|
905
|
+
server-side gate as everything else the agent sends unprompted. That gate
|
|
906
|
+
sees the standing rule, the kill switch and a mute. It also means your
|
|
907
|
+
updates dial governs idle pings: set updates to Off and they stop.
|
|
908
|
+
|
|
909
|
+
## 0.80.1
|
|
910
|
+
|
|
911
|
+
### The bell's heartbeat directory is private to you now
|
|
912
|
+
|
|
913
|
+
`pushary bell` counts how many agents are live by keeping one empty file
|
|
914
|
+
per session in the system temp directory. On macOS that is already a
|
|
915
|
+
per-user directory, so this was fine. On Linux it is the shared `/tmp`,
|
|
916
|
+
where it was not: the first user to create `pushary-bell` owned it, and
|
|
917
|
+
every other user on the box got a permission error and silently counted
|
|
918
|
+
zero agents. Anyone who could write to it could also inflate the count
|
|
919
|
+
and trigger the upgrade line.
|
|
920
|
+
|
|
921
|
+
The directory is now per uid and `0700`, the heartbeats inside it `0600`,
|
|
922
|
+
and the sweep judges an entry with `lstat` so a planted symlink is dropped
|
|
923
|
+
rather than followed. Nothing about the bell itself changes, and it still
|
|
924
|
+
makes no network calls.
|
|
925
|
+
|
|
926
|
+
## 0.80.0
|
|
927
|
+
|
|
928
|
+
### A bell, free, for when you only need to know it finished
|
|
929
|
+
|
|
930
|
+
`npx @pushary/agent-hooks@latest bell` makes a noise in your terminal and raises
|
|
931
|
+
a desktop notification when Claude Code says an agent finished or is waiting on
|
|
932
|
+
you. No account, no API key, no network call. Nothing leaves your machine, and
|
|
933
|
+
`pushary bell --off` removes it without touching anything else.
|
|
934
|
+
|
|
935
|
+
If you run one agent, that is genuinely all you need and we would rather you did
|
|
936
|
+
not pay for more. Above three agents at once the bell says so, once a day, and
|
|
937
|
+
then stops talking, because a bell cannot tell you which of them is asking and
|
|
938
|
+
cannot reach you once you have walked away.
|
|
939
|
+
|
|
940
|
+
If you got here from `claude config set --global preferrednotifchannel
|
|
941
|
+
terminal_bell` and the `unknown option '--global'` error, the README now answers
|
|
942
|
+
that directly.
|
|
943
|
+
|
|
944
|
+
### Setup waits for you to tap, instead of trusting that a push went out
|
|
945
|
+
|
|
946
|
+
Setup used to finish on a delivered notification. A notification that arrives
|
|
947
|
+
and cannot be answered looks exactly the same at that point, and that is the
|
|
948
|
+
most common way this quietly breaks: approvals keep arriving and keep going
|
|
949
|
+
unanswered.
|
|
950
|
+
|
|
951
|
+
So setup now sends a real question and waits for you to answer it, and only says
|
|
952
|
+
`Setup complete, and proven.` once you have. If nothing comes back it tells you
|
|
953
|
+
rather than congratulating you. It only does this when someone is actually
|
|
954
|
+
there, which includes a pairing you just scanned, and never in CI. Pass
|
|
955
|
+
`--verify push` for the old behaviour, or `--verify none` to skip the check.
|
|
956
|
+
|
|
957
|
+
`pushary doctor --roundtrip` runs the same check and now says the same things
|
|
958
|
+
about it.
|
|
959
|
+
|
|
960
|
+
### Your agent works out when to reach you, without being told
|
|
961
|
+
|
|
962
|
+
The bundled skill used to describe itself only in terms of what you might say to
|
|
963
|
+
it: ping me on my phone, run this overnight. That only fires when you are there
|
|
964
|
+
to say something. It now also describes the moments that are true of the work
|
|
965
|
+
itself: about to do something irreversible, about to spend money or deploy,
|
|
966
|
+
blocked on a decision that is not the agent's to make, or a long task finishing
|
|
967
|
+
with nobody watching.
|
|
968
|
+
|
|
969
|
+
Everything it matched before, it still matches. Re-run
|
|
970
|
+
`npx @pushary/agent-hooks setup` to update the copy your agents already have.
|
|
971
|
+
|
|
972
|
+
## 0.79.0
|
|
973
|
+
|
|
974
|
+
### Your agent now says what kind of update it is sending, so you can route it
|
|
975
|
+
|
|
976
|
+
Pushary can now send task updates somewhere different from questions: approvals
|
|
977
|
+
can wait at your keyboard while completions still buzz your phone, or the other
|
|
978
|
+
way round. That only works if the agent says which one it is sending.
|
|
979
|
+
|
|
980
|
+
The instructions setup writes now tell your agent to pass a context type when a
|
|
981
|
+
task finishes. Without it a completion looks like any other notification, and
|
|
982
|
+
the setting you chose for task updates has nothing to act on.
|
|
983
|
+
|
|
984
|
+
Nothing changes for questions and approvals, and nothing you have configured
|
|
985
|
+
needs revisiting. Re-run `npx @pushary/agent-hooks setup` to update the
|
|
986
|
+
instructions your agents already have.
|
|
987
|
+
|
|
988
|
+
## 0.78.0
|
|
989
|
+
|
|
990
|
+
### Your agent can ask you several things at once, and reach your phone for all of them
|
|
991
|
+
|
|
992
|
+
An agent that stops to ask you something often asks more than one thing in the
|
|
993
|
+
same breath. Until this release the hook only recognised a single question, so
|
|
994
|
+
two or more meant nothing arrived on your phone at all and the whole exchange
|
|
995
|
+
waited at the terminal. That is exactly the moment this product exists for.
|
|
996
|
+
|
|
997
|
+
Up to four questions now come through in turn. The notification says which one
|
|
998
|
+
you are on, "(2 of 3)", so the first does not look like the last.
|
|
999
|
+
|
|
1000
|
+
If any question goes unanswered, whether you deferred it, it timed out, or no
|
|
1001
|
+
device was reachable, the entire exchange goes back to the terminal, including
|
|
1002
|
+
anything you already answered. A partly filled answer sheet would leave your
|
|
1003
|
+
agent guessing at questions it never put to you, and being asked twice is better
|
|
1004
|
+
than being answered wrongly.
|
|
1005
|
+
|
|
1006
|
+
The whole exchange shares one wait budget. Four questions do not cost four times
|
|
1007
|
+
the wait you agreed to.
|
|
1008
|
+
|
|
1009
|
+
Questions that let you tick several options at once still go to the terminal.
|
|
1010
|
+
Those answers travel as a single piece of text and the packing is not something
|
|
1011
|
+
this release can verify, so it is left alone rather than guessed at.
|
|
1012
|
+
|
|
1013
|
+
## 0.77.0
|
|
1014
|
+
|
|
1015
|
+
### Stop ends the process
|
|
1016
|
+
|
|
1017
|
+
Until this release Stop was a gate verdict. Every pending and new tool call was
|
|
1018
|
+
denied, which is the guarantee and has not changed, but the agent kept running,
|
|
1019
|
+
kept thinking and kept spending tokens. For someone who hits Stop because
|
|
1020
|
+
something is going wrong, "it can no longer act" and "it stopped" are different
|
|
1021
|
+
promises.
|
|
1022
|
+
|
|
1023
|
+
- every session event reports its own pid, so the server can name the process
|
|
1024
|
+
- the daemon advertises a `stop` capability, which is how the server knows a
|
|
1025
|
+
machine can act on one at all
|
|
1026
|
+
- the daemon applies stops from the drain with SIGTERM, and refuses to signal
|
|
1027
|
+
its own pid, since killing itself would take every other session's stop too
|
|
1028
|
+
|
|
1029
|
+
Two additive wire changes, both safe in either direction: `pid` on the event
|
|
1030
|
+
payload, which an older server ignores, and `stops` on the drain response, which
|
|
1031
|
+
an older daemon ignores.
|
|
1032
|
+
|
|
1033
|
+
## 0.76.1
|
|
1034
|
+
|
|
1035
|
+
### Actually closing the spawn argv injection
|
|
1036
|
+
|
|
1037
|
+
0.76.0 shipped under the message "closing the spawn argv injection" and did not.
|
|
1038
|
+
It sanitised `model` and `cwd` but not `prompt`, and `prompt` is the field the
|
|
1039
|
+
daemon passes as the token immediately after `-p`, so a flag-shaped prompt was
|
|
1040
|
+
read back as a flag:
|
|
1041
|
+
|
|
1042
|
+
['claude','--remote','-p','--permission-mode=dontAsk'] -> {permissionMode:'dontAsk'}
|
|
1043
|
+
|
|
1044
|
+
`dontAsk` is a full-defer mode, so the gate stopped asking about every tool for
|
|
1045
|
+
that session.
|
|
1046
|
+
|
|
1047
|
+
The server-side rejection landed in production first and is what actually
|
|
1048
|
+
protects users, 0.76.0 included, with no upgrade required. This release carries
|
|
1049
|
+
the defence in depth: `buildSpawnLaunch` returns null for a flag-shaped prompt,
|
|
1050
|
+
so a daemon never builds that argv even if the server is bypassed.
|
|
1051
|
+
|
|
1052
|
+
## 0.76.0
|
|
1053
|
+
|
|
1054
|
+
### A phone-supplied field could switch your approval gate off
|
|
1055
|
+
|
|
1056
|
+
`pushary claude --remote` sessions started from the phone build their command
|
|
1057
|
+
line from fields the request supplies. The wrapper then read that command line
|
|
1058
|
+
back to learn what it had been asked to do, and it scanned every position without
|
|
1059
|
+
knowing which ones were a flag's **value** rather than a flag.
|
|
1060
|
+
|
|
1061
|
+
So a prompt or a model of `--permission-mode=dontAsk` was read as a real
|
|
1062
|
+
permission mode. `dontAsk`, `plan` and `auto` all defer to the agent's own prompt
|
|
1063
|
+
unconditionally, which means the session stopped asking you for approvals
|
|
1064
|
+
entirely, and the decision log recorded a session that was never gated. A clean
|
|
1065
|
+
approval history that is not true is worse than a missing one.
|
|
1066
|
+
|
|
1067
|
+
Flag values are now consumed with the flag that owns them, so nothing a request
|
|
1068
|
+
supplies can be mistaken for an instruction. A `--permission-mode` you passed
|
|
1069
|
+
yourself still works, in both the `--permission-mode plan` and
|
|
1070
|
+
`--permission-mode=plan` forms.
|
|
1071
|
+
|
|
1072
|
+
The server that queues these sessions now also refuses flag-shaped model and
|
|
1073
|
+
working-directory values, so an out-of-date CLI is protected against those two.
|
|
1074
|
+
It cannot do the same for the prompt, which is free text you are entitled to
|
|
1075
|
+
start with a dash, so **updating is what closes this one properly.**
|
|
1076
|
+
|
|
1077
|
+
Reaching it needed an authenticated session on your own account, spawning to your
|
|
1078
|
+
own machine, with the daemon running. It is not cross-tenant and not remote code
|
|
1079
|
+
execution. It matters most where the person spawning is not the person governed:
|
|
1080
|
+
Team approval routing, Partner, and any case where a phone session or API key has
|
|
1081
|
+
been compromised.
|
|
1082
|
+
|
|
1083
|
+
## 0.75.0
|
|
1084
|
+
|
|
1085
|
+
### An agent can now run the install
|
|
1086
|
+
|
|
1087
|
+
Pasting "install pushary for me" into Claude Code, Codex or Cursor exited 3
|
|
1088
|
+
before doing anything:
|
|
1089
|
+
|
|
1090
|
+
! No API key, and no terminal to sign in from.
|
|
1091
|
+
|
|
1092
|
+
The agent runs setup with stdin and stdout piped, so `isTTY` is false, and app
|
|
1093
|
+
pairing sat behind the same check as browser login. That check was answering the
|
|
1094
|
+
wrong question. Pairing needs a person, not a terminal, and the agent shows its
|
|
1095
|
+
output to one, so a QR written to stdout is read just as well as a QR drawn on a
|
|
1096
|
+
terminal.
|
|
1097
|
+
|
|
1098
|
+
Setup now pairs whenever it is not running in CI. It prints the QR, the tappable
|
|
1099
|
+
link and the fingerprint, and waits for the scan, so the whole install is one
|
|
1100
|
+
command with nothing to paste:
|
|
1101
|
+
|
|
1102
|
+
npx @pushary/agent-hooks@latest setup
|
|
1103
|
+
|
|
1104
|
+
Browser login still requires a real terminal, because it opens a browser on the
|
|
1105
|
+
machine and waits for a redirect. CI still declines to pair, so no build hangs
|
|
1106
|
+
drawing a QR into a log nobody is watching.
|
|
1107
|
+
|
|
1108
|
+
The bundled skill told agents to do the opposite: hand the user a signup link
|
|
1109
|
+
and wait for them to paste a key back. It now says to run setup and show the
|
|
1110
|
+
user the QR.
|
|
1111
|
+
|
|
1112
|
+
The failure message above also had to change, because after this it was naming a
|
|
1113
|
+
cause that no longer applies. Reaching that branch now means CI, `--skip-phone`,
|
|
1114
|
+
or a connect mode other than `app`, so setup says which of those it hit instead
|
|
1115
|
+
of blaming a missing terminal. A headless run with no flags is the one worth
|
|
1116
|
+
calling out: it used to be told to drop into a browser login it cannot open.
|
|
1117
|
+
|
|
1118
|
+
|
|
1119
|
+
## 0.74.0
|
|
1120
|
+
|
|
1121
|
+
### The terminal went quiet while your phone was deciding
|
|
1122
|
+
|
|
1123
|
+
A gated tool call sent the push and then blocked, writing nothing. Claude Code
|
|
1124
|
+
showed a bare spinner. If you missed the notification there was no way to tell
|
|
1125
|
+
that a question existed, let alone where to answer it, and the terminal prompt
|
|
1126
|
+
only appeared once the wait expired.
|
|
1127
|
+
|
|
1128
|
+
The hook now says so before it blocks:
|
|
1129
|
+
|
|
1130
|
+
[pushary] Sent to your phone. Waiting 10s, then this terminal takes over.
|
|
1131
|
+
Answer here instead: https://pushary.com/dashboard/agent
|
|
1132
|
+
|
|
1133
|
+
The wait line prints per question, because each question is its own blocking
|
|
1134
|
+
moment with its own countdown. The URL prints once per session, because it is
|
|
1135
|
+
the same page every time and repeating it on every gated call is noise.
|
|
1136
|
+
|
|
1137
|
+
That URL is the signed-in dashboard, not the `/decide` link the push carries.
|
|
1138
|
+
The decide link is a bearer credential: anyone holding it can answer. Handing it
|
|
1139
|
+
to the agent that is being gated would let it approve its own action, so the
|
|
1140
|
+
hook is only ever told about the page that requires your login.
|
|
1141
|
+
|
|
1142
|
+
Servers that predate this send no URL, and the hook just prints the wait line.
|
|
1143
|
+
|
|
1144
|
+
## 0.73.0
|
|
1145
|
+
|
|
1146
|
+
### Codex could finish setup with no skill installed
|
|
1147
|
+
|
|
1148
|
+
If your machine already uses skills.sh, setup installs the Pushary skill
|
|
1149
|
+
through that CLI so the install registers rather than being invisible. For
|
|
1150
|
+
Codex, `skills add --agent codex` prints "copy to Codex", says "Installation
|
|
1151
|
+
complete" and exits 0, then writes only `~/.agents/skills/pushary`. Nothing
|
|
1152
|
+
lands in `~/.codex/skills`, which is where Codex reads. Setup took the exit
|
|
1153
|
+
code as proof, skipped the copy it ships, and Codex ended up with no skill.
|
|
1154
|
+
|
|
1155
|
+
Doctor caught it, and then gave advice that could not work: clean and set up
|
|
1156
|
+
again reinstalled exactly the same way and failed the same check.
|
|
1157
|
+
|
|
1158
|
+
The exit code no longer decides this. What decides it is whether the run left
|
|
1159
|
+
a SKILL.md where that agent reads, which is the same thing doctor looks at,
|
|
1160
|
+
and setup writes its own copy when it did not. Anything skills.sh really did
|
|
1161
|
+
install is left exactly as it installed it, symlinks included.
|
|
1162
|
+
|
|
1163
|
+
Everything else about the wiring was fine, so this cost you the skill and
|
|
1164
|
+
nothing more. Approvals, hooks and the instructions in `~/.codex/AGENTS.md`
|
|
1165
|
+
were all in place.
|
|
1166
|
+
|
|
1167
|
+
### A skill that will not install no longer stops setup
|
|
1168
|
+
|
|
1169
|
+
The skill is the one piece of the wiring nothing else depends on. It used to
|
|
1170
|
+
be able to abort setup partway, leaving an agent half configured. Now it warns
|
|
1171
|
+
and setup finishes the rest, and `pushary doctor` tells you the skill is
|
|
1172
|
+
missing.
|
|
1173
|
+
|
|
1174
|
+
## 0.72.0
|
|
1175
|
+
|
|
1176
|
+
### Setup tells you when Codex was already quarantined
|
|
1177
|
+
|
|
1178
|
+
0.71.0 stopped setup from getting your Codex binary deleted. This handles the
|
|
1179
|
+
machines where it already happened. What macOS leaves behind passes every check
|
|
1180
|
+
setup made: the `codex` on your PATH is a small JavaScript launcher and it
|
|
1181
|
+
survives, `which codex` still answers, only the binary it launches is gone. So
|
|
1182
|
+
setup wired up an agent that could not start and reported success.
|
|
1183
|
+
|
|
1184
|
+
It now says so before writing anything, and only when it can prove it: a vendor
|
|
1185
|
+
directory that exists and holds nothing the size of a native binary. A layout it
|
|
1186
|
+
does not recognise stays quiet, because telling somebody their working install is
|
|
1187
|
+
broken is the worse mistake. Reinstall with `npm install -g @openai/codex` or
|
|
1188
|
+
`brew install --cask codex`.
|
|
1189
|
+
|
|
1190
|
+
### Setup no longer runs the Hermes binary either
|
|
1191
|
+
|
|
1192
|
+
The same hazard, one agent over. When the config edit that enables the plugin
|
|
1193
|
+
failed, setup fell back to running `hermes plugins enable pushary` — executing a
|
|
1194
|
+
third-party agent binary, which is exactly what cost people their Codex install.
|
|
1195
|
+
The fallback also did not work, because `hermes plugins enable` does not see a
|
|
1196
|
+
pip install. It is gone, and a failed config edit now tells you the one line to
|
|
1197
|
+
add by hand.
|
|
1198
|
+
|
|
1199
|
+
Executing an agent binary is now a build failure rather than a habit: every
|
|
1200
|
+
source file in this package is scanned on every CI run, and the check knows the
|
|
1201
|
+
difference between running `codex` and asking `which` where it is.
|
|
1202
|
+
|
|
1203
|
+
## 0.71.0
|
|
1204
|
+
|
|
1205
|
+
### Setting up Codex no longer gets Codex deleted by macOS
|
|
1206
|
+
|
|
1207
|
+
Setup ran `codex --version` to decide whether your Codex was new enough for
|
|
1208
|
+
native hooks. On macOS, executing a binary hands it to XProtect, and current
|
|
1209
|
+
definitions false-positive on the Codex CLI: macOS killed it and moved it to the
|
|
1210
|
+
Trash. Reading a version number destroyed the tool it was asking, mid-setup, and
|
|
1211
|
+
the only thing you saw was "codex was not opened because it contains malware".
|
|
1212
|
+
|
|
1213
|
+
Setup no longer runs Codex, or any other agent's binary, to learn something about
|
|
1214
|
+
it. The version now comes from the package manifest next to the binary, which
|
|
1215
|
+
covers npm, bun, pnpm and yarn installs, and from `brew list` for the Homebrew
|
|
1216
|
+
cask, which ships no manifest. Same result, nothing launched.
|
|
1217
|
+
|
|
1218
|
+
## 0.70.0
|
|
1219
|
+
|
|
1220
|
+
### A key you name is the key you get
|
|
1221
|
+
|
|
1222
|
+
Passing `--key` with a malformed value did not fail. Setup read it as no key at
|
|
1223
|
+
all, went looking elsewhere, and used whichever key it found: one already stored
|
|
1224
|
+
on the machine, one in your environment, or a brand new one it created by pairing.
|
|
1225
|
+
You named a credential and quietly got a different one. It now stops and says the
|
|
1226
|
+
key is not valid.
|
|
1227
|
+
|
|
1228
|
+
### setup --json no longer stops to ask a question nobody can see
|
|
1229
|
+
|
|
1230
|
+
Asking for machine-readable output says plainly that no human is watching, but
|
|
1231
|
+
setup could still reach an agent-selection prompt. Piped into a script it waited
|
|
1232
|
+
for a keypress that was never coming, and the prompt drew itself onto the same
|
|
1233
|
+
output a program was trying to read as JSON. It now selects the agents it detects,
|
|
1234
|
+
which is exactly what pressing Enter on that prompt would have chosen.
|
|
1235
|
+
|
|
1236
|
+
`--json` also promises a single object of output, and every failing exit used to
|
|
1237
|
+
print nothing at all. A refused key, a cancelled pairing and a crash were
|
|
1238
|
+
indistinguishable to anything reading the output. Each now says which it was.
|
|
1239
|
+
|
|
1240
|
+
### Setup checks its own work
|
|
1241
|
+
|
|
1242
|
+
An agent installer that finished without an error was reported as configured,
|
|
1243
|
+
even if nothing had been written. Doctor would then contradict setup minutes
|
|
1244
|
+
later. Setup now confirms the configuration is really there, using the same check
|
|
1245
|
+
doctor uses, so the two cannot disagree.
|
|
1246
|
+
|
|
1247
|
+
### connect points at the fix that matches your situation
|
|
1248
|
+
|
|
1249
|
+
`pushary connect --app` gave one answer for every way a phone can be out of
|
|
1250
|
+
reach: open the app and allow notifications. That is right when the app is signed
|
|
1251
|
+
in and the permission was declined, and wrong otherwise. A terminal that was
|
|
1252
|
+
never paired needs setup. A phone signed in to a colleague's account needs your
|
|
1253
|
+
account, not a permission toggle. It now says which applies, and when it cannot
|
|
1254
|
+
tell the two apart it says both rather than sounding certain about the wrong one.
|
|
1255
|
+
|
|
1256
|
+
### The editor gates say when a proxy is in the way
|
|
1257
|
+
|
|
1258
|
+
The Cursor and VS Code gates cannot route through a corporate proxy. They still
|
|
1259
|
+
hand the decision safely to the editor's own prompt, so nothing is blocked, but
|
|
1260
|
+
every approval quietly stopped reaching your phone with no explanation. When a
|
|
1261
|
+
proxy is configured they now name it as the likely cause, and on Node 24 or newer
|
|
1262
|
+
they name the setting that fixes it.
|
|
1263
|
+
|
|
1264
|
+
VS Code is also now listed among the supported agents, which it has been since
|
|
1265
|
+
its plugin shipped.
|
|
1266
|
+
|
|
1267
|
+
## 0.69.0
|
|
1268
|
+
|
|
1269
|
+
### An approval is never granted on a state Pushary could not confirm
|
|
1270
|
+
|
|
1271
|
+
When the hook could not reach Pushary, it read that as "you have not been
|
|
1272
|
+
stopped, and you are under no agreed scope". Those are answers, and not reaching
|
|
1273
|
+
the server is not an answer. A rule you had set to approve automatically could
|
|
1274
|
+
therefore fire during an outage on exactly the kind of call a live scope would
|
|
1275
|
+
have held back.
|
|
1276
|
+
|
|
1277
|
+
It now tells the two apart. If the last successful check was under a minute ago,
|
|
1278
|
+
that answer still stands, so a brief network blip changes nothing, and a stop you
|
|
1279
|
+
issued moments earlier still stops the agent. Past that, an automatic approval is
|
|
1280
|
+
withheld and the decision goes to your agent's own prompt instead. Read only
|
|
1281
|
+
shell commands are unaffected, because that list is decided on your machine and
|
|
1282
|
+
needs no server at all.
|
|
1283
|
+
|
|
1284
|
+
A rejected key is treated as a real answer rather than a blip, so a key you
|
|
1285
|
+
revoked stops being honoured at once.
|
|
1286
|
+
|
|
1287
|
+
### A scope you agreed to is now honoured everywhere, not just in one place
|
|
1288
|
+
|
|
1289
|
+
Scope was checked when Claude Code asked before running a tool, and skipped on
|
|
1290
|
+
four other paths that could reach the same decision: Claude's permission dialog,
|
|
1291
|
+
Codex, Gemini CLI, and the remote wrapper. Each of them fetched the scope you had
|
|
1292
|
+
agreed to and then ignored it, so the same edit could be waved through depending
|
|
1293
|
+
only on which route it arrived by. All five now run the same check in the same
|
|
1294
|
+
order.
|
|
1295
|
+
|
|
1296
|
+
### Codex patches are judged on every file they touch
|
|
1297
|
+
|
|
1298
|
+
A Codex patch that changes several files at once was judged on the patch as a
|
|
1299
|
+
whole, so a rule you wrote for one file did not apply when that file was part of
|
|
1300
|
+
a larger change. Every file is looked at now, and the strictest rule wins. Rules
|
|
1301
|
+
written as paths also work for Codex and Gemini, which they quietly did not
|
|
1302
|
+
before.
|
|
1303
|
+
|
|
1304
|
+
### Command details that could contain a secret no longer leave your machine
|
|
1305
|
+
|
|
1306
|
+
A command like `GH_TOKEN=... gh pr create` carries its credential in the first
|
|
1307
|
+
few words, and those words were being sent and stored as the label for what the
|
|
1308
|
+
agent wanted to do. Notification text was not being cleaned at all. Both are
|
|
1309
|
+
cleaned now, on your machine and again on arrival, and the Cursor and VS Code
|
|
1310
|
+
gates clean the command before it is sent rather than after.
|
|
1311
|
+
|
|
1312
|
+
### setup --dry-run really does change nothing
|
|
1313
|
+
|
|
1314
|
+
On a machine with no key, a dry run reached the pairing step, drew a QR code,
|
|
1315
|
+
waited for your phone, created a real API key, and then said nothing had been
|
|
1316
|
+
written. It now stops before anything is created and tells you a real run would
|
|
1317
|
+
connect a key first.
|
|
1318
|
+
|
|
1319
|
+
### clean can no longer leave Cursor blocking your commands
|
|
1320
|
+
|
|
1321
|
+
Cursor is told to block a matched command if the Pushary gate cannot answer, and
|
|
1322
|
+
that list includes ordinary work like rebase, migrate, deploy and publish. Clean
|
|
1323
|
+
removed the gate's files before removing that instruction, so if the second step
|
|
1324
|
+
failed, Cursor was left blocking all of them on a file that no longer existed,
|
|
1325
|
+
under a message saying clean had finished. The order is reversed and checked, and
|
|
1326
|
+
if the instruction cannot be removed the files stay put, clean says so, and it
|
|
1327
|
+
exits with an error.
|
|
1328
|
+
|
|
1329
|
+
### Pairing survives a dropped reply
|
|
1330
|
+
|
|
1331
|
+
If the reply carrying your key was lost in transit, the terminal reported that
|
|
1332
|
+
pairing had expired and you started again, while the key it never received stayed
|
|
1333
|
+
on your account. The key is now held until your terminal confirms it has it, so
|
|
1334
|
+
retrying finishes the pairing you already started. A terminal that cannot reach
|
|
1335
|
+
Pushary at all now says so instead of sitting under a QR code for fifteen
|
|
1336
|
+
minutes.
|
|
1337
|
+
|
|
1338
|
+
### doctor reports on the agents you actually use
|
|
1339
|
+
|
|
1340
|
+
Doctor checked Claude Code whether or not you had it, so setting Pushary up for
|
|
1341
|
+
Codex alone produced a column of failures about software you had never installed.
|
|
1342
|
+
It now reports on the agents Pushary is set up for, and says plainly when it is
|
|
1343
|
+
set up for none.
|
|
1344
|
+
|
|
1345
|
+
Three things it used to call healthy, it no longer does. A key that lives only in
|
|
1346
|
+
a shell profile cannot be read by any hook, so an install where nothing could
|
|
1347
|
+
work was passing every check. A key saved inside Claude, Cursor or VS Code that
|
|
1348
|
+
no longer matches the one in use, which happens whenever pairing issues a new
|
|
1349
|
+
one, went unmentioned. And when doctor could not reach Pushary at all, it said
|
|
1350
|
+
everything was fine.
|
|
1351
|
+
|
|
1352
|
+
Its exit codes now distinguish a broken setup from having no phone connected from
|
|
1353
|
+
not being able to reach Pushary, so a script can tell what went wrong. A test
|
|
1354
|
+
question nobody answers is now withdrawn instead of sitting on your phone.
|
|
1355
|
+
|
|
1356
|
+
## 0.67.0
|
|
1357
|
+
|
|
1358
|
+
### The VS Code agent can now ask for approval on your phone
|
|
1359
|
+
|
|
1360
|
+
Setup has a VS Code option. It installs a Pushary agent plugin, connects the MCP
|
|
1361
|
+
tools so the agent can notify you and ask you questions, and registers a
|
|
1362
|
+
permission gate so a risky terminal command reaches your phone before it runs.
|
|
1363
|
+
The policy behind it is the one your other agents already use, so a rule you
|
|
1364
|
+
wrote for Claude Code applies here too.
|
|
1365
|
+
|
|
1366
|
+
Two things about VS Code shaped how this works.
|
|
1367
|
+
|
|
1368
|
+
VS Code reads a hook's matcher but does not act on it, so the gate is called for
|
|
1369
|
+
every tool the agent uses, including reading a file. It therefore decides for
|
|
1370
|
+
itself, and for anything that is not a risky shell command it returns straight
|
|
1371
|
+
away without touching the disk or the network.
|
|
1372
|
+
|
|
1373
|
+
And a plugin folder does nothing until VS Code is told where it is. Setup adds
|
|
1374
|
+
that entry to your settings.json. That file usually has comments in it, and
|
|
1375
|
+
rewriting it as plain JSON would delete every one of them, so an existing file
|
|
1376
|
+
is edited one line at a time and left otherwise byte for byte identical. If your
|
|
1377
|
+
settings already have a plugin list with comments around it, setup does not
|
|
1378
|
+
guess: it prints the single line to paste and names the file.
|
|
1379
|
+
|
|
1380
|
+
Unlike Cursor, VS Code hooks cannot ask the editor to block a command on the
|
|
1381
|
+
plugin's behalf. So when Pushary cannot reach you, or cannot reach its own
|
|
1382
|
+
server, the gate hands the decision to VS Code's own approval prompt rather than
|
|
1383
|
+
letting the command through.
|
|
1384
|
+
|
|
1385
|
+
`pushary clean` removes all of it again, the settings.json entry included, and
|
|
1386
|
+
`pushary doctor` checks the three things that can be silently wrong: whether the
|
|
1387
|
+
plugin is registered at all, whether the gate script resolves, and whether your
|
|
1388
|
+
key is linked.
|
|
1389
|
+
|
|
1390
|
+
## 0.66.0
|
|
1391
|
+
|
|
1392
|
+
### Setup waited three minutes for a step that takes longer than three minutes
|
|
1393
|
+
|
|
1394
|
+
If you already had the app installed and signed in, pairing resolved in seconds
|
|
1395
|
+
and this never came up. If you did not, it could not work at all.
|
|
1396
|
+
|
|
1397
|
+
Scanning the QR without the app opens a page that sends you to the store. Install
|
|
1398
|
+
it, sign in, subscribe, come back, scan: that is never three minutes. The CLI had
|
|
1399
|
+
given up and cancelled the pairing long before, so a first-time user's first scan
|
|
1400
|
+
was guaranteed to fail, and the only route through was to notice the terminal had
|
|
1401
|
+
stopped waiting and run setup again.
|
|
1402
|
+
|
|
1403
|
+
Setup now waits as long as the code is actually valid, and after the first
|
|
1404
|
+
45 seconds the spinner says what to do if the app is not installed yet. Ctrl-C
|
|
1405
|
+
still exits cleanly and cancels the pairing, so nothing holds you there.
|
|
1406
|
+
|
|
1407
|
+
The window itself is unchanged in what protects it: a pairing id is 128 bits,
|
|
1408
|
+
single use, deleted the moment it is claimed, and authorising one requires a
|
|
1409
|
+
signed-in session for the owning account and the public key you physically
|
|
1410
|
+
scanned.
|
|
1411
|
+
|
|
1412
|
+
## 0.65.0
|
|
1413
|
+
|
|
1414
|
+
### The pairing QR now works when you scan it with your phone camera
|
|
1415
|
+
|
|
1416
|
+
It did not. Scanning it opened the app on "Unmatched Route", and the only way
|
|
1417
|
+
through was the in-app scanner, which parses the QR itself and never routes.
|
|
1418
|
+
|
|
1419
|
+
The QR encoded `https://pushary.com/app/pair`, and `/app/*` is claimed by the
|
|
1420
|
+
app: the AASA lists it and so does the Android manifest. The OS therefore handed
|
|
1421
|
+
the link to the app before the page could load, which was the intent. What it
|
|
1422
|
+
handed over was the path `/app/pair`, and the app's pairing screen is `/pair`.
|
|
1423
|
+
Nothing matched, on every build ever shipped.
|
|
1424
|
+
|
|
1425
|
+
The QR points at `/pair` now. Nothing claims it, so the browser always opens it,
|
|
1426
|
+
and the page hands off to `pushary://pair`, which every shipped build already
|
|
1427
|
+
routes correctly. That is the whole reason for the move: the Android claim is
|
|
1428
|
+
compiled into the installed app, so no amount of server or app-store work fixes
|
|
1429
|
+
the phones already out there, and this does, today.
|
|
1430
|
+
|
|
1431
|
+
`/app/pair` still serves the same page, so a link from an older CLI is not dead.
|
|
1432
|
+
|
|
1433
|
+
## 0.64.4
|
|
1434
|
+
|
|
1435
|
+
### The shell you ran setup in still exported the key it replaced
|
|
1436
|
+
|
|
1437
|
+
0.64.3 stopped a stale export from hiding the working key on disk, and the rc
|
|
1438
|
+
file has been rewritten in place since 0.62. Neither reaches the shell you are
|
|
1439
|
+
standing in: the export there is still the old key, the environment wins over the
|
|
1440
|
+
config file at hook time, and the agent you start in that terminal a minute later
|
|
1441
|
+
talks to whichever workspace that key belongs to.
|
|
1442
|
+
|
|
1443
|
+
`--connect app` mints on every run and is the command onboarding prints, so this
|
|
1444
|
+
is the ordinary case rather than an exotic one. Setup now says so under "Still to
|
|
1445
|
+
do": open a new terminal, or re-export. No key is printed in that line.
|
|
1446
|
+
|
|
1447
|
+
## 0.64.3
|
|
1448
|
+
|
|
1449
|
+
### A stale shell export hid the working key on disk
|
|
1450
|
+
|
|
1451
|
+
`PUSHARY_API_KEY` wins over `~/.pushary/config.json`, which is right and
|
|
1452
|
+
unchanged. But the environment used to win on the key's *shape* alone, so a
|
|
1453
|
+
well-formed key the server had since revoked shadowed a working one in the config
|
|
1454
|
+
file and setup exited "This key was rejected by pushary.com" with a perfectly
|
|
1455
|
+
good key sitting on the machine.
|
|
1456
|
+
|
|
1457
|
+
That is the state any shell left open across a key rotation is in: the export is
|
|
1458
|
+
the old key, the file is the new one, and the terminal you happen to be typing in
|
|
1459
|
+
decides whether setup works.
|
|
1460
|
+
|
|
1461
|
+
The environment key is checked with the server before it is chosen now, and a
|
|
1462
|
+
rejected one falls through to the stored key rather than ending the run. It costs
|
|
1463
|
+
no extra call: the verdict is handed to the verification step that was already
|
|
1464
|
+
making it.
|
|
1465
|
+
|
|
1466
|
+
### `--connect app` on a machine with no terminal named the wrong problem
|
|
1467
|
+
|
|
1468
|
+
A headless run that asked to pair said "No API key, and no terminal to sign in
|
|
1469
|
+
from" even with a key in the config file, because asking to pair deliberately
|
|
1470
|
+
takes the stored key off the table. It now says the flag needs a terminal for the
|
|
1471
|
+
QR, and that dropping it uses the key already there.
|
|
1472
|
+
|
|
1473
|
+
## 0.64.2
|
|
1474
|
+
|
|
1475
|
+
### A run that gave up reported nothing, and said it stopped at the start
|
|
1476
|
+
|
|
1477
|
+
`agent_setup_completed` was wired to one of setup's fifteen exits: the last line
|
|
1478
|
+
of a run that finished. Every other way out returned before it. A key the server
|
|
1479
|
+
refused, a cancelled pairing, no terminal to sign in from, an installer that
|
|
1480
|
+
threw, a Ctrl-C at the agent checkbox: all silent. The beacon was added so failed
|
|
1481
|
+
and abandoned runs would stop being invisible, and the runs it could see were
|
|
1482
|
+
still only the ones that already worked.
|
|
1483
|
+
|
|
1484
|
+
Every early exit reports now, and each says why it stopped: `key-refused`,
|
|
1485
|
+
`pairing-cancelled`, `no-key`, `login-rejected`, `no-agents-detected`,
|
|
1486
|
+
`invalid-key`, `interrupted`, `crashed`.
|
|
1487
|
+
|
|
1488
|
+
Argv-time exits stay silent, along with `--help` and `--dry-run`. A flag typo is
|
|
1489
|
+
not an abandoned run: nothing was attempted and nothing was written. It is also
|
|
1490
|
+
the fastest-failing path there is, and waiting on a beacon took `setup --bogus`
|
|
1491
|
+
from 60ms to two seconds against an unreachable server. A dry run stays silent
|
|
1492
|
+
for the other reason: counting it as a completed setup would be its own lie.
|
|
1493
|
+
|
|
1494
|
+
`abandonedAt` said where. It was always `start`, because `markStep` had no call
|
|
1495
|
+
site outside its own test, so the field that was supposed to locate a failure was
|
|
1496
|
+
a constant. It now moves through the run, per agent inside the configure loop, so
|
|
1497
|
+
an installer that hangs is named rather than grouped.
|
|
1498
|
+
|
|
1499
|
+
Ctrl-C at the pairing QR reports before the process leaves. The cancel and the
|
|
1500
|
+
beacon run concurrently and the exit waits for both, so this adds nothing to the
|
|
1501
|
+
worst case a cancel already had.
|
|
1502
|
+
|
|
1503
|
+
The beacon authenticates with the key the run resolved rather than the one
|
|
1504
|
+
already installed. Most of these exits happen before the key is written to disk,
|
|
1505
|
+
so without that they posted as nobody, which meant the window holding the
|
|
1506
|
+
interesting failures was exactly the window that could not report.
|
|
1507
|
+
|
|
1508
|
+
## 0.64.1
|
|
1509
|
+
|
|
1510
|
+
### Ctrl-C at the pairing QR printed nothing
|
|
1511
|
+
|
|
1512
|
+
The QR screen said "No app? Press Ctrl-C for the ways to finish without one", and
|
|
1513
|
+
pressing it printed nothing at all. `connectViaAppPairing` handles SIGINT by
|
|
1514
|
+
exiting the process itself, so control never returned to setup and the lines it
|
|
1515
|
+
meant to show were unreachable on the one path that advertised them.
|
|
1516
|
+
|
|
1517
|
+
Setup now hands the escape to the pairing wait, which prints it inside the
|
|
1518
|
+
handler before exiting, and the line on screen names the command directly instead
|
|
1519
|
+
of telling you to interrupt the program to find out what it is.
|
|
1520
|
+
|
|
1521
|
+
The same two commands are printed when a pairing simply never completes, so both
|
|
1522
|
+
ways out of the QR screen say the same thing.
|
|
1523
|
+
|
|
1524
|
+
|
|
1525
|
+
## 0.64.0
|
|
1526
|
+
|
|
1527
|
+
### A typo on `--dry-run` ran a real clean
|
|
1528
|
+
|
|
1529
|
+
`pushary clean --yes --dryrun`, one missing hyphen, silently dropped the flag it
|
|
1530
|
+
did not recognise and performed a full destructive clean: the stored key, the
|
|
1531
|
+
Cursor plugin, every agent config, and the global package itself. It printed
|
|
1532
|
+
"Clean complete." with the past tense throughout and exited `0`. The typo most
|
|
1533
|
+
worth catching was the one on the flag that means "change nothing", sitting next
|
|
1534
|
+
to the flag that means "do not ask me".
|
|
1535
|
+
|
|
1536
|
+
`clean` now rejects a flag it does not know and exits `2` before removing
|
|
1537
|
+
anything. `--dry-run`, `--yes` and `-y` are unchanged.
|
|
1538
|
+
|
|
1539
|
+
### Setup reuses the key this machine already has
|
|
1540
|
+
|
|
1541
|
+
`setup` read `PUSHARY_API_KEY` and never `~/.pushary/config.json`, so the key
|
|
1542
|
+
`pushary login` had just written was invisible to it. `login` followed by `setup`
|
|
1543
|
+
in the same shell ran a second browser login and minted a second key, and a
|
|
1544
|
+
machine with no terminal that had been set up by `login` exited "no API key" with
|
|
1545
|
+
a working key sitting on disk. The shell export made it work in a *new* shell,
|
|
1546
|
+
which is what hid it.
|
|
1547
|
+
|
|
1548
|
+
Setup now reads the stored key, verifies it, and uses it. A key the server no
|
|
1549
|
+
longer recognises falls through to a fresh sign-in rather than dead-ending, and a
|
|
1550
|
+
server it cannot reach keeps the key rather than discarding it.
|
|
1551
|
+
|
|
1552
|
+
### The Pushary app is the default connect path
|
|
1553
|
+
|
|
1554
|
+
An unflagged `setup` now pairs the app instead of sending you to the browser
|
|
1555
|
+
subscribe page. `--connect browser` is the fallback for a machine with no app,
|
|
1556
|
+
`--connect web` is the legacy page and keeps working exactly as before, and
|
|
1557
|
+
`--connect none` skips the phone step like `--skip-phone`. `auto` and `pwa` are
|
|
1558
|
+
accepted as input spellings.
|
|
1559
|
+
|
|
1560
|
+
Pairing only happens when there is a terminal to show the QR to and no usable key
|
|
1561
|
+
already on the machine, so re-running setup to add an agent no longer asks you to
|
|
1562
|
+
scan anything, and no longer mints a key you did not ask for. Typing
|
|
1563
|
+
`--connect app` explicitly still pairs every time.
|
|
1564
|
+
|
|
1565
|
+
When pairing does not complete, setup now names the two ways to finish without
|
|
1566
|
+
the app rather than only inviting you to try the QR again.
|
|
1567
|
+
|
|
1568
|
+
|
|
1569
|
+
## 0.63.2
|
|
1570
|
+
|
|
1571
|
+
### Setup reports what happened, not only that it worked
|
|
1572
|
+
|
|
1573
|
+
The setup event fired only when at least one agent had been configured, so a run
|
|
1574
|
+
that configured nothing, failed, or stopped partway reported nothing at all. The
|
|
1575
|
+
only visible runs were the ones that already worked.
|
|
1576
|
+
|
|
1577
|
+
It now fires once per run whatever the outcome, and carries the connect mode and
|
|
1578
|
+
its result, how many agents failed, which were skipped, whether the run was
|
|
1579
|
+
non-interactive, and where a run stopped if it did.
|
|
1580
|
+
|
|
1581
|
+
This is the same event that already powers your activity feed, sent with your own
|
|
1582
|
+
API key to your own workspace. No repo path, hostname or agent content is
|
|
1583
|
+
included, and nothing is sent before you have a key.
|
|
1584
|
+
|
|
1585
|
+
|
|
1586
|
+
## 0.63.1
|
|
1587
|
+
|
|
1588
|
+
### `pushary status --bogus` printed a stack trace
|
|
1589
|
+
|
|
1590
|
+
An unknown flag on `status` threw where nothing was catching it, so Node printed
|
|
1591
|
+
a raw stack trace and exited `1` instead of the clean usage error and exit `2`
|
|
1592
|
+
every other command gives. Other commands were unaffected.
|
|
1593
|
+
|
|
1594
|
+
|
|
1595
|
+
## 0.63.0
|
|
1596
|
+
|
|
1597
|
+
### Machines
|
|
1598
|
+
|
|
1599
|
+
`pushary daemon` now reports itself to your workspace once a minute, and
|
|
1600
|
+
`pushary status --devices` lists what is there:
|
|
1601
|
+
|
|
1602
|
+
```
|
|
1603
|
+
Machines
|
|
1604
|
+
● macbook-pro 0.63.0 just now
|
|
1605
|
+
○ build-server 0.62.6 4h ago
|
|
1606
|
+
```
|
|
1607
|
+
|
|
1608
|
+
`online` is worked out from the last heartbeat rather than stored, so a laptop
|
|
1609
|
+
that closes mid-session reads offline a few minutes later without anything
|
|
1610
|
+
having to notice it left.
|
|
1611
|
+
|
|
1612
|
+
This is what lets a phone start a session on a machine you name, instead of on
|
|
1613
|
+
whichever one happens to answer first.
|
|
1614
|
+
|
|
1615
|
+
Presence is best effort throughout. A heartbeat that fails never stops the daemon
|
|
1616
|
+
draining its queue, and `status` still reports your key and channels if the
|
|
1617
|
+
machine list cannot be read. Against a server that predates the endpoint,
|
|
1618
|
+
`--devices` says so rather than reporting an empty list.
|
|
1619
|
+
|
|
1620
|
+
**Needs a database migration** (`0066`). Until it is applied, `--devices` reports
|
|
1621
|
+
that it could not read the list, and nothing else is affected.
|
|
1622
|
+
|
|
1623
|
+
|
|
1624
|
+
## 0.62.6
|
|
1625
|
+
|
|
1626
|
+
### `clean` rewrote your project's Cursor config and left your own alone
|
|
1627
|
+
|
|
1628
|
+
`clean` built the Cursor MCP path without a home directory, so it resolved
|
|
1629
|
+
against whatever directory you ran it from. Inside a repo that has a
|
|
1630
|
+
`.cursor/mcp.json`, it rewrote that committed file, stripping the Pushary entry
|
|
1631
|
+
and reformatting the rest. Meanwhile your real `~/.cursor/mcp.json` was never
|
|
1632
|
+
touched, so the API key stayed in it.
|
|
1633
|
+
|
|
1634
|
+
Check `git status` in any project you ran `clean` from, and check
|
|
1635
|
+
`~/.cursor/mcp.json` for a key you thought was gone.
|
|
1636
|
+
|
|
1637
|
+
### Every file the CLI touches is now named in one place
|
|
1638
|
+
|
|
1639
|
+
`setup`, `clean`, `doctor`, `logout` and `upgrade` each derived the same paths by
|
|
1640
|
+
hand, and they drifted. That drift caused this bug and the `CODEX_HOME` one
|
|
1641
|
+
before it. All twenty-three call sites now resolve through one module, and a test
|
|
1642
|
+
fails any command that starts spelling a path out again.
|
|
1643
|
+
|
|
1644
|
+
No behaviour changed anywhere else: setup's output is byte-identical across every
|
|
1645
|
+
mode before and after.
|
|
1646
|
+
|
|
1647
|
+
|
|
1648
|
+
## 0.62.5
|
|
1649
|
+
|
|
1650
|
+
### `clean --dry-run` changed things again
|
|
1651
|
+
|
|
1652
|
+
0.62.4 moved the dry-run guards into their own module and passed the flag in by
|
|
1653
|
+
value. That happened at module load, before the flag had been parsed from the
|
|
1654
|
+
command line, so every guard was built in live mode and stayed there. A dry run
|
|
1655
|
+
stripped the Codex key, rewrote agent configs and removed the plugin directory,
|
|
1656
|
+
while printing "would" beside each one.
|
|
1657
|
+
|
|
1658
|
+
The guards now read the flag when they run rather than when they are built, so
|
|
1659
|
+
there is no longer an order in which they can be created too early.
|
|
1660
|
+
|
|
1661
|
+
If you ran `clean --dry-run` on 0.62.4, treat it as though you ran a real clean:
|
|
1662
|
+
re-run `setup` to reconfigure.
|
|
1663
|
+
|
|
1664
|
+
|
|
1665
|
+
## 0.62.4
|
|
1666
|
+
|
|
1667
|
+
### `pushary clean` crashed partway through on Node 24
|
|
1668
|
+
|
|
1669
|
+
`clean` threw `ERR_INVALID_ARG_TYPE: The "options.force" property must be of
|
|
1670
|
+
type boolean` and died mid-run, after cleaning some agents and before touching
|
|
1671
|
+
the rest. Introduced in 0.62.0 by the `--dry-run` work, which started passing
|
|
1672
|
+
`force: undefined` to `rmSync` on every call that omitted the option. Older Node
|
|
1673
|
+
ignored that; Node 24 rejects it.
|
|
1674
|
+
|
|
1675
|
+
If a clean died on you, re-run it. It is safe to run repeatedly and picks up
|
|
1676
|
+
whatever is left.
|
|
1677
|
+
|
|
1678
|
+
The three mutation primitives behind `--dry-run` now live in `src/cli/dry-run.ts`
|
|
1679
|
+
instead of inside the command file. Both `--dry-run` defects that reached users
|
|
1680
|
+
were in those few lines, and both got past tests that could only read the source
|
|
1681
|
+
as text, because code in `bin/` runs at import and cannot be called by a test.
|
|
1682
|
+
They are ordinary functions with ordinary tests now.
|
|
1683
|
+
|
|
1684
|
+
### The authorize page's code input overflowed its card
|
|
1685
|
+
|
|
1686
|
+
The eight character slots were a fixed width, so they added up to 488px inside a
|
|
1687
|
+
384px card and spilled out of both edges. They now share the row and shrink to
|
|
1688
|
+
fit, down to a 320px phone.
|
|
1689
|
+
|
|
1690
|
+
|
|
1691
|
+
## 0.62.3
|
|
1692
|
+
|
|
1693
|
+
### `setup --connect app --key ...` discarded your key
|
|
1694
|
+
|
|
1695
|
+
Pairing was checked before the supplied key, so passing `--key` or `--key-stdin`
|
|
1696
|
+
alongside `--connect app` silently threw that key away and minted a new one. The
|
|
1697
|
+
key you named stayed live on the account with nothing pointing at it. An
|
|
1698
|
+
explicitly supplied key now wins, and pairing is only the key source when there
|
|
1699
|
+
is no key to use.
|
|
1700
|
+
|
|
1701
|
+
Pairing still mints a key each time it runs, which is inherent to the protocol.
|
|
1702
|
+
When that replaces a key this machine was already using, setup now says so and
|
|
1703
|
+
names the old key, rather than leaving you to find it in the dashboard later. It
|
|
1704
|
+
does not revoke it for you: the same key may be in use on another machine.
|
|
1705
|
+
|
|
1706
|
+
### The update banner offered downgrades
|
|
1707
|
+
|
|
1708
|
+
Setup compared its version to the registry with `!==`, so it announced an update
|
|
1709
|
+
whenever the two merely differed. A machine running a build ahead of npm was told
|
|
1710
|
+
`Update available: 0.62.3 -> 0.62.2` and pointed at a command that would have
|
|
1711
|
+
downgraded it. It now prompts only when the registry is genuinely ahead, using
|
|
1712
|
+
the same comparison `pushary upgrade` already used. There is one implementation
|
|
1713
|
+
of that comparison now instead of two.
|
|
1714
|
+
|
|
1715
|
+
### A reused key skipped its own verification
|
|
1716
|
+
|
|
1717
|
+
Setup skipped the server key check whenever `--connect app` was passed, on the
|
|
1718
|
+
assumption the key had just been minted. That is only true when pairing actually
|
|
1719
|
+
produced it, so a key reused on an `--connect app` run was never verified. The
|
|
1720
|
+
skip now follows where the key came from.
|
|
1721
|
+
|
|
1722
|
+
|
|
1723
|
+
## 0.62.2
|
|
1724
|
+
|
|
1725
|
+
### An ignored `--dry-run` made real changes
|
|
1726
|
+
|
|
1727
|
+
`mode`, `wait`, `suggestions` and `status` read their arguments positionally and
|
|
1728
|
+
silently discarded any flag they did not recognise. So `pushary mode push_only
|
|
1729
|
+
--dry-run` dropped the flag and changed the mode for real. Those four now reject
|
|
1730
|
+
an unknown flag and exit `2`, and they do it before looking for your API key, so
|
|
1731
|
+
a typo is reported as a typo rather than as a missing key.
|
|
1732
|
+
|
|
1733
|
+
### The proxy support was never switched on
|
|
1734
|
+
|
|
1735
|
+
`HTTP_PROXY` and `HTTPS_PROXY` handling was written but never called from
|
|
1736
|
+
anywhere, and Node's fetch ignores both without it. Behind a corporate proxy,
|
|
1737
|
+
setup verified your key through the one code path that used it, reported
|
|
1738
|
+
everything healthy, and every approval afterwards failed. It is now installed by
|
|
1739
|
+
the shared HTTP layer, the MCP transport and the command dispatcher.
|
|
1740
|
+
|
|
1741
|
+
### `doctor --json` woke your phone on every run
|
|
1742
|
+
|
|
1743
|
+
The test push was skipped only for `--no-push`. Running `pushary doctor --json`
|
|
1744
|
+
on a schedule sent a real notification each time. It is now opt-out for a person
|
|
1745
|
+
at a terminal and off by default for `--json` or a non-TTY. Pass `--roundtrip` to
|
|
1746
|
+
send one deliberately.
|
|
1747
|
+
|
|
1748
|
+
### Browser login could stall on the plan step
|
|
1749
|
+
|
|
1750
|
+
Choosing a plan navigated the same tab away from the authorize page, and that
|
|
1751
|
+
page was the only thing that could finish the login once the plan activated, so
|
|
1752
|
+
the terminal waited on a promise nothing was going to keep. Checkout now opens in
|
|
1753
|
+
a new tab and the page completes the login by itself. The CLI no longer claims it
|
|
1754
|
+
finishes on its own without saying the tab has to stay open.
|
|
1755
|
+
|
|
1756
|
+
|
|
1757
|
+
## 0.62.1
|
|
1758
|
+
|
|
1759
|
+
### `pushary clean --dry-run` was not a dry run
|
|
1760
|
+
|
|
1761
|
+
In 0.62.0 the flag ran four real operations while printing "would":
|
|
1762
|
+
|
|
1763
|
+
- uninstalled the global `@pushary/agent-hooks` package
|
|
1764
|
+
- rewrote your shell profile to remove the `claude` alias
|
|
1765
|
+
- stripped the Pushary block from `~/.codex/AGENTS.md`
|
|
1766
|
+
- stripped it from `~/.gemini/GEMINI.md`
|
|
1767
|
+
|
|
1768
|
+
If you ran `pushary clean --dry-run` on 0.62.0 and the CLI then seemed to
|
|
1769
|
+
disappear, that is why. Reinstall with `npm i -g @pushary/agent-hooks`, and check
|
|
1770
|
+
your shell profile and those two files against version control if you keep them
|
|
1771
|
+
there.
|
|
1772
|
+
|
|
1773
|
+
### `pushary upgrade` could destroy `~/.claude.json`
|
|
1774
|
+
|
|
1775
|
+
`upgrade` treated an unparseable config as an empty one and wrote it back, so a
|
|
1776
|
+
truncated `~/.claude.json` was replaced with nothing but the Pushary MCP entry,
|
|
1777
|
+
losing every project, MCP server and permission rule in it. A half-written
|
|
1778
|
+
`~/.claude.json` is what an interrupted write leaves behind, so this needed only
|
|
1779
|
+
one earlier crash to line up.
|
|
1780
|
+
|
|
1781
|
+
A config that cannot be parsed is now left untouched, and the other agent files
|
|
1782
|
+
are still refreshed around it.
|
|
1783
|
+
|
|
1784
|
+
|
|
1785
|
+
## 0.62.0
|
|
1786
|
+
|
|
1787
|
+
### `pushary clean` could leave your API key on disk
|
|
1788
|
+
|
|
1789
|
+
If `CODEX_HOME` was set, `clean` looked for Codex's `config.toml` in `~/.codex`
|
|
1790
|
+
and never found the real one. It printed `Codex config (not found)`, exited `0`,
|
|
1791
|
+
and left the key in the file. A user who ran `clean` to remove their key was told
|
|
1792
|
+
it had worked.
|
|
1793
|
+
|
|
1794
|
+
The same fault ran through the rest of the Codex path. With `CODEX_HOME` set:
|
|
1795
|
+
|
|
1796
|
+
- `setup` wrote the MCP server, the trust hashes and the notify entry to
|
|
1797
|
+
`~/.codex/config.toml` while writing `hooks.json`, `AGENTS.md` and the skill to
|
|
1798
|
+
the real `CODEX_HOME`, splitting the install across two directories
|
|
1799
|
+
- `setup --dry-run` named the relocated path, so the preview and the real run
|
|
1800
|
+
disagreed about where the key would land
|
|
1801
|
+
- `doctor` read the relocated config and reported the key missing
|
|
1802
|
+
- `logout` treated an exported-but-empty `CODEX_HOME` as a path relative to the
|
|
1803
|
+
current directory
|
|
1804
|
+
|
|
1805
|
+
If you use `CODEX_HOME`, run `pushary clean --dry-run` to see what is still
|
|
1806
|
+
there, and check `$CODEX_HOME/config.toml` for a key you thought was gone.
|
|
1807
|
+
|
|
1808
|
+
### New
|
|
1809
|
+
|
|
1810
|
+
- `pushary clean --dry-run` lists everything it would remove and changes nothing
|
|
1811
|
+
- `pushary doctor --bundle` writes a redacted diagnostics file for a support
|
|
1812
|
+
thread. Keys, your home directory and the machine name are stripped.
|
|
1813
|
+
- `pushary logout --revoke` kills the key server-side as well as locally, so it
|
|
1814
|
+
stops working everywhere rather than only on this machine
|
|
1815
|
+
|
|
1816
|
+
### Changed
|
|
1817
|
+
|
|
1818
|
+
- `pushary upgrade` now refreshes Codex, Gemini CLI and Cursor, not only Claude
|
|
1819
|
+
Code, and names the agents it actually touched. It matters most for Codex,
|
|
1820
|
+
whose hooks are trusted by hash: a refreshed hook command without a refreshed
|
|
1821
|
+
trust entry is a hook Codex silently declines to run.
|
|
1822
|
+
- `pushary upgrade` with no global install now names the install modes it can
|
|
1823
|
+
see and the command that updates each, instead of one generic line and exit 0
|
|
1824
|
+
- `pushary daemon` stops on a rejected key or a closed plan and exits `4`
|
|
1825
|
+
(`UNAUTHENTICATED`). It used to treat that like a network blip, back off to a
|
|
1826
|
+
two-minute poll and sit there under an `online` banner, so a revoked key left a
|
|
1827
|
+
daemon that looked healthy and would never launch anything again.
|
|
1828
|
+
- Ctrl-C during app pairing now cancels the pairing on the server, so a QR still
|
|
1829
|
+
on your screen stops being claimable immediately instead of staying live for
|
|
1830
|
+
the rest of its five minutes
|
|
1831
|
+
- `setup` prints the command surface when it finishes
|
|
1832
|
+
|
|
1833
|
+
## 0.61.0
|
|
1834
|
+
|
|
1835
|
+
The largest release since the CLI shipped. Setup no longer asks you to paste an
|
|
1836
|
+
API key, the key is checked against the server before anything is written to your
|
|
1837
|
+
machine, and commands exit with codes that mean something.
|
|
1838
|
+
|
|
1839
|
+
### Read this first: exit codes changed
|
|
1840
|
+
|
|
1841
|
+
Commands that used to print a failure and exit `0` now exit non-zero. If you run
|
|
1842
|
+
any of these in CI or chained behind `&&`, this is the part that affects you.
|
|
1843
|
+
|
|
1844
|
+
| Command | Was | Now |
|
|
1845
|
+
| --- | --- | --- |
|
|
1846
|
+
| any unknown command or `pushary` typo | `0`, printed help | `2` |
|
|
1847
|
+
| `doctor` with a failing check | `0` | `5` |
|
|
1848
|
+
| `setup` with a revoked key | `0` | `4` |
|
|
1849
|
+
| `setup` with an expired or locked plan | `0` | `5` |
|
|
1850
|
+
| `mode`, `wait` with a rejected key | `0` | non-zero |
|
|
1851
|
+
| `status` with no key configured | did not exist | `3` |
|
|
1852
|
+
|
|
1853
|
+
The full ladder: `0` ok, `1` failed, `2` usage, `3` not configured,
|
|
1854
|
+
`4` unauthenticated, `5` problems found, `6` no device, `7` input required,
|
|
1855
|
+
`8` unreachable, `130` interrupted.
|
|
1856
|
+
|
|
1857
|
+
`pushary doctor && deploy` used to succeed against a machine doctor had just
|
|
1858
|
+
declared broken. That is the change worth knowing about.
|
|
1859
|
+
|
|
1860
|
+
### Sign in instead of pasting a key
|
|
1861
|
+
|
|
1862
|
+
`pushary setup` opens a browser, shows you a code, and receives the key sealed to
|
|
1863
|
+
a keypair generated on your machine. The key is never pasted, never shown in a
|
|
1864
|
+
terminal, and never transits in a form the server could read.
|
|
1865
|
+
|
|
1866
|
+
- `pushary login` and `pushary logout` as standalone commands
|
|
1867
|
+
- Pasting a key still works, as a fallback and via `--key`
|
|
1868
|
+
- `--key-stdin` reads the key from stdin, so it stays out of your shell history
|
|
1869
|
+
and out of the process list
|
|
1870
|
+
|
|
1871
|
+
### New commands
|
|
1872
|
+
|
|
1873
|
+
- `pushary status` reports what this machine is connected to, with `--json`
|
|
1874
|
+
- `pushary login`, `pushary logout`, `pushary connect`
|
|
1875
|
+
|
|
1876
|
+
### Setup
|
|
1877
|
+
|
|
1878
|
+
- The key is verified against the server before a single file is written. A
|
|
1879
|
+
revoked key or a locked plan stops the run instead of producing a config that
|
|
1880
|
+
looks finished and never delivers.
|
|
1881
|
+
- `--yes` runs setup with no prompts, for CI and images
|
|
1882
|
+
- `--agents claude_code,codex,...` picks agents without asking
|
|
1883
|
+
- `--dry-run` prints what it would do and touches nothing
|
|
1884
|
+
- Agent detection is real, so an agent you do not have is reported as skipped
|
|
1885
|
+
rather than counted as configured
|
|
1886
|
+
- Ctrl-C in a trailing prompt no longer reports the whole run as failed
|
|
1887
|
+
- A stale `PUSHARY_API_KEY` export in your shell profile is now replaced rather
|
|
1888
|
+
than skipped. It used to win forever over a newly rotated key.
|
|
1889
|
+
|
|
1890
|
+
### Delivery is proved to your phone, not just to the workspace
|
|
1891
|
+
|
|
1892
|
+
Setup and doctor now check that an approval reaches **you**, not merely that
|
|
1893
|
+
somebody in the workspace has a device. On a shared site where the only reachable
|
|
1894
|
+
channel cannot be attributed to you, that is said plainly rather than reported as
|
|
1895
|
+
success.
|
|
1896
|
+
|
|
1897
|
+
### Diagnostics
|
|
1898
|
+
|
|
1899
|
+
- `pushary doctor --json` for one machine-readable envelope
|
|
1900
|
+
- `pushary doctor --roundtrip` sends a real approval and waits for the tap. It
|
|
1901
|
+
works without a TTY, where the old confirm prompt could not run at all.
|
|
1902
|
+
- `pushary doctor --no-push` for a check that stays silent
|
|
1903
|
+
- doctor reads the same key the hooks will actually use, honoring
|
|
1904
|
+
`PUSHARY_CONFIG_FILE`, and reports a Claude or Cursor config holding a
|
|
1905
|
+
different key
|
|
1906
|
+
|
|
1907
|
+
### Correctness
|
|
1908
|
+
|
|
1909
|
+
- `--help` and `--version` print and exit instead of running the command.
|
|
1910
|
+
`pushary daemon --help` used to start a daemon; `pushary hook --help` used to
|
|
1911
|
+
block forever on stdin.
|
|
1912
|
+
- Unknown flags are rejected on the commands that parse them, instead of being
|
|
1913
|
+
coerced into a plausible wrong value. `--connect` with an unrecognized mode used
|
|
1914
|
+
to silently mean `web`.
|
|
1915
|
+
- Every file this CLI writes is written atomically, so an interrupted setup can
|
|
1916
|
+
no longer truncate a large `~/.claude.json`
|
|
1917
|
+
- An unparseable agent config is left alone instead of being overwritten
|
|
1918
|
+
- Files holding your key are created `0600`
|
|
1919
|
+
- `CODEX_HOME` is honored when locating `config.toml`
|
|
1920
|
+
- Hermes setup works on Windows
|
|
1921
|
+
- The Cursor plugin is staged and verified before the old one is replaced
|
|
1922
|
+
- `installGlobally` pins the exact running version instead of drifting to latest
|
|
1923
|
+
- `pushary clean` removes only the export line it wrote, not every line in your
|
|
1924
|
+
shell profile that mentions the key variable
|
|
1925
|
+
- Requests honor `HTTP_PROXY` and `HTTPS_PROXY`
|
|
1926
|
+
- Color honors `NO_COLOR` and `FORCE_COLOR`, and turns itself off when output is
|
|
1927
|
+
not a terminal
|
|
1928
|
+
- `--json` output is machine-readable on stdout, with human text on stderr, so a
|
|
1929
|
+
pipe never receives a spinner
|
|
1930
|
+
|
|
1931
|
+
### Pairing
|
|
1932
|
+
|
|
1933
|
+
The pairing QR now points at a real web page, so scanning it on a phone without
|
|
1934
|
+
the app installed lands on install instructions for that platform instead of a
|
|
1935
|
+
dead custom-scheme link.
|
|
1936
|
+
|
|
1937
|
+
### Known gaps
|
|
1938
|
+
|
|
1939
|
+
- `status`, `clean`, `mode`, `wait`, `stats`, `suggestions` and `upgrade` still
|
|
1940
|
+
ignore an unknown flag rather than exiting `2`. Unchanged from 0.60.0.
|
|
1941
|
+
- `pushary upgrade` reapplies Claude settings only. Other agents need `setup`.
|
|
1942
|
+
- Codex hook trust still uses a fixed version ceiling, so a newer Codex is sent
|
|
1943
|
+
to the manual `/hooks` step.
|