@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
package/dist/bin/exe-boot.js
CHANGED
|
@@ -2853,18 +2853,69 @@ var init_provider_table = __esm({
|
|
|
2853
2853
|
}
|
|
2854
2854
|
});
|
|
2855
2855
|
|
|
2856
|
-
// src/lib/
|
|
2857
|
-
|
|
2856
|
+
// src/lib/runtime-table.ts
|
|
2857
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
2858
|
+
var init_runtime_table = __esm({
|
|
2859
|
+
"src/lib/runtime-table.ts"() {
|
|
2860
|
+
"use strict";
|
|
2861
|
+
RUNTIME_TABLE = {
|
|
2862
|
+
codex: {
|
|
2863
|
+
binary: "codex",
|
|
2864
|
+
launchMode: "exec",
|
|
2865
|
+
autoApproveFlag: "--full-auto",
|
|
2866
|
+
inlineFlag: "--no-alt-screen",
|
|
2867
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
2868
|
+
defaultModel: "gpt-5.4"
|
|
2869
|
+
}
|
|
2870
|
+
};
|
|
2871
|
+
DEFAULT_RUNTIME = "claude";
|
|
2872
|
+
}
|
|
2873
|
+
});
|
|
2874
|
+
|
|
2875
|
+
// src/lib/agent-config.ts
|
|
2876
|
+
import { readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync7, mkdirSync as mkdirSync3 } from "fs";
|
|
2858
2877
|
import path7 from "path";
|
|
2878
|
+
function loadAgentConfig() {
|
|
2879
|
+
if (!existsSync7(AGENT_CONFIG_PATH)) return {};
|
|
2880
|
+
try {
|
|
2881
|
+
return JSON.parse(readFileSync5(AGENT_CONFIG_PATH, "utf-8"));
|
|
2882
|
+
} catch {
|
|
2883
|
+
return {};
|
|
2884
|
+
}
|
|
2885
|
+
}
|
|
2886
|
+
function getAgentRuntime(agentId) {
|
|
2887
|
+
const config = loadAgentConfig();
|
|
2888
|
+
const entry = config[agentId];
|
|
2889
|
+
if (entry) return entry;
|
|
2890
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
2891
|
+
}
|
|
2892
|
+
var AGENT_CONFIG_PATH, DEFAULT_MODELS;
|
|
2893
|
+
var init_agent_config = __esm({
|
|
2894
|
+
"src/lib/agent-config.ts"() {
|
|
2895
|
+
"use strict";
|
|
2896
|
+
init_config();
|
|
2897
|
+
init_runtime_table();
|
|
2898
|
+
AGENT_CONFIG_PATH = path7.join(EXE_AI_DIR, "agent-config.json");
|
|
2899
|
+
DEFAULT_MODELS = {
|
|
2900
|
+
claude: "claude-opus-4",
|
|
2901
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
2902
|
+
opencode: "minimax-m2.7"
|
|
2903
|
+
};
|
|
2904
|
+
}
|
|
2905
|
+
});
|
|
2906
|
+
|
|
2907
|
+
// src/lib/intercom-queue.ts
|
|
2908
|
+
import { readFileSync as readFileSync6, writeFileSync as writeFileSync4, renameSync as renameSync3, existsSync as existsSync8, mkdirSync as mkdirSync4 } from "fs";
|
|
2909
|
+
import path8 from "path";
|
|
2859
2910
|
import os5 from "os";
|
|
2860
2911
|
function ensureDir() {
|
|
2861
|
-
const dir =
|
|
2862
|
-
if (!
|
|
2912
|
+
const dir = path8.dirname(QUEUE_PATH);
|
|
2913
|
+
if (!existsSync8(dir)) mkdirSync4(dir, { recursive: true });
|
|
2863
2914
|
}
|
|
2864
2915
|
function readQueue() {
|
|
2865
2916
|
try {
|
|
2866
|
-
if (!
|
|
2867
|
-
return JSON.parse(
|
|
2917
|
+
if (!existsSync8(QUEUE_PATH)) return [];
|
|
2918
|
+
return JSON.parse(readFileSync6(QUEUE_PATH, "utf8"));
|
|
2868
2919
|
} catch {
|
|
2869
2920
|
return [];
|
|
2870
2921
|
}
|
|
@@ -2872,7 +2923,7 @@ function readQueue() {
|
|
|
2872
2923
|
function writeQueue(queue) {
|
|
2873
2924
|
ensureDir();
|
|
2874
2925
|
const tmp = `${QUEUE_PATH}.tmp`;
|
|
2875
|
-
|
|
2926
|
+
writeFileSync4(tmp, JSON.stringify(queue, null, 2));
|
|
2876
2927
|
renameSync3(tmp, QUEUE_PATH);
|
|
2877
2928
|
}
|
|
2878
2929
|
function queueIntercom(targetSession, reason) {
|
|
@@ -2896,9 +2947,9 @@ var QUEUE_PATH, TTL_MS, INTERCOM_LOG;
|
|
|
2896
2947
|
var init_intercom_queue = __esm({
|
|
2897
2948
|
"src/lib/intercom-queue.ts"() {
|
|
2898
2949
|
"use strict";
|
|
2899
|
-
QUEUE_PATH =
|
|
2950
|
+
QUEUE_PATH = path8.join(os5.homedir(), ".exe-os", "intercom-queue.json");
|
|
2900
2951
|
TTL_MS = 60 * 60 * 1e3;
|
|
2901
|
-
INTERCOM_LOG =
|
|
2952
|
+
INTERCOM_LOG = path8.join(os5.homedir(), ".exe-os", "intercom.log");
|
|
2902
2953
|
}
|
|
2903
2954
|
});
|
|
2904
2955
|
|
|
@@ -2919,9 +2970,9 @@ __export(license_exports, {
|
|
|
2919
2970
|
stopLicenseRevalidation: () => stopLicenseRevalidation,
|
|
2920
2971
|
validateLicense: () => validateLicense
|
|
2921
2972
|
});
|
|
2922
|
-
import { readFileSync as
|
|
2973
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync9, mkdirSync as mkdirSync5 } from "fs";
|
|
2923
2974
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
2924
|
-
import
|
|
2975
|
+
import path9 from "path";
|
|
2925
2976
|
import { jwtVerify, importSPKI } from "jose";
|
|
2926
2977
|
async function fetchRetry(url, init) {
|
|
2927
2978
|
try {
|
|
@@ -2932,37 +2983,37 @@ async function fetchRetry(url, init) {
|
|
|
2932
2983
|
}
|
|
2933
2984
|
}
|
|
2934
2985
|
function loadDeviceId() {
|
|
2935
|
-
const deviceJsonPath =
|
|
2986
|
+
const deviceJsonPath = path9.join(EXE_AI_DIR, "device.json");
|
|
2936
2987
|
try {
|
|
2937
|
-
if (
|
|
2938
|
-
const data = JSON.parse(
|
|
2988
|
+
if (existsSync9(deviceJsonPath)) {
|
|
2989
|
+
const data = JSON.parse(readFileSync7(deviceJsonPath, "utf8"));
|
|
2939
2990
|
if (data.deviceId) return data.deviceId;
|
|
2940
2991
|
}
|
|
2941
2992
|
} catch {
|
|
2942
2993
|
}
|
|
2943
2994
|
try {
|
|
2944
|
-
if (
|
|
2945
|
-
const id2 =
|
|
2995
|
+
if (existsSync9(DEVICE_ID_PATH)) {
|
|
2996
|
+
const id2 = readFileSync7(DEVICE_ID_PATH, "utf8").trim();
|
|
2946
2997
|
if (id2) return id2;
|
|
2947
2998
|
}
|
|
2948
2999
|
} catch {
|
|
2949
3000
|
}
|
|
2950
3001
|
const id = randomUUID3();
|
|
2951
|
-
|
|
2952
|
-
|
|
3002
|
+
mkdirSync5(EXE_AI_DIR, { recursive: true });
|
|
3003
|
+
writeFileSync5(DEVICE_ID_PATH, id, "utf8");
|
|
2953
3004
|
return id;
|
|
2954
3005
|
}
|
|
2955
3006
|
function loadLicense() {
|
|
2956
3007
|
try {
|
|
2957
|
-
if (!
|
|
2958
|
-
return
|
|
3008
|
+
if (!existsSync9(LICENSE_PATH)) return null;
|
|
3009
|
+
return readFileSync7(LICENSE_PATH, "utf8").trim();
|
|
2959
3010
|
} catch {
|
|
2960
3011
|
return null;
|
|
2961
3012
|
}
|
|
2962
3013
|
}
|
|
2963
3014
|
function saveLicense(apiKey) {
|
|
2964
|
-
|
|
2965
|
-
|
|
3015
|
+
mkdirSync5(EXE_AI_DIR, { recursive: true });
|
|
3016
|
+
writeFileSync5(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
|
|
2966
3017
|
}
|
|
2967
3018
|
async function verifyLicenseJwt(token) {
|
|
2968
3019
|
try {
|
|
@@ -2988,8 +3039,8 @@ async function verifyLicenseJwt(token) {
|
|
|
2988
3039
|
}
|
|
2989
3040
|
async function getCachedLicense() {
|
|
2990
3041
|
try {
|
|
2991
|
-
if (!
|
|
2992
|
-
const raw = JSON.parse(
|
|
3042
|
+
if (!existsSync9(CACHE_PATH)) return null;
|
|
3043
|
+
const raw = JSON.parse(readFileSync7(CACHE_PATH, "utf8"));
|
|
2993
3044
|
if (!raw.token || typeof raw.token !== "string") return null;
|
|
2994
3045
|
return await verifyLicenseJwt(raw.token);
|
|
2995
3046
|
} catch {
|
|
@@ -2998,8 +3049,8 @@ async function getCachedLicense() {
|
|
|
2998
3049
|
}
|
|
2999
3050
|
function readCachedToken() {
|
|
3000
3051
|
try {
|
|
3001
|
-
if (!
|
|
3002
|
-
const raw = JSON.parse(
|
|
3052
|
+
if (!existsSync9(CACHE_PATH)) return null;
|
|
3053
|
+
const raw = JSON.parse(readFileSync7(CACHE_PATH, "utf8"));
|
|
3003
3054
|
return typeof raw.token === "string" ? raw.token : null;
|
|
3004
3055
|
} catch {
|
|
3005
3056
|
return null;
|
|
@@ -3033,7 +3084,7 @@ function getRawCachedPlan() {
|
|
|
3033
3084
|
}
|
|
3034
3085
|
function cacheResponse(token) {
|
|
3035
3086
|
try {
|
|
3036
|
-
|
|
3087
|
+
writeFileSync5(CACHE_PATH, JSON.stringify({ token }), "utf8");
|
|
3037
3088
|
} catch {
|
|
3038
3089
|
}
|
|
3039
3090
|
}
|
|
@@ -3086,8 +3137,8 @@ async function validateLicense(apiKey, deviceId) {
|
|
|
3086
3137
|
}
|
|
3087
3138
|
function getCacheAgeMs() {
|
|
3088
3139
|
try {
|
|
3089
|
-
const { statSync:
|
|
3090
|
-
const s =
|
|
3140
|
+
const { statSync: statSync2 } = __require("fs");
|
|
3141
|
+
const s = statSync2(CACHE_PATH);
|
|
3091
3142
|
return Date.now() - s.mtimeMs;
|
|
3092
3143
|
} catch {
|
|
3093
3144
|
return Infinity;
|
|
@@ -3097,9 +3148,9 @@ async function checkLicense() {
|
|
|
3097
3148
|
let key = loadLicense();
|
|
3098
3149
|
if (!key) {
|
|
3099
3150
|
try {
|
|
3100
|
-
const configPath =
|
|
3101
|
-
if (
|
|
3102
|
-
const raw = JSON.parse(
|
|
3151
|
+
const configPath = path9.join(EXE_AI_DIR, "config.json");
|
|
3152
|
+
if (existsSync9(configPath)) {
|
|
3153
|
+
const raw = JSON.parse(readFileSync7(configPath, "utf8"));
|
|
3103
3154
|
const cloud = raw.cloud;
|
|
3104
3155
|
if (cloud?.apiKey) {
|
|
3105
3156
|
key = cloud.apiKey;
|
|
@@ -3258,9 +3309,9 @@ var init_license = __esm({
|
|
|
3258
3309
|
"src/lib/license.ts"() {
|
|
3259
3310
|
"use strict";
|
|
3260
3311
|
init_config();
|
|
3261
|
-
LICENSE_PATH =
|
|
3262
|
-
CACHE_PATH =
|
|
3263
|
-
DEVICE_ID_PATH =
|
|
3312
|
+
LICENSE_PATH = path9.join(EXE_AI_DIR, "license.key");
|
|
3313
|
+
CACHE_PATH = path9.join(EXE_AI_DIR, "license-cache.json");
|
|
3314
|
+
DEVICE_ID_PATH = path9.join(EXE_AI_DIR, "device-id");
|
|
3264
3315
|
API_BASE = "https://askexe.com/cloud";
|
|
3265
3316
|
RETRY_DELAY_MS = 500;
|
|
3266
3317
|
LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
|
|
@@ -3290,12 +3341,12 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
|
|
|
3290
3341
|
});
|
|
3291
3342
|
|
|
3292
3343
|
// src/lib/plan-limits.ts
|
|
3293
|
-
import { readFileSync as
|
|
3294
|
-
import
|
|
3344
|
+
import { readFileSync as readFileSync8, existsSync as existsSync10 } from "fs";
|
|
3345
|
+
import path10 from "path";
|
|
3295
3346
|
function getLicenseSync() {
|
|
3296
3347
|
try {
|
|
3297
|
-
if (!
|
|
3298
|
-
const raw = JSON.parse(
|
|
3348
|
+
if (!existsSync10(CACHE_PATH2)) return freeLicense();
|
|
3349
|
+
const raw = JSON.parse(readFileSync8(CACHE_PATH2, "utf8"));
|
|
3299
3350
|
if (!raw.token || typeof raw.token !== "string") return freeLicense();
|
|
3300
3351
|
const parts = raw.token.split(".");
|
|
3301
3352
|
if (parts.length !== 3) return freeLicense();
|
|
@@ -3333,8 +3384,8 @@ function assertEmployeeLimitSync(rosterPath) {
|
|
|
3333
3384
|
const filePath = rosterPath ?? EMPLOYEES_PATH;
|
|
3334
3385
|
let count = 0;
|
|
3335
3386
|
try {
|
|
3336
|
-
if (
|
|
3337
|
-
const raw =
|
|
3387
|
+
if (existsSync10(filePath)) {
|
|
3388
|
+
const raw = readFileSync8(filePath, "utf8");
|
|
3338
3389
|
const employees = JSON.parse(raw);
|
|
3339
3390
|
count = Array.isArray(employees) ? employees.length : 0;
|
|
3340
3391
|
}
|
|
@@ -3363,19 +3414,19 @@ var init_plan_limits = __esm({
|
|
|
3363
3414
|
this.name = "PlanLimitError";
|
|
3364
3415
|
}
|
|
3365
3416
|
};
|
|
3366
|
-
CACHE_PATH2 =
|
|
3417
|
+
CACHE_PATH2 = path10.join(EXE_AI_DIR, "license-cache.json");
|
|
3367
3418
|
}
|
|
3368
3419
|
});
|
|
3369
3420
|
|
|
3370
3421
|
// src/lib/notifications.ts
|
|
3371
3422
|
import crypto from "crypto";
|
|
3372
|
-
import
|
|
3423
|
+
import path11 from "path";
|
|
3373
3424
|
import os6 from "os";
|
|
3374
3425
|
import {
|
|
3375
|
-
readFileSync as
|
|
3426
|
+
readFileSync as readFileSync9,
|
|
3376
3427
|
readdirSync as readdirSync2,
|
|
3377
3428
|
unlinkSync as unlinkSync3,
|
|
3378
|
-
existsSync as
|
|
3429
|
+
existsSync as existsSync11,
|
|
3379
3430
|
rmdirSync
|
|
3380
3431
|
} from "fs";
|
|
3381
3432
|
async function writeNotification(notification) {
|
|
@@ -3488,9 +3539,9 @@ async function markDoneTaskNotificationsAsRead() {
|
|
|
3488
3539
|
}
|
|
3489
3540
|
}
|
|
3490
3541
|
async function migrateJsonNotifications() {
|
|
3491
|
-
const base = process.env.EXE_OS_DIR || process.env.EXE_MEM_DIR ||
|
|
3492
|
-
const notifDir =
|
|
3493
|
-
if (!
|
|
3542
|
+
const base = process.env.EXE_OS_DIR || process.env.EXE_MEM_DIR || path11.join(os6.homedir(), ".exe-os");
|
|
3543
|
+
const notifDir = path11.join(base, "notifications");
|
|
3544
|
+
if (!existsSync11(notifDir)) return 0;
|
|
3494
3545
|
let migrated = 0;
|
|
3495
3546
|
try {
|
|
3496
3547
|
const files = readdirSync2(notifDir).filter((f) => f.endsWith(".json"));
|
|
@@ -3498,8 +3549,8 @@ async function migrateJsonNotifications() {
|
|
|
3498
3549
|
const client = getClient();
|
|
3499
3550
|
for (const file of files) {
|
|
3500
3551
|
try {
|
|
3501
|
-
const filePath =
|
|
3502
|
-
const data = JSON.parse(
|
|
3552
|
+
const filePath = path11.join(notifDir, file);
|
|
3553
|
+
const data = JSON.parse(readFileSync9(filePath, "utf8"));
|
|
3503
3554
|
await client.execute({
|
|
3504
3555
|
sql: `INSERT OR IGNORE INTO notifications (id, agent_id, agent_role, event, project, summary, task_file, read, created_at)
|
|
3505
3556
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
@@ -3646,11 +3697,11 @@ var init_session_kill_telemetry = __esm({
|
|
|
3646
3697
|
|
|
3647
3698
|
// src/lib/tasks-crud.ts
|
|
3648
3699
|
import crypto3 from "crypto";
|
|
3649
|
-
import
|
|
3700
|
+
import path12 from "path";
|
|
3650
3701
|
import os7 from "os";
|
|
3651
3702
|
import { execSync as execSync5 } from "child_process";
|
|
3652
3703
|
import { mkdir as mkdir4, writeFile as writeFile4, appendFile } from "fs/promises";
|
|
3653
|
-
import { existsSync as
|
|
3704
|
+
import { existsSync as existsSync12, readFileSync as readFileSync10 } from "fs";
|
|
3654
3705
|
async function writeCheckpoint(input) {
|
|
3655
3706
|
const client = getClient();
|
|
3656
3707
|
const row = await resolveTask(client, input.taskId);
|
|
@@ -3825,8 +3876,8 @@ ${laneWarning}` : laneWarning;
|
|
|
3825
3876
|
}
|
|
3826
3877
|
if (input.baseDir) {
|
|
3827
3878
|
try {
|
|
3828
|
-
await mkdir4(
|
|
3829
|
-
await mkdir4(
|
|
3879
|
+
await mkdir4(path12.join(input.baseDir, "exe", "output"), { recursive: true });
|
|
3880
|
+
await mkdir4(path12.join(input.baseDir, "exe", "research"), { recursive: true });
|
|
3830
3881
|
await ensureArchitectureDoc(input.baseDir, input.projectName);
|
|
3831
3882
|
await ensureGitignoreExe(input.baseDir);
|
|
3832
3883
|
} catch {
|
|
@@ -3862,10 +3913,10 @@ ${laneWarning}` : laneWarning;
|
|
|
3862
3913
|
});
|
|
3863
3914
|
if (input.baseDir) {
|
|
3864
3915
|
try {
|
|
3865
|
-
const EXE_OS_DIR =
|
|
3866
|
-
const mdPath =
|
|
3867
|
-
const mdDir =
|
|
3868
|
-
if (!
|
|
3916
|
+
const EXE_OS_DIR = path12.join(os7.homedir(), ".exe-os");
|
|
3917
|
+
const mdPath = path12.join(EXE_OS_DIR, taskFile);
|
|
3918
|
+
const mdDir = path12.dirname(mdPath);
|
|
3919
|
+
if (!existsSync12(mdDir)) await mkdir4(mdDir, { recursive: true });
|
|
3869
3920
|
const reviewer = input.reviewer ?? input.assignedBy;
|
|
3870
3921
|
const mdContent = `# ${input.title}
|
|
3871
3922
|
|
|
@@ -3890,7 +3941,11 @@ If you skip this, your reviewer will not know you're done and your work won't be
|
|
|
3890
3941
|
Do NOT let a failed commit or any error prevent you from calling update_task(done).
|
|
3891
3942
|
`;
|
|
3892
3943
|
await writeFile4(mdPath, mdContent, "utf-8");
|
|
3893
|
-
} catch {
|
|
3944
|
+
} catch (err) {
|
|
3945
|
+
process.stderr.write(
|
|
3946
|
+
`[create-task] WARNING: .md file write failed for ${taskFile}: ${err instanceof Error ? err.message : String(err)}
|
|
3947
|
+
`
|
|
3948
|
+
);
|
|
3894
3949
|
}
|
|
3895
3950
|
}
|
|
3896
3951
|
return {
|
|
@@ -4150,9 +4205,9 @@ async function deleteTaskCore(taskId, _baseDir) {
|
|
|
4150
4205
|
return { taskFile, assignedTo, assignedBy, taskSlug };
|
|
4151
4206
|
}
|
|
4152
4207
|
async function ensureArchitectureDoc(baseDir, projectName) {
|
|
4153
|
-
const archPath =
|
|
4208
|
+
const archPath = path12.join(baseDir, "exe", "ARCHITECTURE.md");
|
|
4154
4209
|
try {
|
|
4155
|
-
if (
|
|
4210
|
+
if (existsSync12(archPath)) return;
|
|
4156
4211
|
const template = [
|
|
4157
4212
|
`# ${projectName} \u2014 System Architecture`,
|
|
4158
4213
|
"",
|
|
@@ -4185,10 +4240,10 @@ async function ensureArchitectureDoc(baseDir, projectName) {
|
|
|
4185
4240
|
}
|
|
4186
4241
|
}
|
|
4187
4242
|
async function ensureGitignoreExe(baseDir) {
|
|
4188
|
-
const gitignorePath =
|
|
4243
|
+
const gitignorePath = path12.join(baseDir, ".gitignore");
|
|
4189
4244
|
try {
|
|
4190
|
-
if (
|
|
4191
|
-
const content =
|
|
4245
|
+
if (existsSync12(gitignorePath)) {
|
|
4246
|
+
const content = readFileSync10(gitignorePath, "utf-8");
|
|
4192
4247
|
if (/^\/?exe\/?$/m.test(content)) return;
|
|
4193
4248
|
await appendFile(gitignorePath, "\n# Employee task assignments (private)\n/exe/\n");
|
|
4194
4249
|
} else {
|
|
@@ -4219,8 +4274,8 @@ var init_tasks_crud = __esm({
|
|
|
4219
4274
|
});
|
|
4220
4275
|
|
|
4221
4276
|
// src/lib/tasks-review.ts
|
|
4222
|
-
import
|
|
4223
|
-
import { existsSync as
|
|
4277
|
+
import path13 from "path";
|
|
4278
|
+
import { existsSync as existsSync13, readdirSync as readdirSync3, unlinkSync as unlinkSync4 } from "fs";
|
|
4224
4279
|
async function countPendingReviews(sessionScope) {
|
|
4225
4280
|
const client = getClient();
|
|
4226
4281
|
if (sessionScope) {
|
|
@@ -4401,11 +4456,11 @@ async function cleanupReviewFile(row, taskFile, _baseDir) {
|
|
|
4401
4456
|
);
|
|
4402
4457
|
}
|
|
4403
4458
|
try {
|
|
4404
|
-
const cacheDir =
|
|
4405
|
-
if (
|
|
4459
|
+
const cacheDir = path13.join(EXE_AI_DIR, "session-cache");
|
|
4460
|
+
if (existsSync13(cacheDir)) {
|
|
4406
4461
|
for (const f of readdirSync3(cacheDir)) {
|
|
4407
4462
|
if (f.startsWith("review-notified-")) {
|
|
4408
|
-
unlinkSync4(
|
|
4463
|
+
unlinkSync4(path13.join(cacheDir, f));
|
|
4409
4464
|
}
|
|
4410
4465
|
}
|
|
4411
4466
|
}
|
|
@@ -4426,7 +4481,7 @@ var init_tasks_review = __esm({
|
|
|
4426
4481
|
});
|
|
4427
4482
|
|
|
4428
4483
|
// src/lib/tasks-chain.ts
|
|
4429
|
-
import
|
|
4484
|
+
import path14 from "path";
|
|
4430
4485
|
import { readFile as readFile4, writeFile as writeFile5 } from "fs/promises";
|
|
4431
4486
|
async function cascadeUnblock(taskId, baseDir, now) {
|
|
4432
4487
|
const client = getClient();
|
|
@@ -4443,7 +4498,7 @@ async function cascadeUnblock(taskId, baseDir, now) {
|
|
|
4443
4498
|
});
|
|
4444
4499
|
for (const ur of unblockedRows.rows) {
|
|
4445
4500
|
try {
|
|
4446
|
-
const ubFile =
|
|
4501
|
+
const ubFile = path14.join(baseDir, String(ur.task_file));
|
|
4447
4502
|
let ubContent = await readFile4(ubFile, "utf-8");
|
|
4448
4503
|
ubContent = ubContent.replace(/\*\*Status:\*\* blocked/, "**Status:** open");
|
|
4449
4504
|
ubContent = ubContent.replace(/\n\*\*Blocked by:\*\*.*\n/, "\n");
|
|
@@ -4517,7 +4572,7 @@ __export(project_name_exports, {
|
|
|
4517
4572
|
getProjectName: () => getProjectName
|
|
4518
4573
|
});
|
|
4519
4574
|
import { execSync as execSync6 } from "child_process";
|
|
4520
|
-
import
|
|
4575
|
+
import path15 from "path";
|
|
4521
4576
|
function getProjectName(cwd) {
|
|
4522
4577
|
const dir = cwd ?? process.cwd();
|
|
4523
4578
|
if (_cached2 && _cachedCwd === dir) return _cached2;
|
|
@@ -4530,7 +4585,7 @@ function getProjectName(cwd) {
|
|
|
4530
4585
|
timeout: 2e3,
|
|
4531
4586
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4532
4587
|
}).trim();
|
|
4533
|
-
repoRoot =
|
|
4588
|
+
repoRoot = path15.dirname(gitCommonDir);
|
|
4534
4589
|
} catch {
|
|
4535
4590
|
repoRoot = execSync6("git rev-parse --show-toplevel", {
|
|
4536
4591
|
cwd: dir,
|
|
@@ -4539,11 +4594,11 @@ function getProjectName(cwd) {
|
|
|
4539
4594
|
stdio: ["pipe", "pipe", "pipe"]
|
|
4540
4595
|
}).trim();
|
|
4541
4596
|
}
|
|
4542
|
-
_cached2 =
|
|
4597
|
+
_cached2 = path15.basename(repoRoot);
|
|
4543
4598
|
_cachedCwd = dir;
|
|
4544
4599
|
return _cached2;
|
|
4545
4600
|
} catch {
|
|
4546
|
-
_cached2 =
|
|
4601
|
+
_cached2 = path15.basename(dir);
|
|
4547
4602
|
_cachedCwd = dir;
|
|
4548
4603
|
return _cached2;
|
|
4549
4604
|
}
|
|
@@ -5020,8 +5075,8 @@ __export(tasks_exports, {
|
|
|
5020
5075
|
updateTaskStatus: () => updateTaskStatus,
|
|
5021
5076
|
writeCheckpoint: () => writeCheckpoint
|
|
5022
5077
|
});
|
|
5023
|
-
import
|
|
5024
|
-
import { writeFileSync as
|
|
5078
|
+
import path16 from "path";
|
|
5079
|
+
import { writeFileSync as writeFileSync6, mkdirSync as mkdirSync6, unlinkSync as unlinkSync5 } from "fs";
|
|
5025
5080
|
async function createTask(input) {
|
|
5026
5081
|
const result = await createTaskCore(input);
|
|
5027
5082
|
if (!input.skipDispatch && result.status !== "blocked" && !process.env.VITEST) {
|
|
@@ -5040,11 +5095,11 @@ async function updateTask(input) {
|
|
|
5040
5095
|
const { row, taskFile, now, taskId } = await updateTaskStatus(input);
|
|
5041
5096
|
try {
|
|
5042
5097
|
const agent = String(row.assigned_to);
|
|
5043
|
-
const cacheDir =
|
|
5044
|
-
const cachePath =
|
|
5098
|
+
const cacheDir = path16.join(EXE_AI_DIR, "session-cache");
|
|
5099
|
+
const cachePath = path16.join(cacheDir, `current-task-${agent}.json`);
|
|
5045
5100
|
if (input.status === "in_progress") {
|
|
5046
|
-
|
|
5047
|
-
|
|
5101
|
+
mkdirSync6(cacheDir, { recursive: true });
|
|
5102
|
+
writeFileSync6(cachePath, JSON.stringify({ taskId, title: String(row.title) }));
|
|
5048
5103
|
} else if (input.status === "done" || input.status === "blocked" || input.status === "cancelled") {
|
|
5049
5104
|
try {
|
|
5050
5105
|
unlinkSync5(cachePath);
|
|
@@ -5511,13 +5566,13 @@ __export(tmux_routing_exports, {
|
|
|
5511
5566
|
verifyPaneAtCapacity: () => verifyPaneAtCapacity
|
|
5512
5567
|
});
|
|
5513
5568
|
import { execFileSync as execFileSync2, execSync as execSync7 } from "child_process";
|
|
5514
|
-
import { readFileSync as
|
|
5515
|
-
import
|
|
5569
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync7, mkdirSync as mkdirSync7, existsSync as existsSync14, appendFileSync } from "fs";
|
|
5570
|
+
import path17 from "path";
|
|
5516
5571
|
import os8 from "os";
|
|
5517
5572
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
5518
5573
|
import { unlinkSync as unlinkSync6 } from "fs";
|
|
5519
5574
|
function spawnLockPath(sessionName) {
|
|
5520
|
-
return
|
|
5575
|
+
return path17.join(SPAWN_LOCK_DIR, `${sessionName}.lock`);
|
|
5521
5576
|
}
|
|
5522
5577
|
function isProcessAlive(pid) {
|
|
5523
5578
|
try {
|
|
@@ -5528,13 +5583,13 @@ function isProcessAlive(pid) {
|
|
|
5528
5583
|
}
|
|
5529
5584
|
}
|
|
5530
5585
|
function acquireSpawnLock2(sessionName) {
|
|
5531
|
-
if (!
|
|
5532
|
-
|
|
5586
|
+
if (!existsSync14(SPAWN_LOCK_DIR)) {
|
|
5587
|
+
mkdirSync7(SPAWN_LOCK_DIR, { recursive: true });
|
|
5533
5588
|
}
|
|
5534
5589
|
const lockFile = spawnLockPath(sessionName);
|
|
5535
|
-
if (
|
|
5590
|
+
if (existsSync14(lockFile)) {
|
|
5536
5591
|
try {
|
|
5537
|
-
const lock = JSON.parse(
|
|
5592
|
+
const lock = JSON.parse(readFileSync11(lockFile, "utf8"));
|
|
5538
5593
|
const age = Date.now() - lock.timestamp;
|
|
5539
5594
|
if (isProcessAlive(lock.pid) && age < 6e4) {
|
|
5540
5595
|
return false;
|
|
@@ -5542,7 +5597,7 @@ function acquireSpawnLock2(sessionName) {
|
|
|
5542
5597
|
} catch {
|
|
5543
5598
|
}
|
|
5544
5599
|
}
|
|
5545
|
-
|
|
5600
|
+
writeFileSync7(lockFile, JSON.stringify({ pid: process.pid, timestamp: Date.now() }));
|
|
5546
5601
|
return true;
|
|
5547
5602
|
}
|
|
5548
5603
|
function releaseSpawnLock2(sessionName) {
|
|
@@ -5554,13 +5609,13 @@ function releaseSpawnLock2(sessionName) {
|
|
|
5554
5609
|
function resolveBehaviorsExporterScript() {
|
|
5555
5610
|
try {
|
|
5556
5611
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
5557
|
-
const scriptPath =
|
|
5558
|
-
|
|
5612
|
+
const scriptPath = path17.join(
|
|
5613
|
+
path17.dirname(thisFile),
|
|
5559
5614
|
"..",
|
|
5560
5615
|
"bin",
|
|
5561
5616
|
"exe-export-behaviors.js"
|
|
5562
5617
|
);
|
|
5563
|
-
return
|
|
5618
|
+
return existsSync14(scriptPath) ? scriptPath : null;
|
|
5564
5619
|
} catch {
|
|
5565
5620
|
return null;
|
|
5566
5621
|
}
|
|
@@ -5626,12 +5681,12 @@ function extractRootExe(name) {
|
|
|
5626
5681
|
return parts.length > 0 ? parts[parts.length - 1] : null;
|
|
5627
5682
|
}
|
|
5628
5683
|
function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
5629
|
-
if (!
|
|
5630
|
-
|
|
5684
|
+
if (!existsSync14(SESSION_CACHE)) {
|
|
5685
|
+
mkdirSync7(SESSION_CACHE, { recursive: true });
|
|
5631
5686
|
}
|
|
5632
5687
|
const rootExe = extractRootExe(parentExe) ?? parentExe;
|
|
5633
|
-
const filePath =
|
|
5634
|
-
|
|
5688
|
+
const filePath = path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`);
|
|
5689
|
+
writeFileSync7(filePath, JSON.stringify({
|
|
5635
5690
|
parentExe: rootExe,
|
|
5636
5691
|
dispatchedBy: dispatchedBy || rootExe,
|
|
5637
5692
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -5639,7 +5694,7 @@ function registerParentExe(sessionKey, parentExe, dispatchedBy) {
|
|
|
5639
5694
|
}
|
|
5640
5695
|
function getParentExe(sessionKey) {
|
|
5641
5696
|
try {
|
|
5642
|
-
const data = JSON.parse(
|
|
5697
|
+
const data = JSON.parse(readFileSync11(path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`), "utf8"));
|
|
5643
5698
|
return data.parentExe || null;
|
|
5644
5699
|
} catch {
|
|
5645
5700
|
return null;
|
|
@@ -5647,8 +5702,8 @@ function getParentExe(sessionKey) {
|
|
|
5647
5702
|
}
|
|
5648
5703
|
function getDispatchedBy(sessionKey) {
|
|
5649
5704
|
try {
|
|
5650
|
-
const data = JSON.parse(
|
|
5651
|
-
|
|
5705
|
+
const data = JSON.parse(readFileSync11(
|
|
5706
|
+
path17.join(SESSION_CACHE, `parent-exe-${sessionKey}.json`),
|
|
5652
5707
|
"utf8"
|
|
5653
5708
|
));
|
|
5654
5709
|
return data.dispatchedBy ?? data.parentExe ?? null;
|
|
@@ -5709,32 +5764,50 @@ async function verifyPaneAtCapacity(sessionName) {
|
|
|
5709
5764
|
}
|
|
5710
5765
|
function readDebounceState() {
|
|
5711
5766
|
try {
|
|
5712
|
-
if (!
|
|
5713
|
-
|
|
5767
|
+
if (!existsSync14(DEBOUNCE_FILE)) return {};
|
|
5768
|
+
const raw = JSON.parse(readFileSync11(DEBOUNCE_FILE, "utf8"));
|
|
5769
|
+
const state = {};
|
|
5770
|
+
for (const [key, val] of Object.entries(raw)) {
|
|
5771
|
+
if (typeof val === "number") {
|
|
5772
|
+
state[key] = { lastSent: val, pending: 0 };
|
|
5773
|
+
} else if (val && typeof val === "object" && "lastSent" in val) {
|
|
5774
|
+
state[key] = val;
|
|
5775
|
+
}
|
|
5776
|
+
}
|
|
5777
|
+
return state;
|
|
5714
5778
|
} catch {
|
|
5715
5779
|
return {};
|
|
5716
5780
|
}
|
|
5717
5781
|
}
|
|
5718
5782
|
function writeDebounceState(state) {
|
|
5719
5783
|
try {
|
|
5720
|
-
if (!
|
|
5721
|
-
|
|
5784
|
+
if (!existsSync14(SESSION_CACHE)) mkdirSync7(SESSION_CACHE, { recursive: true });
|
|
5785
|
+
writeFileSync7(DEBOUNCE_FILE, JSON.stringify(state));
|
|
5722
5786
|
} catch {
|
|
5723
5787
|
}
|
|
5724
5788
|
}
|
|
5725
5789
|
function isDebounced(targetSession) {
|
|
5726
5790
|
const state = readDebounceState();
|
|
5727
|
-
const
|
|
5728
|
-
|
|
5791
|
+
const entry = state[targetSession];
|
|
5792
|
+
const lastSent = entry?.lastSent ?? 0;
|
|
5793
|
+
if (Date.now() - lastSent < INTERCOM_DEBOUNCE_MS) {
|
|
5794
|
+
if (!state[targetSession]) state[targetSession] = { lastSent, pending: 0 };
|
|
5795
|
+
state[targetSession].pending++;
|
|
5796
|
+
writeDebounceState(state);
|
|
5797
|
+
return true;
|
|
5798
|
+
}
|
|
5799
|
+
return false;
|
|
5729
5800
|
}
|
|
5730
5801
|
function recordDebounce(targetSession) {
|
|
5731
5802
|
const state = readDebounceState();
|
|
5732
|
-
state[targetSession]
|
|
5803
|
+
const batched = state[targetSession]?.pending ?? 0;
|
|
5804
|
+
state[targetSession] = { lastSent: Date.now(), pending: 0 };
|
|
5733
5805
|
const cutoff = Date.now() - DEBOUNCE_CLEANUP_AGE_MS;
|
|
5734
5806
|
for (const key of Object.keys(state)) {
|
|
5735
|
-
if ((state[key] ?? 0) < cutoff) delete state[key];
|
|
5807
|
+
if ((state[key]?.lastSent ?? 0) < cutoff) delete state[key];
|
|
5736
5808
|
}
|
|
5737
5809
|
writeDebounceState(state);
|
|
5810
|
+
return batched;
|
|
5738
5811
|
}
|
|
5739
5812
|
function logIntercom(msg) {
|
|
5740
5813
|
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}
|
|
@@ -5779,7 +5852,7 @@ function sendIntercom(targetSession) {
|
|
|
5779
5852
|
return "skipped_exe";
|
|
5780
5853
|
}
|
|
5781
5854
|
if (isDebounced(targetSession)) {
|
|
5782
|
-
logIntercom(`DEBOUNCE \u2192 ${targetSession} (
|
|
5855
|
+
logIntercom(`DEBOUNCE \u2192 ${targetSession} (nudge batched, task safe in DB)`);
|
|
5783
5856
|
return "debounced";
|
|
5784
5857
|
}
|
|
5785
5858
|
try {
|
|
@@ -5791,14 +5864,14 @@ function sendIntercom(targetSession) {
|
|
|
5791
5864
|
const sessionState = getSessionState(targetSession);
|
|
5792
5865
|
if (sessionState === "no_claude") {
|
|
5793
5866
|
queueIntercom(targetSession, "claude not running in session");
|
|
5794
|
-
recordDebounce(targetSession);
|
|
5795
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process
|
|
5867
|
+
const batched2 = recordDebounce(targetSession);
|
|
5868
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (no claude process)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
5796
5869
|
return "queued";
|
|
5797
5870
|
}
|
|
5798
5871
|
if (sessionState === "thinking" || sessionState === "tool") {
|
|
5799
5872
|
queueIntercom(targetSession, "session busy at send time");
|
|
5800
|
-
recordDebounce(targetSession);
|
|
5801
|
-
logIntercom(`QUEUED \u2192 ${targetSession} (session busy
|
|
5873
|
+
const batched2 = recordDebounce(targetSession);
|
|
5874
|
+
logIntercom(`QUEUED \u2192 ${targetSession} (session busy)${batched2 > 0 ? ` [${batched2} batched]` : ""}`);
|
|
5802
5875
|
return "queued";
|
|
5803
5876
|
}
|
|
5804
5877
|
if (transport.isPaneInCopyMode(targetSession)) {
|
|
@@ -5806,8 +5879,8 @@ function sendIntercom(targetSession) {
|
|
|
5806
5879
|
transport.sendKeys(targetSession, "q");
|
|
5807
5880
|
}
|
|
5808
5881
|
transport.sendKeys(targetSession, "/exe-intercom");
|
|
5809
|
-
recordDebounce(targetSession);
|
|
5810
|
-
logIntercom(`DELIVERED \u2192 ${targetSession} (fire-and-forget)`);
|
|
5882
|
+
const batched = recordDebounce(targetSession);
|
|
5883
|
+
logIntercom(`DELIVERED \u2192 ${targetSession}${batched > 0 ? ` [${batched} nudges batched during debounce]` : ""} (fire-and-forget)`);
|
|
5811
5884
|
return "delivered";
|
|
5812
5885
|
} catch {
|
|
5813
5886
|
logIntercom(`FAIL \u2192 ${targetSession}`);
|
|
@@ -5909,26 +5982,26 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5909
5982
|
const transport = getTransport();
|
|
5910
5983
|
const sessionName = employeeSessionName(employeeName, exeSession, opts?.instance);
|
|
5911
5984
|
const instanceLabel = opts?.instance != null && opts.instance > 0 ? `${employeeName}${opts.instance}` : employeeName;
|
|
5912
|
-
const logDir =
|
|
5913
|
-
const logFile =
|
|
5914
|
-
if (!
|
|
5915
|
-
|
|
5985
|
+
const logDir = path17.join(os8.homedir(), ".exe-os", "session-logs");
|
|
5986
|
+
const logFile = path17.join(logDir, `${instanceLabel}-${Date.now()}.log`);
|
|
5987
|
+
if (!existsSync14(logDir)) {
|
|
5988
|
+
mkdirSync7(logDir, { recursive: true });
|
|
5916
5989
|
}
|
|
5917
5990
|
transport.kill(sessionName);
|
|
5918
5991
|
let cleanupSuffix = "";
|
|
5919
5992
|
try {
|
|
5920
5993
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
5921
|
-
const cleanupScript =
|
|
5922
|
-
if (
|
|
5994
|
+
const cleanupScript = path17.join(path17.dirname(thisFile), "..", "bin", "exe-session-cleanup.js");
|
|
5995
|
+
if (existsSync14(cleanupScript)) {
|
|
5923
5996
|
cleanupSuffix = `; ${process.execPath} "${cleanupScript}" "${employeeName}" "${exeSession}"`;
|
|
5924
5997
|
}
|
|
5925
5998
|
} catch {
|
|
5926
5999
|
}
|
|
5927
6000
|
try {
|
|
5928
|
-
const claudeJsonPath =
|
|
6001
|
+
const claudeJsonPath = path17.join(os8.homedir(), ".claude.json");
|
|
5929
6002
|
let claudeJson = {};
|
|
5930
6003
|
try {
|
|
5931
|
-
claudeJson = JSON.parse(
|
|
6004
|
+
claudeJson = JSON.parse(readFileSync11(claudeJsonPath, "utf8"));
|
|
5932
6005
|
} catch {
|
|
5933
6006
|
}
|
|
5934
6007
|
if (!claudeJson.projects) claudeJson.projects = {};
|
|
@@ -5936,17 +6009,17 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5936
6009
|
const trustDir = opts?.cwd ?? projectDir;
|
|
5937
6010
|
if (!projects[trustDir]) projects[trustDir] = {};
|
|
5938
6011
|
projects[trustDir].hasTrustDialogAccepted = true;
|
|
5939
|
-
|
|
6012
|
+
writeFileSync7(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
5940
6013
|
} catch {
|
|
5941
6014
|
}
|
|
5942
6015
|
try {
|
|
5943
|
-
const settingsDir =
|
|
6016
|
+
const settingsDir = path17.join(os8.homedir(), ".claude", "projects");
|
|
5944
6017
|
const normalizedKey = (opts?.cwd ?? projectDir).replace(/\//g, "-").replace(/^-/, "");
|
|
5945
|
-
const projSettingsDir =
|
|
5946
|
-
const settingsPath =
|
|
6018
|
+
const projSettingsDir = path17.join(settingsDir, normalizedKey);
|
|
6019
|
+
const settingsPath = path17.join(projSettingsDir, "settings.json");
|
|
5947
6020
|
let settings = {};
|
|
5948
6021
|
try {
|
|
5949
|
-
settings = JSON.parse(
|
|
6022
|
+
settings = JSON.parse(readFileSync11(settingsPath, "utf8"));
|
|
5950
6023
|
} catch {
|
|
5951
6024
|
}
|
|
5952
6025
|
const perms = settings.permissions ?? {};
|
|
@@ -5974,20 +6047,23 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5974
6047
|
if (changed) {
|
|
5975
6048
|
perms.allow = allow;
|
|
5976
6049
|
settings.permissions = perms;
|
|
5977
|
-
|
|
5978
|
-
|
|
6050
|
+
mkdirSync7(projSettingsDir, { recursive: true });
|
|
6051
|
+
writeFileSync7(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
5979
6052
|
}
|
|
5980
6053
|
} catch {
|
|
5981
6054
|
}
|
|
5982
6055
|
const spawnCwd = opts?.cwd ?? projectDir;
|
|
5983
6056
|
const useExeAgent = !!(opts?.model && opts?.provider);
|
|
5984
|
-
const
|
|
6057
|
+
const agentRtConfig = getAgentRuntime(employeeName);
|
|
6058
|
+
const useCodex = !useExeAgent && agentRtConfig.runtime === "codex";
|
|
6059
|
+
const useOpencode = !useExeAgent && !useCodex && agentRtConfig.runtime === "opencode";
|
|
6060
|
+
const ccProvider = useExeAgent || useCodex || useOpencode ? DEFAULT_PROVIDER : detectActiveProvider();
|
|
5985
6061
|
const useBinSymlink = ccProvider !== DEFAULT_PROVIDER;
|
|
5986
6062
|
let identityFlag = "";
|
|
5987
6063
|
let behaviorsFlag = "";
|
|
5988
6064
|
let legacyFallbackWarned = false;
|
|
5989
6065
|
if (!useExeAgent && !useBinSymlink) {
|
|
5990
|
-
const identityPath =
|
|
6066
|
+
const identityPath = path17.join(
|
|
5991
6067
|
os8.homedir(),
|
|
5992
6068
|
".exe-os",
|
|
5993
6069
|
"identity",
|
|
@@ -5997,13 +6073,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
5997
6073
|
const hasAgentFlag = claudeSupportsAgentFlag();
|
|
5998
6074
|
if (hasAgentFlag) {
|
|
5999
6075
|
identityFlag = ` --agent ${employeeName}`;
|
|
6000
|
-
} else if (
|
|
6076
|
+
} else if (existsSync14(identityPath)) {
|
|
6001
6077
|
identityFlag = ` --append-system-prompt-file ${identityPath}`;
|
|
6002
6078
|
legacyFallbackWarned = true;
|
|
6003
6079
|
}
|
|
6004
6080
|
const behaviorsFile = exportBehaviorsSync(
|
|
6005
6081
|
employeeName,
|
|
6006
|
-
|
|
6082
|
+
path17.basename(spawnCwd),
|
|
6007
6083
|
sessionName
|
|
6008
6084
|
);
|
|
6009
6085
|
if (behaviorsFile) {
|
|
@@ -6018,16 +6094,16 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6018
6094
|
}
|
|
6019
6095
|
let sessionContextFlag = "";
|
|
6020
6096
|
try {
|
|
6021
|
-
const ctxDir =
|
|
6022
|
-
|
|
6023
|
-
const ctxFile =
|
|
6097
|
+
const ctxDir = path17.join(os8.homedir(), ".exe-os", "session-cache");
|
|
6098
|
+
mkdirSync7(ctxDir, { recursive: true });
|
|
6099
|
+
const ctxFile = path17.join(ctxDir, `session-context-${sessionName}.md`);
|
|
6024
6100
|
const ctxContent = [
|
|
6025
6101
|
`## Session Context`,
|
|
6026
6102
|
`You are running in tmux session: ${sessionName}.`,
|
|
6027
6103
|
`Your parent coordinator session is ${exeSession}.`,
|
|
6028
6104
|
`Your employees (if any) use the -${exeSession} suffix.`
|
|
6029
6105
|
].join("\n");
|
|
6030
|
-
|
|
6106
|
+
writeFileSync7(ctxFile, ctxContent);
|
|
6031
6107
|
sessionContextFlag = ` --append-system-prompt-file ${ctxFile}`;
|
|
6032
6108
|
} catch {
|
|
6033
6109
|
}
|
|
@@ -6041,9 +6117,48 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6041
6117
|
}
|
|
6042
6118
|
}
|
|
6043
6119
|
}
|
|
6120
|
+
if (useCodex) {
|
|
6121
|
+
const codexCfg = RUNTIME_TABLE.codex;
|
|
6122
|
+
if (codexCfg?.apiKeyEnv) {
|
|
6123
|
+
const keyVal = process.env[codexCfg.apiKeyEnv];
|
|
6124
|
+
if (keyVal) {
|
|
6125
|
+
envPrefix = `${envPrefix} ${codexCfg.apiKeyEnv}=${keyVal}`;
|
|
6126
|
+
}
|
|
6127
|
+
}
|
|
6128
|
+
envPrefix = `${envPrefix} EXE_AGENT_MODEL=${agentRtConfig.model}`;
|
|
6129
|
+
}
|
|
6130
|
+
if (useOpencode) {
|
|
6131
|
+
const ocCfg = PROVIDER_TABLE.opencode;
|
|
6132
|
+
if (ocCfg?.apiKeyEnv) {
|
|
6133
|
+
const keyVal = process.env[ocCfg.apiKeyEnv];
|
|
6134
|
+
if (keyVal) {
|
|
6135
|
+
envPrefix = `${envPrefix} ${ocCfg.apiKeyEnv}=${keyVal}`;
|
|
6136
|
+
}
|
|
6137
|
+
}
|
|
6138
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
|
|
6139
|
+
}
|
|
6140
|
+
if (!useExeAgent && !useCodex && !useOpencode && !useBinSymlink) {
|
|
6141
|
+
const defaultClaudeModel = DEFAULT_MODELS.claude;
|
|
6142
|
+
if (agentRtConfig.runtime === "claude" && agentRtConfig.model !== defaultClaudeModel) {
|
|
6143
|
+
envPrefix = `${envPrefix} ANTHROPIC_MODEL=${agentRtConfig.model}`;
|
|
6144
|
+
}
|
|
6145
|
+
}
|
|
6044
6146
|
let spawnCommand;
|
|
6045
6147
|
if (useExeAgent) {
|
|
6046
6148
|
spawnCommand = `${envPrefix} exe-agent --employee ${employeeName} --model ${opts.model} --provider ${opts.provider}${cleanupSuffix}`;
|
|
6149
|
+
} else if (useCodex) {
|
|
6150
|
+
process.stderr.write(
|
|
6151
|
+
`[tmux-routing] agent-config: ${employeeName} \u2192 codex (${agentRtConfig.model})
|
|
6152
|
+
`
|
|
6153
|
+
);
|
|
6154
|
+
spawnCommand = `${envPrefix} exe-start-codex --agent ${employeeName}${cleanupSuffix}`;
|
|
6155
|
+
} else if (useOpencode) {
|
|
6156
|
+
const binName = `${employeeName}-opencode`;
|
|
6157
|
+
process.stderr.write(
|
|
6158
|
+
`[tmux-routing] agent-config: ${employeeName} \u2192 opencode (${agentRtConfig.model})
|
|
6159
|
+
`
|
|
6160
|
+
);
|
|
6161
|
+
spawnCommand = `${envPrefix} ${binName}${cleanupSuffix}`;
|
|
6047
6162
|
} else if (useBinSymlink) {
|
|
6048
6163
|
const binName = `${employeeName}-${ccProvider}`;
|
|
6049
6164
|
process.stderr.write(
|
|
@@ -6065,11 +6180,13 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6065
6180
|
transport.pipeLog(sessionName, logFile);
|
|
6066
6181
|
try {
|
|
6067
6182
|
const mySession = getMySession();
|
|
6068
|
-
const dispatchInfo =
|
|
6069
|
-
|
|
6183
|
+
const dispatchInfo = path17.join(SESSION_CACHE, `dispatch-info-${sessionName}.json`);
|
|
6184
|
+
writeFileSync7(dispatchInfo, JSON.stringify({
|
|
6070
6185
|
dispatchedBy: mySession,
|
|
6071
6186
|
rootExe: exeSession,
|
|
6072
|
-
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : "anthropic",
|
|
6187
|
+
provider: useBinSymlink ? ccProvider : useExeAgent ? opts.provider : useCodex ? "openai" : useOpencode ? "opencode" : "anthropic",
|
|
6188
|
+
runtime: useCodex ? "codex" : useOpencode ? "opencode" : useExeAgent ? "exe-agent" : "claude",
|
|
6189
|
+
model: useCodex ? agentRtConfig.model : useOpencode ? agentRtConfig.model : void 0,
|
|
6073
6190
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
6074
6191
|
}));
|
|
6075
6192
|
} catch {
|
|
@@ -6087,6 +6204,11 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6087
6204
|
booted = true;
|
|
6088
6205
|
break;
|
|
6089
6206
|
}
|
|
6207
|
+
} else if (useCodex) {
|
|
6208
|
+
if (pane.includes("codex") || pane.includes("Codex") || pane.includes("exe-start-codex")) {
|
|
6209
|
+
booted = true;
|
|
6210
|
+
break;
|
|
6211
|
+
}
|
|
6090
6212
|
} else {
|
|
6091
6213
|
if (pane.includes("Claude Code") || pane.includes("\u276F")) {
|
|
6092
6214
|
booted = true;
|
|
@@ -6098,9 +6220,10 @@ function spawnEmployee(employeeName, exeSession, projectDir, opts) {
|
|
|
6098
6220
|
}
|
|
6099
6221
|
if (!booted) {
|
|
6100
6222
|
releaseSpawnLock2(sessionName);
|
|
6101
|
-
|
|
6223
|
+
const runtimeLabel = useExeAgent ? "exe-agent" : useCodex ? "codex" : "claude";
|
|
6224
|
+
return { sessionName, error: `${runtimeLabel} did not boot within 15s` };
|
|
6102
6225
|
}
|
|
6103
|
-
if (!useExeAgent) {
|
|
6226
|
+
if (!useExeAgent && !useCodex) {
|
|
6104
6227
|
try {
|
|
6105
6228
|
transport.sendKeys(sessionName, `/exe-call ${employeeName}`);
|
|
6106
6229
|
} catch {
|
|
@@ -6127,17 +6250,19 @@ var init_tmux_routing = __esm({
|
|
|
6127
6250
|
init_cc_agent_support();
|
|
6128
6251
|
init_mcp_prefix();
|
|
6129
6252
|
init_provider_table();
|
|
6253
|
+
init_agent_config();
|
|
6254
|
+
init_runtime_table();
|
|
6130
6255
|
init_intercom_queue();
|
|
6131
6256
|
init_plan_limits();
|
|
6132
6257
|
init_employees();
|
|
6133
|
-
SPAWN_LOCK_DIR =
|
|
6134
|
-
SESSION_CACHE =
|
|
6258
|
+
SPAWN_LOCK_DIR = path17.join(os8.homedir(), ".exe-os", "spawn-locks");
|
|
6259
|
+
SESSION_CACHE = path17.join(os8.homedir(), ".exe-os", "session-cache");
|
|
6135
6260
|
BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
|
|
6136
6261
|
VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
|
|
6137
6262
|
VERIFY_PANE_LINES = 200;
|
|
6138
6263
|
INTERCOM_DEBOUNCE_MS = 3e4;
|
|
6139
|
-
INTERCOM_LOG2 =
|
|
6140
|
-
DEBOUNCE_FILE =
|
|
6264
|
+
INTERCOM_LOG2 = path17.join(os8.homedir(), ".exe-os", "intercom.log");
|
|
6265
|
+
DEBOUNCE_FILE = path17.join(SESSION_CACHE, "intercom-debounce.json");
|
|
6141
6266
|
DEBOUNCE_CLEANUP_AGE_MS = 5 * 60 * 1e3;
|
|
6142
6267
|
BUSY_PATTERN = /[✻✽✶✳·].*…|Running…/;
|
|
6143
6268
|
}
|
|
@@ -6172,158 +6297,8 @@ var task_scanner_exports = {};
|
|
|
6172
6297
|
__export(task_scanner_exports, {
|
|
6173
6298
|
PRIORITY_RE: () => PRIORITY_RE,
|
|
6174
6299
|
STATUS_RE: () => STATUS_RE,
|
|
6175
|
-
TITLE_RE: () => TITLE_RE
|
|
6176
|
-
formatJson: () => formatJson,
|
|
6177
|
-
formatMandatory: () => formatMandatory,
|
|
6178
|
-
formatText: () => formatText,
|
|
6179
|
-
scanAgentTasks: () => scanAgentTasks
|
|
6300
|
+
TITLE_RE: () => TITLE_RE
|
|
6180
6301
|
});
|
|
6181
|
-
import { readdirSync as readdirSync5, readFileSync as readFileSync12, existsSync as existsSync14, statSync as statSync2 } from "fs";
|
|
6182
|
-
import { execSync as execSync9 } from "child_process";
|
|
6183
|
-
import path18 from "path";
|
|
6184
|
-
import os9 from "os";
|
|
6185
|
-
function getProjectRoot() {
|
|
6186
|
-
try {
|
|
6187
|
-
return execSync9("git rev-parse --show-toplevel", {
|
|
6188
|
-
encoding: "utf8",
|
|
6189
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
6190
|
-
timeout: 5e3
|
|
6191
|
-
}).trim();
|
|
6192
|
-
} catch {
|
|
6193
|
-
return process.cwd();
|
|
6194
|
-
}
|
|
6195
|
-
}
|
|
6196
|
-
function getSessionScope() {
|
|
6197
|
-
try {
|
|
6198
|
-
const out = execSync9("tmux display-message -p '#{session_name}' 2>/dev/null", {
|
|
6199
|
-
encoding: "utf8",
|
|
6200
|
-
timeout: 1e3
|
|
6201
|
-
}).trim();
|
|
6202
|
-
if (!out) return "default";
|
|
6203
|
-
const dashIdx = out.lastIndexOf("-");
|
|
6204
|
-
return dashIdx > 0 ? out.slice(dashIdx + 1) : out;
|
|
6205
|
-
} catch {
|
|
6206
|
-
return "default";
|
|
6207
|
-
}
|
|
6208
|
-
}
|
|
6209
|
-
function scanDir(dirPath, seen) {
|
|
6210
|
-
const open = [];
|
|
6211
|
-
const inProgress = [];
|
|
6212
|
-
let done = 0;
|
|
6213
|
-
if (!existsSync14(dirPath)) return { open, inProgress, done };
|
|
6214
|
-
try {
|
|
6215
|
-
const files = readdirSync5(dirPath).filter((f) => f.endsWith(".md"));
|
|
6216
|
-
for (const f of files) {
|
|
6217
|
-
if (seen.has(f)) continue;
|
|
6218
|
-
seen.add(f);
|
|
6219
|
-
try {
|
|
6220
|
-
const content = readFileSync12(path18.join(dirPath, f), "utf8");
|
|
6221
|
-
const statusMatch = content.match(STATUS_RE);
|
|
6222
|
-
const status = statusMatch ? statusMatch[1].toLowerCase() : null;
|
|
6223
|
-
if (status === "done") {
|
|
6224
|
-
done++;
|
|
6225
|
-
continue;
|
|
6226
|
-
}
|
|
6227
|
-
if (status !== "open" && status !== "in_progress") continue;
|
|
6228
|
-
const priMatch = content.match(PRIORITY_RE);
|
|
6229
|
-
const titleMatch = content.match(TITLE_RE);
|
|
6230
|
-
const task = {
|
|
6231
|
-
file: f,
|
|
6232
|
-
title: titleMatch ? titleMatch[1] : f.replace(".md", ""),
|
|
6233
|
-
priority: priMatch ? priMatch[1] : "P2",
|
|
6234
|
-
status,
|
|
6235
|
-
slug: f.replace(".md", "")
|
|
6236
|
-
};
|
|
6237
|
-
if (status === "in_progress") {
|
|
6238
|
-
inProgress.push(task);
|
|
6239
|
-
} else {
|
|
6240
|
-
open.push(task);
|
|
6241
|
-
}
|
|
6242
|
-
} catch {
|
|
6243
|
-
}
|
|
6244
|
-
}
|
|
6245
|
-
} catch {
|
|
6246
|
-
}
|
|
6247
|
-
return { open, inProgress, done };
|
|
6248
|
-
}
|
|
6249
|
-
function scanAgentTasks(agentId) {
|
|
6250
|
-
const open = [];
|
|
6251
|
-
const inProgress = [];
|
|
6252
|
-
let done = 0;
|
|
6253
|
-
const seen = /* @__PURE__ */ new Set();
|
|
6254
|
-
const scope = getSessionScope();
|
|
6255
|
-
const centralDir = path18.join(os9.homedir(), ".exe-os", "tasks", scope, agentId);
|
|
6256
|
-
const central = scanDir(centralDir, seen);
|
|
6257
|
-
open.push(...central.open);
|
|
6258
|
-
inProgress.push(...central.inProgress);
|
|
6259
|
-
done += central.done;
|
|
6260
|
-
const legacyDir = path18.join(getProjectRoot(), "exe", agentId);
|
|
6261
|
-
const legacy = scanDir(legacyDir, seen);
|
|
6262
|
-
open.push(...legacy.open);
|
|
6263
|
-
inProgress.push(...legacy.inProgress);
|
|
6264
|
-
done += legacy.done;
|
|
6265
|
-
const total = open.length + inProgress.length + done;
|
|
6266
|
-
open.sort((a, b) => a.priority.localeCompare(b.priority));
|
|
6267
|
-
inProgress.sort((a, b) => a.priority.localeCompare(b.priority));
|
|
6268
|
-
return { open, inProgress, done, total };
|
|
6269
|
-
}
|
|
6270
|
-
function formatText(agentId, result) {
|
|
6271
|
-
const lines = [];
|
|
6272
|
-
if (result.inProgress.length > 0) {
|
|
6273
|
-
lines.push(`IN_PROGRESS (${result.inProgress.length}):`);
|
|
6274
|
-
for (const t of result.inProgress) {
|
|
6275
|
-
lines.push(` [${t.priority}] ${t.title} \u2014 exe/${agentId}/${t.file}`);
|
|
6276
|
-
}
|
|
6277
|
-
}
|
|
6278
|
-
if (result.open.length > 0) {
|
|
6279
|
-
lines.push(`OPEN (${result.open.length}):`);
|
|
6280
|
-
for (const t of result.open) {
|
|
6281
|
-
lines.push(` [${t.priority}] ${t.title} \u2014 exe/${agentId}/${t.file}`);
|
|
6282
|
-
}
|
|
6283
|
-
}
|
|
6284
|
-
lines.push(`DONE: ${result.done}`);
|
|
6285
|
-
return lines.join("\n");
|
|
6286
|
-
}
|
|
6287
|
-
function formatMandatory(agentId, result) {
|
|
6288
|
-
const { open, inProgress } = result;
|
|
6289
|
-
if (open.length === 0 && inProgress.length === 0) return "";
|
|
6290
|
-
const lines = [];
|
|
6291
|
-
if (inProgress.length > 0) {
|
|
6292
|
-
const current = inProgress[0];
|
|
6293
|
-
let stale = false;
|
|
6294
|
-
try {
|
|
6295
|
-
const stat = statSync2(path18.join(getProjectRoot(), "exe", agentId, current.file));
|
|
6296
|
-
const ageMin = (Date.now() - stat.mtimeMs) / 6e4;
|
|
6297
|
-
if (ageMin > 30) stale = true;
|
|
6298
|
-
} catch {
|
|
6299
|
-
}
|
|
6300
|
-
if (stale) {
|
|
6301
|
-
lines.push(`MANDATORY: Update task status for: ${current.title} [${current.priority}] (exe/${agentId}/${current.file})`);
|
|
6302
|
-
lines.push("This task has been in_progress for over 30 minutes without updates.");
|
|
6303
|
-
lines.push("If work is done, mark done. If blocked, update status to blocked.");
|
|
6304
|
-
} else {
|
|
6305
|
-
lines.push(`Continue working on: ${current.title} [${current.priority}] (exe/${agentId}/${current.file})`);
|
|
6306
|
-
}
|
|
6307
|
-
if (open.length > 0) {
|
|
6308
|
-
lines.push("Queued: " + open.map((t) => `${t.title} [${t.priority}]`).join(", "));
|
|
6309
|
-
}
|
|
6310
|
-
} else {
|
|
6311
|
-
const top = open[0];
|
|
6312
|
-
lines.push(`MANDATORY: You have ${open.length} unstarted task(s).`);
|
|
6313
|
-
lines.push(`Highest priority: ${top.title} [${top.priority}]`);
|
|
6314
|
-
lines.push(`File: exe/${agentId}/${top.file}`);
|
|
6315
|
-
lines.push("Read this task file and START WORKING NOW.");
|
|
6316
|
-
}
|
|
6317
|
-
return lines.join("\n");
|
|
6318
|
-
}
|
|
6319
|
-
function formatJson(result) {
|
|
6320
|
-
return JSON.stringify({
|
|
6321
|
-
open: result.open.map((t) => ({ file: t.file, title: t.title, priority: t.priority })),
|
|
6322
|
-
in_progress: result.inProgress.map((t) => ({ file: t.file, title: t.title, priority: t.priority })),
|
|
6323
|
-
done: result.done,
|
|
6324
|
-
total: result.total
|
|
6325
|
-
});
|
|
6326
|
-
}
|
|
6327
6302
|
var STATUS_RE, PRIORITY_RE, TITLE_RE;
|
|
6328
6303
|
var init_task_scanner = __esm({
|
|
6329
6304
|
"src/lib/task-scanner.ts"() {
|
|
@@ -6344,15 +6319,15 @@ __export(worker_gate_exports, {
|
|
|
6344
6319
|
tryAcquireBackfillLock: () => tryAcquireBackfillLock,
|
|
6345
6320
|
tryAcquireWorkerSlot: () => tryAcquireWorkerSlot
|
|
6346
6321
|
});
|
|
6347
|
-
import { readdirSync as
|
|
6322
|
+
import { readdirSync as readdirSync5, writeFileSync as writeFileSync9, unlinkSync as unlinkSync8, mkdirSync as mkdirSync9, existsSync as existsSync15 } from "fs";
|
|
6348
6323
|
import path19 from "path";
|
|
6349
6324
|
function tryAcquireWorkerSlot() {
|
|
6350
6325
|
try {
|
|
6351
|
-
|
|
6326
|
+
mkdirSync9(WORKER_PID_DIR, { recursive: true });
|
|
6352
6327
|
const reservationId = `res-${process.pid}-${Date.now()}`;
|
|
6353
6328
|
const reservationPath = path19.join(WORKER_PID_DIR, `${reservationId}.pid`);
|
|
6354
|
-
|
|
6355
|
-
const files =
|
|
6329
|
+
writeFileSync9(reservationPath, String(process.pid));
|
|
6330
|
+
const files = readdirSync5(WORKER_PID_DIR);
|
|
6356
6331
|
let alive = 0;
|
|
6357
6332
|
for (const f of files) {
|
|
6358
6333
|
if (!f.endsWith(".pid")) continue;
|
|
@@ -6391,8 +6366,8 @@ function tryAcquireWorkerSlot() {
|
|
|
6391
6366
|
}
|
|
6392
6367
|
function registerWorkerPid(pid) {
|
|
6393
6368
|
try {
|
|
6394
|
-
|
|
6395
|
-
|
|
6369
|
+
mkdirSync9(WORKER_PID_DIR, { recursive: true });
|
|
6370
|
+
writeFileSync9(path19.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
|
|
6396
6371
|
} catch {
|
|
6397
6372
|
}
|
|
6398
6373
|
}
|
|
@@ -6404,7 +6379,7 @@ function cleanupWorkerPid() {
|
|
|
6404
6379
|
}
|
|
6405
6380
|
function tryAcquireBackfillLock() {
|
|
6406
6381
|
try {
|
|
6407
|
-
|
|
6382
|
+
mkdirSync9(WORKER_PID_DIR, { recursive: true });
|
|
6408
6383
|
if (existsSync15(BACKFILL_LOCK)) {
|
|
6409
6384
|
try {
|
|
6410
6385
|
const pid = parseInt(
|
|
@@ -6421,7 +6396,7 @@ function tryAcquireBackfillLock() {
|
|
|
6421
6396
|
} catch {
|
|
6422
6397
|
}
|
|
6423
6398
|
}
|
|
6424
|
-
|
|
6399
|
+
writeFileSync9(BACKFILL_LOCK, String(process.pid));
|
|
6425
6400
|
return true;
|
|
6426
6401
|
} catch {
|
|
6427
6402
|
return true;
|
|
@@ -6542,7 +6517,7 @@ __export(crdt_sync_exports, {
|
|
|
6542
6517
|
rebuildFromDb: () => rebuildFromDb
|
|
6543
6518
|
});
|
|
6544
6519
|
import * as Y from "yjs";
|
|
6545
|
-
import { readFileSync as readFileSync13, writeFileSync as
|
|
6520
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync10, existsSync as existsSync16, mkdirSync as mkdirSync10, unlinkSync as unlinkSync9 } from "fs";
|
|
6546
6521
|
import path20 from "path";
|
|
6547
6522
|
import { homedir } from "os";
|
|
6548
6523
|
function getStatePath() {
|
|
@@ -6700,9 +6675,9 @@ function persistState() {
|
|
|
6700
6675
|
try {
|
|
6701
6676
|
const sp = getStatePath();
|
|
6702
6677
|
const dir = path20.dirname(sp);
|
|
6703
|
-
if (!existsSync16(dir))
|
|
6678
|
+
if (!existsSync16(dir)) mkdirSync10(dir, { recursive: true });
|
|
6704
6679
|
const state = Y.encodeStateAsUpdate(doc);
|
|
6705
|
-
|
|
6680
|
+
writeFileSync10(sp, Buffer.from(state));
|
|
6706
6681
|
} catch {
|
|
6707
6682
|
}
|
|
6708
6683
|
}
|
|
@@ -6777,7 +6752,7 @@ __export(cloud_sync_exports, {
|
|
|
6777
6752
|
mergeRosterFromRemote: () => mergeRosterFromRemote,
|
|
6778
6753
|
recordRosterDeletion: () => recordRosterDeletion
|
|
6779
6754
|
});
|
|
6780
|
-
import { readFileSync as readFileSync14, writeFileSync as
|
|
6755
|
+
import { readFileSync as readFileSync14, writeFileSync as writeFileSync11, existsSync as existsSync17, readdirSync as readdirSync6, mkdirSync as mkdirSync11, appendFileSync as appendFileSync2, unlinkSync as unlinkSync10, openSync as openSync2, closeSync as closeSync2 } from "fs";
|
|
6781
6756
|
import crypto7 from "crypto";
|
|
6782
6757
|
import path21 from "path";
|
|
6783
6758
|
import { homedir as homedir2 } from "os";
|
|
@@ -6796,7 +6771,7 @@ async function withRosterLock(fn) {
|
|
|
6796
6771
|
try {
|
|
6797
6772
|
const fd = openSync2(ROSTER_LOCK_PATH, "wx");
|
|
6798
6773
|
closeSync2(fd);
|
|
6799
|
-
|
|
6774
|
+
writeFileSync11(ROSTER_LOCK_PATH, String(Date.now()));
|
|
6800
6775
|
} catch (err) {
|
|
6801
6776
|
if (err.code === "EEXIST") {
|
|
6802
6777
|
try {
|
|
@@ -6807,7 +6782,7 @@ async function withRosterLock(fn) {
|
|
|
6807
6782
|
unlinkSync10(ROSTER_LOCK_PATH);
|
|
6808
6783
|
const fd = openSync2(ROSTER_LOCK_PATH, "wx");
|
|
6809
6784
|
closeSync2(fd);
|
|
6810
|
-
|
|
6785
|
+
writeFileSync11(ROSTER_LOCK_PATH, String(Date.now()));
|
|
6811
6786
|
} catch (retryErr) {
|
|
6812
6787
|
if (retryErr instanceof Error && retryErr.message.includes("already in progress")) throw retryErr;
|
|
6813
6788
|
throw new Error("Roster merge already in progress \u2014 another sync is running");
|
|
@@ -7183,7 +7158,7 @@ async function cloudSync(config) {
|
|
|
7183
7158
|
rosterResult.employees = employees.length;
|
|
7184
7159
|
const idDir = path21.join(EXE_AI_DIR, "identity");
|
|
7185
7160
|
if (existsSync17(idDir)) {
|
|
7186
|
-
rosterResult.identities =
|
|
7161
|
+
rosterResult.identities = readdirSync6(idDir).filter((f) => f.endsWith(".md")).length;
|
|
7187
7162
|
}
|
|
7188
7163
|
} catch {
|
|
7189
7164
|
}
|
|
@@ -7209,13 +7184,13 @@ function recordRosterDeletion(name) {
|
|
|
7209
7184
|
} catch {
|
|
7210
7185
|
}
|
|
7211
7186
|
if (!deletions.includes(name)) deletions.push(name);
|
|
7212
|
-
|
|
7187
|
+
writeFileSync11(ROSTER_DELETIONS_PATH, JSON.stringify(deletions));
|
|
7213
7188
|
}
|
|
7214
7189
|
function consumeRosterDeletions() {
|
|
7215
7190
|
try {
|
|
7216
7191
|
if (!existsSync17(ROSTER_DELETIONS_PATH)) return [];
|
|
7217
7192
|
const deletions = JSON.parse(readFileSync14(ROSTER_DELETIONS_PATH, "utf-8"));
|
|
7218
|
-
|
|
7193
|
+
writeFileSync11(ROSTER_DELETIONS_PATH, "[]");
|
|
7219
7194
|
return deletions;
|
|
7220
7195
|
} catch {
|
|
7221
7196
|
return [];
|
|
@@ -7234,7 +7209,7 @@ function buildRosterBlob(paths) {
|
|
|
7234
7209
|
}
|
|
7235
7210
|
const identities = {};
|
|
7236
7211
|
if (existsSync17(identityDir)) {
|
|
7237
|
-
for (const file of
|
|
7212
|
+
for (const file of readdirSync6(identityDir).filter((f) => f.endsWith(".md"))) {
|
|
7238
7213
|
try {
|
|
7239
7214
|
identities[file] = readFileSync14(path21.join(identityDir, file), "utf-8");
|
|
7240
7215
|
} catch {
|
|
@@ -7248,10 +7223,18 @@ function buildRosterBlob(paths) {
|
|
|
7248
7223
|
} catch {
|
|
7249
7224
|
}
|
|
7250
7225
|
}
|
|
7226
|
+
let agentConfig;
|
|
7227
|
+
const agentConfigPath = path21.join(EXE_AI_DIR, "agent-config.json");
|
|
7228
|
+
if (existsSync17(agentConfigPath)) {
|
|
7229
|
+
try {
|
|
7230
|
+
agentConfig = JSON.parse(readFileSync14(agentConfigPath, "utf-8"));
|
|
7231
|
+
} catch {
|
|
7232
|
+
}
|
|
7233
|
+
}
|
|
7251
7234
|
const deletedNames = consumeRosterDeletions();
|
|
7252
|
-
const content = JSON.stringify({ roster, identities, config, deletedNames });
|
|
7235
|
+
const content = JSON.stringify({ roster, identities, config, agentConfig, deletedNames });
|
|
7253
7236
|
const hash = crypto7.createHash("sha256").update(content).digest("hex").slice(0, 16);
|
|
7254
|
-
return { roster, identities, config, deletedNames, version: hash };
|
|
7237
|
+
return { roster, identities, config, agentConfig, deletedNames, version: hash };
|
|
7255
7238
|
}
|
|
7256
7239
|
async function cloudPushRoster(config) {
|
|
7257
7240
|
assertSecureEndpoint(config.endpoint);
|
|
@@ -7330,8 +7313,8 @@ function mergeConfig(remoteConfig, configPath) {
|
|
|
7330
7313
|
}
|
|
7331
7314
|
const merged = { ...remoteConfig, ...local };
|
|
7332
7315
|
const dir = path21.dirname(cfgPath);
|
|
7333
|
-
if (!existsSync17(dir))
|
|
7334
|
-
|
|
7316
|
+
if (!existsSync17(dir)) mkdirSync11(dir, { recursive: true });
|
|
7317
|
+
writeFileSync11(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
|
|
7335
7318
|
}
|
|
7336
7319
|
async function mergeRosterFromRemote(remote, paths) {
|
|
7337
7320
|
return withRosterLock(async () => {
|
|
@@ -7357,7 +7340,7 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
7357
7340
|
) ?? lookupKey;
|
|
7358
7341
|
const remoteIdentity = remote.identities[matchedKey];
|
|
7359
7342
|
if (remoteIdentity) {
|
|
7360
|
-
if (!existsSync17(identityDir))
|
|
7343
|
+
if (!existsSync17(identityDir)) mkdirSync11(identityDir, { recursive: true });
|
|
7361
7344
|
const idPath = path21.join(identityDir, `${remoteEmp.name}.md`);
|
|
7362
7345
|
let localIdentity = null;
|
|
7363
7346
|
try {
|
|
@@ -7365,7 +7348,7 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
7365
7348
|
} catch {
|
|
7366
7349
|
}
|
|
7367
7350
|
if (localIdentity !== remoteIdentity) {
|
|
7368
|
-
|
|
7351
|
+
writeFileSync11(idPath, remoteIdentity, "utf-8");
|
|
7369
7352
|
identitiesUpdated++;
|
|
7370
7353
|
}
|
|
7371
7354
|
}
|
|
@@ -7389,6 +7372,21 @@ async function mergeRosterFromRemote(remote, paths) {
|
|
|
7389
7372
|
} catch {
|
|
7390
7373
|
}
|
|
7391
7374
|
}
|
|
7375
|
+
if (remote.agentConfig && Object.keys(remote.agentConfig).length > 0) {
|
|
7376
|
+
try {
|
|
7377
|
+
const agentConfigPath = path21.join(EXE_AI_DIR, "agent-config.json");
|
|
7378
|
+
let local = {};
|
|
7379
|
+
if (existsSync17(agentConfigPath)) {
|
|
7380
|
+
try {
|
|
7381
|
+
local = JSON.parse(readFileSync14(agentConfigPath, "utf-8"));
|
|
7382
|
+
} catch {
|
|
7383
|
+
}
|
|
7384
|
+
}
|
|
7385
|
+
const merged = { ...remote.agentConfig, ...local };
|
|
7386
|
+
writeFileSync11(agentConfigPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
7387
|
+
} catch {
|
|
7388
|
+
}
|
|
7389
|
+
}
|
|
7392
7390
|
return { added, identitiesUpdated };
|
|
7393
7391
|
});
|
|
7394
7392
|
}
|
|
@@ -7838,7 +7836,7 @@ __export(schedules_exports, {
|
|
|
7838
7836
|
parseHumanCron: () => parseHumanCron
|
|
7839
7837
|
});
|
|
7840
7838
|
import crypto8 from "crypto";
|
|
7841
|
-
import { execSync as
|
|
7839
|
+
import { execSync as execSync9 } from "child_process";
|
|
7842
7840
|
async function ensureDb() {
|
|
7843
7841
|
if (!isInitialized()) {
|
|
7844
7842
|
await initStore();
|
|
@@ -7981,7 +7979,7 @@ function addToCrontab(id, cron, prompt, projectDir) {
|
|
|
7981
7979
|
const cwd = projectDir ? `cd ${JSON.stringify(projectDir)} && ` : "";
|
|
7982
7980
|
const escapedPrompt = prompt.replace(/"/g, '\\"');
|
|
7983
7981
|
const entry = `${cron} ${cwd}claude -p --dangerously-skip-permissions "${escapedPrompt}" # exe-schedule:${id}`;
|
|
7984
|
-
|
|
7982
|
+
execSync9(
|
|
7985
7983
|
`(crontab -l 2>/dev/null; echo ${JSON.stringify(entry)}) | crontab -`,
|
|
7986
7984
|
{ timeout: 5e3, stdio: "ignore" }
|
|
7987
7985
|
);
|
|
@@ -7990,7 +7988,7 @@ function addToCrontab(id, cron, prompt, projectDir) {
|
|
|
7990
7988
|
}
|
|
7991
7989
|
function removeFromCrontab(id) {
|
|
7992
7990
|
try {
|
|
7993
|
-
|
|
7991
|
+
execSync9(
|
|
7994
7992
|
`crontab -l 2>/dev/null | grep -v "exe-schedule:${id}" | crontab -`,
|
|
7995
7993
|
{ timeout: 5e3, stdio: "ignore" }
|
|
7996
7994
|
);
|
|
@@ -8009,8 +8007,8 @@ var init_schedules = __esm({
|
|
|
8009
8007
|
init_employees();
|
|
8010
8008
|
import path22 from "path";
|
|
8011
8009
|
import { mkdir as mkdir5, writeFile as writeFile6 } from "fs/promises";
|
|
8012
|
-
import { existsSync as existsSync18, readFileSync as readFileSync15, readdirSync as
|
|
8013
|
-
import
|
|
8010
|
+
import { existsSync as existsSync18, readFileSync as readFileSync15, readdirSync as readdirSync7, unlinkSync as unlinkSync11 } from "fs";
|
|
8011
|
+
import os9 from "os";
|
|
8014
8012
|
|
|
8015
8013
|
// src/lib/employee-templates.ts
|
|
8016
8014
|
init_global_procedures();
|
|
@@ -8486,24 +8484,24 @@ init_notifications();
|
|
|
8486
8484
|
|
|
8487
8485
|
// src/adapters/claude/active-agent.ts
|
|
8488
8486
|
init_config();
|
|
8489
|
-
import { readFileSync as
|
|
8487
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync8, mkdirSync as mkdirSync8, unlinkSync as unlinkSync7, readdirSync as readdirSync4 } from "fs";
|
|
8490
8488
|
import { execSync as execSync8 } from "child_process";
|
|
8491
|
-
import
|
|
8489
|
+
import path18 from "path";
|
|
8492
8490
|
|
|
8493
8491
|
// src/adapters/claude/session-key.ts
|
|
8494
8492
|
init_session_key();
|
|
8495
8493
|
|
|
8496
8494
|
// src/adapters/claude/active-agent.ts
|
|
8497
8495
|
init_employees();
|
|
8498
|
-
var CACHE_DIR =
|
|
8496
|
+
var CACHE_DIR = path18.join(EXE_AI_DIR, "session-cache");
|
|
8499
8497
|
var STALE_MS = 24 * 60 * 60 * 1e3;
|
|
8500
8498
|
function getMarkerPath() {
|
|
8501
|
-
return
|
|
8499
|
+
return path18.join(CACHE_DIR, `active-agent-${getSessionKey()}.json`);
|
|
8502
8500
|
}
|
|
8503
8501
|
function writeActiveAgent(agentId, agentRole) {
|
|
8504
8502
|
try {
|
|
8505
|
-
|
|
8506
|
-
|
|
8503
|
+
mkdirSync8(CACHE_DIR, { recursive: true });
|
|
8504
|
+
writeFileSync8(
|
|
8507
8505
|
getMarkerPath(),
|
|
8508
8506
|
JSON.stringify({ agentId, agentRole, startedAt: (/* @__PURE__ */ new Date()).toISOString() })
|
|
8509
8507
|
);
|
|
@@ -8513,11 +8511,11 @@ function writeActiveAgent(agentId, agentRole) {
|
|
|
8513
8511
|
function cleanupSessionMarkers() {
|
|
8514
8512
|
const key = getSessionKey();
|
|
8515
8513
|
try {
|
|
8516
|
-
unlinkSync7(
|
|
8514
|
+
unlinkSync7(path18.join(CACHE_DIR, `active-agent-${key}.json`));
|
|
8517
8515
|
} catch {
|
|
8518
8516
|
}
|
|
8519
8517
|
try {
|
|
8520
|
-
unlinkSync7(
|
|
8518
|
+
unlinkSync7(path18.join(CACHE_DIR, "active-agent-undefined.json"));
|
|
8521
8519
|
} catch {
|
|
8522
8520
|
}
|
|
8523
8521
|
}
|
|
@@ -8599,18 +8597,18 @@ async function boot(options) {
|
|
|
8599
8597
|
} catch {
|
|
8600
8598
|
}
|
|
8601
8599
|
try {
|
|
8602
|
-
const { readdirSync:
|
|
8600
|
+
const { readdirSync: readdirSync8, readFileSync: readFs } = await import("fs");
|
|
8603
8601
|
const { STATUS_RE: STATUS_RE2, PRIORITY_RE: PRIORITY_RE2, TITLE_RE: TITLE_RE2 } = await Promise.resolve().then(() => (init_task_scanner(), task_scanner_exports));
|
|
8604
8602
|
const { getProjectName: getProjectName2 } = await Promise.resolve().then(() => (init_project_name(), project_name_exports));
|
|
8605
8603
|
const exeDir = "exe";
|
|
8606
|
-
const entries =
|
|
8604
|
+
const entries = readdirSync8(exeDir, { withFileTypes: true });
|
|
8607
8605
|
const employeeDirs = entries.filter((e) => e.isDirectory() && !["output", "research"].includes(e.name));
|
|
8608
8606
|
for (const dir of employeeDirs) {
|
|
8609
8607
|
const employee = dir.name;
|
|
8610
8608
|
const taskDir = path22.join(exeDir, employee);
|
|
8611
8609
|
let files;
|
|
8612
8610
|
try {
|
|
8613
|
-
files =
|
|
8611
|
+
files = readdirSync8(taskDir).filter((f) => f.endsWith(".md"));
|
|
8614
8612
|
} catch {
|
|
8615
8613
|
continue;
|
|
8616
8614
|
}
|
|
@@ -8706,7 +8704,7 @@ async function boot(options) {
|
|
|
8706
8704
|
for (const reviewDirName of /* @__PURE__ */ new Set(["exe", coordinatorName])) {
|
|
8707
8705
|
const reviewDir = path22.join(process.cwd(), "exe", reviewDirName);
|
|
8708
8706
|
if (existsSync18(reviewDir)) {
|
|
8709
|
-
for (const f of
|
|
8707
|
+
for (const f of readdirSync7(reviewDir)) {
|
|
8710
8708
|
if (f.startsWith("review-") && f.endsWith(".md")) {
|
|
8711
8709
|
try {
|
|
8712
8710
|
unlinkSync11(path22.join(reviewDir, f));
|
|
@@ -8759,8 +8757,8 @@ async function boot(options) {
|
|
|
8759
8757
|
if (existsSync18(filePath)) {
|
|
8760
8758
|
let content = readFileSync15(filePath, "utf8");
|
|
8761
8759
|
content = content.replace(/\*\*Status:\*\* needs_review/, "**Status:** done");
|
|
8762
|
-
const { writeFileSync:
|
|
8763
|
-
|
|
8760
|
+
const { writeFileSync: writeFileSync12 } = await import("fs");
|
|
8761
|
+
writeFileSync12(filePath, content);
|
|
8764
8762
|
}
|
|
8765
8763
|
} catch {
|
|
8766
8764
|
}
|
|
@@ -9205,8 +9203,8 @@ async function boot(options) {
|
|
|
9205
9203
|
updatedAt: String(row.updated_at)
|
|
9206
9204
|
}));
|
|
9207
9205
|
try {
|
|
9208
|
-
const { execSync:
|
|
9209
|
-
const gitLog =
|
|
9206
|
+
const { execSync: execSync10 } = await import("child_process");
|
|
9207
|
+
const gitLog = execSync10(
|
|
9210
9208
|
"git log --oneline -10 --grep='Co-Authored-By: Claude' --grep='task(' --all-match 2>/dev/null || git log --oneline -5 --grep='Co-Authored-By: Claude' 2>/dev/null || git log --oneline -5 --grep='^task(' 2>/dev/null",
|
|
9211
9209
|
{
|
|
9212
9210
|
encoding: "utf8",
|
|
@@ -9230,7 +9228,7 @@ async function boot(options) {
|
|
|
9230
9228
|
]);
|
|
9231
9229
|
try {
|
|
9232
9230
|
const configPath = path22.join(
|
|
9233
|
-
process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path22.join(
|
|
9231
|
+
process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path22.join(os9.homedir(), ".exe-os"),
|
|
9234
9232
|
"config.json"
|
|
9235
9233
|
);
|
|
9236
9234
|
if (existsSync18(configPath)) {
|
|
@@ -9429,7 +9427,7 @@ ${brief}`;
|
|
|
9429
9427
|
console.log(brief);
|
|
9430
9428
|
try {
|
|
9431
9429
|
const configPath2 = path22.join(
|
|
9432
|
-
process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path22.join(
|
|
9430
|
+
process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? path22.join(os9.homedir(), ".exe-os"),
|
|
9433
9431
|
"config.json"
|
|
9434
9432
|
);
|
|
9435
9433
|
if (existsSync18(configPath2)) {
|
|
@@ -9493,11 +9491,11 @@ async function updateIdleKillSuspectStreak(client, killsToday, liveSessions, tod
|
|
|
9493
9491
|
}
|
|
9494
9492
|
function runSplash() {
|
|
9495
9493
|
try {
|
|
9496
|
-
const { execSync:
|
|
9494
|
+
const { execSync: execSync10 } = __require("child_process");
|
|
9497
9495
|
const { loadConfigSync: loadConfigSync2 } = (init_config(), __toCommonJS(config_exports));
|
|
9498
9496
|
const config = loadConfigSync2();
|
|
9499
9497
|
if (!config.splashEffect) return;
|
|
9500
|
-
|
|
9498
|
+
execSync10(
|
|
9501
9499
|
'echo "EXE OS" | python3 -m terminaltexteffects decrypt --typing-speed 2 --ciphertext-colors 6B4C9A F5D76E --final-gradient-stops F5D76E F0EDE8 --final-gradient-direction vertical',
|
|
9502
9500
|
{ stdio: "inherit", timeout: 5e3 }
|
|
9503
9501
|
);
|