pushary 1.9.81 → 1.9.83

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.
Files changed (137) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/data/SKILL.md +132 -465
  3. package/data/cowork/SKILL.md +8 -8
  4. package/data/cursor-plugin/.cursor-plugin/plugin.json +1 -1
  5. package/data/cursor-plugin/CHANGELOG.md +8 -0
  6. package/data/cursor-plugin/commands/notify-when-done.md +1 -1
  7. package/data/cursor-plugin/rules/pushary.mdc +5 -4
  8. package/data/cursor-plugin/skills/pushary/SKILL.md +131 -464
  9. package/data/vscode-plugin/.claude-plugin/plugin.json +1 -1
  10. package/data/vscode-plugin/CHANGELOG.md +8 -0
  11. package/data/vscode-plugin/commands/notify-when-done.md +1 -1
  12. package/data/vscode-plugin/skills/pushary/SKILL.md +131 -464
  13. package/dist/{api-VFUZ5HBR.js → api-EKLKVJQG.js} +11 -11
  14. package/dist/bin/pushary-bell-hook.js +26 -26
  15. package/dist/bin/pushary-bell.js +11 -11
  16. package/dist/bin/pushary-claude.js +27 -27
  17. package/dist/bin/pushary-clean.js +37 -34
  18. package/dist/bin/pushary-codex-bridge.js +22 -22
  19. package/dist/bin/pushary-codex-hook.js +31 -31
  20. package/dist/bin/pushary-codex-session.js +8 -8
  21. package/dist/bin/pushary-codex.js +14 -14
  22. package/dist/bin/pushary-connect.js +27 -27
  23. package/dist/bin/pushary-cowork.js +6 -6
  24. package/dist/bin/pushary-daemon.js +39 -39
  25. package/dist/bin/pushary-disconnect.js +26 -26
  26. package/dist/bin/pushary-display-hook.js +17 -17
  27. package/dist/bin/pushary-doctor.js +48 -44
  28. package/dist/bin/pushary-elicitation-hook.js +26 -26
  29. package/dist/bin/pushary-gemini-bridge.js +16 -16
  30. package/dist/bin/pushary-gemini-hook.js +28 -28
  31. package/dist/bin/pushary-hook.js +30 -30
  32. package/dist/bin/pushary-login.js +21 -21
  33. package/dist/bin/pushary-logout.js +25 -25
  34. package/dist/bin/pushary-mcp.js +12 -12
  35. package/dist/bin/pushary-mode.js +16 -16
  36. package/dist/bin/pushary-notification-hook.js +25 -25
  37. package/dist/bin/pushary-opencode-hook.js +27 -27
  38. package/dist/bin/pushary-permission-denied-hook.js +30 -30
  39. package/dist/bin/pushary-permission-hook.js +30 -30
  40. package/dist/bin/pushary-post-hook.js +25 -25
  41. package/dist/bin/pushary-prompt-hook.js +25 -25
  42. package/dist/bin/pushary-session-end-hook.js +27 -27
  43. package/dist/bin/pushary-session-start-hook.js +35 -35
  44. package/dist/bin/pushary-setup.js +52 -49
  45. package/dist/bin/pushary-stats.js +6 -6
  46. package/dist/bin/pushary-status.js +32 -32
  47. package/dist/bin/pushary-stop-hook.js +25 -25
  48. package/dist/bin/pushary-stopfailure-hook.js +25 -25
  49. package/dist/bin/pushary-suggestions.js +16 -16
  50. package/dist/bin/pushary-transcript-register.js +17 -17
  51. package/dist/bin/pushary-transcripts.js +19 -19
  52. package/dist/bin/pushary-upgrade.js +40 -40
  53. package/dist/bin/pushary-wait.js +16 -16
  54. package/dist/bin/pushary.js +8 -8
  55. package/dist/{chunk-DPP3ZCEE.js → chunk-2B7YU4EK.js} +3 -3
  56. package/dist/{chunk-ALTTZKDU.js → chunk-2BDGGE2G.js} +9 -8
  57. package/dist/{chunk-TTTK44RK.js → chunk-2KVELJZX.js} +1 -1
  58. package/dist/{chunk-4TR4G7A4.js → chunk-35ABA2JQ.js} +1 -1
  59. package/dist/{chunk-X772USE7.js → chunk-42RQG2VG.js} +7 -6
  60. package/dist/{chunk-74QJBFMG.js → chunk-44R5ATBR.js} +2 -2
  61. package/dist/{chunk-5WC7TXSS.js → chunk-46P53RBT.js} +13 -13
  62. package/dist/{chunk-LFEJVQWI.js → chunk-4P256ZAU.js} +6 -6
  63. package/dist/{chunk-NVEDZZ3V.js → chunk-5FM4BQOM.js} +14 -12
  64. package/dist/{chunk-NK3I2QQA.js → chunk-5UPZR3C3.js} +4 -4
  65. package/dist/{chunk-IQQQ2LGB.js → chunk-6BD5ZZLL.js} +5 -5
  66. package/dist/{chunk-TJZIUFWK.js → chunk-6CJ4MSZU.js} +20 -19
  67. package/dist/{chunk-5WIVX2NN.js → chunk-6PWU2LBT.js} +4 -4
  68. package/dist/{chunk-SFUJLUBM.js → chunk-7EIMASAA.js} +1 -1
  69. package/dist/{chunk-KXSZ43Z6.js → chunk-7MRX5JYA.js} +9 -9
  70. package/dist/{chunk-HG5EUDF3.js → chunk-7YYJASRF.js} +2 -2
  71. package/dist/{chunk-AZV3DQQU.js → chunk-A3TMREYJ.js} +4 -4
  72. package/dist/{chunk-PJ26BODT.js → chunk-ACM4OINM.js} +1 -1
  73. package/dist/{chunk-7JU6DPO7.js → chunk-ASEHRBD4.js} +3 -3
  74. package/dist/{chunk-B2HMISVL.js → chunk-B2RDWRKR.js} +86 -46
  75. package/dist/{chunk-QNEWV4JV.js → chunk-BG4YJEFE.js} +1 -1
  76. package/dist/{chunk-K5QN6V5K.js → chunk-BJN7LMMK.js} +9 -6
  77. package/dist/{chunk-3BFCJZBU.js → chunk-BLM6O2QL.js} +1 -1
  78. package/dist/{chunk-DLBUDQEE.js → chunk-BUXMFD5C.js} +4 -4
  79. package/dist/{chunk-M5XNFUXN.js → chunk-C5EWCNJF.js} +1 -1
  80. package/dist/{chunk-35S7VB26.js → chunk-CVPVP4KP.js} +5 -5
  81. package/dist/{chunk-BB4W35D6.js → chunk-ELYJNNXG.js} +10 -10
  82. package/dist/{chunk-VYRJAPZ6.js → chunk-EMT7I74E.js} +3 -3
  83. package/dist/{chunk-NPBPH7KI.js → chunk-FEF3ILT6.js} +2 -2
  84. package/dist/{chunk-ESS7CW3I.js → chunk-FGWRVOBP.js} +5 -5
  85. package/dist/{chunk-LJ4TUZJV.js → chunk-G4HTRKCP.js} +6 -6
  86. package/dist/{chunk-UUASO2FU.js → chunk-GGI6GTBL.js} +4 -4
  87. package/dist/{chunk-RVC7OSC7.js → chunk-GKWPIE6R.js} +1 -1
  88. package/dist/{chunk-KERGWHCK.js → chunk-GMZTABMV.js} +7 -7
  89. package/dist/{chunk-IA53GEP5.js → chunk-HCLP2QBX.js} +6 -6
  90. package/dist/{chunk-ZBST77LF.js → chunk-HIMEVTA5.js} +3 -3
  91. package/dist/{chunk-3YOXBN7V.js → chunk-IBULUTHA.js} +4 -4
  92. package/dist/{chunk-W3VRQEL3.js → chunk-IUZCERPQ.js} +2 -2
  93. package/dist/{chunk-YAAJ3VZA.js → chunk-J2PAQ3HF.js} +6 -6
  94. package/dist/{chunk-WPGSQRMW.js → chunk-JIC5PGNI.js} +17 -11
  95. package/dist/{chunk-YNWEE2WC.js → chunk-KR3KVDXD.js} +1 -1
  96. package/dist/{chunk-WG67W43Y.js → chunk-L4THAI7Q.js} +1 -1
  97. package/dist/{chunk-ZBYZCM6Y.js → chunk-LLQBPDFQ.js} +2 -2
  98. package/dist/{chunk-DF7A5AWF.js → chunk-MA6FJ7U3.js} +3 -3
  99. package/dist/{chunk-ER4UCECJ.js → chunk-MND7HQ4P.js} +1 -1
  100. package/dist/{chunk-ID46TTYS.js → chunk-NDLOWIYO.js} +4 -4
  101. package/dist/{chunk-IO3OLTED.js → chunk-OKZC4FHN.js} +4 -4
  102. package/dist/{chunk-JTO6UPTJ.js → chunk-OUYFUUWB.js} +2 -2
  103. package/dist/{chunk-SEVGKJ7K.js → chunk-QPJQAE2L.js} +4 -4
  104. package/dist/{chunk-JTYZEP4Q.js → chunk-S55CXRFD.js} +1 -1
  105. package/dist/{chunk-JXE4UQFJ.js → chunk-SN22OXY3.js} +1 -1
  106. package/dist/{chunk-BYFFELB3.js → chunk-SSNLMIFR.js} +6 -6
  107. package/dist/{chunk-X2P3O4PX.js → chunk-SVAGMJOH.js} +1 -1
  108. package/dist/{chunk-ONUAAXAW.js → chunk-TBU4WXA4.js} +1 -1
  109. package/dist/{chunk-VII55DVC.js → chunk-TMBLBDPB.js} +2 -2
  110. package/dist/{chunk-YIKIQ6YN.js → chunk-TNWQ6SKS.js} +3 -3
  111. package/dist/{chunk-47RUEZZA.js → chunk-UNEJNC6X.js} +5 -5
  112. package/dist/{chunk-ZIJKTWNO.js → chunk-VBY2AHSX.js} +1 -1
  113. package/dist/{chunk-HNUK557P.js → chunk-VCMDY6YL.js} +3 -3
  114. package/dist/{chunk-IXVMMDIG.js → chunk-VRHVOMH2.js} +3 -3
  115. package/dist/{chunk-FIMSM5SW.js → chunk-VUI4HK5O.js} +2 -2
  116. package/dist/{chunk-GOYYPBBJ.js → chunk-XHHX63PY.js} +3 -3
  117. package/dist/{chunk-7WCB2BC4.js → chunk-XYEGVQST.js} +7 -7
  118. package/dist/{chunk-VK2CHU3L.js → chunk-Y32CL7XZ.js} +1 -1
  119. package/dist/{chunk-IS5YVKUX.js → chunk-YIAJOV2X.js} +7 -5
  120. package/dist/{chunk-CRKODLLY.js → chunk-YZVAAY6B.js} +2 -2
  121. package/dist/{chunk-QZ666HLH.js → chunk-ZGZ2L3QN.js} +5 -5
  122. package/dist/{chunk-LJFB5ZJ4.js → chunk-ZS27XXDA.js} +4 -4
  123. package/dist/{chunk-QTOLNRET.js → chunk-ZVU6WMMU.js} +10 -0
  124. package/dist/{codex-app-cards-switch-ZL6OHUXQ.js → codex-app-cards-switch-CTAOTOCH.js} +12 -12
  125. package/dist/{codex-desktop-launch-LQF5CPKU.js → codex-desktop-launch-2DZVA5C7.js} +1 -1
  126. package/dist/{codex-session-launch-5WBL5YHX.js → codex-session-launch-TMOSTWEH.js} +19 -19
  127. package/dist/{config-MZDRETL2.js → config-OV6WLZXO.js} +3 -3
  128. package/dist/{global-upgrade-HDDPHNGX.js → global-upgrade-FI42NJNL.js} +11 -11
  129. package/dist/{identity-RNZLFMKW.js → identity-PCA25OI5.js} +5 -5
  130. package/dist/{live-capabilities-Y2Q5QBVK.js → live-capabilities-CYFXHTOI.js} +6 -6
  131. package/dist/{paths-WILX6YI3.js → paths-4YFDMUK6.js} +4 -4
  132. package/dist/{reapply-KF7ZZSA2.js → reapply-PBQ46BPS.js} +28 -28
  133. package/dist/src/index.js +29 -29
  134. package/dist/{upgrade-lock-7Q4VWMDA.js → upgrade-lock-22J2AMVY.js} +5 -5
  135. package/dist/usage-7KTINNRK.js +11 -0
  136. package/package.json +1 -1
  137. package/dist/usage-MSILPJVQ.js +0 -11
