@ekkos/cli 1.0.4 → 1.0.6

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.
@@ -808,6 +808,61 @@ function cleanupInstanceFile(instanceId) {
808
808
  // Ignore cleanup errors
809
809
  }
810
810
  }
811
+ /**
812
+ * Launch ekkos run + dashboard using Windows Terminal split panes (Windows equivalent of tmux)
813
+ */
814
+ function launchWithDashboardWindows(options) {
815
+ const launchTime = Date.now();
816
+ // Build the ekkos run command WITHOUT --dashboard (prevent recursion)
817
+ const runArgs = ['run'];
818
+ if (options.session)
819
+ runArgs.push('-s', options.session);
820
+ if (options.bypass)
821
+ runArgs.push('-b');
822
+ if (options.verbose)
823
+ runArgs.push('-v');
824
+ if (options.doctor)
825
+ runArgs.push('-d');
826
+ if (options.research)
827
+ runArgs.push('-r');
828
+ if (options.noInject)
829
+ runArgs.push('--skip-inject');
830
+ if (options.noDna)
831
+ runArgs.push('--skip-dna');
832
+ if (options.noProxy)
833
+ runArgs.push('--skip-proxy');
834
+ runArgs.push('--kickstart');
835
+ const ekkosCmd = process.argv[1];
836
+ const cwd = process.cwd();
837
+ // Write dashboard launch marker
838
+ const markerPath = path.join(state_1.EKKOS_DIR, '.dashboard-launch-ts');
839
+ try {
840
+ fs.writeFileSync(markerPath, `${launchTime}\n${cwd}`);
841
+ }
842
+ catch { }
843
+ const runCommand = `node "${ekkosCmd}" ${runArgs.join(' ')}`;
844
+ const dashCommand = `node "${ekkosCmd}" dashboard --wait-for-new --refresh 2000`;
845
+ // Windows Terminal split pane command:
846
+ // wt --window 0 split-pane -V --size 0.4 --title "ekkOS Dashboard" powershell -NoExit -Command "..."
847
+ // If no WT window exists yet, open a new one with two panes
848
+ const wtCmd = [
849
+ 'wt',
850
+ `new-tab --title "ekkOS" powershell -NoExit -Command "${runCommand}"`,
851
+ `; split-pane -V --size 0.4 --title "ekkOS Dashboard" powershell -NoExit -Command "${dashCommand}"`
852
+ ].join(' ');
853
+ try {
854
+ (0, child_process_1.execSync)(wtCmd, { stdio: 'inherit', shell: true });
855
+ console.log(chalk_1.default.cyan('\n Dashboard launched in right pane (40%)'));
856
+ console.log(chalk_1.default.gray(' Switch panes: Alt+Left / Alt+Right in Windows Terminal'));
857
+ // Exit current process — the new WT window takes over
858
+ process.exit(0);
859
+ }
860
+ catch (err) {
861
+ console.log(chalk_1.default.red(`Windows Terminal error: ${err.message}`));
862
+ console.log(chalk_1.default.gray('Tip: Install Windows Terminal from the Microsoft Store for split-pane support.'));
863
+ console.log(chalk_1.default.gray('Falling back to normal mode. Run "ekkos dashboard --latest" in another terminal.'));
864
+ }
865
+ }
811
866
  /**
812
867
  * Launch ekkos run + dashboard in isolated tmux panes (60/40 split)
813
868
  */
@@ -911,25 +966,37 @@ async function run(options) {
911
966
  console.log(chalk_1.default.yellow(' ⏭️ API proxy disabled (--no-proxy)'));
912
967
  }
913
968
  // ══════════════════════════════════════════════════════════════════════════
914
- // DASHBOARD MODE: Launch via tmux with isolated dashboard pane (60/40)
969
+ // DASHBOARD MODE: tmux (Mac/Linux) or Windows Terminal (Windows)
915
970
  // ══════════════════════════════════════════════════════════════════════════
