dual-brain 3.4.0 → 3.5.0
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/hooks/control-panel.mjs +141 -96
- package/install.mjs +1 -0
- package/package.json +1 -1
package/hooks/control-panel.mjs
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* control-panel.mjs — Session
|
|
3
|
+
* control-panel.mjs — Session launcher for Dual-Brain.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Progressive disclosure: first-run shows minimal menu (new/shell + auth).
|
|
6
|
+
* Returning users see recent sessions, profile mode, cost alert settings.
|
|
7
|
+
* Loops until user exits to shell.
|
|
7
8
|
*/
|
|
8
9
|
|
|
9
10
|
import readline from 'readline';
|
|
@@ -14,6 +15,7 @@ import { spawnSync } from 'child_process';
|
|
|
14
15
|
|
|
15
16
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
16
17
|
const PROFILE_FILE = join(__dirname, '..', 'dual-brain.profile.json');
|
|
18
|
+
const LAUNCHED_MARKER = join(__dirname, '..', '.launched');
|
|
17
19
|
const VERSION = (() => {
|
|
18
20
|
try { return JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8')).version; } catch {}
|
|
19
21
|
return '?';
|
|
@@ -32,16 +34,15 @@ const dim = s => e('2', s);
|
|
|
32
34
|
const cyan = s => e('36', s);
|
|
33
35
|
const green = s => e('32', s);
|
|
34
36
|
const yellow = s => e('33', s);
|
|
35
|
-
const magenta = s => e('95', s);
|
|
36
37
|
const orange = s => e('1;38;5;208', s);
|
|
37
38
|
const blue = s => e('1;38;5;33', s);
|
|
38
39
|
|
|
39
40
|
// ─── Profiles ──────────────────────────────────────────────────────────────
|
|
40
41
|
|
|
41
42
|
const PROFILES = {
|
|
42
|
-
balanced: { emoji: '⚖️',
|
|
43
|
-
'cost-saver': { emoji: '💸',
|
|
44
|
-
'quality-first': { emoji: '💎',
|
|
43
|
+
balanced: { emoji: '⚖️', uiLabel: 'Default', desc: 'Auto-routes by complexity, standard alerts' },
|
|
44
|
+
'cost-saver': { emoji: '💸', uiLabel: 'Cost-saver', desc: 'Prefers cheaper models, tighter alerts' },
|
|
45
|
+
'quality-first': { emoji: '💎', uiLabel: 'Quality-first', desc: 'Uses best models, dual-brain for medium+ risk' },
|
|
45
46
|
};
|
|
46
47
|
|
|
47
48
|
const PROFILE_BUDGETS = {
|
|
@@ -55,9 +56,9 @@ function loadProfile() {
|
|
|
55
56
|
const data = JSON.parse(readFileSync(PROFILE_FILE, 'utf8'));
|
|
56
57
|
const name = data.active && PROFILES[data.active] ? data.active : 'balanced';
|
|
57
58
|
const custom = data.custom_overrides || {};
|
|
58
|
-
return { name, budgets: { ...PROFILE_BUDGETS[name], ...custom.budgets } };
|
|
59
|
+
return { name, budgets: { ...PROFILE_BUDGETS[name], ...custom.budgets }, hasCustomBudget: !!custom.budgets };
|
|
59
60
|
} catch {
|
|
60
|
-
return { name: 'balanced', budgets: PROFILE_BUDGETS.balanced };
|
|
61
|
+
return { name: 'balanced', budgets: PROFILE_BUDGETS.balanced, hasCustomBudget: false };
|
|
61
62
|
}
|
|
62
63
|
}
|
|
63
64
|
|
|
@@ -69,6 +70,25 @@ function saveProfile(name, customOverrides) {
|
|
|
69
70
|
renameSync(tmp, PROFILE_FILE);
|
|
70
71
|
}
|
|
71
72
|
|
|
73
|
+
// ─── First-Run Detection ──────────────────────────────────────────────────
|
|
74
|
+
|
|
75
|
+
function isFirstRun() {
|
|
76
|
+
if (existsSync(LAUNCHED_MARKER)) return false;
|
|
77
|
+
// Also check Claude history for any session in this workspace
|
|
78
|
+
const historyFile = join(HOME, '.claude', 'history.jsonl');
|
|
79
|
+
if (existsSync(historyFile)) {
|
|
80
|
+
try {
|
|
81
|
+
const content = readFileSync(historyFile, 'utf8');
|
|
82
|
+
if (content.includes('"sessionId"')) return false;
|
|
83
|
+
} catch {}
|
|
84
|
+
}
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function markLaunched() {
|
|
89
|
+
try { writeFileSync(LAUNCHED_MARKER, new Date().toISOString() + '\n'); } catch {}
|
|
90
|
+
}
|
|
91
|
+
|
|
72
92
|
// ─── Provider Detection ───────────────────────────────────────────────────
|
|
73
93
|
|
|
74
94
|
function detectProviders() {
|
|
@@ -122,17 +142,13 @@ function getRecentSessions() {
|
|
|
122
142
|
return true;
|
|
123
143
|
};
|
|
124
144
|
|
|
125
|
-
// Claude sessions
|
|
126
145
|
const historyFile = join(HOME, '.claude', 'history.jsonl');
|
|
127
146
|
if (existsSync(historyFile)) {
|
|
128
147
|
try {
|
|
129
148
|
const lines = readFileSync(historyFile, 'utf8').trim().split('\n');
|
|
130
149
|
const entries = [];
|
|
131
150
|
for (const line of lines) {
|
|
132
|
-
try {
|
|
133
|
-
const j = JSON.parse(line);
|
|
134
|
-
if (j.sessionId && j.timestamp) entries.push(j);
|
|
135
|
-
} catch {}
|
|
151
|
+
try { const j = JSON.parse(line); if (j.sessionId && j.timestamp) entries.push(j); } catch {}
|
|
136
152
|
}
|
|
137
153
|
entries.sort((a, b) => a.timestamp - b.timestamp);
|
|
138
154
|
for (const j of entries) {
|
|
@@ -151,7 +167,6 @@ function getRecentSessions() {
|
|
|
151
167
|
} catch {}
|
|
152
168
|
}
|
|
153
169
|
|
|
154
|
-
// Codex sessions
|
|
155
170
|
const codexDir = join(HOME, '.codex', 'sessions');
|
|
156
171
|
if (existsSync(codexDir)) {
|
|
157
172
|
const walk = (dir) => {
|
|
@@ -230,68 +245,86 @@ function countRunning() {
|
|
|
230
245
|
return { claude, codex };
|
|
231
246
|
}
|
|
232
247
|
|
|
233
|
-
// ───
|
|
248
|
+
// ─── Cost Alert Label ─────────────────────────────────────────────────────
|
|
234
249
|
|
|
235
|
-
function
|
|
236
|
-
if (
|
|
237
|
-
|
|
250
|
+
function costAlertLabel(profile) {
|
|
251
|
+
if (profile.hasCustomBudget) return 'Custom';
|
|
252
|
+
if (profile.name === 'balanced') return 'Default';
|
|
253
|
+
if (profile.name === 'cost-saver') return 'Tight';
|
|
254
|
+
if (profile.name === 'quality-first') return 'Relaxed';
|
|
255
|
+
return 'Default';
|
|
238
256
|
}
|
|
239
257
|
|
|
240
|
-
// ─── Menu
|
|
241
|
-
|
|
242
|
-
function renderMenu() {
|
|
243
|
-
const providers = detectProviders();
|
|
244
|
-
const profile = loadProfile();
|
|
245
|
-
const sessions = getRecentSessions();
|
|
246
|
-
const running = countRunning();
|
|
247
|
-
const pf = PROFILES[profile.name];
|
|
248
|
-
const hasReplitTools = checkReplitTools();
|
|
258
|
+
// ─── Menu Renderers ───────────────────────────────────────────────────────
|
|
249
259
|
|
|
260
|
+
function renderFirstRunMenu(providers) {
|
|
250
261
|
const lines = [];
|
|
251
262
|
|
|
252
263
|
lines.push('');
|
|
253
264
|
lines.push(` 🧠 ${bold(`Dual-Brain v${VERSION}`)}`);
|
|
254
265
|
lines.push('');
|
|
255
266
|
|
|
256
|
-
//
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
267
|
+
// Provider status
|
|
268
|
+
const cStat = providers.claude.authed ? '✅' : providers.claude.installed ? '⚠️' : '❌';
|
|
269
|
+
const xStat = providers.codex.authed ? '✅' : providers.codex.installed ? '⚠️' : '❌';
|
|
270
|
+
lines.push(` 🟠 Claude ${cStat} 🟢 Codex ${xStat}`);
|
|
271
|
+
|
|
272
|
+
if (providers.claude.authed && providers.codex.authed) {
|
|
273
|
+
lines.push(` ${green('Both providers ready — full dual-brain mode')}`);
|
|
274
|
+
} else if (providers.claude.authed) {
|
|
275
|
+
lines.push(` ${dim('Claude ready. Add Codex for dual-brain features.')}`);
|
|
276
|
+
} else if (!providers.claude.installed) {
|
|
277
|
+
lines.push(` ${yellow('Claude not found — needed to start.')}`);
|
|
261
278
|
} else {
|
|
262
|
-
lines.push(`
|
|
263
|
-
lines.push(` │ ${cyan('npx dual-brain')} = this menu │`);
|
|
279
|
+
lines.push(` ${yellow('Claude needs login to start.')}`);
|
|
264
280
|
}
|
|
265
|
-
lines.push(` │ ${cyan('j')} = login to Claude │`);
|
|
266
|
-
lines.push(` │ ${cyan('k')} = login to Codex │`);
|
|
267
|
-
lines.push(' ├─────────────────────────────┤');
|
|
268
|
-
lines.push(` │ ${orange('In Claude session:')} │`);
|
|
269
|
-
lines.push(` │ ${green('Ctrl+C x2')} = back to menu │`);
|
|
270
|
-
lines.push(` │ ${green('Ctrl+C x3')} = exit to shell │`);
|
|
271
|
-
lines.push(' └─────────────────────────────┘');
|
|
272
|
-
lines.push('');
|
|
273
281
|
|
|
274
|
-
|
|
275
|
-
const cStat = providers.claude.authed ? '✅' : providers.claude.installed ? '⚠️' : '❌';
|
|
276
|
-
const xStat = providers.codex.authed ? '✅' : providers.codex.installed ? '⚠️' : '❌';
|
|
277
|
-
lines.push(` 🟠 Claude ${cStat} 🟢 Codex ${xStat} ${pf.emoji} ${bold(pf.label)} ${dim('$' + profile.budgets.session_limit_usd + '/session')}`);
|
|
282
|
+
lines.push('');
|
|
278
283
|
|
|
279
|
-
//
|
|
284
|
+
// Auth actions if needed
|
|
280
285
|
if (!providers.claude.authed || !providers.codex.authed) {
|
|
286
|
+
if (!providers.claude.installed) {
|
|
287
|
+
lines.push(` ${dim('Install Claude:')} ${cyan('curl -fsSL https://claude.ai/install.sh | sh')}`);
|
|
288
|
+
}
|
|
289
|
+
if (!providers.claude.authed && providers.claude.installed) {
|
|
290
|
+
lines.push(` ${bold('[j]')} Sign in to Claude`);
|
|
291
|
+
}
|
|
292
|
+
if (!providers.codex.installed) {
|
|
293
|
+
lines.push(` ${dim('Install Codex:')} ${cyan('npm i -g @openai/codex')}`);
|
|
294
|
+
} else if (!providers.codex.authed) {
|
|
295
|
+
lines.push(` ${bold('[k]')} Sign in to Codex ${dim('(optional — enables GPT collaboration)')}`);
|
|
296
|
+
}
|
|
281
297
|
lines.push('');
|
|
282
|
-
if (!providers.claude.installed) lines.push(` ${dim('└')} Install Claude: ${cyan('curl -fsSL https://claude.ai/install.sh | sh')}`);
|
|
283
|
-
else if (!providers.claude.authed) lines.push(` ${dim('└')} Auth Claude: press ${bold('j')} below`);
|
|
284
|
-
if (!providers.codex.installed) lines.push(` ${dim('└')} Install Codex: ${cyan('npm i -g @openai/codex')}`);
|
|
285
|
-
else if (!providers.codex.authed) lines.push(` ${dim('└')} Auth Codex: press ${bold('k')} below`);
|
|
286
298
|
}
|
|
287
299
|
|
|
288
300
|
// Replit-tools check
|
|
289
|
-
if (IS_REPLIT && !
|
|
290
|
-
lines.push('');
|
|
291
|
-
lines.push(` ⚠️ ${yellow('replit-tools not found')} — recommended for Replit environments`);
|
|
292
|
-
lines.push(` ${dim('└')} Press ${bold('t')} to install replit-tools`);
|
|
301
|
+
if (IS_REPLIT && !existsSync(join(CWD, '.replit-tools'))) {
|
|
302
|
+
lines.push(` ${bold('[t]')} Install replit-tools ${dim('(recommended for Replit)')}`);
|
|
293
303
|
}
|
|
294
304
|
|
|
305
|
+
// Primary actions
|
|
306
|
+
lines.push(` ${bold('[n]')} Start new session`);
|
|
307
|
+
lines.push(` ${bold('[s]')} Skip — just shell`);
|
|
308
|
+
lines.push('');
|
|
309
|
+
|
|
310
|
+
return lines;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function renderReturningMenu(providers, sessions) {
|
|
314
|
+
const profile = loadProfile();
|
|
315
|
+
const pf = PROFILES[profile.name];
|
|
316
|
+
const running = countRunning();
|
|
317
|
+
const lines = [];
|
|
318
|
+
|
|
319
|
+
lines.push('');
|
|
320
|
+
lines.push(` 🧠 ${bold(`Dual-Brain v${VERSION}`)}`);
|
|
321
|
+
lines.push('');
|
|
322
|
+
|
|
323
|
+
// Compact provider + mode line
|
|
324
|
+
const cStat = providers.claude.authed ? '✅' : '⚠️';
|
|
325
|
+
const xStat = providers.codex.authed ? '✅' : providers.codex.installed ? '⚠️' : '❌';
|
|
326
|
+
lines.push(` 🟠 Claude ${cStat} 🟢 Codex ${xStat} ${pf.emoji} ${bold(pf.uiLabel)}`);
|
|
327
|
+
|
|
295
328
|
// Recent sessions
|
|
296
329
|
if (sessions.length > 0) {
|
|
297
330
|
lines.push('');
|
|
@@ -305,31 +338,29 @@ function renderMenu() {
|
|
|
305
338
|
}
|
|
306
339
|
}
|
|
307
340
|
|
|
308
|
-
// Session manager box
|
|
309
341
|
lines.push('');
|
|
310
|
-
lines.push(' ┌─────────────────────────────┐');
|
|
311
|
-
lines.push(' │ 🧠 Dual-Brain Session Mgr │');
|
|
312
|
-
lines.push(' └─────────────────────────────┘');
|
|
313
342
|
|
|
314
343
|
const runParts = [];
|
|
315
344
|
if (running.claude > 0) runParts.push(`${running.claude} claude`);
|
|
316
345
|
if (running.codex > 0) runParts.push(`${running.codex} codex`);
|
|
317
346
|
if (runParts.length > 0) lines.push(` ${dim('(' + runParts.join(', ') + ' running)')}`);
|
|
318
|
-
lines.push('');
|
|
319
347
|
|
|
320
348
|
// Menu options
|
|
321
349
|
lines.push(` ${bold('[c]')} Continue last session`);
|
|
322
350
|
if (sessions.length > 0) lines.push(` ${bold('[1-9]')} Resume numbered above`);
|
|
323
351
|
lines.push(` ${bold('[n]')} New session`);
|
|
324
|
-
lines.push(` ${bold('[p]')}
|
|
325
|
-
lines.push(` ${bold('[b]')}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
if (
|
|
329
|
-
lines.push(` ${bold('[
|
|
352
|
+
lines.push(` ${bold('[p]')} Mode: ${dim(pf.uiLabel)}`);
|
|
353
|
+
lines.push(` ${bold('[b]')} Cost alerts: ${dim(costAlertLabel(profile))}`);
|
|
354
|
+
|
|
355
|
+
// Auth if needed
|
|
356
|
+
if (!providers.claude.authed) lines.push(` ${bold('[j]')} Sign in to Claude`);
|
|
357
|
+
if (providers.codex.installed && !providers.codex.authed) lines.push(` ${bold('[k]')} Sign in to Codex`);
|
|
358
|
+
if (IS_REPLIT && !existsSync(join(CWD, '.replit-tools'))) lines.push(` ${bold('[t]')} Install replit-tools`);
|
|
359
|
+
|
|
360
|
+
lines.push(` ${bold('[s]')} Shell`);
|
|
330
361
|
lines.push('');
|
|
331
362
|
|
|
332
|
-
return
|
|
363
|
+
return lines;
|
|
333
364
|
}
|
|
334
365
|
|
|
335
366
|
// ─── Profile Picker ───────────────────────────────────────────────────────
|
|
@@ -338,11 +369,11 @@ function showProfilePicker(rl) {
|
|
|
338
369
|
return new Promise((resolve) => {
|
|
339
370
|
const current = loadProfile();
|
|
340
371
|
console.log('');
|
|
341
|
-
console.log(` ${bold('
|
|
372
|
+
console.log(` ${bold('Switch mode:')}`);
|
|
342
373
|
console.log('');
|
|
343
374
|
for (const [i, [name, pf]] of Object.entries(PROFILES).entries()) {
|
|
344
375
|
const active = name === current.name ? ' ✅' : '';
|
|
345
|
-
console.log(` ${bold('[' + (i + 1) + ']')} ${pf.emoji} ${
|
|
376
|
+
console.log(` ${bold('[' + (i + 1) + ']')} ${pf.emoji} ${pf.uiLabel.padEnd(15)} ${dim(pf.desc)}${active}`);
|
|
346
377
|
}
|
|
347
378
|
console.log(` ${bold('[q]')} Cancel`);
|
|
348
379
|
console.log('');
|
|
@@ -358,30 +389,33 @@ function showProfilePicker(rl) {
|
|
|
358
389
|
} catch {}
|
|
359
390
|
saveProfile(names[idx], customOverrides);
|
|
360
391
|
const pf = PROFILES[names[idx]];
|
|
361
|
-
console.log(` ✅ Switched to ${pf.emoji} ${pf.
|
|
392
|
+
console.log(` ✅ Switched to ${pf.emoji} ${pf.uiLabel}`);
|
|
362
393
|
}
|
|
363
394
|
resolve();
|
|
364
395
|
});
|
|
365
396
|
});
|
|
366
397
|
}
|
|
367
398
|
|
|
368
|
-
// ───
|
|
399
|
+
// ─── Cost Alert Editor ────────────────────────────────────────────────────
|
|
369
400
|
|
|
370
|
-
function
|
|
401
|
+
function showCostAlertEditor(rl) {
|
|
371
402
|
return new Promise((resolve) => {
|
|
372
403
|
const profile = loadProfile();
|
|
373
404
|
console.log('');
|
|
374
|
-
console.log(` ${bold('
|
|
375
|
-
console.log(` ${dim('
|
|
405
|
+
console.log(` ${bold('Cost alerts')}`);
|
|
406
|
+
console.log(` ${dim('Dual-brain estimates API costs from session activity.')}`);
|
|
407
|
+
console.log(` ${dim('These are alerts, not billing caps.')}`);
|
|
408
|
+
console.log('');
|
|
409
|
+
console.log(` Current: warn at $${profile.budgets.session_warn_usd}/session, $${profile.budgets.daily_warn_usd}/day`);
|
|
410
|
+
console.log(` limit at $${profile.budgets.session_limit_usd}/session, $${profile.budgets.daily_limit_usd}/day`);
|
|
376
411
|
console.log('');
|
|
377
412
|
|
|
378
|
-
rl.question(' Session limit (
|
|
413
|
+
rl.question(' Session alert limit ($, Enter = keep): ', (sessionStr) => {
|
|
414
|
+
if (!sessionStr.trim()) return resolve();
|
|
379
415
|
const session = parseFloat(sessionStr);
|
|
380
|
-
if (isNaN(session) || session <= 0) {
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
}
|
|
384
|
-
rl.question(' Daily limit ($, Enter = auto): ', (dailyStr) => {
|
|
416
|
+
if (isNaN(session) || session <= 0) { console.log(' Cancelled.'); return resolve(); }
|
|
417
|
+
|
|
418
|
+
rl.question(' Daily alert limit ($, Enter = auto): ', (dailyStr) => {
|
|
385
419
|
const daily = parseFloat(dailyStr);
|
|
386
420
|
const finalDaily = (isNaN(daily) || daily <= 0) ? session * 3 : daily;
|
|
387
421
|
|
|
@@ -399,7 +433,7 @@ function showBudgetEditor(rl) {
|
|
|
399
433
|
writeFileSync(tmp, JSON.stringify(data, null, 2) + '\n');
|
|
400
434
|
renameSync(tmp, PROFILE_FILE);
|
|
401
435
|
|
|
402
|
-
console.log(` ✅
|
|
436
|
+
console.log(` ✅ Cost alerts: $${session}/session · $${finalDaily}/day`);
|
|
403
437
|
resolve();
|
|
404
438
|
});
|
|
405
439
|
});
|
|
@@ -410,11 +444,13 @@ function showBudgetEditor(rl) {
|
|
|
410
444
|
|
|
411
445
|
function runSession(cmd, args, label) {
|
|
412
446
|
console.log('');
|
|
413
|
-
console.log(` ${label}
|
|
447
|
+
console.log(` ${label}`);
|
|
448
|
+
console.log(` ${dim('Inside Claude: press Ctrl+C twice to return here.')}`);
|
|
414
449
|
console.log('');
|
|
450
|
+
markLaunched();
|
|
415
451
|
const result = spawnSync(cmd, args, { stdio: 'inherit' });
|
|
416
452
|
console.log('');
|
|
417
|
-
console.log('
|
|
453
|
+
console.log(' Returned to Dual-Brain.');
|
|
418
454
|
return result.status || 0;
|
|
419
455
|
}
|
|
420
456
|
|
|
@@ -422,11 +458,17 @@ function runSession(cmd, args, label) {
|
|
|
422
458
|
|
|
423
459
|
async function mainLoop() {
|
|
424
460
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
425
|
-
|
|
426
461
|
const ask = () => new Promise(resolve => rl.question(' Choice: ', resolve));
|
|
427
462
|
|
|
428
463
|
while (true) {
|
|
429
|
-
const
|
|
464
|
+
const firstRun = isFirstRun();
|
|
465
|
+
const providers = detectProviders();
|
|
466
|
+
const sessions = firstRun ? [] : getRecentSessions();
|
|
467
|
+
|
|
468
|
+
const lines = firstRun
|
|
469
|
+
? renderFirstRunMenu(providers)
|
|
470
|
+
: renderReturningMenu(providers, sessions);
|
|
471
|
+
|
|
430
472
|
for (const l of lines) console.log(l);
|
|
431
473
|
|
|
432
474
|
const choice = (await ask()).trim().toLowerCase();
|
|
@@ -438,16 +480,15 @@ async function mainLoop() {
|
|
|
438
480
|
}
|
|
439
481
|
|
|
440
482
|
if (choice === 'c' || choice === '') {
|
|
441
|
-
// Continue most recent session
|
|
442
483
|
if (sessions.length > 0) {
|
|
443
484
|
const s = sessions[0];
|
|
444
485
|
if (s.tool === 'codex') {
|
|
445
|
-
runSession('codex', ['--dangerously-bypass-approvals-and-sandbox', 'resume', s.id], `Resuming codex
|
|
486
|
+
runSession('codex', ['--dangerously-bypass-approvals-and-sandbox', 'resume', s.id], `Resuming codex ${s.id.slice(0, 8)}...`);
|
|
446
487
|
} else {
|
|
447
|
-
runSession('claude', ['-r', s.id, '--dangerously-skip-permissions'], `Resuming session ${s.id.slice(0, 8)}
|
|
488
|
+
runSession('claude', ['-r', s.id, '--dangerously-skip-permissions'], `Resuming session ${s.id.slice(0, 8)}...`);
|
|
448
489
|
}
|
|
449
490
|
} else {
|
|
450
|
-
runSession('claude', ['--dangerously-skip-permissions'], 'Starting new session');
|
|
491
|
+
runSession('claude', ['--dangerously-skip-permissions'], 'Starting new session...');
|
|
451
492
|
}
|
|
452
493
|
continue;
|
|
453
494
|
}
|
|
@@ -456,15 +497,15 @@ async function mainLoop() {
|
|
|
456
497
|
if (num >= 1 && num <= 9 && sessions[num - 1]) {
|
|
457
498
|
const s = sessions[num - 1];
|
|
458
499
|
if (s.tool === 'codex') {
|
|
459
|
-
runSession('codex', ['--dangerously-bypass-approvals-and-sandbox', 'resume', s.id], `Resuming codex
|
|
500
|
+
runSession('codex', ['--dangerously-bypass-approvals-and-sandbox', 'resume', s.id], `Resuming codex ${s.id.slice(0, 8)}...`);
|
|
460
501
|
} else {
|
|
461
|
-
runSession('claude', ['-r', s.id, '--dangerously-skip-permissions'], `Resuming session ${s.id.slice(0, 8)}
|
|
502
|
+
runSession('claude', ['-r', s.id, '--dangerously-skip-permissions'], `Resuming session ${s.id.slice(0, 8)}...`);
|
|
462
503
|
}
|
|
463
504
|
continue;
|
|
464
505
|
}
|
|
465
506
|
|
|
466
507
|
if (choice === 'n') {
|
|
467
|
-
runSession('claude', ['--dangerously-skip-permissions'], 'Starting new session');
|
|
508
|
+
runSession('claude', ['--dangerously-skip-permissions'], 'Starting new session...');
|
|
468
509
|
continue;
|
|
469
510
|
}
|
|
470
511
|
|
|
@@ -474,7 +515,7 @@ async function mainLoop() {
|
|
|
474
515
|
}
|
|
475
516
|
|
|
476
517
|
if (choice === 'b') {
|
|
477
|
-
await
|
|
518
|
+
await showCostAlertEditor(rl);
|
|
478
519
|
continue;
|
|
479
520
|
}
|
|
480
521
|
|
|
@@ -508,7 +549,7 @@ async function mainLoop() {
|
|
|
508
549
|
console.log('');
|
|
509
550
|
spawnSync('npx', ['-y', 'data-tools'], { stdio: 'inherit', cwd: CWD });
|
|
510
551
|
console.log('');
|
|
511
|
-
console.log(' ✅ replit-tools installed.
|
|
552
|
+
console.log(' ✅ replit-tools installed.');
|
|
512
553
|
console.log('');
|
|
513
554
|
await ask();
|
|
514
555
|
continue;
|
|
@@ -521,7 +562,11 @@ async function mainLoop() {
|
|
|
521
562
|
// ─── Non-Interactive Fallback ─────────────────────────────────────────────
|
|
522
563
|
|
|
523
564
|
function renderStatic() {
|
|
524
|
-
const
|
|
565
|
+
const providers = detectProviders();
|
|
566
|
+
const sessions = getRecentSessions();
|
|
567
|
+
const lines = sessions.length > 0
|
|
568
|
+
? renderReturningMenu(providers, sessions)
|
|
569
|
+
: renderFirstRunMenu(providers);
|
|
525
570
|
for (const l of lines) console.log(l);
|
|
526
571
|
}
|
|
527
572
|
|
package/install.mjs
CHANGED
|
@@ -313,6 +313,7 @@ function generateGitignoreEntries(workspace) {
|
|
|
313
313
|
'.claude/dual-brain.profile.json',
|
|
314
314
|
'.claude/hooks/usage-summary-*.json',
|
|
315
315
|
'.claude/hooks/decision-ledger.jsonl',
|
|
316
|
+
'.claude/.launched',
|
|
316
317
|
];
|
|
317
318
|
let existing = '';
|
|
318
319
|
try { existing = readFileSync(join(workspace, '.gitignore'), 'utf8'); } catch {}
|
package/package.json
CHANGED