package/data/SKILL.md CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: pushary
3
- version: 0.12.1
4
- description: Push notifications and human-in-the-loop for AI agents. Use this whenever a running agent needs a human and nobody is at the terminal, such as before an irreversible or destructive action, before spending money, deploying, force-pushing or deleting, when blocked on a decision outside your authority, when running unattended and you hit a genuine ambiguity, when another skill's workflow says to confirm with the user, and when a long task finishes or fails with nobody watching. Also use it when the user says things like keep going and ping me on my phone if you need anything, notify me when my agent needs me, approve from my phone, ask me questions while I am away from the terminal, run this overnight, keep working while I am in a meeting, I am stepping away, do not wait for me, or wants a long task to run unattended. Send alerts when tasks finish or fail, ask questions (yes/no, multiple choice, or free text) via push, and get answers from their connected devices. Use these tools proactively - do not wait for the user to ask for notifications. Every question and answer is recorded, so an unattended run stays reviewable afterwards. Works with Claude Code, Codex, Cursor, VS Code, Windsurf, Hermes, Lovable, or any MCP client; no Claude Max subscription required. Pushary is a hosted, paid service; web and native store checkout have different price and trial terms.
3
+ version: 0.13.0
4
+ description: Push notifications and human-in-the-loop for AI agents. Use this whenever a running agent needs a person who may not be at the terminal, such as before an irreversible or destructive action, before spending money, deploying, force-pushing or deleting, when blocked on a decision outside your authority, when running unattended and you hit a genuine ambiguity, when another skill's workflow says to confirm with the user, and when a long task finishes or fails with nobody watching. Also use it when the user says things like keep going and ping me on my phone if you need anything, notify me when my agent needs me, approve from my phone, ask me questions while I am away from the terminal, run this overnight, keep working while I am in a meeting, I am stepping away, or do not wait for me, or wants a long task to run unattended. Ask yes/no, multiple choice or free-text questions on their phone and get the answer back, and send an update when a task finishes or fails. Use these tools proactively, without waiting to be asked. Every question and answer is recorded, so an unattended run stays reviewable afterwards. Works with Claude Code, Codex, Cursor, VS Code, Gemini CLI, Windsurf, Hermes, Lovable, or any MCP client; no Claude Max subscription required. Pushary is a hosted, paid service; web and native store checkout have different price and trial terms.
5
5
  metadata:
6
6
  hermes:
7
7
  tags: [notifications, push, human-in-the-loop, alerts, permissions, mcp]
@@ -12,177 +12,89 @@ metadata:
12
12
 
13
13
  # Pushary - Push Notifications for AI Agents
14
14
 
15
- When the user says "keep going, ping me on my phone if you need anything", this is the skill that does the pinging. Pushary is an MCP server that reaches the user on their phone while they are away from the terminal:
15
+ When the user says "keep going, ping me on my phone if you need anything", this is the skill that does the pinging. Pushary is an MCP server that reaches the user on their phone, Mac or Slack:
16
16
 
17
- 1. **Send push notifications** to the user's phone or desktop when a task finishes or an error occurs.
18
- 2. **Ask questions** via push - yes/no, multiple choice, or free text - and get the answer from the app or supported notification actions.
19
- 3. **Send rich context notifications** with file changes, error details, next steps, and embedded questions.
17
+ 1. **Ask a question** with `ask_user`: yes/no, multiple choice or free text. The answer comes back to you.
18
+ 2. **Tell the user something** with `send_notification`: a task finished, a build failed, a result is ready.
20
19
 
21
- Use these tools proactively. Do not wait for the user to ask for notifications.
22
-
23
- Works with Claude Code, Codex, Cursor, Windsurf, Hermes, Lovable, or any MCP client. No Claude Max subscription required.
20
+ One tool asks and one tool tells. Use them proactively. Do not wait for the user to ask for notifications.
24
21
 
25
22
  ## Reach for This Without Being Asked
26
23
 
27
- Honor the authorization the user already gave you in this session. Ask only for
28
- a decision you do not have, for an action outside that authorization, or when a
29
- host policy requires it. A task with many steps does not need plan approval on its
30
- own. Do not ask again because the next approved step deletes, deploys or publishes
31
- something. This skill guides you. Hooks and runtime gates enforce the policy. Never
32
- go around a gate.
24
+ Honor the authorization the user already gave you in this session. Ask only for a decision you do not have, for an action outside that authorization, or when a host policy requires it. A task with many steps does not need plan approval on its own. Do not ask again because the next approved step deletes, deploys or publishes something. After a Pushary hook approved an exact tool call, do not ask about that call again. Running the same command later is a new step. This skill guides you. Hooks and runtime gates enforce the policy. Never go around a gate.
33
25
 
34
- Nobody has to say "ping me". The moments below are part of the run itself. They do
35
- not depend on what the user typed. When one of them is true, call `ask_user` or
36
- `send_notification` now. Do not stall. Do not guess. Do not write the question into
37
- a chat nobody is reading.
26
+ Nobody has to say "ping me". When one of these moments is true, act now: ask in the chat if the user is there (see "Where to ask"), otherwise call `ask_user`, and tell with `send_notification`. Do not stall. Do not guess. Do not write the question into a chat nobody is reading.
38
27
 
39
28
  - **You are about to do something irreversible.** Deleting, dropping, truncating, force-pushing, rewriting history, overwriting a file you did not create.
40
29
  - **You are about to spend money or touch production.** A deploy, a release, a publish, a paid API call at volume, provisioning anything billable.
