@askexenow/exe-os 0.9.7 → 0.9.9
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/backfill-conversations.js +953 -105
- package/dist/bin/backfill-responses.js +952 -104
- package/dist/bin/backfill-vectors.js +956 -108
- package/dist/bin/cleanup-stale-review-tasks.js +802 -58
- package/dist/bin/cli.js +2292 -1070
- package/dist/bin/exe-agent-config.js +157 -101
- package/dist/bin/exe-agent.js +55 -29
- package/dist/bin/exe-assign.js +940 -92
- package/dist/bin/exe-boot.js +1424 -442
- package/dist/bin/exe-call.js +240 -141
- package/dist/bin/exe-cloud.js +198 -70
- package/dist/bin/exe-dispatch.js +951 -192
- package/dist/bin/exe-doctor.js +791 -51
- package/dist/bin/exe-export-behaviors.js +790 -42
- package/dist/bin/exe-forget.js +771 -31
- package/dist/bin/exe-gateway.js +1592 -521
- package/dist/bin/exe-heartbeat.js +850 -109
- package/dist/bin/exe-kill.js +783 -35
- package/dist/bin/exe-launch-agent.js +1030 -107
- package/dist/bin/exe-link.js +916 -110
- package/dist/bin/exe-new-employee.js +526 -217
- package/dist/bin/exe-pending-messages.js +1046 -62
- package/dist/bin/exe-pending-notifications.js +1318 -111
- package/dist/bin/exe-pending-reviews.js +1040 -72
- package/dist/bin/exe-rename.js +772 -59
- package/dist/bin/exe-review.js +772 -32
- package/dist/bin/exe-search.js +982 -128
- package/dist/bin/exe-session-cleanup.js +1180 -306
- package/dist/bin/exe-settings.js +185 -105
- package/dist/bin/exe-start-codex.js +886 -132
- package/dist/bin/exe-start-opencode.js +873 -119
- package/dist/bin/exe-status.js +803 -59
- package/dist/bin/exe-team.js +772 -32
- package/dist/bin/git-sweep.js +1046 -223
- package/dist/bin/graph-backfill.js +779 -31
- package/dist/bin/graph-export.js +785 -37
- package/dist/bin/install.js +632 -200
- package/dist/bin/scan-tasks.js +1055 -232
- package/dist/bin/setup.js +1419 -320
- package/dist/bin/shard-migrate.js +783 -35
- package/dist/bin/update.js +138 -49
- package/dist/bin/wiki-sync.js +782 -34
- package/dist/gateway/index.js +1444 -449
- package/dist/hooks/bug-report-worker.js +1141 -269
- package/dist/hooks/codex-stop-task-finalizer.js +4678 -0
- package/dist/hooks/commit-complete.js +1044 -221
- package/dist/hooks/error-recall.js +989 -135
- package/dist/hooks/exe-heartbeat-hook.js +99 -75
- package/dist/hooks/ingest-worker.js +4176 -3226
- package/dist/hooks/ingest.js +920 -168
- package/dist/hooks/instructions-loaded.js +874 -70
- package/dist/hooks/notification.js +860 -56
- package/dist/hooks/post-compact.js +881 -73
- package/dist/hooks/pre-compact.js +1050 -227
- package/dist/hooks/pre-tool-use.js +1084 -159
- package/dist/hooks/prompt-ingest-worker.js +1089 -164
- package/dist/hooks/prompt-submit.js +1469 -515
- package/dist/hooks/response-ingest-worker.js +1104 -179
- package/dist/hooks/session-end.js +1085 -251
- package/dist/hooks/session-start.js +1241 -231
- package/dist/hooks/stop.js +935 -109
- package/dist/hooks/subagent-stop.js +881 -73
- package/dist/hooks/summary-worker.js +1323 -307
- package/dist/index.js +1449 -452
- package/dist/lib/agent-config.js +28 -6
- package/dist/lib/cloud-sync.js +909 -115
- package/dist/lib/config.js +30 -10
- package/dist/lib/consolidation.js +42 -9
- package/dist/lib/database.js +739 -33
- package/dist/lib/db-daemon-client.js +73 -19
- package/dist/lib/db.js +2359 -0
- package/dist/lib/device-registry.js +760 -47
- package/dist/lib/embedder.js +201 -73
- package/dist/lib/employee-templates.js +30 -4
- package/dist/lib/employees.js +290 -86
- package/dist/lib/exe-daemon-client.js +187 -83
- package/dist/lib/exe-daemon.js +1696 -616
- package/dist/lib/hybrid-search.js +982 -128
- package/dist/lib/identity.js +43 -13
- package/dist/lib/license.js +133 -48
- package/dist/lib/messaging.js +167 -80
- package/dist/lib/reminders.js +35 -5
- package/dist/lib/schedules.js +772 -32
- package/dist/lib/skill-learning.js +54 -7
- package/dist/lib/store.js +779 -31
- package/dist/lib/task-router.js +94 -73
- package/dist/lib/tasks.js +298 -225
- package/dist/lib/tmux-routing.js +246 -172
- package/dist/lib/token-spend.js +52 -14
- package/dist/mcp/server.js +2893 -850
- package/dist/mcp/tools/complete-reminder.js +35 -5
- package/dist/mcp/tools/create-reminder.js +35 -5
- package/dist/mcp/tools/create-task.js +507 -323
- package/dist/mcp/tools/deactivate-behavior.js +40 -10
- package/dist/mcp/tools/list-reminders.js +35 -5
- package/dist/mcp/tools/list-tasks.js +277 -104
- package/dist/mcp/tools/send-message.js +129 -56
- package/dist/mcp/tools/update-task.js +1864 -188
- package/dist/runtime/index.js +1083 -259
- package/dist/tui/App.js +1501 -434
- package/package.json +3 -2
package/dist/bin/install.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
5
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
6
|
+
}) : x)(function(x) {
|
|
7
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
8
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
9
|
+
});
|
|
4
10
|
var __esm = (fn, res) => function __init() {
|
|
5
11
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
12
|
};
|
|
@@ -9,9 +15,34 @@ var __export = (target, all) => {
|
|
|
9
15
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
16
|
};
|
|
11
17
|
|
|
18
|
+
// src/lib/secure-files.ts
|
|
19
|
+
import { chmodSync, existsSync, mkdirSync } from "fs";
|
|
20
|
+
import { chmod, mkdir } from "fs/promises";
|
|
21
|
+
function ensurePrivateDirSync(dirPath) {
|
|
22
|
+
mkdirSync(dirPath, { recursive: true, mode: PRIVATE_DIR_MODE });
|
|
23
|
+
try {
|
|
24
|
+
chmodSync(dirPath, PRIVATE_DIR_MODE);
|
|
25
|
+
} catch {
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function enforcePrivateFileSync(filePath) {
|
|
29
|
+
try {
|
|
30
|
+
if (existsSync(filePath)) chmodSync(filePath, PRIVATE_FILE_MODE);
|
|
31
|
+
} catch {
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
var PRIVATE_DIR_MODE, PRIVATE_FILE_MODE;
|
|
35
|
+
var init_secure_files = __esm({
|
|
36
|
+
"src/lib/secure-files.ts"() {
|
|
37
|
+
"use strict";
|
|
38
|
+
PRIVATE_DIR_MODE = 448;
|
|
39
|
+
PRIVATE_FILE_MODE = 384;
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
12
43
|
// src/lib/config.ts
|
|
13
|
-
import { readFile, writeFile
|
|
14
|
-
import { readFileSync, existsSync, renameSync } from "fs";
|
|
44
|
+
import { readFile, writeFile } from "fs/promises";
|
|
45
|
+
import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
|
|
15
46
|
import path from "path";
|
|
16
47
|
import os from "os";
|
|
17
48
|
function resolveDataDir() {
|
|
@@ -19,7 +50,7 @@ function resolveDataDir() {
|
|
|
19
50
|
if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
|
|
20
51
|
const newDir = path.join(os.homedir(), ".exe-os");
|
|
21
52
|
const legacyDir = path.join(os.homedir(), ".exe-mem");
|
|
22
|
-
if (!
|
|
53
|
+
if (!existsSync2(newDir) && existsSync2(legacyDir)) {
|
|
23
54
|
try {
|
|
24
55
|
renameSync(legacyDir, newDir);
|
|
25
56
|
process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
|
|
@@ -34,6 +65,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
|
|
|
34
65
|
var init_config = __esm({
|
|
35
66
|
"src/lib/config.ts"() {
|
|
36
67
|
"use strict";
|
|
68
|
+
init_secure_files();
|
|
37
69
|
EXE_AI_DIR = resolveDataDir();
|
|
38
70
|
DB_PATH = path.join(EXE_AI_DIR, "memories.db");
|
|
39
71
|
MODELS_DIR = path.join(EXE_AI_DIR, "models");
|
|
@@ -100,6 +132,120 @@ var init_config = __esm({
|
|
|
100
132
|
}
|
|
101
133
|
});
|
|
102
134
|
|
|
135
|
+
// src/lib/runtime-table.ts
|
|
136
|
+
var RUNTIME_TABLE, DEFAULT_RUNTIME;
|
|
137
|
+
var init_runtime_table = __esm({
|
|
138
|
+
"src/lib/runtime-table.ts"() {
|
|
139
|
+
"use strict";
|
|
140
|
+
RUNTIME_TABLE = {
|
|
141
|
+
codex: {
|
|
142
|
+
binary: "codex",
|
|
143
|
+
launchMode: "interactive",
|
|
144
|
+
autoApproveFlag: "--dangerously-bypass-approvals-and-sandbox",
|
|
145
|
+
inlineFlag: "--no-alt-screen",
|
|
146
|
+
apiKeyEnv: "OPENAI_API_KEY",
|
|
147
|
+
defaultModel: "gpt-5.4"
|
|
148
|
+
},
|
|
149
|
+
opencode: {
|
|
150
|
+
binary: "opencode",
|
|
151
|
+
launchMode: "exec",
|
|
152
|
+
autoApproveFlag: "--dangerously-skip-permissions",
|
|
153
|
+
inlineFlag: "",
|
|
154
|
+
apiKeyEnv: "ANTHROPIC_API_KEY",
|
|
155
|
+
defaultModel: "anthropic/claude-sonnet-4-6"
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
DEFAULT_RUNTIME = "claude";
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// src/lib/agent-config.ts
|
|
163
|
+
var agent_config_exports = {};
|
|
164
|
+
__export(agent_config_exports, {
|
|
165
|
+
AGENT_CONFIG_PATH: () => AGENT_CONFIG_PATH,
|
|
166
|
+
DEFAULT_MODELS: () => DEFAULT_MODELS,
|
|
167
|
+
KNOWN_RUNTIMES: () => KNOWN_RUNTIMES,
|
|
168
|
+
RUNTIME_LABELS: () => RUNTIME_LABELS,
|
|
169
|
+
clearAgentRuntime: () => clearAgentRuntime,
|
|
170
|
+
getAgentRuntime: () => getAgentRuntime,
|
|
171
|
+
loadAgentConfig: () => loadAgentConfig,
|
|
172
|
+
saveAgentConfig: () => saveAgentConfig,
|
|
173
|
+
setAgentRuntime: () => setAgentRuntime
|
|
174
|
+
});
|
|
175
|
+
import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3 } from "fs";
|
|
176
|
+
import path2 from "path";
|
|
177
|
+
function loadAgentConfig() {
|
|
178
|
+
if (!existsSync3(AGENT_CONFIG_PATH)) return {};
|
|
179
|
+
try {
|
|
180
|
+
return JSON.parse(readFileSync2(AGENT_CONFIG_PATH, "utf-8"));
|
|
181
|
+
} catch {
|
|
182
|
+
return {};
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function saveAgentConfig(config) {
|
|
186
|
+
const dir = path2.dirname(AGENT_CONFIG_PATH);
|
|
187
|
+
ensurePrivateDirSync(dir);
|
|
188
|
+
writeFileSync(AGENT_CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
189
|
+
enforcePrivateFileSync(AGENT_CONFIG_PATH);
|
|
190
|
+
}
|
|
191
|
+
function getAgentRuntime(agentId) {
|
|
192
|
+
const config = loadAgentConfig();
|
|
193
|
+
const entry = config[agentId];
|
|
194
|
+
if (entry) return entry;
|
|
195
|
+
const orgDefault = config["default"];
|
|
196
|
+
if (orgDefault) return orgDefault;
|
|
197
|
+
return { runtime: DEFAULT_RUNTIME, model: DEFAULT_MODELS[DEFAULT_RUNTIME] };
|
|
198
|
+
}
|
|
199
|
+
function setAgentRuntime(agentId, runtime, model) {
|
|
200
|
+
const knownModels = KNOWN_RUNTIMES[runtime];
|
|
201
|
+
if (!knownModels) {
|
|
202
|
+
return {
|
|
203
|
+
ok: false,
|
|
204
|
+
error: `Unknown runtime "${runtime}". Valid: ${Object.keys(KNOWN_RUNTIMES).join(", ")}`
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
if (!knownModels.includes(model)) {
|
|
208
|
+
return {
|
|
209
|
+
ok: false,
|
|
210
|
+
error: `Unknown model "${model}" for runtime "${runtime}". Valid: ${knownModels.join(", ")}`
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
const config = loadAgentConfig();
|
|
214
|
+
config[agentId] = { runtime, model };
|
|
215
|
+
saveAgentConfig(config);
|
|
216
|
+
return { ok: true };
|
|
217
|
+
}
|
|
218
|
+
function clearAgentRuntime(agentId) {
|
|
219
|
+
const config = loadAgentConfig();
|
|
220
|
+
delete config[agentId];
|
|
221
|
+
saveAgentConfig(config);
|
|
222
|
+
}
|
|
223
|
+
var AGENT_CONFIG_PATH, KNOWN_RUNTIMES, RUNTIME_LABELS, DEFAULT_MODELS;
|
|
224
|
+
var init_agent_config = __esm({
|
|
225
|
+
"src/lib/agent-config.ts"() {
|
|
226
|
+
"use strict";
|
|
227
|
+
init_config();
|
|
228
|
+
init_runtime_table();
|
|
229
|
+
init_secure_files();
|
|
230
|
+
AGENT_CONFIG_PATH = path2.join(EXE_AI_DIR, "agent-config.json");
|
|
231
|
+
KNOWN_RUNTIMES = {
|
|
232
|
+
claude: ["claude-opus-4", "claude-sonnet-4", "claude-haiku-4.5"],
|
|
233
|
+
codex: ["gpt-5.4", "gpt-5.5", "gpt-5.3-codex-spark", "o3", "o4-mini"],
|
|
234
|
+
opencode: ["anthropic/claude-sonnet-4-6", "openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r3", "minimax/minimax-m2.5"]
|
|
235
|
+
};
|
|
236
|
+
RUNTIME_LABELS = {
|
|
237
|
+
claude: "Claude Code (Anthropic)",
|
|
238
|
+
codex: "Codex (OpenAI)",
|
|
239
|
+
opencode: "OpenCode (open source)"
|
|
240
|
+
};
|
|
241
|
+
DEFAULT_MODELS = {
|
|
242
|
+
claude: "claude-opus-4",
|
|
243
|
+
codex: RUNTIME_TABLE.codex?.defaultModel ?? "gpt-5.4",
|
|
244
|
+
opencode: RUNTIME_TABLE.opencode?.defaultModel ?? "anthropic/claude-sonnet-4-6"
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
|
|
103
249
|
// src/lib/employees.ts
|
|
104
250
|
var employees_exports = {};
|
|
105
251
|
__export(employees_exports, {
|
|
@@ -115,6 +261,7 @@ __export(employees_exports, {
|
|
|
115
261
|
getEmployeeByRole: () => getEmployeeByRole,
|
|
116
262
|
getEmployeeNamesByRole: () => getEmployeeNamesByRole,
|
|
117
263
|
hasRole: () => hasRole,
|
|
264
|
+
hireEmployee: () => hireEmployee,
|
|
118
265
|
isCoordinatorName: () => isCoordinatorName,
|
|
119
266
|
isCoordinatorRole: () => isCoordinatorRole,
|
|
120
267
|
isMultiInstance: () => isMultiInstance,
|
|
@@ -127,9 +274,9 @@ __export(employees_exports, {
|
|
|
127
274
|
validateEmployeeName: () => validateEmployeeName
|
|
128
275
|
});
|
|
129
276
|
import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
130
|
-
import { existsSync as
|
|
277
|
+
import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync3, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync2 } from "fs";
|
|
131
278
|
import { execSync } from "child_process";
|
|
132
|
-
import
|
|
279
|
+
import path3 from "path";
|
|
133
280
|
import os2 from "os";
|
|
134
281
|
function normalizeRole(role) {
|
|
135
282
|
return (role ?? "").trim().toLowerCase();
|
|
@@ -166,7 +313,7 @@ function validateEmployeeName(name) {
|
|
|
166
313
|
return { valid: true };
|
|
167
314
|
}
|
|
168
315
|
async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
169
|
-
if (!
|
|
316
|
+
if (!existsSync4(employeesPath)) {
|
|
170
317
|
return [];
|
|
171
318
|
}
|
|
172
319
|
const raw = await readFile2(employeesPath, "utf-8");
|
|
@@ -177,13 +324,13 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
|
|
|
177
324
|
}
|
|
178
325
|
}
|
|
179
326
|
async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
|
|
180
|
-
await mkdir2(
|
|
327
|
+
await mkdir2(path3.dirname(employeesPath), { recursive: true });
|
|
181
328
|
await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
|
|
182
329
|
}
|
|
183
330
|
function loadEmployeesSync(employeesPath = EMPLOYEES_PATH) {
|
|
184
|
-
if (!
|
|
331
|
+
if (!existsSync4(employeesPath)) return [];
|
|
185
332
|
try {
|
|
186
|
-
return JSON.parse(
|
|
333
|
+
return JSON.parse(readFileSync3(employeesPath, "utf-8"));
|
|
187
334
|
} catch {
|
|
188
335
|
return [];
|
|
189
336
|
}
|
|
@@ -225,6 +372,52 @@ function addEmployee(employees, employee) {
|
|
|
225
372
|
}
|
|
226
373
|
return [...employees, normalized];
|
|
227
374
|
}
|
|
375
|
+
function appendToCoordinatorTeam(employee) {
|
|
376
|
+
const coordinator = getCoordinatorEmployee(loadEmployeesSync());
|
|
377
|
+
if (!coordinator) return;
|
|
378
|
+
const idPath = path3.join(IDENTITY_DIR, `${coordinator.name}.md`);
|
|
379
|
+
if (!existsSync4(idPath)) return;
|
|
380
|
+
const content = readFileSync3(idPath, "utf-8");
|
|
381
|
+
if (content.includes(`**${capitalize(employee.name)}`)) return;
|
|
382
|
+
const teamMatch = content.match(TEAM_SECTION_RE);
|
|
383
|
+
if (!teamMatch || teamMatch.index === void 0) return;
|
|
384
|
+
const afterTeam = content.slice(teamMatch.index + teamMatch[0].length);
|
|
385
|
+
const nextHeading = afterTeam.match(/\n## /);
|
|
386
|
+
const entry = `
|
|
387
|
+
**${capitalize(employee.name)} (${employee.role}):** Newly hired. Update this description as the role develops.
|
|
388
|
+
`;
|
|
389
|
+
let updated;
|
|
390
|
+
if (nextHeading && nextHeading.index !== void 0) {
|
|
391
|
+
const insertAt = teamMatch.index + teamMatch[0].length + nextHeading.index;
|
|
392
|
+
updated = content.slice(0, insertAt) + entry + content.slice(insertAt);
|
|
393
|
+
} else {
|
|
394
|
+
updated = content.trimEnd() + "\n" + entry;
|
|
395
|
+
}
|
|
396
|
+
writeFileSync2(idPath, updated, "utf-8");
|
|
397
|
+
}
|
|
398
|
+
function capitalize(s) {
|
|
399
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
400
|
+
}
|
|
401
|
+
async function hireEmployee(employee) {
|
|
402
|
+
const employees = await loadEmployees();
|
|
403
|
+
const updated = addEmployee(employees, employee);
|
|
404
|
+
await saveEmployees(updated);
|
|
405
|
+
try {
|
|
406
|
+
appendToCoordinatorTeam(employee);
|
|
407
|
+
} catch {
|
|
408
|
+
}
|
|
409
|
+
try {
|
|
410
|
+
const { loadAgentConfig: loadAgentConfig2, saveAgentConfig: saveAgentConfig2 } = await Promise.resolve().then(() => (init_agent_config(), agent_config_exports));
|
|
411
|
+
const config = loadAgentConfig2();
|
|
412
|
+
const name = employee.name.toLowerCase();
|
|
413
|
+
if (!config[name] && config["default"]) {
|
|
414
|
+
config[name] = { ...config["default"] };
|
|
415
|
+
saveAgentConfig2(config);
|
|
416
|
+
}
|
|
417
|
+
} catch {
|
|
418
|
+
}
|
|
419
|
+
return updated;
|
|
420
|
+
}
|
|
228
421
|
async function normalizeRosterCase(rosterPath) {
|
|
229
422
|
const employees = await loadEmployees(rosterPath);
|
|
230
423
|
let changed = false;
|
|
@@ -234,14 +427,14 @@ async function normalizeRosterCase(rosterPath) {
|
|
|
234
427
|
emp.name = emp.name.toLowerCase();
|
|
235
428
|
changed = true;
|
|
236
429
|
try {
|
|
237
|
-
const identityDir =
|
|
238
|
-
const oldPath =
|
|
239
|
-
const newPath =
|
|
240
|
-
if (
|
|
430
|
+
const identityDir = path3.join(os2.homedir(), ".exe-os", "identity");
|
|
431
|
+
const oldPath = path3.join(identityDir, `${oldName}.md`);
|
|
432
|
+
const newPath = path3.join(identityDir, `${emp.name}.md`);
|
|
433
|
+
if (existsSync4(oldPath) && !existsSync4(newPath)) {
|
|
241
434
|
renameSync2(oldPath, newPath);
|
|
242
|
-
} else if (
|
|
243
|
-
const content =
|
|
244
|
-
|
|
435
|
+
} else if (existsSync4(oldPath) && oldPath !== newPath) {
|
|
436
|
+
const content = readFileSync3(oldPath, "utf-8");
|
|
437
|
+
writeFileSync2(newPath, content, "utf-8");
|
|
245
438
|
if (oldPath.toLowerCase() !== newPath.toLowerCase()) {
|
|
246
439
|
unlinkSync(oldPath);
|
|
247
440
|
}
|
|
@@ -271,7 +464,7 @@ function registerBinSymlinks(name) {
|
|
|
271
464
|
errors.push("Could not find 'exe-os' in PATH");
|
|
272
465
|
return { created, skipped, errors };
|
|
273
466
|
}
|
|
274
|
-
const binDir =
|
|
467
|
+
const binDir = path3.dirname(exeBinPath);
|
|
275
468
|
let target;
|
|
276
469
|
try {
|
|
277
470
|
target = readlinkSync(exeBinPath);
|
|
@@ -281,8 +474,8 @@ function registerBinSymlinks(name) {
|
|
|
281
474
|
}
|
|
282
475
|
for (const suffix of ["", "-opencode"]) {
|
|
283
476
|
const linkName = `${name}${suffix}`;
|
|
284
|
-
const linkPath =
|
|
285
|
-
if (
|
|
477
|
+
const linkPath = path3.join(binDir, linkName);
|
|
478
|
+
if (existsSync4(linkPath)) {
|
|
286
479
|
skipped.push(linkName);
|
|
287
480
|
continue;
|
|
288
481
|
}
|
|
@@ -295,51 +488,44 @@ function registerBinSymlinks(name) {
|
|
|
295
488
|
}
|
|
296
489
|
return { created, skipped, errors };
|
|
297
490
|
}
|
|
298
|
-
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES;
|
|
491
|
+
var EMPLOYEES_PATH, DEFAULT_COORDINATOR_TEMPLATE_NAME, COORDINATOR_ROLE, MULTI_INSTANCE_ROLES, IDENTITY_DIR, TEAM_SECTION_RE;
|
|
299
492
|
var init_employees = __esm({
|
|
300
493
|
"src/lib/employees.ts"() {
|
|
301
494
|
"use strict";
|
|
302
495
|
init_config();
|
|
303
|
-
EMPLOYEES_PATH =
|
|
496
|
+
EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
|
|
304
497
|
DEFAULT_COORDINATOR_TEMPLATE_NAME = "exe";
|
|
305
498
|
COORDINATOR_ROLE = "COO";
|
|
306
499
|
MULTI_INSTANCE_ROLES = /* @__PURE__ */ new Set(["principal engineer", "content production specialist", "staff code reviewer"]);
|
|
500
|
+
IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
|
|
501
|
+
TEAM_SECTION_RE = /^## Team\b.*$/m;
|
|
307
502
|
}
|
|
308
503
|
});
|
|
309
504
|
|
|
310
|
-
// src/adapters/claude/installer.ts
|
|
311
|
-
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3, readdir } from "fs/promises";
|
|
312
|
-
import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3, copyFileSync, mkdirSync as mkdirSync3 } from "fs";
|
|
313
|
-
import path5 from "path";
|
|
314
|
-
import os5 from "os";
|
|
315
|
-
import { execSync as execSync2 } from "child_process";
|
|
316
|
-
import { fileURLToPath } from "url";
|
|
317
|
-
|
|
318
505
|
// src/lib/agent-symlinks.ts
|
|
319
|
-
init_employees();
|
|
320
506
|
import os3 from "os";
|
|
321
|
-
import
|
|
507
|
+
import path4 from "path";
|
|
322
508
|
import {
|
|
323
|
-
existsSync as
|
|
509
|
+
existsSync as existsSync5,
|
|
324
510
|
lstatSync,
|
|
325
|
-
mkdirSync,
|
|
511
|
+
mkdirSync as mkdirSync2,
|
|
326
512
|
readlinkSync as readlinkSync2,
|
|
327
513
|
symlinkSync as symlinkSync2
|
|
328
514
|
} from "fs";
|
|
329
515
|
function claudeAgentsDir(homeDir) {
|
|
330
|
-
return
|
|
516
|
+
return path4.join(homeDir, ".claude", "agents");
|
|
331
517
|
}
|
|
332
518
|
function identitySourcePath(homeDir, agentId) {
|
|
333
|
-
return
|
|
519
|
+
return path4.join(homeDir, ".exe-os", "identity", `${agentId}.md`);
|
|
334
520
|
}
|
|
335
521
|
function claudeAgentLinkPath(homeDir, agentId) {
|
|
336
|
-
return
|
|
522
|
+
return path4.join(claudeAgentsDir(homeDir), `${agentId}.md`);
|
|
337
523
|
}
|
|
338
524
|
function ensureAgentSymlink(agentId, homeDir = os3.homedir()) {
|
|
339
525
|
const target = identitySourcePath(homeDir, agentId);
|
|
340
526
|
const link = claudeAgentLinkPath(homeDir, agentId);
|
|
341
|
-
|
|
342
|
-
if (
|
|
527
|
+
mkdirSync2(claudeAgentsDir(homeDir), { recursive: true });
|
|
528
|
+
if (existsSync5(link)) {
|
|
343
529
|
let stat;
|
|
344
530
|
try {
|
|
345
531
|
stat = lstatSync(link);
|
|
@@ -377,14 +563,14 @@ async function ensureAllAgentSymlinks(homeDir = os3.homedir()) {
|
|
|
377
563
|
const employees = await loadEmployees();
|
|
378
564
|
return employees.map((emp) => ensureAgentSymlink(emp.name, homeDir));
|
|
379
565
|
}
|
|
566
|
+
var init_agent_symlinks = __esm({
|
|
567
|
+
"src/lib/agent-symlinks.ts"() {
|
|
568
|
+
"use strict";
|
|
569
|
+
init_employees();
|
|
570
|
+
}
|
|
571
|
+
});
|
|
380
572
|
|
|
381
573
|
// src/lib/mcp-prefix.ts
|
|
382
|
-
var MCP_PRIMARY_KEY = "exe-os";
|
|
383
|
-
var MCP_LEGACY_KEY = "exe-mem";
|
|
384
|
-
var MCP_TOOL_PREFIXES = [
|
|
385
|
-
`mcp__${MCP_PRIMARY_KEY}__`,
|
|
386
|
-
`mcp__${MCP_LEGACY_KEY}__`
|
|
387
|
-
];
|
|
388
574
|
function expandDualPrefixTools(shortNames) {
|
|
389
575
|
const out = [];
|
|
390
576
|
for (const name of shortNames) {
|
|
@@ -394,64 +580,88 @@ function expandDualPrefixTools(shortNames) {
|
|
|
394
580
|
}
|
|
395
581
|
return out;
|
|
396
582
|
}
|
|
583
|
+
var MCP_PRIMARY_KEY, MCP_LEGACY_KEY, MCP_TOOL_PREFIXES;
|
|
584
|
+
var init_mcp_prefix = __esm({
|
|
585
|
+
"src/lib/mcp-prefix.ts"() {
|
|
586
|
+
"use strict";
|
|
587
|
+
MCP_PRIMARY_KEY = "exe-os";
|
|
588
|
+
MCP_LEGACY_KEY = "exe-mem";
|
|
589
|
+
MCP_TOOL_PREFIXES = [
|
|
590
|
+
`mcp__${MCP_PRIMARY_KEY}__`,
|
|
591
|
+
`mcp__${MCP_LEGACY_KEY}__`
|
|
592
|
+
];
|
|
593
|
+
}
|
|
594
|
+
});
|
|
397
595
|
|
|
398
596
|
// src/lib/preferences.ts
|
|
399
|
-
import { existsSync as
|
|
400
|
-
import
|
|
597
|
+
import { existsSync as existsSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
|
|
598
|
+
import path5 from "path";
|
|
401
599
|
import os4 from "os";
|
|
402
600
|
function loadPreferences(homeDir = os4.homedir()) {
|
|
403
|
-
const configPath =
|
|
404
|
-
if (!
|
|
601
|
+
const configPath = path5.join(homeDir, ".exe-os", "config.json");
|
|
602
|
+
if (!existsSync6(configPath)) return {};
|
|
405
603
|
try {
|
|
406
|
-
const config = JSON.parse(
|
|
604
|
+
const config = JSON.parse(readFileSync4(configPath, "utf-8"));
|
|
407
605
|
return config.preferences ?? {};
|
|
408
606
|
} catch {
|
|
409
607
|
return {};
|
|
410
608
|
}
|
|
411
609
|
}
|
|
610
|
+
var init_preferences = __esm({
|
|
611
|
+
"src/lib/preferences.ts"() {
|
|
612
|
+
"use strict";
|
|
613
|
+
init_secure_files();
|
|
614
|
+
}
|
|
615
|
+
});
|
|
412
616
|
|
|
413
617
|
// src/adapters/claude/installer.ts
|
|
618
|
+
import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3, readdir } from "fs/promises";
|
|
619
|
+
import { existsSync as existsSync7, readFileSync as readFileSync5, writeFileSync as writeFileSync4, copyFileSync, mkdirSync as mkdirSync3 } from "fs";
|
|
620
|
+
import path6 from "path";
|
|
621
|
+
import os5 from "os";
|
|
622
|
+
import { execSync as execSync2 } from "child_process";
|
|
623
|
+
import { fileURLToPath } from "url";
|
|
414
624
|
function resolvePackageRoot() {
|
|
415
625
|
const thisFile = fileURLToPath(import.meta.url);
|
|
416
|
-
let dir =
|
|
417
|
-
const root =
|
|
626
|
+
let dir = path6.dirname(thisFile);
|
|
627
|
+
const root = path6.parse(dir).root;
|
|
418
628
|
while (dir !== root) {
|
|
419
|
-
const pkgPath =
|
|
420
|
-
if (
|
|
629
|
+
const pkgPath = path6.join(dir, "package.json");
|
|
630
|
+
if (existsSync7(pkgPath)) {
|
|
421
631
|
try {
|
|
422
|
-
const pkg = JSON.parse(
|
|
632
|
+
const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
|
|
423
633
|
if (pkg.name === "@askexenow/exe-os" || pkg.name === "exe-os") return dir;
|
|
424
634
|
} catch {
|
|
425
635
|
}
|
|
426
636
|
}
|
|
427
|
-
dir =
|
|
637
|
+
dir = path6.dirname(dir);
|
|
428
638
|
}
|
|
429
|
-
return
|
|
639
|
+
return path6.resolve(path6.dirname(thisFile), "..", "..", "..");
|
|
430
640
|
}
|
|
431
641
|
async function copySlashCommands(packageRoot, homeDir = os5.homedir()) {
|
|
432
642
|
let copied = 0;
|
|
433
643
|
let skipped = 0;
|
|
434
|
-
const skillsBase =
|
|
435
|
-
const exeDir =
|
|
436
|
-
if (
|
|
644
|
+
const skillsBase = path6.join(homeDir, ".claude", "skills");
|
|
645
|
+
const exeDir = path6.join(packageRoot, "src", "commands", "exe");
|
|
646
|
+
if (existsSync7(exeDir)) {
|
|
437
647
|
const entries = await readdir(exeDir);
|
|
438
648
|
const mdFiles = entries.filter((f) => f.endsWith(".md"));
|
|
439
649
|
for (const file of mdFiles) {
|
|
440
650
|
const name = file.replace(".md", "");
|
|
441
|
-
const destDir =
|
|
651
|
+
const destDir = path6.join(skillsBase, `exe-${name}`);
|
|
442
652
|
await mkdir3(destDir, { recursive: true });
|
|
443
|
-
const srcPath =
|
|
444
|
-
const destPath =
|
|
653
|
+
const srcPath = path6.join(exeDir, file);
|
|
654
|
+
const destPath = path6.join(destDir, "SKILL.md");
|
|
445
655
|
const result = await copyAsSkill(srcPath, destPath, `exe-${name}`);
|
|
446
656
|
if (result) copied++;
|
|
447
657
|
else skipped++;
|
|
448
658
|
}
|
|
449
659
|
}
|
|
450
|
-
const topLevelSrc =
|
|
451
|
-
if (
|
|
452
|
-
const destDir =
|
|
660
|
+
const topLevelSrc = path6.join(packageRoot, "src", "commands", "exe.md");
|
|
661
|
+
if (existsSync7(topLevelSrc)) {
|
|
662
|
+
const destDir = path6.join(skillsBase, "exe");
|
|
453
663
|
await mkdir3(destDir, { recursive: true });
|
|
454
|
-
const destPath =
|
|
664
|
+
const destPath = path6.join(destDir, "SKILL.md");
|
|
455
665
|
const result = await copyAsSkill(topLevelSrc, destPath, "exe");
|
|
456
666
|
if (result) copied++;
|
|
457
667
|
else skipped++;
|
|
@@ -474,7 +684,7 @@ name: ${skillName}
|
|
|
474
684
|
`);
|
|
475
685
|
}
|
|
476
686
|
}
|
|
477
|
-
if (
|
|
687
|
+
if (existsSync7(destPath)) {
|
|
478
688
|
const existing = await readFile3(destPath, "utf-8");
|
|
479
689
|
if (existing === content) return false;
|
|
480
690
|
}
|
|
@@ -482,9 +692,9 @@ name: ${skillName}
|
|
|
482
692
|
return true;
|
|
483
693
|
}
|
|
484
694
|
async function registerMcpServer(packageRoot, homeDir = os5.homedir()) {
|
|
485
|
-
const claudeJsonPath =
|
|
695
|
+
const claudeJsonPath = path6.join(homeDir, ".claude.json");
|
|
486
696
|
let claudeJson = {};
|
|
487
|
-
if (
|
|
697
|
+
if (existsSync7(claudeJsonPath)) {
|
|
488
698
|
try {
|
|
489
699
|
claudeJson = JSON.parse(await readFile3(claudeJsonPath, "utf-8"));
|
|
490
700
|
} catch {
|
|
@@ -497,25 +707,25 @@ async function registerMcpServer(packageRoot, homeDir = os5.homedir()) {
|
|
|
497
707
|
const newEntry = {
|
|
498
708
|
type: "stdio",
|
|
499
709
|
command: "node",
|
|
500
|
-
args: [
|
|
710
|
+
args: [path6.join(packageRoot, "dist", "mcp", "server.js")],
|
|
501
711
|
env: {}
|
|
502
712
|
};
|
|
503
713
|
const currentMem = claudeJson.mcpServers[MCP_LEGACY_KEY];
|
|
504
|
-
const currentOs = claudeJson.mcpServers[MCP_PRIMARY_KEY];
|
|
505
714
|
const memMatches = currentMem && JSON.stringify(currentMem) === JSON.stringify(newEntry);
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
715
|
+
if (claudeJson.mcpServers[MCP_PRIMARY_KEY]) {
|
|
716
|
+
delete claudeJson.mcpServers[MCP_PRIMARY_KEY];
|
|
717
|
+
}
|
|
718
|
+
if (memMatches && !claudeJson.mcpServers[MCP_PRIMARY_KEY]) {
|
|
719
|
+
await cleanSettingsJsonMcp(path6.join(homeDir, ".claude", "settings.json"));
|
|
509
720
|
return false;
|
|
510
721
|
}
|
|
511
722
|
claudeJson.mcpServers[MCP_LEGACY_KEY] = newEntry;
|
|
512
|
-
claudeJson.mcpServers[MCP_PRIMARY_KEY] = newEntry;
|
|
513
723
|
await writeFile3(claudeJsonPath, JSON.stringify(claudeJson, null, 2) + "\n");
|
|
514
|
-
await cleanSettingsJsonMcp(
|
|
724
|
+
await cleanSettingsJsonMcp(path6.join(homeDir, ".claude", "settings.json"));
|
|
515
725
|
return true;
|
|
516
726
|
}
|
|
517
727
|
async function cleanSettingsJsonMcp(settingsPath) {
|
|
518
|
-
if (!
|
|
728
|
+
if (!existsSync7(settingsPath)) return;
|
|
519
729
|
try {
|
|
520
730
|
const settings = JSON.parse(await readFile3(settingsPath, "utf-8"));
|
|
521
731
|
const servers = settings.mcpServers;
|
|
@@ -536,13 +746,13 @@ async function cleanSettingsJsonMcp(settingsPath) {
|
|
|
536
746
|
}
|
|
537
747
|
}
|
|
538
748
|
async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
539
|
-
const settingsPath =
|
|
540
|
-
const logsDir =
|
|
541
|
-
const hookLogPath =
|
|
749
|
+
const settingsPath = path6.join(homeDir, ".claude", "settings.json");
|
|
750
|
+
const logsDir = path6.join(homeDir, ".exe-os", "logs");
|
|
751
|
+
const hookLogPath = path6.join(logsDir, "hooks.log");
|
|
542
752
|
const logSuffix = ` 2>> "${hookLogPath}"`;
|
|
543
753
|
await mkdir3(logsDir, { recursive: true });
|
|
544
754
|
let settings = {};
|
|
545
|
-
if (
|
|
755
|
+
if (existsSync7(settingsPath)) {
|
|
546
756
|
try {
|
|
547
757
|
settings = JSON.parse(await readFile3(settingsPath, "utf-8"));
|
|
548
758
|
} catch {
|
|
@@ -564,11 +774,11 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
564
774
|
hooks: [
|
|
565
775
|
{
|
|
566
776
|
type: "command",
|
|
567
|
-
command: `node "${
|
|
777
|
+
command: `node "${path6.join(packageRoot, "dist", "hooks", "ingest.js")}"${logSuffix}`
|
|
568
778
|
},
|
|
569
779
|
{
|
|
570
780
|
type: "command",
|
|
571
|
-
command: `node "${
|
|
781
|
+
command: `node "${path6.join(packageRoot, "dist", "hooks", "error-recall.js")}"${logSuffix}`
|
|
572
782
|
}
|
|
573
783
|
]
|
|
574
784
|
},
|
|
@@ -580,7 +790,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
580
790
|
hooks: [
|
|
581
791
|
{
|
|
582
792
|
type: "command",
|
|
583
|
-
command: `node "${
|
|
793
|
+
command: `node "${path6.join(packageRoot, "dist", "hooks", "session-start.js")}"${logSuffix}`,
|
|
584
794
|
timeout: 1e4
|
|
585
795
|
}
|
|
586
796
|
]
|
|
@@ -593,7 +803,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
593
803
|
hooks: [
|
|
594
804
|
{
|
|
595
805
|
type: "command",
|
|
596
|
-
command: `node "${
|
|
806
|
+
command: `node "${path6.join(packageRoot, "dist", "hooks", "prompt-submit.js")}"${logSuffix}`
|
|
597
807
|
}
|
|
598
808
|
]
|
|
599
809
|
},
|
|
@@ -605,7 +815,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
605
815
|
hooks: [
|
|
606
816
|
{
|
|
607
817
|
type: "command",
|
|
608
|
-
command: `node "${
|
|
818
|
+
command: `node "${path6.join(packageRoot, "dist", "hooks", "exe-heartbeat-hook.js")}"${logSuffix}`,
|
|
609
819
|
timeout: 5e3
|
|
610
820
|
}
|
|
611
821
|
]
|
|
@@ -618,7 +828,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
618
828
|
hooks: [
|
|
619
829
|
{
|
|
620
830
|
type: "command",
|
|
621
|
-
command: `node "${
|
|
831
|
+
command: `node "${path6.join(packageRoot, "dist", "hooks", "stop.js")}"${logSuffix}`
|
|
622
832
|
}
|
|
623
833
|
]
|
|
624
834
|
},
|
|
@@ -631,7 +841,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
631
841
|
hooks: [
|
|
632
842
|
{
|
|
633
843
|
type: "command",
|
|
634
|
-
command: `node "${
|
|
844
|
+
command: `node "${path6.join(packageRoot, "dist", "hooks", "pre-tool-use.js")}"${logSuffix}`
|
|
635
845
|
}
|
|
636
846
|
]
|
|
637
847
|
},
|
|
@@ -643,7 +853,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
643
853
|
hooks: [
|
|
644
854
|
{
|
|
645
855
|
type: "command",
|
|
646
|
-
command: `node "${
|
|
856
|
+
command: `node "${path6.join(packageRoot, "dist", "hooks", "subagent-stop.js")}"${logSuffix}`
|
|
647
857
|
}
|
|
648
858
|
]
|
|
649
859
|
},
|
|
@@ -655,7 +865,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
655
865
|
hooks: [
|
|
656
866
|
{
|
|
657
867
|
type: "command",
|
|
658
|
-
command: `node "${
|
|
868
|
+
command: `node "${path6.join(packageRoot, "dist", "hooks", "pre-compact.js")}"${logSuffix}`,
|
|
659
869
|
timeout: 1e4
|
|
660
870
|
}
|
|
661
871
|
]
|
|
@@ -668,7 +878,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
668
878
|
hooks: [
|
|
669
879
|
{
|
|
670
880
|
type: "command",
|
|
671
|
-
command: `node "${
|
|
881
|
+
command: `node "${path6.join(packageRoot, "dist", "hooks", "session-end.js")}"${logSuffix}`
|
|
672
882
|
}
|
|
673
883
|
]
|
|
674
884
|
},
|
|
@@ -680,7 +890,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
680
890
|
hooks: [
|
|
681
891
|
{
|
|
682
892
|
type: "command",
|
|
683
|
-
command: `node "${
|
|
893
|
+
command: `node "${path6.join(packageRoot, "dist", "hooks", "notification.js")}"${logSuffix}`
|
|
684
894
|
}
|
|
685
895
|
]
|
|
686
896
|
},
|
|
@@ -692,7 +902,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
692
902
|
hooks: [
|
|
693
903
|
{
|
|
694
904
|
type: "command",
|
|
695
|
-
command: `node "${
|
|
905
|
+
command: `node "${path6.join(packageRoot, "dist", "hooks", "post-compact.js")}"${logSuffix}`,
|
|
696
906
|
timeout: 1e4
|
|
697
907
|
}
|
|
698
908
|
]
|
|
@@ -705,7 +915,7 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
705
915
|
hooks: [
|
|
706
916
|
{
|
|
707
917
|
type: "command",
|
|
708
|
-
command: `node "${
|
|
918
|
+
command: `node "${path6.join(packageRoot, "dist", "hooks", "instructions-loaded.js")}"${logSuffix}`
|
|
709
919
|
}
|
|
710
920
|
]
|
|
711
921
|
},
|
|
@@ -804,13 +1014,13 @@ async function mergeHooks(packageRoot, homeDir = os5.homedir()) {
|
|
|
804
1014
|
allowList.push(tool);
|
|
805
1015
|
}
|
|
806
1016
|
}
|
|
807
|
-
await mkdir3(
|
|
1017
|
+
await mkdir3(path6.dirname(settingsPath), { recursive: true });
|
|
808
1018
|
await writeFile3(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
809
1019
|
return { added, skipped };
|
|
810
1020
|
}
|
|
811
1021
|
async function cleanOldShellFunctions(homeDir = os5.homedir()) {
|
|
812
|
-
const rosterPath =
|
|
813
|
-
if (!
|
|
1022
|
+
const rosterPath = path6.join(homeDir, ".exe-os", "exe-employees.json");
|
|
1023
|
+
if (!existsSync7(rosterPath)) return 0;
|
|
814
1024
|
let employees;
|
|
815
1025
|
try {
|
|
816
1026
|
employees = JSON.parse(await readFile3(rosterPath, "utf-8"));
|
|
@@ -825,13 +1035,13 @@ async function cleanOldShellFunctions(homeDir = os5.homedir()) {
|
|
|
825
1035
|
return { name: n, funcDef, forLoop };
|
|
826
1036
|
});
|
|
827
1037
|
const rcFiles = [
|
|
828
|
-
|
|
829
|
-
|
|
1038
|
+
path6.join(homeDir, ".zshrc"),
|
|
1039
|
+
path6.join(homeDir, ".bashrc")
|
|
830
1040
|
];
|
|
831
1041
|
const REMOVED_MARKER = "# Removed by exe-os \u2014 wrappers now at ~/.exe-os/bin/";
|
|
832
1042
|
let totalRemoved = 0;
|
|
833
1043
|
for (const rcPath of rcFiles) {
|
|
834
|
-
if (!
|
|
1044
|
+
if (!existsSync7(rcPath)) continue;
|
|
835
1045
|
let content;
|
|
836
1046
|
try {
|
|
837
1047
|
content = await readFile3(rcPath, "utf-8");
|
|
@@ -934,22 +1144,9 @@ async function cleanOldShellFunctions(homeDir = os5.homedir()) {
|
|
|
934
1144
|
function escapeRegExp(s) {
|
|
935
1145
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
936
1146
|
}
|
|
937
|
-
var EXE_SECTION_START = "<!-- exe-os:orchestration-start -->";
|
|
938
|
-
var EXE_SECTION_END = "<!-- exe-os:orchestration-end -->";
|
|
939
|
-
var ORCHESTRATION_RULES = `${EXE_SECTION_START}
|
|
940
|
-
## exe-os Multi-Agent Orchestration (auto-managed \u2014 do not edit)
|
|
941
|
-
|
|
942
|
-
These rules are injected by exe-os and override default behavior for multi-agent coordination.
|
|
943
|
-
|
|
944
|
-
- **Session routing is deterministic.** When working in a coordinator session, ALL employee sessions use that same coordinator-session suffix. NEVER reference, consider, or dispatch to employee sessions from other project sessions. This is not a choice \u2014 it is a hard constraint.
|
|
945
|
-
- **Always use create_task to assign work.** Never use messages, tmux send-keys, or ad-hoc instructions as a substitute for tasks.
|
|
946
|
-
- **Never modify another agent's in-progress task.** Create a new task instead. Modifying active tasks causes race conditions.
|
|
947
|
-
- **Chain of command:** founder -> COO -> managers -> specialists. The COO does not bypass managers for specialist work.
|
|
948
|
-
- **Verify dispatch.** After every create_task, confirm the employee received and started the task via tmux capture-pane.
|
|
949
|
-
${EXE_SECTION_END}`;
|
|
950
1147
|
async function injectOrchestrationRules(homeDir) {
|
|
951
|
-
const claudeDir =
|
|
952
|
-
const claudeMdPath =
|
|
1148
|
+
const claudeDir = path6.join(homeDir, ".claude");
|
|
1149
|
+
const claudeMdPath = path6.join(claudeDir, "CLAUDE.md");
|
|
953
1150
|
await mkdir3(claudeDir, { recursive: true });
|
|
954
1151
|
let existing = "";
|
|
955
1152
|
try {
|
|
@@ -974,16 +1171,16 @@ async function injectOrchestrationRules(homeDir) {
|
|
|
974
1171
|
async function installStatusLine(packageRoot, homeDir = os5.homedir()) {
|
|
975
1172
|
const prefs = loadPreferences(homeDir);
|
|
976
1173
|
if (prefs.ccStatusLine === false) return "opted-out";
|
|
977
|
-
const claudeDir =
|
|
1174
|
+
const claudeDir = path6.join(homeDir, ".claude");
|
|
978
1175
|
await mkdir3(claudeDir, { recursive: true });
|
|
979
|
-
const assetPath =
|
|
980
|
-
if (!
|
|
981
|
-
const destScript =
|
|
1176
|
+
const assetPath = path6.join(packageRoot, "dist", "assets", "statusline-command.sh");
|
|
1177
|
+
if (!existsSync7(assetPath)) return "asset-missing";
|
|
1178
|
+
const destScript = path6.join(claudeDir, "statusline-command.sh");
|
|
982
1179
|
const assetContent = await readFile3(assetPath, "utf-8");
|
|
983
1180
|
await writeFile3(destScript, assetContent, { mode: 493 });
|
|
984
|
-
const settingsPath =
|
|
1181
|
+
const settingsPath = path6.join(claudeDir, "settings.json");
|
|
985
1182
|
let settings = {};
|
|
986
|
-
if (
|
|
1183
|
+
if (existsSync7(settingsPath)) {
|
|
987
1184
|
try {
|
|
988
1185
|
settings = JSON.parse(await readFile3(settingsPath, "utf-8"));
|
|
989
1186
|
} catch {
|
|
@@ -1021,12 +1218,12 @@ async function runInstaller(homeDir) {
|
|
|
1021
1218
|
`
|
|
1022
1219
|
);
|
|
1023
1220
|
const resolvedHome = homeDir ?? os5.homedir();
|
|
1024
|
-
const exeWorkspace =
|
|
1025
|
-
if (!
|
|
1221
|
+
const exeWorkspace = path6.join(resolvedHome, "exe");
|
|
1222
|
+
if (!existsSync7(exeWorkspace)) {
|
|
1026
1223
|
try {
|
|
1027
|
-
await mkdir3(
|
|
1028
|
-
await mkdir3(
|
|
1029
|
-
await mkdir3(
|
|
1224
|
+
await mkdir3(path6.join(exeWorkspace, "content"), { recursive: true });
|
|
1225
|
+
await mkdir3(path6.join(exeWorkspace, "operations"), { recursive: true });
|
|
1226
|
+
await mkdir3(path6.join(exeWorkspace, "output"), { recursive: true });
|
|
1030
1227
|
process.stderr.write(
|
|
1031
1228
|
`Created ~/exe/ \u2014 your automation workspace for non-code projects
|
|
1032
1229
|
`
|
|
@@ -1062,33 +1259,33 @@ exe-os installed successfully.
|
|
|
1062
1259
|
}
|
|
1063
1260
|
function setupTmux(home) {
|
|
1064
1261
|
const homeDir = home ?? os5.homedir();
|
|
1065
|
-
const exeDir =
|
|
1066
|
-
const exeTmuxConf =
|
|
1067
|
-
const userTmuxConf =
|
|
1068
|
-
const backupPath =
|
|
1262
|
+
const exeDir = path6.join(homeDir, ".exe-os");
|
|
1263
|
+
const exeTmuxConf = path6.join(exeDir, "tmux.conf");
|
|
1264
|
+
const userTmuxConf = path6.join(homeDir, ".tmux.conf");
|
|
1265
|
+
const backupPath = path6.join(homeDir, ".tmux.conf.backup");
|
|
1069
1266
|
const sourceLine = "source-file ~/.exe-os/tmux.conf";
|
|
1070
1267
|
const pkgRoot = resolvePackageRoot();
|
|
1071
|
-
const assetPath =
|
|
1072
|
-
if (!
|
|
1268
|
+
const assetPath = path6.join(pkgRoot, "dist", "assets", "tmux.conf");
|
|
1269
|
+
if (!existsSync7(assetPath)) {
|
|
1073
1270
|
process.stderr.write(`exe-os: tmux.conf asset not found at ${assetPath} \u2014 skipping tmux setup
|
|
1074
1271
|
`);
|
|
1075
1272
|
return;
|
|
1076
1273
|
}
|
|
1077
1274
|
mkdirSync3(exeDir, { recursive: true });
|
|
1078
1275
|
copyFileSync(assetPath, exeTmuxConf);
|
|
1079
|
-
if (
|
|
1080
|
-
const existing =
|
|
1276
|
+
if (existsSync7(userTmuxConf)) {
|
|
1277
|
+
const existing = readFileSync5(userTmuxConf, "utf8");
|
|
1081
1278
|
if (!existing.includes(sourceLine)) {
|
|
1082
|
-
if (!
|
|
1279
|
+
if (!existsSync7(backupPath)) {
|
|
1083
1280
|
copyFileSync(userTmuxConf, backupPath);
|
|
1084
1281
|
process.stderr.write(`exe-os: backed up existing tmux config to ${backupPath}
|
|
1085
1282
|
`);
|
|
1086
1283
|
}
|
|
1087
|
-
|
|
1284
|
+
writeFileSync4(userTmuxConf, `${sourceLine}
|
|
1088
1285
|
${existing}`);
|
|
1089
1286
|
}
|
|
1090
1287
|
} else {
|
|
1091
|
-
|
|
1288
|
+
writeFileSync4(userTmuxConf, `# Exe OS tmux defaults \u2014 remove this line to use your own config
|
|
1092
1289
|
${sourceLine}
|
|
1093
1290
|
`);
|
|
1094
1291
|
}
|
|
@@ -1100,9 +1297,9 @@ ${sourceLine}
|
|
|
1100
1297
|
}
|
|
1101
1298
|
function setupGhostty(home) {
|
|
1102
1299
|
const homeDir = home ?? os5.homedir();
|
|
1103
|
-
const xdgConfig =
|
|
1104
|
-
const macConfig =
|
|
1105
|
-
const ghosttyInstalled =
|
|
1300
|
+
const xdgConfig = path6.join(homeDir, ".config", "ghostty");
|
|
1301
|
+
const macConfig = path6.join(homeDir, "Library", "Application Support", "com.mitchellh.ghostty");
|
|
1302
|
+
const ghosttyInstalled = existsSync7(xdgConfig) || existsSync7(macConfig) || (() => {
|
|
1106
1303
|
try {
|
|
1107
1304
|
execSync2("which ghostty 2>/dev/null");
|
|
1108
1305
|
return true;
|
|
@@ -1114,47 +1311,47 @@ function setupGhostty(home) {
|
|
|
1114
1311
|
return;
|
|
1115
1312
|
}
|
|
1116
1313
|
const pkgRoot = resolvePackageRoot();
|
|
1117
|
-
const assetPath =
|
|
1118
|
-
if (!
|
|
1314
|
+
const assetPath = path6.join(pkgRoot, "dist", "assets", "ghostty.conf");
|
|
1315
|
+
if (!existsSync7(assetPath)) {
|
|
1119
1316
|
process.stderr.write("exe-os: ghostty.conf asset not found \u2014 skipping Ghostty setup\n");
|
|
1120
1317
|
return;
|
|
1121
1318
|
}
|
|
1122
1319
|
const configDir = xdgConfig;
|
|
1123
|
-
const configPath =
|
|
1124
|
-
const backupPath =
|
|
1320
|
+
const configPath = path6.join(configDir, "config");
|
|
1321
|
+
const backupPath = path6.join(configDir, "config.backup");
|
|
1125
1322
|
mkdirSync3(configDir, { recursive: true });
|
|
1126
1323
|
const START_MARKER = "# \u2500\u2500 exe-os:ghostty-start \u2500\u2500";
|
|
1127
1324
|
const END_MARKER = "# \u2500\u2500 exe-os:ghostty-end \u2500\u2500";
|
|
1128
|
-
const assetContent =
|
|
1325
|
+
const assetContent = readFileSync5(assetPath, "utf8").trim();
|
|
1129
1326
|
const markedSection = `${START_MARKER}
|
|
1130
1327
|
${assetContent}
|
|
1131
1328
|
${END_MARKER}`;
|
|
1132
|
-
if (
|
|
1133
|
-
const existing =
|
|
1329
|
+
if (existsSync7(configPath)) {
|
|
1330
|
+
const existing = readFileSync5(configPath, "utf8");
|
|
1134
1331
|
if (existing.includes(START_MARKER) && existing.includes(END_MARKER)) {
|
|
1135
1332
|
const before = existing.slice(0, existing.indexOf(START_MARKER));
|
|
1136
1333
|
const after = existing.slice(existing.indexOf(END_MARKER) + END_MARKER.length);
|
|
1137
|
-
|
|
1334
|
+
writeFileSync4(configPath, `${before}${markedSection}${after}`);
|
|
1138
1335
|
} else if (existing.includes("Exe OS")) {
|
|
1139
|
-
if (!
|
|
1336
|
+
if (!existsSync7(backupPath)) {
|
|
1140
1337
|
copyFileSync(configPath, backupPath);
|
|
1141
1338
|
process.stderr.write(`exe-os: backed up existing Ghostty config to ${backupPath}
|
|
1142
1339
|
`);
|
|
1143
1340
|
}
|
|
1144
|
-
|
|
1341
|
+
writeFileSync4(configPath, `${markedSection}
|
|
1145
1342
|
`);
|
|
1146
1343
|
} else {
|
|
1147
|
-
if (!
|
|
1344
|
+
if (!existsSync7(backupPath)) {
|
|
1148
1345
|
copyFileSync(configPath, backupPath);
|
|
1149
1346
|
process.stderr.write(`exe-os: backed up existing Ghostty config to ${backupPath}
|
|
1150
1347
|
`);
|
|
1151
1348
|
}
|
|
1152
|
-
|
|
1349
|
+
writeFileSync4(configPath, `${markedSection}
|
|
1153
1350
|
|
|
1154
1351
|
${existing}`);
|
|
1155
1352
|
}
|
|
1156
1353
|
} else {
|
|
1157
|
-
|
|
1354
|
+
writeFileSync4(configPath, `${markedSection}
|
|
1158
1355
|
`);
|
|
1159
1356
|
}
|
|
1160
1357
|
process.stderr.write("exe-os: Ghostty config installed\n");
|
|
@@ -1171,46 +1368,275 @@ function summarizeSymlinkResults(results) {
|
|
|
1171
1368
|
}
|
|
1172
1369
|
return summary;
|
|
1173
1370
|
}
|
|
1371
|
+
var EXE_SECTION_START, EXE_SECTION_END, ORCHESTRATION_RULES;
|
|
1372
|
+
var init_installer = __esm({
|
|
1373
|
+
"src/adapters/claude/installer.ts"() {
|
|
1374
|
+
"use strict";
|
|
1375
|
+
init_agent_symlinks();
|
|
1376
|
+
init_mcp_prefix();
|
|
1377
|
+
init_preferences();
|
|
1378
|
+
EXE_SECTION_START = "<!-- exe-os:orchestration-start -->";
|
|
1379
|
+
EXE_SECTION_END = "<!-- exe-os:orchestration-end -->";
|
|
1380
|
+
ORCHESTRATION_RULES = `${EXE_SECTION_START}
|
|
1381
|
+
## exe-os Multi-Agent Orchestration (auto-managed \u2014 do not edit)
|
|
1382
|
+
|
|
1383
|
+
These rules are injected by exe-os and override default behavior for multi-agent coordination.
|
|
1384
|
+
|
|
1385
|
+
- **Session routing is deterministic.** When working in a coordinator session, ALL employee sessions use that same coordinator-session suffix. NEVER reference, consider, or dispatch to employee sessions from other project sessions. This is not a choice \u2014 it is a hard constraint.
|
|
1386
|
+
- **Always use create_task to assign work.** Never use messages, tmux send-keys, or ad-hoc instructions as a substitute for tasks.
|
|
1387
|
+
- **Never modify another agent's in-progress task.** Create a new task instead. Modifying active tasks causes race conditions.
|
|
1388
|
+
- **Chain of command:** founder -> COO -> managers -> specialists. The COO does not bypass managers for specialist work.
|
|
1389
|
+
- **Verify dispatch.** After every create_task, confirm the employee received and started the task via tmux capture-pane.
|
|
1390
|
+
${EXE_SECTION_END}`;
|
|
1391
|
+
}
|
|
1392
|
+
});
|
|
1393
|
+
|
|
1394
|
+
// src/adapters/codex/installer.ts
|
|
1395
|
+
var installer_exports = {};
|
|
1396
|
+
__export(installer_exports, {
|
|
1397
|
+
installCodexStatusLine: () => installCodexStatusLine,
|
|
1398
|
+
mergeCodexHooks: () => mergeCodexHooks,
|
|
1399
|
+
runCodexInstaller: () => runCodexInstaller,
|
|
1400
|
+
verifyCodexHooks: () => verifyCodexHooks
|
|
1401
|
+
});
|
|
1402
|
+
import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir4 } from "fs/promises";
|
|
1403
|
+
import { existsSync as existsSync9 } from "fs";
|
|
1404
|
+
import path8 from "path";
|
|
1405
|
+
import os6 from "os";
|
|
1406
|
+
async function mergeCodexHooks(packageRoot, homeDir = os6.homedir()) {
|
|
1407
|
+
const codexDir = path8.join(homeDir, ".codex");
|
|
1408
|
+
const hooksPath = path8.join(codexDir, "hooks.json");
|
|
1409
|
+
const logsDir = path8.join(homeDir, ".exe-os", "logs");
|
|
1410
|
+
const hookLogPath = path8.join(logsDir, "hooks.log");
|
|
1411
|
+
const logSuffix = ` 2>> "${hookLogPath}"`;
|
|
1412
|
+
await mkdir4(codexDir, { recursive: true });
|
|
1413
|
+
await mkdir4(logsDir, { recursive: true });
|
|
1414
|
+
let hooksJson = {};
|
|
1415
|
+
if (existsSync9(hooksPath)) {
|
|
1416
|
+
try {
|
|
1417
|
+
hooksJson = JSON.parse(await readFile4(hooksPath, "utf-8"));
|
|
1418
|
+
} catch {
|
|
1419
|
+
hooksJson = {};
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
if (!hooksJson.hooks) {
|
|
1423
|
+
hooksJson.hooks = {};
|
|
1424
|
+
}
|
|
1425
|
+
const hooksToRegister = [
|
|
1426
|
+
{
|
|
1427
|
+
event: "SessionStart",
|
|
1428
|
+
group: {
|
|
1429
|
+
hooks: [
|
|
1430
|
+
{
|
|
1431
|
+
type: "command",
|
|
1432
|
+
command: `node "${path8.join(packageRoot, "dist", "hooks", "session-start.js")}"${logSuffix}`,
|
|
1433
|
+
timeout: 30,
|
|
1434
|
+
statusMessage: "exe-os: loading memory brief"
|
|
1435
|
+
}
|
|
1436
|
+
]
|
|
1437
|
+
},
|
|
1438
|
+
marker: "dist/hooks/session-start.js"
|
|
1439
|
+
},
|
|
1440
|
+
{
|
|
1441
|
+
event: "PostToolUse",
|
|
1442
|
+
group: {
|
|
1443
|
+
matcher: "Bash|apply_patch|Edit|Write|Read|Glob|Grep|mcp__.*",
|
|
1444
|
+
hooks: [
|
|
1445
|
+
{
|
|
1446
|
+
type: "command",
|
|
1447
|
+
command: `node "${path8.join(packageRoot, "dist", "hooks", "ingest.js")}"${logSuffix}`
|
|
1448
|
+
},
|
|
1449
|
+
{
|
|
1450
|
+
type: "command",
|
|
1451
|
+
command: `node "${path8.join(packageRoot, "dist", "hooks", "error-recall.js")}"${logSuffix}`
|
|
1452
|
+
}
|
|
1453
|
+
]
|
|
1454
|
+
},
|
|
1455
|
+
marker: "dist/hooks/ingest.js"
|
|
1456
|
+
},
|
|
1457
|
+
{
|
|
1458
|
+
event: "UserPromptSubmit",
|
|
1459
|
+
group: {
|
|
1460
|
+
hooks: [
|
|
1461
|
+
{
|
|
1462
|
+
type: "command",
|
|
1463
|
+
command: `node "${path8.join(packageRoot, "dist", "hooks", "prompt-submit.js")}"${logSuffix}`
|
|
1464
|
+
},
|
|
1465
|
+
{
|
|
1466
|
+
type: "command",
|
|
1467
|
+
command: `node "${path8.join(packageRoot, "dist", "hooks", "exe-heartbeat-hook.js")}"${logSuffix}`,
|
|
1468
|
+
timeout: 5
|
|
1469
|
+
}
|
|
1470
|
+
]
|
|
1471
|
+
},
|
|
1472
|
+
marker: "dist/hooks/prompt-submit.js"
|
|
1473
|
+
},
|
|
1474
|
+
{
|
|
1475
|
+
event: "Stop",
|
|
1476
|
+
group: {
|
|
1477
|
+
hooks: [
|
|
1478
|
+
{
|
|
1479
|
+
type: "command",
|
|
1480
|
+
command: `node "${path8.join(packageRoot, "dist", "hooks", "stop.js")}"${logSuffix}`
|
|
1481
|
+
}
|
|
1482
|
+
]
|
|
1483
|
+
},
|
|
1484
|
+
marker: "dist/hooks/stop.js"
|
|
1485
|
+
},
|
|
1486
|
+
{
|
|
1487
|
+
event: "PreToolUse",
|
|
1488
|
+
group: {
|
|
1489
|
+
matcher: "Bash|apply_patch",
|
|
1490
|
+
hooks: [
|
|
1491
|
+
{
|
|
1492
|
+
type: "command",
|
|
1493
|
+
command: `node "${path8.join(packageRoot, "dist", "hooks", "pre-tool-use.js")}"${logSuffix}`
|
|
1494
|
+
}
|
|
1495
|
+
]
|
|
1496
|
+
},
|
|
1497
|
+
marker: "dist/hooks/pre-tool-use.js"
|
|
1498
|
+
}
|
|
1499
|
+
];
|
|
1500
|
+
let added = 0;
|
|
1501
|
+
let skipped = 0;
|
|
1502
|
+
for (const { event, group, marker } of hooksToRegister) {
|
|
1503
|
+
if (!hooksJson.hooks[event]) {
|
|
1504
|
+
hooksJson.hooks[event] = [];
|
|
1505
|
+
}
|
|
1506
|
+
const existing = hooksJson.hooks[event];
|
|
1507
|
+
const correctCommand = group.hooks[0]?.command ?? "";
|
|
1508
|
+
const alreadyCorrect = existing.some(
|
|
1509
|
+
(g) => g.hooks.some((h) => h.command === correctCommand)
|
|
1510
|
+
);
|
|
1511
|
+
if (alreadyCorrect) {
|
|
1512
|
+
skipped++;
|
|
1513
|
+
} else {
|
|
1514
|
+
hooksJson.hooks[event] = existing.filter(
|
|
1515
|
+
(g) => !g.hooks.some((h) => h.command.includes(marker))
|
|
1516
|
+
);
|
|
1517
|
+
hooksJson.hooks[event].push(group);
|
|
1518
|
+
added++;
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
await writeFile4(hooksPath, JSON.stringify(hooksJson, null, 2) + "\n");
|
|
1522
|
+
return { added, skipped };
|
|
1523
|
+
}
|
|
1524
|
+
function verifyCodexHooks(homeDir = os6.homedir()) {
|
|
1525
|
+
const hooksPath = path8.join(homeDir, ".codex", "hooks.json");
|
|
1526
|
+
if (!existsSync9(hooksPath)) return false;
|
|
1527
|
+
try {
|
|
1528
|
+
const hooksJson = JSON.parse(
|
|
1529
|
+
__require("fs").readFileSync(hooksPath, "utf-8")
|
|
1530
|
+
);
|
|
1531
|
+
if (!hooksJson.hooks) return false;
|
|
1532
|
+
const required = ["SessionStart", "PostToolUse", "UserPromptSubmit", "Stop", "PreToolUse"];
|
|
1533
|
+
for (const event of required) {
|
|
1534
|
+
const groups = hooksJson.hooks[event];
|
|
1535
|
+
if (!groups || !groups.some(
|
|
1536
|
+
(g) => g.hooks.some((h) => h.command.includes("dist/hooks/"))
|
|
1537
|
+
)) {
|
|
1538
|
+
return false;
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
return true;
|
|
1542
|
+
} catch {
|
|
1543
|
+
return false;
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
async function installCodexStatusLine(homeDir = os6.homedir()) {
|
|
1547
|
+
const prefs = loadPreferences(homeDir);
|
|
1548
|
+
if (prefs.codexStatusLine === false) return "opted-out";
|
|
1549
|
+
const codexDir = path8.join(homeDir, ".codex");
|
|
1550
|
+
const configPath = path8.join(codexDir, "config.toml");
|
|
1551
|
+
await mkdir4(codexDir, { recursive: true });
|
|
1552
|
+
let content = "";
|
|
1553
|
+
if (existsSync9(configPath)) {
|
|
1554
|
+
content = await readFile4(configPath, "utf-8");
|
|
1555
|
+
if (/\[tui\][\s\S]*?status_line\s*=/.test(content)) {
|
|
1556
|
+
return "already-configured";
|
|
1557
|
+
}
|
|
1558
|
+
}
|
|
1559
|
+
const statusLineToml = `[tui]
|
|
1560
|
+
status_line = [${DEFAULT_CODEX_STATUS_LINE.map((s) => `"${s}"`).join(", ")}]`;
|
|
1561
|
+
if (content.includes("[tui]")) {
|
|
1562
|
+
content = content.replace(/\[tui\]/, `${statusLineToml}`);
|
|
1563
|
+
} else {
|
|
1564
|
+
const separator = content.length > 0 && !content.endsWith("\n") ? "\n\n" : content.length > 0 ? "\n" : "";
|
|
1565
|
+
content = content + separator + statusLineToml + "\n";
|
|
1566
|
+
}
|
|
1567
|
+
await writeFile4(configPath, content);
|
|
1568
|
+
return "installed";
|
|
1569
|
+
}
|
|
1570
|
+
async function runCodexInstaller(homeDir) {
|
|
1571
|
+
const packageRoot = resolvePackageRoot();
|
|
1572
|
+
const result = await mergeCodexHooks(packageRoot, homeDir);
|
|
1573
|
+
process.stderr.write(
|
|
1574
|
+
`[exe-os] Codex hooks: ${result.added} added, ${result.skipped} unchanged
|
|
1575
|
+
`
|
|
1576
|
+
);
|
|
1577
|
+
const statusResult = await installCodexStatusLine(homeDir);
|
|
1578
|
+
process.stderr.write(
|
|
1579
|
+
`[exe-os] Codex status line: ${statusResult}
|
|
1580
|
+
`
|
|
1581
|
+
);
|
|
1582
|
+
}
|
|
1583
|
+
var DEFAULT_CODEX_STATUS_LINE;
|
|
1584
|
+
var init_installer2 = __esm({
|
|
1585
|
+
"src/adapters/codex/installer.ts"() {
|
|
1586
|
+
"use strict";
|
|
1587
|
+
init_installer();
|
|
1588
|
+
init_preferences();
|
|
1589
|
+
DEFAULT_CODEX_STATUS_LINE = [
|
|
1590
|
+
"model-with-reasoning",
|
|
1591
|
+
"current-dir",
|
|
1592
|
+
"project-name",
|
|
1593
|
+
"git-branch",
|
|
1594
|
+
"run-state",
|
|
1595
|
+
"context-used"
|
|
1596
|
+
];
|
|
1597
|
+
}
|
|
1598
|
+
});
|
|
1174
1599
|
|
|
1175
1600
|
// src/bin/install.ts
|
|
1176
|
-
|
|
1601
|
+
init_installer();
|
|
1602
|
+
import { existsSync as existsSync10, readFileSync as readFileSync7, unlinkSync as unlinkSync3, readdirSync as readdirSync2, openSync, closeSync } from "fs";
|
|
1177
1603
|
import { spawn, execSync as execSync3 } from "child_process";
|
|
1178
|
-
import
|
|
1179
|
-
import
|
|
1604
|
+
import path9 from "path";
|
|
1605
|
+
import os7 from "os";
|
|
1180
1606
|
|
|
1181
1607
|
// src/lib/session-wrappers.ts
|
|
1182
1608
|
import {
|
|
1183
|
-
existsSync as
|
|
1184
|
-
readFileSync as
|
|
1185
|
-
writeFileSync as
|
|
1609
|
+
existsSync as existsSync8,
|
|
1610
|
+
readFileSync as readFileSync6,
|
|
1611
|
+
writeFileSync as writeFileSync5,
|
|
1186
1612
|
mkdirSync as mkdirSync4,
|
|
1187
|
-
chmodSync,
|
|
1613
|
+
chmodSync as chmodSync2,
|
|
1188
1614
|
readdirSync,
|
|
1189
1615
|
unlinkSync as unlinkSync2
|
|
1190
1616
|
} from "fs";
|
|
1191
|
-
import
|
|
1617
|
+
import path7 from "path";
|
|
1192
1618
|
import { homedir } from "os";
|
|
1193
1619
|
var MAX_N = 9;
|
|
1194
1620
|
function generateSessionWrappers(packageRoot, homeDir) {
|
|
1195
1621
|
const home = homeDir ?? homedir();
|
|
1196
|
-
const binDir =
|
|
1197
|
-
const rosterPath =
|
|
1622
|
+
const binDir = path7.join(home, ".exe-os", "bin");
|
|
1623
|
+
const rosterPath = path7.join(home, ".exe-os", "exe-employees.json");
|
|
1198
1624
|
mkdirSync4(binDir, { recursive: true });
|
|
1199
|
-
const exeStartDst =
|
|
1625
|
+
const exeStartDst = path7.join(binDir, "exe-start");
|
|
1200
1626
|
const candidates = [
|
|
1201
|
-
|
|
1202
|
-
|
|
1627
|
+
path7.join(packageRoot, "dist", "bin", "exe-start.sh"),
|
|
1628
|
+
path7.join(packageRoot, "src", "bin", "exe-start.sh")
|
|
1203
1629
|
];
|
|
1204
1630
|
for (const src of candidates) {
|
|
1205
|
-
if (
|
|
1206
|
-
|
|
1207
|
-
|
|
1631
|
+
if (existsSync8(src)) {
|
|
1632
|
+
writeFileSync5(exeStartDst, readFileSync6(src));
|
|
1633
|
+
chmodSync2(exeStartDst, 493);
|
|
1208
1634
|
break;
|
|
1209
1635
|
}
|
|
1210
1636
|
}
|
|
1211
1637
|
let employees = [];
|
|
1212
1638
|
try {
|
|
1213
|
-
employees = JSON.parse(
|
|
1639
|
+
employees = JSON.parse(readFileSync6(rosterPath, "utf8"));
|
|
1214
1640
|
} catch {
|
|
1215
1641
|
return { created: 0, pathConfigured: false };
|
|
1216
1642
|
}
|
|
@@ -1220,9 +1646,9 @@ function generateSessionWrappers(packageRoot, homeDir) {
|
|
|
1220
1646
|
try {
|
|
1221
1647
|
for (const f of readdirSync(binDir)) {
|
|
1222
1648
|
if (f === "exe-start") continue;
|
|
1223
|
-
const fPath =
|
|
1649
|
+
const fPath = path7.join(binDir, f);
|
|
1224
1650
|
try {
|
|
1225
|
-
const content =
|
|
1651
|
+
const content = readFileSync6(fPath, "utf8");
|
|
1226
1652
|
if (content.includes("exe-start")) {
|
|
1227
1653
|
unlinkSync2(fPath);
|
|
1228
1654
|
}
|
|
@@ -1237,31 +1663,31 @@ exec "${exeStartDst}" "$0" "$@"
|
|
|
1237
1663
|
`;
|
|
1238
1664
|
for (const emp of employees) {
|
|
1239
1665
|
for (let n = 1; n <= MAX_N; n++) {
|
|
1240
|
-
const wrapperPath =
|
|
1241
|
-
|
|
1242
|
-
|
|
1666
|
+
const wrapperPath = path7.join(binDir, `${emp.name}${n}`);
|
|
1667
|
+
writeFileSync5(wrapperPath, wrapperContent);
|
|
1668
|
+
chmodSync2(wrapperPath, 493);
|
|
1243
1669
|
created++;
|
|
1244
1670
|
}
|
|
1245
1671
|
}
|
|
1246
1672
|
const codexLauncherCandidates = [
|
|
1247
|
-
|
|
1248
|
-
|
|
1673
|
+
path7.join(packageRoot, "dist", "bin", "exe-start-codex.js"),
|
|
1674
|
+
path7.join(packageRoot, "src", "bin", "exe-start-codex.ts")
|
|
1249
1675
|
];
|
|
1250
1676
|
let codexLauncher = null;
|
|
1251
1677
|
for (const c of codexLauncherCandidates) {
|
|
1252
|
-
if (
|
|
1678
|
+
if (existsSync8(c)) {
|
|
1253
1679
|
codexLauncher = c;
|
|
1254
1680
|
break;
|
|
1255
1681
|
}
|
|
1256
1682
|
}
|
|
1257
1683
|
if (codexLauncher) {
|
|
1258
1684
|
for (const emp of employees) {
|
|
1259
|
-
const wrapperPath =
|
|
1685
|
+
const wrapperPath = path7.join(binDir, `${emp.name}-codex`);
|
|
1260
1686
|
const content = `#!/bin/bash
|
|
1261
1687
|
exec node "${codexLauncher}" --agent ${emp.name} "$@"
|
|
1262
1688
|
`;
|
|
1263
|
-
|
|
1264
|
-
|
|
1689
|
+
writeFileSync5(wrapperPath, content);
|
|
1690
|
+
chmodSync2(wrapperPath, 493);
|
|
1265
1691
|
created++;
|
|
1266
1692
|
}
|
|
1267
1693
|
}
|
|
@@ -1279,24 +1705,24 @@ export PATH="${binDir}:$PATH"
|
|
|
1279
1705
|
const shell = process.env.SHELL ?? "/bin/bash";
|
|
1280
1706
|
const profilePaths = [];
|
|
1281
1707
|
if (shell.includes("zsh")) {
|
|
1282
|
-
profilePaths.push(
|
|
1708
|
+
profilePaths.push(path7.join(home, ".zshrc"));
|
|
1283
1709
|
} else if (shell.includes("bash")) {
|
|
1284
|
-
profilePaths.push(
|
|
1285
|
-
profilePaths.push(
|
|
1710
|
+
profilePaths.push(path7.join(home, ".bashrc"));
|
|
1711
|
+
profilePaths.push(path7.join(home, ".bash_profile"));
|
|
1286
1712
|
} else {
|
|
1287
|
-
profilePaths.push(
|
|
1713
|
+
profilePaths.push(path7.join(home, ".profile"));
|
|
1288
1714
|
}
|
|
1289
1715
|
for (const profilePath of profilePaths) {
|
|
1290
1716
|
try {
|
|
1291
1717
|
let content = "";
|
|
1292
1718
|
try {
|
|
1293
|
-
content =
|
|
1719
|
+
content = readFileSync6(profilePath, "utf8");
|
|
1294
1720
|
} catch {
|
|
1295
1721
|
}
|
|
1296
1722
|
if (content.includes(".exe-os/bin")) {
|
|
1297
1723
|
return false;
|
|
1298
1724
|
}
|
|
1299
|
-
|
|
1725
|
+
writeFileSync5(profilePath, content + exportLine);
|
|
1300
1726
|
return true;
|
|
1301
1727
|
} catch {
|
|
1302
1728
|
continue;
|
|
@@ -1306,14 +1732,14 @@ export PATH="${binDir}:$PATH"
|
|
|
1306
1732
|
}
|
|
1307
1733
|
|
|
1308
1734
|
// src/bin/install.ts
|
|
1309
|
-
var homedir2 =
|
|
1310
|
-
var EXE_DIR =
|
|
1735
|
+
var homedir2 = os7.homedir;
|
|
1736
|
+
var EXE_DIR = path9.join(homedir2(), ".exe-os");
|
|
1311
1737
|
function restartDaemon() {
|
|
1312
|
-
const pidPath =
|
|
1313
|
-
const sockPath =
|
|
1738
|
+
const pidPath = path9.join(EXE_DIR, "exed.pid");
|
|
1739
|
+
const sockPath = path9.join(EXE_DIR, "exed.sock");
|
|
1314
1740
|
try {
|
|
1315
|
-
if (
|
|
1316
|
-
const pid = parseInt(
|
|
1741
|
+
if (existsSync10(pidPath)) {
|
|
1742
|
+
const pid = parseInt(readFileSync7(pidPath, "utf8").trim(), 10);
|
|
1317
1743
|
if (!isNaN(pid) && pid > 0) {
|
|
1318
1744
|
try {
|
|
1319
1745
|
process.kill(pid, "SIGKILL");
|
|
@@ -1354,11 +1780,11 @@ function restartDaemon() {
|
|
|
1354
1780
|
} catch {
|
|
1355
1781
|
}
|
|
1356
1782
|
try {
|
|
1357
|
-
const wpDir =
|
|
1358
|
-
if (
|
|
1783
|
+
const wpDir = path9.join(EXE_DIR, "worker-pids");
|
|
1784
|
+
if (existsSync10(wpDir)) {
|
|
1359
1785
|
for (const f of readdirSync2(wpDir)) {
|
|
1360
1786
|
try {
|
|
1361
|
-
unlinkSync3(
|
|
1787
|
+
unlinkSync3(path9.join(wpDir, f));
|
|
1362
1788
|
} catch {
|
|
1363
1789
|
}
|
|
1364
1790
|
}
|
|
@@ -1375,7 +1801,7 @@ function restartDaemon() {
|
|
|
1375
1801
|
}
|
|
1376
1802
|
} catch {
|
|
1377
1803
|
}
|
|
1378
|
-
const totalGB =
|
|
1804
|
+
const totalGB = os7.totalmem() / (1024 * 1024 * 1024);
|
|
1379
1805
|
if (totalGB <= 8) {
|
|
1380
1806
|
process.stderr.write(
|
|
1381
1807
|
`exe-os: ${totalGB.toFixed(0)}GB system \u2014 skipping daemon spawn (keyword search mode)
|
|
@@ -1385,13 +1811,13 @@ function restartDaemon() {
|
|
|
1385
1811
|
}
|
|
1386
1812
|
try {
|
|
1387
1813
|
const pkgRoot = resolvePackageRoot();
|
|
1388
|
-
const daemonPath =
|
|
1389
|
-
if (!
|
|
1814
|
+
const daemonPath = path9.join(pkgRoot, "dist", "lib", "exe-daemon.js");
|
|
1815
|
+
if (!existsSync10(daemonPath)) {
|
|
1390
1816
|
process.stderr.write(`exe-os: daemon not found at ${daemonPath} \u2014 skipping respawn
|
|
1391
1817
|
`);
|
|
1392
1818
|
return;
|
|
1393
1819
|
}
|
|
1394
|
-
const logPath =
|
|
1820
|
+
const logPath = path9.join(EXE_DIR, "exed.log");
|
|
1395
1821
|
let stderrFd = "ignore";
|
|
1396
1822
|
try {
|
|
1397
1823
|
stderrFd = openSync(logPath, "a");
|
|
@@ -1461,6 +1887,12 @@ if (args.includes("--commands-only")) {
|
|
|
1461
1887
|
} catch (err) {
|
|
1462
1888
|
process.stderr.write(`exe-os: session wrapper generation failed: ${err instanceof Error ? err.message : String(err)}
|
|
1463
1889
|
`);
|
|
1890
|
+
}
|
|
1891
|
+
try {
|
|
1892
|
+
execSync3("which codex", { encoding: "utf8", timeout: 5e3 });
|
|
1893
|
+
const { runCodexInstaller: runCodexInstaller2 } = await Promise.resolve().then(() => (init_installer2(), installer_exports));
|
|
1894
|
+
await runCodexInstaller2();
|
|
1895
|
+
} catch {
|
|
1464
1896
|
}
|
|
1465
1897
|
restartDaemon();
|
|
1466
1898
|
} catch (err) {
|