very-happy-cli 0.2.153 → 0.2.155

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/{AcpBackend-YDGv4E-g.mjs → AcpBackend-DVB0nzgR.mjs} +2 -2
  2. package/dist/{AcpBackend-DSt8nTYm.cjs → AcpBackend-DmFcRh54.cjs} +3 -3
  3. package/dist/{AcpSessionManager-uov6I9C8.cjs → AcpSessionManager-CWaSYTnT.cjs} +1 -1
  4. package/dist/{AcpSessionManager-Cxf5HQX1.mjs → AcpSessionManager-dGDgNb-p.mjs} +1 -1
  5. package/dist/agentGuidance-BWxFC6UL.mjs +1613 -0
  6. package/dist/agentGuidance-un7YPryB.cjs +1665 -0
  7. package/dist/{agentHome-D68Xh2ae.mjs → agentHome-ByLb2HFg.mjs} +5 -5
  8. package/dist/{agentHome-BJ7bde35.cjs → agentHome-aucT8BHw.cjs} +5 -5
  9. package/dist/{auto-CcXxfqkC.mjs → auto-BQnD6gVx.mjs} +21 -21
  10. package/dist/{auto-C-90ojIH.cjs → auto-P9RoJaY0.cjs} +24 -24
  11. package/dist/codex/happyMcpStdioBridge.cjs +6 -3
  12. package/dist/codex/happyMcpStdioBridge.mjs +6 -3
  13. package/dist/{config-CHJ189UJ.mjs → config-BPZ2Y6o8.mjs} +8 -8
  14. package/dist/{config-D23kF0_u.cjs → config-BoePh9PX.cjs} +8 -8
  15. package/dist/{index-D49WvbDT.cjs → index--KCpVs1c.cjs} +7 -7
  16. package/dist/{index-BacwoXzs.mjs → index-B4oXnbYw.mjs} +522 -729
  17. package/dist/{index-BgYZGODP.mjs → index-BjzdXb9x.mjs} +21 -13
  18. package/dist/{index-DEOVn46R.cjs → index-DRrLaljt.cjs} +21 -13
  19. package/dist/{index-BXA9fxbK.mjs → index-DVVRqaVG.mjs} +7 -7
  20. package/dist/{index-DHrwHxHf.cjs → index-D_fYUxVg.cjs} +579 -798
  21. package/dist/index.cjs +6 -6
  22. package/dist/index.mjs +6 -6
  23. package/dist/{install-BiOXBnnS.cjs → install-CGigiocs.cjs} +1 -1
  24. package/dist/{install-Ca7_Z3bV.mjs → install-DP5aPNEX.mjs} +1 -1
  25. package/dist/{installTerminalHooks-BF0kL0ae.cjs → installTerminalHooks-XrcPvfBs.cjs} +5 -5
  26. package/dist/{installTerminalHooks-Bpl32K75.mjs → installTerminalHooks-tIg7mANc.mjs} +5 -5
  27. package/dist/lib.cjs +2 -2
  28. package/dist/lib.mjs +2 -2
  29. package/dist/{limits-BtAuKRl2.mjs → limits-C_DW0M0q.mjs} +309 -13
  30. package/dist/{limits-DY_sFA3J.cjs → limits-DkfHJ3nS.cjs} +329 -12
  31. package/dist/{mcp-JpqI2POW.cjs → mcp-BqlXl10F.cjs} +23 -23
  32. package/dist/{mcp-DQkmk4VV.mjs → mcp-o7QglAQM.mjs} +17 -17
  33. package/dist/{piRuntime-DRbbeQ72.cjs → piRuntime-BxP4B_zY.cjs} +2 -2
  34. package/dist/{piRuntime-DBpe12NN.mjs → piRuntime-MjiJI-nV.mjs} +2 -2
  35. package/dist/{piTerminal-qdJ9KZVL.cjs → piTerminal-DYzRcwgb.cjs} +6 -6
  36. package/dist/{piTerminal-BoHvPEZ_.mjs → piTerminal-Dpq6yqKU.mjs} +6 -6
  37. package/dist/{runGemini-CtOEFvhf.cjs → runGemini-8Pxh94Fm.cjs} +7 -7
  38. package/dist/{runGemini-YqH7a-7O.mjs → runGemini-CDlcQ-OA.mjs} +6 -6
  39. package/dist/{runOpenClaw-DoqZXBvL.cjs → runOpenClaw-B5FSZIbK.cjs} +6 -6
  40. package/dist/{runOpenClaw-DSSyFQBb.mjs → runOpenClaw-YkhsgPs0.mjs} +5 -5
  41. package/dist/{send-9075kHZP.cjs → send-CwrYXTPr.cjs} +58 -54
  42. package/dist/{send-Cs-RwqNf.mjs → send-DJStmLBv.mjs} +56 -53
  43. package/dist/{sessions-iBnYzVUK.mjs → sessions-JaVXlSqj.mjs} +88 -45
  44. package/dist/{sessions-qULzUzlI.cjs → sessions-MX21NJXd.cjs} +99 -56
  45. package/dist/{spawn-C6fRkedx.mjs → spawn-BaPnjClT.mjs} +22 -22
  46. package/dist/{spawn-od9V23cc.cjs → spawn-cIoglKVf.cjs} +30 -30
  47. package/dist/{teams-CkrWQ4uM.mjs → teams-DXV5Pwwt.mjs} +8 -8
  48. package/dist/{teams-CrCzU-AV.cjs → teams-Dqk7F3D-.cjs} +8 -8
  49. package/dist/{todo-aCZmSt27.mjs → todo-2Z1REhfx.mjs} +8 -6
  50. package/dist/{todo-BCT_ZPuV.cjs → todo-Buuqn9gm.cjs} +8 -6
  51. package/dist/{types-BvqOVUOW.mjs → types-CkbfdRzq.mjs} +7 -267
  52. package/dist/{types-CUCU7Vw8.cjs → types-a2xsH2UI.cjs} +19 -295
  53. package/package.json +7 -7
  54. package/dist/agentGuidance-BM4kMNyr.cjs +0 -553
  55. package/dist/agentGuidance-C8unGWp7.mjs +0 -529