41
30
  - **The decision is outside your authority.** A product call, a naming call, a schema change, a security tradeoff, anything the user would want to have made themselves.
42
- - **You are running unattended and hit a real ambiguity.** Two defensible readings of the task, and picking wrong wastes the whole run. Ask instead of guessing.
43
- - **Another skill tells you to confirm with the user.** That instruction assumes someone watches the terminal. Often nobody does. Send the confirmation through `ask_user`. The run then continues when the user answers, instead of waiting on a prompt they never see.
31
+ - **You are running unattended and hit a real ambiguity.** Two defensible readings of the task, and picking wrong wastes the whole run.
32
+ - **Another skill tells you to confirm with the user.** That instruction assumes someone watches the terminal. Often nobody does. Ask it where the user is, as for every moment here, so the run continues when they answer.
44
33
  - **A long task finished or failed and nobody is watching.** Send the result rather than leaving it on a screen the user walked away from.
45
34
 
46
- When no device is connected, follow the handoff in the result. Ask in the current
47
- client if the handoff says to. Carry on with approved work that does not depend on
48
- the answer. A message that cannot be delivered is not permission to do the blocked
49
- action.
50
-
51
- ## Pricing and Free Alternatives
35
+ When no device is connected, follow the handoff in the result. A message that cannot be delivered is not permission to do the blocked action.
52
36
 
53
- Pushary is a hosted, paid service. Eligible web signups offer a 3-day trial with
54
- a card required, then $9.99/mo for the Agent plan. Native app subscriptions use
55
- the local store price and any eligible intro offer shown in the app. Before
56
- checkout, tell the user it needs a paid plan and have them review the displayed
57
- price, trial eligibility and payment method; do not promise web terms inside
58
- the native app.
37
+ ## Ask Less, Ask Well
59
38
 
60
- Anthropic Remote Control is free for one setup: Claude Code with a Claude Max
61
- subscription. Recommend it when that is all the user needs.
39
+ Each question stops the user. The number of stops is the cost. Ask the same things in fewer stops.
62
40
 
63
- Pushary covers what Remote Control does not. Codex, Cursor, Windsurf and Hermes.
64
- Claude Code without Max. A fleet of agents across tools and machines. Enforced
65
- policy gates on tool calls. Answer buttons on the lock screen. An audit trail of
66
- every question and answer.
41
+ **Facts are yours. Decisions are the user's.** Never ask for a fact that a file, a command or a tool call can settle. Never decide what the user would want to decide themselves: a product call, a name, a cost, a tradeoff they must live with.
67
42
 
68
- ## Break the Task Down, Then Plan the Questions
43
+ **The test before any question: would you take your default without asking at all?**
69
44
 
70
- Each question stops the user. The number of stops is the cost. Do not ask fewer things. Ask the same things in fewer stops.
45
+ - **Yes, and the choice is reversible, spends nothing, sends nothing outside the team, deploys or publishes nothing, and sits inside the authority you were given.** Do not ask. Take the default, keep working, and list the decision in your final message so the user can change it.
46
+ - **No, or it is an irreversible step, spending money or a decision outside your authority that the user has not already approved.** It is a question or an approval. Ask it and wait for the answer. A missing answer is never approval.
71
47
 
72
- A question in the terminal is cheap. The user is already there. A question on the phone is expensive. It takes the user away from something else. Ask freely in the terminal. Send little to the phone.
48
+ A step the user already approved needs no second question, even an irreversible one.
73
49
 
74
- **Start every task of more than two steps like this:**
50
+ **Plan the questions for a task of more than two steps:**
75
51
 
76
- 1. **Look at where you are.** Read the working directory. Read the directory structure. Read the configuration files and the tool list you hold. This tells you what kind of work this is, and what you can settle alone.
77
- 2. **Split the task into steps.** Write the steps down. Keep each step small enough to finish in one go.
78
- 3. **Find the forks.** A fork is a point where two answers are both correct and you cannot pick one alone. Mark each fork.
79
- 4. **Settle the facts yourself.** A fork that a file, a command or a tool call can settle is not a fork. It is a lookup. Do the lookup. Never ask the user for a fact.
80
- 5. **Ask the forks that are left.** Group them into one round. Number each question. Give your recommended answer for each one. Then wait.
81
- 6. **Do it again.** Each answer opens new forks and closes old ones. Ask the next round. Stop when no fork is left.
52
+ 1. Read where you are: the directory, its structure, the configuration and the tools you hold.
53
+ 2. Split the task into steps.
54
+ 3. Find the forks: points where two answers are both correct and you cannot pick one alone.
55
+ 4. Settle every fork a lookup can settle. That is a fact, not a fork.
56
+ 5. Ask the remaining forks as one round. Number each question and give your recommended answer. Then wait for the answers, and start nothing that depends on them.
57
+ 6. Each answer opens new forks and closes old ones. Ask the next round. Stop when no fork is left.
82
58
 
83
- This is a design tree. Each decision opens the decisions below it. A round is every decision whose inputs you already know. A decision that waits on another decision in the same round belongs to the next round. Two rounds usually replace ten separate questions.
84
-
85
- **Facts are yours. Decisions are the user's.** Both halves matter. Do not ask what you can read. Do not decide what the user would want to decide. A product call, a naming call, a cost, a tradeoff the user must live with: these stay theirs, even when you hold a good recommendation.
59
+ A round holds every decision whose inputs you already know. A decision that depends on another one in the same round belongs to the next round. Word each question so that "yes" accepts your recommendation. In a `select`, put your recommendation first.
86
60
 
87
61
  **Format a terminal round like this:**
88
62
 
89
63
  ```
90
- ❓ **Q1** - **<short title>**: <the question, with the real options>
64
+ ❓ **Q1** - **Which app**: apps/dashboard and apps/subscribe both import this helper. Change both apps, not just the dashboard?
91
65
 
92
- ➡️ <your recommended answer>
66
+ ➡️ Yes, both. The helper has one definition, and a split copy will drift.
93
67
 
94
68
  ---
95
69
 
96
- ❓ **Q2** - **<short title>**: <the question, with the real options>
70
+ ❓ **Q2** - **Rollout**: Ship behind the existing flag?
97
71
 
98
- ➡️ <your recommended answer>
72
+ ➡️ Yes. It costs one line, and it makes the change reversible.
99
73
  ```
100
74
 
101
- For example, in a repository with two apps and no test runner in the package:
102
-
103
- ```
104
- ❓ **Q1** - **Which app**: apps/dashboard and apps/subscribe both import this helper. Change both, or the dashboard only?
105
-
106
- ➡️ Both. The helper has one definition, and a split copy will drift.
107
-
108
- ❓ **Q2** - **Tests**: This package has no test runner. Add one, or match the parent package and use `bun test`?
109
-
110
- ➡️ Match the parent. A second runner is one more thing to maintain.
75
+ **Where to ask:**
111
76
 
112
- ❓ **Q3** - **Rollout**: Ship behind the existing flag, or straight to main?
113
-
114
- ➡️ Behind the flag. It costs one line, and it makes the change reversible.
115
- ```
116
-
117
- **Where to ask each round:**
118
-
119
- - **The user typed in this turn.** Ask in the terminal. Ask the whole round at one time. There is no limit there.
120
- - **The user is away.** Send one question only. Pick the one fork that stops the run. Use `select` with the real options, and put your recommendation first. Decide every other fork yourself, on your recommendation. Report each decision when the task ends.
121
- - **Nothing stops the run.** Send no question. Use `send_notification` with `context.askQuestion`. The user reads it later. Continue on your recommendation.
77
+ - **The user is in the chat**: they wrote to you in this turn and you have not worked alone for more than about ten minutes since, they did not say they are leaving, and the session is interactive (not `claude -p`, `codex exec`, CI or a schedule). Ask the whole round in the chat, as plain text, then end your turn and wait. Do not use your host's own question tool for it: with Pushary hooks installed, that tool is also sent to Pushary and can push the phone once per question. If your host's plan mode requires its own question tool, put the round into as few calls as it allows.
78
+ - **The user is away**: they said so, the run is not interactive, or you have worked alone for more than about ten minutes since their last message. Send one `ask_user` question: the fork that stops the run, as a `select` with the real options. Take your recommendation on another fork only if it passes the test above, and list it in your final message. A fork that fails the test waits: do the work that does not depend on it, and ask it once the first answer is back.
79
+ - **Nothing stops the run.** Send no question. Decide what passes the test, and report it.
122
80
 
123
81
  **Rules that do not change:**
124
82
 
