vibeteam 0.2.3 → 0.2.4

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/bin/cli.js CHANGED
@@ -384,7 +384,71 @@ if (args[0] === 'setup') {
384
384
  }
385
385
 
386
386
  // ==========================================================================
387
- // Step 5: Verify and report
387
+ // Step 5: MCP Server Installation
388
+ // ==========================================================================
389
+
390
+ let mcpInstalled = false
391
+
392
+ // Check if MCP is already configured
393
+ const mcpAlreadyConfigured = (() => {
394
+ try {
395
+ const s = JSON.parse(readFileSync(settingsPath, 'utf-8'))
396
+ return !!s.mcpServers?.vibeteam
397
+ } catch { return false }
398
+ })()
399
+
400
+ // Also check settings.local.json
401
+ const mcpInLocal = (() => {
402
+ try {
403
+ const localPath = settingsPath.replace('settings.json', 'settings.local.json')
404
+ if (!existsSync(localPath)) return false
405
+ const s = JSON.parse(readFileSync(localPath, 'utf-8'))
406
+ return !!s.mcpServers?.vibeteam
407
+ } catch { return false }
408
+ })()
409
+
410
+ if (mcpAlreadyConfigured || mcpInLocal) {
411
+ console.log('\nMCP server: already configured')
412
+ mcpInstalled = true
413
+ } else {
414
+ // Check if claude CLI is available
415
+ let hasClaudeCli = false
416
+ try {
417
+ execSync('which claude', { stdio: 'ignore' })
418
+ hasClaudeCli = true
419
+ } catch {}
420
+
421
+ if (hasClaudeCli) {
422
+ console.log('\n VibeTeam MCP server lets Claude Code control your agents remotely.')
423
+ const mcpAnswer = await askQuestion(' Install MCP integration for Claude Code? (y/n): ')
424
+
425
+ if (mcpAnswer.toLowerCase() === 'y' || mcpAnswer.toLowerCase() === 'yes') {
426
+ // Determine install method: global npm binary vs local dev path
427
+ let mcpCommand = 'vibeteam-mcp'
428
+ try {
429
+ execSync('which vibeteam-mcp', { stdio: 'ignore' })
430
+ } catch {
431
+ // vibeteam-mcp not in PATH — use absolute path to mcp/server.js
432
+ mcpCommand = resolve(ROOT, 'mcp/server.js')
433
+ }
434
+
435
+ try {
436
+ execSync(
437
+ `CLAUDECODE= claude mcp add vibeteam --scope user -- ${mcpCommand}`,
438
+ { stdio: 'pipe', timeout: 10000 }
439
+ )
440
+ console.log(' ✓ MCP server installed for Claude Code')
441
+ mcpInstalled = true
442
+ } catch (e) {
443
+ console.log(` ✗ Failed to install MCP server: ${e.message}`)
444
+ console.log(` You can install manually: claude mcp add vibeteam -- ${mcpCommand}`)
445
+ }
446
+ }
447
+ }
448
+ }
449
+
450
+ // ==========================================================================
451
+ // Step 6: Verify and report
388
452
  // ==========================================================================
389
453
 
390
454
  console.log('\n' + '='.repeat(50))
