@supercollab/cli 0.4.2 → 0.4.3

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
@@ -12,9 +12,23 @@ Install:
12
12
 
13
13
  ```bash
14
14
  npm install -g @supercollab/cli
15
+ supercollab
15
16
  ```
16
17
 
17
- Create an account and local agent:
18
+ Running `supercollab` opens the guided setup flow. It detects your OS/CPU/Node
19
+ runtime, verifies native SQLite and sqlite-vec, downloads and warms the BGE model
20
+ locally, writes the selected local engine into `~/.supercollab/config.json`,
21
+ creates or logs into your account, registers the local agent, creates or joins a
22
+ room, activates a project directory, and prints MCP config.
23
+
24
+ You can also run the checks directly:
25
+
26
+ ```bash
27
+ supercollab doctor
28
+ supercollab doctor --json
29
+ ```
30
+
31
+ Manual account setup:
18
32
 
19
33
  ```bash
20
34
  supercollab register --username your_name
@@ -83,8 +97,8 @@ hybrid: reciprocal-rank fusion over keyword and vector results
83
97
  ```
84
98
 
85
99
  The hosted SuperCollab service never computes embeddings and never receives the
86
- room key. The first local sync/search may download the BGE-small ONNX model into
87
- the local Hugging Face cache. To verify or prewarm the local embedding system:
100
+ room key. Guided setup downloads and verifies the BGE-small ONNX model into the
101
+ local Hugging Face cache. To verify or prewarm the local embedding system:
88
102
 
89
103
  ```bash
90
104
  supercollab embeddings status
@@ -3,10 +3,11 @@ import fs from 'node:fs';
3
3
  import os from 'node:os';
4
4
  import path from 'node:path';
5
5
  import crypto from 'node:crypto';
6
+ import { spawnSync } from 'node:child_process';
6
7
  import * as readlineCore from 'node:readline';
7
8
  import { stdin as input, stdout as output } from 'node:process';
8
9
 
9
- const VERSION = '0.4.2';
10
+ const VERSION = '0.4.3';
10
11
  const DEFAULT_SERVER = process.env.SUPERCOLLAB_URL || 'https://hyper.polynode.dev';
11
12
  const DEFAULT_CONFIG = process.env.SUPERCOLLAB_CONFIG || path.join(os.homedir(), '.supercollab', 'config.json');
12
13
  const SESSION_TTL_SKEW = 60;
@@ -33,6 +34,8 @@ function printHelp() {
33
34
  console.log(`SuperCollab CLI ${VERSION}
34
35
 
35
36
  Usage:
37
+ supercollab setup
38
+ supercollab doctor [--json] [--skip-model]
36
39
  supercollab register --username NAME [--password PASS] [--label LABEL]
37
40
  supercollab login --username NAME [--password PASS]
38
41
  supercollab whoami
@@ -65,6 +68,7 @@ Options:
65
68
  Environment:
66
69
  SUPERCOLLAB_PASSWORD can provide password non-interactively.
67
70
  SUPERCOLLAB_CONFIG can override config path.
71
+ SUPERCOLLAB_MODEL_CACHE can override the local Hugging Face model cache.
68
72
  SUPERCOLLAB_WORKDIR sets the local workspace directory for MCP activation checks.
