@pushary/agent-hooks 0.81.0 → 0.82.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,37 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.82.0
4
+
5
+ ### Setup reads properly when an agent is the one running it
6
+
7
+ An agent that runs `setup` relays its output to you, and two things in
8
+ that output were written for a terminal that was not there.
9
+
10
+ Colour was unconditional. Piped into an agent's transcript, ten raw
11
+ escape sequences arrived as literal text around the parts you needed to
12
+ read. Colour now follows the same rule the rest of the CLI already used,
13
+ so it is on at a terminal and off everywhere else.
14
+
15
+ The app download was unreachable until you gave up. "No app?" was
16
+ answered with the command for approving in a browser, which sends you
17
+ away from the phone at the moment you were being asked to install it,
18
+ and the download link appeared only after the fifteen minute wait
19
+ expired. The header now names https://pushary.com/download first and
20
+ says the wait continues while you install.
21
+
22
+ ### The pairing QR is smaller
23
+
24
+ The QR encodes a link, and the link's length decides the QR's size. It
25
+ carried the pairing id and the public key, 114 characters, which drew 22
26
+ rows of blocks. Against a server that offers one, the CLI now uses a
27
+ short link instead: 33 characters, 16 rows, and small enough to survive
28
+ being relayed through something that truncates.
29
+
30
+ Nothing depends on it. Against an older server, or if the short link
31
+ cannot be issued, setup uses the long one exactly as before, and the
32
+ `pushary://` link is never shortened at all.
33
+
34
+
3
35
  ## 0.81.0
4
36
 
5
37
  ### The idle ping now honours a Terminal mode set in the dashboard
package/data/SKILL.md CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: pushary
3
- version: 0.9.0
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: 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 the user's lock screen. 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 service, $9.99/mo after a 3-day card-first trial.
3
+ version: 0.9.2
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 the user's lock screen. 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 service, $9.99/mo after a 3-day card-first trial.
5
5
  metadata:
6
6
  hermes:
7
7
  tags: [notifications, push, human-in-the-loop, alerts, permissions, mcp]
@@ -92,11 +92,20 @@ Just run it. No account, no API key, and nothing to paste first:
92
92
  npx @pushary/agent-hooks@latest setup
93
93
  ```
94
94
 
95
- It configures MCP, hooks, permissions and the skill in one step, then prints a QR code and waits.
95
+ 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.
96
96
 
97
- **Show the user your output and ask them to scan that QR with their phone camera.** Scanning pairs their Pushary app, which is what hands this machine its key. A tappable link and a fingerprint print under the QR for a phone already in hand. Do not ask the user for an API key, and do not send them to a signup page first: that is the old flow and it is strictly worse.
97
+ It prints a QR, a short link under it, a fingerprint, and then waits about 15 minutes.
98
98
 
99
- Signup starts a 3-day trial and asks for a card up front, which happens in the app.
99
+ **Do not summarise that output. Show it, and walk the user through all four steps:**
100
+
101
+ 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.
102
+ 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, so nobody has to restart anything.
103
+ 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 will also ask them to sign in and start a plan: $9.99/mo after a 3-day trial, card up front, all inside the app. Say this before they scan rather than letting them discover it mid-flow.
104
+ 4. **They approve.** Setup finishes on its own, and from then on your `ask_user` and `send_notification` calls arrive on their lock screen with answer buttons.
105
+
106
+ 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.
107
+
108
+ If setup exits without pairing, nothing was configured. Say that plainly and offer the two fallbacks below rather than pretending the tools are available.
100
109
 
101
110
  If `PUSHARY_API_KEY` is already in the environment or in an existing MCP config, setup uses it and skips pairing entirely.
102
111
 
@@ -1,6 +1,6 @@
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 human and nobody is at the terminal: 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 the user's lock screen. 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 service, $9.99/mo after a 3-day card-first trial.
3
+ 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 the user's lock screen. 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 service, $9.99/mo after a 3-day card-first trial.
4
4
  ---
5
5
 
6
6
  # Pushary - Push Notifications for AI Agents
@@ -85,11 +85,20 @@ Just run it. No account, no API key, and nothing to paste first:
85
85
  npx @pushary/agent-hooks@latest setup
86
86
  ```
