@ekkos/cli 1.0.22 → 1.0.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.
@@ -332,6 +332,23 @@ function findLatestJsonl(projectPath, createdAfterMs) {
332
332
  }
333
333
  return null;
334
334
  }
335
+ function findJsonlBySessionId(projectPath, sessionId) {
336
+ if (!sessionId)
337
+ return null;
338
+ const candidateEncodings = new Set([
339
+ projectPath.replace(/[\\/]/g, '-'),
340
+ projectPath.replace(/[:\\/]/g, '-'),
341
+ '-' + projectPath.replace(/[:\\/]/g, '-'),
342
+ projectPath.replace(/\//g, '-'),
343
+ ]);
344
+ const projectsRoot = path.join(os.homedir(), '.claude', 'projects');
345
+ for (const encoded of candidateEncodings) {
346
+ const exactPath = path.join(projectsRoot, encoded, `${sessionId}.jsonl`);
347
+ if (fs.existsSync(exactPath))
348
+ return exactPath;
349
+ }
350
+ return null;
351
+ }
335
352
  function getLatestSession() {
336
353
  const sessions = (0, state_js_1.getActiveSessions)();
337
354
  if (sessions.length === 0)
@@ -373,7 +390,8 @@ async function waitForNewSession() {
373
390
  const hint = JSON.parse(fs.readFileSync(hintPath, 'utf-8'));
374
391
  if (hint.ts >= launchTs - 5000 && hint.sessionName && hint.projectPath) {
375
392
  candidateName = hint.sessionName;
376
- const jsonlPath = findLatestJsonl(hint.projectPath, launchTs)
393
+ const jsonlPath = findJsonlBySessionId(hint.projectPath, hint.sessionId || '')
394
+ || findLatestJsonl(hint.projectPath, launchTs)
377
395
  || resolveJsonlPath(hint.sessionName, launchTs);
378
396
  if (jsonlPath) {
379
397
  console.log(chalk_1.default.green(` Found session (hook hint): ${hint.sessionName}`));
@@ -568,7 +586,7 @@ async function launchDashboard(initialSessionName, jsonlPath, refreshMs) {
568
586
  function calcLayout() {
569
587
  const H = screen.height;
570
588
  const remaining = Math.max(6, H - FIXED_H);
571
- const chartH = Math.max(8, Math.floor(remaining * 0.30));
589
+ const chartH = Math.max(8, Math.floor(remaining * 0.40));
572
590
  const tableH = Math.max(4, remaining - chartH);
573
591
  return {
574
592
  header: { top: 0, height: HEADER_H },
@@ -580,6 +598,10 @@ async function launchDashboard(initialSessionName, jsonlPath, refreshMs) {
580
598
  };
581
599
  }
582
600
  let layout = calcLayout();
601
+ let lastFileSize = 0;
602
+ let lastData = null;
603
+ let lastChartSeries = null;
604
+ let lastScrollPerc = 0; // Preserve scroll position across updates
583
605
  // Header: session stats (3 lines)
584
606
  const headerBox = blessed.box({
585
607
  top: layout.header.top, left: 0, width: W, height: layout.header.height,
@@ -609,23 +631,28 @@ async function launchDashboard(initialSessionName, jsonlPath, refreshMs) {
609
631
  tags: false,
610
632
  style: { fg: 'red', bold: true }, // ansi redBright = official Clawd orange
611
633
  });
612
- // Token chart (fills 30% of remaining)
613
- const tokenChart = contrib.line({
614
- top: layout.chart.top, left: 0, width: W, height: layout.chart.height,
615
- label: ' Tokens/Turn (K) ',
616
- showLegend: true,
617
- legend: { width: 8 },
618
- style: {
619
- line: 'green',
620
- text: 'white',
621
- baseline: 'white',
622
- border: { fg: 'cyan' },
623
- },
624
- border: { type: 'line', fg: 'cyan' },
625
- xLabelPadding: 0,
626
- xPadding: 1,
627
- wholeNumbersOnly: false,
628
- });
634
+ // Token chart (fills 40% of remaining)
635
+ function createTokenChart(top, left, width, height) {
636
+ return contrib.line({
637
+ top, left, width, height,
638
+ label: ' Tokens/Turn (K) ',
639
+ showLegend: true,
640
+ legend: { width: 8 },
641
+ style: {
642
+ line: 'green',
643
+ text: 'white',
644
+ baseline: 'white',
645
+ border: { fg: 'cyan' },
646
+ },
647
+ border: { type: 'line', fg: 'cyan' },
648
+ xLabelPadding: 0,
649
+ xPadding: 1,
650
+ wholeNumbersOnly: false,
651
+ });
652
+ }
653
+ let tokenChart = createTokenChart(layout.chart.top, 0, W, layout.chart.height);
654
+ let chartLayoutW = 0;
655
+ let chartLayoutH = 0;
629
656
  // Turn table — manual rendering for full-width columns + dim dividers
630
657
  const turnBox = blessed.box({
631
658
  top: layout.table.top, left: 0, width: W, height: layout.table.height,
@@ -682,10 +709,29 @@ async function launchDashboard(initialSessionName, jsonlPath, refreshMs) {
682
709
  contextBox.left = H_PAD;
683
710
  contextBox.width = contentWidth;
684
711
  contextBox.height = layout.context.height;
685
- tokenChart.top = layout.chart.top;
686
- tokenChart.left = H_PAD;
687
- tokenChart.width = contentWidth;
688
- tokenChart.height = layout.chart.height;
712
+ // blessed-contrib line can keep a stale tiny canvas when terminals report
713
+ // initial dimensions incorrectly (observed in Windows Terminal). Rebuild
714
+ // the chart widget whenever dimensions change so the plot fills the panel.
715
+ if (chartLayoutW !== contentWidth || chartLayoutH !== layout.chart.height) {
716
+ try {
717
+ screen.remove(tokenChart);
718
+ }
719
+ catch { }
720
+ try {
721
+ tokenChart.destroy?.();
722
+ }
723
+ catch { }
724
+ tokenChart = createTokenChart(layout.chart.top, H_PAD, contentWidth, layout.chart.height);
725
+ chartLayoutW = contentWidth;
726
+ chartLayoutH = layout.chart.height;
727
+ screen.append(tokenChart);
728
+ }
729
+ else {
730
+ tokenChart.top = layout.chart.top;
731
+ tokenChart.left = H_PAD;
732
+ tokenChart.width = contentWidth;
733
+ tokenChart.height = layout.chart.height;
734
+ }
689
735
  turnBox.top = layout.table.top;
690
736
  turnBox.left = H_PAD;
691
737
  turnBox.width = contentWidth;
@@ -706,6 +752,8 @@ async function launchDashboard(initialSessionName, jsonlPath, refreshMs) {
706
752
  catch { }
707
753
  }
708
754
  }
755
+ // Apply once at startup so chart/table geometry is correct even before any resize event.
756
+ applyLayout();
709
757
  // Track geometry so we can re-anchor widgets even if tmux resize events are flaky
710
758
  let lastLayoutW = screen.width || 0;
711
759
  let lastLayoutH = screen.height || 0;
@@ -772,10 +820,6 @@ async function launchDashboard(initialSessionName, jsonlPath, refreshMs) {
772
820
  }
773
821
  scheduleWave();
774
822
  // ── Update function ──
775
- let lastFileSize = 0;
776
- let lastData = null;
777
- let lastChartSeries = null;
778
- let lastScrollPerc = 0; // Preserve scroll position across updates
779
823
  const debugLog = path.join(os.homedir(), '.ekkos', 'dashboard.log');
780
824
  function dlog(msg) {
781
825
  try {
@@ -252,11 +252,17 @@ const INTERRUPTED_REGEX = /interrupted.*what should claude do instead/i;
252
252
  const PALETTE_INDICATOR_REGEX = /\/(clear|continue|compact|help|bug|config)/i;
253
253
  // ═══════════════════════════════════════════════════════════════════════════
254
254
  // SESSION NAME DETECTION (3-word slug: word-word-word)
255
- // Claude prints session name in footer: · Turn N · groovy-koala-saves · 📅
255
+ // Claude prints session name in footer:
256
+ // · 🧠 ekkOS_™ · Turn N · groovy-koala-saves · 📅
257
+ // · 🧠 ekkOS_™ · groovy-koala-saves · 📅
256
258
  // ═══════════════════════════════════════════════════════════════════════════
257
- // Strong signal: explicit turn footer emitted by Claude/ekkOS status line.
258
- // Requires "Turn <n> · <session> ·" to avoid matching arbitrary slug text.
259
- const SESSION_NAME_IN_STATUS_REGEX = /turn\s+\d+\s*·\s*([a-z]+-[a-z]+-[a-z]+)\s*·/i;
259
+ // Strong signal: ekkOS-branded footer with optional turn segment.
260
+ // Supports both:
261
+ // "· ekkOS_™ · Turn N · <session> · 📅"
262
+ // "· ekkOS_™ · <session> · 📅"
263
+ const SESSION_NAME_IN_EKKOS_FOOTER_REGEX = /ekkos[^\n·]*·\s*(?:turn\s+\d+\s*·\s*)?([a-z]+-[a-z]+-[a-z]+)\s*·/i;
264
+ // Legacy fallback: plain Claude status line with explicit turn marker.
265
+ const SESSION_NAME_IN_TURN_FOOTER_REGEX = /turn\s+\d+\s*·\s*([a-z]+-[a-z]+-[a-z]+)\s*·/i;
260
266
  // Weaker signal: any 3-word slug (word-word-word pattern)
261
267
  const SESSION_NAME_REGEX = /\b([a-z]+-[a-z]+-[a-z]+)\b/i;
262
268
  // Orphan tool_result marker emitted by ccDNA validate mode
@@ -2429,12 +2435,15 @@ async function run(options) {
2429
2435
  }
2430
2436
  // ════════════════════════════════════════════════════════════════════════
2431
2437
  // SESSION NAME DETECTION (PRIMARY METHOD)
2432
- // Claude footer: "· Turn N · groovy-koala-saves · 📅 2026-01-17"
2438
+ // Claude footer examples:
2439
+ // "· 🧠 ekkOS_™ · Turn N · groovy-koala-saves · 📅 2026-01-17"
2440
+ // "· 🧠 ekkOS_™ · groovy-koala-saves · 📅 2026-01-17"
2433
2441
  // This is MORE reliable than UUID extraction
2434
2442
  // ════════════════════════════════════════════════════════════════════════
2435
2443
  const plain = stripAnsi(data);
2436
- // Strong signal: session name between dot separators in status/footer line
2437
- const statusMatch = plain.match(SESSION_NAME_IN_STATUS_REGEX);
2444
+ // Strong signal: session name in branded footer (with/without turn marker)
2445
+ const statusMatch = plain.match(SESSION_NAME_IN_EKKOS_FOOTER_REGEX) ||
2446
+ plain.match(SESSION_NAME_IN_TURN_FOOTER_REGEX);
2438
2447
  if (statusMatch) {
2439
2448
  const detectedSession = statusMatch[1].toLowerCase();
2440
2449
  // Validate against word lists (SOURCE OF TRUTH)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ekkos/cli",
3
- "version": "1.0.22",
3
+ "version": "1.0.24",
4
4
  "description": "Setup ekkOS memory for AI coding assistants (Claude Code, Cursor, Windsurf)",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -116,12 +116,12 @@ function Convert-UuidToWords {
116
116
  if (-not $uuid -or $uuid -eq "unknown") { return "unknown-session" }
117
117
 
118
118
  $clean = $uuid -replace "-", ""
119
- if ($clean.Length -lt 6) { return "unknown-session" }
119
+ if ($clean.Length -lt 12) { return "unknown-session" }
120
120
 
121
121
  try {
122
- $a = [Convert]::ToInt32($clean.Substring(0,2), 16) % $adjectives.Length
123
- $n = [Convert]::ToInt32($clean.Substring(2,2), 16) % $nouns.Length
124
- $an = [Convert]::ToInt32($clean.Substring(4,2), 16) % $verbs.Length
122
+ $a = [Convert]::ToInt32($clean.Substring(0,4), 16) % $adjectives.Length
123
+ $n = [Convert]::ToInt32($clean.Substring(4,4), 16) % $nouns.Length
124
+ $an = [Convert]::ToInt32($clean.Substring(8,4), 16) % $verbs.Length
125
125
 
126
126
  return "$($adjectives[$a])-$($nouns[$n])-$($verbs[$an])"
127
127
  } catch {
@@ -45,12 +45,6 @@ if (result !== undefined && result !== null) console.log(result);
45
45
  SESSION_ID=$(parse_hook_env "$HOOK_ENV" '.sessionId')
46
46
  [ -z "$SESSION_ID" ] && SESSION_ID="unknown"
47
47
 
48
- TURN=$(parse_hook_env "$HOOK_ENV" '.turn')
49
- [ -z "$TURN" ] && TURN="0"
50
-
51
- CONTEXT_PERCENT=$(parse_hook_env "$HOOK_ENV" '.contextUsagePercent')
52
- [ -z "$CONTEXT_PERCENT" ] && CONTEXT_PERCENT="0"
53
-
54
48
  MODEL=$(parse_hook_env "$HOOK_ENV" '.model')
55
49
  [ -z "$MODEL" ] && MODEL="Claude Code (Opus 4.5)"
56
50
 
@@ -59,6 +53,7 @@ MODEL=$(parse_hook_env "$HOOK_ENV" '.model')
59
53
  # ═══════════════════════════════════════════════════════════════════════════
60
54
  declare -a ADJECTIVES
61
55
  declare -a NOUNS
56
+ declare -a VERBS
62
57
  SESSION_WORDS_LOADED=false
63
58
 
64
59
  load_session_words() {
@@ -79,6 +74,7 @@ load_session_words() {
79
74
  if [ "${BASH_VERSINFO[0]}" -ge 4 ]; then
80
75
  readarray -t ADJECTIVES < <(node "$JSON_PARSE_HELPER" "$words_file" '.adjectives' 2>/dev/null)
81
76
  readarray -t NOUNS < <(node "$JSON_PARSE_HELPER" "$words_file" '.nouns' 2>/dev/null)
77
+ readarray -t VERBS < <(node "$JSON_PARSE_HELPER" "$words_file" '.verbs' 2>/dev/null)
82
78
  else
83
79
  local i=0
84
80
  while IFS= read -r line; do
@@ -90,9 +86,14 @@ load_session_words() {
90
86
  NOUNS[i]="$line"
91
87
  ((i++))
92
88
  done < <(node "$JSON_PARSE_HELPER" "$words_file" '.nouns' 2>/dev/null)
89
+ i=0
90
+ while IFS= read -r line; do
91
+ VERBS[i]="$line"
92
+ ((i++))
93
+ done < <(node "$JSON_PARSE_HELPER" "$words_file" '.verbs' 2>/dev/null)
93
94
  fi
94
95
 
95
- if [ ${#ADJECTIVES[@]} -gt 0 ] && [ ${#NOUNS[@]} -gt 0 ]; then
96
+ if [ ${#ADJECTIVES[@]} -gt 0 ] && [ ${#NOUNS[@]} -gt 0 ] && [ ${#VERBS[@]} -gt 0 ]; then
96
97
  SESSION_WORDS_LOADED=true
97
98
  return 0
98
99
  fi
@@ -108,36 +109,39 @@ convert_uuid_to_name() {
108
109
  return
109
110
  }
110
111
 
111
- local hex="${uuid:0:8}"
112
- hex="${hex//-/}"
112
+ local hex="${uuid//-/}"
113
+ hex="${hex:0:12}"
113
114
 
114
115
  if [[ ! "$hex" =~ ^[0-9a-fA-F]+$ ]]; then
115
116
  echo "unknown-session"
116
117
  return
117
118
  fi
118
119
 
119
- local num=$((16#$hex))
120
- local adj_idx=$((num % ${#ADJECTIVES[@]}))
121
- local noun_idx=$(((num / ${#ADJECTIVES[@]}) % ${#NOUNS[@]}))
120
+ local adj_seed=$((16#${hex:0:4}))
121
+ local noun_seed=$((16#${hex:4:4}))
122
+ local verb_seed=$((16#${hex:8:4}))
123
+ local adj_idx=$((adj_seed % ${#ADJECTIVES[@]}))
124
+ local noun_idx=$((noun_seed % ${#NOUNS[@]}))
125
+ local verb_idx=$((verb_seed % ${#VERBS[@]}))
122
126
 
123
- echo "${ADJECTIVES[$adj_idx]}-${NOUNS[$noun_idx]}"
127
+ echo "${ADJECTIVES[$adj_idx]}-${NOUNS[$noun_idx]}-${VERBS[$verb_idx]}"
124
128
  }
125
129
 
126
130
  SESSION_NAME=$(convert_uuid_to_name "$SESSION_ID")
127
- TIMESTAMP=$(date "+%Y-%m-%d %I:%M %p %Z")
131
+ TIMESTAMP=$(date "+%Y-%m-%d %I:%M:%S %p %Z")
128
132
 
129
133
  # Required footer format
130
134
  REQUIRED_FOOTER="---
131
- $MODEL - $SESSION_NAME - Turn $TURN - ${CONTEXT_PERCENT}% - ekkOS - $TIMESTAMP"
135
+ $MODEL · 🧠 ekkOS_™ · $SESSION_NAME · 📅 $TIMESTAMP"
132
136
 
133
137
  # Check if response has correct footer
134
138
  RESPONSE_CONTENT=$(cat "$RESPONSE_FILE")
135
139
  LAST_LINE=$(echo "$RESPONSE_CONTENT" | tail -1)
136
140
 
137
141
  # Check if footer exists and is correct
138
- if [[ "$LAST_LINE" == *"ekkOS"* ]] && [[ "$LAST_LINE" == *"Turn"* ]]; then
142
+ if [[ "$LAST_LINE" == *"ekkOS"* ]] && [[ "$LAST_LINE" == *"$SESSION_NAME"* ]]; then
139
143
  # Footer exists - validate format
140
- if [[ "$LAST_LINE" == *"Turn $TURN"* ]] && [[ "$LAST_LINE" == *"${CONTEXT_PERCENT}%"* ]] && [[ "$LAST_LINE" == *"$SESSION_NAME"* ]]; then
144
+ if [[ "$LAST_LINE" == *"$SESSION_NAME"* ]] && [[ "$LAST_LINE" == *"📅"* ]]; then
141
145
  # Footer is correct
142
146
  exit 0
143
147
  else
@@ -69,12 +69,12 @@ function Convert-UuidToWords {
69
69
  if (-not $uuid -or $uuid -eq "unknown") { return "unknown-session" }
70
70
 
71
71
  $clean = $uuid -replace "-", ""
72
- if ($clean.Length -lt 6) { return "unknown-session" }
72
+ if ($clean.Length -lt 12) { return "unknown-session" }
73
73
 
74
74
  try {
75
- $a = [Convert]::ToInt32($clean.Substring(0,2), 16) % $adjectives.Length
76
- $n = [Convert]::ToInt32($clean.Substring(2,2), 16) % $nouns.Length
77
- $an = [Convert]::ToInt32($clean.Substring(4,2), 16) % $verbs.Length
75
+ $a = [Convert]::ToInt32($clean.Substring(0,4), 16) % $adjectives.Length
76
+ $n = [Convert]::ToInt32($clean.Substring(4,4), 16) % $nouns.Length
77
+ $an = [Convert]::ToInt32($clean.Substring(8,4), 16) % $verbs.Length
78
78
 
79
79
  return "$($adjectives[$a])-$($nouns[$n])-$($verbs[$an])"
80
80
  } catch {
@@ -69,12 +69,12 @@ function Convert-UuidToWords {
69
69
  if (-not $uuid -or $uuid -eq "unknown") { return "unknown-session" }
70
70
 
71
71
  $clean = $uuid -replace "-", ""
72
- if ($clean.Length -lt 6) { return "unknown-session" }
72
+ if ($clean.Length -lt 12) { return "unknown-session" }
73
73
 
74
74
  try {
75
- $a = [Convert]::ToInt32($clean.Substring(0,2), 16) % $adjectives.Length
76
- $n = [Convert]::ToInt32($clean.Substring(2,2), 16) % $nouns.Length
77
- $an = [Convert]::ToInt32($clean.Substring(4,2), 16) % $verbs.Length
75
+ $a = [Convert]::ToInt32($clean.Substring(0,4), 16) % $adjectives.Length
76
+ $n = [Convert]::ToInt32($clean.Substring(4,4), 16) % $nouns.Length
77
+ $an = [Convert]::ToInt32($clean.Substring(8,4), 16) % $verbs.Length
78
78
 
79
79
  return "$($adjectives[$a])-$($nouns[$n])-$($verbs[$an])"
80
80
  } catch {
@@ -127,12 +127,12 @@ function Convert-UuidToWords {
127
127
  if (-not $uuid -or $uuid -eq "unknown") { return "unknown-session" }
128
128
 
129
129
  $clean = $uuid -replace "-", ""
130
- if ($clean.Length -lt 6) { return "unknown-session" }
130
+ if ($clean.Length -lt 12) { return "unknown-session" }
131
131
 
132
132
  try {
133
- $a = [Convert]::ToInt32($clean.Substring(0,2), 16) % $adjectives.Length
134
- $n = [Convert]::ToInt32($clean.Substring(2,2), 16) % $nouns.Length
135
- $an = [Convert]::ToInt32($clean.Substring(4,2), 16) % $verbs.Length
133
+ $a = [Convert]::ToInt32($clean.Substring(0,4), 16) % $adjectives.Length
134
+ $n = [Convert]::ToInt32($clean.Substring(4,4), 16) % $nouns.Length
135
+ $an = [Convert]::ToInt32($clean.Substring(8,4), 16) % $verbs.Length
136
136
  return "$($adjectives[$a])-$($nouns[$n])-$($verbs[$an])"
137
137
  } catch {
138
138
  return "unknown-session"
@@ -289,7 +289,7 @@ if ($sessionName -ne "unknown-session") {
289
289
  # OUTPUT SYSTEM REMINDER
290
290
  # ═══════════════════════════════════════════════════════════════════════════
291
291
  $esc = [char]27
292
- $header = "${esc}[0;36m${esc}[1m🧠 ekkOS Memory${esc}[0m ${esc}[2m| Turn $turn | $sessionName | $timestamp${esc}[0m"
292
+ $header = "${esc}[0;36m${esc}[1m🧠 ekkOS Memory${esc}[0m ${esc}[2m| $sessionName | $timestamp${esc}[0m"
293
293
 
294
294
  $output = @"
295
295
  $header
@@ -303,6 +303,7 @@ if ($skillReminders.Count -gt 0) {
303
303
  $output += @"
304
304
 
305
305
  <footer-format>End responses with: Claude Code ({Model}) · 🧠 ekkOS_™ · $sessionName · $timestamp</footer-format>
306
+ <footer-note>Do not include a turn counter in the footer.</footer-note>
306
307
  "@
307
308
 
308
309
  Write-Output $output
@@ -405,7 +405,7 @@ turns.slice(0, -1).forEach(t => {
405
405
  echo "═══════════════════════════════════════════════════════════════════════"
406
406
  echo "</system-reminder>"
407
407
 
408
- echo -e "${CYAN}${BOLD}🧠 ekkOS Memory${RESET} ${DIM}| Turn ${TURN_NUMBER} | ${CURRENT_TIME}${RESET}"
408
+ echo -e "${CYAN}${BOLD}🧠 ekkOS Memory${RESET} ${DIM}| ${CURRENT_TIME}${RESET}"
409
409
  exit 0
410
410
  fi
411
411
  fi
@@ -450,7 +450,7 @@ const d = JSON.parse(require('fs').readFileSync('/dev/stdin', 'utf8') || '{}');
450
450
  fi
451
451
 
452
452
  echo ""
453
- echo -e "${CYAN}${BOLD}🧠 ekkOS Memory${RESET} ${DIM}| Turn ${TURN_NUMBER} | ${SESSION_NAME} | ${CURRENT_TIME}${RESET}"
453
+ echo -e "${CYAN}${BOLD}🧠 ekkOS Memory${RESET} ${DIM}| ${SESSION_NAME} | ${CURRENT_TIME}${RESET}"
454
454
 
455
455
  elif [ "$POST_CLEAR_DETECTED" = true ] && [ -n "$AUTH_TOKEN" ]; then
456
456
  # /clear detected - show visible restoration banner
@@ -495,13 +495,13 @@ const d = JSON.parse(require('fs').readFileSync('/dev/stdin', 'utf8') || '{}');
495
495
  echo -e "${DIM}Full history: \"recall\" or \"turns 1-${TURN_NUMBER}\"${RESET}"
496
496
  echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
497
497
  echo ""
498
- echo -e "${CYAN}${BOLD}🧠 ekkOS Memory${RESET} ${DIM}| Turn ${TURN_NUMBER} | ${SESSION_NAME} | ${CURRENT_TIME}${RESET}"
498
+ echo -e "${CYAN}${BOLD}🧠 ekkOS Memory${RESET} ${DIM}| ${SESSION_NAME} | ${CURRENT_TIME}${RESET}"
499
499
 
500
500
  elif [ "$TOKEN_PERCENT" -ge 50 ]; then
501
- echo -e "${CYAN}${BOLD}🧠 ekkOS Memory${RESET} ${DIM}| Turn ${TURN_NUMBER} | ~${IPC_PERCENT:-0}% IPC | ${SESSION_NAME} | ${CURRENT_TIME}${RESET}"
501
+ echo -e "${CYAN}${BOLD}🧠 ekkOS Memory${RESET} ${DIM}| ~${IPC_PERCENT:-0}% IPC | ${SESSION_NAME} | ${CURRENT_TIME}${RESET}"
502
502
 
503
503
  else
504
- echo -e "${CYAN}${BOLD}🧠 ekkOS Memory${RESET} ${DIM}| Turn ${TURN_NUMBER} | ${SESSION_NAME} | ${CURRENT_TIME}${RESET}"
504
+ echo -e "${CYAN}${BOLD}🧠 ekkOS Memory${RESET} ${DIM}| ${SESSION_NAME} | ${CURRENT_TIME}${RESET}"
505
505
  fi
506
506
 
507
507
  # Output skill reminder if detected