50c 3.9.5 → 3.9.7

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.

Potentially problematic release.


This version of 50c might be problematic. Click here for more details.

package/bin/50c.js CHANGED
@@ -1,2278 +1,2079 @@
1
- #!/usr/bin/env node
2
- /**
3
- * 50c - AI developer tools via MCP
4
- * Pay-per-use from $0.01. No subscriptions.
5
- * https://50c.ai
6
- */
7
-
8
- const https = require('https');
9
- const http = require('http');
10
- const readline = require('readline');
11
- const fs = require('fs');
12
- const path = require('path');
13
- const os = require('os');
14
- const { spawn } = require('child_process');
15
-
16
- // Early imports for CLI commands that need these
17
- let call50cTool, subagent, httpFetch, sshExec, localExec, KNOWN_SERVERS;
18
- let team, matchTaskToTools;
19
- try {
20
- const subagentModule = require('../lib/subagent.js');
21
- call50cTool = subagentModule.call50cTool;
22
- subagent = subagentModule.subagent;
23
- httpFetch = subagentModule.httpFetch;
24
- sshExec = subagentModule.sshExec;
25
- localExec = subagentModule.localExec;
26
- KNOWN_SERVERS = subagentModule.KNOWN_SERVERS;
27
-
28
- const teamModule = require('../lib/team.js');
29
- team = teamModule.team;
30
- matchTaskToTools = teamModule.matchTaskToTools;
31
- } catch (e) {
32
- // Graceful fallback if libs not available (shouldn't happen in normal install)
33
- }
34
-
35
- const VERSION = require('../package.json').version;
36
- const HOME = os.homedir();
37
- const HUB_DIR = path.join(HOME, '.50c');
38
- const CONFIG_FILE = path.join(HUB_DIR, 'config.json');
39
- const LOCKFILE = path.join(HUB_DIR, 'lockfile.json');
40
- const CACHE_DIR = path.join(HUB_DIR, 'cache');
41
-
42
- const API_ENDPOINT = process.env.FIFTYC_ENDPOINT || 'https://api.50c.ai';
43
- const MCP_ENDPOINT = API_ENDPOINT + '/mcp';
44
- const BEACON_ENDPOINT = process.env.FIFTYC_BEACON || 'https://beacon.50c.ai/mcp';
45
- const BEACON_TOOLS = ['tip', 'notip', 'health', 'compress', 'extract', 'mint', 'recall', 'remember', 'checkpoint', 'restore', 'drift', 'summarize', 'diff', 'scan', 'rank', 'focus'];
46
-
47
- // Ensure hub directory exists
48
- if (!fs.existsSync(HUB_DIR)) {
49
- fs.mkdirSync(HUB_DIR, { recursive: true });
50
- }
51
-
52
- // Load config
53
- function loadConfig() {
54
- try {
55
- if (fs.existsSync(CONFIG_FILE)) {
56
- return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
57
- }
58
- } catch (e) {}
59
- return { api_key: process.env.FIFTYC_API_KEY || null, packs: ['core'] };
60
- }
61
-
62
- function saveConfig(config) {
63
- fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
64
- }
65
-
66
- const CONFIG = loadConfig();
67
- const API_KEY = process.env.FIFTYC_API_KEY || CONFIG.api_key;
68
-
69
- // ═══════════════════════════════════════════════════════════════
70
- // CLI ROUTER
71
- // ═══════════════════════════════════════════════════════════════
72
-
73
- const args = process.argv.slice(2);
74
- const command = args[0];
75
- const subargs = args.slice(1);
76
-
77
- // Version
78
- if (command === '--version' || command === '-v') {
79
- console.log(VERSION);
80
- process.exit(0);
81
- }
82
-
83
- // Help
84
- if (command === '--help' || command === '-h' || command === 'help') {
85
- showHelp();
86
- process.exit(0);
87
- }
88
-
89
- // Install to IDEs
90
- if (command === 'install') {
91
- installMCP();
92
- process.exit(0);
93
- }
94
-
95
- // Hub commands (no API key needed)
96
- const HUB_COMMANDS = {
97
- 'status': cmdStatus,
98
- 'packs': cmdPacks,
99
- 'search': cmdSearch,
100
- 'add': cmdAdd,
101
- 'remove': cmdRemove,
102
- 'pin': cmdPin,
103
- 'update': cmdUpdate,
104
- 'balance': cmdBalance,
105
- 'snapshots': cmdSnapshots,
106
- 'restore': cmdRestore,
107
- 'tip': cmdTip,
108
- 'notip': cmdNoTip,
109
- 'mint': cmdMint,
110
- 'config': cmdConfig,
111
- };
112
-
113
- // Tool commands (need API key)
114
- const TOOL_COMMANDS = {
115
- 'hints': { tool: 'hints', arg: 'query', cost: '$0.05' },
116
- 'hints+': { tool: 'hints_plus', arg: 'query', cost: '$0.10' },
117
- 'vibe': { tool: 'quick_vibe', arg: 'working_on', cost: '$0.05' },
118
- 'one-liner': { tool: 'one_liner', arg: 'product', cost: '$0.02' },
119
- 'roast': { tool: 'roast', arg: 'code', cost: '$0.05' },
120
- 'name-it': { tool: 'name_it', arg: 'does', cost: '$0.03' },
121
- 'price-it': { tool: 'price_it', arg: 'product', cost: '$0.05' },
122
- 'genius': { tool: 'genius', arg: 'problem', cost: '$0.50' },
123
- 'compute': { tool: 'compute', arg: 'code', cost: '$0.02' },
124
- 'chat': { tool: 'ide_conversation', special: 'chat' },
125
- 'refocus': { tool: 'llm_refocus', special: 'refocus' },
126
- 'invent': { tool: 'auto_invent', special: 'invent', cost: '$2.00' },
127
- 'invent-ui': { tool: 'auto_invent', special: 'invent-ui', cost: '$2.00' },
128
- 'tv': { tool: null, special: 'mcp-tv', cost: 'FREE' },
129
- 'adopt': { tool: 'adoption_calc', special: 'adopt', cost: 'FREE' },
130
- 'adopt-dx': { tool: 'adoption_diagnose', special: 'adopt-dx', cost: 'FREE' },
131
- 'adopt-sim': { tool: 'adoption_simulate', special: 'adopt-sim', cost: 'FREE' },
132
- 'team': { tool: null, special: 'team', cost: 'FREE' },
133
- 'team-create': { tool: null, special: 'team-create', cost: 'FREE' },
134
- 'team-mint': { tool: null, special: 'team-mint', cost: 'FREE' },
135
- 'team-recall': { tool: null, special: 'team-recall', cost: 'FREE' },
136
- 'team-analytics': { tool: null, special: 'team-analytics', cost: 'FREE' },
137
- };
138
-
139
- // Route command
140
- if (command && HUB_COMMANDS[command]) {
141
- HUB_COMMANDS[command](subargs).catch(err => {
142
- console.error('Error:', err.message);
143
- process.exit(1);
144
- });
145
- } else if (command && TOOL_COMMANDS[command]) {
146
- if (!API_KEY) {
147
- console.error('Error: API key required. Run: 50c config key <your_key>');
148
- console.error('Get key at: https://50c.ai');
149
- process.exit(1);
150
- }
151
- runToolCommand(command, subargs).catch(err => {
152
- console.error('Error:', err.message);
153
- process.exit(1);
154
- });
155
- } else if (command && command.includes('.')) {
156
- // Pack.tool format: 50c beacon.compress "text"
157
- runPackTool(command, subargs).catch(err => {
158
- console.error('Error:', err.message);
159
- process.exit(1);
160
- });
161
- } else if (!command || command === 'mcp') {
162
- // MCP mode (stdin JSON-RPC)
163
- startMCPMode();
164
- } else {
165
- console.error(`Unknown command: ${command}`);
166
- console.error('Run: 50c help');
167
- process.exit(1);
168
- }
169
-
170
- // ═══════════════════════════════════════════════════════════════
171
- // HUB COMMANDS
172
- // ═══════════════════════════════════════════════════════════════
173
-
174
- async function cmdStatus() {
175
- console.log('50c Hub v' + VERSION);
176
- console.log('');
177
- console.log('Config:', CONFIG_FILE);
178
- console.log('API Key:', API_KEY ? API_KEY.slice(0, 6) + '...' : '(not set)');
179
- console.log('');
180
-
181
- // Check connectivity
182
- try {
183
- const health = await callAPI('/health', 'GET');
184
- console.log('API Status: online');
185
- } catch (e) {
186
- console.log('API Status: offline (using cache)');
187
- }
188
-
189
- // Show enabled packs
190
- console.log('');
191
- console.log('Enabled Packs:');
192
- const packs = CONFIG.packs || ['core'];
193
- for (const p of packs) {
194
- console.log(' - ' + p);
195
- }
196
- }
197
-
198
- async function cmdPacks() {
199
- console.log('Enabled Packs:');
200
- const packs = CONFIG.packs || ['core'];
201
- for (const p of packs) {
202
- console.log(' ' + p);
203
- }
204
- console.log('');
205
- console.log('Run: 50c search <query> to find more');
206
- console.log('Run: 50c add <pack> to enable');
207
- }
208
-
209
- async function cmdSearch(args) {
210
- const query = args.join(' ');
211
- if (!query) {
212
- console.log('Available Packs:');
213
- console.log('');
214
- console.log(' core - hints, vibe, roast, name-it, price-it (FREE basics)');
215
- console.log(' labs - genius, compute, mind-opener ($0.02-$0.65)');
216
- console.log(' beacon - compress, extract, mint, checkpoint ($0.01-$0.02)');
217
- console.log(' router - compare, battle (multi-model) ($0.05-$0.10)');
218
- console.log('');
219
- console.log('Run: 50c add <pack> to enable');
220
- return;
221
- }
222
-
223
- // Search API or show hardcoded for now
224
- console.log(`Searching for "${query}"...`);
225
- console.log('');
226
-
227
- // TODO: Call registry.50c.ai when ready
228
- const results = searchPacks(query);
229
- if (results.length === 0) {
230
- console.log('No packs found. Try: 50c search');
231
- } else {
232
- for (const r of results) {
233
- console.log(` ${r.pack}.${r.tool} - ${r.desc} (${r.cost})`);
234
- }
235
- }
236
- }
237
-
238
- function searchPacks(query) {
239
- const q = query.toLowerCase();
240
- const all = [
241
- { pack: 'core', tool: 'hints', desc: '5 brutal hints', cost: '$0.05' },
242
- { pack: 'core', tool: 'hints_plus', desc: '10 expanded hints', cost: '$0.10' },
243
- { pack: 'core', tool: 'vibe', desc: '3 unconventional ideas', cost: '$0.05' },
244
- { pack: 'core', tool: 'roast', desc: 'Brutal code review', cost: '$0.05' },
245
- { pack: 'core', tool: 'name_it', desc: '5 names + domain', cost: '$0.03' },
246
- { pack: 'core', tool: 'price_it', desc: 'SaaS pricing', cost: '$0.05' },
247
- { pack: 'core', tool: 'one_liner', desc: 'Elevator pitch', cost: '$0.02' },
248
- { pack: 'labs', tool: 'genius', desc: 'Deep problem solving', cost: '$0.50' },
249
- { pack: 'labs', tool: 'genius_plus', desc: 'Self-improving code', cost: '$0.65' },
250
- { pack: 'labs', tool: 'compute', desc: 'Python sandbox', cost: '$0.02' },
251
- { pack: 'labs', tool: 'mind_opener', desc: '5 curious angles', cost: '$0.08' },
252
- { pack: 'beacon', tool: 'health', desc: 'Context health', cost: 'FREE' },
253
- { pack: 'beacon', tool: 'compress', desc: 'Smart compression', cost: '$0.02' },
254
- { pack: 'beacon', tool: 'extract', desc: 'Extract decisions', cost: '$0.02' },
255
- { pack: 'beacon', tool: 'mint', desc: 'Permanent memory', cost: '$0.01' },
256
- { pack: 'beacon', tool: 'recall', desc: 'Read memories', cost: 'FREE' },
257
- { pack: 'beacon', tool: 'checkpoint', desc: 'Save state', cost: '$0.02' },
258
- { pack: 'router', tool: 'compare', desc: 'Multi-model compare', cost: '$0.05' },
259
- { pack: 'router', tool: 'battle', desc: 'Model battle', cost: '$0.10' },
260
- { pack: 'adoption', tool: 'adoption_calc', desc: 'P(adopt) calculator', cost: 'FREE' },
261
- { pack: 'adoption', tool: 'adoption_diagnose', desc: 'Bottleneck diagnosis', cost: 'FREE' },
262
- { pack: 'adoption', tool: 'adoption_simulate', desc: 'Time-series ODE sim', cost: 'FREE' },
263
- ];
264
-
265
- return all.filter(t =>
266
- t.pack.includes(q) ||
267
- t.tool.includes(q) ||
268
- t.desc.toLowerCase().includes(q)
269
- );
270
- }
271
-
272
- async function cmdAdd(args) {
273
- const pack = args[0];
274
- if (!pack) {
275
- console.error('Usage: 50c add <pack>');
276
- console.error('Run: 50c search to see available packs');
277
- process.exit(1);
278
- }
279
-
280
- const valid = ['core', 'labs', 'beacon', 'router', 'adoption'];
281
- if (!valid.includes(pack)) {
282
- console.error(`Unknown pack: ${pack}`);
283
- console.error('Available: ' + valid.join(', '));
284
- process.exit(1);
285
- }
286
-
287
- if (!CONFIG.packs) CONFIG.packs = ['core'];
288
- if (CONFIG.packs.includes(pack)) {
289
- console.log(`Pack "${pack}" already enabled`);
290
- return;
291
- }
292
-
293
- CONFIG.packs.push(pack);
294
- saveConfig(CONFIG);
295
- console.log(`Added pack: ${pack}`);
296
- console.log('Enabled packs: ' + CONFIG.packs.join(', '));
297
- }
298
-
299
- async function cmdRemove(args) {
300
- const pack = args[0];
301
- if (!pack) {
302
- console.error('Usage: 50c remove <pack>');
303
- process.exit(1);
304
- }
305
-
306
- if (pack === 'core') {
307
- console.error('Cannot remove core pack');
308
- process.exit(1);
309
- }
310
-
311
- if (!CONFIG.packs || !CONFIG.packs.includes(pack)) {
312
- console.log(`Pack "${pack}" not enabled`);
313
- return;
314
- }
315
-
316
- CONFIG.packs = CONFIG.packs.filter(p => p !== pack);
317
- saveConfig(CONFIG);
318
- console.log(`Removed pack: ${pack}`);
319
- }
320
-
321
- async function cmdPin(args) {
322
- const spec = args[0];
323
- if (!spec || !spec.includes('@')) {
324
- console.error('Usage: 50c pin <pack>@<version>');
325
- console.error('Example: 50c pin beacon@2.0.0');
326
- process.exit(1);
327
- }
328
-
329
- const [pack, version] = spec.split('@');
330
-
331
- let lockfile = {};
332
- try {
333
- if (fs.existsSync(LOCKFILE)) {
334
- lockfile = JSON.parse(fs.readFileSync(LOCKFILE, 'utf8'));
335
- }
336
- } catch (e) {}
337
-
338
- lockfile[pack] = version;
339
- fs.writeFileSync(LOCKFILE, JSON.stringify(lockfile, null, 2));
340
- console.log(`Pinned ${pack} to version ${version}`);
341
- }
342
-
343
- async function cmdUpdate() {
344
- console.log('Checking for updates...');
345
- // TODO: Check registry for newer versions
346
- console.log('All packs up to date');
347
- }
348
-
349
- async function cmdBalance() {
350
- if (!API_KEY) {
351
- console.log('Balance: (no API key)');
352
- console.log('Run: 50c config key <your_key>');
353
- return;
354
- }
355
-
356
- try {
357
- const result = await callMCPTool('check_balance', {});
358
- console.log('Balance:', result);
359
- } catch (e) {
360
- console.error('Could not fetch balance:', e.message);
361
- }
362
- }
363
-
364
- async function cmdSnapshots() {
365
- console.log('Recent Snapshots:');
366
- console.log(' (snapshot system coming soon)');
367
- // TODO: Query local + Turso for snapshots
368
- }
369
-
370
- async function cmdRestore(args) {
371
- const id = args[0];
372
- if (!id) {
373
- console.error('Usage: 50c restore <snapshot_id>');
374
- console.error('Run: 50c snapshots to list');
375
- process.exit(1);
376
- }
377
- console.log(`Restoring snapshot ${id}...`);
378
- // TODO: Implement restore
379
- console.log('(restore coming soon)');
380
- }
381
-
382
- async function cmdTip(args) {
383
- const amount = parseInt(args[0]) || null;
384
- const reason = args.slice(1).join(' ') || null;
385
-
386
- if (!API_KEY) {
387
- console.error('Need API key to tip. Run: 50c config key <your_key>');
388
- process.exit(1);
389
- }
390
-
391
- try {
392
- const result = await callMCPTool('beacon_tip', { amount, reason });
393
- if (result.ask) {
394
- console.log(result.ask);
395
- console.log('Run: 50c tip <amount> [reason]');
396
- } else if (result.thanks) {
397
- console.log(result.thanks);
398
- if (result.impact) console.log(result.impact);
399
- console.log(`Balance: ${result.balance} | Tip #${result.tip_number}`);
400
- if (result.tip_pool) {
401
- console.log(`Refund pool: ${result.tip_pool.refund_available} credits`);
402
- }
403
- } else {
404
- console.log(JSON.stringify(result, null, 2));
405
- }
406
- } catch (e) {
407
- console.error('Tip failed:', e.message);
408
- }
409
- }
410
-
411
- async function cmdNoTip(args) {
412
- const tool = args[0];
413
- const reason = args[1];
414
- const details = args.slice(2).join(' ') || null;
415
-
416
- if (!tool) {
417
- console.log('Usage: 50c notip <tool> <reason> [details]');
418
- console.log('');
419
- console.log('Reasons: wrong_answer, too_slow, confusing, not_helpful, crashed, other');
420
- console.log('');
421
- console.log('Example: 50c notip compress not_helpful "output was garbage"');
422
- return;
423
- }
424
-
425
- if (!API_KEY) {
426
- console.error('Need API key. Run: 50c config key <your_key>');
427
- process.exit(1);
428
- }
429
-
430
- try {
431
- const result = await callMCPTool('beacon_notip', { tool, reason, details });
432
- console.log(result.acknowledged || 'Feedback received');
433
- if (result.refund) {
434
- console.log(`Refunded: ${result.refund.credits} credits`);
435
- }
436
- if (result.refund_denied) {
437
- console.log('No refund:', result.refund_denied);
438
- }
439
- if (result.diagnosis) {
440
- console.log('');
441
- console.log('Diagnosis:', result.diagnosis.likely_cause);
442
- console.log('Try:', result.diagnosis.try_tools?.map(t => t.tool).join(', '));
443
- }
444
- } catch (e) {
445
- console.error('No-tip failed:', e.message);
446
- }
447
- }
448
-
449
- async function cmdMint(args) {
450
- const content = args.join(' ');
451
- if (!content) {
452
- console.error('Usage: 50c mint <content>');
453
- console.error('Example: 50c mint "API key rotated on 2026-01-29"');
454
- process.exit(1);
455
- }
456
-
457
- if (!API_KEY) {
458
- console.error('Need API key. Run: 50c config key <your_key>');
459
- process.exit(1);
460
- }
461
-
462
- try {
463
- const result = await callMCPTool('beacon_remember', { content, category: 'mint' });
464
- console.log('Minted:', content.slice(0, 50) + (content.length > 50 ? '...' : ''));
465
- console.log('Total memories:', result.total);
466
- } catch (e) {
467
- console.error('Mint failed:', e.message);
468
- }
469
- }
470
-
471
- async function cmdConfig(args) {
472
- const action = args[0];
473
- const value = args.slice(1).join(' ');
474
-
475
- if (!action) {
476
- console.log('Current config:');
477
- console.log(JSON.stringify(CONFIG, null, 2));
478
- console.log('');
479
- console.log('Commands:');
480
- console.log(' 50c config key <api_key> Set API key');
481
- console.log(' 50c config show Show config');
482
- return;
483
- }
484
-
485
- if (action === 'key') {
486
- if (!value) {
487
- console.log('API Key:', CONFIG.api_key || '(not set)');
488
- return;
489
- }
490
- CONFIG.api_key = value;
491
- saveConfig(CONFIG);
492
- console.log('API key saved to ~/.50c/config.json');
493
- } else if (action === 'show') {
494
- console.log(JSON.stringify(CONFIG, null, 2));
495
- } else {
496
- console.error('Unknown config action:', action);
497
- }
498
- }
499
-
500
- // ═══════════════════════════════════════════════════════════════
501
- // TOOL COMMANDS
502
- // ═══════════════════════════════════════════════════════════════
503
-
504
- async function runToolCommand(cmd, args) {
505
- const spec = TOOL_COMMANDS[cmd];
506
- if (!spec) throw new Error('Unknown command: ' + cmd);
507
-
508
- // Special handling for chat/refocus
509
- if (spec.special === 'chat') {
510
- if (args.length < 2) {
511
- console.error('Usage: 50c chat <session_id> <message>');
512
- process.exit(1);
513
- }
514
- const sessionId = args[0];
515
- const message = args.slice(1).join(' ');
516
- const result = await callMCPTool('ide_conversation', {
517
- session_id: sessionId,
518
- message,
519
- mode: 'chat'
520
- });
521
- console.log(result.response || JSON.stringify(result, null, 2));
522
- return;
523
- }
524
-
525
- if (spec.special === 'refocus') {
526
- if (args.length < 2) {
527
- console.error('Usage: 50c refocus <situation> <goal>');
528
- process.exit(1);
529
- }
530
- const situation = args[0];
531
- const goal = args.slice(1).join(' ');
532
- const result = await callMCPTool('llm_refocus', { situation, goal, tone: 'gentle' });
533
- console.log(result.response || result.redirect || JSON.stringify(result, null, 2));
534
- return;
535
- }
536
-
537
- // INVENT-UI: Visual invention pipeline with browser UI
538
- if (spec.special === 'invent-ui') {
539
- let problem = '';
540
- let rigor = 'deep';
541
- let domain = 'code';
542
-
543
- for (const arg of args) {
544
- if (arg.startsWith('--rigor=')) rigor = arg.split('=')[1];
545
- else if (arg.startsWith('--domain=')) domain = arg.split('=')[1];
546
- else if (!arg.startsWith('--')) problem += (problem ? ' ' : '') + arg;
547
- }
548
-
549
- if (!problem) {
550
- console.error('Usage: 50c invent-ui "problem" [--rigor=deep] [--domain=math]');
551
- console.error('Opens a browser window with real-time swarm visualization.');
552
- process.exit(1);
553
- }
554
-
555
- const { runInventWithUI } = require('../lib/invent-ui.js');
556
- await runInventWithUI(problem, { domain, rigor });
557
- return;
558
- }
559
-
560
- // MCP-TV: Universal MCP Visualization Platform (FREE)
561
- if (spec.special === 'mcp-tv') {
562
- let port = 50888;
563
- let noOpen = false;
564
-
565
- for (const arg of args) {
566
- if (arg.startsWith('--port=')) port = parseInt(arg.split('=')[1]);
567
- if (arg === '--no-open') noOpen = true;
568
- }
569
-
570
- const { createServer } = require('../lib/mcp-tv.js');
571
- const { spawn } = require('child_process');
572
-
573
- createServer(port);
574
-
575
- if (!noOpen) {
576
- setTimeout(() => {
577
- const url = `http://localhost:${port}`;
578
- const platform = process.platform;
579
- if (platform === 'win32') {
580
- spawn('cmd', ['/c', 'start', url], { detached: true, stdio: 'ignore' }).unref();
581
- } else if (platform === 'darwin') {
582
- spawn('open', [url], { detached: true, stdio: 'ignore' }).unref();
583
- } else {
584
- spawn('xdg-open', [url], { detached: true, stdio: 'ignore' }).unref();
585
- }
586
- }, 500);
587
- }
588
-
589
- // Keep process alive
590
- return new Promise(() => {});
591
- }
592
-
593
- // INVENT: Enterprise invention pipeline
594
- if (spec.special === 'invent') {
595
- // Parse args: 50c invent "problem" --rigor=deep --domain=math --constraint="must be fast"
596
- let problem = '';
597
- let rigor = 'deep';
598
- let domain = 'code';
599
- const constraints = [];
600
-
601
- for (const arg of args) {
602
- if (arg.startsWith('--rigor=')) {
603
- rigor = arg.split('=')[1];
604
- } else if (arg.startsWith('--domain=')) {
605
- domain = arg.split('=')[1];
606
- } else if (arg.startsWith('--constraint=')) {
607
- constraints.push(arg.split('=')[1].replace(/^["']|["']$/g, ''));
608
- } else if (!arg.startsWith('--')) {
609
- problem += (problem ? ' ' : '') + arg;
610
- }
611
- }
612
-
613
- if (!problem) {
614
- console.error('Usage: 50c invent "problem description" [options]');
615
- console.error('Options:');
616
- console.error(' --rigor=fast|standard|deep|exhaustive (default: deep)');
617
- console.error(' --domain=math|physics|code|business (default: code)');
618
- console.error(' --constraint="constraint text" (can repeat)');
619
- console.error('');
620
- console.error('Examples:');
621
- console.error(' 50c invent "faster sorting algorithm" --rigor=deep');
622
- console.error(' 50c invent "prove Riemann hypothesis" --domain=math --rigor=exhaustive');
623
- console.error(' 50c invent "novel auth system" --constraint="must be passwordless"');
624
- process.exit(1);
625
- }
626
-
627
- console.log(`\n🔬 50c Auto-Invent Pipeline`);
628
- console.log(` Problem: ${problem.slice(0, 60)}${problem.length > 60 ? '...' : ''}`);
629
- console.log(` Rigor: ${rigor} | Domain: ${domain}`);
630
- if (constraints.length) console.log(` Constraints: ${constraints.length}`);
631
- console.log(` Cost: $2.00\n`);
632
-
633
- // Call auto_invent directly (not via remote API - it's local)
634
- const inventArgs = { problem, rigor, domain, constraints };
635
- const result = await autoInvent(inventArgs);
636
-
637
- // Format output
638
- if (result.ok && result.invention) {
639
- console.log('═══════════════════════════════════════════════════════════════');
640
- console.log(' INVENTION RESULT');
641
- console.log('═══════════════════════════════════════════════════════════════\n');
642
-
643
- if (result.angles && result.angles.length > 0) {
644
- console.log('📐 CREATIVE ANGLES:');
645
- result.angles.slice(0, 5).forEach((a, i) => console.log(` ${i+1}. ${a}`));
646
- console.log('');
647
- }
648
-
649
- if (result.invention.solution) {
650
- console.log('💡 SOLUTION:');
651
- console.log(result.invention.solution);
652
- console.log('');
653
- }
654
-
655
- if (result.proofs && result.proofs.length > 0) {
656
- console.log('✓ PROOFS:', result.proofs.length, 'verification(s)');
657
- }
658
-
659
- if (result.prior_art && result.prior_art.length > 0) {
660
- console.log('📚 PRIOR ART:', result.prior_art.length, 'related work(s) found');
661
- }
662
-
663
- console.log('\n═══════════════════════════════════════════════════════════════');
664
- console.log(`⏱ Duration: ${(result.total_duration_ms / 1000).toFixed(1)}s | Verified: ${result.verified ? '✓' : '○'}`);
665
- console.log('═══════════════════════════════════════════════════════════════\n');
666
- } else {
667
- console.log('Result:', JSON.stringify(result, null, 2));
668
- }
669
- return;
670
- }
671
-
672
- // ADOPTION EQUATION TOOLS (FREE, local compute)
673
- if (spec.special === 'adopt') {
674
- // 50c adopt 2.0 0.3 0.8 [churn] [lambda]
675
- const R = parseFloat(args[0]);
676
- const N = parseFloat(args[1]);
677
- const W = parseFloat(args[2]);
678
- const churn = parseFloat(args[3]) || 0;
679
- const lambda = parseFloat(args[4]) || 1.0;
680
-
681
- if (isNaN(R) || isNaN(N) || isNaN(W)) {
682
- console.error('Usage: 50c adopt <reward> <network> <window> [churn] [lambda]');
683
- console.error('Example: 50c adopt 2.0 0.3 0.8');
684
- process.exit(1);
685
- }
686
-
687
- const result = adoptionCalc({ reward: R, network: N, window: W, churn, lambda });
688
- console.log('\n=== ADOPTION PROBABILITY ===');
689
- console.log(`P(adopt) = ${(result.probability * 100).toFixed(2)}%`);
690
- console.log(`Equation: ${result.equation}`);
691
- console.log(`\nTerms:`);
692
- console.log(` Reward: ${(result.reward_term * 100).toFixed(1)}%`);
693
- console.log(` Network: ${(result.network_effect * 100).toFixed(1)}%`);
694
- console.log(` Window: ${(result.upheaval_window * 100).toFixed(1)}%`);
695
- console.log(`\nDiagnosis: ${result.diagnosis}`);
696
- console.log(`Bottleneck: ${result.bottleneck} (${(result.bottleneck_value * 100).toFixed(1)}%)`);
697
- if (result.threshold_R_for_50pct !== 'impossible') {
698
- console.log(`R needed for 50%: ${result.threshold_R_for_50pct}`);
699
- }
700
- console.log(`Past bifurcation (viral threshold): ${result.past_bifurcation ? 'YES' : 'NO'}`);
701
- return;
702
- }
703
-
704
- if (spec.special === 'adopt-dx') {
705
- // 50c adopt-dx 2.0 0.3 0.8
706
- const R = parseFloat(args[0]);
707
- const N = parseFloat(args[1]);
708
- const W = parseFloat(args[2]);
709
-
710
- if (isNaN(R) || isNaN(N) || isNaN(W)) {
711
- console.error('Usage: 50c adopt-dx <reward> <network> <window>');
712
- process.exit(1);
713
- }
714
-
715
- const result = adoptionDiagnose({ reward: R, network: N, window: W });
716
- console.log('\n=== ADOPTION DIAGNOSIS ===');
717
- console.log(`P(adopt) = ${(result.probability * 100).toFixed(2)}%`);
718
- console.log(`\nDiagnosis: ${result.diagnosis.toUpperCase()}`);
719
- console.log(`Priority: ${result.prescription.priority}`);
720
- console.log(`\nProblem: ${result.prescription.problem}`);
721
- console.log(`Fix: ${result.prescription.fix}`);
722
- if (result.kill_switches.length > 0) {
723
- console.log('\n⚠️ KILL SWITCHES:');
724
- result.kill_switches.forEach(k => console.log(` ${k}`));
725
- }
726
- console.log(`\nInsight: ${result.insight}`);
727
- return;
728
- }
729
-
730
- if (spec.special === 'adopt-sim') {
731
- // 50c adopt-sim 2.0 [steps]
732
- const R = parseFloat(args[0]) || 2.0;
733
- const steps = parseInt(args[1]) || 100;
734
-
735
- const result = adoptionSimulate({ reward: R, steps });
736
- console.log('\n=== ADOPTION SIMULATION ===');
737
- console.log(`Initial: R=${R}, N0=0.1, W0=0.9, alpha=1.5, churn=0.05`);
738
- console.log(`\nTrajectory:`);
739
- result.series.forEach(p => {
740
- const bar = '█'.repeat(Math.round(p.P * 40));
741
- console.log(`t=${p.t.toFixed(1).padStart(4)}: ${(p.P * 100).toFixed(1).padStart(5)}% ${bar}`);
742
- });
743
- console.log(`\nFinal P: ${(result.summary.final_adoption * 100).toFixed(2)}%`);
744
- console.log(`Peak P: ${(result.summary.peak_adoption * 100).toFixed(2)}% at t=${result.summary.peak_time}`);
745
- console.log(`Tipped viral: ${result.summary.tipped_at !== 'never' ? 'YES at t=' + result.summary.tipped_at : 'NO'}`);
746
- console.log(`Trajectory: ${result.summary.trajectory}`);
747
- return;
748
- }
749
-
750
- // TEAM COMMANDS (FREE, uses API)
751
- if (spec.special === 'team') {
752
- const res = await fetch('https://api.50c.ai/v1/team/info', {
753
- method: 'POST',
754
- headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' },
755
- body: '{}'
756
- });
757
- const data = await res.json();
758
- if (data.error === 'Not in a team') {
759
- console.log('\n=== NO TEAM ===');
760
- console.log('You are not in a team yet.');
761
- console.log('Create one with: 50c team-create "My Team"');
762
- } else if (data.team) {
763
- console.log('\n=== TEAM INFO ===');
764
- console.log(`Name: ${data.team.name}`);
765
- console.log(`Plan: ${data.team.plan}`);
766
- console.log(`Members: ${data.members.length}/${data.team.max_members}`);
767
- console.log(`Your role: ${data.your_role}`);
768
- console.log('\nMembers:');
769
- data.members.forEach(m => console.log(` ${m.email || m.customer_id} (${m.role})`));
770
- } else {
771
- console.log(JSON.stringify(data, null, 2));
772
- }
773
- return;
774
- }
775
-
776
- if (spec.special === 'team-create') {
777
- const name = args.join(' ') || 'My Team';
778
- const res = await fetch('https://api.50c.ai/v1/team/create', {
779
- method: 'POST',
780
- headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' },
781
- body: JSON.stringify({ name })
782
- });
783
- const data = await res.json();
784
- console.log('\n=== TEAM CREATED ===');
785
- console.log(`ID: ${data.team_id}`);
786
- console.log(`Name: ${data.name}`);
787
- console.log(`Plan: ${data.plan}`);
788
- return;
789
- }
790
-
791
- if (spec.special === 'team-mint') {
792
- if (args.length < 2) {
793
- console.error('Usage: 50c team-mint <key> <value> [scope]');
794
- process.exit(1);
795
- }
796
- const key = args[0];
797
- const value = args.slice(1, -1).join(' ') || args[1];
798
- const scope = args.length > 2 ? args[args.length - 1] : 'general';
799
- const res = await fetch('https://api.50c.ai/v1/team/mint', {
800
- method: 'POST',
801
- headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' },
802
- body: JSON.stringify({ key, value, scope })
803
- });
804
- const data = await res.json();
805
- console.log('\n=== MEMORY SHARED ===');
806
- console.log(`ID: ${data.id}`);
807
- console.log(`Team: ${data.team_id}`);
808
- console.log(`Shared: ${data.shared}`);
809
- return;
810
- }
811
-
812
- if (spec.special === 'team-recall') {
813
- const query = args.join(' ') || null;
814
- const res = await fetch('https://api.50c.ai/v1/team/recall', {
815
- method: 'POST',
816
- headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' },
817
- body: JSON.stringify({ query, limit: 20 })
818
- });
819
- const data = await res.json();
820
- console.log('\n=== TEAM MEMORIES ===');
821
- console.log(`Team: ${data.team_id}`);
822
- console.log(`Found: ${data.count} memories\n`);
823
- data.memories.forEach(m => {
824
- console.log(`[${m.scope}] ${m.key}`);
825
- console.log(` ${m.value}`);
826
- console.log('');
827
- });
828
- return;
829
- }
830
-
831
- if (spec.special === 'team-analytics') {
832
- const res = await fetch('https://api.50c.ai/v1/team/analytics', {
833
- method: 'POST',
834
- headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' },
835
- body: '{}'
836
- });
837
- const data = await res.json();
838
- if (data.error) {
839
- console.log(data.error);
840
- return;
841
- }
842
- const t = data.totals;
843
- console.log('\n=== TEAM ROI ANALYTICS ===');
844
- console.log(`Team: ${data.team_id}\n`);
845
- console.log(`Bugs Caught: ${t.bugs_caught}`);
846
- console.log(`Hours Saved: ${t.hours_saved.toFixed(1)}`);
847
- console.log(`Tool Calls: ${t.tool_calls}`);
848
- console.log(`Spend: $${t.spend_dollars.toFixed(2)}`);
849
- console.log(`Value Generated: $${t.roi_dollars.toFixed(2)}`);
850
- console.log(`ROI Multiple: ${t.roi_multiple.toFixed(1)}x`);
851
- return;
852
- }
853
-
854
- // Standard tool
855
- const input = args.join(' ');
856
- if (!input) {
857
- console.error(`Usage: 50c ${cmd} <input>`);
858
- process.exit(1);
859
- }
860
-
861
- const result = await callMCPTool(spec.tool, { [spec.arg]: input });
862
-
863
- // Format output nicely
864
- if (typeof result === 'string') {
865
- console.log(result);
866
- } else if (result.hints) {
867
- result.hints.forEach((h, i) => console.log(`${i+1}. ${h}`));
868
- } else if (result.ideas) {
869
- result.ideas.forEach((h, i) => console.log(`${i+1}. ${h}`));
870
- } else if (result.pitch) {
871
- console.log(result.pitch);
872
- } else if (result.flaws) {
873
- result.flaws.forEach((f, i) => {
874
- console.log(`\n[${i+1}] ${f.flaw}`);
875
- console.log(` Fix: ${f.fix}`);
876
- });
877
- } else if (result.names) {
878
- result.names.forEach(n => {
879
- const avail = n.available ? '✓' : '✗';
880
- console.log(` ${avail} ${n.name} - ${n.domain}`);
881
- });
882
- } else if (result.strategy) {
883
- console.log(result.strategy);
884
- } else {
885
- console.log(JSON.stringify(result, null, 2));
886
- }
887
- }
888
-
889
- async function runPackTool(packTool, args) {
890
- const [pack, tool] = packTool.split('.');
891
- if (!pack || !tool) {
892
- console.error('Usage: 50c <pack>.<tool> <input>');
893
- console.error('Example: 50c beacon.compress "long text here"');
894
- process.exit(1);
895
- }
896
-
897
- // Check pack is enabled
898
- if (!CONFIG.packs?.includes(pack) && pack !== 'core') {
899
- console.error(`Pack "${pack}" not enabled. Run: 50c add ${pack}`);
900
- process.exit(1);
901
- }
902
-
903
- if (!API_KEY) {
904
- console.error('Need API key. Run: 50c config key <your_key>');
905
- process.exit(1);
906
- }
907
-
908
- const input = args.join(' ');
909
-
910
- // Map pack.tool to MCP tool name
911
- const toolName = `${pack}_${tool}`;
912
-
913
- // Build args object based on tool
914
- let toolArgs = {};
915
- if (tool === 'compress' || tool === 'extract' || tool === 'summarize') {
916
- toolArgs = { messages: [input] };
917
- } else if (tool === 'health') {
918
- toolArgs = { messages: input ? [input] : [] };
919
- } else if (tool === 'remember' || tool === 'mint') {
920
- toolArgs = { content: input };
921
- } else if (tool === 'recall') {
922
- toolArgs = { query: input || null };
923
- } else if (tool === 'drift') {
924
- toolArgs = { messages: [input], goal: args[0] || 'stay on topic' };
925
- } else if (tool === 'checkpoint') {
926
- toolArgs = { name: args[0] || 'default', messages: args.slice(1) };
927
- } else if (tool === 'restore') {
928
- toolArgs = { name: input || 'default' };
929
- } else {
930
- toolArgs = { input };
931
- }
932
-
933
- const result = await callMCPTool(toolName, toolArgs);
934
- console.log(JSON.stringify(result, null, 2));
935
- }
936
-
937
- // ═══════════════════════════════════════════════════════════════
938
- // MCP MODE
939
- // ═══════════════════════════════════════════════════════════════
940
-
941
- function startMCPMode() {
942
- const rl = readline.createInterface({
943
- input: process.stdin,
944
- output: process.stdout,
945
- terminal: false
946
- });
947
-
948
- rl.on('line', async (line) => {
949
- try {
950
- const clean = line.trim();
951
- if (!clean) return;
952
- const request = JSON.parse(clean);
953
-
954
- // Notifications (no id) don't get responses per JSON-RPC spec
955
- if (request.method && request.method.startsWith('notifications/')) {
956
- return;
957
- }
958
-
959
- // Handle initialize locally
960
- if (request.method === 'initialize') {
961
- process.stdout.write(JSON.stringify({
962
- jsonrpc: '2.0',
963
- id: request.id,
964
- result: {
965
- protocolVersion: '2024-11-05',
966
- serverInfo: { name: '50c', version: VERSION },
967
- capabilities: { tools: {} }
968
- }
969
- }) + '\n');
970
- return;
971
- }
972
-
973
- const response = await handleMCPRequest(request);
974
- if (response) {
975
- process.stdout.write(JSON.stringify(response) + '\n');
976
- }
977
- } catch (e) {
978
- process.stdout.write(JSON.stringify({
979
- jsonrpc: '2.0',
980
- id: null,
981
- error: { code: -32603, message: e.message }
982
- }) + '\n');
983
- }
984
- });
985
- }
986
-
987
- async function handleMCPRequest(request) {
988
- // Handle local file-memory tools first (FREE, no API call)
989
- const localResult = await handleLocalTools(request);
990
- if (localResult) return localResult;
991
-
992
- // Forward to remote MCP endpoint
993
- return callRemoteMCP(request);
994
- }
995
-
996
- // Local file-memory tools (FREE, runs on user machine)
997
- const { FILE_TOOLS, indexFile, findSymbol, getLines, searchFile, fileSummary } = require('../lib/file-memory.js');
998
- // Note: subagent, team already imported at top for CLI commands
999
-
1000
- const LOCAL_TOOLS = [
1001
- // THE MAIN TOOL - Ask the 50c team to do anything
1002
- { name: "team", description: "Ask the 50c team to do anything. Natural language → auto-picks the right tools. Example: 'roast my code and suggest fixes'", inputSchema: { type: "object", properties: { task: { type: "string", description: "What you want done in plain English" }, context: { type: "string", description: "Code, text, or data to work with (optional)" }, dryRun: { type: "boolean", description: "Preview plan without executing" } }, required: ["task"] } },
1003
-
1004
- // File memory tools
1005
- { name: "fm_index", description: "Index a large file for fast symbol/line lookup. Auto-detects Python/JS/TS. FREE.", inputSchema: { type: "object", properties: { filepath: { type: "string", description: "Absolute or relative path to file" } }, required: ["filepath"] } },
1006
- { name: "fm_find", description: "Find where a function/class is defined across all indexed files. FREE.", inputSchema: { type: "object", properties: { name: { type: "string", description: "Symbol name (function, class, variable)" }, filepath: { type: "string", description: "Optional: limit search to one file" } }, required: ["name"] } },
1007
- { name: "fm_lines", description: "Get specific line range from a file with line numbers. FREE.", inputSchema: { type: "object", properties: { filepath: { type: "string" }, start: { type: "number", description: "Start line number" }, end: { type: "number", description: "End line number" } }, required: ["filepath", "start", "end"] } },
1008
- { name: "fm_search", description: "Search for symbols and content in a file. Auto-indexes if needed. FREE.", inputSchema: { type: "object", properties: { filepath: { type: "string" }, query: { type: "string", description: "Search term" } }, required: ["filepath", "query"] } },
1009
- { name: "fm_summary", description: "Get summary of a file: line count, all functions, classes. FREE.", inputSchema: { type: "object", properties: { filepath: { type: "string" } }, required: ["filepath"] } },
1010
- { name: "fm_list", description: "List all indexed files with stats. FREE.", inputSchema: { type: "object", properties: {} } },
1011
- { name: "fm_context", description: "Get context around a symbol (function body, class methods). FREE.", inputSchema: { type: "object", properties: { filepath: { type: "string" }, symbol: { type: "string", description: "Function or class name" }, lines_after: { type: "number", description: "Lines to include after definition (default 50)" } }, required: ["filepath", "symbol"] } },
1012
-
1013
- // 50c Team utilities - for power users who want granular control
1014
- { name: "team_http", description: "50c Team: HTTP/HTTPS fetch. Bypasses IDE restrictions. FREE.", inputSchema: { type: "object", properties: { url: { type: "string", description: "Full URL to fetch" }, method: { type: "string", description: "HTTP method (default GET)" }, headers: { type: "object", description: "Request headers" }, body: { type: "string", description: "Request body for POST/PUT" }, timeout: { type: "number", description: "Timeout in ms (default 30000)" } }, required: ["url"] } },
1015
- { name: "team_ssh", description: "50c Team: SSH remote command. Configure servers in ~/.50c/servers.json. FREE.", inputSchema: { type: "object", properties: { server: { type: "string", description: "Server alias (nj/zurich/chicago) or {host,user,port}" }, command: { type: "string", description: "Command to execute on remote server" }, timeout: { type: "number", description: "Timeout in ms (default 30000)" } }, required: ["server", "command"] } },
1016
- { name: "team_exec", description: "50c Team: Local command execution. Bypasses shell restrictions. FREE.", inputSchema: { type: "object", properties: { command: { type: "string", description: "Command to execute locally" }, cwd: { type: "string", description: "Working directory" }, timeout: { type: "number", description: "Timeout in ms (default 30000)" } }, required: ["command"] } },
1017
- { name: "team_multi", description: "50c Team: Execute multiple tasks in parallel. FREE.", inputSchema: { type: "object", properties: { tasks: { type: "array", description: "Array of task objects [{type, ...params}]", items: { type: "object" } } }, required: ["tasks"] } },
1018
- { name: "team_servers", description: "50c Team: List known SSH servers. FREE.", inputSchema: { type: "object", properties: {} } },
1019
- { name: "team_ask", description: "50c Team: Ask the team to run any 50c tool. Uses your API key.", inputSchema: { type: "object", properties: { tool: { type: "string", description: "50c tool name (e.g., hints, roast, genius, bcalc)" }, args: { type: "object", description: "Tool arguments" } }, required: ["tool"] } },
1020
- { name: "team_chain", description: "50c Team: Chain multiple tools together. Output flows via $prev placeholder.", inputSchema: { type: "object", properties: { steps: { type: "array", description: "Array of {tool, args} - use $prev for previous result", items: { type: "object" } }, input: { type: "string", description: "Initial input (optional)" } }, required: ["steps"] } },
1021
- { name: "guardian_publish", description: "Guardian: Pre-publish supply chain verification. Thorough checks before npm/github/arxiv/medical publish. Profiles: npm, github, arxiv, medical, science, math. Uses AI tools (bCalc, genius+, web_search) for academic verification. FREE.", inputSchema: { type: "object", properties: { profile: { type: "string", description: "Verification profile: npm, github, arxiv, medical, science, math", enum: ["npm", "github", "arxiv", "medical", "science", "math"] }, cwd: { type: "string", description: "Directory to check (default: current)" }, save_receipt: { type: "boolean", description: "Save receipt as markdown file" } } } },
1022
-
1023
- // Security audit tool - runs locally, FREE
1024
- { name: "guardian_audit", description: "Guardian: Security audit for backdoors, unauthorized RDP/SSH, persistence, suspicious connections. Geo-IP tags foreign IPs. Cross-platform. FREE.", inputSchema: { type: "object", properties: { skipGeo: { type: "boolean", description: "Skip geo-IP lookups (faster, offline)" } } } },
1025
-
1026
- // Combined security suite - pre-publish + backdoor check, FREE
1027
- { name: "guardian", description: "50c Guardian: Combined supply chain verification + machine backdoor audit. Catches injected IPs/URLs/eval AND system compromise. Cross-platform. FREE.", inputSchema: { type: "object", properties: { profile: { type: "string", description: "Verification profile (default: npm)", enum: ["npm", "github"], default: "npm" }, cwd: { type: "string", description: "Directory to check (default: current)" }, skipGeo: { type: "boolean", description: "Skip geo-IP lookups (faster)" }, save_receipt: { type: "boolean", description: "Save receipt as markdown file" } } } },
1028
-
1029
- // ENTERPRISE PRESET - Auto-Invent Swarm Pipeline ($2.00)
1030
- { name: "auto_invent", description: "ENTERPRISE ($2.00): Full invention pipeline. Chains mind_opener → idea_fold → bcalc → genius_plus → compute → cvi_verify. Produces provable, verified solutions. Requires Enterprise tier.", inputSchema: { type: "object", properties: { problem: { type: "string", description: "Problem or hypothesis to solve/prove" }, constraints: { type: "array", items: { type: "string" }, description: "Hard constraints the solution must satisfy" }, domain: { type: "string", description: "Domain hint: math, physics, engineering, business, code", enum: ["math", "physics", "engineering", "business", "code"] }, rigor: { type: "string", description: "Rigor level: fast, standard, deep, exhaustive (all $2.00)", enum: ["fast", "standard", "deep", "exhaustive"], default: "deep" } }, required: ["problem"] } },
1031
-
1032
- // PROGRAMMATIC INVENTION - JSON-defined executable pipeline (ENTERPRISE $2.00)
1033
- { name: "invent_program", description: "ENTERPRISE ($2.00): Execute a JSON-defined invention program in one shot. Steps: generate/compute (Python), assert (verify), call (50c tool), return. All steps compile to single compute execution. Requires Enterprise tier.", inputSchema: { type: "object", properties: { program: { type: "object", description: "JSON program with 'problem' and 'steps' array", properties: { problem: { type: "string" }, steps: { type: "array", items: { type: "object", properties: { id: { type: "string" }, action: { type: "string", enum: ["generate", "compute", "assert", "call", "return"] }, code: { type: "string" }, tool: { type: "string" }, args: { type: "object" }, depends: { type: "array", items: { type: "string" } } }, required: ["id", "action"] } } }, required: ["problem", "steps"] } }, required: ["program"] } },
1034
-
1035
- // ADOPTION EQUATION TOOLS - P(adopt) = (1-e^(-λR)) × N × W
1036
- { name: "adoption_calc", description: "Adoption probability calculator. P=(1-e^(-λR))×Network×Window. Returns probability, diagnosis, bottleneck, and threshold. FREE.", inputSchema: { type: "object", properties: { lambda: { type: "number", description: "Reward sensitivity (default 1.0)" }, reward: { type: "number", description: "Reward magnitude (0+)" }, network: { type: "number", description: "Network effect 0-1" }, window: { type: "number", description: "Upheaval window 0-1" }, churn: { type: "number", description: "Churn rate 0-1 (default 0)" } }, required: ["reward", "network", "window"] } },
1037
- { name: "adoption_diagnose", description: "Diagnose WHY adoption is failing. Finds which term (reward/network/window) is the bottleneck. Prescribes fix. FREE.", inputSchema: { type: "object", properties: { reward: { type: "number", description: "Reward magnitude" }, network: { type: "number", description: "Network effect 0-1" }, window: { type: "number", description: "Upheaval window 0-1" }, lambda: { type: "number", description: "Reward sensitivity (default 1.0)" }, context: { type: "string", description: "Optional: product/market context for richer diagnosis" } }, required: ["reward", "network", "window"] } },
1038
- { name: "adoption_simulate", description: "Simulate adoption over time with feedback loops. Network grows with adoption, upheaval windows decay. Returns time-series trajectory. FREE.", inputSchema: { type: "object", properties: { reward: { type: "number", description: "Reward magnitude" }, lambda: { type: "number", description: "Reward sensitivity (default 1.0)" }, n0: { type: "number", description: "Initial network effect (default 0.1)" }, w0: { type: "number", description: "Initial upheaval window (default 0.9)" }, churn: { type: "number", description: "Churn rate (default 0.05)" }, decay: { type: "number", description: "Window decay rate (default 0.05)" }, alpha: { type: "number", description: "Network exponent: 1=linear, 1.5=super, 2=Metcalfe (default 1.5)" }, steps: { type: "number", description: "Simulation steps (default 100)" } }, required: ["reward"] } },
1039
- ];
1040
-
1041
- /**
1042
- * AUTO-INVENT: Enterprise Swarm Invention Pipeline
1043
- * Orchestrates parallel sub-MCPs to produce provable, verified inventions
1044
- *
1045
- * SWARM ARCHITECTURE:
1046
- * Phase 1 (Parallel Exploration):
1047
- * - 3x mind_opener (temp 0.3, 0.7, 1.0) - diverse creative angles
1048
- * - idea_fold - STEM lens analysis
1049
- * - bcalc - mathematical discovery
1050
- * - web_search - prior art research
1051
- *
1052
- * Phase 2 (Synthesis):
1053
- * - genius_plus - self-improving solution generation
1054
- *
1055
- * Phase 3 (Verification Swarm):
1056
- * - compute - execute proofs
1057
- * - cvi_verify - constraint checking
1058
- * - roast (peer_review) - adversarial critique
1059
- *
1060
- * Cost: $2.00 flat for full invention pipeline
1061
- */
1062
-
1063
- /**
1064
- * INVENT_PROGRAM: JSON-defined Executable Invention Pipeline
1065
- * Enterprise-only ($2.00/request) - programmatic multi-step invention
1066
- *
1067
- * Actions:
1068
- * - generate: Create data/variables (Python code in compute sandbox)
1069
- * - compute: Calculate/transform (Python code)
1070
- * - assert: Verify condition (must return truthy or throws)
1071
- * - call: Invoke a 50c tool (genius, bcalc, hints, etc.)
1072
- * - return: Final output
1073
- *
1074
- * GATED: Requires Enterprise tier + $2.00 balance
1075
- */
1076
- async function inventProgram(args) {
1077
- const { program } = args;
1078
- const startTime = Date.now();
1079
-
1080
- if (!program || !program.problem || !program.steps) {
1081
- return { ok: false, error: 'Invalid program: must have "problem" and "steps" array' };
1082
- }
1083
-
1084
- const results = {
1085
- ok: true,
1086
- problem: program.problem,
1087
- steps_completed: 0,
1088
- steps_total: program.steps.length,
1089
- outputs: {},
1090
- assertions: [],
1091
- tool_calls: [],
1092
- final_result: null,
1093
- cost: '$2.00',
1094
- tier_required: 'enterprise'
1095
- };
1096
-
1097
- // Build execution context for Python code
1098
- let pythonContext = `
1099
- import json, math, re, itertools, functools, collections
1100
- from decimal import Decimal, getcontext
1101
- getcontext().prec = 100
1102
-
1103
- # Shared context between steps
1104
- _ctx = {}
1105
- _results = {}
1106
-
1107
- `;
1108
-
1109
- // Execute steps sequentially
1110
- for (const step of program.steps) {
1111
- const stepStart = Date.now();
1112
-
1113
- try {
1114
- if (step.action === 'generate' || step.action === 'compute') {
1115
- // Execute Python code in compute sandbox
1116
- pythonContext += `\n# Step: ${step.id}\n${step.code}\n_results['${step.id}'] = locals().get('result', None)\n`;
1117
-
1118
- } else if (step.action === 'assert') {
1119
- // Add assertion check
1120
- pythonContext += `\n# Assert: ${step.id}\n_assert_${step.id} = ${step.code}\nif not _assert_${step.id}: raise AssertionError('${step.id} failed')\n_results['${step.id}'] = True\n`;
1121
- results.assertions.push({ id: step.id, code: step.code });
1122
-
1123
- } else if (step.action === 'call') {
1124
- // Call a 50c tool
1125
- const toolResult = await call50cTool(step.tool, step.args || {});
1126
- results.tool_calls.push({ id: step.id, tool: step.tool, result: toolResult });
1127
- results.outputs[step.id] = toolResult;
1128
- // Inject result into Python context
1129
- pythonContext += `\n# Tool result: ${step.id}\n_results['${step.id}'] = ${JSON.stringify(toolResult)}\n`;
1130
-
1131
- } else if (step.action === 'return') {
1132
- // Final return - will be evaluated after all code runs
1133
- pythonContext += `\n# Final output\n_final = ${step.code}\nprint('__FINAL__:' + json.dumps(_final))\n`;
1134
- }
1135
-
1136
- results.steps_completed++;
1137
-
1138
- } catch (err) {
1139
- results.ok = false;
1140
- results.error = `Step ${step.id} failed: ${err.message}`;
1141
- break;
1142
- }
1143
- }
1144
-
1145
- // Execute all Python code in one compute call
1146
- if (results.ok) {
1147
- pythonContext += `\n# Output all results\nprint('__RESULTS__:' + json.dumps(_results))\n`;
1148
-
1149
- try {
1150
- const computeResult = await call50cTool('compute', { code: pythonContext });
1151
-
1152
- // Parse outputs
1153
- if (computeResult && typeof computeResult === 'string') {
1154
- const finalMatch = computeResult.match(/__FINAL__:(.+)/);
1155
- if (finalMatch) {
1156
- try {
1157
- results.final_result = JSON.parse(finalMatch[1]);
1158
- } catch (e) {
1159
- results.final_result = finalMatch[1];
1160
- }
1161
- }
1162
-
1163
- const resultsMatch = computeResult.match(/__RESULTS__:(.+)/);
1164
- if (resultsMatch) {
1165
- try {
1166
- results.outputs = { ...results.outputs, ...JSON.parse(resultsMatch[1]) };
1167
- } catch (e) {}
1168
- }
1169
- }
1170
-
1171
- results.compute_output = computeResult;
1172
-
1173
- } catch (err) {
1174
- results.ok = false;
1175
- results.error = `Compute execution failed: ${err.message}`;
1176
- }
1177
- }
1178
-
1179
- results.duration_ms = Date.now() - startTime;
1180
- return results;
1181
- }
1182
-
1183
- async function autoInvent(args) {
1184
- const { problem, constraints = [], domain = 'code', rigor = 'deep' } = args;
1185
- const startTime = Date.now();
1186
-
1187
- const results = {
1188
- ok: true,
1189
- problem,
1190
- domain,
1191
- rigor,
1192
- constraints,
1193
- stages: [],
1194
- invention: null,
1195
- verified: false,
1196
- proofs: [],
1197
- prior_art: [],
1198
- angles: [],
1199
- peer_review: null,
1200
- cost_estimate: '$2.00'
1201
- };
1202
-
1203
- let context = problem;
1204
- let allAngles = [];
1205
- let generatedCode = null;
1206
-
1207
- // ═══════════════════════════════════════════════════════════════
1208
- // PHASE 1: EXPLORATION SWARM (all parallel)
1209
- // ═══════════════════════════════════════════════════════════════
1210
- if (rigor === 'deep' || rigor === 'exhaustive') {
1211
- const phase1Start = Date.now();
1212
- results.stages.push({ name: 'phase1_exploration', status: 'started' });
1213
-
1214
- try {
1215
- // Spawn exploration swarm - 6 parallel calls
1216
- const [
1217
- mind1, mind2, mind3, // Temperature swarm
1218
- ideaResult, // STEM analysis
1219
- bcalcResult, // Math discovery
1220
- priorArt // Web search for prior art
1221
- ] = await Promise.all([
1222
- // Temperature swarm: 3 mind_opener instances
1223
- call50cTool('mind_opener', { problem }).catch(e => ({ error: e.message, temp: 'default' })),
1224
- call50cTool('mind_opener', { problem: `Creative angles for: ${problem}` }).catch(e => ({ error: e.message, temp: 'creative' })),
1225
- call50cTool('mind_opener', { problem: `Unconventional approaches to: ${problem}` }).catch(e => ({ error: e.message, temp: 'wild' })),
1226
- // STEM analysis
1227
- call50cTool('idea_fold', { claim: problem, context: `Domain: ${domain}` }).catch(e => ({ error: e.message })),
1228
- // Math discovery
1229
- call50cTool('bcalc', { expression: domain === 'math' ? problem : `Mathematical model for: ${problem}`, mode: 'explore' }).catch(e => ({ error: e.message })),
1230
- // Prior art search
1231
- call50cTool('web_search', { query: `${problem} research papers solutions` }).catch(e => ({ error: e.message }))
1232
- ]);
1233
-
1234
- // Merge angles from temperature swarm (dedupe)
1235
- const angleSet = new Set();
1236
- [mind1, mind2, mind3].forEach(m => {
1237
- if (m.angles) m.angles.forEach(a => angleSet.add(a));
1238
- if (m.result) angleSet.add(m.result); // Some formats return result string
1239
- });
1240
- allAngles = [...angleSet].slice(0, 10); // Keep top 10 unique angles
1241
- results.angles = allAngles;
1242
-
1243
- if (allAngles.length > 0) {
1244
- context = `${problem}\n\n## Creative Angles:\n${allAngles.map((a, i) => `${i+1}. ${a}`).join('\n')}`;
1245
- }
1246
-
1247
- results.stages.push({
1248
- name: 'mind_opener_swarm',
1249
- success: allAngles.length > 0,
1250
- duration_ms: Date.now() - phase1Start,
1251
- output_summary: `${allAngles.length} unique angles from 3 instances`
1252
- });
1253
-
1254
- // Process idea_fold
1255
- if (ideaResult.synthesis || ideaResult.result) {
1256
- const synthesis = ideaResult.synthesis || ideaResult.result;
1257
- context = `${context}\n\n## STEM Analysis:\n${typeof synthesis === 'string' ? synthesis.slice(0, 500) : JSON.stringify(synthesis).slice(0, 500)}`;
1258
- }
1259
- results.stages.push({
1260
- name: 'idea_fold',
1261
- success: !ideaResult.error,
1262
- duration_ms: Date.now() - phase1Start,
1263
- output_summary: ideaResult.synthesis ? 'STEM synthesis complete' : (ideaResult.error || 'Done')
1264
- });
1265
-
1266
- // Process bcalc
1267
- if (bcalcResult.discovery || bcalcResult.result) {
1268
- results.proofs.push({ type: 'mathematical', data: bcalcResult });
1269
- const mathContent = bcalcResult.discovery || bcalcResult.result;
1270
- context = `${context}\n\n## Mathematical Foundation:\n${typeof mathContent === 'string' ? mathContent.slice(0, 500) : JSON.stringify(mathContent).slice(0, 500)}`;
1271
- }
1272
- results.stages.push({
1273
- name: 'bcalc',
1274
- success: !bcalcResult.error,
1275
- duration_ms: Date.now() - phase1Start,
1276
- output_summary: bcalcResult.discovery ? 'Mathematical discovery' : (bcalcResult.error || 'Done')
1277
- });
1278
-
1279
- // Process prior art search
1280
- if (priorArt.results || priorArt.snippets) {
1281
- const articles = priorArt.results || priorArt.snippets || [];
1282
- results.prior_art = articles.slice(0, 5);
1283
- if (articles.length > 0) {
1284
- context = `${context}\n\n## Prior Art:\n${articles.slice(0, 3).map(a => `- ${a.title || a}`).join('\n')}`;
1285
- }
1286
- }
1287
- results.stages.push({
1288
- name: 'web_search_prior_art',
1289
- success: !priorArt.error,
1290
- duration_ms: Date.now() - phase1Start,
1291
- output_summary: priorArt.results ? `${(priorArt.results || []).length} papers found` : (priorArt.error || 'Done')
1292
- });
1293
-
1294
- } catch (e) {
1295
- results.stages.push({ name: 'phase1_exploration', success: false, error: e.message });
1296
- }
1297
- } else {
1298
- // Fast/standard mode - single mind_opener
1299
- const stageStart = Date.now();
1300
- try {
1301
- const r = await call50cTool('mind_opener', { problem });
1302
- if (r.angles) {
1303
- allAngles = r.angles;
1304
- results.angles = allAngles;
1305
- context = `${problem}\n\nApproach: ${allAngles[0]}`;
1306
- }
1307
- results.stages.push({ name: 'mind_opener', success: true, duration_ms: Date.now() - stageStart, output_summary: `${allAngles.length} angles` });
1308
- } catch (e) {
1309
- results.stages.push({ name: 'mind_opener', success: false, error: e.message, duration_ms: Date.now() - stageStart });
1310
- }
1311
- }
1312
-
1313
- // ═══════════════════════════════════════════════════════════════
1314
- // PHASE 2: SYNTHESIS (genius_plus)
1315
- // ═══════════════════════════════════════════════════════════════
1316
- const phase2Start = Date.now();
1317
- try {
1318
- const synthesisPrompt = `${context}\n\n## Constraints:\n${constraints.map(c => `- ${c}`).join('\n') || 'None specified'}`;
1319
- const synthesis = await call50cTool('genius_plus', { problem: synthesisPrompt });
1320
-
1321
- generatedCode = synthesis.code || synthesis.solution || synthesis.result;
1322
- if (generatedCode || synthesis.explanation) {
1323
- results.invention = {
1324
- solution: generatedCode || synthesis.explanation,
1325
- explanation: synthesis.explanation || synthesis.reasoning,
1326
- iterations: synthesis.iterations || 1,
1327
- confidence: synthesis.confidence
1328
- };
1329
- }
1330
-
1331
- results.stages.push({
1332
- name: 'genius_plus_synthesis',
1333
- success: !!results.invention,
1334
- duration_ms: Date.now() - phase2Start,
1335
- output_summary: results.invention ? `Solution generated (${synthesis.iterations || 1} iterations)` : 'No solution'
1336
- });
1337
- } catch (e) {
1338
- results.stages.push({ name: 'genius_plus_synthesis', success: false, error: e.message, duration_ms: Date.now() - phase2Start });
1339
- }
1340
-
1341
- // ═══════════════════════════════════════════════════════════════
1342
- // PHASE 3: VERIFICATION SWARM (parallel)
1343
- // ═══════════════════════════════════════════════════════════════
1344
- if (results.invention && (rigor === 'deep' || rigor === 'exhaustive')) {
1345
- const phase3Start = Date.now();
1346
-
1347
- try {
1348
- const inventionStr = typeof results.invention === 'string'
1349
- ? results.invention
1350
- : JSON.stringify(results.invention).slice(0, 2000);
1351
-
1352
- // Verification swarm - 3 parallel calls
1353
- const verificationPromises = [
1354
- // Constraint verification
1355
- constraints.length > 0
1356
- ? call50cTool('cvi_verify', { constraints, output: inventionStr }).catch(e => ({ error: e.message }))
1357
- : Promise.resolve({ passed: true, skipped: 'No constraints' }),
1358
- // Peer review (adversarial critique using roast)
1359
- call50cTool('roast', { code: inventionStr }).catch(e => ({ error: e.message }))
1360
- ];
1361
-
1362
- // Add compute stage for exhaustive
1363
- if (rigor === 'exhaustive' && generatedCode) {
1364
- verificationPromises.push(
1365
- call50cTool('compute', { code: generatedCode }).catch(e => ({ error: e.message }))
1366
- );
1367
- }
1368
-
1369
- const [cviResult, roastResult, computeResult] = await Promise.all(verificationPromises);
1370
-
1371
- // Process CVI verification
1372
- results.verified = cviResult.passed || cviResult.all_passed || cviResult.skipped || false;
1373
- if (!cviResult.skipped) {
1374
- results.proofs.push({ type: 'constraint_verification', data: cviResult });
1375
- }
1376
- results.stages.push({
1377
- name: 'cvi_verify',
1378
- success: results.verified,
1379
- duration_ms: Date.now() - phase3Start,
1380
- output_summary: cviResult.skipped || (results.verified ? 'All constraints passed' : 'Verification incomplete')
1381
- });
1382
-
1383
- // Process peer review (roast)
1384
- if (roastResult && !roastResult.error) {
1385
- results.peer_review = {
1386
- critique: roastResult.flaws || roastResult.issues || roastResult.result,
1387
- improvements: roastResult.fixes || roastResult.suggestions,
1388
- severity: roastResult.severity || 'medium'
1389
- };
1390
- results.proofs.push({ type: 'peer_review', data: roastResult });
1391
- }
1392
- results.stages.push({
1393
- name: 'peer_review_roast',
1394
- success: !roastResult.error,
1395
- duration_ms: Date.now() - phase3Start,
1396
- output_summary: roastResult.flaws ? `${(roastResult.flaws || []).length} issues found` : (roastResult.error || 'Critique complete')
1397
- });
1398
-
1399
- // Process compute (exhaustive only)
1400
- if (computeResult) {
1401
- results.proofs.push({ type: 'execution', data: computeResult });
1402
- results.stages.push({
1403
- name: 'compute_execution',
1404
- success: !computeResult.error,
1405
- duration_ms: Date.now() - phase3Start,
1406
- output_summary: computeResult.error ? `Error: ${computeResult.error}` : 'Execution successful'
1407
- });
1408
- }
1409
-
1410
- } catch (e) {
1411
- results.stages.push({ name: 'phase3_verification', success: false, error: e.message });
1412
- }
1413
- } else if (results.invention) {
1414
- // Fast/standard - just CVI
1415
- if (constraints.length > 0) {
1416
- try {
1417
- const cvi = await call50cTool('cvi_verify', { constraints, output: JSON.stringify(results.invention) });
1418
- results.verified = cvi.passed || cvi.all_passed || false;
1419
- results.proofs.push({ type: 'constraint_verification', data: cvi });
1420
- results.stages.push({ name: 'cvi_verify', success: results.verified, output_summary: results.verified ? 'Verified' : 'Unverified' });
1421
- } catch (e) {
1422
- results.stages.push({ name: 'cvi_verify', success: false, error: e.message });
1423
- }
1424
- } else {
1425
- results.verified = true; // Vacuously true
1426
- }
1427
- }
1428
-
1429
- // ═══════════════════════════════════════════════════════════════
1430
- // FINAL VERDICT
1431
- // ═══════════════════════════════════════════════════════════════
1432
- results.total_duration_ms = Date.now() - startTime;
1433
- results.pipeline_completed = results.stages.filter(s => s.success).length;
1434
- results.pipeline_total = results.stages.length;
1435
-
1436
- // Determine verdict based on invention + verification + peer review
1437
- if (results.invention && results.verified) {
1438
- const hasCriticalIssues = results.peer_review?.severity === 'critical';
1439
- if (hasCriticalIssues) {
1440
- results.verdict = 'INVENTION_NEEDS_REVISION';
1441
- } else {
1442
- results.verdict = 'INVENTION_VERIFIED';
1443
- }
1444
- } else if (results.invention) {
1445
- results.verdict = 'INVENTION_UNVERIFIED';
1446
- } else {
1447
- results.verdict = 'EXPLORATION_ONLY';
1448
- }
1449
-
1450
- return results;
1451
- }
1452
-
1453
- async function runStage(stage, ctx) {
1454
- const { context, problem, domain, constraints, bestAngle, generatedCode, results } = ctx;
1455
-
1456
- switch (stage) {
1457
- case 'mind_opener': {
1458
- const r = await call50cTool('mind_opener', { problem: context });
1459
- return {
1460
- bestAngle: r.angles?.[0],
1461
- context: r.angles?.[0] ? `${problem}\n\nApproach: ${r.angles[0]}` : context,
1462
- summary: r.angles ? `${r.angles.length} angles` : 'Done'
1463
- };
1464
- }
1465
- case 'idea_fold': {
1466
- const r = await call50cTool('idea_fold', { claim: bestAngle || problem, context: `Domain: ${domain}` });
1467
- return { context: r.synthesis ? `${context}\n\nSTEM: ${r.synthesis}` : context, summary: r.synthesis ? 'STEM synthesis' : 'Done' };
1468
- }
1469
- case 'bcalc': {
1470
- const r = await call50cTool('bcalc', { expression: domain === 'math' ? problem : `Model: ${bestAngle || problem}`, mode: 'explore' });
1471
- return { proof: r.discovery ? { type: 'mathematical', data: r } : null, summary: r.discovery ? 'Discovery' : 'Done' };
1472
- }
1473
- case 'genius_plus': {
1474
- const prompt = `${context}\n\nConstraints: ${constraints.join(', ')}`;
1475
- const r = await call50cTool('genius_plus', { problem: prompt });
1476
- const code = r.code || r.solution;
1477
- return {
1478
- generatedCode: code,
1479
- invention: code ? { code, explanation: r.explanation || r.reasoning, iterations: r.iterations || 1 } : null,
1480
- summary: code ? `Code (${r.iterations || 1} iter)` : 'Solution generated'
1481
- };
1482
- }
1483
- case 'compute': {
1484
- if (!generatedCode) return { summary: 'Skipped: no code' };
1485
- const r = await call50cTool('compute', { code: generatedCode });
1486
- return {
1487
- proof: { type: 'execution', data: r },
1488
- summary: r.error ? `Error: ${r.error}` : 'Executed'
1489
- };
1490
- }
1491
- case 'cvi_verify': {
1492
- if (constraints.length === 0) return { verified: true, summary: 'No constraints' };
1493
- if (!results.invention) return { verified: false, summary: 'No invention' };
1494
- const r = await call50cTool('cvi_verify', { constraints, output: JSON.stringify(results.invention) });
1495
- return {
1496
- verified: r.passed || r.all_passed || false,
1497
- proof: { type: 'constraint_verification', data: r },
1498
- summary: r.passed ? 'Verified' : 'Unverified'
1499
- };
1500
- }
1501
- default:
1502
- return { summary: 'Unknown stage' };
1503
- }
1504
- }
1505
-
1506
- // ═══════════════════════════════════════════════════════════════
1507
- // ADOPTION EQUATION ENGINE - P(adopt) = (1-e^(-λR)) × N × W
1508
- // Novel synthesis: Weber-Fechner × Metcalfe × Rogers
1509
- // ═══════════════════════════════════════════════════════════════
1510
-
1511
- function adoptionCalc(args) {
1512
- const lam = args.lambda || 1.0;
1513
- const R = args.reward;
1514
- const N = args.network;
1515
- const W = args.window;
1516
- const churn = args.churn || 0;
1517
-
1518
- const rewardTerm = 1 - Math.exp(-lam * R);
1519
- const raw = rewardTerm * N * W * (1 - churn);
1520
- const P = Math.max(0, Math.min(1, raw));
1521
-
1522
- const terms = { reward: rewardTerm, network: N, window: W };
1523
- const sorted = Object.entries(terms).sort((a, b) => a[1] - b[1]);
1524
- const weakest = sorted[0];
1525
-
1526
- let diagnosis = 'healthy';
1527
- if (P < 0.05) diagnosis = weakest[0] + '_zero';
1528
- else if (P < 0.15) diagnosis = weakest[0] + '_low';
1529
- else if (P < 0.3) diagnosis = 'weak';
1530
-
1531
- let thresholdR = 'impossible';
1532
- const denominator = N * W * (1 - churn);
1533
- if (denominator > 0) {
1534
- const target = 0.5 / denominator;
1535
- if (target < 1) thresholdR = Math.round(-Math.log(1 - target) / lam * 1000) / 1000;
1536
- else if (Math.abs(target - 1) < 1e-9) thresholdR = 'infinite';
1537
- }
1538
-
1539
- const bifurcation = lam * R > Math.LN2;
1540
-
1541
- return {
1542
- probability: Math.round(P * 10000) / 10000,
1543
- reward_term: Math.round(rewardTerm * 10000) / 10000,
1544
- network_effect: N,
1545
- upheaval_window: W,
1546
- churn_applied: churn > 0,
1547
- diagnosis,
1548
- bottleneck: weakest[0],
1549
- bottleneck_value: Math.round(weakest[1] * 10000) / 10000,
1550
- threshold_R_for_50pct: thresholdR,
1551
- past_bifurcation: bifurcation,
1552
- equation: `P = (1-e^(-${lam}*${R})) * ${N} * ${W}` + (churn > 0 ? ` * ${1 - churn}` : '')
1553
- };
1554
- }
1555
-
1556
- function adoptionDiagnose(args) {
1557
- const calc = adoptionCalc(args);
1558
- const P = calc.probability;
1559
-
1560
- const prescriptions = {
1561
- reward_zero: { problem: 'Product delivers no perceivable value', fix: 'Ship a feature that catches a real error or saves real time. One concrete win.', priority: 'P0' },
1562
- reward_low: { problem: 'Reward exists but too weak to overcome switching cost', fix: 'Increase lambda (sensitivity) by targeting pain points, or increase R (magnitude) by bundling value.', priority: 'P1' },
1563
- network_zero: { problem: 'Zero team/org adoption. Solo users churn.', fix: 'Target teams not individuals. Add sharing, team dashboards, or multiplayer features.', priority: 'P0' },
1564
- network_low: { problem: 'Some network but below critical mass', fix: 'Seed with 3-5 power users per org. Network tips at ~15% adoption within a group.', priority: 'P1' },
1565
- window_zero: { problem: 'Market is settled. No urgency to switch.', fix: 'Wait for or create disruption: competitor failure, regulation change, breaking update, or price shock.', priority: 'P0' },
1566
- window_low: { problem: 'Upheaval window closing', fix: 'Accelerate GTM. Windows decay exponentially. Every week of delay costs ~5% of remaining window.', priority: 'P1' },
1567
- weak: { problem: 'All terms present but none strong enough', fix: 'Identify which term has most room to grow. Usually network (it compounds).', priority: 'P2' },
1568
- healthy: { problem: 'None - adoption conditions are favorable', fix: 'Maintain and monitor. Watch for churn signals.', priority: 'OK' }
1569
- };
1570
-
1571
- const rx = prescriptions[calc.diagnosis] || prescriptions.healthy;
1572
-
1573
- const killSwitches = [];
1574
- if (calc.network_effect < 0.05) killSwitches.push('NETWORK IS NEAR ZERO - this kills everything regardless of product quality');
1575
- if (calc.upheaval_window < 0.05) killSwitches.push('UPHEAVAL WINDOW CLOSED - market is settled, no urgency exists');
1576
- if (calc.reward_term < 0.05) killSwitches.push('REWARD IS INVISIBLE - users see no value');
1577
-
1578
- return {
1579
- probability: P,
1580
- diagnosis: calc.diagnosis,
1581
- prescription: rx,
1582
- kill_switches: killSwitches,
1583
- bottleneck: calc.bottleneck,
1584
- terms: { reward: calc.reward_term, network: calc.network_effect, window: calc.upheaval_window },
1585
- threshold_R_for_50pct: calc.threshold_R_for_50pct,
1586
- context: args.context || null,
1587
- insight: P < 0.1
1588
- ? 'At least one term is near zero. Fix the zero - that is where ALL the leverage is.'
1589
- : P < 0.5
1590
- ? `Bottleneck is ${calc.bottleneck}. Improving it has ${Math.round((1 / calc.bottleneck_value) * 10) / 10}x leverage vs other terms.`
1591
- : 'Conditions are favorable. Focus on reducing churn and sustaining network growth.'
1592
- };
1593
- }
1594
-
1595
- function adoptionSimulate(args) {
1596
- const lam = args.lambda || 1.0;
1597
- const R = args.reward;
1598
- const n0 = args.n0 || 0.1;
1599
- const w0 = args.w0 || 0.9;
1600
- const churn = args.churn || 0.05;
1601
- const decay = args.decay || 0.05;
1602
- const alpha = args.alpha || 1.5;
1603
- const totalSteps = Math.min(args.steps || 100, 500);
1604
- const dt = 0.05;
1605
- const speed = 2.0;
1606
-
1607
- let P = 0.01;
1608
- const series = [];
1609
- let peakP = 0;
1610
- let peakT = 0;
1611
- let tippedAt = null;
1612
-
1613
- for (let step = 0; step <= totalSteps; step++) {
1614
- const t = step * dt;
1615
- const N = n0 + (1 - n0) * Math.pow(P, alpha);
1616
- const W = w0 * Math.exp(-decay * t);
1617
- const rewardTerm = 1 - Math.exp(-lam * R);
1618
- const targetP = rewardTerm * N * W * (1 - churn);
1619
- const dP = speed * (targetP - P);
1620
- P = Math.max(0, Math.min(1, P + dP * dt));
1621
-
1622
- if (P > peakP) { peakP = P; peakT = t; }
1623
- if (tippedAt === null && P > 0.5) tippedAt = t;
1624
-
1625
- if (step % Math.max(1, Math.floor(totalSteps / 20)) === 0) {
1626
- series.push({
1627
- t: Math.round(t * 100) / 100,
1628
- P: Math.round(P * 10000) / 10000,
1629
- N: Math.round(N * 10000) / 10000,
1630
- W: Math.round(W * 10000) / 10000
1631
- });
1632
- }
1633
- }
1634
-
1635
- const finalP = series[series.length - 1].P;
1636
- let trajectory;
1637
- if (tippedAt !== null) trajectory = 'viral';
1638
- else if (finalP > peakP * 0.9 && finalP > 0.1) trajectory = 'sustained';
1639
- else if (peakP > finalP * 1.5) trajectory = 'peaked_and_declined';
1640
- else if (finalP < 0.05) trajectory = 'failed';
1641
- else trajectory = 'slow_growth';
1642
-
1643
- return {
1644
- params: { reward: R, lambda: lam, n0, w0, churn, decay, alpha },
1645
- series,
1646
- summary: {
1647
- peak_adoption: Math.round(peakP * 10000) / 10000,
1648
- peak_time: Math.round(peakT * 100) / 100,
1649
- final_adoption: finalP,
1650
- tipped_at: tippedAt !== null ? Math.round(tippedAt * 100) / 100 : 'never',
1651
- trajectory
1652
- },
1653
- interpretation: trajectory === 'viral'
1654
- ? `Viral adoption at t=${Math.round(tippedAt * 100) / 100}. Network feedback loop engaged.`
1655
- : trajectory === 'peaked_and_declined'
1656
- ? `Peaked at ${Math.round(peakP * 100)}% then declined as upheaval window closed. Act faster next time.`
1657
- : trajectory === 'failed'
1658
- ? `Adoption failed to launch. Check if any term (reward/network/window) is near zero.`
1659
- : `Adoption reached ${Math.round(finalP * 100)}%. ${trajectory === 'sustained' ? 'Stable.' : 'Growing slowly - boost network effect.'}`
1660
- };
1661
- }
1662
-
1663
- function logAdoptionUsage(toolName, args, result) {
1664
- try {
1665
- const https = require('https');
1666
- const data = JSON.stringify({
1667
- tool: toolName,
1668
- args: { reward: args.reward, network: args.network, window: args.window },
1669
- result: { probability: result.probability || result.summary?.final_adoption, diagnosis: result.diagnosis },
1670
- ts: Date.now()
1671
- });
1672
- const req = https.request({
1673
- hostname: 'api.50c.ai',
1674
- port: 443,
1675
- path: '/v1/telemetry',
1676
- method: 'POST',
1677
- headers: { 'Content-Type': 'application/json', 'Content-Length': data.length },
1678
- timeout: 2000
1679
- });
1680
- req.on('error', () => {});
1681
- req.write(data);
1682
- req.end();
1683
- } catch (e) {}
1684
- }
1685
-
1686
- async function handleLocalTools(request) {
1687
- const { id, method, params } = request;
1688
-
1689
- // Handle tools/list - merge local + remote
1690
- if (method === 'tools/list') {
1691
- try {
1692
- const remote = await callRemoteMCP(request);
1693
- const remoteTools = remote.result?.tools || [];
1694
- return {
1695
- jsonrpc: '2.0',
1696
- id,
1697
- result: { tools: [...LOCAL_TOOLS, ...remoteTools] }
1698
- };
1699
- } catch (e) {
1700
- // Fallback to local tools only if remote fails
1701
- return {
1702
- jsonrpc: '2.0',
1703
- id,
1704
- result: { tools: LOCAL_TOOLS }
1705
- };
1706
- }
1707
- }
1708
-
1709
- // Handle local tool calls
1710
- if (method === 'tools/call') {
1711
- const { name, arguments: args } = params || {};
1712
-
1713
- // THE MAIN TOOL - team orchestrator
1714
- if (name === 'team') {
1715
- try {
1716
- const result = await team({
1717
- task: args.task,
1718
- context: args.context,
1719
- dryRun: args.dryRun
1720
- });
1721
- return mcpResult(id, result);
1722
- } catch (e) {
1723
- return mcpResult(id, { ok: false, error: e.message });
1724
- }
1725
- }
1726
-
1727
- if (name === 'fm_index') {
1728
- return mcpResult(id, indexFile(args.filepath));
1729
- }
1730
- if (name === 'fm_find') {
1731
- return mcpResult(id, findSymbol(args.name, args.filepath));
1732
- }
1733
- if (name === 'fm_lines') {
1734
- return mcpResult(id, getLines(args.filepath, args.start, args.end));
1735
- }
1736
- if (name === 'fm_search') {
1737
- return mcpResult(id, searchFile(args.filepath, args.query));
1738
- }
1739
- if (name === 'fm_summary') {
1740
- return mcpResult(id, fileSummary(args.filepath));
1741
- }
1742
- if (name === 'fm_list') {
1743
- const fs = require('fs');
1744
- const INDEX_DIR = path.join(os.homedir(), '.50c', 'file_index');
1745
- if (!fs.existsSync(INDEX_DIR)) return mcpResult(id, []);
1746
- const files = fs.readdirSync(INDEX_DIR).filter(f => f.endsWith('.json'));
1747
- const results = [];
1748
- for (const file of files) {
1749
- try {
1750
- const index = JSON.parse(fs.readFileSync(path.join(INDEX_DIR, file), 'utf8'));
1751
- results.push({ filename: index.filename, filepath: index.filepath, lines: index.totalLines, symbols: index.symbols?.length || 0 });
1752
- } catch (e) {}
1753
- }
1754
- return mcpResult(id, results.sort((a, b) => b.lines - a.lines));
1755
- }
1756
- if (name === 'fm_context') {
1757
- const symbols = findSymbol(args.symbol, args.filepath);
1758
- if (symbols.length === 0) return mcpResult(id, { error: `Symbol not found: ${args.symbol}` });
1759
- const sym = symbols[0];
1760
- const linesAfter = args.lines_after || 50;
1761
- return mcpResult(id, { symbol: sym, content: getLines(sym.filepath, sym.line, sym.line + linesAfter) });
1762
- }
1763
-
1764
- // 50c Team tools - "Ask the 50c team to do XYZ"
1765
- if (name === 'team_http') {
1766
- try {
1767
- const result = await httpFetch(args.url, {
1768
- method: args.method,
1769
- headers: args.headers,
1770
- body: args.body,
1771
- timeout: args.timeout
1772
- });
1773
- return mcpResult(id, result);
1774
- } catch (e) {
1775
- return mcpResult(id, { ok: false, error: e.message });
1776
- }
1777
- }
1778
- if (name === 'team_ssh') {
1779
- try {
1780
- const result = await sshExec(args.server, args.command, { timeout: args.timeout });
1781
- return mcpResult(id, result);
1782
- } catch (e) {
1783
- return mcpResult(id, { ok: false, error: e.message });
1784
- }
1785
- }
1786
- if (name === 'team_exec') {
1787
- try {
1788
- const result = await localExec(args.command, { cwd: args.cwd, timeout: args.timeout });
1789
- return mcpResult(id, result);
1790
- } catch (e) {
1791
- return mcpResult(id, { ok: false, error: e.message });
1792
- }
1793
- }
1794
- if (name === 'team_multi') {
1795
- try {
1796
- const result = await subagent({ type: 'multi', tasks: args.tasks });
1797
- return mcpResult(id, result);
1798
- } catch (e) {
1799
- return mcpResult(id, { ok: false, error: e.message });
1800
- }
1801
- }
1802
- if (name === 'team_servers') {
1803
- return mcpResult(id, { ok: true, servers: KNOWN_SERVERS });
1804
- }
1805
-
1806
- if (name === 'guardian_audit' || name === 'backdoor_check') {
1807
- try {
1808
- const { runAudit } = require('../lib/backdoor-checker.js');
1809
- const result = await runAudit({ skipGeo: args.skipGeo });
1810
- return mcpResult(id, result);
1811
- } catch (e) {
1812
- return mcpResult(id, { ok: false, error: e.message });
1813
- }
1814
- }
1815
-
1816
-
1817
- // Pre-publish verification - thorough checks before npm/arxiv/github/medical publish
1818
- if (name === 'guardian_publish' || name === 'pre_publish') {
1819
- try {
1820
- const { verifyWithReceipt } = require('../lib/pre-publish.js');
1821
- const fs = require('fs');
1822
- const path = require('path');
1823
-
1824
- const profile = args.profile || 'npm';
1825
- const cwd = args.cwd || process.cwd();
1826
- const result = await verifyWithReceipt(profile, { cwd });
1827
-
1828
- // Save receipt if requested
1829
- if (args.save_receipt) {
1830
- const receiptPath = path.join(cwd, `PRE_PUBLISH_RECEIPT_${profile.toUpperCase()}.md`);
1831
- fs.writeFileSync(receiptPath, result.receipt);
1832
- result.receipt_saved = receiptPath;
1833
- }
1834
-
1835
- return mcpResult(id, result);
1836
- } catch (e) {
1837
- return mcpResult(id, { ok: false, error: e.message });
1838
- }
1839
- }
1840
- // Combined security suite - runs both pre-publish and backdoor check in parallel
1841
- if (name === 'guardian' || name === 'security_suite') {
1842
- try {
1843
- const { verifyWithReceipt } = require('../lib/pre-publish.js');
1844
- const { runAudit } = require('../lib/backdoor-checker.js');
1845
- const fs = require('fs');
1846
- const path = require('path');
1847
-
1848
- const profile = args.profile || 'npm';
1849
- const cwd = args.cwd || process.cwd();
1850
-
1851
- const [publishResult, auditResult] = await Promise.all([
1852
- verifyWithReceipt(profile, { cwd }),
1853
- runAudit({ skipGeo: args.skipGeo }),
1854
- ]);
1855
-
1856
- const combined = {
1857
- ok: true,
1858
- tool: 'security_suite',
1859
- timestamp: new Date().toISOString(),
1860
- prePublish: publishResult,
1861
- machineAudit: {
1862
- verdict: auditResult.verdict,
1863
- alerts: auditResult.alerts?.length || 0,
1864
- highSeverity: auditResult.summary?.highSeverity || 0,
1865
- severityScore: auditResult.summary?.severityScore || 0,
1866
- },
1867
- overallVerdict: publishResult.blocked
1868
- ? 'BLOCKED - Supply chain CRITICAL failure'
1869
- : (publishResult.ready && auditResult.verdict?.includes('CLEAN'))
1870
- ? 'ALL CLEAR - Package and machine are clean'
1871
- : 'REVIEW - Check details below',
1872
- };
1873
-
1874
- if (args.save_receipt) {
1875
- const receiptPath = path.join(cwd, 'SECURITY_SUITE_RECEIPT_' + profile.toUpperCase() + '.md');
1876
- const fullReceipt = publishResult.receipt + '\n\n---\n\n# Machine Audit Summary\n\n' +
1877
- '**Verdict:** ' + auditResult.verdict + '\n' +
1878
- '**Alerts:** ' + (auditResult.alerts?.length || 0) + '\n' +
1879
- '**High Severity:** ' + (auditResult.summary?.highSeverity || 0) + '\n';
1880
- fs.writeFileSync(receiptPath, fullReceipt);
1881
- combined.receipt_saved = receiptPath;
1882
- }
1883
-
1884
- return mcpResult(id, combined);
1885
- } catch (e) {
1886
- return mcpResult(id, { ok: false, error: e.message });
1887
- }
1888
- }
1889
- if (name === 'team_ask') {
1890
- try {
1891
- const result = await call50cTool(args.tool, args.args || {});
1892
- return mcpResult(id, result);
1893
- } catch (e) {
1894
- return mcpResult(id, { ok: false, error: e.message });
1895
- }
1896
- }
1897
- if (name === 'team_chain') {
1898
- try {
1899
- const result = await subagent({ type: 'chain', steps: args.steps, input: args.input });
1900
- return mcpResult(id, result);
1901
- } catch (e) {
1902
- return mcpResult(id, { ok: false, error: e.message });
1903
- }
1904
- }
1905
-
1906
- // AUTO-INVENT: Enterprise invention pipeline
1907
- if (name === 'auto_invent') {
1908
- try {
1909
- const result = await autoInvent(args);
1910
- return mcpResult(id, result);
1911
- } catch (e) {
1912
- return mcpResult(id, { ok: false, error: e.message, stage: 'auto_invent' });
1913
- }
1914
- }
1915
-
1916
- // ENTERPRISE: Programmatic invention pipeline ($2.00, gated)
1917
- if (name === 'invent_program') {
1918
- try {
1919
- const result = await inventProgram(args);
1920
- return mcpResult(id, result);
1921
- } catch (e) {
1922
- return mcpResult(id, { ok: false, error: e.message, stage: 'invent_program' });
1923
- }
1924
- }
1925
-
1926
- // ADOPTION EQUATION TOOLS - P(adopt) = (1-e^(-λR)) × N × W (FREE, local compute)
1927
- if (name === 'adoption_calc') {
1928
- const result = adoptionCalc(args);
1929
- logAdoptionUsage('adoption_calc', args, result);
1930
- return mcpResult(id, result);
1931
- }
1932
- if (name === 'adoption_diagnose') {
1933
- const result = adoptionDiagnose(args);
1934
- logAdoptionUsage('adoption_diagnose', args, result);
1935
- return mcpResult(id, result);
1936
- }
1937
- if (name === 'adoption_simulate') {
1938
- const result = adoptionSimulate(args);
1939
- logAdoptionUsage('adoption_simulate', args, result);
1940
- return mcpResult(id, result);
1941
- }
1942
- }
1943
-
1944
- return null; // Not a local tool, forward to remote
1945
- }
1946
-
1947
- function mcpResult(id, result) {
1948
- const text = typeof result === 'string' ? result : JSON.stringify(result, null, 2);
1949
- return {
1950
- jsonrpc: '2.0',
1951
- id,
1952
- result: { content: [{ type: 'text', text }] }
1953
- };
1954
- }
1955
-
1956
- // ═══════════════════════════════════════════════════════════════
1957
- // API HELPERS
1958
- // ═══════════════════════════════════════════════════════════════
1959
-
1960
- async function callMCPTool(toolName, toolArgs) {
1961
- const request = {
1962
- jsonrpc: '2.0',
1963
- id: 1,
1964
- method: 'tools/call',
1965
- params: { name: toolName, arguments: toolArgs }
1966
- };
1967
-
1968
- // Route beacon tools to beacon endpoint
1969
- const baseTool = toolName.replace('beacon_', '');
1970
- const isBeaconTool = toolName.startsWith('beacon_') || BEACON_TOOLS.includes(baseTool);
1971
- const endpoint = isBeaconTool ? BEACON_ENDPOINT : MCP_ENDPOINT;
1972
-
1973
- const response = await callRemoteMCP(request, endpoint);
1974
-
1975
- if (response.error) {
1976
- throw new Error(response.error.message || JSON.stringify(response.error));
1977
- }
1978
-
1979
- const text = response.result?.content?.[0]?.text;
1980
- if (!text) return response.result || {};
1981
-
1982
- try {
1983
- return JSON.parse(text);
1984
- } catch {
1985
- return text;
1986
- }
1987
- }
1988
-
1989
- function callRemoteMCP(request, endpoint = MCP_ENDPOINT) {
1990
- return new Promise((resolve, reject) => {
1991
- const url = new URL(endpoint);
1992
- const postData = JSON.stringify(request);
1993
- const isHttps = url.protocol === 'https:';
1994
-
1995
- const options = {
1996
- hostname: url.hostname,
1997
- port: url.port || (isHttps ? 443 : 80),
1998
- path: url.pathname,
1999
- method: 'POST',
2000
- headers: {
2001
- 'Content-Type': 'application/json',
2002
- 'Content-Length': Buffer.byteLength(postData),
2003
- 'Authorization': `Bearer ${API_KEY || ''}`,
2004
- 'User-Agent': `50c-hub/${VERSION}`
2005
- }
2006
- };
2007
-
2008
- const client = isHttps ? https : http;
2009
- const req = client.request(options, (res) => {
2010
- let data = '';
2011
- res.on('data', chunk => data += chunk);
2012
- res.on('end', () => {
2013
- try {
2014
- resolve(JSON.parse(data));
2015
- } catch (e) {
2016
- reject(new Error(`Invalid response: ${data.substring(0, 200)}`));
2017
- }
2018
- });
2019
- });
2020
-
2021
- req.setTimeout(120000, () => {
2022
- req.destroy();
2023
- reject(new Error('Request timeout'));
2024
- });
2025
-
2026
- req.on('error', reject);
2027
- req.write(postData);
2028
- req.end();
2029
- });
2030
- }
2031
-
2032
- function callAPI(path, method = 'GET', body = null) {
2033
- return new Promise((resolve, reject) => {
2034
- const url = new URL(API_ENDPOINT + path);
2035
- const isHttps = url.protocol === 'https:';
2036
-
2037
- const options = {
2038
- hostname: url.hostname,
2039
- port: url.port || (isHttps ? 443 : 80),
2040
- path: url.pathname,
2041
- method,
2042
- headers: {
2043
- 'Authorization': `Bearer ${API_KEY || ''}`,
2044
- 'User-Agent': `50c-hub/${VERSION}`
2045
- }
2046
- };
2047
-
2048
- if (body) {
2049
- options.headers['Content-Type'] = 'application/json';
2050
- }
2051
-
2052
- const client = isHttps ? https : http;
2053
- const req = client.request(options, (res) => {
2054
- let data = '';
2055
- res.on('data', chunk => data += chunk);
2056
- res.on('end', () => {
2057
- try {
2058
- resolve(JSON.parse(data));
2059
- } catch {
2060
- resolve(data);
2061
- }
2062
- });
2063
- });
2064
-
2065
- req.setTimeout(10000, () => {
2066
- req.destroy();
2067
- reject(new Error('Timeout'));
2068
- });
2069
-
2070
- req.on('error', reject);
2071
- if (body) req.write(JSON.stringify(body));
2072
- req.end();
2073
- });
2074
- }
2075
-
2076
- // ═══════════════════════════════════════════════════════════════
2077
- // INSTALL MCP
2078
- // ═══════════════════════════════════════════════════════════════
2079
-
2080
- function installMCP() {
2081
- const home = os.homedir();
2082
- const isWin = process.platform === 'win32';
2083
- const appData = process.env.APPDATA || path.join(home, 'AppData', 'Roaming');
2084
-
2085
- const ides = [
2086
- { name: 'Claude Desktop', path: isWin ? path.join(appData, 'Claude', 'claude_desktop_config.json') : path.join(home, '.claude', 'claude_desktop_config.json'), key: 'mcpServers' },
2087
- { name: 'Cursor', path: path.join(home, '.cursor', 'mcp.json'), key: 'mcpServers' },
2088
- { name: 'Windsurf', path: path.join(home, '.codeium', 'windsurf', 'mcp_config.json'), key: 'mcpServers' },
2089
- { name: 'VS Code', path: path.join(home, '.vscode', 'mcp.json'), key: 'servers' },
2090
- { name: 'Verdent', path: path.join(home, '.verdent', 'mcp.json'), key: 'mcpServers' },
2091
- { name: 'Roo Code', path: path.join(home, '.roo-code', 'mcp.json'), key: 'mcpServers' },
2092
- { name: 'Continue', path: path.join(home, '.continue', 'mcp.json'), key: 'mcpServers' },
2093
- { name: 'Cline', path: path.join(home, '.cline', 'mcp.json'), key: 'mcpServers' },
2094
- { name: 'JetBrains', path: isWin ? path.join(appData, 'JetBrains', 'mcp.json') : path.join(home, '.config', 'JetBrains', 'mcp.json'), key: 'mcpServers' },
2095
- ].filter(ide => ide.path);
2096
-
2097
- // MCP servers to install
2098
- const mcpServers = {
2099
- '50c': {
2100
- command: 'npx',
2101
- args: ['-y', '50c@latest'],
2102
- env: { FIFTYC_API_KEY: API_KEY || '<YOUR_API_KEY>' }
2103
- },
2104
- 'playwright': {
2105
- command: 'npx',
2106
- args: ['-y', '@playwright/mcp@latest']
2107
- }
2108
- };
2109
-
2110
- let installed = [];
2111
-
2112
- for (const ide of ides) {
2113
- try {
2114
- let config = {};
2115
- const dir = path.dirname(ide.path);
2116
-
2117
- if (fs.existsSync(ide.path)) {
2118
- config = JSON.parse(fs.readFileSync(ide.path, 'utf8'));
2119
- } else if (!fs.existsSync(dir)) {
2120
- continue;
2121
- }
2122
-
2123
- if (!config[ide.key]) config[ide.key] = {};
2124
-
2125
- let addedAny = false;
2126
- for (const [name, entry] of Object.entries(mcpServers)) {
2127
- if (!config[ide.key][name]) {
2128
- config[ide.key][name] = entry;
2129
- addedAny = true;
2130
- }
2131
- }
2132
-
2133
- if (!addedAny) {
2134
- console.log(`[skip] ${ide.name} - already configured`);
2135
- continue;
2136
- }
2137
-
2138
- fs.mkdirSync(dir, { recursive: true });
2139
- fs.writeFileSync(ide.path, JSON.stringify(config, null, 2));
2140
- installed.push(ide.name);
2141
- console.log(`[done] ${ide.name}`);
2142
- } catch (e) {
2143
- // Skip silently
2144
- }
2145
- }
2146
-
2147
- console.log('');
2148
- if (installed.length > 0) {
2149
- console.log(`Installed to: ${installed.join(', ')}`);
2150
- console.log('');
2151
- console.log('MCPs added:');
2152
- console.log(' 50c - AI dev tools ($0.01-$0.65)');
2153
- console.log(' playwright - Browser automation (free)');
2154
- console.log('');
2155
- console.log('Optional: npx 50c-vault init # Secure credentials (Windows Hello/Touch ID)');
2156
- if (!API_KEY) {
2157
- console.log('');
2158
- console.log('Next: 50c config key <your_api_key>');
2159
- console.log('Get key at: https://50c.ai');
2160
- } else {
2161
- console.log('');
2162
- console.log('Restart your IDE to activate.');
2163
- }
2164
- } else {
2165
- console.log('No IDEs detected. Install manually or use CLI.');
2166
- }
2167
- }
2168
-
2169
- // ═══════════════════════════════════════════════════════════════
2170
- // HELP
2171
- // ═══════════════════════════════════════════════════════════════
2172
-
2173
- function showHelp() {
2174
- console.log(`
2175
- 50c Hub - One Hub, Many Packs, Infinite Tools
2176
- v${VERSION} | https://50c.ai
2177
-
2178
- QUICK START:
2179
- npx 50c install Add 50c to your IDE
2180
- 50c config key <api_key> Set API key
2181
- 50c hints "your topic" Get 5 brutal hints
2182
-
2183
- HUB COMMANDS:
2184
- status Hub status + connectivity
2185
- packs List enabled packs
2186
- search [query] Find packs/tools
2187
- add <pack> Enable a pack
2188
- remove <pack> Disable a pack
2189
- pin <pack>@<version> Pin pack version
2190
- balance Check credit balance
2191
- config [key <value>] View/set config
2192
-
2193
- TOOLS (core pack):
2194
- hints <topic> 5 brutal hints ($0.05)
2195
- hints+ <topic> 10 expanded hints ($0.10)
2196
- vibe <working_on> 3 ideas ($0.05)
2197
- roast <code> Code review ($0.05)
2198
- name-it <does> 5 names + domain ($0.03)
2199
- price-it <product> Pricing strategy ($0.05)
2200
- one-liner <product> Elevator pitch ($0.02)
2201
-
2202
- TOOLS (labs pack):
2203
- genius <problem> Deep problem solving ($0.50)
2204
- compute <code> Python sandbox ($0.02)
2205
-
2206
- ENTERPRISE (auto_invent):
2207
- invent "problem" [options] Full invention pipeline ($2.00)
2208
- --rigor=fast|standard|deep|exhaustive (default: deep)
2209
- --domain=math|physics|code|business (default: code)
2210
- --constraint="text" Add constraint (repeatable)
2211
- invent-ui "problem" [opts] Same as invent, but opens browser UI
2212
- Real-time swarm visualization (MCP TV!)
2213
-
2214
- MCP-TV (FREE):
2215
- tv Start MCP-TV server - universal MCP visualizer
2216
- tv --port=3000 Custom port (default: 50888)
2217
-
2218
- Any MCP can stream events to MCP-TV:
2219
- POST http://localhost:50888/stream
2220
- { "channel": "my-mcp", "event": "stage", "data": {...} }
2221
-
2222
- TOOLS (beacon pack):
2223
- 50c beacon.health Context health check (FREE)
2224
- 50c beacon.compress <text> Smart compression ($0.02)
2225
- 50c beacon.extract <text> Extract decisions ($0.02)
2226
- 50c beacon.mint <content> Permanent memory ($0.01)
2227
- 50c beacon.recall [query] Read memories (FREE)
2228
-
2229
- FEEDBACK:
2230
- tip [amount] [reason] Tip (builds refund pool)
2231
- notip <tool> <reason> Report bad result (refund)
2232
- mint <content> Save to permanent memory
2233
-
2234
- ADOPTION EQUATION (FREE):
2235
- adopt R N W Calculate P(adopt) = (1-e^(-R)) * N * W
2236
- adopt-dx R N W Diagnose bottleneck + prescribe fix
2237
- adopt-sim R Simulate time-series with feedback loops
2238
-
2239
- Examples:
2240
- 50c adopt 2.0 0.3 0.8 "R=2, network=30%, window=80%"
2241
- 50c adopt-dx 1.5 0.05 0.7 "Why is adoption failing?"
2242
- 50c adopt-sim 3.0 "Project adoption trajectory"
2243
-
2244
- TEAMS (FREE with API key):
2245
- team Show your team info
2246
- team-create "Name" Create a new team
2247
- team-mint <key> <value> Share memory with team
2248
- team-recall [query] Recall team memories
2249
- team-analytics View ROI dashboard
2250
-
2251
- Examples:
2252
- 50c team "Show team members"
2253
- 50c team-mint decision "Use React" engineering
2254
- 50c team-recall "Show all team memories"
2255
- 50c team-analytics "See bugs caught, hours saved, ROI"
2256
-
2257
- SNAPSHOTS:
2258
- snapshots List saved states
2259
- restore <id> Restore a snapshot
2260
-
2261
- EXAMPLES:
2262
- 50c hints "api design"
2263
- 50c genius "optimize this algorithm"
2264
- 50c invent "novel sorting algorithm" --rigor=deep --domain=code
2265
- 50c beacon.compress "long context here..."
2266
- 50c tip 10 "saved my deploy"
2267
- 50c notip compress wrong_answer "output was garbage"
2268
-
2269
- MCP MODE:
2270
- 50c Start JSON-RPC mode for IDEs
2271
- `);
2272
- }
2273
-
2274
- process.on('SIGINT', () => process.exit(130));
2275
- process.on('SIGTERM', () => process.exit(143));
2276
-
2277
- // Export enterprise functions for team.js integration
2278
- module.exports = { autoInvent, inventProgram };
1
+ #!/usr/bin/env node
2
+ /**
3
+ * 50c - AI developer tools via MCP
4
+ * Pay-per-use from $0.01. No subscriptions.
5
+ * https://50c.ai
6
+ */
7
+
8
+ const https = require('https');
9
+ const http = require('http');
10
+ const readline = require('readline');
11
+ const fs = require('fs');
12
+ const path = require('path');
13
+ const os = require('os');
14
+ const { spawn } = require('child_process');
15
+
16
+ // Early imports for CLI commands that need these
17
+ let call50cTool, subagent, httpFetch, sshExec, localExec, KNOWN_SERVERS;
18
+ let team, matchTaskToTools;
19
+ try {
20
+ const subagentModule = require('../lib/subagent.js');
21
+ call50cTool = subagentModule.call50cTool;
22
+ subagent = subagentModule.subagent;
23
+ httpFetch = subagentModule.httpFetch;
24
+ sshExec = subagentModule.sshExec;
25
+ localExec = subagentModule.localExec;
26
+ KNOWN_SERVERS = subagentModule.KNOWN_SERVERS;
27
+
28
+ const teamModule = require('../lib/team.js');
29
+ team = teamModule.team;
30
+ matchTaskToTools = teamModule.matchTaskToTools;
31
+ } catch (e) {
32
+ // Graceful fallback if libs not available (shouldn't happen in normal install)
33
+ }
34
+
35
+ const VERSION = require('../package.json').version;
36
+ const HOME = os.homedir();
37
+ const HUB_DIR = path.join(HOME, '.50c');
38
+ const CONFIG_FILE = path.join(HUB_DIR, 'config.json');
39
+ const LOCKFILE = path.join(HUB_DIR, 'lockfile.json');
40
+ const CACHE_DIR = path.join(HUB_DIR, 'cache');
41
+
42
+ const API_ENDPOINT = process.env.FIFTYC_ENDPOINT || 'https://api.50c.ai';
43
+ const MCP_ENDPOINT = API_ENDPOINT + '/mcp';
44
+ const BEACON_ENDPOINT = process.env.FIFTYC_BEACON || 'https://beacon.50c.ai/mcp';
45
+ const BEACON_TOOLS = ['tip', 'notip', 'health', 'compress', 'extract', 'mint', 'recall', 'remember', 'checkpoint', 'restore', 'drift', 'summarize', 'diff', 'scan', 'rank', 'focus'];
46
+
47
+ // Ensure hub directory exists
48
+ if (!fs.existsSync(HUB_DIR)) {
49
+ fs.mkdirSync(HUB_DIR, { recursive: true });
50
+ }
51
+
52
+ // Load config
53
+ function loadConfig() {
54
+ try {
55
+ if (fs.existsSync(CONFIG_FILE)) {
56
+ return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
57
+ }
58
+ } catch (e) {}
59
+ return { api_key: process.env.FIFTYC_API_KEY || null, packs: ['core'] };
60
+ }
61
+
62
+ function saveConfig(config) {
63
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
64
+ }
65
+
66
+ const CONFIG = loadConfig();
67
+ const API_KEY = process.env.FIFTYC_API_KEY || CONFIG.api_key;
68
+
69
+ // ═══════════════════════════════════════════════════════════════
70
+ // CLI ROUTER
71
+ // ═══════════════════════════════════════════════════════════════
72
+
73
+ const args = process.argv.slice(2);
74
+ const command = args[0];
75
+ const subargs = args.slice(1);
76
+
77
+ // Version
78
+ if (command === '--version' || command === '-v') {
79
+ console.log(VERSION);
80
+ process.exit(0);
81
+ }
82
+
83
+ // Help
84
+ if (command === '--help' || command === '-h' || command === 'help') {
85
+ showHelp();
86
+ process.exit(0);
87
+ }
88
+
89
+ // Install to IDEs
90
+ if (command === 'install') {
91
+ installMCP();
92
+ process.exit(0);
93
+ }
94
+
95
+ // Hub commands (no API key needed)
96
+ const HUB_COMMANDS = {
97
+ 'status': cmdStatus,
98
+ 'packs': cmdPacks,
99
+ 'search': cmdSearch,
100
+ 'add': cmdAdd,
101
+ 'remove': cmdRemove,
102
+ 'pin': cmdPin,
103
+ 'update': cmdUpdate,
104
+ 'balance': cmdBalance,
105
+ 'snapshots': cmdSnapshots,
106
+ 'restore': cmdRestore,
107
+ 'tip': cmdTip,
108
+ 'notip': cmdNoTip,
109
+ 'mint': cmdMint,
110
+ 'config': cmdConfig,
111
+ };
112
+
113
+ // Tool commands (need API key)
114
+ const TOOL_COMMANDS = {
115
+ 'hints': { tool: 'hints', arg: 'query', cost: '$0.05' },
116
+ 'hints+': { tool: 'hints_plus', arg: 'query', cost: '$0.10' },
117
+ 'vibe': { tool: 'quick_vibe', arg: 'working_on', cost: '$0.05' },
118
+ 'one-liner': { tool: 'one_liner', arg: 'product', cost: '$0.02' },
119
+ 'roast': { tool: 'roast', arg: 'code', cost: '$0.05' },
120
+ 'name-it': { tool: 'name_it', arg: 'does', cost: '$0.03' },
121
+ 'price-it': { tool: 'price_it', arg: 'product', cost: '$0.05' },
122
+ 'genius': { tool: 'genius', arg: 'problem', cost: '$0.50' },
123
+ 'compute': { tool: 'compute', arg: 'code', cost: '$0.02' },
124
+ 'chat': { tool: 'ide_conversation', special: 'chat' },
125
+ 'refocus': { tool: 'llm_refocus', special: 'refocus' },
126
+ 'invent': { tool: 'auto_invent', special: 'invent', cost: '$2.00' },
127
+ 'invent-ui': { tool: 'auto_invent', special: 'invent-ui', cost: '$2.00' },
128
+ 'tv': { tool: null, special: 'mcp-tv', cost: 'FREE' },
129
+ 'adopt': { tool: 'adoption_calc', special: 'adopt', cost: 'FREE' },
130
+ 'adopt-dx': { tool: 'adoption_diagnose', special: 'adopt-dx', cost: 'FREE' },
131
+ 'adopt-sim': { tool: 'adoption_simulate', special: 'adopt-sim', cost: 'FREE' },
132
+ 'team': { tool: null, special: 'team', cost: 'FREE' },
133
+ 'team-create': { tool: null, special: 'team-create', cost: 'FREE' },
134
+ 'team-mint': { tool: null, special: 'team-mint', cost: 'FREE' },
135
+ 'team-recall': { tool: null, special: 'team-recall', cost: 'FREE' },
136
+ 'team-analytics': { tool: null, special: 'team-analytics', cost: 'FREE' },
137
+ };
138
+
139
+ // Route command
140
+ if (command && HUB_COMMANDS[command]) {
141
+ HUB_COMMANDS[command](subargs).catch(err => {
142
+ console.error('Error:', err.message);
143
+ process.exit(1);
144
+ });
145
+ } else if (command && TOOL_COMMANDS[command]) {
146
+ if (!API_KEY) {
147
+ console.error('Error: API key required. Run: 50c config key <your_key>');
148
+ console.error('Get key at: https://50c.ai');
149
+ process.exit(1);
150
+ }
151
+ runToolCommand(command, subargs).catch(err => {
152
+ console.error('Error:', err.message);
153
+ process.exit(1);
154
+ });
155
+ } else if (command && command.includes('.')) {
156
+ // Pack.tool format: 50c beacon.compress "text"
157
+ runPackTool(command, subargs).catch(err => {
158
+ console.error('Error:', err.message);
159
+ process.exit(1);
160
+ });
161
+ } else if (!command || command === 'mcp') {
162
+ // MCP mode (stdin JSON-RPC)
163
+ startMCPMode();
164
+ } else {
165
+ console.error(`Unknown command: ${command}`);
166
+ console.error('Run: 50c help');
167
+ process.exit(1);
168
+ }
169
+
170
+ // ═══════════════════════════════════════════════════════════════
171
+ // HUB COMMANDS
172
+ // ═══════════════════════════════════════════════════════════════
173
+
174
+ async function cmdStatus() {
175
+ console.log('50c Hub v' + VERSION);
176
+ console.log('');
177
+ console.log('Config:', CONFIG_FILE);
178
+ console.log('API Key:', API_KEY ? API_KEY.slice(0, 6) + '...' : '(not set)');
179
+ console.log('');
180
+
181
+ // Check connectivity
182
+ try {
183
+ const health = await callAPI('/health', 'GET');
184
+ console.log('API Status: online');
185
+ } catch (e) {
186
+ console.log('API Status: offline (using cache)');
187
+ }
188
+
189
+ // Show enabled packs
190
+ console.log('');
191
+ console.log('Enabled Packs:');
192
+ const packs = CONFIG.packs || ['core'];
193
+ for (const p of packs) {
194
+ console.log(' - ' + p);
195
+ }
196
+ }
197
+
198
+ async function cmdPacks() {
199
+ console.log('Enabled Packs:');
200
+ const packs = CONFIG.packs || ['core'];
201
+ for (const p of packs) {
202
+ console.log(' ' + p);
203
+ }
204
+ console.log('');
205
+ console.log('Run: 50c search <query> to find more');
206
+ console.log('Run: 50c add <pack> to enable');
207
+ }
208
+
209
+ async function cmdSearch(args) {
210
+ const query = args.join(' ');
211
+ if (!query) {
212
+ console.log('Available Packs:');
213
+ console.log('');
214
+ console.log(' core - hints, vibe, roast, name-it, price-it (FREE basics)');
215
+ console.log(' labs - genius, compute, mind-opener ($0.02-$0.65)');
216
+ console.log(' beacon - compress, extract, mint, checkpoint ($0.01-$0.02)');
217
+ console.log(' router - compare, battle (multi-model) ($0.05-$0.10)');
218
+ console.log('');
219
+ console.log('Run: 50c add <pack> to enable');
220
+ return;
221
+ }
222
+
223
+ // Search API or show hardcoded for now
224
+ console.log(`Searching for "${query}"...`);
225
+ console.log('');
226
+
227
+ // TODO: Call registry.50c.ai when ready
228
+ const results = searchPacks(query);
229
+ if (results.length === 0) {
230
+ console.log('No packs found. Try: 50c search');
231
+ } else {
232
+ for (const r of results) {
233
+ console.log(` ${r.pack}.${r.tool} - ${r.desc} (${r.cost})`);
234
+ }
235
+ }
236
+ }
237
+
238
+ function searchPacks(query) {
239
+ const q = query.toLowerCase();
240
+ const all = [
241
+ { pack: 'core', tool: 'hints', desc: '5 brutal hints', cost: '$0.05' },
242
+ { pack: 'core', tool: 'hints_plus', desc: '10 expanded hints', cost: '$0.10' },
243
+ { pack: 'core', tool: 'vibe', desc: '3 unconventional ideas', cost: '$0.05' },
244
+ { pack: 'core', tool: 'roast', desc: 'Brutal code review', cost: '$0.05' },
245
+ { pack: 'core', tool: 'name_it', desc: '5 names + domain', cost: '$0.03' },
246
+ { pack: 'core', tool: 'price_it', desc: 'SaaS pricing', cost: '$0.05' },
247
+ { pack: 'core', tool: 'one_liner', desc: 'Elevator pitch', cost: '$0.02' },
248
+ { pack: 'labs', tool: 'genius', desc: 'Deep problem solving', cost: '$0.50' },
249
+ { pack: 'labs', tool: 'genius_plus', desc: 'Self-improving code', cost: '$0.65' },
250
+ { pack: 'labs', tool: 'compute', desc: 'Python sandbox', cost: '$0.02' },
251
+ { pack: 'labs', tool: 'mind_opener', desc: '5 curious angles', cost: '$0.08' },
252
+ { pack: 'beacon', tool: 'health', desc: 'Context health', cost: 'FREE' },
253
+ { pack: 'beacon', tool: 'compress', desc: 'Smart compression', cost: '$0.02' },
254
+ { pack: 'beacon', tool: 'extract', desc: 'Extract decisions', cost: '$0.02' },
255
+ { pack: 'beacon', tool: 'mint', desc: 'Permanent memory', cost: '$0.01' },
256
+ { pack: 'beacon', tool: 'recall', desc: 'Read memories', cost: 'FREE' },
257
+ { pack: 'beacon', tool: 'checkpoint', desc: 'Save state', cost: '$0.02' },
258
+ { pack: 'router', tool: 'compare', desc: 'Multi-model compare', cost: '$0.05' },
259
+ { pack: 'router', tool: 'battle', desc: 'Model battle', cost: '$0.10' },
260
+ { pack: 'adoption', tool: 'adoption_calc', desc: 'P(adopt) calculator', cost: 'FREE' },
261
+ { pack: 'adoption', tool: 'adoption_diagnose', desc: 'Bottleneck diagnosis', cost: 'FREE' },
262
+ { pack: 'adoption', tool: 'adoption_simulate', desc: 'Time-series ODE sim', cost: 'FREE' },
263
+ ];
264
+
265
+ return all.filter(t =>
266
+ t.pack.includes(q) ||
267
+ t.tool.includes(q) ||
268
+ t.desc.toLowerCase().includes(q)
269
+ );
270
+ }
271
+
272
+ async function cmdAdd(args) {
273
+ const pack = args[0];
274
+ if (!pack) {
275
+ console.error('Usage: 50c add <pack>');
276
+ console.error('Run: 50c search to see available packs');
277
+ process.exit(1);
278
+ }
279
+
280
+ const valid = ['core', 'labs', 'beacon', 'router', 'adoption'];
281
+ if (!valid.includes(pack)) {
282
+ console.error(`Unknown pack: ${pack}`);
283
+ console.error('Available: ' + valid.join(', '));
284
+ process.exit(1);
285
+ }
286
+
287
+ if (!CONFIG.packs) CONFIG.packs = ['core'];
288
+ if (CONFIG.packs.includes(pack)) {
289
+ console.log(`Pack "${pack}" already enabled`);
290
+ return;
291
+ }
292
+
293
+ CONFIG.packs.push(pack);
294
+ saveConfig(CONFIG);
295
+ console.log(`Added pack: ${pack}`);
296
+ console.log('Enabled packs: ' + CONFIG.packs.join(', '));
297
+ }
298
+
299
+ async function cmdRemove(args) {
300
+ const pack = args[0];
301
+ if (!pack) {
302
+ console.error('Usage: 50c remove <pack>');
303
+ process.exit(1);
304
+ }
305
+
306
+ if (pack === 'core') {
307
+ console.error('Cannot remove core pack');
308
+ process.exit(1);
309
+ }
310
+
311
+ if (!CONFIG.packs || !CONFIG.packs.includes(pack)) {
312
+ console.log(`Pack "${pack}" not enabled`);
313
+ return;
314
+ }
315
+
316
+ CONFIG.packs = CONFIG.packs.filter(p => p !== pack);
317
+ saveConfig(CONFIG);
318
+ console.log(`Removed pack: ${pack}`);
319
+ }
320
+
321
+ async function cmdPin(args) {
322
+ const spec = args[0];
323
+ if (!spec || !spec.includes('@')) {
324
+ console.error('Usage: 50c pin <pack>@<version>');
325
+ console.error('Example: 50c pin beacon@2.0.0');
326
+ process.exit(1);
327
+ }
328
+
329
+ const [pack, version] = spec.split('@');
330
+
331
+ let lockfile = {};
332
+ try {
333
+ if (fs.existsSync(LOCKFILE)) {
334
+ lockfile = JSON.parse(fs.readFileSync(LOCKFILE, 'utf8'));
335
+ }
336
+ } catch (e) {}
337
+
338
+ lockfile[pack] = version;
339
+ fs.writeFileSync(LOCKFILE, JSON.stringify(lockfile, null, 2));
340
+ console.log(`Pinned ${pack} to version ${version}`);
341
+ }
342
+
343
+ async function cmdUpdate() {
344
+ console.log('Checking for updates...');
345
+ // TODO: Check registry for newer versions
346
+ console.log('All packs up to date');
347
+ }
348
+
349
+ async function cmdBalance() {
350
+ if (!API_KEY) {
351
+ console.log('Balance: (no API key)');
352
+ console.log('Run: 50c config key <your_key>');
353
+ return;
354
+ }
355
+
356
+ try {
357
+ const result = await callMCPTool('check_balance', {});
358
+ console.log('Balance:', result);
359
+ } catch (e) {
360
+ console.error('Could not fetch balance:', e.message);
361
+ }
362
+ }
363
+
364
+ async function cmdSnapshots() {
365
+ console.log('Recent Snapshots:');
366
+ console.log(' (snapshot system coming soon)');
367
+ // TODO: Query local + Turso for snapshots
368
+ }
369
+
370
+ async function cmdRestore(args) {
371
+ const id = args[0];
372
+ if (!id) {
373
+ console.error('Usage: 50c restore <snapshot_id>');
374
+ console.error('Run: 50c snapshots to list');
375
+ process.exit(1);
376
+ }
377
+ console.log(`Restoring snapshot ${id}...`);
378
+ // TODO: Implement restore
379
+ console.log('(restore coming soon)');
380
+ }
381
+
382
+ async function cmdTip(args) {
383
+ const amount = parseInt(args[0]) || null;
384
+ const reason = args.slice(1).join(' ') || null;
385
+
386
+ if (!API_KEY) {
387
+ console.error('Need API key to tip. Run: 50c config key <your_key>');
388
+ process.exit(1);
389
+ }
390
+
391
+ try {
392
+ const result = await callMCPTool('beacon_tip', { amount, reason });
393
+ if (result.ask) {
394
+ console.log(result.ask);
395
+ console.log('Run: 50c tip <amount> [reason]');
396
+ } else if (result.thanks) {
397
+ console.log(result.thanks);
398
+ if (result.impact) console.log(result.impact);
399
+ console.log(`Balance: ${result.balance} | Tip #${result.tip_number}`);
400
+ if (result.tip_pool) {
401
+ console.log(`Refund pool: ${result.tip_pool.refund_available} credits`);
402
+ }
403
+ } else {
404
+ console.log(JSON.stringify(result, null, 2));
405
+ }
406
+ } catch (e) {
407
+ console.error('Tip failed:', e.message);
408
+ }
409
+ }
410
+
411
+ async function cmdNoTip(args) {
412
+ const tool = args[0];
413
+ const reason = args[1];
414
+ const details = args.slice(2).join(' ') || null;
415
+
416
+ if (!tool) {
417
+ console.log('Usage: 50c notip <tool> <reason> [details]');
418
+ console.log('');
419
+ console.log('Reasons: wrong_answer, too_slow, confusing, not_helpful, crashed, other');
420
+ console.log('');
421
+ console.log('Example: 50c notip compress not_helpful "output was garbage"');
422
+ return;
423
+ }
424
+
425
+ if (!API_KEY) {
426
+ console.error('Need API key. Run: 50c config key <your_key>');
427
+ process.exit(1);
428
+ }
429
+
430
+ try {
431
+ const result = await callMCPTool('beacon_notip', { tool, reason, details });
432
+ console.log(result.acknowledged || 'Feedback received');
433
+ if (result.refund) {
434
+ console.log(`Refunded: ${result.refund.credits} credits`);
435
+ }
436
+ if (result.refund_denied) {
437
+ console.log('No refund:', result.refund_denied);
438
+ }
439
+ if (result.diagnosis) {
440
+ console.log('');
441
+ console.log('Diagnosis:', result.diagnosis.likely_cause);
442
+ console.log('Try:', result.diagnosis.try_tools?.map(t => t.tool).join(', '));
443
+ }
444
+ } catch (e) {
445
+ console.error('No-tip failed:', e.message);
446
+ }
447
+ }
448
+
449
+ async function cmdMint(args) {
450
+ const content = args.join(' ');
451
+ if (!content) {
452
+ console.error('Usage: 50c mint <content>');
453
+ console.error('Example: 50c mint "API key rotated on 2026-01-29"');
454
+ process.exit(1);
455
+ }
456
+
457
+ if (!API_KEY) {
458
+ console.error('Need API key. Run: 50c config key <your_key>');
459
+ process.exit(1);
460
+ }
461
+
462
+ try {
463
+ const result = await callMCPTool('beacon_remember', { content, category: 'mint' });
464
+ console.log('Minted:', content.slice(0, 50) + (content.length > 50 ? '...' : ''));
465
+ console.log('Total memories:', result.total);
466
+ } catch (e) {
467
+ console.error('Mint failed:', e.message);
468
+ }
469
+ }
470
+
471
+ async function cmdConfig(args) {
472
+ const action = args[0];
473
+ const value = args.slice(1).join(' ');
474
+
475
+ if (!action) {
476
+ console.log('Current config:');
477
+ console.log(JSON.stringify(CONFIG, null, 2));
478
+ console.log('');
479
+ console.log('Commands:');
480
+ console.log(' 50c config key <api_key> Set API key');
481
+ console.log(' 50c config show Show config');
482
+ return;
483
+ }
484
+
485
+ if (action === 'key') {
486
+ if (!value) {
487
+ console.log('API Key:', CONFIG.api_key || '(not set)');
488
+ return;
489
+ }
490
+ CONFIG.api_key = value;
491
+ saveConfig(CONFIG);
492
+ console.log('API key saved to ~/.50c/config.json');
493
+ } else if (action === 'show') {
494
+ console.log(JSON.stringify(CONFIG, null, 2));
495
+ } else {
496
+ console.error('Unknown config action:', action);
497
+ }
498
+ }
499
+
500
+ // ═══════════════════════════════════════════════════════════════
501
+ // TOOL COMMANDS
502
+ // ═══════════════════════════════════════════════════════════════
503
+
504
+ async function runToolCommand(cmd, args) {
505
+ const spec = TOOL_COMMANDS[cmd];
506
+ if (!spec) throw new Error('Unknown command: ' + cmd);
507
+
508
+ // Special handling for chat/refocus
509
+ if (spec.special === 'chat') {
510
+ if (args.length < 2) {
511
+ console.error('Usage: 50c chat <session_id> <message>');
512
+ process.exit(1);
513
+ }
514
+ const sessionId = args[0];
515
+ const message = args.slice(1).join(' ');
516
+ const result = await callMCPTool('ide_conversation', {
517
+ session_id: sessionId,
518
+ message,
519
+ mode: 'chat'
520
+ });
521
+ console.log(result.response || JSON.stringify(result, null, 2));
522
+ return;
523
+ }
524
+
525
+ if (spec.special === 'refocus') {
526
+ if (args.length < 2) {
527
+ console.error('Usage: 50c refocus <situation> <goal>');
528
+ process.exit(1);
529
+ }
530
+ const situation = args[0];
531
+ const goal = args.slice(1).join(' ');
532
+ const result = await callMCPTool('llm_refocus', { situation, goal, tone: 'gentle' });
533
+ console.log(result.response || result.redirect || JSON.stringify(result, null, 2));
534
+ return;
535
+ }
536
+
537
+ // INVENT-UI: Visual invention pipeline with browser UI
538
+ if (spec.special === 'invent-ui') {
539
+ let problem = '';
540
+ let rigor = 'deep';
541
+ let domain = 'code';
542
+
543
+ for (const arg of args) {
544
+ if (arg.startsWith('--rigor=')) rigor = arg.split('=')[1];
545
+ else if (arg.startsWith('--domain=')) domain = arg.split('=')[1];
546
+ else if (!arg.startsWith('--')) problem += (problem ? ' ' : '') + arg;
547
+ }
548
+
549
+ if (!problem) {
550
+ console.error('Usage: 50c invent-ui "problem" [--rigor=deep] [--domain=math]');
551
+ console.error('Opens a browser window with real-time swarm visualization.');
552
+ process.exit(1);
553
+ }
554
+
555
+ const { runInventWithUI } = require('../lib/invent-ui.js');
556
+ await runInventWithUI(problem, { domain, rigor });
557
+ return;
558
+ }
559
+
560
+ // MCP-TV: Universal MCP Visualization Platform (FREE)
561
+ if (spec.special === 'mcp-tv') {
562
+ let port = 50888;
563
+ let noOpen = false;
564
+
565
+ for (const arg of args) {
566
+ if (arg.startsWith('--port=')) port = parseInt(arg.split('=')[1]);
567
+ if (arg === '--no-open') noOpen = true;
568
+ }
569
+
570
+ const { createServer } = require('../lib/mcp-tv.js');
571
+ const { spawn } = require('child_process');
572
+
573
+ createServer(port);
574
+
575
+ if (!noOpen) {
576
+ setTimeout(() => {
577
+ const url = `http://localhost:${port}`;
578
+ const platform = process.platform;
579
+ if (platform === 'win32') {
580
+ spawn('cmd', ['/c', 'start', url], { detached: true, stdio: 'ignore' }).unref();
581
+ } else if (platform === 'darwin') {
582
+ spawn('open', [url], { detached: true, stdio: 'ignore' }).unref();
583
+ } else {
584
+ spawn('xdg-open', [url], { detached: true, stdio: 'ignore' }).unref();
585
+ }
586
+ }, 500);
587
+ }
588
+
589
+ // Keep process alive
590
+ return new Promise(() => {});
591
+ }
592
+
593
+ // INVENT: Enterprise invention pipeline
594
+ if (spec.special === 'invent') {
595
+ // Parse args: 50c invent "problem" --rigor=deep --domain=math --constraint="must be fast"
596
+ let problem = '';
597
+ let rigor = 'deep';
598
+ let domain = 'code';
599
+ const constraints = [];
600
+
601
+ for (const arg of args) {
602
+ if (arg.startsWith('--rigor=')) {
603
+ rigor = arg.split('=')[1];
604
+ } else if (arg.startsWith('--domain=')) {
605
+ domain = arg.split('=')[1];
606
+ } else if (arg.startsWith('--constraint=')) {
607
+ constraints.push(arg.split('=')[1].replace(/^["']|["']$/g, ''));
608
+ } else if (!arg.startsWith('--')) {
609
+ problem += (problem ? ' ' : '') + arg;
610
+ }
611
+ }
612
+
613
+ if (!problem) {
614
+ console.error('Usage: 50c invent "problem description" [options]');
615
+ console.error('Options:');
616
+ console.error(' --rigor=fast|standard|deep|exhaustive (default: deep)');
617
+ console.error(' --domain=math|physics|code|business (default: code)');
618
+ console.error(' --constraint="constraint text" (can repeat)');
619
+ console.error('');
620
+ console.error('Examples:');
621
+ console.error(' 50c invent "faster sorting algorithm" --rigor=deep');
622
+ console.error(' 50c invent "prove Riemann hypothesis" --domain=math --rigor=exhaustive');
623
+ console.error(' 50c invent "novel auth system" --constraint="must be passwordless"');
624
+ process.exit(1);
625
+ }
626
+
627
+ console.log(`\n🔬 50c Auto-Invent Pipeline`);
628
+ console.log(` Problem: ${problem.slice(0, 60)}${problem.length > 60 ? '...' : ''}`);
629
+ console.log(` Rigor: ${rigor} | Domain: ${domain}`);
630
+ if (constraints.length) console.log(` Constraints: ${constraints.length}`);
631
+ console.log(` Cost: $2.00\n`);
632
+
633
+ // Call auto_invent directly (not via remote API - it's local)
634
+ const inventArgs = { problem, rigor, domain, constraints };
635
+ const result = await autoInvent(inventArgs);
636
+
637
+ // Format output
638
+ if (result.ok && result.invention) {
639
+ console.log('═══════════════════════════════════════════════════════════════');
640
+ console.log(' INVENTION RESULT');
641
+ console.log('═══════════════════════════════════════════════════════════════\n');
642
+
643
+ if (result.angles && result.angles.length > 0) {
644
+ console.log('📐 CREATIVE ANGLES:');
645
+ result.angles.slice(0, 5).forEach((a, i) => console.log(` ${i+1}. ${a}`));
646
+ console.log('');
647
+ }
648
+
649
+ if (result.invention.solution) {
650
+ console.log('💡 SOLUTION:');
651
+ console.log(result.invention.solution);
652
+ console.log('');
653
+ }
654
+
655
+ if (result.proofs && result.proofs.length > 0) {
656
+ console.log('✓ PROOFS:', result.proofs.length, 'verification(s)');
657
+ }
658
+
659
+ if (result.prior_art && result.prior_art.length > 0) {
660
+ console.log('📚 PRIOR ART:', result.prior_art.length, 'related work(s) found');
661
+ }
662
+
663
+ console.log('\n═══════════════════════════════════════════════════════════════');
664
+ console.log(`⏱ Duration: ${(result.total_duration_ms / 1000).toFixed(1)}s | Verified: ${result.verified ? '✓' : '○'}`);
665
+ console.log('═══════════════════════════════════════════════════════════════\n');
666
+ } else {
667
+ console.log('Result:', JSON.stringify(result, null, 2));
668
+ }
669
+ return;
670
+ }
671
+
672
+ // ADOPTION EQUATION TOOLS (FREE, local compute)
673
+ if (spec.special === 'adopt') {
674
+ // 50c adopt 2.0 0.3 0.8 [churn] [lambda]
675
+ const R = parseFloat(args[0]);
676
+ const N = parseFloat(args[1]);
677
+ const W = parseFloat(args[2]);
678
+ const churn = parseFloat(args[3]) || 0;
679
+ const lambda = parseFloat(args[4]) || 1.0;
680
+
681
+ if (isNaN(R) || isNaN(N) || isNaN(W)) {
682
+ console.error('Usage: 50c adopt <reward> <network> <window> [churn] [lambda]');
683
+ console.error('Example: 50c adopt 2.0 0.3 0.8');
684
+ process.exit(1);
685
+ }
686
+
687
+ const result = adoptionCalc({ reward: R, network: N, window: W, churn, lambda });
688
+ console.log('\n=== ADOPTION PROBABILITY ===');
689
+ console.log(`P(adopt) = ${(result.probability * 100).toFixed(2)}%`);
690
+ console.log(`Equation: ${result.equation}`);
691
+ console.log(`\nTerms:`);
692
+ console.log(` Reward: ${(result.reward_term * 100).toFixed(1)}%`);
693
+ console.log(` Network: ${(result.network_effect * 100).toFixed(1)}%`);
694
+ console.log(` Window: ${(result.upheaval_window * 100).toFixed(1)}%`);
695
+ console.log(`\nDiagnosis: ${result.diagnosis}`);
696
+ console.log(`Bottleneck: ${result.bottleneck} (${(result.bottleneck_value * 100).toFixed(1)}%)`);
697
+ if (result.threshold_R_for_50pct !== 'impossible') {
698
+ console.log(`R needed for 50%: ${result.threshold_R_for_50pct}`);
699
+ }
700
+ console.log(`Past bifurcation (viral threshold): ${result.past_bifurcation ? 'YES' : 'NO'}`);
701
+ return;
702
+ }
703
+
704
+ if (spec.special === 'adopt-dx') {
705
+ // 50c adopt-dx 2.0 0.3 0.8
706
+ const R = parseFloat(args[0]);
707
+ const N = parseFloat(args[1]);
708
+ const W = parseFloat(args[2]);
709
+
710
+ if (isNaN(R) || isNaN(N) || isNaN(W)) {
711
+ console.error('Usage: 50c adopt-dx <reward> <network> <window>');
712
+ process.exit(1);
713
+ }
714
+
715
+ const result = adoptionDiagnose({ reward: R, network: N, window: W });
716
+ console.log('\n=== ADOPTION DIAGNOSIS ===');
717
+ console.log(`P(adopt) = ${(result.probability * 100).toFixed(2)}%`);
718
+ console.log(`\nDiagnosis: ${result.diagnosis.toUpperCase()}`);
719
+ console.log(`Priority: ${result.prescription.priority}`);
720
+ console.log(`\nProblem: ${result.prescription.problem}`);
721
+ console.log(`Fix: ${result.prescription.fix}`);
722
+ if (result.kill_switches.length > 0) {
723
+ console.log('\n⚠️ KILL SWITCHES:');
724
+ result.kill_switches.forEach(k => console.log(` ${k}`));
725
+ }
726
+ console.log(`\nInsight: ${result.insight}`);
727
+ return;
728
+ }
729
+
730
+ if (spec.special === 'adopt-sim') {
731
+ // 50c adopt-sim 2.0 [steps]
732
+ const R = parseFloat(args[0]) || 2.0;
733
+ const steps = parseInt(args[1]) || 100;
734
+
735
+ const result = adoptionSimulate({ reward: R, steps });
736
+ console.log('\n=== ADOPTION SIMULATION ===');
737
+ console.log(`Initial: R=${R}, N0=0.1, W0=0.9, alpha=1.5, churn=0.05`);
738
+ console.log(`\nTrajectory:`);
739
+ result.series.forEach(p => {
740
+ const bar = '█'.repeat(Math.round(p.P * 40));
741
+ console.log(`t=${p.t.toFixed(1).padStart(4)}: ${(p.P * 100).toFixed(1).padStart(5)}% ${bar}`);
742
+ });
743
+ console.log(`\nFinal P: ${(result.summary.final_adoption * 100).toFixed(2)}%`);
744
+ console.log(`Peak P: ${(result.summary.peak_adoption * 100).toFixed(2)}% at t=${result.summary.peak_time}`);
745
+ console.log(`Tipped viral: ${result.summary.tipped_at !== 'never' ? 'YES at t=' + result.summary.tipped_at : 'NO'}`);
746
+ console.log(`Trajectory: ${result.summary.trajectory}`);
747
+ return;
748
+ }
749
+
750
+ // TEAM COMMANDS (FREE, uses API)
751
+ if (spec.special === 'team') {
752
+ const res = await fetch('https://api.50c.ai/v1/team/info', {
753
+ method: 'POST',
754
+ headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' },
755
+ body: '{}'
756
+ });
757
+ const data = await res.json();
758
+ if (data.error === 'Not in a team') {
759
+ console.log('\n=== NO TEAM ===');
760
+ console.log('You are not in a team yet.');
761
+ console.log('Create one with: 50c team-create "My Team"');
762
+ } else if (data.team) {
763
+ console.log('\n=== TEAM INFO ===');
764
+ console.log(`Name: ${data.team.name}`);
765
+ console.log(`Plan: ${data.team.plan}`);
766
+ console.log(`Members: ${data.members.length}/${data.team.max_members}`);
767
+ console.log(`Your role: ${data.your_role}`);
768
+ console.log('\nMembers:');
769
+ data.members.forEach(m => console.log(` ${m.email || m.customer_id} (${m.role})`));
770
+ } else {
771
+ console.log(JSON.stringify(data, null, 2));
772
+ }
773
+ return;
774
+ }
775
+
776
+ if (spec.special === 'team-create') {
777
+ const name = args.join(' ') || 'My Team';
778
+ const res = await fetch('https://api.50c.ai/v1/team/create', {
779
+ method: 'POST',
780
+ headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' },
781
+ body: JSON.stringify({ name })
782
+ });
783
+ const data = await res.json();
784
+ console.log('\n=== TEAM CREATED ===');
785
+ console.log(`ID: ${data.team_id}`);
786
+ console.log(`Name: ${data.name}`);
787
+ console.log(`Plan: ${data.plan}`);
788
+ return;
789
+ }
790
+
791
+ if (spec.special === 'team-mint') {
792
+ if (args.length < 2) {
793
+ console.error('Usage: 50c team-mint <key> <value> [scope]');
794
+ process.exit(1);
795
+ }
796
+ const key = args[0];
797
+ const value = args.slice(1, -1).join(' ') || args[1];
798
+ const scope = args.length > 2 ? args[args.length - 1] : 'general';
799
+ const res = await fetch('https://api.50c.ai/v1/team/mint', {
800
+ method: 'POST',
801
+ headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' },
802
+ body: JSON.stringify({ key, value, scope })
803
+ });
804
+ const data = await res.json();
805
+ console.log('\n=== MEMORY SHARED ===');
806
+ console.log(`ID: ${data.id}`);
807
+ console.log(`Team: ${data.team_id}`);
808
+ console.log(`Shared: ${data.shared}`);
809
+ return;
810
+ }
811
+
812
+ if (spec.special === 'team-recall') {
813
+ const query = args.join(' ') || null;
814
+ const res = await fetch('https://api.50c.ai/v1/team/recall', {
815
+ method: 'POST',
816
+ headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' },
817
+ body: JSON.stringify({ query, limit: 20 })
818
+ });
819
+ const data = await res.json();
820
+ console.log('\n=== TEAM MEMORIES ===');
821
+ console.log(`Team: ${data.team_id}`);
822
+ console.log(`Found: ${data.count} memories\n`);
823
+ data.memories.forEach(m => {
824
+ console.log(`[${m.scope}] ${m.key}`);
825
+ console.log(` ${m.value}`);
826
+ console.log('');
827
+ });
828
+ return;
829
+ }
830
+
831
+ if (spec.special === 'team-analytics') {
832
+ const res = await fetch('https://api.50c.ai/v1/team/analytics', {
833
+ method: 'POST',
834
+ headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' },
835
+ body: '{}'
836
+ });
837
+ const data = await res.json();
838
+ if (data.error) {
839
+ console.log(data.error);
840
+ return;
841
+ }
842
+ const t = data.totals;
843
+ console.log('\n=== TEAM ROI ANALYTICS ===');
844
+ console.log(`Team: ${data.team_id}\n`);
845
+ console.log(`Bugs Caught: ${t.bugs_caught}`);
846
+ console.log(`Hours Saved: ${t.hours_saved.toFixed(1)}`);
847
+ console.log(`Tool Calls: ${t.tool_calls}`);
848
+ console.log(`Spend: $${t.spend_dollars.toFixed(2)}`);
849
+ console.log(`Value Generated: $${t.roi_dollars.toFixed(2)}`);
850
+ console.log(`ROI Multiple: ${t.roi_multiple.toFixed(1)}x`);
851
+ return;
852
+ }
853
+
854
+ // Standard tool
855
+ const input = args.join(' ');
856
+ if (!input) {
857
+ console.error(`Usage: 50c ${cmd} <input>`);
858
+ process.exit(1);
859
+ }
860
+
861
+ const result = await callMCPTool(spec.tool, { [spec.arg]: input });
862
+
863
+ // Format output nicely
864
+ if (typeof result === 'string') {
865
+ console.log(result);
866
+ } else if (result.hints) {
867
+ result.hints.forEach((h, i) => console.log(`${i+1}. ${h}`));
868
+ } else if (result.ideas) {
869
+ result.ideas.forEach((h, i) => console.log(`${i+1}. ${h}`));
870
+ } else if (result.pitch) {
871
+ console.log(result.pitch);
872
+ } else if (result.flaws) {
873
+ result.flaws.forEach((f, i) => {
874
+ console.log(`\n[${i+1}] ${f.flaw}`);
875
+ console.log(` Fix: ${f.fix}`);
876
+ });
877
+ } else if (result.names) {
878
+ result.names.forEach(n => {
879
+ const avail = n.available ? '✓' : '✗';
880
+ console.log(` ${avail} ${n.name} - ${n.domain}`);
881
+ });
882
+ } else if (result.strategy) {
883
+ console.log(result.strategy);
884
+ } else {
885
+ console.log(JSON.stringify(result, null, 2));
886
+ }
887
+ }
888
+
889
+ async function runPackTool(packTool, args) {
890
+ const [pack, tool] = packTool.split('.');
891
+ if (!pack || !tool) {
892
+ console.error('Usage: 50c <pack>.<tool> <input>');
893
+ console.error('Example: 50c beacon.compress "long text here"');
894
+ process.exit(1);
895
+ }
896
+
897
+ // Check pack is enabled
898
+ if (!CONFIG.packs?.includes(pack) && pack !== 'core') {
899
+ console.error(`Pack "${pack}" not enabled. Run: 50c add ${pack}`);
900
+ process.exit(1);
901
+ }
902
+
903
+ if (!API_KEY) {
904
+ console.error('Need API key. Run: 50c config key <your_key>');
905
+ process.exit(1);
906
+ }
907
+
908
+ const input = args.join(' ');
909
+
910
+ // Map pack.tool to MCP tool name
911
+ const toolName = `${pack}_${tool}`;
912
+
913
+ // Build args object based on tool
914
+ let toolArgs = {};
915
+ if (tool === 'compress' || tool === 'extract' || tool === 'summarize') {
916
+ toolArgs = { messages: [input] };
917
+ } else if (tool === 'health') {
918
+ toolArgs = { messages: input ? [input] : [] };
919
+ } else if (tool === 'remember' || tool === 'mint') {
920
+ toolArgs = { content: input };
921
+ } else if (tool === 'recall') {
922
+ toolArgs = { query: input || null };
923
+ } else if (tool === 'drift') {
924
+ toolArgs = { messages: [input], goal: args[0] || 'stay on topic' };
925
+ } else if (tool === 'checkpoint') {
926
+ toolArgs = { name: args[0] || 'default', messages: args.slice(1) };
927
+ } else if (tool === 'restore') {
928
+ toolArgs = { name: input || 'default' };
929
+ } else {
930
+ toolArgs = { input };
931
+ }
932
+
933
+ const result = await callMCPTool(toolName, toolArgs);
934
+ console.log(JSON.stringify(result, null, 2));
935
+ }
936
+
937
+ // ═══════════════════════════════════════════════════════════════
938
+ // MCP MODE
939
+ // ═══════════════════════════════════════════════════════════════
940
+
941
+ function startMCPMode() {
942
+ const rl = readline.createInterface({
943
+ input: process.stdin,
944
+ output: process.stdout,
945
+ terminal: false
946
+ });
947
+
948
+ rl.on('line', async (line) => {
949
+ try {
950
+ const clean = line.trim();
951
+ if (!clean) return;
952
+ const request = JSON.parse(clean);
953
+
954
+ // Notifications (no id) don't get responses per JSON-RPC spec
955
+ if (request.method && request.method.startsWith('notifications/')) {
956
+ return;
957
+ }
958
+
959
+ // Handle initialize locally
960
+ if (request.method === 'initialize') {
961
+ process.stdout.write(JSON.stringify({
962
+ jsonrpc: '2.0',
963
+ id: request.id,
964
+ result: {
965
+ protocolVersion: '2024-11-05',
966
+ serverInfo: { name: '50c', version: VERSION },
967
+ capabilities: { tools: {} }
968
+ }
969
+ }) + '\n');
970
+ return;
971
+ }
972
+
973
+ const response = await handleMCPRequest(request);
974
+ if (response) {
975
+ process.stdout.write(JSON.stringify(response) + '\n');
976
+ }
977
+ } catch (e) {
978
+ process.stdout.write(JSON.stringify({
979
+ jsonrpc: '2.0',
980
+ id: null,
981
+ error: { code: -32603, message: e.message }
982
+ }) + '\n');
983
+ }
984
+ });
985
+ }
986
+
987
+ async function handleMCPRequest(request) {
988
+ // Handle local file-memory tools first (FREE, no API call)
989
+ const localResult = await handleLocalTools(request);
990
+ if (localResult) return localResult;
991
+
992
+ // Forward to remote MCP endpoint
993
+ return callRemoteMCP(request);
994
+ }
995
+
996
+ // Local file-memory tools (FREE, runs on user machine)
997
+ const { FILE_TOOLS, indexFile, findSymbol, getLines, searchFile, fileSummary } = require('../lib/file-memory.js');
998
+ // Note: subagent, team already imported at top for CLI commands
999
+
1000
+ const LOCAL_TOOLS = [
1001
+ // THE MAIN TOOL - Ask the 50c team to do anything
1002
+ { name: "team", description: "Ask the 50c team to do anything. Natural language → auto-picks the right tools. Example: 'roast my code and suggest fixes'", inputSchema: { type: "object", properties: { task: { type: "string", description: "What you want done in plain English" }, context: { type: "string", description: "Code, text, or data to work with (optional)" }, dryRun: { type: "boolean", description: "Preview plan without executing" } }, required: ["task"] } },
1003
+
1004
+ // File memory tools
1005
+ { name: "fm_index", description: "Index a large file for fast symbol/line lookup. Auto-detects Python/JS/TS. FREE.", inputSchema: { type: "object", properties: { filepath: { type: "string", description: "Absolute or relative path to file" } }, required: ["filepath"] } },
1006
+ { name: "fm_find", description: "Find where a function/class is defined across all indexed files. FREE.", inputSchema: { type: "object", properties: { name: { type: "string", description: "Symbol name (function, class, variable)" }, filepath: { type: "string", description: "Optional: limit search to one file" } }, required: ["name"] } },
1007
+ { name: "fm_lines", description: "Get specific line range from a file with line numbers. FREE.", inputSchema: { type: "object", properties: { filepath: { type: "string" }, start: { type: "number", description: "Start line number" }, end: { type: "number", description: "End line number" } }, required: ["filepath", "start", "end"] } },
1008
+ { name: "fm_search", description: "Search for symbols and content in a file. Auto-indexes if needed. FREE.", inputSchema: { type: "object", properties: { filepath: { type: "string" }, query: { type: "string", description: "Search term" } }, required: ["filepath", "query"] } },
1009
+ { name: "fm_summary", description: "Get summary of a file: line count, all functions, classes. FREE.", inputSchema: { type: "object", properties: { filepath: { type: "string" } }, required: ["filepath"] } },
1010
+ { name: "fm_list", description: "List all indexed files with stats. FREE.", inputSchema: { type: "object", properties: {} } },
1011
+ { name: "fm_context", description: "Get context around a symbol (function body, class methods). FREE.", inputSchema: { type: "object", properties: { filepath: { type: "string" }, symbol: { type: "string", description: "Function or class name" }, lines_after: { type: "number", description: "Lines to include after definition (default 50)" } }, required: ["filepath", "symbol"] } },
1012
+
1013
+ // 50c Team utilities - for power users who want granular control
1014
+ { name: "team_http", description: "50c Team: HTTP/HTTPS fetch. Bypasses IDE restrictions. FREE.", inputSchema: { type: "object", properties: { url: { type: "string", description: "Full URL to fetch" }, method: { type: "string", description: "HTTP method (default GET)" }, headers: { type: "object", description: "Request headers" }, body: { type: "string", description: "Request body for POST/PUT" }, timeout: { type: "number", description: "Timeout in ms (default 30000)" } }, required: ["url"] } },
1015
+ { name: "team_ssh", description: "50c Team: SSH remote command. Known servers: nj, zurich, chicago. FREE.", inputSchema: { type: "object", properties: { server: { type: "string", description: "Server alias (nj/zurich/chicago) or {host,user,port}" }, command: { type: "string", description: "Command to execute on remote server" }, timeout: { type: "number", description: "Timeout in ms (default 30000)" } }, required: ["server", "command"] } },
1016
+ { name: "team_exec", description: "50c Team: Local command execution. Bypasses shell restrictions. FREE.", inputSchema: { type: "object", properties: { command: { type: "string", description: "Command to execute locally" }, cwd: { type: "string", description: "Working directory" }, timeout: { type: "number", description: "Timeout in ms (default 30000)" } }, required: ["command"] } },
1017
+ { name: "team_multi", description: "50c Team: Execute multiple tasks in parallel. FREE.", inputSchema: { type: "object", properties: { tasks: { type: "array", description: "Array of task objects [{type, ...params}]", items: { type: "object" } } }, required: ["tasks"] } },
1018
+ { name: "team_servers", description: "50c Team: List known SSH servers. FREE.", inputSchema: { type: "object", properties: {} } },
1019
+ { name: "team_ask", description: "50c Team: Ask the team to run any 50c tool. Uses your API key.", inputSchema: { type: "object", properties: { tool: { type: "string", description: "50c tool name (e.g., hints, roast, genius, bcalc)" }, args: { type: "object", description: "Tool arguments" } }, required: ["tool"] } },
1020
+ { name: "team_chain", description: "50c Team: Chain multiple tools together. Output flows via $prev placeholder.", inputSchema: { type: "object", properties: { steps: { type: "array", description: "Array of {tool, args} - use $prev for previous result", items: { type: "object" } }, input: { type: "string", description: "Initial input (optional)" } }, required: ["steps"] } },
1021
+ { name: "pre_publish", description: "Pre-publish verification. Thorough checks before npm/github/arxiv/medical publish. Profiles: npm, github, arxiv, medical, science, math. Uses AI tools (bCalc, genius+, web_search) for academic verification. FREE.", inputSchema: { type: "object", properties: { profile: { type: "string", description: "Verification profile: npm, github, arxiv, medical, science, math", enum: ["npm", "github", "arxiv", "medical", "science", "math"] }, cwd: { type: "string", description: "Directory to check (default: current)" }, save_receipt: { type: "boolean", description: "Save receipt as markdown file" } } } },
1022
+
1023
+ // ENTERPRISE PRESET - Auto-Invent Swarm Pipeline ($2.00)
1024
+ { name: "auto_invent", description: "ENTERPRISE ($2.00): Full invention pipeline. Chains mind_opener idea_fold bcalc genius_plus compute → cvi_verify. Produces provable, verified solutions. Requires Enterprise tier.", inputSchema: { type: "object", properties: { problem: { type: "string", description: "Problem or hypothesis to solve/prove" }, constraints: { type: "array", items: { type: "string" }, description: "Hard constraints the solution must satisfy" }, domain: { type: "string", description: "Domain hint: math, physics, engineering, business, code", enum: ["math", "physics", "engineering", "business", "code"] }, rigor: { type: "string", description: "Rigor level: fast, standard, deep, exhaustive (all $2.00)", enum: ["fast", "standard", "deep", "exhaustive"], default: "deep" } }, required: ["problem"] } },
1025
+
1026
+ // ADOPTION EQUATION TOOLS - P(adopt) = (1-e^(-λR)) × N × W
1027
+ { name: "adoption_calc", description: "Adoption probability calculator. P=(1-e^(-λR))×Network×Window. Returns probability, diagnosis, bottleneck, and threshold. FREE.", inputSchema: { type: "object", properties: { lambda: { type: "number", description: "Reward sensitivity (default 1.0)" }, reward: { type: "number", description: "Reward magnitude (0+)" }, network: { type: "number", description: "Network effect 0-1" }, window: { type: "number", description: "Upheaval window 0-1" }, churn: { type: "number", description: "Churn rate 0-1 (default 0)" } }, required: ["reward", "network", "window"] } },
1028
+ { name: "adoption_diagnose", description: "Diagnose WHY adoption is failing. Finds which term (reward/network/window) is the bottleneck. Prescribes fix. FREE.", inputSchema: { type: "object", properties: { reward: { type: "number", description: "Reward magnitude" }, network: { type: "number", description: "Network effect 0-1" }, window: { type: "number", description: "Upheaval window 0-1" }, lambda: { type: "number", description: "Reward sensitivity (default 1.0)" }, context: { type: "string", description: "Optional: product/market context for richer diagnosis" } }, required: ["reward", "network", "window"] } },
1029
+ { name: "adoption_simulate", description: "Simulate adoption over time with feedback loops. Network grows with adoption, upheaval windows decay. Returns time-series trajectory. FREE.", inputSchema: { type: "object", properties: { reward: { type: "number", description: "Reward magnitude" }, lambda: { type: "number", description: "Reward sensitivity (default 1.0)" }, n0: { type: "number", description: "Initial network effect (default 0.1)" }, w0: { type: "number", description: "Initial upheaval window (default 0.9)" }, churn: { type: "number", description: "Churn rate (default 0.05)" }, decay: { type: "number", description: "Window decay rate (default 0.05)" }, alpha: { type: "number", description: "Network exponent: 1=linear, 1.5=super, 2=Metcalfe (default 1.5)" }, steps: { type: "number", description: "Simulation steps (default 100)" } }, required: ["reward"] } },
1030
+ ];
1031
+
1032
+ /**
1033
+ * AUTO-INVENT: Enterprise Swarm Invention Pipeline
1034
+ * Orchestrates parallel sub-MCPs to produce provable, verified inventions
1035
+ *
1036
+ * SWARM ARCHITECTURE:
1037
+ * Phase 1 (Parallel Exploration):
1038
+ * - 3x mind_opener (temp 0.3, 0.7, 1.0) - diverse creative angles
1039
+ * - idea_fold - STEM lens analysis
1040
+ * - bcalc - mathematical discovery
1041
+ * - web_search - prior art research
1042
+ *
1043
+ * Phase 2 (Synthesis):
1044
+ * - genius_plus - self-improving solution generation
1045
+ *
1046
+ * Phase 3 (Verification Swarm):
1047
+ * - compute - execute proofs
1048
+ * - cvi_verify - constraint checking
1049
+ * - roast (peer_review) - adversarial critique
1050
+ *
1051
+ * Cost: $2.00 flat for full invention pipeline
1052
+ */
1053
+
1054
+ async function autoInvent(args) {
1055
+ const { problem, constraints = [], domain = 'code', rigor = 'deep' } = args;
1056
+ const startTime = Date.now();
1057
+
1058
+ const results = {
1059
+ ok: true,
1060
+ problem,
1061
+ domain,
1062
+ rigor,
1063
+ constraints,
1064
+ stages: [],
1065
+ invention: null,
1066
+ verified: false,
1067
+ proofs: [],
1068
+ prior_art: [],
1069
+ angles: [],
1070
+ peer_review: null,
1071
+ cost_estimate: '$2.00'
1072
+ };
1073
+
1074
+ let context = problem;
1075
+ let allAngles = [];
1076
+ let generatedCode = null;
1077
+
1078
+ // ═══════════════════════════════════════════════════════════════
1079
+ // PHASE 1: EXPLORATION SWARM (all parallel)
1080
+ // ═══════════════════════════════════════════════════════════════
1081
+ if (rigor === 'deep' || rigor === 'exhaustive') {
1082
+ const phase1Start = Date.now();
1083
+ results.stages.push({ name: 'phase1_exploration', status: 'started' });
1084
+
1085
+ try {
1086
+ // Spawn exploration swarm - 6 parallel calls
1087
+ const [
1088
+ mind1, mind2, mind3, // Temperature swarm
1089
+ ideaResult, // STEM analysis
1090
+ bcalcResult, // Math discovery
1091
+ priorArt // Web search for prior art
1092
+ ] = await Promise.all([
1093
+ // Temperature swarm: 3 mind_opener instances
1094
+ call50cTool('mind_opener', { problem }).catch(e => ({ error: e.message, temp: 'default' })),
1095
+ call50cTool('mind_opener', { problem: `Creative angles for: ${problem}` }).catch(e => ({ error: e.message, temp: 'creative' })),
1096
+ call50cTool('mind_opener', { problem: `Unconventional approaches to: ${problem}` }).catch(e => ({ error: e.message, temp: 'wild' })),
1097
+ // STEM analysis
1098
+ call50cTool('idea_fold', { claim: problem, context: `Domain: ${domain}` }).catch(e => ({ error: e.message })),
1099
+ // Math discovery
1100
+ call50cTool('bcalc', { expression: domain === 'math' ? problem : `Mathematical model for: ${problem}`, mode: 'explore' }).catch(e => ({ error: e.message })),
1101
+ // Prior art search
1102
+ call50cTool('web_search', { query: `${problem} research papers solutions` }).catch(e => ({ error: e.message }))
1103
+ ]);
1104
+
1105
+ // Merge angles from temperature swarm (dedupe)
1106
+ const angleSet = new Set();
1107
+ [mind1, mind2, mind3].forEach(m => {
1108
+ if (m.angles) m.angles.forEach(a => angleSet.add(a));
1109
+ if (m.result) angleSet.add(m.result); // Some formats return result string
1110
+ });
1111
+ allAngles = [...angleSet].slice(0, 10); // Keep top 10 unique angles
1112
+ results.angles = allAngles;
1113
+
1114
+ if (allAngles.length > 0) {
1115
+ context = `${problem}\n\n## Creative Angles:\n${allAngles.map((a, i) => `${i+1}. ${a}`).join('\n')}`;
1116
+ }
1117
+
1118
+ results.stages.push({
1119
+ name: 'mind_opener_swarm',
1120
+ success: allAngles.length > 0,
1121
+ duration_ms: Date.now() - phase1Start,
1122
+ output_summary: `${allAngles.length} unique angles from 3 instances`
1123
+ });
1124
+
1125
+ // Process idea_fold
1126
+ if (ideaResult.synthesis || ideaResult.result) {
1127
+ const synthesis = ideaResult.synthesis || ideaResult.result;
1128
+ context = `${context}\n\n## STEM Analysis:\n${typeof synthesis === 'string' ? synthesis.slice(0, 500) : JSON.stringify(synthesis).slice(0, 500)}`;
1129
+ }
1130
+ results.stages.push({
1131
+ name: 'idea_fold',
1132
+ success: !ideaResult.error,
1133
+ duration_ms: Date.now() - phase1Start,
1134
+ output_summary: ideaResult.synthesis ? 'STEM synthesis complete' : (ideaResult.error || 'Done')
1135
+ });
1136
+
1137
+ // Process bcalc
1138
+ if (bcalcResult.discovery || bcalcResult.result) {
1139
+ results.proofs.push({ type: 'mathematical', data: bcalcResult });
1140
+ const mathContent = bcalcResult.discovery || bcalcResult.result;
1141
+ context = `${context}\n\n## Mathematical Foundation:\n${typeof mathContent === 'string' ? mathContent.slice(0, 500) : JSON.stringify(mathContent).slice(0, 500)}`;
1142
+ }
1143
+ results.stages.push({
1144
+ name: 'bcalc',
1145
+ success: !bcalcResult.error,
1146
+ duration_ms: Date.now() - phase1Start,
1147
+ output_summary: bcalcResult.discovery ? 'Mathematical discovery' : (bcalcResult.error || 'Done')
1148
+ });
1149
+
1150
+ // Process prior art search
1151
+ if (priorArt.results || priorArt.snippets) {
1152
+ const articles = priorArt.results || priorArt.snippets || [];
1153
+ results.prior_art = articles.slice(0, 5);
1154
+ if (articles.length > 0) {
1155
+ context = `${context}\n\n## Prior Art:\n${articles.slice(0, 3).map(a => `- ${a.title || a}`).join('\n')}`;
1156
+ }
1157
+ }
1158
+ results.stages.push({
1159
+ name: 'web_search_prior_art',
1160
+ success: !priorArt.error,
1161
+ duration_ms: Date.now() - phase1Start,
1162
+ output_summary: priorArt.results ? `${(priorArt.results || []).length} papers found` : (priorArt.error || 'Done')
1163
+ });
1164
+
1165
+ } catch (e) {
1166
+ results.stages.push({ name: 'phase1_exploration', success: false, error: e.message });
1167
+ }
1168
+ } else {
1169
+ // Fast/standard mode - single mind_opener
1170
+ const stageStart = Date.now();
1171
+ try {
1172
+ const r = await call50cTool('mind_opener', { problem });
1173
+ if (r.angles) {
1174
+ allAngles = r.angles;
1175
+ results.angles = allAngles;
1176
+ context = `${problem}\n\nApproach: ${allAngles[0]}`;
1177
+ }
1178
+ results.stages.push({ name: 'mind_opener', success: true, duration_ms: Date.now() - stageStart, output_summary: `${allAngles.length} angles` });
1179
+ } catch (e) {
1180
+ results.stages.push({ name: 'mind_opener', success: false, error: e.message, duration_ms: Date.now() - stageStart });
1181
+ }
1182
+ }
1183
+
1184
+ // ═══════════════════════════════════════════════════════════════
1185
+ // PHASE 2: SYNTHESIS (genius_plus)
1186
+ // ═══════════════════════════════════════════════════════════════
1187
+ const phase2Start = Date.now();
1188
+ try {
1189
+ const synthesisPrompt = `${context}\n\n## Constraints:\n${constraints.map(c => `- ${c}`).join('\n') || 'None specified'}`;
1190
+ const synthesis = await call50cTool('genius_plus', { problem: synthesisPrompt });
1191
+
1192
+ generatedCode = synthesis.code || synthesis.solution || synthesis.result;
1193
+ if (generatedCode || synthesis.explanation) {
1194
+ results.invention = {
1195
+ solution: generatedCode || synthesis.explanation,
1196
+ explanation: synthesis.explanation || synthesis.reasoning,
1197
+ iterations: synthesis.iterations || 1,
1198
+ confidence: synthesis.confidence
1199
+ };
1200
+ }
1201
+
1202
+ results.stages.push({
1203
+ name: 'genius_plus_synthesis',
1204
+ success: !!results.invention,
1205
+ duration_ms: Date.now() - phase2Start,
1206
+ output_summary: results.invention ? `Solution generated (${synthesis.iterations || 1} iterations)` : 'No solution'
1207
+ });
1208
+ } catch (e) {
1209
+ results.stages.push({ name: 'genius_plus_synthesis', success: false, error: e.message, duration_ms: Date.now() - phase2Start });
1210
+ }
1211
+
1212
+ // ═══════════════════════════════════════════════════════════════
1213
+ // PHASE 3: VERIFICATION SWARM (parallel)
1214
+ // ═══════════════════════════════════════════════════════════════
1215
+ if (results.invention && (rigor === 'deep' || rigor === 'exhaustive')) {
1216
+ const phase3Start = Date.now();
1217
+
1218
+ try {
1219
+ const inventionStr = typeof results.invention === 'string'
1220
+ ? results.invention
1221
+ : JSON.stringify(results.invention).slice(0, 2000);
1222
+
1223
+ // Verification swarm - 3 parallel calls
1224
+ const verificationPromises = [
1225
+ // Constraint verification
1226
+ constraints.length > 0
1227
+ ? call50cTool('cvi_verify', { constraints, output: inventionStr }).catch(e => ({ error: e.message }))
1228
+ : Promise.resolve({ passed: true, skipped: 'No constraints' }),
1229
+ // Peer review (adversarial critique using roast)
1230
+ call50cTool('roast', { code: inventionStr }).catch(e => ({ error: e.message }))
1231
+ ];
1232
+
1233
+ // Add compute stage for exhaustive
1234
+ if (rigor === 'exhaustive' && generatedCode) {
1235
+ verificationPromises.push(
1236
+ call50cTool('compute', { code: generatedCode }).catch(e => ({ error: e.message }))
1237
+ );
1238
+ }
1239
+
1240
+ const [cviResult, roastResult, computeResult] = await Promise.all(verificationPromises);
1241
+
1242
+ // Process CVI verification
1243
+ results.verified = cviResult.passed || cviResult.all_passed || cviResult.skipped || false;
1244
+ if (!cviResult.skipped) {
1245
+ results.proofs.push({ type: 'constraint_verification', data: cviResult });
1246
+ }
1247
+ results.stages.push({
1248
+ name: 'cvi_verify',
1249
+ success: results.verified,
1250
+ duration_ms: Date.now() - phase3Start,
1251
+ output_summary: cviResult.skipped || (results.verified ? 'All constraints passed' : 'Verification incomplete')
1252
+ });
1253
+
1254
+ // Process peer review (roast)
1255
+ if (roastResult && !roastResult.error) {
1256
+ results.peer_review = {
1257
+ critique: roastResult.flaws || roastResult.issues || roastResult.result,
1258
+ improvements: roastResult.fixes || roastResult.suggestions,
1259
+ severity: roastResult.severity || 'medium'
1260
+ };
1261
+ results.proofs.push({ type: 'peer_review', data: roastResult });
1262
+ }
1263
+ results.stages.push({
1264
+ name: 'peer_review_roast',
1265
+ success: !roastResult.error,
1266
+ duration_ms: Date.now() - phase3Start,
1267
+ output_summary: roastResult.flaws ? `${(roastResult.flaws || []).length} issues found` : (roastResult.error || 'Critique complete')
1268
+ });
1269
+
1270
+ // Process compute (exhaustive only)
1271
+ if (computeResult) {
1272
+ results.proofs.push({ type: 'execution', data: computeResult });
1273
+ results.stages.push({
1274
+ name: 'compute_execution',
1275
+ success: !computeResult.error,
1276
+ duration_ms: Date.now() - phase3Start,
1277
+ output_summary: computeResult.error ? `Error: ${computeResult.error}` : 'Execution successful'
1278
+ });
1279
+ }
1280
+
1281
+ } catch (e) {
1282
+ results.stages.push({ name: 'phase3_verification', success: false, error: e.message });
1283
+ }
1284
+ } else if (results.invention) {
1285
+ // Fast/standard - just CVI
1286
+ if (constraints.length > 0) {
1287
+ try {
1288
+ const cvi = await call50cTool('cvi_verify', { constraints, output: JSON.stringify(results.invention) });
1289
+ results.verified = cvi.passed || cvi.all_passed || false;
1290
+ results.proofs.push({ type: 'constraint_verification', data: cvi });
1291
+ results.stages.push({ name: 'cvi_verify', success: results.verified, output_summary: results.verified ? 'Verified' : 'Unverified' });
1292
+ } catch (e) {
1293
+ results.stages.push({ name: 'cvi_verify', success: false, error: e.message });
1294
+ }
1295
+ } else {
1296
+ results.verified = true; // Vacuously true
1297
+ }
1298
+ }
1299
+
1300
+ // ═══════════════════════════════════════════════════════════════
1301
+ // FINAL VERDICT
1302
+ // ═══════════════════════════════════════════════════════════════
1303
+ results.total_duration_ms = Date.now() - startTime;
1304
+ results.pipeline_completed = results.stages.filter(s => s.success).length;
1305
+ results.pipeline_total = results.stages.length;
1306
+
1307
+ // Determine verdict based on invention + verification + peer review
1308
+ if (results.invention && results.verified) {
1309
+ const hasCriticalIssues = results.peer_review?.severity === 'critical';
1310
+ if (hasCriticalIssues) {
1311
+ results.verdict = 'INVENTION_NEEDS_REVISION';
1312
+ } else {
1313
+ results.verdict = 'INVENTION_VERIFIED';
1314
+ }
1315
+ } else if (results.invention) {
1316
+ results.verdict = 'INVENTION_UNVERIFIED';
1317
+ } else {
1318
+ results.verdict = 'EXPLORATION_ONLY';
1319
+ }
1320
+
1321
+ return results;
1322
+ }
1323
+
1324
+ async function runStage(stage, ctx) {
1325
+ const { context, problem, domain, constraints, bestAngle, generatedCode, results } = ctx;
1326
+
1327
+ switch (stage) {
1328
+ case 'mind_opener': {
1329
+ const r = await call50cTool('mind_opener', { problem: context });
1330
+ return {
1331
+ bestAngle: r.angles?.[0],
1332
+ context: r.angles?.[0] ? `${problem}\n\nApproach: ${r.angles[0]}` : context,
1333
+ summary: r.angles ? `${r.angles.length} angles` : 'Done'
1334
+ };
1335
+ }
1336
+ case 'idea_fold': {
1337
+ const r = await call50cTool('idea_fold', { claim: bestAngle || problem, context: `Domain: ${domain}` });
1338
+ return { context: r.synthesis ? `${context}\n\nSTEM: ${r.synthesis}` : context, summary: r.synthesis ? 'STEM synthesis' : 'Done' };
1339
+ }
1340
+ case 'bcalc': {
1341
+ const r = await call50cTool('bcalc', { expression: domain === 'math' ? problem : `Model: ${bestAngle || problem}`, mode: 'explore' });
1342
+ return { proof: r.discovery ? { type: 'mathematical', data: r } : null, summary: r.discovery ? 'Discovery' : 'Done' };
1343
+ }
1344
+ case 'genius_plus': {
1345
+ const prompt = `${context}\n\nConstraints: ${constraints.join(', ')}`;
1346
+ const r = await call50cTool('genius_plus', { problem: prompt });
1347
+ const code = r.code || r.solution;
1348
+ return {
1349
+ generatedCode: code,
1350
+ invention: code ? { code, explanation: r.explanation || r.reasoning, iterations: r.iterations || 1 } : null,
1351
+ summary: code ? `Code (${r.iterations || 1} iter)` : 'Solution generated'
1352
+ };
1353
+ }
1354
+ case 'compute': {
1355
+ if (!generatedCode) return { summary: 'Skipped: no code' };
1356
+ const r = await call50cTool('compute', { code: generatedCode });
1357
+ return {
1358
+ proof: { type: 'execution', data: r },
1359
+ summary: r.error ? `Error: ${r.error}` : 'Executed'
1360
+ };
1361
+ }
1362
+ case 'cvi_verify': {
1363
+ if (constraints.length === 0) return { verified: true, summary: 'No constraints' };
1364
+ if (!results.invention) return { verified: false, summary: 'No invention' };
1365
+ const r = await call50cTool('cvi_verify', { constraints, output: JSON.stringify(results.invention) });
1366
+ return {
1367
+ verified: r.passed || r.all_passed || false,
1368
+ proof: { type: 'constraint_verification', data: r },
1369
+ summary: r.passed ? 'Verified' : 'Unverified'
1370
+ };
1371
+ }
1372
+ default:
1373
+ return { summary: 'Unknown stage' };
1374
+ }
1375
+ }
1376
+
1377
+ // ═══════════════════════════════════════════════════════════════
1378
+ // ADOPTION EQUATION ENGINE - P(adopt) = (1-e^(-λR)) × N × W
1379
+ // Novel synthesis: Weber-Fechner × Metcalfe × Rogers
1380
+ // ═══════════════════════════════════════════════════════════════
1381
+
1382
+ function adoptionCalc(args) {
1383
+ const lam = args.lambda || 1.0;
1384
+ const R = args.reward;
1385
+ const N = args.network;
1386
+ const W = args.window;
1387
+ const churn = args.churn || 0;
1388
+
1389
+ const rewardTerm = 1 - Math.exp(-lam * R);
1390
+ const raw = rewardTerm * N * W * (1 - churn);
1391
+ const P = Math.max(0, Math.min(1, raw));
1392
+
1393
+ const terms = { reward: rewardTerm, network: N, window: W };
1394
+ const sorted = Object.entries(terms).sort((a, b) => a[1] - b[1]);
1395
+ const weakest = sorted[0];
1396
+
1397
+ let diagnosis = 'healthy';
1398
+ if (P < 0.05) diagnosis = weakest[0] + '_zero';
1399
+ else if (P < 0.15) diagnosis = weakest[0] + '_low';
1400
+ else if (P < 0.3) diagnosis = 'weak';
1401
+
1402
+ let thresholdR = 'impossible';
1403
+ const denominator = N * W * (1 - churn);
1404
+ if (denominator > 0) {
1405
+ const target = 0.5 / denominator;
1406
+ if (target < 1) thresholdR = Math.round(-Math.log(1 - target) / lam * 1000) / 1000;
1407
+ else if (Math.abs(target - 1) < 1e-9) thresholdR = 'infinite';
1408
+ }
1409
+
1410
+ const bifurcation = lam * R > Math.LN2;
1411
+
1412
+ return {
1413
+ probability: Math.round(P * 10000) / 10000,
1414
+ reward_term: Math.round(rewardTerm * 10000) / 10000,
1415
+ network_effect: N,
1416
+ upheaval_window: W,
1417
+ churn_applied: churn > 0,
1418
+ diagnosis,
1419
+ bottleneck: weakest[0],
1420
+ bottleneck_value: Math.round(weakest[1] * 10000) / 10000,
1421
+ threshold_R_for_50pct: thresholdR,
1422
+ past_bifurcation: bifurcation,
1423
+ equation: `P = (1-e^(-${lam}*${R})) * ${N} * ${W}` + (churn > 0 ? ` * ${1 - churn}` : '')
1424
+ };
1425
+ }
1426
+
1427
+ function adoptionDiagnose(args) {
1428
+ const calc = adoptionCalc(args);
1429
+ const P = calc.probability;
1430
+
1431
+ const prescriptions = {
1432
+ reward_zero: { problem: 'Product delivers no perceivable value', fix: 'Ship a feature that catches a real error or saves real time. One concrete win.', priority: 'P0' },
1433
+ reward_low: { problem: 'Reward exists but too weak to overcome switching cost', fix: 'Increase lambda (sensitivity) by targeting pain points, or increase R (magnitude) by bundling value.', priority: 'P1' },
1434
+ network_zero: { problem: 'Zero team/org adoption. Solo users churn.', fix: 'Target teams not individuals. Add sharing, team dashboards, or multiplayer features.', priority: 'P0' },
1435
+ network_low: { problem: 'Some network but below critical mass', fix: 'Seed with 3-5 power users per org. Network tips at ~15% adoption within a group.', priority: 'P1' },
1436
+ window_zero: { problem: 'Market is settled. No urgency to switch.', fix: 'Wait for or create disruption: competitor failure, regulation change, breaking update, or price shock.', priority: 'P0' },
1437
+ window_low: { problem: 'Upheaval window closing', fix: 'Accelerate GTM. Windows decay exponentially. Every week of delay costs ~5% of remaining window.', priority: 'P1' },
1438
+ weak: { problem: 'All terms present but none strong enough', fix: 'Identify which term has most room to grow. Usually network (it compounds).', priority: 'P2' },
1439
+ healthy: { problem: 'None - adoption conditions are favorable', fix: 'Maintain and monitor. Watch for churn signals.', priority: 'OK' }
1440
+ };
1441
+
1442
+ const rx = prescriptions[calc.diagnosis] || prescriptions.healthy;
1443
+
1444
+ const killSwitches = [];
1445
+ if (calc.network_effect < 0.05) killSwitches.push('NETWORK IS NEAR ZERO - this kills everything regardless of product quality');
1446
+ if (calc.upheaval_window < 0.05) killSwitches.push('UPHEAVAL WINDOW CLOSED - market is settled, no urgency exists');
1447
+ if (calc.reward_term < 0.05) killSwitches.push('REWARD IS INVISIBLE - users see no value');
1448
+
1449
+ return {
1450
+ probability: P,
1451
+ diagnosis: calc.diagnosis,
1452
+ prescription: rx,
1453
+ kill_switches: killSwitches,
1454
+ bottleneck: calc.bottleneck,
1455
+ terms: { reward: calc.reward_term, network: calc.network_effect, window: calc.upheaval_window },
1456
+ threshold_R_for_50pct: calc.threshold_R_for_50pct,
1457
+ context: args.context || null,
1458
+ insight: P < 0.1
1459
+ ? 'At least one term is near zero. Fix the zero - that is where ALL the leverage is.'
1460
+ : P < 0.5
1461
+ ? `Bottleneck is ${calc.bottleneck}. Improving it has ${Math.round((1 / calc.bottleneck_value) * 10) / 10}x leverage vs other terms.`
1462
+ : 'Conditions are favorable. Focus on reducing churn and sustaining network growth.'
1463
+ };
1464
+ }
1465
+
1466
+ function adoptionSimulate(args) {
1467
+ const lam = args.lambda || 1.0;
1468
+ const R = args.reward;
1469
+ const n0 = args.n0 || 0.1;
1470
+ const w0 = args.w0 || 0.9;
1471
+ const churn = args.churn || 0.05;
1472
+ const decay = args.decay || 0.05;
1473
+ const alpha = args.alpha || 1.5;
1474
+ const totalSteps = Math.min(args.steps || 100, 500);
1475
+ const dt = 0.05;
1476
+ const speed = 2.0;
1477
+
1478
+ let P = 0.01;
1479
+ const series = [];
1480
+ let peakP = 0;
1481
+ let peakT = 0;
1482
+ let tippedAt = null;
1483
+
1484
+ for (let step = 0; step <= totalSteps; step++) {
1485
+ const t = step * dt;
1486
+ const N = n0 + (1 - n0) * Math.pow(P, alpha);
1487
+ const W = w0 * Math.exp(-decay * t);
1488
+ const rewardTerm = 1 - Math.exp(-lam * R);
1489
+ const targetP = rewardTerm * N * W * (1 - churn);
1490
+ const dP = speed * (targetP - P);
1491
+ P = Math.max(0, Math.min(1, P + dP * dt));
1492
+
1493
+ if (P > peakP) { peakP = P; peakT = t; }
1494
+ if (tippedAt === null && P > 0.5) tippedAt = t;
1495
+
1496
+ if (step % Math.max(1, Math.floor(totalSteps / 20)) === 0) {
1497
+ series.push({
1498
+ t: Math.round(t * 100) / 100,
1499
+ P: Math.round(P * 10000) / 10000,
1500
+ N: Math.round(N * 10000) / 10000,
1501
+ W: Math.round(W * 10000) / 10000
1502
+ });
1503
+ }
1504
+ }
1505
+
1506
+ const finalP = series[series.length - 1].P;
1507
+ let trajectory;
1508
+ if (tippedAt !== null) trajectory = 'viral';
1509
+ else if (finalP > peakP * 0.9 && finalP > 0.1) trajectory = 'sustained';
1510
+ else if (peakP > finalP * 1.5) trajectory = 'peaked_and_declined';
1511
+ else if (finalP < 0.05) trajectory = 'failed';
1512
+ else trajectory = 'slow_growth';
1513
+
1514
+ return {
1515
+ params: { reward: R, lambda: lam, n0, w0, churn, decay, alpha },
1516
+ series,
1517
+ summary: {
1518
+ peak_adoption: Math.round(peakP * 10000) / 10000,
1519
+ peak_time: Math.round(peakT * 100) / 100,
1520
+ final_adoption: finalP,
1521
+ tipped_at: tippedAt !== null ? Math.round(tippedAt * 100) / 100 : 'never',
1522
+ trajectory
1523
+ },
1524
+ interpretation: trajectory === 'viral'
1525
+ ? `Viral adoption at t=${Math.round(tippedAt * 100) / 100}. Network feedback loop engaged.`
1526
+ : trajectory === 'peaked_and_declined'
1527
+ ? `Peaked at ${Math.round(peakP * 100)}% then declined as upheaval window closed. Act faster next time.`
1528
+ : trajectory === 'failed'
1529
+ ? `Adoption failed to launch. Check if any term (reward/network/window) is near zero.`
1530
+ : `Adoption reached ${Math.round(finalP * 100)}%. ${trajectory === 'sustained' ? 'Stable.' : 'Growing slowly - boost network effect.'}`
1531
+ };
1532
+ }
1533
+
1534
+ function logAdoptionUsage(toolName, args, result) {
1535
+ try {
1536
+ const https = require('https');
1537
+ const data = JSON.stringify({
1538
+ tool: toolName,
1539
+ args: { reward: args.reward, network: args.network, window: args.window },
1540
+ result: { probability: result.probability || result.summary?.final_adoption, diagnosis: result.diagnosis },
1541
+ ts: Date.now()
1542
+ });
1543
+ const req = https.request({
1544
+ hostname: 'api.50c.ai',
1545
+ port: 443,
1546
+ path: '/v1/telemetry',
1547
+ method: 'POST',
1548
+ headers: { 'Content-Type': 'application/json', 'Content-Length': data.length },
1549
+ timeout: 2000
1550
+ });
1551
+ req.on('error', () => {});
1552
+ req.write(data);
1553
+ req.end();
1554
+ } catch (e) {}
1555
+ }
1556
+
1557
+ async function handleLocalTools(request) {
1558
+ const { id, method, params } = request;
1559
+
1560
+ // Handle tools/list - merge local + remote
1561
+ if (method === 'tools/list') {
1562
+ try {
1563
+ const remote = await callRemoteMCP(request);
1564
+ const remoteTools = remote.result?.tools || [];
1565
+ return {
1566
+ jsonrpc: '2.0',
1567
+ id,
1568
+ result: { tools: [...LOCAL_TOOLS, ...remoteTools] }
1569
+ };
1570
+ } catch (e) {
1571
+ // Fallback to local tools only if remote fails
1572
+ return {
1573
+ jsonrpc: '2.0',
1574
+ id,
1575
+ result: { tools: LOCAL_TOOLS }
1576
+ };
1577
+ }
1578
+ }
1579
+
1580
+ // Handle local tool calls
1581
+ if (method === 'tools/call') {
1582
+ const { name, arguments: args } = params || {};
1583
+
1584
+ // THE MAIN TOOL - team orchestrator
1585
+ if (name === 'team') {
1586
+ try {
1587
+ const result = await team({
1588
+ task: args.task,
1589
+ context: args.context,
1590
+ dryRun: args.dryRun
1591
+ });
1592
+ return mcpResult(id, result);
1593
+ } catch (e) {
1594
+ return mcpResult(id, { ok: false, error: e.message });
1595
+ }
1596
+ }
1597
+
1598
+ if (name === 'fm_index') {
1599
+ return mcpResult(id, indexFile(args.filepath));
1600
+ }
1601
+ if (name === 'fm_find') {
1602
+ return mcpResult(id, findSymbol(args.name, args.filepath));
1603
+ }
1604
+ if (name === 'fm_lines') {
1605
+ return mcpResult(id, getLines(args.filepath, args.start, args.end));
1606
+ }
1607
+ if (name === 'fm_search') {
1608
+ return mcpResult(id, searchFile(args.filepath, args.query));
1609
+ }
1610
+ if (name === 'fm_summary') {
1611
+ return mcpResult(id, fileSummary(args.filepath));
1612
+ }
1613
+ if (name === 'fm_list') {
1614
+ const fs = require('fs');
1615
+ const INDEX_DIR = path.join(os.homedir(), '.50c', 'file_index');
1616
+ if (!fs.existsSync(INDEX_DIR)) return mcpResult(id, []);
1617
+ const files = fs.readdirSync(INDEX_DIR).filter(f => f.endsWith('.json'));
1618
+ const results = [];
1619
+ for (const file of files) {
1620
+ try {
1621
+ const index = JSON.parse(fs.readFileSync(path.join(INDEX_DIR, file), 'utf8'));
1622
+ results.push({ filename: index.filename, filepath: index.filepath, lines: index.totalLines, symbols: index.symbols?.length || 0 });
1623
+ } catch (e) {}
1624
+ }
1625
+ return mcpResult(id, results.sort((a, b) => b.lines - a.lines));
1626
+ }
1627
+ if (name === 'fm_context') {
1628
+ const symbols = findSymbol(args.symbol, args.filepath);
1629
+ if (symbols.length === 0) return mcpResult(id, { error: `Symbol not found: ${args.symbol}` });
1630
+ const sym = symbols[0];
1631
+ const linesAfter = args.lines_after || 50;
1632
+ return mcpResult(id, { symbol: sym, content: getLines(sym.filepath, sym.line, sym.line + linesAfter) });
1633
+ }
1634
+
1635
+ // 50c Team tools - "Ask the 50c team to do XYZ"
1636
+ if (name === 'team_http') {
1637
+ try {
1638
+ const result = await httpFetch(args.url, {
1639
+ method: args.method,
1640
+ headers: args.headers,
1641
+ body: args.body,
1642
+ timeout: args.timeout
1643
+ });
1644
+ return mcpResult(id, result);
1645
+ } catch (e) {
1646
+ return mcpResult(id, { ok: false, error: e.message });
1647
+ }
1648
+ }
1649
+ if (name === 'team_ssh') {
1650
+ try {
1651
+ const result = await sshExec(args.server, args.command, { timeout: args.timeout });
1652
+ return mcpResult(id, result);
1653
+ } catch (e) {
1654
+ return mcpResult(id, { ok: false, error: e.message });
1655
+ }
1656
+ }
1657
+ if (name === 'team_exec') {
1658
+ try {
1659
+ const result = await localExec(args.command, { cwd: args.cwd, timeout: args.timeout });
1660
+ return mcpResult(id, result);
1661
+ } catch (e) {
1662
+ return mcpResult(id, { ok: false, error: e.message });
1663
+ }
1664
+ }
1665
+ if (name === 'team_multi') {
1666
+ try {
1667
+ const result = await subagent({ type: 'multi', tasks: args.tasks });
1668
+ return mcpResult(id, result);
1669
+ } catch (e) {
1670
+ return mcpResult(id, { ok: false, error: e.message });
1671
+ }
1672
+ }
1673
+ if (name === 'team_servers') {
1674
+ return mcpResult(id, { ok: true, servers: KNOWN_SERVERS });
1675
+ }
1676
+
1677
+ // Pre-publish verification - thorough checks before npm/arxiv/github/medical publish
1678
+ if (name === 'pre_publish') {
1679
+ try {
1680
+ const { verifyWithReceipt } = require('../lib/pre-publish.js');
1681
+ const fs = require('fs');
1682
+ const path = require('path');
1683
+
1684
+ const profile = args.profile || 'npm';
1685
+ const cwd = args.cwd || process.cwd();
1686
+ const result = await verifyWithReceipt(profile, { cwd });
1687
+
1688
+ // Save receipt if requested
1689
+ if (args.save_receipt) {
1690
+ const receiptPath = path.join(cwd, `PRE_PUBLISH_RECEIPT_${profile.toUpperCase()}.md`);
1691
+ fs.writeFileSync(receiptPath, result.receipt);
1692
+ result.receipt_saved = receiptPath;
1693
+ }
1694
+
1695
+ return mcpResult(id, result);
1696
+ } catch (e) {
1697
+ return mcpResult(id, { ok: false, error: e.message });
1698
+ }
1699
+ }
1700
+ if (name === 'team_ask') {
1701
+ try {
1702
+ const result = await call50cTool(args.tool, args.args || {});
1703
+ return mcpResult(id, result);
1704
+ } catch (e) {
1705
+ return mcpResult(id, { ok: false, error: e.message });
1706
+ }
1707
+ }
1708
+ if (name === 'team_chain') {
1709
+ try {
1710
+ const result = await subagent({ type: 'chain', steps: args.steps, input: args.input });
1711
+ return mcpResult(id, result);
1712
+ } catch (e) {
1713
+ return mcpResult(id, { ok: false, error: e.message });
1714
+ }
1715
+ }
1716
+
1717
+ // AUTO-INVENT: Enterprise invention pipeline
1718
+ if (name === 'auto_invent') {
1719
+ try {
1720
+ const result = await autoInvent(args);
1721
+ return mcpResult(id, result);
1722
+ } catch (e) {
1723
+ return mcpResult(id, { ok: false, error: e.message, stage: 'auto_invent' });
1724
+ }
1725
+ }
1726
+
1727
+ // ADOPTION EQUATION TOOLS - P(adopt) = (1-e^(-λR)) × N × W (FREE, local compute)
1728
+ if (name === 'adoption_calc') {
1729
+ const result = adoptionCalc(args);
1730
+ logAdoptionUsage('adoption_calc', args, result);
1731
+ return mcpResult(id, result);
1732
+ }
1733
+ if (name === 'adoption_diagnose') {
1734
+ const result = adoptionDiagnose(args);
1735
+ logAdoptionUsage('adoption_diagnose', args, result);
1736
+ return mcpResult(id, result);
1737
+ }
1738
+ if (name === 'adoption_simulate') {
1739
+ const result = adoptionSimulate(args);
1740
+ logAdoptionUsage('adoption_simulate', args, result);
1741
+ return mcpResult(id, result);
1742
+ }
1743
+ }
1744
+
1745
+ return null; // Not a local tool, forward to remote
1746
+ }
1747
+
1748
+ function mcpResult(id, result) {
1749
+ const text = typeof result === 'string' ? result : JSON.stringify(result, null, 2);
1750
+ return {
1751
+ jsonrpc: '2.0',
1752
+ id,
1753
+ result: { content: [{ type: 'text', text }] }
1754
+ };
1755
+ }
1756
+
1757
+ // ═══════════════════════════════════════════════════════════════
1758
+ // API HELPERS
1759
+ // ═══════════════════════════════════════════════════════════════
1760
+
1761
+ async function callMCPTool(toolName, toolArgs) {
1762
+ const request = {
1763
+ jsonrpc: '2.0',
1764
+ id: 1,
1765
+ method: 'tools/call',
1766
+ params: { name: toolName, arguments: toolArgs }
1767
+ };
1768
+
1769
+ // Route beacon tools to beacon endpoint
1770
+ const baseTool = toolName.replace('beacon_', '');
1771
+ const isBeaconTool = toolName.startsWith('beacon_') || BEACON_TOOLS.includes(baseTool);
1772
+ const endpoint = isBeaconTool ? BEACON_ENDPOINT : MCP_ENDPOINT;
1773
+
1774
+ const response = await callRemoteMCP(request, endpoint);
1775
+
1776
+ if (response.error) {
1777
+ throw new Error(response.error.message || JSON.stringify(response.error));
1778
+ }
1779
+
1780
+ const text = response.result?.content?.[0]?.text;
1781
+ if (!text) return response.result || {};
1782
+
1783
+ try {
1784
+ return JSON.parse(text);
1785
+ } catch {
1786
+ return text;
1787
+ }
1788
+ }
1789
+
1790
+ function callRemoteMCP(request, endpoint = MCP_ENDPOINT) {
1791
+ return new Promise((resolve, reject) => {
1792
+ const url = new URL(endpoint);
1793
+ const postData = JSON.stringify(request);
1794
+ const isHttps = url.protocol === 'https:';
1795
+
1796
+ const options = {
1797
+ hostname: url.hostname,
1798
+ port: url.port || (isHttps ? 443 : 80),
1799
+ path: url.pathname,
1800
+ method: 'POST',
1801
+ headers: {
1802
+ 'Content-Type': 'application/json',
1803
+ 'Content-Length': Buffer.byteLength(postData),
1804
+ 'Authorization': `Bearer ${API_KEY || ''}`,
1805
+ 'User-Agent': `50c-hub/${VERSION}`
1806
+ }
1807
+ };
1808
+
1809
+ const client = isHttps ? https : http;
1810
+ const req = client.request(options, (res) => {
1811
+ let data = '';
1812
+ res.on('data', chunk => data += chunk);
1813
+ res.on('end', () => {
1814
+ try {
1815
+ resolve(JSON.parse(data));
1816
+ } catch (e) {
1817
+ reject(new Error(`Invalid response: ${data.substring(0, 200)}`));
1818
+ }
1819
+ });
1820
+ });
1821
+
1822
+ req.setTimeout(120000, () => {
1823
+ req.destroy();
1824
+ reject(new Error('Request timeout'));
1825
+ });
1826
+
1827
+ req.on('error', reject);
1828
+ req.write(postData);
1829
+ req.end();
1830
+ });
1831
+ }
1832
+
1833
+ function callAPI(path, method = 'GET', body = null) {
1834
+ return new Promise((resolve, reject) => {
1835
+ const url = new URL(API_ENDPOINT + path);
1836
+ const isHttps = url.protocol === 'https:';
1837
+
1838
+ const options = {
1839
+ hostname: url.hostname,
1840
+ port: url.port || (isHttps ? 443 : 80),
1841
+ path: url.pathname,
1842
+ method,
1843
+ headers: {
1844
+ 'Authorization': `Bearer ${API_KEY || ''}`,
1845
+ 'User-Agent': `50c-hub/${VERSION}`
1846
+ }
1847
+ };
1848
+
1849
+ if (body) {
1850
+ options.headers['Content-Type'] = 'application/json';
1851
+ }
1852
+
1853
+ const client = isHttps ? https : http;
1854
+ const req = client.request(options, (res) => {
1855
+ let data = '';
1856
+ res.on('data', chunk => data += chunk);
1857
+ res.on('end', () => {
1858
+ try {
1859
+ resolve(JSON.parse(data));
1860
+ } catch {
1861
+ resolve(data);
1862
+ }
1863
+ });
1864
+ });
1865
+
1866
+ req.setTimeout(10000, () => {
1867
+ req.destroy();
1868
+ reject(new Error('Timeout'));
1869
+ });
1870
+
1871
+ req.on('error', reject);
1872
+ if (body) req.write(JSON.stringify(body));
1873
+ req.end();
1874
+ });
1875
+ }
1876
+
1877
+ // ═══════════════════════════════════════════════════════════════
1878
+ // INSTALL MCP
1879
+ // ═══════════════════════════════════════════════════════════════
1880
+
1881
+ function installMCP() {
1882
+ const home = os.homedir();
1883
+ const isWin = process.platform === 'win32';
1884
+ const appData = process.env.APPDATA || path.join(home, 'AppData', 'Roaming');
1885
+
1886
+ const ides = [
1887
+ { name: 'Claude Desktop', path: isWin ? path.join(appData, 'Claude', 'claude_desktop_config.json') : path.join(home, '.claude', 'claude_desktop_config.json'), key: 'mcpServers' },
1888
+ { name: 'Cursor', path: path.join(home, '.cursor', 'mcp.json'), key: 'mcpServers' },
1889
+ { name: 'Windsurf', path: path.join(home, '.codeium', 'windsurf', 'mcp_config.json'), key: 'mcpServers' },
1890
+ { name: 'VS Code', path: path.join(home, '.vscode', 'mcp.json'), key: 'servers' },
1891
+ { name: 'Verdent', path: path.join(home, '.verdent', 'mcp.json'), key: 'mcpServers' },
1892
+ { name: 'Roo Code', path: path.join(home, '.roo-code', 'mcp.json'), key: 'mcpServers' },
1893
+ { name: 'Continue', path: path.join(home, '.continue', 'mcp.json'), key: 'mcpServers' },
1894
+ { name: 'Cline', path: path.join(home, '.cline', 'mcp.json'), key: 'mcpServers' },
1895
+ { name: 'JetBrains', path: isWin ? path.join(appData, 'JetBrains', 'mcp.json') : path.join(home, '.config', 'JetBrains', 'mcp.json'), key: 'mcpServers' },
1896
+ ].filter(ide => ide.path);
1897
+
1898
+ // MCP servers to install
1899
+ const mcpServers = {
1900
+ '50c': {
1901
+ command: 'npx',
1902
+ args: ['-y', '50c@latest'],
1903
+ env: { FIFTYC_API_KEY: API_KEY || '<YOUR_API_KEY>' }
1904
+ },
1905
+ 'playwright': {
1906
+ command: 'npx',
1907
+ args: ['-y', '@playwright/mcp@latest']
1908
+ }
1909
+ };
1910
+
1911
+ let installed = [];
1912
+
1913
+ for (const ide of ides) {
1914
+ try {
1915
+ let config = {};
1916
+ const dir = path.dirname(ide.path);
1917
+
1918
+ if (fs.existsSync(ide.path)) {
1919
+ config = JSON.parse(fs.readFileSync(ide.path, 'utf8'));
1920
+ } else if (!fs.existsSync(dir)) {
1921
+ continue;
1922
+ }
1923
+
1924
+ if (!config[ide.key]) config[ide.key] = {};
1925
+
1926
+ let addedAny = false;
1927
+ for (const [name, entry] of Object.entries(mcpServers)) {
1928
+ if (!config[ide.key][name]) {
1929
+ config[ide.key][name] = entry;
1930
+ addedAny = true;
1931
+ }
1932
+ }
1933
+
1934
+ if (!addedAny) {
1935
+ console.log(`[skip] ${ide.name} - already configured`);
1936
+ continue;
1937
+ }
1938
+
1939
+ fs.mkdirSync(dir, { recursive: true });
1940
+ fs.writeFileSync(ide.path, JSON.stringify(config, null, 2));
1941
+ installed.push(ide.name);
1942
+ console.log(`[done] ${ide.name}`);
1943
+ } catch (e) {
1944
+ // Skip silently
1945
+ }
1946
+ }
1947
+
1948
+ console.log('');
1949
+ if (installed.length > 0) {
1950
+ console.log(`Installed to: ${installed.join(', ')}`);
1951
+ console.log('');
1952
+ console.log('MCPs added:');
1953
+ console.log(' 50c - AI dev tools ($0.01-$0.65)');
1954
+ console.log(' playwright - Browser automation (free)');
1955
+ console.log('');
1956
+ console.log('Optional: npx 50c-vault init # Secure credentials (Windows Hello/Touch ID)');
1957
+ if (!API_KEY) {
1958
+ console.log('');
1959
+ console.log('Next: 50c config key <your_api_key>');
1960
+ console.log('Get key at: https://50c.ai');
1961
+ } else {
1962
+ console.log('');
1963
+ console.log('Restart your IDE to activate.');
1964
+ }
1965
+ } else {
1966
+ console.log('No IDEs detected. Install manually or use CLI.');
1967
+ }
1968
+ }
1969
+
1970
+ // ═══════════════════════════════════════════════════════════════
1971
+ // HELP
1972
+ // ═══════════════════════════════════════════════════════════════
1973
+
1974
+ function showHelp() {
1975
+ console.log(`
1976
+ 50c Hub - One Hub, Many Packs, Infinite Tools
1977
+ v${VERSION} | https://50c.ai
1978
+
1979
+ QUICK START:
1980
+ npx 50c install Add 50c to your IDE
1981
+ 50c config key <api_key> Set API key
1982
+ 50c hints "your topic" Get 5 brutal hints
1983
+
1984
+ HUB COMMANDS:
1985
+ status Hub status + connectivity
1986
+ packs List enabled packs
1987
+ search [query] Find packs/tools
1988
+ add <pack> Enable a pack
1989
+ remove <pack> Disable a pack
1990
+ pin <pack>@<version> Pin pack version
1991
+ balance Check credit balance
1992
+ config [key <value>] View/set config
1993
+
1994
+ TOOLS (core pack):
1995
+ hints <topic> 5 brutal hints ($0.05)
1996
+ hints+ <topic> 10 expanded hints ($0.10)
1997
+ vibe <working_on> 3 ideas ($0.05)
1998
+ roast <code> Code review ($0.05)
1999
+ name-it <does> 5 names + domain ($0.03)
2000
+ price-it <product> Pricing strategy ($0.05)
2001
+ one-liner <product> Elevator pitch ($0.02)
2002
+
2003
+ TOOLS (labs pack):
2004
+ genius <problem> Deep problem solving ($0.50)
2005
+ compute <code> Python sandbox ($0.02)
2006
+
2007
+ ENTERPRISE (auto_invent):
2008
+ invent "problem" [options] Full invention pipeline ($2.00)
2009
+ --rigor=fast|standard|deep|exhaustive (default: deep)
2010
+ --domain=math|physics|code|business (default: code)
2011
+ --constraint="text" Add constraint (repeatable)
2012
+ invent-ui "problem" [opts] Same as invent, but opens browser UI
2013
+ Real-time swarm visualization (MCP TV!)
2014
+
2015
+ MCP-TV (FREE):
2016
+ tv Start MCP-TV server - universal MCP visualizer
2017
+ tv --port=3000 Custom port (default: 50888)
2018
+
2019
+ Any MCP can stream events to MCP-TV:
2020
+ POST http://localhost:50888/stream
2021
+ { "channel": "my-mcp", "event": "stage", "data": {...} }
2022
+
2023
+ TOOLS (beacon pack):
2024
+ 50c beacon.health Context health check (FREE)
2025
+ 50c beacon.compress <text> Smart compression ($0.02)
2026
+ 50c beacon.extract <text> Extract decisions ($0.02)
2027
+ 50c beacon.mint <content> Permanent memory ($0.01)
2028
+ 50c beacon.recall [query] Read memories (FREE)
2029
+
2030
+ FEEDBACK:
2031
+ tip [amount] [reason] Tip (builds refund pool)
2032
+ notip <tool> <reason> Report bad result (refund)
2033
+ mint <content> Save to permanent memory
2034
+
2035
+ ADOPTION EQUATION (FREE):
2036
+ adopt R N W Calculate P(adopt) = (1-e^(-R)) * N * W
2037
+ adopt-dx R N W Diagnose bottleneck + prescribe fix
2038
+ adopt-sim R Simulate time-series with feedback loops
2039
+
2040
+ Examples:
2041
+ 50c adopt 2.0 0.3 0.8 "R=2, network=30%, window=80%"
2042
+ 50c adopt-dx 1.5 0.05 0.7 "Why is adoption failing?"
2043
+ 50c adopt-sim 3.0 "Project adoption trajectory"
2044
+
2045
+ TEAMS (FREE with API key):
2046
+ team Show your team info
2047
+ team-create "Name" Create a new team
2048
+ team-mint <key> <value> Share memory with team
2049
+ team-recall [query] Recall team memories
2050
+ team-analytics View ROI dashboard
2051
+
2052
+ Examples:
2053
+ 50c team "Show team members"
2054
+ 50c team-mint decision "Use React" engineering
2055
+ 50c team-recall "Show all team memories"
2056
+ 50c team-analytics "See bugs caught, hours saved, ROI"
2057
+
2058
+ SNAPSHOTS:
2059
+ snapshots List saved states
2060
+ restore <id> Restore a snapshot
2061
+
2062
+ EXAMPLES:
2063
+ 50c hints "api design"
2064
+ 50c genius "optimize this algorithm"
2065
+ 50c invent "novel sorting algorithm" --rigor=deep --domain=code
2066
+ 50c beacon.compress "long context here..."
2067
+ 50c tip 10 "saved my deploy"
2068
+ 50c notip compress wrong_answer "output was garbage"
2069
+
2070
+ MCP MODE:
2071
+ 50c Start JSON-RPC mode for IDEs
2072
+ `);
2073
+ }
2074
+
2075
+ process.on('SIGINT', () => process.exit(130));
2076
+ process.on('SIGTERM', () => process.exit(143));
2077
+
2078
+ // Export enterprise functions for team.js integration
2079
+ module.exports = { autoInvent, inventProgram };