@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.d.ts.map +1 -1
- package/dist/cli.js +437 -144
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +297 -61
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/__tests__/tool-execution.test.d.ts +0 -2
- package/dist/__tests__/tool-execution.test.d.ts.map +0 -1
- package/dist/__tests__/tool-execution.test.js +0 -499
- package/dist/__tests__/tool-execution.test.js.map +0 -1
- package/dist/__tests__/tools.test.d.ts +0 -2
- package/dist/__tests__/tools.test.d.ts.map +0 -1
- package/dist/__tests__/tools.test.js +0 -100
- package/dist/__tests__/tools.test.js.map +0 -1
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
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
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:
|
|
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:
|
|
616
|
+
// ── Step 4: Linking ──────────────────────────────────────────────────
|
|
590
617
|
console.log();
|
|
591
618
|
console.log(` ${B}STEP 4:${R}`);
|
|
592
|
-
|
|
593
|
-
if (
|
|
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
|
|
639
|
+
// ── Step 6: Continue to setup (reasoning test + IDE config) ──────────
|
|
602
640
|
console.log();
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
//
|
|
733
|
-
|
|
734
|
-
if (
|
|
735
|
-
|
|
736
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
1187
|
-
join(
|
|
1188
|
-
join(
|
|
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: "
|
|
1224
|
+
targets.push({ name: "Zed", path: p, format: "zed" });
|
|
1193
1225
|
break;
|
|
1194
1226
|
}
|
|
1195
1227
|
}
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
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
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
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
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
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
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
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
|
-
|
|
1241
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1249
|
-
console.log(
|
|
1250
|
-
console.log(
|
|
1251
|
-
console.log();
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
}
|
|
1256
|
-
|
|
1257
|
-
|
|
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
|
}
|