@askexenow/exe-os 0.8.83 → 0.8.86

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 (103) hide show
  1. package/dist/bin/backfill-conversations.js +746 -595
  2. package/dist/bin/backfill-responses.js +745 -594
  3. package/dist/bin/backfill-vectors.js +312 -226
  4. package/dist/bin/cleanup-stale-review-tasks.js +154 -21
  5. package/dist/bin/cli.js +14678 -12676
  6. package/dist/bin/exe-agent-config.js +242 -0
  7. package/dist/bin/exe-agent.js +100 -91
  8. package/dist/bin/exe-assign.js +1003 -854
  9. package/dist/bin/exe-boot.js +1420 -485
  10. package/dist/bin/exe-call.js +10 -0
  11. package/dist/bin/exe-cloud.js +29 -6
  12. package/dist/bin/exe-dispatch.js +572 -271
  13. package/dist/bin/exe-doctor.js +403 -6
  14. package/dist/bin/exe-export-behaviors.js +175 -72
  15. package/dist/bin/exe-forget.js +102 -3
  16. package/dist/bin/exe-gateway.js +796 -292
  17. package/dist/bin/exe-healthcheck.js +134 -1
  18. package/dist/bin/exe-heartbeat.js +172 -36
  19. package/dist/bin/exe-kill.js +175 -72
  20. package/dist/bin/exe-launch-agent.js +189 -76
  21. package/dist/bin/exe-link.js +927 -82
  22. package/dist/bin/exe-new-employee.js +60 -8
  23. package/dist/bin/exe-pending-messages.js +151 -19
  24. package/dist/bin/exe-pending-notifications.js +97 -2
  25. package/dist/bin/exe-pending-reviews.js +155 -22
  26. package/dist/bin/exe-rename.js +564 -23
  27. package/dist/bin/exe-review.js +231 -73
  28. package/dist/bin/exe-search.js +995 -228
  29. package/dist/bin/exe-session-cleanup.js +4930 -1664
  30. package/dist/bin/exe-settings.js +20 -5
  31. package/dist/bin/exe-start-codex.js +2598 -0
  32. package/dist/bin/exe-start.sh +15 -3
  33. package/dist/bin/exe-status.js +154 -21
  34. package/dist/bin/exe-team.js +97 -2
  35. package/dist/bin/git-sweep.js +1180 -363
  36. package/dist/bin/graph-backfill.js +175 -72
  37. package/dist/bin/graph-export.js +175 -72
  38. package/dist/bin/install.js +60 -7
  39. package/dist/bin/list-providers.js +1 -0
  40. package/dist/bin/scan-tasks.js +1185 -367
  41. package/dist/bin/setup.js +914 -270
  42. package/dist/bin/shard-migrate.js +175 -72
  43. package/dist/bin/update.js +1 -0
  44. package/dist/bin/wiki-sync.js +175 -72
  45. package/dist/gateway/index.js +792 -285
  46. package/dist/hooks/bug-report-worker.js +445 -135
  47. package/dist/hooks/commit-complete.js +1178 -361
  48. package/dist/hooks/error-recall.js +994 -228
  49. package/dist/hooks/ingest-worker.js +1799 -1234
  50. package/dist/hooks/ingest.js +3 -0
  51. package/dist/hooks/instructions-loaded.js +707 -97
  52. package/dist/hooks/notification.js +699 -89
  53. package/dist/hooks/post-compact.js +757 -109
  54. package/dist/hooks/pre-compact.js +1061 -244
  55. package/dist/hooks/pre-tool-use.js +787 -130
  56. package/dist/hooks/prompt-ingest-worker.js +242 -101
  57. package/dist/hooks/prompt-submit.js +1121 -299
  58. package/dist/hooks/response-ingest-worker.js +242 -101
  59. package/dist/hooks/session-end.js +4063 -397
  60. package/dist/hooks/session-start.js +1071 -254
  61. package/dist/hooks/stop.js +768 -120
  62. package/dist/hooks/subagent-stop.js +757 -109
  63. package/dist/hooks/summary-worker.js +1706 -1011
  64. package/dist/index.js +1821 -1098
  65. package/dist/lib/agent-config.js +167 -0
  66. package/dist/lib/cloud-sync.js +932 -88
  67. package/dist/lib/consolidation.js +2 -1
  68. package/dist/lib/database.js +642 -87
  69. package/dist/lib/db-daemon-client.js +503 -0
  70. package/dist/lib/device-registry.js +547 -7
  71. package/dist/lib/embedder.js +14 -28
  72. package/dist/lib/employee-templates.js +84 -74
  73. package/dist/lib/employees.js +9 -0
  74. package/dist/lib/exe-daemon-client.js +16 -29
  75. package/dist/lib/exe-daemon.js +2733 -1575
  76. package/dist/lib/hybrid-search.js +995 -228
  77. package/dist/lib/identity.js +87 -67
  78. package/dist/lib/keychain.js +9 -1
  79. package/dist/lib/messaging.js +103 -40
  80. package/dist/lib/reminders.js +91 -74
  81. package/dist/lib/runtime-table.js +16 -0
  82. package/dist/lib/schedules.js +96 -2
  83. package/dist/lib/session-wrappers.js +22 -0
  84. package/dist/lib/skill-learning.js +103 -85
  85. package/dist/lib/store.js +234 -73
  86. package/dist/lib/tasks.js +348 -134
  87. package/dist/lib/tmux-routing.js +422 -208
  88. package/dist/lib/token-spend.js +273 -0
  89. package/dist/lib/ws-client.js +11 -0
  90. package/dist/mcp/server.js +5742 -696
  91. package/dist/mcp/tools/complete-reminder.js +94 -77
  92. package/dist/mcp/tools/create-reminder.js +94 -77
  93. package/dist/mcp/tools/create-task.js +375 -152
  94. package/dist/mcp/tools/deactivate-behavior.js +95 -77
  95. package/dist/mcp/tools/list-reminders.js +94 -77
  96. package/dist/mcp/tools/list-tasks.js +99 -31
  97. package/dist/mcp/tools/send-message.js +108 -45
  98. package/dist/mcp/tools/update-task.js +162 -77
  99. package/dist/runtime/index.js +1075 -258
  100. package/dist/tui/App.js +1333 -506
  101. package/package.json +6 -1
  102. package/src/commands/exe/agent-config.md +27 -0
  103. package/src/commands/exe/cc-doctor.md +10 -0
