dual-brain 0.3.22 → 0.3.24
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 +222 -66
- package/package.json +1 -1
package/bin/dual-brain.mjs
CHANGED
|
@@ -26,21 +26,18 @@ import { detectTask, primeAgentRegistry } from '../dist/src/detect.js';
|
|
|
26
26
|
|
|
27
27
|
function _claudeResumeArgs(sessionId, cwd) {
|
|
28
28
|
const args = ['--resume', sessionId];
|
|
29
|
-
|
|
30
|
-
if (prof.bypassPermissions) args.push('--dangerously-skip-permissions');
|
|
29
|
+
if (getEffectiveBypassPermissions(cwd || process.cwd())) args.push('--dangerously-skip-permissions');
|
|
31
30
|
return args;
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
function _claudeNewArgs(cwd) {
|
|
35
34
|
const args = [];
|
|
36
|
-
|
|
37
|
-
if (prof.bypassPermissions) args.push('--dangerously-skip-permissions');
|
|
35
|
+
if (getEffectiveBypassPermissions(cwd || process.cwd())) args.push('--dangerously-skip-permissions');
|
|
38
36
|
return args;
|
|
39
37
|
}
|
|
40
38
|
|
|
41
39
|
function _codexResumeArgs(sessionId, cwd) {
|
|
42
|
-
|
|
43
|
-
if (prof.bypassPermissions) {
|
|
40
|
+
if (getEffectiveBypassPermissions(cwd || process.cwd())) {
|
|
44
41
|
return ['--dangerously-bypass-approvals-and-sandbox', 'resume', sessionId];
|
|
45
42
|
}
|
|
46
43
|
return ['--sandbox', 'workspace-write', '--ask-for-approval', 'on-request', 'resume', sessionId];
|
|
@@ -2084,6 +2081,168 @@ function loadTerminalState(cwd, terminalId) {
|
|
|
2084
2081
|
} catch { return null; }
|
|
2085
2082
|
}
|
|
2086
2083
|
|
|
2084
|
+
function sessionSettingsPath(cwd, terminalId = getTerminalId()) {
|
|
2085
|
+
return join(cwd, '.dualbrain', `session-settings-${terminalId}.json`);
|
|
2086
|
+
}
|
|
2087
|
+
|
|
2088
|
+
function loadSessionSettings(cwd, terminalId = getTerminalId()) {
|
|
2089
|
+
try {
|
|
2090
|
+
return JSON.parse(readFileSync(sessionSettingsPath(cwd, terminalId), 'utf8'));
|
|
2091
|
+
} catch { return {}; }
|
|
2092
|
+
}
|
|
2093
|
+
|
|
2094
|
+
function saveSessionSettings(cwd, settings, terminalId = getTerminalId()) {
|
|
2095
|
+
const dir = join(cwd, '.dualbrain');
|
|
2096
|
+
mkdirSync(dir, { recursive: true });
|
|
2097
|
+
writeFileSync(sessionSettingsPath(cwd, terminalId), JSON.stringify({
|
|
2098
|
+
...settings,
|
|
2099
|
+
terminalId,
|
|
2100
|
+
updatedAt: new Date().toISOString(),
|
|
2101
|
+
}, null, 2) + '\n');
|
|
2102
|
+
}
|
|
2103
|
+
|
|
2104
|
+
function getEffectiveAutomode(profile, cwd) {
|
|
2105
|
+
const session = loadSessionSettings(cwd || process.cwd());
|
|
2106
|
+
if (typeof session.automode === 'boolean') return session.automode;
|
|
2107
|
+
return profile.automode ?? profile.settings?.automode ?? false;
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
function getEffectiveBypassPermissions(cwd) {
|
|
2111
|
+
const workspace = cwd || process.cwd();
|
|
2112
|
+
const session = loadSessionSettings(workspace);
|
|
2113
|
+
if (typeof session.bypassPermissions === 'boolean') return session.bypassPermissions;
|
|
2114
|
+
const profile = loadProfile(workspace);
|
|
2115
|
+
return !!profile.bypassPermissions;
|
|
2116
|
+
}
|
|
2117
|
+
|
|
2118
|
+
function parseModeCommand(input) {
|
|
2119
|
+
const text = input.trim().toLowerCase().replace(/\s+/g, ' ');
|
|
2120
|
+
const wantsSession = /\b(this|current)\s+(session|conversation|terminal|chat)\b|\bfor this\b|\bfor current\b/.test(text);
|
|
2121
|
+
const wantsGlobal = /\b(default|global|profile|always|future sessions|all sessions)\b/.test(text);
|
|
2122
|
+
const scope = wantsGlobal && !wantsSession ? 'profile' : 'session';
|
|
2123
|
+
|
|
2124
|
+
let key = null;
|
|
2125
|
+
if (/\b(auto|automode|auto mode)\b/.test(text)) key = 'automode';
|
|
2126
|
+
if (/\b(bypass|permission|permissions|approval|approvals|sandbox|safe mode)\b/.test(text)) key = 'bypassPermissions';
|
|
2127
|
+
if (!key) return null;
|
|
2128
|
+
|
|
2129
|
+
let value = null;
|
|
2130
|
+
if (/\b(on|enable|enabled|yes|true)\b/.test(text)) value = true;
|
|
2131
|
+
if (/\b(off|disable|disabled|no|false)\b/.test(text)) value = false;
|
|
2132
|
+
if (key === 'bypassPermissions' && /\b(safe|sandbox|approval|approvals)\b/.test(text) && /\bmode\b/.test(text) && !/\bbypass\b/.test(text)) {
|
|
2133
|
+
value = false;
|
|
2134
|
+
}
|
|
2135
|
+
if (value === null) return null;
|
|
2136
|
+
|
|
2137
|
+
return { key, value, scope };
|
|
2138
|
+
}
|
|
2139
|
+
|
|
2140
|
+
function applyModeCommand(cmd, cwd) {
|
|
2141
|
+
if (cmd.scope === 'profile') {
|
|
2142
|
+
const profile = loadProfile(cwd);
|
|
2143
|
+
if (cmd.key === 'automode') {
|
|
2144
|
+
profile.automode = cmd.value;
|
|
2145
|
+
profile.settings = { ...(profile.settings || {}), automode: cmd.value };
|
|
2146
|
+
} else {
|
|
2147
|
+
profile.bypassPermissions = cmd.value;
|
|
2148
|
+
}
|
|
2149
|
+
saveProfile(profile, { cwd });
|
|
2150
|
+
return { scope: 'default', value: cmd.value };
|
|
2151
|
+
}
|
|
2152
|
+
|
|
2153
|
+
const settings = loadSessionSettings(cwd);
|
|
2154
|
+
settings[cmd.key] = cmd.value;
|
|
2155
|
+
saveSessionSettings(cwd, settings);
|
|
2156
|
+
return { scope: 'this session', value: cmd.value };
|
|
2157
|
+
}
|
|
2158
|
+
|
|
2159
|
+
function pidAlive(pid) {
|
|
2160
|
+
if (!pid || pid === process.pid) return false;
|
|
2161
|
+
try {
|
|
2162
|
+
process.kill(pid, 0);
|
|
2163
|
+
return true;
|
|
2164
|
+
} catch { return false; }
|
|
2165
|
+
}
|
|
2166
|
+
|
|
2167
|
+
function activeConversationPath(cwd) {
|
|
2168
|
+
return join(cwd, '.dualbrain', 'active-conversation.json');
|
|
2169
|
+
}
|
|
2170
|
+
|
|
2171
|
+
function readActiveConversation(cwd) {
|
|
2172
|
+
try {
|
|
2173
|
+
const lease = JSON.parse(readFileSync(activeConversationPath(cwd), 'utf8'));
|
|
2174
|
+
if (!lease?.sessionId) return null;
|
|
2175
|
+
if (!pidAlive(lease.ownerPid)) return null;
|
|
2176
|
+
return lease;
|
|
2177
|
+
} catch { return null; }
|
|
2178
|
+
}
|
|
2179
|
+
|
|
2180
|
+
function writeActiveConversation(cwd, session, tool) {
|
|
2181
|
+
const dir = join(cwd, '.dualbrain');
|
|
2182
|
+
const terminalId = getTerminalId();
|
|
2183
|
+
const lease = {
|
|
2184
|
+
conversationId: session.id,
|
|
2185
|
+
sessionId: session.id,
|
|
2186
|
+
provider: tool,
|
|
2187
|
+
terminalId,
|
|
2188
|
+
ownerPid: process.pid,
|
|
2189
|
+
startedAt: new Date().toISOString(),
|
|
2190
|
+
lastHeartbeat: new Date().toISOString(),
|
|
2191
|
+
mode: 'active-head',
|
|
2192
|
+
};
|
|
2193
|
+
mkdirSync(dir, { recursive: true });
|
|
2194
|
+
writeFileSync(activeConversationPath(cwd), JSON.stringify(lease, null, 2) + '\n');
|
|
2195
|
+
return lease;
|
|
2196
|
+
}
|
|
2197
|
+
|
|
2198
|
+
function clearActiveConversation(cwd, sessionId) {
|
|
2199
|
+
try {
|
|
2200
|
+
const lease = JSON.parse(readFileSync(activeConversationPath(cwd), 'utf8'));
|
|
2201
|
+
if (!sessionId || lease.sessionId === sessionId || lease.ownerPid === process.pid) {
|
|
2202
|
+
unlinkSync(activeConversationPath(cwd));
|
|
2203
|
+
}
|
|
2204
|
+
} catch {}
|
|
2205
|
+
}
|
|
2206
|
+
|
|
2207
|
+
async function confirmConversationTakeover(sess, cwd, ask) {
|
|
2208
|
+
const active = readActiveConversation(cwd);
|
|
2209
|
+
if (!active || active.sessionId !== sess.id || active.ownerPid === process.pid) return 'launch';
|
|
2210
|
+
const label = sess.smartName || sess.name || sess.prompts?.first || sess.firstPrompt || sess.id;
|
|
2211
|
+
process.stdout.write('\n');
|
|
2212
|
+
process.stdout.write(` Session already active: ${String(label).replace(/\s+/g, ' ').slice(0, 80)}\n`);
|
|
2213
|
+
process.stdout.write(` Owner: ${active.terminalId || 'unknown terminal'} · pid ${active.ownerPid} · ${active.provider || 'provider'}\n\n`);
|
|
2214
|
+
process.stdout.write(' Enter back t take over n new session\n\n');
|
|
2215
|
+
if (!ask) return 'cancel';
|
|
2216
|
+
const choice = (await ask(' Choice: ')).trim().toLowerCase();
|
|
2217
|
+
if (choice === 't' || choice === 'takeover' || choice === 'take over') {
|
|
2218
|
+
return 'takeover';
|
|
2219
|
+
}
|
|
2220
|
+
if (choice === 'n') return 'new';
|
|
2221
|
+
return 'cancel';
|
|
2222
|
+
}
|
|
2223
|
+
|
|
2224
|
+
async function launchSessionWithLease(sess, cwd, ask = null) {
|
|
2225
|
+
const decision = await confirmConversationTakeover(sess, cwd, ask);
|
|
2226
|
+
if (decision === 'new') return { next: 'new-session' };
|
|
2227
|
+
if (decision === 'cancel') return { next: 'main' };
|
|
2228
|
+
|
|
2229
|
+
const { spawnSync } = await import('node:child_process');
|
|
2230
|
+
const tool = _sessionTool(sess);
|
|
2231
|
+
const launchArgs = _sessionLaunchArgs(sess, cwd);
|
|
2232
|
+
if (decision === 'takeover') {
|
|
2233
|
+
process.stdout.write(' Taking over active conversation in this terminal.\n');
|
|
2234
|
+
}
|
|
2235
|
+
writeActiveConversation(cwd, sess, tool);
|
|
2236
|
+
process.stdout.write(`\n Launching: ${tool} ${launchArgs.join(' ')}\n\n`);
|
|
2237
|
+
try {
|
|
2238
|
+
spawnSync(tool, launchArgs, { stdio: 'inherit' });
|
|
2239
|
+
} finally {
|
|
2240
|
+
saveTerminalState(cwd, getTerminalId(), sess.id, sess.tool || tool);
|
|
2241
|
+
clearActiveConversation(cwd, sess.id);
|
|
2242
|
+
}
|
|
2243
|
+
return { next: 'main' };
|
|
2244
|
+
}
|
|
2245
|
+
|
|
2087
2246
|
// ─── PR Detection ─────────────────────────────────────────────────────────────
|
|
2088
2247
|
|
|
2089
2248
|
/**
|
|
@@ -2427,6 +2586,11 @@ function classifyInput(input) {
|
|
|
2427
2586
|
const cmd = parts[0].toLowerCase();
|
|
2428
2587
|
const args = parts.slice(1);
|
|
2429
2588
|
|
|
2589
|
+
const modeCommand = parseModeCommand(trimmed);
|
|
2590
|
+
if (modeCommand) {
|
|
2591
|
+
return { tier: 'free', command: 'mode', args: [], modeCommand };
|
|
2592
|
+
}
|
|
2593
|
+
|
|
2430
2594
|
// Tier 0: SKILL — slash commands (checked first, deterministic)
|
|
2431
2595
|
if (trimmed.startsWith('/')) {
|
|
2432
2596
|
try {
|
|
@@ -2988,7 +3152,11 @@ async function mainScreen(rl, ask) {
|
|
|
2988
3152
|
const open = getOpenTasks(cwd);
|
|
2989
3153
|
if (open.length > 0) {
|
|
2990
3154
|
const intent = String(open[0].intent || '').replace(/\s+/g, ' ').trim();
|
|
2991
|
-
|
|
3155
|
+
const words = intent.split(' ').filter(Boolean);
|
|
3156
|
+
const looksLikeAccidentalKeys = words.length > 0 && words.every(w => w.length === 1);
|
|
3157
|
+
if (intent.length >= 5 && !looksLikeAccidentalKeys) {
|
|
3158
|
+
openTasks.push(`continue: ${intent.slice(0, 30)}`);
|
|
3159
|
+
}
|
|
2992
3160
|
}
|
|
2993
3161
|
} catch {}
|
|
2994
3162
|
suggestions = openTasks.length > 0
|
|
@@ -3167,7 +3335,7 @@ async function mainScreen(rl, ask) {
|
|
|
3167
3335
|
[`s`, 'settings & profiles'],
|
|
3168
3336
|
[`d`, 'doctor — diagnose issues'],
|
|
3169
3337
|
[`t`, 'team settings'],
|
|
3170
|
-
[`a`, profile
|
|
3338
|
+
[`a`, getEffectiveAutomode(profile, cwd) ? 'auto mode (on)' : 'auto mode (off)'],
|
|
3171
3339
|
[`q`, 'quit'],
|
|
3172
3340
|
];
|
|
3173
3341
|
for (const [key, label] of shortcuts) {
|
|
@@ -3306,6 +3474,15 @@ async function mainScreen(rl, ask) {
|
|
|
3306
3474
|
const cmd = classified.command;
|
|
3307
3475
|
const args = classified.args;
|
|
3308
3476
|
|
|
3477
|
+
if (cmd === 'mode') {
|
|
3478
|
+
const result = applyModeCommand(classified.modeCommand, cwd);
|
|
3479
|
+
const keyLabel = classified.modeCommand.key === 'automode' ? 'Auto mode' : 'Bypass permissions';
|
|
3480
|
+
const state = result.value ? '\x1b[32mON\x1b[0m' : '\x1b[2mOFF\x1b[0m';
|
|
3481
|
+
process.stdout.write(`\n ${keyLabel}: ${state} \x1b[2m(${result.scope})\x1b[0m\n\n`);
|
|
3482
|
+
await ask(' Press Enter to continue...');
|
|
3483
|
+
return { next: 'main' };
|
|
3484
|
+
}
|
|
3485
|
+
|
|
3309
3486
|
if (cmd === 'resume' || cmd === 'r') {
|
|
3310
3487
|
if (recentSessions.length === 0) return { next: 'new-session' };
|
|
3311
3488
|
return { next: 'sessions' };
|
|
@@ -3371,11 +3548,7 @@ async function mainScreen(rl, ask) {
|
|
|
3371
3548
|
const num = parseInt(pick, 10);
|
|
3372
3549
|
if (!isNaN(num) && num >= 1 && num <= Math.min(results.length, 9)) {
|
|
3373
3550
|
const sess = results[num - 1];
|
|
3374
|
-
|
|
3375
|
-
const tool = sess.tool === 'codex' ? 'codex' : 'claude';
|
|
3376
|
-
const launchArgs = tool === 'codex' ? _codexResumeArgs(sess.id, cwd) : _claudeResumeArgs(sess.id, cwd);
|
|
3377
|
-
process.stdout.write(`\n Launching: ${tool} ${launchArgs.join(' ')}\n\n`);
|
|
3378
|
-
sp2(tool, launchArgs, { stdio: 'inherit' });
|
|
3551
|
+
return await launchSessionWithLease(sess, cwd, ask);
|
|
3379
3552
|
}
|
|
3380
3553
|
return { next: 'main' };
|
|
3381
3554
|
}
|
|
@@ -3387,13 +3560,13 @@ async function mainScreen(rl, ask) {
|
|
|
3387
3560
|
if (cmd === 'auto') {
|
|
3388
3561
|
const cwd2 = process.cwd();
|
|
3389
3562
|
const prof = loadProfile(cwd2);
|
|
3390
|
-
const nextAuto = !(prof
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
const state =
|
|
3395
|
-
process.stdout.write(`\n Automode: ${state}\n`);
|
|
3396
|
-
process.stdout.write(` ${
|
|
3563
|
+
const nextAuto = !getEffectiveAutomode(prof, cwd2);
|
|
3564
|
+
const settings = loadSessionSettings(cwd2);
|
|
3565
|
+
settings.automode = nextAuto;
|
|
3566
|
+
saveSessionSettings(cwd2, settings);
|
|
3567
|
+
const state = nextAuto ? '\x1b[32mON\x1b[0m' : '\x1b[2mOFF\x1b[0m';
|
|
3568
|
+
process.stdout.write(`\n Automode: ${state} \x1b[2m(this session)\x1b[0m\n`);
|
|
3569
|
+
process.stdout.write(` ${nextAuto ? 'Tasks dispatch immediately (HEAD still gates dangerous ops)' : 'Tasks require Enter to confirm'}\n\n`);
|
|
3397
3570
|
await ask(' Press Enter to continue...');
|
|
3398
3571
|
return { next: 'main' };
|
|
3399
3572
|
}
|
|
@@ -3491,7 +3664,7 @@ async function mainScreen(rl, ask) {
|
|
|
3491
3664
|
}
|
|
3492
3665
|
|
|
3493
3666
|
// Automode: if HEAD says it's safe, just go — no confirmation needed
|
|
3494
|
-
const automode = profile
|
|
3667
|
+
const automode = getEffectiveAutomode(profile, cwd);
|
|
3495
3668
|
if (automode) {
|
|
3496
3669
|
process.stdout.write(`\n \x1b[36m⚡\x1b[0m ${summary} (${model}, depth: ${hj?.depth || '?'})\n`);
|
|
3497
3670
|
return { next: 'go', prompt: input, model, _loopResult: hj._loopResult };
|
|
@@ -3518,13 +3691,7 @@ async function mainScreen(rl, ask) {
|
|
|
3518
3691
|
return { next: 'new-session' };
|
|
3519
3692
|
}
|
|
3520
3693
|
const sess = recentSessions[0];
|
|
3521
|
-
|
|
3522
|
-
const tool = _sessionTool(sess);
|
|
3523
|
-
const launchArgs = _sessionLaunchArgs(sess, cwd);
|
|
3524
|
-
process.stdout.write(`\n Launching: ${tool} ${launchArgs.join(' ')}\n\n`);
|
|
3525
|
-
spawnSync(tool, launchArgs, { stdio: 'inherit' });
|
|
3526
|
-
saveTerminalState(cwd, getTerminalId(), sess.id, sess.tool || 'claude');
|
|
3527
|
-
return { next: 'main' };
|
|
3694
|
+
return await launchSessionWithLease(sess, cwd, ask);
|
|
3528
3695
|
}
|
|
3529
3696
|
|
|
3530
3697
|
// Number 1-9 → resume that session
|
|
@@ -3539,13 +3706,7 @@ async function mainScreen(rl, ask) {
|
|
|
3539
3706
|
if (ctx.filesTouched.length > 0) process.stdout.write(` Files touched: ${ctx.filesTouched.join(', ')}\n`);
|
|
3540
3707
|
}
|
|
3541
3708
|
} catch {}
|
|
3542
|
-
|
|
3543
|
-
const tool = _sessionTool(sess);
|
|
3544
|
-
const launchArgs = _sessionLaunchArgs(sess, cwd);
|
|
3545
|
-
process.stdout.write(`\n Launching: ${tool} ${launchArgs.join(' ')}\n\n`);
|
|
3546
|
-
spawnSync(tool, launchArgs, { stdio: 'inherit' });
|
|
3547
|
-
saveTerminalState(cwd, getTerminalId(), sess.id, sess.tool || 'claude');
|
|
3548
|
-
return { next: 'main' };
|
|
3709
|
+
return await launchSessionWithLease(sess, cwd, ask);
|
|
3549
3710
|
}
|
|
3550
3711
|
|
|
3551
3712
|
if (choice === 'n') { return { next: 'new-session' }; }
|
|
@@ -3584,11 +3745,7 @@ async function mainScreen(rl, ask) {
|
|
|
3584
3745
|
const num = parseInt(pick, 10);
|
|
3585
3746
|
if (!isNaN(num) && num >= 1 && num <= Math.min(results.length, 9)) {
|
|
3586
3747
|
const sess = results[num - 1];
|
|
3587
|
-
|
|
3588
|
-
const tool = sess.tool === 'codex' ? 'codex' : 'claude';
|
|
3589
|
-
const launchArgs = tool === 'codex' ? _codexResumeArgs(sess.id, cwd) : _claudeResumeArgs(sess.id, cwd);
|
|
3590
|
-
process.stdout.write(`\n Launching: ${tool} ${launchArgs.join(' ')}\n\n`);
|
|
3591
|
-
spawnSync(tool, launchArgs, { stdio: 'inherit' });
|
|
3748
|
+
return await launchSessionWithLease(sess, cwd, ask);
|
|
3592
3749
|
}
|
|
3593
3750
|
return { next: 'main' };
|
|
3594
3751
|
}
|
|
@@ -3598,11 +3755,11 @@ async function mainScreen(rl, ask) {
|
|
|
3598
3755
|
if (choice === 'i') { return { next: 'import-picker' }; }
|
|
3599
3756
|
if (choice === 'a') {
|
|
3600
3757
|
const prof = loadProfile(cwd);
|
|
3601
|
-
const nextAuto = !(prof
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
process.stdout.write(`\n Automode: ${
|
|
3758
|
+
const nextAuto = !getEffectiveAutomode(prof, cwd);
|
|
3759
|
+
const settings = loadSessionSettings(cwd);
|
|
3760
|
+
settings.automode = nextAuto;
|
|
3761
|
+
saveSessionSettings(cwd, settings);
|
|
3762
|
+
process.stdout.write(`\n Automode: ${nextAuto ? '\x1b[32mON\x1b[0m' : '\x1b[2mOFF\x1b[0m'} \x1b[2m(this session)\x1b[0m\n\n`);
|
|
3606
3763
|
await ask(' Press Enter to continue...');
|
|
3607
3764
|
return { next: 'main' };
|
|
3608
3765
|
}
|
|
@@ -4179,8 +4336,11 @@ async function settingsScreen(rl, ask) {
|
|
|
4179
4336
|
// Load current work style
|
|
4180
4337
|
const profile = loadProfile(cwd);
|
|
4181
4338
|
const currentBias = profile?.bias || profile?.mode || 'balanced';
|
|
4182
|
-
const automode = profile
|
|
4183
|
-
const
|
|
4339
|
+
const automode = getEffectiveAutomode(profile, cwd);
|
|
4340
|
+
const sessionSettings = loadSessionSettings(cwd);
|
|
4341
|
+
const bypassPermissions = getEffectiveBypassPermissions(cwd);
|
|
4342
|
+
const autoScope = typeof sessionSettings.automode === 'boolean' ? 'session' : 'default';
|
|
4343
|
+
const permissionScope = typeof sessionSettings.bypassPermissions === 'boolean' ? 'session' : 'default';
|
|
4184
4344
|
|
|
4185
4345
|
// Work style current markers
|
|
4186
4346
|
const _stIsFast = ['cost-saver', 'auto', 'solo-claude', 'solo-openai'].includes(currentBias);
|
|
@@ -4228,8 +4388,8 @@ async function settingsScreen(rl, ask) {
|
|
|
4228
4388
|
? `${RED}bypass approvals and sandbox${RESET}`
|
|
4229
4389
|
: `${GREEN}safe approvals + workspace sandbox${RESET}`;
|
|
4230
4390
|
const convLines = [
|
|
4231
|
-
` ${DIM}Auto mode${RESET} ${autoMark} ${automode ? 'run safe tasks immediately' : 'ask before launching tasks'}`,
|
|
4232
|
-
` ${DIM}Permissions${RESET} ${permMark} ${permMode}`,
|
|
4391
|
+
` ${DIM}Auto mode${RESET} ${autoMark} ${automode ? 'run safe tasks immediately' : 'ask before launching tasks'} ${DIM}[${autoScope}]${RESET}`,
|
|
4392
|
+
` ${DIM}Permissions${RESET} ${permMark} ${permMode} ${DIM}[${permissionScope}]${RESET}`,
|
|
4233
4393
|
` ${DIM}Claude resume${RESET} ${bypassPermissions ? '--dangerously-skip-permissions' : 'normal permissions'}`,
|
|
4234
4394
|
` ${DIM}Codex resume${RESET} ${bypassPermissions ? '--dangerously-bypass-approvals-and-sandbox' : 'workspace-write + on-request'}`,
|
|
4235
4395
|
];
|
|
@@ -4353,19 +4513,20 @@ async function settingsScreen(rl, ask) {
|
|
|
4353
4513
|
// Conversation behavior toggles
|
|
4354
4514
|
if (choice === 'o') {
|
|
4355
4515
|
const nextAuto = !automode;
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
process.stdout.write(`\n Auto mode: ${nextAuto ? GREEN + 'ON' + RESET : DIM + 'OFF' + RESET}\n\n`);
|
|
4516
|
+
const settings = loadSessionSettings(cwd);
|
|
4517
|
+
settings.automode = nextAuto;
|
|
4518
|
+
saveSessionSettings(cwd, settings);
|
|
4519
|
+
process.stdout.write(`\n Auto mode: ${nextAuto ? GREEN + 'ON' + RESET : DIM + 'OFF' + RESET} ${DIM}(this session)${RESET}\n\n`);
|
|
4360
4520
|
await ask(' Press Enter to continue...');
|
|
4361
4521
|
return { next: 'settings' };
|
|
4362
4522
|
}
|
|
4363
4523
|
|
|
4364
4524
|
if (choice === 'v') {
|
|
4365
4525
|
if (bypassPermissions) {
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4526
|
+
const settings = loadSessionSettings(cwd);
|
|
4527
|
+
settings.bypassPermissions = false;
|
|
4528
|
+
saveSessionSettings(cwd, settings);
|
|
4529
|
+
process.stdout.write(`\n Permission mode: ${GREEN}safe approvals + workspace sandbox${RESET} ${DIM}(this session)${RESET}\n\n`);
|
|
4369
4530
|
await ask(' Press Enter to continue...');
|
|
4370
4531
|
return { next: 'settings' };
|
|
4371
4532
|
}
|
|
@@ -4374,9 +4535,10 @@ async function settingsScreen(rl, ask) {
|
|
|
4374
4535
|
process.stdout.write(' Use it only in trusted workspaces where the user explicitly accepts the risk.\n');
|
|
4375
4536
|
const confirm = (await ask(' Type YES to enable bypass mode: ')).trim();
|
|
4376
4537
|
if (confirm === 'YES') {
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4538
|
+
const settings = loadSessionSettings(cwd);
|
|
4539
|
+
settings.bypassPermissions = true;
|
|
4540
|
+
saveSessionSettings(cwd, settings);
|
|
4541
|
+
process.stdout.write(`\n Permission mode: ${RED}bypass approvals and sandbox${RESET} ${DIM}(this session)${RESET}\n\n`);
|
|
4380
4542
|
} else {
|
|
4381
4543
|
process.stdout.write('\n Permission mode unchanged.\n\n');
|
|
4382
4544
|
}
|
|
@@ -5807,13 +5969,7 @@ async function sessionsScreen(rl, ask) {
|
|
|
5807
5969
|
const sess = sessions[cursor];
|
|
5808
5970
|
cleanup();
|
|
5809
5971
|
process.stdout.write('\n');
|
|
5810
|
-
|
|
5811
|
-
const launchArgs = _sessionLaunchArgs(sess, cwd);
|
|
5812
|
-
process.stdout.write(`\n Launching: ${tool} ${launchArgs.join(' ')}\n\n`);
|
|
5813
|
-
const { spawnSync } = await import('node:child_process');
|
|
5814
|
-
spawnSync(tool, launchArgs, { stdio: 'inherit' });
|
|
5815
|
-
saveTerminalState(cwd, getTerminalId(), sess.id, sess.tool || 'claude');
|
|
5816
|
-
resolve({ next: 'main' });
|
|
5972
|
+
resolve(await launchSessionWithLease(sess, cwd, null));
|
|
5817
5973
|
return;
|
|
5818
5974
|
}
|
|
5819
5975
|
|
package/package.json
CHANGED