50c 3.9.4 → 3.9.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/50c.js CHANGED
@@ -1,2226 +1,2284 @@
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: "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
- // Security audit tool - runs locally, FREE
1024
- { name: "backdoor_check", description: "Security audit: check 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
- // ENTERPRISE PRESET - Auto-Invent Swarm Pipeline ($2.00)
1027
- { 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"] } },
1028
-
1029
- // PROGRAMMATIC INVENTION - JSON-defined executable pipeline (ENTERPRISE $2.00)
1030
- { 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"] } },
1031
-
1032
- // ADOPTION EQUATION TOOLS - P(adopt) = (1-e^(-λR)) × N × W
1033
- { 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"] } },
1034
- { 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"] } },
1035
- { 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"] } },
1036
- ];
1037
-
1038
- /**
1039
- * AUTO-INVENT: Enterprise Swarm Invention Pipeline
1040
- * Orchestrates parallel sub-MCPs to produce provable, verified inventions
1041
- *
1042
- * SWARM ARCHITECTURE:
1043
- * Phase 1 (Parallel Exploration):
1044
- * - 3x mind_opener (temp 0.3, 0.7, 1.0) - diverse creative angles
1045
- * - idea_fold - STEM lens analysis
1046
- * - bcalc - mathematical discovery
1047
- * - web_search - prior art research
1048
- *
1049
- * Phase 2 (Synthesis):
1050
- * - genius_plus - self-improving solution generation
1051
- *
1052
- * Phase 3 (Verification Swarm):
1053
- * - compute - execute proofs
1054
- * - cvi_verify - constraint checking
1055
- * - roast (peer_review) - adversarial critique
1056
- *
1057
- * Cost: $2.00 flat for full invention pipeline
1058
- */
1059
-
1060
- /**
1061
- * INVENT_PROGRAM: JSON-defined Executable Invention Pipeline
1062
- * Enterprise-only ($2.00/request) - programmatic multi-step invention
1063
- *
1064
- * Actions:
1065
- * - generate: Create data/variables (Python code in compute sandbox)
1066
- * - compute: Calculate/transform (Python code)
1067
- * - assert: Verify condition (must return truthy or throws)
1068
- * - call: Invoke a 50c tool (genius, bcalc, hints, etc.)
1069
- * - return: Final output
1070
- *
1071
- * GATED: Requires Enterprise tier + $2.00 balance
1072
- */
1073
- async function inventProgram(args) {
1074
- const { program } = args;
1075
- const startTime = Date.now();
1076
-
1077
- if (!program || !program.problem || !program.steps) {
1078
- return { ok: false, error: 'Invalid program: must have "problem" and "steps" array' };
1079
- }
1080
-
1081
- const results = {
1082
- ok: true,
1083
- problem: program.problem,
1084
- steps_completed: 0,
1085
- steps_total: program.steps.length,
1086
- outputs: {},
1087
- assertions: [],
1088
- tool_calls: [],
1089
- final_result: null,
1090
- cost: '$2.00',
1091
- tier_required: 'enterprise'
1092
- };
1093
-
1094
- // Build execution context for Python code
1095
- let pythonContext = `
1096
- import json, math, re, itertools, functools, collections
1097
- from decimal import Decimal, getcontext
1098
- getcontext().prec = 100
1099
-
1100
- # Shared context between steps
1101
- _ctx = {}
1102
- _results = {}
1103
-
1104
- `;
1105
-
1106
- // Execute steps sequentially
1107
- for (const step of program.steps) {
1108
- const stepStart = Date.now();
1109
-
1110
- try {
1111
- if (step.action === 'generate' || step.action === 'compute') {
1112
- // Execute Python code in compute sandbox
1113
- pythonContext += `\n# Step: ${step.id}\n${step.code}\n_results['${step.id}'] = locals().get('result', None)\n`;
1114
-
1115
- } else if (step.action === 'assert') {
1116
- // Add assertion check
1117
- 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`;
1118
- results.assertions.push({ id: step.id, code: step.code });
1119
-
1120
- } else if (step.action === 'call') {
1121
- // Call a 50c tool
1122
- const toolResult = await call50cTool(step.tool, step.args || {});
1123
- results.tool_calls.push({ id: step.id, tool: step.tool, result: toolResult });
1124
- results.outputs[step.id] = toolResult;
1125
- // Inject result into Python context
1126
- pythonContext += `\n# Tool result: ${step.id}\n_results['${step.id}'] = ${JSON.stringify(toolResult)}\n`;
1127
-
1128
- } else if (step.action === 'return') {
1129
- // Final return - will be evaluated after all code runs
1130
- pythonContext += `\n# Final output\n_final = ${step.code}\nprint('__FINAL__:' + json.dumps(_final))\n`;
1131
- }
1132
-
1133
- results.steps_completed++;
1134
-
1135
- } catch (err) {
1136
- results.ok = false;
1137
- results.error = `Step ${step.id} failed: ${err.message}`;
1138
- break;
1139
- }
1140
- }
1141
-
1142
- // Execute all Python code in one compute call
1143
- if (results.ok) {
1144
- pythonContext += `\n# Output all results\nprint('__RESULTS__:' + json.dumps(_results))\n`;
1145
-
1146
- try {
1147
- const computeResult = await call50cTool('compute', { code: pythonContext });
1148
-
1149
- // Parse outputs
1150
- if (computeResult && typeof computeResult === 'string') {
1151
- const finalMatch = computeResult.match(/__FINAL__:(.+)/);
1152
- if (finalMatch) {
1153
- try {
1154
- results.final_result = JSON.parse(finalMatch[1]);
1155
- } catch (e) {
1156
- results.final_result = finalMatch[1];
1157
- }
1158
- }
1159
-
1160
- const resultsMatch = computeResult.match(/__RESULTS__:(.+)/);
1161
- if (resultsMatch) {
1162
- try {
1163
- results.outputs = { ...results.outputs, ...JSON.parse(resultsMatch[1]) };
1164
- } catch (e) {}
1165
- }
1166
- }
1167
-
1168
- results.compute_output = computeResult;
1169
-
1170
- } catch (err) {
1171
- results.ok = false;
1172
- results.error = `Compute execution failed: ${err.message}`;
1173
- }
1174
- }
1175
-
1176
- results.duration_ms = Date.now() - startTime;
1177
- return results;
1178
- }
1179
-
1180
- async function autoInvent(args) {
1181
- const { problem, constraints = [], domain = 'code', rigor = 'deep' } = args;
1182
- const startTime = Date.now();
1183
-
1184
- const results = {
1185
- ok: true,
1186
- problem,
1187
- domain,
1188
- rigor,
1189
- constraints,
1190
- stages: [],
1191
- invention: null,
1192
- verified: false,
1193
- proofs: [],
1194
- prior_art: [],
1195
- angles: [],
1196
- peer_review: null,
1197
- cost_estimate: '$2.00'
1198
- };
1199
-
1200
- let context = problem;
1201
- let allAngles = [];
1202
- let generatedCode = null;
1203
-
1204
- // ═══════════════════════════════════════════════════════════════
1205
- // PHASE 1: EXPLORATION SWARM (all parallel)
1206
- // ═══════════════════════════════════════════════════════════════
1207
- if (rigor === 'deep' || rigor === 'exhaustive') {
1208
- const phase1Start = Date.now();
1209
- results.stages.push({ name: 'phase1_exploration', status: 'started' });
1210
-
1211
- try {
1212
- // Spawn exploration swarm - 6 parallel calls
1213
- const [
1214
- mind1, mind2, mind3, // Temperature swarm
1215
- ideaResult, // STEM analysis
1216
- bcalcResult, // Math discovery
1217
- priorArt // Web search for prior art
1218
- ] = await Promise.all([
1219
- // Temperature swarm: 3 mind_opener instances
1220
- call50cTool('mind_opener', { problem }).catch(e => ({ error: e.message, temp: 'default' })),
1221
- call50cTool('mind_opener', { problem: `Creative angles for: ${problem}` }).catch(e => ({ error: e.message, temp: 'creative' })),
1222
- call50cTool('mind_opener', { problem: `Unconventional approaches to: ${problem}` }).catch(e => ({ error: e.message, temp: 'wild' })),
1223
- // STEM analysis
1224
- call50cTool('idea_fold', { claim: problem, context: `Domain: ${domain}` }).catch(e => ({ error: e.message })),
1225
- // Math discovery
1226
- call50cTool('bcalc', { expression: domain === 'math' ? problem : `Mathematical model for: ${problem}`, mode: 'explore' }).catch(e => ({ error: e.message })),
1227
- // Prior art search
1228
- call50cTool('web_search', { query: `${problem} research papers solutions` }).catch(e => ({ error: e.message }))
1229
- ]);
1230
-
1231
- // Merge angles from temperature swarm (dedupe)
1232
- const angleSet = new Set();
1233
- [mind1, mind2, mind3].forEach(m => {
1234
- if (m.angles) m.angles.forEach(a => angleSet.add(a));
1235
- if (m.result) angleSet.add(m.result); // Some formats return result string
1236
- });
1237
- allAngles = [...angleSet].slice(0, 10); // Keep top 10 unique angles
1238
- results.angles = allAngles;
1239
-
1240
- if (allAngles.length > 0) {
1241
- context = `${problem}\n\n## Creative Angles:\n${allAngles.map((a, i) => `${i+1}. ${a}`).join('\n')}`;
1242
- }
1243
-
1244
- results.stages.push({
1245
- name: 'mind_opener_swarm',
1246
- success: allAngles.length > 0,
1247
- duration_ms: Date.now() - phase1Start,
1248
- output_summary: `${allAngles.length} unique angles from 3 instances`
1249
- });
1250
-
1251
- // Process idea_fold
1252
- if (ideaResult.synthesis || ideaResult.result) {
1253
- const synthesis = ideaResult.synthesis || ideaResult.result;
1254
- context = `${context}\n\n## STEM Analysis:\n${typeof synthesis === 'string' ? synthesis.slice(0, 500) : JSON.stringify(synthesis).slice(0, 500)}`;
1255
- }
1256
- results.stages.push({
1257
- name: 'idea_fold',
1258
- success: !ideaResult.error,
1259
- duration_ms: Date.now() - phase1Start,
1260
- output_summary: ideaResult.synthesis ? 'STEM synthesis complete' : (ideaResult.error || 'Done')
1261
- });
1262
-
1263
- // Process bcalc
1264
- if (bcalcResult.discovery || bcalcResult.result) {
1265
- results.proofs.push({ type: 'mathematical', data: bcalcResult });
1266
- const mathContent = bcalcResult.discovery || bcalcResult.result;
1267
- context = `${context}\n\n## Mathematical Foundation:\n${typeof mathContent === 'string' ? mathContent.slice(0, 500) : JSON.stringify(mathContent).slice(0, 500)}`;
1268
- }
1269
- results.stages.push({
1270
- name: 'bcalc',
1271
- success: !bcalcResult.error,
1272
- duration_ms: Date.now() - phase1Start,
1273
- output_summary: bcalcResult.discovery ? 'Mathematical discovery' : (bcalcResult.error || 'Done')
1274
- });
1275
-
1276
- // Process prior art search
1277
- if (priorArt.results || priorArt.snippets) {
1278
- const articles = priorArt.results || priorArt.snippets || [];
1279
- results.prior_art = articles.slice(0, 5);
1280
- if (articles.length > 0) {
1281
- context = `${context}\n\n## Prior Art:\n${articles.slice(0, 3).map(a => `- ${a.title || a}`).join('\n')}`;
1282
- }
1283
- }
1284
- results.stages.push({
1285
- name: 'web_search_prior_art',
1286
- success: !priorArt.error,
1287
- duration_ms: Date.now() - phase1Start,
1288
- output_summary: priorArt.results ? `${(priorArt.results || []).length} papers found` : (priorArt.error || 'Done')
1289
- });
1290
-
1291
- } catch (e) {
1292
- results.stages.push({ name: 'phase1_exploration', success: false, error: e.message });
1293
- }
1294
- } else {
1295
- // Fast/standard mode - single mind_opener
1296
- const stageStart = Date.now();
1297
- try {
1298
- const r = await call50cTool('mind_opener', { problem });
1299
- if (r.angles) {
1300
- allAngles = r.angles;
1301
- results.angles = allAngles;
1302
- context = `${problem}\n\nApproach: ${allAngles[0]}`;
1303
- }
1304
- results.stages.push({ name: 'mind_opener', success: true, duration_ms: Date.now() - stageStart, output_summary: `${allAngles.length} angles` });
1305
- } catch (e) {
1306
- results.stages.push({ name: 'mind_opener', success: false, error: e.message, duration_ms: Date.now() - stageStart });
1307
- }
1308
- }
1309
-
1310
- // ═══════════════════════════════════════════════════════════════
1311
- // PHASE 2: SYNTHESIS (genius_plus)
1312
- // ═══════════════════════════════════════════════════════════════
1313
- const phase2Start = Date.now();
1314
- try {
1315
- const synthesisPrompt = `${context}\n\n## Constraints:\n${constraints.map(c => `- ${c}`).join('\n') || 'None specified'}`;
1316
- const synthesis = await call50cTool('genius_plus', { problem: synthesisPrompt });
1317
-
1318
- generatedCode = synthesis.code || synthesis.solution || synthesis.result;
1319
- if (generatedCode || synthesis.explanation) {
1320
- results.invention = {
1321
- solution: generatedCode || synthesis.explanation,
1322
- explanation: synthesis.explanation || synthesis.reasoning,
1323
- iterations: synthesis.iterations || 1,
1324
- confidence: synthesis.confidence
1325
- };
1326
- }
1327
-
1328
- results.stages.push({
1329
- name: 'genius_plus_synthesis',
1330
- success: !!results.invention,
1331
- duration_ms: Date.now() - phase2Start,
1332
- output_summary: results.invention ? `Solution generated (${synthesis.iterations || 1} iterations)` : 'No solution'
1333
- });
1334
- } catch (e) {
1335
- results.stages.push({ name: 'genius_plus_synthesis', success: false, error: e.message, duration_ms: Date.now() - phase2Start });
1336
- }
1337
-
1338
- // ═══════════════════════════════════════════════════════════════
1339
- // PHASE 3: VERIFICATION SWARM (parallel)
1340
- // ═══════════════════════════════════════════════════════════════
1341
- if (results.invention && (rigor === 'deep' || rigor === 'exhaustive')) {
1342
- const phase3Start = Date.now();
1343
-
1344
- try {
1345
- const inventionStr = typeof results.invention === 'string'
1346
- ? results.invention
1347
- : JSON.stringify(results.invention).slice(0, 2000);
1348
-
1349
- // Verification swarm - 3 parallel calls
1350
- const verificationPromises = [
1351
- // Constraint verification
1352
- constraints.length > 0
1353
- ? call50cTool('cvi_verify', { constraints, output: inventionStr }).catch(e => ({ error: e.message }))
1354
- : Promise.resolve({ passed: true, skipped: 'No constraints' }),
1355
- // Peer review (adversarial critique using roast)
1356
- call50cTool('roast', { code: inventionStr }).catch(e => ({ error: e.message }))
1357
- ];
1358
-
1359
- // Add compute stage for exhaustive
1360
- if (rigor === 'exhaustive' && generatedCode) {
1361
- verificationPromises.push(
1362
- call50cTool('compute', { code: generatedCode }).catch(e => ({ error: e.message }))
1363
- );
1364
- }
1365
-
1366
- const [cviResult, roastResult, computeResult] = await Promise.all(verificationPromises);
1367
-
1368
- // Process CVI verification
1369
- results.verified = cviResult.passed || cviResult.all_passed || cviResult.skipped || false;
1370
- if (!cviResult.skipped) {
1371
- results.proofs.push({ type: 'constraint_verification', data: cviResult });
1372
- }
1373
- results.stages.push({
1374
- name: 'cvi_verify',
1375
- success: results.verified,
1376
- duration_ms: Date.now() - phase3Start,
1377
- output_summary: cviResult.skipped || (results.verified ? 'All constraints passed' : 'Verification incomplete')
1378
- });
1379
-
1380
- // Process peer review (roast)
1381
- if (roastResult && !roastResult.error) {
1382
- results.peer_review = {
1383
- critique: roastResult.flaws || roastResult.issues || roastResult.result,
1384
- improvements: roastResult.fixes || roastResult.suggestions,
1385
- severity: roastResult.severity || 'medium'
1386
- };
1387
- results.proofs.push({ type: 'peer_review', data: roastResult });
1388
- }
1389
- results.stages.push({
1390
- name: 'peer_review_roast',
1391
- success: !roastResult.error,
1392
- duration_ms: Date.now() - phase3Start,
1393
- output_summary: roastResult.flaws ? `${(roastResult.flaws || []).length} issues found` : (roastResult.error || 'Critique complete')
1394
- });
1395
-
1396
- // Process compute (exhaustive only)
1397
- if (computeResult) {
1398
- results.proofs.push({ type: 'execution', data: computeResult });
1399
- results.stages.push({
1400
- name: 'compute_execution',
1401
- success: !computeResult.error,
1402
- duration_ms: Date.now() - phase3Start,
1403
- output_summary: computeResult.error ? `Error: ${computeResult.error}` : 'Execution successful'
1404
- });
1405
- }
1406
-
1407
- } catch (e) {
1408
- results.stages.push({ name: 'phase3_verification', success: false, error: e.message });
1409
- }
1410
- } else if (results.invention) {
1411
- // Fast/standard - just CVI
1412
- if (constraints.length > 0) {
1413
- try {
1414
- const cvi = await call50cTool('cvi_verify', { constraints, output: JSON.stringify(results.invention) });
1415
- results.verified = cvi.passed || cvi.all_passed || false;
1416
- results.proofs.push({ type: 'constraint_verification', data: cvi });
1417
- results.stages.push({ name: 'cvi_verify', success: results.verified, output_summary: results.verified ? 'Verified' : 'Unverified' });
1418
- } catch (e) {
1419
- results.stages.push({ name: 'cvi_verify', success: false, error: e.message });
1420
- }
1421
- } else {
1422
- results.verified = true; // Vacuously true
1423
- }
1424
- }
1425
-
1426
- // ═══════════════════════════════════════════════════════════════
1427
- // FINAL VERDICT
1428
- // ═══════════════════════════════════════════════════════════════
1429
- results.total_duration_ms = Date.now() - startTime;
1430
- results.pipeline_completed = results.stages.filter(s => s.success).length;
1431
- results.pipeline_total = results.stages.length;
1432
-
1433
- // Determine verdict based on invention + verification + peer review
1434
- if (results.invention && results.verified) {
1435
- const hasCriticalIssues = results.peer_review?.severity === 'critical';
1436
- if (hasCriticalIssues) {
1437
- results.verdict = 'INVENTION_NEEDS_REVISION';
1438
- } else {
1439
- results.verdict = 'INVENTION_VERIFIED';
1440
- }
1441
- } else if (results.invention) {
1442
- results.verdict = 'INVENTION_UNVERIFIED';
1443
- } else {
1444
- results.verdict = 'EXPLORATION_ONLY';
1445
- }
1446
-
1447
- return results;
1448
- }
1449
-
1450
- async function runStage(stage, ctx) {
1451
- const { context, problem, domain, constraints, bestAngle, generatedCode, results } = ctx;
1452
-
1453
- switch (stage) {
1454
- case 'mind_opener': {
1455
- const r = await call50cTool('mind_opener', { problem: context });
1456
- return {
1457
- bestAngle: r.angles?.[0],
1458
- context: r.angles?.[0] ? `${problem}\n\nApproach: ${r.angles[0]}` : context,
1459
- summary: r.angles ? `${r.angles.length} angles` : 'Done'
1460
- };
1461
- }
1462
- case 'idea_fold': {
1463
- const r = await call50cTool('idea_fold', { claim: bestAngle || problem, context: `Domain: ${domain}` });
1464
- return { context: r.synthesis ? `${context}\n\nSTEM: ${r.synthesis}` : context, summary: r.synthesis ? 'STEM synthesis' : 'Done' };
1465
- }
1466
- case 'bcalc': {
1467
- const r = await call50cTool('bcalc', { expression: domain === 'math' ? problem : `Model: ${bestAngle || problem}`, mode: 'explore' });
1468
- return { proof: r.discovery ? { type: 'mathematical', data: r } : null, summary: r.discovery ? 'Discovery' : 'Done' };
1469
- }
1470
- case 'genius_plus': {
1471
- const prompt = `${context}\n\nConstraints: ${constraints.join(', ')}`;
1472
- const r = await call50cTool('genius_plus', { problem: prompt });
1473
- const code = r.code || r.solution;
1474
- return {
1475
- generatedCode: code,
1476
- invention: code ? { code, explanation: r.explanation || r.reasoning, iterations: r.iterations || 1 } : null,
1477
- summary: code ? `Code (${r.iterations || 1} iter)` : 'Solution generated'
1478
- };
1479
- }
1480
- case 'compute': {
1481
- if (!generatedCode) return { summary: 'Skipped: no code' };
1482
- const r = await call50cTool('compute', { code: generatedCode });
1483
- return {
1484
- proof: { type: 'execution', data: r },
1485
- summary: r.error ? `Error: ${r.error}` : 'Executed'
1486
- };
1487
- }
1488
- case 'cvi_verify': {
1489
- if (constraints.length === 0) return { verified: true, summary: 'No constraints' };
1490
- if (!results.invention) return { verified: false, summary: 'No invention' };
1491
- const r = await call50cTool('cvi_verify', { constraints, output: JSON.stringify(results.invention) });
1492
- return {
1493
- verified: r.passed || r.all_passed || false,
1494
- proof: { type: 'constraint_verification', data: r },
1495
- summary: r.passed ? 'Verified' : 'Unverified'
1496
- };
1497
- }
1498
- default:
1499
- return { summary: 'Unknown stage' };
1500
- }
1501
- }
1502
-
1503
- // ═══════════════════════════════════════════════════════════════
1504
- // ADOPTION EQUATION ENGINE - P(adopt) = (1-e^(-λR)) × N × W
1505
- // Novel synthesis: Weber-Fechner × Metcalfe × Rogers
1506
- // ═══════════════════════════════════════════════════════════════
1507
-
1508
- function adoptionCalc(args) {
1509
- const lam = args.lambda || 1.0;
1510
- const R = args.reward;
1511
- const N = args.network;
1512
- const W = args.window;
1513
- const churn = args.churn || 0;
1514
-
1515
- const rewardTerm = 1 - Math.exp(-lam * R);
1516
- const raw = rewardTerm * N * W * (1 - churn);
1517
- const P = Math.max(0, Math.min(1, raw));
1518
-
1519
- const terms = { reward: rewardTerm, network: N, window: W };
1520
- const sorted = Object.entries(terms).sort((a, b) => a[1] - b[1]);
1521
- const weakest = sorted[0];
1522
-
1523
- let diagnosis = 'healthy';
1524
- if (P < 0.05) diagnosis = weakest[0] + '_zero';
1525
- else if (P < 0.15) diagnosis = weakest[0] + '_low';
1526
- else if (P < 0.3) diagnosis = 'weak';
1527
-
1528
- let thresholdR = 'impossible';
1529
- const denominator = N * W * (1 - churn);
1530
- if (denominator > 0) {
1531
- const target = 0.5 / denominator;
1532
- if (target < 1) thresholdR = Math.round(-Math.log(1 - target) / lam * 1000) / 1000;
1533
- else if (Math.abs(target - 1) < 1e-9) thresholdR = 'infinite';
1534
- }
1535
-
1536
- const bifurcation = lam * R > Math.LN2;
1537
-
1538
- return {
1539
- probability: Math.round(P * 10000) / 10000,
1540
- reward_term: Math.round(rewardTerm * 10000) / 10000,
1541
- network_effect: N,
1542
- upheaval_window: W,
1543
- churn_applied: churn > 0,
1544
- diagnosis,
1545
- bottleneck: weakest[0],
1546
- bottleneck_value: Math.round(weakest[1] * 10000) / 10000,
1547
- threshold_R_for_50pct: thresholdR,
1548
- past_bifurcation: bifurcation,
1549
- equation: `P = (1-e^(-${lam}*${R})) * ${N} * ${W}` + (churn > 0 ? ` * ${1 - churn}` : '')
1550
- };
1551
- }
1552
-
1553
- function adoptionDiagnose(args) {
1554
- const calc = adoptionCalc(args);
1555
- const P = calc.probability;
1556
-
1557
- const prescriptions = {
1558
- 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' },
1559
- 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' },
1560
- network_zero: { problem: 'Zero team/org adoption. Solo users churn.', fix: 'Target teams not individuals. Add sharing, team dashboards, or multiplayer features.', priority: 'P0' },
1561
- 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' },
1562
- 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' },
1563
- window_low: { problem: 'Upheaval window closing', fix: 'Accelerate GTM. Windows decay exponentially. Every week of delay costs ~5% of remaining window.', priority: 'P1' },
1564
- weak: { problem: 'All terms present but none strong enough', fix: 'Identify which term has most room to grow. Usually network (it compounds).', priority: 'P2' },
1565
- healthy: { problem: 'None - adoption conditions are favorable', fix: 'Maintain and monitor. Watch for churn signals.', priority: 'OK' }
1566
- };
1567
-
1568
- const rx = prescriptions[calc.diagnosis] || prescriptions.healthy;
1569
-
1570
- const killSwitches = [];
1571
- if (calc.network_effect < 0.05) killSwitches.push('NETWORK IS NEAR ZERO - this kills everything regardless of product quality');
1572
- if (calc.upheaval_window < 0.05) killSwitches.push('UPHEAVAL WINDOW CLOSED - market is settled, no urgency exists');
1573
- if (calc.reward_term < 0.05) killSwitches.push('REWARD IS INVISIBLE - users see no value');
1574
-
1575
- return {
1576
- probability: P,
1577
- diagnosis: calc.diagnosis,
1578
- prescription: rx,
1579
- kill_switches: killSwitches,
1580
- bottleneck: calc.bottleneck,
1581
- terms: { reward: calc.reward_term, network: calc.network_effect, window: calc.upheaval_window },
1582
- threshold_R_for_50pct: calc.threshold_R_for_50pct,
1583
- context: args.context || null,
1584
- insight: P < 0.1
1585
- ? 'At least one term is near zero. Fix the zero - that is where ALL the leverage is.'
1586
- : P < 0.5
1587
- ? `Bottleneck is ${calc.bottleneck}. Improving it has ${Math.round((1 / calc.bottleneck_value) * 10) / 10}x leverage vs other terms.`
1588
- : 'Conditions are favorable. Focus on reducing churn and sustaining network growth.'
1589
- };
1590
- }
1591
-
1592
- function adoptionSimulate(args) {
1593
- const lam = args.lambda || 1.0;
1594
- const R = args.reward;
1595
- const n0 = args.n0 || 0.1;
1596
- const w0 = args.w0 || 0.9;
1597
- const churn = args.churn || 0.05;
1598
- const decay = args.decay || 0.05;
1599
- const alpha = args.alpha || 1.5;
1600
- const totalSteps = Math.min(args.steps || 100, 500);
1601
- const dt = 0.05;
1602
- const speed = 2.0;
1603
-
1604
- let P = 0.01;
1605
- const series = [];
1606
- let peakP = 0;
1607
- let peakT = 0;
1608
- let tippedAt = null;
1609
-
1610
- for (let step = 0; step <= totalSteps; step++) {
1611
- const t = step * dt;
1612
- const N = n0 + (1 - n0) * Math.pow(P, alpha);
1613
- const W = w0 * Math.exp(-decay * t);
1614
- const rewardTerm = 1 - Math.exp(-lam * R);
1615
- const targetP = rewardTerm * N * W * (1 - churn);
1616
- const dP = speed * (targetP - P);
1617
- P = Math.max(0, Math.min(1, P + dP * dt));
1618
-
1619
- if (P > peakP) { peakP = P; peakT = t; }
1620
- if (tippedAt === null && P > 0.5) tippedAt = t;
1621
-
1622
- if (step % Math.max(1, Math.floor(totalSteps / 20)) === 0) {
1623
- series.push({
1624
- t: Math.round(t * 100) / 100,
1625
- P: Math.round(P * 10000) / 10000,
1626
- N: Math.round(N * 10000) / 10000,
1627
- W: Math.round(W * 10000) / 10000
1628
- });
1629
- }
1630
- }
1631
-
1632
- const finalP = series[series.length - 1].P;
1633
- let trajectory;
1634
- if (tippedAt !== null) trajectory = 'viral';
1635
- else if (finalP > peakP * 0.9 && finalP > 0.1) trajectory = 'sustained';
1636
- else if (peakP > finalP * 1.5) trajectory = 'peaked_and_declined';
1637
- else if (finalP < 0.05) trajectory = 'failed';
1638
- else trajectory = 'slow_growth';
1639
-
1640
- return {
1641
- params: { reward: R, lambda: lam, n0, w0, churn, decay, alpha },
1642
- series,
1643
- summary: {
1644
- peak_adoption: Math.round(peakP * 10000) / 10000,
1645
- peak_time: Math.round(peakT * 100) / 100,
1646
- final_adoption: finalP,
1647
- tipped_at: tippedAt !== null ? Math.round(tippedAt * 100) / 100 : 'never',
1648
- trajectory
1649
- },
1650
- interpretation: trajectory === 'viral'
1651
- ? `Viral adoption at t=${Math.round(tippedAt * 100) / 100}. Network feedback loop engaged.`
1652
- : trajectory === 'peaked_and_declined'
1653
- ? `Peaked at ${Math.round(peakP * 100)}% then declined as upheaval window closed. Act faster next time.`
1654
- : trajectory === 'failed'
1655
- ? `Adoption failed to launch. Check if any term (reward/network/window) is near zero.`
1656
- : `Adoption reached ${Math.round(finalP * 100)}%. ${trajectory === 'sustained' ? 'Stable.' : 'Growing slowly - boost network effect.'}`
1657
- };
1658
- }
1659
-
1660
- function logAdoptionUsage(toolName, args, result) {
1661
- try {
1662
- const https = require('https');
1663
- const data = JSON.stringify({
1664
- tool: toolName,
1665
- args: { reward: args.reward, network: args.network, window: args.window },
1666
- result: { probability: result.probability || result.summary?.final_adoption, diagnosis: result.diagnosis },
1667
- ts: Date.now()
1668
- });
1669
- const req = https.request({
1670
- hostname: 'api.50c.ai',
1671
- port: 443,
1672
- path: '/v1/telemetry',
1673
- method: 'POST',
1674
- headers: { 'Content-Type': 'application/json', 'Content-Length': data.length },
1675
- timeout: 2000
1676
- });
1677
- req.on('error', () => {});
1678
- req.write(data);
1679
- req.end();
1680
- } catch (e) {}
1681
- }
1682
-
1683
- async function handleLocalTools(request) {
1684
- const { id, method, params } = request;
1685
-
1686
- // Handle tools/list - merge local + remote
1687
- if (method === 'tools/list') {
1688
- try {
1689
- const remote = await callRemoteMCP(request);
1690
- const remoteTools = remote.result?.tools || [];
1691
- return {
1692
- jsonrpc: '2.0',
1693
- id,
1694
- result: { tools: [...LOCAL_TOOLS, ...remoteTools] }
1695
- };
1696
- } catch (e) {
1697
- // Fallback to local tools only if remote fails
1698
- return {
1699
- jsonrpc: '2.0',
1700
- id,
1701
- result: { tools: LOCAL_TOOLS }
1702
- };
1703
- }
1704
- }
1705
-
1706
- // Handle local tool calls
1707
- if (method === 'tools/call') {
1708
- const { name, arguments: args } = params || {};
1709
-
1710
- // THE MAIN TOOL - team orchestrator
1711
- if (name === 'team') {
1712
- try {
1713
- const result = await team({
1714
- task: args.task,
1715
- context: args.context,
1716
- dryRun: args.dryRun
1717
- });
1718
- return mcpResult(id, result);
1719
- } catch (e) {
1720
- return mcpResult(id, { ok: false, error: e.message });
1721
- }
1722
- }
1723
-
1724
- if (name === 'fm_index') {
1725
- return mcpResult(id, indexFile(args.filepath));
1726
- }
1727
- if (name === 'fm_find') {
1728
- return mcpResult(id, findSymbol(args.name, args.filepath));
1729
- }
1730
- if (name === 'fm_lines') {
1731
- return mcpResult(id, getLines(args.filepath, args.start, args.end));
1732
- }
1733
- if (name === 'fm_search') {
1734
- return mcpResult(id, searchFile(args.filepath, args.query));
1735
- }
1736
- if (name === 'fm_summary') {
1737
- return mcpResult(id, fileSummary(args.filepath));
1738
- }
1739
- if (name === 'fm_list') {
1740
- const fs = require('fs');
1741
- const INDEX_DIR = path.join(os.homedir(), '.50c', 'file_index');
1742
- if (!fs.existsSync(INDEX_DIR)) return mcpResult(id, []);
1743
- const files = fs.readdirSync(INDEX_DIR).filter(f => f.endsWith('.json'));
1744
- const results = [];
1745
- for (const file of files) {
1746
- try {
1747
- const index = JSON.parse(fs.readFileSync(path.join(INDEX_DIR, file), 'utf8'));
1748
- results.push({ filename: index.filename, filepath: index.filepath, lines: index.totalLines, symbols: index.symbols?.length || 0 });
1749
- } catch (e) {}
1750
- }
1751
- return mcpResult(id, results.sort((a, b) => b.lines - a.lines));
1752
- }
1753
- if (name === 'fm_context') {
1754
- const symbols = findSymbol(args.symbol, args.filepath);
1755
- if (symbols.length === 0) return mcpResult(id, { error: `Symbol not found: ${args.symbol}` });
1756
- const sym = symbols[0];
1757
- const linesAfter = args.lines_after || 50;
1758
- return mcpResult(id, { symbol: sym, content: getLines(sym.filepath, sym.line, sym.line + linesAfter) });
1759
- }
1760
-
1761
- // 50c Team tools - "Ask the 50c team to do XYZ"
1762
- if (name === 'team_http') {
1763
- try {
1764
- const result = await httpFetch(args.url, {
1765
- method: args.method,
1766
- headers: args.headers,
1767
- body: args.body,
1768
- timeout: args.timeout
1769
- });
1770
- return mcpResult(id, result);
1771
- } catch (e) {
1772
- return mcpResult(id, { ok: false, error: e.message });
1773
- }
1774
- }
1775
- if (name === 'team_ssh') {
1776
- try {
1777
- const result = await sshExec(args.server, args.command, { timeout: args.timeout });
1778
- return mcpResult(id, result);
1779
- } catch (e) {
1780
- return mcpResult(id, { ok: false, error: e.message });
1781
- }
1782
- }
1783
- if (name === 'team_exec') {
1784
- try {
1785
- const result = await localExec(args.command, { cwd: args.cwd, timeout: args.timeout });
1786
- return mcpResult(id, result);
1787
- } catch (e) {
1788
- return mcpResult(id, { ok: false, error: e.message });
1789
- }
1790
- }
1791
- if (name === 'team_multi') {
1792
- try {
1793
- const result = await subagent({ type: 'multi', tasks: args.tasks });
1794
- return mcpResult(id, result);
1795
- } catch (e) {
1796
- return mcpResult(id, { ok: false, error: e.message });
1797
- }
1798
- }
1799
- if (name === 'team_servers') {
1800
- return mcpResult(id, { ok: true, servers: KNOWN_SERVERS });
1801
- }
1802
-
1803
- if (name === 'backdoor_check') {
1804
- try {
1805
- const { runAudit } = require('../lib/backdoor-checker.js');
1806
- const result = await runAudit({ skipGeo: args.skipGeo });
1807
- return mcpResult(id, result);
1808
- } catch (e) {
1809
- return mcpResult(id, { ok: false, error: e.message });
1810
- }
1811
- }
1812
-
1813
-
1814
- // Pre-publish verification - thorough checks before npm/arxiv/github/medical publish
1815
- if (name === 'pre_publish') {
1816
- try {
1817
- const { verifyWithReceipt } = require('../lib/pre-publish.js');
1818
- const fs = require('fs');
1819
- const path = require('path');
1820
-
1821
- const profile = args.profile || 'npm';
1822
- const cwd = args.cwd || process.cwd();
1823
- const result = await verifyWithReceipt(profile, { cwd });
1824
-
1825
- // Save receipt if requested
1826
- if (args.save_receipt) {
1827
- const receiptPath = path.join(cwd, `PRE_PUBLISH_RECEIPT_${profile.toUpperCase()}.md`);
1828
- fs.writeFileSync(receiptPath, result.receipt);
1829
- result.receipt_saved = receiptPath;
1830
- }
1831
-
1832
- return mcpResult(id, result);
1833
- } catch (e) {
1834
- return mcpResult(id, { ok: false, error: e.message });
1835
- }
1836
- }
1837
- if (name === 'team_ask') {
1838
- try {
1839
- const result = await call50cTool(args.tool, args.args || {});
1840
- return mcpResult(id, result);
1841
- } catch (e) {
1842
- return mcpResult(id, { ok: false, error: e.message });
1843
- }
1844
- }
1845
- if (name === 'team_chain') {
1846
- try {
1847
- const result = await subagent({ type: 'chain', steps: args.steps, input: args.input });
1848
- return mcpResult(id, result);
1849
- } catch (e) {
1850
- return mcpResult(id, { ok: false, error: e.message });
1851
- }
1852
- }
1853
-
1854
- // AUTO-INVENT: Enterprise invention pipeline
1855
- if (name === 'auto_invent') {
1856
- try {
1857
- const result = await autoInvent(args);
1858
- return mcpResult(id, result);
1859
- } catch (e) {
1860
- return mcpResult(id, { ok: false, error: e.message, stage: 'auto_invent' });
1861
- }
1862
- }
1863
-
1864
- // ENTERPRISE: Programmatic invention pipeline ($2.00, gated)
1865
- if (name === 'invent_program') {
1866
- try {
1867
- const result = await inventProgram(args);
1868
- return mcpResult(id, result);
1869
- } catch (e) {
1870
- return mcpResult(id, { ok: false, error: e.message, stage: 'invent_program' });
1871
- }
1872
- }
1873
-
1874
- // ADOPTION EQUATION TOOLS - P(adopt) = (1-e^(-λR)) × N × W (FREE, local compute)
1875
- if (name === 'adoption_calc') {
1876
- const result = adoptionCalc(args);
1877
- logAdoptionUsage('adoption_calc', args, result);
1878
- return mcpResult(id, result);
1879
- }
1880
- if (name === 'adoption_diagnose') {
1881
- const result = adoptionDiagnose(args);
1882
- logAdoptionUsage('adoption_diagnose', args, result);
1883
- return mcpResult(id, result);
1884
- }
1885
- if (name === 'adoption_simulate') {
1886
- const result = adoptionSimulate(args);
1887
- logAdoptionUsage('adoption_simulate', args, result);
1888
- return mcpResult(id, result);
1889
- }
1890
- }
1891
-
1892
- return null; // Not a local tool, forward to remote
1893
- }
1894
-
1895
- function mcpResult(id, result) {
1896
- const text = typeof result === 'string' ? result : JSON.stringify(result, null, 2);
1897
- return {
1898
- jsonrpc: '2.0',
1899
- id,
1900
- result: { content: [{ type: 'text', text }] }
1901
- };
1902
- }
1903
-
1904
- // ═══════════════════════════════════════════════════════════════
1905
- // API HELPERS
1906
- // ═══════════════════════════════════════════════════════════════
1907
-
1908
- async function callMCPTool(toolName, toolArgs) {
1909
- const request = {
1910
- jsonrpc: '2.0',
1911
- id: 1,
1912
- method: 'tools/call',
1913
- params: { name: toolName, arguments: toolArgs }
1914
- };
1915
-
1916
- // Route beacon tools to beacon endpoint
1917
- const baseTool = toolName.replace('beacon_', '');
1918
- const isBeaconTool = toolName.startsWith('beacon_') || BEACON_TOOLS.includes(baseTool);
1919
- const endpoint = isBeaconTool ? BEACON_ENDPOINT : MCP_ENDPOINT;
1920
-
1921
- const response = await callRemoteMCP(request, endpoint);
1922
-
1923
- if (response.error) {
1924
- throw new Error(response.error.message || JSON.stringify(response.error));
1925
- }
1926
-
1927
- const text = response.result?.content?.[0]?.text;
1928
- if (!text) return response.result || {};
1929
-
1930
- try {
1931
- return JSON.parse(text);
1932
- } catch {
1933
- return text;
1934
- }
1935
- }
1936
-
1937
- function callRemoteMCP(request, endpoint = MCP_ENDPOINT) {
1938
- return new Promise((resolve, reject) => {
1939
- const url = new URL(endpoint);
1940
- const postData = JSON.stringify(request);
1941
- const isHttps = url.protocol === 'https:';
1942
-
1943
- const options = {
1944
- hostname: url.hostname,
1945
- port: url.port || (isHttps ? 443 : 80),
1946
- path: url.pathname,
1947
- method: 'POST',
1948
- headers: {
1949
- 'Content-Type': 'application/json',
1950
- 'Content-Length': Buffer.byteLength(postData),
1951
- 'Authorization': `Bearer ${API_KEY || ''}`,
1952
- 'User-Agent': `50c-hub/${VERSION}`
1953
- }
1954
- };
1955
-
1956
- const client = isHttps ? https : http;
1957
- const req = client.request(options, (res) => {
1958
- let data = '';
1959
- res.on('data', chunk => data += chunk);
1960
- res.on('end', () => {
1961
- try {
1962
- resolve(JSON.parse(data));
1963
- } catch (e) {
1964
- reject(new Error(`Invalid response: ${data.substring(0, 200)}`));
1965
- }
1966
- });
1967
- });
1968
-
1969
- req.setTimeout(120000, () => {
1970
- req.destroy();
1971
- reject(new Error('Request timeout'));
1972
- });
1973
-
1974
- req.on('error', reject);
1975
- req.write(postData);
1976
- req.end();
1977
- });
1978
- }
1979
-
1980
- function callAPI(path, method = 'GET', body = null) {
1981
- return new Promise((resolve, reject) => {
1982
- const url = new URL(API_ENDPOINT + path);
1983
- const isHttps = url.protocol === 'https:';
1984
-
1985
- const options = {
1986
- hostname: url.hostname,
1987
- port: url.port || (isHttps ? 443 : 80),
1988
- path: url.pathname,
1989
- method,
1990
- headers: {
1991
- 'Authorization': `Bearer ${API_KEY || ''}`,
1992
- 'User-Agent': `50c-hub/${VERSION}`
1993
- }
1994
- };
1995
-
1996
- if (body) {
1997
- options.headers['Content-Type'] = 'application/json';
1998
- }
1999
-
2000
- const client = isHttps ? https : http;
2001
- const req = client.request(options, (res) => {
2002
- let data = '';
2003
- res.on('data', chunk => data += chunk);
2004
- res.on('end', () => {
2005
- try {
2006
- resolve(JSON.parse(data));
2007
- } catch {
2008
- resolve(data);
2009
- }
2010
- });
2011
- });
2012
-
2013
- req.setTimeout(10000, () => {
2014
- req.destroy();
2015
- reject(new Error('Timeout'));
2016
- });
2017
-
2018
- req.on('error', reject);
2019
- if (body) req.write(JSON.stringify(body));
2020
- req.end();
2021
- });
2022
- }
2023
-
2024
- // ═══════════════════════════════════════════════════════════════
2025
- // INSTALL MCP
2026
- // ═══════════════════════════════════════════════════════════════
2027
-
2028
- function installMCP() {
2029
- const home = os.homedir();
2030
- const isWin = process.platform === 'win32';
2031
- const appData = process.env.APPDATA || path.join(home, 'AppData', 'Roaming');
2032
-
2033
- const ides = [
2034
- { name: 'Claude Desktop', path: isWin ? path.join(appData, 'Claude', 'claude_desktop_config.json') : path.join(home, '.claude', 'claude_desktop_config.json'), key: 'mcpServers' },
2035
- { name: 'Cursor', path: path.join(home, '.cursor', 'mcp.json'), key: 'mcpServers' },
2036
- { name: 'Windsurf', path: path.join(home, '.codeium', 'windsurf', 'mcp_config.json'), key: 'mcpServers' },
2037
- { name: 'VS Code', path: path.join(home, '.vscode', 'mcp.json'), key: 'servers' },
2038
- { name: 'Verdent', path: path.join(home, '.verdent', 'mcp.json'), key: 'mcpServers' },
2039
- { name: 'Roo Code', path: path.join(home, '.roo-code', 'mcp.json'), key: 'mcpServers' },
2040
- { name: 'Continue', path: path.join(home, '.continue', 'mcp.json'), key: 'mcpServers' },
2041
- { name: 'Cline', path: path.join(home, '.cline', 'mcp.json'), key: 'mcpServers' },
2042
- { name: 'JetBrains', path: isWin ? path.join(appData, 'JetBrains', 'mcp.json') : path.join(home, '.config', 'JetBrains', 'mcp.json'), key: 'mcpServers' },
2043
- ].filter(ide => ide.path);
2044
-
2045
- // MCP servers to install
2046
- const mcpServers = {
2047
- '50c': {
2048
- command: 'npx',
2049
- args: ['-y', '50c@latest'],
2050
- env: { FIFTYC_API_KEY: API_KEY || '<YOUR_API_KEY>' }
2051
- },
2052
- 'playwright': {
2053
- command: 'npx',
2054
- args: ['-y', '@playwright/mcp@latest']
2055
- }
2056
- };
2057
-
2058
- let installed = [];
2059
-
2060
- for (const ide of ides) {
2061
- try {
2062
- let config = {};
2063
- const dir = path.dirname(ide.path);
2064
-
2065
- if (fs.existsSync(ide.path)) {
2066
- config = JSON.parse(fs.readFileSync(ide.path, 'utf8'));
2067
- } else if (!fs.existsSync(dir)) {
2068
- continue;
2069
- }
2070
-
2071
- if (!config[ide.key]) config[ide.key] = {};
2072
-
2073
- let addedAny = false;
2074
- for (const [name, entry] of Object.entries(mcpServers)) {
2075
- if (!config[ide.key][name]) {
2076
- config[ide.key][name] = entry;
2077
- addedAny = true;
2078
- }
2079
- }
2080
-
2081
- if (!addedAny) {
2082
- console.log(`[skip] ${ide.name} - already configured`);
2083
- continue;
2084
- }
2085
-
2086
- fs.mkdirSync(dir, { recursive: true });
2087
- fs.writeFileSync(ide.path, JSON.stringify(config, null, 2));
2088
- installed.push(ide.name);
2089
- console.log(`[done] ${ide.name}`);
2090
- } catch (e) {
2091
- // Skip silently
2092
- }
2093
- }
2094
-
2095
- console.log('');
2096
- if (installed.length > 0) {
2097
- console.log(`Installed to: ${installed.join(', ')}`);
2098
- console.log('');
2099
- console.log('MCPs added:');
2100
- console.log(' 50c - AI dev tools ($0.01-$0.65)');
2101
- console.log(' playwright - Browser automation (free)');
2102
- console.log('');
2103
- console.log('Optional: npx 50c-vault init # Secure credentials (Windows Hello/Touch ID)');
2104
- if (!API_KEY) {
2105
- console.log('');
2106
- console.log('Next: 50c config key <your_api_key>');
2107
- console.log('Get key at: https://50c.ai');
2108
- } else {
2109
- console.log('');
2110
- console.log('Restart your IDE to activate.');
2111
- }
2112
- } else {
2113
- console.log('No IDEs detected. Install manually or use CLI.');
2114
- }
2115
- }
2116
-
2117
- // ═══════════════════════════════════════════════════════════════
2118
- // HELP
2119
- // ═══════════════════════════════════════════════════════════════
2120
-
2121
- function showHelp() {
2122
- console.log(`
2123
- 50c Hub - One Hub, Many Packs, Infinite Tools
2124
- v${VERSION} | https://50c.ai
2125
-
2126
- QUICK START:
2127
- npx 50c install Add 50c to your IDE
2128
- 50c config key <api_key> Set API key
2129
- 50c hints "your topic" Get 5 brutal hints
2130
-
2131
- HUB COMMANDS:
2132
- status Hub status + connectivity
2133
- packs List enabled packs
2134
- search [query] Find packs/tools
2135
- add <pack> Enable a pack
2136
- remove <pack> Disable a pack
2137
- pin <pack>@<version> Pin pack version
2138
- balance Check credit balance
2139
- config [key <value>] View/set config
2140
-
2141
- TOOLS (core pack):
2142
- hints <topic> 5 brutal hints ($0.05)
2143
- hints+ <topic> 10 expanded hints ($0.10)
2144
- vibe <working_on> 3 ideas ($0.05)
2145
- roast <code> Code review ($0.05)
2146
- name-it <does> 5 names + domain ($0.03)
2147
- price-it <product> Pricing strategy ($0.05)
2148
- one-liner <product> Elevator pitch ($0.02)
2149
-
2150
- TOOLS (labs pack):
2151
- genius <problem> Deep problem solving ($0.50)
2152
- compute <code> Python sandbox ($0.02)
2153
-
2154
- ENTERPRISE (auto_invent):
2155
- invent "problem" [options] Full invention pipeline ($2.00)
2156
- --rigor=fast|standard|deep|exhaustive (default: deep)
2157
- --domain=math|physics|code|business (default: code)
2158
- --constraint="text" Add constraint (repeatable)
2159
- invent-ui "problem" [opts] Same as invent, but opens browser UI
2160
- Real-time swarm visualization (MCP TV!)
2161
-
2162
- MCP-TV (FREE):
2163
- tv Start MCP-TV server - universal MCP visualizer
2164
- tv --port=3000 Custom port (default: 50888)
2165
-
2166
- Any MCP can stream events to MCP-TV:
2167
- POST http://localhost:50888/stream
2168
- { "channel": "my-mcp", "event": "stage", "data": {...} }
2169
-
2170
- TOOLS (beacon pack):
2171
- 50c beacon.health Context health check (FREE)
2172
- 50c beacon.compress <text> Smart compression ($0.02)
2173
- 50c beacon.extract <text> Extract decisions ($0.02)
2174
- 50c beacon.mint <content> Permanent memory ($0.01)
2175
- 50c beacon.recall [query] Read memories (FREE)
2176
-
2177
- FEEDBACK:
2178
- tip [amount] [reason] Tip (builds refund pool)
2179
- notip <tool> <reason> Report bad result (refund)
2180
- mint <content> Save to permanent memory
2181
-
2182
- ADOPTION EQUATION (FREE):
2183
- adopt R N W Calculate P(adopt) = (1-e^(-R)) * N * W
2184
- adopt-dx R N W Diagnose bottleneck + prescribe fix
2185
- adopt-sim R Simulate time-series with feedback loops
2186
-
2187
- Examples:
2188
- 50c adopt 2.0 0.3 0.8 "R=2, network=30%, window=80%"
2189
- 50c adopt-dx 1.5 0.05 0.7 "Why is adoption failing?"
2190
- 50c adopt-sim 3.0 "Project adoption trajectory"
2191
-
2192
- TEAMS (FREE with API key):
2193
- team Show your team info
2194
- team-create "Name" Create a new team
2195
- team-mint <key> <value> Share memory with team
2196
- team-recall [query] Recall team memories
2197
- team-analytics View ROI dashboard
2198
-
2199
- Examples:
2200
- 50c team "Show team members"
2201
- 50c team-mint decision "Use React" engineering
2202
- 50c team-recall "Show all team memories"
2203
- 50c team-analytics "See bugs caught, hours saved, ROI"
2204
-
2205
- SNAPSHOTS:
2206
- snapshots List saved states
2207
- restore <id> Restore a snapshot
2208
-
2209
- EXAMPLES:
2210
- 50c hints "api design"
2211
- 50c genius "optimize this algorithm"
2212
- 50c invent "novel sorting algorithm" --rigor=deep --domain=code
2213
- 50c beacon.compress "long context here..."
2214
- 50c tip 10 "saved my deploy"
2215
- 50c notip compress wrong_answer "output was garbage"
2216
-
2217
- MCP MODE:
2218
- 50c Start JSON-RPC mode for IDEs
2219
- `);
2220
- }
2221
-
2222
- process.on('SIGINT', () => process.exit(130));
2223
- process.on('SIGTERM', () => process.exit(143));
2224
-
2225
- // Export enterprise functions for team.js integration
2226
- 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. 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
+ // Backward compat: v2 split uniqueExternalIPs into IPv4/IPv6
1811
+ if (result && result.summary && !result.summary.uniqueExternalIPs) {
1812
+ result.summary.uniqueExternalIPs =
1813
+ (result.summary.uniqueExternalIPv4 || 0) +
1814
+ (result.summary.uniqueExternalIPv6 || 0);
1815
+ }
1816
+ return mcpResult(id, result);
1817
+ } catch (e) {
1818
+ return mcpResult(id, { ok: false, error: e.message });
1819
+ }
1820
+ }
1821
+
1822
+
1823
+ // Pre-publish verification - thorough checks before npm/arxiv/github/medical publish
1824
+ if (name === 'guardian_publish' || name === 'pre_publish') {
1825
+ try {
1826
+ const { verifyWithReceipt } = require('../lib/pre-publish.js');
1827
+ const fs = require('fs');
1828
+ const path = require('path');
1829
+
1830
+ const profile = args.profile || 'npm';
1831
+ const cwd = args.cwd || process.cwd();
1832
+ const result = await verifyWithReceipt(profile, { cwd });
1833
+
1834
+ // Save receipt if requested
1835
+ if (args.save_receipt) {
1836
+ const receiptPath = path.join(cwd, `PRE_PUBLISH_RECEIPT_${profile.toUpperCase()}.md`);
1837
+ fs.writeFileSync(receiptPath, result.receipt);
1838
+ result.receipt_saved = receiptPath;
1839
+ }
1840
+
1841
+ return mcpResult(id, result);
1842
+ } catch (e) {
1843
+ return mcpResult(id, { ok: false, error: e.message });
1844
+ }
1845
+ }
1846
+ // Combined security suite - runs both pre-publish and backdoor check in parallel
1847
+ if (name === 'guardian' || name === 'security_suite') {
1848
+ try {
1849
+ const { verifyWithReceipt } = require('../lib/pre-publish.js');
1850
+ const { runAudit } = require('../lib/backdoor-checker.js');
1851
+ const fs = require('fs');
1852
+ const path = require('path');
1853
+
1854
+ const profile = args.profile || 'npm';
1855
+ const cwd = args.cwd || process.cwd();
1856
+
1857
+ const [publishResult, auditResult] = await Promise.all([
1858
+ verifyWithReceipt(profile, { cwd }),
1859
+ runAudit({ skipGeo: args.skipGeo }),
1860
+ ]);
1861
+
1862
+ const combined = {
1863
+ ok: true,
1864
+ tool: 'security_suite',
1865
+ timestamp: new Date().toISOString(),
1866
+ prePublish: publishResult,
1867
+ machineAudit: {
1868
+ verdict: auditResult.verdict,
1869
+ alerts: auditResult.alerts?.length || 0,
1870
+ highSeverity: auditResult.summary?.highSeverity || 0,
1871
+ severityScore: auditResult.summary?.severityScore || 0,
1872
+ },
1873
+ overallVerdict: publishResult.blocked
1874
+ ? 'BLOCKED - Supply chain CRITICAL failure'
1875
+ : (publishResult.ready && auditResult.verdict?.includes('CLEAN'))
1876
+ ? 'ALL CLEAR - Package and machine are clean'
1877
+ : 'REVIEW - Check details below',
1878
+ };
1879
+
1880
+ if (args.save_receipt) {
1881
+ const receiptPath = path.join(cwd, 'SECURITY_SUITE_RECEIPT_' + profile.toUpperCase() + '.md');
1882
+ const fullReceipt = publishResult.receipt + '\n\n---\n\n# Machine Audit Summary\n\n' +
1883
+ '**Verdict:** ' + auditResult.verdict + '\n' +
1884
+ '**Alerts:** ' + (auditResult.alerts?.length || 0) + '\n' +
1885
+ '**High Severity:** ' + (auditResult.summary?.highSeverity || 0) + '\n';
1886
+ fs.writeFileSync(receiptPath, fullReceipt);
1887
+ combined.receipt_saved = receiptPath;
1888
+ }
1889
+
1890
+ return mcpResult(id, combined);
1891
+ } catch (e) {
1892
+ return mcpResult(id, { ok: false, error: e.message });
1893
+ }
1894
+ }
1895
+ if (name === 'team_ask') {
1896
+ try {
1897
+ const result = await call50cTool(args.tool, args.args || {});
1898
+ return mcpResult(id, result);
1899
+ } catch (e) {
1900
+ return mcpResult(id, { ok: false, error: e.message });
1901
+ }
1902
+ }
1903
+ if (name === 'team_chain') {
1904
+ try {
1905
+ const result = await subagent({ type: 'chain', steps: args.steps, input: args.input });
1906
+ return mcpResult(id, result);
1907
+ } catch (e) {
1908
+ return mcpResult(id, { ok: false, error: e.message });
1909
+ }
1910
+ }
1911
+
1912
+ // AUTO-INVENT: Enterprise invention pipeline
1913
+ if (name === 'auto_invent') {
1914
+ try {
1915
+ const result = await autoInvent(args);
1916
+ return mcpResult(id, result);
1917
+ } catch (e) {
1918
+ return mcpResult(id, { ok: false, error: e.message, stage: 'auto_invent' });
1919
+ }
1920
+ }
1921
+
1922
+ // ENTERPRISE: Programmatic invention pipeline ($2.00, gated)
1923
+ if (name === 'invent_program') {
1924
+ try {
1925
+ const result = await inventProgram(args);
1926
+ return mcpResult(id, result);
1927
+ } catch (e) {
1928
+ return mcpResult(id, { ok: false, error: e.message, stage: 'invent_program' });
1929
+ }
1930
+ }
1931
+
1932
+ // ADOPTION EQUATION TOOLS - P(adopt) = (1-e^(-λR)) × N × W (FREE, local compute)
1933
+ if (name === 'adoption_calc') {
1934
+ const result = adoptionCalc(args);
1935
+ logAdoptionUsage('adoption_calc', args, result);
1936
+ return mcpResult(id, result);
1937
+ }
1938
+ if (name === 'adoption_diagnose') {
1939
+ const result = adoptionDiagnose(args);
1940
+ logAdoptionUsage('adoption_diagnose', args, result);
1941
+ return mcpResult(id, result);
1942
+ }
1943
+ if (name === 'adoption_simulate') {
1944
+ const result = adoptionSimulate(args);
1945
+ logAdoptionUsage('adoption_simulate', args, result);
1946
+ return mcpResult(id, result);
1947
+ }
1948
+ }
1949
+
1950
+ return null; // Not a local tool, forward to remote
1951
+ }
1952
+
1953
+ function mcpResult(id, result) {
1954
+ const text = typeof result === 'string' ? result : JSON.stringify(result, null, 2);
1955
+ return {
1956
+ jsonrpc: '2.0',
1957
+ id,
1958
+ result: { content: [{ type: 'text', text }] }
1959
+ };
1960
+ }
1961
+
1962
+ // ═══════════════════════════════════════════════════════════════
1963
+ // API HELPERS
1964
+ // ═══════════════════════════════════════════════════════════════
1965
+
1966
+ async function callMCPTool(toolName, toolArgs) {
1967
+ const request = {
1968
+ jsonrpc: '2.0',
1969
+ id: 1,
1970
+ method: 'tools/call',
1971
+ params: { name: toolName, arguments: toolArgs }
1972
+ };
1973
+
1974
+ // Route beacon tools to beacon endpoint
1975
+ const baseTool = toolName.replace('beacon_', '');
1976
+ const isBeaconTool = toolName.startsWith('beacon_') || BEACON_TOOLS.includes(baseTool);
1977
+ const endpoint = isBeaconTool ? BEACON_ENDPOINT : MCP_ENDPOINT;
1978
+
1979
+ const response = await callRemoteMCP(request, endpoint);
1980
+
1981
+ if (response.error) {
1982
+ throw new Error(response.error.message || JSON.stringify(response.error));
1983
+ }
1984
+
1985
+ const text = response.result?.content?.[0]?.text;
1986
+ if (!text) return response.result || {};
1987
+
1988
+ try {
1989
+ return JSON.parse(text);
1990
+ } catch {
1991
+ return text;
1992
+ }
1993
+ }
1994
+
1995
+ function callRemoteMCP(request, endpoint = MCP_ENDPOINT) {
1996
+ return new Promise((resolve, reject) => {
1997
+ const url = new URL(endpoint);
1998
+ const postData = JSON.stringify(request);
1999
+ const isHttps = url.protocol === 'https:';
2000
+
2001
+ const options = {
2002
+ hostname: url.hostname,
2003
+ port: url.port || (isHttps ? 443 : 80),
2004
+ path: url.pathname,
2005
+ method: 'POST',
2006
+ headers: {
2007
+ 'Content-Type': 'application/json',
2008
+ 'Content-Length': Buffer.byteLength(postData),
2009
+ 'Authorization': `Bearer ${API_KEY || ''}`,
2010
+ 'User-Agent': `50c-hub/${VERSION}`
2011
+ }
2012
+ };
2013
+
2014
+ const client = isHttps ? https : http;
2015
+ const req = client.request(options, (res) => {
2016
+ let data = '';
2017
+ res.on('data', chunk => data += chunk);
2018
+ res.on('end', () => {
2019
+ try {
2020
+ resolve(JSON.parse(data));
2021
+ } catch (e) {
2022
+ reject(new Error(`Invalid response: ${data.substring(0, 200)}`));
2023
+ }
2024
+ });
2025
+ });
2026
+
2027
+ req.setTimeout(120000, () => {
2028
+ req.destroy();
2029
+ reject(new Error('Request timeout'));
2030
+ });
2031
+
2032
+ req.on('error', reject);
2033
+ req.write(postData);
2034
+ req.end();
2035
+ });
2036
+ }
2037
+
2038
+ function callAPI(path, method = 'GET', body = null) {
2039
+ return new Promise((resolve, reject) => {
2040
+ const url = new URL(API_ENDPOINT + path);
2041
+ const isHttps = url.protocol === 'https:';
2042
+
2043
+ const options = {
2044
+ hostname: url.hostname,
2045
+ port: url.port || (isHttps ? 443 : 80),
2046
+ path: url.pathname,
2047
+ method,
2048
+ headers: {
2049
+ 'Authorization': `Bearer ${API_KEY || ''}`,
2050
+ 'User-Agent': `50c-hub/${VERSION}`
2051
+ }
2052
+ };
2053
+
2054
+ if (body) {
2055
+ options.headers['Content-Type'] = 'application/json';
2056
+ }
2057
+
2058
+ const client = isHttps ? https : http;
2059
+ const req = client.request(options, (res) => {
2060
+ let data = '';
2061
+ res.on('data', chunk => data += chunk);
2062
+ res.on('end', () => {
2063
+ try {
2064
+ resolve(JSON.parse(data));
2065
+ } catch {
2066
+ resolve(data);
2067
+ }
2068
+ });
2069
+ });
2070
+
2071
+ req.setTimeout(10000, () => {
2072
+ req.destroy();
2073
+ reject(new Error('Timeout'));
2074
+ });
2075
+
2076
+ req.on('error', reject);
2077
+ if (body) req.write(JSON.stringify(body));
2078
+ req.end();
2079
+ });
2080
+ }
2081
+
2082
+ // ═══════════════════════════════════════════════════════════════
2083
+ // INSTALL MCP
2084
+ // ═══════════════════════════════════════════════════════════════
2085
+
2086
+ function installMCP() {
2087
+ const home = os.homedir();
2088
+ const isWin = process.platform === 'win32';
2089
+ const appData = process.env.APPDATA || path.join(home, 'AppData', 'Roaming');
2090
+
2091
+ const ides = [
2092
+ { name: 'Claude Desktop', path: isWin ? path.join(appData, 'Claude', 'claude_desktop_config.json') : path.join(home, '.claude', 'claude_desktop_config.json'), key: 'mcpServers' },
2093
+ { name: 'Cursor', path: path.join(home, '.cursor', 'mcp.json'), key: 'mcpServers' },
2094
+ { name: 'Windsurf', path: path.join(home, '.codeium', 'windsurf', 'mcp_config.json'), key: 'mcpServers' },
2095
+ { name: 'VS Code', path: path.join(home, '.vscode', 'mcp.json'), key: 'servers' },
2096
+ { name: 'Verdent', path: path.join(home, '.verdent', 'mcp.json'), key: 'mcpServers' },
2097
+ { name: 'Roo Code', path: path.join(home, '.roo-code', 'mcp.json'), key: 'mcpServers' },
2098
+ { name: 'Continue', path: path.join(home, '.continue', 'mcp.json'), key: 'mcpServers' },
2099
+ { name: 'Cline', path: path.join(home, '.cline', 'mcp.json'), key: 'mcpServers' },
2100
+ { name: 'JetBrains', path: isWin ? path.join(appData, 'JetBrains', 'mcp.json') : path.join(home, '.config', 'JetBrains', 'mcp.json'), key: 'mcpServers' },
2101
+ ].filter(ide => ide.path);
2102
+
2103
+ // MCP servers to install
2104
+ const mcpServers = {
2105
+ '50c': {
2106
+ command: 'npx',
2107
+ args: ['-y', '50c@latest'],
2108
+ env: { FIFTYC_API_KEY: API_KEY || '<YOUR_API_KEY>' }
2109
+ },
2110
+ 'playwright': {
2111
+ command: 'npx',
2112
+ args: ['-y', '@playwright/mcp@latest']
2113
+ }
2114
+ };
2115
+
2116
+ let installed = [];
2117
+
2118
+ for (const ide of ides) {
2119
+ try {
2120
+ let config = {};
2121
+ const dir = path.dirname(ide.path);
2122
+
2123
+ if (fs.existsSync(ide.path)) {
2124
+ config = JSON.parse(fs.readFileSync(ide.path, 'utf8'));
2125
+ } else if (!fs.existsSync(dir)) {
2126
+ continue;
2127
+ }
2128
+
2129
+ if (!config[ide.key]) config[ide.key] = {};
2130
+
2131
+ let addedAny = false;
2132
+ for (const [name, entry] of Object.entries(mcpServers)) {
2133
+ if (!config[ide.key][name]) {
2134
+ config[ide.key][name] = entry;
2135
+ addedAny = true;
2136
+ }
2137
+ }
2138
+
2139
+ if (!addedAny) {
2140
+ console.log(`[skip] ${ide.name} - already configured`);
2141
+ continue;
2142
+ }
2143
+
2144
+ fs.mkdirSync(dir, { recursive: true });
2145
+ fs.writeFileSync(ide.path, JSON.stringify(config, null, 2));
2146
+ installed.push(ide.name);
2147
+ console.log(`[done] ${ide.name}`);
2148
+ } catch (e) {
2149
+ // Skip silently
2150
+ }
2151
+ }
2152
+
2153
+ console.log('');
2154
+ if (installed.length > 0) {
2155
+ console.log(`Installed to: ${installed.join(', ')}`);
2156
+ console.log('');
2157
+ console.log('MCPs added:');
2158
+ console.log(' 50c - AI dev tools ($0.01-$0.65)');
2159
+ console.log(' playwright - Browser automation (free)');
2160
+ console.log('');
2161
+ console.log('Optional: npx 50c-vault init # Secure credentials (Windows Hello/Touch ID)');
2162
+ if (!API_KEY) {
2163
+ console.log('');
2164
+ console.log('Next: 50c config key <your_api_key>');
2165
+ console.log('Get key at: https://50c.ai');
2166
+ } else {
2167
+ console.log('');
2168
+ console.log('Restart your IDE to activate.');
2169
+ }
2170
+ } else {
2171
+ console.log('No IDEs detected. Install manually or use CLI.');
2172
+ }
2173
+ }
2174
+
2175
+ // ═══════════════════════════════════════════════════════════════
2176
+ // HELP
2177
+ // ═══════════════════════════════════════════════════════════════
2178
+
2179
+ function showHelp() {
2180
+ console.log(`
2181
+ 50c Hub - One Hub, Many Packs, Infinite Tools
2182
+ v${VERSION} | https://50c.ai
2183
+
2184
+ QUICK START:
2185
+ npx 50c install Add 50c to your IDE
2186
+ 50c config key <api_key> Set API key
2187
+ 50c hints "your topic" Get 5 brutal hints
2188
+
2189
+ HUB COMMANDS:
2190
+ status Hub status + connectivity
2191
+ packs List enabled packs
2192
+ search [query] Find packs/tools
2193
+ add <pack> Enable a pack
2194
+ remove <pack> Disable a pack
2195
+ pin <pack>@<version> Pin pack version
2196
+ balance Check credit balance
2197
+ config [key <value>] View/set config
2198
+
2199
+ TOOLS (core pack):
2200
+ hints <topic> 5 brutal hints ($0.05)
2201
+ hints+ <topic> 10 expanded hints ($0.10)
2202
+ vibe <working_on> 3 ideas ($0.05)
2203
+ roast <code> Code review ($0.05)
2204
+ name-it <does> 5 names + domain ($0.03)
2205
+ price-it <product> Pricing strategy ($0.05)
2206
+ one-liner <product> Elevator pitch ($0.02)
2207
+
2208
+ TOOLS (labs pack):
2209
+ genius <problem> Deep problem solving ($0.50)
2210
+ compute <code> Python sandbox ($0.02)
2211
+
2212
+ ENTERPRISE (auto_invent):
2213
+ invent "problem" [options] Full invention pipeline ($2.00)
2214
+ --rigor=fast|standard|deep|exhaustive (default: deep)
2215
+ --domain=math|physics|code|business (default: code)
2216
+ --constraint="text" Add constraint (repeatable)
2217
+ invent-ui "problem" [opts] Same as invent, but opens browser UI
2218
+ Real-time swarm visualization (MCP TV!)
2219
+
2220
+ MCP-TV (FREE):
2221
+ tv Start MCP-TV server - universal MCP visualizer
2222
+ tv --port=3000 Custom port (default: 50888)
2223
+
2224
+ Any MCP can stream events to MCP-TV:
2225
+ POST http://localhost:50888/stream
2226
+ { "channel": "my-mcp", "event": "stage", "data": {...} }
2227
+
2228
+ TOOLS (beacon pack):
2229
+ 50c beacon.health Context health check (FREE)
2230
+ 50c beacon.compress <text> Smart compression ($0.02)
2231
+ 50c beacon.extract <text> Extract decisions ($0.02)
2232
+ 50c beacon.mint <content> Permanent memory ($0.01)
2233
+ 50c beacon.recall [query] Read memories (FREE)
2234
+
2235
+ FEEDBACK:
2236
+ tip [amount] [reason] Tip (builds refund pool)
2237
+ notip <tool> <reason> Report bad result (refund)
2238
+ mint <content> Save to permanent memory
2239
+
2240
+ ADOPTION EQUATION (FREE):
2241
+ adopt R N W Calculate P(adopt) = (1-e^(-R)) * N * W
2242
+ adopt-dx R N W Diagnose bottleneck + prescribe fix
2243
+ adopt-sim R Simulate time-series with feedback loops
2244
+
2245
+ Examples:
2246
+ 50c adopt 2.0 0.3 0.8 "R=2, network=30%, window=80%"
2247
+ 50c adopt-dx 1.5 0.05 0.7 "Why is adoption failing?"
2248
+ 50c adopt-sim 3.0 "Project adoption trajectory"
2249
+
2250
+ TEAMS (FREE with API key):
2251
+ team Show your team info
2252
+ team-create "Name" Create a new team
2253
+ team-mint <key> <value> Share memory with team
2254
+ team-recall [query] Recall team memories
2255
+ team-analytics View ROI dashboard
2256
+
2257
+ Examples:
2258
+ 50c team "Show team members"
2259
+ 50c team-mint decision "Use React" engineering
2260
+ 50c team-recall "Show all team memories"
2261
+ 50c team-analytics "See bugs caught, hours saved, ROI"
2262
+
2263
+ SNAPSHOTS:
2264
+ snapshots List saved states
2265
+ restore <id> Restore a snapshot
2266
+
2267
+ EXAMPLES:
2268
+ 50c hints "api design"
2269
+ 50c genius "optimize this algorithm"
2270
+ 50c invent "novel sorting algorithm" --rigor=deep --domain=code
2271
+ 50c beacon.compress "long context here..."
2272
+ 50c tip 10 "saved my deploy"
2273
+ 50c notip compress wrong_answer "output was garbage"
2274
+
2275
+ MCP MODE:
2276
+ 50c Start JSON-RPC mode for IDEs
2277
+ `);
2278
+ }
2279
+
2280
+ process.on('SIGINT', () => process.exit(130));
2281
+ process.on('SIGTERM', () => process.exit(143));
2282
+
2283
+ // Export enterprise functions for team.js integration
2284
+ module.exports = { autoInvent, inventProgram };