@askexenow/exe-os 0.8.37 → 0.8.39

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 (93) hide show
  1. package/README.md +17 -8
  2. package/dist/bin/backfill-conversations.js +112 -70
  3. package/dist/bin/backfill-responses.js +53 -18
  4. package/dist/bin/backfill-vectors.js +43 -16
  5. package/dist/bin/cleanup-stale-review-tasks.js +38 -16
  6. package/dist/bin/cli.js +790 -468
  7. package/dist/bin/exe-agent.js +19 -4
  8. package/dist/bin/exe-assign.js +46 -13
  9. package/dist/bin/exe-boot.js +288 -129
  10. package/dist/bin/exe-call.js +20 -10
  11. package/dist/bin/exe-cloud.js +135 -30
  12. package/dist/bin/exe-dispatch.js +1 -1
  13. package/dist/bin/exe-doctor.js +38 -16
  14. package/dist/bin/exe-export-behaviors.js +43 -21
  15. package/dist/bin/exe-forget.js +39 -17
  16. package/dist/bin/exe-gateway.js +159 -50
  17. package/dist/bin/exe-heartbeat.js +53 -31
  18. package/dist/bin/exe-kill.js +40 -18
  19. package/dist/bin/exe-launch-agent.js +109 -36
  20. package/dist/bin/exe-link.js +196 -87
  21. package/dist/bin/exe-new-employee.js +56 -17
  22. package/dist/bin/exe-pending-messages.js +47 -25
  23. package/dist/bin/exe-pending-notifications.js +38 -16
  24. package/dist/bin/exe-pending-reviews.js +51 -29
  25. package/dist/bin/exe-rename.js +21 -7
  26. package/dist/bin/exe-review.js +41 -13
  27. package/dist/bin/exe-search.js +57 -21
  28. package/dist/bin/exe-session-cleanup.js +67 -31
  29. package/dist/bin/exe-settings.js +63 -2
  30. package/dist/bin/exe-status.js +35 -13
  31. package/dist/bin/exe-team.js +35 -13
  32. package/dist/bin/git-sweep.js +45 -17
  33. package/dist/bin/graph-backfill.js +38 -16
  34. package/dist/bin/graph-export.js +38 -16
  35. package/dist/bin/install.js +10 -1
  36. package/dist/bin/scan-tasks.js +47 -19
  37. package/dist/bin/setup.js +444 -259
  38. package/dist/bin/shard-migrate.js +38 -16
  39. package/dist/bin/wiki-sync.js +40 -17
  40. package/dist/gateway/index.js +113 -48
  41. package/dist/hooks/bug-report-worker.js +66 -39
  42. package/dist/hooks/commit-complete.js +45 -17
  43. package/dist/hooks/error-recall.js +60 -20
  44. package/dist/hooks/exe-heartbeat-hook.js +3 -2
  45. package/dist/hooks/ingest-worker.js +174 -45
  46. package/dist/hooks/ingest.js +74 -28
  47. package/dist/hooks/instructions-loaded.js +46 -17
  48. package/dist/hooks/notification.js +44 -15
  49. package/dist/hooks/post-compact.js +44 -15
  50. package/dist/hooks/pre-compact.js +42 -14
  51. package/dist/hooks/pre-tool-use.js +59 -22
  52. package/dist/hooks/prompt-ingest-worker.js +75 -14
  53. package/dist/hooks/prompt-submit.js +75 -32
  54. package/dist/hooks/response-ingest-worker.js +76 -15
  55. package/dist/hooks/session-end.js +54 -22
  56. package/dist/hooks/session-start.js +57 -20
  57. package/dist/hooks/stop.js +44 -15
  58. package/dist/hooks/subagent-stop.js +44 -15
  59. package/dist/hooks/summary-worker.js +339 -106
  60. package/dist/index.js +94 -23
  61. package/dist/lib/cloud-sync.js +191 -80
  62. package/dist/lib/config.js +4 -1
  63. package/dist/lib/consolidation.js +5 -4
  64. package/dist/lib/database.js +1 -0
  65. package/dist/lib/device-registry.js +2 -1
  66. package/dist/lib/embedder.js +9 -1
  67. package/dist/lib/employee-templates.js +5 -0
  68. package/dist/lib/employees.js +11 -6
  69. package/dist/lib/exe-daemon-client.js +6 -1
  70. package/dist/lib/exe-daemon.js +95 -36
  71. package/dist/lib/hybrid-search.js +57 -21
  72. package/dist/lib/identity-templates.js +16 -7
  73. package/dist/lib/identity.js +1 -1
  74. package/dist/lib/keychain.js +2 -1
  75. package/dist/lib/license.js +56 -6
  76. package/dist/lib/messaging.js +1 -1
  77. package/dist/lib/reminders.js +2 -2
  78. package/dist/lib/schedules.js +38 -16
  79. package/dist/lib/skill-learning.js +1 -1
  80. package/dist/lib/store.js +44 -16
  81. package/dist/lib/tasks.js +1 -1
  82. package/dist/lib/tmux-routing.js +1 -1
  83. package/dist/mcp/server.js +280 -155
  84. package/dist/mcp/tools/complete-reminder.js +1 -1
  85. package/dist/mcp/tools/create-task.js +14 -6
  86. package/dist/mcp/tools/deactivate-behavior.js +2 -2
  87. package/dist/mcp/tools/list-reminders.js +1 -1
  88. package/dist/mcp/tools/list-tasks.js +36 -28
  89. package/dist/mcp/tools/send-message.js +1 -1
  90. package/dist/mcp/tools/update-task.js +1 -1
  91. package/dist/runtime/index.js +42 -8
  92. package/dist/tui/App.js +220 -99
  93. package/package.json +5 -3