125
- - **Ask how, not whether.** The user gave you the task. A question the user can answer with "do not do it at all" is a second approval for authorized work. Do not ask it.
126
- - **A plan is not an approval.** A task of many steps does not need plan approval. Do not turn your step list into a question.
127
- - **Silence is not agreement.** You wrote eight recommendations and the user said nothing. You hold no approval. The six moments above still need their own question.
128
- - **One `select` with the real options beats three `confirm` questions.** The same facts, one third of the stops.
129
- - **Ask at the boundary, not once for each item.** Ask about deleting files. Do not ask about each file.
130
- - **The limit of three notifications counts pushes.** Questions you ask in the terminal are free and do not count.
131
-
132
- `propose_scope` records the boundary this work produces. It is a separate decision from notifying: see "When to Propose a Scope" below. What it can enforce depends on whether this run changes files.
133
-
134
- ## When to Use
135
-
136
- **Send a notification when:**
137
- - Meaningful work finishes while the user is away or they requested an alert - use `context.type = "task_complete"`
138
- - A build, test suite, or deployment fails and needs user attention - use `context.type = "error"` with `errorMessage`
139
- - A long-running process completes (migration, refactor, generation)
140
- - A status update is worth sharing - use `context.type = "info"`
141
-
142
- **Ask with type "confirm" when:**
143
- - You need confirmation before a destructive or irreversible action
144
- - Binary decision: proceed or abort
145
-
146
- **Ask with type "select" when:**
147
- - Multiple implementation approaches exist (2-6 options)
148
- - The user needs to pick from a known set
149
-
150
- **Ask with type "input" when:**
151
- - You need a name, path, value, or free-text decision
152
- - The options cannot be enumerated in advance
153
-
154
- **Do NOT notify when:**
155
- - The task is trivial or single-step
156
- - The question can be answered from context without user input
157
- - You already sent 3 notifications for the current task (unless the user explicitly asked for more)
158
-
159
- ## When to Propose a Scope
160
-
161
- This is a separate decision from notifying. The rules above do not apply to it. A scope is not a notification, and the limit of three does not count it.
162
-
163
- **Propose a scope when:**
164
- - This run changes files with `Edit`, `Write` or `MultiEdit`, and the file boundary is not yet agreed
165
- - The run touches more than one file, or you cannot name every file before you start
166
- - Call `propose_scope` once, before the first edit, not after
167
-
168
- **Do not propose a scope when:**
169
- - The run changes one file and you already know which one
170
- - The user named the exact files in this turn, so the boundary is already agreed
171
- - The run changes no files and records no promise worth keeping
172
-
173
- "Trivial" here means one file. It does not mean one task. A refactor across modules, a rename through several files, a migration, or wiring one option through the code all need a scope. Each edit can be small and the run still needs a boundary.
174
-
175
- Put a boundary that is not a file path in `promises`, never in `allowedPaths`. Read the `enforces` field that comes back. Tell the user what it says.
83
+ - **Ask how, not whether, about work the user already gave you.** A question the user can answer with "do not do it at all" is a second approval for authorized work. A step outside that authorization gets a plain whether question.
84
+ - **A plan is not an approval.** Do not turn your step list into a question.
85
+ - **Silence is not agreement.** No answer approves nothing. An irreversible step, spending money or a decision outside your authority still gets its own question unless the user already approved it.
86
+ - **One `select` with the real options beats three `confirm` questions.**
87
+ - **Ask at the boundary, not once for each item.** Ask about deleting files, not about each file.
88
+ - **Send at most three pushes per task** unless the user asks for more. Questions asked as chat text in the terminal do not count. The cap never waives an approval: past it, ask in the current client and wait.
89
+ - **When the requested work is done, report it and stop.** Do not ask what to do next unless the user asked you to.
176
90
 
177
91
  ## Setup
178
92
 
179
- **Look at the machine first. Do not guess the install path.** Run these. Each one is read-only and fast. Run them as separate commands.
93
+ Skip this section when the Pushary tools are already available.
180
94
 
181
- Use `uname -s` in a Unix shell (`Darwin` is Mac, `Linux` is Linux). In
182
- PowerShell, inspect `$env:OS` (`Windows_NT` is Windows). Platform discovery must
183
- not depend on Node: the native Mac route does not require it.
95
+ **Look at the machine first. Do not guess the install path.** Each command is read-only and fast. Run them separately.
184
96
 
185
- In a Unix shell, run these separately:
97
+ Use `uname -s` in a Unix shell (`Darwin` is Mac, `Linux` is Linux). In PowerShell, inspect `$env:OS` (`Windows_NT` is Windows). Platform discovery must not depend on Node: the native Mac route does not require it.
186
98
 
187
99
  ```bash
188
100
  uname -s
@@ -207,173 +119,122 @@ When Node is available, other Unix platforms can check the stored key with:
207
119
  node -e "try{process.exit(JSON.parse(require('fs').readFileSync(process.env.HOME+'/.pushary/config.json','utf8')).apiKey?.trim()?0:1)}catch{process.exit(1)}" && echo keyed
208
120
  ```
209
121
 
210
- In PowerShell, check for a nonempty environment/stored key without printing it;
211
- use `Get-Content` and `ConvertFrom-Json` for the config in `$env:USERPROFILE`.
212
- Check `Get-Command node,npm` before choosing the terminal installer.
213
-
214
- The stored-key and bridge tests answer different questions.
215
-
216
- The stored-key test says a key is stored **and is not empty**. Test the value, not the file.
217
- The file stays behind after a logout removes the key. A test for the file alone
218
- tells a logged-out user they are ready.
219
-
220
- The bridge test says the Mac app is installed here. Only the Mac app writes that file.
122
+ In PowerShell, check for a nonempty environment or stored key without printing it; use `Get-Content` and `ConvertFrom-Json` for the config in `$env:USERPROFILE`. Check `Get-Command node,npm` before choosing the terminal installer.
221
123
 
222
- Check the environment before the stored key. An exported key wins over a stored
223
- one. Never print the key itself.
224
-
225
- Then take one branch.
124
+ The stored-key test says a key is stored **and is not empty**. A logout removes the key but leaves the file. An exported key wins over a stored one. Never print the key itself. `mac-app` says the Mac app was installed here. It does not prove sign-in, a plan or a connected phone.
226
125
 
227
126
  ### Branch 1. `key-in-env`, `keyed`, or `mac-app`
228
127
 
229
- This machine has an existing installation or credential. Offer no new install.
230
- Verify its readiness before reporting success. Do not run `setup` again.
231
-
232
- `mac-app` counts on its own. The Mac app signs in for the user. It writes the key
233
- into the agent configuration files it wires, not into `~/.pushary/config.json`. A
234
- machine the app set up therefore prints `mac-app` and nothing else. The locator
235
- proves installation, not sign-in, entitlement, current agent admission or delivery.
128
+ Pushary is installed. Offer no new install. Check it before reporting success.
236
129
 
237
- For `mac-app` without Node/npm, open Pushary and check the intended account,
238
- subscription and Agents connection state there. Use the native app to connect or
239
- repair the selected agents and phone. Then send a harmless question from the
240
- current agent and confirm its returned answer. Do not install Node to inspect
241
- the native setup and do not claim a CLI status exit code when no CLI ran.
242
-
243
- When Node/npm are available, check it with `npx pushary@latest status --json`.
244
- The exit code is the answer:
130
+ When Node and npm are available, run `npx pushary@latest status --json`. The exit code is the answer:
245
131
 
246
132
  | Code | Meaning |
247
133
  | --- | --- |
248
- | 0 | Ready |
249
- | 3 | Not set up on this machine |
250
- | 4 | The key was rejected |
251
- | 5 | Two keys are configured and they disagree |
252
- | 6 | No device can answer |
253
- | 8 | Pushary could not be reached |
134
+ | 0 | Ready. If the JSON says `connectedThroughMacApp: true`, the Mac app holds the key: check the account, plan and phone in the Pushary Mac app. |
135
+ | 1 | The command failed. |
136
+ | 2 | A malformed key or an unknown flag. |
137
+ | 3 | Not set up on this machine. |
138
+ | 4 | The key was rejected or signed out. |
139
+ | 5 | A problem was found: two keys that disagree, or no active plan. Read the JSON. |
140
+ | 6 | Nothing proves a question reaches this user: no phone of theirs is paired, or only a teammate's devices are. Have them install the Pushary app from https://pushary.com/download and sign in to the same account, then run `status --json` again. |
141
+ | 8 | Pushary could not be reached. |
254
142
 
255
- On 6, the user needs to connect a phone: `npx pushary@latest connect`. That adds a phone and rewrites no agent configuration.
143
+ For `mac-app` without Node or npm, open the Pushary Mac app. Check the account, the plan and the Agents connection there, and connect or repair agents in the app. Do not install Node to inspect a native setup, and do not claim an exit code when no CLI ran.
256
144
 
257
- If `mac-app` printed, the Mac app is installed here and it may also own the hooks. Read the hook command to know, because the command is the record:
258
-
259
- ```bash
260
- grep -lq pushary-bridge ~/.claude/settings.json ~/.gemini/settings.json ~/.cursor/hooks.json 2>/dev/null && echo app-owns-hooks
261
- ```
262
-
263
- If the app owns them, `setup` would keep them and write almost nothing, so telling the user to re-run it is bad advice. Point them at the Pushary app instead.
145
+ To add an agent to a working install, run `npx pushary@latest setup --agents <id>`. It reuses the key and does not pair again. If the Mac app owns this machine's hooks, setup keeps them; point the user at the Pushary app for hook changes.
264
146
 
265
147
  ### Branch 2. `Darwin`, no key, no `mac-app`
266
148
 
