dual-brain 0.1.9 → 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.
- package/bin/dual-brain.mjs +147 -234
- package/package.json +1 -1
- package/src/decide.mjs +210 -72
- package/src/detect.mjs +132 -1
- package/src/profile.mjs +357 -483
package/bin/dual-brain.mjs
CHANGED
|
@@ -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
|
|
1254
|
-
* Uses ANSI color codes for the dots
|
|
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 = '[32m●[0m';
|
|
1258
1253
|
const RED = '[31m●[0m';
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
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
|
-
*
|
|
2562
|
-
*
|
|
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
|
-
|
|
2593
|
-
const
|
|
2594
|
-
|
|
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
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
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 —
|
|
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(
|
|
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
|
-
?
|
|
2622
|
-
:
|
|
2672
|
+
? `${GREEN} Claude Code — available`
|
|
2673
|
+
: `${RED} Claude Code — not found`));
|
|
2623
2674
|
console.log(wRow(openaiReady
|
|
2624
|
-
?
|
|
2625
|
-
:
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
console.log(
|
|
2630
|
-
|
|
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
|
|
2695
|
+
// Step 2 — ONE 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} —
|
|
2699
|
+
console.log(wRow(`🧠 Dual-Brain v${version} — Work Style`));
|
|
2707
2700
|
console.log(wSep);
|
|
2708
|
-
console.log(wRow(
|
|
2701
|
+
console.log(wRow('How do you want to work?'));
|
|
2709
2702
|
console.log(wSep);
|
|
2710
|
-
console.log(wRow(
|
|
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(
|
|
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
|
|
2720
|
-
const
|
|
2721
|
-
|
|
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
|
|
2738
|
-
|
|
2739
|
-
console.log(
|
|
2740
|
-
console.log(
|
|
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
|
-
|
|
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(
|
|
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 (
|
|
2811
|
-
finalProfile.providers.claude = { enabled: true
|
|
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
|
-
|
|
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
|
-
|
|
2824
|
-
|
|
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
|
|
4404
|
-
|
|
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
|
|
4358
|
+
// Run onboarding wizard then main screen
|
|
4446
4359
|
const cwd = process.cwd();
|
|
4447
|
-
|
|
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