pendingyou 0.1.0 → 0.2.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/README.md CHANGED
@@ -34,14 +34,40 @@ lines (the question, your answer, the next step) and exits. Claude Code wakes up
34
34
  When you're at the terminal, Claude Code asks you there and puts the same question in Pending You at once; the
35
35
  first answer wins. If you answer in the terminal, it withdraws the card and the hold ends by itself.
36
36
 
37
+ ## A computer with no browser
38
+
39
+ On a server or over SSH, sign in with a code instead:
40
+
41
+ ```sh
42
+ npx pendingyou login --device
43
+ ```
44
+
45
+ It shows a short code. Open pendingyou.com/device on your phone, sign in, enter the code and press Allow. Only enter a
46
+ code your own computer is showing you.
47
+
48
+ ## Always-on scripts
49
+
50
+ `watch` runs a command each time an answer is ready, for a script on your assistant's own machine:
51
+
52
+ ```sh
53
+ npx pendingyou watch -- ./on-answer.sh
54
+ ```
55
+
56
+ When you sign in, pick which assistant it hears for ("Hear answers for: Only Muse"); by default it's your Claude Code
57
+ assistants. The command runs without a shell, once per answer, message or fallback that comes due, oldest first. It
58
+ gets the request as JSON on stdin, and `PENDINGYOU_REQUEST_ID`, `PENDINGYOU_EVENT` (`answer_ready`, `message_ready` or
59
+ `fallback_due`), `PENDINGYOU_VERSION` and `PENDINGYOU_ORIGIN` in its environment. A failing command is tried twice
60
+ more, then skipped. `--once` stops after the first.
61
+
37
62
  ## Commands
38
63
 
39
64
  | Command | What it does |
40
65
  | --- | --- |
41
- | `init` | Set up Claude Code on this computer (`--no-login`, `--no-browser`) |
66
+ | `init` | Set up Claude Code on this computer (`--no-login`, `--no-browser`, `--device`) |
42
67
  | `hold <requestId>` | Wait for the answer, print it, exit (`--timeout 4h`) |
43
68
  | `status` | What's set up; exits 0 when Claude Code hears answers right away |
44
- | `login` / `logout` | Sign this computer in or out (`--force`, `--all`) |
69
+ | `login` / `logout` | Sign this computer in or out (`--device`, `--force`, `--all`) |
70
+ | `watch -- <command>` | Run a command each time an answer is ready (`--once`) |
45
71
  | `pickup` / `handoff` | The session-start and next-message hooks |
46
72
  | `uninstall` | Remove what `init` added, and sign out |
47
73
 