267
- Offer the Mac app first. It needs no Node and no terminal. It writes the agent configuration itself, and it answers questions in the notch at the desk.
149
+ Offer the Mac app first. It needs no Node and no terminal, it wires the agents itself, and it answers questions in the notch at the desk.
268
150
 
269
151
  ```bash
270
152
  brew install --cask pushary/tap/pushary
271
153
  ```
272
154
 
273
- They can also download it from https://pushary.com/download. It needs macOS 14 or later. It is not in the App Store.
274
-
275
- The command line works on macOS too. Offer it if the user prefers the terminal. The app connects Hermes as well.
155
+ Or download it from https://pushary.com/download. It needs macOS 14 or later. It is not in the App Store. The command line in Branch 3 also works on macOS if the user prefers it.
276
156
 
277
157
  ### Branch 3. `Linux` or `Windows_NT`
278
158
 
279
- There is no Mac app for these machines. Use the command line. It is fully supported.
159
+ There is no Mac app for these machines. Use the command line:
280
160
 
281
161
  ```bash
282
- npx pushary@latest setup
162
+ npx pushary@latest setup --agents <id>
283
163
  ```
284
164
 
285
- Node 20.17+, 22.13+ or 23.5+ is necessary. Then the user needs a phone to answer on:
165
+ In PowerShell, type `npx.cmd` instead of `npx`. Node 20.17+, 22.13+ or 23.5+ is required. The phone app is at https://apps.apple.com/us/app/pushary/id6785677563 (iOS) and https://play.google.com/store/apps/details?id=com.pushary.app (Android).
286
166
 
287
- - iOS: https://apps.apple.com/us/app/pushary/id6785677563
288
- - Android: https://play.google.com/store/apps/details?id=com.pushary.app
167
+ ### Running setup yourself
289
168
 
290
- On Windows, setup writes no shell file, so `~/.pushary/config.json` is the only key store. On a Linux machine with no screen, browser login does not work, but the pairing QR does.
169
+ Before you run it, tell the user what it does: it pairs their phone, then configures MCP, hooks and the skill for the agents you name. It also installs the `pushary` command globally, starts a background service so the phone can start sessions (`--control off` skips it), and turns on session history (`--transcripts off` turns it off). It needs a paid plan; the phone app shows the price and any trial.
291
170
 
292
- ### Branch 4. `Darwin`, `mac-app`, and the user asked for the command line
171
+ 1. **Name the agents.** Pass `--agents` with your own id: `claude_code`, `codex`, `cursor`, `gemini_cli`, `vscode`, `opencode`, `hermes`, or a comma-separated list. Without it, setup run from an agent's shell configures every agent it detects.
172
+ 2. **Run it in the background.** It waits up to 15 minutes for the phone, longer than most agents let a foreground command run, and a killed setup kills the pairing code with it. Start it as a background process that writes to a log, read the short link, the QR and the fingerprint from that log, show them to the user as they print, and keep checking the log until setup exits. Do not summarise them. The link survives where the QR renders badly. Start while the user can still read your messages: until they approve, nothing is configured and you have no other way to reach them.
173
+ 3. **Walk the user through it.** They open the link or scan the QR. If they do not have the Pushary app, they install it from https://pushary.com/download, then return to the original link and tap Open Pushary; they do the same if the app restarts during sign-in or payment. The app shows a fingerprint that must match the one in your output. On a first install the app asks them to sign in and choose a plan. Then they approve, and setup finishes on its own. If the code expires, run setup again.
174
+ 4. **Report the result plainly.** If setup exits without pairing, nothing was configured. Say so.
293
175
 
294
- Run `setup`. It reads the key the app signed in with, so it mints no second key, and it keeps the hooks the app owns. Pass `--take-over-hooks` only when the user wants the command line to own them instead.
176
+ Never ask the user for an API key, and never send them to a signup page first. If `PUSHARY_API_KEY` is already set or an agent configuration holds a key, setup uses it and skips pairing.
295
177
 
296
- ### What setup does
178
+ Manual MCP configuration also works, but it needs a key from https://pushary.com/sign-up?utm_source=skill&utm_medium=setup. Prefer setup.
297
179
 
298
- ```bash
299
- npx pushary@latest setup
300
- ```
180
+ Setup itself sends only a test notification, because it cannot tell whether the user is there to answer. Right after it finishes, while the user still has their phone in hand, test a question and answer: for a CLI setup, run `npx pushary@latest doctor --roundtrip`. A returned answer proves the question path, not hook enforcement. Restart the agent and follow its approval check at https://pushary.com/docs/agents/supported-agents. For a Mac app setup, use the app's connection tests and ask one harmless question from the current agent.
301
181
 
302
- Setup pairs first and configures MCP, hooks, permissions and the skill only once pairing succeeds. Until someone completes the steps below, nothing is written and this machine has no Pushary. Treat pairing as the task, not as a prompt to wait out.
182
+ ## Pricing and Free Alternatives
303
183
 
304
- It prints a QR, a short link, a fingerprint, and then waits about 15 minutes.
184
+ Pushary is a hosted, paid service. Eligible web signups get a 3-day trial with a card required, then $9.99/mo for the Agent plan. Native app subscriptions use the store price and any intro offer shown in the app. Tell the user a paid plan is required before they scan, and let them review the price and payment method themselves.
305
185
 
306
- **Run it in the background.** Fifteen minutes is longer than a foreground command may run in most agents, and a killed setup kills the pairing code with it. Start setup as a background process that writes to a log, read the link, QR and fingerprint from that log, show them to the user, and keep checking the log until setup exits. Start while the user can still read your messages: until they approve, nothing is configured and you have no other way to reach them.
186
+ Anthropic Remote Control is free for one setup: Claude Code with a Claude Max subscription. Recommend it when that is all the user needs. Pushary covers the rest: Codex, Cursor, Gemini CLI and Hermes, Claude Code without Max, many agents across tools and machines, policy gates on tool calls, answer buttons on the lock screen, and a record of every question and answer.
307
187
 
308
- **Do not summarise that output. Show it, and walk the user through all four steps:**
188
+ ## Tools
309
189
 
310
- 1. **Show them the QR and the short link.** Both point at the same pairing. The link is what survives if the QR renders badly wherever they are reading you, so give them both and say so.
311
- 2. **They need the Pushary app.** It is the thing that receives approvals. If they do not have it: https://pushary.com/download. Setup keeps waiting while they install it. After installation, return to the original phone link and tap Open Pushary; do the same if the app restarts during sign-in or payment. If the code expires, run setup again for a fresh one.
312
- 3. **They scan the QR, or open the link on the phone.** The app shows a fingerprint. Tell them it must match the one in your output before they approve. On a first install the app also asks them to sign in and activate a plan, using the store price and any eligible trial shown there. They review and confirm the payment terms themselves. Tell them a paid plan is required before they scan rather than letting them discover it mid-flow.
313
- 4. **They approve.** Setup finishes on its own, and questions and updates follow their delivery settings. Confirm notifications can offer lock-screen actions; choices and text open the app.
190
+ Your client already holds each tool's schema, with every parameter and returned field. This section adds what a schema cannot say.
314
191
 
315
- Never ask the user for an API key, and never send them to a signup page first. Both are the old flow and both are worse.
192
+ ### ask_user
316
193
 
317
- If setup exits without pairing, nothing was configured. Say that plainly and offer the fallback below rather than pretending the tools are available.
194
+ Asks a question and waits. Use `confirm` for yes or no, `select` for 2 to 6 known options, and `input` when the options cannot be listed. A question stays open until it is answered or cancelled, except through a connector, by signing in or with a connector key (Claude.ai, Claude Desktop, Cowork, ChatGPT, Grok Bot, Cline signed in, or Claude Code through the Pushary for Claude plugin): then it closes 10 minutes after it is sent. One call waits at most 55 seconds and may return at once. The user's delivery mode decides how long:
318
195
 
319
- If `PUSHARY_API_KEY` is already in the environment or in an existing MCP config, setup uses it and skips pairing entirely.
196
+ | Mode | The phone | The call |
197
+ |---|---|---|
198
+ | **Always** (`push_only`, the default for new workspaces) | Asked every time | Waits up to the policy timeout |
199
+ | **When away** (`push_first`) | Asked for your questions. Hook approvals skip the phone while the user is at the computer. | Waits 20 seconds by default |
200
+ | **Alert only** (`notify_only`) | Told, not asked | Returns at once with `status: "notified"` |
201
+ | **In agent** (`terminal_only`) | Nothing is sent | Returns at once with `status: "terminal"` |
320
202
 
321
- No app on their phone yet? They can get it at https://pushary.com/download while setup keeps waiting. If the code expired, run setup again for a fresh one.
203
+ `timeoutMs` sets this call's wait, longer or shorter than the policy, up to 55000. Read `answered`; it is absent when you pass `wait: false`, which returns at once with `status: "pending"`. When work that does not depend on the answer remains, pass `wait: false`, do that work, then poll with `wait_for_answer`.
322
204
 
