@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.
- package/dist/commands/dashboard.js +71 -27
- package/dist/commands/run.js +16 -7
- package/package.json +1 -1
- package/templates/hooks/assistant-response.ps1 +4 -4
- package/templates/hooks/assistant-response.sh +21 -17
- package/templates/hooks/session-start.ps1 +4 -4
- package/templates/hooks/stop.ps1 +4 -4
- package/templates/hooks/user-prompt-submit.ps1 +6 -5
- package/templates/hooks/user-prompt-submit.sh +5 -5
|
@@ -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 =
|
|
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.
|
|
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
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
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
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
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 {
|
package/dist/commands/run.js
CHANGED
|
@@ -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:
|
|
255
|
+
// Claude prints session name in footer:
|
|
256
|
+
// · 🧠 ekkOS_™ · Turn N · groovy-koala-saves · 📅
|
|
257
|
+
// · 🧠 ekkOS_™ · groovy-koala-saves · 📅
|
|
256
258
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
257
|
-
// Strong signal:
|
|
258
|
-
//
|
|
259
|
-
|
|
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:
|
|
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
|
|
2437
|
-
const statusMatch = plain.match(
|
|
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
|
@@ -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
|
|
119
|
+
if ($clean.Length -lt 12) { return "unknown-session" }
|
|
120
120
|
|
|
121
121
|
try {
|
|
122
|
-
$a = [Convert]::ToInt32($clean.Substring(0,
|
|
123
|
-
$n = [Convert]::ToInt32($clean.Substring(
|
|
124
|
-
$an = [Convert]::ToInt32($clean.Substring(4
|
|
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
|
|
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
|
|
120
|
-
local
|
|
121
|
-
local
|
|
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
|
|
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" == *"
|
|
142
|
+
if [[ "$LAST_LINE" == *"ekkOS"* ]] && [[ "$LAST_LINE" == *"$SESSION_NAME"* ]]; then
|
|
139
143
|
# Footer exists - validate format
|
|
140
|
-
if [[ "$LAST_LINE" == *"
|
|
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
|
|
72
|
+
if ($clean.Length -lt 12) { return "unknown-session" }
|
|
73
73
|
|
|
74
74
|
try {
|
|
75
|
-
$a = [Convert]::ToInt32($clean.Substring(0,
|
|
76
|
-
$n = [Convert]::ToInt32($clean.Substring(
|
|
77
|
-
$an = [Convert]::ToInt32($clean.Substring(4
|
|
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 {
|
package/templates/hooks/stop.ps1
CHANGED
|
@@ -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
|
|
72
|
+
if ($clean.Length -lt 12) { return "unknown-session" }
|
|
73
73
|
|
|
74
74
|
try {
|
|
75
|
-
$a = [Convert]::ToInt32($clean.Substring(0,
|
|
76
|
-
$n = [Convert]::ToInt32($clean.Substring(
|
|
77
|
-
$an = [Convert]::ToInt32($clean.Substring(4
|
|
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
|
|
130
|
+
if ($clean.Length -lt 12) { return "unknown-session" }
|
|
131
131
|
|
|
132
132
|
try {
|
|
133
|
-
$a = [Convert]::ToInt32($clean.Substring(0,
|
|
134
|
-
$n = [Convert]::ToInt32($clean.Substring(
|
|
135
|
-
$an = [Convert]::ToInt32($clean.Substring(4
|
|
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|
|
|
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}|
|
|
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}|
|
|
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}|
|
|
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}|
|
|
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}|
|
|
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
|