@askexenow/exe-os 0.8.64 → 0.8.68

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 (55) hide show
  1. package/dist/bin/cleanup-stale-review-tasks.js +8 -7
  2. package/dist/bin/cli.js +215 -120
  3. package/dist/bin/exe-assign.js +11 -10
  4. package/dist/bin/exe-boot.js +83 -78
  5. package/dist/bin/exe-call.js +33 -1
  6. package/dist/bin/exe-cloud.js +3 -2
  7. package/dist/bin/exe-dispatch.js +31 -30
  8. package/dist/bin/exe-gateway.js +33 -32
  9. package/dist/bin/exe-heartbeat.js +21 -20
  10. package/dist/bin/exe-launch-agent.js +48 -16
  11. package/dist/bin/exe-link.js +16 -11
  12. package/dist/bin/exe-new-employee.js +20 -19
  13. package/dist/bin/exe-pending-messages.js +7 -6
  14. package/dist/bin/exe-pending-reviews.js +16 -15
  15. package/dist/bin/exe-rename.js +12 -11
  16. package/dist/bin/exe-review.js +4 -3
  17. package/dist/bin/exe-session-cleanup.js +20 -19
  18. package/dist/bin/exe-settings.js +3 -2
  19. package/dist/bin/exe-start.sh +2 -2
  20. package/dist/bin/exe-status.js +16 -15
  21. package/dist/bin/exe-team.js +4 -3
  22. package/dist/bin/git-sweep.js +31 -30
  23. package/dist/bin/install.js +284 -113
  24. package/dist/bin/scan-tasks.js +33 -32
  25. package/dist/bin/setup.js +114 -30
  26. package/dist/gateway/index.js +32 -31
  27. package/dist/hooks/bug-report-worker.js +58 -26
  28. package/dist/hooks/commit-complete.js +31 -30
  29. package/dist/hooks/ingest-worker.js +58 -57
  30. package/dist/hooks/post-compact.js +10 -9
  31. package/dist/hooks/pre-compact.js +31 -30
  32. package/dist/hooks/pre-tool-use.js +46 -14
  33. package/dist/hooks/prompt-ingest-worker.js +15 -14
  34. package/dist/hooks/prompt-submit.js +15 -14
  35. package/dist/hooks/response-ingest-worker.js +8 -7
  36. package/dist/hooks/session-end.js +14 -13
  37. package/dist/hooks/session-start.js +10 -9
  38. package/dist/hooks/stop.js +10 -9
  39. package/dist/hooks/subagent-stop.js +10 -9
  40. package/dist/hooks/summary-worker.js +41 -36
  41. package/dist/index.js +43 -42
  42. package/dist/lib/cloud-sync.js +16 -11
  43. package/dist/lib/employees.js +33 -1
  44. package/dist/lib/exe-daemon.js +56 -55
  45. package/dist/lib/messaging.js +9 -8
  46. package/dist/lib/tasks.js +27 -26
  47. package/dist/lib/tmux-routing.js +29 -28
  48. package/dist/mcp/server.js +94 -62
  49. package/dist/mcp/tools/create-task.js +60 -28
  50. package/dist/mcp/tools/list-tasks.js +10 -9
  51. package/dist/mcp/tools/send-message.js +11 -10
  52. package/dist/mcp/tools/update-task.js +21 -20
  53. package/dist/runtime/index.js +31 -30
  54. package/dist/tui/App.js +67 -35
  55. package/package.json +1 -1
@@ -1438,9 +1438,10 @@ var init_config = __esm({
1438
1438
 
1439
1439
  // src/lib/employees.ts
1440
1440
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
1441
- import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync4 } from "fs";
1441
+ import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync4, renameSync as renameSync3, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
1442
1442
  import { execSync as execSync3 } from "child_process";
1443
1443
  import path4 from "path";
1444
+ import os4 from "os";
1444
1445
  function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
1445
1446
  if (!existsSync4(employeesPath)) return [];
1446
1447
  try {
@@ -1469,7 +1470,7 @@ var init_employees = __esm({
1469
1470
  });
1470
1471
 
1471
1472
  // src/lib/license.ts
1472
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync5, mkdirSync as mkdirSync3 } from "fs";
1473
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, existsSync as existsSync5, mkdirSync as mkdirSync3 } from "fs";
1473
1474
  import { randomUUID } from "crypto";
1474
1475
  import path5 from "path";
1475
1476
  import { jwtVerify, importSPKI } from "jose";
