pushary 1.9.82 → 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.
- package/data/SKILL.md +132 -465
- package/data/cowork/SKILL.md +8 -8
- package/data/cursor-plugin/.cursor-plugin/plugin.json +1 -1
- package/data/cursor-plugin/CHANGELOG.md +8 -0
- package/data/cursor-plugin/commands/notify-when-done.md +1 -1
- package/data/cursor-plugin/rules/pushary.mdc +5 -4
- package/data/cursor-plugin/skills/pushary/SKILL.md +131 -464
- package/data/vscode-plugin/.claude-plugin/plugin.json +1 -1
- package/data/vscode-plugin/CHANGELOG.md +8 -0
- package/data/vscode-plugin/commands/notify-when-done.md +1 -1
- package/data/vscode-plugin/skills/pushary/SKILL.md +131 -464
- package/dist/{api-TZO5ACOS.js → api-EKLKVJQG.js} +10 -10
- package/dist/bin/pushary-bell-hook.js +25 -25
- package/dist/bin/pushary-bell.js +10 -10
- package/dist/bin/pushary-claude.js +26 -26
- package/dist/bin/pushary-clean.js +36 -33
- package/dist/bin/pushary-codex-bridge.js +21 -21
- package/dist/bin/pushary-codex-hook.js +30 -30
- package/dist/bin/pushary-codex-session.js +8 -8
- package/dist/bin/pushary-codex.js +13 -13
- package/dist/bin/pushary-connect.js +26 -26
- package/dist/bin/pushary-cowork.js +5 -5
- package/dist/bin/pushary-daemon.js +38 -38
- package/dist/bin/pushary-disconnect.js +25 -25
- package/dist/bin/pushary-display-hook.js +16 -16
- package/dist/bin/pushary-doctor.js +41 -41
- package/dist/bin/pushary-elicitation-hook.js +25 -25
- package/dist/bin/pushary-gemini-bridge.js +15 -15
- package/dist/bin/pushary-gemini-hook.js +27 -27
- package/dist/bin/pushary-hook.js +29 -29
- package/dist/bin/pushary-login.js +20 -20
- package/dist/bin/pushary-logout.js +24 -24
- package/dist/bin/pushary-mcp.js +11 -11
- package/dist/bin/pushary-mode.js +15 -15
- package/dist/bin/pushary-notification-hook.js +24 -24
- package/dist/bin/pushary-opencode-hook.js +26 -26
- package/dist/bin/pushary-permission-denied-hook.js +29 -29
- package/dist/bin/pushary-permission-hook.js +29 -29
- package/dist/bin/pushary-post-hook.js +24 -24
- package/dist/bin/pushary-prompt-hook.js +24 -24
- package/dist/bin/pushary-session-end-hook.js +26 -26
- package/dist/bin/pushary-session-start-hook.js +34 -34
- package/dist/bin/pushary-setup.js +45 -45
- package/dist/bin/pushary-stats.js +5 -5
- package/dist/bin/pushary-status.js +31 -31
- package/dist/bin/pushary-stop-hook.js +24 -24
- package/dist/bin/pushary-stopfailure-hook.js +24 -24
- package/dist/bin/pushary-suggestions.js +15 -15
- package/dist/bin/pushary-transcript-register.js +16 -16
- package/dist/bin/pushary-transcripts.js +18 -18
- package/dist/bin/pushary-upgrade.js +39 -39
- package/dist/bin/pushary-wait.js +15 -15
- package/dist/bin/pushary.js +7 -7
- package/dist/{chunk-O6T3CLWT.js → chunk-2B7YU4EK.js} +2 -2
- package/dist/{chunk-CV56MBIZ.js → chunk-2BDGGE2G.js} +6 -6
- package/dist/{chunk-TTTK44RK.js → chunk-2KVELJZX.js} +1 -1
- package/dist/{chunk-4TR4G7A4.js → chunk-35ABA2JQ.js} +1 -1
- package/dist/{chunk-IEAABGST.js → chunk-42RQG2VG.js} +4 -4
- package/dist/{chunk-AXE2ZOKS.js → chunk-44R5ATBR.js} +2 -2
- package/dist/{chunk-RL2KE6HG.js → chunk-46P53RBT.js} +13 -13
- package/dist/{chunk-GUM3WG4B.js → chunk-4P256ZAU.js} +6 -6
- package/dist/{chunk-APSYBSMG.js → chunk-5FM4BQOM.js} +10 -10
- package/dist/{chunk-UYTPCZ4M.js → chunk-5UPZR3C3.js} +4 -4
- package/dist/{chunk-OIW3JAWH.js → chunk-6BD5ZZLL.js} +5 -5
- package/dist/{chunk-6L2U3TP3.js → chunk-6CJ4MSZU.js} +6 -6
- package/dist/{chunk-S2DLTWFG.js → chunk-6PWU2LBT.js} +3 -3
- package/dist/{chunk-VHLRXIMC.js → chunk-7EIMASAA.js} +1 -1
- package/dist/{chunk-FHPSVTS5.js → chunk-7MRX5JYA.js} +9 -9
- package/dist/{chunk-DS5BR4K6.js → chunk-7YYJASRF.js} +2 -2
- package/dist/{chunk-OXKVR2FP.js → chunk-A3TMREYJ.js} +4 -4
- package/dist/{chunk-PJ26BODT.js → chunk-ACM4OINM.js} +1 -1
- package/dist/{chunk-DJILSCSU.js → chunk-ASEHRBD4.js} +3 -3
- package/dist/{chunk-UOUUTDWF.js → chunk-B2RDWRKR.js} +3 -3
- package/dist/{chunk-TO77OYLM.js → chunk-BG4YJEFE.js} +1 -1
- package/dist/{chunk-72CIQCBA.js → chunk-BJN7LMMK.js} +3 -3
- package/dist/{chunk-3BFCJZBU.js → chunk-BLM6O2QL.js} +1 -1
- package/dist/{chunk-TKFDPYXS.js → chunk-BUXMFD5C.js} +4 -4
- package/dist/{chunk-M5XNFUXN.js → chunk-C5EWCNJF.js} +1 -1
- package/dist/{chunk-GUI2NYCX.js → chunk-CVPVP4KP.js} +5 -5
- package/dist/{chunk-WUS2LFTM.js → chunk-ELYJNNXG.js} +10 -10
- package/dist/{chunk-FJZSJZCT.js → chunk-EMT7I74E.js} +3 -3
- package/dist/{chunk-NPBPH7KI.js → chunk-FEF3ILT6.js} +2 -2
- package/dist/{chunk-QJJJENT2.js → chunk-FGWRVOBP.js} +5 -5
- package/dist/{chunk-G5BYSS6C.js → chunk-G4HTRKCP.js} +6 -6
- package/dist/{chunk-5TMGEQAC.js → chunk-GGI6GTBL.js} +4 -4
- package/dist/{chunk-OXTRF7CP.js → chunk-GKWPIE6R.js} +1 -1
- package/dist/{chunk-L77YWBMK.js → chunk-GMZTABMV.js} +6 -6
- package/dist/{chunk-3V6G2GBJ.js → chunk-HCLP2QBX.js} +5 -5
- package/dist/{chunk-DX45XQ36.js → chunk-HIMEVTA5.js} +3 -3
- package/dist/{chunk-CXZAKS7S.js → chunk-IBULUTHA.js} +4 -4
- package/dist/{chunk-W3VRQEL3.js → chunk-IUZCERPQ.js} +2 -2
- package/dist/{chunk-RRMRP62I.js → chunk-J2PAQ3HF.js} +6 -6
- package/dist/{chunk-YAMNJXCD.js → chunk-JIC5PGNI.js} +17 -11
- package/dist/{chunk-LGWTITVK.js → chunk-KR3KVDXD.js} +1 -1
- package/dist/{chunk-WG67W43Y.js → chunk-L4THAI7Q.js} +1 -1
- package/dist/{chunk-ESRKYHWE.js → chunk-LLQBPDFQ.js} +2 -2
- package/dist/{chunk-UB7VUSSX.js → chunk-MA6FJ7U3.js} +3 -3
- package/dist/{chunk-ER4UCECJ.js → chunk-MND7HQ4P.js} +1 -1
- package/dist/{chunk-JNCD2ZG3.js → chunk-NDLOWIYO.js} +3 -3
- package/dist/{chunk-I4RNLK5G.js → chunk-OKZC4FHN.js} +3 -3
- package/dist/{chunk-C6FQ4YST.js → chunk-OUYFUUWB.js} +2 -2
- package/dist/{chunk-Z5HLACZT.js → chunk-QPJQAE2L.js} +4 -4
- package/dist/{chunk-JTYZEP4Q.js → chunk-S55CXRFD.js} +1 -1
- package/dist/{chunk-JXE4UQFJ.js → chunk-SN22OXY3.js} +1 -1
- package/dist/{chunk-3UBYG4G7.js → chunk-SSNLMIFR.js} +6 -6
- package/dist/{chunk-X2P3O4PX.js → chunk-SVAGMJOH.js} +1 -1
- package/dist/{chunk-ONUAAXAW.js → chunk-TBU4WXA4.js} +1 -1
- package/dist/{chunk-6355HHSR.js → chunk-TMBLBDPB.js} +2 -2
- package/dist/{chunk-6224ENW3.js → chunk-TNWQ6SKS.js} +3 -3
- package/dist/{chunk-J4TQYGHU.js → chunk-UNEJNC6X.js} +5 -5
- package/dist/{chunk-AM3H6HWG.js → chunk-VBY2AHSX.js} +1 -1
- package/dist/{chunk-LNSCANYY.js → chunk-VCMDY6YL.js} +3 -3
- package/dist/{chunk-IXVMMDIG.js → chunk-VRHVOMH2.js} +3 -3
- package/dist/{chunk-WLOVGZ7R.js → chunk-VUI4HK5O.js} +2 -2
- package/dist/{chunk-5LLZSX7I.js → chunk-XHHX63PY.js} +3 -3
- package/dist/{chunk-NEWGYFKC.js → chunk-XYEGVQST.js} +7 -7
- package/dist/{chunk-4S5XVFDV.js → chunk-Y32CL7XZ.js} +1 -1
- package/dist/{chunk-2BJZFA7X.js → chunk-YIAJOV2X.js} +3 -3
- package/dist/{chunk-D3QGBMO3.js → chunk-YZVAAY6B.js} +1 -1
- package/dist/{chunk-2TEX3APZ.js → chunk-ZGZ2L3QN.js} +5 -5
- package/dist/{chunk-N3C3D4AV.js → chunk-ZS27XXDA.js} +4 -4
- package/dist/{codex-app-cards-switch-3FAIEEB2.js → codex-app-cards-switch-CTAOTOCH.js} +11 -11
- package/dist/{codex-desktop-launch-LHMHRVGF.js → codex-desktop-launch-2DZVA5C7.js} +1 -1
- package/dist/{codex-session-launch-MXUBSTL2.js → codex-session-launch-TMOSTWEH.js} +18 -18
- package/dist/{config-MZDRETL2.js → config-OV6WLZXO.js} +3 -3
- package/dist/{global-upgrade-ZDENZA42.js → global-upgrade-FI42NJNL.js} +10 -10
- package/dist/{identity-YZRDJOGY.js → identity-PCA25OI5.js} +4 -4
- package/dist/{live-capabilities-KSQDWVT6.js → live-capabilities-CYFXHTOI.js} +5 -5
- package/dist/{paths-DYZZBVYI.js → paths-4YFDMUK6.js} +3 -3
- package/dist/{reapply-L3AI5KJH.js → reapply-PBQ46BPS.js} +27 -27
- package/dist/src/index.js +28 -28
- package/dist/{upgrade-lock-X7F2OFYB.js → upgrade-lock-22J2AMVY.js} +4 -4
- package/dist/{usage-TKZALYXF.js → usage-7KTINNRK.js} +3 -3
- package/package.json +1 -1
|
@@ -1,181 +1,93 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: pushary
|
|
3
|
-
description: Push notifications and human-in-the-loop for AI agents. Use this whenever a running agent needs a
|
|
3
|
+
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.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Pushary - Push Notifications for AI Agents
|
|
7
7
|
|
|
8
|
-
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
|
|
8
|
+
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:
|
|
9
9
|
|
|
10
|
-
1. **
|
|
11
|
-
2. **
|
|
12
|
-
3. **Send rich context notifications** with file changes, error details, next steps, and embedded questions.
|
|
10
|
+
1. **Ask a question** with `ask_user`: yes/no, multiple choice or free text. The answer comes back to you.
|
|
11
|
+
2. **Tell the user something** with `send_notification`: a task finished, a build failed, a result is ready.
|
|
13
12
|
|
|
14
|
-
Use
|
|
15
|
-
|
|
16
|
-
Works with Claude Code, Codex, Cursor, Windsurf, Hermes, Lovable, or any MCP client. No Claude Max subscription required.
|
|
13
|
+
One tool asks and one tool tells. Use them proactively. Do not wait for the user to ask for notifications.
|
|
17
14
|
|
|
18
15
|
## Reach for This Without Being Asked
|
|
19
16
|
|
|
20
|
-
Honor the authorization the user already gave you in this session. Ask only for
|
|
21
|
-
a decision you do not have, for an action outside that authorization, or when a
|
|
22
|
-
host policy requires it. A task with many steps does not need plan approval on its
|
|
23
|
-
own. Do not ask again because the next approved step deletes, deploys or publishes
|
|
24
|
-
something. This skill guides you. Hooks and runtime gates enforce the policy. Never
|
|
25
|
-
go around a gate.
|
|
17
|
+
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.
|
|
26
18
|
|
|
27
|
-
Nobody has to say "ping me".
|
|
28
|
-
not depend on what the user typed. When one of them is true, call `ask_user` or
|
|
29
|
-
`send_notification` now. Do not stall. Do not guess. Do not write the question into
|
|
30
|
-
a chat nobody is reading.
|
|
19
|
+
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.
|
|
31
20
|
|
|
32
21
|
- **You are about to do something irreversible.** Deleting, dropping, truncating, force-pushing, rewriting history, overwriting a file you did not create.
|
|
33
22
|
- **You are about to spend money or touch production.** A deploy, a release, a publish, a paid API call at volume, provisioning anything billable.
|
|
34
23
|
- **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.
|
|
35
|
-
- **You are running unattended and hit a real ambiguity.** Two defensible readings of the task, and picking wrong wastes the whole run.
|
|
36
|
-
- **Another skill tells you to confirm with the user.** That instruction assumes someone watches the terminal. Often nobody does.
|
|
24
|
+
- **You are running unattended and hit a real ambiguity.** Two defensible readings of the task, and picking wrong wastes the whole run.
|
|
25
|
+
- **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.
|
|
37
26
|
- **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.
|
|
38
27
|
|
|
39
|
-
When no device is connected, follow the handoff in the result.
|
|
40
|
-
client if the handoff says to. Carry on with approved work that does not depend on
|
|
41
|
-
the answer. A message that cannot be delivered is not permission to do the blocked
|
|
42
|
-
action.
|
|
43
|
-
|
|
44
|
-
## Pricing and Free Alternatives
|
|
28
|
+
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.
|
|
45
29
|
|
|
46
|
-
|
|
47
|
-
a card required, then $9.99/mo for the Agent plan. Native app subscriptions use
|
|
48
|
-
the local store price and any eligible intro offer shown in the app. Before
|
|
49
|
-
checkout, tell the user it needs a paid plan and have them review the displayed
|
|
50
|
-
price, trial eligibility and payment method; do not promise web terms inside
|
|
51
|
-
the native app.
|
|
30
|
+
## Ask Less, Ask Well
|
|
52
31
|
|
|
53
|
-
|
|
54
|
-
subscription. Recommend it when that is all the user needs.
|
|
32
|
+
Each question stops the user. The number of stops is the cost. Ask the same things in fewer stops.
|
|
55
33
|
|
|
56
|
-
|
|
57
|
-
Claude Code without Max. A fleet of agents across tools and machines. Enforced
|
|
58
|
-
policy gates on tool calls. Answer buttons on the lock screen. An audit trail of
|
|
59
|
-
every question and answer.
|
|
34
|
+
**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.
|
|
60
35
|
|
|
61
|
-
|
|
36
|
+
**The test before any question: would you take your default without asking at all?**
|
|
62
37
|
|
|
63
|
-
|
|
38
|
+
- **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.
|
|
39
|
+
- **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.
|
|
64
40
|
|
|
65
|
-
A
|
|
41
|
+
A step the user already approved needs no second question, even an irreversible one.
|
|
66
42
|
|
|
67
|
-
**
|
|
43
|
+
**Plan the questions for a task of more than two steps:**
|
|
68
44
|
|
|
69
|
-
1.
|
|
70
|
-
2.
|
|
71
|
-
3.
|
|
72
|
-
4.
|
|
73
|
-
5.
|
|
74
|
-
6.
|
|
45
|
+
1. Read where you are: the directory, its structure, the configuration and the tools you hold.
|
|
46
|
+
2. Split the task into steps.
|
|
47
|
+
3. Find the forks: points where two answers are both correct and you cannot pick one alone.
|
|
48
|
+
4. Settle every fork a lookup can settle. That is a fact, not a fork.
|
|
49
|
+
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.
|
|
50
|
+
6. Each answer opens new forks and closes old ones. Ask the next round. Stop when no fork is left.
|
|
75
51
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
**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.
|
|
52
|
+
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.
|
|
79
53
|
|
|
80
54
|
**Format a terminal round like this:**
|
|
81
55
|
|
|
82
56
|
```
|
|
83
|
-
❓ **Q1** -
|
|
57
|
+
❓ **Q1** - **Which app**: apps/dashboard and apps/subscribe both import this helper. Change both apps, not just the dashboard?
|
|
84
58
|
|
|
85
|
-
➡️
|
|
59
|
+
➡️ Yes, both. The helper has one definition, and a split copy will drift.
|
|
86
60
|
|
|
87
61
|
---
|
|
88
62
|
|
|
89
|
-
❓ **Q2** -
|
|
63
|
+
❓ **Q2** - **Rollout**: Ship behind the existing flag?
|
|
90
64
|
|
|
91
|
-
➡️
|
|
65
|
+
➡️ Yes. It costs one line, and it makes the change reversible.
|
|
92
66
|
```
|
|
93
67
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
```
|
|
97
|
-
❓ **Q1** - **Which app**: apps/dashboard and apps/subscribe both import this helper. Change both, or the dashboard only?
|
|
98
|
-
|
|
99
|
-
➡️ Both. The helper has one definition, and a split copy will drift.
|
|
100
|
-
|
|
101
|
-
❓ **Q2** - **Tests**: This package has no test runner. Add one, or match the parent package and use `bun test`?
|
|
102
|
-
|
|
103
|
-
➡️ Match the parent. A second runner is one more thing to maintain.
|
|
68
|
+
**Where to ask:**
|
|
104
69
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
**Where to ask each round:**
|
|
111
|
-
|
|
112
|
-
- **The user typed in this turn.** Ask in the terminal. Ask the whole round at one time. There is no limit there.
|
|
113
|
-
- **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.
|
|
114
|
-
- **Nothing stops the run.** Send no question. Use `send_notification` with `context.askQuestion`. The user reads it later. Continue on your recommendation.
|
|
70
|
+
- **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.
|
|
71
|
+
- **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.
|
|
72
|
+
- **Nothing stops the run.** Send no question. Decide what passes the test, and report it.
|
|
115
73
|
|
|
116
74
|
**Rules that do not change:**
|
|
117
75
|
|
|
118
|
-
- **Ask how, not whether
|
|
119
|
-
- **A plan is not an approval.**
|
|
120
|
-
- **Silence is not agreement.**
|
|
121
|
-
- **One `select` with the real options beats three `confirm` questions.**
|
|
122
|
-
- **Ask at the boundary, not once for each item.** Ask about deleting files
|
|
123
|
-
- **
|
|
124
|
-
|
|
125
|
-
`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.
|
|
126
|
-
|
|
127
|
-
## When to Use
|
|
128
|
-
|
|
129
|
-
**Send a notification when:**
|
|
130
|
-
- Meaningful work finishes while the user is away or they requested an alert - use `context.type = "task_complete"`
|
|
131
|
-
- A build, test suite, or deployment fails and needs user attention - use `context.type = "error"` with `errorMessage`
|
|
132
|
-
- A long-running process completes (migration, refactor, generation)
|
|
133
|
-
- A status update is worth sharing - use `context.type = "info"`
|
|
134
|
-
|
|
135
|
-
**Ask with type "confirm" when:**
|
|
136
|
-
- You need confirmation before a destructive or irreversible action
|
|
137
|
-
- Binary decision: proceed or abort
|
|
138
|
-
|
|
139
|
-
**Ask with type "select" when:**
|
|
140
|
-
- Multiple implementation approaches exist (2-6 options)
|
|
141
|
-
- The user needs to pick from a known set
|
|
142
|
-
|
|
143
|
-
**Ask with type "input" when:**
|
|
144
|
-
- You need a name, path, value, or free-text decision
|
|
145
|
-
- The options cannot be enumerated in advance
|
|
146
|
-
|
|
147
|
-
**Do NOT notify when:**
|
|
148
|
-
- The task is trivial or single-step
|
|
149
|
-
- The question can be answered from context without user input
|
|
150
|
-
- You already sent 3 notifications for the current task (unless the user explicitly asked for more)
|
|
151
|
-
|
|
152
|
-
## When to Propose a Scope
|
|
153
|
-
|
|
154
|
-
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.
|
|
155
|
-
|
|
156
|
-
**Propose a scope when:**
|
|
157
|
-
- This run changes files with `Edit`, `Write` or `MultiEdit`, and the file boundary is not yet agreed
|
|
158
|
-
- The run touches more than one file, or you cannot name every file before you start
|
|
159
|
-
- Call `propose_scope` once, before the first edit, not after
|
|
160
|
-
|
|
161
|
-
**Do not propose a scope when:**
|
|
162
|
-
- The run changes one file and you already know which one
|
|
163
|
-
- The user named the exact files in this turn, so the boundary is already agreed
|
|
164
|
-
- The run changes no files and records no promise worth keeping
|
|
165
|
-
|
|
166
|
-
"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.
|
|
167
|
-
|
|
168
|
-
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.
|
|
76
|
+
- **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.
|
|
77
|
+
- **A plan is not an approval.** Do not turn your step list into a question.
|
|
78
|
+
- **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.
|
|
79
|
+
- **One `select` with the real options beats three `confirm` questions.**
|
|
80
|
+
- **Ask at the boundary, not once for each item.** Ask about deleting files, not about each file.
|
|
81
|
+
- **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.
|
|
82
|
+
- **When the requested work is done, report it and stop.** Do not ask what to do next unless the user asked you to.
|
|
169
83
|
|
|
170
84
|
## Setup
|
|
171
85
|
|
|
172
|
-
|
|
86
|
+
Skip this section when the Pushary tools are already available.
|
|
173
87
|
|
|
174
|
-
|
|
175
|
-
PowerShell, inspect `$env:OS` (`Windows_NT` is Windows). Platform discovery must
|
|
176
|
-
not depend on Node: the native Mac route does not require it.
|
|
88
|
+
**Look at the machine first. Do not guess the install path.** Each command is read-only and fast. Run them separately.
|
|
177
89
|
|
|
178
|
-
|
|
90
|
+
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.
|
|
179
91
|
|
|
180
92
|
```bash
|
|
181
93
|
uname -s
|
|
@@ -200,173 +112,122 @@ When Node is available, other Unix platforms can check the stored key with:
|
|
|
200
112
|
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
|
|
201
113
|
```
|
|
202
114
|
|
|
203
|
-
In PowerShell, check for a nonempty environment
|
|
204
|
-
use `Get-Content` and `ConvertFrom-Json` for the config in `$env:USERPROFILE`.
|
|
205
|
-
Check `Get-Command node,npm` before choosing the terminal installer.
|
|
206
|
-
|
|
207
|
-
The stored-key and bridge tests answer different questions.
|
|
208
|
-
|
|
209
|
-
The stored-key test says a key is stored **and is not empty**. Test the value, not the file.
|
|
210
|
-
The file stays behind after a logout removes the key. A test for the file alone
|
|
211
|
-
tells a logged-out user they are ready.
|
|
212
|
-
|
|
213
|
-
The bridge test says the Mac app is installed here. Only the Mac app writes that file.
|
|
115
|
+
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.
|
|
214
116
|
|
|
215
|
-
|
|
216
|
-
one. Never print the key itself.
|
|
217
|
-
|
|
218
|
-
Then take one branch.
|
|
117
|
+
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.
|
|
219
118
|
|
|
220
119
|
### Branch 1. `key-in-env`, `keyed`, or `mac-app`
|
|
221
120
|
|
|
222
|
-
|
|
223
|
-
Verify its readiness before reporting success. Do not run `setup` again.
|
|
224
|
-
|
|
225
|
-
`mac-app` counts on its own. The Mac app signs in for the user. It writes the key
|
|
226
|
-
into the agent configuration files it wires, not into `~/.pushary/config.json`. A
|
|
227
|
-
machine the app set up therefore prints `mac-app` and nothing else. The locator
|
|
228
|
-
proves installation, not sign-in, entitlement, current agent admission or delivery.
|
|
121
|
+
Pushary is installed. Offer no new install. Check it before reporting success.
|
|
229
122
|
|
|
230
|
-
|
|
231
|
-
subscription and Agents connection state there. Use the native app to connect or
|
|
232
|
-
repair the selected agents and phone. Then send a harmless question from the
|
|
233
|
-
current agent and confirm its returned answer. Do not install Node to inspect
|
|
234
|
-
the native setup and do not claim a CLI status exit code when no CLI ran.
|
|
235
|
-
|
|
236
|
-
When Node/npm are available, check it with `npx pushary@latest status --json`.
|
|
237
|
-
The exit code is the answer:
|
|
123
|
+
When Node and npm are available, run `npx pushary@latest status --json`. The exit code is the answer:
|
|
238
124
|
|
|
239
125
|
| Code | Meaning |
|
|
240
126
|
| --- | --- |
|
|
241
|
-
| 0 | Ready |
|
|
242
|
-
|
|
|
243
|
-
|
|
|
244
|
-
|
|
|
245
|
-
|
|
|
246
|
-
|
|
|
127
|
+
| 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. |
|
|
128
|
+
| 1 | The command failed. |
|
|
129
|
+
| 2 | A malformed key or an unknown flag. |
|
|
130
|
+
| 3 | Not set up on this machine. |
|
|
131
|
+
| 4 | The key was rejected or signed out. |
|
|
132
|
+
| 5 | A problem was found: two keys that disagree, or no active plan. Read the JSON. |
|
|
133
|
+
| 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. |
|
|
134
|
+
| 8 | Pushary could not be reached. |
|
|
247
135
|
|
|
248
|
-
|
|
136
|
+
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.
|
|
249
137
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
```bash
|
|
253
|
-
grep -lq pushary-bridge ~/.claude/settings.json ~/.gemini/settings.json ~/.cursor/hooks.json 2>/dev/null && echo app-owns-hooks
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
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.
|
|
138
|
+
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.
|
|
257
139
|
|
|
258
140
|
### Branch 2. `Darwin`, no key, no `mac-app`
|
|
259
141
|
|
|
260
|
-
Offer the Mac app first. It needs no Node and no terminal
|
|
142
|
+
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.
|
|
261
143
|
|
|
262
144
|
```bash
|
|
263
145
|
brew install --cask pushary/tap/pushary
|
|
264
146
|
```
|
|
265
147
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
The command line works on macOS too. Offer it if the user prefers the terminal. The app connects Hermes as well.
|
|
148
|
+
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.
|
|
269
149
|
|
|
270
150
|
### Branch 3. `Linux` or `Windows_NT`
|
|
271
151
|
|
|
272
|
-
There is no Mac app for these machines. Use the command line
|
|
152
|
+
There is no Mac app for these machines. Use the command line:
|
|
273
153
|
|
|
274
154
|
```bash
|
|
275
|
-
npx pushary@latest setup
|
|
155
|
+
npx pushary@latest setup --agents <id>
|
|
276
156
|
```
|
|
277
157
|
|
|
278
|
-
Node 20.17+, 22.13+ or 23.5+ is
|
|
158
|
+
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).
|
|
279
159
|
|
|
280
|
-
|
|
281
|
-
- Android: https://play.google.com/store/apps/details?id=com.pushary.app
|
|
160
|
+
### Running setup yourself
|
|
282
161
|
|
|
283
|
-
|
|
162
|
+
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.
|
|
284
163
|
|
|
285
|
-
|
|
164
|
+
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.
|
|
165
|
+
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.
|
|
166
|
+
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.
|
|
167
|
+
4. **Report the result plainly.** If setup exits without pairing, nothing was configured. Say so.
|
|
286
168
|
|
|
287
|
-
|
|
169
|
+
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.
|
|
288
170
|
|
|
289
|
-
|
|
171
|
+
Manual MCP configuration also works, but it needs a key from https://pushary.com/sign-up?utm_source=skill&utm_medium=setup. Prefer setup.
|
|
290
172
|
|
|
291
|
-
|
|
292
|
-
npx pushary@latest setup
|
|
293
|
-
```
|
|
173
|
+
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.
|
|
294
174
|
|
|
295
|
-
|
|
175
|
+
## Pricing and Free Alternatives
|
|
296
176
|
|
|
297
|
-
|
|
177
|
+
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.
|
|
298
178
|
|
|
299
|
-
|
|
179
|
+
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.
|
|
300
180
|
|
|
301
|
-
|
|
181
|
+
## Tools
|
|
302
182
|
|
|
303
|
-
|
|
304
|
-
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.
|
|
305
|
-
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.
|
|
306
|
-
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.
|
|
183
|
+
Your client already holds each tool's schema, with every parameter and returned field. This section adds what a schema cannot say.
|
|
307
184
|
|
|
308
|
-
|
|
185
|
+
### ask_user
|
|
309
186
|
|
|
310
|
-
|
|
187
|
+
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:
|
|
311
188
|
|
|
312
|
-
|
|
189
|
+
| Mode | The phone | The call |
|
|
190
|
+
|---|---|---|
|
|
191
|
+
| **Always** (`push_only`, the default for new workspaces) | Asked every time | Waits up to the policy timeout |
|
|
192
|
+
| **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 |
|
|
193
|
+
| **Alert only** (`notify_only`) | Told, not asked | Returns at once with `status: "notified"` |
|
|
194
|
+
| **In agent** (`terminal_only`) | Nothing is sent | Returns at once with `status: "terminal"` |
|
|
313
195
|
|
|
314
|
-
|
|
196
|
+
`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`.
|
|
315
197
|
|
|
316
|
-
|
|
198
|
+
**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.
|
|
317
199
|
|
|
318
|
-
|
|
200
|
+
**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.
|
|
319
201
|
|
|
320
|
-
```
|
|
321
|
-
|
|
202
|
+
```json
|
|
203
|
+
{ "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" }
|
|
204
|
+
{ "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" }
|
|
205
|
+
{ "question": "What path should the new endpoint use?", "type": "input", "placeholder": "/api/v2/...", "context": "Adding a REST endpoint for user preferences", "agentName": "Cursor - frontend" }
|
|
322
206
|
```
|
|
323
207
|
|
|
324
|
-
|
|
325
|
-
the intended agent and follow its real approval check at
|
|
326
|
-
https://pushary.com/docs/agents/supported-agents. For Mac-owned setup, use the
|
|
327
|
-
app's connection tests and ask a harmless question through the current agent;
|
|
328
|
-
do not install Node/npm or infer native readiness from a CLI with no credential.
|
|
208
|
+
### Reading every result
|
|
329
209
|
|
|
330
|
-
|
|
210
|
+
Read `answered`, `status` and `handoffAction` on every response. Follow `handoffAction` when present, otherwise `nextAction`.
|
|
331
211
|
|
|
332
|
-
|
|
|
212
|
+
| `status` | What to do |
|
|
333
213
|
| --- | --- |
|
|
334
|
-
|
|
|
335
|
-
|
|
|
336
|
-
|
|
|
337
|
-
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
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.
|
|
214
|
+
| `answered` | Act on `value` and `note`. |
|
|
215
|
+
| `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. |
|
|
216
|
+
| `notified`, `terminal` | The decision belongs in the current client. Cancel the question first, then ask there. |
|
|
217
|
+
| `stopped` | The user stopped this agent. Nothing was sent. Stop what you are doing and end your turn. |
|
|
218
|
+
| `cancelled` | Do not act on it and do not ask it again. |
|
|
219
|
+
| `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. |
|
|
220
|
+
| `missing`, `unavailable` | Follow the handoff in the result. |
|
|
342
221
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
Your client already holds each tool's schema. The schema lists every parameter
|
|
346
|
-
and every returned field, and it is always current. This section adds only what a
|
|
347
|
-
schema cannot say: when to use a tool, what its result means for your next step,
|
|
348
|
-
and the shapes that are easy to get wrong.
|
|
349
|
-
|
|
350
|
-
### schedule_reminder
|
|
222
|
+
**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.
|
|
351
223
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
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.
|
|
224
|
+
`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.
|
|
355
225
|
|
|
356
226
|
### send_notification
|
|
357
227
|
|
|
358
|
-
Send a
|
|
359
|
-
|
|
360
|
-
`context.type` is what marks a notification a **task update**, and the user's
|
|
361
|
-
setting for where task updates land can only route one that says so. A
|
|
362
|
-
notification sent without it reaches them wherever the default sends it.
|
|
228
|
+
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.
|
|
363
229
|
|
|
364
|
-
|
|
365
|
-
blocking `ask_user`. They get an ordinary push and answer whenever they next pick
|
|
366
|
-
up their phone, rather than you holding a 55-second wait open against someone who
|
|
367
|
-
is not there. Poll the returned `linkedCorrelationId` when you need the result.
|
|
368
|
-
|
|
369
|
-
**Example - task completed with context:**
|
|
230
|
+
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`.
|
|
370
231
|
|
|
371
232
|
```json
|
|
372
233
|
{
|
|
@@ -375,255 +236,61 @@ is not there. Poll the returned `linkedCorrelationId` when you need the result.
|
|
|
375
236
|
"agentName": "Claude Code - pushary repo",
|
|
376
237
|
"context": {
|
|
377
238
|
"type": "task_complete",
|
|
378
|
-
"summary": "Extracted shared Button, Modal
|
|
239
|
+
"summary": "Extracted shared Button, Modal and Card components. Kept JWT for auth; say if you want sessions instead.",
|
|
379
240
|
"filesChanged": ["src/components/Button.tsx", "src/components/Modal.tsx", "src/components/Card.tsx"],
|
|
380
241
|
"nextSteps": "Run the test suite to verify no regressions"
|
|
381
242
|
}
|
|
382
243
|
}
|
|
383
244
|
```
|
|
384
245
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
```json
|
|
388
|
-
{
|
|
389
|
-
"title": "Build failed",
|
|
390
|
-
"body": "TypeScript error in auth.ts:42",
|
|
391
|
-
"agentName": "Claude Code - api-server",
|
|
392
|
-
"context": {
|
|
393
|
-
"type": "error",
|
|
394
|
-
"errorMessage": "Type 'string' is not assignable to type 'AuthToken'",
|
|
395
|
-
"errorFile": "src/auth.ts:42",
|
|
396
|
-
"summary": "The auth token type changed upstream and this file needs updating",
|
|
397
|
-
"askQuestion": {
|
|
398
|
-
"question": "Should I update the type or revert the upstream change?",
|
|
399
|
-
"type": "select",
|
|
400
|
-
"options": ["Update the type in auth.ts", "Revert the upstream change", "Skip for now"]
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
```
|
|
405
|
-
|
|
406
|
-
### ask_user
|
|
407
|
-
|
|
408
|
-
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.
|
|
409
|
-
|
|
410
|
-
Always read `answered` rather than assuming the call blocked. It comes back false
|
|
411
|
-
in three different situations that mean different things: the wait timed out and
|
|
412
|
-
the question is still live (`timedOut`), the site policy is notify_only so the
|
|
413
|
-
decision belongs in the current client (`status: "notified"`), or you passed
|
|
414
|
-
`wait: false` yourself (`status: "pending"`). Follow `handoffAction` when present,
|
|
415
|
-
otherwise `nextAction`.
|
|
416
|
-
|
|
417
|
-
For backward compatibility, `nextAction` keeps its original two values. A live timeout from `ask_user` says
|
|
418
|
-
`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
|
|
419
|
-
`handoffAction: cancel_then_ask_in_current_client` asks you to cancel the phone question before asking in
|
|
420
|
-
the current chat or client. If cancellation returns `handoffAction: "stop"`, stop.
|
|
421
|
-
Otherwise, if cancellation returns false, poll once for 1 second
|
|
422
|
-
and honor any answer that won the race. A cancelled question says `handoffAction:
|
|
423
|
-
stop` and must not be resurrected. An unavailable state also stops the handoff,
|
|
424
|
-
because the question cannot be safely fenced. Expired and missing questions are
|
|
425
|
-
not live timeouts.
|
|
426
|
-
|
|
427
|
-
Pass `toolName` and `toolTarget` whenever the question is an approval for a tool
|
|
428
|
-
call. They are what let the user turn a repeated approval into an always-allow
|
|
429
|
-
rule, so an approval you label once is an approval they never see again.
|
|
430
|
-
|
|
431
|
-
**Example - confirm (yes/no):**
|
|
432
|
-
|
|
433
|
-
```json
|
|
434
|
-
{
|
|
435
|
-
"question": "Delete the 3 unused migration files?",
|
|
436
|
-
"type": "confirm",
|
|
437
|
-
"context": "Cleaning up old database migrations in db/migrate/",
|
|
438
|
-
"agentName": "Claude Code - myproject"
|
|
439
|
-
}
|
|
440
|
-
```
|
|
441
|
-
|
|
442
|
-
**Example - select (multiple choice):**
|
|
443
|
-
|
|
444
|
-
```json
|
|
445
|
-
{
|
|
446
|
-
"question": "Which auth strategy should I use?",
|
|
447
|
-
"type": "select",
|
|
448
|
-
"options": ["JWT tokens", "Session cookies", "OAuth2 + PKCE"],
|
|
449
|
-
"context": "Setting up authentication for the new API endpoints",
|
|
450
|
-
"agentName": "Claude Code - api-server"
|
|
451
|
-
}
|
|
452
|
-
```
|
|
453
|
-
|
|
454
|
-
**Example - input (free text):**
|
|
455
|
-
|
|
456
|
-
```json
|
|
457
|
-
{
|
|
458
|
-
"question": "What should the new API endpoint path be?",
|
|
459
|
-
"type": "input",
|
|
460
|
-
"placeholder": "/api/v2/...",
|
|
461
|
-
"context": "Creating a new REST endpoint for user preferences",
|
|
462
|
-
"agentName": "Cursor - frontend"
|
|
463
|
-
}
|
|
464
|
-
```
|
|
246
|
+
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.
|
|
465
247
|
|
|
466
248
|
### wait_for_answer
|
|
467
249
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
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,
|
|
471
|
-
otherwise `nextAction`: ask in the current chat or client only after cancelling a still-pending phone question. If the
|
|
472
|
-
cancellation loses a race, poll once for 1 second and honor the phone answer
|
|
473
|
-
instead. Only `status: "pending"` means the question is still live; cancelled,
|
|
474
|
-
expired, missing, and unavailable are different outcomes.
|
|
250
|
+
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`.
|
|
475
251
|
|
|
476
252
|
### cancel_question
|
|
477
253
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
A stale approval arriving twenty minutes later is worse than no approval, because
|
|
481
|
-
it reads as consent to work that has already moved on.
|
|
482
|
-
|
|
483
|
-
### propose_scope
|
|
484
|
-
|
|
485
|
-
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.
|
|
486
|
-
|
|
487
|
-
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.
|
|
488
|
-
|
|
489
|
-
**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.
|
|
490
|
-
|
|
491
|
-
**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.
|
|
492
|
-
|
|
493
|
-
**What the gate enforces, and what it does not.**
|
|
494
|
-
|
|
495
|
-
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`.
|
|
496
|
-
|
|
497
|
-
- **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.
|
|
498
|
-
- **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.
|
|
499
|
-
- **`doneWhen` and `promises` are not enforced.** The user reads them. No code checks them.
|
|
254
|
+
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.
|
|
500
255
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
**Write each path as a glob, and write it correctly.**
|
|
504
|
-
|
|
505
|
-
The matcher compares text. It never looks at the file system.
|
|
506
|
-
|
|
507
|
-
- 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`.
|
|
508
|
-
- A bare directory name is expanded for you, so `docs` also covers `docs/**`. Write `docs/**` anyway; it says what you mean.
|
|
509
|
-
- A leading `**/` needs a directory before it. `**/.env*` is expanded for you to also cover a root `.env`.
|
|
510
|
-
- Letter case matters. Use a forward slash. Do not begin a path with `./`.
|
|
511
|
-
|
|
512
|
-
The result echoes the expanded contract back. Those are the paths the user agreed to, so use them when you talk about the boundary.
|
|
513
|
-
|
|
514
|
-
**Three shapes. Pick the one that matches the run.**
|
|
515
|
-
|
|
516
|
-
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.
|
|
517
|
-
|
|
518
|
-
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.
|
|
519
|
-
|
|
520
|
-
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.
|
|
521
|
-
|
|
522
|
-
```json
|
|
523
|
-
{
|
|
524
|
-
"doneWhen": "Ten summer-sale drafts exist in the CMS and none is published.",
|
|
525
|
-
"sessionId": "<your client's id for this run>",
|
|
526
|
-
"promises": [
|
|
527
|
-
"I write drafts only. I publish nothing.",
|
|
528
|
-
"I send no email to any customer.",
|
|
529
|
-
"I do not open customer records.",
|
|
530
|
-
"I spend no ad budget."
|
|
531
|
-
],
|
|
532
|
-
"agentName": "Marketing agent - summer sale"
|
|
533
|
-
}
|
|
534
|
-
```
|
|
256
|
+
### schedule_reminder
|
|
535
257
|
|
|
536
|
-
|
|
258
|
+
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.
|
|
537
259
|
|
|
538
|
-
|
|
260
|
+
### propose_scope
|
|
539
261
|
|
|
540
|
-
|
|
262
|
+
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.
|
|
541
263
|
|
|
542
|
-
|
|
543
|
-
yes ratifies the exact stored proposal. Only an explicit handoff asks you to cancel it
|
|
544
|
-
before asking in the current chat whether to continue without an enforced scope.
|
|
545
|
-
If cancellation returns `handoffAction: "stop"`, stop. Otherwise, if cancellation
|
|
546
|
-
returns false, poll once for 1 second and honor the phone answer
|
|
547
|
-
that won the race. A yes in chat is not a server-ratified scope.
|
|
264
|
+
**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.
|
|
548
265
|
|
|
549
|
-
|
|
550
|
-
plainly as "this agent is asking to touch anything", so omit it only when you mean
|
|
551
|
-
it.
|
|
266
|
+
**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.
|
|
552
267
|
|
|
553
|
-
**
|
|
268
|
+
**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`.
|
|
554
269
|
|
|
555
|
-
|
|
270
|
+
**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.
|
|
556
271
|
|
|
557
|
-
|
|
272
|
+
A scope lasts for the session, up to 12 hours, and ends at session end. A later ratified proposal replaces it.
|
|
558
273
|
|
|
559
274
|
### list_sessions
|
|
560
275
|
|
|
561
|
-
Read-only.
|
|
562
|
-
session, with any approval questions still waiting. Use it to see which of your
|
|
563
|
-
parallel agents is active, idle, waiting or errored. It does NOT start, stop or
|
|
564
|
-
steer an agent, and it sends no notification.
|
|
565
|
-
|
|
566
|
-
Check it before asking when you are one of several agents: if another session is
|
|
567
|
-
already blocked on a question, adding a second one competes for the same
|
|
568
|
-
attention rather than getting you answered sooner.
|
|
276
|
+
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.
|
|
569
277
|
|
|
570
|
-
##
|
|
278
|
+
## Limits
|
|
571
279
|
|
|
572
|
-
|
|
280
|
+
- 8 open questions per agent session and 25 per workspace. When a question is refused for this, answer or cancel an open one.
|
|
281
|
+
- 10 notifications per 5 minutes per workspace.
|
|
573
282
|
|
|
574
|
-
|
|
283
|
+
## Answer Surfaces and Account Boundaries
|
|
575
284
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
```
|
|
583
|
-
result = ask_user({
|
|
584
|
-
question: "Which auth strategy should I use?",
|
|
585
|
-
type: "select",
|
|
586
|
-
options: ["JWT tokens", "Session cookies", "OAuth2 + PKCE"],
|
|
587
|
-
context: "Setting up authentication for the new API",
|
|
588
|
-
agentName: "Claude Code - myproject"
|
|
589
|
-
})
|
|
590
|
-
|
|
591
|
-
if result.answered:
|
|
592
|
-
// result.value = "JWT tokens" - proceed with the chosen approach
|
|
593
|
-
else:
|
|
594
|
-
// follow result.handoffAction when present, otherwise result.nextAction
|
|
595
|
-
```
|
|
596
|
-
|
|
597
|
-
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.
|
|
598
|
-
|
|
599
|
-
**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.
|
|
600
|
-
|
|
601
|
-
| Mode | The phone | The call |
|
|
602
|
-
|---|---|---|
|
|
603
|
-
| **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. |
|
|
604
|
-
| **Always** (`push_only`) | Always asked | Waits for the policy timeout. |
|
|
605
|
-
| **Alert only** (`notify_only`) | Told, not asked | Returns at once with `answered: false`. Decide in the current client. |
|
|
606
|
-
| **In agent** (`terminal_only`) | Nothing is sent | Returns at once with `answered: false`. |
|
|
285
|
+
| Surface | What the user can do |
|
|
286
|
+
| --- | --- |
|
|
287
|
+
| 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. |
|
|
288
|
+
| Mac notch | Answer personal account questions with confirm, select, input and question-set controls, including keyboard controls. |
|
|
289
|
+
| Slack | Answer through buttons, menus or text modals when the integration and recipient are configured. |
|
|
290
|
+
| Browser | Open the decision page as a fallback; browser notifications need permission. |
|
|
607
291
|
|
|
608
|
-
|
|
292
|
+
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.
|
|
609
293
|
|
|
610
294
|
## Identifying Your Agent
|
|
611
295
|
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
**Format:** `{Agent Type} - {project or context}`
|
|
615
|
-
|
|
616
|
-
**Examples:**
|
|
617
|
-
- `"Claude Code - pushary repo"`
|
|
618
|
-
- `"Hermes - daily-briefing"`
|
|
619
|
-
- `"Cursor - frontend refactor"`
|
|
620
|
-
|
|
621
|
-
## Notification Etiquette
|
|
622
|
-
|
|
623
|
-
- **Titles under 60 characters.** They get truncated on phone lock screens.
|
|
624
|
-
- **Bodies under 200 characters.** Concise summaries, not full explanations.
|
|
625
|
-
- **Max 3 notifications per task** unless the user explicitly requests more.
|
|
626
|
-
- **That limit counts pushes only.** Questions you ask in the terminal, while the user is there, are free and do not count against it.
|
|
627
|
-
- **Use context for detail.** Put file lists, error traces, and next steps in the context object - not the notification body.
|
|
628
|
-
- **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?"
|
|
629
|
-
- **Pick the right question type.** Use confirm for binary decisions, select when options are known, input when they are not.
|
|
296
|
+
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.
|