@@ -401,6 +465,10 @@ if (args[0] === 'setup') {
401
465
  console.log(' - UserPromptSubmit')
402
466
  console.log(' - Notification')
403
467
 
468
+ if (mcpInstalled) {
469
+ console.log('\nMCP server: ✓ configured')
470
+ }
471
+
404
472
  // Check dependencies
405
473
  let hasWarnings = false
406
474
 
@@ -1008,6 +1076,78 @@ async function checkRequiredDependencies() {
1008
1076
  // Check dependencies before starting
1009
1077
  await checkRequiredDependencies()
1010
1078
 
1079
+ // ============================================================================
1080
+ // First-run MCP install offer
1081
+ // ============================================================================
1082
+
1083
+ async function offerMcpInstall() {
1084
+ // Check if we already offered (don't nag on every start)
1085
+ const flagFile = join(homedir(), '.vibeteam', '.mcp-offered')
1086
+ if (existsSync(flagFile)) return
1087
+
1088
+ // Check if MCP is already configured in settings or settings.local
1089
+ const settingsPaths = [
1090
+ join(homedir(), '.claude', 'settings.json'),
1091
+ join(homedir(), '.claude', 'settings.local.json'),
1092
+ join(homedir(), '.config', 'claude', 'settings.json'),
1093
+ join(homedir(), '.config', 'claude', 'settings.local.json'),
1094
+ ]
1095
+
1096
+ for (const sp of settingsPaths) {
1097
+ try {
1098
+ if (!existsSync(sp)) continue
1099
+ const s = JSON.parse(readFileSync(sp, 'utf-8'))
1100
+ if (s.mcpServers?.vibeteam) return // Already configured
1101
+ } catch {}
1102
+ }
1103
+
1104
+ // Check if claude CLI is available
1105
+ try {
1106
+ execSync('which claude', { stdio: 'ignore' })
1107
+ } catch {
1108
+ return // No claude CLI, skip
1109
+ }
1110
+
1111
+ console.log(' VibeTeam MCP server lets Claude Code control your agents remotely.')
1112
+ const answer = await askQuestion(' Install MCP integration for Claude Code? (y/n): ')
1113
+
1114
+ // Ensure .vibeteam dir exists for the flag file
1115
+ const vibeteamDir = join(homedir(), '.vibeteam')
1116
+ if (!existsSync(vibeteamDir)) {
1117
+ mkdirSync(vibeteamDir, { recursive: true })
1118
+ }
1119
+
1120
+ if (answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes') {
1121
+ let mcpCommand = 'vibeteam-mcp'
1122
+ try {
1123
+ execSync('which vibeteam-mcp', { stdio: 'ignore' })
1124
+ } catch {
1125
+ mcpCommand = resolve(ROOT, 'mcp/server.js')
1126
+ }
1127
+
1128
+ try {
1129
+ execSync(
1130
+ `CLAUDECODE= claude mcp add vibeteam --scope user -- ${mcpCommand}`,
1131
+ { stdio: 'pipe', timeout: 10000 }
1132
+ )
1133
+ console.log(' ✓ MCP server installed for Claude Code\n')
1134
+ } catch (e) {
1135
+ console.log(` ✗ Failed to install MCP: ${e.message}`)
1136
+ console.log(` Install manually: claude mcp add vibeteam -- ${mcpCommand}\n`)
1137
+ }
1138
+ } else {
1139
+ console.log()
1140
+ }
1141
+
1142
+ // Write flag so we don't ask again
1143
+ const { writeFileSync } = await import('fs')
1144
+ try {
1145
+ writeFileSync(flagFile, new Date().toISOString())
1146
+ } catch {}
1147
+ }
1148
+
1149
+ await offerMcpInstall()
1150
+
1011
1151
  // Run health checks (warnings only)
1012
1152
  printHealthCheck()
1013
1153
 
@@ -1024,44 +1164,58 @@ console.log()
1024
1164
  const compiledPath = resolve(ROOT, 'dist/server/server/index.js')
1025
1165
  const sourcePath = resolve(ROOT, 'server/index.ts')
1026
1166
 
1027
- let server
1028
- if (existsSync(compiledPath)) {
1029
- // Use compiled JS (production/npm install)
1030
- server = spawn('node', [compiledPath], {
1031
- cwd: ROOT,
1032
- env: {
1033
- ...process.env,
1034
- VIBETEAM_PORT: port,
1035
- },
1036
- stdio: 'inherit',
1037
- })
1038
- } else {
1039
- // Fall back to tsx (development)
1040
- console.log('(dev mode - using tsx)')
1041
- server = spawn('npx', ['tsx', sourcePath], {
1042
- cwd: ROOT,
1043
- env: {
1044
- ...process.env,
1045
- VIBETEAM_PORT: port,
1046
- },
1047
- stdio: 'inherit',
1167
+ /** Exit code 75 = restart requested (e.g. via POST /api/restart) */
1168
+ const RESTART_EXIT_CODE = 75
1169
+
1170
+ function spawnServer() {
1171
+ let server
1172
+ if (existsSync(compiledPath)) {
1173
+ // Use compiled JS (production/npm install)
1174
+ server = spawn('node', [compiledPath], {
1175
+ cwd: ROOT,
1176
+ env: {
1177
+ ...process.env,
1178
+ VIBETEAM_PORT: port,
1179
+ },
1180
+ stdio: 'inherit',
1181
+ })
1182
+ } else {
1183
+ // Fall back to tsx (development)
1184
+ console.log('(dev mode - using tsx)')
1185
+ server = spawn('npx', ['tsx', sourcePath], {
1186
+ cwd: ROOT,
1187
+ env: {
1188
+ ...process.env,
1189
+ VIBETEAM_PORT: port,
1190
+ },
1191
+ stdio: 'inherit',
1192
+ })
1193
+ }
1194
+
1195
+ server.on('error', (err) => {
1196
+ console.error('Failed to start server:', err.message)
1197
+ process.exit(1)
1048
1198
  })
1049
- }
1050
1199
 
1051
- server.on('error', (err) => {
1052
- console.error('Failed to start server:', err.message)
1053
- process.exit(1)
1054
- })
1200
+ server.on('close', (code) => {
1201
+ if (code === RESTART_EXIT_CODE) {
1202
+ console.log('\n Restarting server...\n')
1203
+ // Small delay to let port release
1204
+ setTimeout(() => spawnServer(), 1000)
1205
+ return
1206
+ }
1207
+ process.exit(code || 0)
1208
+ })
1055
1209
 
1056
- server.on('close', (code) => {
1057
- process.exit(code || 0)
1058
- })
1210
+ // Handle signals - forward to child
1211
+ const onSigInt = () => server.kill('SIGINT')
1212
+ const onSigTerm = () => server.kill('SIGTERM')
1059
1213
 
1060
- // Handle signals
1061
- process.on('SIGINT', () => {
1062
- server.kill('SIGINT')
1063
- })
1214
+ // Remove old listeners to avoid stacking on restart
1215
+ process.removeAllListeners('SIGINT')
1216
+ process.removeAllListeners('SIGTERM')
1217
+ process.on('SIGINT', onSigInt)
1218
+ process.on('SIGTERM', onSigTerm)
1219
+ }
1064
1220
 
1065
- process.on('SIGTERM', () => {
1066
- server.kill('SIGTERM')
1067
- })
1221
+ spawnServer()