minivibe 0.2.0 → 0.2.2

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 (4) hide show
  1. package/README.md +27 -37
  2. package/agent/agent.js +42 -34
  3. package/package.json +1 -1
  4. package/vibe.js +118 -65
package/README.md CHANGED
@@ -16,21 +16,19 @@ CLI wrapper for Claude Code with mobile remote control via MiniVibe iOS app.
16
16
  ## Quick Start
17
17
 
18
18
  ```bash
19
- # Install globally from npm
19
+ # Install
20
20
  npm install -g minivibe
21
21
 
22
- # Authenticate (one-time)
23
- vibe --login # Desktop (opens browser)
24
- vibe --login --headless # Server/EC2 (device code)
25
-
26
- # Option 1: Direct bridge connection
27
- vibe --bridge wss://ws.minivibeapp.com
22
+ # Login (one-time)
23
+ vibe --login
28
24
 
29
- # Option 2: Agent mode (recommended for servers)
30
- vibe-agent --bridge wss://ws.minivibeapp.com & # Start agent
31
- vibe --agent # Create sessions
25
+ # Start coding with remote control!
26
+ vibe
27
+ vibe "Fix the bug in main.js"
32
28
  ```
33
29
 
30
+ > **Note:** For local-only use without remote control, just run `claude` directly.
31
+
34
32
  ## Installation
35
33
 
36
34
  ### From npm (Recommended)
@@ -80,22 +78,24 @@ Get token from MiniVibe iOS app: Settings > Copy Token for CLI.
80
78
 
81
79
  ## Usage Modes
82
80
 
83
- ### Direct Bridge Mode
81
+ ### Direct Mode (Default)
84
82
 
85
- Connect directly to the bridge server:
83
+ Just run `vibe` after logging in:
86
84
 
87
85
  ```bash
88
- vibe --bridge wss://ws.minivibeapp.com
89
- vibe --bridge wss://ws.minivibeapp.com "Fix the bug in main.js"
86
+ vibe # Start session
87
+ vibe "Fix the bug" # With initial prompt
88
+ vibe --e2e # With end-to-end encryption
90
89
  ```
91
90
 
92
91
  ### Agent Mode (Recommended for Servers)
93
92
 
94
- Use a local agent to manage sessions:
93
+ Use a local agent to manage multiple sessions:
95
94
 
96
95
  ```bash
97
96
  # Terminal 1: Start the agent (runs continuously)
98
- vibe-agent --bridge wss://ws.minivibeapp.com
97
+ vibe-agent --login # First time only
98
+ vibe-agent # Start daemon
99
99
 
100
100
  # Terminal 2+: Create sessions via agent
101
101
  vibe --agent
@@ -109,34 +109,25 @@ vibe --agent --name "Backend Work"
109
109
  - Sessions survive network hiccups
110
110
  - Cleaner process management
111
111
 
112
- ### Local Mode (No Bridge)
113
-
114
- Run without remote control:
115
-
116
- ```bash
117
- vibe # Interactive
118
- vibe "Explain this code" # With prompt
119
- ```
120
-
121
112
  ## Options
122
113
 
123
114
  ### vibe
124
115
 
125
116
  | Option | Description |
126
117
  |--------|-------------|
127
- | `--bridge <url>` | Connect to bridge server |
118
+ | `--login` | Sign in with Google |
119
+ | `--headless` | Use device code flow for headless environments |
128
120
  | `--agent [url]` | Connect via local vibe-agent (default: auto-discover) |
129
121
  | `--name <name>` | Name this session (shown in mobile app) |
130
122
  | `--resume <id>` | Resume a previous session (auto-detects directory) |
131
123
  | `--attach <id>` | Attach to running session via local agent |
132
124
  | `--remote <id>` | Remote control session via bridge (no local Claude needed) |
133
125
  | `--list` | List running sessions on local agent |
134
- | `--login` | Sign in with Google |
135
- | `--headless` | Use device code flow for headless environments |
126
+ | `--e2e` | Enable end-to-end encryption (auto key exchange with iOS) |
127
+ | `--dangerously-skip-permissions` | Auto-approve all tool executions |
128
+ | `--bridge <url>` | Override bridge URL (default: wss://ws.minivibeapp.com) |
136
129
  | `--token <token>` | Set Firebase auth token manually |
137
130
  | `--logout` | Remove stored auth token |
138
- | `--dangerously-skip-permissions` | Auto-approve all tool executions |
139
- | `--e2e` | Enable end-to-end encryption (auto key exchange with iOS) |
140
131
  | `--node-pty` | Use Node.js PTY wrapper (required for Windows) |
141
132
  | `--help, -h` | Show help message |
142
133
 
@@ -144,11 +135,11 @@ vibe "Explain this code" # With prompt
144
135
 
145
136
  | Option | Description |
146
137
  |--------|-------------|
147
- | `--bridge <url>` | Bridge server URL (required) |
148
- | `--login` | Start device code login flow |
149
- | `--token <token>` | Use specific Firebase token |
138
+ | `--login` | Sign in via device code flow |
150
139
  | `--name <name>` | Set host display name |
151
140
  | `--status` | Show current status and exit |
141
+ | `--bridge <url>` | Override bridge URL (default: wss://ws.minivibeapp.com) |
142
+ | `--token <token>` | Use specific Firebase token |
152
143
  | `--help, -h` | Show help message |
153
144
 
154
145
  ## Skip Permissions Mode
@@ -156,7 +147,7 @@ vibe "Explain this code" # With prompt
156
147
  For automated/headless environments where you trust the execution context:
157
148
 
158
149
  ```bash
