@askexenow/exe-os 0.8.32 → 0.8.36

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 (87) hide show
  1. package/dist/bin/backfill-conversations.js +332 -348
  2. package/dist/bin/backfill-responses.js +72 -12
  3. package/dist/bin/backfill-vectors.js +72 -12
  4. package/dist/bin/cleanup-stale-review-tasks.js +63 -3
  5. package/dist/bin/cli.js +1518 -1122
  6. package/dist/bin/exe-agent.js +4 -4
  7. package/dist/bin/exe-assign.js +80 -18
  8. package/dist/bin/exe-boot.js +408 -89
  9. package/dist/bin/exe-call.js +83 -24
  10. package/dist/bin/exe-dispatch.js +18 -10
  11. package/dist/bin/exe-doctor.js +63 -3
  12. package/dist/bin/exe-export-behaviors.js +64 -3
  13. package/dist/bin/exe-forget.js +69 -4
  14. package/dist/bin/exe-gateway.js +121 -36
  15. package/dist/bin/exe-heartbeat.js +77 -13
  16. package/dist/bin/exe-kill.js +64 -3
  17. package/dist/bin/exe-launch-agent.js +162 -35
  18. package/dist/bin/exe-link.js +946 -0
  19. package/dist/bin/exe-new-employee.js +121 -36
  20. package/dist/bin/exe-pending-messages.js +72 -7
  21. package/dist/bin/exe-pending-notifications.js +63 -3
  22. package/dist/bin/exe-pending-reviews.js +75 -10
  23. package/dist/bin/exe-rename.js +1287 -0
  24. package/dist/bin/exe-review.js +64 -4
  25. package/dist/bin/exe-search.js +79 -13
  26. package/dist/bin/exe-session-cleanup.js +91 -26
  27. package/dist/bin/exe-status.js +64 -4
  28. package/dist/bin/exe-team.js +64 -4
  29. package/dist/bin/git-sweep.js +71 -4
  30. package/dist/bin/graph-backfill.js +64 -3
  31. package/dist/bin/graph-export.js +64 -3
  32. package/dist/bin/install.js +3 -3
  33. package/dist/bin/scan-tasks.js +71 -4
  34. package/dist/bin/setup.js +156 -38
  35. package/dist/bin/shard-migrate.js +64 -3
  36. package/dist/bin/wiki-sync.js +64 -3
  37. package/dist/gateway/index.js +122 -37
  38. package/dist/hooks/bug-report-worker.js +209 -23
  39. package/dist/hooks/commit-complete.js +71 -4
  40. package/dist/hooks/error-recall.js +79 -13
  41. package/dist/hooks/ingest-worker.js +129 -43
  42. package/dist/hooks/instructions-loaded.js +71 -4
  43. package/dist/hooks/notification.js +71 -4
  44. package/dist/hooks/post-compact.js +71 -4
  45. package/dist/hooks/pre-compact.js +71 -4
  46. package/dist/hooks/pre-tool-use.js +413 -194
  47. package/dist/hooks/prompt-ingest-worker.js +82 -22
  48. package/dist/hooks/prompt-submit.js +103 -37
  49. package/dist/hooks/response-ingest-worker.js +87 -22
  50. package/dist/hooks/session-end.js +71 -4
  51. package/dist/hooks/session-start.js +79 -13
  52. package/dist/hooks/stop.js +71 -4
  53. package/dist/hooks/subagent-stop.js +71 -4
  54. package/dist/hooks/summary-worker.js +303 -50
  55. package/dist/index.js +134 -46
  56. package/dist/lib/cloud-sync.js +209 -15
  57. package/dist/lib/consolidation.js +4 -4
  58. package/dist/lib/database.js +64 -2
  59. package/dist/lib/device-registry.js +70 -3
  60. package/dist/lib/employee-templates.js +48 -22
  61. package/dist/lib/employees.js +34 -1
  62. package/dist/lib/exe-daemon.js +136 -53
  63. package/dist/lib/hybrid-search.js +79 -13
  64. package/dist/lib/identity-templates.js +57 -6
  65. package/dist/lib/identity.js +3 -3
  66. package/dist/lib/messaging.js +22 -14
  67. package/dist/lib/reminders.js +3 -3
  68. package/dist/lib/schedules.js +63 -3
  69. package/dist/lib/skill-learning.js +3 -3
  70. package/dist/lib/status-brief.js +63 -5
  71. package/dist/lib/store.js +64 -3
  72. package/dist/lib/task-router.js +4 -2
  73. package/dist/lib/tasks.js +48 -21
  74. package/dist/lib/tmux-routing.js +47 -20
  75. package/dist/mcp/server.js +727 -58
  76. package/dist/mcp/tools/complete-reminder.js +3 -3
  77. package/dist/mcp/tools/create-reminder.js +3 -3
  78. package/dist/mcp/tools/create-task.js +151 -24
  79. package/dist/mcp/tools/deactivate-behavior.js +3 -3
  80. package/dist/mcp/tools/list-reminders.js +3 -3
  81. package/dist/mcp/tools/list-tasks.js +17 -8
  82. package/dist/mcp/tools/send-message.js +24 -16
  83. package/dist/mcp/tools/update-task.js +25 -16
  84. package/dist/runtime/index.js +112 -24
  85. package/dist/tui/App.js +139 -36
  86. package/package.json +6 -2
  87. package/src/commands/exe/rename.md +12 -0
@@ -271,7 +271,7 @@ var init_config = __esm({
271
271
 
272
272
  // src/lib/employees.ts
273
273
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
274
- import { existsSync as existsSync2, symlinkSync, readlinkSync } from "fs";
274
+ import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2 } from "fs";
275
275
  import { execSync } from "child_process";
276
276
  import path2 from "path";
277
277
  async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
@@ -289,12 +289,65 @@ async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
289
289
  await mkdir2(path2.dirname(employeesPath), { recursive: true });
290
290
  await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
291
291
  }
292
- var EMPLOYEES_PATH;
292
+ function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
293
+ if (!existsSync2(employeesPath)) return [];
294
+ try {
295
+ return JSON.parse(readFileSync2(employeesPath, "utf-8"));
296
+ } catch {
297
+ return [];
298
+ }
299
+ }
300
+ function getEmployee(employees, name) {
301
+ return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
302
+ }
303
+ function isMultiInstance(agentName, employees) {
304
+ const roster = employees ?? loadEmployeesSync();
305
+ const emp = getEmployee(roster, agentName);
306
+ if (!emp) return false;
307
+ return MULTI_INSTANCE_ROLES.has(emp.role.toLowerCase());
308
+ }
309
+ function registerBinSymlinks(name) {
310
+ const created = [];
311
+ const skipped = [];
312
+ const errors = [];
313
+ let exeBinPath;
314
+ try {
315
+ exeBinPath = execSync("which exe", { encoding: "utf-8" }).trim();
316
+ } catch {
317
+ errors.push("Could not find 'exe' in PATH");
318
+ return { created, skipped, errors };
319
+ }
320
+ const binDir = path2.dirname(exeBinPath);
321
+ let target;
322
+ try {
323
+ target = readlinkSync(exeBinPath);
324
+ } catch {
325
+ errors.push("Could not read 'exe' symlink");
326
+ return { created, skipped, errors };
327
+ }
328
+ for (const suffix of ["", "-opencode"]) {
329
+ const linkName = `${name}${suffix}`;
330
+ const linkPath = path2.join(binDir, linkName);
331
+ if (existsSync2(linkPath)) {
332
+ skipped.push(linkName);
333
+ continue;
334
+ }
335
+ try {
336
+ symlinkSync(target, linkPath);
337
+ created.push(linkName);
338
+ } catch (err) {
339
+ errors.push(`${linkName}: ${err instanceof Error ? err.message : String(err)}`);
340
+ }
341
+ }
342
+ return { created, skipped, errors };
343
+ }
344
+ var EMPLOYEES_PATH, MULTI_INSTANCE_ROLES;
293
345
  var init_employees = __esm({
294
346
  "src/lib/employees.ts"() {
295
347
  "use strict";
296
348
  init_config();
297
349
  EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
350
+ MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
298
351
  }
299
352
  });