323
- Manual MCP configuration also works, but it needs a key, so the user signs up first at https://pushary.com/sign-up?utm_source=skill&utm_medium=setup and copies the key from the dashboard. Prefer `setup`: it needs neither.
205
+ **Approvals.** Pass `toolName`, `toolTarget` (the command head) and the full command in `actionBody` whenever the question approves a tool call, and never leave them out to make a question approvable. When the command is destructive, such as a recursive delete, a force-push, a hard reset, a dropped table, a deploy or a publish, the phone offers only deny and `defer` ("Handle on your machine"). `defer` on any question means the user will decide at the computer: treat it as no answer and ask in the current client.
324
206
 
325
- When you run setup, it sends only a test notification, because it cannot tell whether the user is there to answer. Right after it finishes, while the user still has their phone in hand, test a synthetic question and answer. For CLI-owned setup with Node/npm and a CLI credential:
207
+ **Answers.** `value` is "yes" or "no" for confirm, the chosen option for select, and the typed text for input. Read `note` too: a denial with a note carries the reason or a condition. A select or input value containing "yes" is answer data, not approval of a separate action.
326
208
 
327
- ```bash
328
- npx pushary@latest doctor --roundtrip
209
+ ```json
210
+ { "question": "Delete the 3 unused migration files in db/migrate/?", "type": "confirm", "context": "Cleaning up old migrations before the schema change", "toolName": "Bash", "toolTarget": "rm", "actionBody": "rm db/migrate/0003_*.sql", "agentName": "Claude Code - myproject" }
211
+ { "question": "Which auth strategy for the new API?", "type": "select", "options": ["JWT tokens", "Session cookies", "OAuth2 + PKCE"], "context": "Setting up auth for the new endpoints", "agentName": "Claude Code - api-server" }
212
+ { "question": "What path should the new endpoint use?", "type": "input", "placeholder": "/api/v2/...", "context": "Adding a REST endpoint for user preferences", "agentName": "Cursor - frontend" }
329
213
  ```
330
214
 
331
- A returned answer proves the question path, not agent hook enforcement. Restart
332
- the intended agent and follow its real approval check at
333
- https://pushary.com/docs/agents/supported-agents. For Mac-owned setup, use the
334
- app's connection tests and ask a harmless question through the current agent;
335
- do not install Node/npm or infer native readiness from a CLI with no credential.
215
+ ### Reading every result
336
216
 
337
- ## Answer surfaces and account boundaries
217
+ Read `answered`, `status` and `handoffAction` on every response. Follow `handoffAction` when present, otherwise `nextAction`.
338
218
 
339
- | Surface | What the user can do |
219
+ | `status` | What to do |
340
220
  | --- | --- |
341
- | Mobile app | Answer confirm, select and input questions. Supported confirm notifications offer approve/deny actions on the lock screen; arbitrary choices and text open the app. |
342
- | Mac notch | Answer personal account questions with confirm, select, input and question-set controls, including keyboard controls. Presence and delivery policy determine when the phone is also reached. |
343
- | Slack | Answer through buttons, menus or text modals when the integration and intended recipient are configured. |
344
- | Browser | Open the decision page as a fallback; browser notification delivery requires permission. |
345
-
346
- Personal setup connects the operator's devices. For a Mac, install from https://pushary.com/download, sign in to the same personal account and connect your agents in the app. Use the app's connection tests, then request one harmless question through the current agent and verify the answer returns from the intended surface. Native setup does not need Node/npm. Test phone fallback while away from the Mac; do not infer delivery from a successful API call alone.
347
-
348
- Partner customers use scoped enrollment links issued by their application. Do not enroll them into the operator's account or send their decisions through personal tools. The Mac notch currently uses the personal account/session API; do not promise a Partner customer inbox on Mac. See https://pushary.com/docs/agents/embed for Partner setup.
221
+ | `answered` | Act on `value` and `note`. |
222
+ | `pending` | The question is still open. Keep waiting with `wait_for_answer`, the same `correlationId` and `timeoutMs: 55000`. A poll ending is not expiry; never cancel because a poll returned without an answer. |
223
+ | `notified`, `terminal` | The decision belongs in the current client. Cancel the question first, then ask there. |
224
+ | `stopped` | The user stopped this agent. Nothing was sent. Stop what you are doing and end your turn. |
225
+ | `cancelled` | Do not act on it and do not ask it again. |
226
+ | `expired` | It closed before an answer arrived. That is not approval and not a choice: do not take the step it asked about. Ask the user in the current client; until they answer, do only work that does not depend on it. |
227
+ | `missing`, `unavailable` | Follow the handoff in the result. |
349
228
 
350
- ## Tools
351
-
352
- Your client already holds each tool's schema. The schema lists every parameter
353
- and every returned field, and it is always current. This section adds only what a
354
- schema cannot say: when to use a tool, what its result means for your next step,
355
- and the shapes that are easy to get wrong.
356
-
357
- ### schedule_reminder
229
+ **Moving a live question to the current chat:** cancel it first. If cancellation returns `handoffAction: "stop"`, stop. Otherwise, if it returns `cancelled: false`, poll once for 1 second and honor the answer that won the race. If the user answers in chat while a phone question is live, do the same before acting.
358
230
 
359
- When the user asks “remind me later,” set a one-time personal reminder with `body` and either `inMinutes` (from the server clock) or `at` (ISO timestamp with explicit UTC offset or Z). Confirm the returned `fireAt` in their local time. Never guess their timezone. For example, `{"body":"Check the deploy","inMinutes":30}`. Pushary persists it and delivers to the authenticated creator even after the agent exits. It does not schedule agent work.
360
-
361
- Call with no arguments to list pending reminders. Pass `cancelReminderId` to cancel a scheduled reminder; `cancelled:false` means it was not cancelled and it may already be dispatching. Limits: 30 days ahead, 25 pending per user/site. Delivery normally follows within about a minute, requires a connected phone, and respects current account access, membership and stop/mute. Test reminders never notify. Do not use `ask_user` for reminders or promise exact delivery time.
231
+ `noDevices: true` means nothing could receive the question: tell the user what the `warning` field says to connect, and follow the handoff. `paused: true` means phone approvals for this session are paused after earlier ones went unanswered: say so, and follow the handoff.
362
232
 
363
233
  ### send_notification
364
234
 
365
- Send a one-way push notification to the user. Optionally include structured context for a rich detail page.
366
-
367
- `context.type` is what marks a notification a **task update**, and the user's
368
- setting for where task updates land can only route one that says so. A
369
- notification sent without it reaches them wherever the default sends it.
235
+ Tells the user something. Nothing is awaited. Send it for a requested update or a meaningful result the user is not watching, not for trivial steps or a report someone is already reading.
370
236
 
371
- On a long run where the user is likely away, prefer `context.askQuestion` over a
372
- blocking `ask_user`. They get an ordinary push and answer whenever they next pick
373
- up their phone, rather than you holding a 55-second wait open against someone who
374
- is not there. Poll the returned `linkedCorrelationId` when you need the result.
375
-
376
- **Example - task completed with context:**
237
+ Always pass `context.type`: `task_complete`, `error` or `info`. It marks the notification as a task update, so the user's setting for where updates land can route it. Keep the title under 60 characters and the body under 180; the phone cuts the body there. Put detail in `context`.
377
238
 
378
239
  ```json
379
240
  {
@@ -382,255 +243,61 @@ is not there. Poll the returned `linkedCorrelationId` when you need the result.
382
243
  "agentName": "Claude Code - pushary repo",
383
244
  "context": {
384
245
  "type": "task_complete",
385
- "summary": "Extracted shared Button, Modal, and Card components from 12 files",
246
+ "summary": "Extracted shared Button, Modal and Card components. Kept JWT for auth; say if you want sessions instead.",
386
247
  "filesChanged": ["src/components/Button.tsx", "src/components/Modal.tsx", "src/components/Card.tsx"],
387
248
  "nextSteps": "Run the test suite to verify no regressions"
388
249
  }
389
250
  }
