dual-brain 7.1.8 → 7.1.10
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 +74 -98
- package/package.json +1 -1
- package/src/decide.mjs +2 -2
- package/src/detect.mjs +9 -0
package/bin/dual-brain.mjs
CHANGED
|
@@ -838,8 +838,7 @@ async function mainScreen(rl, ask) {
|
|
|
838
838
|
}
|
|
839
839
|
menuOpts.push({ key: 'e', label: 'Manage sessions', section: 'Sessions' });
|
|
840
840
|
menuOpts.push({ key: 'd', label: 'Switch to data-tools', section: 'Tools' });
|
|
841
|
-
menuOpts.push({ key: '
|
|
842
|
-
menuOpts.push({ key: 'k', label: 'Login to Codex', section: 'Auth' });
|
|
841
|
+
menuOpts.push({ key: 'm', label: 'Manage subscriptions', section: 'Subscriptions' });
|
|
843
842
|
menuOpts.push({ key: 's', label: 'Settings', section: '' });
|
|
844
843
|
menuOpts.push({ key: 'q', label: 'Exit', section: '' });
|
|
845
844
|
console.log(menu(menuOpts));
|
|
@@ -885,17 +884,7 @@ async function mainScreen(rl, ask) {
|
|
|
885
884
|
return { next: 'main' };
|
|
886
885
|
}
|
|
887
886
|
|
|
888
|
-
if (choice === '
|
|
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
|
-
}
|
|
887
|
+
if (choice === 'm') { return { next: 'subscriptions' }; }
|
|
899
888
|
|
|
900
889
|
if (choice === 's') { return { next: 'settings' }; }
|
|
901
890
|
if (choice === 'q' || choice === 'exit') { return { next: 'exit' }; }
|
|
@@ -1014,116 +1003,103 @@ async function settingsScreen(rl, ask) {
|
|
|
1014
1003
|
// ─── Screen: subscriptionsScreen ─────────────────────────────────────────────
|
|
1015
1004
|
|
|
1016
1005
|
async function subscriptionsScreen(rl, ask) {
|
|
1006
|
+
console.clear();
|
|
1017
1007
|
const cwd = process.cwd();
|
|
1018
1008
|
const profile = loadProfile(cwd);
|
|
1019
1009
|
const auth = await detectAuth();
|
|
1020
1010
|
const plans = detectPlans();
|
|
1021
1011
|
|
|
1022
|
-
|
|
1023
|
-
const
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
: '
|
|
1028
|
-
|
|
1029
|
-
? (
|
|
1030
|
-
:
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
`
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1012
|
+
// Build status lines
|
|
1013
|
+
const lines = [];
|
|
1014
|
+
if (auth.claude.found) {
|
|
1015
|
+
const plan = plans.claude?.label || plans.claude?.plan || 'unknown plan';
|
|
1016
|
+
const sub = profile?.providers?.claude;
|
|
1017
|
+
const label = sub?.label ? ` [${sub.label}]` : '';
|
|
1018
|
+
const d = sub?.expiresAt ? daysUntil(sub.expiresAt) : null;
|
|
1019
|
+
const expiry = d !== null ? ` (${d < 0 ? 'expired' : d === 0 ? 'today' : `${d}d left`})` : '';
|
|
1020
|
+
lines.push(` ✅ Claude: ${plan}${label}${expiry}`);
|
|
1021
|
+
} else {
|
|
1022
|
+
lines.push(` ⚠️ Claude: not linked`);
|
|
1023
|
+
}
|
|
1024
|
+
if (auth.openai.found) {
|
|
1025
|
+
const plan = plans.openai?.label || plans.openai?.plan || 'unknown plan';
|
|
1026
|
+
const sub = profile?.providers?.openai;
|
|
1027
|
+
const label = sub?.label ? ` [${sub.label}]` : '';
|
|
1028
|
+
const d = sub?.expiresAt ? daysUntil(sub.expiresAt) : null;
|
|
1029
|
+
const expiry = d !== null ? ` (${d < 0 ? 'expired' : d === 0 ? 'today' : `${d}d left`})` : '';
|
|
1030
|
+
lines.push(` ✅ OpenAI: ${plan}${label}${expiry}`);
|
|
1031
|
+
} else {
|
|
1032
|
+
lines.push(` ⚠️ OpenAI: not linked`);
|
|
1033
|
+
}
|
|
1041
1034
|
|
|
1035
|
+
console.log(box('Subscriptions', lines));
|
|
1042
1036
|
console.log('');
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
{ key: '
|
|
1047
|
-
{ key: '
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
{ key: 'b', label: 'Back to settings', section: '' },
|
|
1051
|
-
]));
|
|
1037
|
+
|
|
1038
|
+
const menuOpts = [
|
|
1039
|
+
{ key: '1', label: 'Add Claude sub', section: 'Link' },
|
|
1040
|
+
{ key: '2', label: 'Add Codex sub', section: 'Link' },
|
|
1041
|
+
{ key: 'b', label: 'Back to home', section: '' },
|
|
1042
|
+
];
|
|
1043
|
+
console.log(menu(menuOpts));
|
|
1052
1044
|
console.log('');
|
|
1053
1045
|
|
|
1054
1046
|
const choice = (await ask(' Choice: ')).trim().toLowerCase();
|
|
1055
1047
|
|
|
1056
|
-
if (choice === '
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
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]) {
|
|
1048
|
+
if (choice === '1') {
|
|
1049
|
+
console.log('\n Linking Claude subscription...');
|
|
1050
|
+
console.log(' A browser window will open — paste the code below when prompted.\n');
|
|
1051
|
+
const { spawnSync } = await import('node:child_process');
|
|
1052
|
+
const r = spawnSync('claude', ['login'], { stdio: 'inherit', timeout: 60000 });
|
|
1053
|
+
if (r.status === 0) {
|
|
1054
|
+
console.log('\n ✅ Claude linked successfully!\n');
|
|
1055
|
+
const label = (await ask(" Label (e.g. \"Josh's $100 sub\", or Enter to skip): ")).trim();
|
|
1056
|
+
const expiry = await askExpiry(ask, 'Claude');
|
|
1057
|
+
const newPlans = detectPlans();
|
|
1058
|
+
const plan = newPlans.claude?.plan || 'pro';
|
|
1059
|
+
if (!profile.providers) profile.providers = {};
|
|
1079
1060
|
if (!profile.providers.claude) profile.providers.claude = { enabled: true };
|
|
1080
|
-
profile.providers.claude.plan =
|
|
1061
|
+
profile.providers.claude.plan = plan;
|
|
1081
1062
|
profile.providers.claude.enabled = true;
|
|
1063
|
+
if (label) profile.providers.claude.label = label;
|
|
1064
|
+
if (expiry) profile.providers.claude.expiresAt = expiry;
|
|
1082
1065
|
saveProfile(profile, { cwd });
|
|
1083
|
-
console.log(
|
|
1066
|
+
console.log(' ✓ Saved\n');
|
|
1067
|
+
await ask(' Press Enter to continue...');
|
|
1068
|
+
} else {
|
|
1069
|
+
console.log('\n ❌ Claude login failed or was cancelled.\n');
|
|
1070
|
+
await ask(' Press Enter to continue...');
|
|
1084
1071
|
}
|
|
1085
1072
|
return { next: 'subscriptions' };
|
|
1086
1073
|
}
|
|
1087
1074
|
|
|
1088
|
-
if (choice === '
|
|
1089
|
-
console.log('');
|
|
1090
|
-
console.log('
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1075
|
+
if (choice === '2') {
|
|
1076
|
+
console.log('\n Linking Codex subscription...');
|
|
1077
|
+
console.log(' A browser window will open — paste the code below when prompted.\n');
|
|
1078
|
+
const { spawnSync } = await import('node:child_process');
|
|
1079
|
+
const r = spawnSync('codex', ['login'], { stdio: 'inherit', timeout: 60000 });
|
|
1080
|
+
if (r.status === 0) {
|
|
1081
|
+
console.log('\n ✅ Codex linked successfully!\n');
|
|
1082
|
+
const label = (await ask(' Label (e.g. "Team Codex Pro", or Enter to skip): ')).trim();
|
|
1083
|
+
const expiry = await askExpiry(ask, 'Codex');
|
|
1084
|
+
const newPlans = detectPlans();
|
|
1085
|
+
const plan = newPlans.openai?.plan || 'plus';
|
|
1086
|
+
if (!profile.providers) profile.providers = {};
|
|
1097
1087
|
if (!profile.providers.openai) profile.providers.openai = { enabled: true };
|
|
1098
|
-
profile.providers.openai.plan =
|
|
1088
|
+
profile.providers.openai.plan = plan;
|
|
1099
1089
|
profile.providers.openai.enabled = true;
|
|
1090
|
+
if (label) profile.providers.openai.label = label;
|
|
1091
|
+
if (expiry) profile.providers.openai.expiresAt = expiry;
|
|
1100
1092
|
saveProfile(profile, { cwd });
|
|
1101
|
-
console.log(
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
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; }
|
|
1093
|
+
console.log(' ✓ Saved\n');
|
|
1094
|
+
await ask(' Press Enter to continue...');
|
|
1095
|
+
} else {
|
|
1096
|
+
console.log('\n ❌ Codex login failed or was cancelled.\n');
|
|
1097
|
+
await ask(' Press Enter to continue...');
|
|
1118
1098
|
}
|
|
1119
|
-
saveProfile(profile, { cwd });
|
|
1120
|
-
console.log(' Team config saved.');
|
|
1121
1099
|
return { next: 'subscriptions' };
|
|
1122
1100
|
}
|
|
1123
1101
|
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
return { next: 'subscriptions' };
|
|
1102
|
+
return { next: 'main' };
|
|
1127
1103
|
}
|
|
1128
1104
|
|
|
1129
1105
|
// ─── Screen: dashboardScreen (kept for internal reference, unreachable) ───────
|
package/package.json
CHANGED
package/src/decide.mjs
CHANGED
|
@@ -213,7 +213,7 @@ function getHealthScores(tier, cwd) {
|
|
|
213
213
|
* @returns {boolean}
|
|
214
214
|
*/
|
|
215
215
|
export function shouldDualBrain(detection, profile) {
|
|
216
|
-
const { intent = '', risk = 'low', complexity = 'simple' } = detection;
|
|
216
|
+
const { intent = '', risk = 'low', complexity = 'simple', designImpact = false } = detection;
|
|
217
217
|
const dualEnabled = profile?.dual_brain_enabled !== false;
|
|
218
218
|
const hasBothProviders = !!(
|
|
219
219
|
profile?.providers?.claude?.enabled &&
|
|
@@ -227,7 +227,7 @@ export function shouldDualBrain(detection, profile) {
|
|
|
227
227
|
const archOrSecurity = ['architecture', 'security'].includes(intent);
|
|
228
228
|
const complexHighRisk = complexity === 'complex' && risk === 'high';
|
|
229
229
|
|
|
230
|
-
return criticalRisk || archOrSecurity || complexHighRisk;
|
|
230
|
+
return criticalRisk || archOrSecurity || complexHighRisk || designImpact;
|
|
231
231
|
}
|
|
232
232
|
|
|
233
233
|
// ─── Internal: select model for provider ─────────────────────────────────────
|
package/src/detect.mjs
CHANGED
|
@@ -43,6 +43,13 @@ const RISK_KEYWORDS = [
|
|
|
43
43
|
{ level: 'low', regex: /\b(readme|docs?|comment|format|lint|changelog|typo|whitespace)\b/i },
|
|
44
44
|
];
|
|
45
45
|
|
|
46
|
+
const DESIGN_IMPACT_PATTERNS = [
|
|
47
|
+
/\bbin\/dual-brain\.mjs\b/,
|
|
48
|
+
/\bsrc\/(?:tui|profile|detect|decide|dispatch|session|health|index)\.mjs\b/,
|
|
49
|
+
/\bhooks\/(?:head-guard|enforce-tier|budget-balancer|dual-brain-think|dual-brain-review|wave-orchestrator)\.mjs\b/,
|
|
50
|
+
/\bVISION\.md\b/,
|
|
51
|
+
];
|
|
52
|
+
|
|
46
53
|
const LEVEL_ORDER = { critical: 3, high: 2, medium: 1, low: 0 };
|
|
47
54
|
|
|
48
55
|
// ─── Helpers / Exported functions ─────────────────────────────────────────────
|
|
@@ -152,6 +159,7 @@ function detectTask(input) {
|
|
|
152
159
|
const extractedPaths = extractPaths(prompt);
|
|
153
160
|
const allPaths = [...files, ...extractedPaths];
|
|
154
161
|
const { level: pathRiskLevel, riskyFiles } = classifyRisk(allPaths);
|
|
162
|
+
const designImpact = allPaths.some(p => DESIGN_IMPACT_PATTERNS.some(re => re.test(p)));
|
|
155
163
|
|
|
156
164
|
// 3. Keyword risk from description
|
|
157
165
|
let keywordRisk = 'low';
|
|
@@ -199,6 +207,7 @@ function detectTask(input) {
|
|
|
199
207
|
tier,
|
|
200
208
|
fileCount,
|
|
201
209
|
riskyFiles,
|
|
210
|
+
designImpact,
|
|
202
211
|
requiresWrite: requiresWrite(intent),
|
|
203
212
|
explanation,
|
|
204
213
|
};
|