159
- vibe --dangerously-skip-permissions --bridge wss://ws.minivibeapp.com
150
+ vibe --dangerously-skip-permissions
160
151
  vibe --dangerously-skip-permissions --agent
161
152
  ```
162
153
 
@@ -168,7 +159,7 @@ Enable E2E encryption to ensure the bridge server cannot read your message conte
168
159
 
169
160
  ```bash
170
161
  # Start with E2E encryption enabled
171
- vibe --e2e --bridge wss://ws.minivibeapp.com
162
+ vibe --e2e
172
163
  ```
173
164
 
174
165
  Key exchange happens automatically when both CLI and iOS connect to the bridge:
@@ -207,7 +198,7 @@ Key exchange happens automatically when both CLI and iOS connect to the bridge:
207
198
  └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
208
199
  ```
209
200
 
210
- **Direct mode:** `vibe --bridge` connects directly to bridge server
201
+ **Direct mode:** `vibe` connects directly to bridge server
211
202
 
212
203
  **Agent mode:** `vibe --agent` connects to local `vibe-agent`, which manages bridge connection
213
204
 
@@ -238,7 +229,6 @@ May also need Visual Studio Build Tools and Python for native compilation.
238
229
  | Path | Description |
239
230
  |------|-------------|
240
231
  | `~/.vibe/auth.json` | Stored authentication (token + refresh token) |
241
- | `~/.vibe/token` | Legacy token file |
242
232
  | `~/.vibe/e2e-keys.json` | E2E encryption keypair and peer info |
243
233
  | `~/.vibe-agent/port` | Agent port file for auto-discovery |
244
234
 
package/agent/agent.js CHANGED
@@ -9,8 +9,8 @@
9
9
  * - Stop running sessions
10
10
  *
11
11
  * Usage:
12
- * vibe-agent --bridge wss://ws.minivibeapp.com --token <firebase-token>
13
- * vibe-agent --login --bridge wss://ws.minivibeapp.com
12
+ * vibe-agent --login Sign in (one-time)
13
+ * vibe-agent Start agent daemon
14
14
  */
15
15
 
16
16
  const { spawn, execSync } = require('child_process');
@@ -35,6 +35,22 @@ const HEARTBEAT_INTERVAL_MS = 30000;
35
35
  const LOCAL_SERVER_PORT = 9999;
36
36
  const PORT_FILE = path.join(os.homedir(), '.vibe-agent', 'port');
37
37
  const MAX_SESSION_HISTORY_AGE_DAYS = 30;
38
+ const DEFAULT_BRIDGE_URL = 'wss://ws.minivibeapp.com';
39
+
40
+ // Show welcome message for first-time users (no auth)
41
+ function showWelcomeMessage() {
42
+ console.log(`
43
+ Welcome to vibe-agent!
44
+
45
+ vibe-agent lets you manage Claude Code sessions from your iPhone.
46
+
47
+ To get started:
48
+ 1. Download MiniVibe from the App Store
49
+ 2. Run: vibe-agent --login
50
+
51
+ For help: vibe-agent --help
52
+ `);
53
+ }
38
54
 
39
55
  // Colors for terminal output
