dual-brain 0.3.18 → 0.3.20

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.
@@ -355,6 +355,7 @@ Commands:
355
355
  --files a,b (specialist commands) Provide file context
356
356
  watch [dir] Monitor file changes and suggest actions
357
357
  --auto Auto-execute safe suggestions (tests, install)
358
+ menu Open the dual-brain shell menu
358
359
  shell-hook Output bash snippet to add dual-brain to your shell
359
360
  Usage: dual-brain shell-hook >> ~/.bashrc
360
361
 
@@ -2720,6 +2721,8 @@ async function mainScreen(rl, ask) {
2720
2721
 
2721
2722
  // ── Continuation card (interrupted work) ─────────────────────────────────
2722
2723
  if (interrupted) {
2724
+ if (_spinnerTimeout) clearTimeout(_spinnerTimeout);
2725
+ if (dashSpinner) { try { dashSpinner.stop(); } catch {} dashSpinner = null; }
2723
2726
  const DIM = '\x1b[2m', RST = '\x1b[0m', YLW = '\x1b[33m';
2724
2727
  process.stdout.write(`\n ${YLW}Continue:${RST} ${interrupted.sessionName}\n`);
2725
2728
  if (interrupted.lastState) {
@@ -6846,6 +6849,35 @@ async function main() {
6846
6849
  // Interactive-only commands: enter screen state machine (only when TTY)
6847
6850
  const isInteractive = process.stdin.isTTY;
6848
6851
 
6852
+ if (cmd === 'menu') {
6853
+ if (!isInteractive) {
6854
+ process.stderr.write('dual-brain menu requires an interactive terminal.\n');
6855
+ process.exit(1);
6856
+ }
6857
+ const cwd = process.cwd();
6858
+ cleanStaleMarkers(cwd);
6859
+ if (!process.argv.includes('--force') && checkLoopMarker(cwd)) {
6860
+ process.exit(0);
6861
+ }
6862
+ setLoopMarker(cwd);
6863
+ if (profileExists(cwd)) {
6864
+ await runScreens('main');
6865
+ } else {
6866
+ const auth = await detectAuth();
6867
+ const plans = detectPlans();
6868
+ const existingSessions = importReplitSessions(cwd);
6869
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
6870
+ const wizardProfile = await runOnboardingWizard({ auth, plans, existingSessions }, cwd, rl);
6871
+ if (wizardProfile) {
6872
+ saveProfile(wizardProfile, { cwd });
6873
+ await cmdInstall(cwd);
6874
+ }
6875
+ rl.close();
6876
+ await runScreens('main');
6877
+ }
6878
+ return;
6879
+ }
6880
+
6849
6881
  if (!cmd) {
6850
6882
  if (isInteractive) {
6851
6883
  const cwd = process.cwd();
@@ -7136,7 +7168,7 @@ fi
7136
7168
  // If cmd is not a recognized subcommand, treat the entire arg list as a task.
7137
7169
  // e.g. `dual-brain fix failing tests` → same as `dual-brain go "fix failing tests"`
7138
7170
  const KNOWN_COMMANDS = new Set([
7139
- 'init', 'install', 'uninstall', 'auth', 'go', 'do', 'plan', 'ship', 'think', 'review', 'pr', 'status', 'handoff', 'switch', 'hot', 'cool',
7171
+ 'menu', 'init', 'install', 'uninstall', 'auth', 'go', 'do', 'plan', 'ship', 'think', 'review', 'pr', 'status', 'handoff', 'switch', 'hot', 'cool',
7140
7172
  'remember', 'forget', 'break-glass', 'specialists', 'search', 'shell-hook', 'watch', 'update', 'upgrade',
7141
7173
  '--help', '-h', '--version', '-v',
7142
7174
  ...Object.keys(loadSpecialistRegistry()),
package/install.mjs CHANGED
@@ -1064,12 +1064,25 @@ function installDefaultShellLauncher(workspace, actions) {
1064
1064
  'fi',
1065
1065
  quietSetupEnd,
1066
1066
  ].join('\n');
1067
+ const quietSessionStart = '# >>> dual-brain quiet data-tools session manager >>>';
1068
+ const quietSessionEnd = '# <<< dual-brain quiet data-tools session manager <<<';
1069
+ const quietSessionBlock = [
1070
+ quietSessionStart,
1071
+ 'if [ -f "${SESSION_MANAGER}" ]; then',
1072
+ ' if [ "${DUAL_BRAIN_DEFAULT_SHELL}" = "true" ]; then',
1073
+ ' source "${SESSION_MANAGER}" >/dev/null 2>&1',
1074
+ ' else',
1075
+ ' source "${SESSION_MANAGER}"',
1076
+ ' fi',
1077
+ 'fi',
1078
+ quietSessionEnd,
1079
+ ].join('\n');
1067
1080
  const block = [
1068
1081
  start,
1069
1082
  '# Added by dual-brain install. Set DUAL_BRAIN_SKIP=1 to bypass.',
1070
1083
  'if [ -t 1 ] && [ -z "${DUAL_BRAIN_LOADED}" ] && [ -z "${DUAL_BRAIN_SKIP}" ]; then',
1071
1084
  ' export DUAL_BRAIN_LOADED=1',
1072
- ' command -v dual-brain >/dev/null 2>&1 && dual-brain',
1085
+ ' command -v dual-brain >/dev/null 2>&1 && dual-brain menu',
1073
1086
  'fi',
1074
1087
  end,
1075
1088
  ].join('\n');
@@ -1080,10 +1093,13 @@ function installDefaultShellLauncher(workspace, actions) {
1080
1093
  const existing = new RegExp(`${esc(start)}[\\s\\S]*?${esc(end)}\\n?`, 'm');
1081
1094
  const existingSuppress = new RegExp(`${esc(suppressStart)}[\\s\\S]*?${esc(suppressEnd)}\\n?`, 'm');
1082
1095
  const existingQuietSetup = new RegExp(`${esc(quietSetupStart)}[\\s\\S]*?${esc(quietSetupEnd)}\\n?`, 'm');
1096
+ const existingQuietSession = new RegExp(`${esc(quietSessionStart)}[\\s\\S]*?${esc(quietSessionEnd)}\\n?`, 'm');
1083
1097
  src = src.replace(existing, '');
1084
1098
  src = src.replace(existingSuppress, '');
1085
1099
  src = src.replace(existingQuietSetup, '');
1100
+ src = src.replace(existingQuietSession, '');
1086
1101
  src = src.replace(/\[ -f "\$\{SETUP_SCRIPT\}" \] && source "\$\{SETUP_SCRIPT\}"\n?/g, '');
1102
+ src = src.replace(/\[ -f "\$\{SESSION_MANAGER\}" \] && source "\$\{SESSION_MANAGER\}"\n?/g, '');
1087
1103
  const earlyMarker = '# Claude Code Setup';
1088
1104
  const sessionMarker = '# Session Manager (interactive menu)';
1089
1105
  if (src.includes(earlyMarker)) src = src.replace(earlyMarker, `${suppressBlock}\n\n${earlyMarker}`);
@@ -1091,6 +1107,8 @@ function installDefaultShellLauncher(workspace, actions) {
1091
1107
  else src = `${suppressBlock}\n\n${src.replace(/^\s*/, '')}`;
1092
1108
  const setupMarker = 'SETUP_SCRIPT="/home/runner/workspace/.replit-tools/scripts/setup-claude-code.sh"';
1093
1109
  if (src.includes(setupMarker)) src = src.replace(setupMarker, `${setupMarker}\n${quietSetupBlock}`);
1110
+ const sessionMgrMarker = 'SESSION_MANAGER="/home/runner/workspace/.replit-tools/scripts/claude-session-manager.sh"';
1111
+ if (src.includes(sessionMgrMarker)) src = src.replace(sessionMgrMarker, `${sessionMgrMarker}\n${quietSessionBlock}`);
1094
1112
  const marker = '# Auto-show menu on shell start';
1095
1113
  if (src.includes(marker)) src = src.replace(marker, `${block}\n\n${marker}`);
1096
1114
  else src = `${src.replace(/\s*$/, '\n\n')}${block}\n`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dual-brain",
3
- "version": "0.3.18",
3
+ "version": "0.3.20",
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/shell-hook.sh CHANGED
@@ -21,6 +21,6 @@ if [ -t 1 ] \
21
21
  && [ -z "$CLAUDE_MENU_LOADED" ]; then
22
22
  export DUAL_BRAIN_LOADED=1
23
23
  if command -v dual-brain &>/dev/null; then
24
- dual-brain
24
+ dual-brain menu
25
25
  fi
26
26
  fi