@askexenow/exe-os 0.9.85 → 0.9.87

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.
Files changed (86) hide show
  1. package/dist/bin/agentic-ontology-backfill.js +50 -14
  2. package/dist/bin/agentic-reflection-backfill.js +50 -14
  3. package/dist/bin/agentic-semantic-label.js +50 -14
  4. package/dist/bin/backfill-conversations.js +50 -14
  5. package/dist/bin/backfill-responses.js +50 -14
  6. package/dist/bin/backfill-vectors.js +50 -14
  7. package/dist/bin/bulk-sync-postgres.js +50 -14
  8. package/dist/bin/cleanup-stale-review-tasks.js +53 -17
  9. package/dist/bin/cli.js +339 -81
  10. package/dist/bin/exe-agent.js +18 -0
  11. package/dist/bin/exe-assign.js +50 -14
  12. package/dist/bin/exe-boot.js +75 -39
  13. package/dist/bin/exe-call.js +18 -0
  14. package/dist/bin/exe-cloud.js +40 -4
  15. package/dist/bin/exe-dispatch.js +61 -25
  16. package/dist/bin/exe-doctor.js +40 -4
  17. package/dist/bin/exe-export-behaviors.js +50 -14
  18. package/dist/bin/exe-forget.js +50 -14
  19. package/dist/bin/exe-gateway.js +65 -29
  20. package/dist/bin/exe-heartbeat.js +55 -19
  21. package/dist/bin/exe-kill.js +54 -18
  22. package/dist/bin/exe-launch-agent.js +58 -22
  23. package/dist/bin/exe-new-employee.js +33 -2
  24. package/dist/bin/exe-pending-messages.js +53 -17
  25. package/dist/bin/exe-pending-notifications.js +53 -17
  26. package/dist/bin/exe-pending-reviews.js +55 -19
  27. package/dist/bin/exe-rename.js +52 -16
  28. package/dist/bin/exe-review.js +50 -14
  29. package/dist/bin/exe-search.js +58 -22
  30. package/dist/bin/exe-session-cleanup.js +85 -44
  31. package/dist/bin/exe-start-codex.js +57 -21
  32. package/dist/bin/exe-start-opencode.js +55 -19
  33. package/dist/bin/exe-status.js +62 -26
  34. package/dist/bin/exe-team.js +50 -14
  35. package/dist/bin/git-sweep.js +63 -27
  36. package/dist/bin/graph-backfill.js +50 -14
  37. package/dist/bin/graph-export.js +50 -14
  38. package/dist/bin/install.js +9 -0
  39. package/dist/bin/intercom-check.js +67 -31
  40. package/dist/bin/scan-tasks.js +63 -27
  41. package/dist/bin/setup.js +53 -13
  42. package/dist/bin/shard-migrate.js +50 -14
  43. package/dist/bin/stack-update.js +59 -2
  44. package/dist/bin/update.js +1 -1
  45. package/dist/gateway/index.js +65 -29
  46. package/dist/hooks/bug-report-worker.js +65 -29
  47. package/dist/hooks/codex-stop-task-finalizer.js +59 -23
  48. package/dist/hooks/commit-complete.js +64 -28
  49. package/dist/hooks/error-recall.js +62 -26
  50. package/dist/hooks/ingest-worker.js +4 -4
  51. package/dist/hooks/ingest.js +56 -20
  52. package/dist/hooks/instructions-loaded.js +50 -14
  53. package/dist/hooks/notification.js +50 -14
  54. package/dist/hooks/post-compact.js +50 -14
  55. package/dist/hooks/post-tool-combined.js +63 -27
  56. package/dist/hooks/pre-compact.js +61 -25
  57. package/dist/hooks/pre-tool-use.js +58 -22
  58. package/dist/hooks/prompt-submit.js +78 -42
  59. package/dist/hooks/session-end.js +66 -30
  60. package/dist/hooks/session-start.js +68 -32
  61. package/dist/hooks/stop.js +53 -17
  62. package/dist/hooks/subagent-stop.js +50 -14
  63. package/dist/hooks/summary-worker.js +55 -19
  64. package/dist/index.js +61 -25
  65. package/dist/lib/cloud-sync.js +32 -14
  66. package/dist/lib/database.js +22 -4
  67. package/dist/lib/db-daemon-client.js +16 -4
  68. package/dist/lib/db.js +22 -4
  69. package/dist/lib/device-registry.js +22 -4
  70. package/dist/lib/embedder.js +16 -4
  71. package/dist/lib/employee-templates.js +18 -0
  72. package/dist/lib/exe-daemon-client.js +16 -4
  73. package/dist/lib/exe-daemon.js +874 -232
  74. package/dist/lib/hybrid-search.js +58 -22
  75. package/dist/lib/identity-templates.js +6 -2
  76. package/dist/lib/schedules.js +53 -17
  77. package/dist/lib/skill-learning.js +16 -4
  78. package/dist/lib/store.js +50 -14
  79. package/dist/lib/tasks.js +16 -4
  80. package/dist/lib/tmux-routing.js +18 -6
  81. package/dist/mcp/server.js +809 -200
  82. package/dist/mcp/tools/create-task.js +24 -8
  83. package/dist/mcp/tools/update-task.js +18 -6
  84. package/dist/runtime/index.js +61 -25
  85. package/dist/tui/App.js +91 -55
  86. package/package.json +1 -1
package/dist/bin/cli.js CHANGED
@@ -1229,6 +1229,15 @@ async function registerMcpServer(packageRoot, homeDir = os6.homedir()) {
1229
1229
  delete claudeJson.mcpServers[MCP_LEGACY_KEY];
1230
1230
  process.stderr.write("exe-os: migrated MCP server key exe-mem \u2192 exe-os\n");
1231
1231
  }
1232
+ if (claudeJson.projects) {
1233
+ for (const [projectPath, projectConfig] of Object.entries(claudeJson.projects)) {
1234
+ if (projectConfig.mcpServers?.[MCP_LEGACY_KEY]) {
1235
+ delete projectConfig.mcpServers[MCP_LEGACY_KEY];
1236
+ process.stderr.write(`exe-os: removed stale project-level exe-mem from ${projectPath}
1237
+ `);
1238
+ }
1239
+ }
1240
+ }
1232
1241
  const currentOs = claudeJson.mcpServers[MCP_PRIMARY_KEY];
1233
1242
  const osMatches = currentOs && JSON.stringify(currentOs) === JSON.stringify(newEntry);
1234
1243
  if (osMatches) {
@@ -3665,7 +3674,7 @@ __export(exe_daemon_client_exports, {
3665
3674
  });
3666
3675
  import net from "net";
3667
3676
  import os10 from "os";
3668
- import { spawn } from "child_process";
3677
+ import { spawn, execSync as execSync4 } from "child_process";
3669
3678
  import { randomUUID } from "crypto";
3670
3679
  import { existsSync as existsSync12, unlinkSync as unlinkSync3, readFileSync as readFileSync9, openSync, closeSync, statSync as statSync2 } from "fs";
3671
3680
  import path12 from "path";
@@ -3695,6 +3704,14 @@ function handleData(chunk) {
3695
3704
  }
3696
3705
  }
3697
3706
  }