@@ -1,54 +1,20 @@
1
1
  import chalk from 'chalk';
2
- import { D as DEFAULT_RECENT_LIMIT, M as MAX_READ_LIMIT, l as listAccountSessions, a as listSessions, w as waitForTurnEnd, T as TurnWaitTimeoutError, r as readSessionTranscript, s as stopSession, b as resolvePermissionRequest, c as archiveSession } from './index-BacwoXzs.mjs';
3
- import { i as isValidSessionId } from './types-BvqOVUOW.mjs';
4
- import 'node:os';
2
+ import { D as DEFAULT_RECENT_LIMIT, M as MAX_READ_LIMIT, l as listAccountSessions, e as listSessions, f as cliPeerSelf, g as listPeerSessions, s as sendPeerMessage, w as waitForTurnEnd, T as TurnWaitTimeoutError, h as readSessionTranscript, i as stopSession, j as resolvePermissionRequest, k as archiveSession, m as PEER_SCOPES } from './agentGuidance-BWxFC6UL.mjs';
3
+ import { d as readPersistedSessions, i as isValidSessionId } from './limits-C_DW0M0q.mjs';
4
+ import { resolve } from 'node:path';
5
+ import 'zod';
5
6
  import 'node:crypto';
6
- import './limits-BtAuKRl2.mjs';
7
7
  import 'node:fs/promises';
8
8
  import 'node:fs';
9
- import 'node:path';
10
- import 'zod';
11
- import 'tweetnacl';
12
- import 'node:util';
13
- import '@paralleldrive/cuid2';
14
- import 'cross-spawn';
15
- import 'node:readline';
16
- import './terminalTitleSuggest-DKgX3osW.mjs';
17
- import 'path';
18
- import 'url';
19
- import './agentGuidance-C8unGWp7.mjs';
20
- import 'fs/promises';
21
- import 'ink';
22
- import 'react';
23
- import '@anthropic-ai/claude-agent-sdk';
9
+ import 'node:os';
24
10
  import 'axios';
25
- import 'node:events';
26
11
  import 'socket.io-client';
27
- import 'os';
28
- import 'child_process';
29
- import 'node:child_process';
30
- import 'fs';
31
- import '@anthropic-ai/sandbox-runtime';
32
- import 'expo-server-sdk';
33
- import 'crypto';
34
- import 'ps-list';
35
- import 'tmp';
36
- import 'open';
37
- import 'fastify';
38
- import 'fastify-type-provider-zod';
39
- import 'node:module';
40
- import '@modelcontextprotocol/sdk/server/mcp.js';
41
- import 'node:http';
42
- import '@modelcontextprotocol/sdk/server/streamableHttp.js';
43
- import 'http';
44
- import 'util';
45
- import 'inquirer';
46
- import 'node:url';
47
- import 'node:readline/promises';
48
- import 'node-pty';
12
+ import '@paralleldrive/cuid2';
13
+ import 'tweetnacl';
14
+ import 'node:util';
49
15
 
50
- const ACTIONS = ["list", "read", "stop", "archive", "approve", "deny"];
51
- const ACTIONS_NEEDING_ID = /* @__PURE__ */ new Set(["read", "stop", "archive", "approve", "deny"]);
16
+ const ACTIONS = ["list", "read", "stop", "archive", "approve", "deny", "peers", "message"];
17
+ const ACTIONS_NEEDING_ID = /* @__PURE__ */ new Set(["read", "stop", "archive", "approve", "deny", "message"]);
52
18
  const ACTIONS_NEEDING_REQUEST_ID = /* @__PURE__ */ new Set(["approve", "deny"]);