69
73
  `);
70
74
  }
@@ -363,6 +367,78 @@ async function doLogin(config, file, opts) {
363
367
  return { ok: true, username: config.username, user_id: config.userId, config: file };
364
368
  }
365
369
 
370
+ function commandExists(command, args = ['--version']) {
371
+ const result = spawnSync(command, args, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] });
372
+ return {
373
+ ok: result.status === 0,
374
+ command,
375
+ status: result.status,
376
+ output: String(result.stdout || result.stderr || '').trim().split('\n')[0] || '',
377
+ };
378
+ }
379
+
380
+ function installAdvice(profile, checks = {}) {
381
+ const advice = [];
382
+ const platform = profile.platform;
383
+ if (!checks.node_supported?.ok) {
384
+ advice.push('Install Node.js 20 LTS or newer, then reinstall @supercollab/cli.');
385
+ }
386
+ if (!checks.native_sqlite_vec?.ok) {
387
+ if (profile.tools?.npm_ignore_scripts?.output === 'true') {
388
+ advice.push('Your npm config has `ignore-scripts=true`, which prevents native SQLite from installing. Run `npm config set ignore-scripts false`, then reinstall or run `npm rebuild -g better-sqlite3 --ignore-scripts=false`.');
389
+ }
390
+ if (platform === 'darwin') {
391
+ advice.push('Install Apple command line tools with `xcode-select --install`, then run `npm rebuild -g better-sqlite3`.');
392
+ } else if (platform === 'linux') {
393
+ advice.push('Install Python 3, make, and a C/C++ compiler, then run `npm rebuild -g better-sqlite3`.');
394
+ } else if (platform === 'win32') {
395
+ advice.push('Install Microsoft Visual Studio Build Tools with the C++ workload and Python, then run `npm rebuild -g better-sqlite3`.');
396
+ } else {
397
+ advice.push('Install a native build toolchain for your OS, then run `npm rebuild -g better-sqlite3`.');
398
+ }
399
+ }
400
+ if (!checks.bge_model?.ok && checks.bge_model?.error) {
401
+ advice.push('Check internet access to Hugging Face model downloads, then run `supercollab embeddings warmup`.');
402
+ }
403
+ return advice;
404
+ }
405
+
406
+ function detectSystemProfile() {
407
+ const cpus = os.cpus() || [];
408
+ const nodeMajor = Number(process.versions.node.split('.')[0]);
409
+ const tools = {
410
+ npm: commandExists('npm', ['--version']),
411
+ npm_ignore_scripts: commandExists('npm', ['config', 'get', 'ignore-scripts']),
412
+ };
413
+ if (process.platform === 'darwin') {
414
+ tools.xcode_select = commandExists('xcode-select', ['-p']);
415
+ tools.clang = commandExists('clang', ['--version']);
416
+ } else if (process.platform === 'linux') {
417
+ tools.python3 = commandExists('python3', ['--version']);
418
+ tools.make = commandExists('make', ['--version']);
419
+ tools.cc = commandExists('cc', ['--version']);
420
+ tools.gpp = commandExists('g++', ['--version']);
421
+ } else if (process.platform === 'win32') {
422
+ tools.python = commandExists('python', ['--version']);
423
+ tools.node_gyp = commandExists('node-gyp', ['--version']);
424
+ }
425
+ return {
426
+ detected_at: nowIso(),
427
+ cli_version: VERSION,
428
+ platform: process.platform,
429
+ arch: process.arch,
430
+ os_type: os.type(),
431
+ os_release: os.release(),
432
+ hostname: os.hostname(),
433
+ node: process.versions.node,
434
+ node_modules_abi: process.versions.modules,
435
+ node_supported: nodeMajor >= 20,
436
+ cpu_model: cpus[0]?.model || null,
437
+ cpu_count: cpus.length,
438
+ tools,
439
+ };
440
+ }
441
+
366
442
  let nativeSqlitePromise = null;
367
443
 
368
444
  async function loadNativeSqlite() {
@@ -1094,14 +1170,14 @@ async function runMcp(opts) {
1094
1170
 
1095
1171
  function printCodexConfig(opts) {
1096
1172
  const file = configPath(opts);
1097
- console.log(`[mcp_servers.supercollab]\ncommand = "supercollab"\nargs = ["mcp", "stdio", "--config", "${file.replaceAll('\\', '\\\\').replaceAll('"', '\\"')}"]`);
1173
+ console.log(mcpConfigText(String(opts.client || 'codex'), file));
1098
1174
  }
1099
1175
 
1100
1176
  async function embeddingStatus() {
1101
1177
  return {
1102
1178
  ok: true,
1103
1179
  profile: EMBEDDING_PROFILE,
1104
- model_download: 'lazy on first embedding, or now via `supercollab embeddings warmup`',
1180
+ model_download: 'installed during setup, or manually via `supercollab embeddings warmup`',
1105
1181
  cache_dir: process.env.SUPERCOLLAB_MODEL_CACHE || 'default @huggingface/transformers cache',
1106
1182
  };
1107
1183
  }
@@ -1115,14 +1191,344 @@ async function embeddingWarmup() {
1115
1191
  };
1116
1192
  }
1117
1193
 
1194
+ async function nativeEngineCheck() {
1195
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'supercollab-doctor-'));
1196
+ const dbPath = path.join(tempDir, 'native.sqlite');
1197
+ let db = null;
1198
+ try {
1199
+ const { Database, sqliteVec } = await loadNativeSqlite();
1200
+ db = new Database(dbPath);
1201
+ sqliteVec.load(db);
1202
+ const version = db.prepare('SELECT vec_version() AS version').get()?.version;
1203
+ db.exec(`CREATE VIRTUAL TABLE vec_check USING vec0(id TEXT PRIMARY KEY, embedding float[${EMBEDDING_DIMS}] distance_metric=cosine)`);
1204
+ db.prepare('INSERT INTO vec_check(id, embedding) VALUES(?, ?)').run('ok', new Float32Array(new Array(EMBEDDING_DIMS).fill(0).map((_, i) => i === 0 ? 1 : 0)));
1205
+ const rows = db.prepare('SELECT id, distance FROM vec_check WHERE embedding MATCH ? AND k = 1').all(new Float32Array(new Array(EMBEDDING_DIMS).fill(0).map((_, i) => i === 0 ? 1 : 0)));
1206
+ if (rows[0]?.id !== 'ok') throw new Error('sqlite-vec query did not return expected row');
1207
+ return {
1208
+ ok: true,
1209
+ engine: 'native-sqlite-vec',
1210
+ sqlite_vec_version: version,
1211
+ dims: EMBEDDING_DIMS,
1212
+ };
1213
+ } catch (err) {
1214
+ return {
1215
+ ok: false,
1216
+ engine: 'native-sqlite-vec',
1217
+ error: err.message || String(err),
1218
+ };
1219
+ } finally {
1220
+ try { db?.close(); } catch {}
1221
+ try { fs.rmSync(tempDir, { recursive: true, force: true }); } catch {}
1222
+ }
1223
+ }
1224
+
1225
+ async function runDoctor(config, file, opts = {}) {
1226
+ const profile = detectSystemProfile();
1227
+ const checks = {
1228
+ node_supported: {
1229
+ ok: profile.node_supported,
1230
+ version: profile.node,
1231
+ required: '>=20',
1232
+ },
1233
+ native_sqlite_vec: await nativeEngineCheck(),
1234
+ };
1235
+
1236
+ if (!opts['skip-model']) {
1237
+ try {
1238
+ const warmed = await embeddingWarmup();
1239
+ checks.bge_model = {
1240
+ ok: warmed.ok && warmed.dims === EMBEDDING_DIMS,
1241
+ dims: warmed.dims,
1242
+ profile: EMBEDDING_PROFILE,
1243
+ cache_dir: process.env.SUPERCOLLAB_MODEL_CACHE || 'default @huggingface/transformers cache',
1244
+ };
1245
+ } catch (err) {
1246
+ checks.bge_model = {
1247
+ ok: false,
1248
+ profile: EMBEDDING_PROFILE,
1249
+ error: err.message || String(err),
1250
+ };
1251
+ }
1252
+ } else {
1253
+ checks.bge_model = {
1254
+ ok: null,
1255
+ skipped: true,
1256
+ profile: EMBEDDING_PROFILE,
1257
+ };
1258
+ }
1259
+
1260
+ const ok = checks.node_supported.ok && checks.native_sqlite_vec.ok && (checks.bge_model.ok === true || checks.bge_model.skipped === true);
1261
+ const result = {
1262
+ ok,
1263
+ checked_at: nowIso(),
1264
+ system: profile,
1265
+ checks,
1266
+ local_engine: {
1267
+ id: 'native-sqlite-vec',
1268
+ transcript_store: 'better-sqlite3',
1269
+ vector_store: 'sqlite-vec',
1270
+ vector_version: checks.native_sqlite_vec.sqlite_vec_version || null,
1271
+ embedding_profile_id: EMBEDDING_PROFILE.id,
1272
+ },
1273
+ advice: installAdvice(profile, checks),
1274
+ };
1275
+
1276
+ config.systemProfile = profile;
1277
+ config.localEngine = result.local_engine;
1278
+ config.embeddingProfile = EMBEDDING_PROFILE;
1279
+ config.setupChecks = {
1280
+ ok,
1281
+ checked_at: result.checked_at,
1282
+ checks,
1283
+ advice: result.advice,
1284
+ };
1285
+ saveConfig(config, file);
1286
+ return result;
1287
+ }
1288
+
1289
+ function printDoctor(result) {
1290
+ console.log(JSON.stringify(result, null, 2));
1291
+ }
1292
+
1293
+ async function loadPrompts() {
1294
+ return import('@clack/prompts');
1295
+ }
1296
+
1297
+ function isConfigured(config) {
1298
+ return Boolean(config.userToken && config.agentId && config.agentPrivateKeyPem);
1299
+ }
1300
+
1301
+ function promptRequired(value) {
1302
+ return String(value || '').trim() ? undefined : 'Required';
1303
+ }
1304
+
1305
+ async function ensureAgentForSetup(config, file, prompts) {
1306
+ if (config.agentId && config.agentPrivateKeyPem) return null;
1307
+ const label = await prompts.text({
1308
+ message: 'Name this local agent',
1309
+ placeholder: `${os.hostname()}-agent`,
1310
+ defaultValue: `${os.hostname()}-agent`,
1311
+ validate: promptRequired,
1312
+ });
1313
+ if (prompts.isCancel(label)) throw new Error('cancelled');
1314
+ const agent = await registerAgent(config, String(label));
1315
+ saveConfig(config, file);
1316
+ return agent;
1317
+ }
1318
+
1319
+ async function runAuthSetup(config, file, prompts) {
1320
+ if (isConfigured(config)) {
1321
+ const reuse = await prompts.confirm({
1322
+ message: `Use existing login as ${config.username || 'current user'}?`,
1323
+ initialValue: true,
1324
+ });
1325
+ if (prompts.isCancel(reuse)) throw new Error('cancelled');
1326
+ if (reuse) return { reused: true };
1327
+ }
1328
+
1329
+ const mode = await prompts.select({
1330
+ message: 'Account setup',
1331
+ options: [
1332
+ { value: 'register', label: 'Create account', hint: 'new SuperCollab username' },
1333
+ { value: 'login', label: 'Log in', hint: 'existing username' },
1334
+ ],
1335
+ });
1336
+ if (prompts.isCancel(mode)) throw new Error('cancelled');
1337
+
1338
+ const username = await prompts.text({
1339
+ message: mode === 'register' ? 'Choose a username' : 'Username',
1340
+ validate: promptRequired,
1341
+ });
1342
+ if (prompts.isCancel(username)) throw new Error('cancelled');
1343
+
1344
+ const pass = await prompts.password({
1345
+ message: mode === 'register' ? 'Choose a password' : 'Password',
1346
+ validate: (value) => String(value || '').length >= 8 ? undefined : 'Use at least 8 characters',
1347
+ });
1348
+ if (prompts.isCancel(pass)) throw new Error('cancelled');
1349
+
1350
+ if (mode === 'register') {
1351
+ return doRegister(config, file, { username: String(username), password: String(pass), label: `${os.hostname()}-agent` });
1352
+ }
1353
+
1354
+ const login = await doLogin(config, file, { username: String(username), password: String(pass) });
1355
+ const agent = await ensureAgentForSetup(config, file, prompts);
1356
+ return { ...login, agent_id: agent?.agent_id || config.agentId, fingerprint: agent?.fingerprint || config.agentFingerprint };
1357
+ }
1358
+
1359
+ async function runRoomSetup(config, file, prompts) {
1360
+ const choice = await prompts.select({
1361
+ message: 'Room setup',
1362
+ options: [
1363
+ { value: 'create', label: 'Create a new room', hint: 'start solo or invite agents later' },
1364
+ { value: 'join', label: 'Join with private invite', hint: 'paste sci_...sck_...' },
1365
+ { value: 'skip', label: 'Skip for now', hint: 'set up auth and local engine only' },
1366
+ ],
1367
+ });
1368
+ if (prompts.isCancel(choice)) throw new Error('cancelled');
1369
+ if (choice === 'skip') return null;
1370
+
1371
+ if (choice === 'join') {
1372
+ const invite = await prompts.text({
1373
+ message: 'Paste private invite',
1374
+ placeholder: 'sci_....sck_...',
1375
+ validate: promptRequired,
1376
+ });
1377
+ if (prompts.isCancel(invite)) throw new Error('cancelled');
1378
+ const joined = await doRoomJoin(config, file, { invite: String(invite).trim() });
1379
+ return { room_id: joined.room_id || joined.workspace_id, joined };
1380
+ }
1381
+
1382
+ const title = await prompts.text({
1383
+ message: 'Room title',
1384
+ placeholder: 'Launch Room',
1385
+ validate: promptRequired,
1386
+ });
1387
+ if (prompts.isCancel(title)) throw new Error('cancelled');
1388
+ const goal = await prompts.text({
1389
+ message: 'Room goal',
1390
+ placeholder: 'Coordinate agents on this project',
1391
+ validate: promptRequired,
1392
+ });
1393
+ if (prompts.isCancel(goal)) throw new Error('cancelled');
1394
+ const created = await doRoomCreate(config, file, { title: String(title), goal: String(goal) });
1395
+ return { room_id: created.room_id || created.id, created };
1396
+ }
1397
+
1398
+ async function runActivationSetup(config, file, roomId, prompts) {
1399
+ if (!roomId) return null;
1400
+ const shouldActivate = await prompts.confirm({
1401
+ message: 'Activate SuperCollab for a local project directory now?',
1402
+ initialValue: true,
1403
+ });
1404
+ if (prompts.isCancel(shouldActivate)) throw new Error('cancelled');
1405
+ if (!shouldActivate) return null;
1406
+ const cwd = await prompts.text({
1407
+ message: 'Project directory',
1408
+ defaultValue: process.cwd(),
1409
+ placeholder: process.cwd(),
1410
+ validate: (value) => {
1411
+ const resolved = path.resolve(String(value || ''));
1412
+ return fs.existsSync(resolved) ? undefined : 'Directory does not exist';
1413
+ },
1414
+ });
1415
+ if (prompts.isCancel(cwd)) throw new Error('cancelled');
1416
+ return activate(config, file, { room: roomId, cwd: String(cwd) });
1417
+ }
1418
+
1419
+ async function runSetupSmoke(config, file, roomId, prompts) {
1420
+ if (!roomId) return null;
1421
+ const shouldSmoke = await prompts.confirm({
1422
+ message: 'Send a setup note and verify local BGE search?',
1423
+ initialValue: true,
1424
+ });
1425
+ if (prompts.isCancel(shouldSmoke)) throw new Error('cancelled');
1426
+ if (!shouldSmoke) return null;
1427
+ const marker = `setup-${Date.now()}-${crypto.randomBytes(4).toString('hex')}`;
1428
+ const text = `SuperCollab setup complete on ${os.hostname()} (${marker})`;
1429
+ await doChatSend(config, file, { room: roomId, text, channel: 'agents', kind: 'setup.note' });
1430
+ const search = await doChatSearch(config, file, { room: roomId, query: marker, mode: 'hybrid', limit: 5 });
1431
+ return {
1432
+ ok: search.results.some((row) => String(row.body || '').includes(marker)),
1433
+ marker,
1434
+ search_count: search.results.length,
1435
+ };
1436
+ }
1437
+
1438
+ function mcpConfigText(client, file) {
1439
+ const escaped = file.replaceAll('\\', '\\\\').replaceAll('"', '\\"');
1440
+ if (client === 'claude') {
1441
+ return JSON.stringify({
1442
+ mcpServers: {
1443
+ supercollab: {
1444
+ command: 'supercollab',
1445
+ args: ['mcp', 'stdio', '--config', file],
1446
+ },
1447
+ },
1448
+ }, null, 2);
1449
+ }
1450
+ if (client === 'codex') {
1451
+ return `[mcp_servers.supercollab]\ncommand = "supercollab"\nargs = ["mcp", "stdio", "--config", "${escaped}"]`;
1452
+ }
1453
+ return `supercollab mcp stdio --config "${escaped}"`;
1454
+ }
1455
+
1456
+ async function runSetupWizard(config, file, opts = {}) {
1457
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
1458
+ throw new Error('interactive setup requires a TTY; run `supercollab doctor --json` for non-interactive diagnostics');
1459
+ }
1460
+ const prompts = await loadPrompts();
1461
+ prompts.intro(`SuperCollab ${VERSION}`);
1462
+ try {
1463
+ const spin = prompts.spinner();
1464
+ spin.start('Checking this machine and installing the local BGE model');
1465
+ const doctor = await runDoctor(config, file, {});
1466
+ if (doctor.ok) {
1467
+ spin.stop(`Local engine ready: ${doctor.local_engine.id}, ${doctor.local_engine.vector_version || 'sqlite-vec'}`);
1468
+ } else {
1469
+ spin.stop('Local engine check needs attention');
1470
+ for (const line of doctor.advice || []) prompts.note(line, 'Fix');
1471
+ const cont = await prompts.confirm({ message: 'Continue setup anyway?', initialValue: false });
1472
+ if (prompts.isCancel(cont) || !cont) throw new Error('cancelled');
1473
+ }
1474
+
1475
+ await runAuthSetup(config, file, prompts);
1476
+ const room = await runRoomSetup(config, file, prompts);
1477
+ const roomId = room?.room_id || null;
1478
+ const activation = await runActivationSetup(config, file, roomId, prompts);
1479
+ const smoke = await runSetupSmoke(config, file, roomId, prompts);
1480
+
1481
+ const client = await prompts.select({
1482
+ message: 'MCP client config',
1483
+ options: [
1484
+ { value: 'codex', label: 'Codex', hint: 'TOML config snippet' },
1485
+ { value: 'claude', label: 'Claude', hint: 'JSON config snippet' },
1486
+ { value: 'manual', label: 'Manual', hint: 'stdio command' },
1487
+ { value: 'skip', label: 'Skip', hint: 'show later with mcp print-config' },
1488
+ ],
1489
+ });
1490
+ if (prompts.isCancel(client)) throw new Error('cancelled');
1491
+
1492
+ config.onboarding = {
1493
+ completed_at: nowIso(),
1494
+ cli_version: VERSION,
1495
+ room_id: roomId,
1496
+ activation_root: activation?.cwd || null,
1497
+ smoke,
1498
+ };
1499
+ saveConfig(config, file);
1500
+
1501
+ if (client !== 'skip') {
1502
+ prompts.note(mcpConfigText(client, file), `${client} MCP config`);
1503
+ }
1504
+ prompts.outro(`Ready. Config saved at ${file}`);
1505
+ return { ok: true, room_id: roomId, activation, smoke, config: file };
1506
+ } catch (err) {
1507
+ prompts.cancel(err.message === 'cancelled' ? 'Setup cancelled.' : `Setup stopped: ${err.message}`);
1508
+ throw err;
1509
+ }
1510
+ }
1511
+
1118
1512
  async function main() {
1119
1513
  const { positionals, opts } = parse(process.argv.slice(2));
1120
- if (opts.help || positionals.length === 0) { printHelp(); return; }
1514
+ if (opts.help) { printHelp(); return; }
1121
1515
  const [cmd, sub] = positionals;
1122
1516
  const file = configPath(opts);
1123
1517
  const config = attachRuntimeConfig(loadConfig(file), file);
1124
1518
  config.serverUrl = opts.server || config.serverUrl || DEFAULT_SERVER;
1125
1519
 
1520
+ if (positionals.length === 0) {
1521
+ if (process.stdin.isTTY && process.stdout.isTTY) return runSetupWizard(config, file, opts);
1522
+ printHelp();
1523
+ return;
1524
+ }
1525
+ if (cmd === 'setup') return runSetupWizard(config, file, opts);
1526
+ if (cmd === 'doctor') {
1527
+ const data = await runDoctor(config, file, opts);
1528
+ if (opts.json) return console.log(JSON.stringify(data, null, 2));
1529
+ printDoctor(data);
1530
+ return;
1531
+ }
1126
1532
  if (cmd === 'register') return console.log(JSON.stringify(await doRegister(config, file, opts), null, 2));
1127
1533
  if (cmd === 'login') return console.log(JSON.stringify(await doLogin(config, file, opts), null, 2));
1128
1534
  if (cmd === 'whoami') return console.log(JSON.stringify(await api(config, 'GET', '/v1/me'), null, 2));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supercollab/cli",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
4
4
  "description": "SuperCollab CLI and MCP bridge for encrypted local-search agent group chat.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -14,6 +14,7 @@
14
14
  "node": ">=20"
15
15
  },
16
16
  "dependencies": {
17
+ "@clack/prompts": "0.11.0",
17
18
  "@huggingface/transformers": "3.8.1",
18
19
  "better-sqlite3": "12.11.1",
19
20
  "sqlite-vec": "0.1.9"