@askexenow/exe-os 0.8.0 → 0.8.1

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 (90) hide show
  1. package/README.md +178 -79
  2. package/dist/bin/backfill-responses.js +160 -8
  3. package/dist/bin/backfill-vectors.js +130 -1
  4. package/dist/bin/cleanup-stale-review-tasks.js +130 -1
  5. package/dist/bin/cli.js +10111 -7540
  6. package/dist/bin/exe-agent.js +159 -1
  7. package/dist/bin/exe-assign.js +235 -16
  8. package/dist/bin/exe-boot.js +344 -472
  9. package/dist/bin/exe-call.js +145 -1
  10. package/dist/bin/exe-cloud.js +11 -0
  11. package/dist/bin/exe-dispatch.js +37 -24
  12. package/dist/bin/exe-doctor.js +130 -1
  13. package/dist/bin/exe-export-behaviors.js +150 -7
  14. package/dist/bin/exe-forget.js +822 -665
  15. package/dist/bin/exe-gateway.js +470 -62
  16. package/dist/bin/exe-heartbeat.js +133 -2
  17. package/dist/bin/exe-kill.js +150 -7
  18. package/dist/bin/exe-launch-agent.js +150 -7
  19. package/dist/bin/exe-new-employee.js +756 -224
  20. package/dist/bin/exe-pending-messages.js +132 -2
  21. package/dist/bin/exe-pending-notifications.js +130 -1
  22. package/dist/bin/exe-pending-reviews.js +132 -2
  23. package/dist/bin/exe-review.js +160 -8
  24. package/dist/bin/exe-search.js +2473 -2008
  25. package/dist/bin/exe-session-cleanup.js +238 -51
  26. package/dist/bin/exe-settings.js +11 -0
  27. package/dist/bin/exe-status.js +130 -1
  28. package/dist/bin/exe-team.js +130 -1
  29. package/dist/bin/git-sweep.js +272 -16
  30. package/dist/bin/graph-backfill.js +150 -7
  31. package/dist/bin/graph-export.js +150 -7
  32. package/dist/bin/install.js +5 -0
  33. package/dist/bin/scan-tasks.js +238 -19
  34. package/dist/bin/setup.js +1776 -10
  35. package/dist/bin/shard-migrate.js +150 -7
  36. package/dist/bin/update.js +9 -6
  37. package/dist/bin/wiki-sync.js +150 -7
  38. package/dist/gateway/index.js +470 -62
  39. package/dist/hooks/bug-report-worker.js +195 -35
  40. package/dist/hooks/commit-complete.js +272 -16
  41. package/dist/hooks/error-recall.js +2313 -1847
  42. package/dist/hooks/exe-heartbeat-hook.js +5 -0
  43. package/dist/hooks/ingest-worker.js +330 -58
  44. package/dist/hooks/ingest.js +11 -0
  45. package/dist/hooks/instructions-loaded.js +199 -10
  46. package/dist/hooks/notification.js +199 -10
  47. package/dist/hooks/post-compact.js +199 -10
  48. package/dist/hooks/pre-compact.js +199 -10
  49. package/dist/hooks/pre-tool-use.js +199 -10
  50. package/dist/hooks/prompt-ingest-worker.js +179 -14
  51. package/dist/hooks/prompt-submit.js +781 -285
  52. package/dist/hooks/response-ingest-worker.js +1900 -1405
  53. package/dist/hooks/session-end.js +456 -12
  54. package/dist/hooks/session-start.js +2188 -1724
  55. package/dist/hooks/stop.js +200 -10
  56. package/dist/hooks/subagent-stop.js +199 -10
  57. package/dist/hooks/summary-worker.js +604 -334
  58. package/dist/index.js +554 -61
  59. package/dist/lib/cloud-sync.js +5 -0
  60. package/dist/lib/config.js +13 -0
  61. package/dist/lib/consolidation.js +5 -0
  62. package/dist/lib/database.js +104 -0
  63. package/dist/lib/device-registry.js +109 -0
  64. package/dist/lib/embedder.js +13 -0
  65. package/dist/lib/employee-templates.js +53 -26
  66. package/dist/lib/employees.js +5 -0
  67. package/dist/lib/exe-daemon-client.js +5 -0
  68. package/dist/lib/exe-daemon.js +493 -79
  69. package/dist/lib/file-grep.js +20 -4
  70. package/dist/lib/hybrid-search.js +1435 -190
  71. package/dist/lib/identity-templates.js +126 -5
  72. package/dist/lib/identity.js +5 -0
  73. package/dist/lib/license.js +5 -0
  74. package/dist/lib/messaging.js +37 -24
  75. package/dist/lib/schedules.js +130 -1
  76. package/dist/lib/skill-learning.js +11 -0
  77. package/dist/lib/status-brief.js +5 -0
  78. package/dist/lib/store.js +199 -10
  79. package/dist/lib/task-router.js +72 -6
  80. package/dist/lib/tasks.js +179 -50
  81. package/dist/lib/tmux-routing.js +179 -46
  82. package/dist/mcp/server.js +2129 -1855
  83. package/dist/mcp/tools/create-task.js +86 -36
  84. package/dist/mcp/tools/deactivate-behavior.js +5 -0
  85. package/dist/mcp/tools/list-tasks.js +39 -11
  86. package/dist/mcp/tools/send-message.js +37 -24
  87. package/dist/mcp/tools/update-task.js +153 -38
  88. package/dist/runtime/index.js +451 -59
  89. package/dist/tui/App.js +454 -59
  90. package/package.json +1 -1
