@askexenow/exe-os 0.8.85 → 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.
- package/dist/bin/cleanup-stale-review-tasks.js +57 -19
- package/dist/bin/cli.js +507 -337
- package/dist/bin/exe-agent-config.js +242 -0
- package/dist/bin/exe-agent.js +3 -3
- package/dist/bin/exe-boot.js +344 -346
- package/dist/bin/exe-dispatch.js +375 -250
- package/dist/bin/exe-forget.js +5 -1
- package/dist/bin/exe-gateway.js +260 -135
- package/dist/bin/exe-healthcheck.js +133 -1
- package/dist/bin/exe-heartbeat.js +72 -31
- package/dist/bin/exe-link.js +25 -2
- package/dist/bin/exe-new-employee.js +22 -0
- package/dist/bin/exe-pending-messages.js +55 -17
- package/dist/bin/exe-pending-reviews.js +57 -19
- package/dist/bin/exe-search.js +6 -2
- package/dist/bin/exe-session-cleanup.js +260 -135
- package/dist/bin/exe-start-codex.js +2598 -0
- package/dist/bin/exe-start.sh +15 -3
- package/dist/bin/exe-status.js +57 -19
- package/dist/bin/git-sweep.js +391 -266
- package/dist/bin/install.js +22 -0
- package/dist/bin/scan-tasks.js +394 -269
- package/dist/bin/setup.js +47 -2
- package/dist/gateway/index.js +257 -132
- package/dist/hooks/bug-report-worker.js +242 -117
- package/dist/hooks/commit-complete.js +389 -264
- package/dist/hooks/error-recall.js +6 -2
- package/dist/hooks/ingest-worker.js +314 -193
- package/dist/hooks/post-compact.js +84 -46
- package/dist/hooks/pre-compact.js +272 -147
- package/dist/hooks/pre-tool-use.js +104 -66
- package/dist/hooks/prompt-submit.js +126 -66
- package/dist/hooks/session-end.js +277 -152
- package/dist/hooks/session-start.js +70 -28
- package/dist/hooks/stop.js +90 -52
- package/dist/hooks/subagent-stop.js +84 -46
- package/dist/hooks/summary-worker.js +175 -114
- package/dist/index.js +296 -171
- package/dist/lib/agent-config.js +167 -0
- package/dist/lib/cloud-sync.js +25 -2
- package/dist/lib/exe-daemon.js +338 -213
- package/dist/lib/hybrid-search.js +7 -2
- package/dist/lib/messaging.js +95 -39
- package/dist/lib/runtime-table.js +16 -0
- package/dist/lib/session-wrappers.js +22 -0
- package/dist/lib/tasks.js +242 -117
- package/dist/lib/tmux-routing.js +314 -189
- package/dist/mcp/server.js +573 -274
- package/dist/mcp/tools/create-task.js +260 -135
- package/dist/mcp/tools/list-tasks.js +68 -30
- package/dist/mcp/tools/send-message.js +100 -44
- package/dist/mcp/tools/update-task.js +123 -67
- package/dist/runtime/index.js +276 -151
- package/dist/tui/App.js +479 -354
- package/package.json +1 -1
- package/src/commands/exe/agent-config.md +27 -0
- package/src/commands/exe/cc-doctor.md +10 -0
|
@@ -854,18 +854,69 @@ var init_provider_table = __esm({
|
|
|
854
854
|
}
|
|
855
855
|
});
|
|
856
856
|
|
|
857
|
-
// src/lib/
|
|
858
|
-
|
|
857
|
+
// src/lib/runtime-table.ts
|
|
858
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
859
|
+
var init_runtime_table = __esm({
|
|
860
|
+
"src/lib/runtime-table.ts"() {
|
|
861
|
+
"use strict";
|
|
862
|
+
RUNTIME_TABLE = {
|
|
863
|
+
codex: {
|
|
864
|
+
binary: "codex",
|
|
865
|
+
launchMode: "exec",
|
|
866
|
+
autoApproveFlag: "--full-auto",
|
|
867
|
+
inlineFlag: "--no-alt-screen",
|
|
868
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
869
|
+
defaultModel: "gpt-5.4"
|
|
870
|
+
}
|
|
871
|
+
};
|
|
872
|
+
DEFAULT_RUNTIME = "claude";
|
|
873
|
+
}
|
|
874
|
+
});
|
|
875
|
+
|
|
876
|
+
// src/lib/agent-config.ts
|
|
877
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
|
|
859
878
|
import path5 from "path";
|
|
879
|
+
function loadAgentConfig() {
|
|
880
|
+
if (!existsSync5(AGENT_CONFIG_PATH)) return {};
|
|
881
|
+
try {
|
|
882
|
+
return JSON.parse(readFileSync5(AGENT_CONFIG_PATH, "utf-8"));
|
|
883
|
+
} catch {
|
|
884
|
+
return {};
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
function getAgentRuntime(agentId) {
|
|
888
|
+
const config = loadAgentConfig();
|
|
889
|
+
const entry = config[agentId];
|
|
890
|
+
if (entry) return entry;
|
|
891
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
892
|
+
}
|
|
893
|
+
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
894
|
+
var init_agent_config = __esm({
|
|
895
|
+
"src/lib/agent-config.ts"() {
|
|
896
|
+
"use strict";
|
|
897
|
+
init_config();
|
|
898
|
+
init_runtime_table();
|
|
899
|
+
AGENT_CONFIG_PATH = path5.join(EXE_AI_DIR, "agent-config.json");
|
|
900
|
+
DEFAULT_MODELS = {
|
|
901
|
+
claude: "claude-opus-4",
|
|
902
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
903
|
+
opencode: "minimax-m2.7"
|
|
904
|
+
};
|
|
905
|
+
}
|
|
906
|
+
});
|
|
907
|
+
|
|
908
|
+
// src/lib/intercom-queue.ts
|
|
909
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
|
|
910
|
+
import path6 from "path";
|
|
860
911
|
import os5 from "os";
|
|
861
912
|
function ensureDir() {
|
|
862
|
-
const dir =
|
|
863
|
-
if (!
|
|
913
|
+
const dir = path6.dirname(QUEUE_PATH);
|
|
914
|
+
if (!existsSync6(dir)) mkdirSync3(dir, { recursive: true });
|
|
864
915
|
}
|
|
865
916
|
function readQueue() {
|
|
866
917
|
try {
|
|
867
|
-
if (!
|
|
868
|
-
return JSON.parse(
|
|
918
|
+
if (!existsSync6(QUEUE_PATH)) return [];
|
|
919
|
+
return JSON.parse(readFileSync6(QUEUE_PATH, "utf8"));
|
|
869
920
|
} catch {
|
|
870
921
|
return [];
|
|
871
922
|
}
|
|
@@ -873,7 +924,7 @@ function readQueue() {
|
|
|
873
924
|
function writeQueue(queue) {
|
|
874
925
|
ensureDir();
|
|
875
926
|
const tmp = `${QUEUE_PATH}.tmp`;
|
|
876
|
-
|
|
927
|
+
writeFileSync4(tmp, JSON.stringify(queue, null, 2));
|
|
877
928
|
renameSync3(tmp, QUEUE_PATH);
|
|
878
929
|
}
|
|
879
930
|
function queueIntercom(targetSession, reason) {
|
|
@@ -897,25 +948,25 @@ var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
|
897
948
|
var init_intercom_queue = __esm({
|
|
898
949
|
"src/lib/intercom-queue.ts"() {
|
|
899
950
|
"use strict";
|
|
900
|
-
QUEUE_PATH =
|
|
951
|
+
QUEUE_PATH = path6.join(os5.homedir(), ".exe-os", "intercom-queue.json");
|
|
901
952
|
TTL_MS = 60 * 60 * 1e3;
|
|
902
|
-
INTERCOM_LOG =
|
|
953
|
+
INTERCOM_LOG = path6.join(os5.homedir(), ".exe-os", "intercom.log");
|
|
903
954
|
}
|
|
904
955
|
});
|
|
905
956
|
|
|
906
957
|
// src/lib/license.ts
|
|
907
|
-
import { readFileSync as
|
|
958
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
|
|
908
959
|
import { randomUUID } from "crypto";
|
|
909
|
-
import
|
|
960
|
+
import path7 from "path";
|
|
910
961
|
import { jwtVerify, importSPKI } from "jose";
|
|
911
962
|
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
|
|
912
963
|
var init_license = __esm({
|
|
913
964
|
"src/lib/license.ts"() {
|
|
914
965
|
"use strict";
|
|
915
966
|
init_config();
|
|
916
|
-
LICENSE_PATH =
|
|
917
|
-
CACHE_PATH =
|
|
918
|
-
DEVICE_ID_PATH =
|
|
967
|
+
LICENSE_PATH = path7.join(EXE_AI_DIR, "license.key");
|
|
968
|
+
CACHE_PATH = path7.join(EXE_AI_DIR, "license-cache.json");
|
|
969
|
+
DEVICE_ID_PATH = path7.join(EXE_AI_DIR, "device-id");
|
|
919
970
|
PLAN_LIMITS = {
|
|
920
971
|
free: { devices: 1, employees: 1, memories: 5e3 },
|
|
921
972
|
pro: { devices: 3, employees: 5, memories: 1e5 },
|
|
@@ -927,12 +978,12 @@ var init_license = __esm({
|
|
|
927
978
|
});
|
|
928
979
|
|
|
929
980
|
// src/lib/plan-limits.ts
|
|
930
|
-
import { readFileSync as
|
|
931
|
-
import
|
|
981
|
+
import { readFileSync as readFileSync8, existsSync as existsSync8 } from "fs";
|
|
982
|
+
import path8 from "path";
|
|
932
983
|
function getLicenseSync() {
|
|
933
984
|
try {
|
|
934
|
-
if (!
|
|
935
|
-
const raw = JSON.parse(
|
|
985
|
+
if (!existsSync8(CACHE_PATH2)) return freeLicense();
|
|
986
|
+
const raw = JSON.parse(readFileSync8(CACHE_PATH2, "utf8"));
|
|
936
987
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
937
988
|
const parts = raw.token.split(".");
|
|
938
989
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -970,8 +1021,8 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
970
1021
|
const filePath = rosterPath ?? EMPLOYEES_PATH;
|
|
971
1022
|
let count = 0;
|
|
972
1023
|
try {
|
|
973
|
-
if (
|
|
974
|
-
const raw =
|
|
1024
|
+
if (existsSync8(filePath)) {
|
|
1025
|
+
const raw = readFileSync8(filePath, "utf8");
|
|
975
1026
|
const employees = JSON.parse(raw);
|
|
976
1027
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
977
1028
|
}
|
|
@@ -1000,7 +1051,7 @@ var init_plan_limits = __esm({
|
|
|
1000
1051
|
this.name = "PlanLimitError";
|
|
1001
1052
|
}
|
|
1002
1053
|
};
|
|
1003
|
-
CACHE_PATH2 =
|
|
1054
|
+
CACHE_PATH2 = path8.join(EXE_AI_DIR, "license-cache.json");
|
|
1004
1055
|
}
|
|
1005
1056
|
});
|
|
1006
1057
|
|
|
@@ -1348,13 +1399,13 @@ __export(tmux_routing_exports, {
|
|
|
1348
1399
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
1349
1400
|
});
|
|
1350
1401
|
import { execFileSync as execFileSync2, execSync as execSync4 } from "child_process";
|
|
1351
|
-
import { readFileSync as
|
|
1352
|
-
import
|
|
1402
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, existsSync as existsSync9, appendFileSync } from "fs";
|
|
1403
|
+
import path9 from "path";
|
|
1353
1404
|
import os6 from "os";
|
|
1354
1405
|
import { fileURLToPath } from "url";
|
|
1355
1406
|
import { unlinkSync as unlinkSync3 } from "fs";
|
|
1356
1407
|
function spawnLockPath(sessionName) {
|
|
1357
|
-
return
|
|
1408
|
+
return path9.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
1358
1409
|
}
|
|
1359
1410
|
function isProcessAlive(pid) {
|
|
1360
1411
|
try {
|
|
@@ -1365,13 +1416,13 @@ function isProcessAlive(pid) {
|
|
|
1365
1416
|
}
|
|
1366
1417
|
}
|
|
1367
1418
|
function acquireSpawnLock(sessionName) {
|
|
1368
|
-
if (!
|
|
1369
|
-
|
|
1419
|
+
if (!existsSync9(SPAWN_LOCK_DIR)) {
|
|
1420
|
+
mkdirSync5(SPAWN_LOCK_DIR, { recursive: true });
|
|
1370
1421
|
}
|
|
1371
1422
|
const lockFile = spawnLockPath(sessionName);
|
|
1372
|
-
if (
|
|
1423
|
+
if (existsSync9(lockFile)) {
|
|
1373
1424
|
try {
|
|
1374
|
-
const lock = JSON.parse(
|
|
1425
|
+
const lock = JSON.parse(readFileSync9(lockFile, "utf8"));
|
|
1375
1426
|
const age = Date.now() - lock.timestamp;
|
|
1376
1427
|
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
1377
1428
|
return false;
|
|
@@ -1379,7 +1430,7 @@ function acquireSpawnLock(sessionName) {
|
|
|
1379
1430
|
} catch {
|
|
1380
1431
|
}
|
|
1381
1432
|
}
|
|
1382
|
-
|
|
1433
|
+
writeFileSync6(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
1383
1434
|
return true;
|
|
1384
1435
|
}
|
|
1385
1436
|
function releaseSpawnLock(sessionName) {
|
|
@@ -1391,13 +1442,13 @@ function releaseSpawnLock(sessionName) {
|
|
|
1391
1442
|
function resolveBehaviorsExporterScript() {
|
|
1392
1443
|
try {
|
|
1393
1444
|
const thisFile = fileURLToPath(import.meta.url);
|
|
1394
|
-
const scriptPath =
|
|
1395
|
-
|
|
1445
|
+
const scriptPath = path9.join(
|
|
1446
|
+
path9.dirname(thisFile),
|
|
1396
1447
|
"..",
|
|
1397
1448
|
"bin",
|
|
1398
1449
|
"exe-export-behaviors.js"
|
|
1399
1450
|
);
|
|
1400
|
-
return
|
|
1451
|
+
return existsSync9(scriptPath) ? scriptPath : null;
|
|
1401
1452
|
} catch {
|
|
1402
1453
|
return null;
|
|
1403
1454
|
}
|
|
@@ -1463,12 +1514,12 @@ function extractRootExe(name) {
|
|
|
1463
1514
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
1464
1515
|
}
|
|
1465
1516
|
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
1466
|
-
if (!
|
|
1467
|
-
|
|
1517
|
+
if (!existsSync9(SESSION_CACHE)) {
|
|
1518
|
+
mkdirSync5(SESSION_CACHE, { recursive: true });
|
|
1468
1519
|
}
|
|
1469
1520
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
1470
|
-
const filePath =
|
|
1471
|
-
|
|
1521
|
+
const filePath = path9.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
1522
|
+
writeFileSync6(filePath, JSON.stringify({
|
|
1472
1523
|
parentExe: rootExe,
|
|
1473
1524
|
dispatchedBy: dispatchedBy || rootExe,
|
|
1474
1525
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -1476,7 +1527,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
1476
1527
|
}
|
|
1477
1528
|
function getParentExe(sessionKey) {
|
|
1478
1529
|
try {
|
|
1479
|
-
const data = JSON.parse(
|
|
1530
|
+
const data = JSON.parse(readFileSync9(path9.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
1480
1531
|
return data.parentExe || null;
|
|
1481
1532
|
} catch {
|
|
1482
1533
|
return null;
|
|
@@ -1484,8 +1535,8 @@ function getParentExe(sessionKey) {
|
|
|
1484
1535
|
}
|
|
1485
1536
|
function getDispatchedBy(sessionKey) {
|
|
1486
1537
|
try {
|
|
1487
|
-
const data = JSON.parse(
|
|
1488
|
-
|
|
1538
|
+
const data = JSON.parse(readFileSync9(
|
|
1539
|
+
path9.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
1489
1540
|
"utf8"
|
|
1490
1541
|
));
|
|
1491
1542
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -1546,32 +1597,50 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
1546
1597
|
}
|
|
1547
1598
|
function readDebounceState() {
|
|
1548
1599
|
try {
|
|
1549
|
-
if (!
|
|
1550
|
-
|
|
1600
|
+
if (!existsSync9(DEBOUNCE_FILE)) return {};
|
|
1601
|
+
const raw = JSON.parse(readFileSync9(DEBOUNCE_FILE, "utf8"));
|
|
1602
|
+
const state = {};
|
|
1603
|
+
for (const [key, val] of Object.entries(raw)) {
|
|
1604
|
+
if (typeof val === "number") {
|
|
1605
|
+
state[key] = { lastSent: val, pending: 0 };
|
|
1606
|
+
} else if (val && typeof val === "object" && "lastSent" in val) {
|
|
1607
|
+
state[key] = val;
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
return state;
|
|
1551
1611
|
} catch {
|
|
1552
1612
|
return {};
|
|
1553
1613
|
}
|
|
1554
1614
|
}
|
|
1555
1615
|
function writeDebounceState(state) {
|
|
1556
1616
|
try {
|
|
1557
|
-
if (!
|
|
1558
|
-
|
|
1617
|
+
if (!existsSync9(SESSION_CACHE)) mkdirSync5(SESSION_CACHE, { recursive: true });
|
|
1618
|
+
writeFileSync6(DEBOUNCE_FILE, JSON.stringify(state));
|
|
1559
1619
|
} catch {
|
|
1560
1620
|
}
|
|
1561
1621
|
}
|
|
1562
1622
|
function isDebounced(targetSession) {
|
|
1563
1623
|
const state = readDebounceState();
|
|
1564
|
-
const
|
|
1565
|
-
|
|
1624
|
+
const entry = state[targetSession];
|
|
1625
|
+
const lastSent = entry?.lastSent ?? 0;
|
|
1626
|
+
if (Date.now() - lastSent < INTERCOM_DEBOUNCE_MS) {
|
|
1627
|
+
if (!state[targetSession]) state[targetSession] = { lastSent, pending: 0 };
|
|
1628
|
+
state[targetSession].pending++;
|
|
1629
|
+
writeDebounceState(state);
|
|
1630
|
+
return true;
|
|
1631
|
+
}
|
|
1632
|
+
return false;
|
|
1566
1633
|
}
|
|
1567
1634
|
function recordDebounce(targetSession) {
|
|
1568
1635
|
const state = readDebounceState();
|
|
1569
|
-
state[targetSession]
|
|
1636
|
+
const batched = state[targetSession]?.pending ?? 0;
|
|
1637
|
+
state[targetSession] = { lastSent: Date.now(), pending: 0 };
|
|
1570
1638
|
const cutoff = Date.now() - DEBOUNCE_CLEANUP_AGE_MS;
|
|
1571
1639
|
for (const key of Object.keys(state)) {
|
|
1572
|
-
if ((state[key] ?? 0) < cutoff) delete state[key];
|
|
1640
|
+
if ((state[key]?.lastSent ?? 0) < cutoff) delete state[key];
|
|
1573
1641
|
}
|
|
1574
1642
|
writeDebounceState(state);
|
|
1643
|
+
return batched;
|
|
1575
1644
|
}
|
|
1576
1645
|
function logIntercom(msg) {
|
|
1577
1646
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}
|
|
@@ -1616,7 +1685,7 @@ function sendIntercom(targetSession) {
|
|
|
1616
1685
|
return "skipped_exe";
|
|
1617
1686
|
}
|
|
1618
1687
|
if (isDebounced(targetSession)) {
|
|
1619
|
-
logIntercom(`DEBOUNCE \u2192 ${targetSession} (
|
|
1688
|
+
logIntercom(`DEBOUNCE \u2192 ${targetSession} (nudge batched, task safe in DB)`);
|
|
1620
1689
|
return "debounced";
|
|
1621
1690
|
}
|
|
1622
1691
|
try {
|
|
@@ -1628,14 +1697,14 @@ function sendIntercom(targetSession) {
|
|
|
1628
1697
|
const sessionState = getSessionState(targetSession);
|
|
1629
1698
|
if (sessionState === "no_claude") {
|
|
1630
1699
|
queueIntercom(targetSession, "claude not running in session");
|
|
1631
|
-
recordDebounce(targetSession);
|
|
1632
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process
|
|
1700
|
+
const batched2 = recordDebounce(targetSession);
|
|
1701
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
1633
1702
|
return "queued";
|
|
1634
1703
|
}
|
|
1635
1704
|
if (sessionState === "thinking" || sessionState === "tool") {
|
|
1636
1705
|
queueIntercom(targetSession, "session busy at send time");
|
|
1637
|
-
recordDebounce(targetSession);
|
|
1638
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (session busy
|
|
1706
|
+
const batched2 = recordDebounce(targetSession);
|
|
1707
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (session busy)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
1639
1708
|
return "queued";
|
|
1640
1709
|
}
|
|
1641
1710
|
if (transport.isPaneInCopyMode(targetSession)) {
|
|
@@ -1643,8 +1712,8 @@ function sendIntercom(targetSession) {
|
|
|
1643
1712
|
transport.sendKeys(targetSession, "q");
|
|
1644
1713
|
}
|
|
1645
1714
|
transport.sendKeys(targetSession, "/exe-intercom");
|
|
1646
|
-
recordDebounce(targetSession);
|
|
1647
|
-
logIntercom(`DELIVERED \u2192 ${targetSession} (fire-and-forget)`);
|
|
1715
|
+
const batched = recordDebounce(targetSession);
|
|
1716
|
+
logIntercom(`DELIVERED \u2192 ${targetSession}${batched > 0 ? ` [${batched} nudges batched during debounce]` : ""} (fire-and-forget)`);
|
|
1648
1717
|
return "delivered";
|
|
1649
1718
|
} catch {
|
|
1650
1719
|
logIntercom(`FAIL \u2192 ${targetSession}`);
|
|
@@ -1746,26 +1815,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
1746
1815
|
const transport = getTransport();
|
|
1747
1816
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
1748
1817
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
1749
|
-
const logDir =
|
|
1750
|
-
const logFile =
|
|
1751
|
-
if (!
|
|
1752
|
-
|
|
1818
|
+
const logDir = path9.join(os6.homedir(), ".exe-os", "session-logs");
|
|
1819
|
+
const logFile = path9.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
1820
|
+
if (!existsSync9(logDir)) {
|
|
1821
|
+
mkdirSync5(logDir, { recursive: true });
|
|
1753
1822
|
}
|
|
1754
1823
|
transport.kill(sessionName);
|
|
1755
1824
|
let cleanupSuffix = "";
|
|
1756
1825
|
try {
|
|
1757
1826
|
const thisFile = fileURLToPath(import.meta.url);
|
|
1758
|
-
const cleanupScript =
|
|
1759
|
-
if (
|
|
1827
|
+
const cleanupScript = path9.join(path9.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
1828
|
+
if (existsSync9(cleanupScript)) {
|
|
1760
1829
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
1761
1830
|
}
|
|
1762
1831
|
} catch {
|
|
1763
1832
|
}
|
|
1764
1833
|
try {
|
|
1765
|
-
const claudeJsonPath =
|
|
1834
|
+
const claudeJsonPath = path9.join(os6.homedir(), ".claude.json");
|
|
1766
1835
|
let claudeJson = {};
|
|
1767
1836
|
try {
|
|
1768
|
-
claudeJson = JSON.parse(
|
|
1837
|
+
claudeJson = JSON.parse(readFileSync9(claudeJsonPath, "utf8"));
|
|
1769
1838
|
} catch {
|
|
1770
1839
|
}
|
|
1771
1840
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -1773,17 +1842,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
1773
1842
|
const trustDir = opts?.cwd ?? projectDir;
|
|
1774
1843
|
if (!projects[trustDir]) projects[trustDir] = {};
|
|
1775
1844
|
projects[trustDir].hasTrustDialogAccepted = true;
|
|
1776
|
-
|
|
1845
|
+
writeFileSync6(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
1777
1846
|
} catch {
|
|
1778
1847
|
}
|
|
1779
1848
|
try {
|
|
1780
|
-
const settingsDir =
|
|
1849
|
+
const settingsDir = path9.join(os6.homedir(), ".claude", "projects");
|
|
1781
1850
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
1782
|
-
const projSettingsDir =
|
|
1783
|
-
const settingsPath =
|
|
1851
|
+
const projSettingsDir = path9.join(settingsDir, normalizedKey);
|
|
1852
|
+
const settingsPath = path9.join(projSettingsDir, "settings.json");
|
|
1784
1853
|
let settings = {};
|
|
1785
1854
|
try {
|
|
1786
|
-
settings = JSON.parse(
|
|
1855
|
+
settings = JSON.parse(readFileSync9(settingsPath, "utf8"));
|
|
1787
1856
|
} catch {
|
|
1788
1857
|
}
|
|
1789
1858
|
const perms = settings.permissions ?? {};
|
|
@@ -1811,20 +1880,23 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
1811
1880
|
if (changed) {
|
|
1812
1881
|
perms.allow = allow;
|
|
1813
1882
|
settings.permissions = perms;
|
|
1814
|
-
|
|
1815
|
-
|
|
1883
|
+
mkdirSync5(projSettingsDir, { recursive: true });
|
|
1884
|
+
writeFileSync6(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
1816
1885
|
}
|
|
1817
1886
|
} catch {
|
|
1818
1887
|
}
|
|
1819
1888
|
const spawnCwd = opts?.cwd ?? projectDir;
|
|
1820
1889
|
const useExeAgent = !!(opts?.model && opts?.provider);
|
|
1821
|
-
const
|
|
1890
|
+
const agentRtConfig = getAgentRuntime(employeeName);
|
|
1891
|
+
const useCodex = !useExeAgent && agentRtConfig.runtime === "codex";
|
|
1892
|
+
const useOpencode = !useExeAgent && !useCodex && agentRtConfig.runtime === "opencode";
|
|
1893
|
+
const ccProvider = useExeAgent || useCodex || useOpencode ? DEFAULT_PROVIDER : detectActiveProvider();
|
|
1822
1894
|
const useBinSymlink = ccProvider !== DEFAULT_PROVIDER;
|
|
1823
1895
|
let identityFlag = "";
|
|
1824
1896
|
let behaviorsFlag = "";
|
|
1825
1897
|
let legacyFallbackWarned = false;
|
|
1826
1898
|
if (!useExeAgent && !useBinSymlink) {
|
|
1827
|
-
const identityPath2 =
|
|
1899
|
+
const identityPath2 = path9.join(
|
|
1828
1900
|
os6.homedir(),
|
|
1829
1901
|
".exe-os",
|
|
1830
1902
|
"identity",
|
|
@@ -1834,13 +1906,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
1834
1906
|
const hasAgentFlag = claudeSupportsAgentFlag();
|
|
1835
1907
|
if (hasAgentFlag) {
|
|
1836
1908
|
identityFlag = ` --agent ${employeeName}`;
|
|
1837
|
-
} else if (
|
|
1909
|
+
} else if (existsSync9(identityPath2)) {
|
|
1838
1910
|
identityFlag = ` --append-system-prompt-file ${identityPath2}`;
|
|
1839
1911
|
legacyFallbackWarned = true;
|
|
1840
1912
|
}
|
|
1841
1913
|
const behaviorsFile = exportBehaviorsSync(
|
|
1842
1914
|
employeeName,
|
|
1843
|
-
|
|
1915
|
+
path9.basename(spawnCwd),
|
|
1844
1916
|
sessionName
|
|
1845
1917
|
);
|
|
1846
1918
|
if (behaviorsFile) {
|
|
@@ -1855,16 +1927,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
1855
1927
|
}
|
|
1856
1928
|
let sessionContextFlag = "";
|
|
1857
1929
|
try {
|
|
1858
|
-
const ctxDir =
|
|
1859
|
-
|
|
1860
|
-
const ctxFile =
|
|
1930
|
+
const ctxDir = path9.join(os6.homedir(), ".exe-os", "session-cache");
|
|
1931
|
+
mkdirSync5(ctxDir, { recursive: true });
|
|
1932
|
+
const ctxFile = path9.join(ctxDir, `session-context-${sessionName}.md`);
|
|
1861
1933
|
const ctxContent = [
|
|
1862
1934
|
`## Session Context`,
|
|
1863
1935
|
`You are running in tmux session: ${sessionName}.`,
|
|
1864
1936
|
`Your parent coordinator session is ${exeSession}.`,
|
|
1865
1937
|
`Your employees (if any) use the -${exeSession} suffix.`
|
|
1866
1938
|
].join("\n");
|
|
1867
|
-
|
|
1939
|
+
writeFileSync6(ctxFile, ctxContent);
|
|
1868
1940
|
sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
|
|
1869
1941
|
} catch {
|
|
1870
1942
|
}
|
|
@@ -1878,9 +1950,48 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
1878
1950
|
}
|
|
1879
1951
|
}
|
|
1880
1952
|
}
|
|
1953
|
+
if (useCodex) {
|
|
1954
|
+
const codexCfg = RUNTIME_TABLE.codex;
|
|
1955
|
+
if (codexCfg?.apiKeyEnv) {
|
|
1956
|
+
const keyVal = process.env[codexCfg.apiKeyEnv];
|
|
1957
|
+
if (keyVal) {
|
|
1958
|
+
envPrefix = `${envPrefix} ${codexCfg.apiKeyEnv}=${keyVal}`;
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
envPrefix = `${envPrefix} EXE_AGENT_MODEL=${agentRtConfig.model}`;
|
|
1962
|
+
}
|
|
1963
|
+
if (useOpencode) {
|
|
1964
|
+
const ocCfg = PROVIDER_TABLE.opencode;
|
|
1965
|
+
if (ocCfg?.apiKeyEnv) {
|
|
1966
|
+
const keyVal = process.env[ocCfg.apiKeyEnv];
|
|
1967
|
+
if (keyVal) {
|
|
1968
|
+
envPrefix = `${envPrefix} ${ocCfg.apiKeyEnv}=${keyVal}`;
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
|
|
1972
|
+
}
|
|
1973
|
+
if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
|
|
1974
|
+
const defaultClaudeModel = DEFAULT_MODELS.claude;
|
|
1975
|
+
if (agentRtConfig.runtime === "claude" && agentRtConfig.model !== defaultClaudeModel) {
|
|
1976
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
|
|
1977
|
+
}
|
|
1978
|
+
}
|
|
1881
1979
|
let spawnCommand;
|
|
1882
1980
|
if (useExeAgent) {
|
|
1883
1981
|
spawnCommand = `${envPrefix} exe-agent --employee ${employeeName} --model ${opts.model} --provider ${opts.provider}${cleanupSuffix}`;
|
|
1982
|
+
} else if (useCodex) {
|
|
1983
|
+
process.stderr.write(
|
|
1984
|
+
`[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
|
|
1985
|
+
`
|
|
1986
|
+
);
|
|
1987
|
+
spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
|
|
1988
|
+
} else if (useOpencode) {
|
|
1989
|
+
const binName = `${employeeName}-opencode`;
|
|
1990
|
+
process.stderr.write(
|
|
1991
|
+
`[tmux-routing] agent-config: ${employeeName} \u2192 opencode (${agentRtConfig.model})
|
|
1992
|
+
`
|
|
1993
|
+
);
|
|
1994
|
+
spawnCommand = `${envPrefix} ${binName}${cleanupSuffix}`;
|
|
1884
1995
|
} else if (useBinSymlink) {
|
|
1885
1996
|
const binName = `${employeeName}-${ccProvider}`;
|
|
1886
1997
|
process.stderr.write(
|
|
@@ -1902,11 +2013,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
1902
2013
|
transport.pipeLog(sessionName, logFile);
|
|
1903
2014
|
try {
|
|
1904
2015
|
const mySession = getMySession();
|
|
1905
|
-
const dispatchInfo =
|
|
1906
|
-
|
|
2016
|
+
const dispatchInfo = path9.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
2017
|
+
writeFileSync6(dispatchInfo, JSON.stringify({
|
|
1907
2018
|
dispatchedBy: mySession,
|
|
1908
2019
|
rootExe: exeSession,
|
|
1909
|
-
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : "anthropic",
|
|
2020
|
+
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
|
|
2021
|
+
runtime: useCodex ? "codex" : useOpencode ? "opencode" : useExeAgent ? "exe-agent" : "claude",
|
|
2022
|
+
model: useCodex ? agentRtConfig.model : useOpencode ? agentRtConfig.model : void 0,
|
|
1910
2023
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1911
2024
|
}));
|
|
1912
2025
|
} catch {
|
|
@@ -1924,6 +2037,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
1924
2037
|
booted = true;
|
|
1925
2038
|
break;
|
|
1926
2039
|
}
|
|
2040
|
+
} else if (useCodex) {
|
|
2041
|
+
if (pane.includes("codex") || pane.includes("Codex") || pane.includes("exe-start-codex")) {
|
|
2042
|
+
booted = true;
|
|
2043
|
+
break;
|
|
2044
|
+
}
|
|
1927
2045
|
} else {
|
|
1928
2046
|
if (pane.includes("Claude Code") || pane.includes("\u276F")) {
|
|
1929
2047
|
booted = true;
|
|
@@ -1935,9 +2053,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
1935
2053
|
}
|
|
1936
2054
|
if (!booted) {
|
|
1937
2055
|
releaseSpawnLock(sessionName);
|
|
1938
|
-
|
|
2056
|
+
const runtimeLabel = useExeAgent ? "exe-agent" : useCodex ? "codex" : "claude";
|
|
2057
|
+
return { sessionName, error: `${runtimeLabel} did not boot within 15s` };
|
|
1939
2058
|
}
|
|
1940
|
-
if (!useExeAgent) {
|
|
2059
|
+
if (!useExeAgent && !useCodex) {
|
|
1941
2060
|
try {
|
|
1942
2061
|
transport.sendKeys(sessionName, `/exe-call ${employeeName}`);
|
|
1943
2062
|
} catch {
|
|
@@ -1964,17 +2083,19 @@ var init_tmux_routing = __esm({
|
|
|
1964
2083
|
init_cc_agent_support();
|
|
1965
2084
|
init_mcp_prefix();
|
|
1966
2085
|
init_provider_table();
|
|
2086
|
+
init_agent_config();
|
|
2087
|
+
init_runtime_table();
|
|
1967
2088
|
init_intercom_queue();
|
|
1968
2089
|
init_plan_limits();
|
|
1969
2090
|
init_employees();
|
|
1970
|
-
SPAWN_LOCK_DIR =
|
|
1971
|
-
SESSION_CACHE =
|
|
2091
|
+
SPAWN_LOCK_DIR = path9.join(os6.homedir(), ".exe-os", "spawn-locks");
|
|
2092
|
+
SESSION_CACHE = path9.join(os6.homedir(), ".exe-os", "session-cache");
|
|
1972
2093
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
1973
2094
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
1974
2095
|
VERIFY_PANE_LINES = 200;
|
|
1975
2096
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
1976
|
-
INTERCOM_LOG2 =
|
|
1977
|
-
DEBOUNCE_FILE =
|
|
2097
|
+
INTERCOM_LOG2 = path9.join(os6.homedir(), ".exe-os", "intercom.log");
|
|
2098
|
+
DEBOUNCE_FILE = path9.join(SESSION_CACHE, "intercom-debounce.json");
|
|
1978
2099
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
1979
2100
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
|
|
1980
2101
|
}
|
|
@@ -2006,11 +2127,11 @@ var init_task_scope = __esm({
|
|
|
2006
2127
|
|
|
2007
2128
|
// src/lib/tasks-crud.ts
|
|
2008
2129
|
import crypto3 from "crypto";
|
|
2009
|
-
import
|
|
2130
|
+
import path10 from "path";
|
|
2010
2131
|
import os7 from "os";
|
|
2011
2132
|
import { execSync as execSync5 } from "child_process";
|
|
2012
2133
|
import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
|
|
2013
|
-
import { existsSync as
|
|
2134
|
+
import { existsSync as existsSync10, readFileSync as readFileSync10 } from "fs";
|
|
2014
2135
|
async function writeCheckpoint(input) {
|
|
2015
2136
|
const client = getClient();
|
|
2016
2137
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -2185,8 +2306,8 @@ ${laneWarning}` : laneWarning;
|
|
|
2185
2306
|
}
|
|
2186
2307
|
if (input.baseDir) {
|
|
2187
2308
|
try {
|
|
2188
|
-
await mkdir3(
|
|
2189
|
-
await mkdir3(
|
|
2309
|
+
await mkdir3(path10.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
2310
|
+
await mkdir3(path10.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
2190
2311
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
2191
2312
|
await ensureGitignoreExe(input.baseDir);
|
|
2192
2313
|
} catch {
|
|
@@ -2222,10 +2343,10 @@ ${laneWarning}` : laneWarning;
|
|
|
2222
2343
|
});
|
|
2223
2344
|
if (input.baseDir) {
|
|
2224
2345
|
try {
|
|
2225
|
-
const EXE_OS_DIR =
|
|
2226
|
-
const mdPath =
|
|
2227
|
-
const mdDir =
|
|
2228
|
-
if (!
|
|
2346
|
+
const EXE_OS_DIR = path10.join(os7.homedir(), ".exe-os");
|
|
2347
|
+
const mdPath = path10.join(EXE_OS_DIR, taskFile);
|
|
2348
|
+
const mdDir = path10.dirname(mdPath);
|
|
2349
|
+
if (!existsSync10(mdDir)) await mkdir3(mdDir, { recursive: true });
|
|
2229
2350
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
2230
2351
|
const mdContent = `# ${input.title}
|
|
2231
2352
|
|
|
@@ -2250,7 +2371,11 @@ If you skip this, your reviewer will not know you're done and your work won't be
|
|
|
2250
2371
|
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
2251
2372
|
`;
|
|
2252
2373
|
await writeFile3(mdPath, mdContent, "utf-8");
|
|
2253
|
-
} catch {
|
|
2374
|
+
} catch (err) {
|
|
2375
|
+
process.stderr.write(
|
|
2376
|
+
`[create-task] WARNING: .md file write failed for ${taskFile}: ${err instanceof Error ? err.message : String(err)}
|
|
2377
|
+
`
|
|
2378
|
+
);
|
|
2254
2379
|
}
|
|
2255
2380
|
}
|
|
2256
2381
|
return {
|
|
@@ -2510,9 +2635,9 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
2510
2635
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
2511
2636
|
}
|
|
2512
2637
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
2513
|
-
const archPath =
|
|
2638
|
+
const archPath = path10.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
2514
2639
|
try {
|
|
2515
|
-
if (
|
|
2640
|
+
if (existsSync10(archPath)) return;
|
|
2516
2641
|
const template = [
|
|
2517
2642
|
`# ${projectName} \u2014 System Architecture`,
|
|
2518
2643
|
"",
|
|
@@ -2545,10 +2670,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
2545
2670
|
}
|
|
2546
2671
|
}
|
|
2547
2672
|
async function ensureGitignoreExe(baseDir) {
|
|
2548
|
-
const gitignorePath =
|
|
2673
|
+
const gitignorePath = path10.join(baseDir, ".gitignore");
|
|
2549
2674
|
try {
|
|
2550
|
-
if (
|
|
2551
|
-
const content =
|
|
2675
|
+
if (existsSync10(gitignorePath)) {
|
|
2676
|
+
const content = readFileSync10(gitignorePath, "utf-8");
|
|
2552
2677
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
2553
2678
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
2554
2679
|
} else {
|
|
@@ -2579,8 +2704,8 @@ var init_tasks_crud = __esm({
|
|
|
2579
2704
|
});
|
|
2580
2705
|
|
|
2581
2706
|
// src/lib/tasks-review.ts
|
|
2582
|
-
import
|
|
2583
|
-
import { existsSync as
|
|
2707
|
+
import path11 from "path";
|
|
2708
|
+
import { existsSync as existsSync11, readdirSync as readdirSync2, unlinkSync as unlinkSync4 } from "fs";
|
|
2584
2709
|
async function countPendingReviews(sessionScope) {
|
|
2585
2710
|
const client = getClient();
|
|
2586
2711
|
if (sessionScope) {
|
|
@@ -2761,11 +2886,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
2761
2886
|
);
|
|
2762
2887
|
}
|
|
2763
2888
|
try {
|
|
2764
|
-
const cacheDir =
|
|
2765
|
-
if (
|
|
2889
|
+
const cacheDir = path11.join(EXE_AI_DIR, "session-cache");
|
|
2890
|
+
if (existsSync11(cacheDir)) {
|
|
2766
2891
|
for (const f of readdirSync2(cacheDir)) {
|
|
2767
2892
|
if (f.startsWith("review-notified-")) {
|
|
2768
|
-
unlinkSync4(
|
|
2893
|
+
unlinkSync4(path11.join(cacheDir, f));
|
|
2769
2894
|
}
|
|
2770
2895
|
}
|
|
2771
2896
|
}
|
|
@@ -2786,7 +2911,7 @@ var init_tasks_review = __esm({
|
|
|
2786
2911
|
});
|
|
2787
2912
|
|
|
2788
2913
|
// src/lib/tasks-chain.ts
|
|
2789
|
-
import
|
|
2914
|
+
import path12 from "path";
|
|
2790
2915
|
import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
2791
2916
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
2792
2917
|
const client = getClient();
|
|
@@ -2803,7 +2928,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
2803
2928
|
});
|
|
2804
2929
|
for (const ur of unblockedRows.rows) {
|
|
2805
2930
|
try {
|
|
2806
|
-
const ubFile =
|
|
2931
|
+
const ubFile = path12.join(baseDir, String(ur.task_file));
|
|
2807
2932
|
let ubContent = await readFile3(ubFile, "utf-8");
|
|
2808
2933
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
2809
2934
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -2872,7 +2997,7 @@ var init_tasks_chain = __esm({
|
|
|
2872
2997
|
|
|
2873
2998
|
// src/lib/project-name.ts
|
|
2874
2999
|
import { execSync as execSync6 } from "child_process";
|
|
2875
|
-
import
|
|
3000
|
+
import path13 from "path";
|
|
2876
3001
|
function getProjectName(cwd) {
|
|
2877
3002
|
const dir = cwd ?? process.cwd();
|
|
2878
3003
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -2885,7 +3010,7 @@ function getProjectName(cwd) {
|
|
|
2885
3010
|
timeout: 2e3,
|
|
2886
3011
|
stdio: ["pipe", "pipe", "pipe"]
|
|
2887
3012
|
}).trim();
|
|
2888
|
-
repoRoot =
|
|
3013
|
+
repoRoot = path13.dirname(gitCommonDir);
|
|
2889
3014
|
} catch {
|
|
2890
3015
|
repoRoot = execSync6("git rev-parse --show-toplevel", {
|
|
2891
3016
|
cwd: dir,
|
|
@@ -2894,11 +3019,11 @@ function getProjectName(cwd) {
|
|
|
2894
3019
|
stdio: ["pipe", "pipe", "pipe"]
|
|
2895
3020
|
}).trim();
|
|
2896
3021
|
}
|
|
2897
|
-
_cached2 =
|
|
3022
|
+
_cached2 = path13.basename(repoRoot);
|
|
2898
3023
|
_cachedCwd = dir;
|
|
2899
3024
|
return _cached2;
|
|
2900
3025
|
} catch {
|
|
2901
|
-
_cached2 =
|
|
3026
|
+
_cached2 = path13.basename(dir);
|
|
2902
3027
|
_cachedCwd = dir;
|
|
2903
3028
|
return _cached2;
|
|
2904
3029
|
}
|
|
@@ -3371,8 +3496,8 @@ __export(tasks_exports, {
|
|
|
3371
3496
|
updateTaskStatus: () => updateTaskStatus,
|
|
3372
3497
|
writeCheckpoint: () => writeCheckpoint
|
|
3373
3498
|
});
|
|
3374
|
-
import
|
|
3375
|
-
import { writeFileSync as
|
|
3499
|
+
import path14 from "path";
|
|
3500
|
+
import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, unlinkSync as unlinkSync5 } from "fs";
|
|
3376
3501
|
async function createTask(input) {
|
|
3377
3502
|
const result = await createTaskCore(input);
|
|
3378
3503
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -3391,11 +3516,11 @@ async function updateTask(input) {
|
|
|
3391
3516
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
3392
3517
|
try {
|
|
3393
3518
|
const agent = String(row.assigned_to);
|
|
3394
|
-
const cacheDir =
|
|
3395
|
-
const cachePath =
|
|
3519
|
+
const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
|
|
3520
|
+
const cachePath = path14.join(cacheDir, `current-task-${agent}.json`);
|
|
3396
3521
|
if (input.status === "in_progress") {
|
|
3397
|
-
|
|
3398
|
-
|
|
3522
|
+
mkdirSync6(cacheDir, { recursive: true });
|
|
3523
|
+
writeFileSync7(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
3399
3524
|
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
|
|
3400
3525
|
try {
|
|
3401
3526
|
unlinkSync5(cachePath);
|
|
@@ -3561,17 +3686,17 @@ __export(identity_exports, {
|
|
|
3561
3686
|
listIdentities: () => listIdentities,
|
|
3562
3687
|
updateIdentity: () => updateIdentity
|
|
3563
3688
|
});
|
|
3564
|
-
import { existsSync as
|
|
3689
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync8, readFileSync as readFileSync12, writeFileSync as writeFileSync9 } from "fs";
|
|
3565
3690
|
import { readdirSync as readdirSync4 } from "fs";
|
|
3566
|
-
import
|
|
3691
|
+
import path16 from "path";
|
|
3567
3692
|
import { createHash } from "crypto";
|
|
3568
3693
|
function ensureDir2() {
|
|
3569
|
-
if (!
|
|
3570
|
-
|
|
3694
|
+
if (!existsSync12(IDENTITY_DIR)) {
|
|
3695
|
+
mkdirSync8(IDENTITY_DIR, { recursive: true });
|
|
3571
3696
|
}
|
|
3572
3697
|
}
|
|
3573
3698
|
function identityPath(agentId) {
|
|
3574
|
-
return
|
|
3699
|
+
return path16.join(IDENTITY_DIR, `${agentId}.md`);
|
|
3575
3700
|
}
|
|
3576
3701
|
function parseFrontmatter(raw) {
|
|
3577
3702
|
const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
@@ -3612,8 +3737,8 @@ function contentHash(content) {
|
|
|
3612
3737
|
}
|
|
3613
3738
|
function getIdentity(agentId) {
|
|
3614
3739
|
const filePath = identityPath(agentId);
|
|
3615
|
-
if (!
|
|
3616
|
-
const raw =
|
|
3740
|
+
if (!existsSync12(filePath)) return null;
|
|
3741
|
+
const raw = readFileSync12(filePath, "utf-8");
|
|
3617
3742
|
const { frontmatter, body } = parseFrontmatter(raw);
|
|
3618
3743
|
return {
|
|
3619
3744
|
agentId,
|
|
@@ -3627,7 +3752,7 @@ async function updateIdentity(agentId, content, updatedBy) {
|
|
|
3627
3752
|
ensureDir2();
|
|
3628
3753
|
const filePath = identityPath(agentId);
|
|
3629
3754
|
const hash = contentHash(content);
|
|
3630
|
-
|
|
3755
|
+
writeFileSync9(filePath, content, "utf-8");
|
|
3631
3756
|
try {
|
|
3632
3757
|
const client = getClient();
|
|
3633
3758
|
await client.execute({
|
|
@@ -3683,7 +3808,7 @@ var init_identity = __esm({
|
|
|
3683
3808
|
"use strict";
|
|
3684
3809
|
init_config();
|
|
3685
3810
|
init_database();
|
|
3686
|
-
IDENTITY_DIR =
|
|
3811
|
+
IDENTITY_DIR = path16.join(EXE_AI_DIR, "identity");
|
|
3687
3812
|
}
|
|
3688
3813
|
});
|
|
3689
3814
|
|
|
@@ -4223,16 +4348,16 @@ import { z } from "zod";
|
|
|
4223
4348
|
|
|
4224
4349
|
// src/adapters/claude/active-agent.ts
|
|
4225
4350
|
init_config();
|
|
4226
|
-
import { readFileSync as
|
|
4351
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync8, mkdirSync as mkdirSync7, unlinkSync as unlinkSync6, readdirSync as readdirSync3 } from "fs";
|
|
4227
4352
|
import { execSync as execSync7 } from "child_process";
|
|
4228
|
-
import
|
|
4353
|
+
import path15 from "path";
|
|
4229
4354
|
|
|
4230
4355
|
// src/adapters/claude/session-key.ts
|
|
4231
4356
|
init_session_key();
|
|
4232
4357
|
|
|
4233
4358
|
// src/adapters/claude/active-agent.ts
|
|
4234
4359
|
init_employees();
|
|
4235
|
-
var CACHE_DIR =
|
|
4360
|
+
var CACHE_DIR = path15.join(EXE_AI_DIR, "session-cache");
|
|
4236
4361
|
var STALE_MS = 24 * 60 * 60 * 1e3;
|
|
4237
4362
|
function isNameWithOptionalInstance(candidate, baseName) {
|
|
4238
4363
|
if (candidate === baseName) return true;
|
|
@@ -4277,12 +4402,12 @@ function resolveActiveAgentFromTmuxSession(sessionName) {
|
|
|
4277
4402
|
return null;
|
|
4278
4403
|
}
|
|
4279
4404
|
function getMarkerPath() {
|
|
4280
|
-
return
|
|
4405
|
+
return path15.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
|
|
4281
4406
|
}
|
|
4282
4407
|
function getActiveAgent() {
|
|
4283
4408
|
try {
|
|
4284
4409
|
const markerPath = getMarkerPath();
|
|
4285
|
-
const raw =
|
|
4410
|
+
const raw = readFileSync11(markerPath, "utf8");
|
|
4286
4411
|
const data = JSON.parse(raw);
|
|
4287
4412
|
if (data.agentId) {
|
|
4288
4413
|
if (data.startedAt) {
|
|
@@ -4367,10 +4492,10 @@ function registerCreateTask(server) {
|
|
|
4367
4492
|
skipDispatch: true
|
|
4368
4493
|
});
|
|
4369
4494
|
try {
|
|
4370
|
-
const { existsSync:
|
|
4495
|
+
const { existsSync: existsSync13, mkdirSync: mkdirSync9, writeFileSync: writeFileSync10 } = await import("fs");
|
|
4371
4496
|
const { identityPath: identityPath2 } = await Promise.resolve().then(() => (init_identity(), identity_exports));
|
|
4372
4497
|
const idPath = identityPath2(assigned_to);
|
|
4373
|
-
if (!
|
|
4498
|
+
if (!existsSync13(idPath)) {
|
|
4374
4499
|
const { loadEmployees: loadEmployees2 } = await Promise.resolve().then(() => (init_employees(), employees_exports));
|
|
4375
4500
|
const employees = await loadEmployees2();
|
|
4376
4501
|
const emp = employees.find((e) => e.name === assigned_to);
|
|
@@ -4379,8 +4504,8 @@ function registerCreateTask(server) {
|
|
|
4379
4504
|
const template = getTemplateForTitle2(emp.role);
|
|
4380
4505
|
if (template) {
|
|
4381
4506
|
const dir = (await import("path")).dirname(idPath);
|
|
4382
|
-
if (!
|
|
4383
|
-
|
|
4507
|
+
if (!existsSync13(dir)) mkdirSync9(dir, { recursive: true });
|
|
4508
|
+
writeFileSync10(idPath, template.replace(/^agent_id: \w+/m, `agent_id: ${assigned_to}`), "utf-8");
|
|
4384
4509
|
}
|
|
4385
4510
|
}
|
|
4386
4511
|
}
|