@askexenow/exe-os 0.8.60 → 0.8.61

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,133 @@
1
+ #!/usr/bin/env bash
2
+ # exe-start — unified session launcher for exe-os Mode 1
3
+ #
4
+ # Invoked by thin wrappers (exe1, yoshi1, etc.) that pass their $0 as first arg.
5
+ #
6
+ # Usage (via wrapper):
7
+ # exe1 → creates tmux session + boots COO
8
+ # exe1 go → attaches to existing exe1 session
9
+ # yoshi1 go → attaches to yoshi-exe1 session
10
+
11
+ set -euo pipefail
12
+
13
+ ROSTER_PATH="${HOME}/.exe-os/exe-employees.json"
14
+
15
+ # --- Parse wrapper name ---
16
+ # Wrappers call: exec ~/.exe-os/bin/exe-start "$0" "$@"
17
+ # So $1 = wrapper path, remaining = user args
18
+ INVOKED_AS="$(basename "${1:-exe-start}")"
19
+ shift # Remove $0 passthrough; remaining args are user's
20
+
21
+ # Extract name + number: exe1 → exe,1 / yoshi3 → yoshi,3
22
+ NAME="$(echo "$INVOKED_AS" | sed 's/[0-9]*$//')"
23
+ NUM="$(echo "$INVOKED_AS" | grep -oE '[0-9]+$' || true)"
24
+
25
+ if [ -z "$NAME" ] || [ -z "$NUM" ]; then
26
+ echo "Could not parse name and number from: $INVOKED_AS"
27
+ echo " Expected format: exe1, yoshi3, etc."
28
+ exit 1
29
+ fi
30
+
31
+ # --- Load COO name from roster ---
32
+ if [ ! -f "$ROSTER_PATH" ]; then
33
+ echo "Employee roster not found at $ROSTER_PATH"
34
+ echo " Run 'exe-os setup' first."
35
+ exit 1
36
+ fi
37
+
38
+ COO_NAME="$(node -e "
39
+ const r = JSON.parse(require('fs').readFileSync('${ROSTER_PATH}','utf8'));
40
+ const coo = r.find(e => e.role === 'COO');
41
+ console.log(coo ? coo.name : '');
42
+ " 2>/dev/null)"
43
+
44
+ if [ -z "$COO_NAME" ]; then
45
+ echo "No COO found in roster. Run 'exe-os setup' first."
46
+ exit 1
47
+ fi
48
+
49
+ # --- Mode B: Attach ("go") ---
50
+ if [ "${1:-}" = "go" ]; then
51
+ if [ "$NAME" = "$COO_NAME" ]; then
52
+ SESSION="${COO_NAME}${NUM}"
53
+ else
54
+ SESSION="${NAME}-${COO_NAME}${NUM}"
55
+ fi
56
+
57
+ if tmux has-session -t "$SESSION" 2>/dev/null; then
58
+ exec tmux attach -t "$SESSION"
59
+ else
60
+ if [ "$NAME" = "$COO_NAME" ]; then
61
+ echo " ${SESSION} is not running. Use ${COO_NAME}${NUM} to start a new session."
62
+ else
63
+ echo " ${SESSION} is not running. Dispatch work to ${NAME} via /exe inside ${COO_NAME}${NUM}."
64
+ fi
65
+ exit 1
66
+ fi
67
+ fi
68
+
69
+ # --- Mode A: Create session (COO only) ---
70
+ if [ "$NAME" != "$COO_NAME" ]; then
71
+ echo " ${INVOKED_AS} is attach-only. Use ${INVOKED_AS} go to connect."
72
+ echo " Only ${COO_NAME}${NUM} can start new sessions."
73
+ exit 1
74
+ fi
75
+
76
+ # Project folder guard — block $HOME, /, /tmp and /tmp subdirectories
77
+ case "$PWD" in
78
+ "$HOME")
79
+ echo " ${COO_NAME}${NUM} must be run from a project folder, not your home directory."
80
+ echo ""
81
+ echo " Create or cd into a project folder first:"
82
+ echo " mkdir ~/my-project && cd ~/my-project"
83
+ echo " ${COO_NAME}${NUM}"
84
+ exit 1
85
+ ;;
86
+ "/")
87
+ echo " ${COO_NAME}${NUM} must be run from a project folder, not /."
88
+ echo ""
89
+ echo " Create or cd into a project folder first:"
90
+ echo " mkdir ~/my-project && cd ~/my-project"
91
+ echo " ${COO_NAME}${NUM}"
92
+ exit 1
93
+ ;;
94
+ /tmp|/tmp/*)
95
+ echo " ${COO_NAME}${NUM} must be run from a project folder, not a temp directory."
96
+ echo ""
97
+ echo " Create or cd into a project folder first:"
98
+ echo " mkdir ~/my-project && cd ~/my-project"
99
+ echo " ${COO_NAME}${NUM}"
100
+ exit 1
101
+ ;;
102
+ esac
103
+
104
+ SESSION="${COO_NAME}${NUM}"
105
+
106
+ # Session collision guard
107
+ if tmux has-session -t "$SESSION" 2>/dev/null; then
108
+ # Find next free N
109
+ NEXT_N=$((NUM + 1))
110
+ while tmux has-session -t "${COO_NAME}${NEXT_N}" 2>/dev/null; do
111
+ NEXT_N=$((NEXT_N + 1))
112
+ done
113
+
114
+ # Try to get the working directory of the existing session
115
+ EXISTING_DIR="$(tmux display-message -t "$SESSION" -p '#{pane_current_path}' 2>/dev/null || true)"
116
+ DIR_HINT=""
117
+ if [ -n "$EXISTING_DIR" ]; then
118
+ DIR_HINT=" ($(echo "$EXISTING_DIR" | sed "s|$HOME|~|"))"
119
+ fi
120
+
121
+ echo " ${SESSION} is already running${DIR_HINT}."
122
+ echo ""
123
+ echo " To reconnect: ${SESSION} go"
124
+ echo " To start a new project here: ${COO_NAME}${NEXT_N}"
125
+ exit 1
126
+ fi
127
+
128
+ # Create session and launch COO
129
+ tmux new-session -d -s "$SESSION" -c "$PWD"
130
+ tmux send-keys -t "$SESSION" "$COO_NAME" Enter
131
+
132
+ # Attach user to session
133
+ exec tmux attach -t "$SESSION"
@@ -2,9 +2,10 @@
2
2
 
3
3
  // src/adapters/claude/installer.ts
4
4
  import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3, readdir } from "fs/promises";
5
- import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
5
+ import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync, copyFileSync, mkdirSync as mkdirSync2 } from "fs";
6
6
  import path4 from "path";
7
7
  import os3 from "os";
8
+ import { execSync as execSync2 } from "child_process";
8
9
  import { fileURLToPath } from "url";
9
10
 
10
11
  // src/lib/agent-symlinks.ts
@@ -658,6 +659,44 @@ async function runInstaller(homeDir) {
658
659
  exe-os installed successfully.
659
660
  `);
660
661
  }
662
+ function setupTmux(home) {
663
+ const homeDir = home ?? os3.homedir();
664
+ const exeDir = path4.join(homeDir, ".exe-os");
665
+ const exeTmuxConf = path4.join(exeDir, "tmux.conf");
666
+ const userTmuxConf = path4.join(homeDir, ".tmux.conf");
667
+ const backupPath = path4.join(homeDir, ".tmux.conf.backup");
668
+ const sourceLine = "source-file ~/.exe-os/tmux.conf";
669
+ const pkgRoot = resolvePackageRoot();
670
+ const assetPath = path4.join(pkgRoot, "dist", "assets", "tmux.conf");
671
+ if (!existsSync4(assetPath)) {
672
+ process.stderr.write(`exe-os: tmux.conf asset not found at ${assetPath} \u2014 skipping tmux setup
673
+ `);
674
+ return;
675
+ }
676
+ mkdirSync2(exeDir, { recursive: true });
677
+ copyFileSync(assetPath, exeTmuxConf);
678
+ if (existsSync4(userTmuxConf)) {
679
+ const existing = readFileSync3(userTmuxConf, "utf8");
680
+ if (!existing.includes(sourceLine)) {
681
+ if (!existsSync4(backupPath)) {
682
+ copyFileSync(userTmuxConf, backupPath);
683
+ process.stderr.write(`exe-os: backed up existing tmux config to ${backupPath}
684
+ `);
685
+ }
686
+ writeFileSync(userTmuxConf, `${sourceLine}
687
+ ${existing}`);
688
+ }
689
+ } else {
690
+ writeFileSync(userTmuxConf, `# Exe OS tmux defaults \u2014 remove this line to use your own config
691
+ ${sourceLine}
692
+ `);
693
+ }
694
+ try {
695
+ execSync2(`tmux source-file ${exeTmuxConf} 2>/dev/null`);
696
+ } catch {
697
+ }
698
+ process.stderr.write("exe-os: tmux config installed\n");
699
+ }
661
700
  function summarizeSymlinkResults(results) {
662
701
  if (results.length === 0) return "no employees in roster";
663
702
  const created = results.filter((r) => r.action === "created").length;
@@ -672,17 +711,124 @@ function summarizeSymlinkResults(results) {
672
711
  }
673
712
 
674
713
  // src/bin/install.ts
675
- import { existsSync as existsSync5, readFileSync as readFileSync4, unlinkSync, readdirSync, openSync, closeSync } from "fs";
676
- import { spawn, execSync as execSync2 } from "child_process";
714
+ import { existsSync as existsSync6, readFileSync as readFileSync5, unlinkSync as unlinkSync2, readdirSync as readdirSync2, openSync, closeSync } from "fs";
715
+ import { spawn, execSync as execSync3 } from "child_process";
716
+ import path6 from "path";
717
+ import { homedir as homedir2 } from "os";
718
+
719
+ // src/lib/session-wrappers.ts
720
+ import {
721
+ existsSync as existsSync5,
722
+ readFileSync as readFileSync4,
723
+ writeFileSync as writeFileSync2,
724
+ mkdirSync as mkdirSync3,
725
+ chmodSync,
726
+ readdirSync,
727
+ unlinkSync
728
+ } from "fs";
677
729
  import path5 from "path";
678
730
  import { homedir } from "os";
679
- var EXE_DIR = path5.join(homedir(), ".exe-os");
731
+ var MAX_N = 9;
732
+ function generateSessionWrappers(packageRoot, homeDir) {
733
+ const home = homeDir ?? homedir();
734
+ const binDir = path5.join(home, ".exe-os", "bin");
735
+ const rosterPath = path5.join(home, ".exe-os", "exe-employees.json");
736
+ mkdirSync3(binDir, { recursive: true });
737
+ const exeStartDst = path5.join(binDir, "exe-start");
738
+ const candidates = [
739
+ path5.join(packageRoot, "dist", "bin", "exe-start.sh"),
740
+ path5.join(packageRoot, "src", "bin", "exe-start.sh")
741
+ ];
742
+ for (const src of candidates) {
743
+ if (existsSync5(src)) {
744
+ writeFileSync2(exeStartDst, readFileSync4(src));
745
+ chmodSync(exeStartDst, 493);
746
+ break;
747
+ }
748
+ }
749
+ let employees = [];
750
+ try {
751
+ employees = JSON.parse(readFileSync4(rosterPath, "utf8"));
752
+ } catch {
753
+ return { created: 0, pathConfigured: false };
754
+ }
755
+ if (employees.length === 0) {
756
+ return { created: 0, pathConfigured: false };
757
+ }
758
+ try {
759
+ for (const f of readdirSync(binDir)) {
760
+ if (f === "exe-start") continue;
761
+ const fPath = path5.join(binDir, f);
762
+ try {
763
+ const content = readFileSync4(fPath, "utf8");
764
+ if (content.includes("exe-start")) {
765
+ unlinkSync(fPath);
766
+ }
767
+ } catch {
768
+ }
769
+ }
770
+ } catch {
771
+ }
772
+ let created = 0;
773
+ const wrapperContent = `#!/bin/bash
774
+ exec "${exeStartDst}" "$0" "$@"
775
+ `;
776
+ for (const emp of employees) {
777
+ for (let n = 1; n <= MAX_N; n++) {
778
+ const wrapperPath = path5.join(binDir, `${emp.name}${n}`);
779
+ writeFileSync2(wrapperPath, wrapperContent);
780
+ chmodSync(wrapperPath, 493);
781
+ created++;
782
+ }
783
+ }
784
+ const pathConfigured = ensurePath(home, binDir);
785
+ return { created, pathConfigured };
786
+ }
787
+ function ensurePath(home, binDir) {
788
+ if (process.env.PATH?.split(":").includes(binDir)) {
789
+ return false;
790
+ }
791
+ const exportLine = `
792
+ # exe-os session commands
793
+ export PATH="${binDir}:$PATH"
794
+ `;
795
+ const shell = process.env.SHELL ?? "/bin/bash";
796
+ const profilePaths = [];
797
+ if (shell.includes("zsh")) {
798
+ profilePaths.push(path5.join(home, ".zshrc"));
799
+ } else if (shell.includes("bash")) {
800
+ profilePaths.push(path5.join(home, ".bashrc"));
801
+ profilePaths.push(path5.join(home, ".bash_profile"));
802
+ } else {
803
+ profilePaths.push(path5.join(home, ".profile"));
804
+ }
805
+ for (const profilePath of profilePaths) {
806
+ try {
807
+ let content = "";
808
+ try {
809
+ content = readFileSync4(profilePath, "utf8");
810
+ } catch {
811
+ }
812
+ if (content.includes(".exe-os/bin")) {
813
+ return false;
814
+ }
815
+ writeFileSync2(profilePath, content + exportLine);
816
+ return true;
817
+ } catch {
818
+ continue;
819
+ }
820
+ }
821
+ return false;
822
+ }
823
+
824
+ // src/bin/install.ts
825
+ var EXE_DIR = path6.join(homedir2(), ".exe-os");
680
826
  function restartDaemon() {
681
- const pidPath = path5.join(EXE_DIR, "exed.pid");
682
- const sockPath = path5.join(EXE_DIR, "exed.sock");
827
+ const pidPath = path6.join(EXE_DIR, "exed.pid");
828
+ const sockPath = path6.join(EXE_DIR, "exed.sock");
683
829
  try {
684
- if (existsSync5(pidPath)) {
685
- const pid = parseInt(readFileSync4(pidPath, "utf8").trim(), 10);
830
+ if (existsSync6(pidPath)) {
831
+ const pid = parseInt(readFileSync5(pidPath, "utf8").trim(), 10);
686
832
  if (!isNaN(pid) && pid > 0) {
687
833
  try {
688
834
  process.kill(pid, "SIGKILL");
@@ -690,12 +836,12 @@ function restartDaemon() {
690
836
  }
691
837
  }
692
838
  try {
693
- unlinkSync(pidPath);
839
+ unlinkSync2(pidPath);
694
840
  } catch {
695
841
  }
696
842
  }
697
843
  try {
698
- const pids = execSync2("pgrep -f 'exe-daemon.js' 2>/dev/null || true", { encoding: "utf8" }).trim().split("\n").filter(Boolean).map(Number).filter((n) => !isNaN(n) && n !== process.pid);
844
+ const pids = execSync3("pgrep -f 'exe-daemon.js' 2>/dev/null || true", { encoding: "utf8" }).trim().split("\n").filter(Boolean).map(Number).filter((n) => !isNaN(n) && n !== process.pid);
699
845
  for (const pid of pids) {
700
846
  try {
701
847
  process.kill(pid, "SIGKILL");
@@ -709,7 +855,7 @@ function restartDaemon() {
709
855
  } catch {
710
856
  }
711
857
  try {
712
- const pids = execSync2("pgrep -f 'backfill-vectors.js' 2>/dev/null || true", { encoding: "utf8" }).trim().split("\n").filter(Boolean).map(Number).filter((n) => !isNaN(n) && n !== process.pid);
858
+ const pids = execSync3("pgrep -f 'backfill-vectors.js' 2>/dev/null || true", { encoding: "utf8" }).trim().split("\n").filter(Boolean).map(Number).filter((n) => !isNaN(n) && n !== process.pid);
713
859
  for (const pid of pids) {
714
860
  try {
715
861
  process.kill(pid, "SIGKILL");
@@ -723,11 +869,11 @@ function restartDaemon() {
723
869
  } catch {
724
870
  }
725
871
  try {
726
- const wpDir = path5.join(EXE_DIR, "worker-pids");
727
- if (existsSync5(wpDir)) {
728
- for (const f of readdirSync(wpDir)) {
872
+ const wpDir = path6.join(EXE_DIR, "worker-pids");
873
+ if (existsSync6(wpDir)) {
874
+ for (const f of readdirSync2(wpDir)) {
729
875
  try {
730
- unlinkSync(path5.join(wpDir, f));
876
+ unlinkSync2(path6.join(wpDir, f));
731
877
  } catch {
732
878
  }
733
879
  }
@@ -735,24 +881,24 @@ function restartDaemon() {
735
881
  } catch {
736
882
  }
737
883
  try {
738
- unlinkSync(sockPath);
884
+ unlinkSync2(sockPath);
739
885
  } catch {
740
886
  }
741
887
  try {
742
- execSync2("sleep 0.5");
888
+ execSync3("sleep 0.5");
743
889
  } catch {
744
890
  }
745
891
  } catch {
746
892
  }
747
893
  try {
748
894
  const pkgRoot = resolvePackageRoot();
749
- const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
750
- if (!existsSync5(daemonPath)) {
895
+ const daemonPath = path6.join(pkgRoot, "dist", "lib", "exe-daemon.js");
896
+ if (!existsSync6(daemonPath)) {
751
897
  process.stderr.write(`exe-os: daemon not found at ${daemonPath} \u2014 skipping respawn
752
898
  `);
753
899
  return;
754
900
  }
755
- const logPath = path5.join(EXE_DIR, "exed.log");
901
+ const logPath = path6.join(EXE_DIR, "exed.log");
756
902
  let stderrFd = "ignore";
757
903
  try {
758
904
  stderrFd = openSync(logPath, "a");
@@ -797,6 +943,22 @@ if (args.includes("--commands-only")) {
797
943
  } else if (args.includes("--global")) {
798
944
  try {
799
945
  await runInstaller();
946
+ setupTmux();
947
+ try {
948
+ const pkgRoot = resolvePackageRoot();
949
+ const wrapResult = generateSessionWrappers(pkgRoot);
950
+ if (wrapResult.created > 0) {
951
+ process.stderr.write(`exe-os: ${wrapResult.created} session wrapper(s) generated
952
+ `);
953
+ }
954
+ if (wrapResult.pathConfigured) {
955
+ process.stderr.write(`exe-os: added ~/.exe-os/bin to PATH (restart your shell to activate)
956
+ `);
957
+ }
958
+ } catch (err) {
959
+ process.stderr.write(`exe-os: session wrapper generation failed: ${err instanceof Error ? err.message : String(err)}
960
+ `);
961
+ }
800
962
  restartDaemon();
801
963
  } catch (err) {
802
964
  console.error(
@@ -2852,7 +2852,9 @@ async function main() {
2852
2852
  try {
2853
2853
  const { assertMemoryLimit: assertMemoryLimit2 } = await Promise.resolve().then(() => (init_plan_limits(), plan_limits_exports));
2854
2854
  await assertMemoryLimit2();
2855
- } catch {
2855
+ } catch (err) {
2856
+ process.stderr.write(`[response-ingest-worker] LIMIT: ${err instanceof Error ? err.message : String(err)}
2857
+ `);
2856
2858
  process.exit(0);
2857
2859
  }
2858
2860
  await writeMemory({
@@ -4660,7 +4660,9 @@ async function main() {
4660
4660
  try {
4661
4661
  const { assertMemoryLimit: assertMemoryLimit2 } = await Promise.resolve().then(() => (init_plan_limits(), plan_limits_exports));
4662
4662
  await assertMemoryLimit2();
4663
- } catch {
4663
+ } catch (err) {
4664
+ process.stderr.write(`[summary-worker] LIMIT: ${err instanceof Error ? err.message : String(err)}
4665
+ `);
4664
4666
  process.exit(0);
4665
4667
  }
4666
4668
  await writeMemory({
@@ -0,0 +1,107 @@
1
+ // src/lib/session-wrappers.ts
2
+ import {
3
+ existsSync,
4
+ readFileSync,
5
+ writeFileSync,
6
+ mkdirSync,
7
+ chmodSync,
8
+ readdirSync,
9
+ unlinkSync
10
+ } from "fs";
11
+ import path from "path";
12
+ import { homedir } from "os";
13
+ var MAX_N = 9;
14
+ function generateSessionWrappers(packageRoot, homeDir) {
15
+ const home = homeDir ?? homedir();
16
+ const binDir = path.join(home, ".exe-os", "bin");
17
+ const rosterPath = path.join(home, ".exe-os", "exe-employees.json");
18
+ mkdirSync(binDir, { recursive: true });
19
+ const exeStartDst = path.join(binDir, "exe-start");
20
+ const candidates = [
21
+ path.join(packageRoot, "dist", "bin", "exe-start.sh"),
22
+ path.join(packageRoot, "src", "bin", "exe-start.sh")
23
+ ];
24
+ for (const src of candidates) {
25
+ if (existsSync(src)) {
26
+ writeFileSync(exeStartDst, readFileSync(src));
27
+ chmodSync(exeStartDst, 493);
28
+ break;
29
+ }
30
+ }
31
+ let employees = [];
32
+ try {
33
+ employees = JSON.parse(readFileSync(rosterPath, "utf8"));
34
+ } catch {
35
+ return { created: 0, pathConfigured: false };
36
+ }
37
+ if (employees.length === 0) {
38
+ return { created: 0, pathConfigured: false };
39
+ }
40
+ try {
41
+ for (const f of readdirSync(binDir)) {
42
+ if (f === "exe-start") continue;
43
+ const fPath = path.join(binDir, f);
44
+ try {
45
+ const content = readFileSync(fPath, "utf8");
46
+ if (content.includes("exe-start")) {
47
+ unlinkSync(fPath);
48
+ }
49
+ } catch {
50
+ }
51
+ }
52
+ } catch {
53
+ }
54
+ let created = 0;
55
+ const wrapperContent = `#!/bin/bash
56
+ exec "${exeStartDst}" "$0" "$@"
57
+ `;
58
+ for (const emp of employees) {
59
+ for (let n = 1; n <= MAX_N; n++) {
60
+ const wrapperPath = path.join(binDir, `${emp.name}${n}`);
61
+ writeFileSync(wrapperPath, wrapperContent);
62
+ chmodSync(wrapperPath, 493);
63
+ created++;
64
+ }
65
+ }
66
+ const pathConfigured = ensurePath(home, binDir);
67
+ return { created, pathConfigured };
68
+ }
69
+ function ensurePath(home, binDir) {
70
+ if (process.env.PATH?.split(":").includes(binDir)) {
71
+ return false;
72
+ }
73
+ const exportLine = `
74
+ # exe-os session commands
75
+ export PATH="${binDir}:$PATH"
76
+ `;
77
+ const shell = process.env.SHELL ?? "/bin/bash";
78
+ const profilePaths = [];
79
+ if (shell.includes("zsh")) {
80
+ profilePaths.push(path.join(home, ".zshrc"));
81
+ } else if (shell.includes("bash")) {
82
+ profilePaths.push(path.join(home, ".bashrc"));
83
+ profilePaths.push(path.join(home, ".bash_profile"));
84
+ } else {
85
+ profilePaths.push(path.join(home, ".profile"));
86
+ }
87
+ for (const profilePath of profilePaths) {
88
+ try {
89
+ let content = "";
90
+ try {
91
+ content = readFileSync(profilePath, "utf8");
92
+ } catch {
93
+ }
94
+ if (content.includes(".exe-os/bin")) {
95
+ return false;
96
+ }
97
+ writeFileSync(profilePath, content + exportLine);
98
+ return true;
99
+ } catch {
100
+ continue;
101
+ }
102
+ }
103
+ return false;
104
+ }
105
+ export {
106
+ generateSessionWrappers
107
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askexenow/exe-os",
3
- "version": "0.8.60",
3
+ "version": "0.8.61",
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",
@@ -65,8 +65,8 @@
65
65
  "test": "vitest run",
66
66
  "test:watch": "vitest",
67
67
  "typecheck": "tsc --noEmit",
68
- "build": "tsup",
69
- "deploy": "node dist/bin/pre-build-guard.js 2>/dev/null; tsup && npm install -g . && node dist/bin/install.js --global && echo '[exe-os] Deploy complete. MCP servers will auto-reconnect on next tool call.'",
68
+ "build": "tsup && mkdir -p dist/assets && cp src/assets/tmux.conf dist/assets/ && cp src/bin/exe-start.sh dist/bin/exe-start.sh",
69
+ "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.'",
70
70
  "postinstall": "node dist/bin/install.js --global 2>/dev/null || true",
71
71
  "prepublishOnly": "npm run typecheck && npm run build && node dist/bin/customer-readiness.js && 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'",
72
72
  "benchmark:longmemeval": "npx tsx tests/benchmarks/longmemeval.ts"