dual-brain 4.5.0 → 4.5.1
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/install.mjs +92 -151
- package/package.json +1 -1
package/install.mjs
CHANGED
|
@@ -29,9 +29,13 @@ const flag = (f) => argv.includes(f);
|
|
|
29
29
|
const force = flag('--force');
|
|
30
30
|
const dryRun = flag('--dry-run');
|
|
31
31
|
const jsonOut = flag('--json');
|
|
32
|
+
const yesFlag = flag('--yes') || flag('-y');
|
|
32
33
|
const positional = argv.filter(a => !a.startsWith('-'));
|
|
33
34
|
const subcommand = positional[0] || null;
|
|
34
35
|
|
|
36
|
+
const BRAND = `Data Tools — Dual Brain v${VERSION}`;
|
|
37
|
+
const BRAND_SUBTITLE = 'Built on replit-tools by Steve Moraco';
|
|
38
|
+
|
|
35
39
|
if (flag('--version') || flag('-v')) {
|
|
36
40
|
console.log(`dual-brain v${VERSION}`);
|
|
37
41
|
process.exit(0);
|
|
@@ -39,13 +43,15 @@ if (flag('--version') || flag('-v')) {
|
|
|
39
43
|
|
|
40
44
|
if (flag('--help') || flag('-h')) {
|
|
41
45
|
console.log(`
|
|
42
|
-
|
|
46
|
+
${BRAND}
|
|
47
|
+
${BRAND_SUBTITLE}
|
|
43
48
|
|
|
44
49
|
Usage: npx -y dual-brain [command] [options]
|
|
45
50
|
|
|
46
51
|
Setup:
|
|
47
|
-
(none)
|
|
52
|
+
(none) Show quick start
|
|
48
53
|
init Alias for default install
|
|
54
|
+
setup Configure runtime, providers, auth, and hooks
|
|
49
55
|
doctor Check system health and report issues
|
|
50
56
|
reset Clear all state files (keeps config/hooks)
|
|
51
57
|
repair Fix corrupt files, stale locks, re-register hooks
|
|
@@ -108,7 +114,8 @@ if (flag('--help') || flag('-h')) {
|
|
|
108
114
|
Aggressive Maximizes both subscriptions, dual-brain for medium+
|
|
109
115
|
|
|
110
116
|
Examples:
|
|
111
|
-
${cmd('npx dual-brain')} #
|
|
117
|
+
${cmd('npx dual-brain')} # quick start
|
|
118
|
+
${cmd('npx dual-brain setup')} # configure providers and hooks
|
|
112
119
|
${cmd('npx dual-brain status')} # open control panel
|
|
113
120
|
${cmd('npx dual-brain mode cost-saver')} # switch profile
|
|
114
121
|
${cmd('npx dual-brain budget 8 25')} # \$8 session / \$25 daily
|
|
@@ -124,7 +131,7 @@ if (flag('--help') || flag('-h')) {
|
|
|
124
131
|
}
|
|
125
132
|
|
|
126
133
|
const SUBCOMMANDS = [
|
|
127
|
-
'init', 'status', 'mode', 'budget', 'explain',
|
|
134
|
+
'init', 'setup', 'status', 'mode', 'budget', 'explain',
|
|
128
135
|
'review', 'think', 'health', 'report', 'gate',
|
|
129
136
|
'vibe', 'plan', 'cost', 'dispatch', 'memory',
|
|
130
137
|
'test', 'ledger', 'doctor', 'reset', 'repair',
|
|
@@ -261,6 +268,51 @@ function detectEnvironment() {
|
|
|
261
268
|
};
|
|
262
269
|
}
|
|
263
270
|
|
|
271
|
+
function runtimeInstalled(workspace = process.cwd()) {
|
|
272
|
+
return existsSync(resolve(workspace, '.replit-tools'));
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function providerReadiness(env) {
|
|
276
|
+
const issues = [];
|
|
277
|
+
if (!env.claude.installed) issues.push('Claude CLI not found.');
|
|
278
|
+
else if (!env.claude.authed) issues.push('Claude CLI not authenticated.');
|
|
279
|
+
|
|
280
|
+
if (!env.codex.installed) issues.push('Codex CLI not found.');
|
|
281
|
+
else if (!env.codex.authed) issues.push('Codex CLI not authenticated.');
|
|
282
|
+
|
|
283
|
+
return { ready: issues.length === 0, issues };
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function readInputLine(prompt) {
|
|
287
|
+
process.stdout.write(prompt);
|
|
288
|
+
return new Promise((resolve) => {
|
|
289
|
+
process.stdin.setEncoding('utf8');
|
|
290
|
+
process.stdin.once('data', (chunk) => resolve(chunk.trim()));
|
|
291
|
+
process.stdin.resume();
|
|
292
|
+
}).finally(() => {
|
|
293
|
+
process.stdin.pause();
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function isRunningFromNpxCache() {
|
|
298
|
+
const scriptPath = fileURLToPath(import.meta.url);
|
|
299
|
+
return scriptPath.includes('/.npm/_npx/') || scriptPath.includes('\\.npm\\_npx\\');
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function warnIfUpdateAvailable() {
|
|
303
|
+
if (!isRunningFromNpxCache()) return;
|
|
304
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, 'package.json'), 'utf8'));
|
|
305
|
+
const packageName = pkg.name || 'dual-brain';
|
|
306
|
+
const latest = run('npm', ['view', packageName, 'version']);
|
|
307
|
+
if (latest.status !== 0) return;
|
|
308
|
+
const newest = latest.stdout.trim().split('\n').pop();
|
|
309
|
+
if (newest && newest !== VERSION) {
|
|
310
|
+
console.log('');
|
|
311
|
+
console.log(` Update available: ${cmd('npx -y dual-brain@latest')}`);
|
|
312
|
+
console.log('');
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
264
316
|
// ─── Mode Resolution ────────────────────────────────────────────────────────
|
|
265
317
|
|
|
266
318
|
function resolveMode(env) {
|
|
@@ -444,131 +496,17 @@ function install(workspace, env, mode) {
|
|
|
444
496
|
return actions;
|
|
445
497
|
}
|
|
446
498
|
|
|
447
|
-
// ─── Guided Auth ────────────────────────────────────────────────────────────
|
|
448
|
-
|
|
449
|
-
function printGuidedAuth(env) {
|
|
450
|
-
const claudeOk = env.claude.installed && env.claude.authed;
|
|
451
|
-
const codexOk = env.codex.installed && env.codex.authed;
|
|
452
|
-
|
|
453
|
-
if (claudeOk && codexOk) return false; // nothing to guide
|
|
454
|
-
|
|
455
|
-
const claudeMissing = !env.claude.installed || !env.claude.authed;
|
|
456
|
-
const codexMissing = !env.codex.installed || !env.codex.authed;
|
|
457
|
-
|
|
458
|
-
console.log('');
|
|
459
|
-
|
|
460
|
-
if (claudeMissing && codexMissing) {
|
|
461
|
-
console.log(' ⚠️ No AI providers detected. You need at least one:');
|
|
462
|
-
console.log('');
|
|
463
|
-
console.log(' Claude (recommended):');
|
|
464
|
-
console.log(' npm install -g @anthropic-ai/claude-code && claude login');
|
|
465
|
-
console.log('');
|
|
466
|
-
console.log(' OpenAI (optional, enables dual-brain):');
|
|
467
|
-
console.log(' npm install -g @openai/codex && codex login');
|
|
468
|
-
console.log('');
|
|
469
|
-
console.log(' Then re-run: npx dual-brain');
|
|
470
|
-
console.log('');
|
|
471
|
-
return true;
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
if (claudeMissing) {
|
|
475
|
-
if (!env.claude.installed) {
|
|
476
|
-
console.log(' ⚠️ Claude CLI not detected. To enable Claude routing:');
|
|
477
|
-
} else {
|
|
478
|
-
console.log(' ⚠️ Claude CLI not authenticated. To enable Claude routing:');
|
|
479
|
-
}
|
|
480
|
-
console.log('');
|
|
481
|
-
console.log(' 1. Install: npm install -g @anthropic-ai/claude-code');
|
|
482
|
-
console.log(' 2. Login: claude login');
|
|
483
|
-
console.log('');
|
|
484
|
-
console.log(' Run these commands, then re-run: npx dual-brain');
|
|
485
|
-
console.log('');
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
if (codexMissing) {
|
|
489
|
-
if (!env.codex.installed) {
|
|
490
|
-
console.log(' ℹ️ Codex CLI not detected. To enable GPT routing:');
|
|
491
|
-
} else {
|
|
492
|
-
console.log(' ℹ️ Codex CLI not authenticated. To enable GPT routing:');
|
|
493
|
-
}
|
|
494
|
-
console.log('');
|
|
495
|
-
console.log(' 1. Install: npm install -g @openai/codex');
|
|
496
|
-
console.log(' 2. Login: codex login');
|
|
497
|
-
console.log('');
|
|
498
|
-
if (claudeMissing) {
|
|
499
|
-
console.log(' Run these commands, then re-run: npx dual-brain');
|
|
500
|
-
} else {
|
|
501
|
-
console.log(' Run these commands, then re-run: npx dual-brain');
|
|
502
|
-
console.log(' GPT features will be disabled until Codex is configured.');
|
|
503
|
-
}
|
|
504
|
-
console.log('');
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
return claudeMissing; // only block/poll if Claude (primary provider) is missing
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
async function waitForAuth(env) {
|
|
511
|
-
if (!process.stdin.isTTY || !process.stdout.isTTY) return;
|
|
512
|
-
|
|
513
|
-
const claudeMissing = !env.claude.installed || !env.claude.authed;
|
|
514
|
-
if (!claudeMissing) return; // only wait if primary provider needs setup
|
|
515
|
-
|
|
516
|
-
process.stdout.write(' Would you like me to wait while you authenticate? [Y/n] ');
|
|
517
|
-
|
|
518
|
-
const answer = await new Promise((resolve) => {
|
|
519
|
-
process.stdin.setEncoding('utf8');
|
|
520
|
-
process.stdin.once('data', (chunk) => resolve(chunk.trim().toLowerCase()));
|
|
521
|
-
process.stdin.resume();
|
|
522
|
-
});
|
|
523
|
-
|
|
524
|
-
if (answer === 'n' || answer === 'no') {
|
|
525
|
-
process.stdin.pause();
|
|
526
|
-
console.log('');
|
|
527
|
-
console.log(' Re-run `npx dual-brain` after authenticating.');
|
|
528
|
-
console.log('');
|
|
529
|
-
process.exit(0);
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
console.log('');
|
|
533
|
-
console.log(' Waiting for Claude CLI to become available (checking every 5s, max 5 min)...');
|
|
534
|
-
console.log('');
|
|
535
|
-
|
|
536
|
-
const maxAttempts = 60; // 60 × 5s = 5 minutes
|
|
537
|
-
for (let i = 0; i < maxAttempts; i++) {
|
|
538
|
-
await new Promise(r => setTimeout(r, 5000));
|
|
539
|
-
process.stdout.write('.');
|
|
540
|
-
const fresh = detectClaude();
|
|
541
|
-
if (fresh.installed) {
|
|
542
|
-
console.log('');
|
|
543
|
-
console.log('');
|
|
544
|
-
console.log(' Claude CLI detected! Continuing setup...');
|
|
545
|
-
console.log('');
|
|
546
|
-
process.stdin.pause();
|
|
547
|
-
// Update env in-place
|
|
548
|
-
env.claude = fresh;
|
|
549
|
-
return;
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
console.log('');
|
|
554
|
-
console.log('');
|
|
555
|
-
console.log(' Timed out after 5 minutes. Re-run `npx dual-brain` after authenticating.');
|
|
556
|
-
console.log('');
|
|
557
|
-
process.stdin.pause();
|
|
558
|
-
process.exit(1);
|
|
559
|
-
}
|
|
560
|
-
|
|
561
499
|
// ─── Quick Start Block ───────────────────────────────────────────────────────
|
|
562
500
|
|
|
563
501
|
function printQuickStart() {
|
|
564
|
-
console.log(' ✓ dual-brain installed successfully');
|
|
565
502
|
console.log('');
|
|
566
|
-
console.log(
|
|
567
|
-
console.log(` npx dual-brain do "fix a bug and write tests" ← full pipeline`);
|
|
568
|
-
console.log(' npx dual-brain status ← check system');
|
|
569
|
-
console.log(' npx dual-brain doctor ← diagnose issues');
|
|
503
|
+
console.log(` ${BRAND}`);
|
|
570
504
|
console.log('');
|
|
571
|
-
console.log('
|
|
505
|
+
console.log(' Quick start:');
|
|
506
|
+
console.log(' npx dual-brain do "fix the login bug" ← one command to PR');
|
|
507
|
+
console.log(' npx dual-brain setup ← configure providers');
|
|
508
|
+
console.log(' npx dual-brain doctor ← check system health');
|
|
509
|
+
console.log(' npx dual-brain help ← full command list');
|
|
572
510
|
console.log('');
|
|
573
511
|
}
|
|
574
512
|
|
|
@@ -578,16 +516,10 @@ function printReport(env, mode, actions, isDryRun) {
|
|
|
578
516
|
const lines = [];
|
|
579
517
|
|
|
580
518
|
lines.push(br('╔', '╗'));
|
|
581
|
-
lines.push(ln(
|
|
519
|
+
lines.push(ln(BRAND));
|
|
520
|
+
lines.push(ln(BRAND_SUBTITLE));
|
|
582
521
|
lines.push(sep());
|
|
583
|
-
|
|
584
|
-
const cAuth = env.claude.authed ? '✅' : env.claude.installed ? '⚠️' : '❌';
|
|
585
|
-
const xAuth = env.codex.authed ? '✅' : env.codex.installed ? '⚠️' : '❌';
|
|
586
|
-
lines.push(ln(` 🟠 Claude ${cAuth} 🟢 Codex ${xAuth}`));
|
|
587
|
-
|
|
588
|
-
if (env.isReplit) {
|
|
589
|
-
lines.push(ln(` 🌀 Replit${env.hasReplitTools ? ' + replit-tools' : ''}`));
|
|
590
|
-
}
|
|
522
|
+
lines.push(ln(`Workspace: ${env.workspace}`));
|
|
591
523
|
|
|
592
524
|
if (actions) {
|
|
593
525
|
lines.push(sep());
|
|
@@ -1847,6 +1779,33 @@ async function main() {
|
|
|
1847
1779
|
return;
|
|
1848
1780
|
}
|
|
1849
1781
|
|
|
1782
|
+
// ── Bare invocation (no subcommand): quick start if already set up, else install ──
|
|
1783
|
+
if (!subcommand) {
|
|
1784
|
+
const workspace = resolve(process.cwd());
|
|
1785
|
+
const alreadySetUp = existsSync(join(workspace, '.claude', 'hooks'))
|
|
1786
|
+
|| existsSync(join(workspace, '.claude', 'settings.json'));
|
|
1787
|
+
if (alreadySetUp && !flag('--force')) {
|
|
1788
|
+
printQuickStart();
|
|
1789
|
+
return;
|
|
1790
|
+
}
|
|
1791
|
+
// Not set up yet — fall through to full install/setup wizard
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
if (subcommand === 'setup') {
|
|
1795
|
+
if (!dryRun && !jsonOut) await checkReplitTools();
|
|
1796
|
+
const env = detectEnvironment();
|
|
1797
|
+
const mode = resolveMode(env);
|
|
1798
|
+
const needsGuidance = printGuidedAuth(env);
|
|
1799
|
+
if (needsGuidance && process.stdin.isTTY && process.stdout.isTTY && !process.env.CI) {
|
|
1800
|
+
await waitForAuth(env);
|
|
1801
|
+
Object.assign(mode, resolveMode(env));
|
|
1802
|
+
}
|
|
1803
|
+
const actions = install(env.workspace, env, mode);
|
|
1804
|
+
printReport(env, mode, actions);
|
|
1805
|
+
printQuickStart();
|
|
1806
|
+
return;
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1850
1809
|
// ── replit-tools check — first thing before auth detection or install ──
|
|
1851
1810
|
if (!dryRun && !jsonOut) {
|
|
1852
1811
|
await checkReplitTools();
|
|
@@ -1869,30 +1828,12 @@ async function main() {
|
|
|
1869
1828
|
const needsGuidance = printGuidedAuth(env);
|
|
1870
1829
|
if (needsGuidance && process.stdin.isTTY && process.stdout.isTTY && !process.env.CI) {
|
|
1871
1830
|
await waitForAuth(env);
|
|
1872
|
-
// Re-resolve mode after potential auth
|
|
1873
1831
|
Object.assign(mode, resolveMode(env));
|
|
1874
1832
|
}
|
|
1875
1833
|
|
|
1876
1834
|
const actions = install(env.workspace, env, mode);
|
|
1877
1835
|
printReport(env, mode, actions);
|
|
1878
|
-
|
|
1879
|
-
// Always print quick-start block after successful install
|
|
1880
1836
|
printQuickStart();
|
|
1881
|
-
|
|
1882
|
-
// Offer to launch control panel (opt-in, interactive TTY only)
|
|
1883
|
-
if (process.stdin.isTTY && process.stdout.isTTY && !process.env.CI) {
|
|
1884
|
-
process.stdout.write(' Launch control panel? [y/N] ');
|
|
1885
|
-
const answer = await new Promise((resolve) => {
|
|
1886
|
-
process.stdin.setEncoding('utf8');
|
|
1887
|
-
process.stdin.once('data', (chunk) => resolve(chunk.trim().toLowerCase()));
|
|
1888
|
-
process.stdin.resume();
|
|
1889
|
-
});
|
|
1890
|
-
process.stdin.pause();
|
|
1891
|
-
console.log('');
|
|
1892
|
-
if (answer === 'y' || answer === 'yes') {
|
|
1893
|
-
launchPanel();
|
|
1894
|
-
}
|
|
1895
|
-
}
|
|
1896
1837
|
}
|
|
1897
1838
|
|
|
1898
1839
|
main();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dual-brain",
|
|
3
|
-
"version": "4.5.
|
|
3
|
+
"version": "4.5.1",
|
|
4
4
|
"description": "Data-Tools Dual-Brain — dual-provider orchestration extension for data-tools/replit-tools. Tiered routing, budget balancing, and GPT dual-brain review across Claude + OpenAI subscriptions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|