3707
+ function isZombie(pid) {
3708
+ try {
3709
+ const state = execSync4(`ps -p ${pid} -o state=`, { encoding: "utf8", timeout: 2e3 }).trim();
3710
+ return state.startsWith("Z");
3711
+ } catch {
3712
+ return false;
3713
+ }
3714
+ }
3698
3715
  function cleanupStaleFiles() {
3699
3716
  if (existsSync12(PID_PATH)) {
3700
3717
  try {
@@ -3702,7 +3719,11 @@ function cleanupStaleFiles() {
3702
3719
  if (pid > 0) {
3703
3720
  try {
3704
3721
  process.kill(pid, 0);
3705
- return;
3722
+ if (!isZombie(pid)) {
3723
+ return;
3724
+ }
3725
+ process.stderr.write(`[exed-client] PID ${pid} is a zombie \u2014 cleaning up stale files
3726
+ `);
3706
3727
  } catch {
3707
3728
  }
3708
3729
  }
@@ -3730,8 +3751,8 @@ function findPackageRoot() {
3730
3751
  function getAvailableMemoryGB() {
3731
3752
  if (process.platform === "darwin") {
3732
3753
  try {
3733
- const { execSync: execSync18 } = __require("child_process");
3734
- const vmstat = execSync18("vm_stat", { encoding: "utf8" });
3754
+ const { execSync: execSync19 } = __require("child_process");
3755
+ const vmstat = execSync19("vm_stat", { encoding: "utf8" });
3735
3756
  const pageSize = 16384;
3736
3757
  const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
3737
3758
  const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
@@ -5686,6 +5707,12 @@ async function disposeDatabase() {
5686
5707
  clearInterval(_walCheckpointTimer);
5687
5708
  _walCheckpointTimer = null;
5688
5709
  }
5710
+ if (_client) {
5711
+ try {
5712
+ await _client.execute("PRAGMA wal_checkpoint(PASSIVE)");
5713
+ } catch {
5714
+ }
5715
+ }
5689
5716
  if (_daemonClient) {
5690
5717
  _daemonClient.close();
5691
5718
  _daemonClient = null;
@@ -8701,6 +8728,24 @@ var init_platform_procedures = __esm({
8701
8728
  priority: "p0",
8702
8729
  content: "When an agent encounters a suspected Exe OS bug, update breakage, MCP/tool failure, installer issue, memory/orchestration defect, or customer-local patch need, it MUST use create_bug_report. Do this before or alongside any local workaround so the report reaches AskExe support directly via the customer's license. Do NOT ask the founder for permission to file a required bug report. If create_bug_report is deferred/lazy-loaded, load it and call it. If it is unavailable in the live MCP surface, report 'create_bug_report unavailable in this session' and save a local report in exe/output \u2014 never claim the tool does not exist unless the live MCP surface was checked. If upstream delivery fails, call support_test (MCP) and include its result in the local report so AskExe can distinguish customer setup, license provisioning, and server intake issues; only ask the founder to run `exe-os support test` if MCP is disconnected/unavailable. Classify first: upstream_bug = reproducible exe-os/platform defect; customer_customization = identity, behavior, procedure, config, branding, workflow preference that belongs in customer-owned layers; emergency_hotfix = temporary local patch. For upstream bugs/emergency hotfixes include version, repro steps, expected/actual, files changed, workaround, and local diff summary. Avoid permanent platform-code patches unless founder approves; if a hotfix is unavoidable, document it in the bug report and re-check after npm update."
8703
8730
  },
8731
+ {
8732
+ title: "Bug report status check \u2014 surface available fixes on boot",
8733
+ domain: "support",
8734
+ priority: "p1",
8735
+ content: "Once per session (COO boot only, never repeat), call list_my_bug_reports to check if any previously filed bug reports have been fixed by AskExe. If any report has status 'fixed' with a fixed_version, surface it to the founder immediately: '\u{1F527} N bug fix(es) available \u2014 run exe-os update to get version X.Y.Z'. This is a one-time check at boot, not a recurring poll. If no reports exist or none are fixed, skip silently. If the MCP tool is unavailable or the network call fails, skip silently \u2014 this is informational, not blocking."
8736
+ },
8737
+ {
8738
+ title: "Feature request triage \u2014 upstream feature vs local customization",
8739
+ domain: "support",
8740
+ priority: "p0",
8741
+ content: "When an agent or founder identifies a desired capability that exe-os does not yet provide, the COO (or equivalent coordinator) must decide: is this a local customization (identity, behavior, procedure, config, branding, workflow preference that can be configured in customer-owned layers) or an upstream feature request (a platform capability that requires changes to exe-os code, shipped via npm update)? Local customizations: implement immediately using store_behavior, update_identity, company_procedure, or config changes. Upstream features: use create_feature_request to submit to AskExe. Include use case, business impact, and current workaround. Do NOT ask the founder for permission to file a feature request \u2014 file it proactively when the need is clear."
8742
+ },
8743
+ {
8744
+ title: "Feature request status check \u2014 surface shipped features on boot",
8745
+ domain: "support",
8746
+ priority: "p1",
8747
+ content: "Once per session (COO boot only, never repeat), call list_my_feature_requests to check if any previously filed feature requests have been shipped by AskExe. If any request has status 'shipped' with a shipped_version, surface it to the founder immediately: '\u{1F680} N feature(s) shipped \u2014 run exe-os update to get version X.Y.Z'. This is a one-time check at boot, not a recurring poll. If no requests exist or none are shipped, skip silently. If the MCP tool is unavailable or the network call fails, skip silently \u2014 this is informational, not blocking."
8748
+ },
8704
8749
  // --- Operations ---
8705
8750
  {
8706
8751
  title: "Managers must supervise deployed workers",
@@ -10436,7 +10481,7 @@ import { createInterface as createInterface2 } from "readline";
10436
10481
  import { existsSync as existsSync19, statSync as statSync6, mkdirSync as mkdirSync12, copyFileSync as copyFileSync3, renameSync as renameSync4, rmSync, writeFileSync as writeFileSync12 } from "fs";
10437
10482
  import path19 from "path";
10438
10483
  import os12 from "os";
10439
- import { execSync as execSync4 } from "child_process";
10484
+ import { execSync as execSync5 } from "child_process";
10440
10485
  import { createClient as createClient3 } from "@libsql/client";
10441
10486
  function isInteractiveTerminal2() {
10442
10487
  return Boolean(process.stdin.isTTY && process.stdout.isTTY && !process.env.CI);
@@ -10467,7 +10512,7 @@ function dbSidecarPaths(dbPath) {
10467
10512
  function daemonStatus() {
10468
10513
  const pidPath = path19.join(dataDir(), "exed.pid");
10469
10514
  if (!existsSync19(pidPath)) return { pid: null, alive: false };
10470
- const pid = execSync4(`cat ${JSON.stringify(pidPath)}`, { encoding: "utf8" }).trim();
10515
+ const pid = execSync5(`cat ${JSON.stringify(pidPath)}`, { encoding: "utf8" }).trim();
10471
10516
  if (!pid) return { pid: null, alive: false };
10472
10517
  try {
10473
10518
  process.kill(Number(pid), 0);
@@ -10481,7 +10526,7 @@ async function stopDaemonIfAlive() {
10481
10526
  if (!daemon.alive || !daemon.pid) return;
10482
10527
  if (Number(daemon.pid) <= 1) {
10483
10528
  try {
10484
- execSync4("docker inspect exed >/dev/null 2>&1 && docker stop exed >/dev/null", { timeout: 3e4 });
10529
+ execSync5("docker inspect exed >/dev/null 2>&1 && docker stop exed >/dev/null", { timeout: 3e4 });
10485
10530
  return;
10486
10531
  } catch {
10487
10532
  throw new Error("Refusing to signal PID 1. Stop the exed Docker container manually, then retry.");
@@ -12950,7 +12995,7 @@ __export(session_registry_exports, {
12950
12995
  registerSession: () => registerSession
12951
12996
  });
12952
12997
  import { readFileSync as readFileSync18, writeFileSync as writeFileSync17, mkdirSync as mkdirSync17, existsSync as existsSync22 } from "fs";
12953
- import { execSync as execSync5 } from "child_process";
12998
+ import { execSync as execSync6 } from "child_process";
12954
12999
  import path25 from "path";
12955
13000
  import os14 from "os";
12956
13001
  function registerSession(entry) {
@@ -12990,7 +13035,7 @@ function pruneStaleSessions() {
12990
13035
  if (sessions.length === 0) return 0;
12991
13036
  let liveSessions = [];
12992
13037
  try {
12993
- liveSessions = execSync5("tmux list-sessions -F '#{session_name}' 2>/dev/null", {
13038
+ liveSessions = execSync6("tmux list-sessions -F '#{session_name}' 2>/dev/null", {
12994
13039
  encoding: "utf8"
12995
13040
  }).trim().split("\n").filter(Boolean);
12996
13041
  } catch {
@@ -13013,7 +13058,7 @@ var init_session_registry = __esm({
13013
13058
  });
13014
13059
 
13015
13060
  // src/lib/session-key.ts
13016
- import { execSync as execSync6 } from "child_process";
13061
+ import { execSync as execSync7 } from "child_process";
13017
13062
  function normalizeCommand(command) {
13018
13063
  const trimmed = command.trim().toLowerCase();
13019
13064
  const parts = trimmed.split(/[\\/]/);
@@ -13032,7 +13077,7 @@ function resolveRuntimeProcess() {
13032
13077
  let pid = process.ppid;
13033
13078
  for (let i = 0; i < 10; i++) {
13034
13079
  try {
13035
- const info = execSync6(`ps -p ${pid} -o ppid=,comm=`, {
13080
+ const info = execSync7(`ps -p ${pid} -o ppid=,comm=`, {
13036
13081
  encoding: "utf8",
13037
13082
  timeout: 2e3
13038
13083
  }).trim();
@@ -13206,14 +13251,14 @@ var init_transport = __esm({
13206
13251
  });
13207
13252
 
13208
13253
  // src/lib/cc-agent-support.ts
13209
- import { execSync as execSync7 } from "child_process";
13254
+ import { execSync as execSync8 } from "child_process";
13210
13255
  function _resetCcAgentSupportCache() {
13211
13256
  _cachedSupport = null;
13212
13257
  }
13213
13258
  function claudeSupportsAgentFlag() {
13214
13259
  if (_cachedSupport !== null) return _cachedSupport;
13215
13260
  try {
13216
- const helpOutput = execSync7("claude --help 2>&1", {
13261
+ const helpOutput = execSync8("claude --help 2>&1", {
13217
13262
  encoding: "utf-8",
13218
13263
  timeout: 5e3
13219
13264
  });
@@ -13590,7 +13635,7 @@ var init_session_kill_telemetry = __esm({
13590
13635
  });
13591
13636
 
13592
13637
  // src/lib/project-name.ts
13593
- import { execSync as execSync8 } from "child_process";
13638
+ import { execSync as execSync9 } from "child_process";
13594
13639
  import path29 from "path";
13595
13640
  function getProjectName(cwd2) {
13596
13641
  const dir = cwd2 ?? process.cwd();
@@ -13598,7 +13643,7 @@ function getProjectName(cwd2) {
13598
13643
  try {
13599
13644
  let repoRoot;
13600
13645
  try {
13601
- const gitCommonDir = execSync8("git rev-parse --path-format=absolute --git-common-dir", {
13646
+ const gitCommonDir = execSync9("git rev-parse --path-format=absolute --git-common-dir", {
13602
13647
  cwd: dir,
13603
13648
  encoding: "utf8",
13604
13649
  timeout: 2e3,
@@ -13606,7 +13651,7 @@ function getProjectName(cwd2) {
13606
13651
  }).trim();
13607
13652
  repoRoot = path29.dirname(gitCommonDir);
13608
13653
  } catch {
13609
- repoRoot = execSync8("git rev-parse --show-toplevel", {
13654
+ repoRoot = execSync9("git rev-parse --show-toplevel", {
13610
13655
  cwd: dir,
13611
13656
  encoding: "utf8",
13612
13657
  timeout: 2e3,
@@ -13713,7 +13758,7 @@ __export(tasks_crud_exports, {
13713
13758
  import crypto9 from "crypto";
13714
13759
  import path30 from "path";
13715
13760
  import os17 from "os";
13716
- import { execSync as execSync9 } from "child_process";
13761
+ import { execSync as execSync10 } from "child_process";
13717
13762
  import { mkdir as mkdir5, writeFile as writeFile5, appendFile } from "fs/promises";
13718
13763
  import { existsSync as existsSync26, readFileSync as readFileSync22 } from "fs";
13719
13764
  async function writeCheckpoint(input) {
@@ -14058,14 +14103,14 @@ function isTmuxSessionAlive(identifier) {
14058
14103
  if (!identifier || identifier === "unknown") return true;
14059
14104
  try {
14060
14105
  if (identifier.startsWith("%")) {
14061
- const output2 = execSync9("tmux list-panes -a -F '#{pane_id}'", {
14106
+ const output2 = execSync10("tmux list-panes -a -F '#{pane_id}'", {
14062
14107
  timeout: 2e3,
14063
14108
  encoding: "utf8",
14064
14109
  stdio: ["pipe", "pipe", "pipe"]
14065
14110
  });
14066
14111
  return output2.split("\n").some((l) => l.trim() === identifier);
14067
14112
  } else {
14068
- execSync9(`tmux has-session -t ${JSON.stringify(identifier)}`, {
14113
+ execSync10(`tmux has-session -t ${JSON.stringify(identifier)}`, {
14069
14114
  timeout: 2e3,
14070
14115
  stdio: ["pipe", "pipe", "pipe"]
14071
14116
  });
@@ -14074,7 +14119,7 @@ function isTmuxSessionAlive(identifier) {
14074
14119
  } catch {
14075
14120
  if (identifier.startsWith("%")) return true;
14076
14121
  try {
14077
- execSync9("tmux list-sessions", {
14122
+ execSync10("tmux list-sessions", {
14078
14123
  timeout: 2e3,
14079
14124
  stdio: ["pipe", "pipe", "pipe"]
14080
14125
  });
@@ -14089,12 +14134,12 @@ function checkStaleCompletion(taskContext, taskCreatedAt) {
14089
14134
  if (!DELEGATION_KEYWORDS.test(taskContext)) return null;
14090
14135
  try {
14091
14136
  const since = new Date(taskCreatedAt).toISOString();
14092
- const branch = execSync9(
14137
+ const branch = execSync10(
14093
14138
  "git rev-parse --abbrev-ref HEAD 2>/dev/null",
14094
14139
  { encoding: "utf8", timeout: 3e3 }
14095
14140
  ).trim();
14096
14141
  const branchArg = branch && branch !== "HEAD" ? branch : "";
14097
- const commitCount = execSync9(
14142
+ const commitCount = execSync10(
14098
14143
  `git log --oneline --since="${since}" ${branchArg} 2>/dev/null | wc -l`,
14099
14144
  { encoding: "utf8", timeout: 5e3 }
14100
14145
  ).trim();
@@ -15693,7 +15738,7 @@ __export(tmux_routing_exports, {
15693
15738
  spawnEmployee: () => spawnEmployee,
15694
15739
  verifyPaneAtCapacity: () => verifyPaneAtCapacity
15695
15740
  });
15696
- import { execFileSync as execFileSync3, execSync as execSync10 } from "child_process";
15741
+ import { execFileSync as execFileSync3, execSync as execSync11 } from "child_process";
15697
15742
  import { readFileSync as readFileSync23, writeFileSync as writeFileSync20, mkdirSync as mkdirSync20, existsSync as existsSync28, appendFileSync as appendFileSync2, readdirSync as readdirSync7 } from "fs";
15698
15743
  import path34 from "path";
15699
15744
  import os18 from "os";
@@ -16414,7 +16459,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
16414
16459
  let booted = false;
16415
16460
  for (let i = 0; i < 30; i++) {
16416
16461
  try {
16417
- execSync10("sleep 0.5");
16462
+ execSync11("sleep 0.5");
16418
16463
  } catch {
16419
16464
  }
16420
16465
  try {
@@ -16787,7 +16832,7 @@ __export(active_agent_exports, {
16787
16832
  writeActiveAgent: () => writeActiveAgent
16788
16833
  });
16789
16834
  import { readFileSync as readFileSync24, writeFileSync as writeFileSync21, mkdirSync as mkdirSync21, unlinkSync as unlinkSync12, readdirSync as readdirSync8 } from "fs";
16790
- import { execSync as execSync11 } from "child_process";
16835
+ import { execSync as execSync12 } from "child_process";
16791
16836
  import path35 from "path";
16792
16837
  function isNameWithOptionalInstance(candidate, baseName) {
16793
16838
  if (candidate === baseName) return true;
@@ -16881,7 +16926,7 @@ function getActiveAgent() {
16881
16926
  } catch {
16882
16927
  }
16883
16928
  try {
16884
- const sessionName = execSync11(
16929
+ const sessionName = execSync12(
16885
16930
  "tmux display-message -p '#{session_name}' 2>/dev/null",
16886
16931
  { encoding: "utf8", timeout: 2e3 }
16887
16932
  ).trim();
@@ -17606,7 +17651,7 @@ __export(exe_rename_exports, {
17606
17651
  renameEmployee: () => renameEmployee
17607
17652
  });
17608
17653
  import { readFileSync as readFileSync25, writeFileSync as writeFileSync22, renameSync as renameSync6, unlinkSync as unlinkSync13, existsSync as existsSync29 } from "fs";
17609
- import { execSync as execSync12 } from "child_process";
17654
+ import { execSync as execSync13 } from "child_process";
17610
17655
  import path36 from "path";
17611
17656
  import { homedir as homedir4 } from "os";
17612
17657
  async function renameEmployee(oldName, newName, opts = {}) {
@@ -17747,7 +17792,7 @@ function rewriteRenamedEmployeeContent(content, oldName, newName) {
17747
17792
  }
17748
17793
  function findExeBin2() {
17749
17794
  try {
17750
- return execSync12(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
17795
+ return execSync13(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
17751
17796
  } catch {
17752
17797
  return null;
17753
17798
  }
@@ -17999,12 +18044,14 @@ On EVERY new conversation, before doing anything else:
17999
18044
  1. **Memory scan**: Run recall_my_memory with broad queries \u2014 "project", "client", "pipeline", "campaign", "deal", "decision", "blocker". Summarize what you find.
18000
18045
  2. **Task scan**: Run list_tasks to see what's open, in progress, blocked, or needs review across all employees.
18001
18046
  3. **Team check**: Run ask_team_memory for recent activity from CTO/CMO/engineers.
18002
- 4. **Present the brief**: Give the founder a concise status report:
18047
+ 4. **Bug fix check** (one-time, never repeat): Call list_my_bug_reports to see if AskExe has fixed any previously filed bugs. If any have status "fixed" with a fixed_version, tell the founder: "\u{1F527} N bug fix(es) available \u2014 run \`exe-os update\` to get version X.Y.Z." Skip silently if none or if the call fails.
18048
+ 5. **Present the brief**: Give the founder a concise status report:
18003
18049
  - What's active and progressing
18004
18050
  - What's blocked and needs attention
18005
18051
  - What decisions are pending
18052
+ - Available bug fixes (from step 4, if any)
18006
18053
  - What you recommend doing next
18007
- 5. Then ask: "What's the priority?"
18054
+ 6. Then ask: "What's the priority?"
18008
18055
 
18009
18056
  If this is your FIRST ever conversation (few or no prior memories):
18010
18057
  - Search more broadly: "product", "SEO", "meeting", "strategy", "revenue"
@@ -18024,6 +18071,8 @@ Never say "I have no memories" without first searching broadly. Your memory may
18024
18071
  - **get_identity** \u2014 read any agent's identity for coordination
18025
18072
  - **set_agent_config** \u2014 view or change which tool (Claude Code, Codex, OpenCode) and model each agent uses. Call with no args to show all agents' current settings. Call with agent_id + runtime + model to change.
18026
18073
  - **send_message** \u2014 direct intercom to employees
18074
+ - **create_bug_report** \u2014 file a bug when you encounter an Exe OS platform issue
18075
+ - **list_my_bug_reports** \u2014 check status of filed bugs (boot check: surface available fixes to founder)
18027
18076
  ${PLAN_MODE_COMPAT}
18028
18077
  ## Completion Workflow
18029
18078
 
@@ -18453,7 +18502,7 @@ import {
18453
18502
  readdirSync as readdirSync9,
18454
18503
  unlinkSync as unlinkSync15
18455
18504
  } from "fs";
18456
- import { execSync as execSync13 } from "child_process";
18505
+ import { execSync as execSync14 } from "child_process";
18457
18506
  import path38 from "path";
18458
18507
  import { homedir as homedir5 } from "os";
18459
18508
  function generateSessionWrappers(packageRoot, homeDir) {
@@ -18550,12 +18599,12 @@ function writeWrapper(wrapperPath, content) {
18550
18599
  }
18551
18600
  function resolveGlobalBinDir() {
18552
18601
  try {
18553
- const exeOsPath = execSync13("command -v exe-os", { encoding: "utf8", timeout: 3e3 }).trim().split("\n")[0];
18602
+ const exeOsPath = execSync14("command -v exe-os", { encoding: "utf8", timeout: 3e3 }).trim().split("\n")[0];
18554
18603
  if (exeOsPath) return path38.dirname(exeOsPath);
18555
18604
  } catch {
18556
18605
  }
18557
18606
  try {
18558
- const prefix = execSync13("npm prefix -g", { encoding: "utf8", timeout: 3e3 }).trim();
18607
+ const prefix = execSync14("npm prefix -g", { encoding: "utf8", timeout: 3e3 }).trim();
18559
18608
  if (prefix) return path38.join(prefix, "bin");
18560
18609
  } catch {
18561
18610
  }
@@ -18662,8 +18711,8 @@ function ask3(rl, prompt) {
18662
18711
  function getAvailableMemoryGB2() {
18663
18712
  if (process.platform === "darwin") {
18664
18713
  try {
18665
- const { execSync: execSync18 } = __require("child_process");
18666
- const vmstat = execSync18("vm_stat", { encoding: "utf8" });
18714
+ const { execSync: execSync19 } = __require("child_process");
18715
+ const vmstat = execSync19("vm_stat", { encoding: "utf8" });
18667
18716
  const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
18668
18717
  const pageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : 16384;
18669
18718
  const free = vmstat.match(/Pages free:\s+(\d+)/);
@@ -19524,7 +19573,7 @@ var init_update_backup = __esm({
19524
19573
  });
19525
19574
 
19526
19575
  // src/lib/update-check.ts
19527
- import { execSync as execSync14 } from "child_process";
19576
+ import { execSync as execSync15 } from "child_process";
19528
19577
  import { readFileSync as readFileSync28 } from "fs";
19529
19578
  import path41 from "path";
19530
19579
  function getLocalVersion(packageRoot) {
@@ -19534,7 +19583,7 @@ function getLocalVersion(packageRoot) {
19534
19583
  }
19535
19584
  function getRemoteVersion() {
19536
19585
  try {
19537
- const output2 = execSync14("npm view @askexenow/exe-os version", {
19586
+ const output2 = execSync15("npm view @askexenow/exe-os version", {
19538
19587
  encoding: "utf-8",
19539
19588
  timeout: 15e3,
19540
19589
  stdio: ["pipe", "pipe", "pipe"]
@@ -19573,7 +19622,7 @@ __export(update_exports, {
19573
19622
  getRemoteVersion: () => getRemoteVersion,
19574
19623
  runUpdate: () => runUpdate
19575
19624
  });
19576
- import { execSync as execSync15 } from "child_process";
19625
+ import { execSync as execSync16 } from "child_process";
19577
19626
  import { createInterface as createInterface5 } from "readline";
19578
19627
  async function runRestore() {
19579
19628
  console.log("\n\u{1F504} Restoring from update backup...");
@@ -19584,7 +19633,7 @@ async function runRestore() {
19584
19633
  console.log(`
19585
19634
  \u{1F4E5} Reinstalling @askexenow/exe-os@${manifest.version}...`);
19586
19635
  try {
19587
- execSync15(`npm install -g @askexenow/exe-os@${manifest.version}`, {
19636
+ execSync16(`npm install -g @askexenow/exe-os@${manifest.version}`, {
19588
19637
  stdio: ["pipe", "pipe", "inherit"],
19589
19638
  timeout: 3e5
19590
19639
  });
@@ -19609,7 +19658,7 @@ async function runRestore() {
19609
19658
  }
19610
19659
  async function runUpdate(cliArgs) {
19611
19660
  const args2 = cliArgs ?? process.argv.slice(2);
19612
- const autoMode = args2.includes("--auto") || args2.includes("-y");
19661
+ const autoMode = args2.includes("--auto") || args2.includes("--yes") || args2.includes("-y");
19613
19662
  const checkOnly = args2.includes("--check");
19614
19663
  const restoreMode = args2.includes("--restore");
19615
19664
  if (restoreMode) {
@@ -19661,7 +19710,7 @@ async function runUpdate(cliArgs) {
19661
19710
  }
19662
19711
  console.log("\u{1F9F9} Clearing npm cache...");
19663
19712
  try {
19664
- execSync15("npm cache clean --force", { stdio: "pipe" });
19713
+ execSync16("npm cache clean --force", { stdio: "pipe" });
19665
19714
  console.log(" Done");
19666
19715
  } catch {
19667
19716
  console.log(" Skipped (non-critical)");
@@ -19669,7 +19718,7 @@ async function runUpdate(cliArgs) {
19669
19718
  console.log("\u{1F4E5} Installing @askexenow/exe-os@latest...");
19670
19719
  console.log(" This may take a minute...\n");
19671
19720
  try {
19672
- execSync15("npm install -g @askexenow/exe-os@latest", {
19721
+ execSync16("npm install -g @askexenow/exe-os@latest", {
19673
19722
  stdio: ["pipe", "pipe", "inherit"],
19674
19723
  timeout: 3e5
19675
19724
  });
@@ -19687,7 +19736,7 @@ async function runUpdate(cliArgs) {
19687
19736
  newVersion = getLocalVersion(packageRoot);
19688
19737
  } catch {
19689
19738
  try {
19690
- const out = execSync15("npm list -g @askexenow/exe-os --depth=0 2>/dev/null", { encoding: "utf8" });
19739
+ const out = execSync16("npm list -g @askexenow/exe-os --depth=0 2>/dev/null", { encoding: "utf8" });
19691
19740
  const match = out.match(/@askexenow\/exe-os@(\S+)/);
19692
19741
  newVersion = match?.[1] ?? "unknown";
19693
19742
  } catch {
@@ -19716,7 +19765,7 @@ async function runUpdate(cliArgs) {
19716
19765
  }
19717
19766
  console.log("\u{1F527} Re-registering MCP, hooks, wrappers, and daemon...");
19718
19767
  try {
19719
- execSync15("exe-os-install --global", {
19768
+ execSync16("exe-os-install --global", {
19720
19769
  stdio: ["pipe", "inherit", "inherit"],
19721
19770
  timeout: 3e5
19722
19771
  });
@@ -19751,7 +19800,7 @@ async function runUpdate(cliArgs) {
19751
19800
  }
19752
19801
  try {
19753
19802
  console.log("\u{1FA7A} Checking AskExe support intake...");
19754
- execSync15("exe-os support health", {
19803
+ execSync16("exe-os support health", {
19755
19804
  stdio: ["pipe", "inherit", "inherit"],
19756
19805
  timeout: 3e4
19757
19806
  });
@@ -20074,6 +20123,9 @@ function installDockerUbuntu(exec2) {
20074
20123
  function randomSecret(bytes = 32) {
20075
20124
  return randomBytes2(bytes).toString("base64url");
20076
20125
  }
20126
+ function randomHexSecret(bytes = 24) {
20127
+ return randomBytes2(bytes).toString("hex");
20128
+ }
20077
20129
  function hydrateEnv(raw, opts) {
20078
20130
  let next = raw;
20079
20131
  const license = opts.licenseKey || process.env.EXE_LICENSE_KEY || loadLicense() || "";
@@ -20083,6 +20135,8 @@ function hydrateEnv(raw, opts) {
20083
20135
  for (const [key, value] of env.entries()) {
20084
20136
  if (!/CHANGEME/.test(value)) continue;
20085
20137
  if (key === "EXE_LICENSE_KEY" && license) replacements[key] = license;
20138
+ else if (key === "MONITOR_AGENT_KEY") continue;
20139
+ else if (key === "EXE_GATEWAY_WS_RELAY_AUTH_TOKEN") replacements[key] = randomHexSecret(24);
20086
20140
  else if (key.endsWith("_PASSWORD")) replacements[key] = randomSecret(24);
20087
20141
  else if (key.endsWith("_SECRET") || key.endsWith("_TOKEN") || key.endsWith("_KEY") || key.endsWith("_SALT")) replacements[key] = value.replace(/CHANGEME[A-Z0-9_]*/g, randomSecret(32));
20088
20142
  else if (key === "EXED_MCP_TOKEN") replacements[key] = randomSecret(32);
@@ -20342,7 +20396,9 @@ function defaultStackPaths() {
20342
20396
  // Packaged manifests keep cold-start installs unblocked even before update-service entitlements are provisioned.
20343
20397
  auditUrl: process.env.EXE_STACK_AUDIT_URL || (/^https?:\/\//.test(manifestRef) ? "https://update.askexe.com/v1/deploy-audits" : void 0),
20344
20398
  imageCredentialsUrl: process.env.EXE_STACK_IMAGE_CREDENTIALS_URL || (/^https?:\/\//.test(manifestRef) ? "https://update.askexe.com/v1/image-credentials" : void 0),
20345
- manifestAuthToken: process.env.EXE_STACK_UPDATE_TOKEN || process.env.EXE_LICENSE_KEY || loadLicense() || void 0,
20399
+ // License key IS the auth token for update.askexe.com no separate update token needed.
20400
+ // EXE_STACK_UPDATE_TOKEN kept as legacy fallback during migration.
20401
+ manifestAuthToken: process.env.EXE_LICENSE_KEY || loadLicense() || process.env.EXE_STACK_UPDATE_TOKEN || void 0,
20346
20402
  manifestPublicKey: loadDefaultPublicKey()
20347
20403
  };
20348
20404
  }
@@ -20482,7 +20538,14 @@ function printChanges(changes, composeFile, envFile) {
20482
20538
  if (changes.length === 0) {
20483
20539
  const running = areCliContainersRunning(composeFile, envFile);
20484
20540
  if (running) {
20485
- console.log("\u2705 Stack already matches target manifest.");
20541
+ console.log("Stack .env matches target manifest. Checking container health...\n");
20542
+ const unhealthy = printContainerHealth(composeFile, envFile);
20543
+ if (unhealthy > 0) {
20544
+ console.log(`
20545
+ \u{1F534} ${unhealthy} service(s) unhealthy or crashlooping. Run \`docker compose logs <service>\` to diagnose.`);
20546
+ } else {
20547
+ console.log("\n\u2705 Stack already matches target manifest. All services healthy.");
20548
+ }
20486
20549
  } else {
20487
20550
  console.log("\u26A0\uFE0F Stack .env matches target manifest but containers are not running. Will start them.");
20488
20551
  }
@@ -20505,6 +20568,49 @@ function areCliContainersRunning(composeFile, envFile) {
20505
20568
  return false;
20506
20569
  }
20507
20570
  }
20571
+ function getContainerHealth(composeFile, envFile) {
20572
+ try {
20573
+ const result = spawnSync2(
20574
+ "docker",
20575
+ ["compose", "--file", composeFile, "--env-file", envFile, "ps", "--format", "json"],
20576
+ { stdio: ["pipe", "pipe", "pipe"], timeout: 15e3 }
20577
+ );
20578
+ if (result.status !== 0) return [];
20579
+ const raw = result.stdout?.toString().trim() ?? "";
20580
+ if (!raw) return [];
20581
+ return raw.split("\n").filter(Boolean).map((line) => {
20582
+ try {
20583
+ const obj = JSON.parse(line);
20584
+ return {
20585
+ service: obj.Service ?? obj.Name ?? "unknown",
20586
+ state: obj.State ?? "unknown",
20587
+ health: obj.Health ?? "",
20588
+ restartCount: typeof obj.RestartCount === "number" ? obj.RestartCount : 0
20589
+ };
20590
+ } catch {
20591
+ return null;
20592
+ }
20593
+ }).filter((x) => x !== null);
20594
+ } catch {
20595
+ return [];
20596
+ }
20597
+ }
20598
+ function printContainerHealth(composeFile, envFile) {
20599
+ const containers = getContainerHealth(composeFile, envFile);
20600
+ if (containers.length === 0) return 0;
20601
+ let unhealthy = 0;
20602
+ for (const c of containers) {
20603
+ const isRestarting = c.state === "restarting" || c.restartCount > 2;
20604
+ const isUnhealthy = c.health === "unhealthy" || c.state === "dead" || c.state === "exited";
20605
+ if (isRestarting || isUnhealthy) {
20606
+ unhealthy++;
20607
+ console.log(` \u274C ${c.service}: ${c.state} (restarts: ${c.restartCount}${c.health ? `, health: ${c.health}` : ""})`);
20608
+ } else {
20609
+ console.log(` \u2705 ${c.service}: ${c.state}${c.health ? ` (${c.health})` : ""}`);
20610
+ }
20611
+ }
20612
+ return unhealthy;
20613
+ }
20508
20614
  function printBreaking(changes) {
20509
20615
  if (changes.length === 0) return;
20510
20616
  console.log("\nBreaking-change notices:");
@@ -27253,13 +27359,13 @@ __export(tmux_status_exports, {
27253
27359
  parseActivity: () => parseActivity,
27254
27360
  parseContextPercentage: () => parseContextPercentage
27255
27361
  });
27256
- import { execSync as execSync16 } from "child_process";
27362
+ import { execSync as execSync17 } from "child_process";
27257
27363
  function inTmux() {
27258
27364
  if (process.env.TMUX || process.env.TMUX_PANE) return true;
27259
27365
  const term = process.env.TERM ?? "";
27260
27366
  if (term.startsWith("tmux") || term.startsWith("screen")) return true;
27261
27367
  try {
27262
- execSync16("tmux display-message -p '#{session_name}' 2>/dev/null", {
27368
+ execSync17("tmux display-message -p '#{session_name}' 2>/dev/null", {
27263
27369
  encoding: "utf8",
27264
27370
  timeout: 2e3
27265
27371
  });
@@ -27269,12 +27375,12 @@ function inTmux() {
27269
27375
  try {
27270
27376
  let pid = process.ppid;
27271
27377
  for (let depth = 0; depth < 8 && pid > 1; depth++) {
27272
- const comm = execSync16(`ps -p ${pid} -o comm= 2>/dev/null`, {
27378
+ const comm = execSync17(`ps -p ${pid} -o comm= 2>/dev/null`, {
27273
27379
  encoding: "utf8",
27274
27380
  timeout: 1e3
27275
27381
  }).trim();
27276
27382
  if (/tmux/.test(comm)) return true;
27277
- const ppid = execSync16(`ps -p ${pid} -o ppid= 2>/dev/null`, {
27383
+ const ppid = execSync17(`ps -p ${pid} -o ppid= 2>/dev/null`, {
27278
27384
  encoding: "utf8",
27279
27385
  timeout: 1e3
27280
27386
  }).trim();
@@ -27287,7 +27393,7 @@ function inTmux() {
27287
27393
  }
27288
27394
  function listTmuxSessions() {
27289
27395
  try {
27290
- const out = execSync16("tmux list-sessions -F '#{session_name}' 2>/dev/null", {
27396
+ const out = execSync17("tmux list-sessions -F '#{session_name}' 2>/dev/null", {
27291
27397
  encoding: "utf8",
27292
27398
  timeout: 3e3
27293
27399
  });
@@ -27298,7 +27404,7 @@ function listTmuxSessions() {
27298
27404
  }
27299
27405
  function capturePaneLines(windowName, lines = 10) {
27300
27406
  try {
27301
- const out = execSync16(
27407
+ const out = execSync17(
27302
27408
  `tmux capture-pane -t ${JSON.stringify(windowName)} -p 2>/dev/null | tail -${lines}`,
27303
27409
  { encoding: "utf8", timeout: 3e3 }
27304
27410
  );
@@ -27309,7 +27415,7 @@ function capturePaneLines(windowName, lines = 10) {
27309
27415
  }
27310
27416
  function getPaneCwd(windowName) {
27311
27417
  try {
27312
- const out = execSync16(
27418
+ const out = execSync17(
27313
27419
  `tmux display-message -t ${JSON.stringify(windowName)} -p '#{pane_current_path}' 2>/dev/null`,
27314
27420
  { encoding: "utf8", timeout: 3e3 }
27315
27421
  );
@@ -27320,7 +27426,7 @@ function getPaneCwd(windowName) {
27320
27426
  }
27321
27427
  function projectFromPath(dir) {
27322
27428
  try {
27323
- const root = execSync16("git -C " + JSON.stringify(dir) + " rev-parse --show-toplevel 2>/dev/null", {
27429
+ const root = execSync17("git -C " + JSON.stringify(dir) + " rev-parse --show-toplevel 2>/dev/null", {
27324
27430
  encoding: "utf8",
27325
27431
  timeout: 3e3
27326
27432
  }).trim();
@@ -27389,7 +27495,7 @@ function getEmployeeStatuses(employeeNames) {
27389
27495
  }
27390
27496
  let paneAlive = true;
27391
27497
  try {
27392
- const paneStatus = execSync16(
27498
+ const paneStatus = execSync17(
27393
27499
  `tmux list-panes -t ${JSON.stringify(sessionName)} -F '#{pane_dead}' 2>/dev/null`,
27394
27500
  { encoding: "utf8", timeout: 3e3 }
27395
27501
  ).trim();
@@ -27579,8 +27685,8 @@ function Footer() {
27579
27685
  setSessions(allSessions.length);
27580
27686
  if (!currentSession) {
27581
27687
  try {
27582
- const { execSync: execSync18 } = await import("child_process");
27583
- const name = execSync18("tmux display-message -p '#{session_name}' 2>/dev/null", {
27688
+ const { execSync: execSync19 } = await import("child_process");
27689
+ const name = execSync19("tmux display-message -p '#{session_name}' 2>/dev/null", {
27584
27690
  encoding: "utf8",
27585
27691
  timeout: 2e3
27586
27692
  }).trim();
@@ -30934,8 +31040,8 @@ function TmuxPane({ sessionName, employeeName, employeeRole, projectName, onDeta
30934
31040
  }
30935
31041
  const capture = () => {
30936
31042
  try {
30937
- const { execSync: execSync18 } = __require("child_process");
30938
- const output2 = execSync18(
31043
+ const { execSync: execSync19 } = __require("child_process");
31044
+ const output2 = execSync19(
30939
31045
  `tmux capture-pane -t ${JSON.stringify(sessionName)} -p -e 2>/dev/null | tail -${CAPTURE_LINES}`,
30940
31046
  { encoding: "utf8", timeout: 3e3 }
30941
31047
  );
@@ -30959,8 +31065,8 @@ function TmuxPane({ sessionName, employeeName, employeeRole, projectName, onDeta
30959
31065
  if (key.return) {
30960
31066
  if (!demo && inputBuffer.trim()) {
30961
31067
  try {
30962
- const { execSync: execSync18 } = __require("child_process");
30963
- execSync18(
31068
+ const { execSync: execSync19 } = __require("child_process");
31069
+ execSync19(
30964
31070
  `tmux send-keys -t ${JSON.stringify(sessionName)} ${JSON.stringify(inputBuffer)} Enter`,
30965
31071
  { timeout: 2e3 }
30966
31072
  );
@@ -30968,8 +31074,8 @@ function TmuxPane({ sessionName, employeeName, employeeRole, projectName, onDeta
30968
31074
  }
30969
31075
  } else if (!demo) {
30970
31076
  try {
30971
- const { execSync: execSync18 } = __require("child_process");
30972
- execSync18(`tmux send-keys -t ${JSON.stringify(sessionName)} Enter`, { timeout: 2e3 });
31077
+ const { execSync: execSync19 } = __require("child_process");
31078
+ execSync19(`tmux send-keys -t ${JSON.stringify(sessionName)} Enter`, { timeout: 2e3 });
30973
31079
  } catch {
30974
31080
  }
30975
31081
  }
@@ -31656,12 +31762,12 @@ function SessionsView({
31656
31762
  return;
31657
31763
  }
31658
31764
  } else {
31659
- const { execSync: execSync18 } = await import("child_process");
31765
+ const { execSync: execSync19 } = await import("child_process");
31660
31766
  const dir = projectDir || process.cwd();
31661
- execSync18(`tmux new-session -d -s ${JSON.stringify(entry.sessionName)} -c ${JSON.stringify(dir)}`, { timeout: 5e3 });
31662
- execSync18(`tmux send-keys -t ${JSON.stringify(entry.sessionName)} "claude --dangerously-skip-permissions" Enter`, { timeout: 3e3 });
31767
+ execSync19(`tmux new-session -d -s ${JSON.stringify(entry.sessionName)} -c ${JSON.stringify(dir)}`, { timeout: 5e3 });
31768
+ execSync19(`tmux send-keys -t ${JSON.stringify(entry.sessionName)} "claude --dangerously-skip-permissions" Enter`, { timeout: 3e3 });
31663
31769
  await new Promise((r) => setTimeout(r, 3e3));
31664
- execSync18(`tmux send-keys -t ${JSON.stringify(entry.sessionName)} "/exe" Enter`, { timeout: 3e3 });
31770
+ execSync19(`tmux send-keys -t ${JSON.stringify(entry.sessionName)} "/exe" Enter`, { timeout: 3e3 });
31665
31771
  }
31666
31772
  const updated = { ...entry, status: "active", activity: "Starting...", attached: false };
31667
31773
  setViewingEmployee(updated);
@@ -31764,7 +31870,7 @@ function SessionsView({
31764
31870
  const { listTmuxSessions: listTmuxSessions2, inTmux: inTmux2, capturePaneLines: capturePaneLines2, parseActivity: parseActivity2 } = await Promise.resolve().then(() => (init_tmux_status(), tmux_status_exports));
31765
31871
  const { getCoordinatorName: getCoordinatorName2, isCoordinatorRole: isCoordinatorRole2, loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
31766
31872
  const { isExeSession: isExeSession2 } = await Promise.resolve().then(() => (init_tmux_routing(), tmux_routing_exports));
31767
- const { execSync: execSync18 } = await import("child_process");
31873
+ const { execSync: execSync19 } = await import("child_process");
31768
31874
  if (!inTmux2()) {
31769
31875
  setTmuxAvailable(false);
31770
31876
  setProjects([]);
@@ -31773,7 +31879,7 @@ function SessionsView({
31773
31879
  setTmuxAvailable(true);
31774
31880
  const attachedMap = /* @__PURE__ */ new Map();
31775
31881
  try {
31776
- const out = execSync18("tmux list-sessions -F '#{session_name}:#{session_attached}' 2>/dev/null", {
31882
+ const out = execSync19("tmux list-sessions -F '#{session_name}:#{session_attached}' 2>/dev/null", {
31777
31883
  encoding: "utf8",
31778
31884
  timeout: 3e3
31779
31885
  });
@@ -32809,8 +32915,8 @@ function upsertConversation(conversations, platform, senderId, message) {
32809
32915
  async function loadGatewayConfig() {
32810
32916
  const state = { running: false, port: 3100, adapters: [], agents: [], gatewayUrl: "" };
32811
32917
  try {
32812
- const { execSync: execSync18 } = await import("child_process");
32813
- const ps = execSync18("pgrep -f exe-gateway 2>/dev/null", { encoding: "utf8", timeout: 3e3 });
32918
+ const { execSync: execSync19 } = await import("child_process");
32919
+ const ps = execSync19("pgrep -f exe-gateway 2>/dev/null", { encoding: "utf8", timeout: 3e3 });
32814
32920
  state.running = ps.trim().length > 0;
32815
32921
  } catch {
32816
32922
  state.running = false;
@@ -33287,10 +33393,10 @@ var init_Gateway = __esm({
33287
33393
  });
33288
33394
 
33289
33395
  // src/tui/utils/agent-status.ts
33290
- import { execSync as execSync17 } from "child_process";
33396
+ import { execSync as execSync18 } from "child_process";
33291
33397
  function getAgentStatus(agentId) {
33292
33398
  try {
33293
- const sessions = execSync17("tmux list-sessions -F '#{session_name}' 2>/dev/null", {
33399
+ const sessions = execSync18("tmux list-sessions -F '#{session_name}' 2>/dev/null", {
33294
33400
  encoding: "utf8",
33295
33401
  timeout: 2e3
33296
33402
  }).trim().split("\n");
@@ -33301,7 +33407,7 @@ function getAgentStatus(agentId) {
33301
33407
  return /^\d?-/.test(suffix) || /^\d+$/.test(suffix);
33302
33408
  });
33303
33409
  if (!agentSession) return { label: "offline", color: "gray" };
33304
- const pane = execSync17(`tmux capture-pane -t "${agentSession}" -p 2>/dev/null | tail -3`, {
33410
+ const pane = execSync18(`tmux capture-pane -t "${agentSession}" -p 2>/dev/null | tail -3`, {
33305
33411
  encoding: "utf8",
33306
33412
  timeout: 2e3
33307
33413
  });
@@ -34224,8 +34330,8 @@ function SettingsView({ onBack }) {
34224
34330
  };
34225
34331
  });
34226
34332
  try {
34227
- const { execSync: execSync18 } = await import("child_process");
34228
- execSync18("curl -s --max-time 1 http://localhost:11434/api/tags", { timeout: 2e3 });
34333
+ const { execSync: execSync19 } = await import("child_process");
34334
+ execSync19("curl -s --max-time 1 http://localhost:11434/api/tags", { timeout: 2e3 });
34229
34335
  providerList.push({ name: "Ollama", configured: true, detail: "localhost:11434" });
34230
34336
  } catch {
34231
34337
  providerList.push({ name: "Ollama", configured: false, detail: "not running" });
@@ -35115,16 +35221,89 @@ var code_context_index_exports = {};
35115
35221
  __export(code_context_index_exports, {
35116
35222
  analyzeBlastRadius: () => analyzeBlastRadius,
35117
35223
  buildCodeContextIndex: () => buildCodeContextIndex,
35224
+ buildCodeContextIndexWithEmbeddings: () => buildCodeContextIndexWithEmbeddings,
35118
35225
  getCodeContextIndexPath: () => getCodeContextIndexPath,
35119
35226
  getCodeContextStats: () => getCodeContextStats,
35120
35227
  loadOrBuildCodeContextIndex: () => loadOrBuildCodeContextIndex,
35121
35228
  searchCodeContext: () => searchCodeContext,
35229
+ searchCodeContextSemantic: () => searchCodeContextSemantic,
35122
35230
  traceCodeSymbol: () => traceCodeSymbol
35123
35231
  });
35124
35232
  import crypto14 from "crypto";
35125
35233
  import path51 from "path";
35126
35234
  import { existsSync as existsSync36, mkdirSync as mkdirSync25, readFileSync as readFileSync32, readdirSync as readdirSync11, statSync as statSync7, writeFileSync as writeFileSync26 } from "fs";
35127
35235
  import { spawnSync as spawnSync3 } from "child_process";
35236
+ function vectorStorePath(projectRoot) {
35237
+ const rootHash = hashText(projectRoot).slice(0, 16);
35238
+ return path51.join(indexDir(), `${rootHash}.vectors.json`);
35239
+ }
35240
+ function loadVectorStore(projectRoot) {
35241
+ const file = vectorStorePath(projectRoot);
35242
+ if (!existsSync36(file)) return null;
35243
+ try {
35244
+ const parsed = JSON.parse(readFileSync32(file, "utf8"));
35245
+ if (parsed.version !== VECTOR_STORE_VERSION) return null;
35246
+ return parsed;
35247
+ } catch {
35248
+ return null;
35249
+ }
35250
+ }
35251
+ function saveVectorStore(projectRoot, store) {
35252
+ writeFileSync26(vectorStorePath(projectRoot), JSON.stringify(store));
35253
+ }
35254
+ function cosineSimilarity(a, b) {
35255
+ let dot = 0, normA = 0, normB = 0;
35256
+ for (let i = 0; i < a.length; i++) {
35257
+ dot += a[i] * b[i];
35258
+ normA += a[i] * a[i];
35259
+ normB += b[i] * b[i];
35260
+ }
35261
+ const denom = Math.sqrt(normA) * Math.sqrt(normB);
35262
+ return denom === 0 ? 0 : dot / denom;
35263
+ }
35264
+ async function embedSymbols(index) {
35265
+ const rootHash = hashText(index.projectRoot).slice(0, 16);
35266
+ const existing = loadVectorStore(index.projectRoot);
35267
+ const store = {
35268
+ version: VECTOR_STORE_VERSION,
35269
+ projectRootHash: rootHash,
35270
+ vectors: {}
35271
+ };
35272
+ const allSymbols = [];
35273
+ for (const file of Object.values(index.files)) {
35274
+ for (const symbol of file.symbols) {
35275
+ if (existing?.vectors[symbol.id]) {
35276
+ store.vectors[symbol.id] = existing.vectors[symbol.id];
35277
+ } else {
35278
+ allSymbols.push({ id: symbol.id, text: symbol.summary || `${symbol.kind} ${symbol.name} in ${symbol.filePath}` });
35279
+ }
35280
+ }
35281
+ }
35282
+ if (allSymbols.length === 0) {
35283
+ saveVectorStore(index.projectRoot, store);
35284
+ return store;
35285
+ }
35286
+ const connected = await connectEmbedDaemon().catch(() => false);
35287
+ if (!connected) {
35288
+ saveVectorStore(index.projectRoot, store);
35289
+ return store;
35290
+ }
35291
+ for (let i = 0; i < allSymbols.length; i += EMBED_BATCH_SIZE) {
35292
+ const batch = allSymbols.slice(i, i + EMBED_BATCH_SIZE);
35293
+ const texts = batch.map((s) => s.text);
35294
+ try {
35295
+ const vectors = await embedBatchViaClient(texts, "low");
35296
+ if (vectors && vectors.length === batch.length) {
35297
+ for (let j = 0; j < batch.length; j++) {
35298
+ store.vectors[batch[j].id] = vectors[j];
35299
+ }
35300
+ }
35301
+ } catch {
35302
+ }
35303
+ }
35304
+ saveVectorStore(index.projectRoot, store);
35305
+ return store;
35306
+ }
35128
35307
  function normalizeProjectRoot(projectRoot) {
35129
35308
  return path51.resolve(projectRoot || process.cwd());
35130
35309
  }
@@ -35400,7 +35579,7 @@ function filteredFiles(index, options = {}) {
35400
35579
  return matchesPath(file.path, options.paths);
35401
35580
  });
35402
35581
  }
35403
- function searchCodeContext(query, options = {}) {
35582
+ function lexicalSearch(query, options = {}) {
35404
35583
  const terms = tokenize2(query);
35405
35584
  if (terms.length === 0) return [];
35406
35585
  const index = loadOrBuildCodeContextIndex({ projectRoot: options.projectRoot, force: options.force || options.refreshIndex, maxFiles: options.maxFiles });
@@ -35426,6 +35605,81 @@ function searchCodeContext(query, options = {}) {
35426
35605
  const limit = options.limit ?? 20;
35427
35606
  return results.sort((a, b) => b.score - a.score || a.filePath.localeCompare(b.filePath)).slice(offset, offset + limit);
35428
35607
  }
35608
+ function searchCodeContext(query, options = {}) {
35609
+ return lexicalSearch(query, options);
35610
+ }
35611
+ async function searchCodeContextSemantic(query, options = {}) {
35612
+ const terms = tokenize2(query);
35613
+ if (terms.length === 0) return [];
35614
+ const index = loadOrBuildCodeContextIndex({ projectRoot: options.projectRoot, force: options.force || options.refreshIndex, maxFiles: options.maxFiles });
35615
+ const projectRoot = normalizeProjectRoot(options.projectRoot);
35616
+ const vectorStore = loadVectorStore(projectRoot);
35617
+ if (!vectorStore || Object.keys(vectorStore.vectors).length === 0) {
35618
+ return lexicalSearch(query, options);
35619
+ }
35620
+ let queryVector = null;
35621
+ try {
35622
+ const connected = await connectEmbedDaemon().catch(() => false);
35623
+ if (connected) {
35624
+ const result = await embedBatchViaClient([query], "high");
35625
+ if (result && result.length === 1) queryVector = result[0];
35626
+ }
35627
+ } catch {
35628
+ }
35629
+ if (!queryVector) return lexicalSearch(query, options);
35630
+ const files = filteredFiles(index, options);
35631
+ const candidates = [];
35632
+ for (const file of files) {
35633
+ for (const symbol of file.symbols) {
35634
+ const lexical = scoreSymbol(symbol, terms);
35635
+ const vec = vectorStore.vectors[symbol.id];
35636
+ const vectorScore = vec ? cosineSimilarity(queryVector, vec) : 0;
35637
+ if (lexical.score > 0 || vectorScore > 0.3) {
35638
+ candidates.push({
35639
+ symbol,
35640
+ lexicalScore: lexical.score,
35641
+ vectorScore,
35642
+ matches: lexical.matches
35643
+ });
35644
+ }
35645
+ }
35646
+ }
35647
+ const byLexical = [...candidates].sort((a, b) => b.lexicalScore - a.lexicalScore);
35648
+ const byVector = [...candidates].sort((a, b) => b.vectorScore - a.vectorScore);
35649
+ const lexicalRank = /* @__PURE__ */ new Map();
35650
+ const vectorRank = /* @__PURE__ */ new Map();
35651
+ byLexical.forEach((c, i) => lexicalRank.set(c.symbol.id, i + 1));
35652
+ byVector.forEach((c, i) => vectorRank.set(c.symbol.id, i + 1));
35653
+ const VECTOR_WEIGHT = 0.6;
35654
+ const LEXICAL_WEIGHT = 0.4;
35655
+ const fused = candidates.map((c) => {
35656
+ const lRank = lexicalRank.get(c.symbol.id) ?? candidates.length + 1;
35657
+ const vRank = vectorRank.get(c.symbol.id) ?? candidates.length + 1;
35658
+ const rrfScore = VECTOR_WEIGHT * (1 / (RRF_K + vRank)) + LEXICAL_WEIGHT * (1 / (RRF_K + lRank));
35659
+ return {
35660
+ symbol: c.symbol,
35661
+ score: rrfScore,
35662
+ matches: c.vectorScore > 0.3 ? [...c.matches, `semantic=${c.vectorScore.toFixed(3)}`] : c.matches,
35663
+ filePath: c.symbol.filePath,
35664
+ language: c.symbol.language,
35665
+ content: c.symbol.text,
35666
+ startLine: c.symbol.startLine,
35667
+ endLine: c.symbol.endLine
35668
+ };
35669
+ });
35670
+ const offset = Math.max(0, options.offset ?? 0);
35671
+ const limit = options.limit ?? 20;
35672
+ return fused.sort((a, b) => b.score - a.score || a.filePath.localeCompare(b.filePath)).slice(offset, offset + limit);
35673
+ }
35674
+ async function buildCodeContextIndexWithEmbeddings(options = {}) {
35675
+ const index = buildCodeContextIndex(options);
35676
+ const existingStore = loadVectorStore(index.projectRoot);
35677
+ const existingCount = existingStore ? Object.keys(existingStore.vectors).length : 0;
35678
+ const store = await embedSymbols(index);
35679
+ const vectorCount = Object.keys(store.vectors).length;
35680
+ const newEmbeddings = vectorCount - Math.min(existingCount, vectorCount);
35681
+ return { index, vectorCount, newEmbeddings };
35682
+ }
35429
35683
  function dependentsMap(index) {
35430
35684
  const map = /* @__PURE__ */ new Map();
35431
35685
  for (const file of Object.values(index.files)) {
@@ -35517,15 +35771,19 @@ function getCodeContextStats(options = {}) {
35517
35771
  indexPath: getCodeContextIndexPath(index.projectRoot)
35518
35772
  };
35519
35773
  }
35520
- var INDEX_VERSION, DEFAULT_MAX_FILES, IGNORE_SEGMENTS;
35774
+ var VECTOR_STORE_VERSION, EMBED_BATCH_SIZE, INDEX_VERSION, DEFAULT_MAX_FILES, IGNORE_SEGMENTS, RRF_K;
35521
35775
  var init_code_context_index = __esm({
35522
35776
  "src/lib/code-context-index.ts"() {
35523
35777
  "use strict";
35524
35778
  init_config();
35525
35779
  init_code_chunker();
35780
+ init_exe_daemon_client();
35781
+ VECTOR_STORE_VERSION = 1;
35782
+ EMBED_BATCH_SIZE = 64;
35526
35783
  INDEX_VERSION = 2;
35527
35784
  DEFAULT_MAX_FILES = 5e3;
35528
35785
  IGNORE_SEGMENTS = /* @__PURE__ */ new Set(["node_modules", "dist", ".git", "coverage", ".worktrees", ".next", "build", "target", "vendor"]);
35786
+ RRF_K = 60;
35529
35787
  }
35530
35788
  });
35531
35789
 
@@ -37024,10 +37282,10 @@ async function runClaudeUninstall(flags = []) {
37024
37282
  }
37025
37283
  }
37026
37284
  try {
37027
- const { execSync: execSync18 } = await import("child_process");
37285
+ const { execSync: execSync19 } = await import("child_process");
37028
37286
  const findExeBin3 = () => {
37029
37287
  try {
37030
- return execSync18(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
37288
+ return execSync19(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
37031
37289
  } catch {
37032
37290
  return null;
37033
37291
  }