dual-brain 0.3.21 → 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 +225 -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 {
|
|
@@ -2986,7 +3150,14 @@ async function mainScreen(rl, ask) {
|
|
|
2986
3150
|
try {
|
|
2987
3151
|
const { getOpenTasks } = await import('../dist/src/ledger.js');
|
|
2988
3152
|
const open = getOpenTasks(cwd);
|
|
2989
|
-
if (open.length > 0)
|
|
3153
|
+
if (open.length > 0) {
|
|
3154
|
+
const intent = String(open[0].intent || '').replace(/\s+/g, ' ').trim();
|
|
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
|
+
}
|
|
3160
|
+
}
|
|
2990
3161
|
} catch {}
|
|
2991
3162
|
suggestions = openTasks.length > 0
|
|
2992
3163
|
? [openTasks[0], 'review changes', 'run tests']
|
|
@@ -3164,7 +3335,7 @@ async function mainScreen(rl, ask) {
|
|
|
3164
3335
|
[`s`, 'settings & profiles'],
|
|
3165
3336
|
[`d`, 'doctor — diagnose issues'],
|
|
3166
3337
|
[`t`, 'team settings'],
|
|
3167
|
-
[`a`, profile
|
|
3338
|
+
[`a`, getEffectiveAutomode(profile, cwd) ? 'auto mode (on)' : 'auto mode (off)'],
|
|
3168
3339
|
[`q`, 'quit'],
|
|
3169
3340
|
];
|
|
3170
3341
|
for (const [key, label] of shortcuts) {
|
|
@@ -3303,6 +3474,15 @@ async function mainScreen(rl, ask) {
|
|
|
3303
3474
|
const cmd = classified.command;
|
|
3304
3475
|
const args = classified.args;
|
|
3305
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
|
+
|
|
3306
3486
|
if (cmd === 'resume' || cmd === 'r') {
|
|
3307
3487
|
if (recentSessions.length === 0) return { next: 'new-session' };
|
|
3308
3488
|
return { next: 'sessions' };
|
|
@@ -3368,11 +3548,7 @@ async function mainScreen(rl, ask) {
|
|
|
3368
3548
|
const num = parseInt(pick, 10);
|
|
3369
3549
|
if (!isNaN(num) && num >= 1 && num <= Math.min(results.length, 9)) {
|
|
3370
3550
|
const sess = results[num - 1];
|
|
3371
|
-
|
|
3372
|
-
const tool = sess.tool === 'codex' ? 'codex' : 'claude';
|
|
3373
|
-
const launchArgs = tool === 'codex' ? _codexResumeArgs(sess.id, cwd) : _claudeResumeArgs(sess.id, cwd);
|
|
3374
|
-
process.stdout.write(`\n Launching: ${tool} ${launchArgs.join(' ')}\n\n`);
|
|
3375
|
-
sp2(tool, launchArgs, { stdio: 'inherit' });
|
|
3551
|
+
return await launchSessionWithLease(sess, cwd, ask);
|
|
3376
3552
|
}
|
|
3377
3553
|
return { next: 'main' };
|
|
3378
3554
|
}
|
|
@@ -3384,13 +3560,13 @@ async function mainScreen(rl, ask) {
|
|
|
3384
3560
|
if (cmd === 'auto') {
|
|
3385
3561
|
const cwd2 = process.cwd();
|
|
3386
3562
|
const prof = loadProfile(cwd2);
|
|
3387
|
-
const nextAuto = !(prof
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
const state =
|
|
3392
|
-
process.stdout.write(`\n Automode: ${state}\n`);
|
|
3393
|
-
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`);
|
|
3394
3570
|
await ask(' Press Enter to continue...');
|
|
3395
3571
|
return { next: 'main' };
|
|
3396
3572
|
}
|
|
@@ -3488,7 +3664,7 @@ async function mainScreen(rl, ask) {
|
|
|
3488
3664
|
}
|
|
3489
3665
|
|
|
3490
3666
|
// Automode: if HEAD says it's safe, just go — no confirmation needed
|
|
3491
|
-
const automode = profile
|
|
3667
|
+
const automode = getEffectiveAutomode(profile, cwd);
|
|
3492
3668
|
if (automode) {
|
|
3493
3669
|
process.stdout.write(`\n \x1b[36m⚡\x1b[0m ${summary} (${model}, depth: ${hj?.depth || '?'})\n`);
|
|
3494
3670
|
return { next: 'go', prompt: input, model, _loopResult: hj._loopResult };
|
|
@@ -3515,13 +3691,7 @@ async function mainScreen(rl, ask) {
|
|
|
3515
3691
|
return { next: 'new-session' };
|
|
3516
3692
|
}
|
|
3517
3693
|
const sess = recentSessions[0];
|
|
3518
|
-
|
|
3519
|
-
const tool = _sessionTool(sess);
|
|
3520
|
-
const launchArgs = _sessionLaunchArgs(sess, cwd);
|
|
3521
|
-
process.stdout.write(`\n Launching: ${tool} ${launchArgs.join(' ')}\n\n`);
|
|
3522
|
-
spawnSync(tool, launchArgs, { stdio: 'inherit' });
|
|
3523
|
-
saveTerminalState(cwd, getTerminalId(), sess.id, sess.tool || 'claude');
|
|
3524
|
-
return { next: 'main' };
|
|
3694
|
+
return await launchSessionWithLease(sess, cwd, ask);
|
|
3525
3695
|
}
|
|
3526
3696
|
|
|
3527
3697
|
// Number 1-9 → resume that session
|
|
@@ -3536,13 +3706,7 @@ async function mainScreen(rl, ask) {
|
|
|
3536
3706
|
if (ctx.filesTouched.length > 0) process.stdout.write(` Files touched: ${ctx.filesTouched.join(', ')}\n`);
|
|
3537
3707
|
}
|
|
3538
3708
|
} catch {}
|
|
3539
|
-
|
|
3540
|
-
const tool = _sessionTool(sess);
|
|
3541
|
-
const launchArgs = _sessionLaunchArgs(sess, cwd);
|
|
3542
|
-
process.stdout.write(`\n Launching: ${tool} ${launchArgs.join(' ')}\n\n`);
|
|
3543
|
-
spawnSync(tool, launchArgs, { stdio: 'inherit' });
|
|
3544
|
-
saveTerminalState(cwd, getTerminalId(), sess.id, sess.tool || 'claude');
|
|
3545
|
-
return { next: 'main' };
|
|
3709
|
+
return await launchSessionWithLease(sess, cwd, ask);
|
|
3546
3710
|
}
|
|
3547
3711
|
|
|
3548
3712
|
if (choice === 'n') { return { next: 'new-session' }; }
|
|
@@ -3581,11 +3745,7 @@ async function mainScreen(rl, ask) {
|
|
|
3581
3745
|
const num = parseInt(pick, 10);
|
|
3582
3746
|
if (!isNaN(num) && num >= 1 && num <= Math.min(results.length, 9)) {
|
|
3583
3747
|
const sess = results[num - 1];
|
|
3584
|
-
|
|
3585
|
-
const tool = sess.tool === 'codex' ? 'codex' : 'claude';
|
|
3586
|
-
const launchArgs = tool === 'codex' ? _codexResumeArgs(sess.id, cwd) : _claudeResumeArgs(sess.id, cwd);
|
|
3587
|
-
process.stdout.write(`\n Launching: ${tool} ${launchArgs.join(' ')}\n\n`);
|
|
3588
|
-
spawnSync(tool, launchArgs, { stdio: 'inherit' });
|
|
3748
|
+
return await launchSessionWithLease(sess, cwd, ask);
|
|
3589
3749
|
}
|
|
3590
3750
|
return { next: 'main' };
|
|
3591
3751
|
}
|
|
@@ -3595,11 +3755,11 @@ async function mainScreen(rl, ask) {
|
|
|
3595
3755
|
if (choice === 'i') { return { next: 'import-picker' }; }
|
|
3596
3756
|
if (choice === 'a') {
|
|
3597
3757
|
const prof = loadProfile(cwd);
|
|
3598
|
-
const nextAuto = !(prof
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
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`);
|
|
3603
3763
|
await ask(' Press Enter to continue...');
|
|
3604
3764
|
return { next: 'main' };
|
|
3605
3765
|
}
|
|
@@ -4176,8 +4336,11 @@ async function settingsScreen(rl, ask) {
|
|
|
4176
4336
|
// Load current work style
|
|
4177
4337
|
const profile = loadProfile(cwd);
|
|
4178
4338
|
const currentBias = profile?.bias || profile?.mode || 'balanced';
|
|
4179
|
-
const automode = profile
|
|
4180
|
-
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';
|
|
4181
4344
|
|
|
4182
4345
|
// Work style current markers
|
|
4183
4346
|
const _stIsFast = ['cost-saver', 'auto', 'solo-claude', 'solo-openai'].includes(currentBias);
|
|
@@ -4225,8 +4388,8 @@ async function settingsScreen(rl, ask) {
|
|
|
4225
4388
|
? `${RED}bypass approvals and sandbox${RESET}`
|
|
4226
4389
|
: `${GREEN}safe approvals + workspace sandbox${RESET}`;
|
|
4227
4390
|
const convLines = [
|
|
4228
|
-
` ${DIM}Auto mode${RESET} ${autoMark} ${automode ? 'run safe tasks immediately' : 'ask before launching tasks'}`,
|
|
4229
|
-
` ${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}`,
|
|
4230
4393
|
` ${DIM}Claude resume${RESET} ${bypassPermissions ? '--dangerously-skip-permissions' : 'normal permissions'}`,
|
|
4231
4394
|
` ${DIM}Codex resume${RESET} ${bypassPermissions ? '--dangerously-bypass-approvals-and-sandbox' : 'workspace-write + on-request'}`,
|
|
4232
4395
|
];
|
|
@@ -4350,19 +4513,20 @@ async function settingsScreen(rl, ask) {
|
|
|
4350
4513
|
// Conversation behavior toggles
|
|
4351
4514
|
if (choice === 'o') {
|
|
4352
4515
|
const nextAuto = !automode;
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
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`);
|
|
4357
4520
|
await ask(' Press Enter to continue...');
|
|
4358
4521
|
return { next: 'settings' };
|
|
4359
4522
|
}
|
|
4360
4523
|
|
|
4361
4524
|
if (choice === 'v') {
|
|
4362
4525
|
if (bypassPermissions) {
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
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`);
|
|
4366
4530
|
await ask(' Press Enter to continue...');
|
|
4367
4531
|
return { next: 'settings' };
|
|
4368
4532
|
}
|
|
@@ -4371,9 +4535,10 @@ async function settingsScreen(rl, ask) {
|
|
|
4371
4535
|
process.stdout.write(' Use it only in trusted workspaces where the user explicitly accepts the risk.\n');
|
|
4372
4536
|
const confirm = (await ask(' Type YES to enable bypass mode: ')).trim();
|
|
4373
4537
|
if (confirm === 'YES') {
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
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`);
|
|
4377
4542
|
} else {
|
|
4378
4543
|
process.stdout.write('\n Permission mode unchanged.\n\n');
|
|
4379
4544
|
}
|
|
@@ -5804,13 +5969,7 @@ async function sessionsScreen(rl, ask) {
|
|
|
5804
5969
|
const sess = sessions[cursor];
|
|
5805
5970
|
cleanup();
|
|
5806
5971
|
process.stdout.write('\n');
|
|
5807
|
-
|
|
5808
|
-
const launchArgs = _sessionLaunchArgs(sess, cwd);
|
|
5809
|
-
process.stdout.write(`\n Launching: ${tool} ${launchArgs.join(' ')}\n\n`);
|
|
5810
|
-
const { spawnSync } = await import('node:child_process');
|
|
5811
|
-
spawnSync(tool, launchArgs, { stdio: 'inherit' });
|
|
5812
|
-
saveTerminalState(cwd, getTerminalId(), sess.id, sess.tool || 'claude');
|
|
5813
|
-
resolve({ next: 'main' });
|
|
5972
|
+
resolve(await launchSessionWithLease(sess, cwd, null));
|
|
5814
5973
|
return;
|
|
5815
5974
|
}
|
|
5816
5975
|
|
package/package.json
CHANGED