40
56
  const colors = {
@@ -225,9 +241,10 @@ const localClients = new Map();
225
241
  // ====================
226
242
 
227
243
  function connect() {
244
+ // Bridge URL should always be set (defaults to DEFAULT_BRIDGE_URL)
228
245
  if (!bridgeUrl) {
229
- log('No bridge URL configured', colors.red);
230
- return;
246
+ log('No bridge URL configured (this should not happen)', colors.red);
247
+ process.exit(1);
231
248
  }
232
249
 
233
250
  log(`Connecting to ${bridgeUrl}...`, colors.cyan);
@@ -244,11 +261,13 @@ function connect() {
244
261
  log('Connected to bridge', colors.green);
245
262
  clearTimeout(reconnectTimer);
246
263
 
247
- // Authenticate
264
+ // Authenticate (auth is required, so this should always be true)
248
265
  if (authToken) {
249
266
  send({ type: 'authenticate', token: authToken });
250
267
  } else {
251
- log('No auth token - run: vibe-agent --login', colors.yellow);
268
+ // Failsafe - should not reach here due to startup check
269
+ showWelcomeMessage();
270
+ process.exit(1);
252
271
  }
253
272
  });
254
273
 
@@ -1319,27 +1338,24 @@ function printHelp() {
1319
1338
  ${colors.cyan}${colors.bold}vibe-agent${colors.reset} - Persistent daemon for remote Claude Code sessions
1320
1339
 
1321
1340
  ${colors.bold}Usage:${colors.reset}
1322
- vibe-agent --bridge <url> Start agent connected to bridge
1323
- vibe-agent --login --bridge <url> Login via device code flow
1324
- vibe-agent --status Show agent status
1341
+ vibe-agent Start agent daemon
1342
+ vibe-agent --login Sign in with Google (one-time)
1343
+ vibe-agent --status Show agent status
1325
1344
 
1326
1345
  ${colors.bold}Options:${colors.reset}
1327
- --bridge <url> Bridge server URL (wss://ws.minivibeapp.com)
1328
- --login Start device code login flow
1329
- --token <token> Use specific Firebase token
1346
+ --login Sign in via device code flow
1330
1347
  --name <name> Set host display name
1331
1348
  --status Show current status and exit
1332
1349
  --help, -h Show this help
1333
1350
 
1334
- ${colors.bold}Examples:${colors.reset}
1335
- # Login (first time)
1336
- vibe-agent --login --bridge wss://ws.minivibeapp.com
1337
-
1338
- # Start agent daemon
1339
- vibe-agent --bridge wss://ws.minivibeapp.com
1351
+ ${colors.bold}Advanced:${colors.reset}
1352
+ --bridge <url> Override bridge URL (default: wss://ws.minivibeapp.com)
1353
+ --token <token> Use specific Firebase token
1340
1354
 
1341
- # Start with custom host name
1342
- vibe-agent --bridge wss://ws.minivibeapp.com --name "AWS Dev Server"
1355
+ ${colors.bold}Examples:${colors.reset}
1356
+ vibe-agent --login Sign in (one-time setup)
1357
+ vibe-agent Start agent
1358
+ vibe-agent --name "EC2" Start with custom name
1343
1359
  `);
1344
1360
  }
1345
1361
 
@@ -1397,7 +1413,7 @@ async function main() {
1397
1413
  // Load saved config
1398
1414
  const config = loadConfig();
1399
1415
 
1400
- bridgeUrl = options.bridge || config.bridgeUrl;
1416
+ bridgeUrl = options.bridge || config.bridgeUrl || DEFAULT_BRIDGE_URL;
1401
1417
  hostName = options.name || config.hostName || os.hostname();
1402
1418
  agentId = config.agentId || null; // Load persisted agentId
1403
1419
 
@@ -1410,7 +1426,7 @@ async function main() {
1410
1426
  }
1411
1427
 
1412
1428
  // Save config for next time
1413
- if (bridgeUrl) {
1429
+ if (options.bridge) {
1414
1430
  config.bridgeUrl = bridgeUrl;
1415
1431
  }
1416
1432
  if (options.name) {
@@ -1420,7 +1436,7 @@ async function main() {
1420
1436
 
1421
1437
  // Status check
1422
1438
  if (options.status) {
1423
- console.log(`Bridge URL: ${bridgeUrl || 'Not configured'}`);
1439
+ console.log(`Bridge URL: ${bridgeUrl}`);
1424
1440
  console.log(`Host Name: ${hostName}`);
1425
1441
  console.log(`Auth Token: ${authToken ? 'Configured' : 'Not configured'}`);
1426
1442
  console.log(`Agent ID: ${agentId || 'Will be assigned on first connect'}`);
@@ -1429,23 +1445,15 @@ async function main() {
1429
1445
 
1430
1446
  // Login flow
1431
1447
  if (options.login) {
1432
- if (!bridgeUrl) {
1433
- log('--bridge URL required for login', colors.red);
1434
- process.exit(1);
1435
- }
1436
1448
  const httpUrl = bridgeUrl.replace('wss://', 'https://').replace('ws://', 'http://');
1437
1449
  await startHeadlessLogin(httpUrl);
1438
1450
  return;
1439
1451
  }
1440
1452
 
1441
- // Validate requirements
1442
- if (!bridgeUrl) {
1443
- log('No bridge URL. Run: vibe-agent --bridge wss://ws.minivibeapp.com', colors.red);
1444
- process.exit(1);
1445
- }
1446
-
1453
+ // Require auth (block instead of warn)
1447
1454
  if (!authToken) {
1448
- log('No auth token. Run: vibe-agent --login --bridge <url>', colors.yellow);
1455
+ showWelcomeMessage();
1456
+ process.exit(1);
1449
1457
  }
1450
1458
 
1451
1459
  // Banner
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "minivibe",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "CLI wrapper for Claude Code with mobile remote control via MiniVibe iOS app",
5
5
  "author": "MiniVibe <hello@minivibeapp.com>",
6
6
  "homepage": "https://github.com/minivibeapp/minivibe",
package/vibe.js CHANGED
@@ -19,6 +19,49 @@ const os = require('os');
19
19
  const crypto = require('crypto');
20
20
  const e2e = require('./e2e');
21
21
 
22
+ // Check if Claude Code is installed
23
+ function checkClaudeInstalled() {
24
+ try {
25
+ execSync('claude --version', { stdio: 'ignore' });
26
+ return true;
27
+ } catch {
28
+ return false;
29
+ }
30
+ }
31
+
32
+ // Show welcome message for first-time users (no auth)
33
+ function showWelcomeMessage() {
34
+ console.log(`
35
+ Welcome to MiniVibe!
36
+
37
+ MiniVibe lets you control Claude Code from your iPhone.
38
+
39
+ To get started:
40
+ 1. Download MiniVibe from the App Store
41
+ 2. Run: vibe --login
42
+
43
+ For help: vibe --help
44
+ `);
45
+ }
46
+
47
+ // Show error when Claude Code is not installed
48
+ function showClaudeNotFoundMessage() {
49
+ console.log(`
50
+ Claude Code not found
51
+
52
+ MiniVibe requires Claude Code CLI to be installed.
53
+
54
+ Install Claude Code:
55
+ https://claude.ai/download
56
+
57
+ After installing, run:
58
+ vibe --login
59
+ `);
60
+ }
61
+
62
+ // Default bridge URL
63
+ const DEFAULT_BRIDGE_URL = 'wss://ws.minivibeapp.com';
64
+
22
65
  // Find claude executable
23
66
  function findClaudePath() {
24
67
  try {
@@ -273,9 +316,6 @@ async function startLoginFlow(openBrowser = true) {
273
316
  }
274
317
  }
275
318
 
276
- // Default bridge URL for headless login
277
- const DEFAULT_BRIDGE_URL = 'wss://ws.minivibeapp.com';
278
-
279
319
  // Production web app URL for device pairing
280
320
  const WEB_APP_URL = 'https://minivibeapp.com';
281
321
 
@@ -303,54 +343,41 @@ let e2eEnabled = false; // --e2e mode: enable end-to-end encryption
303
343
  for (let i = 0; i < args.length; i++) {
304
344
  if (args[i] === '--help' || args[i] === '-h') {
305
345
  console.log(`
306
- vibe-cli - Claude Code wrapper with mobile remote control
346
+ vibe - Claude Code with mobile remote control
307
347
 
308
348
  Usage:
309
- vibe "your prompt here" Start new session with prompt
310
- vibe --bridge ws://server:8080 Connect to bridge server
311
- vibe --agent Connect via local vibe-agent (managed mode)
312
- vibe --resume <id> Resume existing session
313
- vibe --attach <id> Attach to session via local agent (full terminal)
314
- vibe --remote <id> --bridge <url> Remote control session (no local Claude needed)
315
- vibe --list List running sessions on local agent
316
- vibe --login Sign in via minivibeapp.com (opens browser)
317
- vibe --login --headless Sign in on headless server (no browser)
318
- vibe Start interactive session
349
+ vibe Start session (connects to bridge)
350
+ vibe "prompt" Start with initial prompt
351
+ vibe --login Sign in with Google
352
+ vibe --agent Connect via local vibe-agent
319
353
 
320
354
  Options:
321
- --bridge <url> Connect to bridge server (enables internet access)
322
- --agent [url] Connect via local vibe-agent (default: ws://localhost:9999)
355
+ --login Sign in via minivibeapp.com (opens browser)
356
+ --headless Use device code flow for servers (no browser)
357
+ --agent [url] Connect via local vibe-agent (default: auto-discover)
323
358
  --name <name> Name this session (shown in mobile app)
324
- --resume <id> Resume a previous session (auto-detects correct directory)
325
- --attach <id> Attach via local agent (full terminal passthrough)
326
- --remote <id> Remote control via bridge (no local Claude needed, requires --bridge)
359
+ --resume <id> Resume a previous session
360
+ --e2e Enable end-to-end encryption
361
+
362
+ Advanced:
363
+ --bridge <url> Override bridge URL (default: wss://ws.minivibeapp.com)
364
+ --attach <id> Attach to session via local agent (full terminal)
365
+ --remote <id> Remote control session via bridge (no local Claude needed)
327
366
  --list List running sessions on local agent
328
- --login Sign in via minivibeapp.com (opens browser)
329
- --headless Don't open browser (for servers without display)
330
367
  --token <token> Set Firebase auth token manually
331
368
  --logout Remove stored auth token
332
- --node-pty Use Node.js PTY wrapper (required for Windows, optional for Unix)
333
- --dangerously-skip-permissions Run Claude without permission prompts (use with caution!)
334
- --e2e Enable end-to-end encryption (auto key exchange with iOS)
369
+ --node-pty Use Node.js PTY wrapper (required for Windows)
370
+ --dangerously-skip-permissions Auto-approve all tool executions
335
371
  --help, -h Show this help message
336
372
 
337
- Attach vs Remote:
338
- --attach <id> LOCAL: Full terminal passthrough via local vibe-agent
339
- --remote <id> --bridge <url> REMOTE: Chat-style control via bridge server
340
- - No local agent or Claude Code needed
341
- - Control sessions running on any host
342
-
343
- Authentication:
344
- Use --login to sign in via minivibeapp.com (opens browser automatically).
345
- Use --login --headless on servers without a display (EC2, etc.)
346
-
347
373
  Examples:
348
- vibe --login Sign in (opens minivibeapp.com)
349
- vibe --login --headless Sign in (prints code for manual entry)
350
- vibe --bridge wss://ws.minivibeapp.com Connect to bridge
351
- vibe --agent Connect via local agent
352
- vibe --bridge wss://ws.minivibeapp.com "Fix bug" With initial prompt
353
- vibe --remote abc123 --bridge wss://ws.minivibeapp.com Remote control session on another host
374
+ vibe --login Sign in (one-time setup)
375
+ vibe Start session
376
+ vibe "Fix the bug" Start with prompt
377
+ vibe --e2e Enable encryption
378
+ vibe --agent Use local agent
379
+
380
+ For local-only use without remote control, run 'claude' directly.
354
381
  `);
355
382
  process.exit(0);
356
383
  } else if (args[i] === '--headless') {
@@ -442,6 +469,39 @@ if (!authToken) {
442
469
  authToken = getStoredToken();
443
470
  }
444
471
 
472
+ // ====================
473
+ // Startup Checks
474
+ // ====================
475
+
476
+ // Skip checks if in login mode (already handled above)
477
+ if (!loginMode) {
478
+ const isAgentMode = !!agentUrl || listSessions;
479
+
480
+ // 1. --remote mode doesn't need local Claude (controls remote session)
481
+ // 2. --list mode doesn't need local Claude (just queries agent)
482
+ // 3. All other modes need Claude installed
483
+ if (!remoteAttachMode && !listSessions && !checkClaudeInstalled()) {
484
+ showClaudeNotFoundMessage();
485
+ process.exit(1);
486
+ }
487
+
488
+ // 3. Check auth (unless agent mode - agent handles its own auth)
489
+ if (!isAgentMode && !authToken) {
490
+ showWelcomeMessage();
491
+ process.exit(1);
492
+ }
493
+
494
+ // 4. Default to bridge URL when authenticated (and not in agent mode)
495
+ if (!isAgentMode && !bridgeUrl && authToken) {
496
+ bridgeUrl = DEFAULT_BRIDGE_URL;
497
+ }
498
+
499
+ // 5. --remote without --bridge: use default bridge
500
+ if (remoteAttachMode && !bridgeUrl) {
501
+ bridgeUrl = DEFAULT_BRIDGE_URL;
502
+ }
503
+ }
504
+
445
505
  // Initialize E2E encryption if enabled
446
506
  // Track if E2E is pending (enabled but not yet established)
447
507
  let e2ePending = false;
@@ -458,20 +518,7 @@ if (e2eEnabled) {
458
518
  }
459
519
  }
460
520
 
461
- // Validate remote mode requires bridge
462
- if (remoteAttachMode && !bridgeUrl) {
463
- console.error('Error: --remote requires --bridge <url>');
464
- console.error('Example: vibe --remote abc123 --bridge wss://ws.minivibeapp.com');
465
- process.exit(1);
466
- }
467
-
468
- // Validate E2E requires bridge (for key exchange)
469
- if (e2ePending && !bridgeUrl && !agentUrl) {
470
- console.error('Error: --e2e with no saved peer requires --bridge <url>');
471
- console.error('E2E encryption needs a bridge to exchange keys with iOS app.');
472
- console.error('Example: vibe --e2e --bridge wss://ws.minivibeapp.com');
473
- process.exit(1);
474
- }
521
+ // Note: E2E key exchange now works automatically since bridge is defaulted when authenticated
475
522
 
476
523
  // Session state
477
524
  const sessionId = resumeSessionId || uuidv4();
@@ -693,13 +740,10 @@ function connectToBridge() {
693
740
 
694
741
  const isAgentMode = !!agentUrl;
695
742
 
696
- // Check for auth token when bridge is enabled (not needed for agent mode)
743
+ // Auth should already be checked in startup, but double-check here
697
744
  if (!isAgentMode && !authToken) {
698
- log('⚠️ No authentication token found.', colors.yellow);
699
- log(' Use --token <token> to set your Firebase token.', colors.dim);
700
- log(' Get your token from the MiniVibe mobile app.', colors.dim);
701
- log('', '');
702
- log(' Continuing without authentication (bridge may reject connection)', colors.dim);
745
+ showWelcomeMessage();
746
+ process.exit(1);
703
747
  }
704
748
 
705
749
  if (isAgentMode) {
@@ -765,6 +809,17 @@ function connectToBridge() {
765
809
  });
766
810
 
767
811
  bridgeSocket.on('error', (err) => {
812
+ if (isAgentMode && err.code === 'ECONNREFUSED') {
813
+ // Agent not running - show helpful message
814
+ console.log(`
815
+ Cannot connect to vibe-agent at ${targetUrl}
816
+
817
+ Make sure vibe-agent is running:
818
+ vibe-agent --login # First time
819
+ vibe-agent # Start daemon
820
+ `);
821
+ process.exit(1);
822
+ }
768
823
  logStatus(`Bridge connection error: ${err.message}`);
769
824
  });
770
825
  }
@@ -2122,18 +2177,16 @@ function setupShutdown() {
2122
2177
  // ====================
2123
2178
 
2124
2179
  function remoteAttachMain() {
2125
- // Validate before displaying banner
2180
+ // Validate before displaying banner (redundant with startup checks, but good failsafe)
2126
2181
  if (!authToken) {
2127
- console.log('');
2128
- log('❌ Remote attach requires authentication', colors.red);
2129
- log(' Run: vibe --login --bridge ' + bridgeUrl, colors.dim);
2182
+ showWelcomeMessage();
2130
2183
  process.exit(1);
2131
2184
  }
2132
2185
 
2133
2186
  if (!resumeSessionId) {
2134
2187
  console.log('');
2135
2188
  log('❌ Remote mode requires a session ID', colors.red);
2136
- log(' Run: vibe --remote <session-id> --bridge ' + bridgeUrl, colors.dim);
2189
+ log(' Run: vibe --remote <session-id>', colors.dim);
2137
2190
  process.exit(1);
2138
2191
  }
2139
2192