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