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.
- package/bin/dual-brain.mjs +12 -77
- package/package.json +1 -1
package/bin/dual-brain.mjs
CHANGED
|
@@ -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
|
-
//
|
|
2723
|
-
|
|
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)
|
|
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 (
|
|
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