916
971
  if (options.dashboard) {
917
- try {
918
- if (isWindows)
919
- throw new Error('tmux not supported on Windows');
920
- const tmuxPath = (0, child_process_1.execSync)('which tmux', { encoding: 'utf-8' }).trim();
921
- if (tmuxPath) {
922
- launchWithDashboard(options);
972
+ if (isWindows) {
973
+ // Windows: use Windows Terminal split panes
974
+ try {
975
+ (0, child_process_1.execSync)('where wt', { stdio: 'pipe' });
976
+ launchWithDashboardWindows(options);
923
977
  return;
924
978
  }
979
+ catch {
980
+ console.log(chalk_1.default.yellow(' Windows Terminal not found.'));
981
+ console.log(chalk_1.default.gray(' Install from Microsoft Store: https://aka.ms/terminal'));
982
+ console.log(chalk_1.default.gray(' Alternative: run "ekkos dashboard --latest" in a separate terminal'));
983
+ console.log(chalk_1.default.gray(' Continuing without dashboard...\n'));
984
+ }
925
985
  }
926
- catch {
927
- const installHint = isWindows
928
- ? 'Dashboard mode requires tmux (not supported on Windows)'
929
- : 'tmux not found. Install: brew install tmux';
930
- console.log(chalk_1.default.yellow(` ${installHint}`));
931
- console.log(chalk_1.default.gray(' Alternative: run "ekkos dashboard --latest" in a separate terminal'));
932
- console.log(chalk_1.default.gray(' Continuing without dashboard...\n'));
986
+ else {
987
+ // Mac/Linux: use tmux
988
+ try {
989
+ const tmuxPath = (0, child_process_1.execSync)('which tmux', { encoding: 'utf-8' }).trim();
990
+ if (tmuxPath) {
991
+ launchWithDashboard(options);
992
+ return;
993
+ }
994
+ }
995
+ catch {
996
+ console.log(chalk_1.default.yellow(' tmux not found. Install: brew install tmux'));
997
+ console.log(chalk_1.default.gray(' Alternative: run "ekkos dashboard --latest" in a separate terminal'));
998
+ console.log(chalk_1.default.gray(' Continuing without dashboard...\n'));
999
+ }
933
1000
  }
934
1001
  }
935
1002
  // Generate instance ID for this run
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ekkos/cli",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "Setup ekkOS memory for AI coding assistants (Claude Code, Cursor, Windsurf)",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -110,6 +110,37 @@ if ($queryLower -match '(sql|query|supabase|prisma|database|table|column|select
110
110
  $skillReminders += "SCHEMA REQUIRED: Call ekkOS_GetSchema for correct field names"
111
111
  }
112
112
 
113
+ # ═══════════════════════════════════════════════════════════════════════════
114
+ # SESSION NAME - Resolve early so it's available for all downstream use
115
+ # ═══════════════════════════════════════════════════════════════════════════
116
+ function Convert-UuidToWords {
117
+ param([string]$uuid)
118
+
119
+ if (-not $script:SessionWords) { Load-SessionWords }
120
+ if (-not $script:SessionWords) { return "unknown-session" }
121
+
122
+ $adjectives = $script:SessionWords.adjectives
123
+ $nouns = $script:SessionWords.nouns
124
+ $verbs = $script:SessionWords.verbs
125
+
126
+ if (-not $adjectives -or -not $nouns -or -not $verbs) { return "unknown-session" }
127
+ if (-not $uuid -or $uuid -eq "unknown") { return "unknown-session" }
128
+
129
+ $clean = $uuid -replace "-", ""
130
+ if ($clean.Length -lt 6) { return "unknown-session" }
131
+
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
136
+ return "$($adjectives[$a])-$($nouns[$n])-$($verbs[$an])"
137
+ } catch {
138
+ return "unknown-session"
139
+ }
140
+ }
141
+
142
+ $sessionName = Convert-UuidToWords $rawSessionId
143
+
113
144
  # ═══════════════════════════════════════════════════════════════════════════
114
145
  # TURN TRACKING & STATE MANAGEMENT
115
146
  # ═══════════════════════════════════════════════════════════════════════════
@@ -194,47 +225,6 @@ if (Test-Path $configFile) {
194
225
  } catch {}
195
226
  }
196
227
 
197
- # ═══════════════════════════════════════════════════════════════════════════
198
- # SESSION NAME (UUID to words) - Uses external session-words.json
199
- # ═══════════════════════════════════════════════════════════════════════════
200
- function Convert-UuidToWords {
201
- param([string]$uuid)
202
-
203
- # Load session words if not already loaded
204
- if (-not $script:SessionWords) {
205
- Load-SessionWords
206
- }
207
-
208
- # Handle missing session words gracefully
209
- if (-not $script:SessionWords) {
210
- return "unknown-session"
211
- }
212
-
213
- $adjectives = $script:SessionWords.adjectives
214
- $nouns = $script:SessionWords.nouns
215
- $verbs = $script:SessionWords.verbs
216
-
217
- if (-not $adjectives -or -not $nouns -or -not $verbs) {
218
- return "unknown-session"
219
- }
220
-
221
- if (-not $uuid -or $uuid -eq "unknown") { return "unknown-session" }
222
-
223
- $clean = $uuid -replace "-", ""
224
- if ($clean.Length -lt 6) { return "unknown-session" }
225
-
226
- try {
227
- $a = [Convert]::ToInt32($clean.Substring(0,2), 16) % $adjectives.Length
228
- $n = [Convert]::ToInt32($clean.Substring(2,2), 16) % $nouns.Length
229
- $an = [Convert]::ToInt32($clean.Substring(4,2), 16) % $verbs.Length
230
-
231
- return "$($adjectives[$a])-$($nouns[$n])-$($verbs[$an])"
232
- } catch {
233
- return "unknown-session"
234
- }
235
- }
236
-
237
- $sessionName = Convert-UuidToWords $rawSessionId
238
228
  $timestamp = (Get-Date).ToString("yyyy-MM-dd hh:mm:ss tt") + " EST"
239
229
 
240
230
  # ═══════════════════════════════════════════════════════════════════════════