@@ -262,15 +262,22 @@ function getClient() {
262
262
  if (!_resilientClient) {
263
263
  throw new Error("Database client not initialized. Call initDatabase() first.");
264
264
  }
265
+ if (process.env.EXE_IS_DAEMON === "1") {
266
+ return _resilientClient;
267
+ }
268
+ if (_daemonClient && _daemonClient._isDaemonActive()) {
269
+ return _daemonClient;
270
+ }
265
271
  return _resilientClient;
266
272
  }
267
- var _resilientClient;
273
+ var _resilientClient, _daemonClient;
268
274
  var init_database = __esm({
269
275
  "src/lib/database.ts"() {
270
276
  "use strict";
271
277
  init_db_retry();
272
278
  init_employees();
273
279
  _resilientClient = null;
280
+ _daemonClient = null;
274
281
  }
275
282
  });
276
283
 
@@ -563,18 +570,54 @@ var init_provider_table = __esm({
563
570
  }
564
571
  });
565
572
 
566
- // src/lib/intercom-queue.ts
567
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, renameSync as renameSync3, existsSync as existsSync4, mkdirSync } from "fs";
573
+ // src/lib/runtime-table.ts
574
+ var RUNTIME_TABLE;
575
+ var init_runtime_table = __esm({
576
+ "src/lib/runtime-table.ts"() {
577
+ "use strict";
578
+ RUNTIME_TABLE = {
579
+ codex: {
580
+ binary: "codex",
581
+ launchMode: "exec",
582
+ autoApproveFlag: "--full-auto",
583
+ inlineFlag: "--no-alt-screen",
584
+ apiKeyEnv: "OPENAI_API_KEY",
585
+ defaultModel: "gpt-5.4"
586
+ }
587
+ };
588
+ }
589
+ });
590
+
591
+ // src/lib/agent-config.ts
592
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync4, mkdirSync } from "fs";
568
593
  import path5 from "path";
594
+ var AGENT_CONFIG_PATH, DEFAULT_MODELS;
595
+ var init_agent_config = __esm({
596
+ "src/lib/agent-config.ts"() {
597
+ "use strict";
598
+ init_config();
599
+ init_runtime_table();
600
+ AGENT_CONFIG_PATH = path5.join(EXE_AI_DIR, "agent-config.json");
601
+ DEFAULT_MODELS = {
602
+ claude: "claude-opus-4",
603
+ codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
604
+ opencode: "minimax-m2.7"
605
+ };
606
+ }
607
+ });
608
+
609
+ // src/lib/intercom-queue.ts
610
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, renameSync as renameSync3, existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
611
+ import path6 from "path";
569
612
  import os5 from "os";
