@pushary/agent-hooks 0.42.0 → 0.43.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.
@@ -104,15 +104,24 @@ var runLocalPassthrough = (binary, args2) => {
104
104
  import { basename } from "path";
105
105
 
106
106
  // src/wrapper/sdkLoader.ts
107
+ import { spawn as spawn2 } from "child_process";
108
+ import { existsSync, mkdirSync, writeFileSync } from "fs";
109
+ import { homedir } from "os";
110
+ import { join as join2 } from "path";
111
+ import { pathToFileURL } from "url";
107
112
  var userMessage = (text) => ({
108
113
  type: "user",
109
114
  message: { role: "user", content: text },
110
115
  parent_tool_use_id: null
111
116
  });
112
- var loadClaudeSdk = async () => {
113
- const specifier = "@anthropic-ai/claude-agent-sdk";
117
+ var SDK_VERSION = "0.3.207";
118
+ var SDK_PACKAGE = "@anthropic-ai/claude-agent-sdk";
119
+ var SDK_SPEC = `${SDK_PACKAGE}@${SDK_VERSION}`;
120
+ var sdkCacheDir = () => process.env.PUSHARY_REMOTE_SDK_DIR?.trim() || join2(homedir(), ".pushary", "remote-sdk");
121
+ var cachedSdkEntry = () => join2(sdkCacheDir(), "node_modules", "@anthropic-ai", "claude-agent-sdk", "sdk.mjs");
122
+ var importSdk = async (specifierOrUrl) => {
114
123
  try {
115
- const mod = await import(specifier);
124
+ const mod = await import(specifierOrUrl);
116
125
  if (typeof mod.query === "function") return mod;
117
126
  if (mod.default && typeof mod.default.query === "function") return mod.default;
118
127
  return null;
@@ -120,6 +129,44 @@ var loadClaudeSdk = async () => {
120
129
  return null;
121
130
  }
122
131
  };
132
+ var loadClaudeSdk = async () => {
133
+ const fromResolution = await importSdk(SDK_PACKAGE);
134
+ if (fromResolution) return fromResolution;
135
+ const entry = cachedSdkEntry();
136
+ if (existsSync(entry)) return importSdk(pathToFileURL(entry).href);
137
+ return null;
138
+ };
139
+ var ensureClaudeSdk = async (log) => {
140
+ const present = await loadClaudeSdk();
141
+ if (present) return present;
142
+ const dir = sdkCacheDir();
143
+ try {
144
+ mkdirSync(dir, { recursive: true });
145
+ const pkgJson = join2(dir, "package.json");
146
+ if (!existsSync(pkgJson)) {
147
+ writeFileSync(pkgJson, `${JSON.stringify({ name: "pushary-remote-sdk", private: true })}
148
+ `);
149
+ }
150
+ } catch {
151
+ return null;
152
+ }
153
+ log?.("[pushary] setting up remote mode (one-time, fetching the Claude Agent SDK)...");
154
+ const npmCmd = process.platform === "win32" ? "npm.cmd" : "npm";
155
+ const installed = await new Promise((resolve) => {
156
+ const child = spawn2(
157
+ npmCmd,
158
+ ["install", "--no-optional", "--no-audit", "--no-fund", "--loglevel=error", SDK_SPEC],
159
+ { cwd: dir, stdio: "ignore", shell: process.platform === "win32" }
160
+ );
161
+ child.on("error", () => resolve(false));
162
+ child.on("close", (code) => resolve(code === 0));
163
+ });
164
+ if (!installed) {
165
+ log?.(`[pushary] could not auto-install the SDK. Install it manually: npm i -g ${SDK_SPEC}`);
166
+ return null;
167
+ }
168
+ return loadClaudeSdk();
169
+ };
123
170
 
124
171
  // src/wrapper/messageQueue.ts
125
172
  var BoundedInputQueue = class {
@@ -752,7 +799,8 @@ var runRemoteLoop = async (deps) => {
752
799
  permissionMode: deps.permissionMode,
753
800
  canUseTool: deps.canUseTool,
754
801
  env: deps.env,
755
- cwd: deps.cwd
802
+ cwd: deps.cwd,
803
+ pathToClaudeCodeExecutable: deps.pathToClaudeCodeExecutable
756
804
  }
757
805
  });
758
806
  } catch (err) {
@@ -841,6 +889,15 @@ var extractAssistantText = (message) => {
841
889
  }
842
890
  return void 0;
843
891
  };
892
+ var echoLocal = (message) => {
893
+ if (message.type === "assistant") {
894
+ const text = extractAssistantText(message);
895
+ if (text) process.stdout.write(`${text}
896
+ `);
897
+ } else if (message.type === "result") {
898
+ process.stderr.write("[pushary] turn complete \u2014 standing by for phone commands (Ctrl-C to exit)\n");
899
+ }
900
+ };
844
901
  var streamTranscript = (relay, message) => {
845
902
  if (message.type === "assistant") {
846
903
  const text = extractAssistantText(message);
@@ -853,7 +910,7 @@ var streamTranscript = (relay, message) => {
853
910
  );
854
911
  }
855
912
  };
856
- var runRemoteMode = async (_binary, args2) => {
913
+ var runRemoteMode = async (binary, args2) => {
857
914
  let apiKey;
858
915
  try {
859
916
  apiKey = getApiKey();
@@ -863,11 +920,10 @@ var runRemoteMode = async (_binary, args2) => {
863
920
  );
864
921
  return { implemented: false };
865
922
  }
866
- const sdk = await loadClaudeSdk();
923
+ const sdk = await ensureClaudeSdk((message) => process.stderr.write(`${message}
924
+ `));
867
925
  if (!sdk) {
868
- process.stderr.write(
869
- "[pushary] remote mode needs the Claude Agent SDK, which is not installed. Enable it with:\n npm i -g @anthropic-ai/claude-agent-sdk@0.3.207\nRunning the normal passthrough for now.\n"
870
- );
926
+ process.stderr.write("[pushary] remote mode is unavailable; running the normal passthrough.\n");
871
927
  return { implemented: false };
872
928
  }
873
929
  const projectName = basename(process.cwd());
@@ -944,6 +1000,12 @@ var runRemoteMode = async (_binary, args2) => {
944
1000
  // recursion guard so a wrapper-spawned claude cannot re-enter the wrapper.
945
1001
  env: { ...process.env, [WRAPPER_ACTIVE_ENV]: "1" },
946
1002
  cwd: process.cwd(),
1003
+ // Run the user's OWN claude, not the SDK's bundled copy, so whatever login
1004
+ // already works for `claude` works here with no extra setup. On Windows a
1005
+ // `.cmd`/`.bat` shim can't be spawned directly by the SDK, so there we let
1006
+ // the SDK use its bundled binary (Windows reads creds from a plain file, so
1007
+ // there is no keychain-ACL mismatch to work around).
1008
+ pathToClaudeCodeExecutable: needsShell(binary) ? void 0 : binary,
947
1009
  onSessionId: (id) => {
948
1010
  sessionId = id;
949
1011
  relay?.reannounce();
@@ -951,8 +1013,12 @@ var runRemoteMode = async (_binary, args2) => {
951
1013
  onCost: (usd) => {
952
1014
  totalCostUsd = usd;
953
1015
  },
954
- // Stream a bounded live transcript to the phone (relay presence-gates it).
955
- onMessage: relay ? (message) => streamTranscript(relay, message) : void 0,
1016
+ // Always echo the turn to the local terminal; also stream a bounded live
1017
+ // transcript to the phone when the relay is up (it presence-gates it).
1018
+ onMessage: (message) => {
1019
+ echoLocal(message);
1020
+ if (relay) streamTranscript(relay, message);
1021
+ },
956
1022
  log: (message) => process.stderr.write(`${message}
957
1023
  `)
958
1024
  });
@@ -48,6 +48,6 @@ Usage:
48
48
  npx @pushary/agent-hooks@latest doctor
49
49
  npx @pushary/agent-hooks@latest mode push_only --for 30m
50
50
  npx @pushary/agent-hooks@latest wait 45
51
- pushary claude --remote -p "start the refactor" # drive from your phone (needs @anthropic-ai/claude-agent-sdk)
51
+ pushary claude --remote -p "start the refactor" # drive from your phone (uses your existing Claude login; sets up on first run)
52
52
  `);
53
53
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushary/agent-hooks",
3
- "version": "0.42.0",
3
+ "version": "0.43.0",
4
4
  "description": "Permission hooks for AI coding agents: route tool approvals through Pushary push notifications",
5
5
  "keywords": [
6
6
  "pushary",