87
87
 
88
- It configures MCP, hooks, permissions and the skill in one step, then prints a QR code and waits.
88
+ 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.
89
89
 
90
- **Show the user your output and ask them to scan that QR with their phone camera.** Scanning pairs their Pushary app, which is what hands this machine its key. A tappable link and a fingerprint print under the QR for a phone already in hand. Do not ask the user for an API key, and do not send them to a signup page first: that is the old flow and it is strictly worse.
90
+ It prints a QR, a short link under it, a fingerprint, and then waits about 15 minutes.
91
91
 
92
- Signup starts a 3-day trial and asks for a card up front, which happens in the app.
92
+ **Do not summarise that output. Show it, and walk the user through all four steps:**
93
+
94
+ 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.
95
+ 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, so nobody has to restart anything.
96
+ 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 will also ask them to sign in and start a plan: $9.99/mo after a 3-day trial, card up front, all inside the app. Say this before they scan rather than letting them discover it mid-flow.
97
+ 4. **They approve.** Setup finishes on its own, and from then on your `ask_user` and `send_notification` calls arrive on their lock screen with answer buttons.
98
+
99
+ 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.
100
+
101
+ If setup exits without pairing, nothing was configured. Say that plainly and offer the two fallbacks below rather than pretending the tools are available.
93
102
 
94
103
  If `PUSHARY_API_KEY` is already in the environment or in an existing MCP config, setup uses it and skips pairing entirely.
95
104
 
@@ -1,6 +1,6 @@
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 human and nobody is at the terminal: 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 the user's lock screen. 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 service, $9.99/mo after a 3-day card-first trial.
3
+ 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 the user's lock screen. 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 service, $9.99/mo after a 3-day card-first trial.
4
4
  ---
5
5
 
6
6
  # Pushary - Push Notifications for AI Agents
@@ -85,11 +85,20 @@ Just run it. No account, no API key, and nothing to paste first:
85
85
  npx @pushary/agent-hooks@latest setup
