@ekkos/cli 1.0.21 → 1.0.23
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 +39 -15
- 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}`));
|
|
@@ -446,7 +464,8 @@ async function waitForNewSession() {
|
|
|
446
464
|
const jsonlPath = allNewJsonl[0].path;
|
|
447
465
|
// Derive name from filename (e.g. abc123.jsonl → use candidateName if set, else basename)
|
|
448
466
|
const baseName = path.basename(jsonlPath, '.jsonl');
|
|
449
|
-
const
|
|
467
|
+
const derivedName = /^[0-9a-f]{8}-/.test(baseName) ? (0, state_js_1.uuidToWords)(baseName) : baseName;
|
|
468
|
+
const name = candidateName || derivedName;
|
|
450
469
|
console.log(chalk_1.default.green(` Found session via scan: ${name}`));
|
|
451
470
|
return { sessionName: name, jsonlPath };
|
|
452
471
|
}
|
|
@@ -567,7 +586,7 @@ async function launchDashboard(initialSessionName, jsonlPath, refreshMs) {
|
|
|
567
586
|
function calcLayout() {
|
|
568
587
|
const H = screen.height;
|
|
569
588
|
const remaining = Math.max(6, H - FIXED_H);
|
|
570
|
-
const chartH = Math.max(8, Math.floor(remaining * 0.
|
|
589
|
+
const chartH = Math.max(8, Math.floor(remaining * 0.40));
|
|
571
590
|
const tableH = Math.max(4, remaining - chartH);
|
|
572
591
|
return {
|
|
573
592
|
header: { top: 0, height: HEADER_H },
|
|
@@ -579,6 +598,10 @@ async function launchDashboard(initialSessionName, jsonlPath, refreshMs) {
|
|
|
579
598
|
};
|
|
580
599
|
}
|
|
581
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
|
|
582
605
|
// Header: session stats (3 lines)
|
|
583
606
|
const headerBox = blessed.box({
|
|
584
607
|
top: layout.header.top, left: 0, width: W, height: layout.header.height,
|
|
@@ -608,7 +631,7 @@ async function launchDashboard(initialSessionName, jsonlPath, refreshMs) {
|
|
|
608
631
|
tags: false,
|
|
609
632
|
style: { fg: 'red', bold: true }, // ansi redBright = official Clawd orange
|
|
610
633
|
});
|
|
611
|
-
// Token chart (fills
|
|
634
|
+
// Token chart (fills 40% of remaining)
|
|
612
635
|
const tokenChart = contrib.line({
|
|
613
636
|
top: layout.chart.top, left: 0, width: W, height: layout.chart.height,
|
|
614
637
|
label: ' Tokens/Turn (K) ',
|
|
@@ -705,6 +728,8 @@ async function launchDashboard(initialSessionName, jsonlPath, refreshMs) {
|
|
|
705
728
|
catch { }
|
|
706
729
|
}
|
|
707
730
|
}
|
|
731
|
+
// Apply once at startup so chart/table geometry is correct even before any resize event.
|
|
732
|
+
applyLayout();
|
|
708
733
|
// Track geometry so we can re-anchor widgets even if tmux resize events are flaky
|
|
709
734
|
let lastLayoutW = screen.width || 0;
|
|
710
735
|
let lastLayoutH = screen.height || 0;
|
|
@@ -771,10 +796,6 @@ async function launchDashboard(initialSessionName, jsonlPath, refreshMs) {
|
|
|
771
796
|
}
|
|
772
797
|
scheduleWave();
|
|
773
798
|
// ── Update function ──
|
|
774
|
-
let lastFileSize = 0;
|
|
775
|
-
let lastData = null;
|
|
776
|
-
let lastChartSeries = null;
|
|
777
|
-
let lastScrollPerc = 0; // Preserve scroll position across updates
|
|
778
799
|
const debugLog = path.join(os.homedir(), '.ekkos', 'dashboard.log');
|
|
779
800
|
function dlog(msg) {
|
|
780
801
|
try {
|
|
@@ -983,6 +1004,7 @@ async function launchDashboard(initialSessionName, jsonlPath, refreshMs) {
|
|
|
983
1004
|
const savingsStr = totalSavings > 0
|
|
984
1005
|
? ` {green-fg}saved $${totalSavings.toFixed(2)}{/green-fg}`
|
|
985
1006
|
: '';
|
|
1007
|
+
footerBox.setLabel(` ${sessionName} `);
|
|
986
1008
|
footerBox.setContent(` {green-fg}$${data.totalCost.toFixed(2)}{/green-fg}` +
|
|
987
1009
|
` ${totalTokensM}M` +
|
|
988
1010
|
` ${routingStr}` +
|
|
@@ -1183,13 +1205,15 @@ async function launchDashboard(initialSessionName, jsonlPath, refreshMs) {
|
|
|
1183
1205
|
});
|
|
1184
1206
|
screen.append(help);
|
|
1185
1207
|
screen.render();
|
|
1186
|
-
//
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
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);
|
|
1216
|
+
});
|
|
1193
1217
|
});
|
|
1194
1218
|
// Clear terminal buffer — prevents garbage text from previous commands
|
|
1195
1219
|
screen.program.clear();
|
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
|