dual-brain 0.1.10 → 0.1.11

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.
@@ -1243,47 +1243,30 @@ function detectInterruptedWork(sessions, cwd) {
1243
1243
 
1244
1244
  // ─── Provider status helpers ───────────────────────────────────────────────────
1245
1245
 
1246
- const _PLAN_PRICE_MAP = {
1247
- pro: '$20', max5: '$100', max20: '$200',
1248
- plus: '$20', pro100: '$100', pro200: '$200',
1249
- };
1250
-
1251
1246
  /**
1252
1247
  * Build a provider status string for the dashboard status line.
1253
- * Shows: "● Claude $100 ● OpenAI $100"
1254
- * Uses ANSI color codes for the dots (no emoji width issues).
1248
+ * Shows: "● Claude ● OpenAI ⚖️ Balanced"
1249
+ * Uses ANSI color codes for the dots no dollar amounts or usage bars.
1255
1250
  */
1256
1251
  function buildProviderStatusLine(profile, auth) {
1257
1252
  const GREEN = '●';
1258
1253
  const RED = '●';
1259
- const now = Date.now();
1260
-
1261
- function providerSegment(provKey, displayName) {
1262
- const sub = profile?.providers?.[provKey];
1263
- const found = provKey === 'claude' ? auth.claude.found : auth.openai.found;
1264
- if (!found) return `${RED} ${displayName}: not connected`;
1265
-
1266
- const expired = sub?.expiresAt && Date.parse(sub.expiresAt) < now;
1267
- if (expired) return `${RED} ${displayName}: expired`;
1268
-
1269
- const rawSubs = sub?.subs?.length
1270
- ? sub.subs
1271
- : sub?.plan
1272
- ? [{ plan: sub.plan }]
1273
- : [];
1274
- const planLabel = rawSubs.length > 0
1275
- ? rawSubs.map(s => _PLAN_PRICE_MAP[s.plan] || s.plan || '$100').join(' + ')
1276
- : null;
1277
-
1278
- return planLabel
1279
- ? `${GREEN} ${displayName} ${planLabel}`
1280
- : `${GREEN} ${displayName}: connected`;
1281
- }
1282
-
1283
- const parts = [];
1284
- parts.push(providerSegment('claude', 'Claude'));
1285
- parts.push(providerSegment('openai', 'OpenAI'));
1286
- return parts.join(' ');
1254
+
1255
+ const claudeDot = auth.claude.found ? GREEN : RED;
1256
+ const openaiDot = auth.openai.found ? GREEN : RED;
1257
+
1258
+ const WORK_STYLE_LABELS = {
1259
+ 'auto': '⚡ Fast',
1260
+ 'cost-saver': '⚡ Fast',
1261
+ 'balanced': '⚖️ Balanced',
1262
+ 'quality-first': '🔥 Full Power',
1263
+ 'solo-claude': '⚡ Fast',
1264
+ 'solo-openai': '⚡ Fast',
1265
+ };
1266
+ const bias = profile?.bias || profile?.mode || 'balanced';
1267
+ const label = WORK_STYLE_LABELS[bias] || '⚖️ Balanced';
1268
+
1269
+ return `${claudeDot} Claude ${openaiDot} OpenAI ${label}`;
1287
1270
  }
1288
1271
 
1289
1272
  /**
@@ -2297,10 +2280,24 @@ async function settingsScreen(rl, ask) {
2297
2280
  // Detect if gh is available + has PRs for the PR triage option
2298
2281
  const settingsPRs = await detectOpenPRs(cwd);
2299
2282
 
2283
+ // Load current work style
2284
+ const profile = loadProfile(cwd);
2285
+ const currentBias = profile?.bias || profile?.mode || 'balanced';
2286
+ const WORK_STYLE_DISPLAY = {
2287
+ 'cost-saver': '⚡ Fast',
2288
+ 'auto': '⚡ Fast',
2289
+ 'solo-claude': '⚡ Fast',
2290
+ 'solo-openai': '⚡ Fast',
2291
+ 'balanced': '⚖️ Balanced',
2292
+ 'quality-first': '🔥 Full Power',
2293
+ };
2294
+ const workStyleLabel = WORK_STYLE_DISPLAY[currentBias] || '⚖️ Balanced';
2295
+
2300
2296
  const lines = [
2301
2297
  top,
2302
2298
  row('Settings'),
2303
2299
  sep,
2300
+ row(`[w] Work Style: ${workStyleLabel}`),
2304
2301
  row('[m] Manage subscriptions'),
2305
2302
  row('[e] Manage sessions'),
2306
2303
  row('[i] Import from replit-tools'),
@@ -2317,6 +2314,60 @@ async function settingsScreen(rl, ask) {
2317
2314
  const raw = (await ask(' Choice: ')).trim();
2318
2315
  const choice = raw.toLowerCase();
2319
2316
 
2317
+ if (choice === 'w') {
2318
+ // Work style picker
2319
+ const wsTop = ` ┌${'─'.repeat(51)}┐`;
2320
+ const wsSep = ` ├${'─'.repeat(51)}┤`;
2321
+ const wsBot = ` └${'─'.repeat(51)}┘`;
2322
+ const wsPad = (s) => {
2323
+ const plain = s.replace(/\x1b\[[0-9;]*m/g, '');
2324
+ let vlen = 0;
2325
+ for (const ch of plain) {
2326
+ const cp = ch.codePointAt(0);
2327
+ if (
2328
+ (cp >= 0x1f300 && cp <= 0x1faff) ||
2329
+ (cp >= 0x2600 && cp <= 0x27bf) ||
2330
+ cp === 0xfe0f || cp === 0x20e3
2331
+ ) { vlen += 2; } else { vlen += 1; }
2332
+ }
2333
+ return s + ' '.repeat(Math.max(0, 51 - vlen));
2334
+ };
2335
+ const wsRow = (s) => ` │ ${wsPad(s)}│`;
2336
+
2337
+ const isFast = currentBias === 'cost-saver' || currentBias === 'auto' || currentBias === 'solo-claude' || currentBias === 'solo-openai';
2338
+ const isBal = currentBias === 'balanced';
2339
+ const isFull = currentBias === 'quality-first';
2340
+
2341
+ console.log('');
2342
+ console.log(wsTop);
2343
+ console.log(wsRow('Work Style'));
2344
+ console.log(wsSep);
2345
+ console.log(wsRow(` 1. ⚡ Fast — quick, single model${isFast ? ' ← current' : ''}`));
2346
+ console.log(wsRow(` 2. ⚖️ Balanced — smart routing${isBal ? ' ← current' : ''}`));
2347
+ console.log(wsRow(` 3. 🔥 Full Power — dual-brain everything${isFull ? ' ← current' : ''}`));
2348
+ console.log(wsSep);
2349
+ console.log(wsRow('[Enter] Keep current'));
2350
+ console.log(wsBot);
2351
+ console.log('');
2352
+
2353
+ const wsChoice = (await ask(' Choice [1/2/3/Enter]: ')).trim();
2354
+ const wsMap = { '1': 'cost-saver', '2': 'balanced', '3': 'quality-first' };
2355
+ const newBias = wsMap[wsChoice];
2356
+ if (newBias && newBias !== currentBias) {
2357
+ profile.bias = newBias;
2358
+ const enabledCount = [
2359
+ profile.providers?.claude?.enabled,
2360
+ profile.providers?.openai?.enabled,
2361
+ ].filter(Boolean).length;
2362
+ if (enabledCount >= 2) profile.mode = newBias;
2363
+ saveProfile(profile, { cwd });
2364
+ const newLabel = WORK_STYLE_DISPLAY[newBias] || newBias;
2365
+ console.log(`\n ✓ Work style set to ${newLabel}\n`);
2366
+ await ask(' Press Enter to continue...');
2367
+ }
2368
+ return { next: 'settings' };
2369
+ }
2370
+
2320
2371
  if (choice === 'm') { return { next: 'subscriptions' }; }
2321
2372
 
2322
2373
  if (choice === 'e') { return { next: 'sessions' }; }
@@ -2369,6 +2420,7 @@ async function settingsScreen(rl, ask) {
2369
2420
  return { next: 'main' };
2370
2421
  }
2371
2422
 
2423
+
2372
2424
  // ─── Helper: aggregatePlans ───────────────────────────────────────────────────
2373
2425
 
2374
2426
  const PLAN_PRICES = {
@@ -2558,8 +2610,8 @@ async function subscriptionsScreen(rl, ask) {
2558
2610
  // ─── Onboarding Wizard ───────────────────────────────────────────────────────
2559
2611
 
2560
2612
  /**
2561
- * 5-step onboarding wizard shown on first run (no .dualbrain/profile.json).
2562
- * Matches the rounded ┌─┐ box style used in mainScreen / renderHeader.
2613
+ * Streamlined onboarding: auto-detect capabilities, ask ONE question (work style).
2614
+ * Replaces the old 5-step wizard with a ~5-second, one-choice flow.
2563
2615
  * @param {{ auth, plans, existingSessions }} detection
2564
2616
  * @param {string} cwd
2565
2617
  * @param {object} rl readline interface
@@ -2589,49 +2641,49 @@ async function runOnboardingWizard(detection, cwd, rl) {
2589
2641
  };
2590
2642
  const wRow = (s) => ` │ ${wPad(s)}│`;
2591
2643
 
2592
- // ── Collected wizard state ─────────────────────────────────────────────────
2593
- const state = {
2594
- claudePlan: null,
2595
- openaiPlan: null,
2596
- headModel: null,
2597
- importSessions: false,
2598
- profile: 'auto',
2599
- };
2644
+ const { auth, existingSessions } = detection;
2645
+ const claudeReady = auth.claude.found;
2646
+ const openaiReady = auth.openai.found;
2600
2647
 
2601
- const { auth, plans, existingSessions } = detection;
2602
- const claudeReady = auth.claude.found;
2603
- const openaiReady = auth.openai.found;
2648
+ // ── Detect Codex CLI availability ─────────────────────────────────────────
2649
+ let codexAvailable = false;
2650
+ try {
2651
+ const { spawnSync } = await import('node:child_process');
2652
+ const r = spawnSync('which', ['codex'], { encoding: 'utf8' });
2653
+ codexAvailable = r.status === 0;
2654
+ } catch {}
2655
+
2656
+ // ── Detect replit-tools ────────────────────────────────────────────────────
2657
+ const rt = detectReplitTools(cwd);
2658
+
2659
+ const GREEN = '\x1b[32m✓\x1b[0m';
2660
+ const RED = '\x1b[31m✗\x1b[0m';
2604
2661
 
2605
2662
  // ══════════════════════════════════════════════════════════════════════════
2606
- // Step 1 — Welcome & provider detection
2663
+ // Step 1 — Auto-detect capabilities (no user input)
2607
2664
  // ══════════════════════════════════════════════════════════════════════════
2608
2665
  console.log('');
2609
2666
  console.log(wTop);
2610
2667
  console.log(wRow(`🧠 Dual-Brain v${version} — First-time Setup`));
2611
2668
  console.log(wSep);
2612
- console.log(wRow(`Step 1 of 5: Detected providers`));
2669
+ console.log(wRow('Checking your setup...'));
2613
2670
  console.log(wSep);
2614
-
2615
- // Plan tier is inferred from auth config signals — not the actual plan name.
2616
- // Show the tier ($20/$100/$200) with "configured" suffix to be honest.
2617
- const claudePlanSuffix = claudeReady && plans.claude ? ` · ${plans.claude} configured` : '';
2618
- const openaiPlanSuffix = openaiReady && plans.openai ? ` · ${plans.openai} configured` : '';
2619
-
2620
2671
  console.log(wRow(claudeReady
2621
- ? `✓ Claude CLI${claudePlanSuffix}`
2622
- : `✗ Claude CLI not logged in`));
2672
+ ? `${GREEN} Claude Code — available`
2673
+ : `${RED} Claude Code — not found`));
2623
2674
  console.log(wRow(openaiReady
2624
- ? `✓ Codex CLI${openaiPlanSuffix}`
2625
- : `✗ Codex CLI not logged in`));
2626
- if (existingSessions.length > 0) {
2627
- console.log(wRow(`✓ ${existingSessions.length} data-tools session${existingSessions.length !== 1 ? 's' : ''} found`));
2628
- }
2629
- console.log(wSep);
2630
- console.log(wRow(`[Enter] Continue setup [s] Skip wizard`));
2675
+ ? `${GREEN} OpenAI API — available`
2676
+ : `${RED} OpenAI API — not found`));
2677
+ console.log(wRow(codexAvailable
2678
+ ? `${GREEN} Codex CLI available`
2679
+ : `${RED} Codex CLI — not found`));
2680
+ console.log(wRow(rt.installed
2681
+ ? `${GREEN} replit-tools available`
2682
+ : `${RED} replit-tools — not found`));
2631
2683
  console.log(wBottom);
2632
- console.log('');
2633
2684
 
2634
2685
  if (!claudeReady && !openaiReady) {
2686
+ console.log('');
2635
2687
  console.log(' No AI provider found. Log in first:');
2636
2688
  console.log(' claude auth login — for Claude');
2637
2689
  console.log(' codex login — for OpenAI/Codex');
@@ -2639,194 +2691,55 @@ async function runOnboardingWizard(detection, cwd, rl) {
2639
2691
  return null;
2640
2692
  }
2641
2693
 
2642
- const step1 = (await ask(' > ')).trim().toLowerCase();
2643
- if (step1 === 's') {
2644
- // Skip: auto-save detected plans and proceed directly
2645
- const skippedProfile = loadProfile(cwd);
2646
- if (claudeReady) skippedProfile.providers.claude = { enabled: true, plan: plans.claude || 'pro' };
2647
- if (openaiReady) skippedProfile.providers.openai = { enabled: true, plan: plans.openai || 'plus' };
2648
- const enabledCount = [claudeReady, openaiReady].filter(Boolean).length;
2649
- skippedProfile.mode = enabledCount >= 2 ? 'auto' : claudeReady ? 'solo-claude' : 'solo-openai';
2650
- return skippedProfile;
2651
- }
2652
-
2653
- // ══════════════════════════════════════════════════════════════════════════
2654
- // Step 2 — Budget / plan selection
2655
- // ══════════════════════════════════════════════════════════════════════════
2656
- console.log('');
2657
- console.log(wTop);
2658
- console.log(wRow(`🧠 Dual-Brain v${version} — First-time Setup`));
2659
- console.log(wSep);
2660
- console.log(wRow(`Step 2 of 5: Subscription plans`));
2661
- console.log(wSep);
2662
-
2663
- if (claudeReady) {
2664
- // Plan tier is inferred from auth config (rate-limit signal), not the actual plan name.
2665
- const configuredClaudePlan = plans.claude || '$20';
2666
- const configuredClaudeDesc = configuredClaudePlan + ' configured';
2667
- console.log(wRow(`Claude — ${configuredClaudeDesc}`));
2668
- console.log(wRow(` [1] Pro ($20/mo)`));
2669
- console.log(wRow(` [2] Max x5 ($100/mo)`));
2670
- console.log(wRow(` [3] Max x20 ($200/mo)`));
2671
- console.log(wRow(` [Enter] Keep configured (${configuredClaudePlan})`));
2672
- console.log(wSep);
2673
- const claudeChoice = (await ask(' Claude plan [1/2/3/Enter]: ')).trim();
2674
- const claudePlanMap = { '1': 'pro', '2': 'max5', '3': 'max20' };
2675
- state.claudePlan = claudePlanMap[claudeChoice] || configuredClaudePlan;
2676
- }
2677
-
2678
- if (openaiReady) {
2679
- // Plan tier is inferred from JWT claim in auth config, not the actual plan name.
2680
- const configuredOpenaiPlan = plans.openai || '$20';
2681
- const configuredOpenaiDesc = configuredOpenaiPlan + ' configured';
2682
- console.log(wRow(`OpenAI — ${configuredOpenaiDesc}`));
2683
- console.log(wRow(` [1] Plus ($20/mo)`));
2684
- console.log(wRow(` [2] Pro ($100/mo)`));
2685
- console.log(wRow(` [3] Pro ($200/mo higher limits)`));
2686
- console.log(wRow(` [Enter] Keep configured (${configuredOpenaiPlan})`));
2687
- console.log(wSep);
2688
- const openaiChoice = (await ask(' OpenAI plan [1/2/3/Enter]: ')).trim();
2689
- const openaiPlanMap = { '1': 'plus', '2': 'pro', '3': 'pro200' };
2690
- state.openaiPlan = openaiPlanMap[openaiChoice] || configuredOpenaiPlan;
2691
- }
2692
-
2693
- console.log(wBottom);
2694
-
2695
2694
  // ══════════════════════════════════════════════════════════════════════════
2696
- // Step 3HEAD model selection
2695
+ // Step 2ONE question: work style
2697
2696
  // ══════════════════════════════════════════════════════════════════════════
2698
- const hasBigPlan = state.claudePlan === 'max5' || state.claudePlan === 'max20';
2699
- const recommendedModel = hasBigPlan ? 'claude-opus-4-5' : 'claude-sonnet-4-5';
2700
- const recommendedLabel = hasBigPlan
2701
- ? 'Opus (Max plan — best quality)'
2702
- : 'Sonnet (Pro plan — balanced speed/quality)';
2703
-
2704
2697
  console.log('');
2705
2698
  console.log(wTop);
2706
- console.log(wRow(`🧠 Dual-Brain v${version} — First-time Setup`));
2699
+ console.log(wRow(`🧠 Dual-Brain v${version} — Work Style`));
2707
2700
  console.log(wSep);
2708
- console.log(wRow(`Step 3 of 5: HEAD model (think-tier)`));
2701
+ console.log(wRow('How do you want to work?'));
2709
2702
  console.log(wSep);
2710
- console.log(wRow(`Recommended: ${recommendedLabel}`));
2703
+ console.log(wRow(' 1. ⚡ Fast — quick answers, single model, skip reviews'));
2704
+ console.log(wRow(' 2. ⚖️ Balanced — smart routing, reviews on important changes'));
2705
+ console.log(wRow(' (recommended)'));
2706
+ console.log(wRow(' 3. 🔥 Full Power — deep reasoning, dual-brain on everything'));
2707
+ console.log(wRow(' that matters'));
2711
2708
  console.log(wSep);
2712
- console.log(wRow(` [1] Haiku — fastest, lowest cost`));
2713
- console.log(wRow(` [2] Sonnet — balanced (recommended for Pro)`));
2714
- console.log(wRow(` [3] Opus — best quality (recommended for Max)`));
2715
- console.log(wRow(` [Enter] Use recommended`));
2709
+ console.log(wRow('[Enter] Balanced'));
2716
2710
  console.log(wBottom);
2717
2711
  console.log('');
2718
2712
 
2719
- const step3 = (await ask(' HEAD model [1/2/3/Enter]: ')).trim();
2720
- const modelMap = {
2721
- '1': 'claude-haiku-4-5',
2722
- '2': 'claude-sonnet-4-5',
2723
- '3': 'claude-opus-4-5',
2724
- };
2725
- state.headModel = modelMap[step3] || recommendedModel;
2726
-
2727
- // ══════════════════════════════════════════════════════════════════════════
2728
- // Step 4 — Import sessions + profile selection
2729
- // ══════════════════════════════════════════════════════════════════════════
2730
- console.log('');
2731
- console.log(wTop);
2732
- console.log(wRow(`🧠 Dual-Brain v${version} — First-time Setup`));
2733
- console.log(wSep);
2734
- console.log(wRow(`Step 4 of 5: Sessions & routing profile`));
2735
- console.log(wSep);
2713
+ const styleChoice = (await ask(' Choice [1/2/3/Enter]: ')).trim();
2714
+ const styleMap = { '1': 'cost-saver', '2': 'balanced', '3': 'quality-first' };
2715
+ const chosenBias = styleMap[styleChoice] || 'balanced';
2736
2716
 
2737
- if (existingSessions.length > 0) {
2738
- console.log(wRow(`Import ${existingSessions.length} data-tools session${existingSessions.length !== 1 ? 's' : ''}?`));
2739
- console.log(wRow(` [y] Yes [Enter/n] Skip`));
2740
- console.log(wSep);
2741
- const importChoice = (await ask(' Import sessions [y/Enter]: ')).trim().toLowerCase();
2742
- state.importSessions = importChoice === 'y';
2743
- if (state.importSessions) {
2744
- console.log('');
2745
- console.log(` Importing ${existingSessions.length} sessions...`);
2746
- const recent = existingSessions.slice(0, 5);
2747
- for (const sess of recent) {
2748
- console.log(` ${sess.age.padEnd(6)} ${sess.name}`);
2749
- }
2750
- if (existingSessions.length > 5) {
2751
- console.log(` ... and ${existingSessions.length - 5} more`);
2752
- }
2753
- }
2754
- console.log(wSep);
2717
+ // ── Non-blocking note if metered API detected ──────────────────────────────
2718
+ if (openaiReady && !codexAvailable) {
2719
+ console.log('');
2720
+ console.log(' 💡 OpenAI API detected — will confirm before expensive operations');
2755
2721
  }
2756
2722
 
2757
- console.log(wRow(`Routing profile:`));
2758
- console.log(wRow(` [1] auto — adapts based on task risk & outcomes`));
2759
- console.log(wRow(` [2] balanced — best model per tier, normal budgets`));
2760
- console.log(wRow(` [3] cost-saver — prefer cheaper models, skip GPT`));
2761
- console.log(wRow(` [4] quality-first — dual-brain for medium+ risk`));
2762
- console.log(wRow(` [Enter] auto (recommended)`));
2763
- console.log(wBottom);
2764
- console.log('');
2765
-
2766
- const step4 = (await ask(' Profile [1/2/3/4/Enter]: ')).trim();
2767
- const profileMap = { '1': 'auto', '2': 'balanced', '3': 'cost-saver', '4': 'quality-first' };
2768
- state.profile = profileMap[step4] || 'auto';
2769
-
2770
- // ══════════════════════════════════════════════════════════════════════════
2771
- // Step 5 — Summary & confirm
2772
- // ══════════════════════════════════════════════════════════════════════════
2773
- const claudeSummary = state.claudePlan
2774
- ? `Claude: ${CLAUDE_PLAN_LABELS[state.claudePlan] ?? state.claudePlan}`
2775
- : `Claude: not configured`;
2776
- const openaiSummary = state.openaiPlan
2777
- ? `OpenAI: ${OPENAI_PLAN_LABELS[state.openaiPlan] ?? state.openaiPlan}`
2778
- : `OpenAI: not configured`;
2779
- const modelSummary = `HEAD model: ${state.headModel}`;
2780
- const profileSummary = `Profile: ${state.profile}`;
2781
- const sessionSummary = existingSessions.length > 0
2782
- ? `Sessions: ${state.importSessions ? `${existingSessions.length} imported` : 'skipped'}`
2783
- : null;
2784
-
2723
+ // ── Done ───────────────────────────────────────────────────────────────────
2785
2724
  console.log('');
2786
2725
  console.log(wTop);
2787
- console.log(wRow(`🧠 Dual-Brain v${version} First-time Setup`));
2788
- console.log(wSep);
2789
- console.log(wRow(`Step 5 of 5: Summary`));
2790
- console.log(wSep);
2791
- console.log(wRow(claudeSummary));
2792
- console.log(wRow(openaiSummary));
2793
- console.log(wRow(modelSummary));
2794
- console.log(wRow(profileSummary));
2795
- if (sessionSummary) console.log(wRow(sessionSummary));
2796
- console.log(wSep);
2797
- console.log(wRow(`[Enter] Save and start [q] Quit without saving`));
2726
+ console.log(wRow(`✓ Ready. Type a task or press Enter for dashboard.`));
2798
2727
  console.log(wBottom);
2799
2728
  console.log('');
2800
2729
 
2801
- const step5 = (await ask(' > ')).trim().toLowerCase();
2802
- if (step5 === 'q') {
2803
- console.log('\n Setup cancelled.\n');
2804
- return null;
2805
- }
2806
-
2807
2730
  // ── Build and return the profile object ────────────────────────────────────
2808
2731
  const finalProfile = loadProfile(cwd);
2809
2732
 
2810
- if (state.claudePlan) {
2811
- finalProfile.providers.claude = { enabled: true, plan: state.claudePlan };
2812
- } else if (claudeReady) {
2813
- finalProfile.providers.claude = { enabled: true, plan: plans.claude || 'pro' };
2733
+ if (claudeReady) {
2734
+ finalProfile.providers.claude = { enabled: true };
2814
2735
  }
2815
-
2816
- if (state.openaiPlan) {
2817
- finalProfile.providers.openai = { enabled: true, plan: state.openaiPlan };
2818
- } else if (openaiReady) {
2819
- finalProfile.providers.openai = { enabled: true, plan: plans.openai || 'plus' };
2736
+ if (openaiReady) {
2737
+ finalProfile.providers.openai = { enabled: true };
2820
2738
  }
2821
2739
 
2822
- const enabledCount = [
2823
- finalProfile.providers?.claude?.enabled,
2824
- finalProfile.providers?.openai?.enabled,
2825
- ].filter(Boolean).length;
2826
-
2827
- finalProfile.mode = enabledCount >= 2 ? state.profile : claudeReady ? 'solo-claude' : 'solo-openai';
2828
- finalProfile.headModel = state.headModel;
2829
- finalProfile.bias = state.profile;
2740
+ const enabledCount = [claudeReady, openaiReady].filter(Boolean).length;
2741
+ finalProfile.mode = enabledCount >= 2 ? chosenBias : claudeReady ? 'solo-claude' : 'solo-openai';
2742
+ finalProfile.bias = chosenBias;
2830
2743
 
2831
2744
  return finalProfile;
2832
2745
  }
@@ -4400,8 +4313,8 @@ async function main() {
4400
4313
  if (profileExists(cwd)) {
4401
4314
  await runScreens('main');
4402
4315
  } else {
4403
- // First run: run the 5-step onboarding wizard, then go to main.
4404
- process.stdout.write(`\ndual-brain v${readVersion()} Setup\n\nDetecting your setup...\n`);
4316
+ // First run: run the onboarding wizard, then go to main.
4317
+ // (wizard handles detection display)
4405
4318
  const auth = await detectAuth();
4406
4319
  const plans = detectPlans();
4407
4320
  const existingSessions = importReplitSessions(cwd);
@@ -4442,9 +4355,9 @@ async function main() {
4442
4355
 
4443
4356
  if (cmd === 'init') {
4444
4357
  if (isInteractive) {
4445
- // Run 5-step onboarding wizard then main screen
4358
+ // Run onboarding wizard then main screen
4446
4359
  const cwd = process.cwd();
4447
- process.stdout.write(`\ndual-brain v${readVersion()} Setup\n\nDetecting your setup...\n`);
4360
+ // (wizard handles detection display)
4448
4361
  const auth = await detectAuth();
4449
4362
  const plans = detectPlans();
4450
4363
  const existingSessions = importReplitSessions(cwd);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dual-brain",
3
- "version": "0.1.10",
3
+ "version": "0.1.11",
4
4
  "description": "AI orchestration across Claude + OpenAI subscriptions — smart routing, budget awareness, and dual-brain collaboration",
5
5
  "type": "module",
6
6
  "bin": {