@ekkos/cli 1.3.8 → 1.4.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/LICENSE +21 -0
- package/dist/commands/dashboard.js +520 -42
- package/dist/commands/gemini.d.ts +1 -0
- package/dist/commands/gemini.js +180 -19
- package/dist/commands/init-living-docs.d.ts +6 -0
- package/dist/commands/init-living-docs.js +57 -0
- package/dist/commands/living-docs.js +3 -3
- package/dist/commands/run.d.ts +5 -0
- package/dist/commands/run.js +508 -35
- package/dist/commands/setup-ci.d.ts +3 -0
- package/dist/commands/setup-ci.js +107 -0
- package/dist/commands/validate-living-docs.d.ts +27 -0
- package/dist/commands/validate-living-docs.js +489 -0
- package/dist/index.js +97 -25
- package/dist/utils/proxy-url.d.ts +6 -1
- package/dist/utils/proxy-url.js +16 -2
- package/dist/utils/state.d.ts +16 -3
- package/dist/utils/state.js +10 -3
- package/package.json +27 -19
- package/templates/agents/prune.md +83 -0
- package/templates/agents/rewind.md +84 -0
- package/templates/agents/scout.md +102 -0
- package/templates/agents/trace.md +99 -0
- package/templates/commands/continue.md +47 -0
package/dist/commands/run.js
CHANGED
|
@@ -86,6 +86,300 @@ function getConfig(options) {
|
|
|
86
86
|
};
|
|
87
87
|
/* eslint-enable no-restricted-syntax */
|
|
88
88
|
}
|
|
89
|
+
const MAX_OUTPUT_1M_MODELS = '64000';
|
|
90
|
+
const MAX_OUTPUT_200K_OPUS_SONNET = '32768';
|
|
91
|
+
let runtimeClaudeCodeVersion = null;
|
|
92
|
+
let runtimeClaudeContextWindow = 'auto';
|
|
93
|
+
let runtimeClaudeLaunchModel;
|
|
94
|
+
function normalizeContextWindowOption(value) {
|
|
95
|
+
const normalized = (value || '').trim().toLowerCase();
|
|
96
|
+
if (normalized === '200k' || normalized === '200000')
|
|
97
|
+
return '200k';
|
|
98
|
+
if (normalized === '1m' || normalized === '1000000')
|
|
99
|
+
return '1m';
|
|
100
|
+
return 'auto';
|
|
101
|
+
}
|
|
102
|
+
function normalizeRequestedLaunchModel(value) {
|
|
103
|
+
if (typeof value !== 'string')
|
|
104
|
+
return {};
|
|
105
|
+
const raw = (value || '').trim();
|
|
106
|
+
if (!raw)
|
|
107
|
+
return {};
|
|
108
|
+
if (raw.toLowerCase() === 'default') {
|
|
109
|
+
return {};
|
|
110
|
+
}
|
|
111
|
+
const oneMillionMatch = raw.match(/^(.*)\[1m\]$/i);
|
|
112
|
+
if (oneMillionMatch) {
|
|
113
|
+
return {
|
|
114
|
+
model: oneMillionMatch[1].trim(),
|
|
115
|
+
syntheticContextWindow: '1m',
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
const lower = raw.toLowerCase();
|
|
119
|
+
if (lower.endsWith('-200k')) {
|
|
120
|
+
return {
|
|
121
|
+
model: raw.slice(0, -5).trim(),
|
|
122
|
+
syntheticContextWindow: '200k',
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
if (lower.endsWith('-1m')) {
|
|
126
|
+
return {
|
|
127
|
+
model: raw.slice(0, -3).trim(),
|
|
128
|
+
syntheticContextWindow: '1m',
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
return { model: raw };
|
|
132
|
+
}
|
|
133
|
+
function modelSupportsOneMillionContext(model) {
|
|
134
|
+
const normalized = (model || 'default').trim().toLowerCase();
|
|
135
|
+
if (!normalized || normalized === 'default')
|
|
136
|
+
return true;
|
|
137
|
+
if (normalized === 'opus' || normalized === 'sonnet')
|
|
138
|
+
return true;
|
|
139
|
+
return normalized.includes('claude-opus-4-6') || normalized.includes('claude-sonnet-4-6');
|
|
140
|
+
}
|
|
141
|
+
function buildClaudeLaunchModelArg(model, contextWindow) {
|
|
142
|
+
if (!model)
|
|
143
|
+
return undefined;
|
|
144
|
+
const normalized = normalizeRequestedLaunchModel(model).model;
|
|
145
|
+
if (!normalized)
|
|
146
|
+
return undefined;
|
|
147
|
+
if (contextWindow === '1m' && modelSupportsOneMillionContext(normalized)) {
|
|
148
|
+
return `${normalized}[1m]`;
|
|
149
|
+
}
|
|
150
|
+
return normalized;
|
|
151
|
+
}
|
|
152
|
+
function buildClaudeLaunchProfileId(model, contextWindow) {
|
|
153
|
+
const normalized = normalizeRequestedLaunchModel(model).model;
|
|
154
|
+
if (!normalized)
|
|
155
|
+
return undefined;
|
|
156
|
+
const lower = normalized.toLowerCase();
|
|
157
|
+
if (/^claude-opus-4-6(?:$|-)/.test(lower)) {
|
|
158
|
+
if (contextWindow === '200k')
|
|
159
|
+
return 'claude-opus-4-6-200k';
|
|
160
|
+
if (contextWindow === '1m')
|
|
161
|
+
return 'claude-opus-4-6-1m';
|
|
162
|
+
return undefined;
|
|
163
|
+
}
|
|
164
|
+
if (/^claude-sonnet-4-6(?:$|-)/.test(lower)) {
|
|
165
|
+
if (contextWindow === '200k')
|
|
166
|
+
return 'claude-sonnet-4-6-200k';
|
|
167
|
+
if (contextWindow === '1m')
|
|
168
|
+
return 'claude-sonnet-4-6-1m';
|
|
169
|
+
return undefined;
|
|
170
|
+
}
|
|
171
|
+
if (/^claude-opus-4-5(?:$|-)/.test(lower))
|
|
172
|
+
return 'claude-opus-4-5';
|
|
173
|
+
if (/^claude-sonnet-4-5(?:$|-)/.test(lower))
|
|
174
|
+
return 'claude-sonnet-4-5';
|
|
175
|
+
if (/^claude-haiku-4-5(?:$|-)/.test(lower))
|
|
176
|
+
return 'claude-haiku-4-5';
|
|
177
|
+
return normalized;
|
|
178
|
+
}
|
|
179
|
+
function resolveClaudeContextWindowSize(model, contextWindow) {
|
|
180
|
+
if (contextWindow === '200k')
|
|
181
|
+
return 200000;
|
|
182
|
+
if (contextWindow === '1m' && modelSupportsOneMillionContext(model))
|
|
183
|
+
return 1000000;
|
|
184
|
+
if (model && !modelSupportsOneMillionContext(model))
|
|
185
|
+
return 200000;
|
|
186
|
+
return undefined;
|
|
187
|
+
}
|
|
188
|
+
function getClaudeSessionMetadata(options) {
|
|
189
|
+
const normalizedContextWindow = normalizeContextWindowOption(options.contextWindow);
|
|
190
|
+
const selectedModel = typeof options.model === 'string' ? options.model : undefined;
|
|
191
|
+
const launchModel = buildClaudeLaunchModelArg(selectedModel, normalizedContextWindow);
|
|
192
|
+
const profileId = buildClaudeLaunchProfileId(selectedModel, normalizedContextWindow);
|
|
193
|
+
const contextSize = resolveClaudeContextWindowSize(selectedModel, normalizedContextWindow);
|
|
194
|
+
const maxOutputTokens = Number.parseInt(resolveClaudeMaxOutputTokens(selectedModel, normalizedContextWindow), 10);
|
|
195
|
+
return {
|
|
196
|
+
provider: 'claude',
|
|
197
|
+
claudeModel: normalizeRequestedLaunchModel(selectedModel).model,
|
|
198
|
+
claudeLaunchModel: launchModel,
|
|
199
|
+
claudeProfile: profileId,
|
|
200
|
+
claudeContextWindow: normalizedContextWindow,
|
|
201
|
+
claudeContextSize: contextSize,
|
|
202
|
+
claudeMaxOutputTokens: Number.isFinite(maxOutputTokens) ? maxOutputTokens : undefined,
|
|
203
|
+
claudeCodeVersion: runtimeClaudeCodeVersion || undefined,
|
|
204
|
+
dashboardEnabled: options.dashboard === true,
|
|
205
|
+
bypassEnabled: options.bypass === true,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
function isTwoHundredKFixedOutputModel(model) {
|
|
209
|
+
const normalized = (model || '').trim().toLowerCase();
|
|
210
|
+
if (!normalized)
|
|
211
|
+
return false;
|
|
212
|
+
if (normalized === 'opus' || normalized === 'sonnet')
|
|
213
|
+
return true;
|
|
214
|
+
return /^claude-(?:opus|sonnet)-4-(?:5|6)(?:$|-)/.test(normalized);
|
|
215
|
+
}
|
|
216
|
+
function resolveClaudeMaxOutputTokens(model, contextWindow) {
|
|
217
|
+
if (process.env.EKKOS_MAX_OUTPUT_TOKENS) {
|
|
218
|
+
return process.env.EKKOS_MAX_OUTPUT_TOKENS;
|
|
219
|
+
}
|
|
220
|
+
if (contextWindow === '200k' && isTwoHundredKFixedOutputModel(model)) {
|
|
221
|
+
return MAX_OUTPUT_200K_OPUS_SONNET;
|
|
222
|
+
}
|
|
223
|
+
return MAX_OUTPUT_1M_MODELS;
|
|
224
|
+
}
|
|
225
|
+
function shouldOpenLaunchSelector(options) {
|
|
226
|
+
return options.model === true;
|
|
227
|
+
}
|
|
228
|
+
function renderClaudeLaunchSelectorIntro() {
|
|
229
|
+
const neonCyan = chalk_1.default.hex('#00f5ff');
|
|
230
|
+
const acidGreen = chalk_1.default.hex('#9dff00');
|
|
231
|
+
const signalAmber = chalk_1.default.hex('#ffb300');
|
|
232
|
+
const steel = chalk_1.default.hex('#8a92a6');
|
|
233
|
+
const width = 68;
|
|
234
|
+
const line = (text) => {
|
|
235
|
+
const pad = Math.max(0, width - stripAnsi(text).length);
|
|
236
|
+
return `${neonCyan('║')} ${text}${' '.repeat(pad)}${neonCyan('║')}`;
|
|
237
|
+
};
|
|
238
|
+
console.log('');
|
|
239
|
+
console.log(neonCyan('╔══════════════════════════════════════════════════════════════════════╗'));
|
|
240
|
+
console.log(line(chalk_1.default.white.bold('ekkOS.dev // PULSE')));
|
|
241
|
+
console.log(neonCyan('╠══════════════════════════════════════════════════════════════════════╣'));
|
|
242
|
+
console.log(line(`${acidGreen('200K')} ${steel('compat lane')} ${chalk_1.default.white('// Opus/Sonnet 4.5/4.6 => 32,768 output')}`));
|
|
243
|
+
console.log(line(`${signalAmber(' 1M')} ${steel('wide lane')} ${chalk_1.default.white('// Opus/Sonnet 4.6 => 64,000 output')}`));
|
|
244
|
+
console.log(line(steel('Pick a launch vector, then a fixed runtime profile. No hidden context prompt.')));
|
|
245
|
+
console.log(neonCyan('╚══════════════════════════════════════════════════════════════════════╝'));
|
|
246
|
+
console.log('');
|
|
247
|
+
}
|
|
248
|
+
function buildLaunchModeChoices() {
|
|
249
|
+
return [
|
|
250
|
+
{
|
|
251
|
+
name: `${chalk_1.default.hex('#00f5ff').bold('Fresh boot')} ${chalk_1.default.gray('// new shell')}`,
|
|
252
|
+
value: 'fresh',
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
name: `${chalk_1.default.hex('#9dff00').bold('Resume recent')} ${chalk_1.default.gray('// latest thread')}`,
|
|
256
|
+
value: 'continue',
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
name: `${chalk_1.default.hex('#ffb300').bold('Resume by ID')} ${chalk_1.default.gray('// targeted reconnect')}`,
|
|
260
|
+
value: 'resume',
|
|
261
|
+
},
|
|
262
|
+
];
|
|
263
|
+
}
|
|
264
|
+
function buildLaunchModelChoices() {
|
|
265
|
+
const cyan = chalk_1.default.hex('#00f5ff');
|
|
266
|
+
const amber = chalk_1.default.hex('#ffb300');
|
|
267
|
+
const green = chalk_1.default.hex('#00ff88');
|
|
268
|
+
return [
|
|
269
|
+
{
|
|
270
|
+
name: `${cyan.bold('Auto route')} ${chalk_1.default.gray('// Claude decides profile')}`,
|
|
271
|
+
value: 'default',
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
name: `${cyan.bold('Claude Opus 4.5')} ${green('[200K]')} ${chalk_1.default.gray('// out 32,768')}`,
|
|
275
|
+
value: 'claude-opus-4-5',
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
name: `${cyan.bold('Claude Opus 4.6')} ${green('[200K]')} ${chalk_1.default.gray('// out 32,768')}`,
|
|
279
|
+
value: 'claude-opus-4-6-200k',
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
name: `${cyan.bold('Claude Opus 4.6')} ${amber('[1M]')} ${chalk_1.default.gray('// out 64,000')}`,
|
|
283
|
+
value: 'claude-opus-4-6-1m',
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
name: `${cyan.bold('Claude Sonnet 4.5')} ${green('[200K]')} ${chalk_1.default.gray('// out 32,768')}`,
|
|
287
|
+
value: 'claude-sonnet-4-5',
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
name: `${cyan.bold('Claude Sonnet 4.6')} ${green('[200K]')} ${chalk_1.default.gray('// out 32,768')}`,
|
|
291
|
+
value: 'claude-sonnet-4-6-200k',
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
name: `${cyan.bold('Claude Sonnet 4.6')} ${amber('[1M]')} ${chalk_1.default.gray('// out 64,000')}`,
|
|
295
|
+
value: 'claude-sonnet-4-6-1m',
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
name: `${cyan.bold('Claude Haiku 4.5')} ${green('[200K]')} ${chalk_1.default.gray('// fast lane')}`,
|
|
299
|
+
value: 'claude-haiku-4-5',
|
|
300
|
+
},
|
|
301
|
+
];
|
|
302
|
+
}
|
|
303
|
+
async function resolveClaudeLaunchSelection(options) {
|
|
304
|
+
const requestedModel = normalizeRequestedLaunchModel(typeof options.model === 'string' ? options.model : process.env.EKKOS_CLAUDE_MODEL);
|
|
305
|
+
let model = requestedModel.model;
|
|
306
|
+
let contextWindow = requestedModel.syntheticContextWindow
|
|
307
|
+
|| normalizeContextWindowOption(options.contextWindow || process.env.EKKOS_CLAUDE_CONTEXT_WINDOW);
|
|
308
|
+
let continueLast = options.continueLast || false;
|
|
309
|
+
let resumeSession = (options.resumeSession || '').trim();
|
|
310
|
+
const shouldShowLaunchWindow = shouldOpenLaunchSelector(options)
|
|
311
|
+
&& process.stdin.isTTY === true
|
|
312
|
+
&& process.stdout.isTTY === true
|
|
313
|
+
&& process.env.EKKOS_DISABLE_LAUNCH_WINDOW !== '1';
|
|
314
|
+
if (!shouldShowLaunchWindow) {
|
|
315
|
+
if (!modelSupportsOneMillionContext(model)) {
|
|
316
|
+
contextWindow = '200k';
|
|
317
|
+
}
|
|
318
|
+
return {
|
|
319
|
+
model,
|
|
320
|
+
contextWindow,
|
|
321
|
+
continueLast: !!continueLast && !resumeSession,
|
|
322
|
+
resumeSession: resumeSession || undefined,
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
const { default: inquirer } = await Promise.resolve().then(() => __importStar(require('inquirer')));
|
|
326
|
+
renderClaudeLaunchSelectorIntro();
|
|
327
|
+
const defaultLaunchMode = resumeSession ? 'resume' : continueLast ? 'continue' : 'fresh';
|
|
328
|
+
const firstPrompt = await inquirer.prompt([
|
|
329
|
+
{
|
|
330
|
+
type: 'list',
|
|
331
|
+
name: 'launchMode',
|
|
332
|
+
message: chalk_1.default.hex('#00f5ff').bold('>> Boot vector'),
|
|
333
|
+
default: defaultLaunchMode,
|
|
334
|
+
pageSize: 3,
|
|
335
|
+
loop: false,
|
|
336
|
+
choices: buildLaunchModeChoices(),
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
type: 'list',
|
|
340
|
+
name: 'model',
|
|
341
|
+
message: chalk_1.default.hex('#00f5ff').bold('>> Model lane'),
|
|
342
|
+
default: model || 'default',
|
|
343
|
+
pageSize: 8,
|
|
344
|
+
loop: false,
|
|
345
|
+
choices: buildLaunchModelChoices(),
|
|
346
|
+
},
|
|
347
|
+
]);
|
|
348
|
+
const selectedProfile = normalizeRequestedLaunchModel(firstPrompt.model);
|
|
349
|
+
model = firstPrompt.model === 'default' ? undefined : selectedProfile.model;
|
|
350
|
+
continueLast = firstPrompt.launchMode === 'continue';
|
|
351
|
+
resumeSession = firstPrompt.launchMode === 'resume' ? resumeSession : '';
|
|
352
|
+
if (firstPrompt.launchMode === 'resume') {
|
|
353
|
+
const resumePrompt = await inquirer.prompt([
|
|
354
|
+
{
|
|
355
|
+
type: 'input',
|
|
356
|
+
name: 'resumeSession',
|
|
357
|
+
message: 'Claude session ID',
|
|
358
|
+
default: resumeSession,
|
|
359
|
+
validate: (value) => value.trim().length > 0 || 'Session ID is required',
|
|
360
|
+
},
|
|
361
|
+
]);
|
|
362
|
+
resumeSession = resumePrompt.resumeSession.trim();
|
|
363
|
+
}
|
|
364
|
+
if (firstPrompt.model === 'default') {
|
|
365
|
+
contextWindow = 'auto';
|
|
366
|
+
}
|
|
367
|
+
else if (selectedProfile.syntheticContextWindow) {
|
|
368
|
+
contextWindow = selectedProfile.syntheticContextWindow;
|
|
369
|
+
}
|
|
370
|
+
else if (!modelSupportsOneMillionContext(model)) {
|
|
371
|
+
contextWindow = '200k';
|
|
372
|
+
}
|
|
373
|
+
else {
|
|
374
|
+
contextWindow = 'auto';
|
|
375
|
+
}
|
|
376
|
+
return {
|
|
377
|
+
model,
|
|
378
|
+
contextWindow,
|
|
379
|
+
continueLast,
|
|
380
|
+
resumeSession: resumeSession || undefined,
|
|
381
|
+
};
|
|
382
|
+
}
|
|
89
383
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
90
384
|
// PATTERN MATCHING
|
|
91
385
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -291,13 +585,6 @@ const isWindows = os.platform() === 'win32';
|
|
|
291
585
|
// Core ekkOS patches (eviction, context management) work with all recent versions
|
|
292
586
|
// Cosmetic patches may fail on newer versions but don't affect functionality
|
|
293
587
|
const PINNED_CLAUDE_VERSION = 'latest';
|
|
294
|
-
// Max output tokens for Claude responses
|
|
295
|
-
// Default: 128000 (Opus 4.6 / Sonnet 4.6 max output)
|
|
296
|
-
// Override via EKKOS_MAX_OUTPUT_TOKENS env var if needed
|
|
297
|
-
// Note: Claude Code has a known bug where CLAUDE_CODE_MAX_OUTPUT_TOKENS is ignored on Opus 4.6
|
|
298
|
-
// (see github.com/anthropics/claude-code/issues/24159). We set it anyway + the proxy also
|
|
299
|
-
// enforces this via max_tokens override on the API request.
|
|
300
|
-
const EKKOS_MAX_OUTPUT_TOKENS = process.env.EKKOS_MAX_OUTPUT_TOKENS || '128000';
|
|
301
588
|
const proxy_url_1 = require("../utils/proxy-url");
|
|
302
589
|
// Track proxy mode for getEkkosEnv (set by run() based on options)
|
|
303
590
|
let proxyModeEnabled = true;
|
|
@@ -318,12 +605,13 @@ let cliSessionName = null;
|
|
|
318
605
|
let cliSessionId = null;
|
|
319
606
|
/**
|
|
320
607
|
* Get environment with ekkOS enhancements
|
|
321
|
-
* - Sets CLAUDE_CODE_MAX_OUTPUT_TOKENS
|
|
608
|
+
* - Sets CLAUDE_CODE_MAX_OUTPUT_TOKENS dynamically for the selected Claude profile
|
|
322
609
|
* - Routes API through ekkOS proxy for seamless context eviction (when enabled)
|
|
323
610
|
* - Sets EKKOS_PROXY_MODE to signal JSONL rewriter to disable eviction
|
|
324
611
|
* - Passes session headers for eviction/retrieval context tracking
|
|
325
612
|
*/
|
|
326
613
|
function getEkkosEnv() {
|
|
614
|
+
const maxOutputTokens = resolveClaudeMaxOutputTokens(runtimeClaudeLaunchModel, runtimeClaudeContextWindow);
|
|
327
615
|
/* eslint-disable no-restricted-syntax -- System env spreading, not API key access */
|
|
328
616
|
const env = {
|
|
329
617
|
...process.env,
|
|
@@ -335,13 +623,18 @@ function getEkkosEnv() {
|
|
|
335
623
|
// Native autocompact would compact without ekkOS saving state first, causing knowledge loss.
|
|
336
624
|
// Only ekkOS-wrapped sessions get this; vanilla `claude` keeps autocompact on.
|
|
337
625
|
DISABLE_AUTO_COMPACT: 'true',
|
|
338
|
-
//
|
|
339
|
-
//
|
|
340
|
-
//
|
|
341
|
-
|
|
342
|
-
CLAUDE_CODE_MAX_OUTPUT_TOKENS: EKKOS_MAX_OUTPUT_TOKENS,
|
|
626
|
+
// Align Claude's advertised output ceiling with the chosen model/context profile.
|
|
627
|
+
// 1M Opus/Sonnet gets 128K; 200K Opus/Sonnet 4.5/4.6 is forced to 32,768.
|
|
628
|
+
// The proxy also enforces this server-side in case Claude Code ignores the env var.
|
|
629
|
+
CLAUDE_CODE_MAX_OUTPUT_TOKENS: maxOutputTokens,
|
|
343
630
|
};
|
|
344
631
|
/* eslint-enable no-restricted-syntax */
|
|
632
|
+
if (runtimeClaudeContextWindow === '200k') {
|
|
633
|
+
env.CLAUDE_CODE_DISABLE_1M_CONTEXT = '1';
|
|
634
|
+
}
|
|
635
|
+
else {
|
|
636
|
+
delete env.CLAUDE_CODE_DISABLE_1M_CONTEXT;
|
|
637
|
+
}
|
|
345
638
|
// Check if proxy is disabled via env var or options
|
|
346
639
|
// eslint-disable-next-line no-restricted-syntax -- Feature flag, not API key
|
|
347
640
|
const proxyDisabled = process.env.EKKOS_DISABLE_PROXY === '1' || !proxyModeEnabled;
|
|
@@ -354,7 +647,9 @@ function getEkkosEnv() {
|
|
|
354
647
|
if (!cliSessionName) {
|
|
355
648
|
cliSessionId = crypto.randomUUID();
|
|
356
649
|
cliSessionName = (0, state_1.uuidToWords)(cliSessionId);
|
|
357
|
-
|
|
650
|
+
if (process.env.EKKOS_NO_SPLASH !== '1') {
|
|
651
|
+
console.log(chalk_1.default.gray(` 📂 Session: ${cliSessionName}`));
|
|
652
|
+
}
|
|
358
653
|
}
|
|
359
654
|
// Get full userId from config (NOT the truncated version from auth token)
|
|
360
655
|
// Config has full UUID like "d4532ba0-0a86-42ce-bab4-22aa62b55ce6"
|
|
@@ -374,7 +669,11 @@ function getEkkosEnv() {
|
|
|
374
669
|
// CRITICAL: Embed user/session in URL path since ANTHROPIC_HEADERS doesn't work
|
|
375
670
|
// Claude Code SDK doesn't forward custom headers, but it DOES use ANTHROPIC_BASE_URL
|
|
376
671
|
// Gateway extracts from URL: /proxy/{userId}/{sessionName}/v1/messages
|
|
377
|
-
const proxyUrl = (0, proxy_url_1.buildProxyUrl)(userId, cliSessionName, process.cwd(), cliSessionId
|
|
672
|
+
const proxyUrl = (0, proxy_url_1.buildProxyUrl)(userId, cliSessionName, process.cwd(), cliSessionId, {
|
|
673
|
+
claudeCodeVersion: runtimeClaudeCodeVersion || undefined,
|
|
674
|
+
claudeProfile: buildClaudeLaunchProfileId(runtimeClaudeLaunchModel, runtimeClaudeContextWindow),
|
|
675
|
+
claudeContextWindow: runtimeClaudeContextWindow,
|
|
676
|
+
});
|
|
378
677
|
env.ANTHROPIC_BASE_URL = proxyUrl;
|
|
379
678
|
// Proxy URL contains userId + project path — don't leak to terminal
|
|
380
679
|
}
|
|
@@ -585,6 +884,105 @@ function resolveGlobalClaudePath() {
|
|
|
585
884
|
function sleep(ms) {
|
|
586
885
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
587
886
|
}
|
|
887
|
+
function formatPulseLaunchMode(options) {
|
|
888
|
+
if (options.resumeSession)
|
|
889
|
+
return `RESUME // ${options.resumeSession}`;
|
|
890
|
+
if (options.continueLast)
|
|
891
|
+
return 'CONTINUE // most recent Claude thread';
|
|
892
|
+
return 'FRESH // new interactive shell';
|
|
893
|
+
}
|
|
894
|
+
function formatPulseModel(model) {
|
|
895
|
+
const normalized = (model || '').trim().toLowerCase();
|
|
896
|
+
if (!normalized)
|
|
897
|
+
return 'Claude default';
|
|
898
|
+
if (normalized === 'opus')
|
|
899
|
+
return 'Opus alias';
|
|
900
|
+
if (normalized === 'sonnet')
|
|
901
|
+
return 'Sonnet alias';
|
|
902
|
+
if (normalized === 'claude-opus-4-5')
|
|
903
|
+
return 'Claude Opus 4.5';
|
|
904
|
+
if (normalized === 'claude-opus-4-6')
|
|
905
|
+
return 'Claude Opus 4.6';
|
|
906
|
+
if (normalized === 'claude-sonnet-4-5')
|
|
907
|
+
return 'Claude Sonnet 4.5';
|
|
908
|
+
if (normalized === 'claude-sonnet-4-6')
|
|
909
|
+
return 'Claude Sonnet 4.6';
|
|
910
|
+
if (normalized === 'claude-haiku-4-5')
|
|
911
|
+
return 'Claude Haiku 4.5';
|
|
912
|
+
return model || 'Claude default';
|
|
913
|
+
}
|
|
914
|
+
function formatPulseContextLine(contextWindow) {
|
|
915
|
+
if (contextWindow === '200k') {
|
|
916
|
+
return '200K // 200,000 token window // forced 32,768 output';
|
|
917
|
+
}
|
|
918
|
+
if (contextWindow === '1m') {
|
|
919
|
+
return '1M // 1,000,000 token window // safe 64,000 output cap';
|
|
920
|
+
}
|
|
921
|
+
return 'AUTO // ekkOS resolves 200K vs 1M from the selected profile';
|
|
922
|
+
}
|
|
923
|
+
async function showPulseLaunchLoader(options) {
|
|
924
|
+
const cyan = chalk_1.default.hex('#00f0ff');
|
|
925
|
+
const amber = chalk_1.default.hex('#ffb800');
|
|
926
|
+
const green = chalk_1.default.hex('#00ff88');
|
|
927
|
+
const steel = chalk_1.default.hex('#7a7a8e');
|
|
928
|
+
const panel = chalk_1.default.hex('#111118');
|
|
929
|
+
const wordmark = [
|
|
930
|
+
'██████╗ ██╗ ██╗██╗ ███████╗███████╗',
|
|
931
|
+
'██╔══██╗██║ ██║██║ ██╔════╝██╔════╝',
|
|
932
|
+
'██████╔╝██║ ██║██║ ███████╗█████╗ ',
|
|
933
|
+
'██╔═══╝ ██║ ██║██║ ╚════██║██╔══╝ ',
|
|
934
|
+
'██║ ╚██████╔╝███████╗███████║███████╗',
|
|
935
|
+
'╚═╝ ╚═════╝ ╚══════╝╚══════╝╚══════╝',
|
|
936
|
+
];
|
|
937
|
+
const stages = [
|
|
938
|
+
'arming pulse launcher',
|
|
939
|
+
'syncing dashboard split',
|
|
940
|
+
'routing claude session',
|
|
941
|
+
];
|
|
942
|
+
const barWidth = 26;
|
|
943
|
+
const panelInnerWidth = 76;
|
|
944
|
+
const buildPanelLine = (labelColor, label, value) => {
|
|
945
|
+
const content = ` ${label.padEnd(7)} ${value}`;
|
|
946
|
+
const pad = Math.max(0, panelInnerWidth - stripAnsi(content).length);
|
|
947
|
+
return ` ${cyan('║')} ${labelColor(label.padEnd(7))} ${chalk_1.default.white(value)}${' '.repeat(pad)}${cyan('║')}`;
|
|
948
|
+
};
|
|
949
|
+
const headerContent = ' ekkOS.dev // PULSE BOOTSTRAP live launch matrix for Claude + dashboard';
|
|
950
|
+
const headerPad = Math.max(0, panelInnerWidth - headerContent.length);
|
|
951
|
+
console.log('');
|
|
952
|
+
for (const [idx, line] of wordmark.entries()) {
|
|
953
|
+
const color = idx % 2 === 0 ? cyan.bold : chalk_1.default.white.bold;
|
|
954
|
+
console.log(' ' + color(line));
|
|
955
|
+
await sleep(22);
|
|
956
|
+
}
|
|
957
|
+
console.log(' ' + amber('▔').repeat(54));
|
|
958
|
+
console.log(cyan(' ╔══════════════════════════════════════════════════════════════════════════════╗'));
|
|
959
|
+
console.log(` ${cyan('║')}${chalk_1.default.white.bold(' ekkOS.dev // PULSE BOOTSTRAP')}${steel(' live launch matrix for Claude + dashboard')}${' '.repeat(headerPad)}${cyan('║')}`);
|
|
960
|
+
console.log(buildPanelLine(green, 'MODE', formatPulseLaunchMode(options)));
|
|
961
|
+
console.log(buildPanelLine(green, 'MODEL', formatPulseModel(typeof options.model === 'string' ? options.model : undefined)));
|
|
962
|
+
console.log(buildPanelLine(amber, 'WINDOW', formatPulseContextLine(normalizeContextWindowOption(options.contextWindow))));
|
|
963
|
+
console.log(buildPanelLine(steel, 'PATH', 'selector -> dashboard -> claude -> proxy'));
|
|
964
|
+
console.log(cyan(' ╚══════════════════════════════════════════════════════════════════════════════╝'));
|
|
965
|
+
console.log(` ${green('200K')} ${steel('// Opus/Sonnet 4.5/4.6 => 32,768 output')}`);
|
|
966
|
+
console.log(` ${amber('1M ')} ${steel('// Opus/Sonnet 4.6 => 64,000 output')}`);
|
|
967
|
+
console.log('');
|
|
968
|
+
for (const stage of stages) {
|
|
969
|
+
for (let filled = 0; filled <= barWidth; filled += 1) {
|
|
970
|
+
const complete = '█'.repeat(filled);
|
|
971
|
+
const pending = '░'.repeat(barWidth - filled);
|
|
972
|
+
process.stdout.write(`\r ${amber('[')}${cyan(complete)}${steel(pending)}${amber(']')} ${chalk_1.default.white(stage)}`);
|
|
973
|
+
await sleep(16);
|
|
974
|
+
}
|
|
975
|
+
process.stdout.write('\n');
|
|
976
|
+
}
|
|
977
|
+
console.log(` ${green('READY')} ${panel('dashboard split armed')} ${steel('// selector handoff imminent')}`);
|
|
978
|
+
if (options.bypass) {
|
|
979
|
+
console.log(` ${amber('BYPASS')} ${chalk_1.default.white('permission prompts disabled for this launch')}`);
|
|
980
|
+
}
|
|
981
|
+
if (options.verbose) {
|
|
982
|
+
console.log(` ${steel('TRACE')} ${steel('verbose telemetry enabled')}`);
|
|
983
|
+
}
|
|
984
|
+
console.log('');
|
|
985
|
+
}
|
|
588
986
|
/**
|
|
589
987
|
* Show morning dreams with WOW factor right after sparkle animation.
|
|
590
988
|
* Colorful, lively, readable — real magic moment. Then pause for Enter.
|
|
@@ -790,10 +1188,16 @@ function cleanupInstanceFile(instanceId) {
|
|
|
790
1188
|
* Keeps ~/.ekkos/current-session.json, ~/.claude/state/current-session.json,
|
|
791
1189
|
* and ~/.ekkos/session-hint.json in sync whenever the session is known.
|
|
792
1190
|
*/
|
|
793
|
-
function writeSessionFiles(sessionId, sessionName) {
|
|
1191
|
+
function writeSessionFiles(sessionId, sessionName, metadata) {
|
|
794
1192
|
try {
|
|
795
1193
|
const ekkosDir = path.join(os.homedir(), '.ekkos');
|
|
796
1194
|
const claudeStateDir = path.join(os.homedir(), '.claude', 'state');
|
|
1195
|
+
const launchMetadata = metadata || getClaudeSessionMetadata({
|
|
1196
|
+
model: runtimeClaudeLaunchModel,
|
|
1197
|
+
contextWindow: runtimeClaudeContextWindow,
|
|
1198
|
+
dashboard: false,
|
|
1199
|
+
bypass: false,
|
|
1200
|
+
});
|
|
797
1201
|
// Ensure directories exist
|
|
798
1202
|
if (!fs.existsSync(ekkosDir))
|
|
799
1203
|
fs.mkdirSync(ekkosDir, { recursive: true });
|
|
@@ -801,16 +1205,17 @@ function writeSessionFiles(sessionId, sessionName) {
|
|
|
801
1205
|
fs.mkdirSync(claudeStateDir, { recursive: true });
|
|
802
1206
|
const now = new Date().toISOString();
|
|
803
1207
|
// 1. ~/.ekkos/current-session.json
|
|
804
|
-
fs.writeFileSync(path.join(ekkosDir, 'current-session.json'), JSON.stringify({ session_id: sessionId, session_name: sessionName, timestamp: now }, null, 2));
|
|
1208
|
+
fs.writeFileSync(path.join(ekkosDir, 'current-session.json'), JSON.stringify({ session_id: sessionId, session_name: sessionName, timestamp: now, ...launchMetadata }, null, 2));
|
|
805
1209
|
// 2. ~/.claude/state/current-session.json
|
|
806
|
-
fs.writeFileSync(path.join(claudeStateDir, 'current-session.json'), JSON.stringify({ session_id: sessionId, session_name: sessionName, timestamp: now }, null, 2));
|
|
1210
|
+
fs.writeFileSync(path.join(claudeStateDir, 'current-session.json'), JSON.stringify({ session_id: sessionId, session_name: sessionName, timestamp: now, ...launchMetadata }, null, 2));
|
|
807
1211
|
// 3. ~/.ekkos/session-hint.json (dashboard discovery)
|
|
808
1212
|
fs.writeFileSync(path.join(ekkosDir, 'session-hint.json'), JSON.stringify({
|
|
809
1213
|
session_name: sessionName,
|
|
810
1214
|
session_id: sessionId,
|
|
811
1215
|
project_path: process.cwd(),
|
|
812
1216
|
timestamp: now,
|
|
813
|
-
pid: process.pid
|
|
1217
|
+
pid: process.pid,
|
|
1218
|
+
...launchMetadata,
|
|
814
1219
|
}, null, 2));
|
|
815
1220
|
}
|
|
816
1221
|
catch {
|
|
@@ -844,12 +1249,21 @@ function launchWithDashboard(options) {
|
|
|
844
1249
|
runArgs.push('--skip-inject');
|
|
845
1250
|
if (options.noProxy)
|
|
846
1251
|
runArgs.push('--skip-proxy');
|
|
1252
|
+
if (options.continueLast)
|
|
1253
|
+
runArgs.push('--continue-last');
|
|
1254
|
+
if (options.resumeSession)
|
|
1255
|
+
runArgs.push('--resume-session', options.resumeSession);
|
|
1256
|
+
if (typeof options.model === 'string')
|
|
1257
|
+
runArgs.push('--model', options.model);
|
|
1258
|
+
if (options.contextWindow && options.contextWindow !== 'auto') {
|
|
1259
|
+
runArgs.push('--context-window', options.contextWindow);
|
|
1260
|
+
}
|
|
847
1261
|
const ekkosCmd = process.argv[1]; // Path to ekkos CLI
|
|
848
1262
|
const cwd = process.cwd();
|
|
849
1263
|
const termCols = process.stdout.columns ?? 160;
|
|
850
1264
|
const termRows = process.stdout.rows ?? 48;
|
|
851
1265
|
// Write session hint immediately so dashboard can find JSONL as it appears
|
|
852
|
-
writeSessionFiles(crypto.randomUUID(), sessionName);
|
|
1266
|
+
writeSessionFiles(crypto.randomUUID(), sessionName, getClaudeSessionMetadata(options));
|
|
853
1267
|
const runCommand = `EKKOS_NO_SPLASH=1 node "${ekkosCmd}" ${runArgs.join(' ')}`;
|
|
854
1268
|
// Pass session name directly — dashboard starts rendering immediately (lazy JSONL resolution)
|
|
855
1269
|
const dashCommand = `node "${ekkosCmd}" dashboard "${sessionName}" --refresh 2000`;
|
|
@@ -913,7 +1327,7 @@ function launchWithWindowsTerminal(options) {
|
|
|
913
1327
|
// Pre-generate session name so dashboard can start immediately
|
|
914
1328
|
const sessionName = options.session || (0, state_1.uuidToWords)(crypto.randomUUID());
|
|
915
1329
|
// Write session hint immediately
|
|
916
|
-
writeSessionFiles(crypto.randomUUID(), sessionName);
|
|
1330
|
+
writeSessionFiles(crypto.randomUUID(), sessionName, getClaudeSessionMetadata(options));
|
|
917
1331
|
// Build ekkos run args WITHOUT --dashboard (prevent recursion)
|
|
918
1332
|
// Always pass -s so run reuses the same session name we gave the dashboard
|
|
919
1333
|
const runArgs = ['run', '-s', sessionName];
|
|
@@ -929,6 +1343,15 @@ function launchWithWindowsTerminal(options) {
|
|
|
929
1343
|
runArgs.push('--skip-inject');
|
|
930
1344
|
if (options.noProxy)
|
|
931
1345
|
runArgs.push('--skip-proxy');
|
|
1346
|
+
if (options.continueLast)
|
|
1347
|
+
runArgs.push('--continue-last');
|
|
1348
|
+
if (options.resumeSession)
|
|
1349
|
+
runArgs.push('--resume-session', options.resumeSession);
|
|
1350
|
+
if (typeof options.model === 'string')
|
|
1351
|
+
runArgs.push('--model', options.model);
|
|
1352
|
+
if (options.contextWindow && options.contextWindow !== 'auto') {
|
|
1353
|
+
runArgs.push('--context-window', options.contextWindow);
|
|
1354
|
+
}
|
|
932
1355
|
// Write a temp batch file to avoid all quoting issues
|
|
933
1356
|
// wt.exe doesn't resolve PATH for npm global bins — must use `cmd /c`
|
|
934
1357
|
const batPath = path.join(os.tmpdir(), `ekkos-wt-${Date.now()}.cmd`);
|
|
@@ -976,6 +1399,14 @@ async function run(options) {
|
|
|
976
1399
|
const verbose = options.verbose || false;
|
|
977
1400
|
const bypass = options.bypass || false;
|
|
978
1401
|
const noInject = options.noInject || false;
|
|
1402
|
+
const suppressPreClaudeOutput = process.env.EKKOS_NO_SPLASH === '1';
|
|
1403
|
+
const launchSelection = await resolveClaudeLaunchSelection(options);
|
|
1404
|
+
options.model = launchSelection.model;
|
|
1405
|
+
options.contextWindow = launchSelection.contextWindow;
|
|
1406
|
+
options.continueLast = launchSelection.continueLast;
|
|
1407
|
+
options.resumeSession = launchSelection.resumeSession;
|
|
1408
|
+
runtimeClaudeLaunchModel = launchSelection.model;
|
|
1409
|
+
runtimeClaudeContextWindow = launchSelection.contextWindow;
|
|
979
1410
|
// Honour -s flag: lock the session name before getEkkosEnv() can mint a new one.
|
|
980
1411
|
// This is critical for --dashboard mode where launchWithDashboard() pre-generates
|
|
981
1412
|
// a session name and passes it via -s so the dashboard and run command agree.
|
|
@@ -986,11 +1417,28 @@ async function run(options) {
|
|
|
986
1417
|
// Set proxy mode based on options (used by getEkkosEnv)
|
|
987
1418
|
proxyModeEnabled = !(options.noProxy || false);
|
|
988
1419
|
if (proxyModeEnabled) {
|
|
989
|
-
|
|
1420
|
+
if (!suppressPreClaudeOutput && !options.pulse) {
|
|
1421
|
+
console.log(chalk_1.default.cyan(' 🧠 ekkOS_Continuum Loaded!'));
|
|
1422
|
+
}
|
|
990
1423
|
}
|
|
991
|
-
else if (verbose) {
|
|
1424
|
+
else if (verbose && !suppressPreClaudeOutput) {
|
|
992
1425
|
console.log(chalk_1.default.yellow(' ⏭️ API proxy disabled (--no-proxy)'));
|
|
993
1426
|
}
|
|
1427
|
+
let pulseIntroShown = false;
|
|
1428
|
+
if (options.pulse
|
|
1429
|
+
&& process.env.EKKOS_REMOTE_SESSION !== '1'
|
|
1430
|
+
&& process.env.EKKOS_NO_SPLASH !== '1'
|
|
1431
|
+
&& process.stdout.isTTY === true) {
|
|
1432
|
+
await showPulseLaunchLoader({
|
|
1433
|
+
model: options.model,
|
|
1434
|
+
contextWindow: options.contextWindow,
|
|
1435
|
+
continueLast: options.continueLast,
|
|
1436
|
+
resumeSession: options.resumeSession,
|
|
1437
|
+
bypass: options.bypass,
|
|
1438
|
+
verbose: options.verbose,
|
|
1439
|
+
});
|
|
1440
|
+
pulseIntroShown = true;
|
|
1441
|
+
}
|
|
994
1442
|
// ══════════════════════════════════════════════════════════════════════════
|
|
995
1443
|
// DASHBOARD MODE: Launch via tmux (Unix) or Windows Terminal split pane
|
|
996
1444
|
// ══════════════════════════════════════════════════════════════════════════
|
|
@@ -1079,11 +1527,22 @@ async function run(options) {
|
|
|
1079
1527
|
const isNpxMode = rawClaudePath.startsWith('npx:');
|
|
1080
1528
|
const pinnedVersion = isNpxMode ? rawClaudePath.split(':')[1] : null;
|
|
1081
1529
|
const claudePath = isNpxMode ? 'npx' : rawClaudePath;
|
|
1530
|
+
runtimeClaudeCodeVersion = pinnedVersion || getClaudeVersion(claudePath);
|
|
1082
1531
|
// Build args early
|
|
1083
1532
|
const earlyArgs = [];
|
|
1084
1533
|
if (isNpxMode) {
|
|
1085
1534
|
earlyArgs.push(`@anthropic-ai/claude-code@${pinnedVersion}`);
|
|
1086
1535
|
}
|
|
1536
|
+
if (options.resumeSession) {
|
|
1537
|
+
earlyArgs.push('--resume', options.resumeSession);
|
|
1538
|
+
}
|
|
1539
|
+
else if (options.continueLast) {
|
|
1540
|
+
earlyArgs.push('--continue');
|
|
1541
|
+
}
|
|
1542
|
+
const launchModelArg = buildClaudeLaunchModelArg(options.model, options.contextWindow);
|
|
1543
|
+
if (launchModelArg) {
|
|
1544
|
+
earlyArgs.push('--model', launchModelArg);
|
|
1545
|
+
}
|
|
1087
1546
|
if (bypass) {
|
|
1088
1547
|
earlyArgs.push('--dangerously-skip-permissions');
|
|
1089
1548
|
}
|
|
@@ -1146,7 +1605,7 @@ async function run(options) {
|
|
|
1146
1605
|
// ══════════════════════════════════════════════════════════════════════════
|
|
1147
1606
|
// STARTUP BANNER WITH COLOR PULSE ANIMATION
|
|
1148
1607
|
// ══════════════════════════════════════════════════════════════════════════
|
|
1149
|
-
const skipFancyIntro = process.env.EKKOS_REMOTE_SESSION === '1' ||
|
|
1608
|
+
const skipFancyIntro = pulseIntroShown || process.env.EKKOS_REMOTE_SESSION === '1' || suppressPreClaudeOutput || !!process.env.TMUX;
|
|
1150
1609
|
if (!skipFancyIntro) {
|
|
1151
1610
|
const logoLines = [
|
|
1152
1611
|
' ▄▄▄▄▄ ▄▄▄▄▄▄▄ ▄▄▄ ▄▄ ▄▄',
|
|
@@ -1328,7 +1787,7 @@ async function run(options) {
|
|
|
1328
1787
|
}
|
|
1329
1788
|
console.log('');
|
|
1330
1789
|
}
|
|
1331
|
-
else {
|
|
1790
|
+
else if (!suppressPreClaudeOutput) {
|
|
1332
1791
|
// Static banner for Windows / remote / no-splash — no cursor manipulation
|
|
1333
1792
|
console.log('');
|
|
1334
1793
|
console.log(chalk_1.default.hex('#FF6B35').bold(' ekkOS_Pulse') + chalk_1.default.gray(' — Context is finite. Intelligence isn\'t.'));
|
|
@@ -1344,7 +1803,9 @@ async function run(options) {
|
|
|
1344
1803
|
// ══════════════════════════════════════════════════════════════════════════
|
|
1345
1804
|
// MAGIC MOMENT: Morning dreams right after sparkle, before Claude appears
|
|
1346
1805
|
// ══════════════════════════════════════════════════════════════════════════
|
|
1347
|
-
|
|
1806
|
+
if (!suppressPreClaudeOutput && !options.pulse) {
|
|
1807
|
+
await showMorningDreamsIfNeeded();
|
|
1808
|
+
}
|
|
1348
1809
|
// ══════════════════════════════════════════════════════════════════════════
|
|
1349
1810
|
// ANIMATION COMPLETE: Mark ready and flush buffered Claude output
|
|
1350
1811
|
// ══════════════════════════════════════════════════════════════════════════
|
|
@@ -1381,8 +1842,8 @@ async function run(options) {
|
|
|
1381
1842
|
// ════════════════════════════════════════════════════════════════════════════
|
|
1382
1843
|
const initialSessionId = cliSessionId || 'pending';
|
|
1383
1844
|
const initialSessionName = currentSession || 'initializing';
|
|
1384
|
-
(0, state_1.registerActiveSession)(initialSessionId, initialSessionName, process.cwd());
|
|
1385
|
-
writeSessionFiles(initialSessionId, initialSessionName);
|
|
1845
|
+
(0, state_1.registerActiveSession)(initialSessionId, initialSessionName, process.cwd(), getClaudeSessionMetadata(options));
|
|
1846
|
+
writeSessionFiles(initialSessionId, initialSessionName, getClaudeSessionMetadata(options));
|
|
1386
1847
|
dlog(`Registered active session (PID ${process.pid})`);
|
|
1387
1848
|
// Show active sessions count if verbose
|
|
1388
1849
|
if (verbose) {
|
|
@@ -1549,9 +2010,9 @@ async function run(options) {
|
|
|
1549
2010
|
// and shown in "Continuum Loaded". Re-deriving from JSONL UUID produces a
|
|
1550
2011
|
// different name since Claude Code's UUID ≠ the CLI-generated UUID.
|
|
1551
2012
|
currentSession = cliSessionName || (0, state_1.uuidToWords)(sessionId);
|
|
1552
|
-
(0, state_1.updateCurrentProcessSession)(currentSessionId, currentSession);
|
|
2013
|
+
(0, state_1.updateCurrentProcessSession)(currentSessionId, currentSession, getClaudeSessionMetadata(options));
|
|
1553
2014
|
(0, state_1.updateState)({ sessionId: currentSessionId, sessionName: currentSession });
|
|
1554
|
-
writeSessionFiles(currentSessionId, currentSession);
|
|
2015
|
+
writeSessionFiles(currentSessionId, currentSession, getClaudeSessionMetadata(options));
|
|
1555
2016
|
bindRealSessionToProxy(currentSession, 'fast-transcript', currentSessionId);
|
|
1556
2017
|
dlog(`[TRANSCRIPT] FAST DETECT: New transcript found! ${fullPath}`);
|
|
1557
2018
|
evictionDebugLog('TRANSCRIPT_SET', 'Fast poll detected new file', {
|
|
@@ -1835,9 +2296,21 @@ async function run(options) {
|
|
|
1835
2296
|
}
|
|
1836
2297
|
if (verbose) {
|
|
1837
2298
|
// Show Claude version
|
|
1838
|
-
const ccVersion = pinnedVersion || PINNED_CLAUDE_VERSION;
|
|
2299
|
+
const ccVersion = runtimeClaudeCodeVersion || pinnedVersion || PINNED_CLAUDE_VERSION;
|
|
1839
2300
|
const versionStr = `Claude Code v${ccVersion}`;
|
|
1840
2301
|
console.log(chalk_1.default.gray(` 🤖 ${versionStr}`));
|
|
2302
|
+
if (options.model) {
|
|
2303
|
+
console.log(chalk_1.default.gray(` 🧩 Model: ${buildClaudeLaunchModelArg(options.model, options.contextWindow)}`));
|
|
2304
|
+
}
|
|
2305
|
+
if (options.contextWindow && options.contextWindow !== 'auto') {
|
|
2306
|
+
console.log(chalk_1.default.gray(` 🪟 Context: ${options.contextWindow}`));
|
|
2307
|
+
}
|
|
2308
|
+
if (options.continueLast) {
|
|
2309
|
+
console.log(chalk_1.default.gray(' ↩ Continue: most recent Claude conversation'));
|
|
2310
|
+
}
|
|
2311
|
+
if (options.resumeSession) {
|
|
2312
|
+
console.log(chalk_1.default.gray(` ↪ Resume: ${options.resumeSession}`));
|
|
2313
|
+
}
|
|
1841
2314
|
if (currentSession) {
|
|
1842
2315
|
console.log(chalk_1.default.green(` 📍 Session: ${currentSession}`));
|
|
1843
2316
|
}
|
|
@@ -2433,10 +2906,10 @@ async function run(options) {
|
|
|
2433
2906
|
// Keep cliSessionName if set (proxy mode) — JSONL UUID differs from CLI UUID
|
|
2434
2907
|
currentSession = cliSessionName || (0, state_1.uuidToWords)(currentSessionId);
|
|
2435
2908
|
// Update THIS process's session entry (not global state.json)
|
|
2436
|
-
(0, state_1.updateCurrentProcessSession)(currentSessionId, currentSession);
|
|
2909
|
+
(0, state_1.updateCurrentProcessSession)(currentSessionId, currentSession, getClaudeSessionMetadata(options));
|
|
2437
2910
|
// Also update global state for backwards compatibility
|
|
2438
2911
|
(0, state_1.updateState)({ sessionId: currentSessionId, sessionName: currentSession });
|
|
2439
|
-
writeSessionFiles(currentSessionId, currentSession);
|
|
2912
|
+
writeSessionFiles(currentSessionId, currentSession, getClaudeSessionMetadata(options));
|
|
2440
2913
|
dlog(`Session detected from UUID: ${currentSession}`);
|
|
2441
2914
|
resolveTranscriptFromSessionId('session-id-from-output');
|
|
2442
2915
|
bindRealSessionToProxy(currentSession, 'session-id-from-output', currentSessionId || undefined);
|
|
@@ -2469,10 +2942,10 @@ async function run(options) {
|
|
|
2469
2942
|
currentSession = lastSeenSessionName;
|
|
2470
2943
|
observedSessionThisRun = true; // Mark that we've seen a session in THIS process
|
|
2471
2944
|
// Update THIS process's session entry (not global state.json)
|
|
2472
|
-
(0, state_1.updateCurrentProcessSession)(currentSessionId || 'unknown', currentSession);
|
|
2945
|
+
(0, state_1.updateCurrentProcessSession)(currentSessionId || 'unknown', currentSession, getClaudeSessionMetadata(options));
|
|
2473
2946
|
// Also update global state for backwards compatibility
|
|
2474
2947
|
(0, state_1.updateState)({ sessionName: currentSession });
|
|
2475
|
-
writeSessionFiles(currentSessionId || 'unknown', currentSession);
|
|
2948
|
+
writeSessionFiles(currentSessionId || 'unknown', currentSession, getClaudeSessionMetadata(options));
|
|
2476
2949
|
dlog(`Session detected from status line: ${currentSession} (observedSessionThisRun=true)`);
|
|
2477
2950
|
bindRealSessionToProxy(currentSession, 'status-line', currentSessionId || undefined);
|
|
2478
2951
|
resolveTranscriptFromSessionId('status-line');
|