@askexenow/exe-os 0.8.88 → 0.8.90

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.
@@ -0,0 +1,83 @@
1
+ # Exe OS — Ghostty defaults
2
+ # Optimized for tmux + Claude Code agent workflows
3
+
4
+ font-size = 11
5
+
6
+ # ─── Theme: warm light background ───────────────────────────
7
+ background = #fff8f0
8
+ foreground = #000000
9
+ cursor-color = #000000
10
+ selection-background = #b4d5fe
11
+
12
+ # ANSI palette (0-15)
13
+ palette = 0=#000000
14
+ palette = 1=#c91b00
15
+ palette = 2=#00c200
16
+ palette = 3=#c7c400
17
+ palette = 4=#0225c7
18
+ palette = 5=#c930c7
19
+ palette = 6=#00c5c7
20
+ palette = 7=#c7c7c7
21
+ palette = 8=#686868
22
+ palette = 9=#ff6e67
23
+ palette = 10=#5ffa68
24
+ palette = 11=#e5daab
25
+ palette = 12=#6871ff
26
+ palette = 13=#ff77ff
27
+ palette = 14=#60fdff
28
+ palette = 15=#ffffff
29
+
30
+ # ─── Ghostty → tmux keybindings ─────────────────────────────
31
+ # Forward Cmd+key combos to tmux as prefix sequences.
32
+ # Tmux prefix is Ctrl+B (\x02).
33
+
34
+ # Splits (native Ghostty splits)
35
+ keybind = super+d=new_split:right
36
+ keybind = super+shift+d=new_split:down
37
+
38
+ # Close pane/tab
39
+ keybind = super+w=close_surface
40
+
41
+ # New tab
42
+ keybind = super+t=text:\x02t
43
+
44
+ # Tab navigation: Cmd+Shift+[ / ]
45
+ keybind = super+shift+[=text:\x02[
46
+ keybind = super+shift+]=text:\x02]
47
+
48
+ # Go to tab by number: Cmd+1..9
49
+ keybind = super+1=text:\x021
50
+ keybind = super+digit_1=text:\x021
51
+ keybind = super+2=text:\x022
52
+ keybind = super+digit_2=text:\x022
53
+ keybind = super+3=text:\x023
54
+ keybind = super+digit_3=text:\x023
55
+ keybind = super+4=text:\x024
56
+ keybind = super+digit_4=text:\x024
57
+ keybind = super+5=text:\x025
58
+ keybind = super+digit_5=text:\x025
59
+ keybind = super+6=text:\x026
60
+ keybind = super+digit_6=text:\x026
61
+ keybind = super+7=text:\x027
62
+ keybind = super+digit_7=text:\x027
63
+ keybind = super+8=text:\x028
64
+ keybind = super+digit_8=text:\x028
65
+ keybind = super+9=text:\x029
66
+
67
+ # Zoom pane (fullscreen): Cmd+Shift+Enter
68
+ keybind = super+shift+enter=text:\x02z
69
+
70
+ # Clear screen: Cmd+K
71
+ keybind = super+k=text:\x02k
72
+
73
+ # Search: Cmd+F
74
+ keybind = super+f=text:\x02f
75
+
76
+ # Resize splits: Cmd+Ctrl+Arrow
77
+ keybind = super+ctrl+arrow_up=text:\x02\x1b[A
78
+ keybind = super+ctrl+arrow_down=text:\x02\x1b[B
79
+ keybind = super+ctrl+arrow_right=text:\x02\x1b[C
80
+ keybind = super+ctrl+arrow_left=text:\x02\x1b[D
81
+
82
+ # Equalize splits: Cmd+Ctrl+=
83
+ keybind = super+ctrl+=text:\x02=
package/dist/bin/cli.js CHANGED
@@ -572,6 +572,7 @@ __export(installer_exports, {
572
572
  registerMcpServer: () => registerMcpServer,
573
573
  resolvePackageRoot: () => resolvePackageRoot,
574
574
  runInstaller: () => runInstaller,
575
+ setupGhostty: () => setupGhostty,
575
576
  setupTmux: () => setupTmux
576
577
  });
577
578
  import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3, readdir } from "fs/promises";
@@ -1085,6 +1086,48 @@ ${sourceLine}
1085
1086
  }
1086
1087
  process.stderr.write("exe-os: tmux config installed\n");
1087
1088
  }
1089
+ function setupGhostty(home) {
1090
+ const homeDir = home ?? os4.homedir();
1091
+ const xdgConfig = path4.join(homeDir, ".config", "ghostty");
1092
+ const macConfig = path4.join(homeDir, "Library", "Application Support", "com.mitchellh.ghostty");
1093
+ const ghosttyInstalled = existsSync4(xdgConfig) || existsSync4(macConfig) || (() => {
1094
+ try {
1095
+ execSync2("which ghostty 2>/dev/null");
1096
+ return true;
1097
+ } catch {
1098
+ return false;
1099
+ }
1100
+ })();
1101
+ if (!ghosttyInstalled) {
1102
+ return;
1103
+ }
1104
+ const pkgRoot = resolvePackageRoot();
1105
+ const assetPath = path4.join(pkgRoot, "dist", "assets", "ghostty.conf");
1106
+ if (!existsSync4(assetPath)) {
1107
+ process.stderr.write("exe-os: ghostty.conf asset not found \u2014 skipping Ghostty setup\n");
1108
+ return;
1109
+ }
1110
+ const configDir = xdgConfig;
1111
+ const configPath = path4.join(configDir, "config");
1112
+ const backupPath = path4.join(configDir, "config.backup");
1113
+ mkdirSync2(configDir, { recursive: true });
1114
+ if (existsSync4(configPath)) {
1115
+ const existing = readFileSync3(configPath, "utf8");
1116
+ if (existing.includes("Exe OS")) {
1117
+ copyFileSync(assetPath, configPath);
1118
+ } else {
1119
+ if (!existsSync4(backupPath)) {
1120
+ copyFileSync(configPath, backupPath);
1121
+ process.stderr.write(`exe-os: backed up existing Ghostty config to ${backupPath}
1122
+ `);
1123
+ }
1124
+ copyFileSync(assetPath, configPath);
1125
+ }
1126
+ } else {
1127
+ copyFileSync(assetPath, configPath);
1128
+ }
1129
+ process.stderr.write("exe-os: Ghostty config installed\n");
1130
+ }
1088
1131
  function summarizeSymlinkResults(results) {
1089
1132
  if (results.length === 0) return "no employees in roster";
1090
1133
  const created = results.filter((r) => r.action === "created").length;
@@ -12320,7 +12363,33 @@ function ask2(rl, prompt) {
12320
12363
  doAsk();
12321
12364
  });
12322
12365
  }
