@ekkos/cli 1.0.23 → 1.0.25

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.
@@ -283,9 +283,8 @@ function truncateToolResult(line) {
283
283
  return line;
284
284
  }
285
285
  }
286
- // Evicted content store (for retrieval by ccDNA)
286
+ // Local eviction debug store (best-effort forensic log)
287
287
  const EVICTED_STORE = path.join(os.homedir(), '.ekkos', 'evicted-context.jsonl');
288
- const EKKOS_CAPTURE_ENDPOINT = 'https://mcp.ekkos.dev/api/v1/context/evict';
289
288
  const EKKOS_API_URL = process.env.EKKOS_API_URL || 'https://mcp.ekkos.dev';
290
289
  /**
291
290
  * Evict messages using the Handshake Protocol (two-phase commit)
@@ -406,80 +405,16 @@ async function isHandshakeEvictionAvailable() {
406
405
  }
407
406
  return (0, eviction_client_js_1.checkEvictionHealth)(EKKOS_API_URL, authToken);
408
407
  }
409
- /**
410
- * Send evicted content to ekkOS cloud for later retrieval (async, non-blocking)
411
- */
412
- async function captureEvictedToCloud(lines, sessionId) {
413
- try {
414
- // Get auth token from environment
415
- const authToken = process.env.EKKOS_AUTH_TOKEN || process.env.SUPABASE_AUTH_TOKEN;
416
- if (!authToken) {
417
- debugLog('CLOUD_CAPTURE_SKIP', 'No auth token available');
418
- return;
419
- }
420
- // Parse messages from JSONL lines
421
- const messages = [];
422
- for (const line of lines) {
423
- try {
424
- const parsed = JSON.parse(line);
425
- if (parsed.message) {
426
- messages.push({
427
- role: parsed.message.role || 'unknown',
428
- content: parsed.message.content,
429
- });
430
- }
431
- }
432
- catch {
433
- // Skip unparseable lines
434
- }
435
- }
436
- if (messages.length === 0)
437
- return;
438
- // Estimate tokens (rough: 1 token ≈ 4 chars)
439
- const totalChars = lines.reduce((sum, l) => sum + l.length, 0);
440
- const estimatedTokens = Math.ceil(totalChars / 4);
441
- const payload = {
442
- session_id: sessionId || 'unknown',
443
- chunk_index: Date.now(), // Use timestamp as unique chunk index
444
- messages,
445
- token_count: estimatedTokens,
446
- eviction_reason: 'sliding_window',
447
- };
448
- // Non-blocking fetch - don't await, let it happen in background
449
- // 10s timeout to prevent lingering connections from burning resources
450
- fetch(EKKOS_CAPTURE_ENDPOINT, {
451
- method: 'POST',
452
- headers: {
453
- 'Content-Type': 'application/json',
454
- 'Authorization': `Bearer ${authToken}`,
455
- },
456
- body: JSON.stringify(payload),
457
- signal: AbortSignal.timeout(10000),
458
- }).then(res => {
459
- if (res.ok) {
460
- debugLog('CLOUD_CAPTURE_OK', `Sent ${messages.length} msgs to cloud`);
461
- }
462
- else {
463
- debugLog('CLOUD_CAPTURE_FAIL', `HTTP ${res.status}`);
464
- }
465
- }).catch(err => {
466
- debugLog('CLOUD_CAPTURE_ERROR', err.message);
467
- });
468
- }
469
- catch (err) {
470
- debugLog('CLOUD_CAPTURE_ERROR', err.message);
471
- }
472
- }
473
408
  /**
474
409
  * Save evicted content for later retrieval
475
410
  * Now supports handshake eviction when available
476
411
  *
477
412
  * @param lines - JSONL lines being evicted
478
- * @param sessionId - Session ID (for legacy capture)
479
- * @param sessionName - Session name (for handshake eviction)
480
- * @param indices - Original indices of evicted lines
413
+ * @param _sessionId - Session ID (reserved for future diagnostics)
414
+ * @param _sessionName - Session name (reserved for future diagnostics)
415
+ * @param _indices - Original indices of evicted lines (reserved for future diagnostics)
481
416
  */
