dual-brain 7.1.7 → 7.1.9

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.
@@ -598,13 +598,13 @@ async function welcomeScreen(rl, ask) {
598
598
  detectedLines.push(` ${existingSessions.length} session${existingSessions.length !== 1 ? 's' : ''} found from data-tools`);
599
599
  }
600
600
 
601
- // Re-print with full detection results
602
- console.log('\r\x1b[K'); // clear the partial output
603
- console.log('Detected:');
604
- for (const line of detectedLines) {
601
+ // Show detection results in a box
602
+ const detectedFormatted = detectedLines.map(line => {
605
603
  const ok = !line.includes('not logged');
606
- console.log(` ${ok ? '' : ''} ${line.trim()}`);
607
- }
604
+ return `${ok ? '' : '⚠️ '} ${line.trim()}`;
605
+ });
606
+ console.log('');
607
+ console.log(box(`🧠 Dual-Brain v${version} — Setup`, detectedFormatted));
608
608
  console.log('');
609
609
 
610
610
  if (!claudeReady && !openaiReady) {
@@ -775,20 +775,21 @@ async function mainScreen(rl, ask) {
775
775
  const claudeDays = daysUntil(claudeSub?.expiresAt);
776
776
  const openaiDays = daysUntil(openaiSub?.expiresAt);
777
777
 
778
- function subStatus(name, plan, found, expired, days, sub) {
779
- if (!found) return `${name}: not logged in`;
780
- let s = `${name}: ${plan} ✓`;
781
- if (sub?.label) s += ` [${sub.label}]`;
782
- if (expired) return `${name}: ${plan} expired${sub?.label ? ` [${sub.label}]` : ''}`;
783
- if (days !== null && days <= 7) s += ` (${days}d left)`;
784
- return s;
778
+ function subLine(name, plan, found, expired, days, sub) {
779
+ const label = sub?.label ? ` [${sub.label}]` : '';
780
+ if (!found) return `⚠️ ${name}: not logged in — run: ${name === 'Claude' ? 'claude login' : 'codex login'}`;
781
+ if (expired) return `🔴 ${name}: ${plan} expired${label} — will re-auth`;
782
+ const daysNote = (days !== null && days <= 7) ? ` (${days}d left)` : '';
783
+ return `✅ ${name}: ${plan}${label}${daysNote}`;
785
784
  }
786
785
 
787
- let claudeStatus = subStatus('Claude', claudePlan, auth.claude.found, claudeExpired, claudeDays, claudeSub);
788
- let openaiStatus = subStatus('OpenAI', openaiPlan, auth.openai.found, openaiExpired, openaiDays, openaiSub);
786
+ const headerLines = [
787
+ subLine('Claude', claudePlan, auth.claude.found, claudeExpired, claudeDays, claudeSub),
788
+ subLine('OpenAI', openaiPlan, auth.openai.found, openaiExpired, openaiDays, openaiSub),
789
+ ];
789
790
 
790
- console.log(`\ndual-brain v${version}`);
791
- console.log(`${claudeStatus} · ${openaiStatus}`);
791
+ console.log('');
792
+ console.log(box(`🧠 dual-brain v${version}`, headerLines));
792
793
 
793
794
  // Auto-refresh expired subscriptions
794
795
  if (claudeExpired || openaiExpired) {
@@ -819,7 +820,7 @@ async function mainScreen(rl, ask) {
819
820
  const recentSessions = enrichSessions(importReplitSessions(cwd), cwd).slice(0, 7);
820
821
 
821
822
  if (recentSessions.length > 0) {
822
- console.log('Recent:');
823
+ console.log(separator('Recent Sessions'));
823
824
  recentSessions.forEach((sess, i) => {
824
825
  const pin = sess.pinned ? '📌 ' : ' ';
825
826
  const active = sess.isActive ? ' ●' : '';
@@ -829,16 +830,19 @@ async function mainScreen(rl, ask) {
829
830
  console.log('');
830
831
  }
831
832
 
832
- console.log(' [c] Continue last session');
833
- console.log(' [n] New session');
833
+ const menuOpts = [];
834
+ menuOpts.push({ key: 'c', label: 'Continue last session', section: 'Sessions' });
835
+ menuOpts.push({ key: 'n', label: 'New session', section: 'Sessions' });
834
836
  if (recentSessions.length > 0) {
835
- console.log(' [1-9] Resume numbered above');
836
- }
837
- console.log(' [e] Manage sessions');
838
- console.log(' [d] Switch to data-tools');
839
- if (!auth.claude.found) console.log(' [j] Login to Claude');
840
- if (!auth.openai.found) console.log(' [k] Login to Codex');
841
- console.log(' [s] Settings [q] Exit');
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));
842
846
  console.log('');
843
847
 
844
848
  const choice = (await ask(' Choice: ')).trim().toLowerCase();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dual-brain",
3
- "version": "7.1.7",
3
+ "version": "7.1.9",
4
4
  "description": "AI orchestration across Claude + OpenAI subscriptions — smart routing, budget awareness, and dual-brain collaboration",
5
5
  "type": "module",
6
6
  "bin": {
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
  };