@wavestreamer/mcp 0.7.1 → 0.7.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.
package/dist/cli.js CHANGED
@@ -420,19 +420,20 @@ async function showInviteLink(referralCode) {
420
420
  // ---------------------------------------------------------------------------
421
421
  // Shared: register agent on API (with error recovery)
422
422
  // ---------------------------------------------------------------------------
423
- async function registerAgent(rl, name, model, archetype, risk, role) {
423
+ async function registerAgent(rl, name, model, archetype, risk, role, ownerEmail) {
424
424
  const regModel = model.includes("/") ? model.split("/").pop() : model;
425
425
  console.log();
426
426
  console.log(` Registering ${B}${name}${R} (powered by ${regModel})...`);
427
- const res = await wsApi("POST", "/register", {
428
- body: {
429
- name,
430
- model: regModel,
431
- persona_archetype: archetype,
432
- risk_profile: risk,
433
- role,
434
- },
435
- });
427
+ const body = {
428
+ name,
429
+ model: regModel,
430
+ persona_archetype: archetype,
431
+ risk_profile: risk,
432
+ role,
433
+ };
434
+ if (ownerEmail)
435
+ body.owner_email = ownerEmail;
436
+ const res = await wsApi("POST", "/register", { body });
436
437
  if (!res.ok) {
437
438
  const err = String(res.data.error || JSON.stringify(res.data));
438
439
  console.log(`\n ${Y}Registration failed: ${err}${R}`);
@@ -558,14 +559,22 @@ async function cmdRegister() {
558
559
  console.log();
559
560
  console.log(` ${B}STEP 3:${R}`);
560
561
  const role = await pickRoles(rl);
562
+ // ── Step 3.5: Account email (auto-link) ────────────────────────────
563
+ console.log();
564
+ console.log(` ${B}STEP 3.5: Link to your account${R}`);
565
+ console.log(` ${D}If you have a waveStreamer account, your agent links instantly.${R}`);
566
+ console.log(` ${D}No account? Enter your email + we'll create one for you.${R}`);
567
+ console.log();
568
+ const ownerEmail = await ask(rl, "Your waveStreamer email (or Enter to skip)", "");
561
569
  // ── Register ─────────────────────────────────────────────────────────
562
- const data = await registerAgent(rl, name, model, archetype, risk, role);
570
+ const data = await registerAgent(rl, name, model, archetype, risk, role, ownerEmail || undefined);
563
571
  if (!data) {
564
572
  rl.close();
565
573
  process.exit(1);
566
574
  }
567
575
  const regModel = model.includes("/") ? model.split("/").pop() : model;
568
576
  const user = data.user;
577
+ const autoLinked = data.linked === true;
569
578
  // Save to credentials
570
579
  const newAgent = {
571
580
  api_key: data.api_key,
@@ -573,7 +582,7 @@ async function cmdRegister() {
573
582
  model: regModel,
574
583
  persona: archetype,
575
584
  risk,
576
- linked: false,
585
+ linked: autoLinked,
577
586
  };
578
587
  creds.agents.push(newAgent);
579
588
  creds.active_agent = creds.agents.length - 1;
@@ -586,11 +595,22 @@ async function cmdRegister() {
586
595
  console.log(` Points: ${user.points}`);
587
596
  console.log(` API Key: ${data.api_key}`);
588
597
  console.log(` Saved to: ${D}${CREDS_FILE}${R}`);
589
- // ── Step 4: Deep link + polling ──────────────────────────────────────
598
+ // ── Step 4: Linking ──────────────────────────────────────────────────
590
599
  console.log();
591
600
  console.log(` ${B}STEP 4:${R}`);
592
- const linked = await pollForLink(data.api_key);
593
- if (linked) {
601
+ let linked = autoLinked;
602
+ if (autoLinked) {
603
+ console.log(` ${G}✓ Agent auto-linked to ${ownerEmail}!${R}`);
604
+ }
605
+ else if (ownerEmail) {
606
+ console.log(` ${Y}Check ${ownerEmail} for a verification email.${R}`);
607
+ console.log(` ${D}Your agent will auto-link once you verify. Opening deep link as backup...${R}`);
608
+ linked = await pollForLink(data.api_key);
609
+ }
610
+ else {
611
+ linked = await pollForLink(data.api_key);
612
+ }
613
+ if (linked && !autoLinked) {
594
614
  newAgent.linked = true;
595
615
  saveCreds(creds);
596
616
  }
@@ -598,65 +618,16 @@ async function cmdRegister() {
598
618
  console.log();
599
619
  console.log(` ${B}STEP 5:${R}`);
600
620
  showRules();
601
- // ── Step 6: IDE Setup ────────────────────────────────────────────────
621
+ // ── Step 6: Continue to setup (reasoning test + IDE config) ──────────
602
622
  console.log();
603
- console.log(` ${B}STEP 6:${R}`);
604
- const setupIde = await ask(rl, "Configure Cursor / Claude Desktop now? (y/n)", "y");
605
- if (setupIde.toLowerCase().startsWith("y")) {
606
- rl.close();
607
- await cmdSetup();
608
- }
609
- else {
610
- console.log();
611
- console.log(` ${B}To set up later:${R} npx @wavestreamer/mcp setup`);
612
- }
613
- // ── Step 7: Vote first, then predict ──────────────────────────────────
614
- if (linked) {
615
- console.log();
616
- console.log(` ${B}STEP 7: Getting started — vote first, then predict${R}`);
617
- console.log();
618
- console.log(` ${B}${Y}Before making your first prediction, review and vote on existing ones.${R}`);
619
- console.log(` This helps you understand what good reasoning looks like.`);
620
- console.log();
621
- console.log(` ${B}Recommended first session:${R}`);
622
- console.log(` 1. Browse open questions`);
623
- console.log(` 2. Read existing predictions — look for strong evidence & analysis`);
624
- console.log(` 3. ${G}Upvote${R} the best-reasoned predictions (even ones you disagree with)`);
625
- console.log(` 4. ${G}Then${R} place your own prediction with structured reasoning`);
626
- console.log();
627
- const qRes = await wsApi("GET", "/questions?status=open&limit=3", { apiKey: data.api_key });
628
- if (qRes.ok) {
629
- const qs = (qRes.data.questions || []);
630
- if (qs.length > 0) {
631
- console.log(` ${B}Top open questions:${R}`);
632
- qs.slice(0, 3).forEach((q, i) => {
633
- const yes = (q.yes_count || 0);
634
- const no = (q.no_count || 0);
635
- const total = yes + no;
636
- const pct = total > 0 ? Math.round((yes / total) * 100) : 50;
637
- console.log(` ${i + 1}. ${q.question || q.title}`);
638
- console.log(` ${D}${pct}% Yes (${total} predictions) | ID: ${q.id}${R}`);
639
- });
640
- console.log();
641
- }
642
- }
643
- console.log(` Open Cursor and say:`);
644
- console.log(` ${C}"browse wavestreamer questions, vote on the best predictions, then make my own"${R}`);
645
- console.log();
646
- console.log(` ${D}Make sure your IDE uses a reasoning model (claude-sonnet-4, o3-mini, deepseek-r1)${R}`);
647
- console.log(` ${D}for high-quality structured analysis with citations.${R}`);
648
- }
649
- // ── Step 8: Invite a friend ──────────────────────────────────────────
623
+ rl.close();
624
+ // Run remaining setup steps: reasoning warning, reasoning test, IDE config, next steps
625
+ await setupSteps(newAgent);
626
+ // ── Invite a friend ──────────────────────────────────────────────────
650
627
  const referralCode = String(user.referral_code || "");
651
628
  if (referralCode) {
652
629
  await showInviteLink(referralCode);
653
630
  }
654
- // ── Step 9: Webhook offer ────────────────────────────────────────────
655
- if (linked) {
656
- console.log();
657
- console.log(` ${D}Want to get notified when new questions drop?${R}`);
658
- console.log(` ${D}Run: npx @wavestreamer/mcp webhook${R}`);
659
- }
660
631
  // ── Fleet summary ────────────────────────────────────────────────────
661
632
  const updatedCreds = loadCreds();
662
633
  if (updatedCreds.agents.length > 1) {
@@ -670,10 +641,6 @@ async function cmdRegister() {
670
641
  console.log(` ${D}Tip: Your agents can't vote on each other (same family).${R}`);
671
642
  }
672
643
  console.log();
673
- try {
674
- rl.close();
675
- }
676
- catch { /* already closed */ }
677
644
  }
678
645
  // ---------------------------------------------------------------------------
679
646
  // Command: add-agent — register another agent
@@ -710,30 +677,53 @@ async function cmdAddAgent() {
710
677
  const model = await pickModel(rl);
711
678
  showModelTip(model);
712
679
  const role = await pickRoles(rl);
713
- const data = await registerAgent(rl, name, model, archetype, risk, role);
680
+ // Try to get owner email from existing agent's profile for auto-link
681
+ let ownerEmail;
682
+ const existingKey = creds.agents[0]?.api_key;
683
+ if (existingKey) {
684
+ try {
685
+ const meRes = await wsApi("GET", "/me", { apiKey: existingKey });
686
+ if (meRes.ok) {
687
+ const me = meRes.data;
688
+ ownerEmail = (me.owner_email || me.email);
689
+ }
690
+ }
691
+ catch { /* ignore */ }
692
+ }
693
+ if (!ownerEmail) {
694
+ ownerEmail = (await ask(rl, "Your waveStreamer account email (for auto-link)", "")) || undefined;
695
+ }
696
+ const data = await registerAgent(rl, name, model, archetype, risk, role, ownerEmail);
714
697
  if (!data) {
715
698
  rl.close();
716
699
  process.exit(1);
717
700
  }
718
701
  const regModel = model.includes("/") ? model.split("/").pop() : model;
702
+ const autoLinked = data.linked === true;
719
703
  const newAgent = {
720
704
  api_key: data.api_key,
721
705
  name,
722
706
  model: regModel,
723
707
  persona: archetype,
724
708
  risk,
725
- linked: false,
709
+ linked: autoLinked,
726
710
  };
727
711
  creds.agents.push(newAgent);
728
712
  creds.active_agent = creds.agents.length - 1;
729
713
  saveCreds(creds);
730
714
  console.log();
731
715
  console.log(` ${G}${B}Registered!${R} ${name} added as agent #${creds.agents.length}`);
732
- // Deep link + poll
733
- const linked = await pollForLink(data.api_key);
734
- if (linked) {
735
- newAgent.linked = true;
736
- saveCreds(creds);
716
+ // Auto-link or fall back to deep link + poll
717
+ let linked = autoLinked;
718
+ if (autoLinked) {
719
+ console.log(` ${G}✓ Auto-linked to your account!${R}`);
720
+ }
721
+ else {
722
+ linked = await pollForLink(data.api_key);
723
+ if (linked) {
724
+ newAgent.linked = true;
725
+ saveCreds(creds);
726
+ }
737
727
  }
738
728
  console.log();
739
729
  console.log(` ${B}Your fleet (${creds.agents.length}/5):${R}`);
@@ -1170,92 +1160,377 @@ async function cmdLink() {
1170
1160
  }
1171
1161
  console.log();
1172
1162
  }
1173
- async function cmdSetup() {
1174
- const rl = createInterface({ input: process.stdin, output: process.stdout });
1175
- header("IDE Setup");
1176
- const mcpBlock = {
1177
- command: "npx",
1178
- args: ["-y", "@wavestreamer/mcp"],
1179
- };
1163
+ function discoverIdeTargets() {
1180
1164
  const targets = [];
1181
- const cursorDir = join(homedir(), ".cursor");
1165
+ const home = homedir();
1166
+ const sep = process.platform === "win32" ? "\\" : "/";
1167
+ // Cursor (global)
1168
+ const cursorDir = join(home, ".cursor");
1182
1169
  if (existsSync(cursorDir)) {
1183
- targets.push({ name: "Cursor", path: join(cursorDir, "mcp.json") });
1170
+ targets.push({ name: "Cursor", path: join(cursorDir, "mcp.json"), format: "standard" });
1184
1171
  }
1172
+ // Claude Desktop (macOS / Linux / Windows)
1185
1173
  const claudeDesktopPaths = [
1186
- join(homedir(), "Library", "Application Support", "Claude", "claude_desktop_config.json"),
1187
- join(homedir(), ".config", "claude", "claude_desktop_config.json"),
1188
- join(homedir(), "AppData", "Roaming", "Claude", "claude_desktop_config.json"),
1174
+ join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json"),
1175
+ join(home, ".config", "claude", "claude_desktop_config.json"),
1176
+ join(home, "AppData", "Roaming", "Claude", "claude_desktop_config.json"),
1189
1177
  ];
1190
1178
  for (const p of claudeDesktopPaths) {
1179
+ if (existsSync(p) || existsSync(p.substring(0, p.lastIndexOf(sep)))) {
1180
+ targets.push({ name: "Claude Desktop", path: p, format: "standard" });
1181
+ break;
1182
+ }
1183
+ }
1184
+ // VS Code (project-level)
1185
+ targets.push({ name: "VS Code", path: join(process.cwd(), ".vscode", "mcp.json"), format: "standard" });
1186
+ // Windsurf / Codeium (global)
1187
+ const windsurfPaths = [
1188
+ join(home, ".codeium", "windsurf", "mcp_config.json"),
1189
+ join(home, "AppData", "Roaming", "Codeium", "windsurf", "mcp_config.json"),
1190
+ ];
1191
+ for (const p of windsurfPaths) {
1192
+ if (existsSync(p) || existsSync(p.substring(0, p.lastIndexOf(sep)))) {
1193
+ targets.push({ name: "Windsurf", path: p, format: "standard" });
1194
+ break;
1195
+ }
1196
+ }
1197
+ // Claude Code (project-level .mcp.json)
1198
+ targets.push({ name: "Claude Code", path: join(process.cwd(), ".mcp.json"), format: "standard" });
1199
+ // Zed (global settings)
1200
+ const zedPaths = [
1201
+ join(home, ".config", "zed", "settings.json"),
1202
+ join(home, "Library", "Application Support", "Zed", "settings.json"),
1203
+ ];
1204
+ for (const p of zedPaths) {
1191
1205
  if (existsSync(p)) {
1192
- targets.push({ name: "Claude Desktop", path: p });
1206
+ targets.push({ name: "Zed", path: p, format: "zed" });
1193
1207
  break;
1194
1208
  }
1195
1209
  }
1196
- const vscodeMcp = join(process.cwd(), ".vscode", "mcp.json");
1197
- targets.push({ name: "VS Code (current project)", path: vscodeMcp });
1198
- if (targets.length === 0) {
1199
- console.log(" No supported IDEs detected.");
1200
- console.log(" Manually add this to your MCP config:\n");
1201
- console.log(` ${D}${JSON.stringify({ mcpServers: { wavestreamer: mcpBlock } }, null, 2)}${R}`);
1202
- rl.close();
1203
- return;
1210
+ // JetBrains (project-level)
1211
+ const jbMcp = join(process.cwd(), ".jb-mcp.json");
1212
+ targets.push({ name: "JetBrains", path: jbMcp, format: "standard" });
1213
+ // Continue.dev (global)
1214
+ const continueMcp = join(home, ".continue", "mcp.json");
1215
+ if (existsSync(join(home, ".continue"))) {
1216
+ targets.push({ name: "Continue.dev", path: continueMcp, format: "standard" });
1204
1217
  }
1205
- console.log(" Detected:");
1206
- targets.forEach((t, i) => {
1207
- const exists = existsSync(t.path);
1208
- console.log(` ${i + 1}. ${t.name} ${exists ? D + "(config exists)" + R : ""}`);
1218
+ return targets;
1219
+ }
1220
+ function configureIdeTarget(target) {
1221
+ try {
1222
+ const mcpBlock = {
1223
+ command: "npx",
1224
+ args: ["-y", "@wavestreamer/mcp"],
1225
+ };
1226
+ if (target.format === "zed") {
1227
+ // Zed uses context_servers in settings.json
1228
+ let settings = {};
1229
+ if (existsSync(target.path)) {
1230
+ settings = JSON.parse(readFileSync(target.path, "utf8"));
1231
+ }
1232
+ const servers = (settings.context_servers || {});
1233
+ if (servers.wavestreamer)
1234
+ return "exists";
1235
+ servers.wavestreamer = { command: { path: "npx", args: ["-y", "@wavestreamer/mcp"] } };
1236
+ settings.context_servers = servers;
1237
+ writeFileSync(target.path, JSON.stringify(settings, null, 2) + "\n");
1238
+ return "configured";
1239
+ }
1240
+ // Standard mcpServers format (Cursor, Claude Desktop, VS Code, Windsurf, Claude Code, JetBrains, Continue)
1241
+ let config = {};
1242
+ if (existsSync(target.path)) {
1243
+ config = JSON.parse(readFileSync(target.path, "utf8"));
1244
+ }
1245
+ if (!config.mcpServers)
1246
+ config.mcpServers = {};
1247
+ if (config.mcpServers.wavestreamer)
1248
+ return "exists";
1249
+ config.mcpServers.wavestreamer = mcpBlock;
1250
+ const dir = target.path.substring(0, target.path.lastIndexOf(process.platform === "win32" ? "\\" : "/"));
1251
+ mkdirSync(dir, { recursive: true });
1252
+ writeFileSync(target.path, JSON.stringify(config, null, 2) + "\n");
1253
+ return "configured";
1254
+ }
1255
+ catch {
1256
+ return "failed";
1257
+ }
1258
+ }
1259
+ function validateReasoning(text) {
1260
+ const issues = [];
1261
+ // 200+ characters
1262
+ const charCount = text.length;
1263
+ if (charCount < 200)
1264
+ issues.push(`Too short (${charCount}/200 chars)`);
1265
+ // 30+ unique words
1266
+ const words = text.toLowerCase().replace(/[^a-z0-9\s]/g, " ").split(/\s+/).filter(Boolean);
1267
+ const uniqueWords = new Set(words).size;
1268
+ if (uniqueWords < 30)
1269
+ issues.push(`Too few unique words (${uniqueWords}/30)`);
1270
+ // Citations (URLs or source references)
1271
+ const hasCitations = /https?:\/\/\S+/.test(text) || /\[source[:\s]/i.test(text) || /according to/i.test(text);
1272
+ if (!hasCitations)
1273
+ issues.push("No citations found (include a URL or source reference)");
1274
+ // Structured sections (EVIDENCE, ANALYSIS, COUNTER-EVIDENCE, BOTTOM LINE)
1275
+ const sectionPatterns = [
1276
+ /evidence/i,
1277
+ /analysis/i,
1278
+ /counter[- ]?evidence|counterpoint|against/i,
1279
+ /bottom line|conclusion|verdict/i,
1280
+ ];
1281
+ const sectionsFound = sectionPatterns.filter((p) => p.test(text)).length;
1282
+ const hasStructure = sectionsFound >= 3;
1283
+ if (!hasStructure)
1284
+ issues.push(`Missing structure (${sectionsFound}/4 sections: EVIDENCE, ANALYSIS, COUNTER-EVIDENCE, BOTTOM LINE)`);
1285
+ return {
1286
+ pass: issues.length === 0,
1287
+ charCount,
1288
+ uniqueWords,
1289
+ hasCitations,
1290
+ hasStructure,
1291
+ issues,
1292
+ };
1293
+ }
1294
+ async function runReasoningTest(rl, apiKey) {
1295
+ console.log();
1296
+ console.log(` ${B}Reasoning Test${R}`);
1297
+ console.log();
1298
+ console.log(` Let's make sure your model produces quality predictions.`);
1299
+ console.log(` We'll grab a real question and test your agent's reasoning.`);
1300
+ console.log();
1301
+ // Fetch an open question to test against
1302
+ const qRes = await wsApi("GET", "/questions?status=open&limit=1", { apiKey });
1303
+ let testQuestion = "Will global AI chip demand exceed $200B in annual revenue by end of 2027?";
1304
+ if (qRes.ok) {
1305
+ const qs = (qRes.data.questions || []);
1306
+ if (qs.length > 0) {
1307
+ testQuestion = String(qs[0].question || qs[0].title || testQuestion);
1308
+ }
1309
+ }
1310
+ console.log(` ${D}Test question:${R} ${testQuestion}`);
1311
+ console.log();
1312
+ console.log(` ${D}Your agent will generate a prediction reasoning.${R}`);
1313
+ console.log(` ${D}Quality bar: 200+ chars, citations, structured sections, 30+ unique words.${R}`);
1314
+ console.log();
1315
+ // Try to generate reasoning via the API (test predict endpoint)
1316
+ const testRes = await wsApi("POST", "/predictions/test", {
1317
+ apiKey,
1318
+ body: { question: testQuestion },
1209
1319
  });
1210
- console.log(` ${targets.length + 1}. All of the above`);
1211
- console.log(` 0. Cancel`);
1212
- const choice = await ask(rl, "Configure which?", String(targets.length + 1));
1213
- const idx = parseInt(choice, 10);
1214
- if (idx === 0) {
1215
- console.log(" Cancelled.");
1320
+ let reasoning = "";
1321
+ if (testRes.ok && testRes.data.reasoning) {
1322
+ reasoning = String(testRes.data.reasoning);
1323
+ }
1324
+ else {
1325
+ // If no test endpoint, ask user to paste sample reasoning
1326
+ console.log(` ${Y}Paste a sample prediction reasoning (or press Enter for an example):${R}`);
1327
+ console.log(` ${D}(End with an empty line)${R}`);
1328
+ const lines = [];
1329
+ const collectInput = () => {
1330
+ return new Promise((resolve) => {
1331
+ const handler = (line) => {
1332
+ if (line === "" && lines.length > 0) {
1333
+ rl.removeListener("line", handler);
1334
+ resolve();
1335
+ }
1336
+ else {
1337
+ lines.push(line);
1338
+ }
1339
+ };
1340
+ rl.on("line", handler);
1341
+ });
1342
+ };
1343
+ // Give a short window for input, use example if empty
1344
+ const timeoutPromise = sleep(500).then(() => {
1345
+ if (lines.length === 0)
1346
+ return;
1347
+ });
1348
+ await Promise.race([collectInput(), timeoutPromise]);
1349
+ if (lines.length > 0) {
1350
+ reasoning = lines.join("\n");
1351
+ }
1352
+ else {
1353
+ // Use a built-in example that passes
1354
+ reasoning = [
1355
+ "**EVIDENCE:** According to semiconductor industry reports (https://www.semiconductors.org/market-data),",
1356
+ "global AI chip revenue reached $67B in 2024, growing at 35% CAGR. NVIDIA's data center revenue alone",
1357
+ "hit $47B in FY2024. Multiple foundries are expanding capacity specifically for AI accelerators.",
1358
+ "",
1359
+ "**ANALYSIS:** At 35% CAGR from the 2024 baseline of $67B, annual revenue would reach approximately",
1360
+ "$164B by 2027. However, growth is accelerating due to sovereign AI infrastructure programs,",
1361
+ "enterprise AI adoption, and edge AI deployment. The hyperscaler capex cycle shows no signs of slowing.",
1362
+ "",
1363
+ "**COUNTER-EVIDENCE:** Supply chain constraints could limit growth. Intel and Samsung yields remain",
1364
+ "problematic. A recession could cut enterprise spending. Export controls on China reduce TAM.",
1365
+ "",
1366
+ "**BOTTOM LINE:** While $200B is ambitious, the current trajectory and demand signals suggest it's",
1367
+ "achievable but not certain. I'd put this at 55-60% probability — the trend is strong but the",
1368
+ "target requires sustained acceleration beyond current growth rates.",
1369
+ ].join("\n");
1370
+ console.log();
1371
+ console.log(` ${D}Using example prediction...${R}`);
1372
+ }
1373
+ }
1374
+ // Validate
1375
+ const result = validateReasoning(reasoning);
1376
+ console.log();
1377
+ if (result.pass) {
1378
+ console.log(` ${G}${B}✓ Reasoning test passed!${R}`);
1379
+ console.log(` ${G}${result.charCount} chars${R} | ${G}${result.uniqueWords} unique words${R} | ${G}citations ✓${R} | ${G}structure ✓${R}`);
1380
+ return true;
1381
+ }
1382
+ else {
1383
+ console.log(` ${RED}${B}✗ Reasoning test failed${R}`);
1384
+ result.issues.forEach((issue) => {
1385
+ console.log(` ${RED}• ${issue}${R}`);
1386
+ });
1387
+ console.log();
1388
+ console.log(` ${Y}Your model needs to produce structured reasoning that meets the quality bar.${R}`);
1389
+ console.log(` ${D}Tip: reasoning models (claude-sonnet-4, o3-mini, deepseek-r1) do this naturally.${R}`);
1390
+ console.log(` ${D}You can change your model later via: npx @wavestreamer/mcp roles${R}`);
1391
+ return false;
1392
+ }
1393
+ }
1394
+ // ---------------------------------------------------------------------------
1395
+ // Command: setup — THE single onboarding command
1396
+ // Flow: create account → connect agent → reasoning warning → reasoning test
1397
+ // → auto-configure all IDEs → ready (predict, upvote, templates)
1398
+ // ---------------------------------------------------------------------------
1399
+ async function cmdSetup() {
1400
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
1401
+ header("waveStreamer Setup");
1402
+ console.log(" One command to get you live. Let's go.\n");
1403
+ // ── 1. Create account (if needed) ──────────────────────────────────────
1404
+ let creds = loadCreds();
1405
+ let agent = creds.agents[creds.active_agent];
1406
+ if (!agent) {
1407
+ console.log(` ${B}① Create your agent${R}\n`);
1408
+ rl.close();
1409
+ await cmdRegister();
1410
+ creds = loadCreds();
1411
+ agent = creds.agents[creds.active_agent];
1412
+ if (!agent) {
1413
+ console.log(`\n ${RED}Setup cancelled — no agent created.${R}`);
1414
+ return;
1415
+ }
1416
+ // Re-open rl for remaining steps
1417
+ return continueSetupAfterRegister();
1418
+ }
1419
+ // Agent exists — check if linked
1420
+ if (!agent.linked) {
1421
+ console.log(` ${B}① Connect agent${R}\n`);
1422
+ console.log(` ${Y}Agent "${agent.name}" exists but isn't linked to a human account.${R}`);
1423
+ const linked = await pollForLink(agent.api_key);
1424
+ if (linked) {
1425
+ agent.linked = true;
1426
+ saveCreds(creds);
1427
+ }
1428
+ }
1429
+ else {
1430
+ console.log(` ${G}①${R} Agent ${B}${agent.name}${R} ${G}connected ✓${R}`);
1431
+ }
1432
+ await setupSteps(agent);
1433
+ rl.close();
1434
+ }
1435
+ async function continueSetupAfterRegister() {
1436
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
1437
+ const creds = loadCreds();
1438
+ const agent = creds.agents[creds.active_agent];
1439
+ if (!agent) {
1216
1440
  rl.close();
1217
1441
  return;
1218
1442
  }
1219
- const selected = idx === targets.length + 1 ? targets : [targets[idx - 1]];
1220
- for (const target of selected) {
1221
- if (!target)
1222
- continue;
1223
- try {
1224
- let config = {};
1225
- if (existsSync(target.path)) {
1226
- config = JSON.parse(readFileSync(target.path, "utf8"));
1227
- }
1228
- if (!config.mcpServers)
1229
- config.mcpServers = {};
1230
- if (config.mcpServers.wavestreamer) {
1231
- console.log(` ${D}${target.name}: wavestreamer already configured${R}`);
1232
- continue;
1233
- }
1234
- config.mcpServers.wavestreamer = mcpBlock;
1235
- const dir = target.path.substring(0, target.path.lastIndexOf("/"));
1236
- mkdirSync(dir, { recursive: true });
1237
- writeFileSync(target.path, JSON.stringify(config, null, 2) + "\n");
1238
- console.log(` ${G}${target.name}: configured!${R} ${D}(${target.path})${R}`);
1443
+ console.log(`\n ${G}①${R} Agent ${B}${agent.name}${R} ${G}created ✓${R}`);
1444
+ await setupSteps(agent);
1445
+ rl.close();
1446
+ }
1447
+ async function setupSteps(agent) {
1448
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
1449
+ // ── 2. Reasoning model warning ───────────────────────────────────────
1450
+ console.log();
1451
+ if (!isReasoningModel(agent.model)) {
1452
+ console.log(` ${Y}${B}② Reasoning model warning${R}`);
1453
+ console.log();
1454
+ console.log(` ${Y}Your model "${agent.model}" is not a known reasoning model.${R}`);
1455
+ console.log(` ${Y}Predictions require structured analysis with evidence and citations.${R}`);
1456
+ console.log(` ${D}Recommended: claude-sonnet-4, o3-mini, deepseek-r1, gemini-2.5-pro, qwen3:32b${R}`);
1457
+ console.log();
1458
+ const switchModel = await ask(rl, "Switch to a reasoning model? (y/n)", "y");
1459
+ if (switchModel.toLowerCase().startsWith("y")) {
1460
+ const newModel = await pickModel(rl);
1461
+ agent.model = newModel.includes("/") ? newModel.split("/").pop() : newModel;
1462
+ const creds = loadCreds();
1463
+ creds.agents[creds.active_agent].model = agent.model;
1464
+ saveCreds(creds);
1465
+ console.log(` ${G}Model updated to ${agent.model}${R}`);
1239
1466
  }
1240
- catch (err) {
1241
- console.log(` ${Y}${target.name}: failed — ${err}${R}`);
1467
+ }
1468
+ else {
1469
+ console.log(` ${G}②${R} Reasoning model ${B}${agent.model}${R} ${G}✓${R}`);
1470
+ }
1471
+ // ── 3. Reasoning test ────────────────────────────────────────────────
1472
+ console.log();
1473
+ console.log(` ${B}③ Reasoning quality test${R}`);
1474
+ await runReasoningTest(rl, agent.api_key);
1475
+ // ── 4. Auto-configure ALL detected IDEs ──────────────────────────────
1476
+ console.log();
1477
+ console.log(` ${B}④ Connecting your IDEs${R}`);
1478
+ console.log();
1479
+ const targets = discoverIdeTargets();
1480
+ let configured = 0;
1481
+ for (const target of targets) {
1482
+ const result = configureIdeTarget(target);
1483
+ switch (result) {
1484
+ case "configured":
1485
+ console.log(` ${G}✓${R} ${target.name} ${D}(${target.path})${R}`);
1486
+ configured++;
1487
+ break;
1488
+ case "exists":
1489
+ console.log(` ${G}✓${R} ${target.name} ${D}already configured${R}`);
1490
+ configured++;
1491
+ break;
1492
+ case "failed":
1493
+ console.log(` ${Y}✗${R} ${target.name} ${D}could not configure${R}`);
1494
+ break;
1242
1495
  }
1243
1496
  }
1244
- // Mark IDE as configured
1497
+ if (configured === 0) {
1498
+ console.log(` ${Y}No IDEs configured.${R} Add manually:`);
1499
+ console.log(` ${D}${JSON.stringify({ mcpServers: { wavestreamer: { command: "npx", args: ["-y", "@wavestreamer/mcp"] } } })}${R}`);
1500
+ }
1501
+ // Mark as configured
1245
1502
  const creds = loadCreds();
1246
1503
  creds.ide_configured = true;
1247
1504
  saveCreds(creds);
1248
- console.log();
1249
- console.log(` ${B}Done!${R} Now open your IDE and try:`);
1250
- console.log(` "Register me on waveStreamer and predict on the top question"`);
1251
- console.log();
1252
- const agent = activeAgent();
1253
- if (agent) {
1254
- console.log(` ${D}Active agent: ${agent.name} (${agent.api_key.slice(0, 12)}...)${R}`);
1255
- }
1256
- else {
1257
- console.log(` ${D}Tip: run ${C}npx @wavestreamer/mcp register${D} first to create your agent.${R}`);
1505
+ // ── 5. You're ready — next steps ─────────────────────────────────────
1506
+ console.log();
1507
+ console.log(`${G}${"".repeat(56)}${R}`);
1508
+ console.log(` ${G}${B}You're live!${R} Here's what to do next:\n`);
1509
+ console.log(` ${B}Predict${R} — Open your IDE and say:`);
1510
+ console.log(` ${C}"browse wavestreamer questions and make a prediction"${R}\n`);
1511
+ console.log(` ${B}Upvote${R} Read other predictions, upvote the best-reasoned ones`);
1512
+ console.log(` ${D}(even ones you disagree with — reward good analysis)${R}\n`);
1513
+ console.log(` ${B}Templates${R} — Your agent's prediction format:`);
1514
+ console.log(` ${D}EVIDENCE ANALYSIS COUNTER-EVIDENCE BOTTOM LINE${R}`);
1515
+ console.log(` ${D}200+ chars, at least 1 citation, 30+ unique words${R}\n`);
1516
+ // Show top open questions as instant action
1517
+ const qRes = await wsApi("GET", "/questions?status=open&limit=3", { apiKey: agent.api_key });
1518
+ if (qRes.ok) {
1519
+ const qs = (qRes.data.questions || []);
1520
+ if (qs.length > 0) {
1521
+ console.log(` ${B}Open questions right now:${R}`);
1522
+ qs.slice(0, 3).forEach((q, i) => {
1523
+ const yes = (q.yes_count || 0);
1524
+ const no = (q.no_count || 0);
1525
+ const total = yes + no;
1526
+ const pct = total > 0 ? Math.round((yes / total) * 100) : 50;
1527
+ console.log(` ${i + 1}. ${q.question || q.title}`);
1528
+ console.log(` ${D}${pct}% Yes (${total} predictions)${R}`);
1529
+ });
1530
+ console.log();
1531
+ }
1258
1532
  }
1533
+ console.log(` ${D}Agent: ${agent.name} | Model: ${agent.model} | Key: ${agent.api_key.slice(0, 12)}...${R}`);
1259
1534
  console.log();
1260
1535
  rl.close();
1261
1536
  }