@askexenow/exe-os 0.8.106 → 0.8.107

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.
package/dist/bin/cli.js CHANGED
@@ -6436,6 +6436,23 @@ var backfill_metadata_exports = {};
6436
6436
  __export(backfill_metadata_exports, {
6437
6437
  backfillMetadata: () => backfillMetadata
6438
6438
  });
6439
+ function roleAgentMarkers(role, fallbackName) {
6440
+ try {
6441
+ const employees = loadEmployeesSync();
6442
+ const employee = getEmployeeByRole(employees, role);
6443
+ const configuredName = employee?.name?.toLowerCase().trim();
6444
+ if (configuredName) {
6445
+ return [configuredName];
6446
+ }
6447
+ } catch {
6448
+ }
6449
+ return [fallbackName];
6450
+ }
6451
+ function mentionsAgentMarker(text, markers) {
6452
+ return markers.some(
6453
+ (marker) => text.includes(`/${marker}/`) || text.includes(`agent_id: ${marker}`)
6454
+ );
6455
+ }
6439
6456
  function classifyMemoryHeuristic(rawText, toolName) {
6440
6457
  const t = rawText.toLowerCase();
6441
6458
  let intent;
@@ -6467,7 +6484,7 @@ function classifyMemoryHeuristic(rawText, toolName) {
6467
6484
  outcome = "partial";
6468
6485
  }
6469
6486
  let domain;
6470
- if (t.includes("marketing") || t.includes("campaign") || t.includes("brand") || t.includes("carousel") || t.includes("seo") || t.includes("content strategy") || t.includes("/mari/") || t.includes("agent_id: mari")) {
6487
+ if (t.includes("marketing") || t.includes("campaign") || t.includes("brand") || t.includes("carousel") || t.includes("seo") || t.includes("content strategy") || mentionsAgentMarker(t, MARKETING_AGENT_MARKERS)) {
6471
6488
  domain = "marketing";
6472
6489
  } else if (t.includes("architecture") || t.includes("adr") || t.includes("design doc") || t.includes("system design") || t.includes("ARCHITECTURE.md") || t.includes("schema") && t.includes("migration")) {
6473
6490
  domain = "architecture";
@@ -6475,7 +6492,7 @@ function classifyMemoryHeuristic(rawText, toolName) {
6475
6492
  domain = "customer";
6476
6493
  } else if (t.includes("deploy") || t.includes("ci/cd") || t.includes("docker") || t.includes("nginx") || t.includes("cron") || t.includes("schedule") || t.includes("task management") || t.includes("roster") || toolName === "update_task" || toolName === "create_task") {
6477
6494
  domain = "operations";
6478
- } else if (t.includes("research") || t.includes("competitor") || t.includes("benchmark") || t.includes("comparison") || t.includes("analysis") || t.includes("evaluate") || t.includes("/gen/") || t.includes("agent_id: gen")) {
6495
+ } else if (t.includes("research") || t.includes("competitor") || t.includes("benchmark") || t.includes("comparison") || t.includes("analysis") || t.includes("evaluate") || mentionsAgentMarker(t, RESEARCH_AGENT_MARKERS)) {
6479
6496
  domain = "research";
6480
6497
  } else {
6481
6498
  domain = "code";
@@ -6973,13 +6990,14 @@ async function backfillMetadata(options) {
6973
6990
  await disposeStore();
6974
6991
  return { classified, skipped, failed, entitiesPopulated, remaining };
6975
6992
  }
6976
- var VALID_INTENTS, VALID_OUTCOMES, VALID_DOMAINS, VALID_AUDIENCES, VALID_LANGUAGE_TYPES, MAX_TEXT_LENGTH, BATCH_SLEEP_MS;
6993
+ var VALID_INTENTS, VALID_OUTCOMES, VALID_DOMAINS, VALID_AUDIENCES, VALID_LANGUAGE_TYPES, MAX_TEXT_LENGTH, BATCH_SLEEP_MS, MARKETING_AGENT_MARKERS, RESEARCH_AGENT_MARKERS;
6977
6994
  var init_backfill_metadata = __esm({
6978
6995
  "src/bin/backfill-metadata.ts"() {
6979
6996
  "use strict";
6980
6997
  init_store();
6981
6998
  init_database();
6982
6999
  init_is_main();
7000
+ init_employees();
6983
7001
  VALID_INTENTS = /* @__PURE__ */ new Set(["decision", "question", "correction", "report", "discovery", "implementation"]);
6984
7002
  VALID_OUTCOMES = /* @__PURE__ */ new Set(["success", "failure", "partial", "superseded"]);
6985
7003
  VALID_DOMAINS = /* @__PURE__ */ new Set(["code", "architecture", "marketing", "operations", "customer", "research"]);
@@ -6987,6 +7005,8 @@ var init_backfill_metadata = __esm({
6987
7005
  VALID_LANGUAGE_TYPES = /* @__PURE__ */ new Set(["code", "prose", "mixed", "json", "sql"]);
6988
7006
  MAX_TEXT_LENGTH = 2e3;
6989
7007
  BATCH_SLEEP_MS = 1e3;
7008
+ MARKETING_AGENT_MARKERS = roleAgentMarkers("CMO", "mari");
7009
+ RESEARCH_AGENT_MARKERS = roleAgentMarkers("AI Product Lead", "gen");
6990
7010
  if (isMainModule(import.meta.url)) {
6991
7011
  const options = parseArgs2(process.argv.slice(2));
6992
7012
  backfillMetadata(options).then((result) => {
@@ -9790,7 +9810,8 @@ function sendIntercom(targetSession) {
9790
9810
  logIntercom(`COPY_MODE \u2192 ${targetSession} (exiting copy mode first)`);
9791
9811
  transport.sendKeys(targetSession, "q");
9792
9812
  }
9793
- const agentName = targetSession.split("-")[0] ?? targetSession;
9813
+ const rawAgentName = targetSession.split("-")[0] ?? targetSession;
9814
+ const agentName = baseAgentName(rawAgentName);
9794
9815
  const rtConfig = getAgentRuntime(agentName);
9795
9816
  if (rtConfig.runtime === "codex" || rtConfig.runtime === "opencode") {
9796
9817
  transport.sendKeys(targetSession, "NEW TASK ASSIGNED. Call list_tasks now, then get_task on the highest priority open task. Start working immediately \u2014 do not ask for confirmation.");
@@ -10186,7 +10207,7 @@ var init_tmux_routing = __esm({
10186
10207
  INTERCOM_LOG2 = path22.join(os11.homedir(), ".exe-os", "intercom.log");
10187
10208
  DEBOUNCE_FILE = path22.join(SESSION_CACHE, "intercom-debounce.json");
10188
10209
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
10189
- BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
10210
+ BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
10190
10211
  }
10191
10212
  });
10192
10213
 
@@ -10839,9 +10860,9 @@ Your domain:
10839
10860
  - Performance: bottleneck analysis, scaling, caching
10840
10861
  - DevOps: CI/CD, deployment, monitoring and alerting
10841
10862
 
10842
- FEATURE DEVELOPMENT \u2014 use the exe-build-e2e pipeline:
10843
- For ANY new feature, enhancement, or significant change, invoke the exe-build-e2e skill:
10844
- /exe-build-e2e "<feature description>"
10863
+ FEATURE DEVELOPMENT \u2014 use exe-build-adv:
10864
+ For ANY new feature, enhancement, or significant change, run:
10865
+ /exe-build-adv --auto "<feature description>"
10845
10866
 
10846
10867
  This runs the full pipeline: spec \u2192 acceptance criteria \u2192 tests \u2192 implementation \u2192 verification.
10847
10868
  It is NOT optional for feature work. Bug fixes and small patches can skip it, but anything that
@@ -20799,13 +20820,23 @@ function stripMediaBlocks(messages) {
20799
20820
  function isMediaTool(name) {
20800
20821
  return ["upload_image", "screenshot", "render_image"].includes(name);
20801
20822
  }
20802
- async function compactMessages(messages, provider, summaryModel = "claude-haiku-4-5-20251001") {
20823
+ async function compactMessages(messages, provider, summaryModel) {
20803
20824
  if (messages.length <= KEEP_RECENT_MESSAGES) {
20804
20825
  return { messages, removedCount: 0 };
20805
20826
  }
20806
20827
  const toSummarize = messages.slice(0, -KEEP_RECENT_MESSAGES);
20807
20828
  const toKeep = messages.slice(-KEEP_RECENT_MESSAGES);
20808
20829
  const stripped = stripMediaBlocks(toSummarize);
20830
+ if (!summaryModel) {
20831
+ return {
20832
+ messages: [
20833
+ { role: "user", content: "[Earlier conversation history was compacted due to context limits]" },
20834
+ { role: "assistant", content: "Understood. Continuing with recent context." },
20835
+ ...toKeep
20836
+ ],
20837
+ removedCount: toSummarize.length
20838
+ };
20839
+ }
20809
20840
  const serialized = stripped.map((m) => {
20810
20841
  const content = typeof m.content === "string" ? m.content : m.content.map((b) => {
20811
20842
  if (b.type === "text") return b.text;
@@ -20881,8 +20912,8 @@ var init_context_manager = __esm({
20881
20912
  "claude-sonnet-4": 2e5,
20882
20913
  "claude-haiku-4": 2e5,
20883
20914
  // Extended context variants
20884
- "claude-opus-4-6[1m]": 1e6,
20885
- "claude-sonnet-4-6[1m]": 1e6,
20915
+ "claude-opus-4-6": 1e6,
20916
+ "claude-sonnet-4-6": 1e6,
20886
20917
  // OpenAI
20887
20918
  "gpt-4o": 128e3,
20888
20919
  "gpt-4-turbo": 128e3,
@@ -21151,7 +21182,7 @@ async function* agentLoop(userMessage, history, config) {
21151
21182
  `Pre-compaction: extracted ${memoriesToExtract.length} key decisions to memory`
21152
21183
  );
21153
21184
  }
21154
- const compacted = await compactMessages(messages, config.provider);
21185
+ const compacted = await compactMessages(messages, config.provider, config.model);
21155
21186
  const removedCount = compacted.removedCount;
21156
21187
  if (removedCount > 0) {
21157
21188
  messages.length = 0;
@@ -328,13 +328,23 @@ function stripMediaBlocks(messages) {
328
328
  function isMediaTool(name) {
329
329
  return ["upload_image", "screenshot", "render_image"].includes(name);
330
330
  }
331
- async function compactMessages(messages, provider, summaryModel = "claude-haiku-4-5-20251001") {
331
+ async function compactMessages(messages, provider, summaryModel) {
332
332
  if (messages.length <= KEEP_RECENT_MESSAGES) {
333
333
  return { messages, removedCount: 0 };
334
334
  }
335
335
  const toSummarize = messages.slice(0, -KEEP_RECENT_MESSAGES);
336
336
  const toKeep = messages.slice(-KEEP_RECENT_MESSAGES);
337
337
  const stripped = stripMediaBlocks(toSummarize);
338
+ if (!summaryModel) {
339
+ return {
340
+ messages: [
341
+ { role: "user", content: "[Earlier conversation history was compacted due to context limits]" },
342
+ { role: "assistant", content: "Understood. Continuing with recent context." },
343
+ ...toKeep
344
+ ],
345
+ removedCount: toSummarize.length
346
+ };
347
+ }
338
348
  const serialized = stripped.map((m) => {
339
349
  const content = typeof m.content === "string" ? m.content : m.content.map((b) => {
340
350
  if (b.type === "text") return b.text;
@@ -381,8 +391,8 @@ var MODEL_CONTEXT_LIMITS = {
381
391
  "claude-sonnet-4": 2e5,
382
392
  "claude-haiku-4": 2e5,
383
393
  // Extended context variants
384
- "claude-opus-4-6[1m]": 1e6,
385
- "claude-sonnet-4-6[1m]": 1e6,
394
+ "claude-opus-4-6": 1e6,
395
+ "claude-sonnet-4-6": 1e6,
386
396
  // OpenAI
387
397
  "gpt-4o": 128e3,
388
398
  "gpt-4-turbo": 128e3,
@@ -656,7 +666,7 @@ async function* agentLoop(userMessage, history, config) {
656
666
  `Pre-compaction: extracted ${memoriesToExtract.length} key decisions to memory`
657
667
  );
658
668
  }
659
- const compacted = await compactMessages(messages, config.provider);
669
+ const compacted = await compactMessages(messages, config.provider, config.model);
660
670
  const removedCount = compacted.removedCount;
661
671
  if (removedCount > 0) {
662
672
  messages.length = 0;
@@ -314,6 +314,14 @@ function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
314
314
  function getEmployee(employees, name) {
315
315
  return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
316
316
  }
317
+ function baseAgentName(name, employees) {
318
+ const match = name.match(/^([a-zA-Z]+)\d+$/);
319
+ if (!match) return name;
320
+ const base = match[1];
321
+ const roster = employees ?? loadEmployeesSync();
322
+ if (getEmployee(roster, base)) return base;
323
+ return name;
324
+ }
317
325
  function isMultiInstance(agentName, employees) {
318
326
  const roster = employees ?? loadEmployeesSync();
319
327
  const emp = getEmployee(roster, agentName);
@@ -5963,7 +5971,8 @@ function sendIntercom(targetSession) {
5963
5971
  logIntercom(`COPY_MODE \u2192 ${targetSession} (exiting copy mode first)`);
5964
5972
  transport.sendKeys(targetSession, "q");
5965
5973
  }
5966
- const agentName = targetSession.split("-")[0] ?? targetSession;
5974
+ const rawAgentName = targetSession.split("-")[0] ?? targetSession;
5975
+ const agentName = baseAgentName(rawAgentName);
5967
5976
  const rtConfig = getAgentRuntime(agentName);
5968
5977
  if (rtConfig.runtime === "codex" || rtConfig.runtime === "opencode") {
5969
5978
  transport.sendKeys(targetSession, "NEW TASK ASSIGNED. Call list_tasks now, then get_task on the highest priority open task. Start working immediately \u2014 do not ask for confirmation.");
@@ -6359,7 +6368,7 @@ var init_tmux_routing = __esm({
6359
6368
  INTERCOM_LOG2 = path17.join(os9.homedir(), ".exe-os", "intercom.log");
6360
6369
  DEBOUNCE_FILE = path17.join(SESSION_CACHE, "intercom-debounce.json");
6361
6370
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
6362
- BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
6371
+ BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
6363
6372
  }
6364
6373
  });
6365
6374
 
@@ -688,9 +688,9 @@ Your domain:
688
688
  - Performance: bottleneck analysis, scaling, caching
689
689
  - DevOps: CI/CD, deployment, monitoring and alerting
690
690
 
691
- FEATURE DEVELOPMENT \u2014 use the exe-build-e2e pipeline:
692
- For ANY new feature, enhancement, or significant change, invoke the exe-build-e2e skill:
693
- /exe-build-e2e "<feature description>"
691
+ FEATURE DEVELOPMENT \u2014 use exe-build-adv:
692
+ For ANY new feature, enhancement, or significant change, run:
693
+ /exe-build-adv --auto "<feature description>"
694
694
 
695
695
  This runs the full pipeline: spec \u2192 acceptance criteria \u2192 tests \u2192 implementation \u2192 verification.
696
696
  It is NOT optional for feature work. Bug fixes and small patches can skip it, but anything that
@@ -683,6 +683,14 @@ function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
683
683
  function getEmployee(employees, name) {
684
684
  return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
685
685
  }
686
+ function baseAgentName(name, employees) {
687
+ const match = name.match(/^([a-zA-Z]+)\d+$/);
688
+ if (!match) return name;
689
+ const base = match[1];
690
+ const roster = employees ?? loadEmployeesSync();
691
+ if (getEmployee(roster, base)) return base;
692
+ return name;
693
+ }
686
694
  function isMultiInstance(agentName, employees) {
687
695
  const roster = employees ?? loadEmployeesSync();
688
696
  const emp = getEmployee(roster, agentName);
@@ -4129,7 +4137,8 @@ function sendIntercom(targetSession) {
4129
4137
  logIntercom(`COPY_MODE \u2192 ${targetSession} (exiting copy mode first)`);
4130
4138
  transport.sendKeys(targetSession, "q");
4131
4139
  }
4132
- const agentName = targetSession.split("-")[0] ?? targetSession;
4140
+ const rawAgentName = targetSession.split("-")[0] ?? targetSession;
4141
+ const agentName = baseAgentName(rawAgentName);
4133
4142
  const rtConfig = getAgentRuntime(agentName);
4134
4143
  if (rtConfig.runtime === "codex" || rtConfig.runtime === "opencode") {
4135
4144
  transport.sendKeys(targetSession, "NEW TASK ASSIGNED. Call list_tasks now, then get_task on the highest priority open task. Start working immediately \u2014 do not ask for confirmation.");
@@ -4525,7 +4534,7 @@ var init_tmux_routing = __esm({
4525
4534
  INTERCOM_LOG2 = path14.join(os7.homedir(), ".exe-os", "intercom.log");
4526
4535
  DEBOUNCE_FILE = path14.join(SESSION_CACHE, "intercom-debounce.json");
4527
4536
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
4528
- BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
4537
+ BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
4529
4538
  }
4530
4539
  });
4531
4540
 
@@ -5909,12 +5909,12 @@ var init_email = __esm({
5909
5909
  console.error("[email] Message handler error:", err);
5910
5910
  }
5911
5911
  }
5912
- async sendText(channelId, text, _options) {
5912
+ async sendText(channelId, text, options) {
5913
5913
  if (!this.transporter) throw new Error("Email not connected");
5914
5914
  await this.transporter.sendMail({
5915
5915
  from: this.fromAddress,
5916
5916
  to: channelId,
5917
- subject: "Reply from Exe OS",
5917
+ subject: options?.subject?.trim() || "Reply",
5918
5918
  text
5919
5919
  });
5920
5920
  }
@@ -8862,7 +8862,8 @@ function sendIntercom(targetSession) {
8862
8862
  logIntercom(`COPY_MODE \u2192 ${targetSession} (exiting copy mode first)`);
8863
8863
  transport.sendKeys(targetSession, "q");
8864
8864
  }
8865
- const agentName = targetSession.split("-")[0] ?? targetSession;
8865
+ const rawAgentName = targetSession.split("-")[0] ?? targetSession;
8866
+ const agentName = baseAgentName(rawAgentName);
8866
8867
  const rtConfig = getAgentRuntime(agentName);
8867
8868
  if (rtConfig.runtime === "codex" || rtConfig.runtime === "opencode") {
8868
8869
  transport.sendKeys(targetSession, "NEW TASK ASSIGNED. Call list_tasks now, then get_task on the highest priority open task. Start working immediately \u2014 do not ask for confirmation.");
@@ -9258,7 +9259,7 @@ var init_tmux_routing = __esm({
9258
9259
  INTERCOM_LOG2 = path18.join(os10.homedir(), ".exe-os", "intercom.log");
9259
9260
  DEBOUNCE_FILE = path18.join(SESSION_CACHE, "intercom-debounce.json");
9260
9261
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
9261
- BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
9262
+ BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
9262
9263
  }
9263
9264
  });
9264
9265
 
@@ -2111,9 +2111,9 @@ Your domain:
2111
2111
  - Performance: bottleneck analysis, scaling, caching
2112
2112
  - DevOps: CI/CD, deployment, monitoring and alerting
2113
2113
 
2114
- FEATURE DEVELOPMENT \u2014 use the exe-build-e2e pipeline:
2115
- For ANY new feature, enhancement, or significant change, invoke the exe-build-e2e skill:
2116
- /exe-build-e2e "<feature description>"
2114
+ FEATURE DEVELOPMENT \u2014 use exe-build-adv:
2115
+ For ANY new feature, enhancement, or significant change, run:
2116
+ /exe-build-adv --auto "<feature description>"
2117
2117
 
2118
2118
  This runs the full pipeline: spec \u2192 acceptance criteria \u2192 tests \u2192 implementation \u2192 verification.
2119
2119
  It is NOT optional for feature work. Bug fixes and small patches can skip it, but anything that
@@ -1866,9 +1866,9 @@ Your domain:
1866
1866
  - Performance: bottleneck analysis, scaling, caching
1867
1867
  - DevOps: CI/CD, deployment, monitoring and alerting
1868
1868
 
1869
- FEATURE DEVELOPMENT \u2014 use the exe-build-e2e pipeline:
1870
- For ANY new feature, enhancement, or significant change, invoke the exe-build-e2e skill:
1871
- /exe-build-e2e "<feature description>"
1869
+ FEATURE DEVELOPMENT \u2014 use exe-build-adv:
1870
+ For ANY new feature, enhancement, or significant change, run:
1871
+ /exe-build-adv --auto "<feature description>"
1872
1872
 
1873
1873
  This runs the full pipeline: spec \u2192 acceptance criteria \u2192 tests \u2192 implementation \u2192 verification.
1874
1874
  It is NOT optional for feature work. Bug fixes and small patches can skip it, but anything that
@@ -368,6 +368,14 @@ function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
368
368
  function getEmployee(employees, name) {
369
369
  return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
370
370
  }
371
+ function baseAgentName(name, employees) {
372
+ const match = name.match(/^([a-zA-Z]+)\d+$/);
373
+ if (!match) return name;
374
+ const base = match[1];
375
+ const roster = employees ?? loadEmployeesSync();
376
+ if (getEmployee(roster, base)) return base;
377
+ return name;
378
+ }
371
379
  function isMultiInstance(agentName2, employees) {
372
380
  const roster = employees ?? loadEmployeesSync();
373
381
  const emp = getEmployee(roster, agentName2);
@@ -5965,7 +5973,8 @@ function sendIntercom(targetSession) {
5965
5973
  logIntercom(`COPY_MODE \u2192 ${targetSession} (exiting copy mode first)`);
5966
5974
  transport.sendKeys(targetSession, "q");
5967
5975
  }
5968
- const agentName2 = targetSession.split("-")[0] ?? targetSession;
5976
+ const rawAgentName = targetSession.split("-")[0] ?? targetSession;
5977
+ const agentName2 = baseAgentName(rawAgentName);
5969
5978
  const rtConfig = getAgentRuntime(agentName2);
5970
5979
  if (rtConfig.runtime === "codex" || rtConfig.runtime === "opencode") {
5971
5980
  transport.sendKeys(targetSession, "NEW TASK ASSIGNED. Call list_tasks now, then get_task on the highest priority open task. Start working immediately \u2014 do not ask for confirmation.");
@@ -6361,7 +6370,7 @@ var init_tmux_routing = __esm({
6361
6370
  INTERCOM_LOG2 = path17.join(os9.homedir(), ".exe-os", "intercom.log");
6362
6371
  DEBOUNCE_FILE = path17.join(SESSION_CACHE, "intercom-debounce.json");
6363
6372
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
6364
- BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
6373
+ BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
6365
6374
  }
6366
6375
  });
6367
6376
 
@@ -683,6 +683,14 @@ function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
683
683
  function getEmployee(employees, name) {
684
684
  return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
685
685
  }
686
+ function baseAgentName(name, employees) {
687
+ const match = name.match(/^([a-zA-Z]+)\d+$/);
688
+ if (!match) return name;
689
+ const base = match[1];
690
+ const roster = employees ?? loadEmployeesSync();
691
+ if (getEmployee(roster, base)) return base;
692
+ return name;
693
+ }
686
694
  function isMultiInstance(agentName, employees) {
687
695
  const roster = employees ?? loadEmployeesSync();
688
696
  const emp = getEmployee(roster, agentName);
@@ -4617,7 +4625,8 @@ function sendIntercom(targetSession) {
4617
4625
  logIntercom(`COPY_MODE \u2192 ${targetSession} (exiting copy mode first)`);
4618
4626
  transport.sendKeys(targetSession, "q");
4619
4627
  }
4620
- const agentName = targetSession.split("-")[0] ?? targetSession;
4628
+ const rawAgentName = targetSession.split("-")[0] ?? targetSession;
4629
+ const agentName = baseAgentName(rawAgentName);
4621
4630
  const rtConfig = getAgentRuntime(agentName);
4622
4631
  if (rtConfig.runtime === "codex" || rtConfig.runtime === "opencode") {
4623
4632
  transport.sendKeys(targetSession, "NEW TASK ASSIGNED. Call list_tasks now, then get_task on the highest priority open task. Start working immediately \u2014 do not ask for confirmation.");
@@ -5013,7 +5022,7 @@ var init_tmux_routing = __esm({
5013
5022
  INTERCOM_LOG2 = path15.join(os8.homedir(), ".exe-os", "intercom.log");
5014
5023
  DEBOUNCE_FILE = path15.join(SESSION_CACHE, "intercom-debounce.json");
5015
5024
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
5016
- BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
5025
+ BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
5017
5026
  }
5018
5027
  });
5019
5028
 
@@ -695,6 +695,14 @@ function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
695
695
  function getEmployee(employees, name) {
696
696
  return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
697
697
  }
698
+ function baseAgentName(name, employees) {
699
+ const match = name.match(/^([a-zA-Z]+)\d+$/);
700
+ if (!match) return name;
701
+ const base = match[1];
702
+ const roster = employees ?? loadEmployeesSync();
703
+ if (getEmployee(roster, base)) return base;
704
+ return name;
705
+ }
698
706
  function isMultiInstance(agentName, employees) {
699
707
  const roster = employees ?? loadEmployeesSync();
700
708
  const emp = getEmployee(roster, agentName);
@@ -4622,7 +4630,8 @@ function sendIntercom(targetSession) {
4622
4630
  logIntercom(`COPY_MODE \u2192 ${targetSession} (exiting copy mode first)`);
4623
4631
  transport.sendKeys(targetSession, "q");
4624
4632
  }
4625
- const agentName = targetSession.split("-")[0] ?? targetSession;
4633
+ const rawAgentName = targetSession.split("-")[0] ?? targetSession;
4634
+ const agentName = baseAgentName(rawAgentName);
4626
4635
  const rtConfig = getAgentRuntime(agentName);
4627
4636
  if (rtConfig.runtime === "codex" || rtConfig.runtime === "opencode") {
4628
4637
  transport.sendKeys(targetSession, "NEW TASK ASSIGNED. Call list_tasks now, then get_task on the highest priority open task. Start working immediately \u2014 do not ask for confirmation.");
@@ -5018,7 +5027,7 @@ var init_tmux_routing = __esm({
5018
5027
  INTERCOM_LOG2 = path15.join(os8.homedir(), ".exe-os", "intercom.log");
5019
5028
  DEBOUNCE_FILE = path15.join(SESSION_CACHE, "intercom-debounce.json");
5020
5029
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
5021
- BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
5030
+ BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
5022
5031
  }
5023
5032
  });
5024
5033
 
package/dist/bin/setup.js CHANGED
@@ -4452,9 +4452,9 @@ Your domain:
4452
4452
  - Performance: bottleneck analysis, scaling, caching
4453
4453
  - DevOps: CI/CD, deployment, monitoring and alerting
4454
4454
 
4455
- FEATURE DEVELOPMENT \u2014 use the exe-build-e2e pipeline:
4456
- For ANY new feature, enhancement, or significant change, invoke the exe-build-e2e skill:
4457
- /exe-build-e2e "<feature description>"
4455
+ FEATURE DEVELOPMENT \u2014 use exe-build-adv:
4456
+ For ANY new feature, enhancement, or significant change, run:
4457
+ /exe-build-adv --auto "<feature description>"
4458
4458
 
4459
4459
  This runs the full pipeline: spec \u2192 acceptance criteria \u2192 tests \u2192 implementation \u2192 verification.
4460
4460
  It is NOT optional for feature work. Bug fixes and small patches can skip it, but anything that
@@ -7204,7 +7204,8 @@ function sendIntercom(targetSession) {
7204
7204
  logIntercom(`COPY_MODE \u2192 ${targetSession} (exiting copy mode first)`);
7205
7205
  transport.sendKeys(targetSession, "q");
7206
7206
  }
7207
- const agentName = targetSession.split("-")[0] ?? targetSession;
7207
+ const rawAgentName = targetSession.split("-")[0] ?? targetSession;
7208
+ const agentName = baseAgentName(rawAgentName);
7208
7209
  const rtConfig = getAgentRuntime(agentName);
7209
7210
  if (rtConfig.runtime === "codex" || rtConfig.runtime === "opencode") {
7210
7211
  transport.sendKeys(targetSession, "NEW TASK ASSIGNED. Call list_tasks now, then get_task on the highest priority open task. Start working immediately \u2014 do not ask for confirmation.");
@@ -7600,7 +7601,7 @@ var init_tmux_routing = __esm({
7600
7601
  INTERCOM_LOG2 = path18.join(os10.homedir(), ".exe-os", "intercom.log");
7601
7602
  DEBOUNCE_FILE = path18.join(SESSION_CACHE, "intercom-debounce.json");
7602
7603
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
7603
- BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
7604
+ BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
7604
7605
  }
7605
7606
  });
7606
7607
 
@@ -3136,7 +3136,8 @@ function sendIntercom(targetSession) {
3136
3136
  logIntercom(`COPY_MODE \u2192 ${targetSession} (exiting copy mode first)`);
3137
3137
  transport.sendKeys(targetSession, "q");
3138
3138
  }
3139
- const agentName = targetSession.split("-")[0] ?? targetSession;
3139
+ const rawAgentName = targetSession.split("-")[0] ?? targetSession;
3140
+ const agentName = baseAgentName(rawAgentName);
3140
3141
  const rtConfig = getAgentRuntime(agentName);
3141
3142
  if (rtConfig.runtime === "codex" || rtConfig.runtime === "opencode") {
3142
3143
  transport.sendKeys(targetSession, "NEW TASK ASSIGNED. Call list_tasks now, then get_task on the highest priority open task. Start working immediately \u2014 do not ask for confirmation.");
@@ -3532,7 +3533,7 @@ var init_tmux_routing = __esm({
3532
3533
  INTERCOM_LOG2 = path11.join(os7.homedir(), ".exe-os", "intercom.log");
3533
3534
  DEBOUNCE_FILE = path11.join(SESSION_CACHE, "intercom-debounce.json");
3534
3535
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
3535
- BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
3536
+ BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
3536
3537
  }
3537
3538
  });
3538
3539
 
@@ -682,6 +682,14 @@ function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
682
682
  function getEmployee(employees, name) {
683
683
  return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
684
684
  }
685
+ function baseAgentName(name, employees) {
686
+ const match = name.match(/^([a-zA-Z]+)\d+$/);
687
+ if (!match) return name;
688
+ const base = match[1];
689
+ const roster = employees ?? loadEmployeesSync();
690
+ if (getEmployee(roster, base)) return base;
691
+ return name;
692
+ }
685
693
  function isMultiInstance(agentName, employees) {
686
694
  const roster = employees ?? loadEmployeesSync();
687
695
  const emp = getEmployee(roster, agentName);
@@ -4616,7 +4624,8 @@ function sendIntercom(targetSession) {
4616
4624
  logIntercom(`COPY_MODE \u2192 ${targetSession} (exiting copy mode first)`);
4617
4625
  transport.sendKeys(targetSession, "q");
4618
4626
  }
4619
- const agentName = targetSession.split("-")[0] ?? targetSession;
4627
+ const rawAgentName = targetSession.split("-")[0] ?? targetSession;
4628
+ const agentName = baseAgentName(rawAgentName);
4620
4629
  const rtConfig = getAgentRuntime(agentName);
4621
4630
  if (rtConfig.runtime === "codex" || rtConfig.runtime === "opencode") {
4622
4631
  transport.sendKeys(targetSession, "NEW TASK ASSIGNED. Call list_tasks now, then get_task on the highest priority open task. Start working immediately \u2014 do not ask for confirmation.");
@@ -5012,7 +5021,7 @@ var init_tmux_routing = __esm({
5012
5021
  INTERCOM_LOG2 = path15.join(os8.homedir(), ".exe-os", "intercom.log");
5013
5022
  DEBOUNCE_FILE = path15.join(SESSION_CACHE, "intercom-debounce.json");
5014
5023
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
5015
- BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
5024
+ BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
5016
5025
  }
5017
5026
  });
5018
5027
 
@@ -440,6 +440,14 @@ function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
440
440
  function getEmployee(employees, name) {
441
441
  return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
442
442
  }
443
+ function baseAgentName(name, employees) {
444
+ const match = name.match(/^([a-zA-Z]+)\d+$/);
445
+ if (!match) return name;
446
+ const base = match[1];
447
+ const roster = employees ?? loadEmployeesSync();
448
+ if (getEmployee(roster, base)) return base;
449
+ return name;
450
+ }
443
451
  function isMultiInstance(agentName, employees) {
444
452
  const roster = employees ?? loadEmployeesSync();
445
453
  const emp = getEmployee(roster, agentName);
@@ -3995,7 +4003,8 @@ function sendIntercom(targetSession) {
3995
4003
  logIntercom(`COPY_MODE \u2192 ${targetSession} (exiting copy mode first)`);
3996
4004
  transport.sendKeys(targetSession, "q");
3997
4005
  }
3998
- const agentName = targetSession.split("-")[0] ?? targetSession;
4006
+ const rawAgentName = targetSession.split("-")[0] ?? targetSession;
4007
+ const agentName = baseAgentName(rawAgentName);
3999
4008
  const rtConfig = getAgentRuntime(agentName);
4000
4009
  if (rtConfig.runtime === "codex" || rtConfig.runtime === "opencode") {
4001
4010
  transport.sendKeys(targetSession, "NEW TASK ASSIGNED. Call list_tasks now, then get_task on the highest priority open task. Start working immediately \u2014 do not ask for confirmation.");
@@ -4391,7 +4400,7 @@ var init_tmux_routing = __esm({
4391
4400
  INTERCOM_LOG2 = path13.join(os8.homedir(), ".exe-os", "intercom.log");
4392
4401
  DEBOUNCE_FILE = path13.join(SESSION_CACHE, "intercom-debounce.json");
4393
4402
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
4394
- BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
4403
+ BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
4395
4404
  }
4396
4405
  });
4397
4406
 
@@ -789,11 +789,15 @@ Your output files must start with: exe/output/${agent.agentId}-`
789
789
  counter.writes = 0;
790
790
  }
791
791
  saveCounter(data.session_id, counter);
792
- if (WRITE_TOOL_RE.test(data.tool_name) && counter.pipelineWrites >= 3 && !counter.pipelineDetected) {
792
+ if (WRITE_TOOL_RE.test(data.tool_name) && counter.pipelineWrites === 3 && !counter.pipelineDetected) {
793
+ const runtime = process.env.EXE_RUNTIME?.toLowerCase();
794
+ const isCC = !runtime || runtime === "claude";
795
+ const pipelineCmd = isCC ? "run /exe-build-adv --auto" : "call load_skill('exe-build-adv') via MCP";
793
796
  const warning = JSON.stringify({
794
797
  hookSpecificOutput: {
795
798
  hookEventName: "PostToolUse",
796
- additionalContext: "## Pipeline Warning\nWARNING: You are writing code without running the exe-build-e2e pipeline.\nIf this task adds capability or changes behavior, run /exe-build-e2e first.\nIf this is a minor fix (typo, config, one-liner), you can ignore this warning."
799
+ additionalContext: `## Pipeline Warning
800
+ WARNING: You are writing code without running the build pipeline. If this task adds capability or changes behavior, ${pipelineCmd} first. If this is a minor fix (typo, config, one-liner), you can ignore this warning.`
797
801
  }
798
802
  });
799
803
  process.stdout.write(warning);
@@ -303,6 +303,14 @@ function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
303
303
  function getEmployee(employees, name) {
304
304
  return employees.find((e) => e.name.toLowerCase() === name.toLowerCase());
305
305
  }
306
+ function baseAgentName(name, employees) {
307
+ const match = name.match(/^([a-zA-Z]+)\d+$/);
308
+ if (!match) return name;
309
+ const base = match[1];
310
+ const roster = employees ?? loadEmployeesSync();
311
+ if (getEmployee(roster, base)) return base;
312
+ return name;
313
+ }
306
314
  function isMultiInstance(agentName, employees) {
307
315
  const roster = employees ?? loadEmployeesSync();
308
316
  const emp = getEmployee(roster, agentName);
@@ -4600,7 +4608,8 @@ function sendIntercom(targetSession) {
4600
4608
  logIntercom(`COPY_MODE \u2192 ${targetSession} (exiting copy mode first)`);
4601
4609
  transport.sendKeys(targetSession, "q");
4602
4610
  }
4603
- const agentName = targetSession.split("-")[0] ?? targetSession;
4611
+ const rawAgentName = targetSession.split("-")[0] ?? targetSession;
4612
+ const agentName = baseAgentName(rawAgentName);
4604
4613
  const rtConfig = getAgentRuntime(agentName);
4605
4614
  if (rtConfig.runtime === "codex" || rtConfig.runtime === "opencode") {
4606
4615
  transport.sendKeys(targetSession, "NEW TASK ASSIGNED. Call list_tasks now, then get_task on the highest priority open task. Start working immediately \u2014 do not ask for confirmation.");
@@ -4996,7 +5005,7 @@ var init_tmux_routing = __esm({
4996
5005
  INTERCOM_LOG2 = path16.join(os8.homedir(), ".exe-os", "intercom.log");
4997
5006
  DEBOUNCE_FILE = path16.join(SESSION_CACHE, "intercom-debounce.json");
4998
5007
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
4999
- BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
5008
+ BUSY_PATTERN = /[✻✽✶✳·].*…|Running…|• Working|• Ran |• Explored|• Called|esc to interrupt/;
5000
5009
  }
5001
5010
  });
5002
5011
 
@@ -3869,6 +3869,8 @@ init_session_key();
3869
3869
  init_config();
3870
3870
  init_task_scope();
3871
3871
  init_employees();
3872
+ process.on("uncaughtException", () => process.exit(0));
3873
+ process.on("unhandledRejection", () => process.exit(0));
3872
3874
  if (!process.env.AGENT_ID) {
3873
3875
  process.env.AGENT_ID = "default";
3874
3876
  process.env.AGENT_ROLE = "employee";