12366
+ function getAvailableMemoryGB() {
12367
+ return os11.freemem() / (1024 * 1024 * 1024);
12368
+ }
12369
+ function getTotalMemoryGB() {
12370
+ return os11.totalmem() / (1024 * 1024 * 1024);
12371
+ }
12372
+ function isLowMemory() {
12373
+ return getAvailableMemoryGB() < 2;
12374
+ }
12323
12375
  async function validateModel(log) {
12376
+ const totalGB = getTotalMemoryGB();
12377
+ const freeGB = getAvailableMemoryGB();
12378
+ if (totalGB <= 8 || isLowMemory()) {
12379
+ log(`System memory: ${totalGB.toFixed(0)}GB total, ${freeGB.toFixed(1)}GB free`);
12380
+ log("Skipping in-memory model validation (low memory \u2014 will validate on first use).");
12381
+ const modelPath = path28.join(MODELS_DIR, LOCAL_FILENAME);
12382
+ if (existsSync23(modelPath)) {
12383
+ const { statSync: statSync2 } = await import("fs");
12384
+ const size = statSync2(modelPath).size;
12385
+ if (size > 300 * 1e6) {
12386
+ log(`Model file verified (${(size / 1e6).toFixed(0)} MB).`);
12387
+ return;
12388
+ }
12389
+ throw new Error(`Model file too small (${(size / 1e6).toFixed(0)} MB) \u2014 may be corrupted. Delete and re-run setup.`);
12390
+ }
12391
+ throw new Error("Model file not found after download.");
12392
+ }
12324
12393
  log("Validating model...");
12325
12394
  const { embedDirect: embedDirect2 } = await Promise.resolve().then(() => (init_embedder(), embedder_exports));
12326
12395
  const result = await embedDirect2("test embedding");
@@ -12512,6 +12581,15 @@ async function runSetupWizard(opts = {}) {
12512
12581
  log("");
12513
12582
  if (!state.completedSteps.includes(3)) {
12514
12583
  if (!skipModel) {
12584
+ const freeGB = getAvailableMemoryGB();
12585
+ const totalGB = getTotalMemoryGB();
12586
+ if (freeGB < 2) {
12587
+ log(`\u26A0 Low memory detected: ${freeGB.toFixed(1)}GB free of ${totalGB.toFixed(0)}GB total`);
12588
+ log(" Close other applications (browser, Slack, etc.) before continuing.");
12589
+ log(" The embedding model needs ~500MB to download and load.");
12590
+ log("");
12591
+ await ask2(rl, "Press Enter when ready, or Ctrl+C to abort and free memory first: ");
12592
+ }
12515
12593
  log("Note: jina-embeddings-v5-text-small is licensed CC-BY-NC-4.0 (non-commercial)");
12516
12594
  log("");
12517
12595
  await downloadModel({
@@ -994,6 +994,7 @@ __export(installer_exports, {
994
994
  registerMcpServer: () => registerMcpServer,
995
995
  resolvePackageRoot: () => resolvePackageRoot,
996
996
  runInstaller: () => runInstaller,
997
+ setupGhostty: () => setupGhostty,
997
998
  setupTmux: () => setupTmux
998
999
  });
999
1000
  import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3, readdir } from "fs/promises";
@@ -1507,6 +1508,48 @@ ${sourceLine}
1507
1508
  }
1508
1509
  process.stderr.write("exe-os: tmux config installed\n");
1509
1510
  }
1511
+ function setupGhostty(home) {
1512
+ const homeDir = home ?? os4.homedir();
1513
+ const xdgConfig = path8.join(homeDir, ".config", "ghostty");
1514
+ const macConfig = path8.join(homeDir, "Library", "Application Support", "com.mitchellh.ghostty");
1515
+ const ghosttyInstalled = existsSync8(xdgConfig) || existsSync8(macConfig) || (() => {
1516
+ try {
1517
+ execSync2("which ghostty 2>/dev/null");
1518
+ return true;
1519
+ } catch {
1520
+ return false;
1521
+ }
1522
+ })();
1523
+ if (!ghosttyInstalled) {
1524
+ return;
1525
+ }
1526
+ const pkgRoot = resolvePackageRoot();
1527
+ const assetPath = path8.join(pkgRoot, "dist", "assets", "ghostty.conf");
1528
+ if (!existsSync8(assetPath)) {
1529
+ process.stderr.write("exe-os: ghostty.conf asset not found \u2014 skipping Ghostty setup\n");
1530
+ return;
1531
+ }
1532
+ const configDir = xdgConfig;
1533
+ const configPath = path8.join(configDir, "config");
1534
+ const backupPath = path8.join(configDir, "config.backup");
1535
+ mkdirSync5(configDir, { recursive: true });
1536
+ if (existsSync8(configPath)) {
1537
+ const existing = readFileSync7(configPath, "utf8");
1538
+ if (existing.includes("Exe OS")) {
1539
+ copyFileSync(assetPath, configPath);
1540
+ } else {
1541
+ if (!existsSync8(backupPath)) {
1542
+ copyFileSync(configPath, backupPath);
1543
+ process.stderr.write(`exe-os: backed up existing Ghostty config to ${backupPath}
1544
+ `);
1545
+ }
1546
+ copyFileSync(assetPath, configPath);
1547
+ }
1548
+ } else {
1549
+ copyFileSync(assetPath, configPath);
1550
+ }
1551
+ process.stderr.write("exe-os: Ghostty config installed\n");
1552
+ }
1510
1553
  function summarizeSymlinkResults(results) {
1511
1554
  if (results.length === 0) return "no employees in roster";
1512
1555
  const created = results.filter((r) => r.action === "created").length;
@@ -914,6 +914,48 @@ ${sourceLine}
914
914
  }
915
915
  process.stderr.write("exe-os: tmux config installed\n");
916
916
  }
917
+ function setupGhostty(home) {
918
+ const homeDir = home ?? os4.homedir();
919
+ const xdgConfig = path4.join(homeDir, ".config", "ghostty");
920
+ const macConfig = path4.join(homeDir, "Library", "Application Support", "com.mitchellh.ghostty");
921
+ const ghosttyInstalled = existsSync4(xdgConfig) || existsSync4(macConfig) || (() => {
922
+ try {
923
+ execSync2("which ghostty 2>/dev/null");
924
+ return true;
925
+ } catch {
926
+ return false;
927
+ }
928
+ })();
929
+ if (!ghosttyInstalled) {
930
+ return;
931
+ }
932
+ const pkgRoot = resolvePackageRoot();
933
+ const assetPath = path4.join(pkgRoot, "dist", "assets", "ghostty.conf");
934
+ if (!existsSync4(assetPath)) {
935
+ process.stderr.write("exe-os: ghostty.conf asset not found \u2014 skipping Ghostty setup\n");
936
+ return;
937
+ }
938
+ const configDir = xdgConfig;
939
+ const configPath = path4.join(configDir, "config");
940
+ const backupPath = path4.join(configDir, "config.backup");
941
+ mkdirSync2(configDir, { recursive: true });
942
+ if (existsSync4(configPath)) {
943
+ const existing = readFileSync3(configPath, "utf8");
944
+ if (existing.includes("Exe OS")) {
945
+ copyFileSync(assetPath, configPath);
946
+ } else {
947
+ if (!existsSync4(backupPath)) {
948
+ copyFileSync(configPath, backupPath);
949
+ process.stderr.write(`exe-os: backed up existing Ghostty config to ${backupPath}
950
+ `);
951
+ }
952
+ copyFileSync(assetPath, configPath);
953
+ }
954
+ } else {
955
+ copyFileSync(assetPath, configPath);
956
+ }
957
+ process.stderr.write("exe-os: Ghostty config installed\n");
958
+ }
917
959
  function summarizeSymlinkResults(results) {
918
960
  if (results.length === 0) return "no employees in roster";
919
961
  const created = results.filter((r) => r.action === "created").length;
@@ -1183,6 +1225,7 @@ if (args.includes("--commands-only")) {
1183
1225
  try {
1184
1226
  await runInstaller();
1185
1227
  setupTmux();
1228
+ setupGhostty();
1186
1229
  try {
1187
1230
  const { normalizeRosterCase: normalizeRosterCase2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
1188
1231
  const changed = await normalizeRosterCase2();
package/dist/bin/setup.js CHANGED
@@ -5753,7 +5753,33 @@ function ask(rl, prompt) {
5753
5753
  doAsk();
5754
5754
  });
5755
5755
  }
5756
+ function getAvailableMemoryGB() {
5757
+ return os4.freemem() / (1024 * 1024 * 1024);
5758
+ }
5759
+ function getTotalMemoryGB() {
5760
+ return os4.totalmem() / (1024 * 1024 * 1024);
5761
+ }
5762
+ function isLowMemory() {
5763
+ return getAvailableMemoryGB() < 2;
5764
+ }
5756
5765
  async function validateModel(log) {
5766
+ const totalGB = getTotalMemoryGB();
5767
+ const freeGB = getAvailableMemoryGB();
5768
+ if (totalGB <= 8 || isLowMemory()) {
5769
+ log(`System memory: ${totalGB.toFixed(0)}GB total, ${freeGB.toFixed(1)}GB free`);
5770
+ log("Skipping in-memory model validation (low memory \u2014 will validate on first use).");
5771
+ const modelPath = path11.join(MODELS_DIR, LOCAL_FILENAME);
5772
+ if (existsSync11(modelPath)) {
5773
+ const { statSync: statSync2 } = await import("fs");
5774
+ const size = statSync2(modelPath).size;
5775
+ if (size > 300 * 1e6) {
5776
+ log(`Model file verified (${(size / 1e6).toFixed(0)} MB).`);
5777
+ return;
5778
+ }
5779
+ throw new Error(`Model file too small (${(size / 1e6).toFixed(0)} MB) \u2014 may be corrupted. Delete and re-run setup.`);
5780
+ }
5781
+ throw new Error("Model file not found after download.");
5782
+ }
5757
5783
  log("Validating model...");
5758
5784
  const { embedDirect: embedDirect2 } = await Promise.resolve().then(() => (init_embedder(), embedder_exports));
5759
5785
  const result = await embedDirect2("test embedding");
@@ -5945,6 +5971,15 @@ async function runSetupWizard(opts = {}) {
5945
5971
  log("");
5946
5972
  if (!state.completedSteps.includes(3)) {
5947
5973
  if (!skipModel2) {
5974
+ const freeGB = getAvailableMemoryGB();
5975
+ const totalGB = getTotalMemoryGB();
5976
+ if (freeGB < 2) {
5977
+ log(`\u26A0 Low memory detected: ${freeGB.toFixed(1)}GB free of ${totalGB.toFixed(0)}GB total`);
5978
+ log(" Close other applications (browser, Slack, etc.) before continuing.");
5979
+ log(" The embedding model needs ~500MB to download and load.");
5980
+ log("");
5981
+ await ask(rl, "Press Enter when ready, or Ctrl+C to abort and free memory first: ");
5982
+ }
5948
5983
  log("Note: jina-embeddings-v5-text-small is licensed CC-BY-NC-4.0 (non-commercial)");
5949
5984
  log("");
5950
5985
  await downloadModel({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askexenow/exe-os",
3
- "version": "0.8.88",
3
+ "version": "0.8.90",
4
4
  "description": "AI employee operating system — persistent memory, task management, and multi-agent coordination for Claude Code.",
5
5
  "license": "CC-BY-NC-4.0",
6
6
  "type": "module",
@@ -61,8 +61,8 @@
61
61
  "test": "vitest run",
62
62
  "test:watch": "vitest",
63
63
  "typecheck": "tsc --noEmit",
64
- "build": "tsup && mkdir -p dist/assets && cp src/assets/tmux.conf dist/assets/ && cp src/bin/exe-start.sh dist/bin/exe-start.sh",
65
- "deploy": "node dist/bin/pre-build-guard.js 2>/dev/null; tsup && mkdir -p dist/assets && cp src/assets/tmux.conf dist/assets/ && cp src/bin/exe-start.sh dist/bin/exe-start.sh && npm install -g . && node dist/bin/install.js --global && echo '[exe-os] Deploy complete. MCP servers will auto-reconnect on next tool call.'",
64
+ "build": "tsup && mkdir -p dist/assets && cp src/assets/tmux.conf dist/assets/ && cp src/assets/ghostty.conf dist/assets/ && cp src/bin/exe-start.sh dist/bin/exe-start.sh",
65
+ "deploy": "node dist/bin/pre-build-guard.js 2>/dev/null; tsup && mkdir -p dist/assets && cp src/assets/tmux.conf dist/assets/ && cp src/assets/ghostty.conf dist/assets/ && cp src/bin/exe-start.sh dist/bin/exe-start.sh && npm install -g . && node dist/bin/install.js --global && echo '[exe-os] Deploy complete. MCP servers will auto-reconnect on next tool call.'",
66
66
  "postinstall": "node dist/bin/install.js --global 2>/dev/null || true",
67
67
  "prepublishOnly": "npm run typecheck && npm run build && node dist/bin/customer-readiness.js",
68
68
  "test:publish": "npx vitest run --maxWorkers=4 --exclude 'tests/tui/**' --exclude 'tests/lib/tmux-routing.test.ts' --exclude 'tests/lib/intercom-routing.test.ts' --exclude 'tests/gateway/**' --exclude 'tests/installer/setup-wizard.test.ts' --exclude 'tests/mcp/ingest-document.test.ts' --exclude 'tests/lib/hybrid-search.test.ts' --exclude 'tests/lib/worker-gate.test.ts'",