86
86
  ```
87
87
 
88
- It configures MCP, hooks, permissions and the skill in one step, then prints a QR code and waits.
88
+ 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.
89
89
 
90
- **Show the user your output and ask them to scan that QR with their phone camera.** Scanning pairs their Pushary app, which is what hands this machine its key. A tappable link and a fingerprint print under the QR for a phone already in hand. Do not ask the user for an API key, and do not send them to a signup page first: that is the old flow and it is strictly worse.
90
+ It prints a QR, a short link under it, a fingerprint, and then waits about 15 minutes.
91
91
 
92
- Signup starts a 3-day trial and asks for a card up front, which happens in the app.
92
+ **Do not summarise that output. Show it, and walk the user through all four steps:**
93
+
94
+ 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.
95
+ 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, so nobody has to restart anything.
96
+ 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 will also ask them to sign in and start a plan: $9.99/mo after a 3-day trial, card up front, all inside the app. Say this before they scan rather than letting them discover it mid-flow.
97
+ 4. **They approve.** Setup finishes on its own, and from then on your `ask_user` and `send_notification` calls arrive on their lock screen with answer buttons.
98
+
99
+ 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.
100
+
101
+ If setup exits without pairing, nothing was configured. Say that plainly and offer the two fallbacks below rather than pretending the tools are available.
93
102
 
94
103
  If `PUSHARY_API_KEY` is already in the environment or in an existing MCP config, setup uses it and skips pairing entirely.
95
104
 
@@ -11,7 +11,7 @@ import {
11
11
  } from "../chunk-Q4QULGAV.js";
12
12
  import {
13
13
  createIo
14
- } from "../chunk-TX7KBKT7.js";
14
+ } from "../chunk-5RUIFDTP.js";
15
15
  import "../chunk-E6I3DBJW.js";
16
16
  import {
17
17
  readJsonSafe,
@@ -3,17 +3,17 @@ import {
3
3
  confirmAppConnection,
4
4
  connectDevice,
5
5
  printConnectInstructions
6
- } from "../chunk-BOMF4C2Q.js";
6
+ } from "../chunk-DFTD6W5B.js";
7
7
  import "../chunk-3EGEA4KH.js";
8
8
  import {
9
9
  printKeyCheck
10
- } from "../chunk-U2PZKYZM.js";
10
+ } from "../chunk-MWZZ5G7B.js";
11
11
  import {
12
12
  checkApiKey
13
13
  } from "../chunk-EBP4YG75.js";
14
14
  import {
15
15
  createIo
16
- } from "../chunk-TX7KBKT7.js";
16
+ } from "../chunk-5RUIFDTP.js";
17
17
  import {
18
18
  readKeySource
19
19
  } from "../chunk-E6I3DBJW.js";
@@ -49,7 +49,7 @@ import {
49
49
  fetchChannels,
50
50
  reachVerdict,
51
51
  verifyRoundTrip
52
- } from "../chunk-BOMF4C2Q.js";
52
+ } from "../chunk-DFTD6W5B.js";
53
53
  import "../chunk-3EGEA4KH.js";
54
54
  import {
55
55
  checkApiKey,
@@ -57,7 +57,7 @@ import {
57
57
  } from "../chunk-EBP4YG75.js";
58
58
  import {
59
59
  createIo
60
- } from "../chunk-TX7KBKT7.js";
60
+ } from "../chunk-5RUIFDTP.js";
61
61
  import {
62
62
  keyPrefix,
63
63
  readKeySource
@@ -9,7 +9,7 @@ import {
9
9
  } from "../chunk-EBP4YG75.js";
10
10
  import {
11
11
  createIo
12
- } from "../chunk-TX7KBKT7.js";
12
+ } from "../chunk-5RUIFDTP.js";
13
13
  import {
14
14
  readKeySource,
15
15
  writeKey
@@ -7,7 +7,7 @@ import {
7
7
  } from "../chunk-Q4QULGAV.js";
8
8
  import {
9
9
  createIo
10
- } from "../chunk-TX7KBKT7.js";
10
+ } from "../chunk-5RUIFDTP.js";
11
11
  import {
12
12
  clearKey,
13
13
  readKeySource
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  exitOnFailedResponse
4
- } from "../chunk-U2PZKYZM.js";
4
+ } from "../chunk-MWZZ5G7B.js";
5
5
  import "../chunk-EBP4YG75.js";
6
6
  import {
7
7
  createIo
8
- } from "../chunk-TX7KBKT7.js";
8
+ } from "../chunk-5RUIFDTP.js";
9
9
  import {
10
10
  rejectUnknownFlags
11
11
  } from "../chunk-GMXKITVA.js";
@@ -58,12 +58,15 @@ import {
58
58
  printConnectInstructions,
59
59
  setHumanStream,
60
60
  verifyRoundTrip
61
- } from "../chunk-BOMF4C2Q.js";
61
+ } from "../chunk-DFTD6W5B.js";
62
62
  import "../chunk-3EGEA4KH.js";
63
63
  import {
64
64
  checkApiKey,
65
65
  describeKeyCheck
66
66
  } from "../chunk-EBP4YG75.js";
67
+ import {
68
+ useColor
69
+ } from "../chunk-5RUIFDTP.js";
67
70
  import {
68
71
  KEY_FILE_MODE,
69
72
  exportLine,
@@ -526,11 +529,12 @@ var writeHuman = (text) => {
526
529
  humanStream.write(text);
527
530
  };
528
531
  var jsonMode = false;
529
- var dim = (s) => `\x1B[2m${s}\x1B[0m`;
530
- var bold = (s) => `\x1B[1m${s}\x1B[0m`;
531
- var green = (s) => `\x1B[32m${s}\x1B[0m`;
532
- var cyan = (s) => `\x1B[36m${s}\x1B[0m`;
533
- var yellow = (s) => `\x1B[33m${s}\x1B[0m`;
532
+ var paint = (open) => (s) => useColor({ json: jsonMode }) ? `${open}${s}\x1B[0m` : s;
533
+ var dim = paint("\x1B[2m");
534
+ var bold = paint("\x1B[1m");
535
+ var green = paint("\x1B[32m");
536
+ var cyan = paint("\x1B[36m");
537
+ var yellow = paint("\x1B[33m");
534
538
  var check = green("\u2713");
535
539
  var abortWith = async (reason, code, keyCheck) => {
536
540
  await reportSetupAbort(reason, code, keyCheck);
@@ -1627,7 +1631,8 @@ var main = async () => {
1627
1631
  process.exit(EXIT.OK);
1628
1632
  } else if (connectMode === "app" && canShowPairingQr() && !options.skipPhone) {
1629
1633
  console.log(` ${dim("Pairing with your Pushary app.")}`);
1630
- console.log(` ${dim("No app? Ctrl-C, then:")} ${cyan("npx @pushary/agent-hooks setup --connect browser")}`);
1634
+ console.log(` ${dim("No app yet? Get it at")} ${cyan("https://pushary.com/download")}${dim(", this keeps waiting.")}`);
1635
+ console.log(` ${dim("No phone? Ctrl-C, then:")} ${cyan("npx @pushary/agent-hooks setup --connect browser")}`);
1631
1636
  markStep("pair");
1632
1637
  const previous = readKeySource();
1633
1638
  const paired = await connectViaAppPairing({
@@ -3,7 +3,7 @@ import {
3
3
  describeReach,
4
4
  fetchChannels,
5
5
  reachVerdict
6
- } from "../chunk-BOMF4C2Q.js";
6
+ } from "../chunk-DFTD6W5B.js";
7
7
  import "../chunk-3EGEA4KH.js";
8
8
  import {
9
9
  checkApiKey,
@@ -11,7 +11,7 @@ import {
11
11
  } from "../chunk-EBP4YG75.js";
12
12
  import {
13
13
  createIo
14
- } from "../chunk-TX7KBKT7.js";
14
+ } from "../chunk-5RUIFDTP.js";
15
15
  import {
16
16
  readKeySource
17
17
  } from "../chunk-E6I3DBJW.js";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  createIo
4
- } from "../chunk-TX7KBKT7.js";
4
+ } from "../chunk-5RUIFDTP.js";
5
5
  import {
6
6
  rejectUnknownFlags
7
7
  } from "../chunk-GMXKITVA.js";
@@ -4,11 +4,11 @@ import {
4
4
  } from "../chunk-IIENZE2J.js";
5
5
  import {
6
6
  exitOnFailedResponse
7
- } from "../chunk-U2PZKYZM.js";
7
+ } from "../chunk-MWZZ5G7B.js";
8
8
  import "../chunk-EBP4YG75.js";
9
9
  import {
10
10
  createIo
11
- } from "../chunk-TX7KBKT7.js";
11
+ } from "../chunk-5RUIFDTP.js";
12
12
  import {
13
13
  rejectUnknownFlags
14
14
  } from "../chunk-GMXKITVA.js";
@@ -75,5 +75,6 @@ var createIo = (stream = process.stdout, context = {}) => {
75
75
  };
76
76
 
77
77
  export {
78
+ useColor,
78
79
  createIo
79
80
  };
@@ -3,6 +3,9 @@ import {
3
3
  openSealedKey,
4
4
  publicKeyFingerprint
5
5
  } from "./chunk-3EGEA4KH.js";
6
+ import {
7
+ useColor
8
+ } from "./chunk-5RUIFDTP.js";
6
9
  import {
7
10
  callMcpTool
8
11
  } from "./chunk-BSZYIAZL.js";
@@ -263,11 +266,12 @@ var humanIsTty = () => stream === process.stdout && process.stdout.isTTY === tru
263
266
 
264
267
  // src/onboarding.ts
265
268
  import qrcodeTerminal from "qrcode-terminal";
266
- var dim = (s) => `\x1B[2m${s}\x1B[0m`;
267
- var bold = (s) => `\x1B[1m${s}\x1B[0m`;
268
- var green = (s) => `\x1B[32m${s}\x1B[0m`;
269
- var cyan = (s) => `\x1B[36m${s}\x1B[0m`;
270
- var yellow = (s) => `\x1B[33m${s}\x1B[0m`;
269
+ var paint = (open) => (s) => useColor() ? `${open}${s}\x1B[0m` : s;
270
+ var dim = paint("\x1B[2m");
271
+ var bold = paint("\x1B[1m");
272
+ var green = paint("\x1B[32m");
273
+ var cyan = paint("\x1B[36m");
274
+ var yellow = paint("\x1B[33m");
271
275
  var check = green("\u2713");
272
276
  var apiBase = () => process.env.PUSHARY_API_URL?.trim() || process.env.PUSHARY_BASE_URL?.trim() || "https://pushary.com";
273
277
  var EXIT_ABORTED = 130;
@@ -631,7 +635,8 @@ var startPairing = async (cliPublicKey) => {
631
635
  });
632
636
  if (!res.ok) return null;
633
637
  const data = await res.json().catch(() => null);
634
- return data && typeof data.pairId === "string" ? data.pairId : null;
638
+ if (!data || typeof data.pairId !== "string") return null;
639
+ return { pairId: data.pairId, code: typeof data.code === "string" ? data.code : null };
635
640
  } catch {
636
641
  return null;
637
642
  }
@@ -682,22 +687,28 @@ var ackPairing = async (pairId) => {
682
687
  } catch {
683
688
  }
684
689
  };
685
- var buildPairLinks = (pairId, publicKeyB64, baseUrl = apiBase()) => {
690
+ var buildPairLinks = (pairId, publicKeyB64, baseUrl = apiBase(), code = null) => {
686
691
  const id = encodeURIComponent(pairId);
687
692
  const pk = encodeURIComponent(publicKeyB64);
693
+ const origin = baseUrl.replace(/\/+$/, "");
688
694
  return {
689
- universalLink: `${baseUrl.replace(/\/+$/, "")}/pair?id=${id}&pk=${pk}`,
695
+ // 33 characters against 114, so 16 rows of QR against 22. Without a code this
696
+ // is the exact URL every published CLI already encodes.
697
+ universalLink: code ? `${origin}/pair/${encodeURIComponent(code)}` : `${origin}/pair?id=${id}&pk=${pk}`,
698
+ // Never shortened: the scheme reaches an installed app with no server round
699
+ // trip, which is the path that has to survive the short code failing.
690
700
  deepLink: `pushary://pair?pk=${pk}&id=${id}`
691
701
  };
692
702
  };
693
703
  var connectViaAppPairing = async (options = {}) => {
694
704
  const keypair = generateKeypair();
695
- const pairId = await startPairing(keypair.publicKeyB64);
696
- if (!pairId) {
705
+ const started = await startPairing(keypair.publicKeyB64);
706
+ if (!started) {
697
707
  console.log(` ${yellow("!")} Couldn't start pairing. Check your connection and try again.`);
698
708
  return null;
699
709
  }
700
- const { universalLink, deepLink } = buildPairLinks(pairId, keypair.publicKeyB64);
710
+ const { pairId, code } = started;
711
+ const { universalLink, deepLink } = buildPairLinks(pairId, keypair.publicKeyB64, apiBase(), code);
701
712
  console.log();
702
713
  console.log(` ${bold("Connect your Pushary app")}`);
703
714
  console.log(` ${dim("Scan this with your phone camera:")}`);
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-EBP4YG75.js";
5
5
  import {
6
6
  createIo
7
- } from "./chunk-TX7KBKT7.js";
7
+ } from "./chunk-5RUIFDTP.js";
8
8
  import {
9
9
  EXIT
10
10
  } from "./chunk-KZERVKTD.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushary/agent-hooks",
3
- "version": "0.81.0",
3
+ "version": "0.82.0",
4
4
  "description": "Permission hooks for AI coding agents: route tool approvals through Pushary push notifications",
5
5
  "keywords": [
6
6
  "pushary",