dual-brain 0.3.20 → 0.3.22

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 +12 -77
  2. package/package.json +1 -1
@@ -2719,81 +2719,8 @@ async function mainScreen(rl, ask) {
2719
2719
  const termW = process.stdout.columns || 80;
2720
2720
  const W = Math.min(termW - 2, 78); // usable content width
2721
2721
 
2722
- // ── Continuation card (interrupted work) ─────────────────────────────────
2723
- if (interrupted) {
2724
- if (_spinnerTimeout) clearTimeout(_spinnerTimeout);
2725
- if (dashSpinner) { try { dashSpinner.stop(); } catch {} dashSpinner = null; }
2726
- const DIM = '\x1b[2m', RST = '\x1b[0m', YLW = '\x1b[33m';
2727
- process.stdout.write(`\n ${YLW}Continue:${RST} ${interrupted.sessionName}\n`);
2728
- if (interrupted.lastState) {
2729
- process.stdout.write(` ${DIM}Last: ${interrupted.lastState} · ${interrupted.ageLabel}${RST}\n`);
2730
- } else {
2731
- process.stdout.write(` ${DIM}${interrupted.reason} · ${interrupted.ageLabel}${RST}\n`);
2732
- }
2733
- process.stdout.write(` \x1b[36mEnter\x1b[0m resume \x1b[36mn\x1b[0m new \x1b[36ms\x1b[0m skip\n\n`);
2734
-
2735
- // Wait for a keypress to decide what to do with the card
2736
- const readline2 = await import('node:readline');
2737
- readline2.emitKeypressEvents(process.stdin, rl);
2738
-
2739
- const cardChoice = await new Promise((resolve) => {
2740
- const wasRaw2 = process.stdin.isRaw;
2741
- const canRaw2 = process.stdin.isTTY && typeof process.stdin.setRawMode === 'function';
2742
- if (canRaw2) process.stdin.setRawMode(true);
2743
-
2744
- const cleanup2 = () => {
2745
- process.stdin.removeListener('keypress', onCardKey);
2746
- if (canRaw2) {
2747
- try { process.stdin.setRawMode(wasRaw2 || false); } catch {}
2748
- }
2749
- };
2750
-
2751
- const onCardKey = (str, key) => {
2752
- if (!key) return;
2753
- const name = key.name || '';
2754
- const seq = key.sequence || str || '';
2755
-
2756
- if (key.ctrl && (name === 'c' || name === 'd')) {
2757
- cleanup2();
2758
- process.stdout.write('\n');
2759
- resolve('q');
2760
- return;
2761
- }
2762
-
2763
- if (name === 'return' || name === 'enter' || seq === '\r' || seq === '\n') {
2764
- cleanup2();
2765
- process.stdout.write('\n');
2766
- resolve('resume');
2767
- return;
2768
- }
2769
-
2770
- if (!str || str.length === 0) return;
2771
- const lower = str.toLowerCase();
2772
- if (lower === 'n' || lower === 's' || lower === 'q') {
2773
- cleanup2();
2774
- process.stdout.write('\n');
2775
- resolve(lower);
2776
- return;
2777
- }
2778
- };
2779
-
2780
- process.stdin.on('keypress', onCardKey);
2781
- });
2782
-
2783
- if (cardChoice === 'q') return { next: 'exit' };
2784
-
2785
- if (cardChoice === 'resume') {
2786
- const { spawnSync } = await import('node:child_process');
2787
- process.stdout.write(` Launching: claude --resume ${interrupted.sessionId}\n\n`);
2788
- spawnSync('claude', _claudeResumeArgs(interrupted.sessionId, cwd), { stdio: 'inherit' });
2789
- saveTerminalState(cwd, getTerminalId(), interrupted.sessionId, 'claude');
2790
- return { next: 'main' };
2791
- }
2792
-
2793
- if (cardChoice === 'n') return { next: 'new-session' };
2794
-
2795
- // 's' → fall through to normal dashboard
2796
- }
2722
+ // Interrupted work is rendered as a dashboard signal below. New shells should
2723
+ // always land in the full menu instead of blocking on a pre-dashboard prompt.
2797
2724
 
2798
2725
  // ── Environment awareness (powers Box 1 dots + Box 3) ────────────────────
2799
2726
  let envReport = null;
@@ -3059,7 +2986,10 @@ async function mainScreen(rl, ask) {
3059
2986
  try {
3060
2987
  const { getOpenTasks } = await import('../dist/src/ledger.js');
3061
2988
  const open = getOpenTasks(cwd);
3062
- if (open.length > 0) openTasks.push(`continue: ${open[0].intent.slice(0, 30)}`);
2989
+ if (open.length > 0) {
2990
+ const intent = String(open[0].intent || '').replace(/\s+/g, ' ').trim();
2991
+ if (intent.length >= 5) openTasks.push(`continue: ${intent.slice(0, 30)}`);
2992
+ }
3063
2993
  } catch {}
3064
2994
  suggestions = openTasks.length > 0
3065
2995
  ? [openTasks[0], 'review changes', 'run tests']
@@ -3195,7 +3125,12 @@ async function mainScreen(rl, ask) {
3195
3125
  }
3196
3126
 
3197
3127
  // Resume / continuation hint
3198
- if (isReturning) {
3128
+ if (interrupted) {
3129
+ const labelTrunc = (interrupted.sessionName || 'last session').slice(0, 40);
3130
+ const agePart = interrupted.ageLabel ? ` ${DIM}${interrupted.ageLabel}${RST}` : '';
3131
+ const statePart = interrupted.lastState ? ` ${DIM}→ ${interrupted.lastState.slice(0, 48)}${RST}` : '';
3132
+ signalLines.push(`${CYAN}↩${RST} Resume: ${BOLD}${labelTrunc}${RST}${agePart}${statePart}`);
3133
+ } else if (isReturning) {
3199
3134
  const labelTrunc = (resumeState.label || 'last session').slice(0, 40);
3200
3135
  const agePart = resumeState.ageLabel ? ` ${DIM}${resumeState.ageLabel}${RST}` : '';
3201
3136
  const nextPart = resumeState.nextAction ? ` ${DIM}→ ${resumeState.nextAction}${RST}` : '';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dual-brain",
3
- "version": "0.3.20",
3
+ "version": "0.3.22",
4
4
  "description": "AI orchestration across Claude + OpenAI subscriptions — smart routing, budget awareness, and dual-brain collaboration",
5
5
  "type": "module",
6
6
  "bin": {