53
19
  const REQUEST_ID_RE = /^[a-zA-Z0-9_.:-]{1,128}$/;
54
20
  function defaultOptions() {
@@ -86,6 +52,19 @@ function parseSessionsArgs(args) {
86
52
  const value = args[++i];
87
53
  if (value === void 0) throw new Error("--reason requires a value");
88
54
  options.reason = value;
55
+ } else if (arg === "--scope") {
56
+ const value = args[++i];
57
+ if (value === void 0) throw new Error("--scope requires a value");
58
+ if (!PEER_SCOPES.includes(value)) throw new Error(`--scope must be one of ${PEER_SCOPES.join(", ")}`);
59
+ options.scope = value;
60
+ } else if (arg === "--cwd") {
61
+ const value = args[++i];
62
+ if (value === void 0) throw new Error("--cwd requires a value");
63
+ options.cwd = value;
64
+ } else if (arg === "--reply-to") {
65
+ const value = args[++i];
66
+ if (value === void 0 || value.trim() === "") throw new Error("--reply-to requires a value");
67
+ options.replyTo = value;
89
68
  } else if (arg === "--tag") {
90
69
  const value = args[++i];
91
70
  if (value === void 0) throw new Error("--tag requires a value");
@@ -109,7 +88,7 @@ function parseSessionsArgs(args) {
109
88
  }
110
89
  }
111
90
  if (!actionSeen) return defaultOptions();
112
- const expectedPositionals = ACTIONS_NEEDING_REQUEST_ID.has(options.action) ? 2 : ACTIONS_NEEDING_ID.has(options.action) ? 1 : 0;
91
+ const expectedPositionals = ACTIONS_NEEDING_REQUEST_ID.has(options.action) || options.action === "message" ? 2 : ACTIONS_NEEDING_ID.has(options.action) ? 1 : 0;
113
92
  if (positionals.length > expectedPositionals) {
114
93
  throw new Error(`Unexpected ${expectedPositionals > 0 ? "extra " : ""}argument: ${positionals[expectedPositionals]}`);
115
94
  }
@@ -119,6 +98,12 @@ function parseSessionsArgs(args) {
119
98
  if (!isValidSessionId(id)) throw new Error(`Invalid session id: ${id}`);
120
99
  options.sessionId = id;
121
100
  }
101
+ if (options.action === "message") {
102
+ const text = positionals[1];
103
+ if (text === void 0) throw new Error("message requires a session id and the text to send");
104
+ if (text.trim() === "") throw new Error("message text is empty");
105
+ options.text = text;
106
+ }
122
107
  if (ACTIONS_NEEDING_REQUEST_ID.has(options.action)) {
123
108
  const requestId = positionals[1];
124
109
  if (requestId === void 0) throw new Error(`${options.action} requires a session id and a request id`);
@@ -140,6 +125,11 @@ function parseSessionsArgs(args) {
140
125
  if (options.wait) throw new Error("--wait only applies to `sessions read`");
141
126
  }
142
127
  if (options.timeoutSec !== void 0 && !options.wait) throw new Error("--timeout only applies to `sessions read --wait`");
128
+ if (options.action !== "peers") {
129
+ if (options.scope !== void 0) throw new Error("--scope only applies to `sessions peers`");
130
+ if (options.cwd !== void 0) throw new Error("--cwd only applies to `sessions peers`");
131
+ }
132
+ if (options.replyTo !== void 0 && options.action !== "message") throw new Error("--reply-to only applies to `sessions message`");
143
133
  return options;
144
134
  }
145
135
  const SESSIONS_HELP = `
@@ -153,6 +143,8 @@ ${chalk.bold("Usage:")}
153
143
  very-happy sessions archive <id> [--json]
154
144
  very-happy sessions approve <id> <requestId> [--for-session] [--json]
155
145
  very-happy sessions deny <id> <requestId> [--reason <text>] [--json]
146
+ very-happy sessions peers [--scope repo|cwd|machine] [--cwd <dir>] [--json]
147
+ very-happy sessions message <id> <text> [--reply-to <msgId>] [--json]
156
148
 
157
149
  ${chalk.bold("Actions:")}
158
150
  list Running sessions plus recently seen ones, newest first.
@@ -169,6 +161,17 @@ ${chalk.bold("Actions:")}
169
161
  the same RPC the web permission card sends. --for-session makes
170
162
  it \`approved_for_session\` (the card's "allow for this session").
171
163
  deny Answer it with deny (optional --reason is shown to the agent).
164
+ peers Other live sessions on this machine and the files each edited in
165
+ the last 30 minutes. Default scope \`repo\`: same git repository,
166
+ including its other worktrees (\`sameWorktree\` says which share
167
+ your checkout); \`cwd\`: same directory; \`machine\`: everything.
168
+ message Send <text> into a running session here. It arrives tagged with
169
+ the sender (the session named by HAPPY_SESSION_ID when run from a
170
+ managed session's shell, else this CLI) and tells the peer how to
171
+ reply. Refused for sessions that are not running here, not
172
+ spawned by this machine, or terminal mirrors. \`delivered\`
173
+ follows \`very-happy send\`: true only when a wrapper was attached
174
+ before and after the POST (\`stored\` = it is on the server anyway).
172
175
 
173
176
  ${chalk.bold("Options:")}
174
177
  --all list only: account-wide over REST instead of this
@@ -190,6 +193,10 @@ ${chalk.bold("Options:")}
190
193
  --timeout <s> read --wait only: give up after <s> seconds (default 600).
191
194
  --for-session approve only: approved_for_session instead of approved.
192
195
  --reason <text> deny only: reason forwarded to the wrapper.
196
+ --scope <s> peers only: repo (default) | cwd | machine.
197
+ --cwd <dir> peers only: directory the scope is computed from
198
+ (default: the process cwd, or HAPPY_SESSION_ID's cwd).
199
+ --reply-to <id> message only: the peer message id being answered.
193
200
  --json Machine-readable output.
194
201
  -h, --help Show this help
195
202
 
@@ -290,6 +297,42 @@ async function handleSessionsCommand(args) {
290
297
  }
291
298
  process.exit(0);
292
299
  }
300
+ if (options.action === "peers" || options.action === "message") {
301
+ const cwd = options.cwd ? resolve(options.cwd) : process.cwd();
302
+ const persisted = readPersistedSessions();
303
+ const context = { self: () => cliPeerSelf(process.env, cwd, persisted), readPersisted: () => persisted };
304
+ if (options.action === "peers") {
305
+ const listing = await listPeerSessions(context, options.scope ?? "repo");
306
+ if (options.json) {
307
+ console.log(JSON.stringify(listing));
308
+ } else if (listing.peers.length === 0) {
309
+ console.log(`No other live sessions in scope "${listing.scope}"${listing.self.repoRoot ? ` (repo ${listing.self.repoRoot})` : ""}.`);
310
+ } else {
311
+ for (const peer of listing.peers) {
312
+ const parts = [peer.sessionId, `[${peer.kind === "mirror" ? "terminal" : "running"}]`];
313
+ if (peer.title) parts.push(`title="${peer.title}"`);
314
+ if (peer.flavor) parts.push(`agent=${peer.flavor}`);
315
+ if (peer.cwd) parts.push(`cwd=${peer.cwd}${peer.sameWorktree ? "" : " (other worktree)"}`);
316
+ parts.push(peer.url);
317
+ console.log(parts.join(" "));
318
+ for (const edit of peer.edits) console.log(chalk.dim(` ${edit.tool} ${edit.path} (${Math.round((Date.now() - edit.at) / 6e4)}m ago)`));
319
+ }
320
+ }
321
+ process.exit(0);
322
+ }
323
+ const target = options.sessionId;
324
+ try {
325
+ const result = await sendPeerMessage(context, { to: target, body: options.text, replyTo: options.replyTo });
326
+ if (options.json) console.log(JSON.stringify(result));
327
+ else if (result.delivered) console.log(`Message ${result.messageId} delivered to ${target}
328
+ ${result.url}`);
329
+ else console.error(chalk.yellow("Not delivered:"), result.error ?? `session is ${result.status}`, result.stored ? "(stored server-side, unread)" : "");
330
+ process.exit(result.delivered ? 0 : 1);
331
+ } catch (error) {
332
+ if (options.json) console.log(JSON.stringify({ delivered: false, to: target, error: error instanceof Error ? error.message : String(error) }));
333
+ throw error;
334
+ }
335
+ }
293
336
  const sessionId = options.sessionId;
294
337
  if (options.action === "read") {
295
338
  let timedOut = false;
@@ -1,56 +1,22 @@
1
1
  'use strict';
2
2
 
3
3
  var chalk = require('chalk');
4
- var index = require('./index-DHrwHxHf.cjs');
5
- var index$1 = require('./types-CUCU7Vw8.cjs');
6
- require('node:os');
4
+ var agentGuidance = require('./agentGuidance-un7YPryB.cjs');
5
+ var persistence = require('./limits-DkfHJ3nS.cjs');
6
+ var path = require('node:path');
7
+ require('zod');
7
8
  require('node:crypto');
8
- require('./limits-DY_sFA3J.cjs');
9
9
  require('node:fs/promises');
10
10
  require('node:fs');
11
- require('node:path');
12
- require('zod');
13
- require('tweetnacl');
14
- require('node:util');
15
- require('@paralleldrive/cuid2');
16
- require('cross-spawn');
17
- require('node:readline');
18
- require('./terminalTitleSuggest-Dji8I0Jy.cjs');
19
- require('path');
20
- require('url');
21
- require('./agentGuidance-BM4kMNyr.cjs');
22
- require('fs/promises');
23
- require('ink');
24
- require('react');
25
- require('@anthropic-ai/claude-agent-sdk');
11
+ require('node:os');
26
12
  require('axios');
27
- require('node:events');
28
13
  require('socket.io-client');
29
- require('os');
30
- require('child_process');
31
- require('node:child_process');
32
- require('fs');
33
- require('@anthropic-ai/sandbox-runtime');
34
- require('expo-server-sdk');
35
- require('crypto');
36
- require('ps-list');
37
- require('tmp');
38
- require('open');
39
- require('fastify');
40
- require('fastify-type-provider-zod');
41
- require('node:module');
42
- require('@modelcontextprotocol/sdk/server/mcp.js');
43
- require('node:http');
44
- require('@modelcontextprotocol/sdk/server/streamableHttp.js');
45
- require('http');
46
- require('util');
47
- require('inquirer');
48
- require('node:url');
49
- require('node:readline/promises');
50
- require('node-pty');
14
+ require('@paralleldrive/cuid2');
15
+ require('tweetnacl');
16
+ require('node:util');
51
17
 
52
- const ACTIONS = ["list", "read", "stop", "archive", "approve", "deny"];
53
- const ACTIONS_NEEDING_ID = /* @__PURE__ */ new Set(["read", "stop", "archive", "approve", "deny"]);
18
+ const ACTIONS = ["list", "read", "stop", "archive", "approve", "deny", "peers", "message"];
19
+ const ACTIONS_NEEDING_ID = /* @__PURE__ */ new Set(["read", "stop", "archive", "approve", "deny", "message"]);
54
20
  const ACTIONS_NEEDING_REQUEST_ID = /* @__PURE__ */ new Set(["approve", "deny"]);
55
21
  const REQUEST_ID_RE = /^[a-zA-Z0-9_.:-]{1,128}$/;
56
22
  function defaultOptions() {
@@ -88,6 +54,19 @@ function parseSessionsArgs(args) {
88
54
  const value = args[++i];
89
55
  if (value === void 0) throw new Error("--reason requires a value");
90
56
  options.reason = value;
57
+ } else if (arg === "--scope") {
58
+ const value = args[++i];
59
+ if (value === void 0) throw new Error("--scope requires a value");
60
+ if (!agentGuidance.PEER_SCOPES.includes(value)) throw new Error(`--scope must be one of ${agentGuidance.PEER_SCOPES.join(", ")}`);
61
+ options.scope = value;
62
+ } else if (arg === "--cwd") {
63
+ const value = args[++i];
64
+ if (value === void 0) throw new Error("--cwd requires a value");
65
+ options.cwd = value;
66
+ } else if (arg === "--reply-to") {
67
+ const value = args[++i];
68
+ if (value === void 0 || value.trim() === "") throw new Error("--reply-to requires a value");
69
+ options.replyTo = value;
91
70
  } else if (arg === "--tag") {
92
71
  const value = args[++i];
93
72
  if (value === void 0) throw new Error("--tag requires a value");
@@ -111,16 +90,22 @@ function parseSessionsArgs(args) {
111
90
  }
112
91
  }
113
92
  if (!actionSeen) return defaultOptions();
114
- const expectedPositionals = ACTIONS_NEEDING_REQUEST_ID.has(options.action) ? 2 : ACTIONS_NEEDING_ID.has(options.action) ? 1 : 0;
93
+ const expectedPositionals = ACTIONS_NEEDING_REQUEST_ID.has(options.action) || options.action === "message" ? 2 : ACTIONS_NEEDING_ID.has(options.action) ? 1 : 0;
115
94
  if (positionals.length > expectedPositionals) {
116
95
  throw new Error(`Unexpected ${expectedPositionals > 0 ? "extra " : ""}argument: ${positionals[expectedPositionals]}`);
117
96
  }
118
97
  if (ACTIONS_NEEDING_ID.has(options.action)) {
119
98
  const id = positionals[0];
120
99
  if (id === void 0) throw new Error(`${options.action} requires a session id`);
121
- if (!index$1.isValidSessionId(id)) throw new Error(`Invalid session id: ${id}`);
100
+ if (!persistence.isValidSessionId(id)) throw new Error(`Invalid session id: ${id}`);
122
101
  options.sessionId = id;
123
102
  }
103
+ if (options.action === "message") {
104
+ const text = positionals[1];
105
+ if (text === void 0) throw new Error("message requires a session id and the text to send");
106
+ if (text.trim() === "") throw new Error("message text is empty");
107
+ options.text = text;
108
+ }
124
109
  if (ACTIONS_NEEDING_REQUEST_ID.has(options.action)) {
125
110
  const requestId = positionals[1];
126
111
  if (requestId === void 0) throw new Error(`${options.action} requires a session id and a request id`);
@@ -142,6 +127,11 @@ function parseSessionsArgs(args) {
142
127
  if (options.wait) throw new Error("--wait only applies to `sessions read`");
143
128
  }
144
129
  if (options.timeoutSec !== void 0 && !options.wait) throw new Error("--timeout only applies to `sessions read --wait`");
130
+ if (options.action !== "peers") {
131
+ if (options.scope !== void 0) throw new Error("--scope only applies to `sessions peers`");
132
+ if (options.cwd !== void 0) throw new Error("--cwd only applies to `sessions peers`");
133
+ }
134
+ if (options.replyTo !== void 0 && options.action !== "message") throw new Error("--reply-to only applies to `sessions message`");
145
135
  return options;
146
136
  }
147
137
  const SESSIONS_HELP = `
@@ -155,6 +145,8 @@ ${chalk.bold("Usage:")}
155
145
  very-happy sessions archive <id> [--json]
156
146
  very-happy sessions approve <id> <requestId> [--for-session] [--json]
157
147
  very-happy sessions deny <id> <requestId> [--reason <text>] [--json]
148
+ very-happy sessions peers [--scope repo|cwd|machine] [--cwd <dir>] [--json]
149
+ very-happy sessions message <id> <text> [--reply-to <msgId>] [--json]
158
150
 
159
151
  ${chalk.bold("Actions:")}
160
152
  list Running sessions plus recently seen ones, newest first.
@@ -171,6 +163,17 @@ ${chalk.bold("Actions:")}
171
163
  the same RPC the web permission card sends. --for-session makes
172
164
  it \`approved_for_session\` (the card's "allow for this session").
173
165
  deny Answer it with deny (optional --reason is shown to the agent).
166
+ peers Other live sessions on this machine and the files each edited in
167
+ the last 30 minutes. Default scope \`repo\`: same git repository,
168
+ including its other worktrees (\`sameWorktree\` says which share
169
+ your checkout); \`cwd\`: same directory; \`machine\`: everything.
170
+ message Send <text> into a running session here. It arrives tagged with
171
+ the sender (the session named by HAPPY_SESSION_ID when run from a
172
+ managed session's shell, else this CLI) and tells the peer how to
173
+ reply. Refused for sessions that are not running here, not
174
+ spawned by this machine, or terminal mirrors. \`delivered\`
175
+ follows \`very-happy send\`: true only when a wrapper was attached
176
+ before and after the POST (\`stored\` = it is on the server anyway).
174
177
 
175
178
  ${chalk.bold("Options:")}
176
179
  --all list only: account-wide over REST instead of this
@@ -180,8 +183,8 @@ ${chalk.bold("Options:")}
180
183
  \`spawn --spawned-by\`). With --all only decryptable rows
181
184
  can match.
182
185
  --limit <n> list: how many NOT-running / not-attention sessions to
183
- include (default ${index.DEFAULT_RECENT_LIMIT}; running and attention rows are
184
- never cut). read: how many messages (default 20, max ${index.MAX_READ_LIMIT}).
186
+ include (default ${agentGuidance.DEFAULT_RECENT_LIMIT}; running and attention rows are
187
+ never cut). read: how many messages (default 20, max ${agentGuidance.MAX_READ_LIMIT}).
185
188
  --full read only: do not truncate entries (default caps each
186
189
  line at 500 chars).
187
190
  --answer read only: print just the agent's reply to the latest
@@ -192,6 +195,10 @@ ${chalk.bold("Options:")}
192
195
  --timeout <s> read --wait only: give up after <s> seconds (default 600).
193
196
  --for-session approve only: approved_for_session instead of approved.
194
197
  --reason <text> deny only: reason forwarded to the wrapper.
198
+ --scope <s> peers only: repo (default) | cwd | machine.
199
+ --cwd <dir> peers only: directory the scope is computed from
200
+ (default: the process cwd, or HAPPY_SESSION_ID's cwd).
201
+ --reply-to <id> message only: the peer message id being answered.
195
202
  --json Machine-readable output.
196
203
  -h, --help Show this help
197
204
 
@@ -267,7 +274,7 @@ async function handleSessionsCommand(args) {
267
274
  }
268
275
  try {
269
276
  if (options.action === "list" && options.all) {
270
- const sessions = await index.listAccountSessions({ tag: options.tag, recentLimit: options.limit, includeArchived: options.includeArchived });
277
+ const sessions = await agentGuidance.listAccountSessions({ tag: options.tag, recentLimit: options.limit, includeArchived: options.includeArchived });
271
278
  if (options.json) {
272
279
  console.log(JSON.stringify({ sessions, scope: "account" }));
273
280
  } else if (sessions.length === 0) {
@@ -282,7 +289,7 @@ async function handleSessionsCommand(args) {
282
289
  process.exit(0);
283
290
  }
284
291
  if (options.action === "list") {
285
- const sessions = await index.listSessions({ tag: options.tag, recentLimit: options.limit });
292
+ const sessions = await agentGuidance.listSessions({ tag: options.tag, recentLimit: options.limit });
286
293
  if (options.json) {
287
294
  console.log(JSON.stringify({ sessions }));
288
295
  } else if (sessions.length === 0) {
@@ -292,19 +299,55 @@ async function handleSessionsCommand(args) {
292
299
  }
293
300
  process.exit(0);
294
301
  }
302
+ if (options.action === "peers" || options.action === "message") {
303
+ const cwd = options.cwd ? path.resolve(options.cwd) : process.cwd();
304
+ const persisted = persistence.readPersistedSessions();
305
+ const context = { self: () => agentGuidance.cliPeerSelf(process.env, cwd, persisted), readPersisted: () => persisted };
306
+ if (options.action === "peers") {
307
+ const listing = await agentGuidance.listPeerSessions(context, options.scope ?? "repo");
308
+ if (options.json) {
309
+ console.log(JSON.stringify(listing));
310
+ } else if (listing.peers.length === 0) {
311
+ console.log(`No other live sessions in scope "${listing.scope}"${listing.self.repoRoot ? ` (repo ${listing.self.repoRoot})` : ""}.`);
312
+ } else {
313
+ for (const peer of listing.peers) {
314
+ const parts = [peer.sessionId, `[${peer.kind === "mirror" ? "terminal" : "running"}]`];
315
+ if (peer.title) parts.push(`title="${peer.title}"`);
316
+ if (peer.flavor) parts.push(`agent=${peer.flavor}`);
317
+ if (peer.cwd) parts.push(`cwd=${peer.cwd}${peer.sameWorktree ? "" : " (other worktree)"}`);
318
+ parts.push(peer.url);
319
+ console.log(parts.join(" "));
320
+ for (const edit of peer.edits) console.log(chalk.dim(` ${edit.tool} ${edit.path} (${Math.round((Date.now() - edit.at) / 6e4)}m ago)`));
321
+ }
322
+ }
323
+ process.exit(0);
324
+ }
325
+ const target = options.sessionId;
326
+ try {
327
+ const result = await agentGuidance.sendPeerMessage(context, { to: target, body: options.text, replyTo: options.replyTo });
328
+ if (options.json) console.log(JSON.stringify(result));
329
+ else if (result.delivered) console.log(`Message ${result.messageId} delivered to ${target}
330
+ ${result.url}`);
331
+ else console.error(chalk.yellow("Not delivered:"), result.error ?? `session is ${result.status}`, result.stored ? "(stored server-side, unread)" : "");
332
+ process.exit(result.delivered ? 0 : 1);
333
+ } catch (error) {
334
+ if (options.json) console.log(JSON.stringify({ delivered: false, to: target, error: error instanceof Error ? error.message : String(error) }));
335
+ throw error;
336
+ }
337
+ }
295
338
  const sessionId = options.sessionId;
296
339
  if (options.action === "read") {
297
340
  let timedOut = false;
298
341
  if (options.wait) {
299
342
  try {
300
- await index.waitForTurnEnd(sessionId, { timeoutMs: (options.timeoutSec ?? 600) * 1e3 });
343
+ await agentGuidance.waitForTurnEnd(sessionId, { timeoutMs: (options.timeoutSec ?? 600) * 1e3 });
301
344
  } catch (error) {
302
- if (!(error instanceof index.TurnWaitTimeoutError)) throw error;
345
+ if (!(error instanceof agentGuidance.TurnWaitTimeoutError)) throw error;
303
346
  timedOut = true;
304
347
  console.error(chalk.yellow("Timeout:"), error.message);
305
348
  }
306
349
  }
307
- const result = await index.readSessionTranscript(sessionId, options.limit ?? 20, { full: options.full });
350
+ const result = await agentGuidance.readSessionTranscript(sessionId, options.limit ?? 20, { full: options.full });
308
351
  if (options.json) {
309
352
  console.log(JSON.stringify(options.answer ? { sessionId, turn: result.turn } : result));
310
353
  } else if (options.answer) {
@@ -320,7 +363,7 @@ async function handleSessionsCommand(args) {
320
363
  process.exit(timedOut ? 2 : 0);
321
364
  }
322
365
  if (options.action === "stop") {
323
- const stopped = await index.stopSession(sessionId);
366
+ const stopped = await agentGuidance.stopSession(sessionId);
324
367
  if (!stopped) {
325
368
  if (options.json) console.log(JSON.stringify({ sessionId, stopped: false }));
326
369
  console.error(chalk.red("Error:"), `Session ${sessionId} is not among the daemon's running sessions.`);
@@ -335,7 +378,7 @@ async function handleSessionsCommand(args) {
335
378
  const verdict = options.action === "approve" ? { kind: "approve", forSession: options.forSession } : { kind: "deny", reason: options.reason };
336
379
  let result;
337
380
  try {
338
- result = await index.resolvePermissionRequest(sessionId, requestId, verdict, {
381
+ result = await agentGuidance.resolvePermissionRequest(sessionId, requestId, verdict, {
339
382
  // T-014: a rate refusal waits (bounded) before re-sending;
340
383
  // say so on stderr so the wait does not look like a hang.
341
384
  onRateLimited: ({ attempt, waitMs, serverError }) => {
@@ -360,7 +403,7 @@ async function handleSessionsCommand(args) {
360
403
  }
361
404
  process.exit(0);
362
405
  }
363
- await index.archiveSession(sessionId);
406
+ await agentGuidance.archiveSession(sessionId);
364
407
  if (options.json) console.log(JSON.stringify({ sessionId, archived: true }));
365
408
  else console.log(`Archived ${sessionId}`);
366
409
  process.exit(0);
@@ -1,38 +1,36 @@
1
1
  import chalk from 'chalk';
2
2
  import { readFileSync, statSync } from 'node:fs';
3
3
  import { resolve } from 'node:path';
4
- import { f as forkSession, g as getProjectPath, F as ForkSourceMissingError, C as CodexAppServerClient, h as forkCodexThread, j as checkIfDaemonRunningAndCleanupStaleState, s as spawnDaemonSession, k as stopDaemonSession } from './types-BvqOVUOW.mjs';
5
- import { d as readPersistedSessions, l as logger } from './limits-BtAuKRl2.mjs';
6
- import { k as readSessionMetadata, m as resolveSpawnPermissionMode, e as sessionWebUrl, n as resolveFirstMessageMeta, o as isValidSpawnOrigin, i as sanitizeSpawnPermissionMode, A as ALLOWED_SPAWN_PERMISSION_MODES, S as SPAWN_AGENTS, u as waitForSessionKey, d as sendUserMessage } from './index-BacwoXzs.mjs';
7
- import 'axios';
8
- import 'node:events';
9
- import 'socket.io-client';
4
+ import { d as readPersistedSessions, k as checkIfDaemonRunningAndCleanupStaleState, l as logger, m as spawnDaemonSession, n as stopDaemonSession } from './limits-C_DW0M0q.mjs';
5
+ import { r as resolveSpawnPermissionMode, c as resolveFirstMessageMeta, i as isValidSpawnOrigin, s as sanitizeSpawnPermissionMode, A as ALLOWED_SPAWN_PERMISSION_MODES, S as SPAWN_AGENTS } from './index-B4oXnbYw.mjs';
6
+ import { f as forkSession, g as getProjectPath, F as ForkSourceMissingError, C as CodexAppServerClient, c as forkCodexThread } from './types-CkbfdRzq.mjs';
7
+ import { x as readSessionMetadata, o as sessionWebUrl, y as waitForSessionKey, z as sendUserMessage } from './agentGuidance-BWxFC6UL.mjs';
10
8
  import 'zod';
9
+ import '@paralleldrive/cuid2';
10
+ import 'node:fs/promises';
11
11
  import 'node:crypto';
12
- import 'child_process';
13
- import 'util';
14
- import 'fs/promises';
15
- import 'crypto';
16
- import 'path';
12
+ import 'tweetnacl';
13
+ import 'node:util';
14
+ import 'node:os';
17
15
  import 'cross-spawn';
16
+ import 'node:readline';
18
17
  import './terminalTitleSuggest-DKgX3osW.mjs';
18
+ import 'path';
19
19
  import 'url';
20
+ import 'fs/promises';
21
+ import 'ink';
22
+ import 'react';
23
+ import '@anthropic-ai/claude-agent-sdk';
24
+ import 'axios';
25
+ import 'node:events';
26
+ import 'socket.io-client';
20
27
  import 'os';
21
- import '@paralleldrive/cuid2';
22
- import 'node:fs/promises';
23
- import 'node:os';
28
+ import 'child_process';
24
29
  import 'node:child_process';
25
- import 'node-pty';
26
30
  import 'fs';
27
- import 'tweetnacl';
28
- import 'node:readline';
29
31
  import '@anthropic-ai/sandbox-runtime';
30
32
  import 'expo-server-sdk';
31
- import 'node:util';
32
- import './agentGuidance-C8unGWp7.mjs';
33
- import 'ink';
34
- import 'react';
35
- import '@anthropic-ai/claude-agent-sdk';
33
+ import 'crypto';
36
34
  import 'ps-list';
37
35
  import 'tmp';
38
36
  import 'open';
@@ -43,9 +41,11 @@ import '@modelcontextprotocol/sdk/server/mcp.js';
43
41
  import 'node:http';
44
42
  import '@modelcontextprotocol/sdk/server/streamableHttp.js';
45
43
  import 'http';
44
+ import 'util';
46
45
  import 'inquirer';
47
46
  import 'node:url';
48
47
  import 'node:readline/promises';
48
+ import 'node-pty';
49
49
 
50
50
  function nonEmpty(value) {
51
51
  return typeof value === "string" && value.trim().length > 0;