300
353
 
@@ -305,12 +358,68 @@ var init_memory = __esm({
305
358
  }
306
359
  });
307
360
 
361
+ // src/lib/db-retry.ts
362
+ function isBusyError(err) {
363
+ if (err instanceof Error) {
364
+ const msg = err.message.toLowerCase();
365
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
366
+ }
367
+ return false;
368
+ }
369
+ function delay(ms) {
370
+ return new Promise((resolve) => setTimeout(resolve, ms));
371
+ }
372
+ async function retryOnBusy(fn, label) {
373
+ let lastError;
374
+ for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
375
+ try {
376
+ return await fn();
377
+ } catch (err) {
378
+ lastError = err;
379
+ if (!isBusyError(err) || attempt === MAX_RETRIES) {
380
+ throw err;
381
+ }
382
+ const backoff = BASE_DELAY_MS * Math.pow(2, attempt);
383
+ const jitter = Math.floor(Math.random() * MAX_JITTER_MS);
384
+ process.stderr.write(
385
+ `[exe-os] SQLITE_BUSY ${label} retry ${attempt + 1}/${MAX_RETRIES} \u2014 waiting ${backoff + jitter}ms
386
+ `
387
+ );
388
+ await delay(backoff + jitter);
389
+ }
390
+ }
391
+ throw lastError;
392
+ }
393
+ function wrapWithRetry(client) {
394
+ return new Proxy(client, {
395
+ get(target, prop, receiver) {
396
+ if (prop === "execute") {
397
+ return (sql) => retryOnBusy(() => target.execute(sql), "execute");
398
+ }
399
+ if (prop === "batch") {
400
+ return (stmts) => retryOnBusy(() => target.batch(stmts), "batch");
401
+ }
402
+ return Reflect.get(target, prop, receiver);
403
+ }
404
+ });
405
+ }
406
+ var MAX_RETRIES, BASE_DELAY_MS, MAX_JITTER_MS;
407
+ var init_db_retry = __esm({
408
+ "src/lib/db-retry.ts"() {
409
+ "use strict";
410
+ MAX_RETRIES = 3;
411
+ BASE_DELAY_MS = 200;
412
+ MAX_JITTER_MS = 300;
413
+ }
414
+ });
415
+
308
416
  // src/lib/database.ts
309
417
  import { createClient } from "@libsql/client";
