@askexenow/exe-os 0.8.85 → 0.8.87
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 +510 -340
- 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 +50 -5
- 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
package/dist/lib/tasks.js
CHANGED
|
@@ -641,18 +641,69 @@ var init_provider_table = __esm({
|
|
|
641
641
|
}
|
|
642
642
|
});
|
|
643
643
|
|
|
644
|
-
// src/lib/
|
|
645
|
-
|
|
644
|
+
// src/lib/runtime-table.ts
|
|
645
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
646
|
+
var init_runtime_table = __esm({
|
|
647
|
+
"src/lib/runtime-table.ts"() {
|
|
648
|
+
"use strict";
|
|
649
|
+
RUNTIME_TABLE = {
|
|
650
|
+
codex: {
|
|
651
|
+
binary: "codex",
|
|
652
|
+
launchMode: "exec",
|
|
653
|
+
autoApproveFlag: "--full-auto",
|
|
654
|
+
inlineFlag: "--no-alt-screen",
|
|
655
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
656
|
+
defaultModel: "gpt-5.4"
|
|
657
|
+
}
|
|
658
|
+
};
|
|
659
|
+
DEFAULT_RUNTIME = "claude";
|
|
660
|
+
}
|
|
661
|
+
});
|
|
662
|
+
|
|
663
|
+
// src/lib/agent-config.ts
|
|
664
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
|
|
646
665
|
import path5 from "path";
|
|
666
|
+
function loadAgentConfig() {
|
|
667
|
+
if (!existsSync5(AGENT_CONFIG_PATH)) return {};
|
|
668
|
+
try {
|
|
669
|
+
return JSON.parse(readFileSync5(AGENT_CONFIG_PATH, "utf-8"));
|
|
670
|
+
} catch {
|
|
671
|
+
return {};
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
function getAgentRuntime(agentId) {
|
|
675
|
+
const config = loadAgentConfig();
|
|
676
|
+
const entry = config[agentId];
|
|
677
|
+
if (entry) return entry;
|
|
678
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
679
|
+
}
|
|
680
|
+
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
681
|
+
var init_agent_config = __esm({
|
|
682
|
+
"src/lib/agent-config.ts"() {
|
|
683
|
+
"use strict";
|
|
684
|
+
init_config();
|
|
685
|
+
init_runtime_table();
|
|
686
|
+
AGENT_CONFIG_PATH = path5.join(EXE_AI_DIR, "agent-config.json");
|
|
687
|
+
DEFAULT_MODELS = {
|
|
688
|
+
claude: "claude-opus-4",
|
|
689
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
690
|
+
opencode: "minimax-m2.7"
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
// src/lib/intercom-queue.ts
|
|
696
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as existsSync6, mkdirSync as mkdirSync3 } from "fs";
|
|
697
|
+
import path6 from "path";
|
|
647
698
|
import os5 from "os";
|
|
648
699
|
function ensureDir() {
|
|
649
|
-
const dir =
|
|
650
|
-
if (!
|
|
700
|
+
const dir = path6.dirname(QUEUE_PATH);
|
|
701
|
+
if (!existsSync6(dir)) mkdirSync3(dir, { recursive: true });
|
|
651
702
|
}
|
|
652
703
|
function readQueue() {
|
|
653
704
|
try {
|
|
654
|
-
if (!
|
|
655
|
-
return JSON.parse(
|
|
705
|
+
if (!existsSync6(QUEUE_PATH)) return [];
|
|
706
|
+
return JSON.parse(readFileSync6(QUEUE_PATH, "utf8"));
|
|
656
707
|
} catch {
|
|
657
708
|
return [];
|
|
658
709
|
}
|
|
@@ -660,7 +711,7 @@ function readQueue() {
|
|
|
660
711
|
function writeQueue(queue) {
|
|
661
712
|
ensureDir();
|
|
662
713
|
const tmp = `${QUEUE_PATH}.tmp`;
|
|
663
|
-
|
|
714
|
+
writeFileSync4(tmp, JSON.stringify(queue, null, 2));
|
|
664
715
|
renameSync3(tmp, QUEUE_PATH);
|
|
665
716
|
}
|
|
666
717
|
function queueIntercom(targetSession, reason) {
|
|
@@ -684,25 +735,25 @@ var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
|
684
735
|
var init_intercom_queue = __esm({
|
|
685
736
|
"src/lib/intercom-queue.ts"() {
|
|
686
737
|
"use strict";
|
|
687
|
-
QUEUE_PATH =
|
|
738
|
+
QUEUE_PATH = path6.join(os5.homedir(), ".exe-os", "intercom-queue.json");
|
|
688
739
|
TTL_MS = 60 * 60 * 1e3;
|
|
689
|
-
INTERCOM_LOG =
|
|
740
|
+
INTERCOM_LOG = path6.join(os5.homedir(), ".exe-os", "intercom.log");
|
|
690
741
|
}
|
|
691
742
|
});
|
|
692
743
|
|
|
693
744
|
// src/lib/license.ts
|
|
694
|
-
import { readFileSync as
|
|
745
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
|
|
695
746
|
import { randomUUID } from "crypto";
|
|
696
|
-
import
|
|
747
|
+
import path7 from "path";
|
|
697
748
|
import { jwtVerify, importSPKI } from "jose";
|
|
698
749
|
var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, PLAN_LIMITS;
|
|
699
750
|
var init_license = __esm({
|
|
700
751
|
"src/lib/license.ts"() {
|
|
701
752
|
"use strict";
|
|
702
753
|
init_config();
|
|
703
|
-
LICENSE_PATH =
|
|
704
|
-
CACHE_PATH =
|
|
705
|
-
DEVICE_ID_PATH =
|
|
754
|
+
LICENSE_PATH = path7.join(EXE_AI_DIR, "license.key");
|
|
755
|
+
CACHE_PATH = path7.join(EXE_AI_DIR, "license-cache.json");
|
|
756
|
+
DEVICE_ID_PATH = path7.join(EXE_AI_DIR, "device-id");
|
|
706
757
|
PLAN_LIMITS = {
|
|
707
758
|
free: { devices: 1, employees: 1, memories: 5e3 },
|
|
708
759
|
pro: { devices: 3, employees: 5, memories: 1e5 },
|
|
@@ -714,12 +765,12 @@ var init_license = __esm({
|
|
|
714
765
|
});
|
|
715
766
|
|
|
716
767
|
// src/lib/plan-limits.ts
|
|
717
|
-
import { readFileSync as
|
|
718
|
-
import
|
|
768
|
+
import { readFileSync as readFileSync8, existsSync as existsSync8 } from "fs";
|
|
769
|
+
import path8 from "path";
|
|
719
770
|
function getLicenseSync() {
|
|
720
771
|
try {
|
|
721
|
-
if (!
|
|
722
|
-
const raw = JSON.parse(
|
|
772
|
+
if (!existsSync8(CACHE_PATH2)) return freeLicense();
|
|
773
|
+
const raw = JSON.parse(readFileSync8(CACHE_PATH2, "utf8"));
|
|
723
774
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
724
775
|
const parts = raw.token.split(".");
|
|
725
776
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -757,8 +808,8 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
757
808
|
const filePath = rosterPath ?? EMPLOYEES_PATH;
|
|
758
809
|
let count = 0;
|
|
759
810
|
try {
|
|
760
|
-
if (
|
|
761
|
-
const raw =
|
|
811
|
+
if (existsSync8(filePath)) {
|
|
812
|
+
const raw = readFileSync8(filePath, "utf8");
|
|
762
813
|
const employees = JSON.parse(raw);
|
|
763
814
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
764
815
|
}
|
|
@@ -787,7 +838,7 @@ var init_plan_limits = __esm({
|
|
|
787
838
|
this.name = "PlanLimitError";
|
|
788
839
|
}
|
|
789
840
|
};
|
|
790
|
-
CACHE_PATH2 =
|
|
841
|
+
CACHE_PATH2 = path8.join(EXE_AI_DIR, "license-cache.json");
|
|
791
842
|
}
|
|
792
843
|
});
|
|
793
844
|
|
|
@@ -1135,13 +1186,13 @@ __export(tmux_routing_exports, {
|
|
|
1135
1186
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
1136
1187
|
});
|
|
1137
1188
|
import { execFileSync as execFileSync2, execSync as execSync4 } from "child_process";
|
|
1138
|
-
import { readFileSync as
|
|
1139
|
-
import
|
|
1189
|
+
import { readFileSync as readFileSync9, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, existsSync as existsSync9, appendFileSync } from "fs";
|
|
1190
|
+
import path9 from "path";
|
|
1140
1191
|
import os6 from "os";
|
|
1141
1192
|
import { fileURLToPath } from "url";
|
|
1142
1193
|
import { unlinkSync as unlinkSync3 } from "fs";
|
|
1143
1194
|
function spawnLockPath(sessionName) {
|
|
1144
|
-
return
|
|
1195
|
+
return path9.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
1145
1196
|
}
|
|
1146
1197
|
function isProcessAlive(pid) {
|
|
1147
1198
|
try {
|
|
@@ -1152,13 +1203,13 @@ function isProcessAlive(pid) {
|
|
|
1152
1203
|
}
|
|
1153
1204
|
}
|
|
1154
1205
|
function acquireSpawnLock(sessionName) {
|
|
1155
|
-
if (!
|
|
1156
|
-
|
|
1206
|
+
if (!existsSync9(SPAWN_LOCK_DIR)) {
|
|
1207
|
+
mkdirSync5(SPAWN_LOCK_DIR, { recursive: true });
|
|
1157
1208
|
}
|
|
1158
1209
|
const lockFile = spawnLockPath(sessionName);
|
|
1159
|
-
if (
|
|
1210
|
+
if (existsSync9(lockFile)) {
|
|
1160
1211
|
try {
|
|
1161
|
-
const lock = JSON.parse(
|
|
1212
|
+
const lock = JSON.parse(readFileSync9(lockFile, "utf8"));
|
|
1162
1213
|
const age = Date.now() - lock.timestamp;
|
|
1163
1214
|
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
1164
1215
|
return false;
|
|
@@ -1166,7 +1217,7 @@ function acquireSpawnLock(sessionName) {
|
|
|
1166
1217
|
} catch {
|
|
1167
1218
|
}
|
|
1168
1219
|
}
|
|
1169
|
-
|
|
1220
|
+
writeFileSync6(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
1170
1221
|
return true;
|
|
1171
1222
|
}
|
|
1172
1223
|
function releaseSpawnLock(sessionName) {
|
|
@@ -1178,13 +1229,13 @@ function releaseSpawnLock(sessionName) {
|
|
|
1178
1229
|
function resolveBehaviorsExporterScript() {
|
|
1179
1230
|
try {
|
|
1180
1231
|
const thisFile = fileURLToPath(import.meta.url);
|
|
1181
|
-
const scriptPath =
|
|
1182
|
-
|
|
1232
|
+
const scriptPath = path9.join(
|
|
1233
|
+
path9.dirname(thisFile),
|
|
1183
1234
|
"..",
|
|
1184
1235
|
"bin",
|
|
1185
1236
|
"exe-export-behaviors.js"
|
|
1186
1237
|
);
|
|
1187
|
-
return
|
|
1238
|
+
return existsSync9(scriptPath) ? scriptPath : null;
|
|
1188
1239
|
} catch {
|
|
1189
1240
|
return null;
|
|
1190
1241
|
}
|
|
@@ -1250,12 +1301,12 @@ function extractRootExe(name) {
|
|
|
1250
1301
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
1251
1302
|
}
|
|
1252
1303
|
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
1253
|
-
if (!
|
|
1254
|
-
|
|
1304
|
+
if (!existsSync9(SESSION_CACHE)) {
|
|
1305
|
+
mkdirSync5(SESSION_CACHE, { recursive: true });
|
|
1255
1306
|
}
|
|
1256
1307
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
1257
|
-
const filePath =
|
|
1258
|
-
|
|
1308
|
+
const filePath = path9.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
1309
|
+
writeFileSync6(filePath, JSON.stringify({
|
|
1259
1310
|
parentExe: rootExe,
|
|
1260
1311
|
dispatchedBy: dispatchedBy || rootExe,
|
|
1261
1312
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -1263,7 +1314,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
1263
1314
|
}
|
|
1264
1315
|
function getParentExe(sessionKey) {
|
|
1265
1316
|
try {
|
|
1266
|
-
const data = JSON.parse(
|
|
1317
|
+
const data = JSON.parse(readFileSync9(path9.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
1267
1318
|
return data.parentExe || null;
|
|
1268
1319
|
} catch {
|
|
1269
1320
|
return null;
|
|
@@ -1271,8 +1322,8 @@ function getParentExe(sessionKey) {
|
|
|
1271
1322
|
}
|
|
1272
1323
|
function getDispatchedBy(sessionKey) {
|
|
1273
1324
|
try {
|
|
1274
|
-
const data = JSON.parse(
|
|
1275
|
-
|
|
1325
|
+
const data = JSON.parse(readFileSync9(
|
|
1326
|
+
path9.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
1276
1327
|
"utf8"
|
|
1277
1328
|
));
|
|
1278
1329
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -1333,32 +1384,50 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
1333
1384
|
}
|
|
1334
1385
|
function readDebounceState() {
|
|
1335
1386
|
try {
|
|
1336
|
-
if (!
|
|
1337
|
-
|
|
1387
|
+
if (!existsSync9(DEBOUNCE_FILE)) return {};
|
|
1388
|
+
const raw = JSON.parse(readFileSync9(DEBOUNCE_FILE, "utf8"));
|
|
1389
|
+
const state = {};
|
|
1390
|
+
for (const [key, val] of Object.entries(raw)) {
|
|
1391
|
+
if (typeof val === "number") {
|
|
1392
|
+
state[key] = { lastSent: val, pending: 0 };
|
|
1393
|
+
} else if (val && typeof val === "object" && "lastSent" in val) {
|
|
1394
|
+
state[key] = val;
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
return state;
|
|
1338
1398
|
} catch {
|
|
1339
1399
|
return {};
|
|
1340
1400
|
}
|
|
1341
1401
|
}
|
|
1342
1402
|
function writeDebounceState(state) {
|
|
1343
1403
|
try {
|
|
1344
|
-
if (!
|
|
1345
|
-
|
|
1404
|
+
if (!existsSync9(SESSION_CACHE)) mkdirSync5(SESSION_CACHE, { recursive: true });
|
|
1405
|
+
writeFileSync6(DEBOUNCE_FILE, JSON.stringify(state));
|
|
1346
1406
|
} catch {
|
|
1347
1407
|
}
|
|
1348
1408
|
}
|
|
1349
1409
|
function isDebounced(targetSession) {
|
|
1350
1410
|
const state = readDebounceState();
|
|
1351
|
-
const
|
|
1352
|
-
|
|
1411
|
+
const entry = state[targetSession];
|
|
1412
|
+
const lastSent = entry?.lastSent ?? 0;
|
|
1413
|
+
if (Date.now() - lastSent < INTERCOM_DEBOUNCE_MS) {
|
|
1414
|
+
if (!state[targetSession]) state[targetSession] = { lastSent, pending: 0 };
|
|
1415
|
+
state[targetSession].pending++;
|
|
1416
|
+
writeDebounceState(state);
|
|
1417
|
+
return true;
|
|
1418
|
+
}
|
|
1419
|
+
return false;
|
|
1353
1420
|
}
|
|
1354
1421
|
function recordDebounce(targetSession) {
|
|
1355
1422
|
const state = readDebounceState();
|
|
1356
|
-
state[targetSession]
|
|
1423
|
+
const batched = state[targetSession]?.pending ?? 0;
|
|
1424
|
+
state[targetSession] = { lastSent: Date.now(), pending: 0 };
|
|
1357
1425
|
const cutoff = Date.now() - DEBOUNCE_CLEANUP_AGE_MS;
|
|
1358
1426
|
for (const key of Object.keys(state)) {
|
|
1359
|
-
if ((state[key] ?? 0) < cutoff) delete state[key];
|
|
1427
|
+
if ((state[key]?.lastSent ?? 0) < cutoff) delete state[key];
|
|
1360
1428
|
}
|
|
1361
1429
|
writeDebounceState(state);
|
|
1430
|
+
return batched;
|
|
1362
1431
|
}
|
|
1363
1432
|
function logIntercom(msg) {
|
|
1364
1433
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}
|
|
@@ -1403,7 +1472,7 @@ function sendIntercom(targetSession) {
|
|
|
1403
1472
|
return "skipped_exe";
|
|
1404
1473
|
}
|
|
1405
1474
|
if (isDebounced(targetSession)) {
|
|
1406
|
-
logIntercom(`DEBOUNCE \u2192 ${targetSession} (
|
|
1475
|
+
logIntercom(`DEBOUNCE \u2192 ${targetSession} (nudge batched, task safe in DB)`);
|
|
1407
1476
|
return "debounced";
|
|
1408
1477
|
}
|
|
1409
1478
|
try {
|
|
@@ -1415,14 +1484,14 @@ function sendIntercom(targetSession) {
|
|
|
1415
1484
|
const sessionState = getSessionState(targetSession);
|
|
1416
1485
|
if (sessionState === "no_claude") {
|
|
1417
1486
|
queueIntercom(targetSession, "claude not running in session");
|
|
1418
|
-
recordDebounce(targetSession);
|
|
1419
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process
|
|
1487
|
+
const batched2 = recordDebounce(targetSession);
|
|
1488
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
1420
1489
|
return "queued";
|
|
1421
1490
|
}
|
|
1422
1491
|
if (sessionState === "thinking" || sessionState === "tool") {
|
|
1423
1492
|
queueIntercom(targetSession, "session busy at send time");
|
|
1424
|
-
recordDebounce(targetSession);
|
|
1425
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (session busy
|
|
1493
|
+
const batched2 = recordDebounce(targetSession);
|
|
1494
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (session busy)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
1426
1495
|
return "queued";
|
|
1427
1496
|
}
|
|
1428
1497
|
if (transport.isPaneInCopyMode(targetSession)) {
|
|
@@ -1430,8 +1499,8 @@ function sendIntercom(targetSession) {
|
|
|
1430
1499
|
transport.sendKeys(targetSession, "q");
|
|
1431
1500
|
}
|
|
1432
1501
|
transport.sendKeys(targetSession, "/exe-intercom");
|
|
1433
|
-
recordDebounce(targetSession);
|
|
1434
|
-
logIntercom(`DELIVERED \u2192 ${targetSession} (fire-and-forget)`);
|
|
1502
|
+
const batched = recordDebounce(targetSession);
|
|
1503
|
+
logIntercom(`DELIVERED \u2192 ${targetSession}${batched > 0 ? ` [${batched} nudges batched during debounce]` : ""} (fire-and-forget)`);
|
|
1435
1504
|
return "delivered";
|
|
1436
1505
|
} catch {
|
|
1437
1506
|
logIntercom(`FAIL \u2192 ${targetSession}`);
|
|
@@ -1533,26 +1602,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
1533
1602
|
const transport = getTransport();
|
|
1534
1603
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
1535
1604
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
1536
|
-
const logDir =
|
|
1537
|
-
const logFile =
|
|
1538
|
-
if (!
|
|
1539
|
-
|
|
1605
|
+
const logDir = path9.join(os6.homedir(), ".exe-os", "session-logs");
|
|
1606
|
+
const logFile = path9.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
1607
|
+
if (!existsSync9(logDir)) {
|
|
1608
|
+
mkdirSync5(logDir, { recursive: true });
|
|
1540
1609
|
}
|
|
1541
1610
|
transport.kill(sessionName);
|
|
1542
1611
|
let cleanupSuffix = "";
|
|
1543
1612
|
try {
|
|
1544
1613
|
const thisFile = fileURLToPath(import.meta.url);
|
|
1545
|
-
const cleanupScript =
|
|
1546
|
-
if (
|
|
1614
|
+
const cleanupScript = path9.join(path9.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
1615
|
+
if (existsSync9(cleanupScript)) {
|
|
1547
1616
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
1548
1617
|
}
|
|
1549
1618
|
} catch {
|
|
1550
1619
|
}
|
|
1551
1620
|
try {
|
|
1552
|
-
const claudeJsonPath =
|
|
1621
|
+
const claudeJsonPath = path9.join(os6.homedir(), ".claude.json");
|
|
1553
1622
|
let claudeJson = {};
|
|
1554
1623
|
try {
|
|
1555
|
-
claudeJson = JSON.parse(
|
|
1624
|
+
claudeJson = JSON.parse(readFileSync9(claudeJsonPath, "utf8"));
|
|
1556
1625
|
} catch {
|
|
1557
1626
|
}
|
|
1558
1627
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -1560,17 +1629,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
1560
1629
|
const trustDir = opts?.cwd ?? projectDir;
|
|
1561
1630
|
if (!projects[trustDir]) projects[trustDir] = {};
|
|
1562
1631
|
projects[trustDir].hasTrustDialogAccepted = true;
|
|
1563
|
-
|
|
1632
|
+
writeFileSync6(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
1564
1633
|
} catch {
|
|
1565
1634
|
}
|
|
1566
1635
|
try {
|
|
1567
|
-
const settingsDir =
|
|
1636
|
+
const settingsDir = path9.join(os6.homedir(), ".claude", "projects");
|
|
1568
1637
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
1569
|
-
const projSettingsDir =
|
|
1570
|
-
const settingsPath =
|
|
1638
|
+
const projSettingsDir = path9.join(settingsDir, normalizedKey);
|
|
1639
|
+
const settingsPath = path9.join(projSettingsDir, "settings.json");
|
|
1571
1640
|
let settings = {};
|
|
1572
1641
|
try {
|
|
1573
|
-
settings = JSON.parse(
|
|
1642
|
+
settings = JSON.parse(readFileSync9(settingsPath, "utf8"));
|
|
1574
1643
|
} catch {
|
|
1575
1644
|
}
|
|
1576
1645
|
const perms = settings.permissions ?? {};
|
|
@@ -1598,20 +1667,23 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
1598
1667
|
if (changed) {
|
|
1599
1668
|
perms.allow = allow;
|
|
1600
1669
|
settings.permissions = perms;
|
|
1601
|
-
|
|
1602
|
-
|
|
1670
|
+
mkdirSync5(projSettingsDir, { recursive: true });
|
|
1671
|
+
writeFileSync6(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
1603
1672
|
}
|
|
1604
1673
|
} catch {
|
|
1605
1674
|
}
|
|
1606
1675
|
const spawnCwd = opts?.cwd ?? projectDir;
|
|
1607
1676
|
const useExeAgent = !!(opts?.model && opts?.provider);
|
|
1608
|
-
const
|
|
1677
|
+
const agentRtConfig = getAgentRuntime(employeeName);
|
|
1678
|
+
const useCodex = !useExeAgent && agentRtConfig.runtime === "codex";
|
|
1679
|
+
const useOpencode = !useExeAgent && !useCodex && agentRtConfig.runtime === "opencode";
|
|
1680
|
+
const ccProvider = useExeAgent || useCodex || useOpencode ? DEFAULT_PROVIDER : detectActiveProvider();
|
|
1609
1681
|
const useBinSymlink = ccProvider !== DEFAULT_PROVIDER;
|
|
1610
1682
|
let identityFlag = "";
|
|
1611
1683
|
let behaviorsFlag = "";
|
|
1612
1684
|
let legacyFallbackWarned = false;
|
|
1613
1685
|
if (!useExeAgent && !useBinSymlink) {
|
|
1614
|
-
const identityPath =
|
|
1686
|
+
const identityPath = path9.join(
|
|
1615
1687
|
os6.homedir(),
|
|
1616
1688
|
".exe-os",
|
|
1617
1689
|
"identity",
|
|
@@ -1621,13 +1693,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
1621
1693
|
const hasAgentFlag = claudeSupportsAgentFlag();
|
|
1622
1694
|
if (hasAgentFlag) {
|
|
1623
1695
|
identityFlag = ` --agent ${employeeName}`;
|
|
1624
|
-
} else if (
|
|
1696
|
+
} else if (existsSync9(identityPath)) {
|
|
1625
1697
|
identityFlag = ` --append-system-prompt-file ${identityPath}`;
|
|
1626
1698
|
legacyFallbackWarned = true;
|
|
1627
1699
|
}
|
|
1628
1700
|
const behaviorsFile = exportBehaviorsSync(
|
|
1629
1701
|
employeeName,
|
|
1630
|
-
|
|
1702
|
+
path9.basename(spawnCwd),
|
|
1631
1703
|
sessionName
|
|
1632
1704
|
);
|
|
1633
1705
|
if (behaviorsFile) {
|
|
@@ -1642,16 +1714,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
1642
1714
|
}
|
|
1643
1715
|
let sessionContextFlag = "";
|
|
1644
1716
|
try {
|
|
1645
|
-
const ctxDir =
|
|
1646
|
-
|
|
1647
|
-
const ctxFile =
|
|
1717
|
+
const ctxDir = path9.join(os6.homedir(), ".exe-os", "session-cache");
|
|
1718
|
+
mkdirSync5(ctxDir, { recursive: true });
|
|
1719
|
+
const ctxFile = path9.join(ctxDir, `session-context-${sessionName}.md`);
|
|
1648
1720
|
const ctxContent = [
|
|
1649
1721
|
`## Session Context`,
|
|
1650
1722
|
`You are running in tmux session: ${sessionName}.`,
|
|
1651
1723
|
`Your parent coordinator session is ${exeSession}.`,
|
|
1652
1724
|
`Your employees (if any) use the -${exeSession} suffix.`
|
|
1653
1725
|
].join("\n");
|
|
1654
|
-
|
|
1726
|
+
writeFileSync6(ctxFile, ctxContent);
|
|
1655
1727
|
sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
|
|
1656
1728
|
} catch {
|
|
1657
1729
|
}
|
|
@@ -1665,9 +1737,48 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
1665
1737
|
}
|
|
1666
1738
|
}
|
|
1667
1739
|
}
|
|
1740
|
+
if (useCodex) {
|
|
1741
|
+
const codexCfg = RUNTIME_TABLE.codex;
|
|
1742
|
+
if (codexCfg?.apiKeyEnv) {
|
|
1743
|
+
const keyVal = process.env[codexCfg.apiKeyEnv];
|
|
1744
|
+
if (keyVal) {
|
|
1745
|
+
envPrefix = `${envPrefix} ${codexCfg.apiKeyEnv}=${keyVal}`;
|
|
1746
|
+
}
|
|
1747
|
+
}
|
|
1748
|
+
envPrefix = `${envPrefix} EXE_AGENT_MODEL=${agentRtConfig.model}`;
|
|
1749
|
+
}
|
|
1750
|
+
if (useOpencode) {
|
|
1751
|
+
const ocCfg = PROVIDER_TABLE.opencode;
|
|
1752
|
+
if (ocCfg?.apiKeyEnv) {
|
|
1753
|
+
const keyVal = process.env[ocCfg.apiKeyEnv];
|
|
1754
|
+
if (keyVal) {
|
|
1755
|
+
envPrefix = `${envPrefix} ${ocCfg.apiKeyEnv}=${keyVal}`;
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
|
|
1759
|
+
}
|
|
1760
|
+
if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
|
|
1761
|
+
const defaultClaudeModel = DEFAULT_MODELS.claude;
|
|
1762
|
+
if (agentRtConfig.runtime === "claude" && agentRtConfig.model !== defaultClaudeModel) {
|
|
1763
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1668
1766
|
let spawnCommand;
|
|
1669
1767
|
if (useExeAgent) {
|
|
1670
1768
|
spawnCommand = `${envPrefix} exe-agent --employee ${employeeName} --model ${opts.model} --provider ${opts.provider}${cleanupSuffix}`;
|
|
1769
|
+
} else if (useCodex) {
|
|
1770
|
+
process.stderr.write(
|
|
1771
|
+
`[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
|
|
1772
|
+
`
|
|
1773
|
+
);
|
|
1774
|
+
spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
|
|
1775
|
+
} else if (useOpencode) {
|
|
1776
|
+
const binName = `${employeeName}-opencode`;
|
|
1777
|
+
process.stderr.write(
|
|
1778
|
+
`[tmux-routing] agent-config: ${employeeName} \u2192 opencode (${agentRtConfig.model})
|
|
1779
|
+
`
|
|
1780
|
+
);
|
|
1781
|
+
spawnCommand = `${envPrefix} ${binName}${cleanupSuffix}`;
|
|
1671
1782
|
} else if (useBinSymlink) {
|
|
1672
1783
|
const binName = `${employeeName}-${ccProvider}`;
|
|
1673
1784
|
process.stderr.write(
|
|
@@ -1689,11 +1800,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
1689
1800
|
transport.pipeLog(sessionName, logFile);
|
|
1690
1801
|
try {
|
|
1691
1802
|
const mySession = getMySession();
|
|
1692
|
-
const dispatchInfo =
|
|
1693
|
-
|
|
1803
|
+
const dispatchInfo = path9.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
1804
|
+
writeFileSync6(dispatchInfo, JSON.stringify({
|
|
1694
1805
|
dispatchedBy: mySession,
|
|
1695
1806
|
rootExe: exeSession,
|
|
1696
|
-
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : "anthropic",
|
|
1807
|
+
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
|
|
1808
|
+
runtime: useCodex ? "codex" : useOpencode ? "opencode" : useExeAgent ? "exe-agent" : "claude",
|
|
1809
|
+
model: useCodex ? agentRtConfig.model : useOpencode ? agentRtConfig.model : void 0,
|
|
1697
1810
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1698
1811
|
}));
|
|
1699
1812
|
} catch {
|
|
@@ -1711,6 +1824,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
1711
1824
|
booted = true;
|
|
1712
1825
|
break;
|
|
1713
1826
|
}
|
|
1827
|
+
} else if (useCodex) {
|
|
1828
|
+
if (pane.includes("codex") || pane.includes("Codex") || pane.includes("exe-start-codex")) {
|
|
1829
|
+
booted = true;
|
|
1830
|
+
break;
|
|
1831
|
+
}
|
|
1714
1832
|
} else {
|
|
1715
1833
|
if (pane.includes("Claude Code") || pane.includes("\u276F")) {
|
|
1716
1834
|
booted = true;
|
|
@@ -1722,9 +1840,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
1722
1840
|
}
|
|
1723
1841
|
if (!booted) {
|
|
1724
1842
|
releaseSpawnLock(sessionName);
|
|
1725
|
-
|
|
1843
|
+
const runtimeLabel = useExeAgent ? "exe-agent" : useCodex ? "codex" : "claude";
|
|
1844
|
+
return { sessionName, error: `${runtimeLabel} did not boot within 15s` };
|
|
1726
1845
|
}
|
|
1727
|
-
if (!useExeAgent) {
|
|
1846
|
+
if (!useExeAgent && !useCodex) {
|
|
1728
1847
|
try {
|
|
1729
1848
|
transport.sendKeys(sessionName, `/exe-call ${employeeName}`);
|
|
1730
1849
|
} catch {
|
|
@@ -1751,17 +1870,19 @@ var init_tmux_routing = __esm({
|
|
|
1751
1870
|
init_cc_agent_support();
|
|
1752
1871
|
init_mcp_prefix();
|
|
1753
1872
|
init_provider_table();
|
|
1873
|
+
init_agent_config();
|
|
1874
|
+
init_runtime_table();
|
|
1754
1875
|
init_intercom_queue();
|
|
1755
1876
|
init_plan_limits();
|
|
1756
1877
|
init_employees();
|
|
1757
|
-
SPAWN_LOCK_DIR =
|
|
1758
|
-
SESSION_CACHE =
|
|
1878
|
+
SPAWN_LOCK_DIR = path9.join(os6.homedir(), ".exe-os", "spawn-locks");
|
|
1879
|
+
SESSION_CACHE = path9.join(os6.homedir(), ".exe-os", "session-cache");
|
|
1759
1880
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
1760
1881
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
1761
1882
|
VERIFY_PANE_LINES = 200;
|
|
1762
1883
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
1763
|
-
INTERCOM_LOG2 =
|
|
1764
|
-
DEBOUNCE_FILE =
|
|
1884
|
+
INTERCOM_LOG2 = path9.join(os6.homedir(), ".exe-os", "intercom.log");
|
|
1885
|
+
DEBOUNCE_FILE = path9.join(SESSION_CACHE, "intercom-debounce.json");
|
|
1765
1886
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
1766
1887
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
|
|
1767
1888
|
}
|
|
@@ -1793,11 +1914,11 @@ var init_task_scope = __esm({
|
|
|
1793
1914
|
|
|
1794
1915
|
// src/lib/tasks-crud.ts
|
|
1795
1916
|
import crypto3 from "crypto";
|
|
1796
|
-
import
|
|
1917
|
+
import path10 from "path";
|
|
1797
1918
|
import os7 from "os";
|
|
1798
1919
|
import { execSync as execSync5 } from "child_process";
|
|
1799
1920
|
import { mkdir as mkdir3, writeFile as writeFile3, appendFile } from "fs/promises";
|
|
1800
|
-
import { existsSync as
|
|
1921
|
+
import { existsSync as existsSync10, readFileSync as readFileSync10 } from "fs";
|
|
1801
1922
|
async function writeCheckpoint(input) {
|
|
1802
1923
|
const client = getClient();
|
|
1803
1924
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -1972,8 +2093,8 @@ ${laneWarning}` : laneWarning;
|
|
|
1972
2093
|
}
|
|
1973
2094
|
if (input.baseDir) {
|
|
1974
2095
|
try {
|
|
1975
|
-
await mkdir3(
|
|
1976
|
-
await mkdir3(
|
|
2096
|
+
await mkdir3(path10.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
2097
|
+
await mkdir3(path10.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
1977
2098
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
1978
2099
|
await ensureGitignoreExe(input.baseDir);
|
|
1979
2100
|
} catch {
|
|
@@ -2009,10 +2130,10 @@ ${laneWarning}` : laneWarning;
|
|
|
2009
2130
|
});
|
|
2010
2131
|
if (input.baseDir) {
|
|
2011
2132
|
try {
|
|
2012
|
-
const EXE_OS_DIR =
|
|
2013
|
-
const mdPath =
|
|
2014
|
-
const mdDir =
|
|
2015
|
-
if (!
|
|
2133
|
+
const EXE_OS_DIR = path10.join(os7.homedir(), ".exe-os");
|
|
2134
|
+
const mdPath = path10.join(EXE_OS_DIR, taskFile);
|
|
2135
|
+
const mdDir = path10.dirname(mdPath);
|
|
2136
|
+
if (!existsSync10(mdDir)) await mkdir3(mdDir, { recursive: true });
|
|
2016
2137
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
2017
2138
|
const mdContent = `# ${input.title}
|
|
2018
2139
|
|
|
@@ -2037,7 +2158,11 @@ If you skip this, your reviewer will not know you're done and your work won't be
|
|
|
2037
2158
|
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
2038
2159
|
`;
|
|
2039
2160
|
await writeFile3(mdPath, mdContent, "utf-8");
|
|
2040
|
-
} catch {
|
|
2161
|
+
} catch (err) {
|
|
2162
|
+
process.stderr.write(
|
|
2163
|
+
`[create-task] WARNING: .md file write failed for ${taskFile}: ${err instanceof Error ? err.message : String(err)}
|
|
2164
|
+
`
|
|
2165
|
+
);
|
|
2041
2166
|
}
|
|
2042
2167
|
}
|
|
2043
2168
|
return {
|
|
@@ -2297,9 +2422,9 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
2297
2422
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
2298
2423
|
}
|
|
2299
2424
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
2300
|
-
const archPath =
|
|
2425
|
+
const archPath = path10.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
2301
2426
|
try {
|
|
2302
|
-
if (
|
|
2427
|
+
if (existsSync10(archPath)) return;
|
|
2303
2428
|
const template = [
|
|
2304
2429
|
`# ${projectName} \u2014 System Architecture`,
|
|
2305
2430
|
"",
|
|
@@ -2332,10 +2457,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
2332
2457
|
}
|
|
2333
2458
|
}
|
|
2334
2459
|
async function ensureGitignoreExe(baseDir) {
|
|
2335
|
-
const gitignorePath =
|
|
2460
|
+
const gitignorePath = path10.join(baseDir, ".gitignore");
|
|
2336
2461
|
try {
|
|
2337
|
-
if (
|
|
2338
|
-
const content =
|
|
2462
|
+
if (existsSync10(gitignorePath)) {
|
|
2463
|
+
const content = readFileSync10(gitignorePath, "utf-8");
|
|
2339
2464
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
2340
2465
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
2341
2466
|
} else {
|
|
@@ -2366,8 +2491,8 @@ var init_tasks_crud = __esm({
|
|
|
2366
2491
|
});
|
|
2367
2492
|
|
|
2368
2493
|
// src/lib/tasks-review.ts
|
|
2369
|
-
import
|
|
2370
|
-
import { existsSync as
|
|
2494
|
+
import path11 from "path";
|
|
2495
|
+
import { existsSync as existsSync11, readdirSync as readdirSync2, unlinkSync as unlinkSync4 } from "fs";
|
|
2371
2496
|
async function countPendingReviews(sessionScope) {
|
|
2372
2497
|
const client = getClient();
|
|
2373
2498
|
if (sessionScope) {
|
|
@@ -2548,11 +2673,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
2548
2673
|
);
|
|
2549
2674
|
}
|
|
2550
2675
|
try {
|
|
2551
|
-
const cacheDir =
|
|
2552
|
-
if (
|
|
2676
|
+
const cacheDir = path11.join(EXE_AI_DIR, "session-cache");
|
|
2677
|
+
if (existsSync11(cacheDir)) {
|
|
2553
2678
|
for (const f of readdirSync2(cacheDir)) {
|
|
2554
2679
|
if (f.startsWith("review-notified-")) {
|
|
2555
|
-
unlinkSync4(
|
|
2680
|
+
unlinkSync4(path11.join(cacheDir, f));
|
|
2556
2681
|
}
|
|
2557
2682
|
}
|
|
2558
2683
|
}
|
|
@@ -2573,7 +2698,7 @@ var init_tasks_review = __esm({
|
|
|
2573
2698
|
});
|
|
2574
2699
|
|
|
2575
2700
|
// src/lib/tasks-chain.ts
|
|
2576
|
-
import
|
|
2701
|
+
import path12 from "path";
|
|
2577
2702
|
import { readFile as readFile3, writeFile as writeFile4 } from "fs/promises";
|
|
2578
2703
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
2579
2704
|
const client = getClient();
|
|
@@ -2590,7 +2715,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
2590
2715
|
});
|
|
2591
2716
|
for (const ur of unblockedRows.rows) {
|
|
2592
2717
|
try {
|
|
2593
|
-
const ubFile =
|
|
2718
|
+
const ubFile = path12.join(baseDir, String(ur.task_file));
|
|
2594
2719
|
let ubContent = await readFile3(ubFile, "utf-8");
|
|
2595
2720
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
2596
2721
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -2659,7 +2784,7 @@ var init_tasks_chain = __esm({
|
|
|
2659
2784
|
|
|
2660
2785
|
// src/lib/project-name.ts
|
|
2661
2786
|
import { execSync as execSync6 } from "child_process";
|
|
2662
|
-
import
|
|
2787
|
+
import path13 from "path";
|
|
2663
2788
|
function getProjectName(cwd) {
|
|
2664
2789
|
const dir = cwd ?? process.cwd();
|
|
2665
2790
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -2672,7 +2797,7 @@ function getProjectName(cwd) {
|
|
|
2672
2797
|
timeout: 2e3,
|
|
2673
2798
|
stdio: ["pipe", "pipe", "pipe"]
|
|
2674
2799
|
}).trim();
|
|
2675
|
-
repoRoot =
|
|
2800
|
+
repoRoot = path13.dirname(gitCommonDir);
|
|
2676
2801
|
} catch {
|
|
2677
2802
|
repoRoot = execSync6("git rev-parse --show-toplevel", {
|
|
2678
2803
|
cwd: dir,
|
|
@@ -2681,11 +2806,11 @@ function getProjectName(cwd) {
|
|
|
2681
2806
|
stdio: ["pipe", "pipe", "pipe"]
|
|
2682
2807
|
}).trim();
|
|
2683
2808
|
}
|
|
2684
|
-
_cached2 =
|
|
2809
|
+
_cached2 = path13.basename(repoRoot);
|
|
2685
2810
|
_cachedCwd = dir;
|
|
2686
2811
|
return _cached2;
|
|
2687
2812
|
} catch {
|
|
2688
|
-
_cached2 =
|
|
2813
|
+
_cached2 = path13.basename(dir);
|
|
2689
2814
|
_cachedCwd = dir;
|
|
2690
2815
|
return _cached2;
|
|
2691
2816
|
}
|
|
@@ -3158,8 +3283,8 @@ __export(tasks_exports, {
|
|
|
3158
3283
|
updateTaskStatus: () => updateTaskStatus,
|
|
3159
3284
|
writeCheckpoint: () => writeCheckpoint
|
|
3160
3285
|
});
|
|
3161
|
-
import
|
|
3162
|
-
import { writeFileSync as
|
|
3286
|
+
import path14 from "path";
|
|
3287
|
+
import { writeFileSync as writeFileSync7, mkdirSync as mkdirSync6, unlinkSync as unlinkSync5 } from "fs";
|
|
3163
3288
|
async function createTask(input) {
|
|
3164
3289
|
const result = await createTaskCore(input);
|
|
3165
3290
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -3178,11 +3303,11 @@ async function updateTask(input) {
|
|
|
3178
3303
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
3179
3304
|
try {
|
|
3180
3305
|
const agent = String(row.assigned_to);
|
|
3181
|
-
const cacheDir =
|
|
3182
|
-
const cachePath =
|
|
3306
|
+
const cacheDir = path14.join(EXE_AI_DIR, "session-cache");
|
|
3307
|
+
const cachePath = path14.join(cacheDir, `current-task-${agent}.json`);
|
|
3183
3308
|
if (input.status === "in_progress") {
|
|
3184
|
-
|
|
3185
|
-
|
|
3309
|
+
mkdirSync6(cacheDir, { recursive: true });
|
|
3310
|
+
writeFileSync7(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
3186
3311
|
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
|
|
3187
3312
|
try {
|
|
3188
3313
|
unlinkSync5(cachePath);
|