570
613
  function ensureDir() {
571
- const dir = path5.dirname(QUEUE_PATH);
572
- if (!existsSync4(dir)) mkdirSync(dir, { recursive: true });
614
+ const dir = path6.dirname(QUEUE_PATH);
615
+ if (!existsSync5(dir)) mkdirSync2(dir, { recursive: true });
573
616
  }
574
617
  function readQueue() {
575
618
  try {
576
- if (!existsSync4(QUEUE_PATH)) return [];
577
- return JSON.parse(readFileSync4(QUEUE_PATH, "utf8"));
619
+ if (!existsSync5(QUEUE_PATH)) return [];
620
+ return JSON.parse(readFileSync5(QUEUE_PATH, "utf8"));
578
621
  } catch {
579
622
  return [];
580
623
  }
@@ -582,7 +625,7 @@ function readQueue() {
582
625
  function writeQueue(queue) {
583
626
  ensureDir();
584
627
  const tmp = `${QUEUE_PATH}.tmp`;
585
- writeFileSync2(tmp, JSON.stringify(queue, null, 2));
628
+ writeFileSync3(tmp, JSON.stringify(queue, null, 2));
586
629
  renameSync3(tmp, QUEUE_PATH);
587
630
  }
588
631
  function queueIntercom(targetSession, reason) {
@@ -606,31 +649,31 @@ var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
606
649
  var init_intercom_queue = __esm({
607
650
  "src/lib/intercom-queue.ts"() {
608
651
  "use strict";
609
- QUEUE_PATH = path5.join(os5.homedir(), ".exe-os", "intercom-queue.json");
652
+ QUEUE_PATH = path6.join(os5.homedir(), ".exe-os", "intercom-queue.json");
610
653
  TTL_MS = 60 * 60 * 1e3;
611
- INTERCOM_LOG = path5.join(os5.homedir(), ".exe-os", "intercom.log");
654
+ INTERCOM_LOG = path6.join(os5.homedir(), ".exe-os", "intercom.log");
612
655
  }
613
656
  });
614
657
 
615
658
  // src/lib/license.ts
616
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
659
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
617
660
  import { randomUUID } from "crypto";
618
- import path6 from "path";
661
+ import path7 from "path";
619
662
  import { jwtVerify, importSPKI } from "jose";
620
663
  var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH;
621
664
  var init_license = __esm({
622
665
  "src/lib/license.ts"() {
623
666
  "use strict";
624
667
  init_config();
625
- LICENSE_PATH = path6.join(EXE_AI_DIR, "license.key");
626
- CACHE_PATH = path6.join(EXE_AI_DIR, "license-cache.json");
627
- DEVICE_ID_PATH = path6.join(EXE_AI_DIR, "device-id");
668
+ LICENSE_PATH = path7.join(EXE_AI_DIR, "license.key");
669
+ CACHE_PATH = path7.join(EXE_AI_DIR, "license-cache.json");
670
+ DEVICE_ID_PATH = path7.join(EXE_AI_DIR, "device-id");
628
671
  }
629
672
  });
630
673
 
631
674
  // src/lib/plan-limits.ts
632
- import { readFileSync as readFileSync6, existsSync as existsSync6 } from "fs";
633
- import path7 from "path";
675
+ import { readFileSync as readFileSync7, existsSync as existsSync7 } from "fs";
676
+ import path8 from "path";
634
677
  var CACHE_PATH2;
635
678
  var init_plan_limits = __esm({
636
679
  "src/lib/plan-limits.ts"() {
@@ -639,13 +682,13 @@ var init_plan_limits = __esm({
639
682
  init_employees();
640
683
  init_license();
641
684
  init_config();
642
- CACHE_PATH2 = path7.join(EXE_AI_DIR, "license-cache.json");
685
+ CACHE_PATH2 = path8.join(EXE_AI_DIR, "license-cache.json");
643
686
  }
644
687
  });
645
688
 
646
689
  // src/lib/tmux-routing.ts
647
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3, existsSync as existsSync7, appendFileSync } from "fs";
648
- import path8 from "path";
690
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, existsSync as existsSync8, appendFileSync } from "fs";
691
+ import path9 from "path";
649
692
  import os6 from "os";
650
693
  import { fileURLToPath } from "url";
651
694
  function getMySession() {
@@ -659,7 +702,7 @@ function extractRootExe(name) {
659
702
  }
660
703
  function getParentExe(sessionKey) {
661
704
  try {
662
- const data = JSON.parse(readFileSync7(path8.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
705
+ const data = JSON.parse(readFileSync8(path9.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
663
706
  return data.parentExe || null;
664
707
  } catch {
665
708
  return null;
@@ -667,8 +710,8 @@ function getParentExe(sessionKey) {
667
710
  }
668
711
  function getDispatchedBy(sessionKey) {
669
712
  try {
670
- const data = JSON.parse(readFileSync7(
671
- path8.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
713
+ const data = JSON.parse(readFileSync8(
714
+ path9.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
672
715
  "utf8"
673
716
  ));
674
717
  return data.dispatchedBy ?? data.parentExe ?? null;
@@ -691,32 +734,50 @@ function resolveExeSession() {
691
734
  }
692
735
  function readDebounceState() {
693
736
  try {
694
- if (!existsSync7(DEBOUNCE_FILE)) return {};
695
- return JSON.parse(readFileSync7(DEBOUNCE_FILE, "utf8"));
737
+ if (!existsSync8(DEBOUNCE_FILE)) return {};
738
+ const raw = JSON.parse(readFileSync8(DEBOUNCE_FILE, "utf8"));
739
+ const state = {};
740
+ for (const [key, val] of Object.entries(raw)) {
741
+ if (typeof val === "number") {
742
+ state[key] = { lastSent: val, pending: 0 };
743
+ } else if (val && typeof val === "object" && "lastSent" in val) {
744
+ state[key] = val;
745
+ }
746
+ }
747
+ return state;
696
748
  } catch {
697
749
  return {};
698
750
  }
699
751
  }
700
752
  function writeDebounceState(state) {
701
753
  try {
702
- if (!existsSync7(SESSION_CACHE)) mkdirSync3(SESSION_CACHE, { recursive: true });
703
- writeFileSync4(DEBOUNCE_FILE, JSON.stringify(state));
754
+ if (!existsSync8(SESSION_CACHE)) mkdirSync4(SESSION_CACHE, { recursive: true });
755
+ writeFileSync5(DEBOUNCE_FILE, JSON.stringify(state));
704
756
  } catch {
705
757
  }
706
758
  }
707
759
  function isDebounced(targetSession) {
708
760
  const state = readDebounceState();
709
- const lastSent = state[targetSession] ?? 0;
710
- return Date.now() - lastSent < INTERCOM_DEBOUNCE_MS;
761
+ const entry = state[targetSession];
762
+ const lastSent = entry?.lastSent ?? 0;
763
+ if (Date.now() - lastSent < INTERCOM_DEBOUNCE_MS) {
764
+ if (!state[targetSession]) state[targetSession] = { lastSent, pending: 0 };
765
+ state[targetSession].pending++;
766
+ writeDebounceState(state);
767
+ return true;
768
+ }
769
+ return false;
711
770
  }
712
771
  function recordDebounce(targetSession) {
713
772
  const state = readDebounceState();
714
- state[targetSession] = Date.now();
773
+ const batched = state[targetSession]?.pending ?? 0;
774
+ state[targetSession] = { lastSent: Date.now(), pending: 0 };
715
775
  const cutoff = Date.now() - DEBOUNCE_CLEANUP_AGE_MS;
716
776
  for (const key of Object.keys(state)) {
717
- if ((state[key] ?? 0) < cutoff) delete state[key];
777
+ if ((state[key]?.lastSent ?? 0) < cutoff) delete state[key];
718
778
  }
719
779
  writeDebounceState(state);
780
+ return batched;
720
781
  }
721
782
  function logIntercom(msg) {
722
783
  const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}
@@ -757,7 +818,7 @@ function sendIntercom(targetSession) {
757
818
  return "skipped_exe";
758
819
  }
759
820
  if (isDebounced(targetSession)) {
760
- logIntercom(`DEBOUNCE \u2192 ${targetSession} (cross-process file debounce)`);
821
+ logIntercom(`DEBOUNCE \u2192 ${targetSession} (nudge batched, task safe in DB)`);
761
822
  return "debounced";
762
823
  }
763
824
  try {
@@ -769,14 +830,14 @@ function sendIntercom(targetSession) {
769
830
  const sessionState = getSessionState(targetSession);
770
831
  if (sessionState === "no_claude") {
771
832
  queueIntercom(targetSession, "claude not running in session");
772
- recordDebounce(targetSession);
773
- logIntercom(`QUEUED \u2192 ${targetSession} (no claude process \u2014 raw shell detected)`);
833
+ const batched2 = recordDebounce(targetSession);
834
+ logIntercom(`QUEUED \u2192 ${targetSession} (no claude process)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
774
835
  return "queued";
775
836
  }
776
837
  if (sessionState === "thinking" || sessionState === "tool") {
777
838
  queueIntercom(targetSession, "session busy at send time");
778
- recordDebounce(targetSession);
779
- logIntercom(`QUEUED \u2192 ${targetSession} (session busy, will retry from queue)`);
839
+ const batched2 = recordDebounce(targetSession);
840
+ logIntercom(`QUEUED \u2192 ${targetSession} (session busy)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
780
841
  return "queued";
781
842
  }
782
843
  if (transport.isPaneInCopyMode(targetSession)) {
@@ -784,8 +845,8 @@ function sendIntercom(targetSession) {
784
845
  transport.sendKeys(targetSession, "q");
785
846
  }
786
847
  transport.sendKeys(targetSession, "/exe-intercom");
787
- recordDebounce(targetSession);
788
- logIntercom(`DELIVERED \u2192 ${targetSession} (fire-and-forget)`);
848
+ const batched = recordDebounce(targetSession);
849
+ logIntercom(`DELIVERED \u2192 ${targetSession}${batched > 0 ? ` [${batched} nudges batched during debounce]` : ""} (fire-and-forget)`);
789
850
  return "delivered";
790
851
  } catch {
791
852
  logIntercom(`FAIL \u2192 ${targetSession}`);
@@ -824,14 +885,16 @@ var init_tmux_routing = __esm({
824
885
  init_cc_agent_support();
825
886
  init_mcp_prefix();
826
887
  init_provider_table();
888
+ init_agent_config();
889
+ init_runtime_table();
827
890
  init_intercom_queue();
828
891
  init_plan_limits();
829
892
  init_employees();
830
- SPAWN_LOCK_DIR = path8.join(os6.homedir(), ".exe-os", "spawn-locks");
831
- SESSION_CACHE = path8.join(os6.homedir(), ".exe-os", "session-cache");
893
+ SPAWN_LOCK_DIR = path9.join(os6.homedir(), ".exe-os", "spawn-locks");
894
+ SESSION_CACHE = path9.join(os6.homedir(), ".exe-os", "session-cache");
832
895
  INTERCOM_DEBOUNCE_MS = 3e4;
833
- INTERCOM_LOG2 = path8.join(os6.homedir(), ".exe-os", "intercom.log");
834
- DEBOUNCE_FILE = path8.join(SESSION_CACHE, "intercom-debounce.json");
896
+ INTERCOM_LOG2 = path9.join(os6.homedir(), ".exe-os", "intercom.log");
897
+ DEBOUNCE_FILE = path9.join(SESSION_CACHE, "intercom-debounce.json");
835
898
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
836
899
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
837
900
  }
@@ -863,10 +926,11 @@ var init_task_scope = __esm({
863
926
 
864
927
  // src/lib/tasks-crud.ts
865
928
  import crypto2 from "crypto";
866
- import path9 from "path";
929
+ import path10 from "path";
930
+ import os7 from "os";
867
931
  import { execSync as execSync4 } from "child_process";
868
932
  import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
869
- import { existsSync as existsSync8, readFileSync as readFileSync8 } from "fs";
933
+ import { existsSync as existsSync9, readFileSync as readFileSync9 } from "fs";
870
934
  async function writeCheckpoint(input) {
871
935
  const client = getClient();
872
936
  const row = await resolveTask(client, input.taskId);
@@ -897,6 +961,17 @@ async function writeCheckpoint(input) {
897
961
  const checkpointCount = Number(countResult.rows[0]?.checkpoint_count ?? 1);
898
962
  return { checkpointCount };
899
963
  }
964
+ function buildKeywordIndex() {
965
+ const idx = /* @__PURE__ */ new Map();
966
+ for (const [role, keywords] of Object.entries(LANE_KEYWORDS)) {
967
+ for (const kw of keywords) {
968
+ const existing = idx.get(kw) ?? [];
969
+ existing.push(role);
970
+ idx.set(kw, existing);
971
+ }
972
+ }
973
+ return idx;
974
+ }
900
975
  async function resolveTask(client, identifier, scopeSession) {
901
976
  const scope = sessionScopeFilter(scopeSession);
902
977
  let result = await client.execute({
@@ -1065,7 +1140,7 @@ ${input.result}` : `\u26A0\uFE0F ${warning}`;
1065
1140
  return { row, taskFile, now, taskId };
1066
1141
  }
1067
1142
  }
1068
- if (curStatus === "in_progress" && input.callerAgentId && (input.callerAgentId === assignedBy || input.callerAgentId === "exe")) {
1143
+ if (curStatus === "in_progress" && input.callerAgentId && (input.callerAgentId === assignedBy || isCoordinatorName(input.callerAgentId))) {
1069
1144
  process.stderr.write(
1070
1145
  `[tasks] Assigner override: ${input.callerAgentId} reclaiming ${taskId}
1071
1146
  `
@@ -1118,20 +1193,30 @@ ${input.result}` : `\u26A0\uFE0F ${warning}`;
1118
1193
  }
1119
1194
  return { row, taskFile, now, taskId };
1120
1195
  }
1121
- var DELEGATION_KEYWORDS, TASK_ALREADY_CLAIMED_PREFIX;
1196
+ var LANE_KEYWORDS, KEYWORD_INDEX, DELEGATION_KEYWORDS, TASK_ALREADY_CLAIMED_PREFIX;
1122
1197
  var init_tasks_crud = __esm({
1123
1198
  "src/lib/tasks-crud.ts"() {
1124
1199
  "use strict";
1125
1200
  init_database();
1126
1201
  init_task_scope();
1202
+ init_employees();
1203
+ LANE_KEYWORDS = {
1204
+ CMO: ["sales", "script", "pitch", "offer", "copy", "objection", "brand", "content", "seo", "marketing", "newsletter", "carousel", "social", "campaign"],
1205
+ CTO: ["spec", "architecture", "migration", "schema", "database", "design doc", "adr", "security audit", "tech stack"],
1206
+ "Principal Engineer": ["implement", "build", "fix", "commit", "refactor", "bug", "feature", "wire", "integration"],
1207
+ "Staff Code Reviewer": ["critique", "verdict", "review", "audit", "code quality"],
1208
+ "Content Production Specialist": ["render", "video", "image", "b-roll", "remotion", "animation", "thumbnail"],
1209
+ "AI Product Lead": ["competitive", "analysis", "benchmark", "compare", "scout", "evaluate", "poc"]
1210
+ };
1211
+ KEYWORD_INDEX = buildKeywordIndex();
1127
1212
  DELEGATION_KEYWORDS = /parallel|delegate|wave|worktree|multi-instance/i;
1128
1213
  TASK_ALREADY_CLAIMED_PREFIX = "TASK_ALREADY_CLAIMED";
1129
1214
  }
1130
1215
  });
1131
1216
 
1132
1217
  // src/lib/tasks-review.ts
1133
- import path10 from "path";
1134
- import { existsSync as existsSync9, readdirSync as readdirSync2, unlinkSync as unlinkSync3 } from "fs";
1218
+ import path11 from "path";
1219
+ import { existsSync as existsSync10, readdirSync as readdirSync2, unlinkSync as unlinkSync3 } from "fs";
1135
1220
  async function cleanupReviewFile(row, taskFile, _baseDir) {
1136
1221
  if (String(row.assigned_by) !== "system" || !taskFile.includes("review-")) return;
1137
1222
  try {
@@ -1156,14 +1241,14 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
1156
1241
  if (parts.length >= 3 && parts[0] === "review") {
1157
1242
  const agent = parts[1];
1158
1243
  const slug = parts.slice(2).join("-");
1159
- const originalTaskFile = `exe/${agent}/${slug}.md`;
1244
+ const legacyTaskFile = `exe/${agent}/${slug}.md`;
1160
1245
  const result = await client.execute({
1161
- sql: "UPDATE tasks SET status = 'done', updated_at = ? WHERE task_file = ? AND status = 'needs_review'",
1162
- args: [now, originalTaskFile]
1246
+ sql: "UPDATE tasks SET status = 'done', updated_at = ? WHERE (task_file = ? OR task_file LIKE ?) AND status = 'needs_review'",
1247
+ args: [now, legacyTaskFile, `tasks/%/${agent}/${slug}.md`]
1163
1248
  });
1164
1249
  if (result.rowsAffected > 0) {
1165
1250
  process.stderr.write(
1166
- `[review-cleanup] Cascaded original task to done (legacy path): ${originalTaskFile}
1251
+ `[review-cleanup] Cascaded original task to done: ${agent}/${slug}.md
1167
1252
  `
1168
1253
  );
1169
1254
  }
@@ -1176,11 +1261,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
1176
1261
  );
1177
1262
  }
1178
1263
  try {
1179
- const cacheDir = path10.join(EXE_AI_DIR, "session-cache");
1180
- if (existsSync9(cacheDir)) {
1264
+ const cacheDir = path11.join(EXE_AI_DIR, "session-cache");
1265
+ if (existsSync10(cacheDir)) {
1181
1266
  for (const f of readdirSync2(cacheDir)) {
1182
1267
  if (f.startsWith("review-notified-")) {
1183
- unlinkSync3(path10.join(cacheDir, f));
1268
+ unlinkSync3(path11.join(cacheDir, f));
1184
1269
  }
1185
1270
  }
1186
1271
  }
@@ -1201,7 +1286,7 @@ var init_tasks_review = __esm({
1201
1286
  });
1202
1287
 
1203
1288
  // src/lib/tasks-chain.ts
1204
- import path11 from "path";
1289
+ import path12 from "path";
1205
1290
  import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
1206
1291
  async function cascadeUnblock(taskId, baseDir, now) {
1207
1292
  const client = getClient();
@@ -1218,7 +1303,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
1218
1303
  });
1219
1304
  for (const ur of unblockedRows.rows) {
1220
1305
  try {
1221
- const ubFile = path11.join(baseDir, String(ur.task_file));
1306
+ const ubFile = path12.join(baseDir, String(ur.task_file));
1222
1307
  let ubContent = await readFile3(ubFile, "utf-8");
1223
1308
  ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
1224
1309
  ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
@@ -1621,17 +1706,17 @@ var init_skill_learning = __esm({
1621
1706
  });
1622
1707
 
1623
1708
  // src/lib/tasks.ts
1624
- import path12 from "path";
1625
- import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, unlinkSync as unlinkSync4 } from "fs";
1709
+ import path13 from "path";
1710
+ import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, unlinkSync as unlinkSync4 } from "fs";
1626
1711
  async function updateTask(input) {
1627
1712
  const { row, taskFile, now, taskId } = await updateTaskStatus(input);
1628
1713
  try {
1629
1714
  const agent = String(row.assigned_to);
1630
- const cacheDir = path12.join(EXE_AI_DIR, "session-cache");
1631
- const cachePath = path12.join(cacheDir, `current-task-${agent}.json`);
1715
+ const cacheDir = path13.join(EXE_AI_DIR, "session-cache");
1716
+ const cachePath = path13.join(cacheDir, `current-task-${agent}.json`);
1632
1717
  if (input.status === "in_progress") {
1633
- mkdirSync4(cacheDir, { recursive: true });
1634
- writeFileSync5(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
1718
+ mkdirSync5(cacheDir, { recursive: true });
1719
+ writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
1635
1720
  } else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
1636
1721
  try {
1637
1722
  unlinkSync4(cachePath);
@@ -1691,7 +1776,7 @@ async function updateTask(input) {
1691
1776
  }
1692
1777
  const isTerminal = input.status === "done" || input.status === "needs_review";
1693
1778
  if (isTerminal) {
1694
- const isCoordinator = String(row.assigned_to) === "exe" || isCoordinatorName(String(row.assigned_to));
1779
+ const isCoordinator = isCoordinatorName(String(row.assigned_to));
1695
1780
  if (!isCoordinator) {
1696
1781
  notifyTaskDone();
1697
1782
  }
@@ -1716,7 +1801,7 @@ async function updateTask(input) {
1716
1801
  }
1717
1802
  }
1718
1803
  }
1719
- if (input.status === "done" && String(row.assigned_to) !== "exe" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
1804
+ if (input.status === "done" && !isCoordinatorName(String(row.assigned_to)) && !process.env.VITEST) {
1720
1805
  Promise.resolve().then(() => (init_skill_learning(), skill_learning_exports)).then(
1721
1806
  ({ captureAndLearn: captureAndLearn2 }) => captureAndLearn2({
1722
1807
  taskId,
@@ -1732,7 +1817,7 @@ async function updateTask(input) {
1732
1817
  });
1733
1818
  }
1734
1819
  let nextTask;
1735
- if (isTerminal && String(row.assigned_to) !== "exe" && !isCoordinatorName(String(row.assigned_to))) {
1820
+ if (isTerminal && !isCoordinatorName(String(row.assigned_to))) {
1736
1821
  try {
1737
1822
  nextTask = await findNextTask(String(row.assigned_to));
1738
1823
  } catch {
@@ -1791,9 +1876,9 @@ __export(active_agent_exports, {
1791
1876
  resolveActiveAgentFromTmuxSession: () => resolveActiveAgentFromTmuxSession,
1792
1877
  writeActiveAgent: () => writeActiveAgent
1793
1878
  });
1794
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, unlinkSync as unlinkSync5, readdirSync as readdirSync3 } from "fs";
1879
+ import { readFileSync as readFileSync10, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, unlinkSync as unlinkSync5, readdirSync as readdirSync3 } from "fs";
1795
1880
  import { execSync as execSync5 } from "child_process";
1796
- import path13 from "path";
1881
+ import path14 from "path";
1797
1882
  function isNameWithOptionalInstance(candidate, baseName) {
1798
1883
  if (candidate === baseName) return true;
1799
1884
  if (!candidate.startsWith(baseName)) return false;
@@ -1837,12 +1922,12 @@ function resolveActiveAgentFromTmuxSession(sessionName) {
1837
1922
  return null;
1838
1923
  }
1839
1924
  function getMarkerPath() {
1840
- return path13.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
1925
+ return path14.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
1841
1926
  }
1842
1927
  function writeActiveAgent(agentId, agentRole) {
1843
1928
  try {
1844
- mkdirSync5(CACHE_DIR, { recursive: true });
1845
- writeFileSync6(
1929
+ mkdirSync6(CACHE_DIR, { recursive: true });
1930
+ writeFileSync7(
1846
1931
  getMarkerPath(),
1847
1932
  JSON.stringify({ agentId, agentRole, startedAt: (/* @__PURE__ */ new Date()).toISOString() })
1848
1933
  );
@@ -1858,7 +1943,7 @@ function clearActiveAgent() {
1858
1943
  function getActiveAgent() {
1859
1944
  try {
1860
1945
  const markerPath = getMarkerPath();
1861
- const raw = readFileSync9(markerPath, "utf8");
1946
+ const raw = readFileSync10(markerPath, "utf8");
1862
1947
  const data = JSON.parse(raw);
1863
1948
  if (data.agentId) {
1864
1949
  if (data.startedAt) {
@@ -1906,14 +1991,14 @@ function getAllActiveAgents() {
1906
1991
  const key = file.slice("active-agent-".length, -".json".length);
1907
1992
  if (key === "undefined") continue;
1908
1993
  try {
1909
- const raw = readFileSync9(path13.join(CACHE_DIR, file), "utf8");
1994
+ const raw = readFileSync10(path14.join(CACHE_DIR, file), "utf8");
1910
1995
  const data = JSON.parse(raw);
1911
1996
  if (!data.agentId) continue;
1912
1997
  if (data.startedAt) {
1913
1998
  const age = Date.now() - new Date(data.startedAt).getTime();
1914
1999
  if (age > STALE_MS) {
1915
2000
  try {
1916
- unlinkSync5(path13.join(CACHE_DIR, file));
2001
+ unlinkSync5(path14.join(CACHE_DIR, file));
1917
2002
  } catch {
1918
2003
  }
1919
2004
  continue;
@@ -1936,11 +2021,11 @@ function getAllActiveAgents() {
1936
2021
  function cleanupSessionMarkers() {
1937
2022
  const key = getSessionKey();
1938
2023
  try {
1939
- unlinkSync5(path13.join(CACHE_DIR, `active-agent-${key}.json`));
2024
+ unlinkSync5(path14.join(CACHE_DIR, `active-agent-${key}.json`));
1940
2025
  } catch {
1941
2026
  }
1942
2027
  try {
1943
- unlinkSync5(path13.join(CACHE_DIR, "active-agent-undefined.json"));
2028
+ unlinkSync5(path14.join(CACHE_DIR, "active-agent-undefined.json"));
1944
2029
  } catch {
1945
2030
  }
1946
2031
  }
@@ -1951,7 +2036,7 @@ var init_active_agent = __esm({
1951
2036
  init_config();
1952
2037
  init_session_key2();
1953
2038
  init_employees();
1954
- CACHE_DIR = path13.join(EXE_AI_DIR, "session-cache");
2039
+ CACHE_DIR = path14.join(EXE_AI_DIR, "session-cache");
1955
2040
  STALE_MS = 24 * 60 * 60 * 1e3;
1956
2041
  }
1957
2042
  });