310
418
  async function initDatabase(config) {
311
419
  if (_client) {
312
420
  _client.close();
313
421
  _client = null;
422
+ _resilientClient = null;
314
423
  }
315
424
  const opts = {
316
425
  url: `file:${config.dbPath}`
@@ -319,20 +428,27 @@ async function initDatabase(config) {
319
428
  opts.encryptionKey = config.encryptionKey;
320
429
  }
321
430
  _client = createClient(opts);
431
+ _resilientClient = wrapWithRetry(_client);
322
432
  }
323
433
  function isInitialized() {
324
434
  return _client !== null;
325
435
  }
326
436
  function getClient() {
437
+ if (!_resilientClient) {
438
+ throw new Error("Database client not initialized. Call initDatabase() first.");
439
+ }
440
+ return _resilientClient;
441
+ }
442
+ function getRawClient() {
327
443
  if (!_client) {
328
444
  throw new Error("Database client not initialized. Call initDatabase() first.");
329
445
  }
330
446
  return _client;
331
447
  }
332
448
  async function ensureSchema() {
333
- const client = getClient();
449
+ const client = getRawClient();
334
450
  await client.execute("PRAGMA journal_mode = WAL");
335
- await client.execute("PRAGMA busy_timeout = 5000");
451
+ await client.execute("PRAGMA busy_timeout = 30000");
336
452
  try {
337
453
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
338
454
  } catch {
@@ -1121,11 +1237,13 @@ async function ensureSchema() {
1121
1237
  }
1122
1238
  }
1123
1239
  }
1124
- var _client, initTurso;
1240
+ var _client, _resilientClient, initTurso;
1125
1241
  var init_database = __esm({
1126
1242
  "src/lib/database.ts"() {
1127
1243
  "use strict";
1244
+ init_db_retry();
1128
1245
  _client = null;
1246
+ _resilientClient = null;
1129
1247
  initTurso = initDatabase;
1130
1248
  }
1131
1249
  });
@@ -1329,12 +1447,12 @@ function shardExists(projectName) {
1329
1447
  }
1330
1448
  function listShards() {
1331
1449
  if (!existsSync4(SHARDS_DIR)) return [];
1332
- const { readdirSync: readdirSync6 } = __require("fs");
1333
- return readdirSync6(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
1450
+ const { readdirSync: readdirSync7 } = __require("fs");
1451
+ return readdirSync7(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
1334
1452
  }
1335
1453
  async function ensureShardSchema(client) {
1336
1454
  await client.execute("PRAGMA journal_mode = WAL");
1337
- await client.execute("PRAGMA busy_timeout = 5000");
1455
+ await client.execute("PRAGMA busy_timeout = 30000");
1338
1456
  try {
1339
1457
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
1340
1458
  } catch {
@@ -1579,7 +1697,7 @@ import crypto2 from "crypto";
1579
1697
  import path5 from "path";
1580
1698
  import os2 from "os";
1581
1699
  import {
1582
- readFileSync as readFileSync2,
1700
+ readFileSync as readFileSync3,
1583
1701
  readdirSync,
1584
1702
  unlinkSync,
1585
1703
  existsSync as existsSync5,
@@ -1706,7 +1824,7 @@ async function migrateJsonNotifications() {
1706
1824
  for (const file of files) {
1707
1825
  try {
1708
1826
  const filePath = path5.join(notifDir, file);
1709
- const data = JSON.parse(readFileSync2(filePath, "utf8"));
1827
+ const data = JSON.parse(readFileSync3(filePath, "utf8"));
1710
1828
  await client.execute({
1711
1829
  sql: `INSERT OR IGNORE INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, read, created_at)
1712
1830
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
@@ -1789,7 +1907,7 @@ __export(session_registry_exports, {
1789
1907
  pruneStaleSessions: () => pruneStaleSessions,
1790
1908
  registerSession: () => registerSession
1791
1909
  });
1792
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3, existsSync as existsSync6 } from "fs";
1910
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3, existsSync as existsSync6 } from "fs";
1793
1911
  import { execSync as execSync4 } from "child_process";
1794
1912
  import path7 from "path";
1795
1913
  import os3 from "os";
@@ -1809,7 +1927,7 @@ function registerSession(entry) {
1809
1927
  }
1810
1928
  function listSessions() {
1811
1929
  try {
1812
- const raw = readFileSync4(REGISTRY_PATH, "utf8");
1930
+ const raw = readFileSync5(REGISTRY_PATH, "utf8");
1813
1931
  return JSON.parse(raw);
1814
1932
  } catch {
1815
1933
  return [];
@@ -2023,7 +2141,7 @@ var init_provider_table = __esm({
2023
2141
  });
2024
2142
 
2025
2143
  // src/lib/intercom-queue.ts
2026
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, renameSync as renameSync2, existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
2144
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, renameSync as renameSync2, existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
2027
2145
  import path8 from "path";
2028
2146
  import os4 from "os";
2029
2147
  function ensureDir() {
@@ -2033,7 +2151,7 @@ function ensureDir() {
2033
2151
  function readQueue() {
2034
2152
  try {
2035
2153
  if (!existsSync7(QUEUE_PATH)) return [];
2036
- return JSON.parse(readFileSync5(QUEUE_PATH, "utf8"));
2154
+ return JSON.parse(readFileSync6(QUEUE_PATH, "utf8"));
2037
2155
  } catch {
2038
2156
  return [];
2039
2157
  }
@@ -2086,7 +2204,7 @@ __export(license_exports, {
2086
2204
  saveLicense: () => saveLicense,
2087
2205
  validateLicense: () => validateLicense
2088
2206
  });
2089
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, existsSync as existsSync8, mkdirSync as mkdirSync5 } from "fs";
2207
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, existsSync as existsSync8, mkdirSync as mkdirSync5 } from "fs";
2090
2208
  import { randomUUID } from "crypto";
2091
2209
  import path9 from "path";
2092
2210
  import { jwtVerify, importSPKI } from "jose";
@@ -2094,14 +2212,14 @@ function loadDeviceId() {
2094
2212
  const deviceJsonPath = path9.join(EXE_AI_DIR, "device.json");
2095
2213
  try {
2096
2214
  if (existsSync8(deviceJsonPath)) {
2097
- const data = JSON.parse(readFileSync6(deviceJsonPath, "utf8"));
2215
+ const data = JSON.parse(readFileSync7(deviceJsonPath, "utf8"));
2098
2216
  if (data.deviceId) return data.deviceId;
2099
2217
  }
2100
2218
  } catch {
2101
2219
  }
2102
2220
  try {
2103
2221
  if (existsSync8(DEVICE_ID_PATH)) {
2104
- const id2 = readFileSync6(DEVICE_ID_PATH, "utf8").trim();
2222
+ const id2 = readFileSync7(DEVICE_ID_PATH, "utf8").trim();
2105
2223
  if (id2) return id2;
2106
2224
  }
2107
2225
  } catch {
@@ -2114,7 +2232,7 @@ function loadDeviceId() {
2114
2232
  function loadLicense() {
2115
2233
  try {
2116
2234
  if (!existsSync8(LICENSE_PATH)) return null;
2117
- return readFileSync6(LICENSE_PATH, "utf8").trim();
2235
+ return readFileSync7(LICENSE_PATH, "utf8").trim();
2118
2236
  } catch {
2119
2237
  return null;
2120
2238
  }
@@ -2148,7 +2266,7 @@ async function verifyLicenseJwt(token) {
2148
2266
  async function getCachedLicense() {
2149
2267
  try {
2150
2268
  if (!existsSync8(CACHE_PATH)) return null;
2151
- const raw = JSON.parse(readFileSync6(CACHE_PATH, "utf8"));
2269
+ const raw = JSON.parse(readFileSync7(CACHE_PATH, "utf8"));
2152
2270
  if (!raw.token || typeof raw.token !== "string") return null;
2153
2271
  return await verifyLicenseJwt(raw.token);
2154
2272
  } catch {
@@ -2158,7 +2276,7 @@ async function getCachedLicense() {
2158
2276
  function readCachedToken() {
2159
2277
  try {
2160
2278
  if (!existsSync8(CACHE_PATH)) return null;
2161
- const raw = JSON.parse(readFileSync6(CACHE_PATH, "utf8"));
2279
+ const raw = JSON.parse(readFileSync7(CACHE_PATH, "utf8"));
2162
2280
  return typeof raw.token === "string" ? raw.token : null;
2163
2281
  } catch {
2164
2282
  return null;
@@ -2370,12 +2488,12 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
2370
2488
  });
2371
2489
 
2372
2490
  // src/lib/plan-limits.ts
2373
- import { readFileSync as readFileSync7, existsSync as existsSync9 } from "fs";
2491
+ import { readFileSync as readFileSync8, existsSync as existsSync9 } from "fs";
2374
2492
  import path10 from "path";
2375
2493
  function getLicenseSync() {
2376
2494
  try {
2377
2495
  if (!existsSync9(CACHE_PATH2)) return freeLicense();
2378
- const raw = JSON.parse(readFileSync7(CACHE_PATH2, "utf8"));
2496
+ const raw = JSON.parse(readFileSync8(CACHE_PATH2, "utf8"));
2379
2497
  if (!raw.token || typeof raw.token !== "string") return freeLicense();
2380
2498
  const parts = raw.token.split(".");
2381
2499
  if (parts.length !== 3) return freeLicense();
@@ -2414,7 +2532,7 @@ function assertEmployeeLimitSync(rosterPath) {
2414
2532
  let count = 0;
2415
2533
  try {
2416
2534
  if (existsSync9(filePath)) {
2417
- const raw = readFileSync7(filePath, "utf8");
2535
+ const raw = readFileSync8(filePath, "utf8");
2418
2536
  const employees = JSON.parse(raw);
2419
2537
  count = Array.isArray(employees) ? employees.length : 0;
2420
2538
  }
@@ -2457,7 +2575,7 @@ var init_plan_limits = __esm({
2457
2575
 
2458
2576
  // src/lib/tmux-routing.ts
2459
2577
  import { execFileSync as execFileSync2, execSync as execSync6 } from "child_process";
2460
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, existsSync as existsSync10, appendFileSync } from "fs";
2578
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, existsSync as existsSync10, appendFileSync } from "fs";
2461
2579
  import path11 from "path";
2462
2580
  import os5 from "os";
2463
2581
  import { fileURLToPath as fileURLToPath2 } from "url";
@@ -2506,7 +2624,7 @@ function extractRootExe(name) {
2506
2624
  }
2507
2625
  function getParentExe(sessionKey) {
2508
2626
  try {
2509
- const data = JSON.parse(readFileSync8(path11.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
2627
+ const data = JSON.parse(readFileSync9(path11.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
2510
2628
  return data.parentExe || null;
2511
2629
  } catch {
2512
2630
  return null;
@@ -2514,7 +2632,7 @@ function getParentExe(sessionKey) {
2514
2632
  }
2515
2633
  function getDispatchedBy(sessionKey) {
2516
2634
  try {
2517
- const data = JSON.parse(readFileSync8(
2635
+ const data = JSON.parse(readFileSync9(
2518
2636
  path11.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
2519
2637
  "utf8"
2520
2638
  ));
@@ -2551,7 +2669,7 @@ function findFreeInstance(employeeName, exeSession, maxInstances = 10, isAlive =
2551
2669
  function readDebounceState() {
2552
2670
  try {
2553
2671
  if (!existsSync10(DEBOUNCE_FILE)) return {};
2554
- return JSON.parse(readFileSync8(DEBOUNCE_FILE, "utf8"));
2672
+ return JSON.parse(readFileSync9(DEBOUNCE_FILE, "utf8"));
2555
2673
  } catch {
2556
2674
  return {};
2557
2675
  }
@@ -2747,7 +2865,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
2747
2865
  const claudeJsonPath = path11.join(os5.homedir(), ".claude.json");
2748
2866
  let claudeJson = {};
2749
2867
  try {
2750
- claudeJson = JSON.parse(readFileSync8(claudeJsonPath, "utf8"));
2868
+ claudeJson = JSON.parse(readFileSync9(claudeJsonPath, "utf8"));
2751
2869
  } catch {
2752
2870
  }
2753
2871
  if (!claudeJson.projects) claudeJson.projects = {};
@@ -2765,7 +2883,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
2765
2883
  const settingsPath = path11.join(projSettingsDir, "settings.json");
2766
2884
  let settings = {};
2767
2885
  try {
2768
- settings = JSON.parse(readFileSync8(settingsPath, "utf8"));
2886
+ settings = JSON.parse(readFileSync9(settingsPath, "utf8"));
2769
2887
  } catch {
2770
2888
  }
2771
2889
  const perms = settings.permissions ?? {};
@@ -2966,7 +3084,7 @@ __export(task_scanner_exports, {
2966
3084
  formatText: () => formatText,
2967
3085
  scanAgentTasks: () => scanAgentTasks
2968
3086
  });
2969
- import { readdirSync as readdirSync3, readFileSync as readFileSync9, existsSync as existsSync11, statSync } from "fs";
3087
+ import { readdirSync as readdirSync3, readFileSync as readFileSync10, existsSync as existsSync11, statSync } from "fs";
2970
3088
  import { execSync as execSync7 } from "child_process";
2971
3089
  import path12 from "path";
2972
3090
  function getProjectRoot() {
@@ -2992,7 +3110,7 @@ function scanAgentTasks(agentId) {
2992
3110
  total = files.length;
2993
3111
  for (const f of files) {
2994
3112
  try {
2995
- const content = readFileSync9(path12.join(taskDir, f), "utf8");
3113
+ const content = readFileSync10(path12.join(taskDir, f), "utf8");
2996
3114
  const statusMatch = content.match(STATUS_RE);
2997
3115
  const status = statusMatch ? statusMatch[1].toLowerCase() : null;
2998
3116
  if (status === "done") {
@@ -3146,7 +3264,7 @@ import crypto3 from "crypto";
3146
3264
  import path14 from "path";
3147
3265
  import { execSync as execSync9 } from "child_process";
3148
3266
  import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
3149
- import { existsSync as existsSync12, readFileSync as readFileSync10 } from "fs";
3267
+ import { existsSync as existsSync12, readFileSync as readFileSync11 } from "fs";
3150
3268
  async function writeCheckpoint(input) {
3151
3269
  const client = getClient();
3152
3270
  const row = await resolveTask(client, input.taskId);
@@ -3523,7 +3641,7 @@ async function ensureGitignoreExe(baseDir) {
3523
3641
  const gitignorePath = path14.join(baseDir, ".gitignore");
3524
3642
  try {
3525
3643
  if (existsSync12(gitignorePath)) {
3526
- const content = readFileSync10(gitignorePath, "utf-8");
3644
+ const content = readFileSync11(gitignorePath, "utf-8");
3527
3645
  if (/^\/?exe\/?$/m.test(content)) return;
3528
3646
  await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
3529
3647
  } else {
@@ -3857,7 +3975,7 @@ async function dispatchTaskToEmployee(input) {
3857
3975
  } else {
3858
3976
  const projectDir = input.projectDir ?? process.cwd();
3859
3977
  const result = ensureEmployee(input.assignedTo, exeSession, projectDir, {
3860
- autoInstance: input.assignedTo === "tom" || input.assignedTo === "sasha"
3978
+ autoInstance: isMultiInstance(input.assignedTo)
3861
3979
  });
3862
3980
  if (result.status === "failed") {
3863
3981
  process.stderr.write(
@@ -3892,6 +4010,7 @@ var init_tasks_notify = __esm({
3892
4010
  init_session_key();
3893
4011
  init_notifications();
3894
4012
  init_transport();
4013
+ init_employees();
3895
4014
  }
3896
4015
  });
3897
4016
 
@@ -4548,10 +4667,25 @@ var init_compress = __esm({
4548
4667
  // src/lib/cloud-sync.ts
4549
4668
  var cloud_sync_exports = {};
4550
4669
  __export(cloud_sync_exports, {
4670
+ buildRosterBlob: () => buildRosterBlob,
4551
4671
  cloudPull: () => cloudPull,
4672
+ cloudPullRoster: () => cloudPullRoster,
4552
4673
  cloudPush: () => cloudPush,
4553
- cloudSync: () => cloudSync
4674
+ cloudPushRoster: () => cloudPushRoster,
4675
+ cloudSync: () => cloudSync,
4676
+ mergeRosterFromRemote: () => mergeRosterFromRemote
4554
4677
  });
4678
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync7, existsSync as existsSync14, readdirSync as readdirSync5, mkdirSync as mkdirSync8, appendFileSync as appendFileSync2 } from "fs";
4679
+ import path18 from "path";
4680
+ import { homedir } from "os";
4681
+ function logError(msg) {
4682
+ try {
4683
+ const logPath = path18.join(homedir(), ".exe-os", "workers.log");
4684
+ appendFileSync2(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
4685
+ `);
4686
+ } catch {
4687
+ }
4688
+ }
4555
4689
  function assertSecureEndpoint(endpoint) {
4556
4690
  if (endpoint.startsWith("https://")) return;
4557
4691
  if (endpoint.startsWith("http://")) {
@@ -4584,8 +4718,7 @@ async function cloudPush(records, maxVersion, config) {
4584
4718
  });
4585
4719
  return resp.ok;
4586
4720
  } catch (err) {
4587
- process.stderr.write(`[cloud-sync] PUSH FAILED: ${err instanceof Error ? err.message : String(err)}
4588
- `);
4721
+ logError(`[cloud-sync] PUSH FAILED: ${err instanceof Error ? err.message : String(err)}`);
4589
4722
  return false;
4590
4723
  }
4591
4724
  }
@@ -4616,8 +4749,7 @@ async function cloudPull(sinceVersion, config) {
4616
4749
  }
4617
4750
  return { records: allRecords, maxVersion: data.max_version ?? sinceVersion };
4618
4751
  } catch (err) {
4619
- process.stderr.write(`[cloud-sync] PULL FAILED: ${err instanceof Error ? err.message : String(err)}
4620
- `);
4752
+ logError(`[cloud-sync] PULL FAILED: ${err instanceof Error ? err.message : String(err)}`);
4621
4753
  return { records: [], maxVersion: sinceVersion };
4622
4754
  }
4623
4755
  }
@@ -4706,8 +4838,137 @@ async function cloudSync(config) {
4706
4838
  pushed = records.length;
4707
4839
  }
4708
4840
  }
4841
+ try {
4842
+ await cloudPushRoster(config);
4843
+ } catch (err) {
4844
+ process.stderr.write(`[cloud-sync] Roster push warning: ${err instanceof Error ? err.message : String(err)}
4845
+ `);
4846
+ }
4847
+ try {
4848
+ await cloudPullRoster(config);
4849
+ } catch (err) {
4850
+ process.stderr.write(`[cloud-sync] Roster pull warning: ${err instanceof Error ? err.message : String(err)}
4851
+ `);
4852
+ }
4709
4853
  return { pushed, pulled };
4710
4854
  }
4855
+ function buildRosterBlob(paths) {
4856
+ const rosterPath = paths?.rosterPath ?? path18.join(EXE_AI_DIR, "exe-employees.json");
4857
+ const identityDir = paths?.identityDir ?? path18.join(EXE_AI_DIR, "identity");
4858
+ let roster = [];
4859
+ if (existsSync14(rosterPath)) {
4860
+ try {
4861
+ roster = JSON.parse(readFileSync12(rosterPath, "utf-8"));
4862
+ } catch {
4863
+ }
4864
+ }
4865
+ const identities = {};
4866
+ if (existsSync14(identityDir)) {
4867
+ for (const file of readdirSync5(identityDir).filter((f) => f.endsWith(".md"))) {
4868
+ try {
4869
+ identities[file] = readFileSync12(path18.join(identityDir, file), "utf-8");
4870
+ } catch {
4871
+ }
4872
+ }
4873
+ }
4874
+ const content = JSON.stringify({ roster, identities });
4875
+ const hash = Buffer.from(content).length;
4876
+ return { roster, identities, version: hash };
4877
+ }
4878
+ async function cloudPushRoster(config) {
4879
+ assertSecureEndpoint(config.endpoint);
4880
+ const blob = buildRosterBlob();
4881
+ if (blob.roster.length === 0) return true;
4882
+ try {
4883
+ const client = getClient();
4884
+ const meta = await client.execute(
4885
+ "SELECT value FROM sync_meta WHERE key = 'last_roster_push_version'"
4886
+ );
4887
+ const lastVersion = meta.rows.length > 0 ? Number(meta.rows[0].value) : 0;
4888
+ if (blob.version === lastVersion) return true;
4889
+ } catch {
4890
+ }
4891
+ try {
4892
+ const json = JSON.stringify(blob);
4893
+ const compressed = compress(Buffer.from(json, "utf8"));
4894
+ const encrypted = encryptSyncBlob(compressed);
4895
+ const resp = await fetch(`${config.endpoint}/sync/push-roster`, {
4896
+ method: "POST",
4897
+ headers: {
4898
+ Authorization: `Bearer ${config.apiKey}`,
4899
+ "Content-Type": "application/json",
4900
+ "X-Device-Id": loadDeviceId()
4901
+ },
4902
+ body: JSON.stringify({ blob: encrypted })
4903
+ });
4904
+ if (resp.ok) {
4905
+ try {
4906
+ const client = getClient();
4907
+ await client.execute({
4908
+ sql: "INSERT OR REPLACE INTO sync_meta (key, value) VALUES ('last_roster_push_version', ?)",
4909
+ args: [String(blob.version)]
4910
+ });
4911
+ } catch {
4912
+ }
4913
+ }
4914
+ return resp.ok;
4915
+ } catch (err) {
4916
+ process.stderr.write(`[cloud-sync] ROSTER PUSH FAILED: ${err instanceof Error ? err.message : String(err)}
4917
+ `);
4918
+ return false;
4919
+ }
4920
+ }
4921
+ async function cloudPullRoster(config) {
4922
+ assertSecureEndpoint(config.endpoint);
4923
+ try {
4924
+ const resp = await fetch(`${config.endpoint}/sync/pull-roster`, {
4925
+ method: "GET",
4926
+ headers: {
4927
+ Authorization: `Bearer ${config.apiKey}`,
4928
+ "X-Device-Id": loadDeviceId()
4929
+ }
4930
+ });
4931
+ if (!resp.ok) return { added: 0 };
4932
+ const data = await resp.json();
4933
+ if (!data.blob) return { added: 0 };
4934
+ const compressed = decryptSyncBlob(data.blob);
4935
+ const json = decompress(compressed).toString("utf8");
4936
+ const remote = JSON.parse(json);
4937
+ return mergeRosterFromRemote(remote);
4938
+ } catch (err) {
4939
+ process.stderr.write(`[cloud-sync] ROSTER PULL FAILED: ${err instanceof Error ? err.message : String(err)}
4940
+ `);
4941
+ return { added: 0 };
4942
+ }
4943
+ }
4944
+ async function mergeRosterFromRemote(remote, paths) {
4945
+ const rosterPath = paths?.rosterPath ?? void 0;
4946
+ const identityDir = paths?.identityDir ?? path18.join(EXE_AI_DIR, "identity");
4947
+ const localEmployees = await loadEmployees(rosterPath);
4948
+ const localNames = new Set(localEmployees.map((e) => e.name));
4949
+ let added = 0;
4950
+ for (const remoteEmp of remote.roster) {
4951
+ if (localNames.has(remoteEmp.name)) continue;
4952
+ localEmployees.push(remoteEmp);
4953
+ localNames.add(remoteEmp.name);
4954
+ added++;
4955
+ if (remote.identities[`${remoteEmp.name}.md`]) {
4956
+ if (!existsSync14(identityDir)) mkdirSync8(identityDir, { recursive: true });
4957
+ const idPath = path18.join(identityDir, `${remoteEmp.name}.md`);
4958
+ if (!existsSync14(idPath)) {
4959
+ writeFileSync7(idPath, remote.identities[`${remoteEmp.name}.md`], "utf-8");
4960
+ }
4961
+ }
4962
+ try {
4963
+ registerBinSymlinks(remoteEmp.name);
4964
+ } catch {
4965
+ }
4966
+ }
4967
+ if (added > 0) {
4968
+ await saveEmployees(localEmployees, rosterPath);
4969
+ }
4970
+ return { added };
4971
+ }
4711
4972
  var LOCALHOST_PATTERNS;
4712
4973
  var init_cloud_sync = __esm({
4713
4974
  "src/lib/cloud-sync.ts"() {
@@ -4717,6 +4978,8 @@ var init_cloud_sync = __esm({
4717
4978
  init_compress();
4718
4979
  init_plan_limits();
4719
4980
  init_license();
4981
+ init_config();
4982
+ init_employees();
4720
4983
  LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
4721
4984
  }
4722
4985
  });
@@ -4899,9 +5162,9 @@ var init_schedules = __esm({
4899
5162
 
4900
5163
  // src/bin/exe-boot.ts
4901
5164
  init_employees();
4902
- import path18 from "path";
5165
+ import path19 from "path";
4903
5166
  import { mkdir as mkdir5, writeFile as writeFile6 } from "fs/promises";
4904
- import { existsSync as existsSync14, readFileSync as readFileSync11, readdirSync as readdirSync5, unlinkSync as unlinkSync5 } from "fs";
5167
+ import { existsSync as existsSync15, readFileSync as readFileSync13, readdirSync as readdirSync6, unlinkSync as unlinkSync5 } from "fs";
4905
5168
  import os6 from "os";
4906
5169
 
4907
5170
  // src/lib/employee-templates.ts
@@ -4912,7 +5175,7 @@ var DEFAULT_EXE = {
4912
5175
 
4913
5176
  Character: No bullshit. Precise. Accountable. Direct but never offensive. Calm foresight. You see problems before they arrive and propose solutions. If the founder decides differently, you commit fully.
4914
5177
 
4915
- You are the single interface. The founder talks to you \u2014 only you. When they ask for technical work, you delegate to yoshi (CTO) via sub-agent and review his output before presenting. When they ask for status, you synthesize across all projects. You never tell the founder to run commands or talk to someone else.
5178
+ You are the single interface. The founder talks to you \u2014 only you. When they ask for technical work, you delegate to the CTO via sub-agent and review their output before presenting. When they ask for status, you synthesize across all projects. You never tell the founder to run commands or talk to someone else.
4916
5179
 
4917
5180
  After every specialist task: verify tests ran, behavior was checked, and a memory summary was stored. If not, flag it.
4918
5181
 
@@ -4955,8 +5218,22 @@ function boxMid(w) {
4955
5218
  function boxBot(w) {
4956
5219
  return `\u2514${"\u2500".repeat(w)}\u2518`;
4957
5220
  }
5221
+ function truncateToWidth(str, maxW) {
5222
+ if (displayWidth(str) <= maxW) return str;
5223
+ let w = 0;
5224
+ let i = 0;
5225
+ for (const ch of str) {
5226
+ const cw = ch.codePointAt(0) > 255 ? 2 : 1;
5227
+ if (w + cw + 1 > maxW) break;
5228
+ w += cw;
5229
+ i += ch.length;
5230
+ }
5231
+ return str.slice(0, i) + "\u2026";
5232
+ }
4958
5233
  function boxRow(content, w) {
4959
- return `\u2502 ${padRight(content, w - 2)} \u2502`;
5234
+ const maxContent = w - 2;
5235
+ const truncated = truncateToWidth(content, maxContent);
5236
+ return `\u2502 ${padRight(truncated, maxContent)} \u2502`;
4960
5237
  }
4961
5238
  function formatUptime(seconds) {
4962
5239
  if (seconds < 3600) return `${Math.floor(seconds / 60)}m`;
@@ -4966,6 +5243,9 @@ async function generateStatusBrief(employees, data, _activeAgentIds) {
4966
5243
  const now = /* @__PURE__ */ new Date();
4967
5244
  const dateStr = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")} ${String(now.getHours()).padStart(2, "0")}:${String(now.getMinutes()).padStart(2, "0")}`;
4968
5245
  const sessionTag = data.exeSession ? ` [${data.exeSession}]` : "";
5246
+ if (data.embedding && data.embedding.totalMemories < 5 && data.globalTasks.length === 0 && (data.projects?.length ?? 0) === 0) {
5247
+ return buildFirstBootBrief(employees, dateStr, sessionTag);
5248
+ }
4969
5249
  const sections = [];
4970
5250
  sections.push([` EXE STATUS BRIEF \u2014 ${dateStr}${sessionTag}`]);
4971
5251
  const reminderLines = buildReminders(data);
@@ -4975,6 +5255,8 @@ async function generateStatusBrief(employees, data, _activeAgentIds) {
4975
5255
  sections.push(buildProjects(data));
4976
5256
  sections.push(buildTeam(employees, data));
4977
5257
  sections.push(buildHealth(data));
5258
+ const termCols = process.stdout.columns || 80;
5259
+ const maxInnerW = termCols - 2;
4978
5260
  let maxW = 0;
4979
5261
  for (const sec of sections) {
4980
5262
  for (const line of sec) {
@@ -4982,7 +5264,7 @@ async function generateStatusBrief(employees, data, _activeAgentIds) {
4982
5264
  if (w > maxW) maxW = w;
4983
5265
  }
4984
5266
  }
4985
- const innerW = maxW + 4;
5267
+ const innerW = Math.min(maxW + 4, maxInnerW);
4986
5268
  const out = [];
4987
5269
  out.push("[VERBATIM OUTPUT \u2014 do not reformat]");
4988
5270
  for (let i = 0; i < sections.length; i++) {
@@ -4994,6 +5276,39 @@ async function generateStatusBrief(employees, data, _activeAgentIds) {
4994
5276
  out.push(boxBot(innerW));
4995
5277
  return out.join("\n");
4996
5278
  }
5279
+ function buildFirstBootBrief(employees, dateStr, sessionTag) {
5280
+ const termCols = process.stdout.columns || 80;
5281
+ const maxInnerW = termCols - 2;
5282
+ const lines = [];
5283
+ lines.push(` WELCOME TO EXE OS \u2014 ${dateStr}${sessionTag}`);
5284
+ const bodyLines = [];
5285
+ bodyLines.push(" \u{1F44B} First time? Here's your team:");
5286
+ bodyLines.push("");
5287
+ for (const emp of employees) {
5288
+ const emoji = EMPLOYEE_EMOJIS[emp.name] ?? "\u{1F464}";
5289
+ const role = emp.role ? ` (${emp.role})` : "";
5290
+ bodyLines.push(` ${emoji} ${emp.name}${role}`);
5291
+ }
5292
+ bodyLines.push("");
5293
+ bodyLines.push(" \u{1F4A1} Quick start:");
5294
+ bodyLines.push(" \u2022 Run `exe-os backfill-conversations` to import Claude Code history");
5295
+ bodyLines.push(" \u2022 Say `/exe` to launch your COO with a full status brief");
5296
+ bodyLines.push(" \u2022 Say `/exe-team` to manage your team");
5297
+ let maxW = 0;
5298
+ for (const line of [...lines, ...bodyLines]) {
5299
+ const w = displayWidth(line);
5300
+ if (w > maxW) maxW = w;
5301
+ }
5302
+ const innerW = Math.min(maxW + 4, maxInnerW);
5303
+ const out = [];
5304
+ out.push("[VERBATIM OUTPUT \u2014 do not reformat]");
5305
+ out.push(boxTop(innerW));
5306
+ for (const line of lines) out.push(boxRow(line, innerW));
5307
+ out.push(boxMid(innerW));
5308
+ for (const line of bodyLines) out.push(boxRow(line, innerW));
5309
+ out.push(boxBot(innerW));
5310
+ return out.join("\n");
5311
+ }
4997
5312
  function buildReminders(data) {
4998
5313
  if (!data.reminders || data.reminders.length === 0) return [];
4999
5314
  const lines = [];
@@ -5061,7 +5376,10 @@ function buildProjects(data) {
5061
5376
  idle.push(p);
5062
5377
  }
5063
5378
  }
5064
- for (const p of active) {
5379
+ const MAX_SHOWN_PROJECTS = 6;
5380
+ const shown = active.slice(0, MAX_SHOWN_PROJECTS);
5381
+ const hidden = active.length - shown.length;
5382
+ for (const p of shown) {
5065
5383
  const blocked = p.blockedCount ?? 0;
5066
5384
  const statusEmoji = blocked > 0 ? "\u{1F534}" : "\u{1F7E2}";
5067
5385
  const sessionStr = p.sessionName ? ` (${p.sessionName})` : p.isCurrent ? " (current)" : "";
@@ -5083,7 +5401,9 @@ function buildProjects(data) {
5083
5401
  if (blocked > 0) {
5084
5402
  lines.push(` Blocked: ${blocked}`);
5085
5403
  }
5086
- lines.push("");
5404
+ }
5405
+ if (hidden > 0) {
5406
+ lines.push(` ... and ${hidden} more project${hidden === 1 ? "" : "s"}`);
5087
5407
  }
5088
5408
  if (idle.length > 0) {
5089
5409
  const idleNames = idle.map((p) => p.projectName);
@@ -5143,7 +5463,8 @@ function buildHealth(data) {
5143
5463
  }
5144
5464
  }
5145
5465
  }
5146
- lines.push(` \u2601\uFE0F Cloud: ${data.cloudConnected ? "\u{1F7E2}" : "not configured"}`);
5466
+ const cloudLabel = data.cloudConnected ? "\u{1F7E2} synced" : data.cloudConnected === false && data.plan && data.plan !== "free" ? "\u{1F534} offline" : "not configured";
5467
+ lines.push(` \u2601\uFE0F Cloud: ${cloudLabel}`);
5147
5468
  if (data.sessionsKilledToday !== void 0) {
5148
5469
  lines.push(` \u{1F480} Sessions killed today: ${data.sessionsKilledToday}`);
5149
5470
  }
@@ -5182,7 +5503,7 @@ init_notifications();
5182
5503
 
5183
5504
  // src/adapters/claude/active-agent.ts
5184
5505
  init_config();
5185
- import { readFileSync as readFileSync3, writeFileSync, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2, readdirSync as readdirSync2 } from "fs";
5506
+ import { readFileSync as readFileSync4, writeFileSync, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2, readdirSync as readdirSync2 } from "fs";
5186
5507
  import { execSync as execSync3 } from "child_process";
5187
5508
  import path6 from "path";
5188
5509
 
@@ -5292,18 +5613,18 @@ async function boot(options) {
5292
5613
  } catch {
5293
5614
  }
5294
5615
  try {
5295
- const { readdirSync: readdirSync6, readFileSync: readFs } = await import("fs");
5616
+ const { readdirSync: readdirSync7, readFileSync: readFs } = await import("fs");
5296
5617
  const { STATUS_RE: STATUS_RE2, PRIORITY_RE: PRIORITY_RE2, TITLE_RE: TITLE_RE2 } = await Promise.resolve().then(() => (init_task_scanner(), task_scanner_exports));
5297
5618
  const { getProjectName: getProjectName2 } = await Promise.resolve().then(() => (init_project_name(), project_name_exports));
5298
5619
  const exeDir = "exe";
5299
- const entries = readdirSync6(exeDir, { withFileTypes: true });
5620
+ const entries = readdirSync7(exeDir, { withFileTypes: true });
5300
5621
  const employeeDirs = entries.filter((e) => e.isDirectory() && !["output", "research"].includes(e.name));
5301
5622
  for (const dir of employeeDirs) {
5302
5623
  const employee = dir.name;
5303
- const taskDir = path18.join(exeDir, employee);
5624
+ const taskDir = path19.join(exeDir, employee);
5304
5625
  let files;
5305
5626
  try {
5306
- files = readdirSync6(taskDir).filter((f) => f.endsWith(".md"));
5627
+ files = readdirSync7(taskDir).filter((f) => f.endsWith(".md"));
5307
5628
  } catch {
5308
5629
  continue;
5309
5630
  }
@@ -5311,7 +5632,7 @@ async function boot(options) {
5311
5632
  const taskFilePath = `exe/${employee}/${file}`;
5312
5633
  let content;
5313
5634
  try {
5314
- content = readFs(path18.join(taskDir, file), "utf8");
5635
+ content = readFs(path19.join(taskDir, file), "utf8");
5315
5636
  } catch {
5316
5637
  continue;
5317
5638
  }
@@ -5395,12 +5716,12 @@ async function boot(options) {
5395
5716
  } catch {
5396
5717
  }
5397
5718
  try {
5398
- const exeExeDir = path18.join(process.cwd(), "exe", "exe");
5399
- if (existsSync14(exeExeDir)) {
5400
- for (const f of readdirSync5(exeExeDir)) {
5719
+ const exeExeDir = path19.join(process.cwd(), "exe", "exe");
5720
+ if (existsSync15(exeExeDir)) {
5721
+ for (const f of readdirSync6(exeExeDir)) {
5401
5722
  if (f.startsWith("review-") && f.endsWith(".md")) {
5402
5723
  try {
5403
- unlinkSync5(path18.join(exeExeDir, f));
5724
+ unlinkSync5(path19.join(exeExeDir, f));
5404
5725
  } catch {
5405
5726
  }
5406
5727
  }
@@ -5444,12 +5765,12 @@ async function boot(options) {
5444
5765
  });
5445
5766
  const taskFile = String(r.task_file);
5446
5767
  try {
5447
- const filePath = path18.join(process.cwd(), taskFile);
5448
- if (existsSync14(filePath)) {
5449
- let content = readFileSync11(filePath, "utf8");
5768
+ const filePath = path19.join(process.cwd(), taskFile);
5769
+ if (existsSync15(filePath)) {
5770
+ let content = readFileSync13(filePath, "utf8");
5450
5771
  content = content.replace(/\*\*Status:\*\* needs_review/, "**Status:** done");
5451
- const { writeFileSync: writeFileSync7 } = await import("fs");
5452
- writeFileSync7(filePath, content);
5772
+ const { writeFileSync: writeFileSync8 } = await import("fs");
5773
+ writeFileSync8(filePath, content);
5453
5774
  }
5454
5775
  } catch {
5455
5776
  }
@@ -5909,19 +6230,19 @@ async function boot(options) {
5909
6230
  })()
5910
6231
  ]);
5911
6232
  try {
5912
- const configPath = path18.join(
5913
- process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path18.join(os6.homedir(), ".exe-os"),
6233
+ const configPath = path19.join(
6234
+ process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path19.join(os6.homedir(), ".exe-os"),
5914
6235
  "config.json"
5915
6236
  );
5916
- if (existsSync14(configPath)) {
5917
- const raw = JSON.parse(readFileSync11(configPath, "utf8"));
6237
+ if (existsSync15(configPath)) {
6238
+ const raw = JSON.parse(readFileSync13(configPath, "utf8"));
5918
6239
  briefData.cloudConnected = !!(raw.cloud || raw.turso);
5919
6240
  }
5920
6241
  } catch {
5921
6242
  }
5922
6243
  try {
5923
- const backfillFlagPath = path18.join(EXE_AI_DIR, "session-cache", "needs-backfill");
5924
- const isBackfillNeeded = () => existsSync14(backfillFlagPath);
6244
+ const backfillFlagPath = path19.join(EXE_AI_DIR, "session-cache", "needs-backfill");
6245
+ const isBackfillNeeded = () => existsSync15(backfillFlagPath);
5925
6246
  const coverageResult = await client.execute({
5926
6247
  sql: `SELECT COUNT(*) as total,
5927
6248
  SUM(CASE WHEN vector IS NOT NULL THEN 1 ELSE 0 END) as with_vectors
@@ -5943,8 +6264,8 @@ async function boot(options) {
5943
6264
  let daemonRunning = false;
5944
6265
  let daemonUptime;
5945
6266
  let daemonRequestsServed;
5946
- const socketPath = path18.join(EXE_AI_DIR, "exed.sock");
5947
- if (existsSync14(socketPath)) {
6267
+ const socketPath = path19.join(EXE_AI_DIR, "exed.sock");
6268
+ if (existsSync15(socketPath)) {
5948
6269
  try {
5949
6270
  const net = await import("net");
5950
6271
  const health = await new Promise((resolve) => {
@@ -5986,10 +6307,10 @@ async function boot(options) {
5986
6307
  }
5987
6308
  }
5988
6309
  if (!daemonRunning) {
5989
- const pidPath = path18.join(EXE_AI_DIR, "exed.pid");
5990
- if (existsSync14(pidPath)) {
6310
+ const pidPath = path19.join(EXE_AI_DIR, "exed.pid");
6311
+ if (existsSync15(pidPath)) {
5991
6312
  try {
5992
- const pid = parseInt(readFileSync11(pidPath, "utf8").trim(), 10);
6313
+ const pid = parseInt(readFileSync13(pidPath, "utf8").trim(), 10);
5993
6314
  if (pid > 0) {
5994
6315
  process.kill(pid, 0);
5995
6316
  daemonRunning = true;
@@ -6000,8 +6321,8 @@ async function boot(options) {
6000
6321
  }
6001
6322
  if (nullCount === 0) {
6002
6323
  try {
6003
- const flagPath = path18.join(EXE_AI_DIR, "session-cache", "needs-backfill");
6004
- if (existsSync14(flagPath)) {
6324
+ const flagPath = path19.join(EXE_AI_DIR, "session-cache", "needs-backfill");
6325
+ if (existsSync15(flagPath)) {
6005
6326
  const { unlinkSync: unlinkSync6 } = await import("fs");
6006
6327
  unlinkSync6(flagPath);
6007
6328
  }
@@ -6023,10 +6344,10 @@ async function boot(options) {
6023
6344
  const { spawn } = await import("child_process");
6024
6345
  const { fileURLToPath: fileURLToPath3 } = await import("url");
6025
6346
  const thisFile = fileURLToPath3(import.meta.url);
6026
- const backfillPath = path18.resolve(path18.dirname(thisFile), "backfill-vectors.js");
6027
- if (existsSync14(backfillPath)) {
6347
+ const backfillPath = path19.resolve(path19.dirname(thisFile), "backfill-vectors.js");
6348
+ if (existsSync15(backfillPath)) {
6028
6349
  const { openSync, closeSync } = await import("fs");
6029
- const workerLogPath = path18.join(EXE_AI_DIR, "workers.log");
6350
+ const workerLogPath = path19.join(EXE_AI_DIR, "workers.log");
6030
6351
  let stderrFd = "ignore";
6031
6352
  try {
6032
6353
  stderrFd = openSync(workerLogPath, "a");
@@ -6054,8 +6375,8 @@ async function boot(options) {
6054
6375
  const criticalBinaries = ["backfill-vectors.js", "scan-tasks.js"];
6055
6376
  const missing = [];
6056
6377
  for (const bin of criticalBinaries) {
6057
- const binPath = path18.resolve(path18.dirname(thisFile), bin);
6058
- if (!existsSync14(binPath)) {
6378
+ const binPath = path19.resolve(path19.dirname(thisFile), bin);
6379
+ if (!existsSync15(binPath)) {
6059
6380
  missing.push(`dist/bin/${bin}`);
6060
6381
  }
6061
6382
  }
@@ -6085,7 +6406,7 @@ async function boot(options) {
6085
6406
  return;
6086
6407
  }
6087
6408
  const exeEmployee = employees.find((e) => e.name === "exe") ?? DEFAULT_EXE;
6088
- const sessionDir = path18.join(EXE_AI_DIR, "sessions", "exe");
6409
+ const sessionDir = path19.join(EXE_AI_DIR, "sessions", "exe");
6089
6410
  await mkdir5(sessionDir, { recursive: true });
6090
6411
  const claudeMdContent = `${exeEmployee.systemPrompt}
6091
6412
 
@@ -6094,7 +6415,7 @@ async function boot(options) {
6094
6415
  # Status Brief
6095
6416
 
6096
6417
  ${brief}`;
6097
- await writeFile6(path18.join(sessionDir, "CLAUDE.md"), claudeMdContent, "utf-8");
6418
+ await writeFile6(path19.join(sessionDir, "CLAUDE.md"), claudeMdContent, "utf-8");
6098
6419
  const unread = await readUnreadNotifications();
6099
6420
  if (unread.length > 0) {
6100
6421
  console.log(`\u{1F4EC} ${unread.length} unread notification${unread.length === 1 ? "" : "s"}`);
@@ -6103,12 +6424,12 @@ ${brief}`;
6103
6424
  await cleanupOldNotifications();
6104
6425
  console.log(brief);
6105
6426
  try {
6106
- const configPath2 = path18.join(
6107
- process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path18.join(os6.homedir(), ".exe-os"),
6427
+ const configPath2 = path19.join(
6428
+ process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path19.join(os6.homedir(), ".exe-os"),
6108
6429
  "config.json"
6109
6430
  );
6110
- if (existsSync14(configPath2)) {
6111
- const rawCfg = JSON.parse(readFileSync11(configPath2, "utf8"));
6431
+ if (existsSync15(configPath2)) {
6432
+ const rawCfg = JSON.parse(readFileSync13(configPath2, "utf8"));
6112
6433
  const cloudCfg = rawCfg.cloud;
6113
6434
  if (cloudCfg?.apiKey) {
6114
6435
  const { initSyncCrypto: initSyncCrypto2, isSyncCryptoInitialized: isSyncCryptoInitialized2 } = await Promise.resolve().then(() => (init_crypto(), crypto_exports));
@@ -6118,9 +6439,7 @@ ${brief}`;
6118
6439
  if (mk) initSyncCrypto2(mk);
6119
6440
  }
6120
6441
  const { cloudSync: cloudSync2 } = await Promise.resolve().then(() => (init_cloud_sync(), cloud_sync_exports));
6121
- cloudSync2({ apiKey: cloudCfg.apiKey, endpoint: cloudCfg.endpoint ?? "" }).catch((err) => {
6122
- process.stderr.write(`[boot] cloud sync failed: ${err instanceof Error ? err.message : String(err)}
6123
- `);
6442
+ cloudSync2({ apiKey: cloudCfg.apiKey, endpoint: cloudCfg.endpoint ?? "" }).catch(() => {
6124
6443
  });
6125
6444
  }
6126
6445
  }