@@ -49,7 +75,7 @@ Every command takes `--origin` (default `https://www.pendingyou.com`, or `$PENDI
49
75
 
50
76
  ## What it can see, and where it keeps things
51
77
 
52
- The sign-in only hears answers to your own Claude Code assistants' questions. It isn't an assistant: it can't ask,
78
+ The sign-in only hears answers to your own Claude Code assistants' questions, or to the one assistant you picked. It isn't an assistant: it can't ask,
53
79
  answer or change anything, and it doesn't show up as one. It's stored in `~/.config/pendingyou/credentials.json`
54
80
  (readable only by you), refreshes itself, and ends after 30 days or on `logout`. `state.json` beside it remembers
55
81
  which answers were already handed over (ids only). Nothing is sent anywhere but Pending You.
package/dist/args.js CHANGED
@@ -46,19 +46,21 @@ const COMMANDS = new Set([
46
46
  'handoff',
47
47
  'init',
48
48
  'uninstall',
49
+ 'watch',
49
50
  ]);
50
51
  /** Flags each command takes; `true` when the flag takes a value. */
51
52
  const FLAGS = {
52
53
  help: {},
53
54
  version: {},
54
- login: { origin: true, 'no-browser': false, force: false },
55
+ login: { origin: true, 'no-browser': false, force: false, device: false },
55
56
  logout: { origin: true, all: false },
56
57
  status: { origin: true },
57
58
  hold: { origin: true, timeout: true },
58
59
  pickup: { origin: true },
59
60
  handoff: { origin: true },
60
- init: { origin: true, 'no-login': false, 'no-browser': false },
61
+ init: { origin: true, 'no-login': false, 'no-browser': false, device: false },
61
62
  uninstall: { origin: true },
63
+ watch: { origin: true, once: false },
62
64
  };
63
65
  export function parseArgs(argv, env = {}) {
64
66
  const [first, ...rest] = argv;
@@ -71,8 +73,14 @@ export function parseArgs(argv, env = {}) {
71
73
  const allowed = FLAGS[first] ?? {};
72
74
  const flags = new Map();
73
75
  const positional = [];
76
+ // `watch -- <command> [args…]`: everything after `--` is the command, taken as it is.
77
+ let command;
74
78
  for (let index = 0; index < rest.length; index++) {
75
79
  const arg = rest[index];
80
+ if (arg === '--' && first === 'watch') {
81
+ command = rest.slice(index + 1);
82
+ break;
83
+ }
76
84
  if (arg === '-h' || arg === '--help')
77
85
  return { name: 'help' };
78
86
  if (!arg.startsWith('--')) {
@@ -112,7 +120,13 @@ export function parseArgs(argv, env = {}) {
112
120
  return { name: 'version' };
113
121
  case 'login':
114
122
  none();
115
- return { name: 'login', origin, browser: !flags.has('no-browser'), force: flags.has('force') };
123
+ return {
124
+ name: 'login',
125
+ origin,
126
+ browser: !flags.has('no-browser'),
127
+ force: flags.has('force'),
128
+ device: flags.has('device'),
129
+ };
116
130
  case 'logout':
117
131
  none();
118
132
  return { name: 'logout', origin, all: flags.has('all') };
@@ -135,7 +149,13 @@ export function parseArgs(argv, env = {}) {
135
149
  origin,
136
150
  login: !flags.has('no-login'),
137
151
  browser: !flags.has('no-browser'),
152
+ device: flags.has('device'),
138
153
  };
154
+ case 'watch':
155
+ none();
156
+ if (!command?.length || !command[0])
157
+ throw new UsageError('watch needs a command after --, like: pendingyou watch -- ./on-answer.sh');
158
+ return { name: 'watch', origin, command, once: flags.has('once') };
139
159
  case 'hold': {
140
160
  if (positional.length !== 1)
141
161
  throw new UsageError('hold needs one request id, like: pendingyou hold req_0123…');
@@ -158,11 +178,14 @@ export const USAGE = `pendingyou: Pending You for Claude Code
158
178
  npx pendingyou hold <req_…> Wait in the background until the answer to a request is ready,
159
179
  print it, and exit (which wakes Claude Code)
160
180
  npx pendingyou status What's set up, and whether Claude Code hears answers right away
161
- npx pendingyou login | logout
181
+ npx pendingyou login [--device] | logout
182
+ --device: on a computer with no browser, approve a short code on your phone
183
+ npx pendingyou watch -- <command> [args…]
184
+ Run a command each time an answer is ready (always-on scripts)
162
185
  npx pendingyou uninstall Remove exactly what init added
163
186
  npx pendingyou pickup Answers waiting for this folder (the session-start hook)
164
187
  npx pendingyou handoff The same, only while a hold is outstanding (the next-message hook)
165
188
 
166
- Options: --origin <url> (default ${DEFAULT_ORIGIN}), --timeout 4h (hold), --no-browser (login, init),
167
- --no-login (init), --all (logout), --force (login).
189
+ Options: --origin <url> (default ${DEFAULT_ORIGIN}), --timeout 4h (hold), --no-browser and --device (login,
190
+ init), --no-login (init), --all (logout), --force (login), --once (watch).
168
191
  `;
package/dist/index.js CHANGED
@@ -3,5 +3,5 @@ export { getJson, SignInNeeded, Unavailable } from "./api.js";
3
3
  export { DEFAULT_ORIGIN, parseArgs } from "./args.js";
4
4
  export { readCredential, updateCredential } from "./credentials.js";
5
5
  export { main } from "./main.js";
6
- export { CLIENT_NAME, discover, register, revoke, SCOPE, signIn } from "./oauth.js";
6
+ export { CLIENT_NAME, discover, register, revoke, SCOPE, signIn, signInWithDevice, } from "./oauth.js";
7
7
  export { VERSION } from "./version.js";
package/dist/io.js CHANGED
@@ -28,6 +28,38 @@ export function runProgram(command, args, timeoutMs = 60_000) {
28
28
  child.on('close', (code) => resolve({ code: code ?? 1, stdout, stderr }));
29
29
  });
30
30
  }
31
+ export function execProgram(command, args, options) {
32
+ return new Promise((resolve) => {
33
+ let child;
34
+ try {
35
+ child = spawn(command, [...args], {
36
+ stdio: ['pipe', 'inherit', 'inherit'],
37
+ env: { ...process.env, ...options.env },
38
+ windowsHide: true,
39
+ });
40
+ }
41
+ catch {
42
+ resolve(127);
43
+ return;
44
+ }
45
+ let timedOut = false;
46
+ const stop = () => child.kill('SIGTERM');
47
+ const timer = setTimeout(() => {
48
+ timedOut = true;
49
+ stop();
50
+ }, options.timeoutMs);
51
+ options.signal?.addEventListener('abort', stop, { once: true });
52
+ const done = (code) => {
53
+ clearTimeout(timer);
54
+ options.signal?.removeEventListener('abort', stop);
55
+ resolve(code);
56
+ };
57
+ child.on('error', (error) => done(error.code === 'ENOENT' ? 127 : 1));
58
+ child.on('close', (code) => done(timedOut ? 124 : (code ?? 1)));
59
+ child.stdin?.on('error', () => { });
60
+ child.stdin?.end(options.stdin);
61
+ });
62
+ }
31
63
  export function sleep(ms, signal) {
32
64
  return new Promise((resolve) => {
33
65
  if (signal?.aborted || ms <= 0) {
@@ -93,6 +125,7 @@ export function realIo() {
93
125
  err: (text) => process.stderr.write(text),
94
126
  fetch: (input, init) => fetch(input, init),
95
127
  run: runProgram,
128
+ exec: execProgram,
96
129
  openBrowser,
97
130
  readStdin,
98
131
  now: () => Date.now(),
package/dist/main.js CHANGED
@@ -6,16 +6,21 @@ import { claudeState, installClaude, uninstallClaude } from "./claude.js";
6
6
  import { readCredential, signedInOrigins, updateCredential } from "./credentials.js";
7
7
  import { PlainError } from "./errors.js";
8
8
  import { hold } from "./hold.js";
9
- import { OAuthError, revoke, signIn } from "./oauth.js";
9
+ import { OAuthError, revoke, signIn, signInWithDevice } from "./oauth.js";
10
10
  import { pickup } from "./pickup.js";
11
11
  import { VERSION } from "./version.js";
12
+ import { watch } from "./watch.js";
12
13
  /** Whether this computer's sign-in works, and how many Claude Code connections it hears for. */
13
14
  async function checkSignIn(io, origin) {
14
15
  try {
15
16
  const { status, body } = await getJson(io, origin, '/mcp/cli/answers?limit=0', 15_000, io.signal);
16
17
  if (status !== 200)
17
18
  return { state: 'unreachable' };
18
- return { state: 'ok', connections: body.connections ?? 0 };
19
+ return {
20
+ state: 'ok',
21
+ connections: body.connections ?? 0,
22
+ ...(body.for ? { for: body.for } : {}),
23
+ };
19
24
  }
20
25
  catch (error) {
21
26
  if (error instanceof SignInNeeded)
@@ -32,10 +37,9 @@ async function login(io, options) {
32
37
  }
33
38
  }
34
39
  const previous = await readCredential(io, options.origin);
35
- const { clientId, tokens } = await signIn(io, {
36
- origin: options.origin,
37
- browser: options.browser,
38
- });
40
+ const { clientId, tokens } = options.device
41
+ ? await signInWithDevice(io, { origin: options.origin })
42
+ : await signIn(io, { origin: options.origin, browser: options.browser });
39
43
  await updateCredential(io, options.origin, () => ({
40
44
  clientId,
41
45
  ...tokens,
@@ -70,7 +74,9 @@ async function status(io, origin) {
70
74
  const lines = [`Pending You for Claude Code · ${origin}`];
71
75
  const mark = (ok) => (ok ? 'ok ' : 'missing');
72
76
  lines.push(` ${mark(signIn.state === 'ok')} Sign-in: ${signIn.state === 'ok'
73
- ? `hears for ${signIn.connections} Claude Code connection${signIn.connections === 1 ? '' : 's'}`
77
+ ? signIn.for
78
+ ? `hears for ${signIn.for.name} only (for pendingyou watch)`
79
+ : `hears for ${signIn.connections} Claude Code connection${signIn.connections === 1 ? '' : 's'}`
74
80
  : signIn.state === 'ended'
75
81
  ? 'ended; run npx pendingyou login'
76
82
  : signIn.state === 'none'
@@ -90,7 +96,12 @@ async function init(io, options) {
90
96
  io.out(` ${step.ok ? 'ok ' : 'check '} ${step.text}\n`);
91
97
  if (options.login) {
92
98
  io.out('Signing this computer in, so answers can wake Claude Code:\n');
93
- await login(io, { origin: options.origin, browser: options.browser, force: false });
99
+ await login(io, {
100
+ origin: options.origin,
101
+ browser: options.browser,
102
+ device: options.device,
103
+ force: false,
104
+ });
94
105
  }
95
106
  else
96
107
  io.out('Skipped signing in. Run npx pendingyou login when you’re ready.\n');
@@ -128,6 +139,8 @@ async function run(io, command) {
128
139
  return init(io, command);
129
140
  case 'uninstall':
130
141
  return uninstall(io, command.origin);
142
+ case 'watch':
143
+ return watch(io, command);
131
144
  }
132
145
  }
133
146
  export async function main(argv, io) {
package/dist/oauth.js CHANGED
@@ -233,3 +233,72 @@ export async function revoke(io, origin, clientId, refreshToken) {
233
233
  return false;
234
234
  }
235
235
  }
236
+ /**
237
+ * Device sign-in (RFC 8628) for a computer with no browser: shows a short code and where to enter it, then polls until
238
+ * the person presses Allow on their phone (or Cancel, or the code runs out).
239
+ */
240
+ export async function signInWithDevice(io, options) {
241
+ const metadata = await discover(io, options.origin);
242
+ const clientId = await register(io, metadata);
243
+ const started = await io.fetch(`${options.origin}/oauth/device`, {
244
+ method: 'POST',
245
+ headers: { 'content-type': 'application/x-www-form-urlencoded', accept: 'application/json' },
246
+ body: new URLSearchParams({ client_id: clientId, scope: SCOPE }).toString(),
247
+ signal: AbortSignal.timeout(15_000),
248
+ });
249
+ const start = (await started.json().catch(() => ({})));
250
+ if (!started.ok || typeof start.device_code !== 'string' || typeof start.user_code !== 'string')
251
+ throw new OAuthError(start.error ?? `http_${started.status}`, start.error === 'slow_down'
252
+ ? 'Too many sign-ins from here. Wait 10 minutes, then try again.'
253
+ : `Pending You didn’t start a device sign-in (${started.status}).`);
254
+ const page = start.verification_uri ?? `${options.origin}/device`;
255
+ if (new URL(page).origin !== options.origin)
256
+ throw new OAuthError('discovery', 'Pending You sent the sign-in page somewhere else.');
257
+ io.err(`On your phone or any browser, open ${page} and enter this code:\n\n ${start.user_code}\n\n` +
258
+ `Or go straight to ${start.verification_uri_complete ?? `${page}?code=${start.user_code}`}\nWaiting for you to press Allow…\n`);
259
+ let interval = Math.max(1, start.interval ?? 5) * 1000;
260
+ const deadline = io.now() + Math.max(60, start.expires_in ?? 600) * 1000;
261
+ for (;;) {
262
+ await io.sleep(interval, io.signal);
263
+ if (io.signal.aborted)
264
+ throw new OAuthError('interrupted', 'Signing in was stopped.');
265
+ if (io.now() > deadline)
266
+ throw new OAuthError('expired_token', 'The code ran out. Run the command again for a new one.');
267
+ const response = await io.fetch(`${options.origin}/oauth/device/token`, {
268
+ method: 'POST',
269
+ headers: { 'content-type': 'application/x-www-form-urlencoded', accept: 'application/json' },
270
+ body: new URLSearchParams({
271
+ grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
272
+ device_code: start.device_code,
273
+ client_id: clientId,
274
+ }).toString(),
275
+ signal: AbortSignal.timeout(20_000),
276
+ });
277
+ const body = (await response.json().catch(() => ({})));
278
+ if (response.ok &&
279
+ typeof body.access_token === 'string' &&
280
+ typeof body.refresh_token === 'string') {
281
+ const seconds = typeof body.expires_in === 'number' ? body.expires_in : 900;
282
+ return {
283
+ clientId,
284
+ tokens: {
285
+ accessToken: body.access_token,
286
+ refreshToken: body.refresh_token,
287
+ expiresAt: io.now() + seconds * 1000,
288
+ },
289
+ };
290
+ }
291
+ const error = typeof body.error === 'string' ? body.error : `http_${response.status}`;
292
+ if (error === 'authorization_pending')
293
+ continue;
294
+ if (error === 'slow_down') {
295
+ interval += 5000;
296
+ continue;
297
+ }
298
+ if (error === 'access_denied')
299
+ throw new OAuthError(error, 'You pressed Cancel, so this computer isn’t signed in.');
300
+ if (error === 'expired_token')
301
+ throw new OAuthError(error, 'The code ran out. Run the command again for a new one.');
302
+ throw new OAuthError(error, `Pending You refused the sign-in (${error}).`);
303
+ }
304
+ }
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // The command line's version, as published (package.json). A test keeps them the same.
2
- export const VERSION = '0.1.0';
2
+ export const VERSION = '0.2.0';
package/dist/watch.js ADDED
@@ -0,0 +1,132 @@
1
+ // `pendingyou watch -- <command> [args…]`: for an always-on script on an assistant's own machine (a Muse job, a cron
2
+ // box, a home server). It waits on Pending You for any of the sign-in's requests to become the assistant's move, and
3
+ // runs the command once for each: the answer after its 5-second hold, a message from the person, or a fallback that
4
+ // came due. Point the sign-in at one assistant on the consent page ("Hear answers for: Only Muse"); without that it
5
+ // watches the person's Claude Code assistants.
6
+ //
7
+ // The command runs without a shell, one at a time, with its output passed through. It gets the request on stdin as
8
+ // JSON (the same fields as /mcp/cli/answers: id, title, status, the answer in words, new messages, fallback) and, in
9
+ // the environment, PENDINGYOU_REQUEST_ID, PENDINGYOU_EVENT (answer_ready, message_ready or fallback_due),
10
+ // PENDINGYOU_VERSION (for ack_answer's expectedVersion) and PENDINGYOU_ORIGIN. Nothing about the answer goes in the
11
+ // environment or the arguments, where other programs could see it. The script then acts through its own assistant
12
+ // (get_request, ack_answer): watch only reads.
13
+ //
14
+ // A command that fails (a non-zero exit, or 5 minutes without finishing) is tried twice more, 5 and 15 seconds
15
+ // apart, then left: watch says so and moves on, so one bad answer can't wedge it. A command that can't be started at
16
+ // all stops watch (exit 127) without handing anything over. What was handed over is remembered
17
+ // (state.json), so a restart doesn't run anything twice. --once exits after the first, with the command's exit code.
18
+ import { getJson, SignInNeeded, Unavailable } from "./api.js";
19
+ import { readCredential } from "./credentials.js";
20
+ import { markHanded, momentOf, wasHanded } from "./state.js";
21
+ const LONG_POLL_SECONDS = 25;
22
+ const COMMAND_MS = 5 * 60 * 1000;
23
+ const RETRY_MS = [5000, 15_000];
24
+ const PATIENT_AFTER_MS = 30 * 60 * 1000;
25
+ const PATIENT_PAUSE_MS = 30_000;
26
+ const BACKOFF_MS = [2000, 5000, 10_000, 20_000, 30_000, 60_000];
27
+ export function eventOf(heard) {
28
+ if (heard.status === 'answered')
29
+ return 'answer_ready';
30
+ if (heard.status === 'expired')
31
+ return 'fallback_due';
32
+ return 'message_ready';
33
+ }
34
+ export async function watch(io, options) {
35
+ const { origin } = options;
36
+ const [program, ...args] = options.command;
37
+ const say = (text) => io.err(`pendingyou watch: ${text}\n`);
38
+ if (!(await readCredential(io, origin))) {
39
+ say('this computer isn’t signed in. Run `npx pendingyou login` (or `login --device` without a browser).');
40
+ return 1;
41
+ }
42
+ say(`watching ${origin}. Ctrl-C stops.`);
43
+ let since;
44
+ let quietSince = io.now();
45
+ let failures = 0;
46
+ for (;;) {
47
+ if (io.signal.aborted) {
48
+ say('stopped.');
49
+ return 0;
50
+ }
51
+ const query = new URLSearchParams({ limit: '20', wait: String(LONG_POLL_SECONDS) });
52
+ if (since)
53
+ query.set('since', since);
54
+ let result;
55
+ try {
56
+ result = await getJson(io, origin, `/mcp/cli/answers?${query}`, (LONG_POLL_SECONDS + 20) * 1000, io.signal);
57
+ }
58
+ catch (error) {
59
+ if (io.signal.aborted)
60
+ continue;
61
+ if (error instanceof SignInNeeded) {
62
+ say(error.ended
63
+ ? 'the sign-in has ended. Run `npx pendingyou login` again.'
64
+ : 'this computer isn’t signed in. Run `npx pendingyou login`.');
65
+ return 1;
66
+ }
67
+ const pause = error instanceof Unavailable && error.retryAfter
68
+ ? error.retryAfter * 1000
69
+ : BACKOFF_MS[Math.min(failures, BACKOFF_MS.length - 1)];
70
+ failures++;
71
+ await io.sleep(pause, io.signal);
72
+ continue;
73
+ }
74
+ failures = 0;
75
+ const heard = Array.isArray(result.body.requests) ? result.body.requests : [];
76
+ if (result.status !== 200) {
77
+ await io.sleep(BACKOFF_MS[1], io.signal);
78
+ continue;
79
+ }
80
+ for (const request of heard)
81
+ if (!since || request.changedAt > since)
82
+ since = request.changedAt;
83
+ // Oldest first, and only what this computer hasn't handed over yet.
84
+ const fresh = [];
85
+ for (const request of [...heard].sort((a, b) => a.changedAt.localeCompare(b.changedAt)))
86
+ if (!(await wasHanded(io, origin, request.requestId, momentOf(request))))
87
+ fresh.push(request);
88
+ for (const request of fresh) {
89
+ if (io.signal.aborted)
90
+ break;
91
+ const code = await runFor(io, program, args, origin, request, say);
92
+ // A command that can't even start is a mistake in the command line: stop, and hand nothing over.
93
+ if (code === 127)
94
+ return 127;
95
+ await markHanded(io, origin, [
96
+ { requestId: request.requestId, moment: momentOf(request), closed: true },
97
+ ]);
98
+ if (options.once)
99
+ return code;
100
+ }
101
+ if (fresh.length > 0)
102
+ quietSince = io.now();
103
+ else if (io.now() - quietSince > PATIENT_AFTER_MS)
104
+ await io.sleep(PATIENT_PAUSE_MS, io.signal);
105
+ }
106
+ }
107
+ /** Runs the command for one request, trying twice more if it fails. Returns its last exit code. */
108
+ async function runFor(io, program, args, origin, request, say) {
109
+ const env = {
110
+ PENDINGYOU_REQUEST_ID: request.requestId,
111
+ PENDINGYOU_EVENT: eventOf(request),
112
+ PENDINGYOU_VERSION: String(request.version),
113
+ PENDINGYOU_ORIGIN: origin,
114
+ };
115
+ const stdin = `${JSON.stringify(request)}\n`;
116
+ let code = 0;
117
+ for (let attempt = 0; attempt <= RETRY_MS.length; attempt++) {
118
+ if (attempt > 0)
119
+ await io.sleep(RETRY_MS[attempt - 1], io.signal);
120
+ if (io.signal.aborted)
121
+ return code;
122
+ code = await io.exec(program, args, { env, stdin, timeoutMs: COMMAND_MS, signal: io.signal });
123
+ if (code === 0)
124
+ return 0;
125
+ if (code === 127) {
126
+ say(`couldn’t start ${program}. Check the command after --. Stopping.`);
127
+ return code;
128
+ }
129
+ }
130
+ say(`the command failed for ${request.requestId} (exit ${code}) three times; moving on.`);
131
+ return code;
132
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pendingyou",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Pending You for Claude Code: hear your answers from Pending You in the background, and set Claude Code up in one command.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://www.pendingyou.com",