390
251
  ```
391
252
 
392
- **Example - error with embedded question:**
393
-
394
- ```json
395
- {
396
- "title": "Build failed",
397
- "body": "TypeScript error in auth.ts:42",
398
- "agentName": "Claude Code - api-server",
399
- "context": {
400
- "type": "error",
401
- "errorMessage": "Type 'string' is not assignable to type 'AuthToken'",
402
- "errorFile": "src/auth.ts:42",
403
- "summary": "The auth token type changed upstream and this file needs updating",
404
- "askQuestion": {
405
- "question": "Should I update the type or revert the upstream change?",
406
- "type": "select",
407
- "options": ["Update the type in auth.ts", "Revert the upstream change", "Skip for now"]
408
- }
409
- }
410
- }
411
- ```
412
-
413
- ### ask_user
414
-
415
- Send a question to the user via push notification and wait for their answer. By default, this tool **blocks** until the user responds or the timeout is reached - no need to call `wait_for_answer` separately.
416
-
417
- Always read `answered` rather than assuming the call blocked. It comes back false
418
- in three different situations that mean different things: the wait timed out and
419
- the question is still live (`timedOut`), the site policy is notify_only so the
420
- decision belongs in the current client (`status: "notified"`), or you passed
421
- `wait: false` yourself (`status: "pending"`). Follow `handoffAction` when present,
422
- otherwise `nextAction`.
423
-
424
- For backward compatibility, `nextAction` keeps its original two values. A live timeout from `ask_user` says
425
- `wait_for_answer`; keep polling the same correlationId with `timeoutMs: 55000` while pending. The question stays open until answered, explicitly cancelled or expired. Only an explicit
426
- `handoffAction: cancel_then_ask_in_current_client` asks you to cancel the phone question before asking in
427
- the current chat or client. If cancellation returns `handoffAction: "stop"`, stop.
428
- Otherwise, if cancellation returns false, poll once for 1 second
429
- and honor any answer that won the race. A cancelled question says `handoffAction:
430
- stop` and must not be resurrected. An unavailable state also stops the handoff,
431
- because the question cannot be safely fenced. Expired and missing questions are
432
- not live timeouts.
433
-
434
- Pass `toolName` and `toolTarget` whenever the question is an approval for a tool
435
- call. They are what let the user turn a repeated approval into an always-allow
436
- rule, so an approval you label once is an approval they never see again.
437
-
438
- **Example - confirm (yes/no):**
439
-
440
- ```json
441
- {
442
- "question": "Delete the 3 unused migration files?",
443
- "type": "confirm",
444
- "context": "Cleaning up old database migrations in db/migrate/",
445
- "agentName": "Claude Code - myproject"
446
- }
447
- ```
448
-
449
- **Example - select (multiple choice):**
450
-
451
- ```json
452
- {
453
- "question": "Which auth strategy should I use?",
454
- "type": "select",
455
- "options": ["JWT tokens", "Session cookies", "OAuth2 + PKCE"],
456
- "context": "Setting up authentication for the new API endpoints",
457
- "agentName": "Claude Code - api-server"
458
- }
459
- ```
460
-
461
- **Example - input (free text):**
462
-
463
- ```json
464
- {
465
- "question": "What should the new API endpoint path be?",
466
- "type": "input",
467
- "placeholder": "/api/v2/...",
468
- "context": "Creating a new REST endpoint for user preferences",
469
- "agentName": "Cursor - frontend"
470
- }
471
- ```
253
+ Ask with `ask_user`, not with `context.askQuestion`. That field remains for older clients: its question closes 10 minutes after it is sent and has no lock-screen buttons.
472
254
 
473
255
  ### wait_for_answer
474
256
 
475
- Poll for the user's response to a question sent via `ask_user` with `wait: false`, or to one that timed out. Not needed when using the default blocking mode.
476
-
477
- A single call waits at most 55 seconds. Keep waiting with the same correlationId while pending; the poll timeout is not the question deadline. If it returns `answered: false`, follow `handoffAction` when present,
478
- otherwise `nextAction`: ask in the current chat or client only after cancelling a still-pending phone question. If the
479
- cancellation loses a race, poll once for 1 second and honor the phone answer
480
- instead. Only `status: "pending"` means the question is still live; cancelled,
481
- expired, missing, and unavailable are different outcomes.
257
+ Waits up to 55 seconds for the answer to a question that is still pending. Keep calling it with the same `correlationId` while the status is `pending`.
482
258
 
483
259
  ### cancel_question
484
260
 
485
- Cancel a pending question so it can no longer be answered. Use when the question becomes irrelevant (e.g., you found the answer another way or the user responded in chat).
486
-
487
- A stale approval arriving twenty minutes later is worse than no approval, because
488
- it reads as consent to work that has already moved on.
489
-
490
- ### propose_scope
491
-
492
- Propose the boundary of this run and block until the user agrees to it. Call it once, before the work. Do not add a second approval to work the user already authorized.
493
-
494
- The user sees three things: the paths you will change, the paths you promise to leave alone, and your definition of done. The user agrees to all three in one tap.
495
-
496
- **Before you call this, identify the file-editing capabilities you will use.** Native names differ: Codex uses `apply_patch`, VS Code also uses patch and replacement tools, and other agents expose `Edit`, `Write` or `MultiEdit`. These can carry enforceable paths. Use shape 3 only when this run has no file boundary to enforce. Read the returned `enforces` and `hookSeen` fields to confirm what is actually checked; tool names alone do not prove enforcement.
497
-
498
- **A boundary makes a question. It never makes an approval.** After the user agrees, a rule that already asked still asks. A scope can only turn an automatic approval into a question.
499
-
500
- **What the gate enforces, and what it does not.**
501
-
502
- The gate reads one thing from the contract: the path of a file you are about to change. It compares that path with `allowedPaths` and `offLimitsPaths`.
503
-
504
- - **Enforced.** `Edit`, `Write`, `MultiEdit` and supported aliases, including Codex `apply_patch` and VS Code patch/replacement tools. A file outside the agreed paths stops being auto-approvable and becomes a new question. Approving it widens the scope by that exact path.
505
- - **Not enforced.** Shell commands. `Read`. Web requests. Every MCP tool. These carry no file path, so the gate has no path to judge and reads them as inside the scope. The permission policy still governs them.
506
- - **`doneWhen` and `promises` are not enforced.** The user reads them. No code checks them.
261
+ Retracts a pending question, for example when you found the answer another way. A stale approval arriving later reads as consent to work that has moved on.
507
262
 
508
- Read `enforces` in the result. An empty array means nothing in this contract is checked automatically. Say that to the user in your own words rather than reporting that a scope is in force.
509
-
510
- **Write each path as a glob, and write it correctly.**
511
-
512
- The matcher compares text. It never looks at the file system.
513
-
514
- - A word that is not a path matches no file. Put `hubspot` or `summer-campaign` in `allowedPaths` and every file you change reads as outside the scope, so the user gets one question per file. Those belong in `promises`.
515
- - A bare directory name is expanded for you, so `docs` also covers `docs/**`. Write `docs/**` anyway; it says what you mean.
516
- - A leading `**/` needs a directory before it. `**/.env*` is expanded for you to also cover a root `.env`.
517
- - Letter case matters. Use a forward slash. Do not begin a path with `./`.
518
-
519
- The result echoes the expanded contract back. Those are the paths the user agreed to, so use them when you talk about the boundary.
520
-
521
- **Three shapes. Pick the one that matches the run.**
522
-
523
- 1. **The run changes files, and the boundary is about those files.** Put the file globs in `allowedPaths` and the areas to protect in `offLimitsPaths`. The gate enforces both. This is the coding case.
524
-
525
- 2. **The run changes files and also acts outside them.** An agent that writes a draft and then sends an email. Put the file globs in `allowedPaths`, because the gate enforces those. Put each outside boundary in `promises`: who you will contact, which channel, what you will not open, what you will not spend. Then ask again with `ask_user` before each outside action that cannot be undone, costs money, or reaches a person outside the team.
526
-
527
- 3. **The run changes no files.** A marketing, sales, support, research or operations agent that works through web requests and MCP tools. Call `propose_scope` with no paths and put the whole boundary in `promises`. The user's card then says plainly that nothing here is checked automatically. Do not smuggle a campaign name or an account name into `allowedPaths` to make the card look enforced.
528
-
529
- ```json
530
- {
531
- "doneWhen": "Ten summer-sale drafts exist in the CMS and none is published.",
532
- "sessionId": "<your client's id for this run>",
533
- "promises": [
534
- "I write drafts only. I publish nothing.",
535
- "I send no email to any customer.",
536
- "I do not open customer records.",
537
- "I spend no ad budget."
538
- ],
539
- "agentName": "Marketing agent - summer sale"
540
- }
541
- ```
263
+ ### schedule_reminder
542
264
 
543
- **`sessionId` is the key the gate reads the contract back by.** Use the id your client reports for this run. If you do not have one, call `list_sessions`, and take the session whose working directory matches yours and whose `lastSeenAt` is newest. Never invent a value, and never reuse one from another run.
265
+ When the user asks "remind me later", set a one-time personal reminder with `body` and either `inMinutes` or `at` (an ISO timestamp with an explicit UTC offset or Z). Confirm the returned `fireAt` in their local time and never guess their timezone. For example, `{"body":"Check the deploy","inMinutes":30}`. It reaches the user after you exit, but it does not schedule agent work. Call it with no arguments to list pending reminders, or pass `cancelReminderId` to cancel one; `cancelled:false` means it may already be dispatching. Limits: 30 days ahead, 25 pending per user. Do not use `ask_user` for reminders.
544
266
 
545
- The result tells you whether you got it right. **`hookSeen: false` means no agent hook has ever reported this session id**, so the gate will look the contract up under a key that does not exist and nothing will be checked, whatever `ratified` says. Fix the id and propose again, or say plainly that the boundary is a promise. `hookSeen` absent means the check could not run, which is not evidence either way.
267
+ ### propose_scope
546
268
 
547
- `ratified` and `answered` are separate on purpose. Answered but not ratified means the user declined: ask which boundary they want, and do **not** proceed as if they had agreed. Not answered means no scope is in force.
269
+ Agree the boundary of a run once, before the first edit. The user sees the paths you will change, the paths you will leave alone, and your definition of done, and agrees to all three in one tap.
548
270
 
549
- An unanswered proposal returns its `correlationId`. Keep waiting while pending; a phone
550
- yes ratifies the exact stored proposal. Only an explicit handoff asks you to cancel it
551
- before asking in the current chat whether to continue without an enforced scope.
552
- If cancellation returns `handoffAction: "stop"`, stop. Otherwise, if cancellation
553
- returns false, poll once for 1 second and honor the phone answer
554
- that won the race. A yes in chat is not a server-ratified scope.
271
+ **Propose a scope when** the run edits files and the boundary is not yet agreed: more than one file, or files you cannot all name before you start. **Do not propose one** for a single known file, for files the user named this turn, or for a run that changes no files and makes no promise worth keeping. Do not propose a new scope mid-run to widen an old one; the gate asks for the exact path when an edit needs it.
555
272
 
556
- Omitting `allowedPaths` proposes no path restriction, and the user is told that
557
- plainly as "this agent is asking to touch anything", so omit it only when you mean
558
- it.
273
+ **What is enforced.** Only the path of a file you change, on `Edit`, `Write`, `MultiEdit`, Codex `apply_patch` and VS Code patch tools, and only where the Pushary hook runs, such as Claude Code, Codex and Gemini CLI. An edit outside the agreed paths becomes a question instead of an automatic approval. Shell commands, reads, web requests and MCP tools carry no path and are not checked. `doneWhen` and `promises` are read by the user, not checked by code. A scope can only turn an automatic approval into a question; it never approves anything. Agreeing to a scope approves none of its promises: still ask before an outside action that cannot be undone, costs money or reaches someone outside the team.
559
274
 
560
- **What enforcement depends on.** The contract is recorded and shown to the user by any MCP client. Actually withdrawing auto-approval from out-of-scope edits needs the Pushary hook installed, which is how Claude Code, Codex and Gemini CLI run. Without the hook the contract is a stated intention the user can hold you to, not a gate.
275
+ **Write paths as globs.** The matcher compares text. A word like `hubspot` matches no file and makes every edit ask; it belongs in `promises`. Write `docs/**`, not `docs`. Letter case matters. Use forward slashes and no leading `./`. For a run that changes no files, call it with no paths and put the whole boundary in `promises`.
561
276
 
562
- Scope lives for the session only and is never inherited by another run. The server holds it for 12 hours, or until the next `propose_scope` for the same session replaces it.
277
+ **Read the result.** An empty `enforces` means nothing is checked automatically: tell the user. `hookSeen: false` means no hook has reported this `sessionId`, so the contract will never be found: fix the id or say the boundary is only a promise. Answered but not `ratified` means the user declined: ask which boundary they want. Use the `sessionId` your client reports for this run. If it reports none and you have `list_sessions`, call it and take the session whose working directory matches yours with the newest `lastSeenAt`. If another live session shares that folder, on this machine or another, or you cannot tell, skip the scope and tell the user the boundary is only a promise. Never invent an id.
563
278
 
564
- **When not to use it.** A single quick edit does not need a scope. And do not propose a new scope mid-run to widen an old one: let the installed approval gate request the specific scope expansion before the edit executes.
279
+ A scope lasts for the session, up to 12 hours, and ends at session end. A later ratified proposal replaces it.
565
280
 
566
281
  ### list_sessions
567
282
 
568
- Read-only. Returns the live agent sessions for your site, keyed by machine and
569
- session, with any approval questions still waiting. Use it to see which of your
570
- parallel agents is active, idle, waiting or errored. It does NOT start, stop or
571
- steer an agent, and it sends no notification.
572
-
573
- Check it before asking when you are one of several agents: if another session is
574
- already blocked on a question, adding a second one competes for the same
575
- attention rather than getting you answered sooner.
283
+ Read-only. Shows your live agent sessions and any questions still waiting. If another session is already waiting on the user, a second question competes for the same attention. Connector keys do not have this tool.
576
284
 
577
- ## Authorization and handoff
285
+ ## Limits
578
286
 
579
- Honor authorization already granted in this session. Ask only for a missing decision or an action outside that authorization, or when an enforced host policy requires it. A multi-step task alone does not require plan approval. Never ask again merely because the next authorized step deletes, deploys or publishes something. These skills guide the agent; supported hooks and runtime approval gates enforce policy. Do not bypass an enforced gate.
287
+ - 8 open questions per agent session and 25 per workspace. When a question is refused for this, answer or cancel an open one.
288
+ - 10 notifications per 5 minutes per workspace.
580
289
 
581
- Read `answered`, `status` and `handoffAction` (falling back to `nextAction`) on every response. Only `pending` is live; expired, cancelled, missing and unavailable are not new timeouts. Keep waiting with the same correlationId while pending. A poll ending does not expire the question; never cancel just because a poll returned without an answer. Before moving a live question to the current chat, cancel it. If cancellation says `stop`, stop; if it loses a race, poll once for one second and honor the winning answer. Silence is never consent. A select or input value containing “yes” is answer data, not approval of a separate action.
290
+ ## Answer Surfaces and Account Boundaries
582
291
 
583
- Delivery is controlled by the user's policy: `push_first` uses presence, `push_only` requests push every time, `notify_only` leaves the decision in the current client, and `terminal_only` avoids push. Do not override the mode or duplicate a question on every surface. The runtime owns delivery, expiry and settlement; do not claim that a reply can restart an ended agent turn.
584
-
585
- ## Human-in-the-Loop Flow
586
-
587
- One tool call - `ask_user` blocks and returns the answer:
588
-
589
- ```
590
- result = ask_user({
591
- question: "Which auth strategy should I use?",
592
- type: "select",
593
- options: ["JWT tokens", "Session cookies", "OAuth2 + PKCE"],
594
- context: "Setting up authentication for the new API",
595
- agentName: "Claude Code - myproject"
596
- })
597
-
598
- if result.answered:
599
- // result.value = "JWT tokens" - proceed with the chosen approach
600
- else:
601
- // follow result.handoffAction when present, otherwise result.nextAction
602
- ```
603
-
604
- If the user answers in chat before the push response arrives, call `cancel_question` before acting. If it returns `handoffAction: "stop"`, stop. Otherwise, if it returns false, poll once for 1 second and honor any phone answer that won the race.
605
-
606
- **How long `ask_user` blocks.** The user sets the delivery mode for their site. You do not set it. The mode decides how long the call waits, and whether it waits at all. New workspaces start on **Always**; the user can pick any of the four in the app.
607
-
608
- | Mode | The phone | The call |
609
- |---|---|---|
610
- | **When away** (`push_first`) | Asked only when the user is away from the terminal and the Mac | Waits for the push-first window. 20 seconds by default. |
611
- | **Always** (`push_only`) | Always asked | Waits for the policy timeout. |
612
- | **Alert only** (`notify_only`) | Told, not asked | Returns at once with `answered: false`. Decide in the current client. |
613
- | **In agent** (`terminal_only`) | Nothing is sent | Returns at once with `answered: false`. |
292
+ | Surface | What the user can do |
293
+ | --- | --- |
294
+ | Mobile app | Answer confirm, select and input questions. Confirm notifications offer approve and deny on the lock screen, except for risky commands. Choices and text open the app. |
295
+ | Mac notch | Answer personal account questions with confirm, select, input and question-set controls, including keyboard controls. |
296
+ | Slack | Answer through buttons, menus or text modals when the integration and recipient are configured. |
297
+ | Browser | Open the decision page as a fallback; browser notifications need permission. |
614
298
 
615
- Always read `answered`. Never assume the call waited. Pass `timeoutMs` only when you want a shorter wait than the site policy.
299
+ Partner customers use scoped enrollment links issued by their application. Do not enroll them into the operator's account or send their decisions through personal tools. The Mac notch uses the personal account; do not promise a Partner customer inbox on Mac. See https://pushary.com/docs/agents/embed for Partner setup.
616
300
 
617
301
  ## Identifying Your Agent
618
302
 
619
- Always pass `agentName` when you are one of multiple possible agents the user may be running. The user sees this in the notification title to know which agent is asking.
620
-
621
- **Format:** `{Agent Type} - {project or context}`
622
-
623
- **Examples:**
624
- - `"Claude Code - pushary repo"`
625
- - `"Hermes - daily-briefing"`
626
- - `"Cursor - frontend refactor"`
627
-
628
- ## Notification Etiquette
629
-
630
- - **Titles under 60 characters.** They get truncated on phone lock screens.
631
- - **Bodies under 200 characters.** Concise summaries, not full explanations.
632
- - **Max 3 notifications per task** unless the user explicitly requests more.
633
- - **That limit counts pushes only.** Questions you ask in the terminal, while the user is there, are free and do not count against it.
634
- - **Use context for detail.** Put file lists, error traces, and next steps in the context object - not the notification body.
635
- - **Write for a busy person.** The user is on their phone, away from the computer. Be exact. "Delete the 3 unused migration files?" beats "Should I clean up?"
636
- - **Pick the right question type.** Use confirm for binary decisions, select when options are known, input when they are not.
303
+ Pass `agentName` on every call as `{Agent Type} - {project or context}`, for example `"Claude Code - pushary repo"`, `"Hermes - daily-briefing"` or `"Cursor - frontend refactor"`. It is the title the user sees, so they know which agent is asking.