482
- function saveEvictedContent(lines, sessionId, sessionName, indices) {
417
+ function saveEvictedContent(lines, _sessionId, _sessionName, _indices) {
483
418
  if (lines.length === 0)
484
419
  return;
485
420
  try {
@@ -516,8 +451,8 @@ function saveEvictedContent(lines, sessionId, sessionName, indices) {
516
451
  const entries = content.split('\n').filter(l => l.trim());
517
452
  fs.writeFileSync(EVICTED_STORE, entries.slice(-100).join('\n') + '\n');
518
453
  }
519
- // Send to cloud for cross-session retrieval (non-blocking)
520
- captureEvictedToCloud(lines, sessionId);
454
+ // No direct cloud capture here. Proxy/R2 handshake owns remote persistence.
455
+ // Keep this local file only for operator debugging.
521
456
  }
522
457
  catch {
523
458
  // Silent fail
@@ -502,6 +502,7 @@ async function launchDashboard(initialSessionName, jsonlPath, refreshMs) {
502
502
  let sessionName = initialSessionName;
503
503
  const blessed = require('blessed');
504
504
  const contrib = require('blessed-contrib');
505
+ const inTmux = process.env.TMUX !== undefined;
505
506
  // ══════════════════════════════════════════════════════════════════════════
506
507
  // TMUX SPLIT PANE ISOLATION
507
508
  // When dashboard runs in a separate tmux pane from `ekkos run`, blessed must
@@ -632,22 +633,27 @@ async function launchDashboard(initialSessionName, jsonlPath, refreshMs) {
632
633
  style: { fg: 'red', bold: true }, // ansi redBright = official Clawd orange
633
634
  });
634
635
  // Token chart (fills 40% of remaining)
635
- const tokenChart = contrib.line({
636
- top: layout.chart.top, left: 0, width: W, height: layout.chart.height,
637
- label: ' Tokens/Turn (K) ',
638
- showLegend: true,
639
- legend: { width: 8 },
640
- style: {
641
- line: 'green',
642
- text: 'white',
643
- baseline: 'white',
644
- border: { fg: 'cyan' },
645
- },
646
- border: { type: 'line', fg: 'cyan' },
647
- xLabelPadding: 0,
648
- xPadding: 1,
649
- wholeNumbersOnly: false,
650
- });
636
+ function createTokenChart(top, left, width, height) {
637
+ return contrib.line({
638
+ top, left, width, height,
639
+ label: ' Tokens/Turn (K) ',
640
+ showLegend: true,
641
+ legend: { width: 8 },
642
+ style: {
643
+ line: 'green',
644
+ text: 'white',
645
+ baseline: 'white',
646
+ border: { fg: 'cyan' },
647
+ },
648
+ border: { type: 'line', fg: 'cyan' },
649
+ xLabelPadding: 0,
650
+ xPadding: 1,
651
+ wholeNumbersOnly: false,
652
+ });
653
+ }
654
+ let tokenChart = createTokenChart(layout.chart.top, 0, W, layout.chart.height);
655
+ let chartLayoutW = 0;
656
+ let chartLayoutH = 0;
651
657
  // Turn table — manual rendering for full-width columns + dim dividers
652
658
  const turnBox = blessed.box({
653
659
  top: layout.table.top, left: 0, width: W, height: layout.table.height,
@@ -656,11 +662,11 @@ async function launchDashboard(initialSessionName, jsonlPath, refreshMs) {
656
662
  scrollable: true,
657
663
  alwaysScroll: true,
658
664
  scrollbar: { ch: '│', style: { fg: 'cyan' } },
659
- keys: true, // Enable keyboard scrolling
660
- vi: true, // Enable vi-style keys (j/k for scroll)
665
+ keys: !inTmux, // In tmux split mode keep dashboard passive
666
+ vi: !inTmux, // Avoid single-key handlers interfering with paste
661
667
  mouse: false, // Mouse disabled (use keyboard for scrolling, allows text selection)
662
- input: true,
663
- interactive: true, // Make box interactive for scrolling
668
+ input: !inTmux,
669
+ interactive: !inTmux, // Standalone only; passive in tmux split
664
670
  label: ' Turns (scroll: ↑↓/k/j, page: PgUp/u, home/end: g/G) ',
665
671
  border: { type: 'line', fg: 'cyan' },
666
672
  style: { fg: 'white', border: { fg: 'cyan' } },
@@ -704,10 +710,29 @@ async function launchDashboard(initialSessionName, jsonlPath, refreshMs) {
704
710
  contextBox.left = H_PAD;
705
711
  contextBox.width = contentWidth;
706
712
  contextBox.height = layout.context.height;
707
- tokenChart.top = layout.chart.top;
708
- tokenChart.left = H_PAD;
709
- tokenChart.width = contentWidth;
710
- tokenChart.height = layout.chart.height;
713
+ // blessed-contrib line can keep a stale tiny canvas when terminals report
714
+ // initial dimensions incorrectly (observed in Windows Terminal). Rebuild
715
+ // the chart widget whenever dimensions change so the plot fills the panel.
716
+ if (chartLayoutW !== contentWidth || chartLayoutH !== layout.chart.height) {
717
+ try {
718
+ screen.remove(tokenChart);
719
+ }
720
+ catch { }
721
+ try {
722
+ tokenChart.destroy?.();
723
+ }
724
+ catch { }
725
+ tokenChart = createTokenChart(layout.chart.top, H_PAD, contentWidth, layout.chart.height);
726
+ chartLayoutW = contentWidth;
727
+ chartLayoutH = layout.chart.height;
728
+ screen.append(tokenChart);
729
+ }
730
+ else {
731
+ tokenChart.top = layout.chart.top;
732
+ tokenChart.left = H_PAD;
733
+ tokenChart.width = contentWidth;
734
+ tokenChart.height = layout.chart.height;
735
+ }
711
736
  turnBox.top = layout.table.top;
712
737
  turnBox.left = H_PAD;
713
738
  turnBox.width = contentWidth;
@@ -1010,7 +1035,9 @@ async function launchDashboard(initialSessionName, jsonlPath, refreshMs) {
1010
1035
  ` ${routingStr}` +
1011
1036
  ` R[A:${data.replayAppliedCount} SZ:${data.replaySkippedSizeCount} ST:${data.replaySkipStoreCount}]` +
1012
1037
  savingsStr +
1013
- ` {gray-fg}? help q quit r refresh{/gray-fg}`);
1038
+ (inTmux
1039
+ ? ` {gray-fg}Ctrl+C quit{/gray-fg}`
1040
+ : ` {gray-fg}? help q quit r refresh{/gray-fg}`));
1014
1041
  }
1015
1042
  catch (err) {
1016
1043
  dlog(`Footer: ${err.message}`);
@@ -1131,90 +1158,99 @@ async function launchDashboard(initialSessionName, jsonlPath, refreshMs) {
1131
1158
  // KEYBOARD SHORTCUTS - Only capture when dashboard pane has focus
1132
1159
  // In tmux split mode, this prevents capturing keys from Claude Code pane
1133
1160
  // ══════════════════════════════════════════════════════════════════════════
1134
- screen.key(['q', 'C-c'], () => {
1161
+ screen.key(['C-c'], () => {
1135
1162
  clearInterval(pollInterval);
1136
1163
  clearInterval(windowPollInterval);
1137
1164
  clearTimeout(sparkleTimer);
1138
1165
  screen.destroy();
1139
1166
  process.exit(0);
1140
1167
  });
1141
- screen.key(['r'], () => {
1142
- lastFileSize = 0;
1143
- updateDashboard();
1144
- updateWindowBox();
1145
- });
1168
+ if (!inTmux) {
1169
+ screen.key(['q'], () => {
1170
+ clearInterval(pollInterval);
1171
+ clearInterval(windowPollInterval);
1172
+ clearTimeout(sparkleTimer);
1173
+ screen.destroy();
1174
+ process.exit(0);
1175
+ });
1176
+ screen.key(['r'], () => {
1177
+ lastFileSize = 0;
1178
+ updateDashboard();
1179
+ updateWindowBox();
1180
+ });
1181
+ }
1146
1182
  // ══════════════════════════════════════════════════════════════════════════
1147
1183
  // FOCUS MANAGEMENT: In tmux split mode, DON'T auto-focus the turnBox
1148
1184
  // This prevents the dashboard from stealing focus from Claude Code on startup
1149
1185
  // User can manually focus by clicking into the dashboard pane
1150
1186
  // ══════════════════════════════════════════════════════════════════════════
1151
- // Check if we're in a tmux session
1152
- const inTmux = process.env.TMUX !== undefined;
1153
1187
  if (!inTmux) {
1154
1188
  // Only auto-focus when running standalone (not in tmux split)
1155
1189
  turnBox.focus();
1156
1190
  }
1157
1191
  // Scroll controls for turn table
1158
- screen.key(['up', 'k'], () => {
1159
- turnBox.scroll(-1);
1160
- screen.render();
1161
- });
1162
- screen.key(['down', 'j'], () => {
1163
- turnBox.scroll(1);
1164
- screen.render();
1165
- });
1166
- screen.key(['pageup', 'u'], () => {
1167
- turnBox.scroll(-(turnBox.height - 2));
1168
- screen.render();
1169
- });
1170
- screen.key(['pagedown', 'd'], () => {
1171
- turnBox.scroll((turnBox.height - 2));
1172
- screen.render();
1173
- });
1174
- screen.key(['home', 'g'], () => {
1175
- turnBox.setScrollPerc(0);
1176
- screen.render();
1177
- });
1178
- screen.key(['end', 'G'], () => {
1179
- turnBox.setScrollPerc(100);
1180
- screen.render();
1181
- });
1182
- screen.key(['?', 'h'], () => {
1183
- // Quick help overlay
1184
- const help = blessed.box({
1185
- top: 'center',
1186
- left: 'center',
1187
- width: 50,
1188
- height: 16,
1189
- content: ('{bold}Navigation{/bold}\n' +
1190
- ' ↑/k/j/↓ Scroll line\n' +
1191
- ' PgUp/u Scroll page up\n' +
1192
- ' PgDn/d Scroll page down\n' +
1193
- ' g/Home Scroll to top\n' +
1194
- ' G/End Scroll to bottom\n' +
1195
- '\n' +
1196
- '{bold}Controls{/bold}\n' +
1197
- ' r Refresh now\n' +
1198
- ' q/Ctrl+C Quit\n' +
1199
- '\n' +
1200
- '{gray-fg}Press any key to close{/gray-fg}'),
1201
- tags: true,
1202
- border: 'line',
1203
- style: { border: { fg: 'cyan' } },
1204
- padding: 1,
1192
+ if (!inTmux) {
1193
+ screen.key(['up', 'k'], () => {
1194
+ turnBox.scroll(-1);
1195
+ screen.render();
1205
1196
  });
1206
- screen.append(help);
1207
- screen.render();
1208
- // Defer listener so the '?' keypress that opened help doesn't immediately close it
1209
- setImmediate(() => {
1210
- const closeHelp = () => {
1211
- help.destroy();
1212
- screen.render();
1213
- screen.removeListener('key', closeHelp);
1214
- };
1215
- screen.once('key', closeHelp);
1197
+ screen.key(['down', 'j'], () => {
1198
+ turnBox.scroll(1);
1199
+ screen.render();
1216
1200
  });
1217
- });
1201
+ screen.key(['pageup', 'u'], () => {
1202
+ turnBox.scroll(-(turnBox.height - 2));
1203
+ screen.render();
1204
+ });
1205
+ screen.key(['pagedown', 'd'], () => {
1206
+ turnBox.scroll((turnBox.height - 2));
1207
+ screen.render();
1208
+ });
1209
+ screen.key(['home', 'g'], () => {
1210
+ turnBox.setScrollPerc(0);
1211
+ screen.render();
1212
+ });
1213
+ screen.key(['end', 'G'], () => {
1214
+ turnBox.setScrollPerc(100);
1215
+ screen.render();
1216
+ });
1217
+ screen.key(['?', 'h'], () => {
1218
+ // Quick help overlay
1219
+ const help = blessed.box({
1220
+ top: 'center',
1221
+ left: 'center',
1222
+ width: 50,
1223
+ height: 16,
1224
+ content: ('{bold}Navigation{/bold}\n' +
1225
+ ' ↑/k/j/↓ Scroll line\n' +
1226
+ ' PgUp/u Scroll page up\n' +
1227
+ ' PgDn/d Scroll page down\n' +
1228
+ ' g/Home Scroll to top\n' +
1229
+ ' G/End Scroll to bottom\n' +
1230
+ '\n' +
1231
+ '{bold}Controls{/bold}\n' +
1232
+ ' r Refresh now\n' +
1233
+ ' q/Ctrl+C Quit\n' +
1234
+ '\n' +
1235
+ '{gray-fg}Press any key to close{/gray-fg}'),
1236
+ tags: true,
1237
+ border: 'line',
1238
+ style: { border: { fg: 'cyan' } },
1239
+ padding: 1,
1240
+ });
1241
+ screen.append(help);
1242
+ screen.render();
1243
+ // Defer listener so the '?' keypress that opened help doesn't immediately close it
1244
+ setImmediate(() => {
1245
+ const closeHelp = () => {
1246
+ help.destroy();
1247
+ screen.render();
1248
+ screen.removeListener('key', closeHelp);
1249
+ };
1250
+ screen.once('key', closeHelp);
1251
+ });
1252
+ });
1253
+ }
1218
1254
  // Clear terminal buffer — prevents garbage text from previous commands
1219
1255
  screen.program.clear();
1220
1256
  // Dashboard is fully passive — no widget captures keyboard input
@@ -43,139 +43,6 @@ const fs = __importStar(require("fs"));
43
43
  const path = __importStar(require("path"));
44
44
  const os = __importStar(require("os"));
45
45
  const child_process_1 = require("child_process");
46
- // ═══════════════════════════════════════════════════════════════════════════
47
- // ccDNA AUTO-LOAD: Apply Claude Code patches before spawning
48
- // ═══════════════════════════════════════════════════════════════════════════
49
- const CCDNA_PATHS = [
50
- // Development path (DEV sibling directory)
51
- // From: EKKOS/packages/ekkos-cli/dist/commands/ → DEV/ekkos-ccdna/
52
- path.join(__dirname, '..', '..', '..', '..', '..', 'ekkos-ccdna', 'dist', 'index.mjs'),
53
- // User install path
54
- path.join(os.homedir(), '.ekkos', 'ccdna', 'dist', 'index.mjs'),
55
- // npm global (homebrew)
56
- '/opt/homebrew/lib/node_modules/ekkos-ccdna/dist/index.mjs',
57
- // npm global (standard)
58
- path.join(os.homedir(), '.npm-global', 'lib', 'node_modules', 'ekkos-ccdna', 'dist', 'index.mjs'),
59
- ];
60
- /**
61
- * Find ccDNA installation path
62
- */
63
- function findCcdnaPath() {
64
- for (const p of CCDNA_PATHS) {
65
- if (fs.existsSync(p)) {
66
- return p;
67
- }
68
- }
69
- return null;
70
- }
71
- /**
72
- * Apply ccDNA patches silently before Claude spawns
73
- * Returns version string if patches were applied, null otherwise
74
- *
75
- * @param verbose - Show detailed output
76
- * @param claudePath - Path to Claude Code to patch (if different from default)
77
- */
78
- function applyCcdnaPatches(verbose, claudePath) {
79
- // DISABLED: ccDNA patching is currently corrupting cli.js (JSON parse error at position 7945)
80
- // See: https://github.com/anthropics/ekkos/issues/2856
81
- // The patching process is injecting code that breaks the minified cli.js
82
- // Temporarily disabled until ccDNA is fixed upstream
83
- if (verbose) {
84
- console.log(chalk_1.default.gray(' ccDNA patching disabled (see issue #2856)'));
85
- }
86
- return null;
87
- // Original implementation (disabled):
88
- /*
89
- const ccdnaPath = findCcdnaPath();
90
- if (!ccdnaPath) {
91
- if (verbose) {
92
- console.log(chalk.gray(' ccDNA not found - skipping patches'));
93
- }
94
- return null;
95
- }
96
-
97
- // Read ccDNA version from package.json FIRST
98
- let ccdnaVersion = 'unknown';
99
- try {
100
- const pkgPath = path.join(path.dirname(ccdnaPath), '..', 'package.json');
101
- if (fs.existsSync(pkgPath)) {
102
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
103
- ccdnaVersion = pkg.version || 'unknown';
104
- }
105
- } catch {
106
- // Ignore version detection errors
107
- }
108
-
109
- try {
110
- // Set env var to tell ccDNA which Claude to patch
111
- // eslint-disable-next-line no-restricted-syntax
112
- const env = { ...process.env };
113
- if (claudePath) {
114
- // ccDNA checks CCDNA_CC_INSTALLATION_PATH to override default detection
115
- env.CCDNA_CC_INSTALLATION_PATH = claudePath;
116
- }
117
-
118
- // Run ccDNA in apply mode (non-interactive)
119
- execSync(`node "${ccdnaPath}" -a`, {
120
- stdio: verbose ? 'inherit' : 'pipe',
121
- timeout: 30000, // 30 second timeout
122
- env,
123
- });
124
-
125
- if (verbose) {
126
- console.log(chalk.green(` ✓ ccDNA v${ccdnaVersion} patches applied`));
127
- }
128
- return ccdnaVersion;
129
- } catch (err) {
130
- if (verbose) {
131
- console.log(chalk.yellow(` ⚠ ccDNA patch failed: ${(err as Error).message}`));
132
- }
133
- return null;
134
- }
135
- */
136
- }
137
- /**
138
- * Restore original Claude Code (remove ccDNA patches) on exit
139
- * This restores the ekkOS-managed installation (~/.ekkos/claude-code/) to its base state
140
- *
141
- * NOTE: We intentionally DON'T restore on exit anymore because:
142
- * 1. ekkOS uses a SEPARATE installation (~/.ekkos/claude-code/) from homebrew
143
- * 2. The homebrew `claude` command should always be vanilla (untouched)
144
- * 3. The ekkOS installation can stay patched - it's only used by `ekkos run`
145
- *
146
- * This function is kept for manual/explicit restore scenarios.
147
- */
148
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
149
- function restoreCcdnaPatches(verbose, claudePath) {
150
- const ccdnaPath = findCcdnaPath();
151
- if (!ccdnaPath) {
152
- return false;
153
- }
154
- try {
155
- // Set env var to tell ccDNA which Claude to restore
156
- // eslint-disable-next-line no-restricted-syntax
157
- const env = { ...process.env };
158
- if (claudePath) {
159
- env.CCDNA_CC_INSTALLATION_PATH = claudePath;
160
- }
161
- // Run ccDNA in restore mode (non-interactive)
162
- (0, child_process_1.execSync)(`node "${ccdnaPath}" -r`, {
163
- stdio: verbose ? 'inherit' : 'pipe',
164
- timeout: 30000, // 30 second timeout
165
- env,
166
- });
167
- if (verbose) {
168
- console.log(chalk_1.default.green(' ✓ ccDNA patches removed (vanilla restored)'));
169
- }
170
- return true;
171
- }
172
- catch (err) {
173
- if (verbose) {
174
- console.log(chalk_1.default.yellow(` ⚠ ccDNA restore failed: ${err.message}`));
175
- }
176
- return false;
177
- }
178
- }
179
46
  const state_1 = require("../utils/state");
180
47
  const session_binding_1 = require("../utils/session-binding");
181
48
  const doctor_1 = require("./doctor");
@@ -443,7 +310,7 @@ const isWindows = os.platform() === 'win32';
443
310
  // 'latest' = use latest version, or specify like '2.1.33' for specific version
444
311
  // Core ekkOS patches (eviction, context management) work with all recent versions
445
312
  // Cosmetic patches may fail on newer versions but don't affect functionality
446
- const PINNED_CLAUDE_VERSION = '2.1.45';
313
+ const PINNED_CLAUDE_VERSION = 'latest';
447
314
  // Max output tokens for Claude responses
448
315
  // Default: 16384 (safe for Sonnet 4.5)
449
316
  // Opus 4.5 supports up to 64k - set EKKOS_MAX_OUTPUT_TOKENS=32768 or =65536 to use higher limits
@@ -1074,22 +941,15 @@ async function run(options) {
1074
941
  }
1075
942
  }
1076
943
  // ══════════════════════════════════════════════════════════════════════════
1077
- // ccDNA AUTO-PATCH: Apply Claude Code customizations before spawn
1078
- // This patches the context warning, themes, and other ccDNA features
1079
- // Skip if --no-dna flag is set
944
+ // Legacy ccDNA flags are now no-ops.
945
+ // Context eviction and replay are owned by the proxy.
1080
946
  // ══════════════════════════════════════════════════════════════════════════
1081
947
  const noDna = options.noDna || false;
1082
- let ccdnaVersion = null;
1083
- if (noDna) {
1084
- if (verbose) {
1085
- console.log(chalk_1.default.yellow(' ⏭️ Skipping ccDNA injection (--no-dna)'));
1086
- }
948
+ if (verbose && noDna) {
949
+ console.log(chalk_1.default.gray(' --skip-dna is deprecated (ccDNA patching is removed)'));
1087
950
  }
1088
- else {
1089
- if (verbose && claudeCliPath) {
1090
- console.log(chalk_1.default.gray(` 🔧 Patching: ${claudeCliPath}`));
1091
- }
1092
- ccdnaVersion = applyCcdnaPatches(verbose, claudeCliPath);
951
+ if (verbose && claudeCliPath) {
952
+ console.log(chalk_1.default.gray(` 🤖 Claude CLI: ${claudeCliPath}`));
1093
953
  }
1094
954
  const pinnedVersion = isNpxMode ? rawClaudePath.split(':')[1] : null;
1095
955
  const claudePath = isNpxMode ? 'npx' : rawClaudePath;
@@ -1250,10 +1110,10 @@ async function run(options) {
1250
1110
  logoLines.forEach(line => console.log(chalk_1.default.magenta(line)));
1251
1111
  console.log('');
1252
1112
  // ══════════════════════════════════════════════════════════════════════════
1253
- // ANIMATED TITLE: "Cognitive Continuity Engine" with orange/white shine
1113
+ // ANIMATED TITLE: "ekkOS_Pulse" with orange/white shine
1254
1114
  // ══════════════════════════════════════════════════════════════════════════
1255
- const titleText = 'Cognitive Continuity Engine';
1256
- const taglineText = 'Context is finite. Intelligence isn\'t.';
1115
+ const titleText = 'ekkOS_Pulse';
1116
+ const taglineText = 'Infinite context. Native model quality.';
1257
1117
  // Color palette for shine effect
1258
1118
  const whiteShine = chalk_1.default.whiteBright;
1259
1119
  // Phase 1: Typewriter effect for title
@@ -1333,9 +1193,6 @@ async function run(options) {
1333
1193
  if (bypass) {
1334
1194
  console.log(chalk_1.default.yellow(' ⚡ Bypass permissions mode enabled'));
1335
1195
  }
1336
- if (noDna) {
1337
- console.log(chalk_1.default.yellow(' ⏭️ ccDNA injection skipped (--no-dna)'));
1338
- }
1339
1196
  if (verbose) {
1340
1197
  console.log(chalk_1.default.gray(` 📁 Debug log: ${config.debugLogPath}`));
1341
1198
  console.log(chalk_1.default.gray(` ⏱ Timing: clear=${config.clearWaitMs}ms, idleMax=${config.maxIdleWaitMs}ms (~${Math.round((config.clearWaitMs + config.maxIdleWaitMs * 2 + 1700) / 1000)}s total)`));
@@ -1348,9 +1205,6 @@ async function run(options) {
1348
1205
  if (bypass) {
1349
1206
  console.log(chalk_1.default.yellow(' ⚡ Bypass permissions mode enabled'));
1350
1207
  }
1351
- if (noDna) {
1352
- console.log(chalk_1.default.yellow(' ⏭️ ccDNA injection skipped (--no-dna)'));
1353
- }
1354
1208
  if (verbose) {
1355
1209
  console.log(chalk_1.default.gray(` 📁 Debug log: ${config.debugLogPath}`));
1356
1210
  }
@@ -1791,12 +1645,8 @@ async function run(options) {
1791
1645
  console.log(chalk_1.default.yellow(' Monitor-only mode (--no-inject)'));
1792
1646
  }
1793
1647
  if (verbose) {
1794
- // Show Claude version with ccDNA version if patched
1795
1648
  const ccVersion = pinnedVersion || PINNED_CLAUDE_VERSION;
1796
- const versionStr = ccdnaVersion
1797
- ? `Claude Code v${ccVersion} + ekkOS_Continuum v${ccdnaVersion}`
1798
- : `Claude Code v${ccVersion}`;
1799
- console.log(chalk_1.default.gray(` 🤖 ${versionStr}`));
1649
+ console.log(chalk_1.default.gray(` 🤖 Claude Code v${ccVersion}`));
1800
1650
  if (currentSession) {
1801
1651
  console.log(chalk_1.default.green(` 📍 Session: ${currentSession}`));
1802
1652
  }
package/dist/index.js CHANGED
@@ -246,7 +246,7 @@ commander_1.program
246
246
  .option('-d, --doctor', 'Run diagnostics before starting')
247
247
  .option('-r, --research', 'Auto-run research agent on startup (scans arXiv for new AI papers)')
248
248
  .option('--skip-inject', 'Monitor-only mode (detect context wall but print instructions instead of auto-inject)')
249
- .option('--skip-dna', 'Skip ccDNA injection (bypass Claude Code patching)')
249
+ .option('--skip-dna', 'Deprecated no-op (legacy ccDNA patching has been removed)')
250
250
  .option('--skip-proxy', 'Skip API proxy (use direct Anthropic API, disables seamless context eviction)')
251
251
  .option('--dashboard', 'Launch with live usage dashboard in an isolated 60/40 tmux split (requires tmux)')
252
252
  .option('--kickstart', 'Auto-send "test" on load to create session immediately (used internally by --dashboard)')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ekkos/cli",
3
- "version": "1.0.23",
3
+ "version": "1.0.25",
4
4
  "description": "Setup ekkOS memory for AI coding assistants (Claude Code, Cursor, Windsurf)",
5
5
  "main": "dist/index.js",
6
6
  "bin": {