@@ -1572,11 +1573,11 @@ var init_plan_limits = __esm({
1572
1573
  // src/lib/notifications.ts
1573
1574
  import crypto from "crypto";
1574
1575
  import path7 from "path";
1575
- import os4 from "os";
1576
+ import os5 from "os";
1576
1577
  import {
1577
1578
  readFileSync as readFileSync7,
1578
1579
  readdirSync,
1579
- unlinkSync,
1580
+ unlinkSync as unlinkSync2,
1580
1581
  existsSync as existsSync7,
1581
1582
  rmdirSync
1582
1583
  } from "fs";
@@ -2128,7 +2129,7 @@ var init_tasks_crud = __esm({
2128
2129
 
2129
2130
  // src/lib/tasks-review.ts
2130
2131
  import path9 from "path";
2131
- import { existsSync as existsSync9, readdirSync as readdirSync2, unlinkSync as unlinkSync2 } from "fs";
2132
+ import { existsSync as existsSync9, readdirSync as readdirSync2, unlinkSync as unlinkSync3 } from "fs";
2132
2133
  async function countPendingReviews(sessionScope) {
2133
2134
  const client = getClient();
2134
2135
  if (sessionScope) {
@@ -2313,7 +2314,7 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
2313
2314
  if (existsSync9(cacheDir)) {
2314
2315
  for (const f of readdirSync2(cacheDir)) {
2315
2316
  if (f.startsWith("review-notified-")) {
2316
- unlinkSync2(path9.join(cacheDir, f));
2317
+ unlinkSync3(path9.join(cacheDir, f));
2317
2318
  }
2318
2319
  }
2319
2320
  }
@@ -2928,7 +2929,7 @@ __export(tasks_exports, {
2928
2929
  writeCheckpoint: () => writeCheckpoint
2929
2930
  });
2930
2931
  import path12 from "path";
2931
- import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, unlinkSync as unlinkSync3 } from "fs";
2932
+ import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, unlinkSync as unlinkSync4 } from "fs";
2932
2933
  async function createTask(input) {
2933
2934
  const result = await createTaskCore(input);
2934
2935
  if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
@@ -2951,10 +2952,10 @@ async function updateTask(input) {
2951
2952
  const cachePath = path12.join(cacheDir, `current-task-${agent}.json`);
2952
2953
  if (input.status === "in_progress") {
2953
2954
  mkdirSync4(cacheDir, { recursive: true });
2954
- writeFileSync4(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
2955
+ writeFileSync5(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
2955
2956
  } else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
2956
2957
  try {
2957
- unlinkSync3(cachePath);
2958
+ unlinkSync4(cachePath);
2958
2959
  } catch {
2959
2960
  }
2960
2961
  }
@@ -3396,11 +3397,11 @@ __export(tmux_routing_exports, {
3396
3397
  verifyPaneAtCapacity: () => verifyPaneAtCapacity
3397
3398
  });
3398
3399
  import { execFileSync as execFileSync2, execSync as execSync6 } from "child_process";
3399
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, existsSync as existsSync10, appendFileSync } from "fs";
3400
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, existsSync as existsSync10, appendFileSync } from "fs";
3400
3401
  import path13 from "path";
3401
- import os5 from "os";
3402
+ import os6 from "os";
3402
3403
  import { fileURLToPath as fileURLToPath2 } from "url";
3403
- import { unlinkSync as unlinkSync4 } from "fs";
3404
+ import { unlinkSync as unlinkSync5 } from "fs";
3404
3405
  function spawnLockPath(sessionName) {
3405
3406
  return path13.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
3406
3407
  }
@@ -3427,12 +3428,12 @@ function acquireSpawnLock(sessionName) {
3427
3428
  } catch {
3428
3429
  }
3429
3430
  }
3430
- writeFileSync5(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
3431
+ writeFileSync6(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
3431
3432
  return true;
3432
3433
  }
3433
3434
  function releaseSpawnLock(sessionName) {
3434
3435
  try {
3435
- unlinkSync4(spawnLockPath(sessionName));
3436
+ unlinkSync5(spawnLockPath(sessionName));
3436
3437
  } catch {
3437
3438
  }
3438
3439
  }
@@ -3514,7 +3515,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
3514
3515
  }
3515
3516
  const rootExe = extractRootExe(parentExe) ?? parentExe;
3516
3517
  const filePath = path13.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
3517
- writeFileSync5(filePath, JSON.stringify({
3518
+ writeFileSync6(filePath, JSON.stringify({
3518
3519
  parentExe: rootExe,
3519
3520
  dispatchedBy: dispatchedBy || rootExe,
3520
3521
  registeredAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -3601,7 +3602,7 @@ function readDebounceState() {
3601
3602
  function writeDebounceState(state) {
3602
3603
  try {
3603
3604
  if (!existsSync10(SESSION_CACHE)) mkdirSync5(SESSION_CACHE, { recursive: true });
3604
- writeFileSync5(DEBOUNCE_FILE, JSON.stringify(state));
3605
+ writeFileSync6(DEBOUNCE_FILE, JSON.stringify(state));
3605
3606
  } catch {
3606
3607
  }
3607
3608
  }
@@ -3790,7 +3791,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3790
3791
  const transport = getTransport();
3791
3792
  const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
3792
3793
  const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
3793
- const logDir = path13.join(os5.homedir(), ".exe-os", "session-logs");
3794
+ const logDir = path13.join(os6.homedir(), ".exe-os", "session-logs");
3794
3795
  const logFile = path13.join(logDir, `${instanceLabel}-${Date.now()}.log`);
3795
3796
  if (!existsSync10(logDir)) {
3796
3797
  mkdirSync5(logDir, { recursive: true });
@@ -3806,7 +3807,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3806
3807
  } catch {
3807
3808
  }
3808
3809
  try {
3809
- const claudeJsonPath = path13.join(os5.homedir(), ".claude.json");
3810
+ const claudeJsonPath = path13.join(os6.homedir(), ".claude.json");
3810
3811
  let claudeJson = {};
3811
3812
  try {
3812
3813
  claudeJson = JSON.parse(readFileSync9(claudeJsonPath, "utf8"));
@@ -3817,11 +3818,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3817
3818
  const trustDir = opts?.cwd ?? projectDir;
3818
3819
  if (!projects[trustDir]) projects[trustDir] = {};
3819
3820
  projects[trustDir].hasTrustDialogAccepted = true;
3820
- writeFileSync5(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
3821
+ writeFileSync6(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
3821
3822
  } catch {
3822
3823
  }
3823
3824
  try {
3824
- const settingsDir = path13.join(os5.homedir(), ".claude", "projects");
3825
+ const settingsDir = path13.join(os6.homedir(), ".claude", "projects");
3825
3826
  const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
3826
3827
  const projSettingsDir = path13.join(settingsDir, normalizedKey);
3827
3828
  const settingsPath = path13.join(projSettingsDir, "settings.json");
@@ -3856,7 +3857,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3856
3857
  perms.allow = allow;
3857
3858
  settings.permissions = perms;
3858
3859
  mkdirSync5(projSettingsDir, { recursive: true });
3859
- writeFileSync5(settingsPath, JSON.stringify(settings, null, 2) + "\n");
3860
+ writeFileSync6(settingsPath, JSON.stringify(settings, null, 2) + "\n");
3860
3861
  }
3861
3862
  } catch {
3862
3863
  }
@@ -3869,7 +3870,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3869
3870
  let legacyFallbackWarned = false;
3870
3871
  if (!useExeAgent && !useBinSymlink) {
3871
3872
  const identityPath = path13.join(
3872
- os5.homedir(),
3873
+ os6.homedir(),
3873
3874
  ".exe-os",
3874
3875
  "identity",
3875
3876
  `${employeeName}.md`
@@ -3899,7 +3900,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3899
3900
  }
3900
3901
  let sessionContextFlag = "";
3901
3902
  try {
3902
- const ctxDir = path13.join(os5.homedir(), ".exe-os", "session-cache");
3903
+ const ctxDir = path13.join(os6.homedir(), ".exe-os", "session-cache");
3903
3904
  mkdirSync5(ctxDir, { recursive: true });
3904
3905
  const ctxFile = path13.join(ctxDir, `session-context-${sessionName}.md`);
3905
3906
  const ctxContent = [
@@ -3908,7 +3909,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3908
3909
  `Your parent exe session is ${exeSession}.`,
3909
3910
  `Your employees (if any) use the -${exeSession} suffix (e.g., tom-${exeSession}).`
3910
3911
  ].join("\n");
3911
- writeFileSync5(ctxFile, ctxContent);
3912
+ writeFileSync6(ctxFile, ctxContent);
3912
3913
  sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
3913
3914
  } catch {
3914
3915
  }
@@ -3947,7 +3948,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
3947
3948
  try {
3948
3949
  const mySession = getMySession();
3949
3950
  const dispatchInfo = path13.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
3950
- writeFileSync5(dispatchInfo, JSON.stringify({
3951
+ writeFileSync6(dispatchInfo, JSON.stringify({
3951
3952
  dispatchedBy: mySession,
3952
3953
  rootExe: exeSession,
3953
3954
  provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : "anthropic",
@@ -4010,13 +4011,13 @@ var init_tmux_routing = __esm({
4010
4011
  init_provider_table();
4011
4012
  init_intercom_queue();
4012
4013
  init_plan_limits();
4013
- SPAWN_LOCK_DIR = path13.join(os5.homedir(), ".exe-os", "spawn-locks");
4014
- SESSION_CACHE = path13.join(os5.homedir(), ".exe-os", "session-cache");
4014
+ SPAWN_LOCK_DIR = path13.join(os6.homedir(), ".exe-os", "spawn-locks");
4015
+ SESSION_CACHE = path13.join(os6.homedir(), ".exe-os", "session-cache");
4015
4016
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
4016
4017
  VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
4017
4018
  VERIFY_PANE_LINES = 200;
4018
4019
  INTERCOM_DEBOUNCE_MS = 3e4;
4019
- INTERCOM_LOG2 = path13.join(os5.homedir(), ".exe-os", "intercom.log");
4020
+ INTERCOM_LOG2 = path13.join(os6.homedir(), ".exe-os", "intercom.log");
4020
4021
  DEBOUNCE_FILE = path13.join(SESSION_CACHE, "intercom-debounce.json");
4021
4022
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
4022
4023
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
@@ -4060,9 +4061,9 @@ var init_memory = __esm({
4060
4061
  import { readFile as readFile4, writeFile as writeFile5, unlink, mkdir as mkdir4, chmod as chmod2 } from "fs/promises";
4061
4062
  import { existsSync as existsSync11 } from "fs";
4062
4063
  import path14 from "path";
4063
- import os6 from "os";
4064
+ import os7 from "os";
4064
4065
  function getKeyDir() {
4065
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path14.join(os6.homedir(), ".exe-os");
4066
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path14.join(os7.homedir(), ".exe-os");
4066
4067
  }
4067
4068
  function getKeyPath() {
4068
4069
  return path14.join(getKeyDir(), "master.key");
@@ -5018,7 +5019,7 @@ var init_store = __esm({
5018
5019
  // src/bin/scan-tasks.ts
5019
5020
  import { existsSync as existsSync13, readFileSync as readFileSync10 } from "fs";
5020
5021
  import path16 from "path";
5021
- import os7 from "os";
5022
+ import os8 from "os";
5022
5023
 
5023
5024
  // src/lib/is-main.ts
5024
5025
  import { realpathSync } from "fs";
@@ -5038,7 +5039,7 @@ function isMainModule(importMetaUrl) {
5038
5039
  init_task_scope();
5039
5040
  function checkMcpHealth() {
5040
5041
  try {
5041
- const claudeJson = path16.join(os7.homedir(), ".claude.json");
5042
+ const claudeJson = path16.join(os8.homedir(), ".claude.json");
5042
5043
  if (!existsSync13(claudeJson)) {
5043
5044
  process.stderr.write(
5044
5045
  "\u26A0\uFE0F MCP config missing (~/.claude.json not found) \u2014 close_task won't work. Run /exe-setup\n"
package/dist/bin/setup.js CHANGED
@@ -1298,14 +1298,16 @@ __export(employees_exports, {
1298
1298
  isMultiInstance: () => isMultiInstance,
1299
1299
  loadEmployees: () => loadEmployees,
1300
1300
  loadEmployeesSync: () => loadEmployeesSync,
1301
+ normalizeRosterCase: () => normalizeRosterCase,
1301
1302
  registerBinSymlinks: () => registerBinSymlinks,
1302
1303
  saveEmployees: () => saveEmployees,
1303
1304
  validateEmployeeName: () => validateEmployeeName
1304
1305
  });
1305
1306
  import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir4 } from "fs/promises";
1306
- import { existsSync as existsSync6, symlinkSync, readlinkSync, readFileSync as readFileSync4 } from "fs";
1307
+ import { existsSync as existsSync6, symlinkSync, readlinkSync, readFileSync as readFileSync4, renameSync as renameSync3, unlinkSync as unlinkSync3, writeFileSync as writeFileSync2 } from "fs";
1307
1308
  import { execSync } from "child_process";
1308
1309
  import path6 from "path";
1310
+ import os3 from "os";
1309
1311
  function validateEmployeeName(name) {
1310
1312
  if (!name) {
1311
1313
  return { valid: false, error: "Name is required" };
@@ -1373,6 +1375,36 @@ function addEmployee(employees, employee) {
1373
1375
  }
1374
1376
  return [...employees, normalized];
1375
1377
  }
1378
+ async function normalizeRosterCase(rosterPath) {
1379
+ const employees = await loadEmployees(rosterPath);
1380
+ let changed = false;
1381
+ for (const emp of employees) {
1382
+ if (emp.name !== emp.name.toLowerCase()) {
1383
+ const oldName = emp.name;
1384
+ emp.name = emp.name.toLowerCase();
1385
+ changed = true;
1386
+ try {
1387
+ const identityDir = path6.join(os3.homedir(), ".exe-os", "identity");
1388
+ const oldPath = path6.join(identityDir, `${oldName}.md`);
1389
+ const newPath = path6.join(identityDir, `${emp.name}.md`);
1390
+ if (existsSync6(oldPath) && !existsSync6(newPath)) {
1391
+ renameSync3(oldPath, newPath);
1392
+ } else if (existsSync6(oldPath) && oldPath !== newPath) {
1393
+ const content = readFileSync4(oldPath, "utf-8");
1394
+ writeFileSync2(newPath, content, "utf-8");
1395
+ if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
1396
+ unlinkSync3(oldPath);
1397
+ }
1398
+ }
1399
+ } catch {
1400
+ }
1401
+ }
1402
+ }
1403
+ if (changed) {
1404
+ await saveEmployees(employees, rosterPath);
1405
+ }
1406
+ return changed;
1407
+ }
1376
1408
  function findExeBin() {
1377
1409
  try {
1378
1410
  return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
@@ -1451,7 +1483,7 @@ __export(cloud_sync_exports, {
1451
1483
  mergeRosterFromRemote: () => mergeRosterFromRemote,
1452
1484
  recordRosterDeletion: () => recordRosterDeletion
1453
1485
  });
1454
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, existsSync as existsSync7, readdirSync, mkdirSync as mkdirSync2, appendFileSync, unlinkSync as unlinkSync3, openSync as openSync2, closeSync as closeSync2 } from "fs";
1486
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync7, readdirSync, mkdirSync as mkdirSync2, appendFileSync, unlinkSync as unlinkSync4, openSync as openSync2, closeSync as closeSync2 } from "fs";
1455
1487
  import crypto2 from "crypto";
1456
1488
  import path7 from "path";
1457
1489
  import { homedir } from "os";
@@ -1470,7 +1502,7 @@ async function withRosterLock(fn) {
1470
1502
  try {
1471
1503
  const fd = openSync2(ROSTER_LOCK_PATH, "wx");
1472
1504
  closeSync2(fd);
1473
- writeFileSync2(ROSTER_LOCK_PATH, String(Date.now()));
1505
+ writeFileSync3(ROSTER_LOCK_PATH, String(Date.now()));
1474
1506
  } catch (err) {
1475
1507
  if (err.code === "EEXIST") {
1476
1508
  try {
@@ -1478,10 +1510,10 @@ async function withRosterLock(fn) {
1478
1510
  if (Date.now() - ts < LOCK_STALE_MS) {
1479
1511
  throw new Error("Roster merge already in progress \u2014 another sync is running");
1480
1512
  }
1481
- unlinkSync3(ROSTER_LOCK_PATH);
1513
+ unlinkSync4(ROSTER_LOCK_PATH);
1482
1514
  const fd = openSync2(ROSTER_LOCK_PATH, "wx");
1483
1515
  closeSync2(fd);
1484
- writeFileSync2(ROSTER_LOCK_PATH, String(Date.now()));
1516
+ writeFileSync3(ROSTER_LOCK_PATH, String(Date.now()));
1485
1517
  } catch (retryErr) {
1486
1518
  if (retryErr instanceof Error && retryErr.message.includes("already in progress")) throw retryErr;
1487
1519
  throw new Error("Roster merge already in progress \u2014 another sync is running");
@@ -1494,7 +1526,7 @@ async function withRosterLock(fn) {
1494
1526
  return await fn();
1495
1527
  } finally {
1496
1528
  try {
1497
- unlinkSync3(ROSTER_LOCK_PATH);
1529
+ unlinkSync4(ROSTER_LOCK_PATH);
1498
1530
  } catch {
1499
1531
  }
1500
1532
  }
@@ -1794,13 +1826,13 @@ function recordRosterDeletion(name) {
1794
1826
  } catch {
1795
1827
  }
1796
1828
  if (!deletions.includes(name)) deletions.push(name);
1797
- writeFileSync2(ROSTER_DELETIONS_PATH, JSON.stringify(deletions));
1829
+ writeFileSync3(ROSTER_DELETIONS_PATH, JSON.stringify(deletions));
1798
1830
  }
1799
1831
  function consumeRosterDeletions() {
1800
1832
  try {
1801
1833
  if (!existsSync7(ROSTER_DELETIONS_PATH)) return [];
1802
1834
  const deletions = JSON.parse(readFileSync5(ROSTER_DELETIONS_PATH, "utf-8"));
1803
- writeFileSync2(ROSTER_DELETIONS_PATH, "[]");
1835
+ writeFileSync3(ROSTER_DELETIONS_PATH, "[]");
1804
1836
  return deletions;
1805
1837
  } catch {
1806
1838
  return [];
@@ -1916,7 +1948,7 @@ function mergeConfig(remoteConfig, configPath) {
1916
1948
  const merged = { ...remoteConfig, ...local };
1917
1949
  const dir = path7.dirname(cfgPath);
1918
1950
  if (!existsSync7(dir)) mkdirSync2(dir, { recursive: true });
1919
- writeFileSync2(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
1951
+ writeFileSync3(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
1920
1952
  }
1921
1953
  async function mergeRosterFromRemote(remote, paths) {
1922
1954
  return withRosterLock(async () => {
@@ -1936,7 +1968,11 @@ async function mergeRosterFromRemote(remote, paths) {
1936
1968
  } catch {
1937
1969
  }
1938
1970
  }
1939
- const remoteIdentity = remote.identities[`${remoteEmp.name}.md`];
1971
+ const lookupKey = `${remoteEmp.name}.md`;
1972
+ const matchedKey = Object.keys(remote.identities).find(
1973
+ (k) => k.toLowerCase() === lookupKey.toLowerCase()
1974
+ ) ?? lookupKey;
1975
+ const remoteIdentity = remote.identities[matchedKey];
1940
1976
  if (remoteIdentity) {
1941
1977
  if (!existsSync7(identityDir)) mkdirSync2(identityDir, { recursive: true });
1942
1978
  const idPath = path7.join(identityDir, `${remoteEmp.name}.md`);
@@ -1946,7 +1982,7 @@ async function mergeRosterFromRemote(remote, paths) {
1946
1982
  } catch {
1947
1983
  }
1948
1984
  if (localIdentity !== remoteIdentity) {
1949
- writeFileSync2(idPath, remoteIdentity, "utf-8");
1985
+ writeFileSync3(idPath, remoteIdentity, "utf-8");
1950
1986
  identitiesUpdated++;
1951
1987
  }
1952
1988
  }
@@ -3170,7 +3206,7 @@ __export(identity_exports, {
3170
3206
  listIdentities: () => listIdentities,
3171
3207
  updateIdentity: () => updateIdentity
3172
3208
  });
3173
- import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "fs";
3209
+ import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
3174
3210
  import { readdirSync as readdirSync2 } from "fs";
3175
3211
  import path8 from "path";
3176
3212
  import { createHash as createHash2 } from "crypto";
@@ -3236,7 +3272,7 @@ async function updateIdentity(agentId, content, updatedBy) {
3236
3272
  ensureDir();
3237
3273
  const filePath = identityPath(agentId);
3238
3274
  const hash = contentHash(content);
3239
- writeFileSync3(filePath, content, "utf-8");
3275
+ writeFileSync4(filePath, content, "utf-8");
3240
3276
  try {
3241
3277
  const client = getClient();
3242
3278
  await client.execute({
@@ -3834,11 +3870,11 @@ __export(session_wrappers_exports, {
3834
3870
  import {
3835
3871
  existsSync as existsSync9,
3836
3872
  readFileSync as readFileSync7,
3837
- writeFileSync as writeFileSync4,
3873
+ writeFileSync as writeFileSync5,
3838
3874
  mkdirSync as mkdirSync4,
3839
3875
  chmodSync,
3840
3876
  readdirSync as readdirSync3,
3841
- unlinkSync as unlinkSync4
3877
+ unlinkSync as unlinkSync5
3842
3878
  } from "fs";
3843
3879
  import path9 from "path";
3844
3880
  import { homedir as homedir2 } from "os";
@@ -3854,7 +3890,7 @@ function generateSessionWrappers(packageRoot, homeDir) {
3854
3890
  ];
3855
3891
  for (const src of candidates) {
3856
3892
  if (existsSync9(src)) {
3857
- writeFileSync4(exeStartDst, readFileSync7(src));
3893
+ writeFileSync5(exeStartDst, readFileSync7(src));
3858
3894
  chmodSync(exeStartDst, 493);
3859
3895
  break;
3860
3896
  }
@@ -3875,7 +3911,7 @@ function generateSessionWrappers(packageRoot, homeDir) {
3875
3911
  try {
3876
3912
  const content = readFileSync7(fPath, "utf8");
3877
3913
  if (content.includes("exe-start")) {
3878
- unlinkSync4(fPath);
3914
+ unlinkSync5(fPath);
3879
3915
  }
3880
3916
  } catch {
3881
3917
  }
@@ -3889,7 +3925,7 @@ exec "${exeStartDst}" "$0" "$@"
3889
3925
  for (const emp of employees) {
3890
3926
  for (let n = 1; n <= MAX_N; n++) {
3891
3927
  const wrapperPath = path9.join(binDir, `${emp.name}${n}`);
3892
- writeFileSync4(wrapperPath, wrapperContent);
3928
+ writeFileSync5(wrapperPath, wrapperContent);
3893
3929
  chmodSync(wrapperPath, 493);
3894
3930
  created++;
3895
3931
  }
@@ -3925,7 +3961,7 @@ export PATH="${binDir}:$PATH"
3925
3961
  if (content.includes(".exe-os/bin")) {
3926
3962
  return false;
3927
3963
  }
3928
- writeFileSync4(profilePath, content + exportLine);
3964
+ writeFileSync5(profilePath, content + exportLine);
3929
3965
  return true;
3930
3966
  } catch {
3931
3967
  continue;
@@ -3945,8 +3981,8 @@ var init_session_wrappers = __esm({
3945
3981
  init_config();
3946
3982
  init_keychain();
3947
3983
  import crypto3 from "crypto";
3948
- import { existsSync as existsSync10, mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync5, unlinkSync as unlinkSync5 } from "fs";
3949
- import os3 from "os";
3984
+ import { existsSync as existsSync10, mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync6, unlinkSync as unlinkSync6 } from "fs";
3985
+ import os4 from "os";
3950
3986
  import path10 from "path";
3951
3987
  import { createInterface } from "readline";
3952
3988
 
@@ -4054,7 +4090,7 @@ function findPackageRoot2() {
4054
4090
  }
4055
4091
  return null;
4056
4092
  }
4057
- var SETUP_STATE_PATH = path10.join(os3.homedir(), ".exe-os", "setup-state.json");
4093
+ var SETUP_STATE_PATH = path10.join(os4.homedir(), ".exe-os", "setup-state.json");
4058
4094
  function loadSetupState() {
4059
4095
  try {
4060
4096
  return JSON.parse(readFileSync8(SETUP_STATE_PATH, "utf8"));
@@ -4064,11 +4100,11 @@ function loadSetupState() {
4064
4100
  }
4065
4101
  function saveSetupState(state) {
4066
4102
  mkdirSync5(path10.dirname(SETUP_STATE_PATH), { recursive: true });
4067
- writeFileSync5(SETUP_STATE_PATH, JSON.stringify(state, null, 2));
4103
+ writeFileSync6(SETUP_STATE_PATH, JSON.stringify(state, null, 2));
4068
4104
  }
4069
4105
  function clearSetupState() {
4070
4106
  try {
4071
- unlinkSync5(SETUP_STATE_PATH);
4107
+ unlinkSync6(SETUP_STATE_PATH);
4072
4108
  } catch {
4073
4109
  }
4074
4110
  }
@@ -4274,7 +4310,7 @@ async function runSetupWizard(opts = {}) {
4274
4310
  await saveConfig(config);
4275
4311
  log("");
4276
4312
  try {
4277
- const claudeJsonPath = path10.join(os3.homedir(), ".claude.json");
4313
+ const claudeJsonPath = path10.join(os4.homedir(), ".claude.json");
4278
4314
  let claudeJson = {};
4279
4315
  try {
4280
4316
  claudeJson = JSON.parse(readFileSync8(claudeJsonPath, "utf8"));
@@ -4282,11 +4318,11 @@ async function runSetupWizard(opts = {}) {
4282
4318
  }
4283
4319
  if (!claudeJson.projects) claudeJson.projects = {};
4284
4320
  const projects = claudeJson.projects;
4285
- for (const dir of [process.cwd(), os3.homedir()]) {
4321
+ for (const dir of [process.cwd(), os4.homedir()]) {
4286
4322
  if (!projects[dir]) projects[dir] = {};
4287
4323
  projects[dir].hasTrustDialogAccepted = true;
4288
4324
  }
4289
- writeFileSync5(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
4325
+ writeFileSync6(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
4290
4326
  } catch {
4291
4327
  }
4292
4328
  state.completedSteps.push(5);
@@ -4322,6 +4358,54 @@ async function runSetupWizard(opts = {}) {
4322
4358
  log(`Team: ${teamList}`);
4323
4359
  createdEmployees.push(...roster.map((e) => ({ name: e.name, role: e.role })));
4324
4360
  }
4361
+ let missingIdentities = [];
4362
+ for (const emp of roster) {
4363
+ const idPath = identityPath2(emp.name);
4364
+ if (!existsSync10(idPath)) {
4365
+ missingIdentities.push(emp.name);
4366
+ }
4367
+ }
4368
+ if (missingIdentities.length > 0) {
4369
+ log("");
4370
+ log(`\u26A0 Identity files missing for: ${missingIdentities.join(", ")}`);
4371
+ log(" Your main device needs to push them to cloud first.");
4372
+ log("");
4373
+ log(" On your MAIN device, run: exe-os cloud sync");
4374
+ log(" Then come back here and press Enter to retry the pull.");
4375
+ log("");
4376
+ await ask(rl, "Press Enter after syncing from your main device... ");
4377
+ try {
4378
+ const { initSyncCrypto: retryInitCrypto } = await Promise.resolve().then(() => (init_crypto(), crypto_exports));
4379
+ const { cloudPullRoster: retryPull } = await Promise.resolve().then(() => (init_cloud_sync(), cloud_sync_exports));
4380
+ const retryKey = await getMasterKey();
4381
+ if (retryKey) {
4382
+ retryInitCrypto(retryKey);
4383
+ const retryConfig = await loadConfig();
4384
+ if (retryConfig.cloud?.apiKey && retryConfig.cloud?.endpoint) {
4385
+ const retryResult = await retryPull({
4386
+ apiKey: retryConfig.cloud.apiKey,
4387
+ endpoint: retryConfig.cloud.endpoint
4388
+ });
4389
+ if (retryResult.identitiesUpdated && retryResult.identitiesUpdated > 0) {
4390
+ log(`Pulled ${retryResult.identitiesUpdated} identity file(s) from cloud.`);
4391
+ }
4392
+ }
4393
+ }
4394
+ missingIdentities = [];
4395
+ for (const emp of roster) {
4396
+ if (!existsSync10(identityPath2(emp.name))) {
4397
+ missingIdentities.push(emp.name);
4398
+ }
4399
+ }
4400
+ if (missingIdentities.length > 0) {
4401
+ log(`\u26A0 Still missing: ${missingIdentities.join(", ")}. Identities will sync on next daemon cycle.`);
4402
+ } else {
4403
+ log("All identity files synced successfully.");
4404
+ }
4405
+ } catch {
4406
+ log("Retry pull failed \u2014 identities will sync when the daemon starts.");
4407
+ }
4408
+ }
4325
4409
  for (const emp of roster) {
4326
4410
  registerBinSymlinks2(emp.name);
4327
4411
  }
@@ -4367,7 +4451,7 @@ async function runSetupWizard(opts = {}) {
4367
4451
  const cooIdPath = identityPath2(cooName);
4368
4452
  mkdirSync5(path10.dirname(cooIdPath), { recursive: true });
4369
4453
  const replaced = cooIdentityContent.replace(/agent_id:\s*exe/g, `agent_id: ${cooName}`).replace(/\$\{agent_id\}/g, cooName);
4370
- writeFileSync5(cooIdPath, replaced, "utf-8");
4454
+ writeFileSync6(cooIdPath, replaced, "utf-8");
4371
4455
  }
4372
4456
  registerBinSymlinks2(cooName);
4373
4457
  createdEmployees.push({ name: cooName, role: "COO" });
@@ -4468,7 +4552,7 @@ async function runSetupWizard(opts = {}) {
4468
4552
  const ctoIdPath = identityPath2(ctoName);
4469
4553
  mkdirSync5(path10.dirname(ctoIdPath), { recursive: true });
4470
4554
  const replaced = ctoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${ctoName}`).replace(/\$\{agent_id\}/g, ctoName);
4471
- writeFileSync5(ctoIdPath, replaced, "utf-8");
4555
+ writeFileSync6(ctoIdPath, replaced, "utf-8");
4472
4556
  }
4473
4557
  registerBinSymlinks2(ctoName);
4474
4558
  createdEmployees.push({ name: ctoName, role: "CTO" });
@@ -4496,7 +4580,7 @@ async function runSetupWizard(opts = {}) {
4496
4580
  const cmoIdPath = identityPath2(cmoName);
4497
4581
  mkdirSync5(path10.dirname(cmoIdPath), { recursive: true });
4498
4582
  const replaced = cmoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${cmoName}`).replace(/\$\{agent_id\}/g, cmoName);
4499
- writeFileSync5(cmoIdPath, replaced, "utf-8");
4583
+ writeFileSync6(cmoIdPath, replaced, "utf-8");
4500
4584
  }
4501
4585
  registerBinSymlinks2(cmoName);
4502
4586
  createdEmployees.push({ name: cmoName, role: "CMO" });