vibeteam 0.2.2 → 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 +334 -38
- package/dist/server/server/index.js +2078 -1822
- package/mcp/server.js +389 -0
- package/package.json +5 -2
- package/public/apple-touch-icon.png +0 -0
- package/public/assets/index-DURW-QlU.js +4330 -0
- package/public/assets/index-DgQWq5c0.css +1 -0
- package/public/favicon-16x16.png +0 -0
- package/public/favicon-32x32.png +0 -0
- package/public/favicon.ico +0 -0
- package/public/favicon.svg +14 -0
- package/public/index.html +21 -0
- package/public/logo.svg +22 -0
- package/public/models/.gitkeep +0 -0
- package/public/models/models/.gitkeep +0 -0
- package/public/site.webmanifest +25 -0
- package/public/sounds/sounds/yoshi.mp3 +0 -0
- package/public/sounds/yoshi.mp3 +0 -0
- package/dist/server/server/CommitWatcher.js +0 -307
package/bin/cli.js
CHANGED
|
@@ -384,7 +384,71 @@ if (args[0] === 'setup') {
|
|
|
384
384
|
}
|
|
385
385
|
|
|
386
386
|
// ==========================================================================
|
|
387
|
-
// Step 5:
|
|
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
|
|
|
@@ -866,7 +934,221 @@ console.log(`
|
|
|
866
934
|
╰─────────────────────────────────────╯
|
|
867
935
|
`)
|
|
868
936
|
|
|
869
|
-
//
|
|
937
|
+
// ============================================================================
|
|
938
|
+
// Critical Dependency Check (blocks startup if missing)
|
|
939
|
+
// ============================================================================
|
|
940
|
+
|
|
941
|
+
function detectPackageManager() {
|
|
942
|
+
const platform = process.platform
|
|
943
|
+
|
|
944
|
+
if (platform === 'darwin') {
|
|
945
|
+
// macOS - check for brew
|
|
946
|
+
try {
|
|
947
|
+
execSync('which brew', { stdio: 'ignore' })
|
|
948
|
+
return { name: 'brew', install: (pkg) => `brew install ${pkg}` }
|
|
949
|
+
} catch {
|
|
950
|
+
return null
|
|
951
|
+
}
|
|
952
|
+
} else if (platform === 'linux') {
|
|
953
|
+
// Linux - check for apt, then yum/dnf
|
|
954
|
+
try {
|
|
955
|
+
execSync('which apt', { stdio: 'ignore' })
|
|
956
|
+
return { name: 'apt', install: (pkg) => `sudo apt install -y ${pkg}` }
|
|
957
|
+
} catch {
|
|
958
|
+
try {
|
|
959
|
+
execSync('which dnf', { stdio: 'ignore' })
|
|
960
|
+
return { name: 'dnf', install: (pkg) => `sudo dnf install -y ${pkg}` }
|
|
961
|
+
} catch {
|
|
962
|
+
try {
|
|
963
|
+
execSync('which yum', { stdio: 'ignore' })
|
|
964
|
+
return { name: 'yum', install: (pkg) => `sudo yum install -y ${pkg}` }
|
|
965
|
+
} catch {
|
|
966
|
+
return null
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
return null
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
async function installDependency(pkgManager, depName) {
|
|
975
|
+
const cmd = pkgManager.install(depName)
|
|
976
|
+
console.log(` Running: ${cmd}`)
|
|
977
|
+
console.log()
|
|
978
|
+
|
|
979
|
+
try {
|
|
980
|
+
execSync(cmd, { stdio: 'inherit' })
|
|
981
|
+
return true
|
|
982
|
+
} catch (e) {
|
|
983
|
+
console.log(` ✗ Failed to install ${depName}`)
|
|
984
|
+
return false
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
async function checkRequiredDependencies() {
|
|
989
|
+
const missing = []
|
|
990
|
+
|
|
991
|
+
// Check tmux (REQUIRED - agents won't spawn without it)
|
|
992
|
+
if (!checkTmux()) {
|
|
993
|
+
missing.push({
|
|
994
|
+
name: 'tmux',
|
|
995
|
+
reason: 'Required for running Claude agents in background sessions',
|
|
996
|
+
})
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
// Check jq (REQUIRED - hooks won't work without it)
|
|
1000
|
+
if (!checkJq()) {
|
|
1001
|
+
missing.push({
|
|
1002
|
+
name: 'jq',
|
|
1003
|
+
reason: 'Required for Claude Code hooks to capture events',
|
|
1004
|
+
})
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
if (missing.length === 0) {
|
|
1008
|
+
return true // All good
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
// Show what's missing
|
|
1012
|
+
console.log(' ╭─────────────────────────────────────╮')
|
|
1013
|
+
console.log(' │ Missing Required Dependencies │')
|
|
1014
|
+
console.log(' ╰─────────────────────────────────────╯')
|
|
1015
|
+
console.log()
|
|
1016
|
+
|
|
1017
|
+
for (const dep of missing) {
|
|
1018
|
+
console.log(` ✗ ${dep.name} - ${dep.reason}`)
|
|
1019
|
+
}
|
|
1020
|
+
console.log()
|
|
1021
|
+
|
|
1022
|
+
// Detect package manager
|
|
1023
|
+
const pkgManager = detectPackageManager()
|
|
1024
|
+
|
|
1025
|
+
if (pkgManager) {
|
|
1026
|
+
const answer = await askQuestion(` Install missing dependencies using ${pkgManager.name}? (y/n): `)
|
|
1027
|
+
|
|
1028
|
+
if (answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes') {
|
|
1029
|
+
console.log()
|
|
1030
|
+
|
|
1031
|
+
let allInstalled = true
|
|
1032
|
+
for (const dep of missing) {
|
|
1033
|
+
const success = await installDependency(pkgManager, dep.name)
|
|
1034
|
+
if (!success) {
|
|
1035
|
+
allInstalled = false
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
if (allInstalled) {
|
|
1040
|
+
console.log()
|
|
1041
|
+
console.log(' ✓ All dependencies installed successfully!')
|
|
1042
|
+
console.log()
|
|
1043
|
+
return true
|
|
1044
|
+
} else {
|
|
1045
|
+
console.log()
|
|
1046
|
+
console.log(' Some dependencies failed to install. Please install them manually.')
|
|
1047
|
+
process.exit(1)
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
} else {
|
|
1051
|
+
// No package manager detected - show manual instructions
|
|
1052
|
+
console.log(' Could not detect package manager. Please install manually:')
|
|
1053
|
+
console.log()
|
|
1054
|
+
console.log(' macOS: brew install tmux jq')
|
|
1055
|
+
console.log(' Ubuntu: sudo apt install tmux jq')
|
|
1056
|
+
console.log(' Fedora: sudo dnf install tmux jq')
|
|
1057
|
+
console.log()
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
// Ask if they want to continue anyway
|
|
1061
|
+
const continueAnswer = await askQuestion(' Continue without these dependencies? (y/n): ')
|
|
1062
|
+
|
|
1063
|
+
if (continueAnswer.toLowerCase() === 'y' || continueAnswer.toLowerCase() === 'yes') {
|
|
1064
|
+
console.log()
|
|
1065
|
+
console.log(' ⚠ Continuing without all dependencies - some features may not work!')
|
|
1066
|
+
console.log()
|
|
1067
|
+
return true
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
console.log()
|
|
1071
|
+
console.log(' Install the missing dependencies and run vibeteam again.')
|
|
1072
|
+
console.log()
|
|
1073
|
+
process.exit(1)
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
// Check dependencies before starting
|
|
1077
|
+
await checkRequiredDependencies()
|
|
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
|
+
|
|
1151
|
+
// Run health checks (warnings only)
|
|
870
1152
|
printHealthCheck()
|
|
871
1153
|
|
|
872
1154
|
console.log(`Starting server on port ${port}...`)
|
|
@@ -882,44 +1164,58 @@ console.log()
|
|
|
882
1164
|
const compiledPath = resolve(ROOT, 'dist/server/server/index.js')
|
|
883
1165
|
const sourcePath = resolve(ROOT, 'server/index.ts')
|
|
884
1166
|
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
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)
|
|
906
1198
|
})
|
|
907
|
-
}
|
|
908
1199
|
|
|
909
|
-
server.on('
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
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
|
+
})
|
|
913
1209
|
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
1210
|
+
// Handle signals - forward to child
|
|
1211
|
+
const onSigInt = () => server.kill('SIGINT')
|
|
1212
|
+
const onSigTerm = () => server.kill('SIGTERM')
|
|
917
1213
|
|
|
918
|
-
//
|
|
919
|
-
process.
|
|
920
|
-
|
|
921
|
-
|
|
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
|
+
}
|
|
922
1220
|
|
|
923
|
-
|
|
924
|
-
server.kill('SIGTERM')
|
|
925
|
-
})
|
|
1221
|
+
spawnServer()
|