dual-brain 7.1.9 → 7.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.
Files changed (2) hide show
  1. package/bin/dual-brain.mjs +219 -113
  2. package/package.json +1 -1
@@ -126,7 +126,7 @@ function printSubscriptionTable(auth, profile) {
126
126
 
127
127
  const claudeLine1 = auth.claude.found
128
128
  ? ` Claude: logged in (${auth.claude.source})`
129
- : ` Claude: not logged in — run: claude login`;
129
+ : ` Claude: not logged in — run: claude auth login`;
130
130
  const claudeLine2 = ` plan: ${claudePlanLabel}${claudeLabel}`;
131
131
 
132
132
  const openaiLine1 = auth.openai.found
@@ -156,7 +156,7 @@ async function cmdInit(rl) {
156
156
  const noneFound = !auth.claude.found && !auth.openai.found;
157
157
  if (noneFound) {
158
158
  console.log('\nNo AI provider found. Log in first:');
159
- console.log(' Claude: claude login');
159
+ console.log(' Claude: claude auth login');
160
160
  console.log(' OpenAI: codex login\n');
161
161
  console.log('Then re-run: dual-brain init');
162
162
  return;
@@ -189,7 +189,7 @@ async function cmdAuth(subArgs = []) {
189
189
 
190
190
  if (!auth.claude.found || !auth.openai.found) {
191
191
  console.log('');
192
- if (!auth.claude.found) console.log(' Claude not logged in. Run: claude login');
192
+ if (!auth.claude.found) console.log(' Claude not logged in. Run: claude auth login');
193
193
  if (!auth.openai.found) console.log(' OpenAI not logged in. Run: codex login');
194
194
  }
195
195
  }
@@ -525,6 +525,35 @@ function cmdForget(text) {
525
525
 
526
526
  // ─── Screen helpers ───────────────────────────────────────────────────────────
527
527
 
528
+ /**
529
+ * Render the data-tools-style rounded header box for the main screen.
530
+ * Inner width is 39 chars. Lines are padded with spaces to fill the box.
531
+ */
532
+ function renderHeader(version, providerLines) {
533
+ const W = 39; // inner width
534
+ const pad = (s) => {
535
+ // Strip ANSI codes for length calculation
536
+ const visible = s.replace(/\x1b\[[0-9;]*m/g, '');
537
+ return s + ' '.repeat(Math.max(0, W - visible.length));
538
+ };
539
+ const top = ` ┌${'─'.repeat(W)}┐`;
540
+ const sep = ` ├${'─'.repeat(W)}┤`;
541
+ const bottom = ` └${'─'.repeat(W)}┘`;
542
+
543
+ const title = `DATA Tools - Dual Brain v${version}`;
544
+ const credit = `by Steve Moraco + dual-brain`;
545
+
546
+ const lines = [top];
547
+ lines.push(` │ ${pad(title)}│`);
548
+ lines.push(` │ ${pad(credit)}│`);
549
+ lines.push(sep);
550
+ for (const pl of providerLines) {
551
+ lines.push(` │ ${pad(pl)}│`);
552
+ }
553
+ lines.push(bottom);
554
+ return lines.join('\n');
555
+ }
556
+
528
557
  function profileExists(cwd) {
529
558
  const dir = cwd || process.cwd();
530
559
  const globalPath = join(process.env.HOME || '/root', '.config', 'dual-brain', 'profile.json');
@@ -609,8 +638,8 @@ async function welcomeScreen(rl, ask) {
609
638
 
610
639
  if (!claudeReady && !openaiReady) {
611
640
  console.log('No CLI login found. Log in first:');
612
- console.log(' claude login — for Claude');
613
- console.log(' codex login — for OpenAI/Codex\n');
641
+ console.log(' claude auth login — for Claude');
642
+ console.log(' codex login — for OpenAI/Codex\n');
614
643
  console.log('Then re-run: dual-brain init');
615
644
  return { next: 'exit' };
616
645
  }
@@ -777,7 +806,13 @@ async function mainScreen(rl, ask) {
777
806
 
778
807
  function subLine(name, plan, found, expired, days, sub) {
779
808
  const label = sub?.label ? ` [${sub.label}]` : '';
780
- if (!found) return `⚠️ ${name}: not logged in — run: ${name === 'Claude' ? 'claude login' : 'codex login'}`;
809
+ if (!found) return `⚠️ ${name}: not logged in — run: ${name === 'Claude' ? 'claude auth login' : 'codex login'}`;
810
+ // Multi-sub: show aggregated counts when more than one sub exists
811
+ const subs = sub?.subs;
812
+ if (subs && subs.length > 1) {
813
+ const aggregate = aggregatePlans(subs);
814
+ return `✅ ${name}: ${aggregate} [${subs.length} subs]`;
815
+ }
781
816
  if (expired) return `🔴 ${name}: ${plan} expired${label} — will re-auth`;
782
817
  const daysNote = (days !== null && days <= 7) ? ` (${days}d left)` : '';
783
818
  return `✅ ${name}: ${plan}${label}${daysNote}`;
@@ -789,7 +824,7 @@ async function mainScreen(rl, ask) {
789
824
  ];
790
825
 
791
826
  console.log('');
792
- console.log(box(`🧠 dual-brain v${version}`, headerLines));
827
+ console.log(renderHeader(version, headerLines));
793
828
 
794
829
  // Auto-refresh expired subscriptions
795
830
  if (claudeExpired || openaiExpired) {
@@ -799,7 +834,7 @@ async function mainScreen(rl, ask) {
799
834
  if (openaiExpired) expired.push('OpenAI');
800
835
  console.log(`\n ${expired.join(' & ')} subscription expired. Re-authenticating...`);
801
836
  if (claudeExpired) {
802
- const r = spawnSync('claude', ['login'], { stdio: 'inherit', timeout: 30000 });
837
+ const r = spawnSync('claude', ['auth', 'login'], { stdio: 'inherit', timeout: 30000 });
803
838
  if (r.status === 0) {
804
839
  claudeSub.expiresAt = null;
805
840
  saveProfile(profile, { cwd });
@@ -820,7 +855,7 @@ async function mainScreen(rl, ask) {
820
855
  const recentSessions = enrichSessions(importReplitSessions(cwd), cwd).slice(0, 7);
821
856
 
822
857
  if (recentSessions.length > 0) {
823
- console.log(separator('Recent Sessions'));
858
+ console.log(' Recent Sessions:');
824
859
  recentSessions.forEach((sess, i) => {
825
860
  const pin = sess.pinned ? '📌 ' : ' ';
826
861
  const active = sess.isActive ? ' ●' : '';
@@ -830,19 +865,17 @@ async function mainScreen(rl, ask) {
830
865
  console.log('');
831
866
  }
832
867
 
833
- const menuOpts = [];
834
- menuOpts.push({ key: 'c', label: 'Continue last session', section: 'Sessions' });
835
- menuOpts.push({ key: 'n', label: 'New session', section: 'Sessions' });
868
+ console.log(' [c] Continue last session');
869
+ console.log(' [n] New session');
836
870
  if (recentSessions.length > 0) {
837
- menuOpts.push({ key: '1-9', label: 'Resume numbered above', section: 'Sessions' });
838
- }
839
- menuOpts.push({ key: 'e', label: 'Manage sessions', section: 'Sessions' });
840
- menuOpts.push({ key: 'd', label: 'Switch to data-tools', section: 'Tools' });
841
- menuOpts.push({ key: 'j', label: 'Login to Claude', section: 'Auth' });
842
- menuOpts.push({ key: 'k', label: 'Login to Codex', section: 'Auth' });
843
- menuOpts.push({ key: 's', label: 'Settings', section: '' });
844
- menuOpts.push({ key: 'q', label: 'Exit', section: '' });
845
- console.log(menu(menuOpts));
871
+ console.log(' [1-9] Resume numbered above');
872
+ }
873
+ console.log(' [e] Manage sessions');
874
+ console.log(' [i] Import from replit-tools');
875
+ console.log(' [m] Manage subscriptions');
876
+ console.log(' [d] Switch to data-tools');
877
+ console.log(' [s] Settings');
878
+ console.log(' [q] Exit');
846
879
  console.log('');
847
880
 
848
881
  const choice = (await ask(' Choice: ')).trim().toLowerCase();
@@ -873,6 +906,18 @@ async function mainScreen(rl, ask) {
873
906
 
874
907
  if (choice === 'e') { return { next: 'sessions' }; }
875
908
 
909
+ if (choice === 'i') {
910
+ const sessions = importReplitSessions(cwd);
911
+ if (sessions.length === 0) {
912
+ console.log('\n No replit-tools sessions found to import.\n');
913
+ } else {
914
+ console.log(`\n ✅ Found ${sessions.length} sessions from replit-tools.`);
915
+ console.log(' Sessions are automatically available in the list above.\n');
916
+ }
917
+ await ask(' Press Enter to continue...');
918
+ return { next: 'main' };
919
+ }
920
+
876
921
  if (choice === 'd') {
877
922
  const { spawnSync } = await import('node:child_process');
878
923
  const which = spawnSync('which', ['claude-menu'], { encoding: 'utf8' });
@@ -885,17 +930,7 @@ async function mainScreen(rl, ask) {
885
930
  return { next: 'main' };
886
931
  }
887
932
 
888
- if (choice === 'j') {
889
- const { spawnSync } = await import('node:child_process');
890
- spawnSync('claude', ['login'], { stdio: 'inherit' });
891
- return { next: 'main' };
892
- }
893
-
894
- if (choice === 'k') {
895
- const { spawnSync } = await import('node:child_process');
896
- spawnSync('codex', ['login'], { stdio: 'inherit' });
897
- return { next: 'main' };
898
- }
933
+ if (choice === 'm') { return { next: 'subscriptions' }; }
899
934
 
900
935
  if (choice === 's') { return { next: 'settings' }; }
901
936
  if (choice === 'q' || choice === 'exit') { return { next: 'exit' }; }
@@ -1011,119 +1046,190 @@ async function settingsScreen(rl, ask) {
1011
1046
  return { next: 'settings' };
1012
1047
  }
1013
1048
 
1049
+ // ─── Helper: aggregatePlans ───────────────────────────────────────────────────
1050
+
1051
+ const PLAN_PRICES = {
1052
+ pro: '$20', max5: '$100', max20: '$200',
1053
+ plus: '$20', pro100: '$100', pro200: '$200',
1054
+ };
1055
+
1056
+ function aggregatePlans(subs) {
1057
+ if (!subs || subs.length === 0) return '';
1058
+ const counts = {};
1059
+ for (const s of subs) {
1060
+ const price = PLAN_PRICES[s.plan] || s.plan;
1061
+ counts[price] = (counts[price] || 0) + 1;
1062
+ }
1063
+ return Object.entries(counts)
1064
+ .sort((a, b) => parseInt(b[0].slice(1)) - parseInt(a[0].slice(1)))
1065
+ .map(([price, count]) => `${price}×${count}`)
1066
+ .join(' ');
1067
+ }
1068
+
1014
1069
  // ─── Screen: subscriptionsScreen ─────────────────────────────────────────────
1015
1070
 
1016
1071
  async function subscriptionsScreen(rl, ask) {
1072
+ console.clear();
1017
1073
  const cwd = process.cwd();
1018
1074
  const profile = loadProfile(cwd);
1019
1075
  const auth = await detectAuth();
1020
- const plans = detectPlans();
1021
1076
 
1022
- const claudeSub = profile?.providers?.claude;
1023
- const openaiSub = profile?.providers?.openai;
1077
+ // Backward compat: migrate old single-sub format to subs array
1078
+ for (const prov of ['claude', 'openai']) {
1079
+ const p = profile?.providers?.[prov];
1080
+ if (p && !p.subs && p.plan) {
1081
+ p.subs = [{ plan: p.plan, label: p.label || null, expiresAt: p.expiresAt || null }];
1082
+ }
1083
+ }
1024
1084
 
1025
- const claudePlanLabel = claudeSub?.enabled
1026
- ? (CLAUDE_PLAN_LABELS[claudeSub.plan] ?? claudeSub.plan ?? 'n/a')
1027
- : 'disabled';
1028
- const openaiPlanLabel = openaiSub?.enabled
1029
- ? (OPENAI_PLAN_LABELS[openaiSub.plan] ?? openaiSub.plan ?? 'n/a')
1030
- : 'disabled';
1085
+ // Build status lines — roster format
1086
+ const lines = [];
1031
1087
 
1032
- const subLines = [
1033
- `Claude: ${auth.claude.found ? 'logged in' : 'not logged in'} — ${claudePlanLabel}`,
1034
- claudeSub?.label ? ` label: ${claudeSub.label}` : '',
1035
- claudeSub?.expiresAt ? ` expires: ${claudeSub.expiresAt.slice(0, 10)}` : '',
1036
- '',
1037
- `OpenAI: ${auth.openai.found ? 'logged in' : 'not logged in'} — ${openaiPlanLabel}`,
1038
- openaiSub?.label ? ` label: ${openaiSub.label}` : '',
1039
- openaiSub?.expiresAt ? ` expires: ${openaiSub.expiresAt.slice(0, 10)}` : '',
1040
- ].filter(line => line !== '');
1088
+ function buildProviderLines(provKey, displayName, authFound) {
1089
+ const sub = profile?.providers?.[provKey];
1090
+ const subs = sub?.subs || [];
1091
+ if (!authFound && subs.length === 0) {
1092
+ lines.push(` ⚠️ ${displayName}: not linked`);
1093
+ return;
1094
+ }
1095
+ const aggregate = aggregatePlans(subs);
1096
+ const prefix = authFound ? '' : '⚠️ ';
1097
+ lines.push(` ${prefix} ${displayName}:${aggregate ? ' ' + aggregate : ' (no subs)'}`);
1098
+ subs.forEach((s, i) => {
1099
+ const planLabels = provKey === 'claude' ? CLAUDE_PLAN_LABELS : OPENAI_PLAN_LABELS;
1100
+ const planLabel = planLabels[s.plan] ?? s.plan ?? 'unknown';
1101
+ const nameStr = (s.label || '(no label)').padEnd(22);
1102
+ const d = s.expiresAt ? daysUntil(s.expiresAt) : null;
1103
+ const expiry = d === null ? '' : d < 0 ? ' (expired)' : d === 0 ? ' (today)' : ` (${d}d left)`;
1104
+ lines.push(` ${i + 1}. ${nameStr} ${planLabel}${expiry}`);
1105
+ });
1106
+ }
1041
1107
 
1108
+ buildProviderLines('claude', 'Claude', auth.claude.found);
1109
+ lines.push('');
1110
+ buildProviderLines('openai', 'OpenAI', auth.openai.found);
1111
+
1112
+ console.log(box('Subscriptions', lines));
1042
1113
  console.log('');
1043
- console.log(box('Subscriptions', subLines));
1044
- console.log('');
1045
- console.log(menu([
1046
- { key: 'd', label: 'Re-detect from CLI', section: '' },
1047
- { key: 'c', label: 'Set Claude plan tier', section: '' },
1048
- { key: 'o', label: 'Set OpenAI plan tier', section: '' },
1049
- { key: 't', label: 'Set team label/expiry',section: '' },
1050
- { key: 'b', label: 'Back to settings', section: '' },
1051
- ]));
1114
+
1115
+ const menuOpts = [
1116
+ { key: '1', label: 'Add Claude sub', section: 'Link' },
1117
+ { key: '2', label: 'Add Codex sub', section: 'Link' },
1118
+ { key: 'r', label: 'Remove a sub', section: 'Link' },
1119
+ { key: 'b', label: 'Back to home', section: '' },
1120
+ ];
1121
+ console.log(menu(menuOpts));
1052
1122
  console.log('');
1053
1123
 
1054
1124
  const choice = (await ask(' Choice: ')).trim().toLowerCase();
1055
1125
 
1056
- if (choice === 'd') {
1057
- // Re-detect from CLI config files
1058
- if (plans.claude && claudeSub) {
1059
- profile.providers.claude.plan = plans.claude;
1060
- console.log(` Detected Claude: ${CLAUDE_PLAN_LABELS[plans.claude] ?? plans.claude}`);
1061
- }
1062
- if (plans.openai && openaiSub) {
1063
- profile.providers.openai.plan = plans.openai;
1064
- console.log(` Detected OpenAI: ${OPENAI_PLAN_LABELS[plans.openai] ?? plans.openai}`);
1065
- }
1066
- saveProfile(profile, { cwd });
1067
- return { next: 'subscriptions' };
1068
- }
1069
-
1070
- if (choice === 'c') {
1071
- console.log('');
1072
- console.log(' Claude plan:');
1073
- console.log(' (1) Pro ($20/mo)');
1074
- console.log(' (2) Max x5 ($100/mo)');
1075
- console.log(' (3) Max x20 ($200/mo)');
1076
- const c = (await ask(' > ')).trim();
1077
- const planMap = { '1': 'pro', '2': 'max5', '3': 'max20' };
1078
- if (planMap[c]) {
1126
+ if (choice === '1') {
1127
+ console.log('\n Linking Claude subscription...');
1128
+ console.log(' A browser window will open — paste the code below when prompted.\n');
1129
+ const { spawnSync } = await import('node:child_process');
1130
+ const r = spawnSync('claude', ['auth', 'login'], { stdio: 'inherit', timeout: 60000 });
1131
+ if (r.status === 0) {
1132
+ console.log('\n ✅ Claude linked successfully!\n');
1133
+ const label = (await ask(" Label (e.g. \"Josh's $100 sub\", or Enter to skip): ")).trim();
1134
+ const expiry = await askExpiry(ask, 'Claude');
1135
+ const newPlans = detectPlans();
1136
+ const plan = newPlans.claude?.plan || 'pro';
1137
+ if (!profile.providers) profile.providers = {};
1079
1138
  if (!profile.providers.claude) profile.providers.claude = { enabled: true };
1080
- profile.providers.claude.plan = planMap[c];
1139
+ profile.providers.claude.plan = plan;
1081
1140
  profile.providers.claude.enabled = true;
1141
+ // Push to subs array instead of overwriting
1142
+ if (!profile.providers.claude.subs) profile.providers.claude.subs = [];
1143
+ profile.providers.claude.subs.push({ plan, label: label || null, expiresAt: expiry || null });
1082
1144
  saveProfile(profile, { cwd });
1083
- console.log(` Claude plan set to: ${CLAUDE_PLAN_LABELS[planMap[c]]}`);
1145
+ console.log(' Saved\n');
1146
+ await ask(' Press Enter to continue...');
1147
+ } else {
1148
+ console.log('\n ❌ Claude login failed or was cancelled.\n');
1149
+ await ask(' Press Enter to continue...');
1084
1150
  }
1085
1151
  return { next: 'subscriptions' };
1086
1152
  }
1087
1153
 
1088
- if (choice === 'o') {
1089
- console.log('');
1090
- console.log(' OpenAI plan:');
1091
- console.log(' (1) Plus ($20/mo)');
1092
- console.log(' (2) Pro ($100/mo)');
1093
- console.log(' (3) Pro ($200/mo higher limits)');
1094
- const c = (await ask(' > ')).trim();
1095
- const planMap = { '1': 'plus', '2': 'pro', '3': 'pro200' };
1096
- if (planMap[c]) {
1154
+ if (choice === '2') {
1155
+ console.log('\n Linking Codex subscription...');
1156
+ console.log(' A browser window will open — paste the code below when prompted.\n');
1157
+ const { spawnSync } = await import('node:child_process');
1158
+ const r = spawnSync('codex', ['login'], { stdio: 'inherit', timeout: 60000 });
1159
+ if (r.status === 0) {
1160
+ console.log('\n Codex linked successfully!\n');
1161
+ const label = (await ask(' Label (e.g. "Team Codex Pro", or Enter to skip): ')).trim();
1162
+ const expiry = await askExpiry(ask, 'Codex');
1163
+ const newPlans = detectPlans();
1164
+ const plan = newPlans.openai?.plan || 'plus';
1165
+ if (!profile.providers) profile.providers = {};
1097
1166
  if (!profile.providers.openai) profile.providers.openai = { enabled: true };
1098
- profile.providers.openai.plan = planMap[c];
1167
+ profile.providers.openai.plan = plan;
1099
1168
  profile.providers.openai.enabled = true;
1169
+ // Push to subs array instead of overwriting
1170
+ if (!profile.providers.openai.subs) profile.providers.openai.subs = [];
1171
+ profile.providers.openai.subs.push({ plan, label: label || null, expiresAt: expiry || null });
1100
1172
  saveProfile(profile, { cwd });
1101
- console.log(` OpenAI plan set to: ${OPENAI_PLAN_LABELS[planMap[c]]}`);
1173
+ console.log(' Saved\n');
1174
+ await ask(' Press Enter to continue...');
1175
+ } else {
1176
+ console.log('\n ❌ Codex login failed or was cancelled.\n');
1177
+ await ask(' Press Enter to continue...');
1102
1178
  }
1103
1179
  return { next: 'subscriptions' };
1104
1180
  }
1105
1181
 
1106
- if (choice === 't') {
1107
- // Team label/expiry for each provider
1108
- for (const provider of ['claude', 'openai']) {
1109
- const prov = profile.providers[provider];
1110
- if (!prov?.enabled) continue;
1111
- const provLabel = provider === 'claude' ? 'Claude' : 'OpenAI';
1112
- const currentLabel = prov.label || '';
1113
- const label = (await ask(` ${provLabel} label [${currentLabel || 'none'}]: `)).trim();
1114
- if (label === '-') { delete prov.label; }
1115
- else if (label) { prov.label = label; }
1116
- const expiry = await askExpiry(ask, provLabel);
1117
- if (expiry) { prov.expiresAt = expiry; }
1182
+ if (choice === 'r') {
1183
+ // Build a flat numbered list of all subs across both providers
1184
+ const allSubs = [];
1185
+ for (const [provKey, displayName] of [['claude', 'Claude'], ['openai', 'OpenAI']]) {
1186
+ const subs = profile?.providers?.[provKey]?.subs || [];
1187
+ for (const s of subs) {
1188
+ allSubs.push({ provKey, displayName, sub: s });
1189
+ }
1118
1190
  }
1119
- saveProfile(profile, { cwd });
1120
- console.log(' Team config saved.');
1191
+
1192
+ if (allSubs.length === 0) {
1193
+ console.log('\n No subscriptions to remove.\n');
1194
+ await ask(' Press Enter to continue...');
1195
+ return { next: 'subscriptions' };
1196
+ }
1197
+
1198
+ console.log('\n Remove a subscription:\n');
1199
+ allSubs.forEach(({ displayName, sub }, i) => {
1200
+ const planLabels = displayName === 'Claude' ? CLAUDE_PLAN_LABELS : OPENAI_PLAN_LABELS;
1201
+ const planLabel = planLabels[sub.plan] ?? sub.plan ?? 'unknown';
1202
+ const labelStr = sub.label ? ` [${sub.label}]` : '';
1203
+ console.log(` (${i + 1}) ${displayName}: ${planLabel}${labelStr}`);
1204
+ });
1205
+ console.log(' (Enter) Cancel\n');
1206
+
1207
+ const numStr = (await ask(' Remove #: ')).trim();
1208
+ const numChoice = parseInt(numStr, 10);
1209
+ if (!isNaN(numChoice) && numChoice >= 1 && numChoice <= allSubs.length) {
1210
+ const { provKey, sub } = allSubs[numChoice - 1];
1211
+ const confirm = (await ask(` Remove "${sub.label || sub.plan}" from ${provKey}? (y/N): `)).trim().toLowerCase();
1212
+ if (confirm === 'y') {
1213
+ const subs = profile.providers[provKey].subs;
1214
+ const idx = subs.indexOf(sub);
1215
+ if (idx !== -1) subs.splice(idx, 1);
1216
+ // Update top-level plan to first remaining sub (or keep as-is)
1217
+ if (subs.length > 0) {
1218
+ profile.providers[provKey].plan = subs[0].plan;
1219
+ }
1220
+ saveProfile(profile, { cwd });
1221
+ console.log(' ✓ Removed\n');
1222
+ } else {
1223
+ console.log(' Cancelled.\n');
1224
+ }
1225
+ } else {
1226
+ console.log(' Cancelled.\n');
1227
+ }
1228
+ await ask(' Press Enter to continue...');
1121
1229
  return { next: 'subscriptions' };
1122
1230
  }
1123
1231
 
1124
- if (choice === 'b' || choice === 'back') { return { next: 'settings' }; }
1125
-
1126
- return { next: 'subscriptions' };
1232
+ return { next: 'main' };
1127
1233
  }
1128
1234
 
1129
1235
  // ─── Screen: dashboardScreen (kept for internal reference, unreachable) ───────
@@ -1152,7 +1258,7 @@ async function authScreen(rl, ask) {
1152
1258
  'Claude:',
1153
1259
  auth.claude.found
1154
1260
  ? ` logged in via ${auth.claude.source}`
1155
- : ` not logged in — run: claude login`,
1261
+ : ` not logged in — run: claude auth login`,
1156
1262
  ` plan: ${claudePlanLabel}${claudeSub?.label ? ` [${claudeSub.label}]` : ''}`,
1157
1263
  '',
1158
1264
  'OpenAI:',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dual-brain",
3
- "version": "7.1.9",
3
+ "version": "7.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": {