@@ -16,15 +16,15 @@ var __export = (target, all) => {
16
16
  };
17
17
 
18
18
  // src/lib/config.ts
19
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
19
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
20
20
  import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
21
21
  import path2 from "path";
22
- import os from "os";
22
+ import os2 from "os";
23
23
  function resolveDataDir() {
24
24
  if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
25
25
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
26
- const newDir = path2.join(os.homedir(), ".exe-os");
27
- const legacyDir = path2.join(os.homedir(), ".exe-mem");
26
+ const newDir = path2.join(os2.homedir(), ".exe-os");
27
+ const legacyDir = path2.join(os2.homedir(), ".exe-mem");
28
28
  if (!existsSync2(newDir) && existsSync2(legacyDir)) {
29
29
  try {
30
30
  renameSync(legacyDir, newDir);
@@ -111,7 +111,7 @@ async function loadConfig() {
111
111
  normalizeAutoUpdate(migratedCfg);
112
112
  const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
113
113
  if (config.dbPath.startsWith("~")) {
114
- config.dbPath = config.dbPath.replace(/^~/, os.homedir());
114
+ config.dbPath = config.dbPath.replace(/^~/, os2.homedir());
115
115
  }
116
116
  return config;
117
117
  } catch {
@@ -218,7 +218,7 @@ __export(shard_manager_exports, {
218
218
  shardExists: () => shardExists
219
219
  });
220
220
  import path3 from "path";
221
- import { existsSync as existsSync3, mkdirSync } from "fs";
221
+ import { existsSync as existsSync3, mkdirSync, readdirSync } from "fs";
222
222
  import { createClient as createClient2 } from "@libsql/client";
223
223
  function initShardManager(encryptionKey) {
224
224
  _encryptionKey = encryptionKey;
@@ -257,8 +257,7 @@ function shardExists(projectName) {
257
257
  }
258
258
  function listShards() {
259
259
  if (!existsSync3(SHARDS_DIR)) return [];
260
- const { readdirSync: readdirSync4 } = __require("fs");
261
- return readdirSync4(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
260
+ return readdirSync(SHARDS_DIR).filter((f) => f.endsWith(".db")).map((f) => f.replace(".db", ""));
262
261
  }
263
262
  async function ensureShardSchema(client) {
264
263
  await client.execute("PRAGMA journal_mode = WAL");
@@ -533,15 +532,20 @@ function addEmployee(employees, employee) {
533
532
  }
534
533
  return [...employees, normalized];
535
534
  }
535
+ function findExeBin() {
536
+ try {
537
+ return execSync2(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
538
+ } catch {
539
+ return null;
540
+ }
541
+ }
536
542
  function registerBinSymlinks(name) {
537
543
  const created = [];
538
544
  const skipped = [];
539
545
  const errors = [];
540
- let exeBinPath;
541
- try {
542
- exeBinPath = execSync2("which exe", { encoding: "utf-8" }).trim();
543
- } catch {
544
- errors.push("Could not find 'exe' in PATH");
546
+ const exeBinPath = findExeBin();
547
+ if (!exeBinPath) {
548
+ errors.push("Could not find 'exe-os' in PATH");
545
549
  return { created, skipped, errors };
546
550
  }
547
551
  const binDir = path5.dirname(exeBinPath);
@@ -589,6 +593,7 @@ __export(employee_templates_exports, {
589
593
  buildCustomEmployeePrompt: () => buildCustomEmployeePrompt,
590
594
  getSessionPrompt: () => getSessionPrompt,
591
595
  getTemplate: () => getTemplate,
596
+ getTemplateByRole: () => getTemplateByRole,
592
597
  personalizePrompt: () => personalizePrompt,
593
598
  renderClientCOOTemplate: () => renderClientCOOTemplate
594
599
  });
@@ -604,6 +609,10 @@ function buildCustomEmployeePrompt(name, role) {
604
609
  function getTemplate(name) {
605
610
  return TEMPLATES[name];
606
611
  }
612
+ function getTemplateByRole(role) {
613
+ const lower = role.toLowerCase();
614
+ return Object.values(TEMPLATES).find((t) => t.role.toLowerCase() === lower);
615
+ }
607
616
  function personalizePrompt(prompt, templateName, actualName) {
608
617
  if (templateName === actualName) return prompt;
609
618
  const escaped = templateName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -1240,7 +1249,7 @@ __export(active_agent_exports, {
1240
1249
  getAllActiveAgents: () => getAllActiveAgents,
1241
1250
  writeActiveAgent: () => writeActiveAgent
1242
1251
  });
1243
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3, unlinkSync as unlinkSync2, readdirSync as readdirSync2 } from "fs";
1252
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync2, mkdirSync as mkdirSync3, unlinkSync as unlinkSync2, readdirSync as readdirSync3 } from "fs";
1244
1253
  import { execSync as execSync4 } from "child_process";
1245
1254
  import path6 from "path";
1246
1255
  function getMarkerPath() {
@@ -1311,7 +1320,7 @@ function getActiveAgent() {
1311
1320
  }
1312
1321
  function getAllActiveAgents() {
1313
1322
  try {
1314
- const files = readdirSync2(CACHE_DIR);
1323
+ const files = readdirSync3(CACHE_DIR);
1315
1324
  const sessions = [];
1316
1325
  for (const file of files) {
1317
1326
  if (!file.startsWith("active-agent-") || !file.endsWith(".json")) continue;
@@ -1368,9 +1377,9 @@ var init_active_agent = __esm({
1368
1377
  });
1369
1378
 
1370
1379
  // src/bin/exe-launch-agent.ts
1371
- import os3 from "os";
1380
+ import os4 from "os";
1372
1381
  import path7 from "path";
1373
- import { existsSync as existsSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, readdirSync as readdirSync3 } from "fs";
1382
+ import { existsSync as existsSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, readdirSync as readdirSync4 } from "fs";
1374
1383
  import { spawnSync } from "child_process";
1375
1384
 
1376
1385
  // src/lib/database.ts
@@ -1460,6 +1469,7 @@ async function ensureSchema() {
1460
1469
  const client = getRawClient();
1461
1470
  await client.execute("PRAGMA journal_mode = WAL");
1462
1471
  await client.execute("PRAGMA busy_timeout = 30000");
1472
+ await client.execute("PRAGMA wal_autocheckpoint = 1000");
1463
1473
  try {
1464
1474
  await client.execute("PRAGMA libsql_vector_search_ef = 128");
1465
1475
  } catch {
@@ -2261,11 +2271,12 @@ async function disposeDatabase() {
2261
2271
  import { readFile, writeFile, unlink, mkdir, chmod } from "fs/promises";
2262
2272
  import { existsSync } from "fs";
2263
2273
  import path from "path";
2274
+ import os from "os";
2264
2275
  import crypto from "crypto";
2265
2276
  var SERVICE = "exe-mem";
2266
2277
  var ACCOUNT = "master-key";
2267
2278
  function getKeyDir() {
2268
- return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(process.env.HOME ?? "/tmp", ".exe-os");
2279
+ return process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path.join(os.homedir(), ".exe-os");
2269
2280
  }
2270
2281
  function getKeyPath() {
2271
2282
  return path.join(getKeyDir(), "master.key");
@@ -2302,6 +2313,30 @@ async function getMasterKey() {
2302
2313
 
2303
2314
  // src/lib/store.ts
2304
2315
  init_config();
2316
+ var INIT_MAX_RETRIES = 3;
2317
+ var INIT_RETRY_DELAY_MS = 1e3;
2318
+ function isBusyError2(err) {
2319
+ if (err instanceof Error) {
2320
+ const msg = err.message.toLowerCase();
2321
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
2322
+ }
2323
+ return false;
2324
+ }
2325
+ async function retryOnBusy2(fn, label) {
2326
+ for (let attempt = 0; attempt <= INIT_MAX_RETRIES; attempt++) {
2327
+ try {
2328
+ return await fn();
2329
+ } catch (err) {
2330
+ if (!isBusyError2(err) || attempt === INIT_MAX_RETRIES) throw err;
2331
+ process.stderr.write(
2332
+ `[store] SQLITE_BUSY during ${label}, retry ${attempt + 1}/${INIT_MAX_RETRIES}
2333
+ `
2334
+ );
2335
+ await new Promise((r) => setTimeout(r, INIT_RETRY_DELAY_MS * (attempt + 1)));
2336
+ }
2337
+ }
2338
+ throw new Error("unreachable");
2339
+ }
2305
2340
  var _pendingRecords = [];
2306
2341
  var _batchSize = 20;
2307
2342
  var _flushIntervalMs = 1e4;
@@ -2336,14 +2371,17 @@ async function initStore(options) {
2336
2371
  dbPath,
2337
2372
  encryptionKey: hexKey
2338
2373
  });
2339
- await ensureSchema();
2374
+ await retryOnBusy2(() => ensureSchema(), "ensureSchema");
2340
2375
  try {
2341
2376
  const { initShardManager: initShardManager2 } = await Promise.resolve().then(() => (init_shard_manager(), shard_manager_exports));
2342
2377
  initShardManager2(hexKey);
2343
2378
  } catch {
2344
2379
  }
2345
2380
  const client = getClient();
2346
- const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
2381
+ const vResult = await retryOnBusy2(
2382
+ () => client.execute("SELECT MAX(version) as max_v FROM memories"),
2383
+ "version-query"
2384
+ );
2347
2385
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
2348
2386
  }
2349
2387
  async function flushBatch() {
@@ -2495,12 +2533,12 @@ function vectorToBlob(vector) {
2495
2533
  }
2496
2534
 
2497
2535
  // src/lib/behaviors-export.ts
2498
- import os2 from "os";
2536
+ import os3 from "os";
2499
2537
  import path4 from "path";
2500
2538
  import {
2501
2539
  existsSync as existsSync4,
2502
2540
  mkdirSync as mkdirSync2,
2503
- readdirSync,
2541
+ readdirSync as readdirSync2,
2504
2542
  statSync,
2505
2543
  unlinkSync,
2506
2544
  writeFileSync
@@ -2538,7 +2576,7 @@ async function listBehaviors(agentId, projectName, limit = 30) {
2538
2576
 
2539
2577
  // src/lib/behaviors-export.ts
2540
2578
  var BEHAVIORS_EXPORT_DIR = path4.join(
2541
- os2.homedir(),
2579
+ os3.homedir(),
2542
2580
  ".exe-os",
2543
2581
  "behaviors-export"
2544
2582
  );
@@ -2548,7 +2586,7 @@ function sweepStaleBehaviorExports(now = Date.now()) {
2548
2586
  if (!existsSync4(BEHAVIORS_EXPORT_DIR)) return;
2549
2587
  let entries;
2550
2588
  try {
2551
- entries = readdirSync(BEHAVIORS_EXPORT_DIR);
2589
+ entries = readdirSync2(BEHAVIORS_EXPORT_DIR);
2552
2590
  } catch {
2553
2591
  return;
2554
2592
  }
@@ -2627,6 +2665,7 @@ function claudeSupportsAgentFlag() {
2627
2665
 
2628
2666
  // src/bin/exe-launch-agent.ts
2629
2667
  init_employees();
2668
+ import { execSync as execSync5 } from "child_process";
2630
2669
 
2631
2670
  // src/lib/provider-table.ts
2632
2671
  var PROVIDER_TABLE = {
@@ -2642,7 +2681,9 @@ var DEFAULT_PROVIDER = "default";
2642
2681
  function getKnownAgents() {
2643
2682
  try {
2644
2683
  return loadEmployeesSync().map((e) => e.name);
2645
- } catch {
2684
+ } catch (err) {
2685
+ process.stderr.write(`[exe-launch-agent] roster load failed, using default: ${err instanceof Error ? err.message : String(err)}
2686
+ `);
2646
2687
  return ["exe"];
2647
2688
  }
2648
2689
  }
@@ -2681,22 +2722,44 @@ async function isKnownAgent(agent) {
2681
2722
  }
2682
2723
  }
2683
2724
  function identityPathFor(agent) {
2684
- return path7.join(os3.homedir(), ".exe-os", "identity", `${agent}.md`);
2725
+ return path7.join(os4.homedir(), ".exe-os", "identity", `${agent}.md`);
2685
2726
  }
2686
2727
  function leanMcpConfigFor(agent) {
2687
- const p = path7.join(os3.homedir(), ".exe-os", "mcp-configs", `${agent}-lean.json`);
2728
+ const p = path7.join(os4.homedir(), ".exe-os", "mcp-configs", `${agent}-lean.json`);
2688
2729
  return existsSync6(p) ? p : null;
2689
2730
  }
2731
+ var _ccHelpOutput = null;
2732
+ function getCcHelpOutput() {
2733
+ if (_ccHelpOutput === null) {
2734
+ try {
2735
+ const result = execSync5("claude --help 2>&1", { encoding: "utf8", timeout: 5e3 });
2736
+ _ccHelpOutput = typeof result === "string" ? result : "";
2737
+ } catch {
2738
+ _ccHelpOutput = "";
2739
+ }
2740
+ }
2741
+ return _ccHelpOutput;
2742
+ }
2743
+ function ccSupportsFlag(flag) {
2744
+ return getCcHelpOutput().includes(flag);
2745
+ }
2746
+ function _resetCcHelpCache() {
2747
+ _ccHelpOutput = null;
2748
+ }
2690
2749
  function buildLaunchPlan(agent, behaviorsPath, passthrough, _hasAgentFlag, _provider) {
2691
2750
  const args = ["--dangerously-skip-permissions"];
2692
2751
  const idPath = identityPathFor(agent);
2693
- const ccAgentPath = path7.join(os3.homedir(), ".claude", "agents", `${agent}.md`);
2752
+ const ccAgentPath = path7.join(os4.homedir(), ".claude", "agents", `${agent}.md`);
2694
2753
  const effectiveIdPath = existsSync6(idPath) ? idPath : existsSync6(ccAgentPath) ? ccAgentPath : null;
2695
2754
  if (effectiveIdPath) {
2696
- try {
2697
- const identity = readFileSync4(effectiveIdPath, "utf-8");
2698
- args.push("--system-prompt", identity);
2699
- } catch {
2755
+ if (ccSupportsFlag("--system-prompt")) {
2756
+ try {
2757
+ const identity = readFileSync4(effectiveIdPath, "utf-8");
2758
+ args.push("--system-prompt", identity);
2759
+ } catch {
2760
+ args.push("--append-system-prompt-file", effectiveIdPath);
2761
+ }
2762
+ } else {
2700
2763
  args.push("--append-system-prompt-file", effectiveIdPath);
2701
2764
  }
2702
2765
  }
@@ -2705,7 +2768,15 @@ function buildLaunchPlan(agent, behaviorsPath, passthrough, _hasAgentFlag, _prov
2705
2768
  }
2706
2769
  const leanMcp = leanMcpConfigFor(agent);
2707
2770
  if (leanMcp) {
2708
- args.push("--mcp-config", leanMcp, "--strict-mcp-config");
2771
+ if (ccSupportsFlag("--mcp-config")) {
2772
+ args.push("--mcp-config", leanMcp);
2773
+ if (ccSupportsFlag("--strict-mcp-config")) {
2774
+ args.push("--strict-mcp-config");
2775
+ }
2776
+ } else {
2777
+ process.stderr.write(`[exe-launch-agent] CC lacks --mcp-config \u2014 skipping lean MCP for ${agent}
2778
+ `);
2779
+ }
2709
2780
  }
2710
2781
  for (const arg of passthrough) args.push(arg);
2711
2782
  return { command: "claude", args };
@@ -2810,7 +2881,7 @@ async function main() {
2810
2881
  _resetCcAgentSupportCache();
2811
2882
  const hasAgentFlag = claudeSupportsAgentFlag();
2812
2883
  if (hasAgentFlag) {
2813
- const ccAgentDir = path7.join(os3.homedir(), ".claude", "agents");
2884
+ const ccAgentDir = path7.join(os4.homedir(), ".claude", "agents");
2814
2885
  const ccAgentFile = path7.join(ccAgentDir, `${agent}.md`);
2815
2886
  if (!existsSync6(ccAgentFile)) {
2816
2887
  const exeIdentity = identityPathFor(agent);
@@ -2820,7 +2891,7 @@ async function main() {
2820
2891
  } else {
2821
2892
  try {
2822
2893
  const identityDir = path7.dirname(exeIdentity);
2823
- const files = readdirSync3(identityDir);
2894
+ const files = readdirSync4(identityDir);
2824
2895
  const match = files.find((f) => f.toLowerCase() === `${agent.toLowerCase()}.md`);
2825
2896
  if (match) sourceFile = path7.join(identityDir, match);
2826
2897
  } catch {
@@ -2846,7 +2917,7 @@ async function main() {
2846
2917
  const empRole = (() => {
2847
2918
  try {
2848
2919
  const emps = __require("fs").readFileSync(
2849
- path7.join(os3.homedir(), ".exe-os", "exe-employees.json"),
2920
+ path7.join(os4.homedir(), ".exe-os", "exe-employees.json"),
2850
2921
  "utf-8"
2851
2922
  );
2852
2923
  const found = JSON.parse(emps).find(
@@ -2890,8 +2961,10 @@ if (!process.env.VITEST) {
2890
2961
  export {
2891
2962
  DEFAULT_PROVIDER,
2892
2963
  PROVIDER_TABLE,
2964
+ _resetCcHelpCache,
2893
2965
  applyProviderEnv,
2894
2966
  buildLaunchPlan,
2967
+ ccSupportsFlag,
2895
2968
  parseBasename,
2896
2969
  resolveAgent
2897
2970
  };