@@ -233,6 +233,27 @@ async function ensureSchema() {
233
233
  });
234
234
  } catch {
235
235
  }
236
+ try {
237
+ await client.execute({
238
+ sql: `ALTER TABLE tasks ADD COLUMN checkpoint TEXT`,
239
+ args: []
240
+ });
241
+ } catch {
242
+ }
243
+ try {
244
+ await client.execute({
245
+ sql: `ALTER TABLE tasks ADD COLUMN checkpoint_count INTEGER NOT NULL DEFAULT 0`,
246
+ args: []
247
+ });
248
+ } catch {
249
+ }
250
+ try {
251
+ await client.execute({
252
+ sql: `ALTER TABLE tasks ADD COLUMN complexity TEXT NOT NULL DEFAULT 'standard'`,
253
+ args: []
254
+ });
255
+ } catch {
256
+ }
236
257
  try {
237
258
  await client.execute({
238
259
  sql: `ALTER TABLE memories ADD COLUMN task_id TEXT`,
@@ -643,6 +664,15 @@ async function ensureSchema() {
643
664
  } catch {
644
665
  }
645
666
  }
667
+ for (const col of [
668
+ "ALTER TABLE memories ADD COLUMN source_path TEXT",
669
+ "ALTER TABLE memories ADD COLUMN source_type TEXT DEFAULT 'text'"
670
+ ]) {
671
+ try {
672
+ await client.execute(col);
673
+ } catch {
674
+ }
675
+ }
646
676
  await client.executeMultiple(`
647
677
  CREATE INDEX IF NOT EXISTS idx_memories_workspace
648
678
  ON memories(workspace_id);
@@ -707,6 +737,34 @@ async function ensureSchema() {
707
737
  CREATE INDEX IF NOT EXISTS idx_conversations_channel
708
738
  ON conversations(channel_id);
709
739
  `);
740
+ try {
741
+ await client.execute({
742
+ sql: `ALTER TABLE tasks ADD COLUMN budget_tokens INTEGER`,
743
+ args: []
744
+ });
745
+ } catch {
746
+ }
747
+ try {
748
+ await client.execute({
749
+ sql: `ALTER TABLE tasks ADD COLUMN budget_fallback_model TEXT`,
750
+ args: []
751
+ });
752
+ } catch {
753
+ }
754
+ try {
755
+ await client.execute({
756
+ sql: `ALTER TABLE tasks ADD COLUMN tokens_used INTEGER DEFAULT 0`,
757
+ args: []
758
+ });
759
+ } catch {
760
+ }
761
+ try {
762
+ await client.execute({
763
+ sql: `ALTER TABLE tasks ADD COLUMN tokens_warned_at INTEGER`,
764
+ args: []
765
+ });
766
+ } catch {
767
+ }
710
768
  await client.executeMultiple(`
711
769
  CREATE VIRTUAL TABLE IF NOT EXISTS conversations_fts USING fts5(
712
770
  content_text,
@@ -733,6 +791,52 @@ async function ensureSchema() {
733
791
  VALUES (new.rowid, new.content_text, new.sender_name, new.agent_response);
734
792
  END;
735
793
  `);
794
+ try {
795
+ await client.execute({
796
+ sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
797
+ args: []
798
+ });
799
+ } catch {
800
+ }
801
+ try {
802
+ await client.execute(
803
+ `CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)`
804
+ );
805
+ } catch {
806
+ }
807
+ try {
808
+ await client.execute({
809
+ sql: `UPDATE memories SET tier = 1 WHERE tool_name = 'commit_to_long_term_memory' AND importance >= 8 AND tier = 3`,
810
+ args: []
811
+ });
812
+ await client.execute({
813
+ sql: `UPDATE memories SET tier = 2 WHERE tool_name IN ('store_memory', 'manual') AND importance >= 5 AND tier = 3`,
814
+ args: []
815
+ });
816
+ } catch {
817
+ }
818
+ try {
819
+ await client.execute({
820
+ sql: `ALTER TABLE memories ADD COLUMN supersedes_id TEXT`,
821
+ args: []
822
+ });
823
+ } catch {
824
+ }
825
+ try {
826
+ await client.execute(
827
+ `CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL`
828
+ );
829
+ } catch {
830
+ }
831
+ for (const col of [
832
+ "ALTER TABLE tasks ADD COLUMN checkpoint TEXT",
833
+ "ALTER TABLE tasks ADD COLUMN checkpoint_count INTEGER DEFAULT 0"
834
+ ]) {
835
+ try {
836
+ await client.execute(col);
837
+ } catch {
838
+ }
839
+ }
736
840
  }
737
841
  var _client, initTurso;
738
842
  var init_database = __esm({
@@ -809,6 +913,11 @@ function normalizeSessionLifecycle(raw) {
809
913
  const userSL = raw.sessionLifecycle ?? {};
810
914
  raw.sessionLifecycle = { ...defaultSL, ...userSL };
811
915
  }
916
+ function normalizeAutoUpdate(raw) {
917
+ const defaultAU = DEFAULT_CONFIG.autoUpdate;
918
+ const userAU = raw.autoUpdate ?? {};
919
+ raw.autoUpdate = { ...defaultAU, ...userAU };
920
+ }
812
921
  async function loadConfig() {
813
922
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
814
923
  await mkdir2(dir, { recursive: true });
@@ -831,6 +940,7 @@ async function loadConfig() {
831
940
  }
832
941
  normalizeScalingRoadmap(migratedCfg);
833
942
  normalizeSessionLifecycle(migratedCfg);
943
+ normalizeAutoUpdate(migratedCfg);
834
944
  const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
835
945
  if (config.dbPath.startsWith("~")) {
836
946
  config.dbPath = config.dbPath.replace(/^~/, os.homedir());
@@ -906,6 +1016,11 @@ var init_config = __esm({
906
1016
  idleKillTicksRequired: 3,
907
1017
  idleKillIntercomAckWindowMs: 1e4,
908
1018
  maxAutoInstances: 10
1019
+ },
1020
+ autoUpdate: {
1021
+ checkOnBoot: true,
1022
+ autoInstall: false,
1023
+ checkIntervalMs: 24 * 60 * 60 * 1e3
909
1024
  }
910
1025
  };
911
1026
  CONFIG_MIGRATIONS = [
@@ -1039,13 +1154,27 @@ async function ensureShardSchema(client) {
1039
1154
  "ALTER TABLE memories ADD COLUMN document_id TEXT",
1040
1155
  "ALTER TABLE memories ADD COLUMN user_id TEXT",
1041
1156
  "ALTER TABLE memories ADD COLUMN char_offset INTEGER",
1042
- "ALTER TABLE memories ADD COLUMN page_number INTEGER"
1157
+ "ALTER TABLE memories ADD COLUMN page_number INTEGER",
1158
+ // Source provenance columns (must match database.ts)
1159
+ "ALTER TABLE memories ADD COLUMN source_path TEXT",
1160
+ "ALTER TABLE memories ADD COLUMN source_type TEXT DEFAULT 'text'",
1161
+ "ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3",
1162
+ "ALTER TABLE memories ADD COLUMN supersedes_id TEXT"
1043
1163
  ]) {
1044
1164
  try {
1045
1165
  await client.execute(col);
1046
1166
  } catch {
1047
1167
  }
1048
1168
  }
1169
+ for (const idx of [
1170
+ "CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
1171
+ "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
1172
+ ]) {
1173
+ try {
1174
+ await client.execute(idx);
1175
+ } catch {
1176
+ }
1177
+ }
1049
1178
  try {
1050
1179
  await client.execute("CREATE INDEX IF NOT EXISTS idx_memories_status ON memories(status)");
1051
1180
  } catch {
@@ -1279,9 +1408,10 @@ async function createTaskCore(input) {
1279
1408
  } catch {
1280
1409
  }
1281
1410
  }
1411
+ const complexity = input.complexity ?? "standard";
1282
1412
  await client.execute({
1283
- sql: `INSERT INTO tasks (id, title, assigned_to, assigned_by, project_name, priority, status, task_file, blocked_by, parent_task_id, reviewer, context, created_at, updated_at)
1284
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
1413
+ sql: `INSERT INTO tasks (id, title, assigned_to, assigned_by, project_name, priority, status, task_file, blocked_by, parent_task_id, reviewer, context, complexity, budget_tokens, budget_fallback_model, tokens_used, tokens_warned_at, created_at, updated_at)
1414
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
1285
1415
  args: [
1286
1416
  id,
1287
1417
  input.title,
@@ -1295,6 +1425,11 @@ async function createTaskCore(input) {
1295
1425
  parentTaskId,
1296
1426
  input.reviewer ?? null,
1297
1427
  input.context,
1428
+ input.budgetTokens ?? null,
1429
+ input.budgetFallbackModel ?? null,
1430
+ 0,
1431
+ null,
1432
+ complexity,
1298
1433
  now,
1299
1434
  now
1300
1435
  ]
@@ -1310,7 +1445,11 @@ async function createTaskCore(input) {
1310
1445
  taskFile,
1311
1446
  createdAt: now,
1312
1447
  updatedAt: now,
1313
- warning
1448
+ warning,
1449
+ budgetTokens: input.budgetTokens ?? null,
1450
+ budgetFallbackModel: input.budgetFallbackModel ?? null,
1451
+ tokensUsed: 0,
1452
+ tokensWarnedAt: null
1314
1453
  };
1315
1454
  }
1316
1455
  async function ensureArchitectureDoc(baseDir, projectName) {
@@ -1670,11 +1809,12 @@ function queueIntercom(targetSession, reason) {
1670
1809
  }
1671
1810
  writeQueue(queue);
1672
1811
  }
1673
- var QUEUE_PATH, INTERCOM_LOG;
1812
+ var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
1674
1813
  var init_intercom_queue = __esm({
1675
1814
  "src/lib/intercom-queue.ts"() {
1676
1815
  "use strict";
1677
1816
  QUEUE_PATH = path8.join(os4.homedir(), ".exe-os", "intercom-queue.json");
1817
+ TTL_MS = 60 * 60 * 1e3;
1678
1818
  INTERCOM_LOG = path8.join(os4.homedir(), ".exe-os", "intercom.log");
1679
1819
  }
1680
1820
  });
@@ -1797,6 +1937,17 @@ function getGitRoot(dir) {
1797
1937
  return null;
1798
1938
  }
1799
1939
  }
1940
+ function getMainRepoRoot(dir) {
1941
+ try {
1942
+ const commonDir = execSync5(
1943
+ "git rev-parse --path-format=absolute --git-common-dir",
1944
+ { cwd: dir, encoding: "utf-8", timeout: GIT_TIMEOUT_MS, stdio: ["pipe", "pipe", "pipe"] }
1945
+ ).trim();
1946
+ return realpath(path11.dirname(commonDir));
1947
+ } catch {
1948
+ return null;
1949
+ }
1950
+ }
1800
1951
  function worktreePath(repoRoot, employeeName, instance) {
1801
1952
  const label = instanceLabel(employeeName, instance);
1802
1953
  return path11.join(repoRoot, ".worktrees", label);
@@ -2022,6 +2173,11 @@ function getSessionState(sessionName) {
2022
2173
  if (!transport.isAlive(sessionName)) return "offline";
2023
2174
  try {
2024
2175
  const pane = transport.capturePane(sessionName, 5);
2176
+ if (!pane.includes("\u276F") && !pane.includes("Claude Code") && !BUSY_PATTERN.test(pane) && !/Running…/.test(pane)) {
2177
+ if (/\$\s*$/.test(pane) || /% $/.test(pane.trimEnd())) {
2178
+ return "no_claude";
2179
+ }
2180
+ }
2025
2181
  if (/Running…/.test(pane)) return "tool";
2026
2182
  if (BUSY_PATTERN.test(pane)) return "thinking";
2027
2183
  return "idle";
@@ -2029,10 +2185,6 @@ function getSessionState(sessionName) {
2029
2185
  return "offline";
2030
2186
  }
2031
2187
  }
2032
- function isSessionBusy(sessionName) {
2033
- const state = getSessionState(sessionName);
2034
- return state === "thinking" || state === "tool";
2035
- }
2036
2188
  function isExeSession(sessionName) {
2037
2189
  return /^exe\d*$/.test(sessionName);
2038
2190
  }
@@ -2052,7 +2204,14 @@ function sendIntercom(targetSession) {
2052
2204
  logIntercom(`SKIP \u2192 ${targetSession} (session not found)`);
2053
2205
  return "failed";
2054
2206
  }
2055
- if (isSessionBusy(targetSession)) {
2207
+ const sessionState = getSessionState(targetSession);
2208
+ if (sessionState === "no_claude") {
2209
+ queueIntercom(targetSession, "claude not running in session");
2210
+ recordDebounce(targetSession);
2211
+ logIntercom(`QUEUED \u2192 ${targetSession} (no claude process \u2014 raw shell detected)`);
2212
+ return "queued";
2213
+ }
2214
+ if (sessionState === "thinking" || sessionState === "tool") {
2056
2215
  queueIntercom(targetSession, "session busy at send time");
2057
2216
  recordDebounce(targetSession);
2058
2217
  logIntercom(`QUEUED \u2192 ${targetSession} (session busy, will retry from queue)`);
@@ -2064,18 +2223,7 @@ function sendIntercom(targetSession) {
2064
2223
  }
2065
2224
  transport.sendKeys(targetSession, "/exe-intercom");
2066
2225
  recordDebounce(targetSession);
2067
- for (let i = 0; i < INTERCOM_POLL_MAX_ATTEMPTS; i++) {
2068
- try {
2069
- execSync6(`sleep ${INTERCOM_POLL_INTERVAL_S}`);
2070
- } catch {
2071
- }
2072
- const state = getSessionState(targetSession);
2073
- if (state === "thinking" || state === "tool") {
2074
- logIntercom(`ACKNOWLEDGED \u2192 ${targetSession} (state=${state}, poll=${i + 1})`);
2075
- return "acknowledged";
2076
- }
2077
- }
2078
- logIntercom(`DELIVERED \u2192 ${targetSession} (no state transition after ${INTERCOM_POLL_MAX_ATTEMPTS}s)`);
2226
+ logIntercom(`DELIVERED \u2192 ${targetSession} (fire-and-forget)`);
2079
2227
  return "delivered";
2080
2228
  } catch {
2081
2229
  logIntercom(`FAIL \u2192 ${targetSession}`);
@@ -2129,7 +2277,8 @@ function ensureEmployee(employeeName, exeSession, projectDir, opts) {
2129
2277
  return { status: "failed", sessionName, error: "intercom delivery failed" };
2130
2278
  }
2131
2279
  const spawnOpts = { ...opts, instance: effectiveInstance };
2132
- const wtPath = ensureWorktree(projectDir, employeeName, effectiveInstance);
2280
+ const mainRoot = getMainRepoRoot(projectDir) ?? projectDir;
2281
+ const wtPath = ensureWorktree(mainRoot, employeeName, effectiveInstance);
2133
2282
  if (wtPath) {
2134
2283
  spawnOpts.cwd = wtPath;
2135
2284
  }
@@ -2310,7 +2459,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
2310
2459
  let booted = false;
2311
2460
  for (let i = 0; i < 30; i++) {
2312
2461
  try {
2313
- execSync6("sleep 1");
2462
+ execSync6("sleep 0.5");
2314
2463
  } catch {
2315
2464
  }
2316
2465
  try {
@@ -2330,7 +2479,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
2330
2479
  }
2331
2480
  }
2332
2481
  if (!booted) {
2333
- return { sessionName, error: `${useExeAgent ? "exe-agent" : "claude"} did not boot within 30s` };
2482
+ return { sessionName, error: `${useExeAgent ? "exe-agent" : "claude"} did not boot within 15s` };
2334
2483
  }
2335
2484
  if (!useExeAgent) {
2336
2485
  try {
@@ -2348,7 +2497,7 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
2348
2497
  });
2349
2498
  return { sessionName };
2350
2499
  }
2351
- var SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN, INTERCOM_POLL_INTERVAL_S, INTERCOM_POLL_MAX_ATTEMPTS;
2500
+ var SESSION_CACHE, BEHAVIORS_EXPORT_TIMEOUT_MS, INTERCOM_DEBOUNCE_MS, INTERCOM_LOG2, DEBOUNCE_FILE, DEBOUNCE_CLEANUP_AGE_MS, BUSY_PATTERN;
2352
2501
  var init_tmux_routing = __esm({
2353
2502
  "src/lib/tmux-routing.ts"() {
2354
2503
  "use strict";
@@ -2368,8 +2517,6 @@ var init_tmux_routing = __esm({
2368
2517
  DEBOUNCE_FILE = path12.join(SESSION_CACHE, "intercom-debounce.json");
2369
2518
  DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
2370
2519
  BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
2371
- INTERCOM_POLL_INTERVAL_S = 1;
2372
- INTERCOM_POLL_MAX_ATTEMPTS = 8;
2373
2520
  }
2374
2521
  });
2375
2522
 
@@ -2407,12 +2554,23 @@ function getProjectName(cwd) {
2407
2554
  const dir = cwd ?? process.cwd();
2408
2555
  if (_cached2 && _cachedCwd === dir) return _cached2;
2409
2556
  try {
2410
- const repoRoot = execSync7("git rev-parse --show-toplevel", {
2411
- cwd: dir,
2412
- encoding: "utf8",
2413
- timeout: 2e3,
2414
- stdio: ["pipe", "pipe", "pipe"]
2415
- }).trim();
2557
+ let repoRoot;
2558
+ try {
2559
+ const gitCommonDir = execSync7("git rev-parse --path-format=absolute --git-common-dir", {
2560
+ cwd: dir,
2561
+ encoding: "utf8",
2562
+ timeout: 2e3,
2563
+ stdio: ["pipe", "pipe", "pipe"]
2564
+ }).trim();
2565
+ repoRoot = path15.dirname(gitCommonDir);
2566
+ } catch {
2567
+ repoRoot = execSync7("git rev-parse --show-toplevel", {
2568
+ cwd: dir,
2569
+ encoding: "utf8",
2570
+ timeout: 2e3,
2571
+ stdio: ["pipe", "pipe", "pipe"]
2572
+ }).trim();
2573
+ }
2416
2574
  _cached2 = path15.basename(repoRoot);
2417
2575
  _cachedCwd = dir;
2418
2576
  return _cached2;
@@ -2518,7 +2676,9 @@ async function dispatchTaskToEmployee(input) {
2518
2676
  return { dispatched, session: sessionName, crossProject };
2519
2677
  } else {
2520
2678
  const projectDir = input.projectDir ?? process.cwd();
2521
- const result = ensureEmployee(input.assignedTo, exeSession, projectDir);
2679
+ const result = ensureEmployee(input.assignedTo, exeSession, projectDir, {
2680
+ autoInstance: input.assignedTo === "tom" || input.assignedTo === "sasha"
2681
+ });
2522
2682
  if (result.status === "failed") {
2523
2683
  process.stderr.write(
2524
2684
  `[dispatch] Failed to spawn ${input.assignedTo}: ${result.error}